gsd-pi 2.44.0-dev.62b5d6c → 2.44.0-dev.73f2fd5
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 +30 -12
- package/dist/resources/extensions/gsd/auto/infra-errors.js +3 -0
- package/dist/resources/extensions/gsd/auto/phases.js +36 -36
- package/dist/resources/extensions/gsd/auto-prompts.js +24 -1
- package/dist/resources/extensions/gsd/auto-start.js +10 -0
- package/dist/resources/extensions/gsd/auto-timers.js +57 -3
- package/dist/resources/extensions/gsd/auto-worktree-sync.js +4 -0
- package/dist/resources/extensions/gsd/auto-worktree.js +9 -6
- package/dist/resources/extensions/gsd/auto.js +30 -3
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +156 -0
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +46 -12
- package/dist/resources/extensions/gsd/commands/catalog.js +6 -1
- package/dist/resources/extensions/gsd/commands/handlers/core.js +1 -0
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +5 -0
- package/dist/resources/extensions/gsd/commands/handlers/workflow.js +5 -0
- package/dist/resources/extensions/gsd/commands-mcp-status.js +187 -0
- package/dist/resources/extensions/gsd/db-writer.js +34 -16
- package/dist/resources/extensions/gsd/doctor.js +8 -0
- package/dist/resources/extensions/gsd/git-service.js +8 -3
- package/dist/resources/extensions/gsd/gsd-db.js +12 -1
- package/dist/resources/extensions/gsd/markdown-renderer.js +1 -1
- package/dist/resources/extensions/gsd/preferences.js +9 -1
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +2 -4
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
- package/dist/resources/extensions/gsd/prompts/replan-slice.md +3 -14
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
- package/dist/resources/extensions/gsd/provider-error-pause.js +7 -0
- package/dist/resources/extensions/gsd/state.js +19 -2
- package/dist/resources/extensions/gsd/tools/plan-slice.js +1 -0
- package/dist/resources/extensions/gsd/tools/plan-task.js +1 -0
- package/dist/resources/extensions/gsd/tools/replan-slice.js +2 -0
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +88 -0
- package/dist/resources/extensions/gsd/worktree-resolver.js +6 -0
- package/dist/resources/extensions/mcp-client/index.js +14 -0
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- 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 +17 -17
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +3 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js +15 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js +6 -8
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +24 -26
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/fs-utils.test.js +29 -48
- package/packages/pi-coding-agent/dist/core/fs-utils.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/local-model-check.d.ts +15 -0
- package/packages/pi-coding-agent/dist/core/local-model-check.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/local-model-check.js +41 -0
- package/packages/pi-coding-agent/dist/core/local-model-check.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts +11 -0
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +20 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +34 -44
- package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.test.js +30 -34
- package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +6 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +10 -12
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/main.js +17 -0
- package/packages/pi-coding-agent/dist/main.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js +32 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +3 -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 +8 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +12 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts +15 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js +40 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js.map +1 -0
- 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 +4 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts +5 -2
- 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 +13 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +17 -8
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +7 -3
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js +43 -47
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js.map +1 -1
- package/packages/pi-coding-agent/src/core/auth-storage.test.ts +7 -7
- package/packages/pi-coding-agent/src/core/auth-storage.ts +15 -1
- package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +26 -26
- package/packages/pi-coding-agent/src/core/fs-utils.test.ts +31 -43
- package/packages/pi-coding-agent/src/core/local-model-check.ts +45 -0
- package/packages/pi-coding-agent/src/core/model-registry.ts +21 -1
- package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +40 -45
- package/packages/pi-coding-agent/src/core/session-manager.test.ts +33 -33
- package/packages/pi-coding-agent/src/core/settings-manager.ts +9 -0
- package/packages/pi-coding-agent/src/core/tools/edit-diff.test.ts +17 -17
- package/packages/pi-coding-agent/src/main.ts +19 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/timestamp.test.ts +38 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +10 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/timestamp.ts +48 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +3 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/user-message.ts +18 -3
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +16 -7
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +8 -1
- package/packages/pi-coding-agent/src/resources/extensions/memory/storage.test.ts +74 -74
- package/src/resources/extensions/gsd/auto/infra-errors.ts +3 -0
- package/src/resources/extensions/gsd/auto/phases.ts +45 -48
- package/src/resources/extensions/gsd/auto-prompts.ts +24 -1
- package/src/resources/extensions/gsd/auto-start.ts +14 -0
- package/src/resources/extensions/gsd/auto-timers.ts +64 -3
- package/src/resources/extensions/gsd/auto-worktree-sync.ts +5 -0
- package/src/resources/extensions/gsd/auto-worktree.ts +9 -6
- package/src/resources/extensions/gsd/auto.ts +37 -3
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +148 -0
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +48 -11
- package/src/resources/extensions/gsd/commands/catalog.ts +6 -1
- package/src/resources/extensions/gsd/commands/handlers/core.ts +1 -0
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +5 -0
- package/src/resources/extensions/gsd/commands/handlers/workflow.ts +8 -0
- package/src/resources/extensions/gsd/commands-mcp-status.ts +247 -0
- package/src/resources/extensions/gsd/db-writer.ts +39 -17
- package/src/resources/extensions/gsd/doctor.ts +7 -1
- package/src/resources/extensions/gsd/git-service.ts +6 -2
- package/src/resources/extensions/gsd/gsd-db.ts +16 -1
- package/src/resources/extensions/gsd/markdown-renderer.ts +1 -1
- package/src/resources/extensions/gsd/preferences.ts +11 -1
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +2 -4
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
- package/src/resources/extensions/gsd/prompts/replan-slice.md +3 -14
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
- package/src/resources/extensions/gsd/provider-error-pause.ts +9 -0
- package/src/resources/extensions/gsd/state.ts +19 -1
- package/src/resources/extensions/gsd/tests/all-milestones-complete-merge.test.ts +99 -99
- package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +14 -16
- package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +43 -57
- package/src/resources/extensions/gsd/tests/auto-pr-bugs.test.ts +88 -0
- package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +11 -13
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +465 -523
- package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +73 -75
- package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +34 -56
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +533 -656
- package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +165 -143
- package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +29 -52
- package/src/resources/extensions/gsd/tests/captures.test.ts +148 -176
- package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +32 -33
- package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +141 -143
- package/src/resources/extensions/gsd/tests/commands-inspect-open-db.test.ts +25 -25
- package/src/resources/extensions/gsd/tests/commands-logs.test.ts +81 -81
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +38 -59
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +228 -263
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +250 -302
- package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +114 -0
- package/src/resources/extensions/gsd/tests/context-store.test.ts +354 -367
- package/src/resources/extensions/gsd/tests/continue-here.test.ts +68 -72
- package/src/resources/extensions/gsd/tests/cost-projection.test.ts +92 -106
- package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +27 -35
- package/src/resources/extensions/gsd/tests/dashboard-budget.test.ts +220 -237
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +465 -416
- package/src/resources/extensions/gsd/tests/definition-loader.test.ts +76 -92
- package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +68 -83
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +210 -181
- package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +78 -101
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +192 -227
- package/src/resources/extensions/gsd/tests/detection.test.ts +232 -278
- package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +30 -34
- package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +164 -180
- package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +43 -49
- package/src/resources/extensions/gsd/tests/dispatch-uat-last-completed.test.ts +28 -32
- package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +27 -29
- package/src/resources/extensions/gsd/tests/doctor-delimiter-fix.test.ts +34 -38
- package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +54 -75
- package/src/resources/extensions/gsd/tests/doctor-environment-worktree.test.ts +21 -32
- package/src/resources/extensions/gsd/tests/doctor-environment.test.ts +72 -97
- package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +38 -44
- package/src/resources/extensions/gsd/tests/doctor-git.test.ts +104 -145
- package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +84 -106
- package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +54 -60
- package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +72 -93
- package/src/resources/extensions/gsd/tests/doctor.test.ts +104 -134
- package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +123 -131
- package/src/resources/extensions/gsd/tests/est-annotation-timeout.test.ts +120 -0
- package/src/resources/extensions/gsd/tests/exit-command.test.ts +20 -24
- package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +48 -57
- package/src/resources/extensions/gsd/tests/files-loadfile-eisdir.test.ts +5 -7
- package/src/resources/extensions/gsd/tests/flag-file-db.test.ts +30 -42
- package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +198 -206
- package/src/resources/extensions/gsd/tests/git-locale.test.ts +13 -27
- package/src/resources/extensions/gsd/tests/git-service.test.ts +285 -388
- package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +31 -39
- package/src/resources/extensions/gsd/tests/graph-operations.test.ts +63 -69
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +255 -264
- package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +108 -119
- package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +81 -103
- package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +229 -262
- package/src/resources/extensions/gsd/tests/headless-answers.test.ts +13 -13
- package/src/resources/extensions/gsd/tests/health-widget.test.ts +29 -37
- package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +81 -102
- package/src/resources/extensions/gsd/tests/infra-error.test.ts +20 -2
- package/src/resources/extensions/gsd/tests/init-wizard.test.ts +16 -18
- package/src/resources/extensions/gsd/tests/integration-edge.test.ts +41 -46
- package/src/resources/extensions/gsd/tests/integration-lifecycle.test.ts +42 -53
- package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +75 -91
- package/src/resources/extensions/gsd/tests/integration-proof.test.ts +18 -18
- package/src/resources/extensions/gsd/tests/knowledge.test.ts +89 -0
- package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +150 -194
- package/src/resources/extensions/gsd/tests/mcp-status.test.ts +103 -0
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +101 -125
- package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +45 -54
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +80 -93
- package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +66 -0
- package/src/resources/extensions/gsd/tests/migrate-command.test.ts +57 -66
- package/src/resources/extensions/gsd/tests/migrate-hierarchy.test.ts +83 -93
- package/src/resources/extensions/gsd/tests/migrate-parser.test.ts +161 -170
- package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +125 -141
- package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +107 -131
- package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +87 -96
- package/src/resources/extensions/gsd/tests/migrate-writer.test.ts +125 -164
- package/src/resources/extensions/gsd/tests/must-have-parser.test.ts +81 -94
- package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +35 -36
- package/src/resources/extensions/gsd/tests/overrides.test.ts +99 -106
- package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +40 -47
- package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +25 -28
- package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +66 -83
- package/src/resources/extensions/gsd/tests/park-edge-cases.test.ts +54 -77
- package/src/resources/extensions/gsd/tests/park-milestone.test.ts +68 -115
- package/src/resources/extensions/gsd/tests/parsers.test.ts +546 -611
- package/src/resources/extensions/gsd/tests/paths.test.ts +72 -87
- package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +77 -117
- package/src/resources/extensions/gsd/tests/preferences.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +11 -7
- package/src/resources/extensions/gsd/tests/prompt-db.test.ts +56 -56
- package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +93 -119
- package/src/resources/extensions/gsd/tests/queue-order.test.ts +70 -82
- package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +42 -55
- package/src/resources/extensions/gsd/tests/quick-auto-guard.test.ts +100 -0
- package/src/resources/extensions/gsd/tests/quick-branch-lifecycle.test.ts +45 -73
- package/src/resources/extensions/gsd/tests/reassess-prompt.test.ts +28 -38
- package/src/resources/extensions/gsd/tests/replan-slice.test.ts +73 -80
- package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +71 -74
- package/src/resources/extensions/gsd/tests/requirements.test.ts +70 -75
- package/src/resources/extensions/gsd/tests/retry-state-reset.test.ts +44 -66
- package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +114 -181
- package/src/resources/extensions/gsd/tests/rule-registry.test.ts +63 -65
- package/src/resources/extensions/gsd/tests/run-uat.test.ts +66 -128
- package/src/resources/extensions/gsd/tests/session-lock-multipath.test.ts +18 -25
- package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +37 -44
- package/src/resources/extensions/gsd/tests/shared-wal.test.ts +19 -26
- package/src/resources/extensions/gsd/tests/sqlite-unavailable-gate.test.ts +63 -0
- package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +6 -8
- package/src/resources/extensions/gsd/tests/stop-auto-merge-back.test.ts +67 -0
- package/src/resources/extensions/gsd/tests/symlink-numbered-variants.test.ts +22 -28
- package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +49 -0
- package/src/resources/extensions/gsd/tests/token-savings.test.ts +54 -56
- package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +23 -25
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +10 -11
- package/src/resources/extensions/gsd/tests/unique-milestone-ids.test.ts +66 -82
- package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +46 -47
- package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -22
- package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +84 -86
- package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +41 -43
- package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +94 -96
- package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +11 -13
- package/src/resources/extensions/gsd/tests/worker-registry.test.ts +27 -29
- package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +50 -52
- package/src/resources/extensions/gsd/tests/worktree-bugfix.test.ts +10 -13
- package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +14 -18
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +38 -39
- package/src/resources/extensions/gsd/tests/worktree-e2e.test.ts +17 -21
- package/src/resources/extensions/gsd/tests/worktree-health.test.ts +25 -30
- package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +30 -37
- package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +15 -22
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +59 -66
- package/src/resources/extensions/gsd/tests/worktree.test.ts +44 -50
- package/src/resources/extensions/gsd/tools/plan-slice.ts +2 -0
- package/src/resources/extensions/gsd/tools/plan-task.ts +2 -0
- package/src/resources/extensions/gsd/tools/replan-slice.ts +3 -0
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +127 -0
- package/src/resources/extensions/gsd/worktree-resolver.ts +7 -0
- package/src/resources/extensions/mcp-client/index.ts +20 -0
- /package/dist/web/standalone/.next/static/{fOnWQBjWXMKUs4bqTg530 → kxxAA66bah_yhPYqLBHE2}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{fOnWQBjWXMKUs4bqTg530 → kxxAA66bah_yhPYqLBHE2}/_ssgManifest.js +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
|
-
import { describe, it } from "node:test";
|
|
2
|
+
import { describe, it, afterEach } from "node:test";
|
|
3
3
|
import { mkdtempSync, rmSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
@@ -22,44 +22,44 @@ function makeAssistantMessage(input: number, output: number, cacheRead = 0, cach
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
describe("SessionManager usage totals", () => {
|
|
25
|
-
|
|
26
|
-
const dir = mkdtempSync(join(tmpdir(), "gsd-session-manager-test-"));
|
|
27
|
-
try {
|
|
28
|
-
const manager = SessionManager.create(dir, dir);
|
|
29
|
-
|
|
30
|
-
manager.appendMessage({ role: "user", content: [{ type: "text", text: "hello" }] } as any);
|
|
31
|
-
manager.appendMessage(makeAssistantMessage(10, 5, 3, 2, 0.25));
|
|
32
|
-
manager.appendMessage(makeAssistantMessage(7, 4, 1, 0, 0.1));
|
|
25
|
+
let dir: string;
|
|
33
26
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
output: 9,
|
|
37
|
-
cacheRead: 4,
|
|
38
|
-
cacheWrite: 2,
|
|
39
|
-
cost: 0.35,
|
|
40
|
-
});
|
|
41
|
-
} finally {
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
if (dir) {
|
|
42
29
|
rmSync(dir, { recursive: true, force: true });
|
|
43
30
|
}
|
|
44
31
|
});
|
|
45
32
|
|
|
33
|
+
it("tracks assistant usage incrementally without rescanning entries", () => {
|
|
34
|
+
dir = mkdtempSync(join(tmpdir(), "gsd-session-manager-test-"));
|
|
35
|
+
const manager = SessionManager.create(dir, dir);
|
|
36
|
+
|
|
37
|
+
manager.appendMessage({ role: "user", content: [{ type: "text", text: "hello" }] } as any);
|
|
38
|
+
manager.appendMessage(makeAssistantMessage(10, 5, 3, 2, 0.25));
|
|
39
|
+
manager.appendMessage(makeAssistantMessage(7, 4, 1, 0, 0.1));
|
|
40
|
+
|
|
41
|
+
assert.deepEqual(manager.getUsageTotals(), {
|
|
42
|
+
input: 17,
|
|
43
|
+
output: 9,
|
|
44
|
+
cacheRead: 4,
|
|
45
|
+
cacheWrite: 2,
|
|
46
|
+
cost: 0.35,
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
46
50
|
it("resets totals when starting a new session", () => {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
assert.equal(manager.getUsageTotals().input, 5);
|
|
51
|
+
dir = mkdtempSync(join(tmpdir(), "gsd-session-manager-test-"));
|
|
52
|
+
const manager = SessionManager.create(dir, dir);
|
|
53
|
+
manager.appendMessage(makeAssistantMessage(5, 5, 0, 0, 0.05));
|
|
54
|
+
assert.equal(manager.getUsageTotals().input, 5);
|
|
52
55
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
} finally {
|
|
62
|
-
rmSync(dir, { recursive: true, force: true });
|
|
63
|
-
}
|
|
56
|
+
manager.newSession();
|
|
57
|
+
assert.deepEqual(manager.getUsageTotals(), {
|
|
58
|
+
input: 0,
|
|
59
|
+
output: 0,
|
|
60
|
+
cacheRead: 0,
|
|
61
|
+
cacheWrite: 0,
|
|
62
|
+
cost: 0,
|
|
63
|
+
});
|
|
64
64
|
});
|
|
65
65
|
});
|
|
@@ -151,6 +151,7 @@ export interface Settings {
|
|
|
151
151
|
fallback?: FallbackSettings;
|
|
152
152
|
modelDiscovery?: ModelDiscoverySettings;
|
|
153
153
|
editMode?: "standard" | "hashline"; // Edit tool mode: "standard" (text match) or "hashline" (LINE#ID anchors). Default: "standard"
|
|
154
|
+
timestampFormat?: "date-time-iso" | "date-time-us"; // Timestamp display format for messages. Default: "date-time-iso"
|
|
154
155
|
}
|
|
155
156
|
|
|
156
157
|
/** Deep merge settings: project/overrides take precedence, nested objects merge recursively */
|
|
@@ -1087,4 +1088,12 @@ export class SettingsManager {
|
|
|
1087
1088
|
setEditMode(mode: "standard" | "hashline"): void {
|
|
1088
1089
|
this.setGlobalSetting("editMode", mode);
|
|
1089
1090
|
}
|
|
1091
|
+
|
|
1092
|
+
getTimestampFormat(): "date-time-iso" | "date-time-us" {
|
|
1093
|
+
return this.settings.timestampFormat ?? "date-time-iso";
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
setTimestampFormat(format: "date-time-iso" | "date-time-us"): void {
|
|
1097
|
+
this.setGlobalSetting("timestampFormat", format);
|
|
1098
|
+
}
|
|
1090
1099
|
}
|
|
@@ -60,26 +60,26 @@ describe("edit-diff", () => {
|
|
|
60
60
|
assert.match(result.diff, /CHANGED/);
|
|
61
61
|
});
|
|
62
62
|
|
|
63
|
-
it("computes diffs for preview without native helpers", async () => {
|
|
63
|
+
it("computes diffs for preview without native helpers", async (t) => {
|
|
64
64
|
const dir = mkdtempSync(join(tmpdir(), "edit-diff-test-"));
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
t.after(() => {
|
|
66
|
+
rmSync(dir, { recursive: true, force: true });
|
|
67
|
+
});
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
"const title = \"Hello\";\n",
|
|
72
|
-
"const title = \"Hi\";\n",
|
|
73
|
-
dir,
|
|
74
|
-
);
|
|
69
|
+
const file = join(dir, "sample.ts");
|
|
70
|
+
writeFileSync(file, "const title = “Hello”;\n", "utf-8");
|
|
75
71
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
72
|
+
const result = await computeEditDiff(
|
|
73
|
+
file,
|
|
74
|
+
"const title = \"Hello\";\n",
|
|
75
|
+
"const title = \"Hi\";\n",
|
|
76
|
+
dir,
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
assert.ok(!("error" in result), "expected a diff result");
|
|
80
|
+
if (!("error" in result)) {
|
|
81
|
+
assert.equal(result.firstChangedLine, 1);
|
|
82
|
+
assert.match(result.diff, /\+1 const title = "Hi";/);
|
|
83
83
|
}
|
|
84
84
|
});
|
|
85
85
|
});
|
|
@@ -391,6 +391,25 @@ export async function main(args: string[]) {
|
|
|
391
391
|
const authStorage = AuthStorage.create();
|
|
392
392
|
const modelRegistry = new ModelRegistry(authStorage, getModelsPath());
|
|
393
393
|
|
|
394
|
+
// Offline mode validation / auto-detection
|
|
395
|
+
if (offlineMode) {
|
|
396
|
+
// --offline flag: validate all models are local
|
|
397
|
+
if (!modelRegistry.isAllLocalChain()) {
|
|
398
|
+
const remoteModel = modelRegistry.getAll().find((m) => !ModelRegistry.isLocalModel(m));
|
|
399
|
+
if (remoteModel) {
|
|
400
|
+
console.error(
|
|
401
|
+
`Error: --offline requires all configured models to be local. Found remote model: ${remoteModel.name} (${remoteModel.baseUrl || "cloud API"})`,
|
|
402
|
+
);
|
|
403
|
+
process.exit(1);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
} else if (modelRegistry.isAllLocalChain() && modelRegistry.getAll().length > 0) {
|
|
407
|
+
// Auto-detect: all models are local, enable offline mode
|
|
408
|
+
process.env.PI_OFFLINE = "1";
|
|
409
|
+
process.env.PI_SKIP_VERSION_CHECK = "1";
|
|
410
|
+
console.log("[gsd] All configured models are local \u2014 enabling offline mode automatically.");
|
|
411
|
+
}
|
|
412
|
+
|
|
394
413
|
const resourceLoader = new DefaultResourceLoader({
|
|
395
414
|
cwd,
|
|
396
415
|
agentDir,
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { test, describe } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { formatTimestamp } from "../timestamp.js";
|
|
4
|
+
|
|
5
|
+
describe("formatTimestamp", () => {
|
|
6
|
+
// Use a fixed local timestamp to avoid timezone issues
|
|
7
|
+
const d = new Date(2026, 2, 24, 10, 34, 0); // Mar 24, 2026 10:34:00 local time
|
|
8
|
+
const ts = d.getTime();
|
|
9
|
+
|
|
10
|
+
test("date-time-iso format (default)", () => {
|
|
11
|
+
assert.equal(formatTimestamp(ts, "date-time-iso"), "2026-03-24 10:34");
|
|
12
|
+
assert.equal(formatTimestamp(ts), "2026-03-24 10:34"); // default
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test("date-time-us format", () => {
|
|
16
|
+
assert.equal(formatTimestamp(ts, "date-time-us"), "03-24-2026 10:34 AM");
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("US format handles PM correctly", () => {
|
|
20
|
+
const pm = new Date(2026, 2, 24, 14, 5, 0).getTime();
|
|
21
|
+
assert.equal(formatTimestamp(pm, "date-time-us"), "03-24-2026 2:05 PM");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("US format handles noon as 12 PM", () => {
|
|
25
|
+
const noon = new Date(2026, 2, 24, 12, 0, 0).getTime();
|
|
26
|
+
assert.equal(formatTimestamp(noon, "date-time-us"), "03-24-2026 12:00 PM");
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("US format handles midnight as 12 AM", () => {
|
|
30
|
+
const midnight = new Date(2026, 2, 24, 0, 0, 0).getTime();
|
|
31
|
+
assert.equal(formatTimestamp(midnight, "date-time-us"), "03-24-2026 12:00 AM");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("ISO format pads single digit months and days", () => {
|
|
35
|
+
const jan1 = new Date(2026, 0, 1, 9, 5, 0).getTime();
|
|
36
|
+
assert.equal(formatTimestamp(jan1, "date-time-iso"), "2026-01-01 09:05");
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { AssistantMessage } from "@gsd/pi-ai";
|
|
2
2
|
import { Container, Markdown, type MarkdownTheme, Spacer, Text } from "@gsd/pi-tui";
|
|
3
3
|
import { getMarkdownTheme, theme } from "../theme/theme.js";
|
|
4
|
+
import { formatTimestamp, type TimestampFormat } from "./timestamp.js";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Component that renders a complete assistant message
|
|
@@ -10,16 +11,19 @@ export class AssistantMessageComponent extends Container {
|
|
|
10
11
|
private hideThinkingBlock: boolean;
|
|
11
12
|
private markdownTheme: MarkdownTheme;
|
|
12
13
|
private lastMessage?: AssistantMessage;
|
|
14
|
+
private timestampFormat: TimestampFormat;
|
|
13
15
|
|
|
14
16
|
constructor(
|
|
15
17
|
message?: AssistantMessage,
|
|
16
18
|
hideThinkingBlock = false,
|
|
17
19
|
markdownTheme: MarkdownTheme = getMarkdownTheme(),
|
|
20
|
+
timestampFormat: TimestampFormat = "date-time-iso",
|
|
18
21
|
) {
|
|
19
22
|
super();
|
|
20
23
|
|
|
21
24
|
this.hideThinkingBlock = hideThinkingBlock;
|
|
22
25
|
this.markdownTheme = markdownTheme;
|
|
26
|
+
this.timestampFormat = timestampFormat;
|
|
23
27
|
|
|
24
28
|
// Container for text/thinking content
|
|
25
29
|
this.contentContainer = new Container();
|
|
@@ -111,5 +115,11 @@ export class AssistantMessageComponent extends Container {
|
|
|
111
115
|
this.contentContainer.addChild(new Text(theme.fg("error", `Error: ${errorMsg}`), 1, 0));
|
|
112
116
|
}
|
|
113
117
|
}
|
|
118
|
+
|
|
119
|
+
// Show timestamp when the message is complete (has a stop reason)
|
|
120
|
+
if (message.stopReason && message.timestamp) {
|
|
121
|
+
const timeStr = formatTimestamp(message.timestamp, this.timestampFormat);
|
|
122
|
+
this.contentContainer.addChild(new Text(theme.fg("dim", timeStr), 1, 0));
|
|
123
|
+
}
|
|
114
124
|
}
|
|
115
125
|
}
|
|
@@ -45,6 +45,7 @@ export interface SettingsConfig {
|
|
|
45
45
|
respectGitignoreInPicker: boolean;
|
|
46
46
|
quietStartup: boolean;
|
|
47
47
|
clearOnShrink: boolean;
|
|
48
|
+
timestampFormat: "date-time-iso" | "date-time-us";
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
export interface SettingsCallbacks {
|
|
@@ -69,6 +70,7 @@ export interface SettingsCallbacks {
|
|
|
69
70
|
onRespectGitignoreInPickerChange: (enabled: boolean) => void;
|
|
70
71
|
onQuietStartupChange: (enabled: boolean) => void;
|
|
71
72
|
onClearOnShrinkChange: (enabled: boolean) => void;
|
|
73
|
+
onTimestampFormatChange: (format: "date-time-iso" | "date-time-us") => void;
|
|
72
74
|
onCancel: () => void;
|
|
73
75
|
}
|
|
74
76
|
|
|
@@ -355,6 +357,16 @@ export class SettingsSelectorComponent extends Container {
|
|
|
355
357
|
values: ["true", "false"],
|
|
356
358
|
});
|
|
357
359
|
|
|
360
|
+
// Timestamp format (insert after respect-gitignore-in-picker)
|
|
361
|
+
const gitignoreIndex = items.findIndex((item) => item.id === "respect-gitignore-in-picker");
|
|
362
|
+
items.splice(gitignoreIndex + 1, 0, {
|
|
363
|
+
id: "timestamp-format",
|
|
364
|
+
label: "Timestamp format",
|
|
365
|
+
description: "Date/time format for message timestamps",
|
|
366
|
+
currentValue: config.timestampFormat,
|
|
367
|
+
values: ["date-time-iso", "date-time-us"],
|
|
368
|
+
});
|
|
369
|
+
|
|
358
370
|
// Add borders
|
|
359
371
|
this.addChild(new DynamicBorder());
|
|
360
372
|
|
|
@@ -420,6 +432,9 @@ export class SettingsSelectorComponent extends Container {
|
|
|
420
432
|
case "respect-gitignore-in-picker":
|
|
421
433
|
callbacks.onRespectGitignoreInPickerChange(newValue === "true");
|
|
422
434
|
break;
|
|
435
|
+
case "timestamp-format":
|
|
436
|
+
callbacks.onTimestampFormatChange(newValue as "date-time-iso" | "date-time-us");
|
|
437
|
+
break;
|
|
423
438
|
}
|
|
424
439
|
},
|
|
425
440
|
callbacks.onCancel,
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timestamp formatting for message display.
|
|
3
|
+
*
|
|
4
|
+
* Formats:
|
|
5
|
+
* - "time-date-iso": 10:34 2025-03-24 (default)
|
|
6
|
+
* - "date-time-iso": 2025-03-24 10:34
|
|
7
|
+
* - "time-date-us": 10:34 AM 03/24/2025
|
|
8
|
+
* - "date-time-us": 03/24/2025 10:34 AM
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export type TimestampFormat = "date-time-iso" | "date-time-us";
|
|
12
|
+
|
|
13
|
+
function pad2(n: number): string {
|
|
14
|
+
return n.toString().padStart(2, "0");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function isoDate(d: Date): string {
|
|
18
|
+
return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function isoTime(d: Date): string {
|
|
22
|
+
return `${pad2(d.getHours())}:${pad2(d.getMinutes())}`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function usDate(d: Date): string {
|
|
26
|
+
return `${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}-${d.getFullYear()}`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function usTime(d: Date): string {
|
|
30
|
+
const hours = d.getHours();
|
|
31
|
+
const period = hours >= 12 ? "PM" : "AM";
|
|
32
|
+
const h = hours % 12 || 12;
|
|
33
|
+
return `${h}:${pad2(d.getMinutes())} ${period}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Format a timestamp for message display using the specified format.
|
|
38
|
+
*/
|
|
39
|
+
export function formatTimestamp(timestamp: number, format: TimestampFormat = "date-time-iso"): string {
|
|
40
|
+
const d = new Date(timestamp);
|
|
41
|
+
|
|
42
|
+
switch (format) {
|
|
43
|
+
case "date-time-iso":
|
|
44
|
+
return `${isoDate(d)} ${isoTime(d)}`;
|
|
45
|
+
case "date-time-us":
|
|
46
|
+
return `${usDate(d)} ${usTime(d)}`;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -895,7 +895,9 @@ export class ToolExecutionComponent extends Container {
|
|
|
895
895
|
// Server-side Anthropic web search
|
|
896
896
|
text = theme.fg("toolTitle", theme.bold("web search"));
|
|
897
897
|
|
|
898
|
-
if (
|
|
898
|
+
if (process.env.PI_OFFLINE === "1") {
|
|
899
|
+
text += "\n\n" + theme.fg("muted", "\u{1F50C} Offline \u{2014} web search unavailable");
|
|
900
|
+
} else if (this.result) {
|
|
899
901
|
const output = this.getTextOutput().trim();
|
|
900
902
|
if (output) {
|
|
901
903
|
const lines = output.split("\n");
|
|
@@ -1,15 +1,21 @@
|
|
|
1
|
-
import { Container, Markdown, type MarkdownTheme, Spacer } from "@gsd/pi-tui";
|
|
1
|
+
import { Container, Markdown, type MarkdownTheme, Spacer, Text } from "@gsd/pi-tui";
|
|
2
2
|
import { getMarkdownTheme, theme } from "../theme/theme.js";
|
|
3
|
+
import { formatTimestamp, type TimestampFormat } from "./timestamp.js";
|
|
3
4
|
|
|
4
5
|
const OSC133_ZONE_START = "\x1b]133;A\x07";
|
|
5
6
|
const OSC133_ZONE_END = "\x1b]133;B\x07";
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
|
-
* Component that renders a user message
|
|
9
|
+
* Component that renders a user message with a right-aligned timestamp.
|
|
9
10
|
*/
|
|
10
11
|
export class UserMessageComponent extends Container {
|
|
11
|
-
|
|
12
|
+
private timestamp: number | undefined;
|
|
13
|
+
private timestampFormat: TimestampFormat;
|
|
14
|
+
|
|
15
|
+
constructor(text: string, markdownTheme: MarkdownTheme = getMarkdownTheme(), timestamp?: number, timestampFormat: TimestampFormat = "date-time-iso") {
|
|
12
16
|
super();
|
|
17
|
+
this.timestamp = timestamp;
|
|
18
|
+
this.timestampFormat = timestampFormat;
|
|
13
19
|
this.addChild(new Spacer(1));
|
|
14
20
|
this.addChild(
|
|
15
21
|
new Markdown(text, 1, 1, markdownTheme, {
|
|
@@ -25,6 +31,15 @@ export class UserMessageComponent extends Container {
|
|
|
25
31
|
return lines;
|
|
26
32
|
}
|
|
27
33
|
|
|
34
|
+
// Insert right-aligned timestamp above the message content
|
|
35
|
+
if (this.timestamp) {
|
|
36
|
+
const timeStr = formatTimestamp(this.timestamp, this.timestampFormat);
|
|
37
|
+
const label = theme.fg("dim", timeStr);
|
|
38
|
+
const padding = Math.max(0, width - timeStr.length - 1);
|
|
39
|
+
const timestampLine = " ".repeat(padding) + label;
|
|
40
|
+
lines.splice(0, 0, timestampLine);
|
|
41
|
+
}
|
|
42
|
+
|
|
28
43
|
lines[0] = OSC133_ZONE_START + lines[0];
|
|
29
44
|
lines[lines.length - 1] = lines[lines.length - 1] + OSC133_ZONE_END;
|
|
30
45
|
return lines;
|
|
@@ -100,6 +100,7 @@ export async function handleAgentEvent(host: InteractiveModeStateHost & {
|
|
|
100
100
|
undefined,
|
|
101
101
|
host.hideThinkingBlock,
|
|
102
102
|
host.getMarkdownThemeWithSettings(),
|
|
103
|
+
host.settingsManager.getTimestampFormat(),
|
|
103
104
|
);
|
|
104
105
|
host.streamingMessage = event.message;
|
|
105
106
|
host.chatContainer.addChild(host.streamingComponent);
|
|
@@ -144,13 +145,21 @@ export async function handleAgentEvent(host: InteractiveModeStateHost & {
|
|
|
144
145
|
} else if (content.type === "webSearchResult") {
|
|
145
146
|
const component = host.pendingTools.get(content.toolUseId);
|
|
146
147
|
if (component) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
148
|
+
if (process.env.PI_OFFLINE === "1") {
|
|
149
|
+
component.updateResult({
|
|
150
|
+
content: [{ type: "text", text: "Web search disabled (offline mode)" }],
|
|
151
|
+
isError: false,
|
|
152
|
+
});
|
|
153
|
+
host.pendingTools.delete(content.toolUseId);
|
|
154
|
+
} else {
|
|
155
|
+
const searchContent = content.content;
|
|
156
|
+
const isError = searchContent && typeof searchContent === "object" && "type" in (searchContent as any) && (searchContent as any).type === "web_search_tool_result_error";
|
|
157
|
+
component.updateResult({
|
|
158
|
+
content: [{ type: "text", text: host.formatWebSearchResult(searchContent) }],
|
|
159
|
+
isError: !!isError,
|
|
160
|
+
});
|
|
161
|
+
host.pendingTools.delete(content.toolUseId);
|
|
162
|
+
}
|
|
154
163
|
}
|
|
155
164
|
}
|
|
156
165
|
}
|
|
@@ -2099,11 +2099,13 @@ export class InteractiveMode {
|
|
|
2099
2099
|
const userComponent = new UserMessageComponent(
|
|
2100
2100
|
skillBlock.userMessage,
|
|
2101
2101
|
this.getMarkdownThemeWithSettings(),
|
|
2102
|
+
message.timestamp,
|
|
2103
|
+
this.settingsManager.getTimestampFormat(),
|
|
2102
2104
|
);
|
|
2103
2105
|
this.chatContainer.addChild(userComponent);
|
|
2104
2106
|
}
|
|
2105
2107
|
} else {
|
|
2106
|
-
const userComponent = new UserMessageComponent(textContent, this.getMarkdownThemeWithSettings());
|
|
2108
|
+
const userComponent = new UserMessageComponent(textContent, this.getMarkdownThemeWithSettings(), message.timestamp, this.settingsManager.getTimestampFormat());
|
|
2107
2109
|
this.chatContainer.addChild(userComponent);
|
|
2108
2110
|
}
|
|
2109
2111
|
if (options?.populateHistory) {
|
|
@@ -2117,6 +2119,7 @@ export class InteractiveMode {
|
|
|
2117
2119
|
message,
|
|
2118
2120
|
this.hideThinkingBlock,
|
|
2119
2121
|
this.getMarkdownThemeWithSettings(),
|
|
2122
|
+
this.settingsManager.getTimestampFormat(),
|
|
2120
2123
|
);
|
|
2121
2124
|
this.chatContainer.addChild(assistantComponent);
|
|
2122
2125
|
break;
|
|
@@ -2795,6 +2798,7 @@ export class InteractiveMode {
|
|
|
2795
2798
|
respectGitignoreInPicker: this.settingsManager.getRespectGitignoreInPicker(),
|
|
2796
2799
|
quietStartup: this.settingsManager.getQuietStartup(),
|
|
2797
2800
|
clearOnShrink: this.settingsManager.getClearOnShrink(),
|
|
2801
|
+
timestampFormat: this.settingsManager.getTimestampFormat(),
|
|
2798
2802
|
},
|
|
2799
2803
|
{
|
|
2800
2804
|
onAutoCompactChange: (enabled) => {
|
|
@@ -2898,6 +2902,9 @@ export class InteractiveMode {
|
|
|
2898
2902
|
this.settingsManager.setRespectGitignoreInPicker(enabled);
|
|
2899
2903
|
this.autocompleteProvider?.setRespectGitignore(enabled);
|
|
2900
2904
|
},
|
|
2905
|
+
onTimestampFormatChange: (format) => {
|
|
2906
|
+
this.settingsManager.setTimestampFormat(format);
|
|
2907
|
+
},
|
|
2901
2908
|
onCancel: () => {
|
|
2902
2909
|
done();
|
|
2903
2910
|
this.ui.requestRender();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
|
-
import { describe, it,
|
|
2
|
+
import { describe, it, afterEach } from "node:test";
|
|
3
3
|
import { mkdtempSync, rmSync, readFileSync, existsSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
@@ -15,84 +15,84 @@ function wait(ms: number): Promise<void> {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
describe("MemoryStorage debounced persistence", () => {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const storage = await MemoryStorage.create(dbPath);
|
|
23
|
-
|
|
24
|
-
const initialStat = readFileSync(dbPath);
|
|
25
|
-
const initialMtime = initialStat.length;
|
|
26
|
-
|
|
27
|
-
storage.upsertThreads([
|
|
28
|
-
{ threadId: "t1", filePath: "/a.txt", fileSize: 100, fileMtime: 1000, cwd: "/proj" },
|
|
29
|
-
]);
|
|
30
|
-
storage.upsertThreads([
|
|
31
|
-
{ threadId: "t2", filePath: "/b.txt", fileSize: 200, fileMtime: 2000, cwd: "/proj" },
|
|
32
|
-
]);
|
|
33
|
-
storage.upsertThreads([
|
|
34
|
-
{ threadId: "t3", filePath: "/c.txt", fileSize: 300, fileMtime: 3000, cwd: "/proj" },
|
|
35
|
-
]);
|
|
36
|
-
|
|
37
|
-
const afterMutationsBuf = readFileSync(dbPath);
|
|
38
|
-
assert.deepEqual(
|
|
39
|
-
afterMutationsBuf,
|
|
40
|
-
initialStat,
|
|
41
|
-
"File should not have been written yet (debounce window has not elapsed)",
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
await wait(700);
|
|
45
|
-
|
|
46
|
-
const afterDebounceBuf = readFileSync(dbPath);
|
|
47
|
-
assert.notDeepEqual(
|
|
48
|
-
afterDebounceBuf,
|
|
49
|
-
initialStat,
|
|
50
|
-
"File should have been written after debounce window elapsed",
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
const stats = storage.getStats();
|
|
54
|
-
assert.equal(stats.totalThreads, 3);
|
|
55
|
-
|
|
56
|
-
storage.close();
|
|
57
|
-
} finally {
|
|
18
|
+
let dir: string;
|
|
19
|
+
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
if (dir) {
|
|
58
22
|
rmSync(dir, { recursive: true, force: true });
|
|
59
23
|
}
|
|
60
24
|
});
|
|
61
25
|
|
|
26
|
+
it("multiple rapid mutations only trigger one persist write", async () => {
|
|
27
|
+
dir = makeTmpDir();
|
|
28
|
+
const dbPath = join(dir, "test.db");
|
|
29
|
+
const storage = await MemoryStorage.create(dbPath);
|
|
30
|
+
|
|
31
|
+
const initialStat = readFileSync(dbPath);
|
|
32
|
+
const initialMtime = initialStat.length;
|
|
33
|
+
|
|
34
|
+
storage.upsertThreads([
|
|
35
|
+
{ threadId: "t1", filePath: "/a.txt", fileSize: 100, fileMtime: 1000, cwd: "/proj" },
|
|
36
|
+
]);
|
|
37
|
+
storage.upsertThreads([
|
|
38
|
+
{ threadId: "t2", filePath: "/b.txt", fileSize: 200, fileMtime: 2000, cwd: "/proj" },
|
|
39
|
+
]);
|
|
40
|
+
storage.upsertThreads([
|
|
41
|
+
{ threadId: "t3", filePath: "/c.txt", fileSize: 300, fileMtime: 3000, cwd: "/proj" },
|
|
42
|
+
]);
|
|
43
|
+
|
|
44
|
+
const afterMutationsBuf = readFileSync(dbPath);
|
|
45
|
+
assert.deepEqual(
|
|
46
|
+
afterMutationsBuf,
|
|
47
|
+
initialStat,
|
|
48
|
+
"File should not have been written yet (debounce window has not elapsed)",
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
await wait(700);
|
|
52
|
+
|
|
53
|
+
const afterDebounceBuf = readFileSync(dbPath);
|
|
54
|
+
assert.notDeepEqual(
|
|
55
|
+
afterDebounceBuf,
|
|
56
|
+
initialStat,
|
|
57
|
+
"File should have been written after debounce window elapsed",
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
const stats = storage.getStats();
|
|
61
|
+
assert.equal(stats.totalThreads, 3);
|
|
62
|
+
|
|
63
|
+
storage.close();
|
|
64
|
+
});
|
|
65
|
+
|
|
62
66
|
it("close() flushes pending changes immediately without waiting for debounce", async () => {
|
|
63
|
-
|
|
67
|
+
dir = makeTmpDir();
|
|
64
68
|
const dbPath = join(dir, "test.db");
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
reopened.close();
|
|
94
|
-
} finally {
|
|
95
|
-
rmSync(dir, { recursive: true, force: true });
|
|
96
|
-
}
|
|
69
|
+
const storage = await MemoryStorage.create(dbPath);
|
|
70
|
+
|
|
71
|
+
const initialBuf = readFileSync(dbPath);
|
|
72
|
+
|
|
73
|
+
storage.upsertThreads([
|
|
74
|
+
{ threadId: "t1", filePath: "/a.txt", fileSize: 100, fileMtime: 1000, cwd: "/proj" },
|
|
75
|
+
]);
|
|
76
|
+
|
|
77
|
+
const beforeCloseBuf = readFileSync(dbPath);
|
|
78
|
+
assert.deepEqual(
|
|
79
|
+
beforeCloseBuf,
|
|
80
|
+
initialBuf,
|
|
81
|
+
"File should not have been written yet (debounce window has not elapsed)",
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
storage.close();
|
|
85
|
+
|
|
86
|
+
const afterCloseBuf = readFileSync(dbPath);
|
|
87
|
+
assert.notDeepEqual(
|
|
88
|
+
afterCloseBuf,
|
|
89
|
+
initialBuf,
|
|
90
|
+
"File should have been written immediately on close()",
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
const reopened = await MemoryStorage.create(dbPath);
|
|
94
|
+
const stats = reopened.getStats();
|
|
95
|
+
assert.equal(stats.totalThreads, 1, "Data should be persisted and readable after close");
|
|
96
|
+
reopened.close();
|
|
97
97
|
});
|
|
98
98
|
});
|
|
@@ -18,6 +18,9 @@ export const INFRA_ERROR_CODES: ReadonlySet<string> = new Set([
|
|
|
18
18
|
"EDQUOT", // disk quota exceeded
|
|
19
19
|
"EMFILE", // too many open files (process)
|
|
20
20
|
"ENFILE", // too many open files (system)
|
|
21
|
+
"ECONNREFUSED", // connection refused (offline / local server down)
|
|
22
|
+
"ENOTFOUND", // DNS lookup failed (offline / no network)
|
|
23
|
+
"ENETUNREACH", // network unreachable (offline / no route)
|
|
21
24
|
]);
|
|
22
25
|
|
|
23
26
|
/**
|