gsd-pi 2.74.0-dev.2b524c3 → 2.74.0-dev.658744a
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/cli.js +85 -0
- package/dist/headless-query.js +4 -1
- package/dist/help-text.js +23 -0
- package/dist/resources/extensions/gsd/activity-log.js +16 -0
- package/dist/resources/extensions/gsd/auto/detect-stuck.js +11 -4
- package/dist/resources/extensions/gsd/auto/loop.js +147 -10
- package/dist/resources/extensions/gsd/auto/phases.js +209 -10
- 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 +54 -8
- package/dist/resources/extensions/gsd/auto-post-unit.js +220 -17
- package/dist/resources/extensions/gsd/auto-prompts.js +12 -0
- 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-worktree.js +2 -0
- package/dist/resources/extensions/gsd/auto.js +36 -4
- package/dist/resources/extensions/gsd/bootstrap/provider-error-resume.js +5 -3
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +30 -8
- 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/catalog.js +31 -1
- package/dist/resources/extensions/gsd/commands/handlers/core.js +5 -1
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +25 -0
- package/dist/resources/extensions/gsd/commands/handlers/workflow.js +68 -9
- package/dist/resources/extensions/gsd/commands-add-tests.js +111 -0
- package/dist/resources/extensions/gsd/commands-backlog.js +140 -0
- package/dist/resources/extensions/gsd/commands-do.js +79 -0
- package/dist/resources/extensions/gsd/commands-extract-learnings.js +225 -0
- package/dist/resources/extensions/gsd/commands-maintenance.js +6 -6
- package/dist/resources/extensions/gsd/commands-pr-branch.js +180 -0
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +51 -4
- package/dist/resources/extensions/gsd/commands-session-report.js +82 -0
- package/dist/resources/extensions/gsd/commands-ship.js +187 -0
- package/dist/resources/extensions/gsd/db-writer.js +3 -5
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +16 -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 +157 -0
- package/dist/resources/extensions/gsd/gsd-db.js +581 -2
- package/dist/resources/extensions/gsd/guided-flow.js +31 -6
- package/dist/resources/extensions/gsd/index.js +15 -2
- package/dist/resources/extensions/gsd/init-wizard.js +1 -0
- package/dist/resources/extensions/gsd/journal.js +27 -0
- package/dist/resources/extensions/gsd/md-importer.js +3 -4
- package/dist/resources/extensions/gsd/memory-store.js +19 -51
- package/dist/resources/extensions/gsd/metrics.js +19 -0
- package/dist/resources/extensions/gsd/milestone-validation-gates.js +13 -12
- package/dist/resources/extensions/gsd/native-git-bridge.js +7 -4
- 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 +2 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +118 -2
- package/dist/resources/extensions/gsd/preferences.js +31 -0
- package/dist/resources/extensions/gsd/prompts/add-tests.md +35 -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/state.js +5 -1
- package/dist/resources/extensions/gsd/templates/PREFERENCES.md +19 -0
- package/dist/resources/extensions/gsd/tools/complete-slice.js +20 -0
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +39 -4
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +3 -14
- package/dist/resources/extensions/gsd/triage-resolution.js +2 -5
- 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/gsd/workflow-manifest.js +8 -69
- package/dist/resources/extensions/gsd/workflow-migration.js +21 -22
- package/dist/resources/extensions/gsd/workflow-projections.js +4 -1
- package/dist/resources/extensions/gsd/workflow-reconcile.js +14 -11
- package/dist/resources/extensions/ttsr/ttsr-manager.js +3 -1
- package/dist/tsconfig.extensions.tsbuildinfo +1 -0
- 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 +3 -2
- package/packages/daemon/package.json +2 -2
- package/packages/mcp-server/dist/index.d.ts +3 -0
- package/packages/mcp-server/dist/index.d.ts.map +1 -1
- package/packages/mcp-server/dist/index.js +3 -0
- package/packages/mcp-server/dist/index.js.map +1 -1
- package/packages/mcp-server/dist/readers/graph.d.ts +87 -0
- package/packages/mcp-server/dist/readers/graph.d.ts.map +1 -0
- package/packages/mcp-server/dist/readers/graph.js +655 -0
- package/packages/mcp-server/dist/readers/graph.js.map +1 -0
- package/packages/mcp-server/dist/readers/index.d.ts +2 -0
- package/packages/mcp-server/dist/readers/index.d.ts.map +1 -1
- package/packages/mcp-server/dist/readers/index.js +1 -0
- package/packages/mcp-server/dist/readers/index.js.map +1 -1
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +65 -0
- package/packages/mcp-server/dist/server.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/package.json +2 -2
- package/packages/mcp-server/src/index.ts +15 -0
- package/packages/mcp-server/src/readers/graph.test.ts +604 -0
- package/packages/mcp-server/src/readers/graph.ts +855 -0
- package/packages/mcp-server/src/readers/index.ts +12 -0
- package/packages/mcp-server/src/server.ts +83 -0
- package/packages/mcp-server/src/workflow-tools.ts +95 -10
- package/packages/mcp-server/tsconfig.json +1 -0
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -0
- package/packages/native/package.json +2 -2
- package/packages/native/tsconfig.tsbuildinfo +1 -0
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-agent-core/tsconfig.json +1 -0
- package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -0
- 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/package.json +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.json +1 -0
- package/packages/pi-ai/tsconfig.tsbuildinfo +1 -0
- 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 +3 -2
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -0
- package/packages/pi-tui/package.json +1 -1
- package/packages/pi-tui/tsconfig.json +1 -0
- package/packages/pi-tui/tsconfig.tsbuildinfo +1 -0
- package/packages/rpc-client/package.json +1 -1
- package/packages/rpc-client/tsconfig.json +1 -0
- package/packages/rpc-client/tsconfig.tsbuildinfo +1 -0
- package/src/resources/extensions/gsd/activity-log.ts +21 -0
- package/src/resources/extensions/gsd/auto/detect-stuck.ts +12 -4
- package/src/resources/extensions/gsd/auto/loop-deps.ts +10 -0
- package/src/resources/extensions/gsd/auto/loop.ts +159 -10
- package/src/resources/extensions/gsd/auto/phases.ts +261 -10
- 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 +69 -8
- package/src/resources/extensions/gsd/auto-post-unit.ts +238 -18
- package/src/resources/extensions/gsd/auto-prompts.ts +13 -0
- 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-worktree.ts +1 -0
- package/src/resources/extensions/gsd/auto.ts +41 -2
- package/src/resources/extensions/gsd/bootstrap/provider-error-resume.ts +5 -3
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +38 -8
- 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/catalog.ts +31 -1
- package/src/resources/extensions/gsd/commands/handlers/core.ts +5 -1
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +25 -0
- package/src/resources/extensions/gsd/commands/handlers/workflow.ts +74 -9
- package/src/resources/extensions/gsd/commands-add-tests.ts +137 -0
- package/src/resources/extensions/gsd/commands-backlog.ts +182 -0
- package/src/resources/extensions/gsd/commands-do.ts +109 -0
- package/src/resources/extensions/gsd/commands-extract-learnings.ts +304 -0
- package/src/resources/extensions/gsd/commands-maintenance.ts +6 -6
- package/src/resources/extensions/gsd/commands-pr-branch.ts +234 -0
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +58 -4
- package/src/resources/extensions/gsd/commands-session-report.ts +101 -0
- package/src/resources/extensions/gsd/commands-ship.ts +219 -0
- package/src/resources/extensions/gsd/db-writer.ts +3 -5
- package/src/resources/extensions/gsd/docs/preferences-reference.md +16 -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 +212 -0
- package/src/resources/extensions/gsd/gsd-db.ts +788 -3
- package/src/resources/extensions/gsd/guided-flow.ts +36 -2
- package/src/resources/extensions/gsd/index.ts +18 -2
- package/src/resources/extensions/gsd/init-wizard.ts +3 -2
- package/src/resources/extensions/gsd/journal.ts +30 -0
- package/src/resources/extensions/gsd/md-importer.ts +3 -5
- package/src/resources/extensions/gsd/memory-store.ts +31 -62
- package/src/resources/extensions/gsd/metrics.ts +26 -0
- package/src/resources/extensions/gsd/milestone-validation-gates.ts +13 -14
- package/src/resources/extensions/gsd/native-git-bridge.ts +11 -12
- 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 +38 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +117 -2
- package/src/resources/extensions/gsd/preferences.ts +34 -0
- package/src/resources/extensions/gsd/prompts/add-tests.md +35 -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/state.ts +9 -2
- package/src/resources/extensions/gsd/templates/PREFERENCES.md +19 -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/auto-warning-noise-regression.test.ts +117 -0
- package/src/resources/extensions/gsd/tests/cold-resume-db-reopen.test.ts +6 -2
- package/src/resources/extensions/gsd/tests/commands-backlog.test.ts +158 -0
- package/src/resources/extensions/gsd/tests/commands-do.test.ts +127 -0
- package/src/resources/extensions/gsd/tests/commands-extract-learnings.test.ts +340 -0
- package/src/resources/extensions/gsd/tests/commands-pr-branch.test.ts +68 -0
- package/src/resources/extensions/gsd/tests/commands-session-report.test.ts +82 -0
- package/src/resources/extensions/gsd/tests/commands-ship.test.ts +71 -0
- package/src/resources/extensions/gsd/tests/commands-workflow-custom.test.ts +14 -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/extension-bootstrap-isolation.test.ts +154 -0
- package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +10 -7
- package/src/resources/extensions/gsd/tests/graph-context.test.ts +337 -0
- 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/journal-integration.test.ts +71 -4
- 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/native-git-bridge-exec-fallback.test.ts +140 -0
- 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/preferences.test.ts +145 -0
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +57 -2
- package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/single-writer-invariant.test.ts +180 -0
- 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/tests/workflow-logger-wiring.test.ts +223 -0
- package/src/resources/extensions/gsd/tools/complete-slice.ts +26 -0
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +48 -3
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +3 -11
- package/src/resources/extensions/gsd/triage-resolution.ts +2 -7
- 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/gsd/workflow-manifest.ts +9 -104
- package/src/resources/extensions/gsd/workflow-migration.ts +21 -29
- package/src/resources/extensions/gsd/workflow-projections.ts +8 -1
- package/src/resources/extensions/gsd/workflow-reconcile.ts +15 -15
- 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/{YzIEI9sxJy4t5xgClF08g → Es_JWCfFZjIvYZShmjyye}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{YzIEI9sxJy4t5xgClF08g → Es_JWCfFZjIvYZShmjyye}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,604 @@
|
|
|
1
|
+
// GSD MCP Server — knowledge graph reader tests
|
|
2
|
+
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
|
3
|
+
|
|
4
|
+
import { describe, it, before, after, beforeEach, afterEach } from 'node:test';
|
|
5
|
+
import assert from 'node:assert/strict';
|
|
6
|
+
import { mkdirSync, writeFileSync, rmSync, existsSync, readFileSync } from 'node:fs';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
import { tmpdir } from 'node:os';
|
|
9
|
+
import { randomBytes } from 'node:crypto';
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
buildGraph,
|
|
13
|
+
writeGraph,
|
|
14
|
+
writeSnapshot,
|
|
15
|
+
graphStatus,
|
|
16
|
+
graphQuery,
|
|
17
|
+
graphDiff,
|
|
18
|
+
} from './graph.js';
|
|
19
|
+
import type { KnowledgeGraph } from './graph.js';
|
|
20
|
+
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// Fixture helpers
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
function tmpProject(): string {
|
|
26
|
+
const dir = join(tmpdir(), `gsd-graph-test-${randomBytes(4).toString('hex')}`);
|
|
27
|
+
mkdirSync(dir, { recursive: true });
|
|
28
|
+
return dir;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function writeFixture(base: string, relPath: string, content: string): void {
|
|
32
|
+
const full = join(base, relPath);
|
|
33
|
+
mkdirSync(join(full, '..'), { recursive: true });
|
|
34
|
+
writeFileSync(full, content, 'utf-8');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function makeProjectWithArtifacts(projectDir: string): void {
|
|
38
|
+
writeFixture(projectDir, '.gsd/STATE.md', [
|
|
39
|
+
'# GSD State',
|
|
40
|
+
'',
|
|
41
|
+
'**Active Milestone:** M001: Auth System',
|
|
42
|
+
'**Active Slice:** S01: Login flow',
|
|
43
|
+
'**Phase:** execution',
|
|
44
|
+
'',
|
|
45
|
+
'## Milestone Registry',
|
|
46
|
+
'',
|
|
47
|
+
'- 🔄 **M001:** Auth System',
|
|
48
|
+
'',
|
|
49
|
+
'## Next Action',
|
|
50
|
+
'',
|
|
51
|
+
'Execute T01 in S01.',
|
|
52
|
+
].join('\n'));
|
|
53
|
+
|
|
54
|
+
writeFixture(projectDir, '.gsd/KNOWLEDGE.md', [
|
|
55
|
+
'# Project Knowledge',
|
|
56
|
+
'',
|
|
57
|
+
'## Rules',
|
|
58
|
+
'',
|
|
59
|
+
'| # | Scope | Rule | Why | Added |',
|
|
60
|
+
'|---|-------|------|-----|-------|',
|
|
61
|
+
'| K001 | auth | Hash passwords with bcrypt | Security requirement | manual |',
|
|
62
|
+
'| K002 | db | Use transactions for multi-table | Data consistency | auto |',
|
|
63
|
+
'',
|
|
64
|
+
'## Patterns',
|
|
65
|
+
'',
|
|
66
|
+
'| # | Pattern | Where | Notes |',
|
|
67
|
+
'|---|---------|-------|-------|',
|
|
68
|
+
'| P001 | Singleton services | services/ | Prevents duplication |',
|
|
69
|
+
'',
|
|
70
|
+
'## Lessons Learned',
|
|
71
|
+
'',
|
|
72
|
+
'| # | What Happened | Root Cause | Fix | Scope |',
|
|
73
|
+
'|---|--------------|------------|-----|-------|',
|
|
74
|
+
'| L001 | CI tests failed | Env diff | Added setup script | testing |',
|
|
75
|
+
].join('\n'));
|
|
76
|
+
|
|
77
|
+
writeFixture(projectDir, '.gsd/milestones/M001/M001-ROADMAP.md', [
|
|
78
|
+
'# M001: Auth System',
|
|
79
|
+
'',
|
|
80
|
+
'## Vision',
|
|
81
|
+
'',
|
|
82
|
+
'Build authentication for the platform.',
|
|
83
|
+
'',
|
|
84
|
+
'## Slice Overview',
|
|
85
|
+
'',
|
|
86
|
+
'| ID | Slice | Risk | Depends | Done | After this |',
|
|
87
|
+
'|----|-------|------|---------|------|------------|',
|
|
88
|
+
'| S01 | Login flow | low | — | 🔄 | Users can log in |',
|
|
89
|
+
].join('\n'));
|
|
90
|
+
|
|
91
|
+
writeFixture(projectDir, '.gsd/milestones/M001/slices/S01/S01-PLAN.md', [
|
|
92
|
+
'# S01: Login flow',
|
|
93
|
+
'',
|
|
94
|
+
'## Tasks',
|
|
95
|
+
'',
|
|
96
|
+
'- [ ] **T01: Implement login endpoint** — Core auth logic',
|
|
97
|
+
'- [ ] **T02: Add session management** — Keep users logged in',
|
|
98
|
+
].join('\n'));
|
|
99
|
+
}
|
|
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
|
+
|
|
144
|
+
// ---------------------------------------------------------------------------
|
|
145
|
+
// buildGraph tests
|
|
146
|
+
// ---------------------------------------------------------------------------
|
|
147
|
+
|
|
148
|
+
describe('buildGraph', () => {
|
|
149
|
+
let projectDir: string;
|
|
150
|
+
|
|
151
|
+
before(() => {
|
|
152
|
+
projectDir = tmpProject();
|
|
153
|
+
makeProjectWithArtifacts(projectDir);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
after(() => rmSync(projectDir, { recursive: true, force: true }));
|
|
157
|
+
|
|
158
|
+
it('returns nodeCount > 0 for a project with artifacts', async () => {
|
|
159
|
+
const graph = await buildGraph(projectDir);
|
|
160
|
+
assert.ok(graph.nodes.length > 0, `Expected nodes, got ${graph.nodes.length}`);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('returns edgeCount >= 0 (valid graph structure)', async () => {
|
|
164
|
+
const graph = await buildGraph(projectDir);
|
|
165
|
+
assert.ok(graph.edges.length >= 0);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('includes builtAt ISO timestamp', async () => {
|
|
169
|
+
const graph = await buildGraph(projectDir);
|
|
170
|
+
assert.ok(typeof graph.builtAt === 'string');
|
|
171
|
+
assert.ok(!isNaN(Date.parse(graph.builtAt)));
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('skips unparseable artifact and does not throw', async () => {
|
|
175
|
+
const badProject = tmpProject();
|
|
176
|
+
// Write a corrupt/minimal STATE.md that is technically valid but empty
|
|
177
|
+
writeFixture(badProject, '.gsd/STATE.md', 'not valid gsd state at all \0\0\0');
|
|
178
|
+
// Should not throw
|
|
179
|
+
const graph = await buildGraph(badProject);
|
|
180
|
+
assert.ok(graph.nodes.length >= 0);
|
|
181
|
+
rmSync(badProject, { recursive: true, force: true });
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('returns empty graph for project with no .gsd/ directory', async () => {
|
|
185
|
+
const emptyProject = tmpProject();
|
|
186
|
+
const graph = await buildGraph(emptyProject);
|
|
187
|
+
assert.ok(graph.nodes.length >= 0); // no throw
|
|
188
|
+
assert.equal(typeof graph.builtAt, 'string');
|
|
189
|
+
rmSync(emptyProject, { recursive: true, force: true });
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('nodes have required fields: id, label, type, confidence', async () => {
|
|
193
|
+
const graph = await buildGraph(projectDir);
|
|
194
|
+
for (const node of graph.nodes) {
|
|
195
|
+
assert.ok(typeof node.id === 'string', 'node.id must be string');
|
|
196
|
+
assert.ok(typeof node.label === 'string', 'node.label must be string');
|
|
197
|
+
assert.ok(typeof node.type === 'string', 'node.type must be string');
|
|
198
|
+
assert.ok(
|
|
199
|
+
node.confidence === 'EXTRACTED' ||
|
|
200
|
+
node.confidence === 'INFERRED' ||
|
|
201
|
+
node.confidence === 'AMBIGUOUS',
|
|
202
|
+
`Invalid confidence: ${node.confidence}`,
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
});
|
|
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
|
+
|
|
343
|
+
// ---------------------------------------------------------------------------
|
|
344
|
+
// writeGraph tests
|
|
345
|
+
// ---------------------------------------------------------------------------
|
|
346
|
+
|
|
347
|
+
describe('writeGraph', () => {
|
|
348
|
+
let projectDir: string;
|
|
349
|
+
let graph: KnowledgeGraph;
|
|
350
|
+
|
|
351
|
+
before(async () => {
|
|
352
|
+
projectDir = tmpProject();
|
|
353
|
+
makeProjectWithArtifacts(projectDir);
|
|
354
|
+
graph = await buildGraph(projectDir);
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
after(() => rmSync(projectDir, { recursive: true, force: true }));
|
|
358
|
+
|
|
359
|
+
it('creates graph.json in .gsd/graphs/ after writeGraph()', async () => {
|
|
360
|
+
const gsdRoot = join(projectDir, '.gsd');
|
|
361
|
+
await writeGraph(gsdRoot, graph);
|
|
362
|
+
const graphPath = join(gsdRoot, 'graphs', 'graph.json');
|
|
363
|
+
assert.ok(existsSync(graphPath), `Expected ${graphPath} to exist`);
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
it('write is atomic — no temp file remains after writeGraph()', async () => {
|
|
367
|
+
const gsdRoot = join(projectDir, '.gsd');
|
|
368
|
+
await writeGraph(gsdRoot, graph);
|
|
369
|
+
const tmpPath = join(gsdRoot, 'graphs', 'graph.tmp.json');
|
|
370
|
+
assert.ok(!existsSync(tmpPath), 'Temp file should not exist after successful write');
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
it('written graph.json is valid JSON with nodes and edges', async () => {
|
|
374
|
+
const gsdRoot = join(projectDir, '.gsd');
|
|
375
|
+
await writeGraph(gsdRoot, graph);
|
|
376
|
+
const raw = readFileSync(join(gsdRoot, 'graphs', 'graph.json'), 'utf-8');
|
|
377
|
+
const parsed = JSON.parse(raw) as KnowledgeGraph;
|
|
378
|
+
assert.ok(Array.isArray(parsed.nodes));
|
|
379
|
+
assert.ok(Array.isArray(parsed.edges));
|
|
380
|
+
assert.ok(typeof parsed.builtAt === 'string');
|
|
381
|
+
});
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
// ---------------------------------------------------------------------------
|
|
385
|
+
// graphStatus tests
|
|
386
|
+
// ---------------------------------------------------------------------------
|
|
387
|
+
|
|
388
|
+
describe('graphStatus', () => {
|
|
389
|
+
let projectDir: string;
|
|
390
|
+
|
|
391
|
+
beforeEach(() => {
|
|
392
|
+
projectDir = tmpProject();
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
afterEach(() => rmSync(projectDir, { recursive: true, force: true }));
|
|
396
|
+
|
|
397
|
+
it('returns { exists: false } when no graph.json exists', async () => {
|
|
398
|
+
const status = await graphStatus(projectDir);
|
|
399
|
+
assert.equal(status.exists, false);
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
it('returns { exists: true, nodeCount, edgeCount, ageHours } when graph exists', async () => {
|
|
403
|
+
makeProjectWithArtifacts(projectDir);
|
|
404
|
+
const gsdRoot = join(projectDir, '.gsd');
|
|
405
|
+
const graph = await buildGraph(projectDir);
|
|
406
|
+
await writeGraph(gsdRoot, graph);
|
|
407
|
+
|
|
408
|
+
const status = await graphStatus(projectDir);
|
|
409
|
+
assert.equal(status.exists, true);
|
|
410
|
+
assert.ok(typeof status.nodeCount === 'number');
|
|
411
|
+
assert.ok(typeof status.edgeCount === 'number');
|
|
412
|
+
assert.ok(typeof status.ageHours === 'number');
|
|
413
|
+
assert.ok(status.ageHours >= 0);
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
it('stale = false for a freshly built graph', async () => {
|
|
417
|
+
makeProjectWithArtifacts(projectDir);
|
|
418
|
+
const gsdRoot = join(projectDir, '.gsd');
|
|
419
|
+
const graph = await buildGraph(projectDir);
|
|
420
|
+
await writeGraph(gsdRoot, graph);
|
|
421
|
+
|
|
422
|
+
const status = await graphStatus(projectDir);
|
|
423
|
+
assert.equal(status.stale, false);
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
it('stale = true for a graph older than 24h (builtAt backdated)', async () => {
|
|
427
|
+
makeProjectWithArtifacts(projectDir);
|
|
428
|
+
const gsdRoot = join(projectDir, '.gsd');
|
|
429
|
+
mkdirSync(join(gsdRoot, 'graphs'), { recursive: true });
|
|
430
|
+
|
|
431
|
+
// Write a graph with a builtAt 25 hours ago
|
|
432
|
+
const oldGraph: KnowledgeGraph = {
|
|
433
|
+
nodes: [],
|
|
434
|
+
edges: [],
|
|
435
|
+
builtAt: new Date(Date.now() - 25 * 60 * 60 * 1000).toISOString(),
|
|
436
|
+
};
|
|
437
|
+
writeFileSync(
|
|
438
|
+
join(gsdRoot, 'graphs', 'graph.json'),
|
|
439
|
+
JSON.stringify(oldGraph),
|
|
440
|
+
'utf-8',
|
|
441
|
+
);
|
|
442
|
+
|
|
443
|
+
const status = await graphStatus(projectDir);
|
|
444
|
+
assert.equal(status.exists, true);
|
|
445
|
+
assert.equal(status.stale, true);
|
|
446
|
+
});
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
// ---------------------------------------------------------------------------
|
|
450
|
+
// graphQuery tests
|
|
451
|
+
// ---------------------------------------------------------------------------
|
|
452
|
+
|
|
453
|
+
describe('graphQuery', () => {
|
|
454
|
+
let projectDir: string;
|
|
455
|
+
|
|
456
|
+
before(async () => {
|
|
457
|
+
projectDir = tmpProject();
|
|
458
|
+
makeProjectWithArtifacts(projectDir);
|
|
459
|
+
const gsdRoot = join(projectDir, '.gsd');
|
|
460
|
+
const graph = await buildGraph(projectDir);
|
|
461
|
+
await writeGraph(gsdRoot, graph);
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
after(() => rmSync(projectDir, { recursive: true, force: true }));
|
|
465
|
+
|
|
466
|
+
it('returns matching nodes for a known term', async () => {
|
|
467
|
+
const result = await graphQuery(projectDir, 'auth');
|
|
468
|
+
assert.ok(Array.isArray(result.nodes));
|
|
469
|
+
// Should match nodes with 'auth' in label or description
|
|
470
|
+
assert.ok(result.nodes.length > 0, 'Expected at least one match for "auth"');
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
it('returns empty array for a term that matches nothing', async () => {
|
|
474
|
+
const result = await graphQuery(projectDir, 'xxxxxxnotfound999zzz');
|
|
475
|
+
assert.ok(Array.isArray(result.nodes));
|
|
476
|
+
assert.equal(result.nodes.length, 0);
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
it('search is case-insensitive', async () => {
|
|
480
|
+
const lower = await graphQuery(projectDir, 'auth');
|
|
481
|
+
const upper = await graphQuery(projectDir, 'AUTH');
|
|
482
|
+
assert.deepEqual(
|
|
483
|
+
lower.nodes.map((n) => n.id).sort(),
|
|
484
|
+
upper.nodes.map((n) => n.id).sort(),
|
|
485
|
+
);
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
it('budget trims AMBIGUOUS edges first', async () => {
|
|
489
|
+
const gsdRoot = join(projectDir, '.gsd');
|
|
490
|
+
// Write a graph with mixed confidence edges
|
|
491
|
+
const mixedGraph: KnowledgeGraph = {
|
|
492
|
+
builtAt: new Date().toISOString(),
|
|
493
|
+
nodes: [
|
|
494
|
+
{ id: 'n1', label: 'seed node budget', type: 'milestone', confidence: 'EXTRACTED' },
|
|
495
|
+
{ id: 'n2', label: 'connected via AMBIGUOUS', type: 'task', confidence: 'AMBIGUOUS' },
|
|
496
|
+
{ id: 'n3', label: 'connected via INFERRED', type: 'task', confidence: 'INFERRED' },
|
|
497
|
+
],
|
|
498
|
+
edges: [
|
|
499
|
+
{ from: 'n1', to: 'n2', type: 'contains', confidence: 'AMBIGUOUS' },
|
|
500
|
+
{ from: 'n1', to: 'n3', type: 'contains', confidence: 'INFERRED' },
|
|
501
|
+
],
|
|
502
|
+
};
|
|
503
|
+
await writeGraph(gsdRoot, mixedGraph);
|
|
504
|
+
|
|
505
|
+
// With a very small budget, AMBIGUOUS edges should be trimmed first
|
|
506
|
+
const result = await graphQuery(projectDir, 'seed node budget', 10);
|
|
507
|
+
// At minimum, the seed node itself should be present
|
|
508
|
+
assert.ok(result.nodes.some((n) => n.id === 'n1'), 'Seed node should be in result');
|
|
509
|
+
|
|
510
|
+
// Restore the original graph
|
|
511
|
+
const originalGraph = await buildGraph(projectDir);
|
|
512
|
+
await writeGraph(gsdRoot, originalGraph);
|
|
513
|
+
});
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
// ---------------------------------------------------------------------------
|
|
517
|
+
// writeSnapshot + graphDiff tests
|
|
518
|
+
// ---------------------------------------------------------------------------
|
|
519
|
+
|
|
520
|
+
describe('graphDiff', () => {
|
|
521
|
+
let projectDir: string;
|
|
522
|
+
|
|
523
|
+
beforeEach(async () => {
|
|
524
|
+
projectDir = tmpProject();
|
|
525
|
+
makeProjectWithArtifacts(projectDir);
|
|
526
|
+
const gsdRoot = join(projectDir, '.gsd');
|
|
527
|
+
const graph = await buildGraph(projectDir);
|
|
528
|
+
await writeGraph(gsdRoot, graph);
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
afterEach(() => rmSync(projectDir, { recursive: true, force: true }));
|
|
532
|
+
|
|
533
|
+
it('returns empty diff when comparing graph to itself (snapshot = current)', async () => {
|
|
534
|
+
const gsdRoot = join(projectDir, '.gsd');
|
|
535
|
+
await writeSnapshot(gsdRoot);
|
|
536
|
+
const diff = await graphDiff(projectDir);
|
|
537
|
+
assert.ok(Array.isArray(diff.nodes.added));
|
|
538
|
+
assert.ok(Array.isArray(diff.nodes.removed));
|
|
539
|
+
assert.ok(Array.isArray(diff.nodes.changed));
|
|
540
|
+
assert.equal(diff.nodes.added.length, 0);
|
|
541
|
+
assert.equal(diff.nodes.removed.length, 0);
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
it('returns added nodes when a new node appears after snapshot', async () => {
|
|
545
|
+
const gsdRoot = join(projectDir, '.gsd');
|
|
546
|
+
// Take snapshot of the original graph
|
|
547
|
+
await writeSnapshot(gsdRoot);
|
|
548
|
+
|
|
549
|
+
// Now write a graph with an extra node
|
|
550
|
+
const extraGraph: KnowledgeGraph = {
|
|
551
|
+
builtAt: new Date().toISOString(),
|
|
552
|
+
nodes: [
|
|
553
|
+
{ id: 'brand-new-node', label: 'New Feature', type: 'milestone', confidence: 'EXTRACTED' },
|
|
554
|
+
],
|
|
555
|
+
edges: [],
|
|
556
|
+
};
|
|
557
|
+
await writeGraph(gsdRoot, extraGraph);
|
|
558
|
+
|
|
559
|
+
const diff = await graphDiff(projectDir);
|
|
560
|
+
assert.ok(diff.nodes.added.includes('brand-new-node'), 'new node should be in added');
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
it('returns removed nodes when a node disappears after snapshot', async () => {
|
|
564
|
+
const gsdRoot = join(projectDir, '.gsd');
|
|
565
|
+
// Create snapshot with a node that won't exist in current graph
|
|
566
|
+
const snapshotGraph: KnowledgeGraph = {
|
|
567
|
+
builtAt: new Date().toISOString(),
|
|
568
|
+
nodes: [
|
|
569
|
+
{ id: 'old-node-to-be-removed', label: 'Old', type: 'task', confidence: 'EXTRACTED' },
|
|
570
|
+
],
|
|
571
|
+
edges: [],
|
|
572
|
+
};
|
|
573
|
+
writeFileSync(
|
|
574
|
+
join(gsdRoot, 'graphs', '.last-build-snapshot.json'),
|
|
575
|
+
JSON.stringify({ ...snapshotGraph, snapshotAt: new Date().toISOString() }),
|
|
576
|
+
'utf-8',
|
|
577
|
+
);
|
|
578
|
+
|
|
579
|
+
// Current graph.json has no such node
|
|
580
|
+
const diff = await graphDiff(projectDir);
|
|
581
|
+
assert.ok(diff.nodes.removed.includes('old-node-to-be-removed'), 'old node should be in removed');
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
it('returns empty diff structure when no snapshot exists', async () => {
|
|
585
|
+
// No snapshot file — diff should be empty/meaningful
|
|
586
|
+
const diff = await graphDiff(projectDir);
|
|
587
|
+
assert.ok(Array.isArray(diff.nodes.added));
|
|
588
|
+
assert.ok(Array.isArray(diff.nodes.removed));
|
|
589
|
+
assert.ok(Array.isArray(diff.nodes.changed));
|
|
590
|
+
assert.ok(Array.isArray(diff.edges.added));
|
|
591
|
+
assert.ok(Array.isArray(diff.edges.removed));
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
it('writeSnapshot creates .last-build-snapshot.json with snapshotAt', async () => {
|
|
595
|
+
const gsdRoot = join(projectDir, '.gsd');
|
|
596
|
+
await writeSnapshot(gsdRoot);
|
|
597
|
+
const snapshotPath = join(gsdRoot, 'graphs', '.last-build-snapshot.json');
|
|
598
|
+
assert.ok(existsSync(snapshotPath));
|
|
599
|
+
const raw = readFileSync(snapshotPath, 'utf-8');
|
|
600
|
+
const parsed = JSON.parse(raw) as KnowledgeGraph & { snapshotAt: string };
|
|
601
|
+
assert.ok(typeof parsed.snapshotAt === 'string');
|
|
602
|
+
assert.ok(!isNaN(Date.parse(parsed.snapshotAt)));
|
|
603
|
+
});
|
|
604
|
+
});
|