gsd-pi 2.20.0 → 2.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -0
- package/dist/cli.js +21 -18
- package/dist/help-text.d.ts +2 -0
- package/dist/help-text.js +47 -0
- package/dist/loader.js +2 -16
- package/dist/mcp-server.d.ts +18 -0
- package/dist/mcp-server.js +53 -0
- package/dist/onboarding.d.ts +0 -6
- package/dist/onboarding.js +0 -28
- package/dist/resources/agents/javascript-pro.md +280 -0
- package/dist/resources/agents/typescript-pro.md +255 -0
- package/dist/resources/extensions/bg-shell/index.ts +14 -2
- package/dist/resources/extensions/bg-shell/utilities.ts +14 -0
- package/dist/resources/extensions/browser-tools/{core.js → core.ts} +329 -190
- package/dist/resources/extensions/gsd/auto-dashboard.ts +2 -1
- package/dist/resources/extensions/gsd/auto-prompts.ts +42 -1
- package/dist/resources/extensions/gsd/auto-worktree.ts +35 -2
- package/dist/resources/extensions/gsd/auto.ts +116 -10
- package/dist/resources/extensions/gsd/claude-import.ts +656 -0
- package/dist/resources/extensions/gsd/collision-diagnostics.ts +332 -0
- package/dist/resources/extensions/gsd/commands.ts +60 -7
- package/dist/resources/extensions/gsd/diff-context.ts +220 -0
- package/dist/resources/extensions/gsd/docs/claude-marketplace-import.md +214 -0
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +2 -1
- package/dist/resources/extensions/gsd/doctor.ts +12 -4
- package/dist/resources/extensions/gsd/file-watcher.ts +97 -0
- package/dist/resources/extensions/gsd/files.ts +1 -1
- package/dist/resources/extensions/gsd/forensics.ts +596 -0
- package/dist/resources/extensions/gsd/git-service.ts +2 -1
- package/dist/resources/extensions/gsd/github-client.ts +235 -0
- package/dist/resources/extensions/gsd/gitignore.ts +1 -0
- package/dist/resources/extensions/gsd/marketplace-discovery.ts +507 -0
- package/dist/resources/extensions/gsd/mcp-server.ts +87 -0
- package/dist/resources/extensions/gsd/namespaced-registry.ts +467 -0
- package/dist/resources/extensions/gsd/namespaced-resolver.ts +307 -0
- package/dist/resources/extensions/gsd/plugin-importer.ts +410 -0
- package/dist/resources/extensions/gsd/preferences.ts +14 -3
- package/dist/resources/extensions/gsd/prompts/execute-task.md +1 -1
- package/dist/resources/extensions/gsd/prompts/forensics.md +71 -0
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/research-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/system.md +12 -3
- package/dist/resources/extensions/gsd/roadmap-slices.ts +1 -1
- package/dist/resources/extensions/gsd/session-forensics.ts +5 -5
- package/dist/resources/extensions/gsd/tests/claude-import-tui.test.ts +351 -0
- package/dist/resources/extensions/gsd/tests/collision-diagnostics.test.ts +705 -0
- package/dist/resources/extensions/gsd/tests/context-compression.test.ts +13 -0
- package/dist/resources/extensions/gsd/tests/diff-context.test.ts +136 -0
- package/dist/resources/extensions/gsd/tests/doctor-git.test.ts +118 -0
- package/dist/resources/extensions/gsd/tests/marketplace-discovery.test.ts +202 -0
- package/dist/resources/extensions/gsd/tests/marketplace-test-fixtures.ts +91 -0
- package/dist/resources/extensions/gsd/tests/namespaced-registry.test.ts +1027 -0
- package/dist/resources/extensions/gsd/tests/namespaced-resolver.test.ts +671 -0
- package/dist/resources/extensions/gsd/tests/none-mode-gates.test.ts +105 -0
- package/dist/resources/extensions/gsd/tests/plugin-importer-live.test.ts +481 -0
- package/dist/resources/extensions/gsd/tests/plugin-importer.test.ts +1383 -0
- package/dist/resources/extensions/gsd/tests/preferences-git.test.ts +21 -2
- package/dist/resources/extensions/gsd/tests/preferences-schema-validation.test.ts +8 -1
- package/dist/resources/extensions/gsd/tests/visualizer-data.test.ts +11 -0
- package/dist/resources/extensions/gsd/tests/worktree-bugfix.test.ts +120 -0
- package/dist/resources/extensions/gsd/token-counter.ts +45 -0
- package/dist/resources/extensions/gsd/visualizer-data.ts +2 -2
- package/dist/resources/extensions/gsd/worktree-manager.ts +29 -1
- package/dist/resources/extensions/gsd/worktree.ts +3 -0
- package/dist/resources/extensions/mcporter/index.ts +90 -7
- package/dist/resources/extensions/search-the-web/native-search.ts +2 -0
- package/dist/resources/extensions/search-the-web/tool-fetch-page.ts +9 -1
- package/dist/resources/extensions/search-the-web/url-utils.ts +35 -1
- package/dist/resources/extensions/shared/terminal.ts +1 -1
- package/dist/resources/extensions/universal-config/discovery.ts +4 -0
- package/dist/resources/extensions/universal-config/format.ts +35 -4
- package/dist/resources/extensions/universal-config/index.ts +5 -3
- package/dist/resources/extensions/universal-config/scanners.ts +65 -2
- package/dist/resources/extensions/universal-config/tests/discovery.test.ts +9 -1
- package/dist/resources/extensions/universal-config/tests/format.test.ts +22 -6
- package/dist/resources/extensions/universal-config/tests/scanners.test.ts +18 -0
- package/dist/resources/extensions/universal-config/types.ts +20 -1
- package/dist/resources/skills/lint/SKILL.md +141 -0
- package/dist/resources/skills/review/SKILL.md +214 -0
- package/dist/resources/skills/test/SKILL.md +201 -0
- package/dist/tool-bootstrap.js +2 -1
- package/dist/wizard.js +2 -0
- package/package.json +12 -7
- package/packages/pi-ai/dist/providers/azure-openai-responses.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/azure-openai-responses.js +12 -1
- package/packages/pi-ai/dist/providers/azure-openai-responses.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 +12 -1
- package/packages/pi-ai/dist/providers/openai-responses.js.map +1 -1
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/LICENSE +201 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/README.md +9 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-cjs/index.js +762 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/build-abort-error.js +19 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/constants.js +1 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/get-transformed-headers.js +9 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/index.js +3 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/node-http-handler.js +230 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/node-http2-connection-manager.js +87 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/node-http2-connection-pool.js +32 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/node-http2-handler.js +169 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/readable.mock.js +21 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/server.mock.js +88 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/set-connection-timeout.js +36 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/set-request-timeout.js +21 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/set-socket-keep-alive.js +22 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/set-socket-timeout.js +23 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/stream-collector/collector.js +8 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/stream-collector/index.js +41 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/stream-collector/readable.mock.js +21 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/timing.js +4 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/write-request-body.js +63 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/build-abort-error.d.ts +10 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/constants.d.ts +5 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/get-transformed-headers.d.ts +4 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/index.d.ts +3 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/node-http-handler.d.ts +46 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/node-http2-connection-manager.d.ts +24 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/node-http2-connection-pool.d.ts +12 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/node-http2-handler.d.ts +63 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/readable.mock.d.ts +13 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/server.mock.d.ts +12 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/set-connection-timeout.d.ts +2 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/set-request-timeout.d.ts +6 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/set-socket-keep-alive.d.ts +6 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/set-socket-timeout.d.ts +2 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/stream-collector/collector.d.ts +5 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/stream-collector/index.d.ts +6 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/stream-collector/readable.mock.d.ts +13 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/timing.d.ts +8 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/build-abort-error.d.ts +10 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/constants.d.ts +5 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/get-transformed-headers.d.ts +4 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/index.d.ts +3 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/node-http-handler.d.ts +46 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/node-http2-connection-manager.d.ts +24 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/node-http2-connection-pool.d.ts +12 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/node-http2-handler.d.ts +63 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/readable.mock.d.ts +13 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/server.mock.d.ts +12 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/set-connection-timeout.d.ts +2 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/set-request-timeout.d.ts +6 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/set-socket-keep-alive.d.ts +6 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/set-socket-timeout.d.ts +2 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/stream-collector/collector.d.ts +5 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/stream-collector/index.d.ts +6 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/stream-collector/readable.mock.d.ts +13 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/timing.d.ts +8 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/write-request-body.d.ts +12 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/write-request-body.d.ts +12 -0
- package/packages/pi-ai/node_modules/@smithy/node-http-handler/package.json +68 -0
- package/packages/pi-ai/package.json +3 -0
- package/packages/pi-ai/pnpm-lock.yaml +2022 -0
- package/packages/pi-ai/src/providers/azure-openai-responses.ts +12 -1
- package/packages/pi-ai/src/providers/openai-responses.ts +12 -1
- package/packages/pi-coding-agent/dist/core/extensions/index.d.ts +1 -1
- 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/loader.d.ts +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +12 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.test.js +113 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/project-trust.d.ts +4 -0
- package/packages/pi-coding-agent/dist/core/extensions/project-trust.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/project-trust.js +42 -0
- package/packages/pi-coding-agent/dist/core/extensions/project-trust.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/resolve-config-value.d.ts +1 -0
- package/packages/pi-coding-agent/dist/core/resolve-config-value.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/resolve-config-value.js +18 -0
- package/packages/pi-coding-agent/dist/core/resolve-config-value.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resolve-config-value.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/resolve-config-value.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +114 -0
- package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +8 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.d.ts +2 -0
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js +23 -9
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js.map +1 -1
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js +67 -0
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js.map +1 -0
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/pnpm-lock.yaml +454 -0
- package/packages/pi-coding-agent/scripts/copy-assets.cjs +24 -0
- package/packages/pi-coding-agent/src/core/extensions/index.ts +3 -0
- package/packages/pi-coding-agent/src/core/extensions/loader.test.ts +141 -0
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +14 -1
- package/packages/pi-coding-agent/src/core/extensions/project-trust.ts +51 -0
- package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +132 -0
- package/packages/pi-coding-agent/src/core/resolve-config-value.ts +20 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/login-dialog.ts +7 -2
- package/packages/pi-coding-agent/src/resources/extensions/memory/storage.test.ts +98 -0
- package/packages/pi-coding-agent/src/resources/extensions/memory/storage.ts +24 -9
- package/packages/pi-tui/dist/tui.d.ts.map +1 -1
- package/packages/pi-tui/dist/tui.js +2 -2
- package/packages/pi-tui/dist/tui.js.map +1 -1
- package/packages/pi-tui/package.json +3 -1
- package/packages/pi-tui/src/tui.ts +2 -2
- package/src/resources/agents/javascript-pro.md +280 -0
- package/src/resources/agents/typescript-pro.md +255 -0
- package/src/resources/extensions/bg-shell/index.ts +14 -2
- package/src/resources/extensions/bg-shell/utilities.ts +14 -0
- package/src/resources/extensions/browser-tools/{core.js → core.ts} +329 -190
- package/src/resources/extensions/gsd/auto-dashboard.ts +2 -1
- package/src/resources/extensions/gsd/auto-prompts.ts +42 -1
- package/src/resources/extensions/gsd/auto-worktree.ts +35 -2
- package/src/resources/extensions/gsd/auto.ts +116 -10
- package/src/resources/extensions/gsd/claude-import.ts +656 -0
- package/src/resources/extensions/gsd/collision-diagnostics.ts +332 -0
- package/src/resources/extensions/gsd/commands.ts +60 -7
- package/src/resources/extensions/gsd/diff-context.ts +220 -0
- package/src/resources/extensions/gsd/docs/claude-marketplace-import.md +214 -0
- package/src/resources/extensions/gsd/docs/preferences-reference.md +2 -1
- package/src/resources/extensions/gsd/doctor.ts +12 -4
- package/src/resources/extensions/gsd/file-watcher.ts +97 -0
- package/src/resources/extensions/gsd/files.ts +1 -1
- package/src/resources/extensions/gsd/forensics.ts +596 -0
- package/src/resources/extensions/gsd/git-service.ts +2 -1
- package/src/resources/extensions/gsd/github-client.ts +235 -0
- package/src/resources/extensions/gsd/gitignore.ts +1 -0
- package/src/resources/extensions/gsd/marketplace-discovery.ts +507 -0
- package/src/resources/extensions/gsd/mcp-server.ts +87 -0
- package/src/resources/extensions/gsd/namespaced-registry.ts +467 -0
- package/src/resources/extensions/gsd/namespaced-resolver.ts +307 -0
- package/src/resources/extensions/gsd/plugin-importer.ts +410 -0
- package/src/resources/extensions/gsd/preferences.ts +14 -3
- package/src/resources/extensions/gsd/prompts/execute-task.md +1 -1
- package/src/resources/extensions/gsd/prompts/forensics.md +71 -0
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/research-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/system.md +12 -3
- package/src/resources/extensions/gsd/roadmap-slices.ts +1 -1
- package/src/resources/extensions/gsd/session-forensics.ts +5 -5
- package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +351 -0
- package/src/resources/extensions/gsd/tests/collision-diagnostics.test.ts +705 -0
- package/src/resources/extensions/gsd/tests/context-compression.test.ts +13 -0
- package/src/resources/extensions/gsd/tests/diff-context.test.ts +136 -0
- package/src/resources/extensions/gsd/tests/doctor-git.test.ts +118 -0
- package/src/resources/extensions/gsd/tests/marketplace-discovery.test.ts +202 -0
- package/src/resources/extensions/gsd/tests/marketplace-test-fixtures.ts +91 -0
- package/src/resources/extensions/gsd/tests/namespaced-registry.test.ts +1027 -0
- package/src/resources/extensions/gsd/tests/namespaced-resolver.test.ts +671 -0
- package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +105 -0
- package/src/resources/extensions/gsd/tests/plugin-importer-live.test.ts +481 -0
- package/src/resources/extensions/gsd/tests/plugin-importer.test.ts +1383 -0
- package/src/resources/extensions/gsd/tests/preferences-git.test.ts +21 -2
- package/src/resources/extensions/gsd/tests/preferences-schema-validation.test.ts +8 -1
- package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +11 -0
- package/src/resources/extensions/gsd/tests/worktree-bugfix.test.ts +120 -0
- package/src/resources/extensions/gsd/token-counter.ts +45 -0
- package/src/resources/extensions/gsd/visualizer-data.ts +2 -2
- package/src/resources/extensions/gsd/worktree-manager.ts +29 -1
- package/src/resources/extensions/gsd/worktree.ts +3 -0
- package/src/resources/extensions/mcporter/index.ts +90 -7
- package/src/resources/extensions/search-the-web/native-search.ts +2 -0
- package/src/resources/extensions/search-the-web/tool-fetch-page.ts +9 -1
- package/src/resources/extensions/search-the-web/url-utils.ts +35 -1
- package/src/resources/extensions/shared/terminal.ts +1 -1
- package/src/resources/extensions/universal-config/discovery.ts +4 -0
- package/src/resources/extensions/universal-config/format.ts +35 -4
- package/src/resources/extensions/universal-config/index.ts +5 -3
- package/src/resources/extensions/universal-config/scanners.ts +65 -2
- package/src/resources/extensions/universal-config/tests/discovery.test.ts +9 -1
- package/src/resources/extensions/universal-config/tests/format.test.ts +22 -6
- package/src/resources/extensions/universal-config/tests/scanners.test.ts +18 -0
- package/src/resources/extensions/universal-config/types.ts +20 -1
- package/src/resources/skills/lint/SKILL.md +141 -0
- package/src/resources/skills/review/SKILL.md +214 -0
- package/src/resources/skills/test/SKILL.md +201 -0
- package/dist/resources/extensions/browser-tools/core.d.ts +0 -205
- package/src/resources/extensions/browser-tools/core.d.ts +0 -205
|
@@ -0,0 +1,596 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GSD Forensics — Post-mortem investigation of auto-mode failures
|
|
3
|
+
*
|
|
4
|
+
* Programmatically scans activity logs, metrics, crash locks, and doctor
|
|
5
|
+
* diagnostics for anomalies, then hands a structured report to the LLM
|
|
6
|
+
* for interactive investigation.
|
|
7
|
+
*
|
|
8
|
+
* Entry point: handleForensics() called from commands.ts
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { ExtensionAPI, ExtensionCommandContext } from "@gsd/pi-coding-agent";
|
|
12
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
13
|
+
import { join, dirname, relative } from "node:path";
|
|
14
|
+
import { fileURLToPath } from "node:url";
|
|
15
|
+
|
|
16
|
+
import { extractTrace, type ExecutionTrace } from "./session-forensics.js";
|
|
17
|
+
import { nativeParseJsonlTail } from "./native-parser-bridge.js";
|
|
18
|
+
import {
|
|
19
|
+
loadLedgerFromDisk, getAverageCostPerUnitType, getProjectTotals,
|
|
20
|
+
formatCost, formatTokenCount, type UnitMetrics, type MetricsLedger,
|
|
21
|
+
} from "./metrics.js";
|
|
22
|
+
import { readCrashLock, isLockProcessAlive, formatCrashInfo, type LockData } from "./crash-recovery.js";
|
|
23
|
+
import { runGSDDoctor, formatDoctorIssuesForPrompt, type DoctorIssue } from "./doctor.js";
|
|
24
|
+
import { verifyExpectedArtifact } from "./auto-recovery.js";
|
|
25
|
+
import { deriveState } from "./state.js";
|
|
26
|
+
import { isAutoActive } from "./auto.js";
|
|
27
|
+
import { loadPrompt } from "./prompt-loader.js";
|
|
28
|
+
import { gsdRoot } from "./paths.js";
|
|
29
|
+
import { formatDuration } from "./history.js";
|
|
30
|
+
|
|
31
|
+
// ─── Types ────────────────────────────────────────────────────────────────────
|
|
32
|
+
|
|
33
|
+
interface ForensicAnomaly {
|
|
34
|
+
type: "stuck-loop" | "cost-spike" | "timeout" | "missing-artifact" | "crash" | "doctor-issue" | "error-trace";
|
|
35
|
+
severity: "info" | "warning" | "error";
|
|
36
|
+
unitType?: string;
|
|
37
|
+
unitId?: string;
|
|
38
|
+
summary: string;
|
|
39
|
+
details: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface UnitTrace {
|
|
43
|
+
file: string;
|
|
44
|
+
unitType: string;
|
|
45
|
+
unitId: string;
|
|
46
|
+
seq: number;
|
|
47
|
+
trace: ExecutionTrace;
|
|
48
|
+
mtime: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface ForensicReport {
|
|
52
|
+
gsdVersion: string;
|
|
53
|
+
timestamp: string;
|
|
54
|
+
basePath: string;
|
|
55
|
+
activeMilestone: string | null;
|
|
56
|
+
activeSlice: string | null;
|
|
57
|
+
unitTraces: UnitTrace[];
|
|
58
|
+
metrics: MetricsLedger | null;
|
|
59
|
+
completedKeys: string[];
|
|
60
|
+
crashLock: LockData | null;
|
|
61
|
+
doctorIssues: DoctorIssue[];
|
|
62
|
+
anomalies: ForensicAnomaly[];
|
|
63
|
+
recentUnits: { type: string; id: string; cost: number; duration: number; model: string; finishedAt: number }[];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ─── JSONL Parser (inline — session-forensics.ts version is module-private) ──
|
|
67
|
+
|
|
68
|
+
const MAX_JSONL_BYTES = 5 * 1024 * 1024;
|
|
69
|
+
|
|
70
|
+
function parseJSONL(raw: string): unknown[] {
|
|
71
|
+
const source = raw.length > MAX_JSONL_BYTES ? raw.slice(-MAX_JSONL_BYTES) : raw;
|
|
72
|
+
return source.trim().split("\n").map(line => {
|
|
73
|
+
try { return JSON.parse(line); } catch { return null; }
|
|
74
|
+
}).filter(Boolean) as unknown[];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ─── Entry Point ──────────────────────────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
export async function handleForensics(
|
|
80
|
+
args: string,
|
|
81
|
+
ctx: ExtensionCommandContext,
|
|
82
|
+
pi: ExtensionAPI,
|
|
83
|
+
): Promise<void> {
|
|
84
|
+
if (isAutoActive()) {
|
|
85
|
+
ctx.ui.notify("Cannot run forensics while auto-mode is active. Stop auto-mode first.", "error");
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const basePath = process.cwd();
|
|
90
|
+
const root = gsdRoot(basePath);
|
|
91
|
+
if (!existsSync(root)) {
|
|
92
|
+
ctx.ui.notify("No GSD state found. Run /gsd auto first.", "warning");
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
let problemDescription = args.trim();
|
|
97
|
+
if (!problemDescription) {
|
|
98
|
+
problemDescription = await ctx.ui.input(
|
|
99
|
+
"Describe what went wrong:",
|
|
100
|
+
"e.g. auto-mode got stuck on task T03",
|
|
101
|
+
) ?? "";
|
|
102
|
+
}
|
|
103
|
+
if (!problemDescription?.trim()) {
|
|
104
|
+
ctx.ui.notify("Problem description required for forensic analysis.", "warning");
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
ctx.ui.notify("Building forensic report...", "info");
|
|
109
|
+
|
|
110
|
+
const report = await buildForensicReport(basePath);
|
|
111
|
+
const savedPath = saveForensicReport(basePath, report, problemDescription);
|
|
112
|
+
|
|
113
|
+
// Derive GSD source dir for prompt
|
|
114
|
+
const __extensionDir = dirname(fileURLToPath(import.meta.url));
|
|
115
|
+
const gsdSourceDir = __extensionDir;
|
|
116
|
+
|
|
117
|
+
const forensicData = formatReportForPrompt(report);
|
|
118
|
+
const content = loadPrompt("forensics", {
|
|
119
|
+
problemDescription,
|
|
120
|
+
forensicData,
|
|
121
|
+
gsdSourceDir,
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
ctx.ui.notify(`Forensic report saved: ${relative(basePath, savedPath)}`, "info");
|
|
125
|
+
|
|
126
|
+
pi.sendMessage(
|
|
127
|
+
{ customType: "gsd-forensics", content, display: false },
|
|
128
|
+
{ triggerTurn: true },
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// ─── Report Builder ───────────────────────────────────────────────────────────
|
|
133
|
+
|
|
134
|
+
async function buildForensicReport(basePath: string): Promise<ForensicReport> {
|
|
135
|
+
const anomalies: ForensicAnomaly[] = [];
|
|
136
|
+
|
|
137
|
+
// 1. Derive current state
|
|
138
|
+
let activeMilestone: string | null = null;
|
|
139
|
+
let activeSlice: string | null = null;
|
|
140
|
+
try {
|
|
141
|
+
const state = await deriveState(basePath);
|
|
142
|
+
activeMilestone = state.activeMilestone?.id ?? null;
|
|
143
|
+
activeSlice = state.activeSlice?.id ?? null;
|
|
144
|
+
} catch { /* state derivation failure is non-fatal */ }
|
|
145
|
+
|
|
146
|
+
// 2. Scan activity logs (last 5)
|
|
147
|
+
const unitTraces = scanActivityLogs(basePath);
|
|
148
|
+
|
|
149
|
+
// 3. Load metrics
|
|
150
|
+
const metrics = loadLedgerFromDisk(basePath);
|
|
151
|
+
|
|
152
|
+
// 4. Load completed keys
|
|
153
|
+
const completedKeys = loadCompletedKeys(basePath);
|
|
154
|
+
|
|
155
|
+
// 5. Check crash lock
|
|
156
|
+
const crashLock = readCrashLock(basePath);
|
|
157
|
+
|
|
158
|
+
// 6. Run doctor
|
|
159
|
+
let doctorIssues: DoctorIssue[] = [];
|
|
160
|
+
try {
|
|
161
|
+
const report = await runGSDDoctor(basePath, { scope: undefined });
|
|
162
|
+
doctorIssues = report.issues;
|
|
163
|
+
} catch { /* doctor failure is non-fatal */ }
|
|
164
|
+
|
|
165
|
+
// 7. Build recent units from metrics
|
|
166
|
+
const recentUnits: ForensicReport["recentUnits"] = [];
|
|
167
|
+
if (metrics?.units) {
|
|
168
|
+
const sorted = [...metrics.units].sort((a, b) => b.finishedAt - a.finishedAt).slice(0, 10);
|
|
169
|
+
for (const u of sorted) {
|
|
170
|
+
recentUnits.push({
|
|
171
|
+
type: u.type,
|
|
172
|
+
id: u.id,
|
|
173
|
+
cost: u.cost,
|
|
174
|
+
duration: u.finishedAt - u.startedAt,
|
|
175
|
+
model: u.model,
|
|
176
|
+
finishedAt: u.finishedAt,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// 8. GSD version
|
|
182
|
+
let gsdVersion = "unknown";
|
|
183
|
+
try {
|
|
184
|
+
const pkgPath = join(dirname(fileURLToPath(import.meta.url)), "../../../../package.json");
|
|
185
|
+
if (existsSync(pkgPath)) {
|
|
186
|
+
gsdVersion = JSON.parse(readFileSync(pkgPath, "utf-8")).version ?? "unknown";
|
|
187
|
+
}
|
|
188
|
+
} catch { /* non-fatal */ }
|
|
189
|
+
|
|
190
|
+
// 9. Run anomaly detectors
|
|
191
|
+
if (metrics?.units) detectStuckLoops(metrics.units, anomalies);
|
|
192
|
+
if (metrics?.units) detectCostSpikes(metrics.units, anomalies);
|
|
193
|
+
detectTimeouts(unitTraces, anomalies);
|
|
194
|
+
detectMissingArtifacts(completedKeys, basePath, anomalies);
|
|
195
|
+
detectCrash(crashLock, anomalies);
|
|
196
|
+
detectDoctorIssues(doctorIssues, anomalies);
|
|
197
|
+
detectErrorTraces(unitTraces, anomalies);
|
|
198
|
+
|
|
199
|
+
return {
|
|
200
|
+
gsdVersion,
|
|
201
|
+
timestamp: new Date().toISOString(),
|
|
202
|
+
basePath,
|
|
203
|
+
activeMilestone,
|
|
204
|
+
activeSlice,
|
|
205
|
+
unitTraces,
|
|
206
|
+
metrics,
|
|
207
|
+
completedKeys,
|
|
208
|
+
crashLock,
|
|
209
|
+
doctorIssues,
|
|
210
|
+
anomalies,
|
|
211
|
+
recentUnits,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// ─── Activity Log Scanner ─────────────────────────────────────────────────────
|
|
216
|
+
|
|
217
|
+
const ACTIVITY_FILENAME_RE = /^(\d+)-(.+?)-(.+)\.jsonl$/;
|
|
218
|
+
|
|
219
|
+
function scanActivityLogs(basePath: string): UnitTrace[] {
|
|
220
|
+
const activityDir = join(gsdRoot(basePath), "activity");
|
|
221
|
+
if (!existsSync(activityDir)) return [];
|
|
222
|
+
|
|
223
|
+
const files = readdirSync(activityDir).filter(f => f.endsWith(".jsonl")).sort();
|
|
224
|
+
const lastFiles = files.slice(-5);
|
|
225
|
+
const traces: UnitTrace[] = [];
|
|
226
|
+
|
|
227
|
+
for (const file of lastFiles) {
|
|
228
|
+
const match = ACTIVITY_FILENAME_RE.exec(file);
|
|
229
|
+
if (!match) continue;
|
|
230
|
+
|
|
231
|
+
const seq = parseInt(match[1]!, 10);
|
|
232
|
+
const unitType = match[2]!;
|
|
233
|
+
const unitId = match[3]!;
|
|
234
|
+
const filePath = join(activityDir, file);
|
|
235
|
+
|
|
236
|
+
let entries: unknown[] = [];
|
|
237
|
+
const nativeResult = nativeParseJsonlTail(filePath, MAX_JSONL_BYTES);
|
|
238
|
+
if (nativeResult) {
|
|
239
|
+
entries = nativeResult.entries;
|
|
240
|
+
} else {
|
|
241
|
+
try {
|
|
242
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
243
|
+
entries = parseJSONL(raw);
|
|
244
|
+
} catch { continue; }
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const trace = extractTrace(entries);
|
|
248
|
+
const stat = statSync(filePath, { throwIfNoEntry: false });
|
|
249
|
+
|
|
250
|
+
traces.push({
|
|
251
|
+
file,
|
|
252
|
+
unitType,
|
|
253
|
+
unitId,
|
|
254
|
+
seq,
|
|
255
|
+
trace,
|
|
256
|
+
mtime: stat?.mtimeMs ?? 0,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return traces.sort((a, b) => b.seq - a.seq);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// ─── Completed Keys Loader ────────────────────────────────────────────────────
|
|
264
|
+
|
|
265
|
+
function loadCompletedKeys(basePath: string): string[] {
|
|
266
|
+
const file = join(basePath, ".gsd", "completed-units.json");
|
|
267
|
+
try {
|
|
268
|
+
if (existsSync(file)) {
|
|
269
|
+
return JSON.parse(readFileSync(file, "utf-8"));
|
|
270
|
+
}
|
|
271
|
+
} catch { /* non-fatal */ }
|
|
272
|
+
return [];
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// ─── Anomaly Detectors ───────────────────────────────────────────────────────
|
|
276
|
+
|
|
277
|
+
function detectStuckLoops(units: UnitMetrics[], anomalies: ForensicAnomaly[]): void {
|
|
278
|
+
const counts = new Map<string, number>();
|
|
279
|
+
for (const u of units) {
|
|
280
|
+
const key = `${u.type}/${u.id}`;
|
|
281
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
282
|
+
}
|
|
283
|
+
for (const [key, count] of counts) {
|
|
284
|
+
if (count > 1) {
|
|
285
|
+
const [unitType, ...idParts] = key.split("/");
|
|
286
|
+
anomalies.push({
|
|
287
|
+
type: "stuck-loop",
|
|
288
|
+
severity: count >= 3 ? "error" : "warning",
|
|
289
|
+
unitType,
|
|
290
|
+
unitId: idParts.join("/"),
|
|
291
|
+
summary: `Unit ${key} was dispatched ${count} times`,
|
|
292
|
+
details: `Repeated dispatch suggests the unit completed but its artifacts weren't verified, or the state machine kept returning it.`,
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function detectCostSpikes(units: UnitMetrics[], anomalies: ForensicAnomaly[]): void {
|
|
299
|
+
const avgMap = getAverageCostPerUnitType(units);
|
|
300
|
+
for (const u of units) {
|
|
301
|
+
const avg = avgMap.get(u.type);
|
|
302
|
+
if (avg && avg > 0 && u.cost > avg * 3) {
|
|
303
|
+
anomalies.push({
|
|
304
|
+
type: "cost-spike",
|
|
305
|
+
severity: "warning",
|
|
306
|
+
unitType: u.type,
|
|
307
|
+
unitId: u.id,
|
|
308
|
+
summary: `${formatCost(u.cost)} vs ${formatCost(avg)} average for ${u.type}`,
|
|
309
|
+
details: `Unit ${u.type}/${u.id} cost ${(u.cost / avg).toFixed(1)}x the average. May indicate excessive retries or large context.`,
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function detectTimeouts(traces: UnitTrace[], anomalies: ForensicAnomaly[]): void {
|
|
316
|
+
for (const ut of traces) {
|
|
317
|
+
// Check for timeout-recovery custom messages in tool calls
|
|
318
|
+
const hasTimeout = ut.trace.toolCalls.some(tc =>
|
|
319
|
+
tc.name === "sendmessage" &&
|
|
320
|
+
JSON.stringify(tc.input).includes("gsd-auto-timeout-recovery"),
|
|
321
|
+
);
|
|
322
|
+
// Check for timeout keywords in last reasoning
|
|
323
|
+
const reasoningTimeout = ut.trace.lastReasoning &&
|
|
324
|
+
/(?:idle.?timeout|hard.?timeout|timeout.?recovery)/i.test(ut.trace.lastReasoning);
|
|
325
|
+
|
|
326
|
+
if (hasTimeout || reasoningTimeout) {
|
|
327
|
+
anomalies.push({
|
|
328
|
+
type: "timeout",
|
|
329
|
+
severity: "warning",
|
|
330
|
+
unitType: ut.unitType,
|
|
331
|
+
unitId: ut.unitId,
|
|
332
|
+
summary: `Timeout detected in ${ut.unitType}/${ut.unitId}`,
|
|
333
|
+
details: `Activity log ${ut.file} contains timeout recovery patterns. The unit may have stalled.`,
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function detectMissingArtifacts(completedKeys: string[], basePath: string, anomalies: ForensicAnomaly[]): void {
|
|
340
|
+
for (const key of completedKeys) {
|
|
341
|
+
const slashIdx = key.indexOf("/");
|
|
342
|
+
if (slashIdx === -1) continue;
|
|
343
|
+
const unitType = key.slice(0, slashIdx);
|
|
344
|
+
const unitId = key.slice(slashIdx + 1);
|
|
345
|
+
|
|
346
|
+
if (!verifyExpectedArtifact(unitType, unitId, basePath)) {
|
|
347
|
+
anomalies.push({
|
|
348
|
+
type: "missing-artifact",
|
|
349
|
+
severity: "error",
|
|
350
|
+
unitType,
|
|
351
|
+
unitId,
|
|
352
|
+
summary: `Completed key ${key} but artifact missing or invalid`,
|
|
353
|
+
details: `The unit is recorded as completed but verifyExpectedArtifact() returns false. The completion state is stale.`,
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function detectCrash(crashLock: LockData | null, anomalies: ForensicAnomaly[]): void {
|
|
360
|
+
if (!crashLock) return;
|
|
361
|
+
if (isLockProcessAlive(crashLock)) return; // Process still running, not a crash
|
|
362
|
+
|
|
363
|
+
anomalies.push({
|
|
364
|
+
type: "crash",
|
|
365
|
+
severity: "error",
|
|
366
|
+
unitType: crashLock.unitType,
|
|
367
|
+
unitId: crashLock.unitId,
|
|
368
|
+
summary: `Stale crash lock: PID ${crashLock.pid} is dead`,
|
|
369
|
+
details: formatCrashInfo(crashLock),
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function detectDoctorIssues(issues: DoctorIssue[], anomalies: ForensicAnomaly[]): void {
|
|
374
|
+
for (const issue of issues) {
|
|
375
|
+
if (issue.severity === "error") {
|
|
376
|
+
anomalies.push({
|
|
377
|
+
type: "doctor-issue",
|
|
378
|
+
severity: "error",
|
|
379
|
+
summary: `Doctor: ${issue.message}`,
|
|
380
|
+
details: `Code: ${issue.code}, Scope: ${issue.scope}, Unit: ${issue.unitId}${issue.file ? `, File: ${issue.file}` : ""}`,
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function detectErrorTraces(traces: UnitTrace[], anomalies: ForensicAnomaly[]): void {
|
|
387
|
+
for (const ut of traces) {
|
|
388
|
+
if (ut.trace.errors.length > 0) {
|
|
389
|
+
anomalies.push({
|
|
390
|
+
type: "error-trace",
|
|
391
|
+
severity: "warning",
|
|
392
|
+
unitType: ut.unitType,
|
|
393
|
+
unitId: ut.unitId,
|
|
394
|
+
summary: `${ut.trace.errors.length} error(s) in ${ut.unitType}/${ut.unitId}`,
|
|
395
|
+
details: ut.trace.errors.slice(0, 3).join("\n"),
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// ─── Report Persistence ───────────────────────────────────────────────────────
|
|
402
|
+
|
|
403
|
+
function saveForensicReport(basePath: string, report: ForensicReport, problemDescription: string): string {
|
|
404
|
+
const dir = join(gsdRoot(basePath), "forensics");
|
|
405
|
+
mkdirSync(dir, { recursive: true });
|
|
406
|
+
|
|
407
|
+
const ts = new Date().toISOString().replace(/[:.]/g, "-").replace("T", "-").slice(0, 19);
|
|
408
|
+
const filePath = join(dir, `report-${ts}.md`);
|
|
409
|
+
|
|
410
|
+
const redact = (s: string) => redactForGitHub(s, basePath);
|
|
411
|
+
|
|
412
|
+
const sections: string[] = [
|
|
413
|
+
`# GSD Forensic Report`,
|
|
414
|
+
``,
|
|
415
|
+
`**Generated:** ${report.timestamp}`,
|
|
416
|
+
`**GSD Version:** ${report.gsdVersion}`,
|
|
417
|
+
`**Active Milestone:** ${report.activeMilestone ?? "none"}`,
|
|
418
|
+
`**Active Slice:** ${report.activeSlice ?? "none"}`,
|
|
419
|
+
``,
|
|
420
|
+
`## Problem Description`,
|
|
421
|
+
``,
|
|
422
|
+
problemDescription,
|
|
423
|
+
``,
|
|
424
|
+
];
|
|
425
|
+
|
|
426
|
+
// Anomalies
|
|
427
|
+
if (report.anomalies.length > 0) {
|
|
428
|
+
sections.push(`## Anomalies Detected (${report.anomalies.length})`, ``);
|
|
429
|
+
for (const a of report.anomalies) {
|
|
430
|
+
sections.push(`### [${a.severity.toUpperCase()}] ${a.type}: ${a.summary}`);
|
|
431
|
+
if (a.unitType) sections.push(`- Unit: ${a.unitType}/${a.unitId ?? ""}`);
|
|
432
|
+
sections.push(`- ${redact(a.details)}`, ``);
|
|
433
|
+
}
|
|
434
|
+
} else {
|
|
435
|
+
sections.push(`## Anomalies`, ``, `No anomalies detected.`, ``);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// Recent units
|
|
439
|
+
if (report.recentUnits.length > 0) {
|
|
440
|
+
sections.push(`## Recent Units`, ``);
|
|
441
|
+
sections.push(`| Type | ID | Cost | Duration | Model |`);
|
|
442
|
+
sections.push(`|------|-----|------|----------|-------|`);
|
|
443
|
+
for (const u of report.recentUnits) {
|
|
444
|
+
sections.push(`| ${u.type} | ${u.id} | ${formatCost(u.cost)} | ${formatDuration(u.duration)} | ${u.model} |`);
|
|
445
|
+
}
|
|
446
|
+
sections.push(``);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// Unit traces
|
|
450
|
+
if (report.unitTraces.length > 0) {
|
|
451
|
+
sections.push(`## Activity Log Traces (last ${report.unitTraces.length})`, ``);
|
|
452
|
+
for (const ut of report.unitTraces) {
|
|
453
|
+
sections.push(`### ${ut.unitType}/${ut.unitId} (seq ${ut.seq})`);
|
|
454
|
+
sections.push(`- Tool calls: ${ut.trace.toolCallCount}`);
|
|
455
|
+
sections.push(`- Files written: ${ut.trace.filesWritten.length}`);
|
|
456
|
+
sections.push(`- Errors: ${ut.trace.errors.length}`);
|
|
457
|
+
if (ut.trace.lastReasoning) {
|
|
458
|
+
sections.push(`- Last reasoning: ${redact(ut.trace.lastReasoning.slice(0, 200))}`);
|
|
459
|
+
}
|
|
460
|
+
sections.push(``);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Doctor issues
|
|
465
|
+
if (report.doctorIssues.length > 0) {
|
|
466
|
+
sections.push(`## Doctor Issues`, ``);
|
|
467
|
+
sections.push(formatDoctorIssuesForPrompt(report.doctorIssues), ``);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// Crash lock
|
|
471
|
+
if (report.crashLock) {
|
|
472
|
+
sections.push(`## Crash Lock`, ``);
|
|
473
|
+
sections.push(redact(formatCrashInfo(report.crashLock)), ``);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
writeFileSync(filePath, sections.join("\n"), "utf-8");
|
|
477
|
+
return filePath;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// ─── Prompt Formatter ─────────────────────────────────────────────────────────
|
|
481
|
+
|
|
482
|
+
function formatReportForPrompt(report: ForensicReport): string {
|
|
483
|
+
const MAX_BYTES = 30 * 1024;
|
|
484
|
+
const sections: string[] = [];
|
|
485
|
+
|
|
486
|
+
// Anomalies (most important, first)
|
|
487
|
+
sections.push(`### Anomalies (${report.anomalies.length})`);
|
|
488
|
+
if (report.anomalies.length === 0) {
|
|
489
|
+
sections.push("No anomalies detected.");
|
|
490
|
+
} else {
|
|
491
|
+
for (const a of report.anomalies) {
|
|
492
|
+
sections.push(`- **[${a.severity.toUpperCase()}] ${a.type}**: ${a.summary}`);
|
|
493
|
+
if (a.details) sections.push(` ${a.details.slice(0, 300)}`);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
sections.push("");
|
|
497
|
+
|
|
498
|
+
// Recent unit history
|
|
499
|
+
if (report.recentUnits.length > 0) {
|
|
500
|
+
sections.push(`### Recent Units (last ${report.recentUnits.length})`);
|
|
501
|
+
sections.push("| Type | ID | Cost | Duration | Model |");
|
|
502
|
+
sections.push("|------|-----|------|----------|-------|");
|
|
503
|
+
for (const u of report.recentUnits) {
|
|
504
|
+
sections.push(`| ${u.type} | ${u.id} | ${formatCost(u.cost)} | ${formatDuration(u.duration)} | ${u.model} |`);
|
|
505
|
+
}
|
|
506
|
+
sections.push("");
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// Trace summaries (last 3)
|
|
510
|
+
const recentTraces = report.unitTraces.slice(0, 3);
|
|
511
|
+
if (recentTraces.length > 0) {
|
|
512
|
+
sections.push(`### Activity Log Traces (last ${recentTraces.length})`);
|
|
513
|
+
for (const ut of recentTraces) {
|
|
514
|
+
sections.push(`**${ut.unitType}/${ut.unitId}** (seq ${ut.seq})`);
|
|
515
|
+
sections.push(`- Tool calls: ${ut.trace.toolCallCount}, Errors: ${ut.trace.errors.length}`);
|
|
516
|
+
if (ut.trace.filesWritten.length > 0) {
|
|
517
|
+
sections.push(`- Files written: ${ut.trace.filesWritten.slice(0, 5).join(", ")}`);
|
|
518
|
+
}
|
|
519
|
+
if (ut.trace.errors.length > 0) {
|
|
520
|
+
sections.push(`- Errors: ${ut.trace.errors.slice(0, 2).map(e => e.slice(0, 200)).join("; ")}`);
|
|
521
|
+
}
|
|
522
|
+
if (ut.trace.lastReasoning) {
|
|
523
|
+
sections.push(`- Last reasoning: "${ut.trace.lastReasoning.slice(0, 300)}"`);
|
|
524
|
+
}
|
|
525
|
+
sections.push("");
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// Doctor issues (error severity only)
|
|
530
|
+
const errorIssues = report.doctorIssues.filter(i => i.severity === "error");
|
|
531
|
+
if (errorIssues.length > 0) {
|
|
532
|
+
sections.push(`### Doctor Issues (${errorIssues.length} errors)`);
|
|
533
|
+
sections.push(formatDoctorIssuesForPrompt(errorIssues));
|
|
534
|
+
sections.push("");
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// Crash lock
|
|
538
|
+
if (report.crashLock) {
|
|
539
|
+
sections.push("### Crash Lock");
|
|
540
|
+
sections.push(formatCrashInfo(report.crashLock));
|
|
541
|
+
const alive = isLockProcessAlive(report.crashLock);
|
|
542
|
+
sections.push(`Process alive: ${alive}`);
|
|
543
|
+
sections.push("");
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// Metrics summary
|
|
547
|
+
if (report.metrics?.units) {
|
|
548
|
+
const totals = getProjectTotals(report.metrics.units);
|
|
549
|
+
sections.push("### Metrics Summary");
|
|
550
|
+
sections.push(`- Total units: ${totals.units}`);
|
|
551
|
+
sections.push(`- Total cost: ${formatCost(totals.cost)}`);
|
|
552
|
+
sections.push(`- Total tokens: ${formatTokenCount(totals.tokens.total)}`);
|
|
553
|
+
sections.push(`- Total duration: ${formatDuration(totals.duration)}`);
|
|
554
|
+
sections.push("");
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// Completed keys count
|
|
558
|
+
sections.push(`### Completed Keys: ${report.completedKeys.length}`);
|
|
559
|
+
sections.push(`### GSD Version: ${report.gsdVersion}`);
|
|
560
|
+
sections.push(`### Active Milestone: ${report.activeMilestone ?? "none"}`);
|
|
561
|
+
sections.push(`### Active Slice: ${report.activeSlice ?? "none"}`);
|
|
562
|
+
|
|
563
|
+
let result = sections.join("\n");
|
|
564
|
+
if (result.length > MAX_BYTES) {
|
|
565
|
+
result = result.slice(0, MAX_BYTES) + "\n\n[... truncated at 30KB ...]";
|
|
566
|
+
}
|
|
567
|
+
return result;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// ─── Redaction ────────────────────────────────────────────────────────────────
|
|
571
|
+
|
|
572
|
+
function redactForGitHub(text: string, basePath: string): string {
|
|
573
|
+
let result = text;
|
|
574
|
+
|
|
575
|
+
// Replace absolute paths
|
|
576
|
+
result = result.replaceAll(basePath, ".");
|
|
577
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
578
|
+
if (home) result = result.replaceAll(home, "~");
|
|
579
|
+
|
|
580
|
+
// Strip API key patterns
|
|
581
|
+
result = result.replace(/sk-[a-zA-Z0-9]{20,}/g, "sk-***");
|
|
582
|
+
result = result.replace(/Bearer\s+\S+/g, "Bearer ***");
|
|
583
|
+
|
|
584
|
+
// Strip env var assignments
|
|
585
|
+
result = result.replace(/[A-Z_]{2,}=\S+/g, (match) => {
|
|
586
|
+
const eq = match.indexOf("=");
|
|
587
|
+
return match.slice(0, eq + 1) + "***";
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
// Truncate long lines
|
|
591
|
+
result = result.split("\n").map(line =>
|
|
592
|
+
line.length > 500 ? line.slice(0, 497) + "..." : line,
|
|
593
|
+
).join("\n");
|
|
594
|
+
|
|
595
|
+
return result;
|
|
596
|
+
}
|
|
@@ -45,8 +45,9 @@ export interface GitPreferences {
|
|
|
45
45
|
/** Controls auto-mode git isolation strategy.
|
|
46
46
|
* - "worktree": (default) creates a milestone worktree for isolated work
|
|
47
47
|
* - "branch": works directly in the project root (for submodule-heavy repos)
|
|
48
|
+
* - "none": no git isolation — commits land on the user's current branch directly
|
|
48
49
|
*/
|
|
49
|
-
isolation?: "worktree" | "branch";
|
|
50
|
+
isolation?: "worktree" | "branch" | "none";
|
|
50
51
|
/** When false, prevents GSD from committing .gsd/ planning artifacts to git.
|
|
51
52
|
* The .gsd/ folder is added to .gitignore and kept local-only.
|
|
52
53
|
* Default: true (planning docs are tracked in git).
|