gsd-pi 2.81.0 → 2.82.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/README.md +36 -24
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/gsd/auto/loop.js +111 -8
- package/dist/resources/extensions/gsd/auto/phases.js +190 -97
- package/dist/resources/extensions/gsd/auto/run-unit.js +66 -3
- package/dist/resources/extensions/gsd/auto/session.js +9 -0
- package/dist/resources/extensions/gsd/auto/verification-retry-policy.js +43 -0
- package/dist/resources/extensions/gsd/auto-dashboard.js +182 -178
- package/dist/resources/extensions/gsd/auto-dispatch.js +14 -11
- package/dist/resources/extensions/gsd/auto-post-unit.js +7 -1
- package/dist/resources/extensions/gsd/auto-recovery.js +6 -181
- package/dist/resources/extensions/gsd/auto-runtime-state.js +5 -0
- package/dist/resources/extensions/gsd/auto-start.js +20 -23
- package/dist/resources/extensions/gsd/auto-unit-closeout.js +33 -5
- package/dist/resources/extensions/gsd/auto-verification.js +12 -6
- package/dist/resources/extensions/gsd/auto-worktree.js +8 -0
- package/dist/resources/extensions/gsd/auto.js +265 -76
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +13 -6
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +8 -2
- package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +4 -8
- package/dist/resources/extensions/gsd/commands/handlers/notifications-handler.js +4 -10
- package/dist/resources/extensions/gsd/commands/handlers/parallel.js +9 -0
- package/dist/resources/extensions/gsd/git-service.js +2 -1
- package/dist/resources/extensions/gsd/gsd-db.js +7 -23
- package/dist/resources/extensions/gsd/health-widget-core.js +1 -1
- package/dist/resources/extensions/gsd/health-widget.js +4 -10
- package/dist/resources/extensions/gsd/markdown-renderer.js +0 -95
- package/dist/resources/extensions/gsd/native-git-bridge.js +14 -14
- package/dist/resources/extensions/gsd/notification-overlay.js +35 -40
- package/dist/resources/extensions/gsd/parallel-merge.js +53 -30
- package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +25 -33
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +14 -12
- package/dist/resources/extensions/gsd/prompts/discuss-headless.md +20 -2
- package/dist/resources/extensions/gsd/prompts/discuss.md +20 -2
- package/dist/resources/extensions/gsd/recovery-classification.js +15 -1
- package/dist/resources/extensions/gsd/session-lock.js +40 -0
- package/dist/resources/extensions/gsd/state-reconciliation/drift/completion.js +131 -0
- package/dist/resources/extensions/gsd/state-reconciliation/drift/merge-state.js +247 -0
- package/dist/resources/extensions/gsd/state-reconciliation/drift/project-md.js +50 -0
- package/dist/resources/extensions/gsd/state-reconciliation/drift/roadmap.js +87 -0
- package/dist/resources/extensions/gsd/state-reconciliation/drift/sketch-flag.js +50 -0
- package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-render.js +124 -0
- package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-worker.js +32 -0
- package/dist/resources/extensions/gsd/state-reconciliation/errors.js +41 -0
- package/dist/resources/extensions/gsd/state-reconciliation/index.js +99 -0
- package/dist/resources/extensions/gsd/state-reconciliation/registry.js +24 -0
- package/dist/resources/extensions/gsd/state-reconciliation/spawn-gate.js +43 -0
- package/dist/resources/extensions/gsd/state-reconciliation/types.js +3 -0
- package/dist/resources/extensions/gsd/state-reconciliation.js +5 -26
- package/dist/resources/extensions/gsd/tui/render-kit.js +74 -0
- package/dist/resources/extensions/gsd/watch/header-renderer.js +92 -69
- package/dist/resources/extensions/gsd/watch/splash-palette.js +10 -0
- package/dist/resources/extensions/gsd/workflow-mcp.js +2 -2
- package/dist/resources/extensions/gsd/worktree-lifecycle.js +722 -316
- package/dist/resources/extensions/gsd/worktree-telemetry.js +3 -1
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +9 -9
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +9 -9
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/welcome-screen.d.ts +0 -7
- package/dist/welcome-screen.js +60 -69
- package/package.json +1 -1
- package/packages/daemon/package.json +2 -2
- package/packages/mcp-server/package.json +2 -2
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/assistant-message-design.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/assistant-message-design.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/assistant-message-design.test.js +47 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/assistant-message-design.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +76 -9
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/user-message-design.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/user-message-design.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/user-message-design.test.js +40 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/user-message-design.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts +0 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js +30 -29
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js +10 -3
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +13 -13
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.d.ts +1 -3
- package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.js +58 -3
- package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/diff.d.ts +2 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/diff.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/diff.js +12 -6
- package/packages/pi-coding-agent/dist/modes/interactive/components/diff.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +14 -41
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +0 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +86 -82
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/transcript-design.d.ts +35 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/transcript-design.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/transcript-design.js +152 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/transcript-design.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tui-style-kit.d.ts +16 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tui-style-kit.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tui-style-kit.js +73 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tui-style-kit.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js +12 -8
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-highlight.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-highlight.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-highlight.test.js +17 -0
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-highlight.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +105 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +27 -26
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js +9 -6
- package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/assistant-message-design.test.ts +56 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +113 -9
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/user-message-design.test.ts +48 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.test.ts +10 -3
- package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.ts +43 -42
- package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +14 -14
- package/packages/pi-coding-agent/src/modes/interactive/components/bash-execution.ts +64 -3
- package/packages/pi-coding-agent/src/modes/interactive/components/diff.ts +13 -7
- package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +15 -42
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +84 -104
- package/packages/pi-coding-agent/src/modes/interactive/components/transcript-design.ts +196 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/tui-style-kit.ts +94 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/user-message.ts +14 -9
- package/packages/pi-coding-agent/src/modes/interactive/theme/theme-highlight.test.ts +23 -0
- package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +106 -1
- package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +27 -26
- package/packages/pi-coding-agent/src/modes/interactive/tui-mode.test.ts +9 -6
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-tui/dist/__tests__/overlay-layout.test.js +14 -1
- package/packages/pi-tui/dist/__tests__/overlay-layout.test.js.map +1 -1
- package/packages/pi-tui/dist/overlay-layout.d.ts.map +1 -1
- package/packages/pi-tui/dist/overlay-layout.js +9 -6
- package/packages/pi-tui/dist/overlay-layout.js.map +1 -1
- package/packages/pi-tui/package.json +1 -1
- package/packages/pi-tui/src/__tests__/overlay-layout.test.ts +20 -1
- package/packages/pi-tui/src/overlay-layout.ts +10 -7
- package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
- package/packages/rpc-client/package.json +1 -1
- package/pkg/dist/modes/interactive/theme/theme-highlight.test.d.ts +2 -0
- package/pkg/dist/modes/interactive/theme/theme-highlight.test.d.ts.map +1 -0
- package/pkg/dist/modes/interactive/theme/theme-highlight.test.js +17 -0
- package/pkg/dist/modes/interactive/theme/theme-highlight.test.js.map +1 -0
- package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/pkg/dist/modes/interactive/theme/theme.js +105 -1
- package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
- package/pkg/dist/modes/interactive/theme/themes.d.ts.map +1 -1
- package/pkg/dist/modes/interactive/theme/themes.js +27 -26
- package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/gsd/auto/loop-deps.ts +9 -5
- package/src/resources/extensions/gsd/auto/loop.ts +113 -9
- package/src/resources/extensions/gsd/auto/phases.ts +144 -19
- package/src/resources/extensions/gsd/auto/run-unit.ts +69 -4
- package/src/resources/extensions/gsd/auto/session.ts +10 -0
- package/src/resources/extensions/gsd/auto/verification-retry-policy.ts +82 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +230 -183
- package/src/resources/extensions/gsd/auto-dispatch.ts +15 -1
- package/src/resources/extensions/gsd/auto-post-unit.ts +7 -1
- package/src/resources/extensions/gsd/auto-recovery.ts +7 -209
- package/src/resources/extensions/gsd/auto-runtime-state.ts +5 -0
- package/src/resources/extensions/gsd/auto-start.ts +22 -22
- package/src/resources/extensions/gsd/auto-unit-closeout.ts +51 -0
- package/src/resources/extensions/gsd/auto-verification.ts +12 -6
- package/src/resources/extensions/gsd/auto-worktree.ts +8 -0
- package/src/resources/extensions/gsd/auto.ts +295 -75
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +21 -6
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +6 -2
- package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +5 -8
- package/src/resources/extensions/gsd/commands/handlers/notifications-handler.ts +4 -10
- package/src/resources/extensions/gsd/commands/handlers/parallel.ts +12 -0
- package/src/resources/extensions/gsd/git-service.ts +2 -0
- package/src/resources/extensions/gsd/gsd-db.ts +7 -23
- package/src/resources/extensions/gsd/health-widget-core.ts +1 -1
- package/src/resources/extensions/gsd/health-widget.ts +6 -10
- package/src/resources/extensions/gsd/journal.ts +2 -0
- package/src/resources/extensions/gsd/markdown-renderer.ts +4 -95
- package/src/resources/extensions/gsd/native-git-bridge.ts +14 -13
- package/src/resources/extensions/gsd/notification-overlay.ts +50 -46
- package/src/resources/extensions/gsd/parallel-merge.ts +61 -34
- package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +33 -35
- package/src/resources/extensions/gsd/prompts/complete-slice.md +14 -12
- package/src/resources/extensions/gsd/prompts/discuss-headless.md +20 -2
- package/src/resources/extensions/gsd/prompts/discuss.md +20 -2
- package/src/resources/extensions/gsd/recovery-classification.ts +18 -1
- package/src/resources/extensions/gsd/session-lock.ts +41 -0
- package/src/resources/extensions/gsd/state-reconciliation/drift/completion.ts +172 -0
- package/src/resources/extensions/gsd/state-reconciliation/drift/merge-state.ts +337 -0
- package/src/resources/extensions/gsd/state-reconciliation/drift/project-md.ts +69 -0
- package/src/resources/extensions/gsd/state-reconciliation/drift/roadmap.ts +109 -0
- package/src/resources/extensions/gsd/state-reconciliation/drift/sketch-flag.ts +68 -0
- package/src/resources/extensions/gsd/state-reconciliation/drift/stale-render.ts +185 -0
- package/src/resources/extensions/gsd/state-reconciliation/drift/stale-worker.ts +46 -0
- package/src/resources/extensions/gsd/state-reconciliation/errors.ts +67 -0
- package/src/resources/extensions/gsd/state-reconciliation/index.ts +142 -0
- package/src/resources/extensions/gsd/state-reconciliation/registry.ts +27 -0
- package/src/resources/extensions/gsd/state-reconciliation/spawn-gate.ts +60 -0
- package/src/resources/extensions/gsd/state-reconciliation/types.ts +83 -0
- package/src/resources/extensions/gsd/state-reconciliation.ts +21 -53
- package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +99 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +654 -176
- package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +291 -4
- package/src/resources/extensions/gsd/tests/auto-runtime-state.test.ts +16 -1
- package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +18 -0
- package/src/resources/extensions/gsd/tests/auto-unit-closeout.test.ts +68 -0
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +28 -1
- package/src/resources/extensions/gsd/tests/deep-project-auto-loop.test.ts +20 -2
- package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +44 -0
- package/src/resources/extensions/gsd/tests/header-renderer.test.ts +40 -0
- package/src/resources/extensions/gsd/tests/headless-milestone-parity.test.ts +10 -0
- package/src/resources/extensions/gsd/tests/health-widget.test.ts +14 -4
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +26 -0
- package/src/resources/extensions/gsd/tests/integration/integration-proof.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/integration/parallel-merge.test.ts +116 -24
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +0 -1
- package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +46 -11
- package/src/resources/extensions/gsd/tests/notification-overlay.test.ts +78 -41
- package/src/resources/extensions/gsd/tests/notifications-handler.test.ts +44 -0
- package/src/resources/extensions/gsd/tests/originalbase-path-comparison.test.ts +12 -217
- package/src/resources/extensions/gsd/tests/parallel-monitor-overlay.test.ts +38 -6
- package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/progressive-planning.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +24 -1
- package/src/resources/extensions/gsd/tests/resume-dispatch-worktree.test.ts +7 -3
- package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +6 -3
- package/src/resources/extensions/gsd/tests/session-switch-abort-misclassification.test.ts +24 -0
- package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +65 -58
- package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +952 -0
- package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/tui-header-lifecycle.test.ts +121 -1
- package/src/resources/extensions/gsd/tests/tui-render-kit.test.ts +66 -0
- package/src/resources/extensions/gsd/tests/verification-retry-policy.test.ts +83 -0
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +6 -0
- package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +158 -58
- package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +572 -118
- package/src/resources/extensions/gsd/tests/worktree-telemetry.test.ts +59 -2
- package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +18 -0
- package/src/resources/extensions/gsd/tui/render-kit.ts +109 -0
- package/src/resources/extensions/gsd/watch/header-renderer.ts +121 -79
- package/src/resources/extensions/gsd/watch/splash-palette.ts +11 -0
- package/src/resources/extensions/gsd/workflow-mcp.ts +2 -2
- package/src/resources/extensions/gsd/worktree-lifecycle.ts +1151 -524
- package/src/resources/extensions/gsd/worktree-telemetry.ts +7 -2
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +0 -1544
- /package/dist/web/standalone/.next/static/{drLMkgfHQ8lzS229_HWYR → S44UQTFCUdA44dkjfYt6S}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{drLMkgfHQ8lzS229_HWYR → S44UQTFCUdA44dkjfYt6S}/_ssgManifest.js +0 -0
|
@@ -16,7 +16,7 @@ import { canonicalToolName, clearDiscussionFlowState, isDepthConfirmationAnswer,
|
|
|
16
16
|
import { resolveManifest } from "../unit-context-manifest.js";
|
|
17
17
|
import { isBlockedStateFile, isBashWriteToStateFile, BLOCKED_WRITE_ERROR } from "../write-intercept.js";
|
|
18
18
|
import { loadFile, saveFile, formatContinue } from "../files.js";
|
|
19
|
-
import { getAutoRuntimeSnapshot, isAutoActive, isAutoPaused, markToolEnd, markToolStart, recordToolInvocationError } from "../auto-runtime-state.js";
|
|
19
|
+
import { clearToolInvocationError, getAutoRuntimeSnapshot, isAutoActive, isAutoPaused, markToolEnd, markToolStart, recordToolInvocationError } from "../auto-runtime-state.js";
|
|
20
20
|
|
|
21
21
|
import { checkToolCallLoop, resetToolCallLoopGuard } from "./tool-call-loop-guard.js";
|
|
22
22
|
import { saveActivityLog } from "../activity-log.js";
|
|
@@ -143,7 +143,7 @@ const AUTO_UNIT_SCOPED_TOOLS: Record<string, readonly string[]> = {
|
|
|
143
143
|
"plan-slice": ["gsd_plan_slice", "gsd_plan_task", "gsd_decision_save"],
|
|
144
144
|
"refine-slice": ["gsd_plan_slice", "gsd_plan_task", "gsd_decision_save"],
|
|
145
145
|
"replan-slice": ["gsd_replan_slice", "gsd_plan_task", "gsd_decision_save"],
|
|
146
|
-
"complete-slice": ["gsd_slice_complete", "gsd_decision_save", "gsd_requirement_update", "subagent"],
|
|
146
|
+
"complete-slice": ["gsd_slice_complete", "gsd_task_reopen", "gsd_replan_slice", "gsd_decision_save", "gsd_requirement_update", "subagent"],
|
|
147
147
|
"reassess-roadmap": ["gsd_reassess_roadmap"],
|
|
148
148
|
"execute-task": ["gsd_task_complete", "gsd_decision_save"],
|
|
149
149
|
"execute-task-simple": ["gsd_task_complete", "gsd_decision_save"],
|
|
@@ -892,6 +892,8 @@ export function registerHooks(
|
|
|
892
892
|
// Let recordToolInvocationError classify the failure so non-gsd_ harness
|
|
893
893
|
// errors and deterministic policy rejections are handled consistently.
|
|
894
894
|
recordToolInvocationError(event.toolName, errorText);
|
|
895
|
+
} else if (isAutoActive()) {
|
|
896
|
+
clearToolInvocationError();
|
|
895
897
|
}
|
|
896
898
|
const toolName = canonicalToolName(event.toolName);
|
|
897
899
|
if (toolName !== "ask_user_questions") return;
|
|
@@ -1009,6 +1011,8 @@ export function registerHooks(
|
|
|
1009
1011
|
// Let recordToolInvocationError classify the failure so non-gsd_ harness
|
|
1010
1012
|
// errors and deterministic policy rejections are handled consistently.
|
|
1011
1013
|
recordToolInvocationError(event.toolName, errorText);
|
|
1014
|
+
} else if (isAutoActive()) {
|
|
1015
|
+
clearToolInvocationError();
|
|
1012
1016
|
}
|
|
1013
1017
|
// Safety harness: record tool execution results for evidence cross-referencing
|
|
1014
1018
|
if (isAutoActive()) {
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// Project/App: GSD-2
|
|
2
|
+
// File Purpose: Registers GSD keyboard shortcuts for dashboard, notifications, and parallel overlays.
|
|
3
|
+
|
|
1
4
|
import { existsSync } from "node:fs";
|
|
2
5
|
import { join } from "node:path";
|
|
3
6
|
|
|
@@ -39,18 +42,12 @@ export function registerShortcuts(pi: ExtensionAPI): void {
|
|
|
39
42
|
};
|
|
40
43
|
|
|
41
44
|
const openNotificationsOverlay = async (ctx: ExtensionContext) => {
|
|
42
|
-
const { GSDNotificationOverlay } = await import("../notification-overlay.js");
|
|
45
|
+
const { GSDNotificationOverlay, notificationOverlayOptions } = await import("../notification-overlay.js");
|
|
43
46
|
await ctx.ui.custom<boolean>(
|
|
44
47
|
(tui, theme, _kb, done) => new GSDNotificationOverlay(tui, theme, () => done(true)),
|
|
45
48
|
{
|
|
46
49
|
overlay: true,
|
|
47
|
-
overlayOptions:
|
|
48
|
-
width: "80%",
|
|
49
|
-
minWidth: 60,
|
|
50
|
-
maxHeight: "88%",
|
|
51
|
-
anchor: "center",
|
|
52
|
-
backdrop: true,
|
|
53
|
-
},
|
|
50
|
+
overlayOptions: notificationOverlayOptions(),
|
|
54
51
|
},
|
|
55
52
|
);
|
|
56
53
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
1
|
+
// Project/App: GSD-2
|
|
2
|
+
// File Purpose: Handles /gsd notifications commands and opens the notification history overlay.
|
|
3
3
|
|
|
4
4
|
import type { ExtensionAPI, ExtensionCommandContext } from "@gsd/pi-coding-agent";
|
|
5
5
|
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
unsuppressPersistence,
|
|
12
12
|
type NotifySeverity,
|
|
13
13
|
} from "../../notification-store.js";
|
|
14
|
-
import { GSDNotificationOverlay } from "../../notification-overlay.js";
|
|
14
|
+
import { GSDNotificationOverlay, notificationOverlayOptions } from "../../notification-overlay.js";
|
|
15
15
|
|
|
16
16
|
const MAX_INLINE_ENTRIES = 40;
|
|
17
17
|
|
|
@@ -108,13 +108,7 @@ export async function handleNotificationsCommand(
|
|
|
108
108
|
(tui, theme, _kb, done) => new GSDNotificationOverlay(tui, theme, () => done(true)),
|
|
109
109
|
{
|
|
110
110
|
overlay: true,
|
|
111
|
-
overlayOptions:
|
|
112
|
-
width: "80%",
|
|
113
|
-
minWidth: 60,
|
|
114
|
-
maxHeight: "88%",
|
|
115
|
-
anchor: "center",
|
|
116
|
-
backdrop: true,
|
|
117
|
-
},
|
|
111
|
+
overlayOptions: notificationOverlayOptions(),
|
|
118
112
|
},
|
|
119
113
|
);
|
|
120
114
|
if (result !== undefined) {
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
import { formatEligibilityReport } from "../../parallel-eligibility.js";
|
|
15
15
|
import { formatMergeResults, mergeAllCompleted, mergeCompletedMilestone } from "../../parallel-merge.js";
|
|
16
16
|
import { loadEffectiveGSDPreferences, resolveParallelConfig } from "../../preferences.js";
|
|
17
|
+
import { reconcileBeforeSpawn } from "../../state-reconciliation.js";
|
|
17
18
|
import { projectRoot } from "../context.js";
|
|
18
19
|
function emitParallelMessage(pi: ExtensionAPI, content: string): void {
|
|
19
20
|
pi.sendMessage({ customType: "gsd-parallel", content, display: true });
|
|
@@ -40,6 +41,17 @@ export async function handleParallelCommand(trimmed: string, _ctx: ExtensionComm
|
|
|
40
41
|
emitParallelMessage(pi, `${report}\n\nNo milestones are eligible for parallel execution.`);
|
|
41
42
|
return true;
|
|
42
43
|
}
|
|
44
|
+
// ADR-017 #5707: reconcile before spawning so workers don't independently
|
|
45
|
+
// race on the same drift. Failures abort the spawn with an actionable
|
|
46
|
+
// user-visible message.
|
|
47
|
+
const gate = await reconcileBeforeSpawn(root);
|
|
48
|
+
if (!gate.ok) {
|
|
49
|
+
emitParallelMessage(
|
|
50
|
+
pi,
|
|
51
|
+
`${report}\n\nParallel orchestration aborted before spawn — ${gate.reason}`,
|
|
52
|
+
);
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
43
55
|
const result = await startParallel(
|
|
44
56
|
root,
|
|
45
57
|
candidates.eligible.map((candidate) => candidate.milestoneId),
|
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
nativeAddPaths,
|
|
38
38
|
nativeResetSoft,
|
|
39
39
|
nativeCommitSubject,
|
|
40
|
+
nativeIsIgnored,
|
|
40
41
|
_resetHasChangesCache,
|
|
41
42
|
} from "./native-git-bridge.js";
|
|
42
43
|
import { GSDError, GSD_MERGE_CONFLICT, GSD_GIT_ERROR } from "./errors.js";
|
|
@@ -760,6 +761,7 @@ export class GitServiceImpl {
|
|
|
760
761
|
const normalized = keyFiles
|
|
761
762
|
.map(file => normalizeRepoRelativePath(this.basePath, file))
|
|
762
763
|
.filter((file): file is string => file !== null)
|
|
764
|
+
.filter(file => !nativeIsIgnored(this.basePath, file))
|
|
763
765
|
.filter(file => !isExcludedScopedPath(file, allExclusions));
|
|
764
766
|
|
|
765
767
|
// Drop entries that don't exist on disk. The LLM occasionally lists files
|
|
@@ -1136,33 +1136,17 @@ export function setSliceSketchFlag(milestoneId: string, sliceId: string, isSketc
|
|
|
1136
1136
|
}
|
|
1137
1137
|
|
|
1138
1138
|
/**
|
|
1139
|
-
* ADR-
|
|
1140
|
-
*
|
|
1141
|
-
*
|
|
1142
|
-
*
|
|
1143
|
-
* to keep path logic in one place — do not hand-roll the path inside the callback.
|
|
1144
|
-
*
|
|
1145
|
-
* Recovers from two scenarios:
|
|
1146
|
-
* 1. Crash between `gsd_plan_slice` write and the sketch flag flip.
|
|
1147
|
-
* 2. Flag-OFF downgrade path: when `progressive_planning` is off, the dispatch
|
|
1148
|
-
* rule routes sketch slices to plan-slice, which writes PLAN.md but leaves
|
|
1149
|
-
* `is_sketch=1` — the next state derivation auto-heals it to 0 here.
|
|
1150
|
-
*
|
|
1151
|
-
* Not aggressive in practice: PLAN.md is only written via the DB-backed
|
|
1152
|
-
* `gsd_plan_slice` tool (which also inserts tasks), so a "stale PLAN.md with
|
|
1153
|
-
* is_sketch=1" is extremely unlikely to indicate anything other than the two
|
|
1154
|
-
* recovery scenarios above.
|
|
1139
|
+
* ADR-017 raw primitive: returns slice IDs in a milestone whose is_sketch flag
|
|
1140
|
+
* is still 1. The stale-sketch-flag drift handler at
|
|
1141
|
+
* `state-reconciliation/drift/sketch-flag.ts` composes this with PLAN.md
|
|
1142
|
+
* existence checks to detect drift, then writes via `setSliceSketchFlag`.
|
|
1155
1143
|
*/
|
|
1156
|
-
export function
|
|
1157
|
-
if (!currentDb) return;
|
|
1144
|
+
export function getSketchedSliceIds(milestoneId: string): string[] {
|
|
1145
|
+
if (!currentDb) return [];
|
|
1158
1146
|
const rows = currentDb.prepare(
|
|
1159
1147
|
`SELECT id FROM slices WHERE milestone_id = :mid AND is_sketch = 1`,
|
|
1160
1148
|
).all({ ":mid": milestoneId }) as Array<{ id: string }>;
|
|
1161
|
-
|
|
1162
|
-
if (hasPlanFile(row.id)) {
|
|
1163
|
-
setSliceSketchFlag(milestoneId, row.id, false);
|
|
1164
|
-
}
|
|
1165
|
-
}
|
|
1149
|
+
return rows.map((r) => r.id);
|
|
1166
1150
|
}
|
|
1167
1151
|
|
|
1168
1152
|
export function upsertSlicePlanning(milestoneId: string, sliceId: string, planning: Partial<SlicePlanningRecord>): void {
|
|
@@ -70,7 +70,7 @@ export function buildHealthLines(data: HealthWidgetData, width?: number): string
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
if (data.projectState === "initialized") {
|
|
73
|
-
return [" GSD Project
|
|
73
|
+
return [" GSD Project Initialized"];
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
const leftParts: string[] = [];
|
|
@@ -1,12 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* Shows a compact 1-2 line summary: progress score, budget, provider key
|
|
5
|
-
* status, and doctor/environment issue count. Refreshes every 60 seconds.
|
|
6
|
-
* Quiet when everything is healthy; turns amber/red when issues arise.
|
|
7
|
-
*
|
|
8
|
-
* Widget key: "gsd-health", placement: "belowEditor"
|
|
9
|
-
*/
|
|
1
|
+
// Project/App: GSD-2
|
|
2
|
+
// File Purpose: Always-on ambient health signal rendered below the editor.
|
|
10
3
|
|
|
11
4
|
import type { ExtensionContext } from "@gsd/pi-coding-agent";
|
|
12
5
|
import type { GSDState } from "./types.js";
|
|
@@ -24,6 +17,9 @@ import {
|
|
|
24
17
|
type HealthWidgetData,
|
|
25
18
|
} from "./health-widget-core.js";
|
|
26
19
|
|
|
20
|
+
export const HEALTH_WIDGET_ACTIVE_HINTS =
|
|
21
|
+
" /gsd auto to run · /gsd status for overview · /gsd visualize to inspect · /gsd notifications for history · /gsd help";
|
|
22
|
+
|
|
27
23
|
// ── Data loader ────────────────────────────────────────────────────────────────
|
|
28
24
|
|
|
29
25
|
function loadHealthWidgetData(basePath: string): HealthWidgetData {
|
|
@@ -136,7 +132,7 @@ export function initHealthWidget(ctx: ExtensionContext): void {
|
|
|
136
132
|
if (!cachedLines || cachedWidth !== width) {
|
|
137
133
|
cachedLines = buildHealthLines(data, width);
|
|
138
134
|
if (data.projectState === "active") {
|
|
139
|
-
cachedLines = [...cachedLines, _theme.fg("dim",
|
|
135
|
+
cachedLines = [...cachedLines, _theme.fg("dim", HEALTH_WIDGET_ACTIVE_HINTS)];
|
|
140
136
|
}
|
|
141
137
|
cachedWidth = width;
|
|
142
138
|
}
|
|
@@ -920,101 +920,10 @@ export function detectStaleRenders(basePath: string): StaleEntry[] {
|
|
|
920
920
|
}
|
|
921
921
|
|
|
922
922
|
// ─── Stale Repair ─────────────────────────────────────────────────────────
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
* For each stale entry, calls the appropriate render function:
|
|
928
|
-
* - Roadmap checkbox mismatches → renderRoadmapCheckboxes()
|
|
929
|
-
* - Plan checkbox mismatches → renderPlanCheckboxes()
|
|
930
|
-
* - Missing task summaries → renderTaskSummary()
|
|
931
|
-
* - Missing slice summaries/UATs → renderSliceSummary()
|
|
932
|
-
*
|
|
933
|
-
* Idempotent: calling twice with no DB changes produces zero repairs on the second call.
|
|
934
|
-
*
|
|
935
|
-
* @returns the number of files repaired
|
|
936
|
-
*/
|
|
937
|
-
export async function repairStaleRenders(basePath: string): Promise<number> {
|
|
938
|
-
const staleEntries = detectStaleRenders(basePath);
|
|
939
|
-
if (staleEntries.length === 0) return 0;
|
|
940
|
-
|
|
941
|
-
// Deduplicate: a single roadmap/plan file might appear multiple times
|
|
942
|
-
// (once per mismatched checkbox). We only need to re-render it once.
|
|
943
|
-
const repairedPaths = new Set<string>();
|
|
944
|
-
let repairCount = 0;
|
|
945
|
-
|
|
946
|
-
for (const entry of staleEntries) {
|
|
947
|
-
if (repairedPaths.has(entry.path)) continue;
|
|
948
|
-
// Normalize path separators for cross-platform regex matching
|
|
949
|
-
const normPath = entry.path.replace(/\\/g, "/");
|
|
950
|
-
|
|
951
|
-
try {
|
|
952
|
-
// Determine repair action from the reason
|
|
953
|
-
if (entry.reason.includes("in roadmap")) {
|
|
954
|
-
// Roadmap checkbox mismatch — extract milestone ID from path
|
|
955
|
-
const milestoneMatch = normPath.match(/milestones\/([^/]+)\//);
|
|
956
|
-
if (milestoneMatch) {
|
|
957
|
-
const ok = await renderRoadmapCheckboxes(basePath, milestoneMatch[1]);
|
|
958
|
-
if (ok) {
|
|
959
|
-
repairedPaths.add(entry.path);
|
|
960
|
-
repairCount++;
|
|
961
|
-
}
|
|
962
|
-
}
|
|
963
|
-
} else if (entry.reason.includes("in plan")) {
|
|
964
|
-
// Plan checkbox mismatch — extract milestone + slice IDs from path
|
|
965
|
-
const pathMatch = normPath.match(/milestones\/([^/]+)\/slices\/([^/]+)\//);
|
|
966
|
-
if (pathMatch) {
|
|
967
|
-
const ok = await renderPlanCheckboxes(basePath, pathMatch[1], pathMatch[2]);
|
|
968
|
-
if (ok) {
|
|
969
|
-
repairedPaths.add(entry.path);
|
|
970
|
-
repairCount++;
|
|
971
|
-
}
|
|
972
|
-
}
|
|
973
|
-
} else if (entry.reason.includes("SUMMARY.md missing") && entry.reason.match(/^T\d+/)) {
|
|
974
|
-
// Missing task summary — extract IDs from path
|
|
975
|
-
const pathMatch = normPath.match(/milestones\/([^/]+)\/slices\/([^/]+)\/tasks\//);
|
|
976
|
-
const taskMatch = entry.reason.match(/^(T\d+)/);
|
|
977
|
-
if (pathMatch && taskMatch) {
|
|
978
|
-
const ok = await renderTaskSummary(basePath, pathMatch[1], pathMatch[2], taskMatch[1]);
|
|
979
|
-
if (ok) {
|
|
980
|
-
repairedPaths.add(entry.path);
|
|
981
|
-
repairCount++;
|
|
982
|
-
}
|
|
983
|
-
}
|
|
984
|
-
} else if (entry.reason.includes("SUMMARY.md missing") && entry.reason.match(/^S\d+/)) {
|
|
985
|
-
// Missing slice summary — extract IDs from path
|
|
986
|
-
const pathMatch = normPath.match(/milestones\/([^/]+)\/slices\/([^/]+)\//);
|
|
987
|
-
if (pathMatch) {
|
|
988
|
-
const ok = await renderSliceSummary(basePath, pathMatch[1], pathMatch[2]);
|
|
989
|
-
if (ok) {
|
|
990
|
-
repairedPaths.add(entry.path);
|
|
991
|
-
repairCount++;
|
|
992
|
-
}
|
|
993
|
-
}
|
|
994
|
-
} else if (entry.reason.includes("UAT.md missing")) {
|
|
995
|
-
// Missing slice UAT — renderSliceSummary handles both SUMMARY + UAT
|
|
996
|
-
const pathMatch = normPath.match(/milestones\/([^/]+)\/slices\/([^/]+)\//);
|
|
997
|
-
if (pathMatch) {
|
|
998
|
-
const ok = await renderSliceSummary(basePath, pathMatch[1], pathMatch[2]);
|
|
999
|
-
if (ok) {
|
|
1000
|
-
repairedPaths.add(entry.path);
|
|
1001
|
-
repairCount++;
|
|
1002
|
-
}
|
|
1003
|
-
}
|
|
1004
|
-
}
|
|
1005
|
-
} catch (err) {
|
|
1006
|
-
logWarning("renderer", `repair failed for ${entry.path}: ${(err as Error).message}`);
|
|
1007
|
-
}
|
|
1008
|
-
}
|
|
1009
|
-
|
|
1010
|
-
if (repairCount > 0) {
|
|
1011
|
-
process.stderr.write(
|
|
1012
|
-
`markdown-renderer: repaired ${repairCount} stale render(s)\n`,
|
|
1013
|
-
);
|
|
1014
|
-
}
|
|
1015
|
-
|
|
1016
|
-
return repairCount;
|
|
1017
|
-
}
|
|
923
|
+
// Body relocated to state-reconciliation/drift/stale-render.ts (ADR-017 #5702).
|
|
924
|
+
// detectStaleRenders above stays as a useful diagnostic primitive; the
|
|
925
|
+
// drift handler composes it with the per-reason renderer dispatch and the
|
|
926
|
+
// reconcileBeforeDispatch lifecycle.
|
|
1018
927
|
|
|
1019
928
|
// ─── Replan & Assessment Renderers ────────────────────────────────────────
|
|
1020
929
|
|
|
@@ -705,20 +705,21 @@ export function nativeAddTracked(basePath: string): void {
|
|
|
705
705
|
gitFileExec(basePath, ["add", "-u"]);
|
|
706
706
|
}
|
|
707
707
|
|
|
708
|
-
function
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
// exit 1 means this form is not ignored; try the next variant
|
|
719
|
-
}
|
|
708
|
+
export function nativeIsIgnored(basePath: string, path: string): boolean {
|
|
709
|
+
try {
|
|
710
|
+
execFileSync("git", ["check-ignore", "-q", "--", path], {
|
|
711
|
+
cwd: basePath,
|
|
712
|
+
stdio: "pipe",
|
|
713
|
+
env: GIT_NO_PROMPT_ENV,
|
|
714
|
+
});
|
|
715
|
+
return true;
|
|
716
|
+
} catch {
|
|
717
|
+
return false;
|
|
720
718
|
}
|
|
721
|
-
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
function isDotGsdIgnored(basePath: string): boolean {
|
|
722
|
+
return [".gsd", ".gsd/"].some(path => nativeIsIgnored(basePath, path));
|
|
722
723
|
}
|
|
723
724
|
|
|
724
725
|
/**
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
// GSD
|
|
2
|
-
//
|
|
3
|
-
// Toggled with Ctrl+Alt+N (⌃⌥N on macOS), Ctrl+Shift+N fallback, or /gsd notifications.
|
|
1
|
+
// Project/App: GSD-2
|
|
2
|
+
// File Purpose: Notification history overlay with severity filtering and width-safe TUI rendering.
|
|
4
3
|
|
|
5
4
|
import type { Theme } from "@gsd/pi-coding-agent";
|
|
6
|
-
import { truncateToWidth, visibleWidth,
|
|
5
|
+
import { truncateToWidth, visibleWidth, matchesKey, Key } from "@gsd/pi-tui";
|
|
7
6
|
|
|
8
7
|
import {
|
|
9
8
|
readNotifications,
|
|
@@ -14,10 +13,32 @@ import {
|
|
|
14
13
|
type NotifySeverity,
|
|
15
14
|
} from "./notification-store.js";
|
|
16
15
|
import { formattedShortcutPair } from "./shortcut-defs.js";
|
|
17
|
-
import {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
import {
|
|
17
|
+
padRightVisible,
|
|
18
|
+
renderFrame,
|
|
19
|
+
renderKeyHints,
|
|
20
|
+
rightAlign,
|
|
21
|
+
wrapVisibleText,
|
|
22
|
+
} from "./tui/render-kit.js";
|
|
23
|
+
|
|
24
|
+
type FilterMode = "all" | "error" | "warning" | "success" | "info";
|
|
25
|
+
const FILTER_CYCLE: FilterMode[] = ["all", "error", "warning", "success", "info"];
|
|
26
|
+
const OVERLAY_WIDTH = "58%";
|
|
27
|
+
const OVERLAY_MIN_WIDTH = 68;
|
|
28
|
+
const OVERLAY_MAX_HEIGHT_PERCENT = 52;
|
|
29
|
+
const OVERLAY_MARGIN = { top: 2, right: 2, bottom: 6, left: 2 } as const;
|
|
30
|
+
|
|
31
|
+
export function notificationOverlayOptions() {
|
|
32
|
+
return {
|
|
33
|
+
width: OVERLAY_WIDTH,
|
|
34
|
+
minWidth: OVERLAY_MIN_WIDTH,
|
|
35
|
+
maxHeight: `${OVERLAY_MAX_HEIGHT_PERCENT}%`,
|
|
36
|
+
anchor: "top-center",
|
|
37
|
+
row: "24%",
|
|
38
|
+
margin: OVERLAY_MARGIN,
|
|
39
|
+
backdrop: true,
|
|
40
|
+
} as const;
|
|
41
|
+
}
|
|
21
42
|
|
|
22
43
|
function severityIcon(severity: NotifySeverity): string {
|
|
23
44
|
switch (severity) {
|
|
@@ -29,17 +50,6 @@ function severityIcon(severity: NotifySeverity): string {
|
|
|
29
50
|
}
|
|
30
51
|
}
|
|
31
52
|
|
|
32
|
-
/** Column-aware word wrap using pi-tui's native wrapper (handles unicode/ANSI). */
|
|
33
|
-
function wrapText(text: string, maxWidth: number): string[] {
|
|
34
|
-
if (maxWidth <= 0) return [text];
|
|
35
|
-
const lines = wrapTextWithAnsi(text, maxWidth);
|
|
36
|
-
// Safety clamp: if any line still exceeds maxWidth (e.g. unbreakable long token),
|
|
37
|
-
// truncate it with an ellipsis so it cannot bleed past the box border.
|
|
38
|
-
return lines.map((l) =>
|
|
39
|
-
visibleWidth(l) > maxWidth ? truncateToWidth(l, maxWidth, "…") : l,
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
53
|
function formatTimestamp(ts: string): string {
|
|
44
54
|
try {
|
|
45
55
|
const d = new Date(ts);
|
|
@@ -184,13 +194,19 @@ export class GSDNotificationOverlay {
|
|
|
184
194
|
}
|
|
185
195
|
|
|
186
196
|
const content = this.buildContentLines(width);
|
|
187
|
-
const
|
|
197
|
+
const terminalRows = process.stdout.rows || 32;
|
|
198
|
+
const availableRows = Math.max(1, terminalRows - OVERLAY_MARGIN.top - OVERLAY_MARGIN.bottom);
|
|
199
|
+
const overlayRows = Math.min(
|
|
200
|
+
availableRows,
|
|
201
|
+
Math.max(1, Math.floor((terminalRows * OVERLAY_MAX_HEIGHT_PERCENT) / 100)),
|
|
202
|
+
);
|
|
203
|
+
const maxVisibleRows = Math.max(5, overlayRows - 2);
|
|
188
204
|
const visibleContentRows = Math.min(content.length, maxVisibleRows);
|
|
189
205
|
const maxScroll = Math.max(0, content.length - visibleContentRows);
|
|
190
206
|
this.scrollOffset = Math.min(this.scrollOffset, maxScroll);
|
|
191
207
|
const visibleContent = content.slice(this.scrollOffset, this.scrollOffset + visibleContentRows);
|
|
192
208
|
|
|
193
|
-
const lines = this.
|
|
209
|
+
const lines = renderFrame(this.theme, visibleContent, width);
|
|
194
210
|
|
|
195
211
|
this.cachedWidth = width;
|
|
196
212
|
this.cachedLines = lines;
|
|
@@ -227,33 +243,15 @@ export class GSDNotificationOverlay {
|
|
|
227
243
|
}
|
|
228
244
|
}
|
|
229
245
|
|
|
230
|
-
private wrapInBox(inner: string[], width: number): string[] {
|
|
231
|
-
const th = this.theme;
|
|
232
|
-
const border = (s: string) => th.fg("borderAccent", s);
|
|
233
|
-
const innerWidth = width - 4;
|
|
234
|
-
const lines: string[] = [];
|
|
235
|
-
|
|
236
|
-
lines.push(border("╭" + "─".repeat(width - 2) + "╮"));
|
|
237
|
-
for (const line of inner) {
|
|
238
|
-
const truncated = truncateToWidth(line, innerWidth);
|
|
239
|
-
const padWidth = Math.max(0, innerWidth - visibleWidth(truncated));
|
|
240
|
-
lines.push(border("│") + " " + truncated + " ".repeat(padWidth) + " " + border("│"));
|
|
241
|
-
}
|
|
242
|
-
lines.push(border("╰" + "─".repeat(width - 2) + "╯"));
|
|
243
|
-
return lines;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
246
|
private buildContentLines(width: number): string[] {
|
|
247
247
|
const th = this.theme;
|
|
248
|
-
const shellWidth = width - 4;
|
|
249
|
-
const contentWidth =
|
|
250
|
-
const sidePad = Math.max(0, Math.floor((shellWidth - contentWidth) / 2));
|
|
251
|
-
const leftMargin = " ".repeat(sidePad);
|
|
248
|
+
const shellWidth = Math.max(1, width - 4);
|
|
249
|
+
const contentWidth = shellWidth;
|
|
252
250
|
const lines: string[] = [];
|
|
253
251
|
|
|
254
252
|
const row = (content = ""): string => {
|
|
255
253
|
const truncated = truncateToWidth(content, contentWidth);
|
|
256
|
-
return
|
|
254
|
+
return padRightVisible(truncated, contentWidth);
|
|
257
255
|
};
|
|
258
256
|
const blank = () => row("");
|
|
259
257
|
const hr = () => row(th.fg("dim", "─".repeat(contentWidth)));
|
|
@@ -262,9 +260,15 @@ export class GSDNotificationOverlay {
|
|
|
262
260
|
const title = th.fg("accent", th.bold("Notifications"));
|
|
263
261
|
const filterLabel = this.filter === "all"
|
|
264
262
|
? th.fg("dim", "all")
|
|
265
|
-
: th.fg(
|
|
263
|
+
: th.fg(
|
|
264
|
+
this.filter === "error" ? "error"
|
|
265
|
+
: this.filter === "warning" ? "warning"
|
|
266
|
+
: this.filter === "success" ? "success"
|
|
267
|
+
: "dim",
|
|
268
|
+
this.filter,
|
|
269
|
+
);
|
|
266
270
|
const count = `${this.filteredEntries.length} entries`;
|
|
267
|
-
lines.push(row(
|
|
271
|
+
lines.push(row(rightAlign(
|
|
268
272
|
`${title} ${th.fg("dim", "filter:")} ${filterLabel}`,
|
|
269
273
|
th.fg("dim", count),
|
|
270
274
|
contentWidth,
|
|
@@ -273,7 +277,7 @@ export class GSDNotificationOverlay {
|
|
|
273
277
|
|
|
274
278
|
// Controls
|
|
275
279
|
const closeShortcut = formattedShortcutPair("notifications");
|
|
276
|
-
lines.push(row(th
|
|
280
|
+
lines.push(row(renderKeyHints(th, ["↑/↓ scroll", "f filter", "c clear", `Esc/${closeShortcut} close`], contentWidth)));
|
|
277
281
|
lines.push(blank());
|
|
278
282
|
|
|
279
283
|
// Entries
|
|
@@ -302,7 +306,7 @@ export class GSDNotificationOverlay {
|
|
|
302
306
|
const msgMaxWidth = Math.max(10, contentWidth - prefixWidth);
|
|
303
307
|
|
|
304
308
|
// Wrap long messages onto continuation lines indented to align with message start
|
|
305
|
-
const msgLines =
|
|
309
|
+
const msgLines = wrapVisibleText(entry.message, msgMaxWidth);
|
|
306
310
|
const indent = " ".repeat(prefixWidth);
|
|
307
311
|
for (let i = 0; i < msgLines.length; i++) {
|
|
308
312
|
if (i === 0) {
|