@entelligentsia/forgecli 1.0.20 → 1.0.25
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.
- package/CHANGELOG.md +131 -0
- package/README.md +7 -1
- package/dist/CHANGELOG-forge-plugin.md +118 -0
- package/dist/bin/config.js +4 -4
- package/dist/bin/config.js.map +1 -1
- package/dist/bin/update-cli.d.ts +1 -1
- package/dist/bin/update-cli.js +1 -1
- package/dist/bin/update-cli.js.map +1 -1
- package/dist/extensions/forgecli/ask-user-tool.js +1 -1
- package/dist/extensions/forgecli/ask-user-tool.js.map +1 -1
- package/dist/extensions/forgecli/{add-pipeline.js → commands/add-pipeline.js} +1 -1
- package/dist/extensions/forgecli/commands/add-pipeline.js.map +1 -0
- package/dist/extensions/forgecli/{add-task.js → commands/add-task.js} +1 -1
- package/dist/extensions/forgecli/commands/add-task.js.map +1 -0
- package/dist/extensions/forgecli/{approve.d.ts → commands/approve.d.ts} +2 -2
- package/dist/extensions/forgecli/{approve.js → commands/approve.js} +8 -8
- package/dist/extensions/forgecli/commands/approve.js.map +1 -0
- package/dist/extensions/forgecli/{collate.d.ts → commands/collate.d.ts} +2 -2
- package/dist/extensions/forgecli/{collate.js → commands/collate.js} +6 -6
- package/dist/extensions/forgecli/commands/collate.js.map +1 -0
- package/dist/extensions/forgecli/{commit.d.ts → commands/commit.d.ts} +2 -2
- package/dist/extensions/forgecli/{commit.js → commands/commit.js} +8 -8
- package/dist/extensions/forgecli/commands/commit.js.map +1 -0
- package/dist/extensions/forgecli/{config-command.js → commands/config-command.js} +3 -3
- package/dist/extensions/forgecli/commands/config-command.js.map +1 -0
- package/dist/extensions/forgecli/commands/enhance.d.ts +45 -0
- package/dist/extensions/forgecli/{enhance.js → commands/enhance.js} +95 -75
- package/dist/extensions/forgecli/commands/enhance.js.map +1 -0
- package/dist/extensions/forgecli/{implement.d.ts → commands/implement.d.ts} +2 -2
- package/dist/extensions/forgecli/{implement.js → commands/implement.js} +8 -8
- package/dist/extensions/forgecli/commands/implement.js.map +1 -0
- package/dist/extensions/forgecli/{plan.d.ts → commands/plan.d.ts} +2 -2
- package/dist/extensions/forgecli/{plan.js → commands/plan.js} +8 -8
- package/dist/extensions/forgecli/commands/plan.js.map +1 -0
- package/dist/extensions/forgecli/{quiz-agent.js → commands/quiz-agent.js} +1 -1
- package/dist/extensions/forgecli/commands/quiz-agent.js.map +1 -0
- package/dist/extensions/forgecli/{read-command.js → commands/read-command.js} +2 -2
- package/dist/extensions/forgecli/commands/read-command.js.map +1 -0
- package/dist/extensions/forgecli/{regenerate.js → commands/regenerate.js} +7 -19
- package/dist/extensions/forgecli/commands/regenerate.js.map +1 -0
- package/dist/extensions/forgecli/{remove-command.js → commands/remove-command.js} +1 -1
- package/dist/extensions/forgecli/commands/remove-command.js.map +1 -0
- package/dist/extensions/forgecli/{report-bug.js → commands/report-bug.js} +1 -1
- package/dist/extensions/forgecli/commands/report-bug.js.map +1 -0
- package/dist/extensions/forgecli/{retrospective.d.ts → commands/retrospective.d.ts} +1 -1
- package/dist/extensions/forgecli/{retrospective.js → commands/retrospective.js} +5 -5
- package/dist/extensions/forgecli/commands/retrospective.js.map +1 -0
- package/dist/extensions/forgecli/{review-code.d.ts → commands/review-code.d.ts} +2 -2
- package/dist/extensions/forgecli/{review-code.js → commands/review-code.js} +9 -9
- package/dist/extensions/forgecli/commands/review-code.js.map +1 -0
- package/dist/extensions/forgecli/{review-plan.d.ts → commands/review-plan.d.ts} +2 -2
- package/dist/extensions/forgecli/{review-plan.js → commands/review-plan.js} +9 -9
- package/dist/extensions/forgecli/commands/review-plan.js.map +1 -0
- package/dist/extensions/forgecli/commands/sprint-intake.js.map +1 -0
- package/dist/extensions/forgecli/commands/sprint-plan.js.map +1 -0
- package/dist/extensions/forgecli/{status-command.js → commands/status-command.js} +1 -1
- package/dist/extensions/forgecli/commands/status-command.js.map +1 -0
- package/dist/extensions/forgecli/{store-query.js → commands/store-query.js} +1 -1
- package/dist/extensions/forgecli/commands/store-query.js.map +1 -0
- package/dist/extensions/forgecli/{store-repair.js → commands/store-repair.js} +1 -1
- package/dist/extensions/forgecli/commands/store-repair.js.map +1 -0
- package/dist/extensions/forgecli/{test-orchestrate.js → commands/test-orchestrate.js} +1 -1
- package/dist/extensions/forgecli/commands/test-orchestrate.js.map +1 -0
- package/dist/extensions/forgecli/commands/transcripts-command.d.ts +87 -0
- package/dist/extensions/forgecli/commands/transcripts-command.js +418 -0
- package/dist/extensions/forgecli/commands/transcripts-command.js.map +1 -0
- package/dist/extensions/forgecli/{validate.d.ts → commands/validate.d.ts} +2 -2
- package/dist/extensions/forgecli/{validate.js → commands/validate.js} +8 -8
- package/dist/extensions/forgecli/commands/validate.js.map +1 -0
- package/dist/extensions/forgecli/{config-layer.js → config/config-layer.js} +2 -2
- package/dist/extensions/forgecli/config/config-layer.js.map +1 -0
- package/dist/extensions/forgecli/{config-writer.js → config/config-writer.js} +2 -2
- package/dist/extensions/forgecli/config/config-writer.js.map +1 -0
- package/dist/extensions/forgecli/{model-registry.js → config/model-registry.js} +1 -1
- package/dist/extensions/forgecli/config/model-registry.js.map +1 -0
- package/dist/extensions/forgecli/config/model-resolver.js.map +1 -0
- package/dist/extensions/forgecli/config/model-validator.js.map +1 -0
- package/dist/extensions/forgecli/config-tui/component.js +1 -1
- package/dist/extensions/forgecli/config-tui/component.js.map +1 -1
- package/dist/extensions/forgecli/config-tui/handler.js +1 -1
- package/dist/extensions/forgecli/config-tui/handler.js.map +1 -1
- package/dist/extensions/forgecli/config-tui/screens/override-editor.js +1 -1
- package/dist/extensions/forgecli/config-tui/screens/override-editor.js.map +1 -1
- package/dist/extensions/forgecli/config-tui/screens/overrides-list-phases.js +1 -1
- package/dist/extensions/forgecli/config-tui/screens/overrides-list-phases.js.map +1 -1
- package/dist/extensions/forgecli/config-tui/screens/show-resolved.js +1 -1
- package/dist/extensions/forgecli/config-tui/screens/show-resolved.js.map +1 -1
- package/dist/extensions/forgecli/config-tui/state/buffer.d.ts +2 -2
- package/dist/extensions/forgecli/config-tui/state/model.d.ts +1 -1
- package/dist/extensions/forgecli/config-tui/state/reducer.js.map +1 -1
- package/dist/extensions/forgecli/config-tui/state/selectors.d.ts +2 -2
- package/dist/extensions/forgecli/config-tui/state/selectors.js +1 -1
- package/dist/extensions/forgecli/config-tui/state/selectors.js.map +1 -1
- package/dist/extensions/forgecli/context-governor-compaction.d.ts +13 -2
- package/dist/extensions/forgecli/context-governor-compaction.js +66 -41
- package/dist/extensions/forgecli/context-governor-compaction.js.map +1 -1
- package/dist/extensions/forgecli/context-governor.d.ts +3 -7
- package/dist/extensions/forgecli/context-governor.js +43 -69
- package/dist/extensions/forgecli/context-governor.js.map +1 -1
- package/dist/extensions/forgecli/dashboard/component.d.ts +10 -1
- package/dist/extensions/forgecli/dashboard/component.js +84 -18
- package/dist/extensions/forgecli/dashboard/component.js.map +1 -1
- package/dist/extensions/forgecli/dashboard/register.js +1 -1
- package/dist/extensions/forgecli/dashboard/register.js.map +1 -1
- package/dist/extensions/forgecli/forge-artifact-tool.js +1 -1
- package/dist/extensions/forgecli/forge-commands.js +1 -1
- package/dist/extensions/forgecli/forge-commands.js.map +1 -1
- package/dist/extensions/forgecli/{forge-init.js → forge-init/forge-init.js} +10 -10
- package/dist/extensions/forgecli/forge-init/forge-init.js.map +1 -0
- package/dist/extensions/forgecli/forge-init/init-context.js.map +1 -0
- package/dist/extensions/forgecli/forge-init/init-progress.js.map +1 -0
- package/dist/extensions/forgecli/forge-init/phase4-register.js +1 -1
- package/dist/extensions/forgecli/forge-init/phase4-register.js.map +1 -1
- package/dist/extensions/forgecli/forge-init/run-phases.js +2 -2
- package/dist/extensions/forgecli/forge-init/run-phases.js.map +1 -1
- package/dist/extensions/forgecli/forge-subagent.d.ts +22 -0
- package/dist/extensions/forgecli/forge-subagent.js +42 -15
- package/dist/extensions/forgecli/forge-subagent.js.map +1 -1
- package/dist/extensions/forgecli/forge-tools.d.ts +1 -25
- package/dist/extensions/forgecli/forge-tools.js +79 -37
- package/dist/extensions/forgecli/forge-tools.js.map +1 -1
- package/dist/extensions/forgecli/governor-config.d.ts +19 -0
- package/dist/extensions/forgecli/governor-config.js +58 -0
- package/dist/extensions/forgecli/governor-config.js.map +1 -0
- package/dist/extensions/forgecli/health-check.js +1 -1
- package/dist/extensions/forgecli/health-check.js.map +1 -1
- package/dist/extensions/forgecli/hook-dispatcher.js +2 -2
- package/dist/extensions/forgecli/hook-dispatcher.js.map +1 -1
- package/dist/extensions/forgecli/hooks/post-init-hook.js +11 -6
- package/dist/extensions/forgecli/hooks/post-init-hook.js.map +1 -1
- package/dist/extensions/forgecli/hooks/post-sprint-hook.js +11 -6
- package/dist/extensions/forgecli/hooks/post-sprint-hook.js.map +1 -1
- package/dist/extensions/forgecli/hooks/write-guard.js +1 -1
- package/dist/extensions/forgecli/hooks/write-guard.js.map +1 -1
- package/dist/extensions/forgecli/index.js +37 -35
- package/dist/extensions/forgecli/index.js.map +1 -1
- package/dist/extensions/forgecli/kickoff.d.ts +9 -0
- package/dist/extensions/forgecli/kickoff.js +15 -0
- package/dist/extensions/forgecli/kickoff.js.map +1 -1
- package/dist/extensions/forgecli/lib/forge-config.d.ts +1 -1
- package/dist/extensions/forgecli/lib/forge-config.js +1 -1
- package/dist/extensions/forgecli/lib/forge-config.js.map +1 -1
- package/dist/extensions/forgecli/{forge-root.d.ts → lib/forge-root.d.ts} +5 -0
- package/dist/extensions/forgecli/{forge-root.js → lib/forge-root.js} +14 -1
- package/dist/extensions/forgecli/lib/forge-root.js.map +1 -0
- package/dist/extensions/forgecli/lib/run-cjs.d.ts +26 -0
- package/dist/extensions/forgecli/lib/run-cjs.js +42 -0
- package/dist/extensions/forgecli/lib/run-cjs.js.map +1 -0
- package/dist/extensions/forgecli/orchestrator-tree.d.ts +4 -0
- package/dist/extensions/forgecli/orchestrator-tree.js +21 -3
- package/dist/extensions/forgecli/orchestrator-tree.js.map +1 -1
- package/dist/extensions/forgecli/{calibrate.d.ts → orchestrators/calibrate.d.ts} +2 -2
- package/dist/extensions/forgecli/{calibrate.js → orchestrators/calibrate.js} +4 -4
- package/dist/extensions/forgecli/orchestrators/calibrate.js.map +1 -0
- package/dist/extensions/forgecli/{fix-bug.d.ts → orchestrators/fix-bug.d.ts} +14 -5
- package/dist/extensions/forgecli/{fix-bug.js → orchestrators/fix-bug.js} +212 -61
- package/dist/extensions/forgecli/orchestrators/fix-bug.js.map +1 -0
- package/dist/extensions/forgecli/{lib → orchestrators}/halt-advisor.d.ts +1 -1
- package/dist/extensions/forgecli/{lib → orchestrators}/halt-advisor.js +1 -1
- package/dist/extensions/forgecli/orchestrators/halt-advisor.js.map +1 -0
- package/dist/extensions/forgecli/{materialize.js → orchestrators/materialize.js} +1 -1
- package/dist/extensions/forgecli/orchestrators/materialize.js.map +1 -0
- package/dist/extensions/forgecli/{migrate.d.ts → orchestrators/migrate.d.ts} +1 -1
- package/dist/extensions/forgecli/{migrate.js → orchestrators/migrate.js} +5 -5
- package/dist/extensions/forgecli/orchestrators/migrate.js.map +1 -0
- package/dist/extensions/forgecli/{lib → orchestrators}/orchestrator-preflight.d.ts +2 -2
- package/dist/extensions/forgecli/{lib → orchestrators}/orchestrator-preflight.js +2 -2
- package/dist/extensions/forgecli/orchestrators/orchestrator-preflight.js.map +1 -0
- package/dist/extensions/forgecli/{run-sprint.d.ts → orchestrators/run-sprint.d.ts} +1 -1
- package/dist/extensions/forgecli/{run-sprint.js → orchestrators/run-sprint.js} +85 -18
- package/dist/extensions/forgecli/orchestrators/run-sprint.js.map +1 -0
- package/dist/extensions/forgecli/{run-task.d.ts → orchestrators/run-task.d.ts} +14 -3
- package/dist/extensions/forgecli/{run-task.js → orchestrators/run-task.js} +121 -33
- package/dist/extensions/forgecli/orchestrators/run-task.js.map +1 -0
- package/dist/extensions/forgecli/paths/paths.d.ts +8 -0
- package/dist/extensions/forgecli/paths/paths.js +17 -0
- package/dist/extensions/forgecli/paths/paths.js.map +1 -1
- package/dist/extensions/forgecli/phase-vocab.d.ts +31 -0
- package/dist/extensions/forgecli/phase-vocab.js +82 -0
- package/dist/extensions/forgecli/phase-vocab.js.map +1 -0
- package/dist/extensions/forgecli/session-registry.d.ts +2 -2
- package/dist/extensions/forgecli/session-registry.js +6 -2
- package/dist/extensions/forgecli/session-registry.js.map +1 -1
- package/dist/extensions/forgecli/{friction-emit.d.ts → skill-curation/friction-emit.d.ts} +1 -1
- package/dist/extensions/forgecli/{friction-emit.js → skill-curation/friction-emit.js} +2 -2
- package/dist/extensions/forgecli/skill-curation/friction-emit.js.map +1 -0
- package/dist/extensions/forgecli/{skill-curation-flag.js → skill-curation/skill-curation-flag.js} +1 -1
- package/dist/extensions/forgecli/skill-curation/skill-curation-flag.js.map +1 -0
- package/dist/extensions/forgecli/{skill-curator-subagent.d.ts → skill-curation/skill-curator-subagent.d.ts} +2 -2
- package/dist/extensions/forgecli/{skill-curator-subagent.js → skill-curation/skill-curator-subagent.js} +1 -1
- package/dist/extensions/forgecli/skill-curation/skill-curator-subagent.js.map +1 -0
- package/dist/extensions/forgecli/{skill-retriever.js → skill-curation/skill-retriever.js} +1 -1
- package/dist/extensions/forgecli/skill-curation/skill-retriever.js.map +1 -0
- package/dist/extensions/forgecli/{skill-usage-tracker.js → skill-curation/skill-usage-tracker.js} +1 -1
- package/dist/extensions/forgecli/skill-curation/skill-usage-tracker.js.map +1 -0
- package/dist/extensions/forgecli/store/store-error-remediation.js.map +1 -0
- package/dist/extensions/forgecli/{store-resolver.js → store/store-resolver.js} +2 -2
- package/dist/extensions/forgecli/store/store-resolver.js.map +1 -0
- package/dist/extensions/forgecli/{store-validator.js → store/store-validator.js} +1 -1
- package/dist/extensions/forgecli/store/store-validator.js.map +1 -0
- package/dist/extensions/forgecli/{transition-guard.js → store/transition-guard.js} +2 -2
- package/dist/extensions/forgecli/store/transition-guard.js.map +1 -0
- package/dist/extensions/forgecli/subagent/orchestrator-transcript.js +5 -0
- package/dist/extensions/forgecli/subagent/orchestrator-transcript.js.map +1 -1
- package/dist/extensions/forgecli/transcript-archive-types.d.ts +171 -0
- package/dist/extensions/forgecli/transcript-archive-types.js +130 -0
- package/dist/extensions/forgecli/transcript-archive-types.js.map +1 -0
- package/dist/extensions/forgecli/transcript-archive.d.ts +127 -0
- package/dist/extensions/forgecli/transcript-archive.js +656 -0
- package/dist/extensions/forgecli/transcript-archive.js.map +1 -0
- package/dist/extensions/forgecli/transcript-replay.d.ts +28 -0
- package/dist/extensions/forgecli/transcript-replay.js +153 -0
- package/dist/extensions/forgecli/transcript-replay.js.map +1 -0
- package/dist/extensions/forgecli/transcripts-tui/component.d.ts +36 -0
- package/dist/extensions/forgecli/transcripts-tui/component.js +112 -0
- package/dist/extensions/forgecli/transcripts-tui/component.js.map +1 -0
- package/dist/extensions/forgecli/transcripts-tui/index.d.ts +4 -0
- package/dist/extensions/forgecli/transcripts-tui/index.js +5 -0
- package/dist/extensions/forgecli/transcripts-tui/index.js.map +1 -0
- package/dist/extensions/forgecli/transcripts-tui/screens/browse.d.ts +21 -0
- package/dist/extensions/forgecli/transcripts-tui/screens/browse.js +172 -0
- package/dist/extensions/forgecli/transcripts-tui/screens/browse.js.map +1 -0
- package/dist/extensions/forgecli/transcripts-tui/screens/types.d.ts +22 -0
- package/dist/extensions/forgecli/transcripts-tui/screens/types.js +4 -0
- package/dist/extensions/forgecli/transcripts-tui/screens/types.js.map +1 -0
- package/dist/extensions/forgecli/transcripts-tui/state/index.d.ts +4 -0
- package/dist/extensions/forgecli/transcripts-tui/state/index.js +5 -0
- package/dist/extensions/forgecli/transcripts-tui/state/index.js.map +1 -0
- package/dist/extensions/forgecli/transcripts-tui/state/init.d.ts +8 -0
- package/dist/extensions/forgecli/transcripts-tui/state/init.js +18 -0
- package/dist/extensions/forgecli/transcripts-tui/state/init.js.map +1 -0
- package/dist/extensions/forgecli/transcripts-tui/state/model.d.ts +56 -0
- package/dist/extensions/forgecli/transcripts-tui/state/model.js +6 -0
- package/dist/extensions/forgecli/transcripts-tui/state/model.js.map +1 -0
- package/dist/extensions/forgecli/transcripts-tui/state/reducer.d.ts +2 -0
- package/dist/extensions/forgecli/transcripts-tui/state/reducer.js +51 -0
- package/dist/extensions/forgecli/transcripts-tui/state/reducer.js.map +1 -0
- package/dist/extensions/forgecli/transcripts-tui/state/selectors.d.ts +10 -0
- package/dist/extensions/forgecli/transcripts-tui/state/selectors.js +62 -0
- package/dist/extensions/forgecli/transcripts-tui/state/selectors.js.map +1 -0
- package/dist/extensions/forgecli/transcripts-tui/theme.d.ts +20 -0
- package/dist/extensions/forgecli/transcripts-tui/theme.js +47 -0
- package/dist/extensions/forgecli/transcripts-tui/theme.js.map +1 -0
- package/dist/extensions/forgecli/tui/banner.js.map +1 -0
- package/dist/extensions/forgecli/tui/forge-header.js.map +1 -0
- package/dist/extensions/forgecli/tui/input-router.js.map +1 -0
- package/dist/extensions/forgecli/{orchestrator-status-bar.d.ts → tui/orchestrator-status-bar.d.ts} +1 -1
- package/dist/extensions/forgecli/{orchestrator-status-bar.js → tui/orchestrator-status-bar.js} +1 -1
- package/dist/extensions/forgecli/tui/orchestrator-status-bar.js.map +1 -0
- package/dist/extensions/forgecli/tui/thread-switcher.d.ts +18 -0
- package/dist/extensions/forgecli/{thread-switcher.js → tui/thread-switcher.js} +17 -12
- package/dist/extensions/forgecli/tui/thread-switcher.js.map +1 -0
- package/dist/extensions/forgecli/{forge-update-command.js → update/forge-update-command.js} +3 -3
- package/dist/extensions/forgecli/update/forge-update-command.js.map +1 -0
- package/dist/extensions/forgecli/{migration-engine.js → update/migration-engine.js} +1 -1
- package/dist/extensions/forgecli/update/migration-engine.js.map +1 -0
- package/dist/extensions/forgecli/{update-check.js → update/update-check.js} +1 -1
- package/dist/extensions/forgecli/update/update-check.js.map +1 -0
- package/dist/extensions/forgecli/{update-tools.js → update/update-tools.js} +1 -1
- package/dist/extensions/forgecli/update/update-tools.js.map +1 -0
- package/dist/extensions/forgecli/{whats-new-widget.js → update/whats-new-widget.js} +2 -2
- package/dist/extensions/forgecli/update/whats-new-widget.js.map +1 -0
- package/dist/extensions/forgecli/{whats-new.js → update/whats-new.js} +1 -1
- package/dist/extensions/forgecli/update/whats-new.js.map +1 -0
- package/dist/extensions/forgecli/{viewport-events.d.ts → viewport/events.d.ts} +37 -2
- package/dist/extensions/forgecli/{viewport-events.js → viewport/events.js} +70 -23
- package/dist/extensions/forgecli/viewport/events.js.map +1 -0
- package/dist/extensions/forgecli/{viewport-renderer.d.ts → viewport/renderer.d.ts} +19 -0
- package/dist/extensions/forgecli/{viewport-renderer.js → viewport/renderer.js} +46 -2
- package/dist/extensions/forgecli/viewport/renderer.js.map +1 -0
- package/dist/extensions/forgecli/{viewport-theme.js → viewport/theme.js} +13 -10
- package/dist/extensions/forgecli/viewport/theme.js.map +1 -0
- package/dist/extensions/forgecli/wf-engine/engine.js +1 -1
- package/dist/extensions/forgecli/wf-engine/engine.js.map +1 -1
- package/dist/forge-payload/.base-pack/workflows/_fragments/event-emission-schema.md +4 -0
- package/dist/forge-payload/.base-pack/workflows/_fragments/event-vocabulary.md +88 -0
- package/dist/forge-payload/.base-pack/workflows/commit_task.md +41 -38
- package/dist/forge-payload/.base-pack/workflows/implement_plan.md +3 -3
- package/dist/forge-payload/.base-pack/workflows-js/wfl-fix-bug.js +42 -6
- package/dist/forge-payload/.base-pack/workflows-js/wfl-run-task.js +32 -1
- package/dist/forge-payload/.claude-plugin/plugin.json +1 -1
- package/dist/forge-payload/.schemas/enum-catalog.json +2 -2
- package/dist/forge-payload/.schemas/event.schema.json +8 -3
- package/dist/forge-payload/.schemas/migrations.json +56 -0
- package/dist/forge-payload/integrity.json +3 -3
- package/dist/forge-payload/meta/store-schema/event.schema.md +7 -0
- package/dist/forge-payload/meta/workflows/_fragments/event-emission-schema.md +4 -0
- package/dist/forge-payload/meta/workflows/_fragments/event-vocabulary.md +88 -0
- package/dist/forge-payload/meta/workflows/meta-commit.md +46 -43
- package/dist/forge-payload/meta/workflows/meta-fix-bug.md +7 -2
- package/dist/forge-payload/meta/workflows/meta-implement.md +3 -3
- package/dist/forge-payload/meta/workflows/meta-orchestrate.md +4 -1
- package/dist/forge-payload/schemas/enum-catalog.json +2 -2
- package/dist/forge-payload/schemas/event.schema.json +8 -3
- package/dist/forge-payload/schemas/structure-manifest.json +4 -2
- package/dist/forge-payload/tools/commit-task.cjs +218 -0
- package/dist/forge-payload/tools/store-cli.cjs +6 -1
- package/node_modules/@mariozechner/clipboard/package.json +2 -1
- package/node_modules/@mariozechner/clipboard-linux-x64-musl/README.md +3 -0
- package/node_modules/@mariozechner/clipboard-linux-x64-musl/package.json +25 -0
- package/package.json +5 -3
- package/dist/extensions/forgecli/add-pipeline.js.map +0 -1
- package/dist/extensions/forgecli/add-task.js.map +0 -1
- package/dist/extensions/forgecli/approve.js.map +0 -1
- package/dist/extensions/forgecli/banner.js.map +0 -1
- package/dist/extensions/forgecli/calibrate.js.map +0 -1
- package/dist/extensions/forgecli/collate.js.map +0 -1
- package/dist/extensions/forgecli/commit.js.map +0 -1
- package/dist/extensions/forgecli/config-command.js.map +0 -1
- package/dist/extensions/forgecli/config-layer.js.map +0 -1
- package/dist/extensions/forgecli/config-writer.js.map +0 -1
- package/dist/extensions/forgecli/enhance.d.ts +0 -27
- package/dist/extensions/forgecli/enhance.js.map +0 -1
- package/dist/extensions/forgecli/fix-bug.js.map +0 -1
- package/dist/extensions/forgecli/forge-header.js.map +0 -1
- package/dist/extensions/forgecli/forge-init.js.map +0 -1
- package/dist/extensions/forgecli/forge-root.js.map +0 -1
- package/dist/extensions/forgecli/forge-update-command.js.map +0 -1
- package/dist/extensions/forgecli/friction-emit.js.map +0 -1
- package/dist/extensions/forgecli/implement.js.map +0 -1
- package/dist/extensions/forgecli/init-context.js.map +0 -1
- package/dist/extensions/forgecli/init-progress.js.map +0 -1
- package/dist/extensions/forgecli/input-router.js.map +0 -1
- package/dist/extensions/forgecli/lib/halt-advisor.js.map +0 -1
- package/dist/extensions/forgecli/lib/orchestrator-preflight.js.map +0 -1
- package/dist/extensions/forgecli/materialize.js.map +0 -1
- package/dist/extensions/forgecli/migrate.js.map +0 -1
- package/dist/extensions/forgecli/migration-engine.js.map +0 -1
- package/dist/extensions/forgecli/model-registry.js.map +0 -1
- package/dist/extensions/forgecli/model-resolver.js.map +0 -1
- package/dist/extensions/forgecli/model-validator.js.map +0 -1
- package/dist/extensions/forgecli/orchestrator-status-bar.js.map +0 -1
- package/dist/extensions/forgecli/plan.js.map +0 -1
- package/dist/extensions/forgecli/quiz-agent.js.map +0 -1
- package/dist/extensions/forgecli/read-command.js.map +0 -1
- package/dist/extensions/forgecli/regenerate.js.map +0 -1
- package/dist/extensions/forgecli/remove-command.js.map +0 -1
- package/dist/extensions/forgecli/report-bug.js.map +0 -1
- package/dist/extensions/forgecli/retrospective.js.map +0 -1
- package/dist/extensions/forgecli/review-code.js.map +0 -1
- package/dist/extensions/forgecli/review-plan.js.map +0 -1
- package/dist/extensions/forgecli/run-sprint.js.map +0 -1
- package/dist/extensions/forgecli/run-task.js.map +0 -1
- package/dist/extensions/forgecli/skill-curation-flag.js.map +0 -1
- package/dist/extensions/forgecli/skill-curator-subagent.js.map +0 -1
- package/dist/extensions/forgecli/skill-retriever.js.map +0 -1
- package/dist/extensions/forgecli/skill-usage-tracker.js.map +0 -1
- package/dist/extensions/forgecli/sprint-intake.js.map +0 -1
- package/dist/extensions/forgecli/sprint-plan.js.map +0 -1
- package/dist/extensions/forgecli/status-command.js.map +0 -1
- package/dist/extensions/forgecli/store-error-remediation.js.map +0 -1
- package/dist/extensions/forgecli/store-query.js.map +0 -1
- package/dist/extensions/forgecli/store-repair.js.map +0 -1
- package/dist/extensions/forgecli/store-resolver.js.map +0 -1
- package/dist/extensions/forgecli/store-validator.js.map +0 -1
- package/dist/extensions/forgecli/test-orchestrate.js.map +0 -1
- package/dist/extensions/forgecli/thread-switcher.d.ts +0 -5
- package/dist/extensions/forgecli/thread-switcher.js.map +0 -1
- package/dist/extensions/forgecli/transition-guard.js.map +0 -1
- package/dist/extensions/forgecli/update-check.js.map +0 -1
- package/dist/extensions/forgecli/update-tools.js.map +0 -1
- package/dist/extensions/forgecli/validate.js.map +0 -1
- package/dist/extensions/forgecli/viewport-events.js.map +0 -1
- package/dist/extensions/forgecli/viewport-renderer.js.map +0 -1
- package/dist/extensions/forgecli/viewport-theme.js.map +0 -1
- package/dist/extensions/forgecli/whats-new-widget.js.map +0 -1
- package/dist/extensions/forgecli/whats-new.js.map +0 -1
- /package/dist/extensions/forgecli/{add-pipeline.d.ts → commands/add-pipeline.d.ts} +0 -0
- /package/dist/extensions/forgecli/{add-task.d.ts → commands/add-task.d.ts} +0 -0
- /package/dist/extensions/forgecli/{config-command.d.ts → commands/config-command.d.ts} +0 -0
- /package/dist/extensions/forgecli/{quiz-agent.d.ts → commands/quiz-agent.d.ts} +0 -0
- /package/dist/extensions/forgecli/{read-command.d.ts → commands/read-command.d.ts} +0 -0
- /package/dist/extensions/forgecli/{regenerate.d.ts → commands/regenerate.d.ts} +0 -0
- /package/dist/extensions/forgecli/{remove-command.d.ts → commands/remove-command.d.ts} +0 -0
- /package/dist/extensions/forgecli/{report-bug.d.ts → commands/report-bug.d.ts} +0 -0
- /package/dist/extensions/forgecli/{sprint-intake.d.ts → commands/sprint-intake.d.ts} +0 -0
- /package/dist/extensions/forgecli/{sprint-intake.js → commands/sprint-intake.js} +0 -0
- /package/dist/extensions/forgecli/{sprint-plan.d.ts → commands/sprint-plan.d.ts} +0 -0
- /package/dist/extensions/forgecli/{sprint-plan.js → commands/sprint-plan.js} +0 -0
- /package/dist/extensions/forgecli/{status-command.d.ts → commands/status-command.d.ts} +0 -0
- /package/dist/extensions/forgecli/{store-query.d.ts → commands/store-query.d.ts} +0 -0
- /package/dist/extensions/forgecli/{store-repair.d.ts → commands/store-repair.d.ts} +0 -0
- /package/dist/extensions/forgecli/{test-orchestrate.d.ts → commands/test-orchestrate.d.ts} +0 -0
- /package/dist/extensions/forgecli/{config-layer.d.ts → config/config-layer.d.ts} +0 -0
- /package/dist/extensions/forgecli/{config-writer.d.ts → config/config-writer.d.ts} +0 -0
- /package/dist/extensions/forgecli/{model-registry.d.ts → config/model-registry.d.ts} +0 -0
- /package/dist/extensions/forgecli/{model-resolver.d.ts → config/model-resolver.d.ts} +0 -0
- /package/dist/extensions/forgecli/{model-resolver.js → config/model-resolver.js} +0 -0
- /package/dist/extensions/forgecli/{model-validator.d.ts → config/model-validator.d.ts} +0 -0
- /package/dist/extensions/forgecli/{model-validator.js → config/model-validator.js} +0 -0
- /package/dist/extensions/forgecli/{forge-init.d.ts → forge-init/forge-init.d.ts} +0 -0
- /package/dist/extensions/forgecli/{init-context.d.ts → forge-init/init-context.d.ts} +0 -0
- /package/dist/extensions/forgecli/{init-context.js → forge-init/init-context.js} +0 -0
- /package/dist/extensions/forgecli/{init-progress.d.ts → forge-init/init-progress.d.ts} +0 -0
- /package/dist/extensions/forgecli/{init-progress.js → forge-init/init-progress.js} +0 -0
- /package/dist/extensions/forgecli/{materialize.d.ts → orchestrators/materialize.d.ts} +0 -0
- /package/dist/extensions/forgecli/{skill-curation-flag.d.ts → skill-curation/skill-curation-flag.d.ts} +0 -0
- /package/dist/extensions/forgecli/{skill-retriever.d.ts → skill-curation/skill-retriever.d.ts} +0 -0
- /package/dist/extensions/forgecli/{skill-usage-tracker.d.ts → skill-curation/skill-usage-tracker.d.ts} +0 -0
- /package/dist/extensions/forgecli/{store-error-remediation.d.ts → store/store-error-remediation.d.ts} +0 -0
- /package/dist/extensions/forgecli/{store-error-remediation.js → store/store-error-remediation.js} +0 -0
- /package/dist/extensions/forgecli/{store-resolver.d.ts → store/store-resolver.d.ts} +0 -0
- /package/dist/extensions/forgecli/{store-validator.d.ts → store/store-validator.d.ts} +0 -0
- /package/dist/extensions/forgecli/{transition-guard.d.ts → store/transition-guard.d.ts} +0 -0
- /package/dist/extensions/forgecli/{banner.d.ts → tui/banner.d.ts} +0 -0
- /package/dist/extensions/forgecli/{banner.js → tui/banner.js} +0 -0
- /package/dist/extensions/forgecli/{forge-header.d.ts → tui/forge-header.d.ts} +0 -0
- /package/dist/extensions/forgecli/{forge-header.js → tui/forge-header.js} +0 -0
- /package/dist/extensions/forgecli/{input-router.d.ts → tui/input-router.d.ts} +0 -0
- /package/dist/extensions/forgecli/{input-router.js → tui/input-router.js} +0 -0
- /package/dist/extensions/forgecli/{forge-update-command.d.ts → update/forge-update-command.d.ts} +0 -0
- /package/dist/extensions/forgecli/{migration-engine.d.ts → update/migration-engine.d.ts} +0 -0
- /package/dist/extensions/forgecli/{update-check.d.ts → update/update-check.d.ts} +0 -0
- /package/dist/extensions/forgecli/{update-tools.d.ts → update/update-tools.d.ts} +0 -0
- /package/dist/extensions/forgecli/{whats-new-widget.d.ts → update/whats-new-widget.d.ts} +0 -0
- /package/dist/extensions/forgecli/{whats-new.d.ts → update/whats-new.d.ts} +0 -0
- /package/dist/extensions/forgecli/{viewport-theme.d.ts → viewport/theme.d.ts} +0 -0
|
@@ -0,0 +1,656 @@
|
|
|
1
|
+
// transcript-archive.ts — central, full-retention transcript archive core.
|
|
2
|
+
//
|
|
3
|
+
// Mirrors project-local `.forge/transcripts/<entityId>/` runs into the
|
|
4
|
+
// permanent cross-project archive under `~/.pi/forge-cli/transcripts/`
|
|
5
|
+
// (FORGE_CLI_HOME-aware via paths/paths.ts). Copy-up only: this module has
|
|
6
|
+
// ZERO delete paths — project-local transcripts stay untouched (live
|
|
7
|
+
// debugging and verify-governance.sh depend on them; the archive is the
|
|
8
|
+
// permanent mirror, `.forge/` is the ephemeral working copy).
|
|
9
|
+
//
|
|
10
|
+
// Compression is node:zlib gzip — byte archival of phase payloads (~10:1).
|
|
11
|
+
// NOT @entelligentsia/forge-compress, which is token compression for LLM
|
|
12
|
+
// contexts; nothing here ever re-enters a model context uncompressed.
|
|
13
|
+
//
|
|
14
|
+
// Every write path is best-effort and never throws: archiving is
|
|
15
|
+
// observability data, not load-bearing pipeline state. Every read path
|
|
16
|
+
// Value.Checks untrusted disk JSON and returns null/defaults on mismatch.
|
|
17
|
+
import * as crypto from "node:crypto";
|
|
18
|
+
import * as fs from "node:fs";
|
|
19
|
+
import * as path from "node:path";
|
|
20
|
+
import { gunzipSync, gzipSync } from "node:zlib";
|
|
21
|
+
import { Value } from "typebox/value";
|
|
22
|
+
import { getRunArchiveDir, getTranscriptArchiveRoot, getTranscriptIndexPath, getTranscriptProjectsPath, } from "./paths/paths.js";
|
|
23
|
+
import { IndexEntrySchema, PhaseUsageSchema, ProjectsRegistrySchema, RunManifestSchema, } from "./transcript-archive-types.js";
|
|
24
|
+
/**
|
|
25
|
+
* Read `{prefix, name, projectDir}` from a `.forge/config.json`. Defaults on
|
|
26
|
+
* any failure (missing file, malformed JSON, absent fields) — never throws.
|
|
27
|
+
* `projectDir` is derived from the config path itself (`<dir>/.forge/config.json`).
|
|
28
|
+
*/
|
|
29
|
+
export function readProjectIdentity(configPath) {
|
|
30
|
+
const projectDir = path.dirname(path.dirname(configPath));
|
|
31
|
+
const fallbackName = path.basename(projectDir) || "project";
|
|
32
|
+
try {
|
|
33
|
+
const raw = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
34
|
+
const prefix = typeof raw.project?.prefix === "string" && raw.project.prefix.length > 0
|
|
35
|
+
? raw.project.prefix
|
|
36
|
+
: "project";
|
|
37
|
+
const name = typeof raw.project?.name === "string" && raw.project.name.length > 0
|
|
38
|
+
? raw.project.name
|
|
39
|
+
: fallbackName;
|
|
40
|
+
return { prefix, name, projectDir };
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return { prefix: "project", name: fallbackName, projectDir };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* `<prefix>-<sha256(realpath(projectDir)).slice(0,8)>` — prefix alone can
|
|
48
|
+
* collide across projects (two repos both named "FORGE"); the path hash
|
|
49
|
+
* disambiguates. Falls back to the raw path when realpath throws (deleted
|
|
50
|
+
* or unmounted project dir — archive entries must remain addressable).
|
|
51
|
+
*/
|
|
52
|
+
export function computeProjectKey(projectDir, prefix) {
|
|
53
|
+
let resolved;
|
|
54
|
+
try {
|
|
55
|
+
resolved = fs.realpathSync(projectDir);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
resolved = projectDir;
|
|
59
|
+
}
|
|
60
|
+
const hash = crypto.createHash("sha256").update(resolved).digest("hex").slice(0, 8);
|
|
61
|
+
const slug = prefix
|
|
62
|
+
.toLowerCase()
|
|
63
|
+
.replace(/[^a-z0-9._-]/g, "_")
|
|
64
|
+
.slice(0, 40) || "project";
|
|
65
|
+
return `${slug}-${hash}`;
|
|
66
|
+
}
|
|
67
|
+
// ── Orchestrator JSONL + phase-transcript parsing ───────────────────────
|
|
68
|
+
/** "2026-05-28T13:35:00.123Z" → "20260528T133500Z" (compact UTC). */
|
|
69
|
+
function compactIso(iso) {
|
|
70
|
+
return iso.replace(/[-:]/g, "").replace(/\.\d+/, "");
|
|
71
|
+
}
|
|
72
|
+
/** "20260528T133500Z" → "2026-05-28T13:35:00Z" (inverse of compactIso). */
|
|
73
|
+
function expandCompactIso(compact) {
|
|
74
|
+
const m = /^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})Z$/.exec(compact);
|
|
75
|
+
if (!m)
|
|
76
|
+
return compact;
|
|
77
|
+
return `${m[1]}-${m[2]}-${m[3]}T${m[4]}:${m[5]}:${m[6]}Z`;
|
|
78
|
+
}
|
|
79
|
+
/** Tolerant JSONL parse — skips malformed/truncated lines, never throws. */
|
|
80
|
+
function readJsonlEvents(filePath) {
|
|
81
|
+
let raw;
|
|
82
|
+
try {
|
|
83
|
+
raw = fs.readFileSync(filePath, "utf8");
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return [];
|
|
87
|
+
}
|
|
88
|
+
const events = [];
|
|
89
|
+
for (const line of raw.split("\n")) {
|
|
90
|
+
const trimmed = line.trim();
|
|
91
|
+
if (!trimmed)
|
|
92
|
+
continue;
|
|
93
|
+
try {
|
|
94
|
+
const parsed = JSON.parse(trimmed);
|
|
95
|
+
if (parsed && typeof parsed === "object")
|
|
96
|
+
events.push(parsed);
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
// Crash-truncated tail line — skip.
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return events;
|
|
103
|
+
}
|
|
104
|
+
/** Filename shape written by forge-subagent.ts / orchestrator-transcript.ts. */
|
|
105
|
+
const PHASE_FILE_RE = /^(\d{8}T\d{6}Z)__(.+)__(.+)\.json$/;
|
|
106
|
+
const ORCH_FILE_RE = /^(\d{8}T\d{6}Z)__(.+)__orchestrator\.jsonl$/;
|
|
107
|
+
/** Live tail-view log written by persistTailLog (viewport/events.ts). */
|
|
108
|
+
const TAIL_FILE_RE = /^(\d{8}T\d{6}Z)__(.+)__(.+)\.tail\.jsonl$/;
|
|
109
|
+
/** Extract header fields from a phase-transcript JSON; null on parse failure. */
|
|
110
|
+
function readPhaseHeader(absPath) {
|
|
111
|
+
try {
|
|
112
|
+
const raw = JSON.parse(fs.readFileSync(absPath, "utf8"));
|
|
113
|
+
const header = {};
|
|
114
|
+
if (typeof raw.model === "string" && raw.model.length > 0)
|
|
115
|
+
header.model = raw.model;
|
|
116
|
+
if (typeof raw.provider === "string" && raw.provider.length > 0)
|
|
117
|
+
header.provider = raw.provider;
|
|
118
|
+
if (typeof raw.startedAt === "string")
|
|
119
|
+
header.startedAt = raw.startedAt;
|
|
120
|
+
if (raw.usage && typeof raw.usage === "object") {
|
|
121
|
+
const candidate = raw.usage;
|
|
122
|
+
if (Value.Check(PhaseUsageSchema, candidate))
|
|
123
|
+
header.usage = candidate;
|
|
124
|
+
}
|
|
125
|
+
return header;
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function emptyTotals() {
|
|
132
|
+
return {
|
|
133
|
+
input: 0,
|
|
134
|
+
output: 0,
|
|
135
|
+
cacheRead: 0,
|
|
136
|
+
cacheWrite: 0,
|
|
137
|
+
cost: 0,
|
|
138
|
+
byModel: {},
|
|
139
|
+
byProvider: {},
|
|
140
|
+
revisionLoops: 0,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function bumpAggregate(bucket, key, usage) {
|
|
144
|
+
const agg = bucket[key] ?? { input: 0, output: 0, cost: 0, phases: 0 };
|
|
145
|
+
agg.input += usage.input;
|
|
146
|
+
agg.output += usage.output;
|
|
147
|
+
agg.cost += usage.cost;
|
|
148
|
+
agg.phases += 1;
|
|
149
|
+
bucket[key] = agg;
|
|
150
|
+
}
|
|
151
|
+
function buildManifestForRun(runId, events, phaseFiles, tailFileNames, identity, projectKey, fallbackEntityId, fallbackStartIso, opts) {
|
|
152
|
+
const pipelineStart = events.find((e) => e.kind === "pipeline-start");
|
|
153
|
+
const pipelineEnd = events.find((e) => e.kind === "pipeline-end");
|
|
154
|
+
const phaseEnds = events.filter((e) => e.kind === "phase-end");
|
|
155
|
+
const revisionLoops = events.filter((e) => e.kind === "phase-loopback").length;
|
|
156
|
+
const entityId = typeof pipelineStart?.entityId === "string" && pipelineStart.entityId.length > 0
|
|
157
|
+
? pipelineStart.entityId
|
|
158
|
+
: fallbackEntityId;
|
|
159
|
+
const rawKind = pipelineStart?.entityKind;
|
|
160
|
+
const entityKind = rawKind === "task" || rawKind === "bug" || rawKind === "sprint" ? rawKind : "unknown";
|
|
161
|
+
const rawOutcome = pipelineEnd?.outcome;
|
|
162
|
+
const outcome = rawOutcome === "complete" || rawOutcome === "halted" || rawOutcome === "cancelled" || rawOutcome === "error"
|
|
163
|
+
? rawOutcome
|
|
164
|
+
: "incomplete";
|
|
165
|
+
// Phase records: one per phase-transcript file (chronological), decorated
|
|
166
|
+
// with verdict/attempt/elapsedMs from the matching phase-end event. Two
|
|
167
|
+
// matchers, tried in order:
|
|
168
|
+
// 1. phase-end.subagentTranscriptPath basename (exact, when populated)
|
|
169
|
+
// 2. same role, in chronological order (legacy runs without the path)
|
|
170
|
+
const phases = [];
|
|
171
|
+
const roleSeen = {};
|
|
172
|
+
const usedEnds = new Set();
|
|
173
|
+
for (const pf of [...phaseFiles].sort((a, b) => a.file.localeCompare(b.file))) {
|
|
174
|
+
roleSeen[pf.role] = (roleSeen[pf.role] ?? 0) + 1;
|
|
175
|
+
const header = readPhaseHeader(pf.absPath);
|
|
176
|
+
const record = {
|
|
177
|
+
role: pf.role,
|
|
178
|
+
attempt: roleSeen[pf.role],
|
|
179
|
+
verdict: "n/a",
|
|
180
|
+
file: pf.file,
|
|
181
|
+
};
|
|
182
|
+
// Pair the phase with its live tail-view log when one was persisted
|
|
183
|
+
// (same basename, .tail.jsonl suffix) — replay prefers it verbatim.
|
|
184
|
+
const tailName = `${pf.file.slice(0, -".json".length)}.tail.jsonl`;
|
|
185
|
+
if (tailFileNames.has(tailName))
|
|
186
|
+
record.tailFile = tailName;
|
|
187
|
+
if (header?.model)
|
|
188
|
+
record.model = header.model;
|
|
189
|
+
if (header?.provider)
|
|
190
|
+
record.provider = header.provider;
|
|
191
|
+
if (header?.usage)
|
|
192
|
+
record.usage = header.usage;
|
|
193
|
+
if (header?.startedAt)
|
|
194
|
+
record.startedAt = header.startedAt;
|
|
195
|
+
let matchIdx = phaseEnds.findIndex((e, i) => !usedEnds.has(i) &&
|
|
196
|
+
typeof e.subagentTranscriptPath === "string" &&
|
|
197
|
+
path.basename(e.subagentTranscriptPath) === pf.file);
|
|
198
|
+
if (matchIdx < 0) {
|
|
199
|
+
matchIdx = phaseEnds.findIndex((e, i) => !usedEnds.has(i) && e.phase === pf.role);
|
|
200
|
+
}
|
|
201
|
+
if (matchIdx >= 0) {
|
|
202
|
+
usedEnds.add(matchIdx);
|
|
203
|
+
const end = phaseEnds[matchIdx];
|
|
204
|
+
if (typeof end.verdict === "string")
|
|
205
|
+
record.verdict = end.verdict;
|
|
206
|
+
if (typeof end.attempt === "number")
|
|
207
|
+
record.attempt = end.attempt;
|
|
208
|
+
if (typeof end.elapsedMs === "number")
|
|
209
|
+
record.elapsedMs = end.elapsedMs;
|
|
210
|
+
}
|
|
211
|
+
phases.push(record);
|
|
212
|
+
}
|
|
213
|
+
// Phase-end events with no transcript file (export failed) still surface.
|
|
214
|
+
phaseEnds.forEach((end, i) => {
|
|
215
|
+
if (usedEnds.has(i))
|
|
216
|
+
return;
|
|
217
|
+
phases.push({
|
|
218
|
+
role: typeof end.phase === "string" ? end.phase : "unknown",
|
|
219
|
+
attempt: typeof end.attempt === "number" ? end.attempt : 1,
|
|
220
|
+
verdict: typeof end.verdict === "string" ? end.verdict : "n/a",
|
|
221
|
+
...(typeof end.elapsedMs === "number" ? { elapsedMs: end.elapsedMs } : {}),
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
const totals = emptyTotals();
|
|
225
|
+
totals.revisionLoops = revisionLoops;
|
|
226
|
+
for (const p of phases) {
|
|
227
|
+
if (!p.usage)
|
|
228
|
+
continue;
|
|
229
|
+
totals.input += p.usage.input;
|
|
230
|
+
totals.output += p.usage.output;
|
|
231
|
+
totals.cacheRead += p.usage.cacheRead;
|
|
232
|
+
totals.cacheWrite += p.usage.cacheWrite;
|
|
233
|
+
totals.cost += p.usage.cost;
|
|
234
|
+
bumpAggregate(totals.byModel, p.model ?? "unknown", p.usage);
|
|
235
|
+
bumpAggregate(totals.byProvider, p.provider ?? "unknown", p.usage);
|
|
236
|
+
}
|
|
237
|
+
const startedAt = typeof pipelineStart?.ts === "string" ? pipelineStart.ts : undefined;
|
|
238
|
+
const finishedAt = typeof pipelineEnd?.ts === "string" ? pipelineEnd.ts : undefined;
|
|
239
|
+
const durationMs = typeof pipelineEnd?.elapsedMs === "number" ? pipelineEnd.elapsedMs : undefined;
|
|
240
|
+
return {
|
|
241
|
+
schema: "forge-run-manifest/v1",
|
|
242
|
+
runId,
|
|
243
|
+
projectKey,
|
|
244
|
+
projectPath: identity.projectDir,
|
|
245
|
+
projectName: identity.name,
|
|
246
|
+
projectPrefix: identity.prefix,
|
|
247
|
+
entityId,
|
|
248
|
+
entityKind,
|
|
249
|
+
...(opts.sprintId ? { sprintId: opts.sprintId } : {}),
|
|
250
|
+
startedAt: startedAt ?? fallbackStartIso,
|
|
251
|
+
...(finishedAt ? { finishedAt } : {}),
|
|
252
|
+
...(durationMs !== undefined ? { durationMs } : {}),
|
|
253
|
+
outcome,
|
|
254
|
+
phases,
|
|
255
|
+
totals,
|
|
256
|
+
archivedAt: new Date().toISOString(),
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Discover every run in a project-local entity transcript dir
|
|
261
|
+
* (`.forge/transcripts/<entityId>/`) and build a RunManifest for each.
|
|
262
|
+
*
|
|
263
|
+
* A "run" is one orchestrator JSONL file; its runId is the compact-ISO
|
|
264
|
+
* pipeline-start timestamp (falling back to the filename prefix). Phase
|
|
265
|
+
* transcript files are assigned to the latest run whose start precedes
|
|
266
|
+
* them — compact ISO prefixes compare lexicographically as timestamps.
|
|
267
|
+
*/
|
|
268
|
+
export function buildRunsForEntityDir(entityDir, identity, projectKey, opts = {}) {
|
|
269
|
+
let names;
|
|
270
|
+
try {
|
|
271
|
+
names = fs.readdirSync(entityDir);
|
|
272
|
+
}
|
|
273
|
+
catch {
|
|
274
|
+
return [];
|
|
275
|
+
}
|
|
276
|
+
const fallbackEntityId = path.basename(entityDir);
|
|
277
|
+
const jsonls = [];
|
|
278
|
+
const phaseFiles = [];
|
|
279
|
+
const tailFiles = [];
|
|
280
|
+
for (const name of names) {
|
|
281
|
+
const orchMatch = ORCH_FILE_RE.exec(name);
|
|
282
|
+
if (orchMatch) {
|
|
283
|
+
jsonls.push({ absPath: path.join(entityDir, name), ts: orchMatch[1] });
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
const tailMatch = TAIL_FILE_RE.exec(name);
|
|
287
|
+
if (tailMatch) {
|
|
288
|
+
tailFiles.push({
|
|
289
|
+
absPath: path.join(entityDir, name),
|
|
290
|
+
file: name,
|
|
291
|
+
ts: tailMatch[1],
|
|
292
|
+
role: tailMatch[3],
|
|
293
|
+
});
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
const phaseMatch = PHASE_FILE_RE.exec(name);
|
|
297
|
+
if (phaseMatch) {
|
|
298
|
+
phaseFiles.push({
|
|
299
|
+
absPath: path.join(entityDir, name),
|
|
300
|
+
file: name,
|
|
301
|
+
ts: phaseMatch[1],
|
|
302
|
+
role: phaseMatch[3],
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
jsonls.sort((a, b) => a.ts.localeCompare(b.ts));
|
|
307
|
+
const runs = [];
|
|
308
|
+
for (let i = 0; i < jsonls.length; i++) {
|
|
309
|
+
const jsonl = jsonls[i];
|
|
310
|
+
const nextTs = jsonls[i + 1]?.ts;
|
|
311
|
+
const events = readJsonlEvents(jsonl.absPath);
|
|
312
|
+
const pipelineStart = events.find((e) => e.kind === "pipeline-start");
|
|
313
|
+
const runId = typeof pipelineStart?.ts === "string" ? compactIso(pipelineStart.ts) : jsonl.ts;
|
|
314
|
+
const inWindow = (pf) => pf.ts >= jsonl.ts && (nextTs === undefined || pf.ts < nextTs);
|
|
315
|
+
const runPhaseFiles = phaseFiles.filter(inWindow);
|
|
316
|
+
const runTailFiles = tailFiles.filter(inWindow);
|
|
317
|
+
runs.push({
|
|
318
|
+
runId,
|
|
319
|
+
jsonlPath: jsonl.absPath,
|
|
320
|
+
phaseFiles: runPhaseFiles,
|
|
321
|
+
tailFiles: runTailFiles,
|
|
322
|
+
manifest: buildManifestForRun(runId, events, runPhaseFiles, new Set(runTailFiles.map((tf) => tf.file)), identity, projectKey, fallbackEntityId, expandCompactIso(jsonl.ts), opts),
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
return runs;
|
|
326
|
+
}
|
|
327
|
+
// ── Archive writes ──────────────────────────────────────────────────────
|
|
328
|
+
function writeFileAtomic(target, content) {
|
|
329
|
+
const tmp = `${target}.tmp-${process.pid}`;
|
|
330
|
+
fs.writeFileSync(tmp, content, "utf8");
|
|
331
|
+
fs.renameSync(tmp, target);
|
|
332
|
+
}
|
|
333
|
+
function indexKey(projectKey, entityId, runId) {
|
|
334
|
+
return `${projectKey}/${entityId}/${runId}`;
|
|
335
|
+
}
|
|
336
|
+
function upsertProject(projectKey, identity, newRun) {
|
|
337
|
+
const registry = readProjects();
|
|
338
|
+
const now = new Date().toISOString();
|
|
339
|
+
const existing = registry.projects[projectKey];
|
|
340
|
+
registry.projects[projectKey] = {
|
|
341
|
+
path: identity.projectDir,
|
|
342
|
+
prefix: identity.prefix,
|
|
343
|
+
name: identity.name,
|
|
344
|
+
firstSeen: existing?.firstSeen ?? now,
|
|
345
|
+
lastSeen: now,
|
|
346
|
+
runCount: (existing?.runCount ?? 0) + (newRun ? 1 : 0),
|
|
347
|
+
};
|
|
348
|
+
writeFileAtomic(getTranscriptProjectsPath(), `${JSON.stringify(registry, null, 2)}\n`);
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Archive one discovered run: gzip phase transcripts into the run dir, copy
|
|
352
|
+
* orchestrator.jsonl as-is, write manifest.json (tmp+rename), append the
|
|
353
|
+
* index line (only if this runId isn't already indexed — crash between
|
|
354
|
+
* manifest write and index append heals here), and upsert projects.json.
|
|
355
|
+
*
|
|
356
|
+
* Idempotent: re-archiving the same run (resume with the same runId, or a
|
|
357
|
+
* sweep racing a live pipeline) overwrites the manifest/payloads with newer,
|
|
358
|
+
* fuller data and appends no duplicate index line. Best-effort, never throws.
|
|
359
|
+
*/
|
|
360
|
+
function writeRunArchive(run, identity, projectKey) {
|
|
361
|
+
try {
|
|
362
|
+
const { manifest } = run;
|
|
363
|
+
const runDir = getRunArchiveDir(projectKey, manifest.entityId, run.runId);
|
|
364
|
+
fs.mkdirSync(runDir, { recursive: true });
|
|
365
|
+
for (const pf of [...run.phaseFiles, ...run.tailFiles]) {
|
|
366
|
+
const gz = gzipSync(fs.readFileSync(pf.absPath));
|
|
367
|
+
fs.writeFileSync(path.join(runDir, `${pf.file}.gz`), gz);
|
|
368
|
+
}
|
|
369
|
+
fs.copyFileSync(run.jsonlPath, path.join(runDir, "orchestrator.jsonl"));
|
|
370
|
+
writeFileAtomic(path.join(runDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}\n`);
|
|
371
|
+
const alreadyIndexed = readIndex().some((e) => indexKey(e.projectKey, e.entityId, e.runId) === indexKey(projectKey, manifest.entityId, run.runId));
|
|
372
|
+
if (!alreadyIndexed) {
|
|
373
|
+
const entry = {
|
|
374
|
+
runId: run.runId,
|
|
375
|
+
projectKey,
|
|
376
|
+
entityId: manifest.entityId,
|
|
377
|
+
entityKind: manifest.entityKind,
|
|
378
|
+
...(manifest.sprintId ? { sprintId: manifest.sprintId } : {}),
|
|
379
|
+
startedAt: manifest.startedAt,
|
|
380
|
+
outcome: manifest.outcome,
|
|
381
|
+
input: manifest.totals.input,
|
|
382
|
+
output: manifest.totals.output,
|
|
383
|
+
cost: manifest.totals.cost,
|
|
384
|
+
...(manifest.durationMs !== undefined ? { durationMs: manifest.durationMs } : {}),
|
|
385
|
+
archivedAt: manifest.archivedAt,
|
|
386
|
+
};
|
|
387
|
+
// Single sub-1KB appendFileSync line — atomic on Linux.
|
|
388
|
+
fs.appendFileSync(getTranscriptIndexPath(), `${JSON.stringify(entry)}\n`, "utf8");
|
|
389
|
+
}
|
|
390
|
+
upsertProject(projectKey, identity, !alreadyIndexed);
|
|
391
|
+
return { archived: true, runDir };
|
|
392
|
+
}
|
|
393
|
+
catch (err) {
|
|
394
|
+
const e = err;
|
|
395
|
+
return { archived: false, error: e.message ?? "archive write failed" };
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Archive the run identified by its project-local orchestrator transcript.
|
|
400
|
+
* Called by orchestrators after the pipeline result is known. Best-effort,
|
|
401
|
+
* never throws.
|
|
402
|
+
*/
|
|
403
|
+
export function archiveRun(opts) {
|
|
404
|
+
try {
|
|
405
|
+
const configPath = opts.configPath ?? path.join(opts.cwd, ".forge", "config.json");
|
|
406
|
+
const identity = readProjectIdentity(configPath);
|
|
407
|
+
const projectKey = computeProjectKey(identity.projectDir, identity.prefix);
|
|
408
|
+
const entityDir = path.dirname(opts.orchestratorJsonlPath);
|
|
409
|
+
const runs = buildRunsForEntityDir(entityDir, identity, projectKey, { sprintId: opts.sprintId });
|
|
410
|
+
const wanted = path.resolve(opts.orchestratorJsonlPath);
|
|
411
|
+
const run = runs.find((r) => path.resolve(r.jsonlPath) === wanted);
|
|
412
|
+
if (!run)
|
|
413
|
+
return { archived: false, error: `no run found for ${opts.orchestratorJsonlPath}` };
|
|
414
|
+
return writeRunArchive(run, identity, projectKey);
|
|
415
|
+
}
|
|
416
|
+
catch (err) {
|
|
417
|
+
const e = err;
|
|
418
|
+
return { archived: false, error: e.message ?? "archiveRun failed" };
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Adopt orphans: archive every project-local run whose runId isn't in the
|
|
423
|
+
* central index. Covers crash recovery (pipeline died before its archive
|
|
424
|
+
* call) and adoption of pre-existing history (transcripts written before
|
|
425
|
+
* this feature shipped). Called best-effort at pipeline start; never throws.
|
|
426
|
+
*/
|
|
427
|
+
export function sweepProjectTranscripts(cwd, configPath) {
|
|
428
|
+
try {
|
|
429
|
+
const base = path.join(cwd, ".forge", "transcripts");
|
|
430
|
+
if (!fs.existsSync(base))
|
|
431
|
+
return { adopted: 0, errors: 0 };
|
|
432
|
+
const identity = readProjectIdentity(configPath ?? path.join(cwd, ".forge", "config.json"));
|
|
433
|
+
const projectKey = computeProjectKey(identity.projectDir, identity.prefix);
|
|
434
|
+
const indexed = new Set(readIndex().map((e) => indexKey(e.projectKey, e.entityId, e.runId)));
|
|
435
|
+
let adopted = 0;
|
|
436
|
+
let errors = 0;
|
|
437
|
+
for (const entry of fs.readdirSync(base, { withFileTypes: true })) {
|
|
438
|
+
if (!entry.isDirectory())
|
|
439
|
+
continue;
|
|
440
|
+
const runs = buildRunsForEntityDir(path.join(base, entry.name), identity, projectKey);
|
|
441
|
+
for (const run of runs) {
|
|
442
|
+
if (indexed.has(indexKey(projectKey, run.manifest.entityId, run.runId)))
|
|
443
|
+
continue;
|
|
444
|
+
const result = writeRunArchive(run, identity, projectKey);
|
|
445
|
+
if (result.archived)
|
|
446
|
+
adopted++;
|
|
447
|
+
else
|
|
448
|
+
errors++;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
return { adopted, errors };
|
|
452
|
+
}
|
|
453
|
+
catch {
|
|
454
|
+
return { adopted: 0, errors: 1 };
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
// ── Archive reads ───────────────────────────────────────────────────────
|
|
458
|
+
/** Read index.jsonl — skips malformed/truncated lines, never throws. */
|
|
459
|
+
export function readIndex() {
|
|
460
|
+
const entries = [];
|
|
461
|
+
for (const event of readJsonlEvents(getTranscriptIndexPath())) {
|
|
462
|
+
if (Value.Check(IndexEntrySchema, event))
|
|
463
|
+
entries.push(event);
|
|
464
|
+
}
|
|
465
|
+
return entries;
|
|
466
|
+
}
|
|
467
|
+
/** Read projects.json — empty registry on any failure, never throws. */
|
|
468
|
+
export function readProjects() {
|
|
469
|
+
try {
|
|
470
|
+
const raw = JSON.parse(fs.readFileSync(getTranscriptProjectsPath(), "utf8"));
|
|
471
|
+
if (Value.Check(ProjectsRegistrySchema, raw))
|
|
472
|
+
return raw;
|
|
473
|
+
}
|
|
474
|
+
catch {
|
|
475
|
+
// fall through
|
|
476
|
+
}
|
|
477
|
+
return { version: 1, projects: {} };
|
|
478
|
+
}
|
|
479
|
+
/** Read `<runDir>/manifest.json` — null on missing/truncated/mismatched. */
|
|
480
|
+
export function readManifest(runDir) {
|
|
481
|
+
try {
|
|
482
|
+
const raw = JSON.parse(fs.readFileSync(path.join(runDir, "manifest.json"), "utf8"));
|
|
483
|
+
if (Value.Check(RunManifestSchema, raw))
|
|
484
|
+
return raw;
|
|
485
|
+
}
|
|
486
|
+
catch {
|
|
487
|
+
// fall through
|
|
488
|
+
}
|
|
489
|
+
return null;
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Gunzip + parse an archived phase transcript. `file` is the manifest's
|
|
493
|
+
* phase `file` field (without `.gz`). Null on any failure, never throws.
|
|
494
|
+
*/
|
|
495
|
+
export function gunzipPhase(runDir, file) {
|
|
496
|
+
try {
|
|
497
|
+
const gzPath = path.join(runDir, file.endsWith(".gz") ? file : `${file}.gz`);
|
|
498
|
+
const parsed = JSON.parse(gunzipSync(fs.readFileSync(gzPath)).toString("utf8"));
|
|
499
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
500
|
+
return parsed;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
catch {
|
|
504
|
+
// fall through
|
|
505
|
+
}
|
|
506
|
+
return null;
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Gunzip + parse an archived live tail log (`*.tail.jsonl.gz`). Returns the
|
|
510
|
+
* exact lines the dashboard rendered during the run, in order. Tolerant of
|
|
511
|
+
* truncated tails; null when the file is missing/unreadable. Never throws.
|
|
512
|
+
*/
|
|
513
|
+
export function gunzipTailLog(runDir, file) {
|
|
514
|
+
try {
|
|
515
|
+
const gzPath = path.join(runDir, file.endsWith(".gz") ? file : `${file}.gz`);
|
|
516
|
+
const raw = gunzipSync(fs.readFileSync(gzPath)).toString("utf8");
|
|
517
|
+
const entries = [];
|
|
518
|
+
for (const line of raw.split("\n")) {
|
|
519
|
+
const trimmed = line.trim();
|
|
520
|
+
if (!trimmed)
|
|
521
|
+
continue;
|
|
522
|
+
try {
|
|
523
|
+
const parsed = JSON.parse(trimmed);
|
|
524
|
+
if (typeof parsed.line === "string") {
|
|
525
|
+
entries.push(parsed.warning === true ? { line: parsed.line, warning: true } : { line: parsed.line });
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
catch {
|
|
529
|
+
// truncated tail line — skip
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
return entries;
|
|
533
|
+
}
|
|
534
|
+
catch {
|
|
535
|
+
return null;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
/** Resolve the archive dir for an index entry. */
|
|
539
|
+
export function runDirForEntry(entry) {
|
|
540
|
+
return getRunArchiveDir(entry.projectKey, entry.entityId, entry.runId);
|
|
541
|
+
}
|
|
542
|
+
function fmtSize(n) {
|
|
543
|
+
if (n >= 1_000_000)
|
|
544
|
+
return `${(n / 1_000_000).toFixed(1)}M`;
|
|
545
|
+
if (n >= 1_000)
|
|
546
|
+
return `${(n / 1_000).toFixed(1)}k`;
|
|
547
|
+
return String(n);
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* Per-turn digest of a gunzipped phase payload — never a raw JSON dump.
|
|
551
|
+
* Each assistant message is a turn: first text line + tool-call names;
|
|
552
|
+
* toolResult messages render as compact result markers.
|
|
553
|
+
*/
|
|
554
|
+
export function digestPhasePayload(payload) {
|
|
555
|
+
const messages = Array.isArray(payload.messages) ? payload.messages : [];
|
|
556
|
+
const lines = [];
|
|
557
|
+
let turn = 0;
|
|
558
|
+
for (const msg of messages) {
|
|
559
|
+
if (msg.role === "assistant") {
|
|
560
|
+
turn++;
|
|
561
|
+
const parts = Array.isArray(msg.content) ? msg.content : [];
|
|
562
|
+
const textPart = parts.find((p) => p.type === "text" && typeof p.text === "string");
|
|
563
|
+
const firstLine = textPart ? textPart.text.trim().split("\n")[0] : "";
|
|
564
|
+
lines.push(` t${turn} ${firstLine ? (firstLine.length > 100 ? `${firstLine.slice(0, 100)}…` : firstLine) : "(no text)"}`);
|
|
565
|
+
for (const p of parts) {
|
|
566
|
+
if (p.type === "toolCall" && typeof p.name === "string")
|
|
567
|
+
lines.push(` → ${p.name}`);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
else if (msg.role === "toolResult") {
|
|
571
|
+
const size = typeof msg.content === "string" ? msg.content.length : JSON.stringify(msg.content ?? "").length;
|
|
572
|
+
lines.push(` ← ${msg.toolName ?? "tool"} ${msg.isError ? "✗" : "✓"} (${fmtSize(size)} chars)`);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
if (lines.length === 0)
|
|
576
|
+
lines.push(" (no messages in payload)");
|
|
577
|
+
return lines;
|
|
578
|
+
}
|
|
579
|
+
function firstLineOf(s, max = 120) {
|
|
580
|
+
const line = s.trim().split("\n")[0];
|
|
581
|
+
return line.length > max ? `${line.slice(0, max)}…` : line;
|
|
582
|
+
}
|
|
583
|
+
/** Extract readable text from a toolResult content value (string or parts array). */
|
|
584
|
+
function toolResultText(content) {
|
|
585
|
+
if (typeof content === "string")
|
|
586
|
+
return content;
|
|
587
|
+
if (Array.isArray(content)) {
|
|
588
|
+
return content
|
|
589
|
+
.filter((p) => !!p && typeof p === "object" && p.type === "text" && typeof p.text === "string")
|
|
590
|
+
.map((p) => p.text)
|
|
591
|
+
.join("\n");
|
|
592
|
+
}
|
|
593
|
+
if (content === undefined || content === null)
|
|
594
|
+
return "";
|
|
595
|
+
try {
|
|
596
|
+
return JSON.stringify(content);
|
|
597
|
+
}
|
|
598
|
+
catch {
|
|
599
|
+
return "";
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
/** Per-turn cap on assistant text in the verbose digest. */
|
|
603
|
+
const VERBOSE_TEXT_CAP = 1500;
|
|
604
|
+
/**
|
|
605
|
+
* VERBOSE digest of a gunzipped phase payload — the replay-dashboard feed.
|
|
606
|
+
* Unlike digestPhasePayload (compact markers for the `show` table), this
|
|
607
|
+
* carries the actual transcript content so a replayed run is readable:
|
|
608
|
+
* full assistant text (capped per turn), thinking first-lines, tool-call
|
|
609
|
+
* argument previews, and tool-result first-line previews.
|
|
610
|
+
*
|
|
611
|
+
* Multi-line assistant text is pushed as ONE entry — the dashboard splits
|
|
612
|
+
* lines at display time, and the tree's per-entry char cap still applies.
|
|
613
|
+
*/
|
|
614
|
+
export function digestPhasePayloadVerbose(payload) {
|
|
615
|
+
const messages = Array.isArray(payload.messages) ? payload.messages : [];
|
|
616
|
+
const lines = [];
|
|
617
|
+
let turn = 0;
|
|
618
|
+
for (const msg of messages) {
|
|
619
|
+
if (msg.role === "assistant") {
|
|
620
|
+
turn++;
|
|
621
|
+
lines.push(`── t${turn} ──`);
|
|
622
|
+
const parts = Array.isArray(msg.content) ? msg.content : [];
|
|
623
|
+
for (const p of parts) {
|
|
624
|
+
if (p.type === "thinking" && typeof p.thinking === "string" && p.thinking.trim()) {
|
|
625
|
+
lines.push(`✱ thinking: ${firstLineOf(p.thinking)}`);
|
|
626
|
+
}
|
|
627
|
+
else if (p.type === "text" && typeof p.text === "string" && p.text.trim()) {
|
|
628
|
+
const text = p.text.trim();
|
|
629
|
+
lines.push(text.length > VERBOSE_TEXT_CAP ? `${text.slice(0, VERBOSE_TEXT_CAP)}…(+${text.length - VERBOSE_TEXT_CAP}c)` : text);
|
|
630
|
+
}
|
|
631
|
+
else if (p.type === "toolCall" && typeof p.name === "string") {
|
|
632
|
+
let argsPreview = "";
|
|
633
|
+
try {
|
|
634
|
+
argsPreview = p.arguments === undefined ? "" : JSON.stringify(p.arguments);
|
|
635
|
+
}
|
|
636
|
+
catch {
|
|
637
|
+
argsPreview = "";
|
|
638
|
+
}
|
|
639
|
+
if (argsPreview.length > 140)
|
|
640
|
+
argsPreview = `${argsPreview.slice(0, 140)}…`;
|
|
641
|
+
lines.push(`→ ${p.name}${argsPreview && argsPreview !== "{}" ? ` ${argsPreview}` : ""}`);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
else if (msg.role === "toolResult") {
|
|
646
|
+
const text = toolResultText(msg.content);
|
|
647
|
+
const preview = firstLineOf(text);
|
|
648
|
+
lines.push(`← ${msg.toolName ?? "tool"} ${msg.isError ? "✗" : "✓"} (${fmtSize(text.length)}c)${preview ? ` ${preview}` : ""}`);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
if (lines.length === 0)
|
|
652
|
+
lines.push("(no messages in payload)");
|
|
653
|
+
return lines;
|
|
654
|
+
}
|
|
655
|
+
export { getTranscriptArchiveRoot };
|
|
656
|
+
//# sourceMappingURL=transcript-archive.js.map
|