gsd-pi 2.34.0 → 2.35.0-dev.34ce717
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 +3 -1
- package/dist/cli.js +7 -2
- package/dist/resource-loader.d.ts +1 -1
- package/dist/resource-loader.js +13 -1
- package/dist/resources/extensions/async-jobs/await-tool.js +0 -2
- package/dist/resources/extensions/async-jobs/job-manager.js +0 -6
- package/dist/resources/extensions/bg-shell/output-formatter.js +1 -19
- package/dist/resources/extensions/bg-shell/process-manager.js +0 -4
- package/dist/resources/extensions/bg-shell/types.js +0 -2
- package/dist/resources/extensions/context7/index.js +5 -0
- package/dist/resources/extensions/get-secrets-from-user.js +2 -30
- package/dist/resources/extensions/google-search/index.js +5 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +43 -1
- package/dist/resources/extensions/gsd/auto-loop.js +17 -3
- package/dist/resources/extensions/gsd/auto-model-selection.js +15 -3
- package/dist/resources/extensions/gsd/auto-recovery.js +35 -0
- package/dist/resources/extensions/gsd/auto-start.js +35 -2
- package/dist/resources/extensions/gsd/auto.js +59 -4
- package/dist/resources/extensions/gsd/changelog.js +162 -0
- package/dist/resources/extensions/gsd/commands-bootstrap.js +1 -0
- package/dist/resources/extensions/gsd/commands-handlers.js +2 -2
- package/dist/resources/extensions/gsd/commands-inspect.js +10 -3
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +5 -1
- package/dist/resources/extensions/gsd/commands-rate.js +31 -0
- package/dist/resources/extensions/gsd/commands.js +51 -2
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +10 -0
- package/dist/resources/extensions/gsd/doctor-checks.js +113 -5
- package/dist/resources/extensions/gsd/doctor-environment.js +26 -17
- package/dist/resources/extensions/gsd/doctor-proactive.js +22 -0
- package/dist/resources/extensions/gsd/doctor.js +36 -0
- package/dist/resources/extensions/gsd/files.js +11 -2
- package/dist/resources/extensions/gsd/gitignore.js +54 -7
- package/dist/resources/extensions/gsd/guided-flow.js +12 -4
- package/dist/resources/extensions/gsd/health-widget-core.js +96 -0
- package/dist/resources/extensions/gsd/health-widget.js +97 -46
- package/dist/resources/extensions/gsd/index.js +26 -33
- package/dist/resources/extensions/gsd/migrate-external.js +55 -2
- package/dist/resources/extensions/gsd/milestone-ids.js +3 -2
- package/dist/resources/extensions/gsd/paths.js +74 -7
- package/dist/resources/extensions/gsd/post-unit-hooks.js +4 -1
- package/dist/resources/extensions/gsd/preferences-validation.js +54 -1
- package/dist/resources/extensions/gsd/preferences.js +14 -0
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +2 -0
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +2 -0
- package/dist/resources/extensions/gsd/roadmap-mutations.js +55 -0
- package/dist/resources/extensions/gsd/session-lock.js +53 -2
- package/dist/resources/extensions/gsd/state.js +2 -1
- package/dist/resources/extensions/gsd/templates/plan.md +8 -0
- package/dist/resources/extensions/gsd/worktree-resolver.js +12 -0
- package/dist/resources/extensions/mcp-client/index.js +2 -1
- package/dist/resources/extensions/remote-questions/remote-command.js +2 -22
- package/dist/resources/extensions/shared/mod.js +1 -1
- package/dist/resources/extensions/shared/sanitize.js +30 -0
- package/dist/resources/extensions/subagent/index.js +6 -14
- package/dist/resources/skills/create-gsd-extension/references/events-reference.md +4 -4
- package/package.json +2 -1
- package/packages/native/dist/native.d.ts +0 -2
- package/packages/native/dist/native.js +0 -2
- package/packages/native/src/native.ts +0 -3
- package/packages/pi-agent-core/dist/agent-loop.d.ts +14 -0
- package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +24 -27
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/dist/agent.d.ts +1 -0
- package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent.js +11 -22
- package/packages/pi-agent-core/dist/agent.js.map +1 -1
- package/packages/pi-agent-core/dist/proxy.d.ts +1 -25
- package/packages/pi-agent-core/dist/proxy.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/proxy.js +3 -9
- package/packages/pi-agent-core/dist/proxy.js.map +1 -1
- package/packages/pi-agent-core/src/agent-loop.ts +30 -27
- package/packages/pi-agent-core/src/agent.ts +12 -23
- package/packages/pi-agent-core/src/proxy.ts +3 -9
- package/packages/pi-ai/dist/api-registry.d.ts +0 -2
- package/packages/pi-ai/dist/api-registry.d.ts.map +1 -1
- package/packages/pi-ai/dist/api-registry.js +0 -10
- package/packages/pi-ai/dist/api-registry.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.d.ts +0 -8
- package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.js +1 -1
- package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
- package/packages/pi-ai/dist/providers/azure-openai-responses.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/azure-openai-responses.js +5 -41
- package/packages/pi-ai/dist/providers/azure-openai-responses.js.map +1 -1
- package/packages/pi-ai/dist/providers/github-copilot-headers.d.ts +0 -1
- package/packages/pi-ai/dist/providers/github-copilot-headers.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/github-copilot-headers.js +1 -1
- package/packages/pi-ai/dist/providers/github-copilot-headers.js.map +1 -1
- package/packages/pi-ai/dist/providers/google-gemini-cli.d.ts +1 -43
- package/packages/pi-ai/dist/providers/google-gemini-cli.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/google-gemini-cli.js +2 -2
- package/packages/pi-ai/dist/providers/google-gemini-cli.js.map +1 -1
- package/packages/pi-ai/dist/providers/google-shared.d.ts +0 -4
- package/packages/pi-ai/dist/providers/google-shared.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/google-shared.js +1 -1
- package/packages/pi-ai/dist/providers/google-shared.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.js +10 -73
- package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-responses.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-responses.js +9 -80
- package/packages/pi-ai/dist/providers/openai-responses.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-shared.d.ts +65 -0
- package/packages/pi-ai/dist/providers/openai-shared.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/openai-shared.js +146 -0
- package/packages/pi-ai/dist/providers/openai-shared.js.map +1 -0
- package/packages/pi-ai/dist/providers/register-builtins.d.ts +0 -1
- package/packages/pi-ai/dist/providers/register-builtins.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/register-builtins.js +1 -1
- package/packages/pi-ai/dist/providers/register-builtins.js.map +1 -1
- package/packages/pi-ai/dist/utils/event-stream.d.ts +0 -2
- package/packages/pi-ai/dist/utils/event-stream.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/event-stream.js +0 -4
- package/packages/pi-ai/dist/utils/event-stream.js.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/google-antigravity.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/google-antigravity.js +7 -135
- package/packages/pi-ai/dist/utils/oauth/google-antigravity.js.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.js +7 -135
- package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.js.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/google-oauth-utils.d.ts +46 -0
- package/packages/pi-ai/dist/utils/oauth/google-oauth-utils.d.ts.map +1 -0
- package/packages/pi-ai/dist/utils/oauth/google-oauth-utils.js +160 -0
- package/packages/pi-ai/dist/utils/oauth/google-oauth-utils.js.map +1 -0
- package/packages/pi-ai/dist/utils/overflow.d.ts +0 -4
- package/packages/pi-ai/dist/utils/overflow.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/overflow.js +0 -6
- package/packages/pi-ai/dist/utils/overflow.js.map +1 -1
- package/packages/pi-ai/dist/utils/validation.d.ts +0 -8
- package/packages/pi-ai/dist/utils/validation.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/validation.js +0 -14
- package/packages/pi-ai/dist/utils/validation.js.map +1 -1
- package/packages/pi-ai/src/api-registry.ts +0 -12
- package/packages/pi-ai/src/providers/anthropic.ts +1 -1
- package/packages/pi-ai/src/providers/azure-openai-responses.ts +11 -45
- package/packages/pi-ai/src/providers/github-copilot-headers.ts +1 -1
- package/packages/pi-ai/src/providers/google-gemini-cli.ts +2 -2
- package/packages/pi-ai/src/providers/google-shared.ts +1 -1
- package/packages/pi-ai/src/providers/openai-completions.ts +16 -86
- package/packages/pi-ai/src/providers/openai-responses.ts +16 -96
- package/packages/pi-ai/src/providers/openai-shared.ts +193 -0
- package/packages/pi-ai/src/providers/register-builtins.ts +1 -1
- package/packages/pi-ai/src/utils/event-stream.ts +0 -5
- package/packages/pi-ai/src/utils/oauth/google-antigravity.ts +14 -162
- package/packages/pi-ai/src/utils/oauth/google-gemini-cli.ts +13 -161
- package/packages/pi-ai/src/utils/oauth/google-oauth-utils.ts +201 -0
- package/packages/pi-ai/src/utils/overflow.ts +1 -8
- package/packages/pi-ai/src/utils/validation.ts +0 -15
- package/packages/pi-coding-agent/dist/config.d.ts +0 -9
- package/packages/pi-coding-agent/dist/config.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/config.js +4 -8
- package/packages/pi-coding-agent/dist/config.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts +16 -63
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +104 -641
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +0 -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 +4 -35
- package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.js +5 -43
- package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/compaction.js +11 -69
- package/packages/pi-coding-agent/dist/core/compaction/compaction.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/utils.d.ts +40 -0
- package/packages/pi-coding-agent/dist/core/compaction/utils.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/utils.js +78 -0
- package/packages/pi-coding-agent/dist/core/compaction/utils.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.d.ts +77 -0
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js +331 -0
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/export-html/ansi-to-html.d.ts +0 -4
- package/packages/pi-coding-agent/dist/core/export-html/ansi-to-html.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/export-html/ansi-to-html.js +1 -1
- package/packages/pi-coding-agent/dist/core/export-html/ansi-to-html.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/index.d.ts +2 -2
- package/packages/pi-coding-agent/dist/core/extensions/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/index.js +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +15 -5
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js +129 -256
- package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +49 -42
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js +2 -21
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/keybindings.d.ts +0 -8
- package/packages/pi-coding-agent/dist/core/keybindings.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/keybindings.js +2 -2
- package/packages/pi-coding-agent/dist/core/keybindings.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lock-utils.d.ts +39 -0
- package/packages/pi-coding-agent/dist/core/lock-utils.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lock-utils.js +89 -0
- package/packages/pi-coding-agent/dist/core/lock-utils.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/client.d.ts +0 -17
- package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/client.js +3 -62
- package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/config.d.ts +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/config.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/config.js +2 -6
- package/packages/pi-coding-agent/dist/core/lsp/config.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/edits.d.ts +0 -5
- package/packages/pi-coding-agent/dist/core/lsp/edits.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/edits.js +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/edits.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/index.js +52 -107
- package/packages/pi-coding-agent/dist/core/lsp/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/lspmux.d.ts +0 -1
- package/packages/pi-coding-agent/dist/core/lsp/lspmux.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/lspmux.js +3 -22
- package/packages/pi-coding-agent/dist/core/lsp/lspmux.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/types.d.ts +0 -1
- package/packages/pi-coding-agent/dist/core/lsp/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/types.js +0 -28
- package/packages/pi-coding-agent/dist/core/lsp/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/utils.d.ts +1 -6
- package/packages/pi-coding-agent/dist/core/lsp/utils.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/utils.js +1 -28
- package/packages/pi-coding-agent/dist/core/lsp/utils.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/messages.d.ts +0 -8
- package/packages/pi-coding-agent/dist/core/messages.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/messages.js +5 -5
- package/packages/pi-coding-agent/dist/core/messages.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts +0 -3
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +1 -3
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.d.ts +1 -26
- package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.js +3 -59
- package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/package-manager.js +2 -4
- package/packages/pi-coding-agent/dist/core/package-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/prompt-templates.d.ts +0 -17
- package/packages/pi-coding-agent/dist/core/prompt-templates.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/prompt-templates.js +2 -2
- package/packages/pi-coding-agent/dist/core/prompt-templates.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.d.ts +2 -4
- package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.js +46 -60
- package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.d.ts +87 -0
- package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/retry-handler.js +295 -0
- package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager.d.ts +0 -5
- package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.js +5 -32
- package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +8 -12
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +78 -168
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/skills.js +1 -3
- package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js +1 -1
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/migrations.d.ts +0 -16
- package/packages/pi-coding-agent/dist/migrations.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/migrations.js +2 -2
- package/packages/pi-coding-agent/dist/migrations.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/session-selector-search.d.ts +0 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/session-selector-search.js +2 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/session-selector-search.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/session-selector.d.ts +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/session-selector.js +9 -26
- package/packages/pi-coding-agent/dist/modes/interactive/components/session-selector.js.map +1 -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 +1 -13
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tree-render-utils.d.ts +44 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tree-render-utils.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tree-render-utils.js +61 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tree-render-utils.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tree-selector.js +6 -9
- package/packages/pi-coding-agent/dist/modes/interactive/components/tree-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +1 -24
- 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 +50 -512
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.d.ts +71 -0
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +514 -0
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts +65 -4
- 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 +6 -23
- 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 +12 -0
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +175 -0
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/utils/shorten-path.d.ts +6 -0
- package/packages/pi-coding-agent/dist/modes/interactive/utils/shorten-path.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/utils/shorten-path.js +15 -0
- package/packages/pi-coding-agent/dist/modes/interactive/utils/shorten-path.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/print-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/print-mode.js +2 -30
- package/packages/pi-coding-agent/dist/modes/print-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +2 -28
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/shared/command-context-actions.d.ts +19 -0
- package/packages/pi-coding-agent/dist/modes/shared/command-context-actions.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/shared/command-context-actions.js +45 -0
- package/packages/pi-coding-agent/dist/modes/shared/command-context-actions.js.map +1 -0
- package/packages/pi-coding-agent/dist/utils/changelog.d.ts +0 -4
- package/packages/pi-coding-agent/dist/utils/changelog.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/utils/changelog.js +1 -1
- package/packages/pi-coding-agent/dist/utils/changelog.js.map +1 -1
- package/packages/pi-coding-agent/dist/utils/clipboard-image.d.ts +0 -1
- package/packages/pi-coding-agent/dist/utils/clipboard-image.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/utils/clipboard-image.js +1 -1
- package/packages/pi-coding-agent/dist/utils/clipboard-image.js.map +1 -1
- package/packages/pi-coding-agent/dist/utils/error.d.ts +5 -0
- package/packages/pi-coding-agent/dist/utils/error.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/utils/error.js +7 -0
- package/packages/pi-coding-agent/dist/utils/error.js.map +1 -0
- package/packages/pi-coding-agent/dist/utils/photon.d.ts +0 -19
- package/packages/pi-coding-agent/dist/utils/photon.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/utils/photon.js +1 -120
- package/packages/pi-coding-agent/dist/utils/photon.js.map +1 -1
- package/packages/pi-coding-agent/dist/utils/tools-manager.d.ts +0 -1
- package/packages/pi-coding-agent/dist/utils/tools-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/utils/tools-manager.js +1 -1
- package/packages/pi-coding-agent/dist/utils/tools-manager.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/config.ts +5 -10
- package/packages/pi-coding-agent/src/core/agent-session.ts +117 -745
- package/packages/pi-coding-agent/src/core/auth-storage.ts +4 -38
- package/packages/pi-coding-agent/src/core/compaction/branch-summarization.ts +7 -53
- package/packages/pi-coding-agent/src/core/compaction/compaction.ts +14 -74
- package/packages/pi-coding-agent/src/core/compaction/utils.ts +100 -0
- package/packages/pi-coding-agent/src/core/compaction-orchestrator.ts +424 -0
- package/packages/pi-coding-agent/src/core/export-html/ansi-to-html.ts +1 -1
- package/packages/pi-coding-agent/src/core/extensions/index.ts +1 -21
- package/packages/pi-coding-agent/src/core/extensions/runner.ts +119 -256
- package/packages/pi-coding-agent/src/core/extensions/types.ts +50 -69
- package/packages/pi-coding-agent/src/core/keybindings.ts +2 -2
- package/packages/pi-coding-agent/src/core/lock-utils.ts +113 -0
- package/packages/pi-coding-agent/src/core/lsp/client.ts +3 -73
- package/packages/pi-coding-agent/src/core/lsp/config.ts +2 -10
- package/packages/pi-coding-agent/src/core/lsp/edits.ts +1 -1
- package/packages/pi-coding-agent/src/core/lsp/index.ts +83 -152
- package/packages/pi-coding-agent/src/core/lsp/lspmux.ts +3 -23
- package/packages/pi-coding-agent/src/core/lsp/types.ts +0 -29
- package/packages/pi-coding-agent/src/core/lsp/utils.ts +1 -33
- package/packages/pi-coding-agent/src/core/messages.ts +5 -5
- package/packages/pi-coding-agent/src/core/model-registry.ts +0 -2
- package/packages/pi-coding-agent/src/core/model-resolver.ts +3 -77
- package/packages/pi-coding-agent/src/core/package-manager.ts +1 -4
- package/packages/pi-coding-agent/src/core/prompt-templates.ts +2 -2
- package/packages/pi-coding-agent/src/core/resource-loader.ts +56 -69
- package/packages/pi-coding-agent/src/core/retry-handler.ts +359 -0
- package/packages/pi-coding-agent/src/core/session-manager.ts +5 -34
- package/packages/pi-coding-agent/src/core/settings-manager.ts +87 -166
- package/packages/pi-coding-agent/src/core/skills.ts +1 -4
- package/packages/pi-coding-agent/src/index.ts +1 -7
- package/packages/pi-coding-agent/src/migrations.ts +2 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/session-selector-search.ts +2 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/session-selector.ts +17 -29
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +1 -13
- package/packages/pi-coding-agent/src/modes/interactive/components/tree-render-utils.ts +81 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/tree-selector.ts +14 -19
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +50 -561
- package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +653 -0
- package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +7 -26
- package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +196 -0
- package/packages/pi-coding-agent/src/modes/interactive/utils/shorten-path.ts +14 -0
- package/packages/pi-coding-agent/src/modes/print-mode.ts +2 -30
- package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +2 -28
- package/packages/pi-coding-agent/src/modes/shared/command-context-actions.ts +53 -0
- package/packages/pi-coding-agent/src/utils/changelog.ts +1 -1
- package/packages/pi-coding-agent/src/utils/clipboard-image.ts +1 -1
- package/packages/pi-coding-agent/src/utils/error.ts +6 -0
- package/packages/pi-coding-agent/src/utils/photon.ts +0 -137
- package/packages/pi-coding-agent/src/utils/tools-manager.ts +1 -1
- package/packages/pi-tui/dist/components/editor.d.ts +0 -10
- package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/editor.js +1 -1
- package/packages/pi-tui/dist/components/editor.js.map +1 -1
- package/packages/pi-tui/dist/components/markdown.d.ts +5 -0
- package/packages/pi-tui/dist/components/markdown.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/markdown.js +25 -31
- package/packages/pi-tui/dist/components/markdown.js.map +1 -1
- package/packages/pi-tui/dist/keys.d.ts +0 -4
- package/packages/pi-tui/dist/keys.d.ts.map +1 -1
- package/packages/pi-tui/dist/keys.js +94 -162
- package/packages/pi-tui/dist/keys.js.map +1 -1
- package/packages/pi-tui/dist/overlay-layout.d.ts +55 -0
- package/packages/pi-tui/dist/overlay-layout.d.ts.map +1 -0
- package/packages/pi-tui/dist/overlay-layout.js +288 -0
- package/packages/pi-tui/dist/overlay-layout.js.map +1 -0
- package/packages/pi-tui/dist/tui.d.ts +0 -22
- package/packages/pi-tui/dist/tui.d.ts.map +1 -1
- package/packages/pi-tui/dist/tui.js +6 -272
- package/packages/pi-tui/dist/tui.js.map +1 -1
- package/packages/pi-tui/dist/utils.d.ts +0 -7
- package/packages/pi-tui/dist/utils.d.ts.map +1 -1
- package/packages/pi-tui/dist/utils.js +0 -44
- package/packages/pi-tui/dist/utils.js.map +1 -1
- package/packages/pi-tui/src/components/editor.ts +1 -1
- package/packages/pi-tui/src/components/markdown.ts +25 -29
- package/packages/pi-tui/src/keys.ts +94 -173
- package/packages/pi-tui/src/overlay-layout.ts +372 -0
- package/packages/pi-tui/src/tui.ts +11 -312
- package/packages/pi-tui/src/utils.ts +0 -43
- package/pkg/dist/core/export-html/ansi-to-html.d.ts +0 -4
- package/pkg/dist/core/export-html/ansi-to-html.d.ts.map +1 -1
- package/pkg/dist/core/export-html/ansi-to-html.js +1 -1
- package/pkg/dist/core/export-html/ansi-to-html.js.map +1 -1
- package/pkg/dist/modes/interactive/theme/theme.d.ts +65 -4
- package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/pkg/dist/modes/interactive/theme/theme.js +6 -23
- package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
- package/pkg/dist/modes/interactive/theme/themes.d.ts +12 -0
- package/pkg/dist/modes/interactive/theme/themes.d.ts.map +1 -0
- package/pkg/dist/modes/interactive/theme/themes.js +175 -0
- package/pkg/dist/modes/interactive/theme/themes.js.map +1 -0
- package/pkg/package.json +1 -1
- package/src/resources/extensions/async-jobs/await-tool.ts +0 -2
- package/src/resources/extensions/async-jobs/job-manager.ts +0 -7
- package/src/resources/extensions/bg-shell/output-formatter.ts +0 -17
- package/src/resources/extensions/bg-shell/process-manager.ts +0 -4
- package/src/resources/extensions/bg-shell/types.ts +0 -12
- package/src/resources/extensions/context7/index.ts +7 -0
- package/src/resources/extensions/get-secrets-from-user.ts +2 -35
- package/src/resources/extensions/google-search/index.ts +7 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +49 -1
- package/src/resources/extensions/gsd/auto-loop.ts +22 -2
- package/src/resources/extensions/gsd/auto-model-selection.ts +23 -2
- package/src/resources/extensions/gsd/auto-recovery.ts +39 -0
- package/src/resources/extensions/gsd/auto-start.ts +42 -2
- package/src/resources/extensions/gsd/auto.ts +61 -3
- package/src/resources/extensions/gsd/changelog.ts +213 -0
- package/src/resources/extensions/gsd/commands-bootstrap.ts +1 -0
- package/src/resources/extensions/gsd/commands-handlers.ts +2 -2
- package/src/resources/extensions/gsd/commands-inspect.ts +10 -3
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +5 -1
- package/src/resources/extensions/gsd/commands-rate.ts +55 -0
- package/src/resources/extensions/gsd/commands.ts +52 -2
- package/src/resources/extensions/gsd/docs/preferences-reference.md +10 -0
- package/src/resources/extensions/gsd/doctor-checks.ts +107 -5
- package/src/resources/extensions/gsd/doctor-environment.ts +26 -16
- package/src/resources/extensions/gsd/doctor-proactive.ts +24 -0
- package/src/resources/extensions/gsd/doctor-types.ts +9 -1
- package/src/resources/extensions/gsd/doctor.ts +35 -0
- package/src/resources/extensions/gsd/files.ts +12 -2
- package/src/resources/extensions/gsd/gitignore.ts +54 -7
- package/src/resources/extensions/gsd/guided-flow.ts +12 -4
- package/src/resources/extensions/gsd/health-widget-core.ts +129 -0
- package/src/resources/extensions/gsd/health-widget.ts +103 -59
- package/src/resources/extensions/gsd/index.ts +30 -33
- package/src/resources/extensions/gsd/migrate-external.ts +47 -2
- package/src/resources/extensions/gsd/milestone-ids.ts +3 -2
- package/src/resources/extensions/gsd/paths.ts +73 -7
- package/src/resources/extensions/gsd/post-unit-hooks.ts +5 -1
- package/src/resources/extensions/gsd/preferences-validation.ts +54 -1
- package/src/resources/extensions/gsd/preferences.ts +16 -1
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +2 -0
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +2 -0
- package/src/resources/extensions/gsd/roadmap-mutations.ts +66 -0
- package/src/resources/extensions/gsd/session-lock.ts +59 -2
- package/src/resources/extensions/gsd/state.ts +2 -1
- package/src/resources/extensions/gsd/templates/plan.md +8 -0
- package/src/resources/extensions/gsd/tests/commands-inspect-open-db.test.ts +46 -0
- package/src/resources/extensions/gsd/tests/doctor-git.test.ts +98 -2
- package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +59 -3
- package/src/resources/extensions/gsd/tests/files-loadfile-eisdir.test.ts +20 -0
- package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +214 -0
- package/src/resources/extensions/gsd/tests/health-widget.test.ts +158 -0
- package/src/resources/extensions/gsd/tests/paths.test.ts +113 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +40 -2
- package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +26 -0
- package/src/resources/extensions/gsd/tests/test-utils.ts +165 -0
- package/src/resources/extensions/gsd/tests/validate-directory.test.ts +15 -0
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +7 -0
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +32 -0
- package/src/resources/extensions/gsd/worktree-resolver.ts +11 -0
- package/src/resources/extensions/mcp-client/index.ts +2 -1
- package/src/resources/extensions/remote-questions/remote-command.ts +2 -23
- package/src/resources/extensions/shared/mod.ts +1 -1
- package/src/resources/extensions/shared/sanitize.ts +36 -0
- package/src/resources/extensions/subagent/index.ts +6 -12
- package/src/resources/skills/create-gsd-extension/references/events-reference.md +4 -4
- package/dist/resources/extensions/shared/wizard-ui.js +0 -478
- package/packages/pi-coding-agent/dist/modes/interactive/theme/dark.json +0 -85
- package/packages/pi-coding-agent/dist/modes/interactive/theme/light.json +0 -84
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.json +0 -335
- package/packages/pi-coding-agent/src/modes/interactive/theme/dark.json +0 -85
- package/packages/pi-coding-agent/src/modes/interactive/theme/light.json +0 -84
- package/packages/pi-coding-agent/src/modes/interactive/theme/theme-schema.json +0 -335
- package/pkg/dist/modes/interactive/theme/dark.json +0 -85
- package/pkg/dist/modes/interactive/theme/light.json +0 -84
- package/pkg/dist/modes/interactive/theme/theme-schema.json +0 -335
- package/src/resources/extensions/shared/wizard-ui.ts +0 -551
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Roadmap Mutations — shared utilities for modifying roadmap checkbox state.
|
|
3
|
+
*
|
|
4
|
+
* Extracts the duplicated "flip slice checkbox" pattern that existed in
|
|
5
|
+
* doctor.ts, mechanical-completion.ts, and auto-recovery.ts.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { readFileSync } from "node:fs";
|
|
9
|
+
import { atomicWriteSync } from "./atomic-write.js";
|
|
10
|
+
import { resolveMilestoneFile } from "./paths.js";
|
|
11
|
+
import { clearParseCache } from "./files.js";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Mark a slice as done ([x]) in the milestone roadmap.
|
|
15
|
+
* Idempotent — no-op if already checked or if the slice isn't found.
|
|
16
|
+
*
|
|
17
|
+
* @returns true if the roadmap was modified, false if no change was needed
|
|
18
|
+
*/
|
|
19
|
+
export function markSliceDoneInRoadmap(basePath: string, mid: string, sid: string): boolean {
|
|
20
|
+
const roadmapFile = resolveMilestoneFile(basePath, mid, "ROADMAP");
|
|
21
|
+
if (!roadmapFile) return false;
|
|
22
|
+
|
|
23
|
+
let content: string;
|
|
24
|
+
try {
|
|
25
|
+
content = readFileSync(roadmapFile, "utf-8");
|
|
26
|
+
} catch {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const updated = content.replace(
|
|
31
|
+
new RegExp(`^(\\s*-\\s+)\\[ \\]\\s+\\*\\*${sid}:`, "m"),
|
|
32
|
+
`$1[x] **${sid}:`,
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
if (updated === content) return false;
|
|
36
|
+
|
|
37
|
+
atomicWriteSync(roadmapFile, updated);
|
|
38
|
+
clearParseCache();
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Mark a task as done ([x]) in the slice plan.
|
|
44
|
+
* Idempotent — no-op if already checked or if the task isn't found.
|
|
45
|
+
*
|
|
46
|
+
* @returns true if the plan was modified, false if no change was needed
|
|
47
|
+
*/
|
|
48
|
+
export function markTaskDoneInPlan(basePath: string, planPath: string, tid: string): boolean {
|
|
49
|
+
let content: string;
|
|
50
|
+
try {
|
|
51
|
+
content = readFileSync(planPath, "utf-8");
|
|
52
|
+
} catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const updated = content.replace(
|
|
57
|
+
new RegExp(`^(\\s*-\\s+)\\[ \\]\\s+\\*\\*${tid}:`, "m"),
|
|
58
|
+
`$1[x] **${tid}:`,
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
if (updated === content) return false;
|
|
62
|
+
|
|
63
|
+
atomicWriteSync(planPath, updated);
|
|
64
|
+
clearParseCache();
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
@@ -57,9 +57,19 @@ let _lockCompromised: boolean = false;
|
|
|
57
57
|
/** Whether we've already registered a process.on('exit') handler. */
|
|
58
58
|
let _exitHandlerRegistered: boolean = false;
|
|
59
59
|
|
|
60
|
+
/** Snapshotted lock file path — captured at acquireSessionLock time to avoid
|
|
61
|
+
* gsdRoot() resolving differently in worktree vs project root contexts (#1363). */
|
|
62
|
+
let _snapshotLockPath: string | null = null;
|
|
63
|
+
|
|
64
|
+
/** Timestamp when the session lock was acquired — used to detect false-positive
|
|
65
|
+
* onCompromised events from event loop stalls within the stale window (#1362). */
|
|
66
|
+
let _lockAcquiredAt: number = 0;
|
|
67
|
+
|
|
60
68
|
const LOCK_FILE = "auto.lock";
|
|
61
69
|
|
|
62
70
|
function lockPath(basePath: string): string {
|
|
71
|
+
// If we have a snapshotted path from acquisition, use it for consistency
|
|
72
|
+
if (_snapshotLockPath) return _snapshotLockPath;
|
|
63
73
|
return join(gsdRoot(basePath), LOCK_FILE);
|
|
64
74
|
}
|
|
65
75
|
|
|
@@ -198,8 +208,19 @@ export function acquireSessionLock(basePath: string): SessionLockResult {
|
|
|
198
208
|
onCompromised: () => {
|
|
199
209
|
// proper-lockfile detected mtime drift (system sleep, event loop stall, etc.).
|
|
200
210
|
// Default handler throws inside setTimeout — an uncaught exception that crashes
|
|
201
|
-
// or corrupts process state.
|
|
202
|
-
//
|
|
211
|
+
// or corrupts process state.
|
|
212
|
+
//
|
|
213
|
+
// False-positive suppression (#1362): If we're still within the stale window
|
|
214
|
+
// (30 min since acquisition), the mtime mismatch is from an event loop stall
|
|
215
|
+
// during a long LLM call — not a real takeover. Log and continue.
|
|
216
|
+
const elapsed = Date.now() - _lockAcquiredAt;
|
|
217
|
+
if (elapsed < 1_800_000) {
|
|
218
|
+
process.stderr.write(
|
|
219
|
+
`[gsd] Lock heartbeat mismatch after ${Math.round(elapsed / 1000)}s — event loop stall, continuing.\n`,
|
|
220
|
+
);
|
|
221
|
+
return; // Suppress false positive
|
|
222
|
+
}
|
|
223
|
+
// Past the stale window — this is a real compromise
|
|
203
224
|
_lockCompromised = true;
|
|
204
225
|
_releaseFunction = null;
|
|
205
226
|
},
|
|
@@ -209,6 +230,8 @@ export function acquireSessionLock(basePath: string): SessionLockResult {
|
|
|
209
230
|
_lockedPath = basePath;
|
|
210
231
|
_lockPid = process.pid;
|
|
211
232
|
_lockCompromised = false;
|
|
233
|
+
_lockAcquiredAt = Date.now();
|
|
234
|
+
_snapshotLockPath = lp; // Snapshot the resolved path for consistent access (#1363)
|
|
212
235
|
|
|
213
236
|
// Safety net: clean up lock dir on process exit if _releaseFunction
|
|
214
237
|
// wasn't called (e.g., normal exit after clean completion) (#1245).
|
|
@@ -237,6 +260,16 @@ export function acquireSessionLock(basePath: string): SessionLockResult {
|
|
|
237
260
|
stale: 1_800_000, // 30 minutes — match primary lock settings
|
|
238
261
|
update: 10_000,
|
|
239
262
|
onCompromised: () => {
|
|
263
|
+
// Same false-positive suppression as the primary lock (#1512).
|
|
264
|
+
// Without this, the retry path fires _lockCompromised unconditionally
|
|
265
|
+
// on benign mtime drift (laptop sleep, heavy LLM event loop stalls).
|
|
266
|
+
const elapsed = Date.now() - _lockAcquiredAt;
|
|
267
|
+
if (elapsed < 1_800_000) {
|
|
268
|
+
process.stderr.write(
|
|
269
|
+
`[gsd] Lock heartbeat mismatch after ${Math.round(elapsed / 1000)}s — event loop stall, continuing.\n`,
|
|
270
|
+
);
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
240
273
|
_lockCompromised = true;
|
|
241
274
|
_releaseFunction = null;
|
|
242
275
|
},
|
|
@@ -245,6 +278,8 @@ export function acquireSessionLock(basePath: string): SessionLockResult {
|
|
|
245
278
|
_lockedPath = basePath;
|
|
246
279
|
_lockPid = process.pid;
|
|
247
280
|
_lockCompromised = false;
|
|
281
|
+
_lockAcquiredAt = Date.now();
|
|
282
|
+
_snapshotLockPath = lp; // Snapshot for retry path too (#1363)
|
|
248
283
|
|
|
249
284
|
// Safety net — uses centralized handler to avoid double-registration
|
|
250
285
|
ensureExitHandler(gsdDir);
|
|
@@ -336,6 +371,26 @@ export function updateSessionLock(
|
|
|
336
371
|
export function validateSessionLock(basePath: string): boolean {
|
|
337
372
|
// Lock was compromised by proper-lockfile (mtime drift from sleep, stall, etc.)
|
|
338
373
|
if (_lockCompromised) {
|
|
374
|
+
// Recovery gate (#1512): Before declaring the lock lost, check if the lock
|
|
375
|
+
// file still contains our PID. If it does, no other process took over — the
|
|
376
|
+
// onCompromised fired from benign mtime drift (laptop sleep, event loop stall
|
|
377
|
+
// beyond the stale window). Attempt re-acquisition instead of giving up.
|
|
378
|
+
const lp = lockPath(basePath);
|
|
379
|
+
const existing = readExistingLockData(lp);
|
|
380
|
+
if (existing && existing.pid === process.pid) {
|
|
381
|
+
// Lock file still ours — try to re-acquire the OS lock
|
|
382
|
+
try {
|
|
383
|
+
const result = acquireSessionLock(basePath);
|
|
384
|
+
if (result.acquired) {
|
|
385
|
+
process.stderr.write(
|
|
386
|
+
`[gsd] Lock recovered after onCompromised — lock file PID matched, re-acquired.\n`,
|
|
387
|
+
);
|
|
388
|
+
return true;
|
|
389
|
+
}
|
|
390
|
+
} catch {
|
|
391
|
+
// Re-acquisition failed — fall through to return false
|
|
392
|
+
}
|
|
393
|
+
}
|
|
339
394
|
return false;
|
|
340
395
|
}
|
|
341
396
|
|
|
@@ -394,6 +449,8 @@ export function releaseSessionLock(basePath: string): void {
|
|
|
394
449
|
_lockedPath = null;
|
|
395
450
|
_lockPid = 0;
|
|
396
451
|
_lockCompromised = false;
|
|
452
|
+
_lockAcquiredAt = 0;
|
|
453
|
+
_snapshotLockPath = null;
|
|
397
454
|
}
|
|
398
455
|
|
|
399
456
|
/**
|
|
@@ -64,11 +64,12 @@ export function isValidationTerminal(validationContent: string): boolean {
|
|
|
64
64
|
if (!match) return false;
|
|
65
65
|
const verdict = match[1].match(/verdict:\s*(\S+)/);
|
|
66
66
|
if (!verdict) return false;
|
|
67
|
+
const v = verdict[1] === 'passed' ? 'pass' : verdict[1];
|
|
67
68
|
// 'pass' and 'needs-attention' are always terminal.
|
|
68
69
|
// 'needs-remediation' is treated as terminal to prevent infinite loops
|
|
69
70
|
// when no remediation slices exist in the roadmap (#832). The validation
|
|
70
71
|
// report is preserved on disk for manual review.
|
|
71
|
-
return
|
|
72
|
+
return v === 'pass' || v === 'needs-attention' || v === 'needs-remediation';
|
|
72
73
|
}
|
|
73
74
|
|
|
74
75
|
// ─── State Derivation ──────────────────────────────────────────────────────
|
|
@@ -113,6 +113,14 @@
|
|
|
113
113
|
- Tasks execute sequentially in order (T01, T02, T03, ...)
|
|
114
114
|
- est: is informational (e.g. 30m, 1h, 2h) and optional
|
|
115
115
|
|
|
116
|
+
Verify field rules:
|
|
117
|
+
- MUST be a mechanically executable command: `npm test`, `grep -q "pattern" file`, `test -f path`
|
|
118
|
+
- For content/document tasks: verify file existence, section count, YAML validity, or word count
|
|
119
|
+
NOT exact phrasing, specific formulas, or "zero TBD" aspirational criteria
|
|
120
|
+
- If no command can verify the output, write: "Manual review — file exists and is non-empty"
|
|
121
|
+
- BAD: "Sections 3.1 and 3.2 exist with exact formulas. Zero TBD/TODO."
|
|
122
|
+
- GOOD: `grep -c "^## " doc.md` returns >= 4 (4+ sections), `! grep -q "TBD\|TODO" doc.md`
|
|
123
|
+
|
|
116
124
|
Integration closure rule:
|
|
117
125
|
- At least one slice in any multi-boundary milestone should perform real composition/wiring, not just contract hardening
|
|
118
126
|
- For the final assembly slice, verification must exercise the real entrypoint or runtime path
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import fs from "node:fs";
|
|
6
|
+
|
|
7
|
+
import { handleInspect } from "../commands-inspect.ts";
|
|
8
|
+
import { closeDatabase, openDatabase } from "../gsd-db.ts";
|
|
9
|
+
|
|
10
|
+
test("/gsd inspect opens existing database when it was not yet opened in session", async () => {
|
|
11
|
+
closeDatabase();
|
|
12
|
+
|
|
13
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "gsd-inspect-db-"));
|
|
14
|
+
const prevCwd = process.cwd();
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
const gsdDir = path.join(tmp, ".gsd");
|
|
18
|
+
fs.mkdirSync(gsdDir, { recursive: true });
|
|
19
|
+
const dbPath = path.join(gsdDir, "gsd.db");
|
|
20
|
+
|
|
21
|
+
assert.equal(openDatabase(dbPath), true);
|
|
22
|
+
closeDatabase();
|
|
23
|
+
|
|
24
|
+
process.chdir(tmp);
|
|
25
|
+
|
|
26
|
+
const notifications: Array<{ message: string; level: string }> = [];
|
|
27
|
+
const ctx = {
|
|
28
|
+
ui: {
|
|
29
|
+
notify(message: string, level: string) {
|
|
30
|
+
notifications.push({ message, level });
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
} as any;
|
|
34
|
+
|
|
35
|
+
await handleInspect(ctx);
|
|
36
|
+
|
|
37
|
+
assert.equal(notifications.length, 1);
|
|
38
|
+
assert.equal(notifications[0].level, "info");
|
|
39
|
+
assert.match(notifications[0].message, /=== GSD Database Inspect ===/);
|
|
40
|
+
assert.doesNotMatch(notifications[0].message, /No GSD database available/);
|
|
41
|
+
} finally {
|
|
42
|
+
process.chdir(prevCwd);
|
|
43
|
+
closeDatabase();
|
|
44
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
45
|
+
}
|
|
46
|
+
});
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
* doctor-git.test.ts — Integration tests for doctor git health checks.
|
|
3
3
|
*
|
|
4
4
|
* Creates real temp git repos with deliberate broken state, runs runGSDDoctor,
|
|
5
|
-
* and asserts correct detection and fixing of
|
|
5
|
+
* and asserts correct detection and fixing of git issue codes:
|
|
6
6
|
* orphaned_auto_worktree, stale_milestone_branch,
|
|
7
|
-
* corrupt_merge_state, tracked_runtime_files
|
|
7
|
+
* corrupt_merge_state, tracked_runtime_files,
|
|
8
|
+
* integration_branch_missing, worktree_directory_orphaned
|
|
8
9
|
*/
|
|
9
10
|
|
|
10
11
|
import { mkdtempSync, mkdirSync, writeFileSync, rmSync, existsSync, realpathSync } from "node:fs";
|
|
@@ -299,6 +300,101 @@ async function main(): Promise<void> {
|
|
|
299
300
|
console.log("\n=== none-mode skips stale branch (skipped on Windows) ===");
|
|
300
301
|
}
|
|
301
302
|
|
|
303
|
+
// ─── Test: Integration branch missing ──────────────────────────────
|
|
304
|
+
if (process.platform !== "win32") {
|
|
305
|
+
console.log("\n=== integration_branch_missing ===");
|
|
306
|
+
{
|
|
307
|
+
const dir = createRepoWithActiveMilestone();
|
|
308
|
+
cleanups.push(dir);
|
|
309
|
+
|
|
310
|
+
// Write integration branch metadata for M001 pointing to a non-existent branch
|
|
311
|
+
const metaPath = join(dir, ".gsd", "milestones", "M001", "M001-META.json");
|
|
312
|
+
writeFileSync(metaPath, JSON.stringify({ integrationBranch: "feat/does-not-exist" }, null, 2));
|
|
313
|
+
|
|
314
|
+
const detect = await runGSDDoctor(dir);
|
|
315
|
+
const missingBranchIssues = detect.issues.filter(i => i.code === "integration_branch_missing");
|
|
316
|
+
assertTrue(missingBranchIssues.length > 0, "detects missing integration branch");
|
|
317
|
+
assertTrue(
|
|
318
|
+
missingBranchIssues[0]?.message.includes("feat/does-not-exist"),
|
|
319
|
+
"message includes the missing branch name",
|
|
320
|
+
);
|
|
321
|
+
assertEq(missingBranchIssues[0]?.fixable, false, "integration_branch_missing is not auto-fixable");
|
|
322
|
+
assertEq(missingBranchIssues[0]?.severity, "error", "severity is error");
|
|
323
|
+
}
|
|
324
|
+
} else {
|
|
325
|
+
console.log("\n=== integration_branch_missing (skipped on Windows) ===");
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// ─── Test: Integration branch present — no false positive ──────────
|
|
329
|
+
if (process.platform !== "win32") {
|
|
330
|
+
console.log("\n=== integration_branch_missing (no false positive) ===");
|
|
331
|
+
{
|
|
332
|
+
const dir = createRepoWithActiveMilestone();
|
|
333
|
+
cleanups.push(dir);
|
|
334
|
+
|
|
335
|
+
// Write integration branch metadata for M001 pointing to "main" (which exists)
|
|
336
|
+
const metaPath = join(dir, ".gsd", "milestones", "M001", "M001-META.json");
|
|
337
|
+
writeFileSync(metaPath, JSON.stringify({ integrationBranch: "main" }, null, 2));
|
|
338
|
+
|
|
339
|
+
const detect = await runGSDDoctor(dir);
|
|
340
|
+
const missingBranchIssues = detect.issues.filter(i => i.code === "integration_branch_missing");
|
|
341
|
+
assertEq(missingBranchIssues.length, 0, "existing integration branch NOT flagged");
|
|
342
|
+
}
|
|
343
|
+
} else {
|
|
344
|
+
console.log("\n=== integration_branch_missing (no false positive — skipped on Windows) ===");
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// ─── Test: Orphaned worktree directory ─────────────────────────────
|
|
348
|
+
if (process.platform !== "win32") {
|
|
349
|
+
console.log("\n=== worktree_directory_orphaned ===");
|
|
350
|
+
{
|
|
351
|
+
const dir = createRepoWithActiveMilestone();
|
|
352
|
+
cleanups.push(dir);
|
|
353
|
+
|
|
354
|
+
// Create a worktrees/ dir with an entry that is NOT in git worktree list
|
|
355
|
+
const orphanDir = join(dir, ".gsd", "worktrees", "orphan-feature");
|
|
356
|
+
mkdirSync(orphanDir, { recursive: true });
|
|
357
|
+
writeFileSync(join(orphanDir, "some-file.txt"), "leftover content\n");
|
|
358
|
+
|
|
359
|
+
const detect = await runGSDDoctor(dir);
|
|
360
|
+
const orphanDirIssues = detect.issues.filter(i => i.code === "worktree_directory_orphaned");
|
|
361
|
+
assertTrue(orphanDirIssues.length > 0, "detects orphaned worktree directory");
|
|
362
|
+
assertTrue(
|
|
363
|
+
orphanDirIssues[0]?.message.includes("orphan-feature"),
|
|
364
|
+
"message includes the orphaned directory name",
|
|
365
|
+
);
|
|
366
|
+
assertTrue(orphanDirIssues[0]?.fixable === true, "worktree_directory_orphaned is fixable");
|
|
367
|
+
|
|
368
|
+
const fixed = await runGSDDoctor(dir, { fix: true });
|
|
369
|
+
assertTrue(
|
|
370
|
+
fixed.fixesApplied.some(f => f.includes("removed orphaned worktree directory")),
|
|
371
|
+
"fix removes orphaned worktree directory",
|
|
372
|
+
);
|
|
373
|
+
assertTrue(!existsSync(orphanDir), "orphaned directory removed after fix");
|
|
374
|
+
}
|
|
375
|
+
} else {
|
|
376
|
+
console.log("\n=== worktree_directory_orphaned (skipped on Windows) ===");
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// ─── Test: Registered worktree NOT flagged as orphaned ─────────────
|
|
380
|
+
if (process.platform !== "win32") {
|
|
381
|
+
console.log("\n=== worktree_directory_orphaned (registered worktree not flagged) ===");
|
|
382
|
+
{
|
|
383
|
+
const dir = createRepoWithActiveMilestone();
|
|
384
|
+
cleanups.push(dir);
|
|
385
|
+
|
|
386
|
+
// Create a real registered worktree under .gsd/worktrees/
|
|
387
|
+
mkdirSync(join(dir, ".gsd", "worktrees"), { recursive: true });
|
|
388
|
+
run("git worktree add -b worktree/feature-1 .gsd/worktrees/feature-1", dir);
|
|
389
|
+
|
|
390
|
+
const detect = await runGSDDoctor(dir);
|
|
391
|
+
const orphanDirIssues = detect.issues.filter(i => i.code === "worktree_directory_orphaned");
|
|
392
|
+
assertEq(orphanDirIssues.length, 0, "registered worktree NOT flagged as orphaned");
|
|
393
|
+
}
|
|
394
|
+
} else {
|
|
395
|
+
console.log("\n=== worktree_directory_orphaned (registered worktree not flagged — skipped on Windows) ===");
|
|
396
|
+
}
|
|
397
|
+
|
|
302
398
|
// ─── Test 9: none-mode still detects corrupt merge state ───────────
|
|
303
399
|
console.log("\n=== none-mode keeps corrupt merge state ===");
|
|
304
400
|
{
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* doctor-runtime.test.ts — Tests for doctor runtime health checks.
|
|
3
3
|
*
|
|
4
4
|
* Tests detection and auto-fix of:
|
|
5
|
-
* stale_crash_lock,
|
|
6
|
-
* activity_log_bloat, state_file_missing,
|
|
7
|
-
* gitignore_missing_patterns
|
|
5
|
+
* stale_crash_lock, stranded_lock_directory, orphaned_completed_units,
|
|
6
|
+
* stale_hook_state, activity_log_bloat, state_file_missing,
|
|
7
|
+
* state_file_stale, gitignore_missing_patterns
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { mkdtempSync, mkdirSync, writeFileSync, rmSync, existsSync, readFileSync, realpathSync } from "node:fs";
|
|
@@ -290,6 +290,62 @@ node_modules/
|
|
|
290
290
|
assertEq(content.length, 0, "all orphaned keys removed");
|
|
291
291
|
}
|
|
292
292
|
|
|
293
|
+
// ─── Test: Stranded lock directory detection & fix ────────────────
|
|
294
|
+
// Skip on Windows: proper-lockfile uses advisory file locking on Windows,
|
|
295
|
+
// not the directory-based mechanism. The .gsd.lock/ directory pattern is
|
|
296
|
+
// a POSIX-specific lockfile implementation detail.
|
|
297
|
+
if (process.platform !== "win32") {
|
|
298
|
+
console.log("\n=== stranded_lock_directory ===");
|
|
299
|
+
{
|
|
300
|
+
const dir = createMinimalProject();
|
|
301
|
+
cleanups.push(dir);
|
|
302
|
+
|
|
303
|
+
// Create the proper-lockfile lock directory without a live lock holder.
|
|
304
|
+
// The lock dir sits at <parent of .gsd>/.gsd.lock (i.e., <basePath>/.gsd.lock).
|
|
305
|
+
const lockDir = join(dir, ".gsd.lock");
|
|
306
|
+
mkdirSync(lockDir, { recursive: true });
|
|
307
|
+
|
|
308
|
+
const detect = await runGSDDoctor(dir);
|
|
309
|
+
const strandedIssues = detect.issues.filter(i => i.code === "stranded_lock_directory");
|
|
310
|
+
assertTrue(strandedIssues.length > 0, "detects stranded lock directory");
|
|
311
|
+
assertTrue(strandedIssues[0]?.message.includes("lock directory"), "message describes stranded lock directory");
|
|
312
|
+
assertTrue(strandedIssues[0]?.fixable === true, "stranded lock dir is fixable");
|
|
313
|
+
|
|
314
|
+
const fixed = await runGSDDoctor(dir, { fix: true });
|
|
315
|
+
assertTrue(
|
|
316
|
+
fixed.fixesApplied.some(f => f.includes("removed stranded lock directory")),
|
|
317
|
+
"fix removes stranded lock directory",
|
|
318
|
+
);
|
|
319
|
+
assertTrue(!existsSync(lockDir), "lock directory removed after fix");
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// ─── Test: Stranded lock dir with live lock holder — NOT flagged ───
|
|
323
|
+
console.log("\n=== stranded_lock_directory (live holder not flagged) ===");
|
|
324
|
+
{
|
|
325
|
+
const dir = createMinimalProject();
|
|
326
|
+
cleanups.push(dir);
|
|
327
|
+
|
|
328
|
+
// Create lock dir + auto.lock with PID 1 (init/launchd — always alive, never our own PID)
|
|
329
|
+
const lockDir = join(dir, ".gsd.lock");
|
|
330
|
+
mkdirSync(lockDir, { recursive: true });
|
|
331
|
+
const liveLockData = {
|
|
332
|
+
pid: 1,
|
|
333
|
+
startedAt: new Date().toISOString(),
|
|
334
|
+
unitType: "execute-task",
|
|
335
|
+
unitId: "M001/S01/T01",
|
|
336
|
+
unitStartedAt: new Date().toISOString(),
|
|
337
|
+
completedUnits: 1,
|
|
338
|
+
};
|
|
339
|
+
writeFileSync(join(dir, ".gsd", "auto.lock"), JSON.stringify(liveLockData, null, 2));
|
|
340
|
+
|
|
341
|
+
const detect = await runGSDDoctor(dir);
|
|
342
|
+
const strandedIssues = detect.issues.filter(i => i.code === "stranded_lock_directory");
|
|
343
|
+
assertEq(strandedIssues.length, 0, "live lock holder: stranded_lock_directory NOT detected");
|
|
344
|
+
}
|
|
345
|
+
} else {
|
|
346
|
+
console.log("\n=== stranded_lock_directory (skipped on Windows) ===");
|
|
347
|
+
}
|
|
348
|
+
|
|
293
349
|
} finally {
|
|
294
350
|
for (const dir of cleanups) {
|
|
295
351
|
try { rmSync(dir, { recursive: true, force: true }); } catch { /* ignore */ }
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import fs from "node:fs";
|
|
6
|
+
|
|
7
|
+
import { loadFile } from "../files.ts";
|
|
8
|
+
|
|
9
|
+
test("loadFile returns null for directory paths instead of throwing EISDIR", async () => {
|
|
10
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "gsd-loadfile-eisdir-"));
|
|
11
|
+
const dirPath = path.join(tmp, "tasks");
|
|
12
|
+
fs.mkdirSync(dirPath);
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
const result = await loadFile(dirPath);
|
|
16
|
+
assert.equal(result, null);
|
|
17
|
+
} finally {
|
|
18
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
19
|
+
}
|
|
20
|
+
});
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gitignore-tracked-gsd.test.ts — Regression tests for #1364.
|
|
3
|
+
*
|
|
4
|
+
* Verifies that ensureGitignore() does NOT add ".gsd" to .gitignore
|
|
5
|
+
* when .gsd/ contains git-tracked files, and that migrateToExternalState()
|
|
6
|
+
* aborts migration for tracked .gsd/ directories.
|
|
7
|
+
*
|
|
8
|
+
* Uses real temporary git repos — no mocks.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import test from "node:test";
|
|
12
|
+
import assert from "node:assert/strict";
|
|
13
|
+
import { execFileSync } from "node:child_process";
|
|
14
|
+
import {
|
|
15
|
+
existsSync,
|
|
16
|
+
mkdirSync,
|
|
17
|
+
mkdtempSync,
|
|
18
|
+
readFileSync,
|
|
19
|
+
rmSync,
|
|
20
|
+
writeFileSync,
|
|
21
|
+
} from "node:fs";
|
|
22
|
+
import { join } from "node:path";
|
|
23
|
+
import { tmpdir } from "node:os";
|
|
24
|
+
|
|
25
|
+
import { ensureGitignore, hasGitTrackedGsdFiles } from "../gitignore.ts";
|
|
26
|
+
import { migrateToExternalState } from "../migrate-external.ts";
|
|
27
|
+
|
|
28
|
+
// ─── Helpers ─────────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
function git(dir: string, ...args: string[]): string {
|
|
31
|
+
return execFileSync("git", args, { cwd: dir, stdio: "pipe", encoding: "utf-8" }).trim();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function makeTempRepo(): string {
|
|
35
|
+
const dir = mkdtempSync(join(tmpdir(), "gsd-gitignore-test-"));
|
|
36
|
+
git(dir, "init");
|
|
37
|
+
git(dir, "config", "user.email", "test@test.com");
|
|
38
|
+
git(dir, "config", "user.name", "Test");
|
|
39
|
+
writeFileSync(join(dir, "README.md"), "# init\n");
|
|
40
|
+
git(dir, "add", "-A");
|
|
41
|
+
git(dir, "commit", "-m", "init");
|
|
42
|
+
git(dir, "branch", "-M", "main");
|
|
43
|
+
return dir;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function cleanup(dir: string): void {
|
|
47
|
+
try {
|
|
48
|
+
rmSync(dir, { recursive: true, force: true });
|
|
49
|
+
} catch {
|
|
50
|
+
// ignore
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ─── hasGitTrackedGsdFiles ───────────────────────────────────────────
|
|
55
|
+
|
|
56
|
+
test("hasGitTrackedGsdFiles returns false when .gsd/ does not exist", () => {
|
|
57
|
+
const dir = makeTempRepo();
|
|
58
|
+
try {
|
|
59
|
+
assert.equal(hasGitTrackedGsdFiles(dir), false);
|
|
60
|
+
} finally {
|
|
61
|
+
cleanup(dir);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("hasGitTrackedGsdFiles returns true when .gsd/ has tracked files", () => {
|
|
66
|
+
const dir = makeTempRepo();
|
|
67
|
+
try {
|
|
68
|
+
mkdirSync(join(dir, ".gsd", "milestones"), { recursive: true });
|
|
69
|
+
writeFileSync(join(dir, ".gsd", "PROJECT.md"), "# Test Project\n");
|
|
70
|
+
git(dir, "add", ".gsd/PROJECT.md");
|
|
71
|
+
git(dir, "commit", "-m", "add gsd");
|
|
72
|
+
assert.equal(hasGitTrackedGsdFiles(dir), true);
|
|
73
|
+
} finally {
|
|
74
|
+
cleanup(dir);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("hasGitTrackedGsdFiles returns false when .gsd/ exists but is untracked", () => {
|
|
79
|
+
const dir = makeTempRepo();
|
|
80
|
+
try {
|
|
81
|
+
mkdirSync(join(dir, ".gsd"), { recursive: true });
|
|
82
|
+
writeFileSync(join(dir, ".gsd", "STATE.md"), "state\n");
|
|
83
|
+
// Not git-added — should return false
|
|
84
|
+
assert.equal(hasGitTrackedGsdFiles(dir), false);
|
|
85
|
+
} finally {
|
|
86
|
+
cleanup(dir);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// ─── ensureGitignore — tracked .gsd/ protection ─────────────────────
|
|
91
|
+
|
|
92
|
+
test("ensureGitignore does NOT add .gsd when .gsd/ has tracked files (#1364)", () => {
|
|
93
|
+
const dir = makeTempRepo();
|
|
94
|
+
try {
|
|
95
|
+
// Set up .gsd/ with tracked files
|
|
96
|
+
mkdirSync(join(dir, ".gsd", "milestones"), { recursive: true });
|
|
97
|
+
writeFileSync(join(dir, ".gsd", "PROJECT.md"), "# Test Project\n");
|
|
98
|
+
writeFileSync(join(dir, ".gsd", "DECISIONS.md"), "# Decisions\n");
|
|
99
|
+
git(dir, "add", ".gsd/");
|
|
100
|
+
git(dir, "commit", "-m", "track gsd state");
|
|
101
|
+
|
|
102
|
+
// Run ensureGitignore
|
|
103
|
+
ensureGitignore(dir);
|
|
104
|
+
|
|
105
|
+
// Verify .gsd is NOT in .gitignore
|
|
106
|
+
const gitignore = readFileSync(join(dir, ".gitignore"), "utf-8");
|
|
107
|
+
const lines = gitignore.split("\n").map((l) => l.trim());
|
|
108
|
+
assert.ok(
|
|
109
|
+
!lines.includes(".gsd"),
|
|
110
|
+
`Expected .gsd NOT to appear in .gitignore, but it does:\n${gitignore}`,
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
// Other baseline patterns should still be present
|
|
114
|
+
assert.ok(lines.includes(".DS_Store"), "Expected .DS_Store in .gitignore");
|
|
115
|
+
assert.ok(lines.includes("node_modules/"), "Expected node_modules/ in .gitignore");
|
|
116
|
+
} finally {
|
|
117
|
+
cleanup(dir);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("ensureGitignore adds .gsd when .gsd/ has NO tracked files", () => {
|
|
122
|
+
const dir = makeTempRepo();
|
|
123
|
+
try {
|
|
124
|
+
// Run ensureGitignore (no .gsd/ at all)
|
|
125
|
+
ensureGitignore(dir);
|
|
126
|
+
|
|
127
|
+
// Verify .gsd IS in .gitignore
|
|
128
|
+
const gitignore = readFileSync(join(dir, ".gitignore"), "utf-8");
|
|
129
|
+
const lines = gitignore.split("\n").map((l) => l.trim());
|
|
130
|
+
assert.ok(
|
|
131
|
+
lines.includes(".gsd"),
|
|
132
|
+
`Expected .gsd in .gitignore, but it's missing:\n${gitignore}`,
|
|
133
|
+
);
|
|
134
|
+
} finally {
|
|
135
|
+
cleanup(dir);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test("ensureGitignore respects manageGitignore: false", () => {
|
|
140
|
+
const dir = makeTempRepo();
|
|
141
|
+
try {
|
|
142
|
+
const result = ensureGitignore(dir, { manageGitignore: false });
|
|
143
|
+
assert.equal(result, false);
|
|
144
|
+
assert.ok(!existsSync(join(dir, ".gitignore")), "Should not create .gitignore");
|
|
145
|
+
} finally {
|
|
146
|
+
cleanup(dir);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// ─── ensureGitignore — verify no tracked files become invisible ─────
|
|
151
|
+
|
|
152
|
+
test("ensureGitignore with tracked .gsd/ does not cause git to see files as deleted", () => {
|
|
153
|
+
const dir = makeTempRepo();
|
|
154
|
+
try {
|
|
155
|
+
// Create tracked .gsd/ files
|
|
156
|
+
mkdirSync(join(dir, ".gsd", "milestones", "M001"), { recursive: true });
|
|
157
|
+
writeFileSync(join(dir, ".gsd", "PROJECT.md"), "# Project\n");
|
|
158
|
+
writeFileSync(
|
|
159
|
+
join(dir, ".gsd", "milestones", "M001", "M001-CONTEXT.md"),
|
|
160
|
+
"# M001\n",
|
|
161
|
+
);
|
|
162
|
+
git(dir, "add", ".gsd/");
|
|
163
|
+
git(dir, "commit", "-m", "track gsd state");
|
|
164
|
+
|
|
165
|
+
// Run ensureGitignore
|
|
166
|
+
ensureGitignore(dir);
|
|
167
|
+
|
|
168
|
+
// git status should show NO deleted files under .gsd/
|
|
169
|
+
const status = git(dir, "status", "--porcelain", ".gsd/");
|
|
170
|
+
|
|
171
|
+
// Filter for deletions (lines starting with " D" or "D ")
|
|
172
|
+
const deletions = status
|
|
173
|
+
.split("\n")
|
|
174
|
+
.filter((l) => l.match(/^\s*D\s/) || l.match(/^D\s/));
|
|
175
|
+
|
|
176
|
+
assert.equal(
|
|
177
|
+
deletions.length,
|
|
178
|
+
0,
|
|
179
|
+
`Expected no deleted .gsd/ files, but found:\n${deletions.join("\n")}`,
|
|
180
|
+
);
|
|
181
|
+
} finally {
|
|
182
|
+
cleanup(dir);
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// ─── migrateToExternalState — tracked .gsd/ protection ──────────────
|
|
187
|
+
|
|
188
|
+
test("migrateToExternalState aborts when .gsd/ has tracked files (#1364)", () => {
|
|
189
|
+
const dir = makeTempRepo();
|
|
190
|
+
try {
|
|
191
|
+
// Create tracked .gsd/ files
|
|
192
|
+
mkdirSync(join(dir, ".gsd", "milestones"), { recursive: true });
|
|
193
|
+
writeFileSync(join(dir, ".gsd", "PROJECT.md"), "# Project\n");
|
|
194
|
+
git(dir, "add", ".gsd/");
|
|
195
|
+
git(dir, "commit", "-m", "track gsd state");
|
|
196
|
+
|
|
197
|
+
// Attempt migration — should abort without moving anything
|
|
198
|
+
const result = migrateToExternalState(dir);
|
|
199
|
+
|
|
200
|
+
assert.equal(result.migrated, false, "Should NOT migrate tracked .gsd/");
|
|
201
|
+
assert.equal(result.error, undefined, "Should not report an error — just skip");
|
|
202
|
+
|
|
203
|
+
// .gsd/ should still be a real directory, not a symlink
|
|
204
|
+
assert.ok(existsSync(join(dir, ".gsd", "PROJECT.md")), ".gsd/PROJECT.md should still exist");
|
|
205
|
+
|
|
206
|
+
// No .gsd.migrating should exist
|
|
207
|
+
assert.ok(
|
|
208
|
+
!existsSync(join(dir, ".gsd.migrating")),
|
|
209
|
+
".gsd.migrating should not exist",
|
|
210
|
+
);
|
|
211
|
+
} finally {
|
|
212
|
+
cleanup(dir);
|
|
213
|
+
}
|
|
214
|
+
});
|