gsd-pi 2.74.0-dev.b741afb → 2.74.0-dev.ffbcc03
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/dist/resources/extensions/gsd/activity-log.js +16 -0
- package/dist/resources/extensions/gsd/auto/loop.js +147 -10
- package/dist/resources/extensions/gsd/auto/phases.js +113 -3
- package/dist/resources/extensions/gsd/auto/session.js +10 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +11 -1
- package/dist/resources/extensions/gsd/auto-model-selection.js +51 -5
- package/dist/resources/extensions/gsd/auto-post-unit.js +215 -8
- package/dist/resources/extensions/gsd/auto-recovery.js +24 -10
- package/dist/resources/extensions/gsd/auto-unit-closeout.js +18 -0
- package/dist/resources/extensions/gsd/auto-verification.js +100 -2
- package/dist/resources/extensions/gsd/auto.js +28 -2
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +10 -1
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +61 -9
- package/dist/resources/extensions/gsd/cache.js +16 -5
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +5 -0
- package/dist/resources/extensions/gsd/commands-extract-learnings.js +225 -0
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +1 -1
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +14 -1
- package/dist/resources/extensions/gsd/ecosystem/gsd-extension-api.js +144 -0
- package/dist/resources/extensions/gsd/ecosystem/loader.js +145 -0
- package/dist/resources/extensions/gsd/git-service.js +49 -1
- package/dist/resources/extensions/gsd/graph-context.js +98 -7
- package/dist/resources/extensions/gsd/gsd-db.js +260 -2
- package/dist/resources/extensions/gsd/guided-flow.js +24 -1
- package/dist/resources/extensions/gsd/init-wizard.js +1 -0
- package/dist/resources/extensions/gsd/journal.js +27 -0
- package/dist/resources/extensions/gsd/metrics.js +19 -0
- package/dist/resources/extensions/gsd/parallel-orchestrator.js +33 -1
- package/dist/resources/extensions/gsd/preferences-models.js +20 -3
- package/dist/resources/extensions/gsd/preferences-types.js +1 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +108 -2
- package/dist/resources/extensions/gsd/preferences.js +26 -0
- package/dist/resources/extensions/gsd/safety/evidence-collector.js +15 -30
- package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +12 -2
- package/dist/resources/extensions/gsd/templates/PREFERENCES.md +18 -0
- package/dist/resources/extensions/gsd/tools/complete-slice.js +5 -0
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +39 -4
- package/dist/resources/extensions/gsd/unit-ownership.js +1 -1
- package/dist/resources/extensions/gsd/uok/audit-toggle.js +7 -0
- package/dist/resources/extensions/gsd/uok/audit.js +40 -0
- package/dist/resources/extensions/gsd/uok/contracts.js +1 -0
- package/dist/resources/extensions/gsd/uok/execution-graph.js +179 -0
- package/dist/resources/extensions/gsd/uok/flags.js +29 -0
- package/dist/resources/extensions/gsd/uok/gate-runner.js +109 -0
- package/dist/resources/extensions/gsd/uok/gitops.js +53 -0
- package/dist/resources/extensions/gsd/uok/kernel.js +80 -0
- package/dist/resources/extensions/gsd/uok/loop-adapter.js +133 -0
- package/dist/resources/extensions/gsd/uok/model-policy.js +66 -0
- package/dist/resources/extensions/gsd/uok/plan-v2.js +132 -0
- package/dist/resources/extensions/gsd/workflow-logger.js +22 -0
- package/dist/resources/extensions/ttsr/ttsr-manager.js +3 -1
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +9 -9
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +9 -9
- package/dist/web/standalone/.next/server/chunks/6897.js +3 -3
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/package.json +1 -1
- package/packages/mcp-server/dist/readers/graph.d.ts +1 -1
- package/packages/mcp-server/dist/readers/graph.d.ts.map +1 -1
- package/packages/mcp-server/dist/readers/graph.js +107 -0
- package/packages/mcp-server/dist/readers/graph.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +88 -6
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/src/readers/graph.test.ts +178 -0
- package/packages/mcp-server/src/readers/graph.ts +148 -1
- package/packages/mcp-server/src/workflow-tools.ts +95 -10
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-ai/dist/index.d.ts +1 -9
- package/packages/pi-ai/dist/index.d.ts.map +1 -1
- package/packages/pi-ai/dist/index.js +1 -9
- package/packages/pi-ai/dist/index.js.map +1 -1
- package/packages/pi-ai/dist/models/capability-patches.d.ts +19 -0
- package/packages/pi-ai/dist/models/capability-patches.d.ts.map +1 -0
- package/packages/pi-ai/dist/models/capability-patches.js +36 -0
- package/packages/pi-ai/dist/models/capability-patches.js.map +1 -0
- package/packages/pi-ai/dist/{models.custom.d.ts → models/custom.d.ts} +1 -1
- package/packages/pi-ai/dist/models/custom.d.ts.map +1 -0
- package/packages/pi-ai/dist/{models.custom.js → models/custom.js} +4 -4
- package/packages/pi-ai/dist/models/custom.js.map +1 -0
- package/packages/pi-ai/dist/models/generated/amazon-bedrock.d.ts +1482 -0
- package/packages/pi-ai/dist/models/generated/amazon-bedrock.d.ts.map +1 -0
- package/packages/pi-ai/dist/models/generated/amazon-bedrock.js +1484 -0
- package/packages/pi-ai/dist/models/generated/amazon-bedrock.js.map +1 -0
- package/packages/pi-ai/dist/models/generated/anthropic.d.ts +377 -0
- package/packages/pi-ai/dist/models/generated/anthropic.d.ts.map +1 -0
- package/packages/pi-ai/dist/models/generated/anthropic.js +379 -0
- package/packages/pi-ai/dist/models/generated/anthropic.js.map +1 -0
- package/packages/pi-ai/dist/models/generated/azure-openai-responses.d.ts +700 -0
- package/packages/pi-ai/dist/models/generated/azure-openai-responses.d.ts.map +1 -0
- package/packages/pi-ai/dist/models/generated/azure-openai-responses.js +702 -0
- package/packages/pi-ai/dist/models/generated/azure-openai-responses.js.map +1 -0
- package/packages/pi-ai/dist/models/generated/cerebras.d.ts +71 -0
- package/packages/pi-ai/dist/models/generated/cerebras.d.ts.map +1 -0
- package/packages/pi-ai/dist/models/generated/cerebras.js +73 -0
- package/packages/pi-ai/dist/models/generated/cerebras.js.map +1 -0
- package/packages/pi-ai/dist/models/generated/github-copilot.d.ts +590 -0
- package/packages/pi-ai/dist/models/generated/github-copilot.d.ts.map +1 -0
- package/packages/pi-ai/dist/models/generated/github-copilot.js +444 -0
- package/packages/pi-ai/dist/models/generated/github-copilot.js.map +1 -0
- package/packages/pi-ai/dist/models/generated/google-antigravity.d.ts +156 -0
- package/packages/pi-ai/dist/models/generated/google-antigravity.d.ts.map +1 -0
- package/packages/pi-ai/dist/models/generated/google-antigravity.js +158 -0
- package/packages/pi-ai/dist/models/generated/google-antigravity.js.map +1 -0
- package/packages/pi-ai/dist/models/generated/google-gemini-cli.d.ts +105 -0
- package/packages/pi-ai/dist/models/generated/google-gemini-cli.d.ts.map +1 -0
- package/packages/pi-ai/dist/models/generated/google-gemini-cli.js +107 -0
- package/packages/pi-ai/dist/models/generated/google-gemini-cli.js.map +1 -0
- package/packages/pi-ai/dist/models/generated/google-vertex.d.ts +207 -0
- package/packages/pi-ai/dist/models/generated/google-vertex.d.ts.map +1 -0
- package/packages/pi-ai/dist/models/generated/google-vertex.js +209 -0
- package/packages/pi-ai/dist/models/generated/google-vertex.js.map +1 -0
- package/packages/pi-ai/dist/models/generated/google.d.ts +462 -0
- package/packages/pi-ai/dist/models/generated/google.d.ts.map +1 -0
- package/packages/pi-ai/dist/models/generated/google.js +464 -0
- package/packages/pi-ai/dist/models/generated/google.js.map +1 -0
- package/packages/pi-ai/dist/models/generated/groq.d.ts +309 -0
- package/packages/pi-ai/dist/models/generated/groq.d.ts.map +1 -0
- package/packages/pi-ai/dist/models/generated/groq.js +311 -0
- package/packages/pi-ai/dist/models/generated/groq.js.map +1 -0
- package/packages/pi-ai/dist/models/generated/huggingface.d.ts +383 -0
- package/packages/pi-ai/dist/models/generated/huggingface.d.ts.map +1 -0
- package/packages/pi-ai/dist/models/generated/huggingface.js +347 -0
- package/packages/pi-ai/dist/models/generated/huggingface.js.map +1 -0
- package/packages/pi-ai/dist/{models.generated.d.ts → models/generated/index.d.ts} +1 -1
- package/packages/pi-ai/dist/{models.generated.d.ts.map → models/generated/index.d.ts.map} +1 -1
- package/packages/pi-ai/dist/models/generated/index.js +51 -0
- package/packages/pi-ai/dist/models/generated/index.js.map +1 -0
- package/packages/pi-ai/dist/models/generated/kimi-coding.d.ts +37 -0
- package/packages/pi-ai/dist/models/generated/kimi-coding.d.ts.map +1 -0
- package/packages/pi-ai/dist/models/generated/kimi-coding.js +39 -0
- package/packages/pi-ai/dist/models/generated/kimi-coding.js.map +1 -0
- package/packages/pi-ai/dist/models/generated/minimax-cn.d.ts +105 -0
- package/packages/pi-ai/dist/models/generated/minimax-cn.d.ts.map +1 -0
- package/packages/pi-ai/dist/models/generated/minimax-cn.js +107 -0
- package/packages/pi-ai/dist/models/generated/minimax-cn.js.map +1 -0
- package/packages/pi-ai/dist/models/generated/minimax.d.ts +105 -0
- package/packages/pi-ai/dist/models/generated/minimax.d.ts.map +1 -0
- package/packages/pi-ai/dist/models/generated/minimax.js +107 -0
- package/packages/pi-ai/dist/models/generated/minimax.js.map +1 -0
- package/packages/pi-ai/dist/models/generated/mistral.d.ts +445 -0
- package/packages/pi-ai/dist/models/generated/mistral.d.ts.map +1 -0
- package/packages/pi-ai/dist/models/generated/mistral.js +447 -0
- package/packages/pi-ai/dist/models/generated/mistral.js.map +1 -0
- package/packages/pi-ai/dist/models/generated/openai-codex.d.ts +139 -0
- package/packages/pi-ai/dist/models/generated/openai-codex.d.ts.map +1 -0
- package/packages/pi-ai/dist/models/generated/openai-codex.js +141 -0
- package/packages/pi-ai/dist/models/generated/openai-codex.js.map +1 -0
- package/packages/pi-ai/dist/models/generated/openai.d.ts +700 -0
- package/packages/pi-ai/dist/models/generated/openai.d.ts.map +1 -0
- package/packages/pi-ai/dist/models/generated/openai.js +702 -0
- package/packages/pi-ai/dist/models/generated/openai.js.map +1 -0
- package/packages/pi-ai/dist/models/generated/opencode-go.d.ts +122 -0
- package/packages/pi-ai/dist/models/generated/opencode-go.d.ts.map +1 -0
- package/packages/pi-ai/dist/models/generated/opencode-go.js +124 -0
- package/packages/pi-ai/dist/models/generated/opencode-go.js.map +1 -0
- package/packages/pi-ai/dist/models/generated/opencode.d.ts +530 -0
- package/packages/pi-ai/dist/models/generated/opencode.d.ts.map +1 -0
- package/packages/pi-ai/dist/models/generated/opencode.js +532 -0
- package/packages/pi-ai/dist/models/generated/opencode.js.map +1 -0
- package/packages/pi-ai/dist/models/generated/openrouter.d.ts +4270 -0
- package/packages/pi-ai/dist/models/generated/openrouter.d.ts.map +1 -0
- package/packages/pi-ai/dist/models/generated/openrouter.js +4272 -0
- package/packages/pi-ai/dist/models/generated/openrouter.js.map +1 -0
- package/packages/pi-ai/dist/models/generated/vercel-ai-gateway.d.ts +2604 -0
- package/packages/pi-ai/dist/models/generated/vercel-ai-gateway.d.ts.map +1 -0
- package/packages/pi-ai/dist/models/generated/vercel-ai-gateway.js +2606 -0
- package/packages/pi-ai/dist/models/generated/vercel-ai-gateway.js.map +1 -0
- package/packages/pi-ai/dist/models/generated/xai.d.ts +411 -0
- package/packages/pi-ai/dist/models/generated/xai.d.ts.map +1 -0
- package/packages/pi-ai/dist/models/generated/xai.js +413 -0
- package/packages/pi-ai/dist/models/generated/xai.js.map +1 -0
- package/packages/pi-ai/dist/models/generated/zai.d.ts +276 -0
- package/packages/pi-ai/dist/models/generated/zai.d.ts.map +1 -0
- package/packages/pi-ai/dist/models/generated/zai.js +239 -0
- package/packages/pi-ai/dist/models/generated/zai.js.map +1 -0
- package/packages/pi-ai/dist/models/index.d.ts +27 -0
- package/packages/pi-ai/dist/models/index.d.ts.map +1 -0
- package/packages/pi-ai/dist/models/index.js +80 -0
- package/packages/pi-ai/dist/models/index.js.map +1 -0
- package/packages/pi-ai/dist/models.d.ts +1 -36
- package/packages/pi-ai/dist/models.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.test.js +1 -2
- package/packages/pi-ai/dist/models.generated.test.js.map +1 -1
- package/packages/pi-ai/dist/models.js +3 -112
- package/packages/pi-ai/dist/models.js.map +1 -1
- package/packages/pi-ai/dist/models.test.js +6 -5
- package/packages/pi-ai/dist/models.test.js.map +1 -1
- package/packages/pi-ai/scripts/generate-models.ts +74 -40
- package/packages/pi-ai/src/index.ts +1 -9
- package/packages/pi-ai/src/models/capability-patches.ts +40 -0
- package/packages/pi-ai/src/{models.custom.ts → models/custom.ts} +4 -4
- package/packages/pi-ai/src/models/generated/amazon-bedrock.ts +1486 -0
- package/packages/pi-ai/src/models/generated/anthropic.ts +381 -0
- package/packages/pi-ai/src/models/generated/azure-openai-responses.ts +704 -0
- package/packages/pi-ai/src/models/generated/cerebras.ts +75 -0
- package/packages/pi-ai/src/models/generated/github-copilot.ts +446 -0
- package/packages/pi-ai/src/models/generated/google-antigravity.ts +160 -0
- package/packages/pi-ai/src/models/generated/google-gemini-cli.ts +109 -0
- package/packages/pi-ai/src/models/generated/google-vertex.ts +211 -0
- package/packages/pi-ai/src/models/generated/google.ts +466 -0
- package/packages/pi-ai/src/models/generated/groq.ts +313 -0
- package/packages/pi-ai/src/models/generated/huggingface.ts +349 -0
- package/packages/pi-ai/src/models/generated/index.ts +52 -0
- package/packages/pi-ai/src/models/generated/kimi-coding.ts +41 -0
- package/packages/pi-ai/src/models/generated/minimax-cn.ts +109 -0
- package/packages/pi-ai/src/models/generated/minimax.ts +109 -0
- package/packages/pi-ai/src/models/generated/mistral.ts +449 -0
- package/packages/pi-ai/src/models/generated/openai-codex.ts +143 -0
- package/packages/pi-ai/src/models/generated/openai.ts +704 -0
- package/packages/pi-ai/src/models/generated/opencode-go.ts +126 -0
- package/packages/pi-ai/src/models/generated/opencode.ts +534 -0
- package/packages/pi-ai/src/models/generated/openrouter.ts +4274 -0
- package/packages/pi-ai/src/models/generated/vercel-ai-gateway.ts +2608 -0
- package/packages/pi-ai/src/models/generated/xai.ts +415 -0
- package/packages/pi-ai/src/models/generated/zai.ts +241 -0
- package/packages/pi-ai/src/models/index.ts +106 -0
- package/packages/pi-ai/src/models.generated.test.ts +1 -2
- package/packages/pi-ai/src/models.test.ts +6 -5
- package/packages/pi-ai/src/models.ts +3 -153
- package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +8 -2
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +472 -0
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry-env-fallback.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/model-registry-env-fallback.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry-env-fallback.test.js +52 -0
- package/packages/pi-coding-agent/dist/core/model-registry-env-fallback.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +2 -2
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +11 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +23 -9
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts +11 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js +47 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +8 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +68 -8
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js +22 -22
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +232 -18
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-ordering.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-ordering.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-ordering.test.js +38 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-ordering.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +14 -0
- 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 +70 -6
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +12 -6
- package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +612 -0
- package/packages/pi-coding-agent/src/core/model-registry-env-fallback.test.ts +59 -0
- package/packages/pi-coding-agent/src/core/model-registry.ts +2 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +19 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +25 -10
- package/packages/pi-coding-agent/src/modes/interactive/components/chat-frame.ts +67 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +83 -7
- package/packages/pi-coding-agent/src/modes/interactive/components/user-message.ts +23 -26
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +298 -41
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-ordering.test.ts +44 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +92 -6
- package/packages/pi-coding-agent/src/types/ambient-modules.d.ts +69 -0
- package/packages/pi-coding-agent/tsconfig.json +2 -2
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/src/resources/extensions/gsd/activity-log.ts +21 -0
- package/src/resources/extensions/gsd/auto/loop-deps.ts +4 -0
- package/src/resources/extensions/gsd/auto/loop.ts +159 -10
- package/src/resources/extensions/gsd/auto/phases.ts +123 -3
- package/src/resources/extensions/gsd/auto/session.ts +10 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +16 -6
- package/src/resources/extensions/gsd/auto-model-selection.ts +66 -5
- package/src/resources/extensions/gsd/auto-post-unit.ts +226 -9
- package/src/resources/extensions/gsd/auto-recovery.ts +29 -9
- package/src/resources/extensions/gsd/auto-unit-closeout.ts +25 -1
- package/src/resources/extensions/gsd/auto-verification.ts +129 -2
- package/src/resources/extensions/gsd/auto.ts +34 -2
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +15 -1
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +72 -8
- package/src/resources/extensions/gsd/cache.ts +16 -5
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +5 -0
- package/src/resources/extensions/gsd/commands-extract-learnings.ts +304 -0
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +1 -1
- package/src/resources/extensions/gsd/docs/preferences-reference.md +14 -1
- package/src/resources/extensions/gsd/ecosystem/gsd-extension-api.ts +228 -0
- package/src/resources/extensions/gsd/ecosystem/loader.ts +201 -0
- package/src/resources/extensions/gsd/git-service.ts +68 -0
- package/src/resources/extensions/gsd/graph-context.ts +139 -12
- package/src/resources/extensions/gsd/gsd-db.ts +321 -3
- package/src/resources/extensions/gsd/guided-flow.ts +33 -1
- package/src/resources/extensions/gsd/init-wizard.ts +3 -2
- package/src/resources/extensions/gsd/journal.ts +30 -0
- package/src/resources/extensions/gsd/metrics.ts +26 -0
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +40 -1
- package/src/resources/extensions/gsd/preferences-models.ts +20 -3
- package/src/resources/extensions/gsd/preferences-types.ts +32 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +107 -2
- package/src/resources/extensions/gsd/preferences.ts +28 -0
- package/src/resources/extensions/gsd/safety/evidence-collector.ts +15 -31
- package/src/resources/extensions/gsd/session-lock.ts +14 -2
- package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +20 -1
- package/src/resources/extensions/gsd/templates/PREFERENCES.md +18 -0
- package/src/resources/extensions/gsd/tests/artifacts-table-preserved-on-cache-invalidate.test.ts +177 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +7 -3
- package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +20 -0
- package/src/resources/extensions/gsd/tests/auto-project-root-env.test.ts +7 -3
- package/src/resources/extensions/gsd/tests/auto-retry-mcp-churn-fixes.test.ts +272 -0
- package/src/resources/extensions/gsd/tests/cold-resume-db-reopen.test.ts +6 -2
- package/src/resources/extensions/gsd/tests/commands-extract-learnings.test.ts +340 -0
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +10 -7
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/health-widget.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -2
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -3
- package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +79 -1
- package/src/resources/extensions/gsd/tests/post-unit-state-rebuild.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +40 -1
- package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/token-profile.test.ts +8 -5
- package/src/resources/extensions/gsd/tests/uok-audit-unified.test.ts +101 -0
- package/src/resources/extensions/gsd/tests/uok-contracts.test.ts +85 -0
- package/src/resources/extensions/gsd/tests/uok-execution-graph.test.ts +69 -0
- package/src/resources/extensions/gsd/tests/uok-flags.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/uok-gate-runner.test.ts +70 -0
- package/src/resources/extensions/gsd/tests/uok-gitops-turn-action.test.ts +85 -0
- package/src/resources/extensions/gsd/tests/uok-gitops-wiring.test.ts +35 -0
- package/src/resources/extensions/gsd/tests/uok-model-policy.test.ts +89 -0
- package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +167 -0
- package/src/resources/extensions/gsd/tests/uok-preferences.test.ts +42 -0
- package/src/resources/extensions/gsd/tests/validate-milestone-write-order.test.ts +39 -0
- package/src/resources/extensions/gsd/tools/complete-slice.ts +9 -2
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +48 -3
- package/src/resources/extensions/gsd/types.ts +14 -1
- package/src/resources/extensions/gsd/unit-ownership.ts +2 -2
- package/src/resources/extensions/gsd/uok/audit-toggle.ts +9 -0
- package/src/resources/extensions/gsd/uok/audit.ts +51 -0
- package/src/resources/extensions/gsd/uok/contracts.ts +135 -0
- package/src/resources/extensions/gsd/uok/execution-graph.ts +241 -0
- package/src/resources/extensions/gsd/uok/flags.ts +45 -0
- package/src/resources/extensions/gsd/uok/gate-runner.ts +146 -0
- package/src/resources/extensions/gsd/uok/gitops.ts +75 -0
- package/src/resources/extensions/gsd/uok/kernel.ts +105 -0
- package/src/resources/extensions/gsd/uok/loop-adapter.ts +162 -0
- package/src/resources/extensions/gsd/uok/model-policy.ts +112 -0
- package/src/resources/extensions/gsd/uok/plan-v2.ts +156 -0
- package/src/resources/extensions/gsd/workflow-logger.ts +27 -1
- package/src/resources/extensions/ttsr/ttsr-manager.ts +10 -5
- package/packages/pi-ai/dist/models.custom.d.ts.map +0 -1
- package/packages/pi-ai/dist/models.custom.js.map +0 -1
- package/packages/pi-ai/dist/models.generated.js +0 -14343
- package/packages/pi-ai/dist/models.generated.js.map +0 -1
- package/packages/pi-ai/src/models.generated.ts +0 -14345
- /package/dist/web/standalone/.next/static/{XnHY5eXUsTCFmNodWHetD → kn6xzWKYnogsxp2b6RpDD}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{XnHY5eXUsTCFmNodWHetD → kn6xzWKYnogsxp2b6RpDD}/_ssgManifest.js +0 -0
|
@@ -98,6 +98,49 @@ function makeProjectWithArtifacts(projectDir: string): void {
|
|
|
98
98
|
].join('\n'));
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
// ---------------------------------------------------------------------------
|
|
102
|
+
// LEARNINGS.md fixture helpers
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
|
|
105
|
+
function writeLearningsFixture(projectDir: string, milestoneId: string, content: string): void {
|
|
106
|
+
writeFixture(projectDir, `.gsd/milestones/${milestoneId}/${milestoneId}-LEARNINGS.md`, content);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const SAMPLE_LEARNINGS = `---
|
|
110
|
+
phase: "M001"
|
|
111
|
+
phase_name: "User Auth"
|
|
112
|
+
project: "my-project"
|
|
113
|
+
generated: "2026-04-15T10:00:00Z"
|
|
114
|
+
counts:
|
|
115
|
+
decisions: 2
|
|
116
|
+
lessons: 1
|
|
117
|
+
patterns: 1
|
|
118
|
+
surprises: 1
|
|
119
|
+
missing_artifacts: []
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
# Learnings: User Auth
|
|
123
|
+
|
|
124
|
+
## Decisions
|
|
125
|
+
- Use JWT for stateless auth across services.
|
|
126
|
+
Source: M001-PLAN.md/Architecture
|
|
127
|
+
|
|
128
|
+
- Store refresh tokens in HTTP-only cookies only.
|
|
129
|
+
Source: M001-PLAN.md/Security
|
|
130
|
+
|
|
131
|
+
## Lessons
|
|
132
|
+
- Integration tests need a real DB — mocks missed migration bugs.
|
|
133
|
+
Source: M001-SUMMARY.md/Testing
|
|
134
|
+
|
|
135
|
+
## Patterns
|
|
136
|
+
- Repository pattern abstracts DB access and simplifies testing.
|
|
137
|
+
Source: M001-PLAN.md/Design
|
|
138
|
+
|
|
139
|
+
## Surprises
|
|
140
|
+
- Token expiry edge case caused silent auth failures in prod.
|
|
141
|
+
Source: M001-SUMMARY.md/Issues
|
|
142
|
+
`;
|
|
143
|
+
|
|
101
144
|
// ---------------------------------------------------------------------------
|
|
102
145
|
// buildGraph tests
|
|
103
146
|
// ---------------------------------------------------------------------------
|
|
@@ -162,6 +205,141 @@ describe('buildGraph', () => {
|
|
|
162
205
|
});
|
|
163
206
|
});
|
|
164
207
|
|
|
208
|
+
// ---------------------------------------------------------------------------
|
|
209
|
+
// buildGraph — LEARNINGS.md parsing tests
|
|
210
|
+
// ---------------------------------------------------------------------------
|
|
211
|
+
|
|
212
|
+
describe('buildGraph — LEARNINGS.md parsing', () => {
|
|
213
|
+
let projectDir: string;
|
|
214
|
+
|
|
215
|
+
beforeEach(() => {
|
|
216
|
+
projectDir = tmpProject();
|
|
217
|
+
// Create minimal milestone directory so parseMilestoneFiles finds it
|
|
218
|
+
mkdirSync(join(projectDir, '.gsd', 'milestones', 'M001'), { recursive: true });
|
|
219
|
+
writeLearningsFixture(projectDir, 'M001', SAMPLE_LEARNINGS);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
afterEach(() => rmSync(projectDir, { recursive: true, force: true }));
|
|
223
|
+
|
|
224
|
+
it('extracts decision nodes from ## Decisions section', async () => {
|
|
225
|
+
const graph = await buildGraph(projectDir);
|
|
226
|
+
const decisions = graph.nodes.filter((n) => n.type === 'decision' || (n.type === 'rule' && n.id.startsWith('decision:')));
|
|
227
|
+
// Decisions should be extracted with a 'decision' type (or similar existing type)
|
|
228
|
+
const decisionNodes = graph.nodes.filter((n) => n.id.includes('decision:M001'));
|
|
229
|
+
assert.ok(decisionNodes.length >= 2, `Expected >= 2 decision nodes, got ${decisionNodes.length}`);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it('extracts lesson nodes from ## Lessons section', async () => {
|
|
233
|
+
const graph = await buildGraph(projectDir);
|
|
234
|
+
const lessonNodes = graph.nodes.filter((n) => n.id.includes('lesson:M001'));
|
|
235
|
+
assert.ok(lessonNodes.length >= 1, `Expected >= 1 lesson node, got ${lessonNodes.length}`);
|
|
236
|
+
assert.ok(lessonNodes.every((n) => n.type === 'lesson'), 'All lesson nodes must have type "lesson"');
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it('extracts pattern nodes from ## Patterns section', async () => {
|
|
240
|
+
const graph = await buildGraph(projectDir);
|
|
241
|
+
const patternNodes = graph.nodes.filter((n) => n.id.includes('pattern:M001'));
|
|
242
|
+
assert.ok(patternNodes.length >= 1, `Expected >= 1 pattern node, got ${patternNodes.length}`);
|
|
243
|
+
assert.ok(patternNodes.every((n) => n.type === 'pattern'), 'All pattern nodes must have type "pattern"');
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it('maps surprises to lesson nodes', async () => {
|
|
247
|
+
const graph = await buildGraph(projectDir);
|
|
248
|
+
// Surprises should be mapped to lesson type since no "surprise" NodeType exists
|
|
249
|
+
const surpriseNodes = graph.nodes.filter((n) => n.id.includes('surprise:M001'));
|
|
250
|
+
assert.ok(surpriseNodes.length >= 1, `Expected >= 1 surprise node, got ${surpriseNodes.length}`);
|
|
251
|
+
assert.ok(surpriseNodes.every((n) => n.type === 'lesson'), 'Surprises must be mapped to type "lesson"');
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it('node labels contain the learning text', async () => {
|
|
255
|
+
const graph = await buildGraph(projectDir);
|
|
256
|
+
const hasJwtDecision = graph.nodes.some((n) =>
|
|
257
|
+
n.label.toLowerCase().includes('jwt') || n.description?.toLowerCase().includes('jwt'),
|
|
258
|
+
);
|
|
259
|
+
assert.ok(hasJwtDecision, 'Expected a node describing the JWT decision');
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it('node description includes source attribution', async () => {
|
|
263
|
+
const graph = await buildGraph(projectDir);
|
|
264
|
+
const learningNodes = graph.nodes.filter((n) =>
|
|
265
|
+
n.id.includes(':M001:') || n.id.match(/:(decision|lesson|pattern|surprise):M001/),
|
|
266
|
+
);
|
|
267
|
+
const withSource = learningNodes.filter((n) => n.description?.includes('Source:') || n.description?.includes('M001-PLAN'));
|
|
268
|
+
assert.ok(withSource.length > 0, 'Expected at least one node with source attribution in description');
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it('adds relates_to edge from learning node to milestone node', async () => {
|
|
272
|
+
const graph = await buildGraph(projectDir);
|
|
273
|
+
const edgesToMilestone = graph.edges.filter(
|
|
274
|
+
(e) => e.to === 'milestone:M001' || e.from === 'milestone:M001',
|
|
275
|
+
);
|
|
276
|
+
// At least one learning node should relate to the milestone
|
|
277
|
+
const learningEdges = graph.edges.filter(
|
|
278
|
+
(e) => (e.from.includes('M001') && (e.type === 'relates_to' || e.type === 'contains')) ||
|
|
279
|
+
(e.to.includes('M001') && e.type === 'relates_to'),
|
|
280
|
+
);
|
|
281
|
+
assert.ok(learningEdges.length > 0 || edgesToMilestone.length > 0,
|
|
282
|
+
'Expected edges connecting learning nodes to milestone');
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it('skips LEARNINGS.md gracefully when file is malformed', async () => {
|
|
286
|
+
const badProject = tmpProject();
|
|
287
|
+
mkdirSync(join(badProject, '.gsd', 'milestones', 'M002'), { recursive: true });
|
|
288
|
+
writeLearningsFixture(badProject, 'M002', '\0\0\0 not valid yaml or markdown \0\0\0');
|
|
289
|
+
// Must not throw
|
|
290
|
+
const graph = await buildGraph(badProject);
|
|
291
|
+
assert.ok(graph.nodes.length >= 0);
|
|
292
|
+
assert.equal(typeof graph.builtAt, 'string');
|
|
293
|
+
rmSync(badProject, { recursive: true, force: true });
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
it('produces no learning nodes when all sections are empty', async () => {
|
|
297
|
+
const emptyProject = tmpProject();
|
|
298
|
+
mkdirSync(join(emptyProject, '.gsd', 'milestones', 'M003'), { recursive: true });
|
|
299
|
+
writeLearningsFixture(emptyProject, 'M003', `---
|
|
300
|
+
phase: "M003"
|
|
301
|
+
phase_name: "Empty"
|
|
302
|
+
project: "test"
|
|
303
|
+
generated: "2026-04-15T10:00:00Z"
|
|
304
|
+
counts:
|
|
305
|
+
decisions: 0
|
|
306
|
+
lessons: 0
|
|
307
|
+
patterns: 0
|
|
308
|
+
surprises: 0
|
|
309
|
+
missing_artifacts: []
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
# Learnings: Empty
|
|
313
|
+
|
|
314
|
+
## Decisions
|
|
315
|
+
|
|
316
|
+
## Lessons
|
|
317
|
+
|
|
318
|
+
## Patterns
|
|
319
|
+
|
|
320
|
+
## Surprises
|
|
321
|
+
`);
|
|
322
|
+
const graph = await buildGraph(emptyProject);
|
|
323
|
+
const learningNodes = graph.nodes.filter((n) =>
|
|
324
|
+
n.id.includes('decision:M003') ||
|
|
325
|
+
n.id.includes('lesson:M003') ||
|
|
326
|
+
n.id.includes('pattern:M003') ||
|
|
327
|
+
n.id.includes('surprise:M003'),
|
|
328
|
+
);
|
|
329
|
+
assert.equal(learningNodes.length, 0, 'Empty sections should produce no nodes');
|
|
330
|
+
rmSync(emptyProject, { recursive: true, force: true });
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
it('does not crash when LEARNINGS.md is missing entirely', async () => {
|
|
334
|
+
const noLearningsProject = tmpProject();
|
|
335
|
+
mkdirSync(join(noLearningsProject, '.gsd', 'milestones', 'M004'), { recursive: true });
|
|
336
|
+
// No LEARNINGS.md file written
|
|
337
|
+
const graph = await buildGraph(noLearningsProject);
|
|
338
|
+
assert.ok(graph.nodes.length >= 0);
|
|
339
|
+
rmSync(noLearningsProject, { recursive: true, force: true });
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
|
|
165
343
|
// ---------------------------------------------------------------------------
|
|
166
344
|
// writeGraph tests
|
|
167
345
|
// ---------------------------------------------------------------------------
|
|
@@ -27,7 +27,8 @@ export type NodeType =
|
|
|
27
27
|
| 'rule'
|
|
28
28
|
| 'pattern'
|
|
29
29
|
| 'lesson'
|
|
30
|
-
| 'concept'
|
|
30
|
+
| 'concept'
|
|
31
|
+
| 'decision';
|
|
31
32
|
|
|
32
33
|
export type EdgeType =
|
|
33
34
|
| 'contains'
|
|
@@ -386,6 +387,151 @@ function parseTasksFromPlan(
|
|
|
386
387
|
}
|
|
387
388
|
}
|
|
388
389
|
|
|
390
|
+
// ---------------------------------------------------------------------------
|
|
391
|
+
// LEARNINGS.md parser
|
|
392
|
+
// ---------------------------------------------------------------------------
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Parse all *-LEARNINGS.md files found in milestone directories.
|
|
396
|
+
* Extracts Decisions, Lessons, Patterns, and Surprises as typed graph nodes.
|
|
397
|
+
* Surprises are mapped to the 'lesson' NodeType (no distinct type exists).
|
|
398
|
+
* Parse errors per file are caught — the file is skipped, never rethrows.
|
|
399
|
+
*/
|
|
400
|
+
function parseLearningsFiles(gsdRoot: string, nodes: GraphNode[], edges: GraphEdge[]): void {
|
|
401
|
+
const milestoneIds = findMilestoneIds(gsdRoot);
|
|
402
|
+
|
|
403
|
+
for (const milestoneId of milestoneIds) {
|
|
404
|
+
try {
|
|
405
|
+
parseSingleLearningsFile(gsdRoot, milestoneId, nodes, edges);
|
|
406
|
+
} catch {
|
|
407
|
+
// Skip this milestone's LEARNINGS.md on any error
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function parseSingleLearningsFile(
|
|
413
|
+
gsdRoot: string,
|
|
414
|
+
milestoneId: string,
|
|
415
|
+
nodes: GraphNode[],
|
|
416
|
+
edges: GraphEdge[],
|
|
417
|
+
): void {
|
|
418
|
+
const mDir = resolveMilestoneDir(gsdRoot, milestoneId);
|
|
419
|
+
if (!mDir) return;
|
|
420
|
+
|
|
421
|
+
const learningsPath = join(mDir, `${milestoneId}-LEARNINGS.md`);
|
|
422
|
+
if (!existsSync(learningsPath)) return;
|
|
423
|
+
|
|
424
|
+
let content: string;
|
|
425
|
+
try {
|
|
426
|
+
content = readFileSync(learningsPath, 'utf-8');
|
|
427
|
+
} catch {
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// Strip YAML frontmatter if present
|
|
432
|
+
const withoutFrontmatter = content.replace(/^---[\s\S]*?---\n?/, '');
|
|
433
|
+
|
|
434
|
+
const milestoneNodeId = `milestone:${milestoneId}`;
|
|
435
|
+
const sourceFile = `milestones/${milestoneId}/${milestoneId}-LEARNINGS.md`;
|
|
436
|
+
|
|
437
|
+
// Parse each section: [sectionName, nodeType, idPrefix]
|
|
438
|
+
const sections: Array<[string, NodeType, string]> = [
|
|
439
|
+
['Decisions', 'decision', 'decision'],
|
|
440
|
+
['Lessons', 'lesson', 'lesson'],
|
|
441
|
+
['Patterns', 'pattern', 'pattern'],
|
|
442
|
+
['Surprises', 'lesson', 'surprise'],
|
|
443
|
+
];
|
|
444
|
+
|
|
445
|
+
for (const [sectionName, nodeType, idPrefix] of sections) {
|
|
446
|
+
const sectionMatch = withoutFrontmatter.match(
|
|
447
|
+
new RegExp(`##\\s+${sectionName}\\s*\\n([\\s\\S]*?)(?=\\n##\\s|$)`, 'i'),
|
|
448
|
+
);
|
|
449
|
+
if (!sectionMatch) continue;
|
|
450
|
+
|
|
451
|
+
const sectionContent = sectionMatch[1];
|
|
452
|
+
parseLearningsSection(
|
|
453
|
+
sectionContent,
|
|
454
|
+
milestoneId,
|
|
455
|
+
idPrefix,
|
|
456
|
+
nodeType,
|
|
457
|
+
milestoneNodeId,
|
|
458
|
+
sourceFile,
|
|
459
|
+
nodes,
|
|
460
|
+
edges,
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
function parseLearningsSection(
|
|
466
|
+
sectionContent: string,
|
|
467
|
+
milestoneId: string,
|
|
468
|
+
idPrefix: string,
|
|
469
|
+
nodeType: NodeType,
|
|
470
|
+
milestoneNodeId: string,
|
|
471
|
+
sourceFile: string,
|
|
472
|
+
nodes: GraphNode[],
|
|
473
|
+
edges: GraphEdge[],
|
|
474
|
+
): void {
|
|
475
|
+
// Each item is a bullet line starting with "- " followed by optional
|
|
476
|
+
// indented "Source: ..." line.
|
|
477
|
+
// We collect bullet items and their associated source attribution.
|
|
478
|
+
const lines = sectionContent.split('\n');
|
|
479
|
+
let itemIndex = 0;
|
|
480
|
+
let currentText: string | null = null;
|
|
481
|
+
let currentSource: string | null = null;
|
|
482
|
+
|
|
483
|
+
const flushItem = (): void => {
|
|
484
|
+
if (!currentText) return;
|
|
485
|
+
itemIndex += 1;
|
|
486
|
+
const nodeId = `${idPrefix}:${milestoneId}:${itemIndex}`;
|
|
487
|
+
const description = currentSource ? `${currentSource}` : undefined;
|
|
488
|
+
|
|
489
|
+
nodes.push({
|
|
490
|
+
id: nodeId,
|
|
491
|
+
label: currentText,
|
|
492
|
+
type: nodeType,
|
|
493
|
+
description,
|
|
494
|
+
confidence: 'EXTRACTED',
|
|
495
|
+
sourceFile,
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
// Edge: milestone relates_to this learning node
|
|
499
|
+
edges.push({
|
|
500
|
+
from: milestoneNodeId,
|
|
501
|
+
to: nodeId,
|
|
502
|
+
type: 'relates_to',
|
|
503
|
+
confidence: 'EXTRACTED',
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
currentText = null;
|
|
507
|
+
currentSource = null;
|
|
508
|
+
};
|
|
509
|
+
|
|
510
|
+
for (const line of lines) {
|
|
511
|
+
const bulletMatch = line.match(/^[-*]\s+(.+)/);
|
|
512
|
+
if (bulletMatch) {
|
|
513
|
+
flushItem();
|
|
514
|
+
currentText = bulletMatch[1].trim();
|
|
515
|
+
continue;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// Indented source attribution: " Source: ..."
|
|
519
|
+
const sourceMatch = line.match(/^\s+Source:\s+(.+)/i);
|
|
520
|
+
if (sourceMatch && currentText !== null) {
|
|
521
|
+
currentSource = `Source: ${sourceMatch[1].trim()}`;
|
|
522
|
+
continue;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// Continuation of current item text (indented non-source line)
|
|
526
|
+
const continuationMatch = line.match(/^\s{2,}(.+)/);
|
|
527
|
+
if (continuationMatch && currentText !== null && currentSource === null) {
|
|
528
|
+
currentText += ' ' + continuationMatch[1].trim();
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
flushItem();
|
|
533
|
+
}
|
|
534
|
+
|
|
389
535
|
// ---------------------------------------------------------------------------
|
|
390
536
|
// buildGraph
|
|
391
537
|
// ---------------------------------------------------------------------------
|
|
@@ -407,6 +553,7 @@ export async function buildGraph(projectDir: string): Promise<KnowledgeGraph> {
|
|
|
407
553
|
parseStateFile,
|
|
408
554
|
parseKnowledgeFile,
|
|
409
555
|
parseMilestoneFiles,
|
|
556
|
+
parseLearningsFiles,
|
|
410
557
|
];
|
|
411
558
|
|
|
412
559
|
for (const parser of parsers) {
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* Workflow MCP tools — exposes the core GSD mutation/read handlers over MCP.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { existsSync, realpathSync } from "node:fs";
|
|
6
|
+
import { isAbsolute, join, relative, resolve } from "node:path";
|
|
6
7
|
import { pathToFileURL } from "node:url";
|
|
7
8
|
import { z } from "zod";
|
|
8
9
|
|
|
@@ -262,6 +263,22 @@ function isWithinRoot(candidatePath: string, rootPath: string): boolean {
|
|
|
262
263
|
return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel));
|
|
263
264
|
}
|
|
264
265
|
|
|
266
|
+
/**
|
|
267
|
+
* Resolve the symlink target of `<allowedRoot>/.gsd` when it points into the
|
|
268
|
+
* external state layout (`~/.gsd/projects/<hash>/`). Returns the realpath of
|
|
269
|
+
* that target so callers can accept worktree paths that live under
|
|
270
|
+
* `<external-state>/worktrees/<MID>/`. Returns null when `.gsd` is absent or
|
|
271
|
+
* resolution fails — the caller should fall back to the direct containment
|
|
272
|
+
* check in that case.
|
|
273
|
+
*/
|
|
274
|
+
function resolveExternalStateRoot(allowedRoot: string): string | null {
|
|
275
|
+
try {
|
|
276
|
+
return realpathSync(join(allowedRoot, ".gsd"));
|
|
277
|
+
} catch {
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
265
282
|
function validateProjectDir(projectDir: string, env: NodeJS.ProcessEnv = process.env): string {
|
|
266
283
|
if (!isAbsolute(projectDir)) {
|
|
267
284
|
throw new Error(`projectDir must be an absolute path. Received: ${projectDir}`);
|
|
@@ -269,27 +286,88 @@ function validateProjectDir(projectDir: string, env: NodeJS.ProcessEnv = process
|
|
|
269
286
|
|
|
270
287
|
const resolvedProjectDir = resolve(projectDir);
|
|
271
288
|
const allowedRoot = getAllowedProjectRoot(env);
|
|
272
|
-
if (allowedRoot
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
289
|
+
if (!allowedRoot) return resolvedProjectDir;
|
|
290
|
+
|
|
291
|
+
if (isWithinRoot(resolvedProjectDir, allowedRoot)) return resolvedProjectDir;
|
|
292
|
+
|
|
293
|
+
// External state layout: `<allowedRoot>/.gsd` may be a symlink into
|
|
294
|
+
// `~/.gsd/projects/<hash>/`, and auto-worktrees live under
|
|
295
|
+
// `~/.gsd/projects/<hash>/worktrees/<MID>/`. Accept candidates that are
|
|
296
|
+
// under the realpath of `<allowedRoot>/.gsd` — they belong to this project
|
|
297
|
+
// even though their absolute path is outside allowedRoot (#issue-a44).
|
|
298
|
+
const externalRoot = resolveExternalStateRoot(allowedRoot);
|
|
299
|
+
if (externalRoot && isWithinRoot(resolvedProjectDir, externalRoot)) {
|
|
300
|
+
return resolvedProjectDir;
|
|
276
301
|
}
|
|
277
302
|
|
|
278
|
-
|
|
303
|
+
throw new Error(
|
|
304
|
+
`projectDir must stay within the configured workflow project root. Received: ${resolvedProjectDir}; allowed root: ${allowedRoot}`,
|
|
305
|
+
);
|
|
279
306
|
}
|
|
280
307
|
|
|
281
308
|
function parseToolArgs<T>(schema: z.ZodType<T>, args: Record<string, unknown>): T {
|
|
282
309
|
return schema.parse(args);
|
|
283
310
|
}
|
|
284
311
|
|
|
285
|
-
|
|
312
|
+
/**
|
|
313
|
+
* Extract a milestone ID from parsed tool args, trying common field names.
|
|
314
|
+
* Returns null when no field is present or the value is not a string.
|
|
315
|
+
*/
|
|
316
|
+
function extractMilestoneId(parsed: Record<string, unknown>): string | null {
|
|
317
|
+
const candidates = [parsed.milestoneId, parsed.milestone_id, parsed.mid];
|
|
318
|
+
for (const c of candidates) {
|
|
319
|
+
if (typeof c === "string" && c.trim() !== "") return c.trim();
|
|
320
|
+
}
|
|
321
|
+
return null;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* If an auto-worktree exists for the given milestone under
|
|
326
|
+
* `<projectRoot>/.gsd/worktrees/<milestoneId>/`, return that path as the
|
|
327
|
+
* basePath the tool should write against. Returns null when no worktree
|
|
328
|
+
* exists for this milestone, leaving the caller to use the project root.
|
|
329
|
+
*
|
|
330
|
+
* This unbreaks the external-state layout where the MCP server's process.cwd()
|
|
331
|
+
* is the project root (set at Claude Code launch) but auto-mode is actually
|
|
332
|
+
* working inside a per-milestone worktree. Without this, tool writes go to
|
|
333
|
+
* the shared project `.gsd/` and auto-mode's verifyExpectedArtifact (which
|
|
334
|
+
* uses the worktree `.gsd/`) fails, triggering a guaranteed retry per unit.
|
|
335
|
+
*/
|
|
336
|
+
function resolveActiveWorktreeBasePath(
|
|
337
|
+
projectRoot: string,
|
|
338
|
+
milestoneId: string | null,
|
|
339
|
+
): string | null {
|
|
340
|
+
if (!milestoneId) return null;
|
|
341
|
+
const wtPath = join(projectRoot, ".gsd", "worktrees", milestoneId);
|
|
342
|
+
if (!existsSync(wtPath)) return null;
|
|
343
|
+
// Sanity check: a real git worktree has a `.git` file with a gitdir pointer.
|
|
344
|
+
// Bare directories without it shouldn't hijack the write path.
|
|
345
|
+
if (!existsSync(join(wtPath, ".git"))) return null;
|
|
346
|
+
return wtPath;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function parseWorkflowArgs<T extends { projectDir?: string }>(
|
|
286
350
|
schema: z.ZodType<T>,
|
|
287
351
|
args: Record<string, unknown>,
|
|
288
|
-
): T {
|
|
352
|
+
): T & { projectDir: string } {
|
|
289
353
|
const parsed = parseToolArgs(schema, args);
|
|
354
|
+
// Step 1: figure out the project root. The agent shouldn't need to pass
|
|
355
|
+
// projectDir — default to process.cwd() which the MCP server inherited from
|
|
356
|
+
// Claude Code (launched at the project root).
|
|
357
|
+
const projectRootCandidate = parsed.projectDir ?? process.cwd();
|
|
358
|
+
const projectRoot = validateProjectDir(projectRootCandidate);
|
|
359
|
+
|
|
360
|
+
// Step 2: if this tool call is scoped to a milestone that has an active
|
|
361
|
+
// auto-worktree, re-route writes to the worktree's .gsd rather than the
|
|
362
|
+
// project's shared .gsd. auto-mode's verifyExpectedArtifact runs against
|
|
363
|
+
// the worktree, and a mismatch here causes every unit to retry once.
|
|
364
|
+
const milestoneId = extractMilestoneId(parsed as Record<string, unknown>);
|
|
365
|
+
const worktreeBasePath = resolveActiveWorktreeBasePath(projectRoot, milestoneId);
|
|
366
|
+
const effectiveBasePath = worktreeBasePath ?? projectRoot;
|
|
367
|
+
|
|
290
368
|
return {
|
|
291
369
|
...parsed,
|
|
292
|
-
projectDir:
|
|
370
|
+
projectDir: effectiveBasePath,
|
|
293
371
|
};
|
|
294
372
|
}
|
|
295
373
|
|
|
@@ -664,7 +742,14 @@ async function ensureMilestoneDbRow(milestoneId: string): Promise<void> {
|
|
|
664
742
|
}
|
|
665
743
|
}
|
|
666
744
|
|
|
667
|
-
|
|
745
|
+
// projectDir is optional. When omitted, the server uses process.cwd(). This
|
|
746
|
+
// prevents the agent from burning tokens reasoning about which absolute path
|
|
747
|
+
// to pass (git root vs worktree vs symlink-resolved external state layout) —
|
|
748
|
+
// the server already knows where it is running.
|
|
749
|
+
const projectDirParam = z
|
|
750
|
+
.string()
|
|
751
|
+
.optional()
|
|
752
|
+
.describe("Optional. Omit this field — the server defaults to its current working directory, which is already the correct project or worktree root.");
|
|
668
753
|
|
|
669
754
|
const planMilestoneParams = {
|
|
670
755
|
projectDir: projectDirParam,
|