gsd-pi 2.38.0-dev.96dc7fb → 2.38.0-dev.98b44dc
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 +15 -11
- package/dist/app-paths.js +1 -1
- package/dist/extension-registry.js +2 -2
- package/dist/remote-questions-config.js +2 -2
- package/dist/resource-loader.js +34 -1
- package/dist/resources/extensions/browser-tools/index.js +3 -1
- package/dist/resources/extensions/browser-tools/tools/verify.js +97 -0
- package/dist/resources/extensions/env-utils.js +29 -0
- package/dist/resources/extensions/get-secrets-from-user.js +5 -24
- package/dist/resources/extensions/github-sync/cli.js +284 -0
- package/dist/resources/extensions/github-sync/index.js +73 -0
- package/dist/resources/extensions/github-sync/mapping.js +67 -0
- package/dist/resources/extensions/github-sync/sync.js +424 -0
- package/dist/resources/extensions/github-sync/templates.js +118 -0
- package/dist/resources/extensions/github-sync/types.js +7 -0
- package/dist/resources/extensions/gsd/auto/session.js +6 -23
- package/dist/resources/extensions/gsd/auto-dispatch.js +8 -9
- package/dist/resources/extensions/gsd/auto-loop.js +636 -594
- package/dist/resources/extensions/gsd/auto-post-unit.js +99 -70
- package/dist/resources/extensions/gsd/auto-prompts.js +202 -48
- package/dist/resources/extensions/gsd/auto-start.js +7 -1
- package/dist/resources/extensions/gsd/auto-worktree-sync.js +2 -1
- package/dist/resources/extensions/gsd/auto-worktree.js +3 -3
- package/dist/resources/extensions/gsd/auto.js +143 -96
- package/dist/resources/extensions/gsd/commands-extensions.js +3 -2
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +1 -1
- package/dist/resources/extensions/gsd/commands.js +4 -2
- package/dist/resources/extensions/gsd/context-budget.js +2 -10
- package/dist/resources/extensions/gsd/detection.js +1 -2
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +0 -2
- package/dist/resources/extensions/gsd/doctor-providers.js +30 -11
- package/dist/resources/extensions/gsd/doctor.js +20 -1
- package/dist/resources/extensions/gsd/exit-command.js +2 -1
- package/dist/resources/extensions/gsd/export.js +1 -1
- package/dist/resources/extensions/gsd/files.js +48 -9
- package/dist/resources/extensions/gsd/forensics.js +1 -1
- package/dist/resources/extensions/gsd/git-service.js +30 -12
- package/dist/resources/extensions/gsd/gitignore.js +16 -3
- package/dist/resources/extensions/gsd/guided-flow.js +149 -38
- package/dist/resources/extensions/gsd/health-widget-core.js +32 -70
- package/dist/resources/extensions/gsd/health-widget.js +3 -86
- package/dist/resources/extensions/gsd/index.js +24 -20
- package/dist/resources/extensions/gsd/migrate/parsers.js +1 -1
- package/dist/resources/extensions/gsd/migrate-external.js +18 -1
- package/dist/resources/extensions/gsd/native-git-bridge.js +37 -0
- package/dist/resources/extensions/gsd/paths.js +3 -0
- package/dist/resources/extensions/gsd/preferences-models.js +0 -12
- package/dist/resources/extensions/gsd/preferences-types.js +1 -1
- package/dist/resources/extensions/gsd/preferences-validation.js +59 -11
- package/dist/resources/extensions/gsd/preferences.js +22 -11
- package/dist/resources/extensions/gsd/prompt-loader.js +6 -2
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/discuss.md +11 -14
- package/dist/resources/extensions/gsd/prompts/execute-task.md +5 -3
- package/dist/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +11 -12
- package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -10
- package/dist/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/queue.md +4 -8
- package/dist/resources/extensions/gsd/prompts/reactive-execute.md +11 -8
- package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
- package/dist/resources/extensions/gsd/prompts/research-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/research-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/run-uat.md +28 -11
- package/dist/resources/extensions/gsd/prompts/workflow-start.md +2 -2
- package/dist/resources/extensions/gsd/repo-identity.js +21 -4
- package/dist/resources/extensions/gsd/resource-version.js +2 -1
- package/dist/resources/extensions/gsd/roadmap-mutations.js +24 -0
- package/dist/resources/extensions/gsd/state.js +42 -23
- package/dist/resources/extensions/gsd/templates/runtime.md +21 -0
- package/dist/resources/extensions/gsd/templates/task-plan.md +3 -0
- package/dist/resources/extensions/gsd/visualizer-data.js +1 -1
- package/dist/resources/extensions/mcp-client/index.js +14 -1
- package/dist/resources/extensions/remote-questions/status.js +4 -1
- package/dist/resources/extensions/remote-questions/store.js +4 -1
- package/dist/resources/extensions/search-the-web/provider.js +2 -1
- package/dist/resources/extensions/shared/frontmatter.js +1 -1
- package/dist/resources/extensions/subagent/isolation.js +2 -1
- package/dist/resources/extensions/ttsr/rule-loader.js +2 -1
- package/package.json +1 -1
- package/packages/pi-ai/dist/utils/oauth/anthropic.js +2 -2
- package/packages/pi-ai/dist/utils/oauth/anthropic.js.map +1 -1
- package/packages/pi-ai/src/utils/oauth/anthropic.ts +2 -2
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +205 -7
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/skills.d.ts +1 -0
- package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/skills.js +6 -1
- package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js +1 -1
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +223 -7
- package/packages/pi-coding-agent/src/core/skills.ts +9 -1
- package/packages/pi-coding-agent/src/index.ts +1 -0
- package/src/resources/extensions/browser-tools/index.ts +3 -0
- package/src/resources/extensions/browser-tools/tools/verify.ts +117 -0
- package/src/resources/extensions/env-utils.ts +31 -0
- package/src/resources/extensions/get-secrets-from-user.ts +5 -24
- package/src/resources/extensions/github-sync/cli.ts +364 -0
- package/src/resources/extensions/github-sync/index.ts +93 -0
- package/src/resources/extensions/github-sync/mapping.ts +81 -0
- package/src/resources/extensions/github-sync/sync.ts +556 -0
- package/src/resources/extensions/github-sync/templates.ts +183 -0
- package/src/resources/extensions/github-sync/tests/cli.test.ts +20 -0
- package/src/resources/extensions/github-sync/tests/commit-linking.test.ts +39 -0
- package/src/resources/extensions/github-sync/tests/mapping.test.ts +104 -0
- package/src/resources/extensions/github-sync/tests/templates.test.ts +110 -0
- package/src/resources/extensions/github-sync/types.ts +47 -0
- package/src/resources/extensions/gsd/auto/session.ts +7 -25
- package/src/resources/extensions/gsd/auto-dispatch.ts +7 -9
- package/src/resources/extensions/gsd/auto-loop.ts +526 -545
- package/src/resources/extensions/gsd/auto-post-unit.ts +80 -44
- package/src/resources/extensions/gsd/auto-prompts.ts +247 -50
- package/src/resources/extensions/gsd/auto-start.ts +11 -1
- package/src/resources/extensions/gsd/auto-worktree-sync.ts +3 -1
- package/src/resources/extensions/gsd/auto-worktree.ts +3 -3
- package/src/resources/extensions/gsd/auto.ts +139 -101
- package/src/resources/extensions/gsd/commands-extensions.ts +4 -2
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +1 -1
- package/src/resources/extensions/gsd/commands.ts +5 -3
- package/src/resources/extensions/gsd/context-budget.ts +2 -12
- package/src/resources/extensions/gsd/detection.ts +2 -2
- package/src/resources/extensions/gsd/docs/preferences-reference.md +0 -2
- package/src/resources/extensions/gsd/doctor-providers.ts +30 -9
- package/src/resources/extensions/gsd/doctor.ts +22 -1
- package/src/resources/extensions/gsd/exit-command.ts +2 -2
- package/src/resources/extensions/gsd/export.ts +1 -1
- package/src/resources/extensions/gsd/files.ts +51 -11
- package/src/resources/extensions/gsd/forensics.ts +1 -1
- package/src/resources/extensions/gsd/git-service.ts +44 -10
- package/src/resources/extensions/gsd/gitignore.ts +17 -3
- package/src/resources/extensions/gsd/guided-flow.ts +177 -44
- package/src/resources/extensions/gsd/health-widget-core.ts +28 -80
- package/src/resources/extensions/gsd/health-widget.ts +3 -89
- package/src/resources/extensions/gsd/index.ts +24 -17
- package/src/resources/extensions/gsd/migrate/parsers.ts +1 -1
- package/src/resources/extensions/gsd/migrate-external.ts +18 -1
- package/src/resources/extensions/gsd/native-git-bridge.ts +37 -0
- package/src/resources/extensions/gsd/paths.ts +4 -0
- package/src/resources/extensions/gsd/preferences-models.ts +0 -12
- package/src/resources/extensions/gsd/preferences-types.ts +4 -4
- package/src/resources/extensions/gsd/preferences-validation.ts +51 -11
- package/src/resources/extensions/gsd/preferences.ts +25 -11
- package/src/resources/extensions/gsd/prompt-loader.ts +7 -2
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/discuss.md +11 -14
- package/src/resources/extensions/gsd/prompts/execute-task.md +5 -3
- package/src/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +11 -12
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -10
- package/src/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/queue.md +4 -8
- package/src/resources/extensions/gsd/prompts/reactive-execute.md +11 -8
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
- package/src/resources/extensions/gsd/prompts/research-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/research-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/run-uat.md +28 -11
- package/src/resources/extensions/gsd/prompts/workflow-start.md +2 -2
- package/src/resources/extensions/gsd/repo-identity.ts +23 -4
- package/src/resources/extensions/gsd/resource-version.ts +3 -1
- package/src/resources/extensions/gsd/roadmap-mutations.ts +29 -0
- package/src/resources/extensions/gsd/state.ts +39 -21
- package/src/resources/extensions/gsd/templates/runtime.md +21 -0
- package/src/resources/extensions/gsd/templates/task-plan.md +3 -0
- package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +21 -18
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +122 -68
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +4 -3
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +43 -0
- package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +86 -3
- package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/health-widget.test.ts +16 -54
- package/src/resources/extensions/gsd/tests/parsers.test.ts +131 -14
- package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +209 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +2 -7
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +59 -0
- package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +21 -1
- package/src/resources/extensions/gsd/tests/run-uat.test.ts +16 -4
- package/src/resources/extensions/gsd/tests/skill-activation.test.ts +140 -0
- package/src/resources/extensions/gsd/types.ts +18 -1
- package/src/resources/extensions/gsd/verification-evidence.ts +16 -0
- package/src/resources/extensions/gsd/visualizer-data.ts +1 -1
- package/src/resources/extensions/mcp-client/index.ts +17 -1
- package/src/resources/extensions/remote-questions/status.ts +5 -1
- package/src/resources/extensions/remote-questions/store.ts +5 -1
- package/src/resources/extensions/search-the-web/provider.ts +2 -1
- package/src/resources/extensions/shared/frontmatter.ts +1 -1
- package/src/resources/extensions/subagent/isolation.ts +3 -1
- package/src/resources/extensions/ttsr/rule-loader.ts +3 -1
- package/dist/resources/extensions/gsd/prompt-compressor.js +0 -393
- package/dist/resources/extensions/gsd/semantic-chunker.js +0 -254
- package/dist/resources/extensions/gsd/summary-distiller.js +0 -212
- package/src/resources/extensions/gsd/prompt-compressor.ts +0 -508
- package/src/resources/extensions/gsd/semantic-chunker.ts +0 -336
- package/src/resources/extensions/gsd/summary-distiller.ts +0 -258
- package/src/resources/extensions/gsd/tests/context-compression.test.ts +0 -193
- package/src/resources/extensions/gsd/tests/prompt-compressor.test.ts +0 -529
- package/src/resources/extensions/gsd/tests/semantic-chunker.test.ts +0 -426
- package/src/resources/extensions/gsd/tests/summary-distiller.test.ts +0 -323
- package/src/resources/extensions/gsd/tests/token-optimization-benchmark.test.ts +0 -1272
- package/src/resources/extensions/gsd/tests/token-optimization-prefs.test.ts +0 -164
|
@@ -14,7 +14,7 @@ import { gsdRoot } from "./paths.js";
|
|
|
14
14
|
import { GIT_NO_PROMPT_ENV } from "./git-constants.js";
|
|
15
15
|
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
16
16
|
import { detectWorktreeName, SLICE_BRANCH_RE, } from "./worktree.js";
|
|
17
|
-
import { nativeGetCurrentBranch, nativeDetectMainBranch, nativeBranchExists, nativeHasChanges,
|
|
17
|
+
import { nativeGetCurrentBranch, nativeDetectMainBranch, nativeBranchExists, nativeHasChanges, nativeAddAllWithExclusions, nativeHasStagedChanges, nativeCommit, nativeRmCached, nativeUpdateRef, } from "./native-git-bridge.js";
|
|
18
18
|
import { GSDError, GSD_MERGE_CONFLICT, GSD_GIT_ERROR } from "./errors.js";
|
|
19
19
|
import { getErrorMessage } from "./error-utils.js";
|
|
20
20
|
export const VALID_BRANCH_NAME = /^[a-zA-Z0-9_\-\/.]+$/;
|
|
@@ -36,12 +36,19 @@ export function buildTaskCommitMessage(ctx) {
|
|
|
36
36
|
: description;
|
|
37
37
|
const subject = `${type}(${scope}): ${truncated}`;
|
|
38
38
|
// Build body with key files if available
|
|
39
|
+
const bodyParts = [];
|
|
39
40
|
if (ctx.keyFiles && ctx.keyFiles.length > 0) {
|
|
40
41
|
const fileLines = ctx.keyFiles
|
|
41
42
|
.slice(0, 8) // cap at 8 files to keep commit concise
|
|
42
43
|
.map(f => `- ${f}`)
|
|
43
44
|
.join("\n");
|
|
44
|
-
|
|
45
|
+
bodyParts.push(fileLines);
|
|
46
|
+
}
|
|
47
|
+
if (ctx.issueNumber) {
|
|
48
|
+
bodyParts.push(`Resolves #${ctx.issueNumber}`);
|
|
49
|
+
}
|
|
50
|
+
if (bodyParts.length > 0) {
|
|
51
|
+
return `${subject}\n\n${bodyParts.join("\n\n")}`;
|
|
45
52
|
}
|
|
46
53
|
return subject;
|
|
47
54
|
}
|
|
@@ -254,7 +261,9 @@ export class GitServiceImpl {
|
|
|
254
261
|
}
|
|
255
262
|
this._runtimeFilesCleanedUp = true;
|
|
256
263
|
}
|
|
257
|
-
// Stage everything
|
|
264
|
+
// Stage everything using pathspec exclusions so excluded paths are never
|
|
265
|
+
// hashed by git. The old approach of `git add -A` followed by unstaging
|
|
266
|
+
// hangs indefinitely on repos with large untracked artifact trees (#1605).
|
|
258
267
|
//
|
|
259
268
|
// Exclude only RUNTIME paths from staging — not the entire .gsd/ directory.
|
|
260
269
|
// When .gsd/milestones/ files are already tracked in the index (projects
|
|
@@ -264,15 +273,9 @@ export class GitServiceImpl {
|
|
|
264
273
|
// the second half of a milestone's artifacts are never committed (#1326).
|
|
265
274
|
//
|
|
266
275
|
// If .gsd/ IS in .gitignore (the default for external state projects),
|
|
267
|
-
// git add -A already skips it and the
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
for (const exclusion of runtimeExclusions) {
|
|
271
|
-
try {
|
|
272
|
-
nativeResetPaths(this.basePath, [exclusion]);
|
|
273
|
-
}
|
|
274
|
-
catch { /* path not staged — ignore */ }
|
|
275
|
-
}
|
|
276
|
+
// git add -A already skips it and the exclusions are harmless no-ops.
|
|
277
|
+
const allExclusions = [...RUNTIME_EXCLUSION_PATHS, ...extraExclusions];
|
|
278
|
+
nativeAddAllWithExclusions(this.basePath, allExclusions);
|
|
276
279
|
}
|
|
277
280
|
/** Tracks whether runtime file cleanup has run this session. */
|
|
278
281
|
_runtimeFilesCleanedUp = false;
|
|
@@ -433,6 +436,21 @@ export class GitServiceImpl {
|
|
|
433
436
|
}
|
|
434
437
|
}
|
|
435
438
|
}
|
|
439
|
+
// ─── Draft PR Creation ─────────────────────────────────────────────────────
|
|
440
|
+
/**
|
|
441
|
+
* Create a draft pull request for a completed milestone using `gh pr create`.
|
|
442
|
+
* Returns the PR URL on success, or null on failure.
|
|
443
|
+
* Non-fatal: callers should treat failure as best-effort.
|
|
444
|
+
*/
|
|
445
|
+
export function createDraftPR(basePath, milestoneId, title, body) {
|
|
446
|
+
try {
|
|
447
|
+
const result = execSync(`gh pr create --draft --title ${JSON.stringify(title)} --body ${JSON.stringify(body)}`, { cwd: basePath, encoding: "utf8", timeout: 30000, env: GIT_NO_PROMPT_ENV });
|
|
448
|
+
return result.trim();
|
|
449
|
+
}
|
|
450
|
+
catch {
|
|
451
|
+
return null;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
436
454
|
// ─── Factory ───────────────────────────────────────────────────────────────
|
|
437
455
|
/** Create a GitServiceImpl with the current effective git preferences. */
|
|
438
456
|
export function createGitService(basePath) {
|
|
@@ -6,9 +6,11 @@
|
|
|
6
6
|
* Both idempotent — non-destructive if already present.
|
|
7
7
|
*/
|
|
8
8
|
import { join } from "node:path";
|
|
9
|
+
import { execFileSync } from "node:child_process";
|
|
9
10
|
import { existsSync, lstatSync, readFileSync, writeFileSync } from "node:fs";
|
|
10
11
|
import { nativeRmCached, nativeLsFiles } from "./native-git-bridge.js";
|
|
11
12
|
import { gsdRoot } from "./paths.js";
|
|
13
|
+
import { GIT_NO_PROMPT_ENV } from "./git-constants.js";
|
|
12
14
|
/**
|
|
13
15
|
* GSD runtime patterns for git index cleanup.
|
|
14
16
|
* With external state (symlink), these are a no-op in most cases,
|
|
@@ -93,11 +95,22 @@ export function hasGitTrackedGsdFiles(basePath) {
|
|
|
93
95
|
// Check if git tracks any files under .gsd/
|
|
94
96
|
try {
|
|
95
97
|
const tracked = nativeLsFiles(basePath, ".gsd");
|
|
96
|
-
|
|
98
|
+
if (tracked.length > 0)
|
|
99
|
+
return true;
|
|
100
|
+
// nativeLsFiles swallows git failures and returns []. An empty result
|
|
101
|
+
// could mean "nothing tracked" OR "git failed silently". Verify git is
|
|
102
|
+
// reachable before trusting the empty result — if it isn't, fail safe
|
|
103
|
+
// by assuming files ARE tracked to prevent data loss.
|
|
104
|
+
execFileSync("git", ["rev-parse", "--git-dir"], {
|
|
105
|
+
cwd: basePath,
|
|
106
|
+
stdio: "pipe",
|
|
107
|
+
env: GIT_NO_PROMPT_ENV,
|
|
108
|
+
});
|
|
109
|
+
return false;
|
|
97
110
|
}
|
|
98
111
|
catch {
|
|
99
|
-
//
|
|
100
|
-
return
|
|
112
|
+
// git unavailable, index locked, or repo corrupt — fail safe
|
|
113
|
+
return true;
|
|
101
114
|
}
|
|
102
115
|
}
|
|
103
116
|
/**
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { showNextAction } from "../shared/mod.js";
|
|
9
9
|
import { loadFile, parseRoadmap } from "./files.js";
|
|
10
10
|
import { loadPrompt, inlineTemplate } from "./prompt-loader.js";
|
|
11
|
+
import { buildSkillActivationBlock } from "./auto-prompts.js";
|
|
11
12
|
import { deriveState } from "./state.js";
|
|
12
13
|
import { invalidateAllCaches } from "./cache.js";
|
|
13
14
|
import { startAuto } from "./auto.js";
|
|
@@ -28,6 +29,7 @@ import { showConfirm } from "../shared/mod.js";
|
|
|
28
29
|
import { debugLog } from "./debug-logger.js";
|
|
29
30
|
import { findMilestoneIds, nextMilestoneId } from "./milestone-ids.js";
|
|
30
31
|
import { parkMilestone, discardMilestone } from "./milestone-actions.js";
|
|
32
|
+
import { resolveModelWithFallbacksForUnit } from "./preferences-models.js";
|
|
31
33
|
// ─── Re-exports (preserve public API for existing importers) ────────────────
|
|
32
34
|
export { MILESTONE_ID_RE, generateMilestoneSuffix, nextMilestoneId, extractMilestoneSeq, parseMilestoneId, milestoneIdSort, maxMilestoneNum, findMilestoneIds, } from "./milestone-ids.js";
|
|
33
35
|
export { showQueue, handleQueueReorder, showQueueAdd, buildExistingMilestonesContext, } from "./guided-flow-queue.js";
|
|
@@ -153,8 +155,32 @@ function parseMilestoneSequenceFromProject(content) {
|
|
|
153
155
|
/**
|
|
154
156
|
* Read GSD-WORKFLOW.md and dispatch it to the LLM with a contextual note.
|
|
155
157
|
* This is the only way the wizard triggers work — everything else is the LLM's job.
|
|
158
|
+
*
|
|
159
|
+
* When a unitType is provided, resolves the user's model preference for that
|
|
160
|
+
* phase (e.g., models.planning → "plan-milestone") and applies it before
|
|
161
|
+
* dispatching. This ensures guided-flow dispatches respect the same
|
|
162
|
+
* per-phase model preferences that auto-mode uses.
|
|
156
163
|
*/
|
|
157
|
-
function dispatchWorkflow(pi, note, customType = "gsd-run") {
|
|
164
|
+
async function dispatchWorkflow(pi, note, customType = "gsd-run", ctx, unitType) {
|
|
165
|
+
// Apply model preference for this unit type (if configured)
|
|
166
|
+
if (ctx && unitType) {
|
|
167
|
+
const modelConfig = resolveModelWithFallbacksForUnit(unitType);
|
|
168
|
+
if (modelConfig) {
|
|
169
|
+
const availableModels = ctx.modelRegistry.getAvailable();
|
|
170
|
+
const modelsToTry = [modelConfig.primary, ...modelConfig.fallbacks];
|
|
171
|
+
for (const modelId of modelsToTry) {
|
|
172
|
+
// Resolve model from available models (same logic as auto-model-selection)
|
|
173
|
+
const model = resolveAvailableModel(modelId, availableModels, ctx.model?.provider);
|
|
174
|
+
if (!model)
|
|
175
|
+
continue;
|
|
176
|
+
const ok = await pi.setModel(model, { persist: false });
|
|
177
|
+
if (ok) {
|
|
178
|
+
debugLog("guided-flow-model-applied", { unitType, model: `${model.provider}/${model.id}` });
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
158
184
|
const workflowPath = process.env.GSD_WORKFLOW_PATH ?? join(process.env.HOME ?? "~", ".gsd", "agent", "GSD-WORKFLOW.md");
|
|
159
185
|
const workflow = readFileSync(workflowPath, "utf-8");
|
|
160
186
|
pi.sendMessage({
|
|
@@ -163,6 +189,31 @@ function dispatchWorkflow(pi, note, customType = "gsd-run") {
|
|
|
163
189
|
display: false,
|
|
164
190
|
}, { triggerTurn: true });
|
|
165
191
|
}
|
|
192
|
+
/**
|
|
193
|
+
* Resolve a model ID string to a model object from available models.
|
|
194
|
+
* Handles "provider/model" and bare ID formats.
|
|
195
|
+
*/
|
|
196
|
+
function resolveAvailableModel(modelId, availableModels, currentProvider) {
|
|
197
|
+
const slashIdx = modelId.indexOf("/");
|
|
198
|
+
if (slashIdx !== -1) {
|
|
199
|
+
const maybeProvider = modelId.substring(0, slashIdx);
|
|
200
|
+
const id = modelId.substring(slashIdx + 1);
|
|
201
|
+
const knownProviders = new Set(availableModels.map(m => m.provider.toLowerCase()));
|
|
202
|
+
if (knownProviders.has(maybeProvider.toLowerCase())) {
|
|
203
|
+
const match = availableModels.find(m => m.provider.toLowerCase() === maybeProvider.toLowerCase()
|
|
204
|
+
&& m.id.toLowerCase() === id.toLowerCase());
|
|
205
|
+
if (match)
|
|
206
|
+
return match;
|
|
207
|
+
}
|
|
208
|
+
// Try matching the full string as a model ID (OpenRouter-style)
|
|
209
|
+
const lower = modelId.toLowerCase();
|
|
210
|
+
return availableModels.find(m => m.id.toLowerCase() === lower
|
|
211
|
+
|| `${m.provider}/${m.id}`.toLowerCase() === lower);
|
|
212
|
+
}
|
|
213
|
+
// Bare ID — prefer current provider, then first available
|
|
214
|
+
const exactProviderMatch = availableModels.find(m => m.id === modelId && m.provider === currentProvider);
|
|
215
|
+
return exactProviderMatch ?? availableModels.find(m => m.id === modelId);
|
|
216
|
+
}
|
|
166
217
|
/**
|
|
167
218
|
* Build the discuss-and-plan prompt for a new milestone.
|
|
168
219
|
* Used by all three "new milestone" paths (first ever, no active, all complete).
|
|
@@ -244,8 +295,8 @@ export async function showHeadlessMilestoneCreation(ctx, pi, basePath, seedConte
|
|
|
244
295
|
const prompt = buildHeadlessDiscussPrompt(nextId, seedContext, basePath);
|
|
245
296
|
// Set pending auto start (auto-mode triggers on "Milestone X ready." via checkAutoStartAfterDiscuss)
|
|
246
297
|
pendingAutoStart = { ctx, pi, basePath, milestoneId: nextId };
|
|
247
|
-
// Dispatch
|
|
248
|
-
dispatchWorkflow(pi, prompt);
|
|
298
|
+
// Dispatch — headless milestone creation is a planning activity
|
|
299
|
+
await dispatchWorkflow(pi, prompt, "gsd-run", ctx, "plan-milestone");
|
|
249
300
|
}
|
|
250
301
|
// ─── Discuss Flow ─────────────────────────────────────────────────────────────
|
|
251
302
|
/**
|
|
@@ -381,23 +432,23 @@ export async function showDiscuss(ctx, pi, basePath) {
|
|
|
381
432
|
? `${basePrompt}\n\n## Prior Discussion (Draft Seed)\n\n${draftContent}`
|
|
382
433
|
: basePrompt;
|
|
383
434
|
pendingAutoStart = { ctx, pi, basePath, milestoneId: mid, step: false };
|
|
384
|
-
dispatchWorkflow(pi, seed, "gsd-discuss");
|
|
435
|
+
await dispatchWorkflow(pi, seed, "gsd-discuss", ctx, "plan-milestone");
|
|
385
436
|
}
|
|
386
437
|
else if (choice === "discuss_fresh") {
|
|
387
438
|
const discussMilestoneTemplates = inlineTemplate("context", "Context");
|
|
388
439
|
const structuredQuestionsAvailable = pi.getActiveTools().includes("ask_user_questions") ? "true" : "false";
|
|
389
440
|
pendingAutoStart = { ctx, pi, basePath, milestoneId: mid, step: false };
|
|
390
|
-
dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
|
|
441
|
+
await dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
|
|
391
442
|
milestoneId: mid, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
|
|
392
443
|
commitInstruction: buildDocsCommitInstruction(`docs(${mid}): milestone context from discuss`),
|
|
393
|
-
}), "gsd-discuss");
|
|
444
|
+
}), "gsd-discuss", ctx, "plan-milestone");
|
|
394
445
|
}
|
|
395
446
|
else if (choice === "skip_milestone") {
|
|
396
447
|
const milestoneIds = findMilestoneIds(basePath);
|
|
397
448
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
398
449
|
const nextId = nextMilestoneId(milestoneIds, uniqueMilestoneIds);
|
|
399
450
|
pendingAutoStart = { ctx, pi, basePath, milestoneId: nextId, step: false };
|
|
400
|
-
dispatchWorkflow(pi, buildDiscussPrompt(nextId, `New milestone ${nextId}.`, basePath));
|
|
451
|
+
await dispatchWorkflow(pi, buildDiscussPrompt(nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "plan-milestone");
|
|
401
452
|
}
|
|
402
453
|
return;
|
|
403
454
|
}
|
|
@@ -484,7 +535,7 @@ export async function showDiscuss(ctx, pi, basePath) {
|
|
|
484
535
|
continue;
|
|
485
536
|
}
|
|
486
537
|
const prompt = await buildDiscussSlicePrompt(mid, chosen.id, chosen.title, basePath, { rediscuss: isRediscuss });
|
|
487
|
-
dispatchWorkflow(pi, prompt, "gsd-discuss");
|
|
538
|
+
await dispatchWorkflow(pi, prompt, "gsd-discuss", ctx, "plan-slice");
|
|
488
539
|
// Wait for the discuss session to finish, then loop back to the picker
|
|
489
540
|
await ctx.waitForIdle();
|
|
490
541
|
invalidateAllCaches();
|
|
@@ -611,7 +662,7 @@ async function handleMilestoneActions(ctx, pi, basePath, milestoneId, milestoneT
|
|
|
611
662
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
612
663
|
const nextId = nextMilestoneId(milestoneIds, uniqueMilestoneIds);
|
|
613
664
|
pendingAutoStart = { ctx, pi, basePath, milestoneId: nextId, step: stepMode };
|
|
614
|
-
dispatchWorkflow(pi, buildDiscussPrompt(nextId, `New milestone ${nextId}.`, basePath));
|
|
665
|
+
await dispatchWorkflow(pi, buildDiscussPrompt(nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "plan-milestone");
|
|
615
666
|
return true;
|
|
616
667
|
}
|
|
617
668
|
// "back" or null
|
|
@@ -729,7 +780,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
729
780
|
if (isFirst) {
|
|
730
781
|
// First ever — skip wizard, just ask directly
|
|
731
782
|
pendingAutoStart = { ctx, pi, basePath, milestoneId: nextId, step: stepMode };
|
|
732
|
-
dispatchWorkflow(pi, buildDiscussPrompt(nextId, `New project, milestone ${nextId}. Do NOT read or explore .gsd/ — it's empty scaffolding.`, basePath));
|
|
783
|
+
await dispatchWorkflow(pi, buildDiscussPrompt(nextId, `New project, milestone ${nextId}. Do NOT read or explore .gsd/ — it's empty scaffolding.`, basePath), "gsd-run", ctx, "plan-milestone");
|
|
733
784
|
}
|
|
734
785
|
else {
|
|
735
786
|
const choice = await showNextAction(ctx, {
|
|
@@ -747,7 +798,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
747
798
|
});
|
|
748
799
|
if (choice === "new_milestone") {
|
|
749
800
|
pendingAutoStart = { ctx, pi, basePath, milestoneId: nextId, step: stepMode };
|
|
750
|
-
dispatchWorkflow(pi, buildDiscussPrompt(nextId, `New milestone ${nextId}.`, basePath));
|
|
801
|
+
await dispatchWorkflow(pi, buildDiscussPrompt(nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "plan-milestone");
|
|
751
802
|
}
|
|
752
803
|
}
|
|
753
804
|
return;
|
|
@@ -779,7 +830,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
779
830
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
780
831
|
const nextId = nextMilestoneId(milestoneIds, uniqueMilestoneIds);
|
|
781
832
|
pendingAutoStart = { ctx, pi, basePath, milestoneId: nextId, step: stepMode };
|
|
782
|
-
dispatchWorkflow(pi, buildDiscussPrompt(nextId, `New milestone ${nextId}.`, basePath));
|
|
833
|
+
await dispatchWorkflow(pi, buildDiscussPrompt(nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "plan-milestone");
|
|
783
834
|
}
|
|
784
835
|
else if (choice === "status") {
|
|
785
836
|
const { fireStatusViaCommand } = await import("./commands.js");
|
|
@@ -825,23 +876,23 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
825
876
|
? `${basePrompt}\n\n## Prior Discussion (Draft Seed)\n\n${draftContent}`
|
|
826
877
|
: basePrompt;
|
|
827
878
|
pendingAutoStart = { ctx, pi, basePath, milestoneId, step: stepMode };
|
|
828
|
-
dispatchWorkflow(pi, seed, "gsd-discuss");
|
|
879
|
+
await dispatchWorkflow(pi, seed, "gsd-discuss", ctx, "plan-milestone");
|
|
829
880
|
}
|
|
830
881
|
else if (choice === "discuss_fresh") {
|
|
831
882
|
const discussMilestoneTemplates = inlineTemplate("context", "Context");
|
|
832
883
|
const structuredQuestionsAvailable = pi.getActiveTools().includes("ask_user_questions") ? "true" : "false";
|
|
833
884
|
pendingAutoStart = { ctx, pi, basePath, milestoneId, step: stepMode };
|
|
834
|
-
dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
|
|
885
|
+
await dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
|
|
835
886
|
milestoneId, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
|
|
836
887
|
commitInstruction: buildDocsCommitInstruction(`docs(${milestoneId}): milestone context from discuss`),
|
|
837
|
-
}), "gsd-discuss");
|
|
888
|
+
}), "gsd-discuss", ctx, "plan-milestone");
|
|
838
889
|
}
|
|
839
890
|
else if (choice === "skip_milestone") {
|
|
840
891
|
const milestoneIds = findMilestoneIds(basePath);
|
|
841
892
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
842
893
|
const nextId = nextMilestoneId(milestoneIds, uniqueMilestoneIds);
|
|
843
894
|
pendingAutoStart = { ctx, pi, basePath, milestoneId: nextId, step: stepMode };
|
|
844
|
-
dispatchWorkflow(pi, buildDiscussPrompt(nextId, `New milestone ${nextId}.`, basePath));
|
|
895
|
+
await dispatchWorkflow(pi, buildDiscussPrompt(nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "plan-milestone");
|
|
845
896
|
}
|
|
846
897
|
return;
|
|
847
898
|
}
|
|
@@ -893,24 +944,33 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
893
944
|
inlineTemplate("secrets-manifest", "Secrets Manifest"),
|
|
894
945
|
].join("\n\n---\n\n");
|
|
895
946
|
const secretsOutputPath = relMilestoneFile(basePath, milestoneId, "SECRETS");
|
|
896
|
-
dispatchWorkflow(pi, loadPrompt("guided-plan-milestone", {
|
|
897
|
-
milestoneId,
|
|
898
|
-
|
|
947
|
+
await dispatchWorkflow(pi, loadPrompt("guided-plan-milestone", {
|
|
948
|
+
milestoneId,
|
|
949
|
+
milestoneTitle,
|
|
950
|
+
secretsOutputPath,
|
|
951
|
+
inlinedTemplates: planMilestoneTemplates,
|
|
952
|
+
skillActivation: buildSkillActivationBlock({
|
|
953
|
+
base: basePath,
|
|
954
|
+
milestoneId,
|
|
955
|
+
milestoneTitle,
|
|
956
|
+
extraContext: [planMilestoneTemplates],
|
|
957
|
+
}),
|
|
958
|
+
}), "gsd-run", ctx, "plan-milestone");
|
|
899
959
|
}
|
|
900
960
|
else if (choice === "discuss") {
|
|
901
961
|
const discussMilestoneTemplates = inlineTemplate("context", "Context");
|
|
902
962
|
const structuredQuestionsAvailable = pi.getActiveTools().includes("ask_user_questions") ? "true" : "false";
|
|
903
|
-
dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
|
|
963
|
+
await dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
|
|
904
964
|
milestoneId, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
|
|
905
965
|
commitInstruction: buildDocsCommitInstruction(`docs(${milestoneId}): milestone context from discuss`),
|
|
906
|
-
}));
|
|
966
|
+
}), "gsd-run", ctx, "plan-milestone");
|
|
907
967
|
}
|
|
908
968
|
else if (choice === "skip_milestone") {
|
|
909
969
|
const milestoneIds = findMilestoneIds(basePath);
|
|
910
970
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
911
971
|
const nextId = nextMilestoneId(milestoneIds, uniqueMilestoneIds);
|
|
912
972
|
pendingAutoStart = { ctx, pi, basePath, milestoneId: nextId, step: stepMode };
|
|
913
|
-
dispatchWorkflow(pi, buildDiscussPrompt(nextId, `New milestone ${nextId}.`, basePath));
|
|
973
|
+
await dispatchWorkflow(pi, buildDiscussPrompt(nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "plan-milestone");
|
|
914
974
|
}
|
|
915
975
|
else if (choice === "discard_milestone") {
|
|
916
976
|
const confirmed = await showConfirm(ctx, {
|
|
@@ -1021,18 +1081,38 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1021
1081
|
inlineTemplate("plan", "Slice Plan"),
|
|
1022
1082
|
inlineTemplate("task-plan", "Task Plan"),
|
|
1023
1083
|
].join("\n\n---\n\n");
|
|
1024
|
-
dispatchWorkflow(pi, loadPrompt("guided-plan-slice", {
|
|
1025
|
-
milestoneId,
|
|
1026
|
-
|
|
1084
|
+
await dispatchWorkflow(pi, loadPrompt("guided-plan-slice", {
|
|
1085
|
+
milestoneId,
|
|
1086
|
+
sliceId,
|
|
1087
|
+
sliceTitle,
|
|
1088
|
+
inlinedTemplates: planSliceTemplates,
|
|
1089
|
+
skillActivation: buildSkillActivationBlock({
|
|
1090
|
+
base: basePath,
|
|
1091
|
+
milestoneId,
|
|
1092
|
+
sliceId,
|
|
1093
|
+
sliceTitle,
|
|
1094
|
+
extraContext: [planSliceTemplates],
|
|
1095
|
+
}),
|
|
1096
|
+
}), "gsd-run", ctx, "plan-slice");
|
|
1027
1097
|
}
|
|
1028
1098
|
else if (choice === "discuss") {
|
|
1029
|
-
dispatchWorkflow(pi, await buildDiscussSlicePrompt(milestoneId, sliceId, sliceTitle, basePath, { rediscuss: hasContext }));
|
|
1099
|
+
await dispatchWorkflow(pi, await buildDiscussSlicePrompt(milestoneId, sliceId, sliceTitle, basePath, { rediscuss: hasContext }), "gsd-run", ctx, "plan-slice");
|
|
1030
1100
|
}
|
|
1031
1101
|
else if (choice === "research") {
|
|
1032
1102
|
const researchTemplates = inlineTemplate("research", "Research");
|
|
1033
|
-
dispatchWorkflow(pi, loadPrompt("guided-research-slice", {
|
|
1034
|
-
milestoneId,
|
|
1035
|
-
|
|
1103
|
+
await dispatchWorkflow(pi, loadPrompt("guided-research-slice", {
|
|
1104
|
+
milestoneId,
|
|
1105
|
+
sliceId,
|
|
1106
|
+
sliceTitle,
|
|
1107
|
+
inlinedTemplates: researchTemplates,
|
|
1108
|
+
skillActivation: buildSkillActivationBlock({
|
|
1109
|
+
base: basePath,
|
|
1110
|
+
milestoneId,
|
|
1111
|
+
sliceId,
|
|
1112
|
+
sliceTitle,
|
|
1113
|
+
extraContext: [researchTemplates],
|
|
1114
|
+
}),
|
|
1115
|
+
}), "gsd-run", ctx, "research-slice");
|
|
1036
1116
|
}
|
|
1037
1117
|
else if (choice === "status") {
|
|
1038
1118
|
const { fireStatusViaCommand } = await import("./commands.js");
|
|
@@ -1075,9 +1155,20 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1075
1155
|
inlineTemplate("slice-summary", "Slice Summary"),
|
|
1076
1156
|
inlineTemplate("uat", "UAT"),
|
|
1077
1157
|
].join("\n\n---\n\n");
|
|
1078
|
-
dispatchWorkflow(pi, loadPrompt("guided-complete-slice", {
|
|
1079
|
-
workingDirectory: basePath,
|
|
1080
|
-
|
|
1158
|
+
await dispatchWorkflow(pi, loadPrompt("guided-complete-slice", {
|
|
1159
|
+
workingDirectory: basePath,
|
|
1160
|
+
milestoneId,
|
|
1161
|
+
sliceId,
|
|
1162
|
+
sliceTitle,
|
|
1163
|
+
inlinedTemplates: completeSliceTemplates,
|
|
1164
|
+
skillActivation: buildSkillActivationBlock({
|
|
1165
|
+
base: basePath,
|
|
1166
|
+
milestoneId,
|
|
1167
|
+
sliceId,
|
|
1168
|
+
sliceTitle,
|
|
1169
|
+
extraContext: [completeSliceTemplates],
|
|
1170
|
+
}),
|
|
1171
|
+
}), "gsd-run", ctx, "complete-slice");
|
|
1081
1172
|
}
|
|
1082
1173
|
else if (choice === "status") {
|
|
1083
1174
|
const { fireStatusViaCommand } = await import("./commands.js");
|
|
@@ -1138,15 +1229,35 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1138
1229
|
}
|
|
1139
1230
|
if (choice === "execute") {
|
|
1140
1231
|
if (hasInterrupted) {
|
|
1141
|
-
dispatchWorkflow(pi, loadPrompt("guided-resume-task", {
|
|
1142
|
-
milestoneId,
|
|
1143
|
-
|
|
1232
|
+
await dispatchWorkflow(pi, loadPrompt("guided-resume-task", {
|
|
1233
|
+
milestoneId,
|
|
1234
|
+
sliceId,
|
|
1235
|
+
skillActivation: buildSkillActivationBlock({
|
|
1236
|
+
base: basePath,
|
|
1237
|
+
milestoneId,
|
|
1238
|
+
sliceId,
|
|
1239
|
+
taskId,
|
|
1240
|
+
taskTitle,
|
|
1241
|
+
}),
|
|
1242
|
+
}), "gsd-run", ctx, "execute-task");
|
|
1144
1243
|
}
|
|
1145
1244
|
else {
|
|
1146
1245
|
const executeTaskTemplates = inlineTemplate("task-summary", "Task Summary");
|
|
1147
|
-
dispatchWorkflow(pi, loadPrompt("guided-execute-task", {
|
|
1148
|
-
milestoneId,
|
|
1149
|
-
|
|
1246
|
+
await dispatchWorkflow(pi, loadPrompt("guided-execute-task", {
|
|
1247
|
+
milestoneId,
|
|
1248
|
+
sliceId,
|
|
1249
|
+
taskId,
|
|
1250
|
+
taskTitle,
|
|
1251
|
+
inlinedTemplates: executeTaskTemplates,
|
|
1252
|
+
skillActivation: buildSkillActivationBlock({
|
|
1253
|
+
base: basePath,
|
|
1254
|
+
milestoneId,
|
|
1255
|
+
sliceId,
|
|
1256
|
+
taskId,
|
|
1257
|
+
taskTitle,
|
|
1258
|
+
extraContext: [executeTaskTemplates],
|
|
1259
|
+
}),
|
|
1260
|
+
}), "gsd-run", ctx, "execute-task");
|
|
1150
1261
|
}
|
|
1151
1262
|
}
|
|
1152
1263
|
else if (choice === "status") {
|
|
@@ -4,65 +4,18 @@
|
|
|
4
4
|
* Separates project-state detection and line rendering from the widget's
|
|
5
5
|
* runtime integrations so the regressions can be tested directly.
|
|
6
6
|
*/
|
|
7
|
-
import { existsSync
|
|
7
|
+
import { existsSync } from "node:fs";
|
|
8
|
+
import { detectProjectState } from "./detection.js";
|
|
8
9
|
import { gsdRoot } from "./paths.js";
|
|
9
|
-
import { join } from "node:path";
|
|
10
10
|
export function detectHealthWidgetProjectState(basePath) {
|
|
11
|
-
|
|
12
|
-
if (!existsSync(root))
|
|
11
|
+
if (!existsSync(gsdRoot(basePath)))
|
|
13
12
|
return "none";
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
try {
|
|
17
|
-
const milestonesDir = join(root, "milestones");
|
|
18
|
-
if (existsSync(milestonesDir)) {
|
|
19
|
-
const entries = readdirSync(milestonesDir, { withFileTypes: true });
|
|
20
|
-
if (entries.some(e => e.isDirectory()))
|
|
21
|
-
return "active";
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
catch { /* non-fatal */ }
|
|
25
|
-
return "initialized";
|
|
13
|
+
const { state } = detectProjectState(basePath);
|
|
14
|
+
return state === "v2-gsd" ? "active" : "initialized";
|
|
26
15
|
}
|
|
27
16
|
function formatCost(n) {
|
|
28
17
|
return n >= 1 ? `$${n.toFixed(2)}` : `${(n * 100).toFixed(1)}¢`;
|
|
29
18
|
}
|
|
30
|
-
function formatProgress(progress) {
|
|
31
|
-
if (!progress)
|
|
32
|
-
return null;
|
|
33
|
-
const parts = [];
|
|
34
|
-
parts.push(`M ${progress.milestones.done}/${progress.milestones.total}`);
|
|
35
|
-
if (progress.slices)
|
|
36
|
-
parts.push(`S ${progress.slices.done}/${progress.slices.total}`);
|
|
37
|
-
if (progress.tasks)
|
|
38
|
-
parts.push(`T ${progress.tasks.done}/${progress.tasks.total}`);
|
|
39
|
-
return parts.length > 0 ? `Progress: ${parts.join(" · ")}` : null;
|
|
40
|
-
}
|
|
41
|
-
function formatEnvironmentSummary(errorCount, warningCount) {
|
|
42
|
-
if (errorCount <= 0 && warningCount <= 0)
|
|
43
|
-
return null;
|
|
44
|
-
const parts = [];
|
|
45
|
-
if (errorCount > 0)
|
|
46
|
-
parts.push(`${errorCount} error${errorCount > 1 ? "s" : ""}`);
|
|
47
|
-
if (warningCount > 0)
|
|
48
|
-
parts.push(`${warningCount} warning${warningCount > 1 ? "s" : ""}`);
|
|
49
|
-
return `Env: ${parts.join(", ")}`;
|
|
50
|
-
}
|
|
51
|
-
function formatBudgetSummary(data) {
|
|
52
|
-
if (data.budgetCeiling !== undefined && data.budgetCeiling > 0) {
|
|
53
|
-
const pct = Math.min(100, (data.budgetSpent / data.budgetCeiling) * 100);
|
|
54
|
-
return `Budget: ${formatCost(data.budgetSpent)}/${formatCost(data.budgetCeiling)} (${pct.toFixed(0)}%)`;
|
|
55
|
-
}
|
|
56
|
-
if (data.budgetSpent > 0) {
|
|
57
|
-
return `Spent: ${formatCost(data.budgetSpent)}`;
|
|
58
|
-
}
|
|
59
|
-
return null;
|
|
60
|
-
}
|
|
61
|
-
function buildExecutionHeadline(data) {
|
|
62
|
-
const status = data.executionStatus ?? "Active project";
|
|
63
|
-
const target = data.executionTarget ?? data.blocker ?? "loading status…";
|
|
64
|
-
return ` GSD ${status}${target ? ` - ${target}` : ""}`;
|
|
65
|
-
}
|
|
66
19
|
/**
|
|
67
20
|
* Build compact health lines for the widget.
|
|
68
21
|
* Returns a string array suitable for setWidget().
|
|
@@ -74,23 +27,32 @@ export function buildHealthLines(data) {
|
|
|
74
27
|
if (data.projectState === "initialized") {
|
|
75
28
|
return [" GSD Project initialized — run /gsd to continue setup"];
|
|
76
29
|
}
|
|
77
|
-
const
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if (data.providerIssue)
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
if (
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (
|
|
93
|
-
|
|
30
|
+
const parts = [];
|
|
31
|
+
const totalIssues = data.environmentErrorCount + data.environmentWarningCount + (data.providerIssue ? 1 : 0);
|
|
32
|
+
if (totalIssues === 0) {
|
|
33
|
+
parts.push("● System OK");
|
|
34
|
+
}
|
|
35
|
+
else if (data.environmentErrorCount > 0 || data.providerIssue?.includes("✗")) {
|
|
36
|
+
parts.push(`✗ ${totalIssues} issue${totalIssues > 1 ? "s" : ""}`);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
parts.push(`⚠ ${totalIssues} warning${totalIssues > 1 ? "s" : ""}`);
|
|
40
|
+
}
|
|
41
|
+
if (data.budgetCeiling !== undefined && data.budgetCeiling > 0) {
|
|
42
|
+
const pct = Math.min(100, (data.budgetSpent / data.budgetCeiling) * 100);
|
|
43
|
+
parts.push(`Budget: ${formatCost(data.budgetSpent)}/${formatCost(data.budgetCeiling)} (${pct.toFixed(0)}%)`);
|
|
44
|
+
}
|
|
45
|
+
else if (data.budgetSpent > 0) {
|
|
46
|
+
parts.push(`Spent: ${formatCost(data.budgetSpent)}`);
|
|
47
|
+
}
|
|
48
|
+
if (data.providerIssue) {
|
|
49
|
+
parts.push(data.providerIssue);
|
|
50
|
+
}
|
|
51
|
+
if (data.environmentErrorCount > 0) {
|
|
52
|
+
parts.push(`Env: ${data.environmentErrorCount} error${data.environmentErrorCount > 1 ? "s" : ""}`);
|
|
53
|
+
}
|
|
54
|
+
else if (data.environmentWarningCount > 0) {
|
|
55
|
+
parts.push(`Env: ${data.environmentWarningCount} warning${data.environmentWarningCount > 1 ? "s" : ""}`);
|
|
94
56
|
}
|
|
95
|
-
return
|
|
57
|
+
return [` ${parts.join(" │ ")}`];
|
|
96
58
|
}
|