@shepai/cli 1.103.1-pr308.436876d → 1.103.1-pr309.f0bca17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. package/dist/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.d.ts.map +1 -1
  2. package/dist/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.js +1 -1
  3. package/dist/src/presentation/web/components/common/feature-drawer-tabs/activity-tab.d.ts.map +1 -1
  4. package/dist/src/presentation/web/components/common/feature-drawer-tabs/activity-tab.js +50 -8
  5. package/dist/src/presentation/web/components/common/feature-drawer-tabs/event-log-viewer.d.ts +5 -0
  6. package/dist/src/presentation/web/components/common/feature-drawer-tabs/event-log-viewer.d.ts.map +1 -0
  7. package/dist/src/presentation/web/components/common/feature-drawer-tabs/event-log-viewer.js +113 -0
  8. package/dist/src/presentation/web/components/common/feature-drawer-tabs/event-log-viewer.stories.d.ts +18 -0
  9. package/dist/src/presentation/web/components/common/feature-drawer-tabs/event-log-viewer.stories.d.ts.map +1 -0
  10. package/dist/src/presentation/web/components/common/feature-drawer-tabs/event-log-viewer.stories.js +88 -0
  11. package/dist/src/presentation/web/components/common/feature-drawer-tabs/feature-drawer-tabs.d.ts +4 -1
  12. package/dist/src/presentation/web/components/common/feature-drawer-tabs/feature-drawer-tabs.d.ts.map +1 -1
  13. package/dist/src/presentation/web/components/common/feature-drawer-tabs/feature-drawer-tabs.js +31 -11
  14. package/dist/src/presentation/web/components/common/feature-drawer-tabs/log-tab.d.ts.map +1 -1
  15. package/dist/src/presentation/web/components/common/feature-drawer-tabs/log-tab.js +10 -2
  16. package/dist/src/presentation/web/components/common/feature-drawer-tabs/log-tab.stories.d.ts +5 -3
  17. package/dist/src/presentation/web/components/common/feature-drawer-tabs/log-tab.stories.d.ts.map +1 -1
  18. package/dist/src/presentation/web/components/common/feature-drawer-tabs/log-tab.stories.js +92 -9
  19. package/dist/src/presentation/web/components/common/feature-drawer-tabs/overview-tab.d.ts.map +1 -1
  20. package/dist/src/presentation/web/components/common/feature-drawer-tabs/overview-tab.js +35 -2
  21. package/dist/src/presentation/web/lib/parse-log-line.d.ts +49 -0
  22. package/dist/src/presentation/web/lib/parse-log-line.d.ts.map +1 -0
  23. package/dist/src/presentation/web/lib/parse-log-line.js +126 -0
  24. package/dist/tsconfig.build.tsbuildinfo +1 -1
  25. package/package.json +1 -1
  26. package/web/.next/BUILD_ID +1 -1
  27. package/web/.next/build-manifest.json +2 -2
  28. package/web/.next/fallback-build-manifest.json +2 -2
  29. package/web/.next/prerender-manifest.json +3 -3
  30. package/web/.next/required-server-files.js +1 -1
  31. package/web/.next/required-server-files.json +1 -1
  32. package/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +20 -20
  33. package/web/.next/server/app/(dashboard)/@drawer/create/page.js.nft.json +1 -1
  34. package/web/.next/server/app/(dashboard)/@drawer/create/page_client-reference-manifest.js +1 -1
  35. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page/server-reference-manifest.json +25 -25
  36. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page.js.nft.json +1 -1
  37. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
  38. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +25 -25
  39. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js.nft.json +1 -1
  40. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page_client-reference-manifest.js +1 -1
  41. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +18 -18
  42. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js.nft.json +1 -1
  43. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  44. package/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +20 -20
  45. package/web/.next/server/app/(dashboard)/create/page.js.nft.json +1 -1
  46. package/web/.next/server/app/(dashboard)/create/page_client-reference-manifest.js +1 -1
  47. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page/server-reference-manifest.json +25 -25
  48. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page.js.nft.json +1 -1
  49. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
  50. package/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +25 -25
  51. package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js.nft.json +1 -1
  52. package/web/.next/server/app/(dashboard)/feature/[featureId]/page_client-reference-manifest.js +1 -1
  53. package/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +18 -18
  54. package/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
  55. package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
  56. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +18 -18
  57. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
  58. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  59. package/web/.next/server/app/_global-error.html +2 -2
  60. package/web/.next/server/app/_global-error.rsc +1 -1
  61. package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  62. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  63. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  64. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  65. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  66. package/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  67. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  68. package/web/.next/server/app/api/attachments/preview/route.js.nft.json +1 -1
  69. package/web/.next/server/app/api/cli-upgrade/route.js +2 -2
  70. package/web/.next/server/app/api/evidence/route.js.nft.json +1 -1
  71. package/web/.next/server/app/settings/page/server-reference-manifest.json +5 -5
  72. package/web/.next/server/app/settings/page.js.nft.json +1 -1
  73. package/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  74. package/web/.next/server/app/skills/page/server-reference-manifest.json +6 -6
  75. package/web/.next/server/app/skills/page.js.nft.json +1 -1
  76. package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  77. package/web/.next/server/app/tools/page/server-reference-manifest.json +6 -6
  78. package/web/.next/server/app/tools/page.js.nft.json +1 -1
  79. package/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
  80. package/web/.next/server/app/version/page/server-reference-manifest.json +1 -1
  81. package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  82. package/web/.next/server/chunks/[root-of-the-server]__d2c18946._.js +2 -2
  83. package/web/.next/server/chunks/[root-of-the-server]__f47bb8a5._.js +1 -1
  84. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js +1 -1
  85. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js.map +1 -1
  86. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_0a7546ed.js +1 -1
  87. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_0a7546ed.js.map +1 -1
  88. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_978cbb4c.js +1 -1
  89. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_978cbb4c.js.map +1 -1
  90. package/web/.next/server/chunks/ssr/[root-of-the-server]__11033878._.js +1 -1
  91. package/web/.next/server/chunks/ssr/[root-of-the-server]__11033878._.js.map +1 -1
  92. package/web/.next/server/chunks/ssr/[root-of-the-server]__24dd0927._.js +2 -2
  93. package/web/.next/server/chunks/ssr/[root-of-the-server]__357d99f9._.js +1 -1
  94. package/web/.next/server/chunks/ssr/[root-of-the-server]__51fc98b0._.js +1 -1
  95. package/web/.next/server/chunks/ssr/[root-of-the-server]__51fc98b0._.js.map +1 -1
  96. package/web/.next/server/chunks/ssr/[root-of-the-server]__57f37786._.js +1 -1
  97. package/web/.next/server/chunks/ssr/[root-of-the-server]__57f37786._.js.map +1 -1
  98. package/web/.next/server/chunks/ssr/[root-of-the-server]__5d87af2e._.js +1 -1
  99. package/web/.next/server/chunks/ssr/[root-of-the-server]__708dcad1._.js +1 -1
  100. package/web/.next/server/chunks/ssr/[root-of-the-server]__708dcad1._.js.map +1 -1
  101. package/web/.next/server/chunks/ssr/[root-of-the-server]__845a9d2a._.js +1 -1
  102. package/web/.next/server/chunks/ssr/[root-of-the-server]__845a9d2a._.js.map +1 -1
  103. package/web/.next/server/chunks/ssr/[root-of-the-server]__89e8a5cc._.js +2 -2
  104. package/web/.next/server/chunks/ssr/[root-of-the-server]__8e002910._.js +1 -1
  105. package/web/.next/server/chunks/ssr/[root-of-the-server]__8e002910._.js.map +1 -1
  106. package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js +2 -2
  107. package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js.map +1 -1
  108. package/web/.next/server/chunks/ssr/[root-of-the-server]__b1e68e34._.js +3 -0
  109. package/web/.next/server/chunks/ssr/{[root-of-the-server]__d91fdd51._.js.map → [root-of-the-server]__b1e68e34._.js.map} +1 -1
  110. package/web/.next/server/chunks/ssr/[root-of-the-server]__fdc75809._.js +1 -1
  111. package/web/.next/server/chunks/ssr/[root-of-the-server]__fdc75809._.js.map +1 -1
  112. package/web/.next/server/chunks/ssr/_135edb9c._.js +1 -1
  113. package/web/.next/server/chunks/ssr/_135edb9c._.js.map +1 -1
  114. package/web/.next/server/chunks/ssr/{_afa40e06._.js → _299324d0._.js} +2 -2
  115. package/web/.next/server/chunks/ssr/{_afa40e06._.js.map → _299324d0._.js.map} +1 -1
  116. package/web/.next/server/chunks/ssr/{_a09fb04a._.js → _601a084c._.js} +2 -2
  117. package/web/.next/server/chunks/ssr/{_a09fb04a._.js.map → _601a084c._.js.map} +1 -1
  118. package/web/.next/server/chunks/ssr/_8dacd1e7._.js +1 -1
  119. package/web/.next/server/chunks/ssr/_8dacd1e7._.js.map +1 -1
  120. package/web/.next/server/chunks/ssr/{_baab52a9._.js → _c205e198._.js} +2 -2
  121. package/web/.next/server/chunks/ssr/{_baab52a9._.js.map → _c205e198._.js.map} +1 -1
  122. package/web/.next/server/chunks/ssr/_cc77a143._.js +1 -1
  123. package/web/.next/server/chunks/ssr/_cc77a143._.js.map +1 -1
  124. package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js +1 -1
  125. package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js.map +1 -1
  126. package/web/.next/server/chunks/ssr/src_presentation_web_7275ce5d._.js +2 -2
  127. package/web/.next/server/chunks/ssr/src_presentation_web_7275ce5d._.js.map +1 -1
  128. package/web/.next/server/chunks/ssr/src_presentation_web_758dfa10._.js +2 -2
  129. package/web/.next/server/chunks/ssr/src_presentation_web_758dfa10._.js.map +1 -1
  130. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_84803221.js +1 -1
  131. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_84803221.js.map +1 -1
  132. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1fa8042e.js +1 -1
  133. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1fa8042e.js.map +1 -1
  134. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_24684200.js +1 -1
  135. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_24684200.js.map +1 -1
  136. package/web/.next/server/chunks/ssr/src_presentation_web_components_bf50c527._.js +1 -1
  137. package/web/.next/server/chunks/ssr/src_presentation_web_components_de814675._.js +1 -1
  138. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
  139. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js.map +1 -1
  140. package/web/.next/server/pages/500.html +2 -2
  141. package/web/.next/server/server-reference-manifest.js +1 -1
  142. package/web/.next/server/server-reference-manifest.json +30 -30
  143. package/web/.next/static/chunks/{3b5a1f399be3c204.js → 09fe794bed5a0077.js} +1 -1
  144. package/web/.next/static/chunks/{5b52afbeb6af43f8.js → 20f5682099fbb10c.js} +1 -1
  145. package/web/.next/static/chunks/{2f3dedf11842dbb9.js → 2a63561f9716d615.js} +1 -1
  146. package/web/.next/static/chunks/3bba99704fc1d30c.js +1 -0
  147. package/web/.next/static/chunks/3fa3006034a0add8.css +1 -0
  148. package/web/.next/static/chunks/{c72a9bffb0609ccb.js → 47387a05a4f0f22b.js} +1 -1
  149. package/web/.next/static/chunks/4873b5f96cfdffb9.js +1 -0
  150. package/web/.next/static/chunks/{6f16220ca6a15488.js → 8c4731c87b599597.js} +1 -1
  151. package/web/.next/static/chunks/994e46e475fb78f9.js +2 -0
  152. package/web/.next/static/chunks/a8eceddc883b57bb.js +1 -0
  153. package/web/.next/static/chunks/b1f3c0e8e9872ef8.js +1 -0
  154. package/web/.next/static/chunks/{f164721e9111efd2.js → e7abc4f19da20094.js} +2 -2
  155. package/web/.next/server/chunks/ssr/[root-of-the-server]__d91fdd51._.js +0 -3
  156. package/web/.next/static/chunks/07b853e29af1e5f2.js +0 -1
  157. package/web/.next/static/chunks/2549eb5f74129ce6.js +0 -1
  158. package/web/.next/static/chunks/5a850475e2b3e8d7.js +0 -2
  159. package/web/.next/static/chunks/93058c22968e2fef.css +0 -1
  160. package/web/.next/static/chunks/d7acdf8831c942f1.js +0 -1
  161. package/web/.next/static/chunks/f8a27920b2acfead.js +0 -1
  162. /package/web/.next/static/{Bn9oiHUSrxNyoG9stevwc → vSr396p_Rdr92qmX9fzqd}/_buildManifest.js +0 -0
  163. /package/web/.next/static/{Bn9oiHUSrxNyoG9stevwc → vSr396p_Rdr92qmX9fzqd}/_clientMiddlewareManifest.json +0 -0
  164. /package/web/.next/static/{Bn9oiHUSrxNyoG9stevwc → vSr396p_Rdr92qmX9fzqd}/_ssgManifest.js +0 -0
@@ -8,14 +8,61 @@ const meta = {
8
8
  layout: 'fullscreen',
9
9
  },
10
10
  decorators: [
11
- (Story) => (_jsx("div", { style: { height: '600px', width: '400px', border: '1px solid var(--color-border)' }, children: _jsx(Story, {}) })),
11
+ (Story) => (_jsx("div", { style: { height: '600px', width: '480px', border: '1px solid var(--color-border)' }, children: _jsx(Story, {}) })),
12
12
  ],
13
13
  };
14
14
  export default meta;
15
15
  /* ---------------------------------------------------------------------------
16
16
  * Data fixtures
17
17
  * ------------------------------------------------------------------------- */
18
- const typicalLogContent = `[2026-03-08T10:00:01.123Z] Starting agent run for feature "auth-module"
18
+ const structuredLogContent = `[2026-03-08T10:00:01.000Z] [claude-code|claude-sonnet-4-6] [WORKER] Starting worker — full command:
19
+ [2026-03-08T10:00:01.001Z] [claude-code|claude-sonnet-4-6] [WORKER] feature-agent-worker --feature-id auth-module --run-id run-123 --repo /home/user/myproject --agent-type claude-code --model claude-sonnet-4-6
20
+ [2026-03-08T10:00:01.010Z] [claude-code|claude-sonnet-4-6] [WORKER] Initializing container...
21
+ [2026-03-08T10:00:01.050Z] [claude-code|claude-sonnet-4-6] [WORKER] Creating executor from pinned agent type: claude-code
22
+ [2026-03-08T10:00:01.100Z] [claude-code|claude-sonnet-4-6] [WORKER] Updating status to running (PID 54321)...
23
+ [2026-03-08T10:00:01.110Z] [claude-code|claude-sonnet-4-6] [WORKER] Starting graph invocation...
24
+ [2026-03-08T10:00:05.000Z] [requirements] [claude-code|claude-sonnet-4-6] Starting...
25
+ [2026-03-08T10:00:05.010Z] [requirements] [claude-code|claude-sonnet-4-6] Executing agent at cwd=/home/user/myproject
26
+ [2026-03-08T10:00:05.020Z] [requirements] [claude-code|claude-sonnet-4-6] Prompt length: 2847 chars
27
+ [2026-03-08T10:00:05.100Z] [requirements] [claude-code|claude-sonnet-4-6] Spawning: claude -p "Analyze this project..." --output-format stream-json --verbose
28
+ [2026-03-08T10:00:05.102Z] [requirements] [claude-code|claude-sonnet-4-6] Subprocess PID: 54322
29
+ [2026-03-08T10:00:08.234Z] [requirements] [claude-code|claude-sonnet-4-6] [tool] Read {"file_path": "src/main.ts"}
30
+ [2026-03-08T10:00:10.345Z] [requirements] [claude-code|claude-sonnet-4-6] [text] Analyzing the codebase structure and identifying key entry points for the authentication module
31
+ [2026-03-08T10:00:12.567Z] [requirements] [claude-code|claude-sonnet-4-6] [tool] Glob {"pattern": "src/**/*.ts"}
32
+ [2026-03-08T10:00:14.789Z] [requirements] [claude-code|claude-sonnet-4-6] [tool] Read {"file_path": "src/auth/providers.ts"}
33
+ [2026-03-08T10:00:16.012Z] [requirements] [claude-code|claude-sonnet-4-6] [tool] Grep {"pattern": "interface.*Auth", "path": "src/"}
34
+ [2026-03-08T10:00:18.234Z] [requirements] [claude-code|claude-sonnet-4-6] [text] The project uses a clean architecture pattern with dependency injection. I can see the auth interfaces are defined in the domain layer
35
+ [2026-03-08T10:00:20.456Z] [requirements] [claude-code|claude-sonnet-4-6] [tool] Bash {"command": "find src -name '*.test.ts' | wc -l"}
36
+ [2026-03-08T10:00:45.789Z] [requirements] [claude-code|claude-sonnet-4-6] [result] 8920 chars, session=sess-abc123def456
37
+ [2026-03-08T10:00:45.790Z] [requirements] [claude-code|claude-sonnet-4-6] [tokens] 2450 in / 4560 out
38
+ [2026-03-08T10:00:45.791Z] [requirements] [claude-code|claude-sonnet-4-6] Process closed with code 0, result=8920 chars
39
+ [2026-03-08T10:00:45.800Z] [requirements] [claude-code|claude-sonnet-4-6] Complete (8920 chars, 40.8s)
40
+ [2026-03-08T10:00:45.900Z] [requirements] [claude-code|claude-sonnet-4-6] Interrupting for human approval
41
+ [2026-03-08T10:02:30.000Z] [requirements] [claude-code|claude-sonnet-4-6] Phase approved, skipping re-execution
42
+ [2026-03-08T10:02:30.100Z] [plan] [claude-code|claude-sonnet-4-6] Starting...
43
+ [2026-03-08T10:02:30.110Z] [plan] [claude-code|claude-sonnet-4-6] Executing agent at cwd=/home/user/myproject
44
+ [2026-03-08T10:02:30.120Z] [plan] [claude-code|claude-sonnet-4-6] Prompt length: 15234 chars
45
+ [2026-03-08T10:02:35.100Z] [plan] [claude-code|claude-sonnet-4-6] [tool] Read {"file_path": "CLAUDE.md"}
46
+ [2026-03-08T10:02:38.200Z] [plan] [claude-code|claude-sonnet-4-6] [text] Creating implementation plan with TDD approach as required by project conventions
47
+ [2026-03-08T10:02:42.300Z] [plan] [claude-code|claude-sonnet-4-6] [tool] Write {"file_path": "specs/001-auth/plan.yaml"}
48
+ [2026-03-08T10:02:45.400Z] [plan] [claude-code|claude-sonnet-4-6] [text] Plan includes 3 phases: scaffold auth module, implement OAuth2 providers, add tests and documentation
49
+ [2026-03-08T10:03:15.234Z] [plan] [claude-code|claude-sonnet-4-6] [result] 12345 chars, session=sess-abc123def456
50
+ [2026-03-08T10:03:15.235Z] [plan] [claude-code|claude-sonnet-4-6] [tokens] 8900 in / 3456 out
51
+ [2026-03-08T10:03:15.300Z] [plan] [claude-code|claude-sonnet-4-6] Complete (12345 chars, 45.1s)
52
+ [2026-03-08T10:05:00.000Z] [implement] [claude-code|claude-sonnet-4-6] Starting...
53
+ [2026-03-08T10:05:01.000Z] [implement] [claude-code|claude-sonnet-4-6] [tool] Write {"file_path": "src/auth/oauth-provider.ts"}
54
+ [2026-03-08T10:05:03.000Z] [implement] [claude-code|claude-sonnet-4-6] [tool] Write {"file_path": "src/auth/token-manager.ts"}
55
+ [2026-03-08T10:05:05.000Z] [implement] [claude-code|claude-sonnet-4-6] [tool] Edit {"file_path": "src/auth/index.ts"}
56
+ [2026-03-08T10:05:07.000Z] [implement] [claude-code|claude-sonnet-4-6] [tool] Bash {"command": "pnpm test:unit"}
57
+ [2026-03-08T10:05:30.000Z] [implement] [claude-code|claude-sonnet-4-6] [text] All 14 unit tests passing. Moving to integration tests
58
+ [2026-03-08T10:05:35.000Z] [implement] [claude-code|claude-sonnet-4-6] [tool] Bash {"command": "pnpm test:int"}
59
+ [2026-03-08T10:05:55.000Z] [implement] [claude-code|claude-sonnet-4-6] [result] 24560 chars, session=sess-abc123def456
60
+ [2026-03-08T10:05:55.001Z] [implement] [claude-code|claude-sonnet-4-6] [tokens] 15200 in / 9360 out
61
+ [2026-03-08T10:05:55.100Z] [implement] [claude-code|claude-sonnet-4-6] Complete (24560 chars, 54.1s)
62
+ [2026-03-08T10:06:00.000Z] [claude-code|claude-sonnet-4-6] [WORKER] Graph invocation completed. Error: none
63
+ [2026-03-08T10:06:00.100Z] [claude-code|claude-sonnet-4-6] [WORKER] Run marked as completed
64
+ [2026-03-08T10:06:00.200Z] [claude-code|claude-sonnet-4-6] [WORKER] Worker completed successfully, exiting.`;
65
+ const rawLogContent = `[2026-03-08T10:00:01.123Z] Starting agent run for feature "auth-module"
19
66
  [2026-03-08T10:00:02.789Z] Agent started — analyzing codebase
20
67
  [2026-03-08T10:00:03.456Z] Scanning repository structure...
21
68
  [2026-03-08T10:00:04.012Z] Found 127 source files across 14 packages
@@ -35,22 +82,58 @@ const typicalLogContent = `[2026-03-08T10:00:01.123Z] Starting agent run for fea
35
82
  [2026-03-08T10:00:24.789Z] Phase 3: add tests and documentation
36
83
  [2026-03-08T10:00:26.456Z] Generating test suite...
37
84
  [2026-03-08T10:00:28.012Z] All phases complete — feature "auth-module" ready for review`;
38
- const longLogContent = Array.from({ length: 200 }, (_, i) => `[2026-03-08T10:${String(Math.floor(i / 60)).padStart(2, '0')}:${String(i % 60).padStart(2, '0')}.${String((i * 7) % 1000).padStart(3, '0')}Z] Step ${i + 1}: processing task batch ${Math.floor(i / 10) + 1} — file ${i + 1} of 200`).join('\n');
85
+ const longStructuredContent = Array.from({ length: 100 }, (_, i) => {
86
+ const min = String(Math.floor(i / 60)).padStart(2, '0');
87
+ const sec = String(i % 60).padStart(2, '0');
88
+ const ms = String((i * 7) % 1000).padStart(3, '0');
89
+ const ts = `2026-03-08T10:${min}:${sec}.${ms}Z`;
90
+ const phase = ['requirements', 'research', 'plan', 'implement'][i % 4];
91
+ const tags = ['[tool]', '[text]', '[tool]', '[text]', '[result]', '[tokens]'];
92
+ const tag = tags[i % tags.length];
93
+ const messages = {
94
+ '[tool]': [
95
+ 'Read {"file_path": "src/components/button.tsx"}',
96
+ 'Bash {"command": "pnpm test:unit"}',
97
+ 'Grep {"pattern": "export function", "path": "src/"}',
98
+ 'Write {"file_path": "src/new-file.ts"}',
99
+ 'Glob {"pattern": "**/*.test.ts"}',
100
+ ],
101
+ '[text]': [
102
+ 'Analyzing the component structure for potential improvements',
103
+ 'The test suite is comprehensive with good coverage',
104
+ 'Implementing the authentication flow with OAuth2 providers',
105
+ 'Refactoring to use dependency injection pattern',
106
+ ],
107
+ '[result]': ['4520 chars, session=sess-xyz789'],
108
+ '[tokens]': ['3200 in / 1800 out'],
109
+ };
110
+ const msgList = messages[tag] ?? messages['[text]'];
111
+ const msg = msgList[i % msgList.length];
112
+ return `[${ts}] [${phase}] [claude-code|claude-sonnet-4-6] ${tag} ${msg}`;
113
+ }).join('\n');
39
114
  /* ---------------------------------------------------------------------------
40
115
  * Stories
41
116
  * ------------------------------------------------------------------------- */
42
- /** Default — typical agent log output with live connection. */
117
+ /** Default — structured agent log output with live connection (claude-code). */
43
118
  export const Default = {
44
119
  args: {
45
- content: typicalLogContent,
120
+ content: structuredLogContent,
46
121
  isConnected: true,
47
122
  error: null,
48
123
  },
49
124
  };
50
- /** LongLog — 200 lines of log output to test scrolling behavior. */
51
- export const LongLog = {
125
+ /** LongStructured — 100 structured events to test scrolling behavior. */
126
+ export const LongStructured = {
52
127
  args: {
53
- content: longLogContent,
128
+ content: longStructuredContent,
129
+ isConnected: true,
130
+ error: null,
131
+ },
132
+ };
133
+ /** RawFallback — unstructured log content falls back gracefully. */
134
+ export const RawFallback = {
135
+ args: {
136
+ content: rawLogContent,
54
137
  isConnected: true,
55
138
  error: null,
56
139
  },
@@ -58,7 +141,7 @@ export const LongLog = {
58
141
  /** Disconnected — log content present but connection is lost. */
59
142
  export const Disconnected = {
60
143
  args: {
61
- content: typicalLogContent,
144
+ content: structuredLogContent,
62
145
  isConnected: false,
63
146
  error: null,
64
147
  },
@@ -1 +1 @@
1
- {"version":3,"file":"overview-tab.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer-tabs/overview-tab.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAMxE,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,eAAe,CAAC;CACvB;AAED,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,gBAAgB,2CAwCrD"}
1
+ {"version":3,"file":"overview-tab.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer-tabs/overview-tab.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAOxE,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,eAAe,CAAC;CACvB;AAED,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,gBAAgB,2CAwCrD"}
@@ -1,5 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useState, useEffect, useRef } from 'react';
3
4
  import { AlertTriangle, ExternalLink, GitBranch, GitCommitHorizontal, Zap } from 'lucide-react';
4
5
  import { InlineAttachments } from '../../common/inline-attachments/index.js';
5
6
  import { PrStatus } from '../../../../../../packages/core/src/domain/generated/output.js';
@@ -10,6 +11,7 @@ import { CiStatusBadge } from '../../common/ci-status-badge/index.js';
10
11
  import { CometSpinner } from '../../ui/comet-spinner.js';
11
12
  import { featureNodeStateConfig, lifecycleDisplayLabels } from '../../common/feature-node/index.js';
12
13
  import { getAgentTypeIcon, agentTypeLabels, } from '../../common/feature-node/agent-type-icons.js';
14
+ import { formatDuration } from '../../../lib/format-duration.js';
13
15
  export function OverviewTab({ data }) {
14
16
  const isCompleted = data.lifecycle === 'maintain';
15
17
  return (_jsxs(_Fragment, { children: [_jsxs("div", { "data-testid": "feature-drawer-status", className: "flex flex-col gap-3 p-4", children: [_jsx("div", { className: "text-muted-foreground text-xs font-semibold tracking-wider", children: lifecycleDisplayLabels[data.lifecycle] }), _jsx(FeatureStateBadge, { data: data }), !isCompleted && data.progress > 0 ? (_jsxs("div", { "data-testid": "feature-drawer-progress", className: "flex flex-col gap-1", children: [_jsxs("div", { className: "text-muted-foreground flex items-center justify-between text-xs", children: [_jsx("span", { children: "Progress" }), _jsxs("span", { children: [data.progress, "%"] })] }), _jsx("div", { className: "bg-muted h-2 w-full overflow-hidden rounded-full", children: _jsx("div", { className: cn('h-full rounded-full transition-all', featureNodeStateConfig[data.state].progressClass), style: { width: `${data.progress}%` } }) })] })) : null, isCompleted && data.pr ? _jsx(FeaturePrInfo, { pr: data.pr }) : null] }), _jsx(FeatureInfo, { data: data }), !isCompleted && data.pr ? (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsx("div", { className: "p-4", children: _jsx(FeaturePrInfo, { pr: data.pr }) })] })) : null, _jsx(FeatureDetails, { data: data })] }));
@@ -63,11 +65,42 @@ function FeaturePrInfo({ pr }) {
63
65
  return (_jsx("div", { "data-testid": "feature-drawer-pr", children: _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("a", { href: pr.url, target: "_blank", rel: "noopener noreferrer", className: "text-primary flex items-center gap-1.5 text-sm font-semibold underline underline-offset-2", children: ["PR #", pr.number, _jsx(ExternalLink, { className: "h-3.5 w-3.5" })] }), _jsx(Badge, { className: prStatusStyles[pr.status], children: pr.status })] }), pr.ciStatus ? (_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "CI Status" }), _jsx(CiStatusBadge, { status: pr.ciStatus })] })) : null, pr.mergeable === false ? (_jsxs("div", { "data-testid": "pr-merge-conflict", className: "flex items-center justify-between", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "Merge Status" }), _jsxs(Badge, { className: "border-transparent bg-orange-50 text-orange-700 hover:bg-orange-50", children: [_jsx(AlertTriangle, { className: "mr-1 h-3.5 w-3.5" }), "Conflicts"] })] })) : null, pr.commitHash ? (_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "Commit" }), _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx(GitCommitHorizontal, { className: "text-muted-foreground h-3.5 w-3.5" }), _jsx("code", { className: "bg-muted text-foreground rounded-md px-1.5 py-0.5 font-mono text-[11px]", children: pr.commitHash.slice(0, 7) })] })] })) : null] }) }));
64
66
  }
65
67
  // ── Details section ──────────────────────────────────────────────────
68
+ /** Hook that returns a live-updating elapsed time string for running features. */
69
+ function useElapsedTime(startedAt) {
70
+ const [elapsed, setElapsed] = useState(null);
71
+ const intervalRef = useRef(null);
72
+ useEffect(() => {
73
+ if (!startedAt) {
74
+ setElapsed(null);
75
+ return;
76
+ }
77
+ // Compute immediately
78
+ setElapsed(formatDuration(Math.max(0, Date.now() - startedAt)));
79
+ // Tick every second
80
+ intervalRef.current = setInterval(() => {
81
+ setElapsed(formatDuration(Math.max(0, Date.now() - startedAt)));
82
+ }, 1000);
83
+ return () => {
84
+ if (intervalRef.current) {
85
+ clearInterval(intervalRef.current);
86
+ intervalRef.current = null;
87
+ }
88
+ };
89
+ }, [startedAt]);
90
+ return elapsed;
91
+ }
66
92
  function FeatureDetails({ data }) {
67
- const hasAnyDetail = data.fastMode ?? data.agentType ?? data.runtime ?? data.blockedBy ?? data.errorMessage;
93
+ const isRunning = data.state === 'running' || data.state === 'action-required';
94
+ const elapsedTime = useElapsedTime(isRunning ? data.startedAt : undefined);
95
+ const hasAnyDetail = data.fastMode ??
96
+ data.agentType ??
97
+ data.runtime ??
98
+ elapsedTime ??
99
+ data.blockedBy ??
100
+ data.errorMessage;
68
101
  if (!hasAnyDetail)
69
102
  return null;
70
- return (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsxs("div", { "data-testid": "feature-drawer-details", className: "flex flex-col gap-3 p-4", children: [data.fastMode ? (_jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "Mode" }), _jsxs("span", { className: "flex items-center gap-2 text-sm", children: [_jsx(Zap, { className: "h-4 w-4 shrink-0 text-amber-500" }), "Fast Mode"] })] })) : null, data.agentType ? _jsx(AgentDetailRow, { agentType: data.agentType }) : null, data.runtime ? _jsx(DetailRow, { label: "Runtime", value: data.runtime }) : null, data.blockedBy ? _jsx(DetailRow, { label: "Blocked by", value: data.blockedBy }) : null, data.errorMessage ? _jsx(DetailRow, { label: "Error", value: data.errorMessage }) : null] })] }));
103
+ return (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsxs("div", { "data-testid": "feature-drawer-details", className: "flex flex-col gap-3 p-4", children: [data.fastMode ? (_jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "Mode" }), _jsxs("span", { className: "flex items-center gap-2 text-sm", children: [_jsx(Zap, { className: "h-4 w-4 shrink-0 text-amber-500" }), "Fast Mode"] })] })) : null, data.agentType ? _jsx(AgentDetailRow, { agentType: data.agentType }) : null, data.runtime ? _jsx(DetailRow, { label: "Runtime", value: data.runtime }) : null, !data.runtime && elapsedTime ? (_jsx(DetailRow, { label: "Running for", value: elapsedTime })) : null, data.blockedBy ? _jsx(DetailRow, { label: "Blocked by", value: data.blockedBy }) : null, data.errorMessage ? _jsx(DetailRow, { label: "Error", value: data.errorMessage }) : null] })] }));
71
104
  }
72
105
  function AgentDetailRow({ agentType }) {
73
106
  const Icon = getAgentTypeIcon(agentType);
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Parses structured log lines from the feature agent worker process.
3
+ *
4
+ * Log format: [timestamp] [phase] [agent|model] [tag] message
5
+ * Worker format: [timestamp] [agent|model] [WORKER] message
6
+ * Fallback: raw string lines
7
+ */
8
+ export type LogEventTag = 'tool' | 'text' | 'result' | 'tokens' | 'raw' | 'worker' | 'info';
9
+ export interface ParsedLogLine {
10
+ /** ISO timestamp */
11
+ timestamp: string | null;
12
+ /** Phase name (e.g. "requirements", "implement") */
13
+ phase: string | null;
14
+ /** Agent type (e.g. "claude-code") */
15
+ agent: string | null;
16
+ /** Model name (e.g. "claude-haiku-4-5") */
17
+ model: string | null;
18
+ /** Event tag */
19
+ tag: LogEventTag;
20
+ /** The message content after tags */
21
+ message: string;
22
+ /** Original raw line */
23
+ raw: string;
24
+ }
25
+ export declare function parseLogLine(raw: string): ParsedLogLine;
26
+ /** Tool call info extracted from [tool] messages */
27
+ export interface ToolCallInfo {
28
+ toolName: string;
29
+ args: string;
30
+ }
31
+ /** Parse a [tool] message into tool name and args */
32
+ export declare function parseToolCall(message: string): ToolCallInfo;
33
+ /** Result info extracted from [result] messages */
34
+ export interface ResultInfo {
35
+ chars: number;
36
+ sessionId: string | null;
37
+ }
38
+ /** Parse a [result] message */
39
+ export declare function parseResultMessage(message: string): ResultInfo;
40
+ /** Token info extracted from [tokens] messages */
41
+ export interface TokenInfo {
42
+ inputTokens: number;
43
+ outputTokens: number;
44
+ }
45
+ /** Parse a [tokens] message */
46
+ export declare function parseTokensMessage(message: string): TokenInfo;
47
+ /** Parse all lines from raw content string */
48
+ export declare function parseLogContent(content: string): ParsedLogLine[];
49
+ //# sourceMappingURL=parse-log-line.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-log-line.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/lib/parse-log-line.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE5F,MAAM,WAAW,aAAa;IAC5B,oBAAoB;IACpB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,oDAAoD;IACpD,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,sCAAsC;IACtC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,2CAA2C;IAC3C,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,gBAAgB;IAChB,GAAG,EAAE,WAAW,CAAC;IACjB,qCAAqC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,wBAAwB;IACxB,GAAG,EAAE,MAAM,CAAC;CACb;AAgBD,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CAwEvD;AAED,oDAAoD;AACpD,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qDAAqD;AACrD,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,CAS3D;AAED,mDAAmD;AACnD,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,+BAA+B;AAC/B,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,CAO9D;AAED,kDAAkD;AAClD,MAAM,WAAW,SAAS;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,+BAA+B;AAC/B,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAO7D;AAED,8CAA8C;AAC9C,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,EAAE,CAMhE"}
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Parses structured log lines from the feature agent worker process.
3
+ *
4
+ * Log format: [timestamp] [phase] [agent|model] [tag] message
5
+ * Worker format: [timestamp] [agent|model] [WORKER] message
6
+ * Fallback: raw string lines
7
+ */
8
+ /**
9
+ * Regex to match structured log lines.
10
+ * Captures: timestamp, optional phase, optional agent|model, rest of message
11
+ *
12
+ * Examples:
13
+ * [2026-03-08T10:00:01.123Z] [requirements] [claude-code|claude-haiku-4-5] [tool] Read {"path":"f.ts"}
14
+ * [2026-03-08T10:00:01.123Z] [claude-code|claude-haiku-4-5] [WORKER] Starting worker
15
+ * [2026-03-08T10:00:01.123Z] [requirements] [claude-code|claude-haiku-4-5] Starting...
16
+ */
17
+ const LOG_LINE_REGEX = /^\[(\d{4}-\d{2}-\d{2}T[\d:.]+Z?)\]\s+(?:\[([^\]]+)\]\s+)?(?:\[([^\]|]+)(?:\|([^\]]+))?\]\s+)?(.*)$/;
18
+ const TAG_REGEX = /^\[(tool|text|result|tokens|raw|WORKER)\]\s*(.*)/;
19
+ export function parseLogLine(raw) {
20
+ const match = raw.match(LOG_LINE_REGEX);
21
+ if (!match) {
22
+ return {
23
+ timestamp: null,
24
+ phase: null,
25
+ agent: null,
26
+ model: null,
27
+ tag: 'raw',
28
+ message: raw,
29
+ raw,
30
+ };
31
+ }
32
+ const [, timestamp, group1, group2, group3, rest] = match;
33
+ // Determine phase, agent, model from the captured groups.
34
+ // The log format can be:
35
+ // [ts] [phase] [agent|model] message → group1=phase, group2=agent, group3=model
36
+ // [ts] [agent|model] [WORKER] message → group1="agent|model", group2=WORKER
37
+ // [ts] [phase] [agent] message → group1=phase, group2=agent
38
+ // [ts] [phase] message → group1=phase only
39
+ let phase = null;
40
+ let agent = null;
41
+ let model = null;
42
+ let message = rest ?? '';
43
+ if (group2 != null && group1 != null) {
44
+ // Two bracket groups captured
45
+ if (group1.includes('|')) {
46
+ // group1 is [agent|model], group2 is next tag (e.g. WORKER)
47
+ const [agentPart, modelPart] = group1.split('|');
48
+ agent = agentPart;
49
+ model = modelPart ?? null;
50
+ // group2 becomes part of the message to be parsed as a tag
51
+ message = `[${group2}] ${rest ?? ''}`;
52
+ }
53
+ else {
54
+ // group1 is [phase], group2|group3 is [agent|model]
55
+ phase = group1;
56
+ agent = group2;
57
+ model = group3 ?? null;
58
+ }
59
+ }
60
+ else if (group1 != null) {
61
+ // Only one bracket group
62
+ if (group1.includes('|')) {
63
+ const [agentPart, modelPart] = group1.split('|');
64
+ agent = agentPart;
65
+ model = modelPart ?? null;
66
+ }
67
+ else {
68
+ phase = group1;
69
+ }
70
+ }
71
+ // Parse tag from message
72
+ const tagMatch = message.match(TAG_REGEX);
73
+ let tag = 'info';
74
+ if (tagMatch) {
75
+ tag = tagMatch[1].toLowerCase();
76
+ message = tagMatch[2];
77
+ }
78
+ return {
79
+ timestamp: timestamp ?? null,
80
+ phase,
81
+ agent,
82
+ model,
83
+ tag,
84
+ message: message.trim(),
85
+ raw,
86
+ };
87
+ }
88
+ /** Parse a [tool] message into tool name and args */
89
+ export function parseToolCall(message) {
90
+ const spaceIdx = message.indexOf(' ');
91
+ if (spaceIdx === -1) {
92
+ return { toolName: message, args: '' };
93
+ }
94
+ return {
95
+ toolName: message.slice(0, spaceIdx),
96
+ args: message.slice(spaceIdx + 1),
97
+ };
98
+ }
99
+ /** Parse a [result] message */
100
+ export function parseResultMessage(message) {
101
+ const charsMatch = message.match(/^(\d+)\s+chars/);
102
+ const sessionMatch = message.match(/session=(\S+)/);
103
+ return {
104
+ chars: charsMatch ? parseInt(charsMatch[1], 10) : 0,
105
+ sessionId: sessionMatch ? sessionMatch[1] : null,
106
+ };
107
+ }
108
+ /** Parse a [tokens] message */
109
+ export function parseTokensMessage(message) {
110
+ const match = message.match(/^(\d+)\s+in\s*\/\s*(\d+)\s+out/);
111
+ if (!match)
112
+ return { inputTokens: 0, outputTokens: 0 };
113
+ return {
114
+ inputTokens: parseInt(match[1], 10),
115
+ outputTokens: parseInt(match[2], 10),
116
+ };
117
+ }
118
+ /** Parse all lines from raw content string */
119
+ export function parseLogContent(content) {
120
+ if (!content)
121
+ return [];
122
+ return content
123
+ .split('\n')
124
+ .filter((line) => line.trim().length > 0)
125
+ .map(parseLogLine);
126
+ }