diff --git a/api/services/sessions/parser.ts b/api/services/sessions/parser.ts index 5003383..f079255 100644 --- a/api/services/sessions/parser.ts +++ b/api/services/sessions/parser.ts @@ -19,6 +19,21 @@ function detectHook(text: string | null): string | null { return m ? m[1] : null } +function extractResultText(r: unknown): string | null { + if (r == null) return null + if (typeof r === 'string') return r + if (Array.isArray(r)) { + const parts: string[] = [] + for (const p of r) { + if (p && typeof p === 'object' && 'text' in p && typeof (p as { text: unknown }).text === 'string') { + parts.push((p as { text: string }).text) + } + } + return parts.length ? parts.join('\n') : null + } + return null +} + function extractText(rawMsg: unknown): string | null { if (!rawMsg || typeof rawMsg !== 'object') return null const msg = rawMsg as { content?: unknown } @@ -132,9 +147,12 @@ export async function parseSessionFile(jsonlPath: string): Promise } } + const resultText = extractResultText(toolResult) const skill = detectSkillInvoked(text) - if (skill) skillsInvoked.add(skill) - const hook = detectHook(text) + const skillFromResult = detectSkillInvoked(resultText) + const finalSkill = skill ?? skillFromResult + if (finalSkill) skillsInvoked.add(finalSkill) + const hook = detectHook(text) ?? detectHook(resultText) events.push({ index: idx++, @@ -145,7 +163,7 @@ export async function parseSessionFile(jsonlPath: string): Promise tool_name: toolName, tool_input: toolInput, tool_result: toolResult, - skill_invoked: skill, + skill_invoked: finalSkill, hook_name: hook, }) } diff --git a/api/tests/sessions-parser.test.ts b/api/tests/sessions-parser.test.ts index a456a4b..6669c57 100644 --- a/api/tests/sessions-parser.test.ts +++ b/api/tests/sessions-parser.test.ts @@ -67,6 +67,44 @@ describe('parseSessionFile', () => { expect(result.meta.skills_invoked).toContain('superpowers:brainstorming') }) + it('detecta skill invocation em tool_result.content (string)', async () => { + const path = writeJsonl([ + { + type: 'user', + timestamp: '2026-04-23T10:00:00Z', + message: { + role: 'user', + content: [ + { type: 'tool_result', tool_use_id: 'abc', content: 'Launching skill: infraestrutura:easypanel-monitor\nOther log output' }, + ], + }, + }, + ]) + const result = await parseSessionFile(path) + expect(result.meta.skills_invoked).toContain('infraestrutura:easypanel-monitor') + }) + + it('detecta skill invocation em tool_result.content (array de text blocks)', async () => { + const path = writeJsonl([ + { + type: 'user', + timestamp: '2026-04-23T10:00:00Z', + message: { + role: 'user', + content: [ + { + type: 'tool_result', + tool_use_id: 'abc', + content: [{ type: 'text', text: 'Launching skill: superpowers:brainstorming' }], + }, + ], + }, + }, + ]) + const result = await parseSessionFile(path) + expect(result.meta.skills_invoked).toContain('superpowers:brainstorming') + }) + it('ignora linhas JSON inválidas silenciosamente', async () => { const path = writeJsonl([ { type: 'user', message: { role: 'user', content: [{ type: 'text', text: 'válido' }] } },