@bastani/atomic 0.8.13 → 0.8.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 +23 -0
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/host-html-template.ts +1 -1
- package/dist/builtin/mcp/init.ts +15 -2
- package/dist/builtin/mcp/mcp-callback-server.ts +10 -9
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/mcp/ui-session.ts +9 -6
- package/dist/builtin/subagents/CHANGELOG.md +8 -1
- package/dist/builtin/subagents/README.md +39 -32
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/skills/subagent/SKILL.md +11 -11
- package/dist/builtin/subagents/src/agents/agent-management.ts +6 -1
- package/dist/builtin/subagents/src/agents/agent-serializer.ts +2 -0
- package/dist/builtin/subagents/src/agents/agents.ts +44 -19
- package/dist/builtin/subagents/src/extension/config.ts +16 -0
- package/dist/builtin/subagents/src/extension/fanout-child.ts +246 -0
- package/dist/builtin/subagents/src/extension/index.ts +466 -603
- package/dist/builtin/subagents/src/intercom/intercom-bridge.ts +6 -4
- package/dist/builtin/subagents/src/intercom/result-intercom.ts +109 -1
- package/dist/builtin/subagents/src/runs/background/async-execution.ts +124 -19
- package/dist/builtin/subagents/src/runs/background/async-job-tracker.ts +41 -6
- package/dist/builtin/subagents/src/runs/background/async-resume.ts +28 -15
- package/dist/builtin/subagents/src/runs/background/async-status.ts +60 -30
- package/dist/builtin/subagents/src/runs/background/result-watcher.ts +111 -54
- package/dist/builtin/subagents/src/runs/background/run-id-resolver.ts +83 -0
- package/dist/builtin/subagents/src/runs/background/run-status.ts +79 -3
- package/dist/builtin/subagents/src/runs/background/stale-run-reconciler.ts +46 -1
- package/dist/builtin/subagents/src/runs/background/subagent-runner.ts +66 -14
- package/dist/builtin/subagents/src/runs/foreground/chain-execution.ts +10 -3
- package/dist/builtin/subagents/src/runs/foreground/execution.ts +14 -2
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor.ts +320 -23
- package/dist/builtin/subagents/src/runs/shared/completion-guard.ts +23 -1
- package/dist/builtin/subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +369 -0
- package/dist/builtin/subagents/src/runs/shared/nested-events.ts +935 -0
- package/dist/builtin/subagents/src/runs/shared/nested-path.ts +52 -0
- package/dist/builtin/subagents/src/runs/shared/nested-render.ts +115 -0
- package/dist/builtin/subagents/src/runs/shared/parallel-utils.ts +1 -0
- package/dist/builtin/subagents/src/runs/shared/pi-args.ts +82 -9
- package/dist/builtin/subagents/src/runs/shared/pi-spawn.ts +1 -1
- package/dist/builtin/subagents/src/runs/shared/single-output.ts +12 -2
- package/dist/builtin/subagents/src/runs/shared/subagent-prompt-runtime.ts +32 -10
- package/dist/builtin/subagents/src/runs/shared/worktree.ts +3 -2
- package/dist/builtin/subagents/src/shared/artifacts.ts +0 -1
- package/dist/builtin/subagents/src/shared/types.ts +96 -1
- package/dist/builtin/subagents/src/shared/utils.ts +10 -2
- package/dist/builtin/subagents/src/slash/slash-commands.ts +468 -625
- package/dist/builtin/subagents/src/tui/render.ts +1227 -2093
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +24 -0
- package/dist/builtin/workflows/README.md +28 -11
- package/dist/builtin/workflows/builtin/deep-research-codebase.ts +323 -40
- package/dist/builtin/workflows/builtin/ralph.ts +362 -176
- package/dist/builtin/workflows/package.json +2 -5
- package/dist/builtin/workflows/skills/research-codebase/SKILL.md +1 -1
- package/dist/builtin/workflows/skills/skill-creator/LICENSE.txt +202 -0
- package/dist/builtin/workflows/skills/skill-creator/SKILL.md +489 -0
- package/dist/builtin/workflows/skills/skill-creator/agents/analyzer.md +274 -0
- package/dist/builtin/workflows/skills/skill-creator/agents/comparator.md +202 -0
- package/dist/builtin/workflows/skills/skill-creator/agents/grader.md +223 -0
- package/dist/builtin/workflows/skills/skill-creator/assets/eval_review.html +146 -0
- package/dist/builtin/workflows/skills/skill-creator/eval-viewer/generate_review.py +471 -0
- package/dist/builtin/workflows/skills/skill-creator/eval-viewer/viewer.html +1325 -0
- package/dist/builtin/workflows/skills/skill-creator/references/schemas.md +430 -0
- package/dist/builtin/workflows/skills/skill-creator/scripts/__init__.py +0 -0
- package/dist/builtin/workflows/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/dist/builtin/workflows/skills/skill-creator/scripts/generate_report.py +326 -0
- package/dist/builtin/workflows/skills/skill-creator/scripts/improve_description.py +247 -0
- package/dist/builtin/workflows/skills/skill-creator/scripts/package_skill.py +136 -0
- package/dist/builtin/workflows/skills/skill-creator/scripts/quick_validate.py +103 -0
- package/dist/builtin/workflows/skills/skill-creator/scripts/run_eval.py +310 -0
- package/dist/builtin/workflows/skills/skill-creator/scripts/run_loop.py +328 -0
- package/dist/builtin/workflows/skills/skill-creator/scripts/utils.py +47 -0
- package/dist/builtin/workflows/src/extension/index.ts +869 -93
- package/dist/builtin/workflows/src/extension/render-call.ts +34 -1
- package/dist/builtin/workflows/src/extension/render-result.ts +126 -21
- package/dist/builtin/workflows/src/extension/runtime.ts +91 -3
- package/dist/builtin/workflows/src/extension/wiring.ts +38 -12
- package/dist/builtin/workflows/src/extension/workflow-schema.ts +62 -5
- package/dist/builtin/workflows/src/runs/background/runner.ts +3 -3
- package/dist/builtin/workflows/src/runs/background/status.ts +42 -8
- package/dist/builtin/workflows/src/runs/foreground/executor.ts +410 -95
- package/dist/builtin/workflows/src/runs/foreground/stage-control-registry.ts +5 -2
- package/dist/builtin/workflows/src/runs/foreground/stage-runner.ts +8 -0
- package/dist/builtin/workflows/src/runs/shared/model-fallback.ts +6 -4
- package/dist/builtin/workflows/src/runs/shared/worktree.ts +3 -2
- package/dist/builtin/workflows/src/shared/persistence-restore.ts +138 -5
- package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +30 -0
- package/dist/builtin/workflows/src/shared/render-inputs-schema.ts +78 -120
- package/dist/builtin/workflows/src/shared/stage-ui-broker.ts +193 -0
- package/dist/builtin/workflows/src/shared/store-types.ts +26 -1
- package/dist/builtin/workflows/src/shared/store.ts +145 -17
- package/dist/builtin/workflows/src/shared/timing.ts +6 -2
- package/dist/builtin/workflows/src/shared/workflow-failures.ts +375 -0
- package/dist/builtin/workflows/src/tui/chat-surface.ts +68 -17
- package/dist/builtin/workflows/src/tui/connectors.ts +2 -2
- package/dist/builtin/workflows/src/tui/dispatch-confirm.ts +24 -26
- package/dist/builtin/workflows/src/tui/graph-canvas.ts +4 -8
- package/dist/builtin/workflows/src/tui/graph-view.ts +17 -14
- package/dist/builtin/workflows/src/tui/header.ts +38 -0
- package/dist/builtin/workflows/src/tui/inline-form-card.ts +161 -238
- package/dist/builtin/workflows/src/tui/inline-form-editor.ts +68 -73
- package/dist/builtin/workflows/src/tui/inline-form-overlay.ts +2 -3
- package/dist/builtin/workflows/src/tui/inline-form-store.ts +2 -1
- package/dist/builtin/workflows/src/tui/inputs-overlay.ts +1 -3
- package/dist/builtin/workflows/src/tui/inputs-picker.ts +286 -399
- package/dist/builtin/workflows/src/tui/keybindings-adapter.ts +11 -0
- package/dist/builtin/workflows/src/tui/node-card.ts +2 -1
- package/dist/builtin/workflows/src/tui/overlay-adapter.ts +9 -1
- package/dist/builtin/workflows/src/tui/prompt-card.ts +46 -19
- package/dist/builtin/workflows/src/tui/run-detail.ts +63 -80
- package/dist/builtin/workflows/src/tui/session-confirm.ts +9 -3
- package/dist/builtin/workflows/src/tui/session-picker.ts +19 -16
- package/dist/builtin/workflows/src/tui/stage-chat-layout.ts +88 -0
- package/dist/builtin/workflows/src/tui/stage-chat-view.ts +368 -879
- package/dist/builtin/workflows/src/tui/status-helpers.ts +4 -0
- package/dist/builtin/workflows/src/tui/status-list.ts +67 -75
- package/dist/builtin/workflows/src/tui/store-widget-installer.ts +50 -12
- package/dist/builtin/workflows/src/tui/submit-pane.ts +164 -0
- package/dist/builtin/workflows/src/tui/switcher.ts +27 -4
- package/dist/builtin/workflows/src/tui/text-helpers.ts +98 -4
- package/dist/builtin/workflows/src/tui/widget.ts +90 -68
- package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +23 -2
- package/dist/builtin/workflows/src/tui/workflow-list.ts +44 -68
- package/dist/cli/file-processor.d.ts.map +1 -1
- package/dist/cli/file-processor.js +2 -3
- package/dist/cli/file-processor.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -10
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +2 -1
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +3 -2
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +6 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +16 -2
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/atomic-guide-command.d.ts.map +1 -1
- package/dist/core/atomic-guide-command.js +8 -9
- package/dist/core/atomic-guide-command.js.map +1 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +3 -2
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +2 -1
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/export-html/index.d.ts.map +1 -1
- package/dist/core/export-html/index.js +8 -6
- package/dist/core/export-html/index.js.map +1 -1
- package/dist/core/export-html/template.js +6 -3
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +12 -29
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +5 -1
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/package-manager.d.ts +8 -0
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +145 -58
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/prompt-templates.d.ts.map +1 -1
- package/dist/core/prompt-templates.js +6 -20
- package/dist/core/prompt-templates.js.map +1 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +38 -31
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +9 -4
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +32 -24
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +8 -15
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +8 -22
- package/dist/core/skills.js.map +1 -1
- package/dist/core/tools/ask-user-question/state/questionnaire-session.d.ts +5 -4
- package/dist/core/tools/ask-user-question/state/questionnaire-session.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/state/questionnaire-session.js +34 -11
- package/dist/core/tools/ask-user-question/state/questionnaire-session.js.map +1 -1
- package/dist/core/tools/ask-user-question/state/selectors/contract.d.ts +1 -0
- package/dist/core/tools/ask-user-question/state/selectors/contract.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/state/selectors/contract.js.map +1 -1
- package/dist/core/tools/ask-user-question/state/selectors/projections.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/state/selectors/projections.js +1 -0
- package/dist/core/tools/ask-user-question/state/selectors/projections.js.map +1 -1
- package/dist/core/tools/ask-user-question/state/state-reducer.d.ts +1 -2
- package/dist/core/tools/ask-user-question/state/state-reducer.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/state/state-reducer.js +26 -9
- package/dist/core/tools/ask-user-question/state/state-reducer.js.map +1 -1
- package/dist/core/tools/ask-user-question/state/state.d.ts +4 -0
- package/dist/core/tools/ask-user-question/state/state.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/state/state.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/option-list-view.d.ts +1 -0
- package/dist/core/tools/ask-user-question/view/components/option-list-view.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/option-list-view.js +1 -0
- package/dist/core/tools/ask-user-question/view/components/option-list-view.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/wrapping-select.d.ts +9 -6
- package/dist/core/tools/ask-user-question/view/components/wrapping-select.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/wrapping-select.js +28 -7
- package/dist/core/tools/ask-user-question/view/components/wrapping-select.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/props-adapter.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/view/props-adapter.js +4 -1
- package/dist/core/tools/ask-user-question/view/props-adapter.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +56 -53
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit-diff.d.ts +3 -1
- package/dist/core/tools/edit-diff.d.ts.map +1 -1
- package/dist/core/tools/edit-diff.js +8 -1
- package/dist/core/tools/edit-diff.js.map +1 -1
- package/dist/core/tools/edit.d.ts +3 -1
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +44 -81
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
- package/dist/core/tools/file-mutation-queue.js +27 -12
- package/dist/core/tools/file-mutation-queue.js.map +1 -1
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +2 -3
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +3 -3
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js +5 -5
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/output-accumulator.d.ts +2 -0
- package/dist/core/tools/output-accumulator.d.ts.map +1 -1
- package/dist/core/tools/output-accumulator.js +11 -4
- package/dist/core/tools/output-accumulator.js.map +1 -1
- package/dist/core/tools/path-utils.d.ts +2 -0
- package/dist/core/tools/path-utils.d.ts.map +1 -1
- package/dist/core/tools/path-utils.js +39 -21
- package/dist/core/tools/path-utils.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +9 -8
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/truncate.d.ts.map +1 -1
- package/dist/core/tools/truncate.js +12 -2
- package/dist/core/tools/truncate.js.map +1 -1
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +20 -35
- package/dist/core/tools/write.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +5 -6
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/chat-input-actions.d.ts +24 -0
- package/dist/modes/interactive/chat-input-actions.d.ts.map +1 -0
- package/dist/modes/interactive/chat-input-actions.js +179 -0
- package/dist/modes/interactive/chat-input-actions.js.map +1 -0
- package/dist/modes/interactive/components/chat-message-renderer.d.ts +1 -0
- package/dist/modes/interactive/components/chat-message-renderer.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-message-renderer.js +14 -3
- package/dist/modes/interactive/components/chat-message-renderer.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host.d.ts +157 -0
- package/dist/modes/interactive/components/chat-session-host.d.ts.map +1 -0
- package/dist/modes/interactive/components/chat-session-host.js +1007 -0
- package/dist/modes/interactive/components/chat-session-host.js.map +1 -0
- package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/config-selector.js +1 -1
- package/dist/modes/interactive/components/config-selector.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts +1 -0
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +14 -5
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/index.d.ts +1 -0
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/index.js +1 -0
- package/dist/modes/interactive/components/index.js.map +1 -1
- package/dist/modes/interactive/components/login-dialog.d.ts +9 -1
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/dist/modes/interactive/components/login-dialog.js +29 -4
- package/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +18 -67
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/utils/child-process.d.ts +1 -0
- package/dist/utils/child-process.d.ts.map +1 -1
- package/dist/utils/child-process.js +8 -0
- package/dist/utils/child-process.js.map +1 -1
- package/dist/utils/clipboard-native.d.ts +3 -1
- package/dist/utils/clipboard-native.d.ts.map +1 -1
- package/dist/utils/clipboard-native.js +14 -8
- package/dist/utils/clipboard-native.js.map +1 -1
- package/dist/utils/image-resize-core.d.ts +30 -0
- package/dist/utils/image-resize-core.d.ts.map +1 -0
- package/dist/utils/image-resize-core.js +124 -0
- package/dist/utils/image-resize-core.js.map +1 -0
- package/dist/utils/image-resize-worker.d.ts +2 -0
- package/dist/utils/image-resize-worker.d.ts.map +1 -0
- package/dist/utils/image-resize-worker.js +31 -0
- package/dist/utils/image-resize-worker.js.map +1 -0
- package/dist/utils/image-resize.d.ts +7 -27
- package/dist/utils/image-resize.d.ts.map +1 -1
- package/dist/utils/image-resize.js +75 -115
- package/dist/utils/image-resize.js.map +1 -1
- package/dist/utils/paths.d.ts +16 -1
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +49 -7
- package/dist/utils/paths.js.map +1 -1
- package/docs/changelog.mdx +29 -0
- package/docs/compaction.md +1 -1
- package/docs/custom-provider.md +2 -2
- package/docs/development.md +1 -1
- package/docs/docs.json +98 -143
- package/docs/extensions.md +29 -16
- package/docs/favicon.svg +29 -0
- package/docs/images/interactive-mode.png +0 -0
- package/docs/images/tree-view.png +0 -0
- package/docs/images/workflow-command.png +0 -0
- package/docs/images/workflow-graph.png +0 -0
- package/docs/images/workflow-input-picker.png +0 -0
- package/docs/images/workflow-list.png +0 -0
- package/docs/index.md +10 -1
- package/docs/logo.svg +59 -0
- package/docs/packages.md +3 -3
- package/docs/providers.md +1 -1
- package/docs/quickstart.md +98 -2
- package/docs/rpc.md +8 -8
- package/docs/sdk.md +23 -12
- package/docs/sessions.md +1 -1
- package/docs/skills.md +15 -1
- package/docs/termux.md +11 -1
- package/docs/themes.md +6 -6
- package/docs/tui.md +18 -18
- package/docs/usage.md +1 -1
- package/docs/workflows.md +172 -2
- package/examples/extensions/subagent/index.ts +2 -1
- package/package.json +6 -6
- /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/SKILL.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/element-attributes.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/playwright-tests.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/request-mocking.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/running-code.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/session-management.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/spec-driven-testing.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/storage-state.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/test-generation.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/tracing.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/playwright-cli/references/video-recording.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/tdd/SKILL.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/tdd/deep-modules.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/tdd/interface-design.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/tdd/mocking.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/tdd/refactoring.md +0 -0
- /package/dist/builtin/{workflows → subagents}/skills/tdd/tests.md +0 -0
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
import type { WorkflowFailureKind } from "./store-types.js";
|
|
2
|
+
|
|
3
|
+
export interface WorkflowFailure {
|
|
4
|
+
readonly kind: WorkflowFailureKind;
|
|
5
|
+
/** Original error text, preserved for diagnostics. */
|
|
6
|
+
readonly message: string;
|
|
7
|
+
/** Sanitized workflow-facing text shown on run/stage snapshots. */
|
|
8
|
+
readonly userMessage: string;
|
|
9
|
+
readonly retryable: boolean;
|
|
10
|
+
readonly resumable: boolean;
|
|
11
|
+
readonly cause?: unknown;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const WORKFLOW_AUTH_FAILURE_MESSAGE =
|
|
15
|
+
"You must be logged in to run workflows. Run /login and try again.";
|
|
16
|
+
|
|
17
|
+
const WORKFLOW_FAILURE_KINDS: ReadonlySet<WorkflowFailureKind> = new Set([
|
|
18
|
+
"auth",
|
|
19
|
+
"rate_limit",
|
|
20
|
+
"provider",
|
|
21
|
+
"cancelled",
|
|
22
|
+
"unknown",
|
|
23
|
+
]);
|
|
24
|
+
|
|
25
|
+
export function isWorkflowFailureKind(kind: string): kind is WorkflowFailureKind {
|
|
26
|
+
return WORKFLOW_FAILURE_KINDS.has(kind as WorkflowFailureKind);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function makeWorkflowFailure(
|
|
30
|
+
kind: WorkflowFailureKind,
|
|
31
|
+
message: string,
|
|
32
|
+
opts: {
|
|
33
|
+
readonly retryable: boolean;
|
|
34
|
+
readonly resumable: boolean;
|
|
35
|
+
readonly cause: unknown;
|
|
36
|
+
readonly userMessage?: string;
|
|
37
|
+
},
|
|
38
|
+
): WorkflowFailure {
|
|
39
|
+
return {
|
|
40
|
+
kind,
|
|
41
|
+
message,
|
|
42
|
+
userMessage: opts.userMessage ?? message,
|
|
43
|
+
retryable: opts.retryable,
|
|
44
|
+
resumable: opts.resumable,
|
|
45
|
+
cause: opts.cause,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function asRecord(value: unknown): Record<string, unknown> | undefined {
|
|
50
|
+
return value !== null && typeof value === "object" ? value as Record<string, unknown> : undefined;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function field(value: unknown, key: string): unknown {
|
|
54
|
+
return asRecord(value)?.[key];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function stringField(value: unknown, key: string): string | undefined {
|
|
58
|
+
const raw = field(value, key);
|
|
59
|
+
return typeof raw === "string" && raw.length > 0 ? raw : undefined;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function errorMessage(error: unknown): string {
|
|
63
|
+
const structuredMessage = structuredErrorMessage(error);
|
|
64
|
+
if (structuredMessage !== undefined) return structuredMessage;
|
|
65
|
+
if (error instanceof Error && typeof error.message === "string") return error.message;
|
|
66
|
+
if (typeof error === "string") return error;
|
|
67
|
+
return String(error);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function errorName(error: unknown): string | undefined {
|
|
71
|
+
return error instanceof Error ? error.name : stringField(error, "name");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function structuredErrorMessage(error: unknown): string | undefined {
|
|
75
|
+
return stringField(error, "errorMessage")
|
|
76
|
+
?? stringField(error, "message")
|
|
77
|
+
?? stringField(error, "statusText");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
type StructuredSignal = {
|
|
81
|
+
readonly status?: number;
|
|
82
|
+
readonly code?: string | number;
|
|
83
|
+
readonly name?: string;
|
|
84
|
+
readonly stopReason?: string;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
function integerFrom(value: unknown): number | undefined {
|
|
88
|
+
if (typeof value === "number" && Number.isInteger(value)) return value;
|
|
89
|
+
if (typeof value !== "string" || value.trim().length === 0) return undefined;
|
|
90
|
+
const parsed = Number(value.trim());
|
|
91
|
+
return Number.isInteger(parsed) ? parsed : undefined;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function structuredSignal(error: unknown): StructuredSignal {
|
|
95
|
+
const status = integerFrom(field(error, "status"))
|
|
96
|
+
?? integerFrom(field(error, "statusCode"))
|
|
97
|
+
?? integerFrom(field(error, "httpStatus"));
|
|
98
|
+
const rawCode = field(error, "code");
|
|
99
|
+
const code = typeof rawCode === "string" || typeof rawCode === "number" ? rawCode : undefined;
|
|
100
|
+
return {
|
|
101
|
+
...(status !== undefined ? { status } : {}),
|
|
102
|
+
...(code !== undefined ? { code } : {}),
|
|
103
|
+
...(errorName(error) !== undefined ? { name: errorName(error)! } : {}),
|
|
104
|
+
...(stringField(error, "stopReason") !== undefined ? { stopReason: stringField(error, "stopReason")! } : {}),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function causeOf(error: unknown): unknown {
|
|
109
|
+
if (error instanceof Error) return error.cause;
|
|
110
|
+
return field(error, "cause");
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function diagnosticErrors(error: unknown): readonly unknown[] {
|
|
114
|
+
const diagnostics = field(error, "diagnostics");
|
|
115
|
+
if (!Array.isArray(diagnostics)) return [];
|
|
116
|
+
const errors: unknown[] = [];
|
|
117
|
+
for (const diagnostic of diagnostics) {
|
|
118
|
+
const diagnosticError = field(diagnostic, "error");
|
|
119
|
+
errors.push(diagnosticError ?? diagnostic);
|
|
120
|
+
}
|
|
121
|
+
return errors;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function normalizeCode(value: string | number | undefined): string | undefined {
|
|
125
|
+
if (value === undefined) return undefined;
|
|
126
|
+
return String(value).trim().toLowerCase().replaceAll("-", "_");
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function kindFromStatus(status: number | undefined): WorkflowFailureKind | undefined {
|
|
130
|
+
switch (status) {
|
|
131
|
+
case 401:
|
|
132
|
+
case 403:
|
|
133
|
+
return "auth";
|
|
134
|
+
case 429:
|
|
135
|
+
return "rate_limit";
|
|
136
|
+
case 500:
|
|
137
|
+
case 502:
|
|
138
|
+
case 503:
|
|
139
|
+
case 504:
|
|
140
|
+
return "provider";
|
|
141
|
+
default:
|
|
142
|
+
return undefined;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function kindFromCode(code: string | number | undefined): WorkflowFailureKind | undefined {
|
|
147
|
+
const normalized = normalizeCode(code);
|
|
148
|
+
switch (normalized) {
|
|
149
|
+
case undefined:
|
|
150
|
+
return undefined;
|
|
151
|
+
case "401":
|
|
152
|
+
case "403":
|
|
153
|
+
case "auth":
|
|
154
|
+
case "auth_required":
|
|
155
|
+
case "authentication_required":
|
|
156
|
+
case "unauthorized":
|
|
157
|
+
case "forbidden":
|
|
158
|
+
case "invalid_api_key":
|
|
159
|
+
case "missing_api_key":
|
|
160
|
+
return "auth";
|
|
161
|
+
case "429":
|
|
162
|
+
case "rate_limit":
|
|
163
|
+
case "rate_limit_exceeded":
|
|
164
|
+
case "too_many_requests":
|
|
165
|
+
case "quota_exceeded":
|
|
166
|
+
return "rate_limit";
|
|
167
|
+
case "aborterror":
|
|
168
|
+
case "aborted":
|
|
169
|
+
case "cancelled":
|
|
170
|
+
case "canceled":
|
|
171
|
+
return "cancelled";
|
|
172
|
+
case "500":
|
|
173
|
+
case "502":
|
|
174
|
+
case "503":
|
|
175
|
+
case "504":
|
|
176
|
+
case "provider_error":
|
|
177
|
+
case "service_unavailable":
|
|
178
|
+
case "temporarily_unavailable":
|
|
179
|
+
case "overloaded":
|
|
180
|
+
return "provider";
|
|
181
|
+
default:
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function structuredKind(error: unknown, seen = new Set<unknown>()): WorkflowFailureKind | undefined {
|
|
187
|
+
if (error === undefined || error === null || seen.has(error)) return undefined;
|
|
188
|
+
if (typeof error === "object") seen.add(error);
|
|
189
|
+
|
|
190
|
+
const signal = structuredSignal(error);
|
|
191
|
+
if (signal.stopReason?.toLowerCase() === "aborted") return "cancelled";
|
|
192
|
+
const statusKind = kindFromStatus(signal.status);
|
|
193
|
+
if (statusKind !== undefined) return statusKind;
|
|
194
|
+
const codeKind = kindFromCode(signal.code) ?? kindFromCode(signal.name);
|
|
195
|
+
if (codeKind !== undefined) return codeKind;
|
|
196
|
+
|
|
197
|
+
for (const diagnosticError of diagnosticErrors(error)) {
|
|
198
|
+
const diagnosticKind = structuredKind(diagnosticError, seen);
|
|
199
|
+
if (diagnosticKind !== undefined) return diagnosticKind;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return structuredKind(causeOf(error), seen);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function failureForKind(kind: WorkflowFailureKind, message: string, cause: unknown): WorkflowFailure {
|
|
206
|
+
switch (kind) {
|
|
207
|
+
case "auth":
|
|
208
|
+
return makeWorkflowFailure("auth", message, {
|
|
209
|
+
userMessage: WORKFLOW_AUTH_FAILURE_MESSAGE,
|
|
210
|
+
retryable: true,
|
|
211
|
+
resumable: true,
|
|
212
|
+
cause,
|
|
213
|
+
});
|
|
214
|
+
case "rate_limit":
|
|
215
|
+
return makeWorkflowFailure("rate_limit", message, {
|
|
216
|
+
retryable: true,
|
|
217
|
+
resumable: true,
|
|
218
|
+
cause,
|
|
219
|
+
});
|
|
220
|
+
case "cancelled":
|
|
221
|
+
return makeWorkflowFailure("cancelled", message, {
|
|
222
|
+
retryable: false,
|
|
223
|
+
resumable: false,
|
|
224
|
+
cause,
|
|
225
|
+
});
|
|
226
|
+
case "provider":
|
|
227
|
+
return makeWorkflowFailure("provider", message, {
|
|
228
|
+
retryable: true,
|
|
229
|
+
resumable: true,
|
|
230
|
+
cause,
|
|
231
|
+
});
|
|
232
|
+
case "unknown":
|
|
233
|
+
return makeWorkflowFailure("unknown", message, {
|
|
234
|
+
retryable: false,
|
|
235
|
+
resumable: true,
|
|
236
|
+
cause,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
type TokenMatch = readonly string[];
|
|
242
|
+
|
|
243
|
+
function tokenize(value: string): readonly string[] {
|
|
244
|
+
const tokens: string[] = [];
|
|
245
|
+
let current = "";
|
|
246
|
+
for (const char of value.toLowerCase()) {
|
|
247
|
+
if ((char >= "a" && char <= "z") || (char >= "0" && char <= "9")) {
|
|
248
|
+
current += char;
|
|
249
|
+
} else if (current.length > 0) {
|
|
250
|
+
tokens.push(current);
|
|
251
|
+
current = "";
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
if (current.length > 0) tokens.push(current);
|
|
255
|
+
return tokens;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function hasPhrase(tokens: readonly string[], phrase: TokenMatch): boolean {
|
|
259
|
+
if (phrase.length === 0 || phrase.length > tokens.length) return false;
|
|
260
|
+
for (let index = 0; index <= tokens.length - phrase.length; index += 1) {
|
|
261
|
+
let matched = true;
|
|
262
|
+
for (let offset = 0; offset < phrase.length; offset += 1) {
|
|
263
|
+
if (tokens[index + offset] !== phrase[offset]) {
|
|
264
|
+
matched = false;
|
|
265
|
+
break;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (matched) return true;
|
|
269
|
+
}
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function hasAnyPhrase(tokens: readonly string[], phrases: readonly TokenMatch[]): boolean {
|
|
274
|
+
return phrases.some((phrase) => hasPhrase(tokens, phrase));
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function tokenNearAny(tokens: readonly string[], anchor: string, candidates: ReadonlySet<string>, distance: number): boolean {
|
|
278
|
+
for (let index = 0; index < tokens.length; index += 1) {
|
|
279
|
+
if (tokens[index] !== anchor) continue;
|
|
280
|
+
const start = Math.max(0, index - distance);
|
|
281
|
+
const end = Math.min(tokens.length - 1, index + distance);
|
|
282
|
+
for (let cursor = start; cursor <= end; cursor += 1) {
|
|
283
|
+
if (cursor !== index && candidates.has(tokens[cursor]!)) return true;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const AUTH_PHRASES: readonly TokenMatch[] = [
|
|
290
|
+
["no", "api", "key"],
|
|
291
|
+
["api", "key", "not", "found"],
|
|
292
|
+
["missing", "api", "key"],
|
|
293
|
+
["no", "model", "selected"],
|
|
294
|
+
["no", "models", "available"],
|
|
295
|
+
["not", "logged", "in"],
|
|
296
|
+
["log", "in"],
|
|
297
|
+
["login", "required"],
|
|
298
|
+
["authentication", "required"],
|
|
299
|
+
["unauthorized"],
|
|
300
|
+
];
|
|
301
|
+
|
|
302
|
+
const RATE_LIMIT_PHRASES: readonly TokenMatch[] = [
|
|
303
|
+
["rate", "limit"],
|
|
304
|
+
["429"],
|
|
305
|
+
["quota"],
|
|
306
|
+
["too", "many", "requests"],
|
|
307
|
+
];
|
|
308
|
+
|
|
309
|
+
const CANCELLED_PHRASES: readonly TokenMatch[] = [
|
|
310
|
+
["aborted"],
|
|
311
|
+
["cancelled"],
|
|
312
|
+
["canceled"],
|
|
313
|
+
];
|
|
314
|
+
|
|
315
|
+
const PROVIDER_PHRASES: readonly TokenMatch[] = [
|
|
316
|
+
["model", "not", "found"],
|
|
317
|
+
["overloaded"],
|
|
318
|
+
["temporarily", "unavailable"],
|
|
319
|
+
["service", "unavailable"],
|
|
320
|
+
["503"],
|
|
321
|
+
];
|
|
322
|
+
|
|
323
|
+
const AUTH_CONTEXT = new Set([
|
|
324
|
+
"token",
|
|
325
|
+
"credential",
|
|
326
|
+
"credentials",
|
|
327
|
+
"required",
|
|
328
|
+
"expired",
|
|
329
|
+
"invalid",
|
|
330
|
+
"missing",
|
|
331
|
+
"unauthorized",
|
|
332
|
+
"login",
|
|
333
|
+
"signin",
|
|
334
|
+
]);
|
|
335
|
+
|
|
336
|
+
const MODEL_PROVIDER_CONTEXT = new Set([
|
|
337
|
+
"unavailable",
|
|
338
|
+
"overloaded",
|
|
339
|
+
"temporarily",
|
|
340
|
+
"service",
|
|
341
|
+
]);
|
|
342
|
+
|
|
343
|
+
const PROVIDER_CONTEXT = new Set([
|
|
344
|
+
"error",
|
|
345
|
+
"failure",
|
|
346
|
+
"failed",
|
|
347
|
+
"overloaded",
|
|
348
|
+
"unavailable",
|
|
349
|
+
"temporarily",
|
|
350
|
+
"service",
|
|
351
|
+
]);
|
|
352
|
+
|
|
353
|
+
function fallbackKindFromMessage(message: string, name: string | undefined): WorkflowFailureKind | undefined {
|
|
354
|
+
const tokens = tokenize(message);
|
|
355
|
+
if (hasAnyPhrase(tokens, AUTH_PHRASES) || tokenNearAny(tokens, "oauth", AUTH_CONTEXT, 8)) return "auth";
|
|
356
|
+
if (hasAnyPhrase(tokens, RATE_LIMIT_PHRASES)) return "rate_limit";
|
|
357
|
+
if (name?.toLowerCase() === "aborterror" || hasAnyPhrase(tokens, CANCELLED_PHRASES)) return "cancelled";
|
|
358
|
+
if (
|
|
359
|
+
hasAnyPhrase(tokens, PROVIDER_PHRASES)
|
|
360
|
+
|| tokenNearAny(tokens, "model", MODEL_PROVIDER_CONTEXT, 8)
|
|
361
|
+
|| tokenNearAny(tokens, "provider", PROVIDER_CONTEXT, 8)
|
|
362
|
+
) return "provider";
|
|
363
|
+
return undefined;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
export function classifyWorkflowFailure(error: unknown): WorkflowFailure {
|
|
367
|
+
const message = errorMessage(error);
|
|
368
|
+
const structured = structuredKind(error);
|
|
369
|
+
if (structured !== undefined) return failureForKind(structured, message, error);
|
|
370
|
+
|
|
371
|
+
const fallback = fallbackKindFromMessage(message, errorName(error));
|
|
372
|
+
if (fallback !== undefined) return failureForKind(fallback, message, error);
|
|
373
|
+
|
|
374
|
+
return failureForKind("unknown", message, error);
|
|
375
|
+
}
|
|
@@ -82,6 +82,55 @@ export interface FlatBandBadge {
|
|
|
82
82
|
fg?: string;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
export interface RenderRoundedBoxOpts {
|
|
86
|
+
/** Title embedded in the top border. Keep it short; it is truncated to fit. */
|
|
87
|
+
title?: string;
|
|
88
|
+
/** Body lines. ANSI is preserved and each line is width-clamped. */
|
|
89
|
+
bodyLines: readonly string[];
|
|
90
|
+
/** Full box width in visible cells. */
|
|
91
|
+
width?: number;
|
|
92
|
+
/** Optional theme for ANSI border/text chrome. */
|
|
93
|
+
theme?: GraphTheme;
|
|
94
|
+
/** Border/title colour. Defaults to theme.border or plain output. */
|
|
95
|
+
accent?: string;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Render a width-stable rounded box (`╭╮╰╯`) around arbitrary body lines.
|
|
100
|
+
* This is the shared workflow-tool output container: panels, cards, and
|
|
101
|
+
* compact notices all use the same border vocabulary.
|
|
102
|
+
*/
|
|
103
|
+
export function renderRoundedBox(opts: RenderRoundedBoxOpts): string {
|
|
104
|
+
const width = chatWidth(opts.width);
|
|
105
|
+
return renderRoundedBoxLines({ ...opts, width }).join("\n");
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Render a rounded box as individual width-stable lines. */
|
|
109
|
+
export function renderRoundedBoxLines(opts: RenderRoundedBoxOpts & { width: number }): string[] {
|
|
110
|
+
const width = Math.max(MIN_WIDTH, opts.width);
|
|
111
|
+
const inner = Math.max(2, width - 2);
|
|
112
|
+
const theme = opts.theme;
|
|
113
|
+
const border = theme ? hexToAnsi(opts.accent ?? theme.border) : "";
|
|
114
|
+
const reset = theme ? RESET : "";
|
|
115
|
+
|
|
116
|
+
const titleBudget = Math.max(0, inner - 2);
|
|
117
|
+
const titleText = opts.title && opts.title.length > 0
|
|
118
|
+
? ` ${truncateToWidth(opts.title, titleBudget, ELLIPSIS)} `
|
|
119
|
+
: "";
|
|
120
|
+
const topFill = Math.max(0, inner - visibleWidth(titleText));
|
|
121
|
+
const top = `${border}╭${titleText}${"─".repeat(topFill)}╮${reset}`;
|
|
122
|
+
|
|
123
|
+
const body = opts.bodyLines.length > 0 ? opts.bodyLines : [""];
|
|
124
|
+
const rows = body.map((line) => {
|
|
125
|
+
const clipped = truncateToWidth(line, inner, ELLIPSIS, theme !== undefined);
|
|
126
|
+
const pad = Math.max(0, inner - visibleWidth(clipped));
|
|
127
|
+
return `${border}│${reset}${clipped}${" ".repeat(pad)}${border}│${reset}`;
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const bottom = `${border}╰${"─".repeat(inner)}╯${reset}`;
|
|
131
|
+
return [top, ...rows, bottom];
|
|
132
|
+
}
|
|
133
|
+
|
|
85
134
|
export interface RenderFlatBandOpts {
|
|
86
135
|
/** Pill label, e.g. `"BACKGROUND"`, `"DISPATCHED"`, `"WORKFLOWS"`. */
|
|
87
136
|
label: string;
|
|
@@ -100,8 +149,8 @@ export interface RenderFlatBandOpts {
|
|
|
100
149
|
/**
|
|
101
150
|
* Render one full-width band. Themed mode paints the line with the
|
|
102
151
|
* `surface0` background and styles the label, subtitle, and badges with
|
|
103
|
-
* ANSI colours. Plain mode emits
|
|
104
|
-
* shape carries through to logs.
|
|
152
|
+
* ANSI colours. Plain mode emits an indented `LABEL subtitle badge`
|
|
153
|
+
* shape so the layout carries through to logs.
|
|
105
154
|
*/
|
|
106
155
|
export function renderFlatBand(opts: RenderFlatBandOpts): string {
|
|
107
156
|
const width = chatWidth(opts.width);
|
|
@@ -187,8 +236,8 @@ function renderFlatBandPlain(
|
|
|
187
236
|
const badgeSeg = badges.map((b) => b.text).join(" ");
|
|
188
237
|
// Plain-mode band sits 1 cell from the chat content edge, matching the
|
|
189
238
|
// themed band's leftPad and the card/hint left edges. Single-column
|
|
190
|
-
// alignment for the band
|
|
191
|
-
const leftMarker = "
|
|
239
|
+
// alignment for the band, card, and hint arrow.
|
|
240
|
+
const leftMarker = " ";
|
|
192
241
|
const leftW = visibleWidth(leftMarker);
|
|
193
242
|
const subtitleW = visibleWidth(subtitleSeg);
|
|
194
243
|
const badgeW = visibleWidth(badgeSeg);
|
|
@@ -239,7 +288,7 @@ export interface RenderTaggedCardOpts {
|
|
|
239
288
|
theme?: GraphTheme;
|
|
240
289
|
}
|
|
241
290
|
|
|
242
|
-
const STRIPE_CHAR_THEMED = "
|
|
291
|
+
const STRIPE_CHAR_THEMED = " ";
|
|
243
292
|
const STRIPE_CHAR_PLAIN = "│";
|
|
244
293
|
|
|
245
294
|
/**
|
|
@@ -266,7 +315,7 @@ function renderTaggedCardThemed(
|
|
|
266
315
|
const tagFg = hexToAnsi(opts.accent);
|
|
267
316
|
const text = hexToAnsi(theme.text);
|
|
268
317
|
|
|
269
|
-
// Row 1:
|
|
318
|
+
// Row 1: [tag] title … ● running
|
|
270
319
|
const tagSeg = `${tagBg}${tagFg}${BOLD} ${opts.tag} ${RESET}`;
|
|
271
320
|
const tagW = opts.tag.length + 2;
|
|
272
321
|
const tagSubtitleSeg = opts.tagSubtitle
|
|
@@ -281,14 +330,14 @@ function renderTaggedCardThemed(
|
|
|
281
330
|
: "";
|
|
282
331
|
|
|
283
332
|
// Row 1 sits one cell tighter against the stripe than the body rows
|
|
284
|
-
// (
|
|
333
|
+
// (` [tag] title …`) so that the tag pill's interior text starts at
|
|
285
334
|
// exactly the same column as every body row's leading character —
|
|
286
|
-
//
|
|
287
|
-
// and
|
|
335
|
+
// ` ` (2 cells) + bg-pill leading pad (1 cell) lands tag text at col 4,
|
|
336
|
+
// and ` ` (3 cells) + body content also lands at col 4. The +1
|
|
288
337
|
// hanging indent on the body is what the mockup's §1 / §2 cards show
|
|
289
|
-
// (ui/mockups.html ·
|
|
290
|
-
const row1StripePrefixW = 2; // "
|
|
291
|
-
const bodyStripePrefixW = 3; // "
|
|
338
|
+
// (ui/mockups.html · ` [tag] title` over ` body`).
|
|
339
|
+
const row1StripePrefixW = 2; // " "
|
|
340
|
+
const bodyStripePrefixW = 3; // " "
|
|
292
341
|
const trailingPad = 2;
|
|
293
342
|
const titleSuffixW = opts.titleSuffix ? Math.max(0, opts.titleSuffixWidth ?? 0) : 0;
|
|
294
343
|
const titleSuffixGap = opts.titleSuffix ? 2 : 0;
|
|
@@ -316,10 +365,10 @@ function renderTaggedCardThemed(
|
|
|
316
365
|
width - row1StripePrefixW - tagW - titleSegW - suffixSegW - tagSubtitleW - trailingW - 1,
|
|
317
366
|
);
|
|
318
367
|
|
|
319
|
-
// 1-cell leading space on every card line so the
|
|
320
|
-
// column
|
|
321
|
-
//
|
|
322
|
-
//
|
|
368
|
+
// 1-cell leading space on every card line so the card content is
|
|
369
|
+
// column-aligned with the band's `[ LABEL ]` (which is itself offset by
|
|
370
|
+
// `leftPad` inside its surface0 fill) and with the hint rows' `▸` arrow.
|
|
371
|
+
// All three chat-surface families share the same left edge,
|
|
323
372
|
// matching the mockup's terminal-padding scheme.
|
|
324
373
|
const row1 =
|
|
325
374
|
` ${stripe}${STRIPE_CHAR_THEMED}${RESET} ` +
|
|
@@ -455,6 +504,7 @@ function stageGlyph(status: StageStatus): string {
|
|
|
455
504
|
case "completed": return "✓";
|
|
456
505
|
case "running": return "●";
|
|
457
506
|
case "failed": return "✗";
|
|
507
|
+
case "skipped": return "⊘";
|
|
458
508
|
case "pending":
|
|
459
509
|
default: return "○";
|
|
460
510
|
}
|
|
@@ -465,6 +515,7 @@ function stageColor(status: StageStatus, theme: GraphTheme): string {
|
|
|
465
515
|
case "completed": return hexToAnsi(theme.success);
|
|
466
516
|
case "running": return hexToAnsi(theme.warning);
|
|
467
517
|
case "failed": return hexToAnsi(theme.error);
|
|
518
|
+
case "skipped": return hexToAnsi(theme.dim);
|
|
468
519
|
case "pending":
|
|
469
520
|
default: return hexToAnsi(theme.dim);
|
|
470
521
|
}
|
|
@@ -492,7 +543,7 @@ export interface HintRow {
|
|
|
492
543
|
export function renderHintRows(rows: readonly HintRow[], theme?: GraphTheme): string {
|
|
493
544
|
if (rows.length === 0) return "";
|
|
494
545
|
// 1-cell leading space so the `▸` arrow column-aligns with the band's
|
|
495
|
-
// `[ LABEL ]` opening bracket and the card
|
|
546
|
+
// `[ LABEL ]` opening bracket and the card content. All three share
|
|
496
547
|
// column 1 — see renderTaggedCardThemed for the alignment contract.
|
|
497
548
|
const indent = " ";
|
|
498
549
|
if (!theme) {
|
|
@@ -38,7 +38,7 @@ export function buildMergeConnector(fromXs: number[], toX: number): ConnectorRes
|
|
|
38
38
|
|
|
39
39
|
// Line 0: top row connecting sources with ─, ┬ at source positions
|
|
40
40
|
// Line 1: vertical drop │ from each source down to target row
|
|
41
|
-
// Line 2: bottom row ─ connecting to target with ┴ at source positions
|
|
41
|
+
// Line 2: bottom row ─ connecting to target with ┴ at source positions
|
|
42
42
|
|
|
43
43
|
const fromSet = new Set(fromXs.map((x) => x - minX));
|
|
44
44
|
const targetCol = toX - minX;
|
|
@@ -69,7 +69,7 @@ export function buildMergeConnector(fromXs: number[], toX: number): ConnectorRes
|
|
|
69
69
|
for (const fx of fromSet) {
|
|
70
70
|
botChars[fx] = "┴";
|
|
71
71
|
}
|
|
72
|
-
// Mark target with
|
|
72
|
+
// Mark target with a join glyph.
|
|
73
73
|
botChars[targetCol] = "┴";
|
|
74
74
|
|
|
75
75
|
// Middle line: vertical bars at each source position
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
* ui/dispatch-mockup.html §1 (compact two-row redesign).
|
|
4
4
|
*
|
|
5
5
|
* Visual contract:
|
|
6
|
-
* - One
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* - One rounded `DISPATCHED` panel.
|
|
7
|
+
* - One status-coloured rounded run card:
|
|
8
|
+
* title: runId8 · workflowName · ● running
|
|
9
|
+
* body: compact `k=v · k=v · +N more` input summary when present
|
|
9
10
|
* - One hint row: ▸ /workflow connect <id> attach & watch
|
|
10
11
|
*
|
|
11
12
|
* What we deliberately do NOT emit (was in the legacy 7-row layout):
|
|
@@ -22,8 +23,7 @@
|
|
|
22
23
|
* intent (list other in-flight runs), already discoverable from a
|
|
23
24
|
* bare `/workflow` invocation and the picker's confirm panel.
|
|
24
25
|
*
|
|
25
|
-
* Plain mode
|
|
26
|
-
* preserved.
|
|
26
|
+
* Plain mode drops ANSI; the rounded panel/card layout shape is preserved.
|
|
27
27
|
*
|
|
28
28
|
* cross-ref:
|
|
29
29
|
* - ui/dispatch-mockup.html (before / after side-by-side)
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
|
|
35
35
|
import type { GraphTheme } from "./graph-theme.js";
|
|
36
36
|
import {
|
|
37
|
-
renderTaggedCard,
|
|
38
37
|
renderHintRows,
|
|
38
|
+
renderRoundedBox,
|
|
39
39
|
ELLIPSIS,
|
|
40
40
|
chatWidth,
|
|
41
41
|
} from "./chat-surface.js";
|
|
@@ -67,9 +67,10 @@ export interface RenderDispatchConfirmOpts {
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
/**
|
|
70
|
-
* Render the post-dispatch confirmation: one
|
|
71
|
-
* runId, workflow name, inputs summary, and a
|
|
72
|
-
* followed by one hint row pointing at
|
|
70
|
+
* Render the post-dispatch confirmation: one rounded panel containing a
|
|
71
|
+
* rounded run card with runId, workflow name, inputs summary, and a
|
|
72
|
+
* `● running` status badge, followed by one hint row pointing at
|
|
73
|
+
* `/workflow connect <id>`.
|
|
73
74
|
*/
|
|
74
75
|
export function renderDispatchConfirm(opts: RenderDispatchConfirmOpts): string {
|
|
75
76
|
const width = effectiveWidth(opts.width);
|
|
@@ -87,7 +88,7 @@ export function renderDispatchConfirm(opts: RenderDispatchConfirmOpts): string {
|
|
|
87
88
|
// workflow name) or wrap to a body row. Budget math mirrors
|
|
88
89
|
// `renderTaggedCard`'s row-1 chrome accounting:
|
|
89
90
|
//
|
|
90
|
-
// " "(1)
|
|
91
|
+
// " "(1) (1) " "(1) [tag](tag.length + 2) " "(2) title(titleW)
|
|
91
92
|
// " "(2) suffix(suffixW) gap(≥1) trailing(trailingW) " "(1)
|
|
92
93
|
//
|
|
93
94
|
// Solve for the max suffix width that still leaves room for the
|
|
@@ -116,14 +117,12 @@ export function renderDispatchConfirm(opts: RenderDispatchConfirmOpts): string {
|
|
|
116
117
|
|
|
117
118
|
const hasInputs = Object.keys(opts.inputs).length > 0;
|
|
118
119
|
let titleSuffix: string | undefined;
|
|
119
|
-
let titleSuffixWidth: number | undefined;
|
|
120
120
|
const bodyRows: string[] = [];
|
|
121
121
|
|
|
122
122
|
if (hasInputs && inlineBudget >= MIN_INLINE_INPUT_BUDGET) {
|
|
123
123
|
const inline = renderInputsSegment(opts.inputs, inlineBudget, theme);
|
|
124
124
|
if (inline && inline.fitted) {
|
|
125
125
|
titleSuffix = inline.rendered;
|
|
126
|
-
titleSuffixWidth = inline.visibleWidth;
|
|
127
126
|
}
|
|
128
127
|
}
|
|
129
128
|
|
|
@@ -131,30 +130,29 @@ export function renderDispatchConfirm(opts: RenderDispatchConfirmOpts): string {
|
|
|
131
130
|
// tight, or the rendered inputs spilled past it. Either way, we use
|
|
132
131
|
// the full body interior (width - body chrome) as the wider canvas.
|
|
133
132
|
if (hasInputs && titleSuffix === undefined) {
|
|
134
|
-
const BODY_PREFIX_W = 4; // "
|
|
133
|
+
const BODY_PREFIX_W = 4; // " " — see renderTaggedCard body prefix
|
|
135
134
|
const bodyBudget = Math.max(0, width - BODY_PREFIX_W - 1);
|
|
136
135
|
const overflowSeg = renderInputsSegment(opts.inputs, bodyBudget, theme);
|
|
137
136
|
if (overflowSeg) bodyRows.push(overflowSeg.rendered);
|
|
138
137
|
}
|
|
139
138
|
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
titleSuffixWidth,
|
|
145
|
-
trailing,
|
|
146
|
-
accent,
|
|
147
|
-
width: opts.width,
|
|
148
|
-
theme,
|
|
149
|
-
bodyRows,
|
|
150
|
-
});
|
|
139
|
+
const inputRows = bodyRows.length > 0
|
|
140
|
+
? bodyRows.map((row) => ` ${row} `)
|
|
141
|
+
: [` ${titleSuffix ?? "started in background"} `];
|
|
142
|
+
const titleLine = ` ● ${tag} ${opts.workflowName} ${trailing.text} `;
|
|
151
143
|
|
|
152
144
|
const hints = renderHintRows(
|
|
153
145
|
[{ command: `/workflow connect ${tag}`, hint: "attach & watch" }],
|
|
154
146
|
theme,
|
|
155
|
-
);
|
|
147
|
+
).split("\n").map((line) => ` ${line} `);
|
|
156
148
|
|
|
157
|
-
return
|
|
149
|
+
return renderRoundedBox({
|
|
150
|
+
title: "DISPATCHED",
|
|
151
|
+
bodyLines: [titleLine, ...inputRows, "", ...hints],
|
|
152
|
+
accent,
|
|
153
|
+
theme,
|
|
154
|
+
width,
|
|
155
|
+
});
|
|
158
156
|
}
|
|
159
157
|
|
|
160
158
|
/** First 8 chars of the run UUID — the canonical short form. */
|