gsd-pi 2.74.0-dev.6e23363 → 2.74.0-dev.703eabc
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/auto-post-unit.js +7 -3
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +10 -1
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +45 -4
- 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/ecosystem/gsd-extension-api.js +144 -0
- package/dist/resources/extensions/gsd/ecosystem/loader.js +145 -0
- 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 +16 -16
- 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 +16 -16
- 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/src/readers/graph.test.ts +178 -0
- package/packages/mcp-server/src/readers/graph.ts +148 -1
- 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 +359 -7
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.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.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +51 -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 +192 -22
- 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 +13 -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 +53 -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 +453 -7
- 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 +66 -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 +253 -45
- 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 +73 -6
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/src/resources/extensions/gsd/auto-post-unit.ts +7 -3
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +15 -1
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +56 -3
- 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/ecosystem/gsd-extension-api.ts +228 -0
- package/src/resources/extensions/gsd/ecosystem/loader.ts +201 -0
- package/src/resources/extensions/gsd/tests/commands-extract-learnings.test.ts +340 -0
- package/src/resources/extensions/gsd/tests/health-widget.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +1 -1
- package/src/resources/extensions/gsd/types.ts +13 -0
- package/src/resources/extensions/gsd/workflow-logger.ts +2 -1
- 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/{bc2gRVFTgD7j--BsJE7vP → 3U-oZ5FT59BM7sm2GInic}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{bc2gRVFTgD7j--BsJE7vP → 3U-oZ5FT59BM7sm2GInic}/_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) {
|