@entelligentsia/forgecli 1.0.3 → 1.0.14
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 +93 -0
- package/README.md +2 -1
- package/dist/CHANGELOG-forge-plugin.md +250 -0
- package/dist/CHANGELOG-pi.md +94 -0
- package/dist/bin/forge.js +0 -0
- package/dist/extensions/forgecli/config-layer.d.ts +16 -0
- package/dist/extensions/forgecli/config-layer.js +5 -0
- package/dist/extensions/forgecli/config-layer.js.map +1 -1
- package/dist/extensions/forgecli/dashboard/component.d.ts +102 -0
- package/dist/extensions/forgecli/dashboard/component.js +882 -0
- package/dist/extensions/forgecli/dashboard/component.js.map +1 -0
- package/dist/extensions/forgecli/dashboard/register.d.ts +2 -0
- package/dist/extensions/forgecli/dashboard/register.js +45 -0
- package/dist/extensions/forgecli/dashboard/register.js.map +1 -0
- package/dist/extensions/forgecli/dashboard/view-model.d.ts +35 -0
- package/dist/extensions/forgecli/dashboard/view-model.js +54 -0
- package/dist/extensions/forgecli/dashboard/view-model.js.map +1 -0
- package/dist/extensions/forgecli/fix-bug.js +72 -7
- package/dist/extensions/forgecli/fix-bug.js.map +1 -1
- package/dist/extensions/forgecli/forge-artifact-tool.js +27 -4
- package/dist/extensions/forgecli/forge-artifact-tool.js.map +1 -1
- package/dist/extensions/forgecli/forge-cli-schema.json +4 -0
- package/dist/extensions/forgecli/forge-commands.js +1 -0
- package/dist/extensions/forgecli/forge-commands.js.map +1 -1
- package/dist/extensions/forgecli/forge-init/phase4-register.js +53 -0
- package/dist/extensions/forgecli/forge-init/phase4-register.js.map +1 -1
- package/dist/extensions/forgecli/forge-subagent.js +6 -4
- package/dist/extensions/forgecli/forge-subagent.js.map +1 -1
- package/dist/extensions/forgecli/forge-tools.js +2 -2
- package/dist/extensions/forgecli/forge-tools.js.map +1 -1
- package/dist/extensions/forgecli/index.js +5 -0
- package/dist/extensions/forgecli/index.js.map +1 -1
- package/dist/extensions/forgecli/lib/halt-advisor.d.ts +54 -0
- package/dist/extensions/forgecli/lib/halt-advisor.js +90 -0
- package/dist/extensions/forgecli/lib/halt-advisor.js.map +1 -0
- package/dist/extensions/forgecli/migration-engine.js +25 -12
- package/dist/extensions/forgecli/migration-engine.js.map +1 -1
- package/dist/extensions/forgecli/orchestrator-status-bar.d.ts +25 -0
- package/dist/extensions/forgecli/orchestrator-status-bar.js +183 -0
- package/dist/extensions/forgecli/orchestrator-status-bar.js.map +1 -0
- package/dist/extensions/forgecli/orchestrator-tree.d.ts +96 -0
- package/dist/extensions/forgecli/orchestrator-tree.js +390 -0
- package/dist/extensions/forgecli/orchestrator-tree.js.map +1 -0
- package/dist/extensions/forgecli/project-orientation.js +12 -8
- package/dist/extensions/forgecli/project-orientation.js.map +1 -1
- package/dist/extensions/forgecli/regenerate.d.ts +16 -0
- package/dist/extensions/forgecli/regenerate.js +110 -0
- package/dist/extensions/forgecli/regenerate.js.map +1 -1
- package/dist/extensions/forgecli/run-sprint.js +33 -3
- package/dist/extensions/forgecli/run-sprint.js.map +1 -1
- package/dist/extensions/forgecli/run-task.d.ts +32 -0
- package/dist/extensions/forgecli/run-task.js +185 -12
- package/dist/extensions/forgecli/run-task.js.map +1 -1
- package/dist/extensions/forgecli/subagent/phase-guard.js +15 -5
- package/dist/extensions/forgecli/subagent/phase-guard.js.map +1 -1
- package/dist/extensions/forgecli/subagent/phase-summary-map.d.ts +1 -0
- package/dist/extensions/forgecli/subagent/phase-summary-map.js +17 -0
- package/dist/extensions/forgecli/subagent/phase-summary-map.js.map +1 -1
- package/dist/extensions/forgecli/thread-switcher.js +105 -764
- package/dist/extensions/forgecli/thread-switcher.js.map +1 -1
- package/dist/extensions/forgecli/viewport-events.js +32 -0
- package/dist/extensions/forgecli/viewport-events.js.map +1 -1
- package/dist/forge-payload/.base-pack/commands/fix-bug.md +1 -1
- package/dist/forge-payload/.base-pack/commands/run-sprint.md +1 -1
- package/dist/forge-payload/.base-pack/commands/run-task.md +1 -1
- package/dist/forge-payload/.base-pack/personas/architect.md +1 -1
- package/dist/forge-payload/.base-pack/personas/bug-fixer.md +1 -1
- package/dist/forge-payload/.base-pack/personas/collator.md +3 -3
- package/dist/forge-payload/.base-pack/personas/engineer.md +1 -1
- package/dist/forge-payload/.base-pack/personas/librarian.md +1 -1
- package/dist/forge-payload/.base-pack/personas/orchestrator.md +1 -1
- package/dist/forge-payload/.base-pack/personas/product-manager.md +1 -1
- package/dist/forge-payload/.base-pack/personas/qa-engineer.md +1 -1
- package/dist/forge-payload/.base-pack/personas/supervisor.md +1 -1
- package/dist/forge-payload/.base-pack/workflows/_fragments/event-emission-schema.md +1 -1
- package/dist/forge-payload/.base-pack/workflows/_fragments/friction-emit.md +1 -1
- package/dist/forge-payload/.base-pack/workflows/_fragments/iron-laws.md +1 -1
- package/dist/forge-payload/.base-pack/workflows/_fragments/progress-reporting.md +2 -2
- package/dist/forge-payload/.base-pack/workflows/_fragments/store-cli-verbs.md +29 -5
- package/dist/forge-payload/.base-pack/workflows/architect_approve.md +8 -10
- package/dist/forge-payload/.base-pack/workflows/architect_review_sprint_completion.md +2 -2
- package/dist/forge-payload/.base-pack/workflows/architect_sprint_intake.md +2 -2
- package/dist/forge-payload/.base-pack/workflows/architect_sprint_plan.md +5 -5
- package/dist/forge-payload/.base-pack/workflows/collator_agent.md +5 -7
- package/dist/forge-payload/.base-pack/workflows/commit_task.md +7 -9
- package/dist/forge-payload/.base-pack/workflows/enhance.md +5 -5
- package/dist/forge-payload/.base-pack/workflows/implement_plan.md +9 -9
- package/dist/forge-payload/.base-pack/workflows/migrate_structural.md +12 -13
- package/dist/forge-payload/.base-pack/workflows/plan_task.md +5 -6
- package/dist/forge-payload/.base-pack/workflows/review_code.md +8 -8
- package/dist/forge-payload/.base-pack/workflows/review_plan.md +8 -8
- package/dist/forge-payload/.base-pack/workflows/sprint_retrospective.md +3 -3
- package/dist/forge-payload/.base-pack/workflows/triage.md +13 -10
- package/dist/forge-payload/.base-pack/workflows/update_implementation.md +2 -2
- package/dist/forge-payload/.base-pack/workflows/update_plan.md +2 -2
- package/dist/forge-payload/.base-pack/workflows/validate_task.md +6 -8
- package/dist/forge-payload/.base-pack/workflows-js/wfl-fix-bug.js +490 -0
- package/dist/forge-payload/.base-pack/workflows-js/wfl-run-sprint.js +416 -0
- package/dist/forge-payload/.base-pack/workflows-js/wfl-run-task.js +608 -0
- package/dist/forge-payload/.claude-plugin/plugin.json +1 -1
- package/dist/forge-payload/.schemas/_defs/locator.schema.json +13 -0
- package/dist/forge-payload/.schemas/bug.schema.json +1 -0
- package/dist/forge-payload/.schemas/config.schema.json +2 -3
- package/dist/forge-payload/.schemas/enum-catalog.json +2 -2
- package/dist/forge-payload/.schemas/event.schema.json +16 -0
- package/dist/forge-payload/.schemas/migrations.json +299 -0
- package/dist/forge-payload/.schemas/sprint.schema.json +1 -0
- package/dist/forge-payload/.schemas/task.schema.json +1 -0
- package/dist/forge-payload/commands/health.md +29 -0
- package/dist/forge-payload/commands/rebuild.md +143 -15
- package/dist/forge-payload/commands/update.md +28 -27
- package/dist/forge-payload/hooks/preflight-session.cjs +99 -0
- package/dist/forge-payload/init/phases/phase-3-materialize.md +18 -5
- package/dist/forge-payload/integrity.json +7 -6
- package/dist/forge-payload/meta/fragments/tool-discipline.md +1 -1
- package/dist/forge-payload/meta/personas/meta-architect.md +1 -1
- package/dist/forge-payload/meta/personas/meta-bug-fixer.md +1 -1
- package/dist/forge-payload/meta/personas/meta-collator.md +7 -7
- package/dist/forge-payload/meta/personas/meta-engineer.md +1 -1
- package/dist/forge-payload/meta/personas/meta-orchestrator.md +1 -1
- package/dist/forge-payload/meta/personas/meta-supervisor.md +1 -1
- package/dist/forge-payload/meta/tool-specs/store-cli.spec.md +1 -1
- package/dist/forge-payload/meta/workflows/_fragments/event-emission-schema.md +1 -1
- package/dist/forge-payload/meta/workflows/_fragments/friction-emit.md +1 -1
- package/dist/forge-payload/meta/workflows/_fragments/iron-laws.md +1 -1
- package/dist/forge-payload/meta/workflows/_fragments/progress-reporting.md +2 -2
- package/dist/forge-payload/meta/workflows/_fragments/store-cli-verbs.md +29 -5
- package/dist/forge-payload/meta/workflows/meta-approve.md +8 -10
- package/dist/forge-payload/meta/workflows/meta-bug-triage.md +13 -10
- package/dist/forge-payload/meta/workflows/meta-collate.md +6 -8
- package/dist/forge-payload/meta/workflows/meta-commit.md +7 -9
- package/dist/forge-payload/meta/workflows/meta-enhance.md +5 -5
- package/dist/forge-payload/meta/workflows/meta-fix-bug.md +35 -11
- package/dist/forge-payload/meta/workflows/meta-implement.md +18 -9
- package/dist/forge-payload/meta/workflows/meta-migrate.md +13 -14
- package/dist/forge-payload/meta/workflows/meta-new-sprint.md +3 -3
- package/dist/forge-payload/meta/workflows/meta-orchestrate.md +175 -82
- package/dist/forge-payload/meta/workflows/meta-plan-sprint.md +6 -6
- package/dist/forge-payload/meta/workflows/meta-plan-task.md +12 -6
- package/dist/forge-payload/meta/workflows/meta-retro.md +4 -4
- package/dist/forge-payload/meta/workflows/meta-retrospective.md +4 -4
- package/dist/forge-payload/meta/workflows/meta-review-implementation.md +8 -8
- package/dist/forge-payload/meta/workflows/meta-review-plan.md +8 -8
- package/dist/forge-payload/meta/workflows/meta-review-sprint-completion.md +3 -3
- package/dist/forge-payload/meta/workflows/meta-sprint-intake.md +3 -3
- package/dist/forge-payload/meta/workflows/meta-sprint-plan.md +6 -6
- package/dist/forge-payload/meta/workflows/meta-update-implementation.md +2 -2
- package/dist/forge-payload/meta/workflows/meta-update-plan.md +2 -2
- package/dist/forge-payload/meta/workflows/meta-validate.md +6 -8
- package/dist/forge-payload/schemas/_defs/locator.schema.json +13 -0
- package/dist/forge-payload/schemas/bug.schema.json +1 -0
- package/dist/forge-payload/schemas/config.schema.json +2 -3
- package/dist/forge-payload/schemas/enum-catalog.json +2 -2
- package/dist/forge-payload/schemas/event.schema.json +16 -0
- package/dist/forge-payload/schemas/sprint.schema.json +1 -0
- package/dist/forge-payload/schemas/structure-manifest.json +76 -73
- package/dist/forge-payload/schemas/task.schema.json +1 -0
- package/dist/forge-payload/skills/refresh-kb-links/SKILL.md +14 -7
- package/dist/forge-payload/tools/artifact-store.cjs +242 -0
- package/dist/forge-payload/tools/artifact.cjs +60 -120
- package/dist/forge-payload/tools/banners.cjs +29 -10
- package/dist/forge-payload/tools/check-structure.cjs +88 -7
- package/dist/forge-payload/tools/collate.cjs +16 -2
- package/dist/forge-payload/tools/lib/artifact-kinds.cjs +95 -0
- package/dist/forge-payload/tools/lib/store-nlp.cjs +6 -0
- package/dist/forge-payload/tools/lib/store-query-exec.cjs +39 -5
- package/dist/forge-payload/tools/lib/suggest.cjs +2 -1
- package/dist/forge-payload/tools/manage-config.cjs +5 -7
- package/dist/forge-payload/tools/parse-gates.cjs +73 -1
- package/dist/forge-payload/tools/postflight-gate.cjs +252 -0
- package/dist/forge-payload/tools/preflight-gate.cjs +102 -5
- package/dist/forge-payload/tools/store-cli.cjs +50 -15
- package/dist/forge-payload/tools/substitute-placeholders.cjs +5 -4
- package/dist/forge-payload/tools/verify-phase.cjs +17 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/agent-harness.d.ts +5 -2
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/agent-harness.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/agent-harness.js +81 -18
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/agent-harness.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/branch-summarization.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/branch-summarization.js +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/branch-summarization.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.js +19 -24
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/session.d.ts +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/session.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/session.js +14 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/session.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.d.ts +22 -8
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
- package/node_modules/@earendil-works/pi-ai/README.md +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts +374 -122
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.js +424 -232
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.d.ts +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.js +38 -2
- package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.js +21 -12
- package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.js +6 -10
- package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/google-vertex.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/google-vertex.js +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/google-vertex.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/google.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/google.js +5 -3
- package/node_modules/@earendil-works/pi-ai/dist/providers/google.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/images/openrouter.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/images/openrouter.js +3 -4
- package/node_modules/@earendil-works/pi-ai/dist/providers/images/openrouter.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/mistral.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/mistral.js +2 -3
- package/node_modules/@earendil-works/pi-ai/dist/providers/mistral.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex-responses.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex-responses.js +159 -78
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.js +16 -11
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses-shared.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses-shared.js +4 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses-shared.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses.js +6 -10
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/simple-options.js +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/simple-options.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/stream.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/stream.js +14 -2
- package/node_modules/@earendil-works/pi-ai/dist/stream.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/types.d.ts +14 -4
- package/node_modules/@earendil-works/pi-ai/dist/types.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/types.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/abort-signals.d.ts +6 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/abort-signals.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/abort-signals.js +34 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/abort-signals.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/device-code.d.ts +9 -7
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/device-code.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/device-code.js +8 -7
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/device-code.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/github-copilot.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/github-copilot.js +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/github-copilot.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/index.d.ts +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/index.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/index.js +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/index.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/openai-codex.d.ts +10 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/openai-codex.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/openai-codex.js +179 -79
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/openai-codex.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/package.json +2 -2
- package/node_modules/@earendil-works/pi-coding-agent/CHANGELOG.md +94 -0
- package/node_modules/@earendil-works/pi-coding-agent/README.md +9 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/cli/args.d.ts +3 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/cli/args.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/cli/args.js +27 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/cli/args.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/config.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/config.js +15 -2
- package/node_modules/@earendil-works/pi-coding-agent/dist/config.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session-services.d.ts +1 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session-services.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session-services.js +1 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session-services.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.d.ts +5 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js +28 -4
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/compaction.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/compaction.js +18 -24
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/compaction.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/runner.d.ts +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/runner.js +8 -2
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/types.d.ts +7 -5
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-registry.js +65 -13
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-resolver.js +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/resolve-config-value.d.ts +9 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/resolve-config-value.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/resolve-config-value.js +134 -11
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/resolve-config-value.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/sdk.d.ts +2 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/sdk.js +10 -6
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/session-manager.d.ts +6 -7
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/session-manager.js +75 -28
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/session-manager.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.d.ts +2 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.js +14 -9
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/system-prompt.js +0 -3
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/edit.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/edit.js +7 -10
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/edit.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/find.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/find.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/grep.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/grep.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/ls.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/ls.js +5 -7
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/ls.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/read.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/read.js +6 -7
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/read.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/render-utils.d.ts +5 -2
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/render-utils.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/render-utils.js +17 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/render-utils.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/write.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/write.js +5 -6
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/write.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/index.d.ts +2 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/index.js +2 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/index.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/main.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/main.js +69 -16
- package/node_modules/@earendil-works/pi-coding-agent/dist/main.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/migrations.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/migrations.js +118 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/migrations.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts +1 -3
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +2 -4
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/user-message.js +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +3 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js +59 -6
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/theme/theme.js +10 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-mode.js +3 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts +1 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-types.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/utils/deprecation.d.ts +4 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/utils/deprecation.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/utils/deprecation.js +13 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/utils/deprecation.js.map +1 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/utils/json.d.ts +3 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/utils/json.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/utils/json.js +7 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/utils/json.js.map +1 -0
- package/node_modules/@earendil-works/pi-coding-agent/docs/custom-provider.md +13 -10
- package/node_modules/@earendil-works/pi-coding-agent/docs/development.md +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/docs/extensions.md +12 -6
- package/node_modules/@earendil-works/pi-coding-agent/docs/models.md +25 -12
- package/node_modules/@earendil-works/pi-coding-agent/docs/providers.md +13 -5
- package/node_modules/@earendil-works/pi-coding-agent/docs/quickstart.md +1 -0
- package/node_modules/@earendil-works/pi-coding-agent/docs/rpc.md +2 -1
- package/node_modules/@earendil-works/pi-coding-agent/docs/sdk.md +6 -0
- package/node_modules/@earendil-works/pi-coding-agent/docs/session-format.md +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/docs/sessions.md +8 -0
- package/node_modules/@earendil-works/pi-coding-agent/docs/settings.md +7 -3
- package/node_modules/@earendil-works/pi-coding-agent/docs/terminal-setup.md +2 -0
- package/node_modules/@earendil-works/pi-coding-agent/docs/tui.md +2 -2
- package/node_modules/@earendil-works/pi-coding-agent/docs/usage.md +9 -0
- package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/README.md +1 -0
- package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/custom-provider-anthropic/index.ts +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/custom-provider-gitlab-duo/index.ts +54 -3
- package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/git-merge-and-resolve.ts +115 -0
- package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/input-transform-streaming.ts +39 -0
- package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/sandbox/package.json +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/with-deps/package.json +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/npm-shrinkwrap.json +443 -61
- package/node_modules/@earendil-works/pi-coding-agent/package.json +6 -6
- package/node_modules/@earendil-works/pi-tui/README.md +2 -2
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.js +24 -83
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/input.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/input.js +7 -55
- package/node_modules/@earendil-works/pi-tui/dist/components/input.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/markdown.d.ts +7 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/markdown.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/markdown.js +12 -2
- package/node_modules/@earendil-works/pi-tui/dist/components/markdown.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/index.d.ts +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/index.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/index.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/terminal-image.d.ts +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/terminal-image.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/terminal-image.js +34 -7
- package/node_modules/@earendil-works/pi-tui/dist/terminal-image.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts +33 -10
- package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/terminal.js +172 -37
- package/node_modules/@earendil-works/pi-tui/dist/terminal.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/utils.d.ts +6 -1
- package/node_modules/@earendil-works/pi-tui/dist/utils.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/utils.js +27 -15
- package/node_modules/@earendil-works/pi-tui/dist/utils.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/word-navigation.d.ts +25 -0
- package/node_modules/@earendil-works/pi-tui/dist/word-navigation.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/word-navigation.js +96 -0
- package/node_modules/@earendil-works/pi-tui/dist/word-navigation.js.map +1 -0
- package/node_modules/@earendil-works/pi-tui/package.json +2 -2
- package/node_modules/@entelligentsia/forge-compress/LICENSE +21 -0
- package/node_modules/@entelligentsia/forge-compress/README.md +85 -0
- package/node_modules/@mariozechner/clipboard/Cargo.toml +3 -3
- package/node_modules/@mariozechner/clipboard/index.d.ts +34 -20
- package/node_modules/@mariozechner/clipboard/index.js +546 -257
- package/node_modules/@mariozechner/clipboard/package.json +5 -6
- package/node_modules/@mariozechner/clipboard/package.json.prepack-backup +14 -14
- package/node_modules/@mariozechner/clipboard/src/lib.rs +4 -9
- package/node_modules/@mariozechner/clipboard-linux-x64-gnu/clipboard.linux-x64-gnu.node +0 -0
- package/node_modules/@mariozechner/clipboard-linux-x64-gnu/package.json +2 -2
- package/package.json +7 -7
- package/dist/bin/forgecli.d.ts +0 -2
- package/dist/bin/forgecli.js +0 -6
- package/dist/bin/forgecli.js.map +0 -1
- package/dist/extensions/forgecli/config-tui/index.d.ts +0 -5
- package/dist/extensions/forgecli/config-tui/index.js +0 -5
- package/dist/extensions/forgecli/config-tui/index.js.map +0 -1
- package/dist/extensions/forgecli/loaders/persona-skill-loader.d.ts +0 -45
- package/dist/extensions/forgecli/loaders/persona-skill-loader.js +0 -227
- package/dist/extensions/forgecli/loaders/persona-skill-loader.js.map +0 -1
- package/dist/extensions/forgecli/loaders/template-render.d.ts +0 -20
- package/dist/extensions/forgecli/loaders/template-render.js +0 -85
- package/dist/extensions/forgecli/loaders/template-render.js.map +0 -1
- package/dist/extensions/forgecli/loaders/workflow-loader.d.ts +0 -41
- package/dist/extensions/forgecli/loaders/workflow-loader.js +0 -164
- package/dist/extensions/forgecli/loaders/workflow-loader.js.map +0 -1
- package/dist/forge-payload/.base-pack/commands/quiz-agent.md +0 -6
- package/dist/forge-payload/.base-pack/commands/retrospective.md +0 -6
- package/dist/forge-payload/.base-pack/commands/sprint-intake.md +0 -6
- package/dist/forge-payload/.base-pack/commands/sprint-plan.md +0 -6
- package/dist/forge-payload/.base-pack/workflows/fix_bug.md +0 -446
- package/dist/forge-payload/.base-pack/workflows/orchestrate_task.md +0 -934
- package/dist/forge-payload/.base-pack/workflows/run_sprint.md +0 -225
- package/dist/forge-payload/commands/calibrate.md +0 -10
- package/dist/forge-payload/commands/materialize.md +0 -119
- package/dist/forge-payload/commands/migrate.md +0 -12
- package/dist/forge-payload/commands/quiz-agent.md +0 -6
- package/dist/forge-payload/commands/regenerate.md +0 -6
- package/dist/forge-payload/commands/store-query.md +0 -6
- package/dist/forge-payload/commands/store-repair.md +0 -6
- package/dist/forge-payload/commands/update-tools.md +0 -10
- package/dist/forge-payload/meta/templates/meta-retrospective.md +0 -28
- package/dist/forge-payload/tools/prompts/sprint-plan-prompt.md +0 -70
- package/dist/forge-payload/tools/schemas/task-list.schema.json +0 -53
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/execution-env.d.ts +0 -4
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/execution-env.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/execution-env.js +0 -3
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/execution-env.js.map +0 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo/jsonl.d.ts +0 -20
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo/jsonl.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo/jsonl.js +0 -92
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo/jsonl.js.map +0 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo/memory.d.ts +0 -18
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo/memory.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo/memory.js +0 -42
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo/memory.js.map +0 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo/shared.d.ts +0 -10
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo/shared.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo/shared.js +0 -31
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/repo/shared.js.map +0 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/storage/jsonl.d.ts +0 -30
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/storage/jsonl.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/storage/jsonl.js +0 -170
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/storage/jsonl.js.map +0 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/storage/memory.d.ts +0 -26
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/storage/memory.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/storage/memory.js +0 -90
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/storage/memory.js.map +0 -1
- package/node_modules/@mariozechner/clipboard-linux-x64-musl/README.md +0 -3
- package/node_modules/@mariozechner/clipboard-linux-x64-musl/clipboard.linux-x64-musl.node +0 -0
- package/node_modules/@mariozechner/clipboard-linux-x64-musl/package.json +0 -25
|
@@ -0,0 +1,882 @@
|
|
|
1
|
+
// dashboard/component.ts — Two-panel orchestrator tree dashboard.
|
|
2
|
+
//
|
|
3
|
+
// Renders as a pi-tui overlay (ctx.ui.custom) with:
|
|
4
|
+
// Left panel: tree browser with depth-based indentation, status glyphs,
|
|
5
|
+
// progress labels, and cursor highlighting.
|
|
6
|
+
// Right panel: detail view of the selected node — status, model, metrics,
|
|
7
|
+
// prompt preview, tail-buffer activity, outcome, and children.
|
|
8
|
+
//
|
|
9
|
+
// Keyboard:
|
|
10
|
+
// ↑/↓ move cursor in tree
|
|
11
|
+
// → expand orchestrator / focus leaf detail
|
|
12
|
+
// ← collapse orchestrator / back to tree
|
|
13
|
+
// Enter toggle expand on orchestrator; focus detail on leaf
|
|
14
|
+
// Esc close overlay
|
|
15
|
+
// x request cancellation (with y/n confirm)
|
|
16
|
+
// p (reserved for pause)
|
|
17
|
+
//
|
|
18
|
+
// Architecture (post-MVC-refactor, dashboard-mvc-audit.md V1–V7):
|
|
19
|
+
// DashboardController owns the TreeViewModel, subscribes to model events,
|
|
20
|
+
// manages the refresh timer, and handles input. DashboardComponent reads
|
|
21
|
+
// only from the controller — never directly from the OrchestratorTree model.
|
|
22
|
+
// This eliminates the boundary violations catalogued in the audit.
|
|
23
|
+
import { matchesKey, Key, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
24
|
+
import { getSessionRegistry } from "../session-registry.js";
|
|
25
|
+
import { buildViewModel } from "./view-model.js";
|
|
26
|
+
import { fmtTokenMeter } from "../viewport-renderer.js";
|
|
27
|
+
import { paintTailLine } from "../viewport-theme.js";
|
|
28
|
+
// ── Word-wrap helper ───────────────────────────────────────────────────────────
|
|
29
|
+
//
|
|
30
|
+
// Splits a line (which may contain ANSI escape sequences) into multiple
|
|
31
|
+
// lines at word boundaries so it fits within `maxWidth` visible columns.
|
|
32
|
+
// Preserves ANSI styling across wrapped lines. Falls back to character-level
|
|
33
|
+
// truncation for individual tokens wider than maxWidth.
|
|
34
|
+
function wrapLine(line, maxWidth) {
|
|
35
|
+
if (maxWidth <= 0)
|
|
36
|
+
return [line];
|
|
37
|
+
const visW = visibleWidth(line);
|
|
38
|
+
if (visW <= maxWidth)
|
|
39
|
+
return [line];
|
|
40
|
+
const segments = [];
|
|
41
|
+
const ANSI_RE = /(\x1b\[[0-9;]*m|\x1b\].*?(?:\x07|\x1b\\)|\x1b\[[^m]*m)/g;
|
|
42
|
+
let currentPrefix = "";
|
|
43
|
+
let lastIdx = 0;
|
|
44
|
+
let m;
|
|
45
|
+
while ((m = ANSI_RE.exec(line)) !== null) {
|
|
46
|
+
if (m.index > lastIdx) {
|
|
47
|
+
segments.push({ prefix: currentPrefix, text: line.slice(lastIdx, m.index) });
|
|
48
|
+
}
|
|
49
|
+
currentPrefix += m[0];
|
|
50
|
+
lastIdx = m.index + m[0].length;
|
|
51
|
+
}
|
|
52
|
+
if (lastIdx < line.length) {
|
|
53
|
+
segments.push({ prefix: currentPrefix, text: line.slice(lastIdx) });
|
|
54
|
+
}
|
|
55
|
+
if (segments.length === 0) {
|
|
56
|
+
return [truncateToWidth(line, maxWidth)];
|
|
57
|
+
}
|
|
58
|
+
const tokens = [];
|
|
59
|
+
for (const seg of segments) {
|
|
60
|
+
const words = seg.text.split(/\s+/);
|
|
61
|
+
for (const w of words) {
|
|
62
|
+
if (w.length === 0)
|
|
63
|
+
continue;
|
|
64
|
+
tokens.push({ prefix: seg.prefix, text: w, visLen: visibleWidth(w) });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// Greedy line-fill: accumulate tokens with single-space gaps until the
|
|
68
|
+
// next token would exceed maxWidth, then start a new line.
|
|
69
|
+
const lines = [];
|
|
70
|
+
let curLine = "";
|
|
71
|
+
let curLen = 0;
|
|
72
|
+
for (const tok of tokens) {
|
|
73
|
+
const gapLen = curLen > 0 ? 1 : 0; // one space between tokens
|
|
74
|
+
const addedLen = tok.visLen + gapLen;
|
|
75
|
+
if (curLen > 0 && curLen + addedLen > maxWidth) {
|
|
76
|
+
// Emit current line and start fresh
|
|
77
|
+
if (curLine.length > 0)
|
|
78
|
+
lines.push(curLine);
|
|
79
|
+
curLine = "";
|
|
80
|
+
curLen = 0;
|
|
81
|
+
}
|
|
82
|
+
if (curLen === 0) {
|
|
83
|
+
curLine = tok.prefix + tok.text;
|
|
84
|
+
curLen = tok.visLen;
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
curLine += " " + tok.text;
|
|
88
|
+
curLen += 1 + tok.visLen;
|
|
89
|
+
}
|
|
90
|
+
// If a single token is wider than maxWidth, hard-break via truncation.
|
|
91
|
+
if (curLen > maxWidth) {
|
|
92
|
+
const truncated = truncateToWidth(curLine, maxWidth);
|
|
93
|
+
lines.push(truncated);
|
|
94
|
+
curLine = "";
|
|
95
|
+
curLen = 0;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (curLen > 0) {
|
|
99
|
+
lines.push(curLine);
|
|
100
|
+
}
|
|
101
|
+
return lines.length > 0 ? lines : [line];
|
|
102
|
+
} // ── Refresh timer ───────────────────────────────────────────────────────────
|
|
103
|
+
const REFRESH_INTERVAL_MS = 1000;
|
|
104
|
+
// ── Controller ──────────────────────────────────────────────────────────────
|
|
105
|
+
export class DashboardController {
|
|
106
|
+
tree;
|
|
107
|
+
vm;
|
|
108
|
+
state;
|
|
109
|
+
onInvalidate;
|
|
110
|
+
refreshTimer;
|
|
111
|
+
_handlers;
|
|
112
|
+
constructor(tree, initialCursorId) {
|
|
113
|
+
this.tree = tree;
|
|
114
|
+
this.vm = buildViewModel(tree);
|
|
115
|
+
// Default cursor to the first active root, or empty string if tree is empty.
|
|
116
|
+
const firstVisible = this.vm.roots.length > 0 ? this.vm.roots[0] : "";
|
|
117
|
+
this.state = {
|
|
118
|
+
cursorId: initialCursorId ?? firstVisible,
|
|
119
|
+
expanded: new Set(),
|
|
120
|
+
focusPanel: "tree",
|
|
121
|
+
promptExpanded: false,
|
|
122
|
+
cancelTargetId: null,
|
|
123
|
+
detailScroll: 0,
|
|
124
|
+
};
|
|
125
|
+
this._handlers = [];
|
|
126
|
+
// V2 fix: subscriptions now owned by controller, not view.
|
|
127
|
+
// On every model event, rebuild VM then invalidate the view.
|
|
128
|
+
const onModelChange = () => {
|
|
129
|
+
this.rebuildViewModel();
|
|
130
|
+
this.onInvalidate?.();
|
|
131
|
+
};
|
|
132
|
+
const onTreeChange = (id) => {
|
|
133
|
+
this.rebuildViewModel();
|
|
134
|
+
this.autoExpandNewNode(id);
|
|
135
|
+
this.onInvalidate?.();
|
|
136
|
+
};
|
|
137
|
+
this.tree.on("change", onModelChange);
|
|
138
|
+
this.tree.on("tail", onModelChange);
|
|
139
|
+
this.tree.on("preview", onModelChange);
|
|
140
|
+
this.tree.on("tree", onTreeChange);
|
|
141
|
+
this._handlers = [
|
|
142
|
+
{ event: "change", handler: onModelChange },
|
|
143
|
+
{ event: "tail", handler: onModelChange },
|
|
144
|
+
{ event: "preview", handler: onModelChange },
|
|
145
|
+
{ event: "tree", handler: onTreeChange },
|
|
146
|
+
];
|
|
147
|
+
// V3 fix: timer now owned by controller, not view.
|
|
148
|
+
this.ensureRefreshTimer();
|
|
149
|
+
}
|
|
150
|
+
setOnInvalidate(cb) {
|
|
151
|
+
this.onInvalidate = cb;
|
|
152
|
+
}
|
|
153
|
+
// ── ViewModel projection ────────────────────────────────────────────────
|
|
154
|
+
/** Rebuild the ViewModel from the live model. Called on every model event. */
|
|
155
|
+
rebuildViewModel() {
|
|
156
|
+
this.vm = buildViewModel(this.tree);
|
|
157
|
+
}
|
|
158
|
+
/** Get a node from the current ViewModel. */
|
|
159
|
+
getNode(id) {
|
|
160
|
+
return this.vm.nodes.get(id);
|
|
161
|
+
}
|
|
162
|
+
/** Get a node's children from the current ViewModel. */
|
|
163
|
+
getChildren(id) {
|
|
164
|
+
const node = this.vm.nodes.get(id);
|
|
165
|
+
if (!node)
|
|
166
|
+
return [];
|
|
167
|
+
return node.children
|
|
168
|
+
.map((cid) => this.vm.nodes.get(cid))
|
|
169
|
+
.filter((n) => n !== undefined);
|
|
170
|
+
}
|
|
171
|
+
/** Get the progress (completed/total leaves) for a subtree. */
|
|
172
|
+
getSubtreeProgress(id) {
|
|
173
|
+
let completed = 0;
|
|
174
|
+
let total = 0;
|
|
175
|
+
const stack = [id];
|
|
176
|
+
while (stack.length > 0) {
|
|
177
|
+
const current = stack.pop();
|
|
178
|
+
const node = this.vm.nodes.get(current);
|
|
179
|
+
if (!node)
|
|
180
|
+
continue;
|
|
181
|
+
if (node.kind === "leaf") {
|
|
182
|
+
total++;
|
|
183
|
+
if (node.status === "completed")
|
|
184
|
+
completed++;
|
|
185
|
+
}
|
|
186
|
+
stack.push(...node.children);
|
|
187
|
+
}
|
|
188
|
+
return { completed, total };
|
|
189
|
+
}
|
|
190
|
+
/** Aggregate token usage across all active roots. */
|
|
191
|
+
getAggregateUsage() {
|
|
192
|
+
const agg = { input: 0, output: 0, cacheRead: 0 };
|
|
193
|
+
for (const rootId of this.vm.roots) {
|
|
194
|
+
const stack = [rootId];
|
|
195
|
+
while (stack.length > 0) {
|
|
196
|
+
const current = stack.pop();
|
|
197
|
+
const node = this.vm.nodes.get(current);
|
|
198
|
+
if (!node)
|
|
199
|
+
continue;
|
|
200
|
+
agg.input += node.usage.input;
|
|
201
|
+
agg.output += node.usage.output;
|
|
202
|
+
agg.cacheRead += node.usage.cacheRead;
|
|
203
|
+
stack.push(...node.children);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return agg;
|
|
207
|
+
}
|
|
208
|
+
/** Aggregate compression across all active roots. */
|
|
209
|
+
getAggregateCompression() {
|
|
210
|
+
const agg = { calls: 0, tokensSaved: 0 };
|
|
211
|
+
for (const rootId of this.vm.roots) {
|
|
212
|
+
const stack = [rootId];
|
|
213
|
+
while (stack.length > 0) {
|
|
214
|
+
const current = stack.pop();
|
|
215
|
+
const node = this.vm.nodes.get(current);
|
|
216
|
+
if (!node)
|
|
217
|
+
continue;
|
|
218
|
+
if (node.compression) {
|
|
219
|
+
agg.calls += node.compression.calls;
|
|
220
|
+
agg.tokensSaved += node.compression.tokensSaved;
|
|
221
|
+
}
|
|
222
|
+
stack.push(...node.children);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return agg;
|
|
226
|
+
}
|
|
227
|
+
/** Model/provider from the first running root, if any. */
|
|
228
|
+
getActiveModel() {
|
|
229
|
+
for (const rootId of this.vm.roots) {
|
|
230
|
+
const node = this.vm.nodes.get(rootId);
|
|
231
|
+
if (node && node.model) {
|
|
232
|
+
return { provider: node.provider, model: node.model };
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return undefined;
|
|
236
|
+
}
|
|
237
|
+
/** Check whether any active root is running or cancelling. */
|
|
238
|
+
hasRunningNodes() {
|
|
239
|
+
for (const rootId of this.vm.roots) {
|
|
240
|
+
const node = this.vm.nodes.get(rootId);
|
|
241
|
+
if (node && (node.status === "running" || node.status === "cancelling")) {
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
// ── Visible node list (DFS respecting expand state) ────────────────────
|
|
248
|
+
getVisibleNodes() {
|
|
249
|
+
const result = [];
|
|
250
|
+
const visit = (id) => {
|
|
251
|
+
const node = this.vm.nodes.get(id);
|
|
252
|
+
if (!node)
|
|
253
|
+
return;
|
|
254
|
+
result.push(id);
|
|
255
|
+
if (node.kind === "orchestrator" && this.state.expanded.has(id)) {
|
|
256
|
+
for (const childId of node.children) {
|
|
257
|
+
visit(childId);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
for (const rootId of this.vm.roots) {
|
|
262
|
+
visit(rootId);
|
|
263
|
+
}
|
|
264
|
+
return result;
|
|
265
|
+
}
|
|
266
|
+
// ── Input handling ──────────────────────────────────────────────────────
|
|
267
|
+
handleInput(data) {
|
|
268
|
+
// Cancel confirmation mode takes priority.
|
|
269
|
+
if (this.state.cancelTargetId) {
|
|
270
|
+
this.handleCancelConfirm(data);
|
|
271
|
+
this.onInvalidate?.();
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
switch (this.state.focusPanel) {
|
|
275
|
+
case "tree":
|
|
276
|
+
this.handleTreeInput(data);
|
|
277
|
+
break;
|
|
278
|
+
case "detail":
|
|
279
|
+
this.handleDetailInput(data);
|
|
280
|
+
break;
|
|
281
|
+
}
|
|
282
|
+
this.onInvalidate?.();
|
|
283
|
+
}
|
|
284
|
+
handleTreeInput(data) {
|
|
285
|
+
if (matchesKey(data, Key.up)) {
|
|
286
|
+
this.moveCursor(-1);
|
|
287
|
+
}
|
|
288
|
+
else if (matchesKey(data, Key.down)) {
|
|
289
|
+
this.moveCursor(1);
|
|
290
|
+
}
|
|
291
|
+
else if (matchesKey(data, Key.enter) || matchesKey(data, Key.right)) {
|
|
292
|
+
this.activateCursor();
|
|
293
|
+
}
|
|
294
|
+
else if (matchesKey(data, Key.left)) {
|
|
295
|
+
this.collapseCursor();
|
|
296
|
+
}
|
|
297
|
+
else if (data === "x") {
|
|
298
|
+
this.startCancel();
|
|
299
|
+
}
|
|
300
|
+
// ESC in tree panel is handled in handleInput above — it closes
|
|
301
|
+
// the overlay rather than navigating or setting a sentinel.
|
|
302
|
+
}
|
|
303
|
+
handleDetailInput(data) {
|
|
304
|
+
// ESC in detail panel is handled in handleInput above — it closes
|
|
305
|
+
// the overlay rather than just switching panels.
|
|
306
|
+
if (matchesKey(data, Key.left)) {
|
|
307
|
+
this.state.focusPanel = "tree";
|
|
308
|
+
this.state.detailScroll = 0;
|
|
309
|
+
}
|
|
310
|
+
else if (matchesKey(data, Key.down) || data === "j" || data === "J") {
|
|
311
|
+
this.state.detailScroll++;
|
|
312
|
+
}
|
|
313
|
+
else if (matchesKey(data, Key.up) || data === "k" || data === "K") {
|
|
314
|
+
this.state.detailScroll = Math.max(0, this.state.detailScroll - 1);
|
|
315
|
+
}
|
|
316
|
+
else if (matchesKey(data, Key.enter)) {
|
|
317
|
+
this.state.promptExpanded = !this.state.promptExpanded;
|
|
318
|
+
}
|
|
319
|
+
else if (data === "x") {
|
|
320
|
+
this.startCancel();
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
handleCancelConfirm(data) {
|
|
324
|
+
if (data === "y" || matchesKey(data, Key.enter)) {
|
|
325
|
+
if (this.state.cancelTargetId) {
|
|
326
|
+
this.cancelNodeAndSessions(this.state.cancelTargetId);
|
|
327
|
+
}
|
|
328
|
+
this.state.cancelTargetId = null;
|
|
329
|
+
}
|
|
330
|
+
else if (data === "n") {
|
|
331
|
+
this.state.cancelTargetId = null;
|
|
332
|
+
}
|
|
333
|
+
// All other keys consumed silently in cancel mode. ESC is handled
|
|
334
|
+
// by the view's handleInput (closes the overlay entirely).
|
|
335
|
+
}
|
|
336
|
+
/** Cancel the node in the OrchestratorTree for immediate visual
|
|
337
|
+
* feedback, and propagate cancellation through SessionRegistry
|
|
338
|
+
* for actual pipeline abort. Reads from VM; writes to model. */
|
|
339
|
+
cancelNodeAndSessions(nodeId) {
|
|
340
|
+
// Mutation: mark node as "cancelling" in the model for immediate
|
|
341
|
+
// visual feedback. The model emits "change", which triggers a VM
|
|
342
|
+
// rebuild via the controller's subscription.
|
|
343
|
+
this.tree.requestCancel(nodeId);
|
|
344
|
+
const registry = getSessionRegistry();
|
|
345
|
+
// Walk up the tree to find the session (task-level) node in the registry.
|
|
346
|
+
// Reads from the VM (pre-rebuild stale state is fine — parentId doesn't
|
|
347
|
+
// change on cancel, and we re-read from VM).
|
|
348
|
+
let currentId = nodeId;
|
|
349
|
+
while (currentId) {
|
|
350
|
+
const session = registry.getSession(currentId);
|
|
351
|
+
if (session && (session.status === "running" || session.status === "cancelling")) {
|
|
352
|
+
registry.requestCancel(currentId);
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
const node = this.vm.nodes.get(currentId);
|
|
356
|
+
currentId = node?.parentId ?? null;
|
|
357
|
+
}
|
|
358
|
+
// No session found for this node or ancestors. If this is a
|
|
359
|
+
// sprint-level orchestrator, cancel all running child sessions.
|
|
360
|
+
const node = this.vm.nodes.get(nodeId);
|
|
361
|
+
if (node?.kind === "orchestrator") {
|
|
362
|
+
for (const childId of node.children) {
|
|
363
|
+
const session = registry.getSession(childId);
|
|
364
|
+
if (session && (session.status === "running" || session.status === "cancelling")) {
|
|
365
|
+
registry.requestCancel(childId);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
moveCursor(delta) {
|
|
371
|
+
const visible = this.getVisibleNodes();
|
|
372
|
+
if (visible.length === 0)
|
|
373
|
+
return;
|
|
374
|
+
const idx = visible.indexOf(this.state.cursorId);
|
|
375
|
+
const next = Math.max(0, Math.min(visible.length - 1, idx + delta));
|
|
376
|
+
this.state.cursorId = visible[next];
|
|
377
|
+
// Auto-expand ancestors so the cursor is always visible.
|
|
378
|
+
this.ensureAncestorsExpanded(this.state.cursorId);
|
|
379
|
+
}
|
|
380
|
+
activateCursor() {
|
|
381
|
+
const node = this.vm.nodes.get(this.state.cursorId);
|
|
382
|
+
if (!node)
|
|
383
|
+
return;
|
|
384
|
+
if (node.kind === "orchestrator") {
|
|
385
|
+
this.toggleExpand(node.id);
|
|
386
|
+
}
|
|
387
|
+
else {
|
|
388
|
+
this.state.focusPanel = "detail";
|
|
389
|
+
this.state.detailScroll = 0;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
toggleExpand(id) {
|
|
393
|
+
if (this.state.expanded.has(id)) {
|
|
394
|
+
this.state.expanded.delete(id);
|
|
395
|
+
}
|
|
396
|
+
else {
|
|
397
|
+
this.state.expanded.add(id);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
collapseCursor() {
|
|
401
|
+
const node = this.vm.nodes.get(this.state.cursorId);
|
|
402
|
+
if (!node)
|
|
403
|
+
return;
|
|
404
|
+
if (node.kind === "orchestrator" && this.state.expanded.has(node.id)) {
|
|
405
|
+
this.state.expanded.delete(node.id);
|
|
406
|
+
}
|
|
407
|
+
else if (node.parentId) {
|
|
408
|
+
// Move cursor to parent.
|
|
409
|
+
this.state.cursorId = node.parentId;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
startCancel() {
|
|
413
|
+
const node = this.vm.nodes.get(this.state.cursorId);
|
|
414
|
+
if (!node)
|
|
415
|
+
return;
|
|
416
|
+
// Can cancel any node under a running subtree — find the nearest
|
|
417
|
+
// cancellable ancestor (running or cancelling).
|
|
418
|
+
let target = null;
|
|
419
|
+
let current = node;
|
|
420
|
+
while (current) {
|
|
421
|
+
if (current.status === "running" || current.status === "cancelling") {
|
|
422
|
+
target = current;
|
|
423
|
+
break;
|
|
424
|
+
}
|
|
425
|
+
current = current.parentId ? this.vm.nodes.get(current.parentId) : undefined;
|
|
426
|
+
}
|
|
427
|
+
if (target) {
|
|
428
|
+
this.state.cancelTargetId = target.id;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
ensureAncestorsExpanded(id) {
|
|
432
|
+
let currentId = id;
|
|
433
|
+
while (currentId) {
|
|
434
|
+
const node = this.vm.nodes.get(currentId);
|
|
435
|
+
if (!node)
|
|
436
|
+
break;
|
|
437
|
+
if (node.kind === "orchestrator") {
|
|
438
|
+
this.state.expanded.add(currentId);
|
|
439
|
+
}
|
|
440
|
+
currentId = node.parentId;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
// ── Auto-expand: only expand the root and the direct parent of a new
|
|
444
|
+
// leaf node (phase). Intermediate orchestrator nodes (tasks) are NOT
|
|
445
|
+
// auto-expanded — the user expands them manually. This prevents the tree
|
|
446
|
+
// from exploding into a fully-expanded state on every new node.
|
|
447
|
+
autoExpandNewNode(id) {
|
|
448
|
+
const node = this.vm.nodes.get(id);
|
|
449
|
+
if (!node)
|
|
450
|
+
return;
|
|
451
|
+
// Auto-expand the root level only: if this node IS a root (no parent),
|
|
452
|
+
// expand it so it's visible. If this node is a leaf (phase), expand
|
|
453
|
+
// only its direct parent (the task). Intermediate orchestrators
|
|
454
|
+
// (tasks under a sprint) are NOT auto-expanded.
|
|
455
|
+
if (node.kind === "leaf" && node.parentId) {
|
|
456
|
+
// Leaf node (phase): expand its parent so the phase is visible,
|
|
457
|
+
// but only if the parent is already visible (i.e., the user
|
|
458
|
+
// has expanded the root).
|
|
459
|
+
const parent = this.vm.nodes.get(node.parentId);
|
|
460
|
+
if (parent && this.state.expanded.has(node.parentId) && parent.kind === "orchestrator") {
|
|
461
|
+
// Parent is visible and is an orchestrator — leaf will show.
|
|
462
|
+
// No need to expand further.
|
|
463
|
+
}
|
|
464
|
+
// Also ensure ancestors of the leaf are expanded so it's visible.
|
|
465
|
+
this.ensureAncestorsExpanded(node.id);
|
|
466
|
+
}
|
|
467
|
+
else if (!node.parentId) {
|
|
468
|
+
// Root node (sprint-level orchestrator): always expand.
|
|
469
|
+
this.state.expanded.add(id);
|
|
470
|
+
}
|
|
471
|
+
// Default cursor to the newest running node if no cursor set.
|
|
472
|
+
if (!this.state.cursorId || this.state.cursorId === "") {
|
|
473
|
+
this.state.cursorId = id;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
// ── Refresh timer (V3 fix: owned by controller, not view) ──────────────
|
|
477
|
+
ensureRefreshTimer() {
|
|
478
|
+
if (this.refreshTimer)
|
|
479
|
+
return;
|
|
480
|
+
this.refreshTimer = setInterval(() => {
|
|
481
|
+
if (!this.hasRunningNodes()) {
|
|
482
|
+
// One last render to settle final frame, then stop timer.
|
|
483
|
+
this.onInvalidate?.();
|
|
484
|
+
this.stopRefreshTimer();
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
this.onInvalidate?.();
|
|
488
|
+
}, REFRESH_INTERVAL_MS);
|
|
489
|
+
}
|
|
490
|
+
stopRefreshTimer() {
|
|
491
|
+
if (this.refreshTimer) {
|
|
492
|
+
clearInterval(this.refreshTimer);
|
|
493
|
+
this.refreshTimer = undefined;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
// ── Cleanup ────────────────────────────────────────────────────────────
|
|
497
|
+
dispose() {
|
|
498
|
+
this.stopRefreshTimer();
|
|
499
|
+
for (const { event, handler } of this._handlers) {
|
|
500
|
+
this.tree.off(event, handler);
|
|
501
|
+
}
|
|
502
|
+
this._handlers = [];
|
|
503
|
+
}
|
|
504
|
+
getState() {
|
|
505
|
+
return this.state;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
// ── View ────────────────────────────────────────────────────────────────────
|
|
509
|
+
export class DashboardComponent {
|
|
510
|
+
controller;
|
|
511
|
+
theme;
|
|
512
|
+
tui;
|
|
513
|
+
done;
|
|
514
|
+
constructor(controller, tui, theme, done) {
|
|
515
|
+
this.controller = controller;
|
|
516
|
+
this.tui = tui;
|
|
517
|
+
this.theme = theme;
|
|
518
|
+
this.done = done;
|
|
519
|
+
// V2 fix: subscriptions are now owned by the controller.
|
|
520
|
+
// The view only registers an invalidation callback for re-rendering.
|
|
521
|
+
this.controller.setOnInvalidate(() => this.tui.requestRender());
|
|
522
|
+
}
|
|
523
|
+
// ── Component interface ──────────────────────────────────────────────────
|
|
524
|
+
render(width) {
|
|
525
|
+
// Layout: left panel ~20% (min 22), right panel fills rest, borders.
|
|
526
|
+
const leftWidth = Math.max(22, Math.floor(width * 0.20));
|
|
527
|
+
const separatorWidth = 1;
|
|
528
|
+
const rightWidth = Math.max(20, width - leftWidth - separatorWidth - 3);
|
|
529
|
+
const contentWidth = width - 2; // minus left and right border chars
|
|
530
|
+
const visible = this.controller.getVisibleNodes();
|
|
531
|
+
const state = this.controller.getState();
|
|
532
|
+
const selectedNode = this.controller.getNode(state.cursorId);
|
|
533
|
+
// ── Left panel: tree browser ───────────────────────────────────
|
|
534
|
+
const leftLines = this.renderTreePanel(visible, state, leftWidth);
|
|
535
|
+
// ── Right panel: detail ─────────────────────────────────────────
|
|
536
|
+
const rightLines = selectedNode
|
|
537
|
+
? this.renderDetailPanel(selectedNode, rightWidth)
|
|
538
|
+
: [this.theme.fg("dim", "Select a node in the tree")];
|
|
539
|
+
// ── Compose ─────────────────────────────────────────────────────
|
|
540
|
+
const termHeight = this.tui.terminal.rows;
|
|
541
|
+
// Deduct 3 lines for top border, bottom border, and hints footer
|
|
542
|
+
const contentHeight = Math.max(8, termHeight - 3);
|
|
543
|
+
// Compute detail panel scroll with auto-scrolling fallback for tree focus
|
|
544
|
+
let activeScroll = state.detailScroll;
|
|
545
|
+
if (state.focusPanel === "tree") {
|
|
546
|
+
// Auto-scroll to the bottom so live dispatches are visible immediately
|
|
547
|
+
activeScroll = Math.max(0, rightLines.length - contentHeight);
|
|
548
|
+
}
|
|
549
|
+
else {
|
|
550
|
+
// Clamp user manual scroll within valid bounds
|
|
551
|
+
const maxScroll = Math.max(0, rightLines.length - contentHeight);
|
|
552
|
+
if (activeScroll > maxScroll) {
|
|
553
|
+
activeScroll = maxScroll;
|
|
554
|
+
state.detailScroll = maxScroll; // sync controller state
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
const paddedLeftLines = [];
|
|
558
|
+
for (let i = 0; i < contentHeight; i++) {
|
|
559
|
+
paddedLeftLines.push(leftLines[i] ?? "");
|
|
560
|
+
}
|
|
561
|
+
const paddedRightLines = [];
|
|
562
|
+
for (let i = 0; i < contentHeight; i++) {
|
|
563
|
+
paddedRightLines.push(rightLines[i + activeScroll] ?? "");
|
|
564
|
+
}
|
|
565
|
+
const lines = [];
|
|
566
|
+
const border = (s) => this.theme.fg("border", s);
|
|
567
|
+
const accent = (s) => this.theme.fg("accent", s);
|
|
568
|
+
// ── Header ──────────────────────────────────────────────────────
|
|
569
|
+
const headerText = ` Orchestrator Dashboard `;
|
|
570
|
+
const headerPad = contentWidth - visibleWidth(border("")) - visibleWidth(accent(headerText));
|
|
571
|
+
lines.push(border("╭") +
|
|
572
|
+
accent(headerText) +
|
|
573
|
+
border("─".repeat(Math.max(0, headerPad))) +
|
|
574
|
+
border("╮"));
|
|
575
|
+
for (let i = 0; i < contentHeight; i++) {
|
|
576
|
+
const left = truncateToWidth(paddedLeftLines[i], leftWidth);
|
|
577
|
+
const right = truncateToWidth(paddedRightLines[i], rightWidth);
|
|
578
|
+
const lPad = leftWidth - visibleWidth(left);
|
|
579
|
+
const rPad = rightWidth - visibleWidth(right);
|
|
580
|
+
lines.push(border("│") +
|
|
581
|
+
left +
|
|
582
|
+
" ".repeat(Math.max(0, lPad)) +
|
|
583
|
+
border("│") +
|
|
584
|
+
" " +
|
|
585
|
+
right +
|
|
586
|
+
" ".repeat(Math.max(0, rPad)) +
|
|
587
|
+
border("│"));
|
|
588
|
+
}
|
|
589
|
+
// ── Footer: bottom border + key hints + model/token meter ────────
|
|
590
|
+
const dim = (s) => this.theme.fg("dim", s);
|
|
591
|
+
const hintsBase = state.cancelTargetId
|
|
592
|
+
? " y confirm · n dismiss · esc close"
|
|
593
|
+
: " ↑↓ nav · → expand · ← back · ⏎ focus · x cancel · esc close";
|
|
594
|
+
lines.push(border("╰") + border("─".repeat(contentWidth)) + border("╯"));
|
|
595
|
+
// Aggregate model + token footer (mirrors ViewportFooterComponent).
|
|
596
|
+
const aggUsage = this.controller.getAggregateUsage();
|
|
597
|
+
const aggCompression = this.controller.getAggregateCompression();
|
|
598
|
+
const activeModel = this.controller.getActiveModel();
|
|
599
|
+
const meter = fmtTokenMeter(aggUsage);
|
|
600
|
+
const compSuffix = aggCompression.tokensSaved > 0 ? ` ⇌${aggCompression.tokensSaved}t` : "";
|
|
601
|
+
const modelLabel = activeModel?.provider && activeModel?.model
|
|
602
|
+
? `${activeModel.provider} ${activeModel.model}`
|
|
603
|
+
: (activeModel?.provider ?? activeModel?.model ?? "");
|
|
604
|
+
let footerLine = "";
|
|
605
|
+
if (modelLabel && meter) {
|
|
606
|
+
footerLine = `${hintsBase} ${dim(modelLabel)} Σ ${meter}${compSuffix}`;
|
|
607
|
+
}
|
|
608
|
+
else if (meter) {
|
|
609
|
+
footerLine = `${hintsBase} Σ ${meter}${compSuffix}`;
|
|
610
|
+
}
|
|
611
|
+
else if (modelLabel) {
|
|
612
|
+
footerLine = `${hintsBase} ${dim(modelLabel)}`;
|
|
613
|
+
}
|
|
614
|
+
else {
|
|
615
|
+
footerLine = hintsBase;
|
|
616
|
+
}
|
|
617
|
+
lines.push(dim(truncateToWidth(footerLine, width)));
|
|
618
|
+
// ── Overlay cancel confirmation on top of dashboard content ────
|
|
619
|
+
if (state.cancelTargetId) {
|
|
620
|
+
return this.overlayCancelConfirm(lines, width, state.cancelTargetId);
|
|
621
|
+
}
|
|
622
|
+
return lines;
|
|
623
|
+
}
|
|
624
|
+
handleInput(data) {
|
|
625
|
+
// ESC always closes the overlay, regardless of focus panel or
|
|
626
|
+
// cancel-confirmation state. This takes priority over the controller's
|
|
627
|
+
// ESC handling (which only dismissed cancel-confirm but didn't close).
|
|
628
|
+
if (matchesKey(data, Key.escape)) {
|
|
629
|
+
this.dispose();
|
|
630
|
+
this.done(null);
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
this.controller.handleInput(data);
|
|
634
|
+
}
|
|
635
|
+
invalidate() {
|
|
636
|
+
// No cached render state — pure function of VM + controller state.
|
|
637
|
+
}
|
|
638
|
+
dispose() {
|
|
639
|
+
this.controller.dispose();
|
|
640
|
+
}
|
|
641
|
+
// ── Tree panel renderer ─────────────────────────────────────────────────
|
|
642
|
+
renderTreePanel(visibleIds, state, width) {
|
|
643
|
+
if (visibleIds.length === 0) {
|
|
644
|
+
return [this.theme.fg("dim", "No session running")];
|
|
645
|
+
}
|
|
646
|
+
const lines = [];
|
|
647
|
+
// Header
|
|
648
|
+
lines.push(this.theme.bold(this.theme.fg("accent", " Phases")));
|
|
649
|
+
for (const id of visibleIds) {
|
|
650
|
+
const node = this.controller.getNode(id);
|
|
651
|
+
if (!node)
|
|
652
|
+
continue;
|
|
653
|
+
const isCursor = id === state.cursorId;
|
|
654
|
+
const depth = node.depth;
|
|
655
|
+
const indent = " ".repeat(depth * 2);
|
|
656
|
+
// Status glyph
|
|
657
|
+
const glyph = this.nodeGlyph(node);
|
|
658
|
+
// Progress label for orchestrators
|
|
659
|
+
let label = node.label;
|
|
660
|
+
if (node.kind === "orchestrator") {
|
|
661
|
+
const prog = this.controller.getSubtreeProgress(id);
|
|
662
|
+
label += ` ${prog.completed}/${prog.total}`;
|
|
663
|
+
}
|
|
664
|
+
// Expand indicator
|
|
665
|
+
const expandIndicator = node.kind === "orchestrator"
|
|
666
|
+
? state.expanded.has(id)
|
|
667
|
+
? " "
|
|
668
|
+
: "▸"
|
|
669
|
+
: " ";
|
|
670
|
+
// Combine
|
|
671
|
+
const prefix = `${indent}${isCursor ? "❯" : " "} ${glyph} ${expandIndicator} `;
|
|
672
|
+
const styled = isCursor
|
|
673
|
+
? this.theme.bold(this.theme.fg("accent", `${prefix}${label}`))
|
|
674
|
+
: `${prefix}${label}`;
|
|
675
|
+
lines.push(truncateToWidth(styled, width));
|
|
676
|
+
}
|
|
677
|
+
return lines;
|
|
678
|
+
}
|
|
679
|
+
nodeGlyph(node) {
|
|
680
|
+
switch (node.status) {
|
|
681
|
+
case "completed":
|
|
682
|
+
return this.theme.fg("success", "✔");
|
|
683
|
+
case "running":
|
|
684
|
+
return this.theme.fg("accent", "●");
|
|
685
|
+
case "cancelling":
|
|
686
|
+
return this.theme.fg("warning", "⏳");
|
|
687
|
+
case "cancelled":
|
|
688
|
+
return this.theme.fg("muted", "⊘");
|
|
689
|
+
case "failed":
|
|
690
|
+
return this.theme.fg("error", "✗");
|
|
691
|
+
case "escalated":
|
|
692
|
+
return this.theme.fg("error", "▲");
|
|
693
|
+
case "pending":
|
|
694
|
+
default:
|
|
695
|
+
return this.theme.fg("dim", "○");
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
// ── Detail panel renderer ────────────────────────────────────────────────
|
|
699
|
+
renderDetailPanel(node, width) {
|
|
700
|
+
const lines = [];
|
|
701
|
+
const dim = (s) => this.theme.fg("dim", s);
|
|
702
|
+
const accent = (s) => this.theme.fg("accent", s);
|
|
703
|
+
const bold = (s) => this.theme.bold(s);
|
|
704
|
+
const success = (s) => this.theme.fg("success", s);
|
|
705
|
+
const warn = (s) => this.theme.fg("warning", s);
|
|
706
|
+
// ── Header: label + status (wrap long model strings) ──────────────────
|
|
707
|
+
const statusLabel = this.statusLabel(node.status);
|
|
708
|
+
const modelPart = node.model ? ` · ${node.provider ?? ""} ${node.model}` : "";
|
|
709
|
+
lines.push(...wrapLine(`${this.nodeGlyph(node)} ${bold(statusLabel)}${dim(modelPart)}`, width));
|
|
710
|
+
// ── Metrics line ────────────────────────────────────────────────
|
|
711
|
+
const metrics = this.formatMetrics(node);
|
|
712
|
+
if (metrics)
|
|
713
|
+
lines.push(...wrapLine(dim(metrics), width));
|
|
714
|
+
lines.push("");
|
|
715
|
+
// ── Orchestrator node: list children ─────────────────────────────
|
|
716
|
+
if (node.kind === "orchestrator" && node.children.length > 0) {
|
|
717
|
+
const children = this.controller.getChildren(node.id);
|
|
718
|
+
lines.push(...wrapLine(dim(bold(`Agents · ${children.length}`)), width));
|
|
719
|
+
for (const child of children) {
|
|
720
|
+
const cglyph = this.nodeGlyph(child);
|
|
721
|
+
const cmodel = child.model ? ` ${child.provider ?? ""} ${child.model}` : "";
|
|
722
|
+
const cmetrics = this.formatMetrics(child);
|
|
723
|
+
const cmetricsPart = cmetrics ? ` ${cmetrics}` : "";
|
|
724
|
+
lines.push(...wrapLine(` ${cglyph} ${child.label}${dim(cmodel)}${dim(cmetricsPart)}`, width));
|
|
725
|
+
}
|
|
726
|
+
lines.push("");
|
|
727
|
+
}
|
|
728
|
+
// ── Prompt preview (expandable) ─────────────────────────────────
|
|
729
|
+
if (node.promptPreview) {
|
|
730
|
+
const expandIcon = this.controller.getState().promptExpanded ? "▼" : "▶";
|
|
731
|
+
const lineCount = node.promptPreview.split("\n").length;
|
|
732
|
+
lines.push(...wrapLine(dim(`${expandIcon} Prompt · ${lineCount} lines · ⏎ expand`), width));
|
|
733
|
+
if (this.controller.getState().promptExpanded) {
|
|
734
|
+
for (const pline of node.promptPreview.split("\n").slice(0, 20)) {
|
|
735
|
+
// Indent wrapped prompt lines by 2 spaces
|
|
736
|
+
const wrapped = wrapLine(pline, Math.max(0, width - 4));
|
|
737
|
+
for (let i = 0; i < wrapped.length; i++) {
|
|
738
|
+
lines.push(dim(i === 0 ? ` ${wrapped[i]}` : ` ${wrapped[i]}`));
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
if (lineCount > 20) {
|
|
742
|
+
lines.push(dim(` … ${lineCount - 20} more lines`));
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
lines.push("");
|
|
746
|
+
}
|
|
747
|
+
// ── Activity: running full log of all turns ──────────────────────
|
|
748
|
+
if (node.kind === "leaf" && node.tailBuffer.length > 0) {
|
|
749
|
+
const total = node.tailBuffer.length;
|
|
750
|
+
lines.push(...wrapLine(dim(`Activity · ${total} log line${total === 1 ? "" : "s"}`), width));
|
|
751
|
+
for (const tline of node.tailBuffer) {
|
|
752
|
+
const painted = paintTailLine(tline, this.theme);
|
|
753
|
+
lines.push(...wrapLine(painted, width));
|
|
754
|
+
}
|
|
755
|
+
lines.push("");
|
|
756
|
+
}
|
|
757
|
+
// ── Outcome ─────────────────────────────────────────────────────
|
|
758
|
+
if (node.outcomePreview) {
|
|
759
|
+
lines.push(dim("Outcome"));
|
|
760
|
+
for (const oline of node.outcomePreview.split("\n").slice(0, 8)) {
|
|
761
|
+
const wrapped = wrapLine(oline, Math.max(0, width - 4));
|
|
762
|
+
for (const wl of wrapped) {
|
|
763
|
+
lines.push(` ${wl}`);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
const lineCount = node.outcomePreview.split("\n").length;
|
|
767
|
+
if (lineCount > 8) {
|
|
768
|
+
lines.push(dim(` … ${lineCount - 8} more lines`));
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
return lines;
|
|
772
|
+
}
|
|
773
|
+
/** Render a cancel-confirmation dialog overlaid on the dashboard.
|
|
774
|
+
* The dashboard content stays visible above and below the dialog.
|
|
775
|
+
* The outer frame borders (│ on each side) are preserved across the
|
|
776
|
+
* dialog rows so the two-panel layout doesn't visually collapse.
|
|
777
|
+
* The dialog is centered and accepts y/Enter to confirm or n/Esc to
|
|
778
|
+
* dismiss. The overlay stays until the user decides — Esc only
|
|
779
|
+
* dismisses the cancel prompt, not the dashboard.
|
|
780
|
+
*/
|
|
781
|
+
overlayCancelConfirm(baseLines, width, targetId) {
|
|
782
|
+
const node = this.controller.getNode(targetId);
|
|
783
|
+
const label = node?.label ?? targetId;
|
|
784
|
+
const dim = (s) => this.theme.fg("dim", s);
|
|
785
|
+
const warn = (s) => this.theme.fg("warning", s);
|
|
786
|
+
const bold = (s) => this.theme.bold(s);
|
|
787
|
+
const brd = (s) => this.theme.fg("border", s);
|
|
788
|
+
// Content width is the full width minus the two outer │ borders.
|
|
789
|
+
const contentWidth = width - 2;
|
|
790
|
+
// Build the dialog box (narrower than the full content area so it
|
|
791
|
+
// floats with padding on each side).
|
|
792
|
+
const dialogW = Math.min(contentWidth, 60);
|
|
793
|
+
const diagLines = [];
|
|
794
|
+
const prompt = warn(`⚠ Cancel ${bold(label)}?`);
|
|
795
|
+
const actions = dim("y confirm · n dismiss · esc close");
|
|
796
|
+
const promptW = visibleWidth(prompt);
|
|
797
|
+
const actionsW = visibleWidth(actions);
|
|
798
|
+
diagLines.push(brd("╭") + brd("─".repeat(dialogW - 2)) + brd("╮"));
|
|
799
|
+
diagLines.push(brd("│") + " ".repeat(dialogW - 2) + brd("│"));
|
|
800
|
+
{
|
|
801
|
+
// Place prompt and actions side by side when they fit; stack otherwise.
|
|
802
|
+
const sideBySide = promptW + actionsW + 4 <= dialogW - 2;
|
|
803
|
+
if (sideBySide) {
|
|
804
|
+
const contentGap = Math.max(1, dialogW - 4 - promptW - actionsW);
|
|
805
|
+
const contentLine = brd("│") + " " + prompt + " ".repeat(contentGap) + actions + " " + brd("│");
|
|
806
|
+
if (visibleWidth(contentLine) < dialogW) {
|
|
807
|
+
// Pad content line to full dialog width.
|
|
808
|
+
const pad = dialogW - 2 - visibleWidth(contentLine.slice(1, -1));
|
|
809
|
+
diagLines.push(brd("│") +
|
|
810
|
+
" " + prompt + " ".repeat(contentGap + Math.max(0, pad)) + actions +
|
|
811
|
+
" " + brd("│"));
|
|
812
|
+
}
|
|
813
|
+
else {
|
|
814
|
+
diagLines.push(contentLine);
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
else {
|
|
818
|
+
// Stacked: prompt on one line, actions on the next.
|
|
819
|
+
const promptPad = Math.max(0, dialogW - 2 - 1 - promptW - 1);
|
|
820
|
+
diagLines.push(brd("│") + " " + prompt + " ".repeat(promptPad) + brd("│"));
|
|
821
|
+
const actionsPad = Math.max(0, dialogW - 2 - 1 - actionsW - 1);
|
|
822
|
+
diagLines.push(brd("│") + " " + actions + " ".repeat(actionsPad) + brd("│"));
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
diagLines.push(brd("│") + " ".repeat(dialogW - 2) + brd("│"));
|
|
826
|
+
diagLines.push(brd("╰") + brd("─".repeat(dialogW - 2)) + brd("╯"));
|
|
827
|
+
// Center the dialog vertically within the content rows and
|
|
828
|
+
// horizontally within the content area, preserving the outer │
|
|
829
|
+
// borders on every row so the frame stays intact.
|
|
830
|
+
const totalLines = baseLines.length;
|
|
831
|
+
const diagH = diagLines.length;
|
|
832
|
+
const startRow = Math.max(0, Math.floor((totalLines - diagH) / 2));
|
|
833
|
+
const leftPad = Math.max(0, Math.floor((contentWidth - dialogW) / 2));
|
|
834
|
+
const result = [...baseLines];
|
|
835
|
+
for (let i = 0; i < diagH && startRow + i < result.length; i++) {
|
|
836
|
+
const diagLine = diagLines[i];
|
|
837
|
+
const diagVisW = visibleWidth(diagLine);
|
|
838
|
+
const rightPad = Math.max(0, contentWidth - leftPad - diagVisW);
|
|
839
|
+
// Preserve the outer │ borders: left border + centered dialog + right border.
|
|
840
|
+
result[startRow + i] =
|
|
841
|
+
brd("│") + " ".repeat(leftPad) + diagLine + " ".repeat(rightPad) + brd("│");
|
|
842
|
+
}
|
|
843
|
+
return result;
|
|
844
|
+
}
|
|
845
|
+
statusLabel(status) {
|
|
846
|
+
switch (status) {
|
|
847
|
+
case "completed":
|
|
848
|
+
return "Completed";
|
|
849
|
+
case "running":
|
|
850
|
+
return "Running";
|
|
851
|
+
case "cancelling":
|
|
852
|
+
return "Cancelling…";
|
|
853
|
+
case "cancelled":
|
|
854
|
+
return "Cancelled";
|
|
855
|
+
case "failed":
|
|
856
|
+
return "Failed";
|
|
857
|
+
case "escalated":
|
|
858
|
+
return "Escalated";
|
|
859
|
+
case "pending":
|
|
860
|
+
return "Pending";
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
formatMetrics(node) {
|
|
864
|
+
const parts = [];
|
|
865
|
+
if (node.usage.input || node.usage.output || node.usage.cacheRead) {
|
|
866
|
+
parts.push(fmtTokenMeter(node.usage));
|
|
867
|
+
}
|
|
868
|
+
if (node.metrics.toolCount) {
|
|
869
|
+
parts.push(`${node.metrics.toolCount} tool${node.metrics.toolCount === 1 ? "" : "s"}`);
|
|
870
|
+
}
|
|
871
|
+
if (node.metrics.errCount) {
|
|
872
|
+
parts.push(`${node.metrics.errCount} err`);
|
|
873
|
+
}
|
|
874
|
+
if (node.startedAt) {
|
|
875
|
+
const end = node.endedAt ?? Date.now();
|
|
876
|
+
const secs = Math.floor((end - node.startedAt) / 1000);
|
|
877
|
+
parts.push(secs < 60 ? `${secs}s` : `${Math.floor(secs / 60)}m${String(secs % 60).padStart(2, "0")}s`);
|
|
878
|
+
}
|
|
879
|
+
return parts.join(" · ");
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
//# sourceMappingURL=component.js.map
|