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
|
@@ -1519,6 +1519,13 @@ export class InteractiveMode {
|
|
|
1519
1519
|
options: string[],
|
|
1520
1520
|
opts?: ExtensionUIDialogOptions,
|
|
1521
1521
|
): Promise<string | undefined> {
|
|
1522
|
+
// If a previous selector is still active, dispose it before creating a
|
|
1523
|
+
// new one. This avoids leaking the previous promise and DOM state when
|
|
1524
|
+
// showExtensionSelector is called rapidly.
|
|
1525
|
+
if (this.extensionSelector) {
|
|
1526
|
+
this.hideExtensionSelector();
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1522
1529
|
return new Promise((resolve) => {
|
|
1523
1530
|
if (opts?.signal?.aborted) {
|
|
1524
1531
|
resolve(undefined);
|
|
@@ -2331,18 +2338,24 @@ export class InteractiveMode {
|
|
|
2331
2338
|
const ignoreSigint = () => {};
|
|
2332
2339
|
process.on("SIGINT", ignoreSigint);
|
|
2333
2340
|
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
process.
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2341
|
+
try {
|
|
2342
|
+
// Set up handler to restore TUI when resumed
|
|
2343
|
+
process.once("SIGCONT", () => {
|
|
2344
|
+
process.removeListener("SIGINT", ignoreSigint);
|
|
2345
|
+
this.ui.start();
|
|
2346
|
+
this.ui.requestRender(true);
|
|
2347
|
+
});
|
|
2340
2348
|
|
|
2341
|
-
|
|
2342
|
-
|
|
2349
|
+
// Stop the TUI (restore terminal to normal mode)
|
|
2350
|
+
this.ui.stop();
|
|
2343
2351
|
|
|
2344
|
-
|
|
2345
|
-
|
|
2352
|
+
// Send SIGTSTP to process group (pid=0 means all processes in group)
|
|
2353
|
+
process.kill(0, "SIGTSTP");
|
|
2354
|
+
} catch {
|
|
2355
|
+
// If suspend fails (e.g. SIGTSTP not supported), ensure the
|
|
2356
|
+
// SIGINT listener doesn't leak.
|
|
2357
|
+
process.removeListener("SIGINT", ignoreSigint);
|
|
2358
|
+
}
|
|
2346
2359
|
}
|
|
2347
2360
|
|
|
2348
2361
|
private async handleFollowUp(): Promise<void> {
|
|
@@ -2460,7 +2473,14 @@ export class InteractiveMode {
|
|
|
2460
2473
|
// Determine editor (respect $VISUAL, then $EDITOR)
|
|
2461
2474
|
const editorCmd = process.env.VISUAL || process.env.EDITOR;
|
|
2462
2475
|
if (!editorCmd) {
|
|
2463
|
-
|
|
2476
|
+
let msg = "No editor configured. Set $VISUAL or $EDITOR environment variable.";
|
|
2477
|
+
if (process.env.TERM_PROGRAM === "iTerm.app") {
|
|
2478
|
+
msg +=
|
|
2479
|
+
"\n\nTip: If you meant to open the GSD dashboard (Ctrl+Alt+G), set Left Option Key to" +
|
|
2480
|
+
" \"Esc+\" in iTerm2 → Profiles → Keys. With the default \"Normal\" setting," +
|
|
2481
|
+
" Ctrl+Alt+G sends Ctrl+G instead.";
|
|
2482
|
+
}
|
|
2483
|
+
this.showWarning(msg);
|
|
2464
2484
|
return;
|
|
2465
2485
|
}
|
|
2466
2486
|
|
package/pkg/package.json
CHANGED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* async-bash-timeout.test.ts — Tests for async_bash timeout behavior.
|
|
3
|
+
*
|
|
4
|
+
* Reproduces issue #2186: when an async bash job exceeds its timeout and
|
|
5
|
+
* the child process ignores SIGTERM, the promise hangs indefinitely.
|
|
6
|
+
* The fix adds a SIGKILL fallback and a hard deadline that force-resolves
|
|
7
|
+
* the promise so execution can continue.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import test from "node:test";
|
|
11
|
+
import assert from "node:assert/strict";
|
|
12
|
+
import { createAsyncBashTool } from "./async-bash-tool.ts";
|
|
13
|
+
import { AsyncJobManager } from "./job-manager.ts";
|
|
14
|
+
|
|
15
|
+
function getTextFromResult(result: { content: Array<{ type: string; text?: string }> }): string {
|
|
16
|
+
return result.content.map((c) => c.text ?? "").join("\n");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const noopSignal = new AbortController().signal;
|
|
20
|
+
|
|
21
|
+
test("async_bash with timeout resolves even if process ignores SIGTERM", async () => {
|
|
22
|
+
const manager = new AsyncJobManager();
|
|
23
|
+
const tool = createAsyncBashTool(() => manager, () => process.cwd());
|
|
24
|
+
|
|
25
|
+
// Start a job that traps SIGTERM (ignores it), with a 2s timeout.
|
|
26
|
+
// The process installs a SIGTERM trap and sleeps for 60s.
|
|
27
|
+
// Before the fix, this would hang forever because SIGTERM is ignored
|
|
28
|
+
// and the close event never fires.
|
|
29
|
+
const result = await tool.execute(
|
|
30
|
+
"tc-timeout",
|
|
31
|
+
{
|
|
32
|
+
command: "trap '' TERM; sleep 60",
|
|
33
|
+
timeout: 2,
|
|
34
|
+
label: "sigterm-resistant",
|
|
35
|
+
},
|
|
36
|
+
noopSignal,
|
|
37
|
+
() => {},
|
|
38
|
+
undefined as never,
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const text = getTextFromResult(result);
|
|
42
|
+
assert.match(text, /sigterm-resistant/);
|
|
43
|
+
|
|
44
|
+
const jobId = text.match(/\*\*(bg_[a-f0-9]+)\*\*/)?.[1];
|
|
45
|
+
assert.ok(jobId, "Should have returned a job ID");
|
|
46
|
+
|
|
47
|
+
// Now await the job — it should resolve within a reasonable time
|
|
48
|
+
// (timeout 2s + SIGKILL grace 5s + buffer = well under 15s)
|
|
49
|
+
const start = Date.now();
|
|
50
|
+
const job = manager.getJob(jobId)!;
|
|
51
|
+
assert.ok(job, "Job should exist");
|
|
52
|
+
|
|
53
|
+
await Promise.race([
|
|
54
|
+
job.promise,
|
|
55
|
+
new Promise<never>((_, reject) => {
|
|
56
|
+
const t = setTimeout(() => reject(new Error(
|
|
57
|
+
`Job promise hung for ${Date.now() - start}ms — ` +
|
|
58
|
+
`this is the bug from issue #2186: timeout hangs indefinitely`,
|
|
59
|
+
)), 15_000);
|
|
60
|
+
if (typeof t === "object" && "unref" in t) t.unref();
|
|
61
|
+
}),
|
|
62
|
+
]);
|
|
63
|
+
|
|
64
|
+
const elapsed = Date.now() - start;
|
|
65
|
+
// Should have resolved well within 15s (timeout 2s + kill grace ~5s)
|
|
66
|
+
assert.ok(elapsed < 15_000, `Job took ${elapsed}ms — expected <15s`);
|
|
67
|
+
|
|
68
|
+
// Job should have completed (resolved, not rejected) with timeout message
|
|
69
|
+
assert.ok(
|
|
70
|
+
job.status === "completed" || job.status === "failed",
|
|
71
|
+
`Job status should be completed or failed, got: ${job.status}`,
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
if (job.status === "completed") {
|
|
75
|
+
assert.ok(
|
|
76
|
+
job.resultText?.includes("timed out") || job.resultText?.includes("Timed out"),
|
|
77
|
+
`Result should mention timeout, got: ${job.resultText}`,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
manager.shutdown();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("async_bash with timeout resolves normally when process exits on SIGTERM", async () => {
|
|
85
|
+
const manager = new AsyncJobManager();
|
|
86
|
+
const tool = createAsyncBashTool(() => manager, () => process.cwd());
|
|
87
|
+
|
|
88
|
+
// Start a normal sleep that will die on SIGTERM, with a 1s timeout
|
|
89
|
+
const result = await tool.execute(
|
|
90
|
+
"tc-normal-timeout",
|
|
91
|
+
{
|
|
92
|
+
command: "sleep 60",
|
|
93
|
+
timeout: 1,
|
|
94
|
+
label: "normal-timeout",
|
|
95
|
+
},
|
|
96
|
+
noopSignal,
|
|
97
|
+
() => {},
|
|
98
|
+
undefined as never,
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
const text = getTextFromResult(result);
|
|
102
|
+
const jobId = text.match(/\*\*(bg_[a-f0-9]+)\*\*/)?.[1];
|
|
103
|
+
assert.ok(jobId, "Should have returned a job ID");
|
|
104
|
+
|
|
105
|
+
const job = manager.getJob(jobId)!;
|
|
106
|
+
const start = Date.now();
|
|
107
|
+
|
|
108
|
+
await Promise.race([
|
|
109
|
+
job.promise,
|
|
110
|
+
new Promise<never>((_, reject) => {
|
|
111
|
+
const t = setTimeout(() => reject(new Error("Job hung")), 10_000);
|
|
112
|
+
if (typeof t === "object" && "unref" in t) t.unref();
|
|
113
|
+
}),
|
|
114
|
+
]);
|
|
115
|
+
|
|
116
|
+
const elapsed = Date.now() - start;
|
|
117
|
+
assert.ok(elapsed < 5_000, `Expected quick resolution after SIGTERM, took ${elapsed}ms`);
|
|
118
|
+
assert.equal(job.status, "completed");
|
|
119
|
+
assert.ok(job.resultText?.includes("timed out"), `Should mention timeout: ${job.resultText}`);
|
|
120
|
+
|
|
121
|
+
manager.shutdown();
|
|
122
|
+
});
|
|
@@ -109,6 +109,10 @@ function executeBashInBackground(
|
|
|
109
109
|
timeout?: number,
|
|
110
110
|
): Promise<string> {
|
|
111
111
|
return new Promise<string>((resolve, reject) => {
|
|
112
|
+
let settled = false;
|
|
113
|
+
const safeResolve = (value: string) => { if (!settled) { settled = true; resolve(value); } };
|
|
114
|
+
const safeReject = (err: unknown) => { if (!settled) { settled = true; reject(err); } };
|
|
115
|
+
|
|
112
116
|
const { shell, args } = getShellConfig();
|
|
113
117
|
const resolvedCommand = sanitizeCommand(command);
|
|
114
118
|
|
|
@@ -121,11 +125,39 @@ function executeBashInBackground(
|
|
|
121
125
|
|
|
122
126
|
let timedOut = false;
|
|
123
127
|
let timeoutHandle: ReturnType<typeof setTimeout> | undefined;
|
|
128
|
+
let sigkillHandle: ReturnType<typeof setTimeout> | undefined;
|
|
129
|
+
let hardDeadlineHandle: ReturnType<typeof setTimeout> | undefined;
|
|
130
|
+
|
|
131
|
+
/** Grace period (ms) between SIGTERM and SIGKILL. */
|
|
132
|
+
const SIGKILL_GRACE_MS = 5_000;
|
|
133
|
+
/** Hard deadline (ms) after SIGKILL to force-resolve the promise. */
|
|
134
|
+
const HARD_DEADLINE_MS = 3_000;
|
|
124
135
|
|
|
125
136
|
if (timeout !== undefined && timeout > 0) {
|
|
126
137
|
timeoutHandle = setTimeout(() => {
|
|
127
138
|
timedOut = true;
|
|
128
139
|
if (child.pid) killTree(child.pid);
|
|
140
|
+
|
|
141
|
+
// If the process ignores SIGTERM, escalate to SIGKILL
|
|
142
|
+
sigkillHandle = setTimeout(() => {
|
|
143
|
+
if (child.pid) {
|
|
144
|
+
try { process.kill(-child.pid, "SIGKILL"); } catch { /* ignore */ }
|
|
145
|
+
try { process.kill(child.pid, "SIGKILL"); } catch { /* ignore */ }
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Hard deadline: if even SIGKILL doesn't trigger 'close',
|
|
149
|
+
// force-resolve so the job doesn't hang forever (#2186).
|
|
150
|
+
hardDeadlineHandle = setTimeout(() => {
|
|
151
|
+
const output = Buffer.concat(chunks).toString("utf-8");
|
|
152
|
+
safeResolve(
|
|
153
|
+
output
|
|
154
|
+
? `${output}\n\nCommand timed out after ${timeout} seconds (force-killed)`
|
|
155
|
+
: `Command timed out after ${timeout} seconds (force-killed)`,
|
|
156
|
+
);
|
|
157
|
+
}, HARD_DEADLINE_MS);
|
|
158
|
+
if (typeof hardDeadlineHandle === "object" && "unref" in hardDeadlineHandle) hardDeadlineHandle.unref();
|
|
159
|
+
}, SIGKILL_GRACE_MS);
|
|
160
|
+
if (typeof sigkillHandle === "object" && "unref" in sigkillHandle) sigkillHandle.unref();
|
|
129
161
|
}, timeout * 1000);
|
|
130
162
|
}
|
|
131
163
|
|
|
@@ -168,24 +200,28 @@ function executeBashInBackground(
|
|
|
168
200
|
|
|
169
201
|
child.on("error", (err) => {
|
|
170
202
|
if (timeoutHandle) clearTimeout(timeoutHandle);
|
|
203
|
+
if (sigkillHandle) clearTimeout(sigkillHandle);
|
|
204
|
+
if (hardDeadlineHandle) clearTimeout(hardDeadlineHandle);
|
|
171
205
|
signal.removeEventListener("abort", onAbort);
|
|
172
|
-
|
|
206
|
+
safeReject(err);
|
|
173
207
|
});
|
|
174
208
|
|
|
175
209
|
child.on("close", (code) => {
|
|
176
210
|
if (timeoutHandle) clearTimeout(timeoutHandle);
|
|
211
|
+
if (sigkillHandle) clearTimeout(sigkillHandle);
|
|
212
|
+
if (hardDeadlineHandle) clearTimeout(hardDeadlineHandle);
|
|
177
213
|
signal.removeEventListener("abort", onAbort);
|
|
178
214
|
if (spillStream) spillStream.end();
|
|
179
215
|
|
|
180
216
|
if (signal.aborted) {
|
|
181
217
|
const output = Buffer.concat(chunks).toString("utf-8");
|
|
182
|
-
|
|
218
|
+
safeResolve(output ? `${output}\n\nCommand aborted` : "Command aborted");
|
|
183
219
|
return;
|
|
184
220
|
}
|
|
185
221
|
|
|
186
222
|
if (timedOut) {
|
|
187
223
|
const output = Buffer.concat(chunks).toString("utf-8");
|
|
188
|
-
|
|
224
|
+
safeResolve(output ? `${output}\n\nCommand timed out after ${timeout} seconds` : `Command timed out after ${timeout} seconds`);
|
|
189
225
|
return;
|
|
190
226
|
}
|
|
191
227
|
|
|
@@ -208,7 +244,7 @@ function executeBashInBackground(
|
|
|
208
244
|
text += `\n\nCommand exited with code ${code}`;
|
|
209
245
|
}
|
|
210
246
|
|
|
211
|
-
|
|
247
|
+
safeResolve(text);
|
|
212
248
|
});
|
|
213
249
|
});
|
|
214
250
|
}
|
|
@@ -118,3 +118,50 @@ test("await_job returns not-found message for invalid job IDs", async () => {
|
|
|
118
118
|
|
|
119
119
|
manager.shutdown();
|
|
120
120
|
});
|
|
121
|
+
|
|
122
|
+
test("await_job marks jobs as awaited to suppress follow-up delivery (#2248)", async () => {
|
|
123
|
+
const followUps: string[] = [];
|
|
124
|
+
const manager = new AsyncJobManager({
|
|
125
|
+
onJobComplete: (job) => {
|
|
126
|
+
if (!job.awaited) followUps.push(job.id);
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
const tool = createAwaitTool(() => manager);
|
|
130
|
+
|
|
131
|
+
// Register a job that completes in 50ms
|
|
132
|
+
const jobId = manager.register("bash", "awaited-job", async () => {
|
|
133
|
+
return new Promise<string>((resolve) => setTimeout(() => resolve("result"), 50));
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// await_job consumes the result — should mark as awaited before promise resolves
|
|
137
|
+
await tool.execute("tc7", { jobs: [jobId] }, noopSignal, () => {}, undefined as never);
|
|
138
|
+
|
|
139
|
+
// Give the onJobComplete callback a tick to fire
|
|
140
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
141
|
+
|
|
142
|
+
assert.equal(followUps.length, 0, "onJobComplete should not deliver follow-up for awaited jobs");
|
|
143
|
+
|
|
144
|
+
manager.shutdown();
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test("unawaited jobs still get follow-up delivery (#2248)", async () => {
|
|
148
|
+
const followUps: string[] = [];
|
|
149
|
+
const manager = new AsyncJobManager({
|
|
150
|
+
onJobComplete: (job) => {
|
|
151
|
+
if (!job.awaited) followUps.push(job.id);
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// Register a fire-and-forget job
|
|
156
|
+
const jobId = manager.register("bash", "fire-and-forget", async () => "done");
|
|
157
|
+
const job = manager.getJob(jobId)!;
|
|
158
|
+
await job.promise;
|
|
159
|
+
|
|
160
|
+
// Give the callback a tick
|
|
161
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
162
|
+
|
|
163
|
+
assert.equal(followUps.length, 1, "onJobComplete should deliver follow-up for unawaited jobs");
|
|
164
|
+
assert.equal(followUps[0], jobId);
|
|
165
|
+
|
|
166
|
+
manager.shutdown();
|
|
167
|
+
});
|
|
@@ -66,6 +66,11 @@ export function createAwaitTool(getManager: () => AsyncJobManager): ToolDefiniti
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
// Mark all watched jobs as awaited upfront so the onJobComplete
|
|
70
|
+
// callback (which fires synchronously in the promise .then()) knows
|
|
71
|
+
// to suppress the follow-up message.
|
|
72
|
+
for (const j of watched) j.awaited = true;
|
|
73
|
+
|
|
69
74
|
// If all watched jobs are already done, return immediately
|
|
70
75
|
const running = watched.filter((j) => j.status === "running");
|
|
71
76
|
if (running.length === 0) {
|
|
@@ -42,6 +42,7 @@ export default function AsyncJobs(pi: ExtensionAPI) {
|
|
|
42
42
|
|
|
43
43
|
manager = new AsyncJobManager({
|
|
44
44
|
onJobComplete: (job) => {
|
|
45
|
+
if (job.awaited) return;
|
|
45
46
|
const statusEmoji = job.status === "completed" ? "done" : "error";
|
|
46
47
|
const elapsed = ((Date.now() - job.startTime) / 1000).toFixed(1);
|
|
47
48
|
const output = job.status === "completed"
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
} from "./phases.js";
|
|
29
29
|
import { debugLog } from "../debug-logger.js";
|
|
30
30
|
import { isInfrastructureError } from "./infra-errors.js";
|
|
31
|
+
import { resolveEngine } from "../engine-resolver.js";
|
|
31
32
|
|
|
32
33
|
/**
|
|
33
34
|
* Main auto-mode execution loop. Iterates: derive → dispatch → guards →
|
|
@@ -117,6 +118,96 @@ export async function autoLoop(
|
|
|
117
118
|
deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "iteration-start", data: { iteration } });
|
|
118
119
|
let iterData: IterationData;
|
|
119
120
|
|
|
121
|
+
// ── Custom engine path ──────────────────────────────────────────────
|
|
122
|
+
// When activeEngineId is a non-dev value, bypass runPreDispatch and
|
|
123
|
+
// runDispatch entirely — the custom engine drives its own state via
|
|
124
|
+
// GRAPH.yaml. Shares runGuards and runUnitPhase with the dev path.
|
|
125
|
+
// After unit execution, verifies then reconciles via the engine layer.
|
|
126
|
+
//
|
|
127
|
+
// GSD_ENGINE_BYPASS=1 skips the engine layer entirely — falls through
|
|
128
|
+
// to the dev path below.
|
|
129
|
+
if (s.activeEngineId != null && s.activeEngineId !== "dev" && !sidecarItem && process.env.GSD_ENGINE_BYPASS !== "1") {
|
|
130
|
+
debugLog("autoLoop", { phase: "custom-engine-derive", iteration, engineId: s.activeEngineId });
|
|
131
|
+
|
|
132
|
+
const { engine, policy } = resolveEngine({
|
|
133
|
+
activeEngineId: s.activeEngineId,
|
|
134
|
+
activeRunDir: s.activeRunDir,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const engineState = await engine.deriveState(s.basePath);
|
|
138
|
+
if (engineState.isComplete) {
|
|
139
|
+
await deps.stopAuto(ctx, pi, "Workflow complete");
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
debugLog("autoLoop", { phase: "custom-engine-dispatch", iteration });
|
|
144
|
+
const dispatch = await engine.resolveDispatch(engineState, { basePath: s.basePath });
|
|
145
|
+
|
|
146
|
+
if (dispatch.action === "stop") {
|
|
147
|
+
await deps.stopAuto(ctx, pi, dispatch.reason ?? "Engine stopped");
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
if (dispatch.action === "skip") {
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// dispatch.action === "dispatch"
|
|
155
|
+
const step = dispatch.step!;
|
|
156
|
+
const gsdState = await deps.deriveState(s.basePath);
|
|
157
|
+
|
|
158
|
+
iterData = {
|
|
159
|
+
unitType: step.unitType,
|
|
160
|
+
unitId: step.unitId,
|
|
161
|
+
prompt: step.prompt,
|
|
162
|
+
finalPrompt: step.prompt,
|
|
163
|
+
pauseAfterUatDispatch: false,
|
|
164
|
+
observabilityIssues: [],
|
|
165
|
+
state: gsdState,
|
|
166
|
+
mid: s.currentMilestoneId ?? "workflow",
|
|
167
|
+
midTitle: "Workflow",
|
|
168
|
+
isRetry: false,
|
|
169
|
+
previousTier: undefined,
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
// ── Progress widget (mirrors dev path in runDispatch) ──
|
|
173
|
+
deps.updateProgressWidget(ctx, iterData.unitType, iterData.unitId, iterData.state);
|
|
174
|
+
|
|
175
|
+
// ── Guards (shared with dev path) ──
|
|
176
|
+
const guardsResult = await runGuards(ic, s.currentMilestoneId ?? "workflow");
|
|
177
|
+
if (guardsResult.action === "break") break;
|
|
178
|
+
|
|
179
|
+
// ── Unit execution (shared with dev path) ──
|
|
180
|
+
const unitPhaseResult = await runUnitPhase(ic, iterData, loopState);
|
|
181
|
+
if (unitPhaseResult.action === "break") break;
|
|
182
|
+
|
|
183
|
+
// ── Verify first, then reconcile (only mark complete on pass) ──
|
|
184
|
+
debugLog("autoLoop", { phase: "custom-engine-verify", iteration, unitId: iterData.unitId });
|
|
185
|
+
const verifyResult = await policy.verify(iterData.unitType, iterData.unitId, { basePath: s.basePath });
|
|
186
|
+
if (verifyResult === "pause") {
|
|
187
|
+
await deps.pauseAuto(ctx, pi);
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
if (verifyResult === "retry") {
|
|
191
|
+
debugLog("autoLoop", { phase: "custom-engine-verify-retry", iteration, unitId: iterData.unitId });
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Verification passed — mark step complete
|
|
196
|
+
debugLog("autoLoop", { phase: "custom-engine-reconcile", iteration, unitId: iterData.unitId });
|
|
197
|
+
await engine.reconcile(engineState, {
|
|
198
|
+
unitType: iterData.unitType,
|
|
199
|
+
unitId: iterData.unitId,
|
|
200
|
+
startedAt: s.currentUnit?.startedAt ?? Date.now(),
|
|
201
|
+
finishedAt: Date.now(),
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
deps.clearUnitTimeout();
|
|
205
|
+
consecutiveErrors = 0;
|
|
206
|
+
deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "iteration-end", data: { iteration } });
|
|
207
|
+
debugLog("autoLoop", { phase: "iteration-complete", iteration });
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
|
|
120
211
|
if (!sidecarItem) {
|
|
121
212
|
// ── Phase 1: Pre-dispatch ─────────────────────────────────────────
|
|
122
213
|
const preDispatchResult = await runPreDispatch(ic, loopState);
|
|
@@ -261,9 +261,7 @@ export async function runPreDispatch(
|
|
|
261
261
|
|
|
262
262
|
if (mid) {
|
|
263
263
|
if (deps.getIsolationMode() !== "none") {
|
|
264
|
-
deps.captureIntegrationBranch(s.basePath, mid
|
|
265
|
-
commitDocs: prefs?.git?.commit_docs,
|
|
266
|
-
});
|
|
264
|
+
deps.captureIntegrationBranch(s.basePath, mid);
|
|
267
265
|
}
|
|
268
266
|
deps.resolver.enterMilestone(mid, ctx.ui);
|
|
269
267
|
} else {
|
|
@@ -1133,9 +1131,9 @@ export async function runUnitPhase(
|
|
|
1133
1131
|
);
|
|
1134
1132
|
}
|
|
1135
1133
|
|
|
1136
|
-
const
|
|
1134
|
+
const skipArtifactVerification = unitType.startsWith("hook/") || unitType === "custom-step";
|
|
1137
1135
|
const artifactVerified =
|
|
1138
|
-
|
|
1136
|
+
skipArtifactVerification ||
|
|
1139
1137
|
deps.verifyExpectedArtifact(unitType, unitId, s.basePath);
|
|
1140
1138
|
if (artifactVerified) {
|
|
1141
1139
|
s.completedUnits.push({
|
|
@@ -83,6 +83,8 @@ export class AutoSession {
|
|
|
83
83
|
paused = false;
|
|
84
84
|
stepMode = false;
|
|
85
85
|
verbose = false;
|
|
86
|
+
activeEngineId: string | null = null;
|
|
87
|
+
activeRunDir: string | null = null;
|
|
86
88
|
cmdCtx: ExtensionCommandContext | null = null;
|
|
87
89
|
|
|
88
90
|
// ── Paths ────────────────────────────────────────────────────────────────
|
|
@@ -174,6 +176,8 @@ export class AutoSession {
|
|
|
174
176
|
this.paused = false;
|
|
175
177
|
this.stepMode = false;
|
|
176
178
|
this.verbose = false;
|
|
179
|
+
this.activeEngineId = null;
|
|
180
|
+
this.activeRunDir = null;
|
|
177
181
|
this.cmdCtx = null;
|
|
178
182
|
|
|
179
183
|
// Paths
|
|
@@ -226,6 +230,8 @@ export class AutoSession {
|
|
|
226
230
|
paused: this.paused,
|
|
227
231
|
stepMode: this.stepMode,
|
|
228
232
|
basePath: this.basePath,
|
|
233
|
+
activeEngineId: this.activeEngineId,
|
|
234
|
+
activeRunDir: this.activeRunDir,
|
|
229
235
|
currentMilestoneId: this.currentMilestoneId,
|
|
230
236
|
currentUnit: this.currentUnit,
|
|
231
237
|
completedUnits: this.completedUnits.length,
|
|
@@ -79,6 +79,7 @@ export function unitVerb(unitType: string): string {
|
|
|
79
79
|
case "rewrite-docs": return "rewriting";
|
|
80
80
|
case "reassess-roadmap": return "reassessing";
|
|
81
81
|
case "run-uat": return "running UAT";
|
|
82
|
+
case "custom-step": return "executing workflow step";
|
|
82
83
|
default: return unitType;
|
|
83
84
|
}
|
|
84
85
|
}
|
|
@@ -97,6 +98,7 @@ export function unitPhaseLabel(unitType: string): string {
|
|
|
97
98
|
case "rewrite-docs": return "REWRITE";
|
|
98
99
|
case "reassess-roadmap": return "REASSESS";
|
|
99
100
|
case "run-uat": return "UAT";
|
|
101
|
+
case "custom-step": return "WORKFLOW";
|
|
100
102
|
default: return unitType.toUpperCase();
|
|
101
103
|
}
|
|
102
104
|
}
|
|
@@ -420,8 +420,6 @@ export function buildSkillActivationBlock(params: {
|
|
|
420
420
|
params.sliceTitle,
|
|
421
421
|
params.taskId,
|
|
422
422
|
params.taskTitle,
|
|
423
|
-
...(params.extraContext ?? []),
|
|
424
|
-
params.taskPlanContent ?? undefined,
|
|
425
423
|
);
|
|
426
424
|
|
|
427
425
|
const visibleSkills = (typeof getLoadedSkills === 'function' ? getLoadedSkills() : []).filter(skill => !skill.disableModelInvocation);
|
|
@@ -452,12 +450,6 @@ export function buildSkillActivationBlock(params: {
|
|
|
452
450
|
}
|
|
453
451
|
}
|
|
454
452
|
|
|
455
|
-
for (const skill of visibleSkills) {
|
|
456
|
-
if (skillMatchesContext(skill, contextTokens)) {
|
|
457
|
-
matched.add(normalizeSkillReference(skill.name));
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
|
|
461
453
|
const ordered = [...matched]
|
|
462
454
|
.filter(name => installedNames.has(name) && !avoided.has(name))
|
|
463
455
|
.sort();
|
|
@@ -983,11 +975,7 @@ export async function buildPlanSlicePrompt(
|
|
|
983
975
|
const executorContextConstraints = formatExecutorConstraints();
|
|
984
976
|
|
|
985
977
|
const outputRelPath = relSliceFile(base, mid, sid, "PLAN");
|
|
986
|
-
const
|
|
987
|
-
const commitDocsEnabled = prefs?.preferences?.git?.commit_docs !== false;
|
|
988
|
-
const commitInstruction = commitDocsEnabled
|
|
989
|
-
? `Commit the plan files only: \`git add ${relSlicePath(base, mid, sid)}/ .gsd/DECISIONS.md .gitignore && git commit -m "docs(${sid}): add slice plan"\`. Do not stage .gsd/STATE.md or other runtime files — the system manages those.`
|
|
990
|
-
: "Do not commit — planning docs are not tracked in git for this project.";
|
|
978
|
+
const commitInstruction = "Do not commit — .gsd/ planning docs are managed externally and not tracked in git.";
|
|
991
979
|
return loadPrompt("plan-slice", {
|
|
992
980
|
workingDirectory: base,
|
|
993
981
|
milestoneId: mid, sliceId: sid, sliceTitle: sTitle,
|
|
@@ -1485,11 +1473,7 @@ export async function buildReassessRoadmapPrompt(
|
|
|
1485
1473
|
// Non-fatal — captures module may not be available
|
|
1486
1474
|
}
|
|
1487
1475
|
|
|
1488
|
-
const
|
|
1489
|
-
const reassessCommitDocsEnabled = reassessPrefs?.preferences?.git?.commit_docs !== false;
|
|
1490
|
-
const reassessCommitInstruction = reassessCommitDocsEnabled
|
|
1491
|
-
? `Commit: \`docs(${mid}): reassess roadmap after ${completedSliceId}\`. Stage only the .gsd/milestones/ files you changed — do not stage .gsd/STATE.md or other runtime files.`
|
|
1492
|
-
: "Do not commit — planning docs are not tracked in git for this project.";
|
|
1476
|
+
const reassessCommitInstruction = "Do not commit — .gsd/ planning docs are managed externally and not tracked in git.";
|
|
1493
1477
|
|
|
1494
1478
|
return loadPrompt("reassess-roadmap", {
|
|
1495
1479
|
workingDirectory: base,
|
|
@@ -167,22 +167,19 @@ export async function bootstrapAutoSession(
|
|
|
167
167
|
// ensureGitignore checks for git-tracked .gsd/ files and skips the
|
|
168
168
|
// ".gsd" pattern if the project intentionally tracks .gsd/ in git.
|
|
169
169
|
const gitPrefs = loadEffectiveGSDPreferences()?.preferences?.git;
|
|
170
|
-
const commitDocs = gitPrefs?.commit_docs;
|
|
171
170
|
const manageGitignore = gitPrefs?.manage_gitignore;
|
|
172
|
-
ensureGitignore(base, {
|
|
171
|
+
ensureGitignore(base, { manageGitignore });
|
|
173
172
|
if (manageGitignore !== false) untrackRuntimeFiles(base);
|
|
174
173
|
|
|
175
174
|
// Bootstrap .gsd/ if it doesn't exist
|
|
176
175
|
const gsdDir = join(base, ".gsd");
|
|
177
176
|
if (!existsSync(gsdDir)) {
|
|
178
177
|
mkdirSync(join(gsdDir, "milestones"), { recursive: true });
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
/* nothing to commit */
|
|
185
|
-
}
|
|
178
|
+
try {
|
|
179
|
+
nativeAddAll(base);
|
|
180
|
+
nativeCommit(base, "chore: init gsd");
|
|
181
|
+
} catch {
|
|
182
|
+
/* nothing to commit */
|
|
186
183
|
}
|
|
187
184
|
}
|
|
188
185
|
|
|
@@ -487,7 +484,7 @@ export async function bootstrapAutoSession(
|
|
|
487
484
|
// Capture integration branch
|
|
488
485
|
if (s.currentMilestoneId) {
|
|
489
486
|
if (getIsolationMode() !== "none") {
|
|
490
|
-
captureIntegrationBranch(base, s.currentMilestoneId
|
|
487
|
+
captureIntegrationBranch(base, s.currentMilestoneId);
|
|
491
488
|
}
|
|
492
489
|
setActiveMilestoneId(base, s.currentMilestoneId);
|
|
493
490
|
}
|