gsd-pi 2.41.0-dev.0acbce9 → 2.41.0-dev.5a170d0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cli-web-branch.d.ts +6 -0
- package/dist/cli-web-branch.js +17 -0
- package/dist/onboarding.js +2 -1
- package/dist/resources/extensions/gsd/auto/loop.js +89 -1
- package/dist/resources/extensions/gsd/auto/phases.js +28 -10
- package/dist/resources/extensions/gsd/auto/session.js +6 -0
- package/dist/resources/extensions/gsd/auto-dashboard.js +8 -2
- package/dist/resources/extensions/gsd/auto-dispatch.js +19 -2
- package/dist/resources/extensions/gsd/auto-post-unit.js +7 -0
- package/dist/resources/extensions/gsd/auto-recovery.js +12 -4
- package/dist/resources/extensions/gsd/auto-start.js +8 -3
- package/dist/resources/extensions/gsd/auto-worktree.js +147 -13
- package/dist/resources/extensions/gsd/auto.js +64 -2
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +199 -164
- package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +62 -0
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +2 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +16 -0
- package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +7 -2
- package/dist/resources/extensions/gsd/commands/catalog.js +40 -1
- package/dist/resources/extensions/gsd/commands/handlers/core.js +1 -0
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +5 -0
- package/dist/resources/extensions/gsd/commands/handlers/workflow.js +146 -0
- package/dist/resources/extensions/gsd/context-injector.js +74 -0
- package/dist/resources/extensions/gsd/context-store.js +4 -3
- package/dist/resources/extensions/gsd/custom-execution-policy.js +47 -0
- package/dist/resources/extensions/gsd/custom-verification.js +145 -0
- package/dist/resources/extensions/gsd/custom-workflow-engine.js +164 -0
- package/dist/resources/extensions/gsd/dashboard-overlay.js +1 -0
- package/dist/resources/extensions/gsd/db-writer.js +5 -2
- package/dist/resources/extensions/gsd/definition-loader.js +352 -0
- package/dist/resources/extensions/gsd/detection.js +1 -1
- package/dist/resources/extensions/gsd/dev-execution-policy.js +24 -0
- package/dist/resources/extensions/gsd/dev-workflow-engine.js +82 -0
- package/dist/resources/extensions/gsd/doctor.js +11 -1
- package/dist/resources/extensions/gsd/engine-resolver.js +40 -0
- package/dist/resources/extensions/gsd/engine-types.js +8 -0
- package/dist/resources/extensions/gsd/execution-policy.js +8 -0
- package/dist/resources/extensions/gsd/exit-command.js +12 -2
- package/dist/resources/extensions/gsd/export.js +9 -13
- package/dist/resources/extensions/gsd/extension-manifest.json +2 -2
- package/dist/resources/extensions/gsd/files.js +28 -11
- package/dist/resources/extensions/gsd/forensics.js +10 -3
- package/dist/resources/extensions/gsd/git-service.js +5 -1
- package/dist/resources/extensions/gsd/graph.js +225 -0
- package/dist/resources/extensions/gsd/gsd-db.js +25 -8
- package/dist/resources/extensions/gsd/guided-flow-queue.js +1 -1
- package/dist/resources/extensions/gsd/guided-flow.js +7 -3
- package/dist/resources/extensions/gsd/journal.js +85 -0
- package/dist/resources/extensions/gsd/md-importer.js +5 -0
- package/dist/resources/extensions/gsd/milestone-ids.js +1 -1
- package/dist/resources/extensions/gsd/native-git-bridge.js +2 -2
- package/dist/resources/extensions/gsd/post-unit-hooks.js +24 -412
- package/dist/resources/extensions/gsd/preferences-types.js +1 -0
- package/dist/resources/extensions/gsd/preferences.js +1 -0
- package/dist/resources/extensions/gsd/prompt-loader.js +34 -4
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +11 -10
- package/dist/resources/extensions/gsd/prompts/discuss-headless.md +2 -2
- package/dist/resources/extensions/gsd/prompts/discuss.md +1 -1
- package/dist/resources/extensions/gsd/prompts/queue.md +1 -1
- package/dist/resources/extensions/gsd/repo-identity.js +46 -2
- package/dist/resources/extensions/gsd/rule-registry.js +489 -0
- package/dist/resources/extensions/gsd/rule-types.js +6 -0
- package/dist/resources/extensions/gsd/run-manager.js +134 -0
- package/dist/resources/extensions/gsd/service-tier.js +138 -0
- package/dist/resources/extensions/gsd/structured-data-formatter.js +2 -1
- package/dist/resources/extensions/gsd/templates/decisions.md +2 -2
- package/dist/resources/extensions/gsd/workflow-engine.js +7 -0
- package/dist/resources/extensions/gsd/workflow-templates.js +13 -1
- package/dist/resources/extensions/gsd/worktree-manager.js +20 -6
- package/dist/resources/extensions/gsd/worktree-resolver.js +19 -2
- package/dist/resources/extensions/subagent/index.js +7 -3
- package/dist/resources/extensions/voice/index.js +4 -4
- package/dist/resources/skills/create-workflow/SKILL.md +103 -0
- package/dist/resources/skills/create-workflow/references/feature-patterns.md +128 -0
- package/dist/resources/skills/create-workflow/references/verification-policies.md +76 -0
- package/dist/resources/skills/create-workflow/references/yaml-schema-v1.md +46 -0
- package/dist/resources/skills/create-workflow/templates/blog-post-pipeline.yaml +60 -0
- package/dist/resources/skills/create-workflow/templates/code-audit.yaml +60 -0
- package/dist/resources/skills/create-workflow/templates/release-checklist.yaml +66 -0
- package/dist/resources/skills/create-workflow/templates/workflow-definition.yaml +32 -0
- package/dist/resources/skills/create-workflow/workflows/create-from-scratch.md +104 -0
- package/dist/resources/skills/create-workflow/workflows/create-from-template.md +72 -0
- 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 +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- 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/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/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/undo/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/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/229.js +3 -3
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/4024.c195dc1fdd2adbea.js +9 -0
- package/dist/web/standalone/.next/static/chunks/{webpack-9afaaebf6042a1d7.js → webpack-fa307370fcf9fb2c.js} +1 -1
- package/dist/web-mode.d.ts +2 -0
- package/dist/web-mode.js +29 -7
- package/package.json +1 -1
- package/packages/native/src/__tests__/text.test.mjs +33 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.test.js +3 -1
- package/packages/pi-coding-agent/dist/core/discovery-cache.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +10 -7
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js.map +1 -1
- 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 +4 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/src/core/discovery-cache.test.ts +4 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/login-dialog.ts +11 -7
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +5 -0
- package/src/resources/extensions/gsd/auto/loop-deps.ts +5 -1
- package/src/resources/extensions/gsd/auto/loop.ts +101 -1
- package/src/resources/extensions/gsd/auto/phases.ts +30 -10
- package/src/resources/extensions/gsd/auto/session.ts +6 -0
- package/src/resources/extensions/gsd/auto/types.ts +4 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +9 -2
- package/src/resources/extensions/gsd/auto-dispatch.ts +25 -5
- package/src/resources/extensions/gsd/auto-post-unit.ts +8 -0
- package/src/resources/extensions/gsd/auto-recovery.ts +12 -4
- package/src/resources/extensions/gsd/auto-start.ts +8 -3
- package/src/resources/extensions/gsd/auto-worktree.ts +162 -18
- package/src/resources/extensions/gsd/auto.ts +71 -2
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +209 -162
- package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +62 -0
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +2 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +13 -0
- package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +9 -2
- package/src/resources/extensions/gsd/commands/catalog.ts +40 -1
- package/src/resources/extensions/gsd/commands/handlers/core.ts +1 -0
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +5 -0
- package/src/resources/extensions/gsd/commands/handlers/workflow.ts +164 -0
- package/src/resources/extensions/gsd/context-injector.ts +100 -0
- package/src/resources/extensions/gsd/context-store.ts +4 -3
- package/src/resources/extensions/gsd/custom-execution-policy.ts +73 -0
- package/src/resources/extensions/gsd/custom-verification.ts +180 -0
- package/src/resources/extensions/gsd/custom-workflow-engine.ts +216 -0
- package/src/resources/extensions/gsd/dashboard-overlay.ts +1 -0
- package/src/resources/extensions/gsd/db-writer.ts +6 -2
- package/src/resources/extensions/gsd/definition-loader.ts +462 -0
- package/src/resources/extensions/gsd/detection.ts +1 -1
- package/src/resources/extensions/gsd/dev-execution-policy.ts +51 -0
- package/src/resources/extensions/gsd/dev-workflow-engine.ts +110 -0
- package/src/resources/extensions/gsd/doctor.ts +12 -1
- package/src/resources/extensions/gsd/engine-resolver.ts +57 -0
- package/src/resources/extensions/gsd/engine-types.ts +71 -0
- package/src/resources/extensions/gsd/execution-policy.ts +43 -0
- package/src/resources/extensions/gsd/exit-command.ts +14 -2
- package/src/resources/extensions/gsd/export.ts +8 -15
- package/src/resources/extensions/gsd/extension-manifest.json +2 -2
- package/src/resources/extensions/gsd/files.ts +29 -12
- package/src/resources/extensions/gsd/forensics.ts +9 -3
- package/src/resources/extensions/gsd/git-service.ts +5 -4
- package/src/resources/extensions/gsd/graph.ts +312 -0
- package/src/resources/extensions/gsd/gsd-db.ts +37 -8
- package/src/resources/extensions/gsd/guided-flow-queue.ts +1 -1
- package/src/resources/extensions/gsd/guided-flow.ts +7 -3
- package/src/resources/extensions/gsd/journal.ts +134 -0
- package/src/resources/extensions/gsd/md-importer.ts +6 -0
- package/src/resources/extensions/gsd/milestone-ids.ts +1 -1
- package/src/resources/extensions/gsd/native-git-bridge.ts +2 -2
- package/src/resources/extensions/gsd/post-unit-hooks.ts +24 -462
- package/src/resources/extensions/gsd/preferences-types.ts +3 -0
- package/src/resources/extensions/gsd/preferences.ts +1 -0
- package/src/resources/extensions/gsd/prompt-loader.ts +35 -4
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +11 -10
- package/src/resources/extensions/gsd/prompts/discuss-headless.md +2 -2
- package/src/resources/extensions/gsd/prompts/discuss.md +1 -1
- package/src/resources/extensions/gsd/prompts/queue.md +1 -1
- package/src/resources/extensions/gsd/repo-identity.ts +47 -2
- package/src/resources/extensions/gsd/rule-registry.ts +599 -0
- package/src/resources/extensions/gsd/rule-types.ts +68 -0
- package/src/resources/extensions/gsd/run-manager.ts +180 -0
- package/src/resources/extensions/gsd/service-tier.ts +171 -0
- package/src/resources/extensions/gsd/structured-data-formatter.ts +3 -1
- package/src/resources/extensions/gsd/templates/decisions.md +2 -2
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +103 -120
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +85 -0
- package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +202 -0
- package/src/resources/extensions/gsd/tests/bundled-workflow-defs.test.ts +180 -0
- package/src/resources/extensions/gsd/tests/captures.test.ts +12 -1
- package/src/resources/extensions/gsd/tests/commands-workflow-custom.test.ts +283 -0
- package/src/resources/extensions/gsd/tests/context-injector.test.ts +313 -0
- package/src/resources/extensions/gsd/tests/context-store.test.ts +10 -5
- package/src/resources/extensions/gsd/tests/continue-here.test.ts +20 -20
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +540 -0
- package/src/resources/extensions/gsd/tests/custom-verification.test.ts +382 -0
- package/src/resources/extensions/gsd/tests/custom-workflow-engine.test.ts +339 -0
- package/src/resources/extensions/gsd/tests/dashboard-custom-engine.test.ts +87 -0
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +10 -0
- package/src/resources/extensions/gsd/tests/definition-loader.test.ts +778 -0
- package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +318 -0
- package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +15 -10
- package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +5 -4
- package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +167 -0
- package/src/resources/extensions/gsd/tests/doctor-task-done-missing-summary-slice-loop.test.ts +174 -0
- package/src/resources/extensions/gsd/tests/e2e-workflow-pipeline-integration.test.ts +476 -0
- package/src/resources/extensions/gsd/tests/engine-interfaces-contract.test.ts +271 -0
- package/src/resources/extensions/gsd/tests/exit-command.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/graph-operations.test.ts +599 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +8 -1
- package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +7 -7
- package/src/resources/extensions/gsd/tests/iterate-engine-integration.test.ts +429 -0
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +513 -0
- package/src/resources/extensions/gsd/tests/journal-query-tool.test.ts +147 -0
- package/src/resources/extensions/gsd/tests/journal.test.ts +386 -0
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +31 -1
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/milestone-id-reservation.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/parsers.test.ts +110 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +47 -25
- package/src/resources/extensions/gsd/tests/prompt-db.test.ts +3 -1
- package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +61 -1
- package/src/resources/extensions/gsd/tests/routing-history.test.ts +11 -22
- package/src/resources/extensions/gsd/tests/rule-registry.test.ts +413 -0
- package/src/resources/extensions/gsd/tests/run-manager.test.ts +229 -0
- package/src/resources/extensions/gsd/tests/service-tier.test.ts +98 -0
- package/src/resources/extensions/gsd/tests/skill-lifecycle.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +102 -0
- package/src/resources/extensions/gsd/tests/structured-data-formatter.test.ts +4 -3
- package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +45 -0
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +117 -0
- package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +6 -1
- package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +99 -0
- package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +178 -0
- package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +195 -105
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +78 -3
- package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +140 -0
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +74 -0
- package/src/resources/extensions/gsd/types.ts +3 -0
- package/src/resources/extensions/gsd/workflow-engine.ts +38 -0
- package/src/resources/extensions/gsd/workflow-templates.ts +12 -1
- package/src/resources/extensions/gsd/worktree-manager.ts +21 -6
- package/src/resources/extensions/gsd/worktree-resolver.ts +30 -9
- package/src/resources/extensions/subagent/index.ts +7 -3
- package/src/resources/extensions/voice/index.ts +4 -4
- package/src/resources/skills/create-workflow/SKILL.md +103 -0
- package/src/resources/skills/create-workflow/references/feature-patterns.md +128 -0
- package/src/resources/skills/create-workflow/references/verification-policies.md +76 -0
- package/src/resources/skills/create-workflow/references/yaml-schema-v1.md +46 -0
- package/src/resources/skills/create-workflow/templates/blog-post-pipeline.yaml +60 -0
- package/src/resources/skills/create-workflow/templates/code-audit.yaml +60 -0
- package/src/resources/skills/create-workflow/templates/release-checklist.yaml +66 -0
- package/src/resources/skills/create-workflow/templates/workflow-definition.yaml +32 -0
- package/src/resources/skills/create-workflow/workflows/create-from-scratch.md +104 -0
- package/src/resources/skills/create-workflow/workflows/create-from-template.md +72 -0
- package/dist/web/standalone/.next/static/chunks/4024.279c423e4661ece1.js +0 -9
- /package/dist/web/standalone/.next/static/{SwbKZ7JPNFlEmU4f8pKEv → K7GYOOPvQWX6TKYEKhODM}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{SwbKZ7JPNFlEmU4f8pKEv → K7GYOOPvQWX6TKYEKhODM}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* commands-workflow-custom.test.ts — Tests for `/gsd workflow` subcommands
|
|
3
|
+
* and catalog completions.
|
|
4
|
+
*
|
|
5
|
+
* Uses real temp directories with actual definition YAML files.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, it, afterEach, before } from "node:test";
|
|
9
|
+
import assert from "node:assert/strict";
|
|
10
|
+
import {
|
|
11
|
+
mkdtempSync,
|
|
12
|
+
rmSync,
|
|
13
|
+
mkdirSync,
|
|
14
|
+
writeFileSync,
|
|
15
|
+
existsSync,
|
|
16
|
+
} from "node:fs";
|
|
17
|
+
import { join } from "node:path";
|
|
18
|
+
import { tmpdir } from "node:os";
|
|
19
|
+
|
|
20
|
+
import { getGsdArgumentCompletions, TOP_LEVEL_SUBCOMMANDS } from "../commands/catalog.ts";
|
|
21
|
+
|
|
22
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
const tmpDirs: string[] = [];
|
|
25
|
+
let savedCwd: string;
|
|
26
|
+
|
|
27
|
+
function makeTmpBase(): string {
|
|
28
|
+
const dir = mkdtempSync(join(tmpdir(), "wf-cmd-test-"));
|
|
29
|
+
tmpDirs.push(dir);
|
|
30
|
+
return dir;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
afterEach(() => {
|
|
34
|
+
// Restore cwd if changed during tests
|
|
35
|
+
if (savedCwd && process.cwd() !== savedCwd) {
|
|
36
|
+
process.chdir(savedCwd);
|
|
37
|
+
}
|
|
38
|
+
for (const d of tmpDirs) {
|
|
39
|
+
try { rmSync(d, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 }); } catch { /* Windows EPERM */ }
|
|
40
|
+
}
|
|
41
|
+
tmpDirs.length = 0;
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
before(() => {
|
|
45
|
+
savedCwd = process.cwd();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
function createMockCtx() {
|
|
49
|
+
const notifications: { message: string; level: string }[] = [];
|
|
50
|
+
return {
|
|
51
|
+
notifications,
|
|
52
|
+
ui: {
|
|
53
|
+
notify(message: string, level: string) {
|
|
54
|
+
notifications.push({ message, level });
|
|
55
|
+
},
|
|
56
|
+
custom: async () => {},
|
|
57
|
+
},
|
|
58
|
+
shutdown: async () => {},
|
|
59
|
+
sessionManager: {
|
|
60
|
+
getSessionFile: () => null,
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function createMockPi() {
|
|
66
|
+
return {
|
|
67
|
+
registerCommand() {},
|
|
68
|
+
registerTool() {},
|
|
69
|
+
registerShortcut() {},
|
|
70
|
+
on() {},
|
|
71
|
+
sendMessage() {},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Write a minimal valid workflow definition YAML to the expected location. */
|
|
76
|
+
function writeDefinition(basePath: string, name: string, content: string): void {
|
|
77
|
+
const defsDir = join(basePath, ".gsd", "workflow-defs");
|
|
78
|
+
mkdirSync(defsDir, { recursive: true });
|
|
79
|
+
writeFileSync(join(defsDir, `${name}.yaml`), content, "utf-8");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const SIMPLE_DEF = `
|
|
83
|
+
version: 1
|
|
84
|
+
name: test-workflow
|
|
85
|
+
description: A test workflow
|
|
86
|
+
steps:
|
|
87
|
+
- id: step-1
|
|
88
|
+
name: First Step
|
|
89
|
+
prompt: Do step 1
|
|
90
|
+
requires: []
|
|
91
|
+
produces: []
|
|
92
|
+
`;
|
|
93
|
+
|
|
94
|
+
const INVALID_DEF = `
|
|
95
|
+
version: 2
|
|
96
|
+
name: bad-workflow
|
|
97
|
+
steps: []
|
|
98
|
+
`;
|
|
99
|
+
|
|
100
|
+
// ─── Catalog Registration ────────────────────────────────────────────────
|
|
101
|
+
|
|
102
|
+
describe("workflow catalog registration", () => {
|
|
103
|
+
it("workflow appears in TOP_LEVEL_SUBCOMMANDS", () => {
|
|
104
|
+
const entry = TOP_LEVEL_SUBCOMMANDS.find((c) => c.cmd === "workflow");
|
|
105
|
+
assert.ok(entry, "workflow should be in TOP_LEVEL_SUBCOMMANDS");
|
|
106
|
+
assert.ok(entry!.desc.includes("new"), "description should mention new");
|
|
107
|
+
assert.ok(entry!.desc.includes("run"), "description should mention run");
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("getGsdArgumentCompletions('workflow ') returns six subcommands", () => {
|
|
111
|
+
const completions = getGsdArgumentCompletions("workflow ");
|
|
112
|
+
const labels = completions.map((c: any) => c.label);
|
|
113
|
+
for (const sub of ["new", "run", "list", "validate", "pause", "resume"]) {
|
|
114
|
+
assert.ok(labels.includes(sub), `missing completion: ${sub}`);
|
|
115
|
+
}
|
|
116
|
+
assert.equal(labels.length, 6, "should have exactly 6 subcommands");
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("getGsdArgumentCompletions('workflow r') filters to run and resume", () => {
|
|
120
|
+
const completions = getGsdArgumentCompletions("workflow r");
|
|
121
|
+
const labels = completions.map((c: any) => c.label);
|
|
122
|
+
assert.ok(labels.includes("run"), "should include run");
|
|
123
|
+
assert.ok(labels.includes("resume"), "should include resume");
|
|
124
|
+
assert.ok(!labels.includes("list"), "should not include list");
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("getGsdArgumentCompletions('workflow run ') returns definition names", () => {
|
|
128
|
+
const base = makeTmpBase();
|
|
129
|
+
writeDefinition(base, "deploy-pipeline", SIMPLE_DEF);
|
|
130
|
+
writeDefinition(base, "test-suite", SIMPLE_DEF);
|
|
131
|
+
|
|
132
|
+
// Change cwd so the completion scanner can find `.gsd/workflow-defs/`
|
|
133
|
+
process.chdir(base);
|
|
134
|
+
|
|
135
|
+
const completions = getGsdArgumentCompletions("workflow run ");
|
|
136
|
+
const labels = completions.map((c: any) => c.label);
|
|
137
|
+
assert.ok(labels.includes("deploy-pipeline"), "should include deploy-pipeline");
|
|
138
|
+
assert.ok(labels.includes("test-suite"), "should include test-suite");
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("getGsdArgumentCompletions('workflow validate ') returns definition names", () => {
|
|
142
|
+
const base = makeTmpBase();
|
|
143
|
+
writeDefinition(base, "my-workflow", SIMPLE_DEF);
|
|
144
|
+
|
|
145
|
+
process.chdir(base);
|
|
146
|
+
|
|
147
|
+
const completions = getGsdArgumentCompletions("workflow validate ");
|
|
148
|
+
const labels = completions.map((c: any) => c.label);
|
|
149
|
+
assert.ok(labels.includes("my-workflow"), "should include my-workflow");
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it("getGsdArgumentCompletions('workflow run d') filters by prefix", () => {
|
|
153
|
+
const base = makeTmpBase();
|
|
154
|
+
writeDefinition(base, "deploy-pipeline", SIMPLE_DEF);
|
|
155
|
+
writeDefinition(base, "test-suite", SIMPLE_DEF);
|
|
156
|
+
|
|
157
|
+
process.chdir(base);
|
|
158
|
+
|
|
159
|
+
const completions = getGsdArgumentCompletions("workflow run d");
|
|
160
|
+
const labels = completions.map((c: any) => c.label);
|
|
161
|
+
assert.ok(labels.includes("deploy-pipeline"), "should include deploy-pipeline");
|
|
162
|
+
assert.ok(!labels.includes("test-suite"), "should not include test-suite");
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// ─── Command Handler Tests ───────────────────────────────────────────────
|
|
167
|
+
|
|
168
|
+
describe("workflow command handler", () => {
|
|
169
|
+
// Dynamically import the handler so module-level side effects
|
|
170
|
+
// don't break when auto.ts pulls in heavy runtime deps.
|
|
171
|
+
// We test the pure routing logic by calling handleWorkflowCommand directly.
|
|
172
|
+
|
|
173
|
+
async function callHandler(trimmed: string) {
|
|
174
|
+
const { handleWorkflowCommand } = await import("../commands/handlers/workflow.ts");
|
|
175
|
+
const ctx = createMockCtx();
|
|
176
|
+
const pi = createMockPi();
|
|
177
|
+
const handled = await handleWorkflowCommand(trimmed, ctx as any, pi as any);
|
|
178
|
+
return { handled, notifications: ctx.notifications };
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
it("bare '/gsd workflow' shows usage", async () => {
|
|
182
|
+
const { handled, notifications } = await callHandler("workflow");
|
|
183
|
+
assert.ok(handled, "should be handled");
|
|
184
|
+
assert.ok(
|
|
185
|
+
notifications.some((n) => n.message.includes("Usage: /gsd workflow")),
|
|
186
|
+
"should show usage",
|
|
187
|
+
);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it("'/gsd workflow new' shows skill invocation message", async () => {
|
|
191
|
+
const { handled, notifications } = await callHandler("workflow new");
|
|
192
|
+
assert.ok(handled, "should be handled");
|
|
193
|
+
assert.ok(
|
|
194
|
+
notifications.some((n) => n.message.includes("create-workflow")),
|
|
195
|
+
"should mention create-workflow skill",
|
|
196
|
+
);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it("'/gsd workflow run' without name shows usage warning", async () => {
|
|
200
|
+
const { handled, notifications } = await callHandler("workflow run");
|
|
201
|
+
assert.ok(handled, "should be handled");
|
|
202
|
+
assert.ok(
|
|
203
|
+
notifications.some((n) => n.level === "warning" && n.message.includes("Usage")),
|
|
204
|
+
"should show usage warning",
|
|
205
|
+
);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it("'/gsd workflow run nonexistent' shows error for missing definition", async () => {
|
|
209
|
+
const { handled, notifications } = await callHandler("workflow run nonexistent-def-12345");
|
|
210
|
+
assert.ok(handled, "should be handled");
|
|
211
|
+
assert.ok(
|
|
212
|
+
notifications.some((n) => n.level === "error" && n.message.includes("not found")),
|
|
213
|
+
"should show definition-not-found error",
|
|
214
|
+
);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it("'/gsd workflow validate' without name shows usage warning", async () => {
|
|
218
|
+
const { handled, notifications } = await callHandler("workflow validate");
|
|
219
|
+
assert.ok(handled, "should be handled");
|
|
220
|
+
assert.ok(
|
|
221
|
+
notifications.some((n) => n.level === "warning" && n.message.includes("Usage")),
|
|
222
|
+
"should show usage warning",
|
|
223
|
+
);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it("'/gsd workflow validate nonexistent' shows definition not found", async () => {
|
|
227
|
+
const { handled, notifications } = await callHandler("workflow validate nonexistent-def-12345");
|
|
228
|
+
assert.ok(handled, "should be handled");
|
|
229
|
+
assert.ok(
|
|
230
|
+
notifications.some((n) => n.level === "error" && n.message.includes("not found")),
|
|
231
|
+
"should show not-found error",
|
|
232
|
+
);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it("'/gsd workflow pause' without custom engine shows warning", async () => {
|
|
236
|
+
const { handled, notifications } = await callHandler("workflow pause");
|
|
237
|
+
assert.ok(handled, "should be handled");
|
|
238
|
+
assert.ok(
|
|
239
|
+
notifications.some((n) => n.level === "warning"),
|
|
240
|
+
"should show warning when no custom workflow is running",
|
|
241
|
+
);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it("'/gsd workflow resume' without custom engine shows warning", async () => {
|
|
245
|
+
const { handled, notifications } = await callHandler("workflow resume");
|
|
246
|
+
assert.ok(handled, "should be handled");
|
|
247
|
+
assert.ok(
|
|
248
|
+
notifications.some((n) => n.level === "warning"),
|
|
249
|
+
"should show warning when no custom workflow to resume",
|
|
250
|
+
);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it("'/gsd workflow unknown-sub' shows unknown subcommand", async () => {
|
|
254
|
+
const { handled, notifications } = await callHandler("workflow blurble");
|
|
255
|
+
assert.ok(handled, "should be handled");
|
|
256
|
+
assert.ok(
|
|
257
|
+
notifications.some((n) => n.message.includes("Unknown workflow subcommand")),
|
|
258
|
+
"should show unknown subcommand message",
|
|
259
|
+
);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it("'/gsd workflow list' with no runs shows empty message", async () => {
|
|
263
|
+
const { handled, notifications } = await callHandler("workflow list");
|
|
264
|
+
assert.ok(handled, "should be handled");
|
|
265
|
+
assert.ok(
|
|
266
|
+
notifications.some((n) => n.message.includes("No workflow runs found")),
|
|
267
|
+
"should show no runs message",
|
|
268
|
+
);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it("non-workflow commands are not intercepted by custom workflow routing", async () => {
|
|
272
|
+
const { handleWorkflowCommand } = await import("../commands/handlers/workflow.ts");
|
|
273
|
+
const ctx = createMockCtx();
|
|
274
|
+
const pi = createMockPi();
|
|
275
|
+
// "queue" does not start with "workflow" so the custom routing should not handle it.
|
|
276
|
+
// The function may still handle it via its existing dev-workflow routing, but it
|
|
277
|
+
// should not be captured by the custom workflow `if` block.
|
|
278
|
+
// We verify this by checking that a clearly non-workflow command like "somethingelse"
|
|
279
|
+
// returns false (unhandled).
|
|
280
|
+
const handled = await handleWorkflowCommand("somethingelse", ctx as any, pi as any);
|
|
281
|
+
assert.equal(handled, false, "non-workflow commands should return false");
|
|
282
|
+
});
|
|
283
|
+
});
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* context-injector.test.ts — Tests for injectContext().
|
|
3
|
+
*
|
|
4
|
+
* Tests context injection from prior step artifacts: single-step,
|
|
5
|
+
* multi-step chain, missing artifact, no contextFrom, truncation,
|
|
6
|
+
* and unknown step ID in contextFrom.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { describe, it } from "node:test";
|
|
10
|
+
import assert from "node:assert/strict";
|
|
11
|
+
import { mkdtempSync, writeFileSync, mkdirSync } from "node:fs";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
import { tmpdir } from "node:os";
|
|
14
|
+
import { stringify } from "yaml";
|
|
15
|
+
import { injectContext } from "../context-injector.ts";
|
|
16
|
+
import type { WorkflowDefinition } from "../definition-loader.ts";
|
|
17
|
+
|
|
18
|
+
/** Create a temp run directory with the given definition and optional files. */
|
|
19
|
+
function makeTempRun(
|
|
20
|
+
def: WorkflowDefinition,
|
|
21
|
+
files?: Record<string, string>,
|
|
22
|
+
): string {
|
|
23
|
+
const runDir = mkdtempSync(join(tmpdir(), "ci-test-"));
|
|
24
|
+
writeFileSync(join(runDir, "DEFINITION.yaml"), stringify(def), "utf-8");
|
|
25
|
+
|
|
26
|
+
if (files) {
|
|
27
|
+
for (const [relPath, content] of Object.entries(files)) {
|
|
28
|
+
const absPath = join(runDir, relPath);
|
|
29
|
+
const parentDir = join(absPath, "..");
|
|
30
|
+
mkdirSync(parentDir, { recursive: true });
|
|
31
|
+
writeFileSync(absPath, content, "utf-8");
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return runDir;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Minimal valid workflow definition factory. */
|
|
39
|
+
function makeDef(
|
|
40
|
+
steps: WorkflowDefinition["steps"],
|
|
41
|
+
): WorkflowDefinition {
|
|
42
|
+
return {
|
|
43
|
+
version: 1,
|
|
44
|
+
name: "test-workflow",
|
|
45
|
+
steps,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ─── single-step context ────────────────────────────────────────────────
|
|
50
|
+
|
|
51
|
+
describe("single-step context injection", () => {
|
|
52
|
+
it("prepends step-1 artifact content to step-2 prompt", () => {
|
|
53
|
+
const def = makeDef([
|
|
54
|
+
{
|
|
55
|
+
id: "step-1",
|
|
56
|
+
name: "Research",
|
|
57
|
+
prompt: "Research the topic",
|
|
58
|
+
requires: [],
|
|
59
|
+
produces: ["output.md"],
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: "step-2",
|
|
63
|
+
name: "Write",
|
|
64
|
+
prompt: "Write the report",
|
|
65
|
+
requires: ["step-1"],
|
|
66
|
+
produces: ["report.md"],
|
|
67
|
+
contextFrom: ["step-1"],
|
|
68
|
+
},
|
|
69
|
+
]);
|
|
70
|
+
|
|
71
|
+
const runDir = makeTempRun(def, {
|
|
72
|
+
"output.md": "Research findings: AI is growing fast.",
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const result = injectContext(runDir, "step-2", "Write the report");
|
|
76
|
+
assert.ok(result.includes("Research findings: AI is growing fast."));
|
|
77
|
+
assert.ok(result.includes('Context from step "step-1"'));
|
|
78
|
+
assert.ok(result.includes("(file: output.md)"));
|
|
79
|
+
assert.ok(result.endsWith("Write the report"));
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// ─── multi-step chain ───────────────────────────────────────────────────
|
|
84
|
+
|
|
85
|
+
describe("multi-step context chain", () => {
|
|
86
|
+
it("prepends artifacts from both step-1 and step-2", () => {
|
|
87
|
+
const def = makeDef([
|
|
88
|
+
{
|
|
89
|
+
id: "step-1",
|
|
90
|
+
name: "Research",
|
|
91
|
+
prompt: "Research",
|
|
92
|
+
requires: [],
|
|
93
|
+
produces: ["research.md"],
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
id: "step-2",
|
|
97
|
+
name: "Outline",
|
|
98
|
+
prompt: "Outline",
|
|
99
|
+
requires: ["step-1"],
|
|
100
|
+
produces: ["outline.md"],
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
id: "step-3",
|
|
104
|
+
name: "Draft",
|
|
105
|
+
prompt: "Write the draft",
|
|
106
|
+
requires: ["step-1", "step-2"],
|
|
107
|
+
produces: ["draft.md"],
|
|
108
|
+
contextFrom: ["step-1", "step-2"],
|
|
109
|
+
},
|
|
110
|
+
]);
|
|
111
|
+
|
|
112
|
+
const runDir = makeTempRun(def, {
|
|
113
|
+
"research.md": "Research content here.",
|
|
114
|
+
"outline.md": "Outline content here.",
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
const result = injectContext(runDir, "step-3", "Write the draft");
|
|
118
|
+
assert.ok(result.includes("Research content here."));
|
|
119
|
+
assert.ok(result.includes("Outline content here."));
|
|
120
|
+
assert.ok(result.includes('Context from step "step-1"'));
|
|
121
|
+
assert.ok(result.includes('Context from step "step-2"'));
|
|
122
|
+
assert.ok(result.endsWith("Write the draft"));
|
|
123
|
+
|
|
124
|
+
// Verify order: step-1 context appears before step-2 context
|
|
125
|
+
const idx1 = result.indexOf('Context from step "step-1"');
|
|
126
|
+
const idx2 = result.indexOf('Context from step "step-2"');
|
|
127
|
+
assert.ok(idx1 < idx2, "step-1 context should appear before step-2 context");
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// ─── missing artifact file ──────────────────────────────────────────────
|
|
132
|
+
|
|
133
|
+
describe("missing artifact file", () => {
|
|
134
|
+
it("skips missing artifact and includes existing ones", () => {
|
|
135
|
+
const def = makeDef([
|
|
136
|
+
{
|
|
137
|
+
id: "step-1",
|
|
138
|
+
name: "Research",
|
|
139
|
+
prompt: "Research",
|
|
140
|
+
requires: [],
|
|
141
|
+
produces: ["found.md", "missing.md"],
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
id: "step-2",
|
|
145
|
+
name: "Write",
|
|
146
|
+
prompt: "Write the report",
|
|
147
|
+
requires: ["step-1"],
|
|
148
|
+
produces: ["report.md"],
|
|
149
|
+
contextFrom: ["step-1"],
|
|
150
|
+
},
|
|
151
|
+
]);
|
|
152
|
+
|
|
153
|
+
// Only create found.md, not missing.md
|
|
154
|
+
const runDir = makeTempRun(def, {
|
|
155
|
+
"found.md": "Found content.",
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
const result = injectContext(runDir, "step-2", "Write the report");
|
|
159
|
+
assert.ok(result.includes("Found content."));
|
|
160
|
+
assert.ok(!result.includes("missing.md"));
|
|
161
|
+
assert.ok(result.endsWith("Write the report"));
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it("returns prompt unchanged when all referenced artifacts are missing", () => {
|
|
165
|
+
const def = makeDef([
|
|
166
|
+
{
|
|
167
|
+
id: "step-1",
|
|
168
|
+
name: "Research",
|
|
169
|
+
prompt: "Research",
|
|
170
|
+
requires: [],
|
|
171
|
+
produces: ["missing.md"],
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
id: "step-2",
|
|
175
|
+
name: "Write",
|
|
176
|
+
prompt: "Write the report",
|
|
177
|
+
requires: ["step-1"],
|
|
178
|
+
produces: ["report.md"],
|
|
179
|
+
contextFrom: ["step-1"],
|
|
180
|
+
},
|
|
181
|
+
]);
|
|
182
|
+
|
|
183
|
+
const runDir = makeTempRun(def);
|
|
184
|
+
|
|
185
|
+
const result = injectContext(runDir, "step-2", "Write the report");
|
|
186
|
+
assert.equal(result, "Write the report");
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// ─── no contextFrom ────────────────────────────────────────────────────
|
|
191
|
+
|
|
192
|
+
describe("no contextFrom", () => {
|
|
193
|
+
it("returns prompt unchanged when step has no contextFrom", () => {
|
|
194
|
+
const def = makeDef([
|
|
195
|
+
{
|
|
196
|
+
id: "step-1",
|
|
197
|
+
name: "Research",
|
|
198
|
+
prompt: "Research",
|
|
199
|
+
requires: [],
|
|
200
|
+
produces: ["output.md"],
|
|
201
|
+
},
|
|
202
|
+
]);
|
|
203
|
+
|
|
204
|
+
const runDir = makeTempRun(def, {
|
|
205
|
+
"output.md": "Some content.",
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
const result = injectContext(runDir, "step-1", "Research");
|
|
209
|
+
assert.equal(result, "Research");
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it("returns prompt unchanged when step ID not found in definition", () => {
|
|
213
|
+
const def = makeDef([
|
|
214
|
+
{
|
|
215
|
+
id: "step-1",
|
|
216
|
+
name: "Research",
|
|
217
|
+
prompt: "Research",
|
|
218
|
+
requires: [],
|
|
219
|
+
produces: [],
|
|
220
|
+
},
|
|
221
|
+
]);
|
|
222
|
+
|
|
223
|
+
const runDir = makeTempRun(def);
|
|
224
|
+
|
|
225
|
+
const result = injectContext(runDir, "nonexistent", "Some prompt");
|
|
226
|
+
assert.equal(result, "Some prompt");
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
// ─── truncation ─────────────────────────────────────────────────────────
|
|
231
|
+
|
|
232
|
+
describe("truncation guard", () => {
|
|
233
|
+
it("truncates artifacts exceeding 10,000 characters", () => {
|
|
234
|
+
const largeContent = "A".repeat(15_000);
|
|
235
|
+
|
|
236
|
+
const def = makeDef([
|
|
237
|
+
{
|
|
238
|
+
id: "step-1",
|
|
239
|
+
name: "Generate",
|
|
240
|
+
prompt: "Generate",
|
|
241
|
+
requires: [],
|
|
242
|
+
produces: ["big.md"],
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
id: "step-2",
|
|
246
|
+
name: "Consume",
|
|
247
|
+
prompt: "Use the output",
|
|
248
|
+
requires: ["step-1"],
|
|
249
|
+
produces: [],
|
|
250
|
+
contextFrom: ["step-1"],
|
|
251
|
+
},
|
|
252
|
+
]);
|
|
253
|
+
|
|
254
|
+
const runDir = makeTempRun(def, {
|
|
255
|
+
"big.md": largeContent,
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
const result = injectContext(runDir, "step-2", "Use the output");
|
|
259
|
+
assert.ok(result.includes("...[truncated]"));
|
|
260
|
+
// The injected content should be 10,000 chars + truncation marker, not all 15,000
|
|
261
|
+
const contextPart = result.split("Use the output")[0];
|
|
262
|
+
assert.ok(contextPart.length < 15_000, "Context should be truncated below original size");
|
|
263
|
+
// Verify the truncated content is exactly 10,000 A's (no collision with header text)
|
|
264
|
+
const aCount = (contextPart.match(/A/g) || []).length;
|
|
265
|
+
assert.equal(aCount, 10_000, "Should contain exactly 10,000 chars of original content");
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// ─── unknown step ID in contextFrom ─────────────────────────────────────
|
|
270
|
+
|
|
271
|
+
describe("unknown step in contextFrom", () => {
|
|
272
|
+
it("skips unknown step IDs gracefully", () => {
|
|
273
|
+
const def = makeDef([
|
|
274
|
+
{
|
|
275
|
+
id: "step-1",
|
|
276
|
+
name: "Research",
|
|
277
|
+
prompt: "Research",
|
|
278
|
+
requires: [],
|
|
279
|
+
produces: ["output.md"],
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
id: "step-2",
|
|
283
|
+
name: "Write",
|
|
284
|
+
prompt: "Write the report",
|
|
285
|
+
requires: ["step-1"],
|
|
286
|
+
produces: [],
|
|
287
|
+
contextFrom: ["step-1", "nonexistent-step"],
|
|
288
|
+
},
|
|
289
|
+
]);
|
|
290
|
+
|
|
291
|
+
const runDir = makeTempRun(def, {
|
|
292
|
+
"output.md": "Research content.",
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
const result = injectContext(runDir, "step-2", "Write the report");
|
|
296
|
+
// Should include step-1 content despite nonexistent-step being in contextFrom
|
|
297
|
+
assert.ok(result.includes("Research content."));
|
|
298
|
+
assert.ok(result.endsWith("Write the report"));
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// ─── error handling ─────────────────────────────────────────────────────
|
|
303
|
+
|
|
304
|
+
describe("error handling", () => {
|
|
305
|
+
it("throws when DEFINITION.yaml is missing", () => {
|
|
306
|
+
const runDir = mkdtempSync(join(tmpdir(), "ci-test-nodef-"));
|
|
307
|
+
|
|
308
|
+
assert.throws(
|
|
309
|
+
() => injectContext(runDir, "step-1", "Some prompt"),
|
|
310
|
+
/ENOENT/,
|
|
311
|
+
);
|
|
312
|
+
});
|
|
313
|
+
});
|
|
@@ -51,17 +51,17 @@ console.log('\n=== context-store: query all active decisions ===');
|
|
|
51
51
|
insertDecision({
|
|
52
52
|
id: 'D001', when_context: 'M001/S01', scope: 'architecture',
|
|
53
53
|
decision: 'use SQLite', choice: 'node:sqlite', rationale: 'built-in',
|
|
54
|
-
revisable: 'yes', superseded_by: 'D003', // superseded!
|
|
54
|
+
revisable: 'yes', made_by: 'agent', superseded_by: 'D003', // superseded!
|
|
55
55
|
});
|
|
56
56
|
insertDecision({
|
|
57
57
|
id: 'D002', when_context: 'M001/S01', scope: 'architecture',
|
|
58
58
|
decision: 'use WAL mode', choice: 'WAL', rationale: 'concurrent reads',
|
|
59
|
-
revisable: 'no', superseded_by: null,
|
|
59
|
+
revisable: 'no', made_by: 'agent', superseded_by: null,
|
|
60
60
|
});
|
|
61
61
|
insertDecision({
|
|
62
62
|
id: 'D003', when_context: 'M002/S01', scope: 'performance',
|
|
63
63
|
decision: 'use better-sqlite3', choice: 'better-sqlite3', rationale: 'faster',
|
|
64
|
-
revisable: 'yes', superseded_by: null,
|
|
64
|
+
revisable: 'yes', made_by: 'agent', superseded_by: null,
|
|
65
65
|
});
|
|
66
66
|
|
|
67
67
|
const all = queryDecisions();
|
|
@@ -81,11 +81,13 @@ console.log('\n=== context-store: query decisions by milestone ===');
|
|
|
81
81
|
insertDecision({
|
|
82
82
|
id: 'D001', when_context: 'M001/S01', scope: 'architecture',
|
|
83
83
|
decision: 'decision A', choice: 'A', rationale: 'r', revisable: 'yes',
|
|
84
|
+
made_by: 'agent',
|
|
84
85
|
superseded_by: null,
|
|
85
86
|
});
|
|
86
87
|
insertDecision({
|
|
87
88
|
id: 'D002', when_context: 'M002/S02', scope: 'architecture',
|
|
88
89
|
decision: 'decision B', choice: 'B', rationale: 'r', revisable: 'yes',
|
|
90
|
+
made_by: 'agent',
|
|
89
91
|
superseded_by: null,
|
|
90
92
|
});
|
|
91
93
|
|
|
@@ -107,11 +109,13 @@ console.log('\n=== context-store: query decisions by scope ===');
|
|
|
107
109
|
insertDecision({
|
|
108
110
|
id: 'D001', when_context: 'M001/S01', scope: 'architecture',
|
|
109
111
|
decision: 'decision A', choice: 'A', rationale: 'r', revisable: 'yes',
|
|
112
|
+
made_by: 'agent',
|
|
110
113
|
superseded_by: null,
|
|
111
114
|
});
|
|
112
115
|
insertDecision({
|
|
113
116
|
id: 'D002', when_context: 'M001/S01', scope: 'performance',
|
|
114
117
|
decision: 'decision B', choice: 'B', rationale: 'r', revisable: 'yes',
|
|
118
|
+
made_by: 'agent',
|
|
115
119
|
superseded_by: null,
|
|
116
120
|
});
|
|
117
121
|
|
|
@@ -248,12 +252,12 @@ console.log('\n=== context-store: formatDecisionsForPrompt ===');
|
|
|
248
252
|
{
|
|
249
253
|
seq: 1, id: 'D001', when_context: 'M001/S01', scope: 'architecture',
|
|
250
254
|
decision: 'use SQLite', choice: 'node:sqlite', rationale: 'built-in',
|
|
251
|
-
revisable: 'yes', superseded_by: null,
|
|
255
|
+
revisable: 'yes', made_by: 'agent', superseded_by: null,
|
|
252
256
|
},
|
|
253
257
|
{
|
|
254
258
|
seq: 2, id: 'D002', when_context: 'M001/S02', scope: 'performance',
|
|
255
259
|
decision: 'use WAL', choice: 'WAL', rationale: 'concurrent',
|
|
256
|
-
revisable: 'no', superseded_by: null,
|
|
260
|
+
revisable: 'no', made_by: 'human', superseded_by: null,
|
|
257
261
|
},
|
|
258
262
|
]);
|
|
259
263
|
|
|
@@ -323,6 +327,7 @@ console.log('\n=== context-store: sub-5ms query timing ===');
|
|
|
323
327
|
choice: `choice ${i}`,
|
|
324
328
|
rationale: `rationale ${i}`,
|
|
325
329
|
revisable: i % 3 === 0 ? 'no' : 'yes',
|
|
330
|
+
made_by: 'agent',
|
|
326
331
|
superseded_by: null,
|
|
327
332
|
});
|
|
328
333
|
}
|