gsd-pi 2.41.0-dev.cac69f9 → 2.42.0-dev.1df898f
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 +23 -0
- package/dist/cli.js +18 -3
- package/dist/loader.js +3 -1
- package/dist/resource-loader.js +39 -6
- package/dist/resources/extensions/async-jobs/async-bash-tool.js +52 -4
- package/dist/resources/extensions/async-jobs/await-tool.js +5 -0
- package/dist/resources/extensions/async-jobs/index.js +2 -0
- package/dist/resources/extensions/gsd/auto/loop.js +80 -0
- package/dist/resources/extensions/gsd/auto/phases.js +3 -5
- package/dist/resources/extensions/gsd/auto/session.js +6 -0
- package/dist/resources/extensions/gsd/auto-dashboard.js +2 -0
- package/dist/resources/extensions/gsd/auto-prompts.js +3 -16
- package/dist/resources/extensions/gsd/auto-start.js +8 -11
- package/dist/resources/extensions/gsd/auto.js +28 -1
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +11 -5
- package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +7 -2
- package/dist/resources/extensions/gsd/commands/catalog.js +32 -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/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/definition-loader.js +352 -0
- package/dist/resources/extensions/gsd/detection.js +19 -0
- 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-checks.js +31 -1
- package/dist/resources/extensions/gsd/doctor-providers.js +10 -0
- 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/forensics.js +84 -0
- package/dist/resources/extensions/gsd/git-constants.js +1 -0
- package/dist/resources/extensions/gsd/git-service.js +1 -1
- package/dist/resources/extensions/gsd/graph.js +225 -0
- package/dist/resources/extensions/gsd/native-git-bridge.js +1 -0
- package/dist/resources/extensions/gsd/preferences-types.js +1 -0
- package/dist/resources/extensions/gsd/preferences.js +59 -8
- package/dist/resources/extensions/gsd/prompts/forensics.md +12 -5
- package/dist/resources/extensions/gsd/repo-identity.js +46 -5
- package/dist/resources/extensions/gsd/run-manager.js +134 -0
- package/dist/resources/extensions/gsd/service-tier.js +13 -4
- package/dist/resources/extensions/gsd/session-lock.js +2 -2
- package/dist/resources/extensions/gsd/workflow-engine.js +7 -0
- package/dist/resources/extensions/gsd/worktree-resolver.js +2 -2
- package/dist/resources/extensions/gsd/worktree.js +2 -2
- package/dist/resources/extensions/mcp-client/index.js +2 -1
- package/dist/resources/extensions/search-the-web/tool-search.js +3 -3
- 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 +15 -15
- 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 +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/git/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 +15 -15
- package/dist/web/standalone/.next/server/chunks/229.js +2 -2
- 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-mode.d.ts +2 -0
- package/dist/web-mode.js +40 -4
- package/package.json +1 -1
- package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent.js +2 -0
- package/packages/pi-agent-core/dist/agent.js.map +1 -1
- package/packages/pi-agent-core/dist/types.d.ts +6 -0
- package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/types.js.map +1 -1
- package/packages/pi-agent-core/src/agent.test.ts +53 -0
- package/packages/pi-agent-core/src/agent.ts +3 -0
- package/packages/pi-agent-core/src/types.ts +6 -0
- package/packages/pi-agent-core/tsconfig.json +1 -1
- package/packages/pi-ai/dist/models.d.ts +5 -3
- package/packages/pi-ai/dist/models.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +801 -1468
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +1135 -1588
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/dist/models.js.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/github-copilot.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/github-copilot.js +60 -2
- package/packages/pi-ai/dist/utils/oauth/github-copilot.js.map +1 -1
- package/packages/pi-ai/scripts/generate-models.ts +1543 -0
- package/packages/pi-ai/src/models.generated.ts +1140 -1593
- package/packages/pi-ai/src/models.ts +7 -4
- package/packages/pi-ai/src/utils/oauth/github-copilot.ts +74 -2
- 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 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +7 -0
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js +29 -2
- package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js +60 -0
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +18 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/client.js +23 -0
- package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -1
- 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 -0
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/package-manager.d.ts +6 -0
- package/packages/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/package-manager.js +63 -11
- package/packages/pi-coding-agent/dist/core/package-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.d.ts +9 -0
- package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.js +20 -6
- package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js +6 -5
- package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-editor.js +3 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +9 -6
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.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 +30 -10
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +7 -1
- package/packages/pi-coding-agent/src/core/auth-storage.test.ts +68 -0
- package/packages/pi-coding-agent/src/core/auth-storage.ts +30 -2
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +18 -0
- package/packages/pi-coding-agent/src/core/lsp/client.ts +29 -0
- package/packages/pi-coding-agent/src/core/model-registry.ts +3 -0
- package/packages/pi-coding-agent/src/core/package-manager.ts +99 -58
- package/packages/pi-coding-agent/src/core/resource-loader.ts +24 -6
- package/packages/pi-coding-agent/src/core/system-prompt.ts +6 -5
- package/packages/pi-coding-agent/src/modes/interactive/components/extension-editor.ts +3 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +10 -6
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +31 -11
- package/pkg/package.json +1 -1
- package/src/resources/extensions/async-jobs/async-bash-timeout.test.ts +122 -0
- package/src/resources/extensions/async-jobs/async-bash-tool.ts +40 -4
- package/src/resources/extensions/async-jobs/await-tool.test.ts +47 -0
- package/src/resources/extensions/async-jobs/await-tool.ts +5 -0
- package/src/resources/extensions/async-jobs/index.ts +1 -0
- package/src/resources/extensions/async-jobs/job-manager.ts +2 -0
- package/src/resources/extensions/gsd/auto/loop-deps.ts +0 -1
- package/src/resources/extensions/gsd/auto/loop.ts +91 -0
- package/src/resources/extensions/gsd/auto/phases.ts +3 -5
- package/src/resources/extensions/gsd/auto/session.ts +6 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +2 -0
- package/src/resources/extensions/gsd/auto-prompts.ts +2 -18
- package/src/resources/extensions/gsd/auto-start.ts +7 -10
- package/src/resources/extensions/gsd/auto.ts +31 -1
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +13 -5
- package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +9 -2
- package/src/resources/extensions/gsd/commands/catalog.ts +32 -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/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/definition-loader.ts +462 -0
- package/src/resources/extensions/gsd/detection.ts +19 -0
- 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-checks.ts +32 -1
- package/src/resources/extensions/gsd/doctor-providers.ts +13 -0
- package/src/resources/extensions/gsd/doctor-types.ts +1 -0
- 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/forensics.ts +92 -0
- package/src/resources/extensions/gsd/git-constants.ts +1 -0
- package/src/resources/extensions/gsd/git-service.ts +0 -1
- package/src/resources/extensions/gsd/gitignore.ts +1 -1
- package/src/resources/extensions/gsd/graph.ts +312 -0
- package/src/resources/extensions/gsd/native-git-bridge.ts +1 -0
- package/src/resources/extensions/gsd/preferences-types.ts +3 -0
- package/src/resources/extensions/gsd/preferences.ts +62 -6
- package/src/resources/extensions/gsd/prompts/forensics.md +12 -5
- package/src/resources/extensions/gsd/repo-identity.ts +48 -5
- package/src/resources/extensions/gsd/run-manager.ts +180 -0
- package/src/resources/extensions/gsd/service-tier.ts +17 -4
- package/src/resources/extensions/gsd/session-lock.ts +2 -2
- package/src/resources/extensions/gsd/tests/activity-log.test.ts +31 -69
- package/src/resources/extensions/gsd/tests/bundled-workflow-defs.test.ts +180 -0
- 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/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/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/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/forensics-dedup.test.ts +48 -0
- package/src/resources/extensions/gsd/tests/forensics-issue-routing.test.ts +43 -0
- package/src/resources/extensions/gsd/tests/git-locale.test.ts +133 -0
- package/src/resources/extensions/gsd/tests/git-service.test.ts +44 -0
- package/src/resources/extensions/gsd/tests/graph-operations.test.ts +599 -0
- package/src/resources/extensions/gsd/tests/iterate-engine-integration.test.ts +429 -0
- package/src/resources/extensions/gsd/tests/journal.test.ts +82 -127
- package/src/resources/extensions/gsd/tests/manifest-status.test.ts +73 -82
- package/src/resources/extensions/gsd/tests/run-manager.test.ts +229 -0
- package/src/resources/extensions/gsd/tests/service-tier.test.ts +30 -1
- package/src/resources/extensions/gsd/tests/skill-activation.test.ts +56 -3
- package/src/resources/extensions/gsd/tests/symlink-numbered-variants.test.ts +151 -0
- package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +45 -0
- package/src/resources/extensions/gsd/tests/verification-gate.test.ts +156 -263
- package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +35 -78
- package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +81 -74
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +1 -2
- package/src/resources/extensions/gsd/workflow-engine.ts +38 -0
- package/src/resources/extensions/gsd/worktree-resolver.ts +2 -3
- package/src/resources/extensions/gsd/worktree.ts +2 -2
- package/src/resources/extensions/mcp-client/index.ts +5 -1
- package/src/resources/extensions/search-the-web/tool-search.ts +3 -3
- 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/{EnGUNqHeGbE0tuuUkTJVA → qw8qDHXOTLUXBq1vEknSz}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{EnGUNqHeGbE0tuuUkTJVA → qw8qDHXOTLUXBq1vEknSz}/_ssgManifest.js +0 -0
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { createHash } from "node:crypto";
|
|
9
9
|
import { execFileSync } from "node:child_process";
|
|
10
|
-
import { existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
|
|
10
|
+
import { existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, realpathSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
|
|
11
11
|
import { homedir } from "node:os";
|
|
12
12
|
import { basename, dirname, join, resolve } from "node:path";
|
|
13
13
|
const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
|
|
@@ -247,14 +247,52 @@ export function externalProjectsRoot() {
|
|
|
247
247
|
const base = process.env.GSD_STATE_DIR || gsdHome;
|
|
248
248
|
return join(base, "projects");
|
|
249
249
|
}
|
|
250
|
+
// ─── Numbered Variant Cleanup ────────────────────────────────────────────────
|
|
251
|
+
/**
|
|
252
|
+
* macOS collision pattern: `.gsd 2`, `.gsd 3`, `.gsd 4`, etc.
|
|
253
|
+
*
|
|
254
|
+
* When `symlinkSync` (or Finder) tries to create `.gsd` but a real directory
|
|
255
|
+
* already exists at that path, macOS APFS silently renames the new entry to
|
|
256
|
+
* `.gsd 2`, then `.gsd 3`, and so on. These numbered variants confuse GSD
|
|
257
|
+
* because the canonical `.gsd` path no longer resolves to the external state
|
|
258
|
+
* directory, making tracked planning files appear deleted.
|
|
259
|
+
*
|
|
260
|
+
* This helper scans the project root for entries matching `.gsd <digits>` and
|
|
261
|
+
* removes them. It is called early in `ensureGsdSymlink()` so that the
|
|
262
|
+
* canonical `.gsd` path is always the one in use.
|
|
263
|
+
*/
|
|
264
|
+
const GSD_NUMBERED_VARIANT_RE = /^\.gsd \d+$/;
|
|
265
|
+
export function cleanNumberedGsdVariants(projectPath) {
|
|
266
|
+
const removed = [];
|
|
267
|
+
try {
|
|
268
|
+
const entries = readdirSync(projectPath);
|
|
269
|
+
for (const entry of entries) {
|
|
270
|
+
if (GSD_NUMBERED_VARIANT_RE.test(entry)) {
|
|
271
|
+
const fullPath = join(projectPath, entry);
|
|
272
|
+
try {
|
|
273
|
+
rmSync(fullPath, { recursive: true, force: true });
|
|
274
|
+
removed.push(entry);
|
|
275
|
+
}
|
|
276
|
+
catch {
|
|
277
|
+
// Best-effort: if removal fails (e.g. permissions), continue with next
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
catch {
|
|
283
|
+
// Non-fatal: readdir failure should not block symlink creation
|
|
284
|
+
}
|
|
285
|
+
return removed;
|
|
286
|
+
}
|
|
250
287
|
// ─── Symlink Management ─────────────────────────────────────────────────────
|
|
251
288
|
/**
|
|
252
289
|
* Ensure the `<project>/.gsd` symlink points to the external state directory.
|
|
253
290
|
*
|
|
254
|
-
* 1.
|
|
255
|
-
* 2.
|
|
256
|
-
* 3. If `<project>/.gsd`
|
|
257
|
-
* 4. If `<project>/.gsd` is
|
|
291
|
+
* 1. Clean up any macOS numbered collision variants (`.gsd 2`, `.gsd 3`, etc.)
|
|
292
|
+
* 2. mkdir -p the external dir
|
|
293
|
+
* 3. If `<project>/.gsd` doesn't exist → create symlink
|
|
294
|
+
* 4. If `<project>/.gsd` is already the correct symlink → no-op
|
|
295
|
+
* 5. If `<project>/.gsd` is a real directory → return as-is (migration handles later)
|
|
258
296
|
*
|
|
259
297
|
* Returns the resolved external path.
|
|
260
298
|
*/
|
|
@@ -270,6 +308,9 @@ export function ensureGsdSymlink(projectPath) {
|
|
|
270
308
|
if (localGsdNormalized === gsdHomePath) {
|
|
271
309
|
return localGsd;
|
|
272
310
|
}
|
|
311
|
+
// Clean up macOS numbered collision variants (.gsd 2, .gsd 3, etc.) before
|
|
312
|
+
// any existence checks — otherwise they accumulate and confuse state (#2205).
|
|
313
|
+
cleanNumberedGsdVariants(projectPath);
|
|
273
314
|
// Ensure external directory exists
|
|
274
315
|
mkdirSync(externalPath, { recursive: true });
|
|
275
316
|
// Write repo metadata once so cleanup commands can identify this directory later.
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* run-manager.ts — Create and list isolated workflow run directories.
|
|
3
|
+
*
|
|
4
|
+
* Each run lives under `.gsd/workflow-runs/<name>/<timestamp>/` and contains:
|
|
5
|
+
* - DEFINITION.yaml — frozen snapshot of the workflow definition at run-creation time
|
|
6
|
+
* - GRAPH.yaml — initialized step graph with all steps pending
|
|
7
|
+
* - PARAMS.json — (optional) parameter overrides used for this run
|
|
8
|
+
*
|
|
9
|
+
* Observability:
|
|
10
|
+
* - All run state is on disk in human-readable YAML/JSON — inspectable with cat/less.
|
|
11
|
+
* - `listRuns()` returns structured metadata including step counts and overall status.
|
|
12
|
+
* - Timestamp directory names are filesystem-safe (ISO with hyphens replacing colons).
|
|
13
|
+
* - Errors include the full path context for diagnosis.
|
|
14
|
+
*/
|
|
15
|
+
import { mkdirSync, writeFileSync, existsSync, readdirSync, statSync } from "node:fs";
|
|
16
|
+
import { join } from "node:path";
|
|
17
|
+
import { stringify } from "yaml";
|
|
18
|
+
import { loadDefinition, substituteParams } from "./definition-loader.js";
|
|
19
|
+
import { initializeGraph, writeGraph, readGraph } from "./graph.js";
|
|
20
|
+
// ─── Constants ───────────────────────────────────────────────────────────
|
|
21
|
+
const RUNS_DIR = "workflow-runs";
|
|
22
|
+
const DEFS_DIR = "workflow-defs";
|
|
23
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────
|
|
24
|
+
/**
|
|
25
|
+
* Generate a filesystem-safe timestamp: `YYYY-MM-DDTHH-MM-SS`.
|
|
26
|
+
* Replaces colons with hyphens so the string is safe as a directory name
|
|
27
|
+
* on all platforms (Windows forbids colons in paths).
|
|
28
|
+
*/
|
|
29
|
+
function makeTimestamp(date = new Date()) {
|
|
30
|
+
return date.toISOString().replace(/:/g, "-").replace(/\.\d{3}Z$/, "");
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Derive overall status from a graph's step statuses.
|
|
34
|
+
*/
|
|
35
|
+
function deriveStatus(graph) {
|
|
36
|
+
const hasActive = graph.steps.some((s) => s.status === "active");
|
|
37
|
+
const allDone = graph.steps.every((s) => s.status === "complete" || s.status === "expanded");
|
|
38
|
+
if (allDone)
|
|
39
|
+
return "complete";
|
|
40
|
+
if (hasActive)
|
|
41
|
+
return "running";
|
|
42
|
+
return "pending";
|
|
43
|
+
}
|
|
44
|
+
// ─── Public API ──────────────────────────────────────────────────────────
|
|
45
|
+
/**
|
|
46
|
+
* Create a new isolated run directory for a workflow definition.
|
|
47
|
+
*
|
|
48
|
+
* 1. Loads the definition from `<basePath>/.gsd/workflow-defs/<defName>.yaml`
|
|
49
|
+
* 2. Applies parameter substitution if overrides are provided
|
|
50
|
+
* 3. Creates `<basePath>/.gsd/workflow-runs/<defName>/<timestamp>/`
|
|
51
|
+
* 4. Writes frozen DEFINITION.yaml, initialized GRAPH.yaml, and optional PARAMS.json
|
|
52
|
+
*
|
|
53
|
+
* @param basePath — project root directory
|
|
54
|
+
* @param defName — definition filename (without .yaml extension)
|
|
55
|
+
* @param overrides — optional parameter overrides (merged with definition defaults)
|
|
56
|
+
* @returns Full path to the created run directory
|
|
57
|
+
* @throws Error if the definition file doesn't exist or is invalid
|
|
58
|
+
*/
|
|
59
|
+
export function createRun(basePath, defName, overrides) {
|
|
60
|
+
const defsDir = join(basePath, ".gsd", DEFS_DIR);
|
|
61
|
+
// Load and validate the definition
|
|
62
|
+
const rawDef = loadDefinition(defsDir, defName);
|
|
63
|
+
// Apply parameter substitution if overrides provided
|
|
64
|
+
const def = overrides
|
|
65
|
+
? substituteParams(rawDef, overrides)
|
|
66
|
+
: substituteParams(rawDef); // still resolve default params if any
|
|
67
|
+
// Create the run directory
|
|
68
|
+
const timestamp = makeTimestamp();
|
|
69
|
+
const runDir = join(basePath, ".gsd", RUNS_DIR, defName, timestamp);
|
|
70
|
+
mkdirSync(runDir, { recursive: true });
|
|
71
|
+
// Freeze the definition as DEFINITION.yaml
|
|
72
|
+
writeFileSync(join(runDir, "DEFINITION.yaml"), stringify(def), "utf-8");
|
|
73
|
+
// Initialize and write GRAPH.yaml
|
|
74
|
+
const graph = initializeGraph(def);
|
|
75
|
+
writeGraph(runDir, graph);
|
|
76
|
+
// Write PARAMS.json if overrides were provided
|
|
77
|
+
if (overrides && Object.keys(overrides).length > 0) {
|
|
78
|
+
writeFileSync(join(runDir, "PARAMS.json"), JSON.stringify(overrides, null, 2), "utf-8");
|
|
79
|
+
}
|
|
80
|
+
return runDir;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* List existing workflow runs with metadata.
|
|
84
|
+
*
|
|
85
|
+
* Scans `<basePath>/.gsd/workflow-runs/` for run directories. Each run's
|
|
86
|
+
* GRAPH.yaml is read to derive step counts and overall status.
|
|
87
|
+
*
|
|
88
|
+
* @param basePath — project root directory
|
|
89
|
+
* @param defName — optional filter: only list runs for this definition name
|
|
90
|
+
* @returns Array of run metadata, sorted newest-first within each definition
|
|
91
|
+
*/
|
|
92
|
+
export function listRuns(basePath, defName) {
|
|
93
|
+
const runsRoot = join(basePath, ".gsd", RUNS_DIR);
|
|
94
|
+
if (!existsSync(runsRoot))
|
|
95
|
+
return [];
|
|
96
|
+
const results = [];
|
|
97
|
+
// Get workflow name directories
|
|
98
|
+
const nameDirs = defName ? [defName] : readdirSync(runsRoot).filter((entry) => {
|
|
99
|
+
const full = join(runsRoot, entry);
|
|
100
|
+
return statSync(full).isDirectory();
|
|
101
|
+
});
|
|
102
|
+
for (const name of nameDirs) {
|
|
103
|
+
const nameDir = join(runsRoot, name);
|
|
104
|
+
if (!existsSync(nameDir))
|
|
105
|
+
continue;
|
|
106
|
+
const timestamps = readdirSync(nameDir).filter((entry) => {
|
|
107
|
+
const full = join(nameDir, entry);
|
|
108
|
+
return statSync(full).isDirectory();
|
|
109
|
+
});
|
|
110
|
+
// Sort newest-first (ISO strings sort lexicographically)
|
|
111
|
+
timestamps.sort().reverse();
|
|
112
|
+
for (const ts of timestamps) {
|
|
113
|
+
const runDir = join(nameDir, ts);
|
|
114
|
+
try {
|
|
115
|
+
const graph = readGraph(runDir);
|
|
116
|
+
const total = graph.steps.length;
|
|
117
|
+
const completed = graph.steps.filter((s) => s.status === "complete").length;
|
|
118
|
+
const pending = graph.steps.filter((s) => s.status === "pending").length;
|
|
119
|
+
const active = graph.steps.filter((s) => s.status === "active").length;
|
|
120
|
+
results.push({
|
|
121
|
+
name,
|
|
122
|
+
timestamp: ts,
|
|
123
|
+
runDir,
|
|
124
|
+
steps: { total, completed, pending, active },
|
|
125
|
+
status: deriveStatus(graph),
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
// Skip runs with invalid/missing GRAPH.yaml
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return results;
|
|
134
|
+
}
|
|
@@ -11,6 +11,7 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
11
11
|
import { saveFile } from "./files.js";
|
|
12
12
|
import { getGlobalGSDPreferencesPath, loadEffectiveGSDPreferences, loadGlobalGSDPreferences, } from "./preferences.js";
|
|
13
13
|
import { ensurePreferencesFile, serializePreferencesToFrontmatter } from "./commands-prefs-wizard.js";
|
|
14
|
+
const SERVICE_TIER_SCOPE_NOTE = "Only affects gpt-5.4 models, regardless of provider.";
|
|
14
15
|
// ─── Gating ──────────────────────────────────────────────────────────────────
|
|
15
16
|
/**
|
|
16
17
|
* Returns true when the given model ID supports OpenAI service tiers.
|
|
@@ -37,7 +38,7 @@ export function formatServiceTierStatus(tier) {
|
|
|
37
38
|
" /gsd fast flex Set to flex (0.5x cost, slower)",
|
|
38
39
|
" /gsd fast off Disable service tier",
|
|
39
40
|
"",
|
|
40
|
-
|
|
41
|
+
SERVICE_TIER_SCOPE_NOTE,
|
|
41
42
|
].join("\n");
|
|
42
43
|
}
|
|
43
44
|
const label = tier === "priority" ? "priority (2x cost, faster)" : "flex (0.5x cost, slower)";
|
|
@@ -49,9 +50,14 @@ export function formatServiceTierStatus(tier) {
|
|
|
49
50
|
" /gsd fast flex Set to flex (0.5x cost, slower)",
|
|
50
51
|
" /gsd fast off Disable service tier",
|
|
51
52
|
"",
|
|
52
|
-
|
|
53
|
+
SERVICE_TIER_SCOPE_NOTE,
|
|
53
54
|
].join("\n");
|
|
54
55
|
}
|
|
56
|
+
export function formatServiceTierFooterStatus(tier, modelId) {
|
|
57
|
+
if (!tier || !modelId || !supportsServiceTier(modelId))
|
|
58
|
+
return undefined;
|
|
59
|
+
return tier === "priority" ? "fast: ⚡ priority" : "fast: 💰 flex";
|
|
60
|
+
}
|
|
55
61
|
// ─── Icon Resolution ─────────────────────────────────────────────────────────
|
|
56
62
|
/**
|
|
57
63
|
* Returns the appropriate icon for the active service tier and model.
|
|
@@ -121,17 +127,20 @@ export async function handleFast(args, ctx) {
|
|
|
121
127
|
}
|
|
122
128
|
if (trimmed === "on") {
|
|
123
129
|
await writeGlobalServiceTier(ctx, "priority");
|
|
124
|
-
ctx.ui.
|
|
130
|
+
ctx.ui.setStatus("gsd-fast", formatServiceTierFooterStatus("priority", ctx.model?.id));
|
|
131
|
+
ctx.ui.notify("Service tier set to priority (2x cost, faster responses). Only affects gpt-5.4 models, regardless of provider.", "info");
|
|
125
132
|
return;
|
|
126
133
|
}
|
|
127
134
|
if (trimmed === "off") {
|
|
128
135
|
await writeGlobalServiceTier(ctx, undefined);
|
|
136
|
+
ctx.ui.setStatus("gsd-fast", undefined);
|
|
129
137
|
ctx.ui.notify("Service tier disabled.", "info");
|
|
130
138
|
return;
|
|
131
139
|
}
|
|
132
140
|
if (trimmed === "flex") {
|
|
133
141
|
await writeGlobalServiceTier(ctx, "flex");
|
|
134
|
-
ctx.ui.
|
|
142
|
+
ctx.ui.setStatus("gsd-fast", formatServiceTierFooterStatus("flex", ctx.model?.id));
|
|
143
|
+
ctx.ui.notify("Service tier set to flex (0.5x cost, slower responses). Only affects gpt-5.4 models, regardless of provider.", "info");
|
|
135
144
|
return;
|
|
136
145
|
}
|
|
137
146
|
ctx.ui.notify("Usage: /gsd fast [on|off|flex|status]\n\n on Priority tier (2x cost, faster)\n off Disable service tier\n flex Flex tier (0.5x cost, slower)\n status Show current setting", "warning");
|
|
@@ -199,7 +199,7 @@ export function acquireSessionLock(basePath) {
|
|
|
199
199
|
// during a long LLM call — not a real takeover. Log and continue.
|
|
200
200
|
const elapsed = Date.now() - _lockAcquiredAt;
|
|
201
201
|
if (elapsed < 1_800_000) {
|
|
202
|
-
process.stderr.write(`[gsd] Lock heartbeat
|
|
202
|
+
process.stderr.write(`[gsd] Lock heartbeat caught up after ${Math.round(elapsed / 1000)}s — long LLM call, no action needed.\n`);
|
|
203
203
|
return; // Suppress false positive
|
|
204
204
|
}
|
|
205
205
|
// Past the stale window — check if the lock file still belongs to us before
|
|
@@ -252,7 +252,7 @@ export function acquireSessionLock(basePath) {
|
|
|
252
252
|
// on benign mtime drift (laptop sleep, heavy LLM event loop stalls).
|
|
253
253
|
const elapsed = Date.now() - _lockAcquiredAt;
|
|
254
254
|
if (elapsed < 1_800_000) {
|
|
255
|
-
process.stderr.write(`[gsd] Lock heartbeat
|
|
255
|
+
process.stderr.write(`[gsd] Lock heartbeat caught up after ${Math.round(elapsed / 1000)}s — long LLM call, no action needed.\n`);
|
|
256
256
|
return;
|
|
257
257
|
}
|
|
258
258
|
// Check PID ownership before declaring compromise (#1578)
|
|
@@ -286,9 +286,9 @@ export class WorktreeResolver {
|
|
|
286
286
|
});
|
|
287
287
|
// Surface a clear, actionable error. The worktree and milestone branch are
|
|
288
288
|
// intentionally preserved — nothing has been deleted. The user can retry
|
|
289
|
-
// /complete-milestone or merge manually once the underlying issue is fixed
|
|
289
|
+
// /gsd dispatch complete-milestone or merge manually once the underlying issue is fixed
|
|
290
290
|
// (e.g. checkout to wrong branch, unresolved conflicts). (#1668)
|
|
291
|
-
ctx.notify(`Milestone merge failed: ${msg}. Your worktree and milestone branch are preserved — retry /complete-milestone or merge manually.`, "warning");
|
|
291
|
+
ctx.notify(`Milestone merge failed: ${msg}. Your worktree and milestone branch are preserved — retry /gsd dispatch complete-milestone or merge manually.`, "warning");
|
|
292
292
|
// Clean up stale merge state left by failed squash-merge (#1389)
|
|
293
293
|
try {
|
|
294
294
|
const gitDir = join(originalBase || this.s.basePath, ".git");
|
|
@@ -48,14 +48,14 @@ export function setActiveMilestoneId(basePath, milestoneId) {
|
|
|
48
48
|
* record when the user starts from a different branch (#300). Always a no-op
|
|
49
49
|
* if on a GSD slice branch.
|
|
50
50
|
*/
|
|
51
|
-
export function captureIntegrationBranch(basePath, milestoneId
|
|
51
|
+
export function captureIntegrationBranch(basePath, milestoneId) {
|
|
52
52
|
// In a worktree, the base branch is implicit (worktree/<name>).
|
|
53
53
|
// Writing it to META.json would leave stale metadata after merge back to main.
|
|
54
54
|
if (detectWorktreeName(basePath))
|
|
55
55
|
return;
|
|
56
56
|
const svc = getService(basePath);
|
|
57
57
|
const current = svc.getCurrentBranch();
|
|
58
|
-
writeIntegrationBranch(basePath, milestoneId, current
|
|
58
|
+
writeIntegrationBranch(basePath, milestoneId, current);
|
|
59
59
|
}
|
|
60
60
|
// ─── Pure Utility Functions (unchanged) ────────────────────────────────────
|
|
61
61
|
/**
|
|
@@ -108,7 +108,8 @@ async function getOrConnect(name, signal) {
|
|
|
108
108
|
});
|
|
109
109
|
}
|
|
110
110
|
else if (config.transport === "http" && config.url) {
|
|
111
|
-
|
|
111
|
+
const resolvedUrl = config.url.replace(/\$\{([^}]+)\}/g, (_, name) => process.env[name] ?? "");
|
|
112
|
+
transport = new StreamableHTTPClientTransport(new URL(resolvedUrl));
|
|
112
113
|
}
|
|
113
114
|
else {
|
|
114
115
|
throw new Error(`Server "${name}" has unsupported transport: ${config.transport}`);
|
|
@@ -259,9 +259,9 @@ export function registerSearchTool(pi) {
|
|
|
259
259
|
// with brief interruptions every MAX_CONSECUTIVE_DUPES+1 calls.
|
|
260
260
|
if (cacheKey === lastSearchKey) {
|
|
261
261
|
consecutiveDupeCount++;
|
|
262
|
-
if (consecutiveDupeCount
|
|
262
|
+
if (consecutiveDupeCount > MAX_CONSECUTIVE_DUPES) {
|
|
263
263
|
return {
|
|
264
|
-
content: [{ type: "text", text: `⚠️ Search loop detected: the query "${params.query}" has been searched ${consecutiveDupeCount
|
|
264
|
+
content: [{ type: "text", text: `⚠️ Search loop detected: the query "${params.query}" has been searched ${consecutiveDupeCount} times consecutively with identical results. The information you need is already in the previous search results above. Stop searching and use those results to proceed with your task.` }],
|
|
265
265
|
isError: true,
|
|
266
266
|
details: { errorKind: "search_loop", error: "Consecutive duplicate search detected" },
|
|
267
267
|
};
|
|
@@ -269,7 +269,7 @@ export function registerSearchTool(pi) {
|
|
|
269
269
|
}
|
|
270
270
|
else {
|
|
271
271
|
lastSearchKey = cacheKey;
|
|
272
|
-
consecutiveDupeCount =
|
|
272
|
+
consecutiveDupeCount = 1;
|
|
273
273
|
}
|
|
274
274
|
const cached = searchCache.get(cacheKey);
|
|
275
275
|
if (cached) {
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-workflow
|
|
3
|
+
description: Conversational guide for creating valid YAML workflow definitions. Use when asked to "create a workflow", "new workflow definition", "build a workflow", "workflow YAML", "define workflow steps", or "workflow from template".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<essential_principles>
|
|
7
|
+
You are a workflow definition author. You help users create valid V1 YAML workflow definitions that the GSD workflow engine can execute.
|
|
8
|
+
|
|
9
|
+
**V1 Schema Basics:**
|
|
10
|
+
|
|
11
|
+
- Every definition requires `version: 1`, a non-empty `name`, and at least one step in `steps[]`.
|
|
12
|
+
- Optional top-level fields: `description` (string), `params` (key-value defaults for `{{ key }}` substitution).
|
|
13
|
+
- Each step requires: `id` (unique string), `name` (non-empty string), `prompt` (non-empty string).
|
|
14
|
+
- Each step optionally has: `requires` or `depends_on` (array of step IDs), `produces` (array of artifact paths), `context_from` (array of step IDs), `verify` (verification policy object), `iterate` (fan-out config object).
|
|
15
|
+
- YAML uses **snake_case** keys: `depends_on`, `context_from`. The engine converts to camelCase internally.
|
|
16
|
+
|
|
17
|
+
**Validation Rules:**
|
|
18
|
+
|
|
19
|
+
- Step IDs must be unique across the workflow.
|
|
20
|
+
- Dependencies (`requires`/`depends_on`) must reference existing step IDs — no dangling refs.
|
|
21
|
+
- A step cannot depend on itself.
|
|
22
|
+
- The dependency graph must be acyclic (no circular dependencies).
|
|
23
|
+
- `produces` paths must not contain `..` (path traversal rejected).
|
|
24
|
+
- `iterate.source` must not contain `..` (path traversal rejected).
|
|
25
|
+
- `iterate.pattern` must be a valid regex with at least one capture group.
|
|
26
|
+
|
|
27
|
+
**Four Verification Policies:**
|
|
28
|
+
|
|
29
|
+
1. `content-heuristic` — Checks artifact content. Optional: `minSize` (number), `pattern` (string).
|
|
30
|
+
2. `shell-command` — Runs a shell command. Required: `command` (non-empty string).
|
|
31
|
+
3. `prompt-verify` — Asks an LLM to verify. Required: `prompt` (non-empty string).
|
|
32
|
+
4. `human-review` — Pauses for human approval. No extra fields required.
|
|
33
|
+
|
|
34
|
+
**Parameter Substitution:**
|
|
35
|
+
|
|
36
|
+
- Define defaults in top-level `params: { key: "default_value" }`.
|
|
37
|
+
- Use `{{ key }}` placeholders in step prompts — the engine replaces them at runtime.
|
|
38
|
+
- CLI overrides take precedence over definition defaults.
|
|
39
|
+
- Parameter values must not contain `..` (path traversal guard).
|
|
40
|
+
- Any unresolved `{{ key }}` after substitution causes an error.
|
|
41
|
+
|
|
42
|
+
**Path Traversal Guard:**
|
|
43
|
+
|
|
44
|
+
- The engine rejects any `produces` path or `iterate.source` containing `..`.
|
|
45
|
+
- Parameter values are also checked for `..` during substitution.
|
|
46
|
+
|
|
47
|
+
**Output Location:**
|
|
48
|
+
|
|
49
|
+
- Finished definitions go in `.gsd/workflow-defs/<name>.yaml`.
|
|
50
|
+
- After writing, tell the user to validate with `/gsd workflow validate <name>`.
|
|
51
|
+
</essential_principles>
|
|
52
|
+
|
|
53
|
+
<routing>
|
|
54
|
+
Determine the user's intent and route to the appropriate workflow:
|
|
55
|
+
|
|
56
|
+
**"I want to create a workflow from scratch" / "new workflow" / "build a workflow":**
|
|
57
|
+
→ Read `workflows/create-from-scratch.md` and follow it.
|
|
58
|
+
|
|
59
|
+
**"I want to start from a template" / "from an example" / "customize a template":**
|
|
60
|
+
→ Read `workflows/create-from-template.md` and follow it.
|
|
61
|
+
|
|
62
|
+
**"Help me understand the schema" / "what fields are available?":**
|
|
63
|
+
→ Read `references/yaml-schema-v1.md` and explain the relevant parts.
|
|
64
|
+
|
|
65
|
+
**"How does verification work?" / "verify policies":**
|
|
66
|
+
→ Read `references/verification-policies.md` and explain.
|
|
67
|
+
|
|
68
|
+
**"How do I use context_from / iterate / params?":**
|
|
69
|
+
→ Read `references/feature-patterns.md` and explain the relevant feature.
|
|
70
|
+
|
|
71
|
+
**If intent is unclear, ask one clarifying question:**
|
|
72
|
+
- "Do you want to create a workflow from scratch, or start from an existing template?"
|
|
73
|
+
- Then route based on the answer.
|
|
74
|
+
</routing>
|
|
75
|
+
|
|
76
|
+
<reference_index>
|
|
77
|
+
Read these files when you need detailed schema knowledge during workflow authoring:
|
|
78
|
+
|
|
79
|
+
- `references/yaml-schema-v1.md` — Complete field-by-field V1 schema reference. Read when you need to explain any field's type, constraints, or defaults.
|
|
80
|
+
- `references/verification-policies.md` — All four verify policies with complete YAML examples. Read when helping the user choose or configure verification for a step.
|
|
81
|
+
- `references/feature-patterns.md` — Usage patterns for `context_from`, `iterate`, and `params` with complete YAML examples. Read when the user wants context chaining, fan-out iteration, or parameterized workflows.
|
|
82
|
+
</reference_index>
|
|
83
|
+
|
|
84
|
+
<templates_index>
|
|
85
|
+
Available templates in `templates/`:
|
|
86
|
+
|
|
87
|
+
- `workflow-definition.yaml` — Blank scaffold with all fields shown as comments. Copy and fill for a quick start.
|
|
88
|
+
- `blog-post-pipeline.yaml` — Linear chain with params and content-heuristic verification.
|
|
89
|
+
- `code-audit.yaml` — Iterate-based fan-out with shell-command verification.
|
|
90
|
+
- `release-checklist.yaml` — Diamond dependency graph with human-review verification.
|
|
91
|
+
</templates_index>
|
|
92
|
+
|
|
93
|
+
<output_conventions>
|
|
94
|
+
When assembling the final YAML:
|
|
95
|
+
|
|
96
|
+
1. Use 2-space indentation consistently.
|
|
97
|
+
2. Quote string values that contain special YAML characters (`:`, `{`, `}`, `[`, `]`, `#`).
|
|
98
|
+
3. Always include `version: 1` as the first field.
|
|
99
|
+
4. Order top-level fields: `version`, `name`, `description`, `params`, `steps`.
|
|
100
|
+
5. Order step fields: `id`, `name`, `prompt`, `requires`, `produces`, `context_from`, `verify`, `iterate`.
|
|
101
|
+
6. Write the file to `.gsd/workflow-defs/<name>.yaml`.
|
|
102
|
+
7. After writing, tell the user: "Run `/gsd workflow validate <name>` to check the definition."
|
|
103
|
+
</output_conventions>
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
<feature_patterns>
|
|
2
|
+
Advanced workflow features: `context_from`, `iterate`, and `params`. Each section includes a complete YAML example.
|
|
3
|
+
|
|
4
|
+
**Feature 1: `context_from` — Context Chaining**
|
|
5
|
+
|
|
6
|
+
Injects artifacts from prior steps as context when the current step runs. The value is an array of step IDs.
|
|
7
|
+
|
|
8
|
+
```yaml
|
|
9
|
+
version: 1
|
|
10
|
+
name: research-and-synthesize
|
|
11
|
+
steps:
|
|
12
|
+
- id: gather
|
|
13
|
+
name: Gather sources
|
|
14
|
+
prompt: "Find and summarize the top 5 sources on the topic."
|
|
15
|
+
produces:
|
|
16
|
+
- sources.md
|
|
17
|
+
|
|
18
|
+
- id: analyze
|
|
19
|
+
name: Analyze sources
|
|
20
|
+
prompt: "Analyze the gathered sources for key themes."
|
|
21
|
+
requires:
|
|
22
|
+
- gather
|
|
23
|
+
context_from:
|
|
24
|
+
- gather
|
|
25
|
+
produces:
|
|
26
|
+
- analysis.md
|
|
27
|
+
|
|
28
|
+
- id: synthesize
|
|
29
|
+
name: Write synthesis
|
|
30
|
+
prompt: "Synthesize the analysis into a coherent report."
|
|
31
|
+
requires:
|
|
32
|
+
- analyze
|
|
33
|
+
context_from:
|
|
34
|
+
- gather
|
|
35
|
+
- analyze
|
|
36
|
+
produces:
|
|
37
|
+
- report.md
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
How it works:
|
|
41
|
+
- `context_from: [gather]` means the engine includes artifacts from the `gather` step when executing `analyze`.
|
|
42
|
+
- You can reference multiple prior steps: `context_from: [gather, analyze]`.
|
|
43
|
+
- The referenced steps must exist in the workflow (they are validated as step IDs).
|
|
44
|
+
- `context_from` does not imply a dependency — if you want the step to wait, also add the ID to `requires`.
|
|
45
|
+
|
|
46
|
+
**Feature 2: `iterate` — Fan-Out Iteration**
|
|
47
|
+
|
|
48
|
+
Reads an artifact, applies a regex pattern, and creates one sub-execution per match. The capture group extracts the iteration variable.
|
|
49
|
+
|
|
50
|
+
```yaml
|
|
51
|
+
version: 1
|
|
52
|
+
name: file-by-file-review
|
|
53
|
+
steps:
|
|
54
|
+
- id: inventory
|
|
55
|
+
name: List files to review
|
|
56
|
+
prompt: "List all TypeScript files in src/ that need review, one per line."
|
|
57
|
+
produces:
|
|
58
|
+
- file-list.txt
|
|
59
|
+
|
|
60
|
+
- id: review
|
|
61
|
+
name: Review each file
|
|
62
|
+
prompt: "Review the file for code quality issues."
|
|
63
|
+
requires:
|
|
64
|
+
- inventory
|
|
65
|
+
iterate:
|
|
66
|
+
source: file-list.txt
|
|
67
|
+
pattern: "^(.+\\.ts)$"
|
|
68
|
+
produces:
|
|
69
|
+
- reviews/
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
How it works:
|
|
73
|
+
- `source`: Path to an artifact (relative to the run directory). Must not contain `..`.
|
|
74
|
+
- `pattern`: A regex string applied with the global flag. Must contain at least one capture group `(...)`.
|
|
75
|
+
- The engine reads the source artifact, applies the pattern, and creates one execution per match.
|
|
76
|
+
- Each capture group match becomes available as the iteration variable.
|
|
77
|
+
- The regex is validated at definition-load time — invalid regex or missing capture groups are rejected.
|
|
78
|
+
|
|
79
|
+
Pattern requirements:
|
|
80
|
+
- Must be a valid JavaScript regex.
|
|
81
|
+
- Must contain at least one non-lookahead capture group: `(...)` not `(?:...)`.
|
|
82
|
+
- Example valid patterns: `^(.+)$`, `- (.+\.ts)`, `\[(.+?)\]`.
|
|
83
|
+
|
|
84
|
+
**Feature 3: `params` — Parameterized Workflows**
|
|
85
|
+
|
|
86
|
+
Define default parameter values at the top level. Use `{{ key }}` placeholders in step prompts. CLI overrides take precedence.
|
|
87
|
+
|
|
88
|
+
```yaml
|
|
89
|
+
version: 1
|
|
90
|
+
name: blog-post
|
|
91
|
+
description: Generate a blog post on a configurable topic.
|
|
92
|
+
params:
|
|
93
|
+
topic: "AI in healthcare"
|
|
94
|
+
audience: "technical professionals"
|
|
95
|
+
word_count: "1500"
|
|
96
|
+
steps:
|
|
97
|
+
- id: outline
|
|
98
|
+
name: Create outline
|
|
99
|
+
prompt: "Create a detailed outline for a blog post about {{ topic }} targeting {{ audience }}."
|
|
100
|
+
produces:
|
|
101
|
+
- outline.md
|
|
102
|
+
|
|
103
|
+
- id: draft
|
|
104
|
+
name: Write draft
|
|
105
|
+
prompt: "Write a {{ word_count }}-word blog post about {{ topic }} for {{ audience }} based on the outline."
|
|
106
|
+
requires:
|
|
107
|
+
- outline
|
|
108
|
+
context_from:
|
|
109
|
+
- outline
|
|
110
|
+
produces:
|
|
111
|
+
- draft.md
|
|
112
|
+
verify:
|
|
113
|
+
policy: content-heuristic
|
|
114
|
+
minSize: 500
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
How it works:
|
|
118
|
+
- `params` is a top-level object mapping string keys to string default values.
|
|
119
|
+
- `{{ key }}` in any step prompt is replaced with the corresponding param value.
|
|
120
|
+
- Merge order: definition `params` (defaults) ← CLI overrides (win).
|
|
121
|
+
- After substitution, any remaining `{{ key }}` that has no value causes an error — all placeholders must resolve.
|
|
122
|
+
- Parameter values must not contain `..` (path traversal guard).
|
|
123
|
+
- Keys in `{{ }}` match `\w+` (letters, digits, underscore).
|
|
124
|
+
|
|
125
|
+
Common usage:
|
|
126
|
+
- Make workflows reusable across different topics, projects, or configurations.
|
|
127
|
+
- Users override defaults at run time: `/gsd workflow run blog-post topic="Rust performance"`.
|
|
128
|
+
</feature_patterns>
|