@bastani/atomic 0.8.13-0 → 0.8.14-0
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 +24 -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
|
@@ -28,6 +28,8 @@ export function statusColor(
|
|
|
28
28
|
return theme.error;
|
|
29
29
|
case "blocked":
|
|
30
30
|
return theme.dim;
|
|
31
|
+
case "skipped":
|
|
32
|
+
return theme.dim;
|
|
31
33
|
case "pending":
|
|
32
34
|
default:
|
|
33
35
|
return theme.dim;
|
|
@@ -41,6 +43,8 @@ export function statusIcon(status: StageStatus | RunStatus): string {
|
|
|
41
43
|
return "○";
|
|
42
44
|
case "blocked":
|
|
43
45
|
return "↑";
|
|
46
|
+
case "skipped":
|
|
47
|
+
return "⊘";
|
|
44
48
|
case "running":
|
|
45
49
|
return "●";
|
|
46
50
|
case "paused":
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `/workflow status` list —
|
|
2
|
+
* `/workflow status` list — rounded workflow-tool output surface.
|
|
3
3
|
*
|
|
4
|
-
* Visual contract (
|
|
5
|
-
* - One
|
|
6
|
-
* - One
|
|
7
|
-
*
|
|
8
|
-
* row
|
|
9
|
-
* - Status colour is carried by the
|
|
10
|
-
*
|
|
4
|
+
* Visual contract (DESIGN.md §5):
|
|
5
|
+
* - One rounded `BACKGROUND` panel with subtitle and count badges.
|
|
6
|
+
* - One rounded card per run (replaces the indented per-stage rows):
|
|
7
|
+
* title: runId · workflow · state badge
|
|
8
|
+
* row 1: mode · progress strip · meta
|
|
9
|
+
* - Status colour is carried by the card border and state badge semantics,
|
|
10
|
+
* never by decorative body text.
|
|
11
11
|
* - One trailing hint row pointing at `/workflow status <id>` for the
|
|
12
12
|
* most-recently-active run; full per-stage detail moves into
|
|
13
13
|
* `/workflow status <id>` ({@link renderRunDetail}).
|
|
14
14
|
*
|
|
15
|
-
* Plain mode (theme omitted) preserves the
|
|
16
|
-
*
|
|
15
|
+
* Plain mode (theme omitted) preserves the rounded panel/card shape without
|
|
16
|
+
* ANSI escapes, with ASCII bracket cells `[✓][●][○][✗]`.
|
|
17
17
|
*
|
|
18
18
|
* Powers:
|
|
19
19
|
* - `renderResult({ action: "status" })` (LLM tool path)
|
|
@@ -31,15 +31,14 @@ import { elapsedRunMs, elapsedStageMs } from "../shared/timing.js";
|
|
|
31
31
|
import type { GraphTheme } from "./graph-theme.js";
|
|
32
32
|
import { fmtDuration } from "./status-helpers.js";
|
|
33
33
|
import {
|
|
34
|
-
renderFlatBand,
|
|
35
|
-
renderTaggedCard,
|
|
36
34
|
renderHintRows,
|
|
35
|
+
renderRoundedBox,
|
|
37
36
|
progressStrip,
|
|
38
37
|
ELLIPSIS,
|
|
39
38
|
chatWidth,
|
|
40
39
|
} from "./chat-surface.js";
|
|
41
40
|
import type { FlatBandBadge } from "./chat-surface.js";
|
|
42
|
-
import { hexToAnsi, RESET } from "./color-utils.js";
|
|
41
|
+
import { hexToAnsi, RESET, BOLD } from "./color-utils.js";
|
|
43
42
|
import { visibleWidth, truncateToWidth } from "./text-helpers.js";
|
|
44
43
|
|
|
45
44
|
const SHORT_ID_LEN = 6;
|
|
@@ -58,15 +57,16 @@ export interface RenderStatusListOpts {
|
|
|
58
57
|
}
|
|
59
58
|
|
|
60
59
|
/**
|
|
61
|
-
* Render a list of run snapshots as the canonical `
|
|
62
|
-
* surface: one
|
|
60
|
+
* Render a list of run snapshots as the canonical rounded `BACKGROUND`
|
|
61
|
+
* surface: one panel plus one card per run.
|
|
63
62
|
*/
|
|
64
63
|
export function renderStatusList(
|
|
65
64
|
runs: readonly RunSnapshot[],
|
|
66
65
|
opts: RenderStatusListOpts = {},
|
|
67
66
|
): string {
|
|
68
67
|
const now = opts.now ?? Date.now();
|
|
69
|
-
const width = opts.width;
|
|
68
|
+
const width = effectiveWidth(opts.width);
|
|
69
|
+
const cardWidth = Math.max(20, width - 4);
|
|
70
70
|
|
|
71
71
|
// The list shows active + recently-ended runs together. Sorting:
|
|
72
72
|
// active first, then ended, each bucket by startedAt desc.
|
|
@@ -79,99 +79,80 @@ export function renderStatusList(
|
|
|
79
79
|
? themedBadges(counts, opts.theme)
|
|
80
80
|
: plainBadges(counts);
|
|
81
81
|
|
|
82
|
-
const
|
|
83
|
-
lines.push(renderFlatBand({
|
|
84
|
-
label: "BACKGROUND",
|
|
85
|
-
subtitle,
|
|
86
|
-
badges,
|
|
87
|
-
theme: opts.theme,
|
|
88
|
-
width,
|
|
89
|
-
}));
|
|
90
|
-
// Blank line after the band — same header-vs-content separation used by
|
|
91
|
-
// /workflow list. Without it the band visually fuses into the first card.
|
|
92
|
-
lines.push("");
|
|
82
|
+
const body: string[] = [];
|
|
93
83
|
|
|
94
84
|
if (sorted.length === 0) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
// visual block.
|
|
102
|
-
for (let i = 0; i < sorted.length; i++) {
|
|
103
|
-
if (i > 0) lines.push("");
|
|
104
|
-
lines.push(renderRunCard(sorted[i]!, now, width, opts.theme));
|
|
85
|
+
body.push(` ${emptyStateLine(opts.theme)} `);
|
|
86
|
+
} else {
|
|
87
|
+
for (let i = 0; i < sorted.length; i++) {
|
|
88
|
+
if (i > 0) body.push("");
|
|
89
|
+
body.push(...renderRunEntry(sorted[i]!, now, cardWidth, opts.theme));
|
|
90
|
+
}
|
|
105
91
|
}
|
|
106
92
|
|
|
107
93
|
if (opts.showDetailHint !== false && sorted.length > 0) {
|
|
108
94
|
const sid = shortId(sorted[0]!.id);
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
renderHintRows(
|
|
95
|
+
body.push("");
|
|
96
|
+
body.push(
|
|
97
|
+
...renderHintRows(
|
|
112
98
|
[{ command: `/workflow status ${sid}`, hint: "drill into a run" }],
|
|
113
99
|
opts.theme,
|
|
114
|
-
),
|
|
100
|
+
).split("\n").map((line) => ` ${line} `),
|
|
115
101
|
);
|
|
116
102
|
}
|
|
117
103
|
|
|
118
|
-
|
|
104
|
+
const badgeText = badges && badges.length > 0 ? ` ${badges.map((b) => b.text).join(" ")}` : "";
|
|
105
|
+
return renderRoundedBox({
|
|
106
|
+
title: `BACKGROUND ${subtitle}${badgeText}`,
|
|
107
|
+
bodyLines: body,
|
|
108
|
+
theme: opts.theme,
|
|
109
|
+
width,
|
|
110
|
+
});
|
|
119
111
|
}
|
|
120
112
|
|
|
121
113
|
// ---------------------------------------------------------------------------
|
|
122
114
|
// Run card
|
|
123
115
|
// ---------------------------------------------------------------------------
|
|
124
116
|
|
|
125
|
-
function
|
|
117
|
+
function renderRunEntry(
|
|
126
118
|
run: RunSnapshot,
|
|
127
119
|
now: number,
|
|
128
|
-
width: number
|
|
120
|
+
width: number,
|
|
129
121
|
theme?: GraphTheme,
|
|
130
|
-
): string {
|
|
122
|
+
): string[] {
|
|
131
123
|
const sid = shortId(run.id);
|
|
132
|
-
const stateAccent = runAccent(run, theme);
|
|
133
124
|
const trailing = runTrailing(run, theme);
|
|
134
|
-
|
|
135
125
|
const mode = run.stages.length > 1 ? "chain " : "single";
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
// tail. Reserve ~3 cells for the meta separator and ellipsis padding.
|
|
139
|
-
const cardWidth = effectiveWidth(width);
|
|
140
|
-
const stripPrefixW = 4; // leading chat pad + "▎ "
|
|
141
|
-
const modeW = mode.length + 4; // 4-space separator after mode
|
|
142
|
-
const interior = cardWidth - stripPrefixW;
|
|
126
|
+
const bodyWidth = effectiveWidth(width);
|
|
127
|
+
const interior = Math.max(8, bodyWidth - 4);
|
|
143
128
|
const rawMeta = runCardMeta(run, now);
|
|
144
|
-
|
|
145
|
-
// reaches renderTaggedCard so long/wide stage names cannot overflow.
|
|
129
|
+
const modeW = mode.length + 4;
|
|
146
130
|
const maxMetaW = Math.max(0, interior - modeW - 3);
|
|
147
131
|
const meta = truncateToWidth(rawMeta, maxMetaW, ELLIPSIS);
|
|
148
132
|
const metaW = visibleWidth(meta);
|
|
149
|
-
const stripBudget = Math.max(0,
|
|
133
|
+
const stripBudget = Math.max(0, interior - modeW - metaW - 2);
|
|
150
134
|
|
|
151
|
-
const
|
|
152
|
-
const
|
|
153
|
-
const stripVisibleW = visibleWidth(strip);
|
|
154
|
-
|
|
155
|
-
const usedLeftW = modeW + stripVisibleW;
|
|
135
|
+
const strip = progressStrip(stageCells(run), stripBudget, theme);
|
|
136
|
+
const usedLeftW = modeW + visibleWidth(strip);
|
|
156
137
|
const gap = Math.max(metaW > 0 ? 1 : 0, interior - usedLeftW - metaW);
|
|
157
138
|
|
|
139
|
+
const glyph = statusIconForRun(run);
|
|
140
|
+
const glyphFg = theme ? hexToAnsi(runAccent(run, theme)) : "";
|
|
141
|
+
const accent = theme ? hexToAnsi(theme.accent) : "";
|
|
142
|
+
const text = theme ? hexToAnsi(theme.text) : "";
|
|
158
143
|
const muted = theme ? hexToAnsi(theme.textMuted) : "";
|
|
159
144
|
const dim = theme ? hexToAnsi(theme.dim) : "";
|
|
160
145
|
const reset = theme ? RESET : "";
|
|
161
146
|
|
|
147
|
+
const name = truncateToWidth(run.name, Math.max(MIN_TITLE_BUDGET, interior - visibleWidth(sid) - visibleWidth(trailing?.text ?? "") - 8), ELLIPSIS);
|
|
148
|
+
const line1 = theme
|
|
149
|
+
? ` ${glyphFg}${glyph}${RESET} ${accent}${sid}${RESET} ${text}${BOLD}${name}${RESET} ${glyphFg}${trailing?.text ?? ""}${RESET} `
|
|
150
|
+
: ` ${glyph} ${sid} ${name} ${trailing?.text ?? ""} `;
|
|
162
151
|
const modeSeg = theme ? `${muted}${mode}${reset}` : mode;
|
|
163
152
|
const metaSeg = theme ? `${dim}${meta}${reset}` : meta;
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
return
|
|
167
|
-
tag: sid,
|
|
168
|
-
title: run.name,
|
|
169
|
-
trailing,
|
|
170
|
-
bodyRows: [row2],
|
|
171
|
-
accent: stateAccent,
|
|
172
|
-
width,
|
|
173
|
-
theme,
|
|
174
|
-
});
|
|
153
|
+
const line2 = ` ${modeSeg} ${strip}${" ".repeat(gap)}${metaSeg} `;
|
|
154
|
+
|
|
155
|
+
return [line1, line2];
|
|
175
156
|
}
|
|
176
157
|
|
|
177
158
|
function runAccent(run: RunSnapshot, theme?: GraphTheme): string {
|
|
@@ -207,7 +188,7 @@ function runCardMeta(run: RunSnapshot, now: number): string {
|
|
|
207
188
|
const isChain = run.stages.length > 1;
|
|
208
189
|
const total = run.stages.length;
|
|
209
190
|
const done = run.stages.filter(
|
|
210
|
-
(s) => s.status === "completed" || s.status === "failed",
|
|
191
|
+
(s) => s.status === "completed" || s.status === "failed" || s.status === "skipped",
|
|
211
192
|
).length;
|
|
212
193
|
const ago = run.endedAt !== undefined
|
|
213
194
|
? `${fmtDuration(now - run.endedAt)} ago`
|
|
@@ -258,7 +239,7 @@ function lastStageDuration(run: RunSnapshot, now: number): string | undefined {
|
|
|
258
239
|
// Pick a representative stage duration: the most-recent terminal stage,
|
|
259
240
|
// or the running stage if everything's still in flight.
|
|
260
241
|
const candidate =
|
|
261
|
-
[...run.stages].reverse().find((s) => s.status === "completed" || s.status === "failed") ??
|
|
242
|
+
[...run.stages].reverse().find((s) => s.status === "completed" || s.status === "failed" || s.status === "skipped") ??
|
|
262
243
|
run.stages.find((s) => s.status === "running");
|
|
263
244
|
if (!candidate) return undefined;
|
|
264
245
|
return stageDurationString(candidate, now);
|
|
@@ -359,5 +340,16 @@ function emptyStateLine(theme?: GraphTheme): string {
|
|
|
359
340
|
return ` ${hexToAnsi(theme.dim)}no in-flight runs${RESET}`;
|
|
360
341
|
}
|
|
361
342
|
|
|
343
|
+
function statusIconForRun(run: RunSnapshot): string {
|
|
344
|
+
switch (run.status) {
|
|
345
|
+
case "completed": return "✓";
|
|
346
|
+
case "running": return "●";
|
|
347
|
+
case "failed": return "✗";
|
|
348
|
+
case "killed": return "⊘";
|
|
349
|
+
case "pending":
|
|
350
|
+
default: return "○";
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
362
354
|
// Re-export for callers that need to inspect width budgeting.
|
|
363
355
|
export { MIN_TITLE_BUDGET };
|
|
@@ -13,20 +13,19 @@
|
|
|
13
13
|
* 2. After `setWidget` we call `ui.requestRender()` to flush the new
|
|
14
14
|
* content immediately; pi-subagents does the same in its
|
|
15
15
|
* `rerenderWidget` helper.
|
|
16
|
-
* 3. The widget contents are static per snapshot (no spinner,
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* instantly without animation.
|
|
16
|
+
* 3. The widget contents are static per snapshot (no spinner), but the
|
|
17
|
+
* rendered lines include wall-clock labels (`3s`, `complete · 4s ago`)
|
|
18
|
+
* and recent-ended visibility. We therefore keep one lightweight
|
|
19
|
+
* one-shot refresh timer while the widget is visible, matching other
|
|
20
|
+
* live Atomic widgets without reintroducing a high-frequency spinner.
|
|
22
21
|
* 4. The factory builds a pi-tui `Container` of `Text` children styled
|
|
23
22
|
* via pi's runtime `Theme` (theme.fg, theme.bold). This is what
|
|
24
23
|
* makes the widget visually distinct from chat content.
|
|
25
24
|
*/
|
|
26
25
|
|
|
27
26
|
import type { Store } from "../shared/store.js";
|
|
28
|
-
import type { StoreSnapshot
|
|
29
|
-
import { buildThemedWidgetLines } from "./widget.js";
|
|
27
|
+
import type { StoreSnapshot } from "../shared/store-types.js";
|
|
28
|
+
import { buildThemedWidgetLines, nextWidgetRefreshDelayMs } from "./widget.js";
|
|
30
29
|
|
|
31
30
|
export interface PiTheme {
|
|
32
31
|
fg(color: string, text: string): string;
|
|
@@ -52,6 +51,20 @@ interface UiSlice {
|
|
|
52
51
|
requestRender?: () => void;
|
|
53
52
|
}
|
|
54
53
|
|
|
54
|
+
interface TimerHandle {
|
|
55
|
+
unref?: () => void;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface TimerApi {
|
|
59
|
+
setTimeout(handler: () => void, delayMs: number): TimerHandle;
|
|
60
|
+
clearTimeout(handle: TimerHandle): void;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const defaultTimerApi: TimerApi = {
|
|
64
|
+
setTimeout: (handler, delayMs) => setTimeout(handler, delayMs) as TimerHandle,
|
|
65
|
+
clearTimeout: (handle) => clearTimeout(handle as ReturnType<typeof setTimeout>),
|
|
66
|
+
};
|
|
67
|
+
|
|
55
68
|
export interface LiveWidgetAPI {
|
|
56
69
|
ui?: UiSlice;
|
|
57
70
|
on?: (event: string, handler: (payload: unknown, context?: unknown) => void) => void;
|
|
@@ -84,22 +97,44 @@ function widgetFactory(snap: StoreSnapshot): WidgetFactory {
|
|
|
84
97
|
export function installStoreWidget(
|
|
85
98
|
pi: LiveWidgetAPI,
|
|
86
99
|
storeInstance: Store,
|
|
100
|
+
timers: TimerApi = defaultTimerApi,
|
|
87
101
|
): () => void {
|
|
88
102
|
const ui = pi.ui;
|
|
89
103
|
if (!ui?.setWidget) return () => {};
|
|
90
104
|
|
|
105
|
+
let disposed = false;
|
|
106
|
+
let refreshTimer: TimerHandle | undefined;
|
|
107
|
+
|
|
108
|
+
const clearRefreshTimer = (): void => {
|
|
109
|
+
if (refreshTimer === undefined) return;
|
|
110
|
+
timers.clearTimeout(refreshTimer);
|
|
111
|
+
refreshTimer = undefined;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const scheduleRefresh = (snap: StoreSnapshot): void => {
|
|
115
|
+
const delayMs = nextWidgetRefreshDelayMs(snap);
|
|
116
|
+
if (delayMs === undefined) return;
|
|
117
|
+
refreshTimer = timers.setTimeout(() => {
|
|
118
|
+
refreshTimer = undefined;
|
|
119
|
+
rerender();
|
|
120
|
+
}, delayMs);
|
|
121
|
+
refreshTimer.unref?.();
|
|
122
|
+
};
|
|
123
|
+
|
|
91
124
|
const rerender = (): void => {
|
|
125
|
+
if (disposed) return;
|
|
126
|
+
clearRefreshTimer();
|
|
92
127
|
try {
|
|
93
128
|
const snap = storeInstance.snapshot();
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
);
|
|
97
|
-
if (!hasActiveRuns) {
|
|
129
|
+
const previewLines = buildThemedWidgetLines(snap, undefined);
|
|
130
|
+
if (previewLines.length === 0) {
|
|
98
131
|
ui.setWidget?.(WIDGET_KEY, undefined);
|
|
132
|
+
ui.requestRender?.();
|
|
99
133
|
return;
|
|
100
134
|
}
|
|
101
135
|
ui.setWidget?.(WIDGET_KEY, widgetFactory(snap), { placement: "aboveEditor" });
|
|
102
136
|
ui.requestRender?.();
|
|
137
|
+
scheduleRefresh(snap);
|
|
103
138
|
} catch (err) {
|
|
104
139
|
if (isStale(err)) return;
|
|
105
140
|
throw err;
|
|
@@ -110,9 +145,12 @@ export function installStoreWidget(
|
|
|
110
145
|
rerender();
|
|
111
146
|
|
|
112
147
|
return () => {
|
|
148
|
+
disposed = true;
|
|
149
|
+
clearRefreshTimer();
|
|
113
150
|
unsubscribe();
|
|
114
151
|
try {
|
|
115
152
|
ui.setWidget?.(WIDGET_KEY, undefined);
|
|
153
|
+
ui.requestRender?.();
|
|
116
154
|
} catch (err) {
|
|
117
155
|
if (!isStale(err)) throw err;
|
|
118
156
|
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import type { WorkflowInputEntry } from "../extension/render-result.js";
|
|
2
|
+
import type { GraphTheme } from "./graph-theme.js";
|
|
3
|
+
import { BOLD, hexBg, hexToAnsi, paint, RESET } from "./color-utils.js";
|
|
4
|
+
import {
|
|
5
|
+
formatReviewValue,
|
|
6
|
+
renderWrappedPrefixedLines,
|
|
7
|
+
truncateToWidth,
|
|
8
|
+
visibleWidth,
|
|
9
|
+
wrapPlainText,
|
|
10
|
+
} from "./text-helpers.js";
|
|
11
|
+
|
|
12
|
+
export interface SubmitPaneRenderOpts {
|
|
13
|
+
workflowName: string;
|
|
14
|
+
fields: readonly WorkflowInputEntry[];
|
|
15
|
+
rawText: Record<string, string>;
|
|
16
|
+
theme: GraphTheme;
|
|
17
|
+
width: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface SubmitControlsRenderOpts {
|
|
21
|
+
invalidFieldNames: readonly string[];
|
|
22
|
+
submitFocused: boolean;
|
|
23
|
+
theme: GraphTheme;
|
|
24
|
+
width: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function renderWorkflowFormFooterHints(theme: GraphTheme, width: number): string {
|
|
28
|
+
const hints = [
|
|
29
|
+
"Enter to select · ↑/↓ to navigate · Tab through questions to Submit · Esc to cancel",
|
|
30
|
+
"Enter · ↑/↓ · Tab to Submit · Esc",
|
|
31
|
+
"Enter · Tab · Esc",
|
|
32
|
+
"Esc",
|
|
33
|
+
];
|
|
34
|
+
const selected = hints.find((hint) => visibleWidth(hint) <= width) ?? hints[hints.length - 1]!;
|
|
35
|
+
return paint(truncateToWidth(selected, width, "…"), theme.dim);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function renderAskChoiceRows(
|
|
39
|
+
index: number,
|
|
40
|
+
label: string,
|
|
41
|
+
active: boolean,
|
|
42
|
+
theme: GraphTheme,
|
|
43
|
+
width: number,
|
|
44
|
+
): string[] {
|
|
45
|
+
const plainPrefix = `${active ? "❯ " : " "}${index}. `;
|
|
46
|
+
const firstPrefix = `${active ? paint("❯ ", theme.accent) : " "}${index}. `;
|
|
47
|
+
return renderWrappedPrefixedLines({
|
|
48
|
+
text: label,
|
|
49
|
+
width,
|
|
50
|
+
plainPrefix,
|
|
51
|
+
firstPrefix,
|
|
52
|
+
styleLine: (line) => active
|
|
53
|
+
? paint(line, theme.accent, { bold: true })
|
|
54
|
+
: paint(line, theme.textMuted),
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function renderSubmitReview(opts: SubmitPaneRenderOpts): string[] {
|
|
59
|
+
const { workflowName, fields, rawText, theme, width } = opts;
|
|
60
|
+
const out: string[] = [paint("Review your inputs", theme.accent, { bold: true }), ""];
|
|
61
|
+
out.push(truncateToWidth(paint("/workflow ", theme.dim) + paint(workflowName, theme.text), width, "…", true));
|
|
62
|
+
for (const field of fields) {
|
|
63
|
+
out.push(truncateToWidth(paint(" ● ", theme.dim) + paint(field.name, theme.textMuted), width, "…", true));
|
|
64
|
+
out.push(...renderReviewValueRows(field, rawText[field.name] ?? "", theme, width));
|
|
65
|
+
}
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function renderSubmitControls(opts: SubmitControlsRenderOpts): string[] {
|
|
70
|
+
const { invalidFieldNames, submitFocused, theme, width } = opts;
|
|
71
|
+
const invalidCount = invalidFieldNames.length;
|
|
72
|
+
const submitLabel = "SUBMIT";
|
|
73
|
+
const lines: string[] = [];
|
|
74
|
+
if (invalidCount > 0) {
|
|
75
|
+
lines.push(
|
|
76
|
+
...wrapPlainText(
|
|
77
|
+
`Answer remaining inputs before submitting: ${invalidFieldNames.join(", ")}`,
|
|
78
|
+
width,
|
|
79
|
+
).map((line) => paint(line, theme.warning)),
|
|
80
|
+
"",
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
lines.push(...renderSubmitToolbar(submitLabel, submitFocused, theme, width));
|
|
84
|
+
return lines;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function renderSubmitToolbar(
|
|
88
|
+
label: string,
|
|
89
|
+
focused: boolean,
|
|
90
|
+
theme: GraphTheme,
|
|
91
|
+
width: number,
|
|
92
|
+
): string[] {
|
|
93
|
+
const chromeBg = hexBg(theme.backgroundPanel);
|
|
94
|
+
const button = renderCompactSubmitButton(label, focused, theme, chromeBg);
|
|
95
|
+
const textFg = hexToAnsi(theme.text);
|
|
96
|
+
const mutedFg = hexToAnsi(theme.textMuted);
|
|
97
|
+
const dimFg = hexToAnsi(theme.dim);
|
|
98
|
+
const hint = (key: string, description: string): string =>
|
|
99
|
+
`${chromeBg}${textFg}${BOLD}${key}${RESET}${chromeBg}${mutedFg} ${description}${RESET}${chromeBg}`;
|
|
100
|
+
const hints = [
|
|
101
|
+
hint("enter", "Submit"),
|
|
102
|
+
hint("tab", "Next"),
|
|
103
|
+
hint("shift+tab", "Prev"),
|
|
104
|
+
hint("esc", "Cancel"),
|
|
105
|
+
].join(`${chromeBg}${dimFg} · ${RESET}${chromeBg}`);
|
|
106
|
+
const leftPad = 1;
|
|
107
|
+
const gap = 2;
|
|
108
|
+
const rightPad = 1;
|
|
109
|
+
const hintBudget = Math.max(0, width - leftPad - button.visibleWidth - gap - rightPad);
|
|
110
|
+
const fittedHints = truncateToWidth(hints, hintBudget, "…", true);
|
|
111
|
+
const hintWidth = visibleWidth(fittedHints);
|
|
112
|
+
const filler = Math.max(0, width - leftPad - button.visibleWidth - gap - hintWidth - rightPad);
|
|
113
|
+
const line = `${chromeBg} ${button.text}${chromeBg}${" ".repeat(gap)}${fittedHints}${chromeBg}${" ".repeat(filler)} ${RESET}`;
|
|
114
|
+
const clipped = truncateToWidth(line, width, "", true);
|
|
115
|
+
return [`${clipped}${chromeBg}${" ".repeat(Math.max(0, width - visibleWidth(clipped)))}${RESET}`];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function renderCompactSubmitButton(
|
|
119
|
+
label: string,
|
|
120
|
+
focused: boolean,
|
|
121
|
+
theme: GraphTheme,
|
|
122
|
+
chromeBg: string,
|
|
123
|
+
): { text: string; visibleWidth: number } {
|
|
124
|
+
const plain = ` ${label} `;
|
|
125
|
+
if (focused) {
|
|
126
|
+
const accentBg = hexBg(theme.accent);
|
|
127
|
+
const fg = hexToAnsi(theme.backgroundPanel);
|
|
128
|
+
return {
|
|
129
|
+
text: `${accentBg}${fg}${BOLD}${plain}${RESET}${chromeBg}`,
|
|
130
|
+
visibleWidth: visibleWidth(plain),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
const labelFg = hexToAnsi(theme.accent);
|
|
134
|
+
return {
|
|
135
|
+
text: `${chromeBg}${labelFg}${BOLD}${plain}${RESET}${chromeBg}`,
|
|
136
|
+
visibleWidth: visibleWidth(plain),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function renderReviewValueRows(
|
|
141
|
+
field: WorkflowInputEntry,
|
|
142
|
+
raw: string,
|
|
143
|
+
theme: GraphTheme,
|
|
144
|
+
width: number,
|
|
145
|
+
): string[] {
|
|
146
|
+
const plainPrefix = " → ";
|
|
147
|
+
const firstPrefix = " " + paint("→ ", theme.dim);
|
|
148
|
+
return renderWrappedPrefixedLines({
|
|
149
|
+
text: formatFieldReviewValue(field, raw),
|
|
150
|
+
width,
|
|
151
|
+
plainPrefix,
|
|
152
|
+
firstPrefix,
|
|
153
|
+
styleLine: (line) => paint(line, theme.text),
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function formatFieldReviewValue(field: WorkflowInputEntry, raw: string): string {
|
|
158
|
+
if (field.type === "boolean") {
|
|
159
|
+
const normalized = raw.trim().toLowerCase();
|
|
160
|
+
if (normalized.length === 0) return formatReviewValue(raw);
|
|
161
|
+
return normalized === "true" || normalized === "1" ? "on" : "off";
|
|
162
|
+
}
|
|
163
|
+
return formatReviewValue(raw);
|
|
164
|
+
}
|
|
@@ -15,7 +15,7 @@ import type { StageSnapshot } from "../shared/store-types.js";
|
|
|
15
15
|
import type { GraphTheme } from "./graph-theme.js";
|
|
16
16
|
import { statusIcon, statusColor } from "./status-helpers.js";
|
|
17
17
|
import { hexToAnsi, hexBg, RESET, BOLD } from "./color-utils.js";
|
|
18
|
-
import { truncateToWidth, visibleWidth } from "./text-helpers.js";
|
|
18
|
+
import { sliceColumns, truncateToWidth, visibleWidth } from "./text-helpers.js";
|
|
19
19
|
|
|
20
20
|
export interface SwitcherState {
|
|
21
21
|
query: string;
|
|
@@ -45,6 +45,25 @@ function padVisible(s: string, width: number): string {
|
|
|
45
45
|
return clipped + " ".repeat(Math.max(0, width - visibleWidth(clipped)));
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Truncate without appending any ANSI reset before the suffix.
|
|
50
|
+
*
|
|
51
|
+
* Selected rows are wrapped in a single accent run after row text is composed;
|
|
52
|
+
* inserting RESET before the ellipsis would prematurely end that highlight.
|
|
53
|
+
*/
|
|
54
|
+
function truncateToWidthWithoutReset(text: string, width: number, suffix = ""): string {
|
|
55
|
+
const targetWidth = Math.max(0, width);
|
|
56
|
+
if (visibleWidth(text) <= targetWidth) return text;
|
|
57
|
+
if (targetWidth === 0) return "";
|
|
58
|
+
|
|
59
|
+
const suffixWidth = visibleWidth(suffix);
|
|
60
|
+
if (suffixWidth >= targetWidth) {
|
|
61
|
+
return sliceColumns(suffix, 0, targetWidth, true);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return `${sliceColumns(text, 0, targetWidth - suffixWidth, true)}${suffix}`;
|
|
65
|
+
}
|
|
66
|
+
|
|
48
67
|
function statusText(status: StageSnapshot["status"]): string {
|
|
49
68
|
return status === "awaiting_input"
|
|
50
69
|
? "awaiting"
|
|
@@ -94,8 +113,12 @@ export function renderSwitcher(
|
|
|
94
113
|
`${dim}${hint}${RESET}${panelBg}`;
|
|
95
114
|
lines.push(`${border}│${RESET}${padVisible(header, innerWidth)}${border}│${RESET}`);
|
|
96
115
|
|
|
97
|
-
// Quiet rule under header
|
|
98
|
-
|
|
116
|
+
// Quiet rule under header. Keep it inside the rounded panel instead of
|
|
117
|
+
// joining into the side borders with square tee glyphs.
|
|
118
|
+
const rule = innerWidth <= 2
|
|
119
|
+
? " ".repeat(innerWidth)
|
|
120
|
+
: " " + `${dim}${"─".repeat(innerWidth - 2)}${RESET}${panelBg}` + " ";
|
|
121
|
+
lines.push(`${border}│${RESET}${panelBg}${rule}${border}│${RESET}`);
|
|
99
122
|
|
|
100
123
|
// Stage list — viewport of 8 around selection
|
|
101
124
|
const maxVisible = 8;
|
|
@@ -119,7 +142,7 @@ export function renderSwitcher(
|
|
|
119
142
|
if (isSelected) {
|
|
120
143
|
const metaWidth = visibleWidth(label);
|
|
121
144
|
const nameBudget = Math.max(4, innerWidth - visibleWidth(` ${icon} `) - metaWidth - 2);
|
|
122
|
-
const selectedName =
|
|
145
|
+
const selectedName = truncateToWidthWithoutReset(stage.name, nameBudget, "…");
|
|
123
146
|
const prefix = ` ${icon} ${selectedName}`;
|
|
124
147
|
const visibleRow = `${prefix}${" ".repeat(Math.max(1, innerWidth - visibleWidth(`${prefix}${label}`) - 1))}${label} `;
|
|
125
148
|
const padded = padVisible(visibleRow, innerWidth);
|