gsd-pi 2.5.0 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/cli.js +7 -1
- package/dist/loader.js +21 -3
- package/dist/logo.d.ts +3 -3
- package/dist/logo.js +2 -2
- package/package.json +1 -1
- package/src/resources/extensions/get-secrets-from-user.ts +331 -59
- package/src/resources/extensions/gsd/auto.ts +80 -18
- package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -0
- package/src/resources/extensions/gsd/doctor.ts +23 -4
- package/src/resources/extensions/gsd/files.ts +115 -1
- package/src/resources/extensions/gsd/git-service.ts +67 -105
- package/src/resources/extensions/gsd/gitignore.ts +1 -0
- package/src/resources/extensions/gsd/guided-flow.ts +6 -3
- package/src/resources/extensions/gsd/preferences.ts +8 -0
- package/src/resources/extensions/gsd/prompts/complete-slice.md +7 -5
- package/src/resources/extensions/gsd/prompts/discuss.md +7 -15
- package/src/resources/extensions/gsd/prompts/execute-task.md +2 -6
- package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +4 -0
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +33 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +24 -32
- package/src/resources/extensions/gsd/session-forensics.ts +19 -6
- package/src/resources/extensions/gsd/templates/plan.md +8 -10
- package/src/resources/extensions/gsd/templates/secrets-manifest.md +22 -0
- package/src/resources/extensions/gsd/templates/task-plan.md +6 -6
- package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +196 -0
- package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +469 -0
- package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +170 -0
- package/src/resources/extensions/gsd/tests/git-service.test.ts +106 -0
- package/src/resources/extensions/gsd/tests/manifest-status.test.ts +283 -0
- package/src/resources/extensions/gsd/tests/parsers.test.ts +401 -65
- package/src/resources/extensions/gsd/tests/secure-env-collect.test.ts +185 -0
- package/src/resources/extensions/gsd/types.ts +27 -0
|
@@ -9,8 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { execSync } from "node:child_process";
|
|
12
|
-
import {
|
|
13
|
-
import { join, sep } from "node:path";
|
|
12
|
+
import { sep } from "node:path";
|
|
14
13
|
|
|
15
14
|
import {
|
|
16
15
|
detectWorktreeName,
|
|
@@ -27,8 +26,11 @@ export interface GitPreferences {
|
|
|
27
26
|
snapshots?: boolean;
|
|
28
27
|
pre_merge_check?: boolean | string;
|
|
29
28
|
commit_type?: string;
|
|
29
|
+
main_branch?: string;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
export const VALID_BRANCH_NAME = /^[a-zA-Z0-9_\-\/.]+$/;
|
|
33
|
+
|
|
32
34
|
export interface CommitOptions {
|
|
33
35
|
message: string;
|
|
34
36
|
allowEmpty?: boolean;
|
|
@@ -124,9 +126,11 @@ export class GitServiceImpl {
|
|
|
124
126
|
/**
|
|
125
127
|
* Smart staging: `git add -A` excluding GSD runtime paths via pathspec.
|
|
126
128
|
* Falls back to plain `git add -A` if the exclusion pathspec fails.
|
|
129
|
+
* @param extraExclusions Additional pathspec exclusions beyond RUNTIME_EXCLUSION_PATHS.
|
|
127
130
|
*/
|
|
128
|
-
private smartStage(): void {
|
|
129
|
-
const
|
|
131
|
+
private smartStage(extraExclusions: readonly string[] = []): void {
|
|
132
|
+
const allExclusions = [...RUNTIME_EXCLUSION_PATHS, ...extraExclusions];
|
|
133
|
+
const excludes = allExclusions.map(p => `':(exclude)${p}'`);
|
|
130
134
|
const args = ["add", "-A", "--", ".", ...excludes];
|
|
131
135
|
try {
|
|
132
136
|
this.git(args);
|
|
@@ -158,13 +162,14 @@ export class GitServiceImpl {
|
|
|
158
162
|
/**
|
|
159
163
|
* Auto-commit dirty working tree with a conventional chore message.
|
|
160
164
|
* Returns the commit message on success, or null if nothing to commit.
|
|
165
|
+
* @param extraExclusions Additional paths to exclude from staging (e.g. [".gsd/"] for pre-switch commits).
|
|
161
166
|
*/
|
|
162
|
-
autoCommit(unitType: string, unitId: string): string | null {
|
|
167
|
+
autoCommit(unitType: string, unitId: string, extraExclusions: readonly string[] = []): string | null {
|
|
163
168
|
// Quick check: is there anything dirty at all?
|
|
164
169
|
const status = this.git(["status", "--short"], { allowFailure: true });
|
|
165
170
|
if (!status) return null;
|
|
166
171
|
|
|
167
|
-
this.smartStage();
|
|
172
|
+
this.smartStage(extraExclusions);
|
|
168
173
|
|
|
169
174
|
// After smart staging, check if anything was actually staged
|
|
170
175
|
// (all changes might have been runtime files that got excluded)
|
|
@@ -184,6 +189,11 @@ export class GitServiceImpl {
|
|
|
184
189
|
* In the main tree: origin/HEAD symbolic-ref → main/master fallback → current branch.
|
|
185
190
|
*/
|
|
186
191
|
getMainBranch(): string {
|
|
192
|
+
// Explicit preference takes priority (double-check validity as defense-in-depth)
|
|
193
|
+
if (this.prefs.main_branch && VALID_BRANCH_NAME.test(this.prefs.main_branch)) {
|
|
194
|
+
return this.prefs.main_branch;
|
|
195
|
+
}
|
|
196
|
+
|
|
187
197
|
const wtName = detectWorktreeName(this.basePath);
|
|
188
198
|
if (wtName) {
|
|
189
199
|
const wtBranch = `worktree/${wtName}`;
|
|
@@ -249,9 +259,6 @@ export class GitServiceImpl {
|
|
|
249
259
|
* branch (preserves planning artifacts). Falls back to main when on another
|
|
250
260
|
* slice branch (avoids chaining slice branches).
|
|
251
261
|
*
|
|
252
|
-
* When creating a new branch, fetches from remote first (best-effort) to
|
|
253
|
-
* ensure the local main is up-to-date.
|
|
254
|
-
*
|
|
255
262
|
* Auto-commits dirty state via smart staging before checkout so runtime
|
|
256
263
|
* files are never accidentally committed during branch switches.
|
|
257
264
|
*
|
|
@@ -272,9 +279,8 @@ export class GitServiceImpl {
|
|
|
272
279
|
if (remotes) {
|
|
273
280
|
const remote = this.prefs.remote ?? "origin";
|
|
274
281
|
const fetchResult = this.git(["fetch", "--prune", remote], { allowFailure: true });
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
if (remotes.split("\n").includes(remote)) {
|
|
282
|
+
if (fetchResult === "" && remotes.split("\n").includes(remote)) {
|
|
283
|
+
// Check if local is behind upstream (informational only)
|
|
278
284
|
const behind = this.git(
|
|
279
285
|
["rev-list", "--count", "HEAD..@{upstream}"],
|
|
280
286
|
{ allowFailure: true },
|
|
@@ -302,8 +308,9 @@ export class GitServiceImpl {
|
|
|
302
308
|
}
|
|
303
309
|
}
|
|
304
310
|
|
|
305
|
-
// Auto-commit dirty state via smart staging before checkout
|
|
306
|
-
|
|
311
|
+
// Auto-commit dirty state via smart staging before checkout.
|
|
312
|
+
// Exclude .gsd/ to prevent merge conflicts when both branches modify planning artifacts.
|
|
313
|
+
this.autoCommit("pre-switch", current, [".gsd/"]);
|
|
307
314
|
|
|
308
315
|
this.git(["checkout", branch]);
|
|
309
316
|
return created;
|
|
@@ -317,7 +324,8 @@ export class GitServiceImpl {
|
|
|
317
324
|
const current = this.getCurrentBranch();
|
|
318
325
|
if (current === mainBranch) return;
|
|
319
326
|
|
|
320
|
-
|
|
327
|
+
// Exclude .gsd/ to prevent merge conflicts when both branches modify planning artifacts.
|
|
328
|
+
this.autoCommit("pre-switch", current, [".gsd/"]);
|
|
321
329
|
|
|
322
330
|
this.git(["checkout", mainBranch]);
|
|
323
331
|
}
|
|
@@ -348,95 +356,42 @@ export class GitServiceImpl {
|
|
|
348
356
|
/**
|
|
349
357
|
* Run pre-merge verification check. Auto-detects test runner from project
|
|
350
358
|
* files, or uses custom command from prefs.pre_merge_check.
|
|
351
|
-
*
|
|
352
|
-
*
|
|
353
|
-
* - `false` → skip (return passed:true, skipped:true)
|
|
354
|
-
* - non-empty string (not "auto") → use as custom command
|
|
355
|
-
* - `true`, `"auto"`, or `undefined` → auto-detect from project files
|
|
356
|
-
*
|
|
357
|
-
* Auto-detection order:
|
|
358
|
-
* package.json scripts.test → npm test
|
|
359
|
-
* package.json scripts.build (only if no test) → npm run build
|
|
360
|
-
* Cargo.toml → cargo test
|
|
361
|
-
* Makefile with test: target → make test
|
|
362
|
-
* pyproject.toml → python -m pytest
|
|
363
|
-
*
|
|
364
|
-
* If no runner detected in auto mode, returns passed:true (don't block).
|
|
359
|
+
* Gated on prefs.pre_merge_check (false = skip, string = custom command).
|
|
360
|
+
* Stub: to be implemented in T03.
|
|
365
361
|
*/
|
|
366
362
|
runPreMergeCheck(): PreMergeCheckResult {
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
// Explicitly disabled
|
|
370
|
-
if (pref === false) {
|
|
363
|
+
if (this.prefs.pre_merge_check === false || this.prefs.pre_merge_check === undefined) {
|
|
371
364
|
return { passed: true, skipped: true };
|
|
372
365
|
}
|
|
373
366
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
367
|
+
// Determine command: explicit string or auto-detect from package.json
|
|
368
|
+
let command: string;
|
|
369
|
+
if (typeof this.prefs.pre_merge_check === "string") {
|
|
370
|
+
command = this.prefs.pre_merge_check;
|
|
371
|
+
} else {
|
|
372
|
+
// Auto-detect: look for package.json with a test script
|
|
373
|
+
try {
|
|
374
|
+
const pkg = execSync("cat package.json", { cwd: this.basePath, encoding: "utf-8" });
|
|
375
|
+
const parsed = JSON.parse(pkg);
|
|
376
|
+
if (parsed.scripts?.test) {
|
|
377
|
+
command = "npm test";
|
|
378
|
+
} else {
|
|
379
|
+
return { passed: true, skipped: true };
|
|
380
|
+
}
|
|
381
|
+
} catch {
|
|
382
|
+
return { passed: true, skipped: true };
|
|
383
|
+
}
|
|
388
384
|
}
|
|
389
385
|
|
|
390
|
-
// Execute the command
|
|
391
386
|
try {
|
|
392
|
-
execSync(command, {
|
|
393
|
-
|
|
394
|
-
timeout: 300_000,
|
|
395
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
396
|
-
encoding: "utf-8",
|
|
397
|
-
});
|
|
398
|
-
return { passed: true, command };
|
|
387
|
+
execSync(command, { cwd: this.basePath, stdio: "pipe", encoding: "utf-8" });
|
|
388
|
+
return { passed: true, skipped: false, command };
|
|
399
389
|
} catch (err) {
|
|
400
|
-
const
|
|
401
|
-
|
|
402
|
-
: String(err).slice(0, 2000);
|
|
403
|
-
return { passed: false, command, error: stderr };
|
|
390
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
391
|
+
return { passed: false, skipped: false, command, error: msg };
|
|
404
392
|
}
|
|
405
393
|
}
|
|
406
394
|
|
|
407
|
-
/**
|
|
408
|
-
* Detect a test/build runner from project files in basePath.
|
|
409
|
-
* Returns the command string or null if nothing detected.
|
|
410
|
-
*/
|
|
411
|
-
private detectTestRunner(): string | null {
|
|
412
|
-
const pkgPath = join(this.basePath, "package.json");
|
|
413
|
-
if (existsSync(pkgPath)) {
|
|
414
|
-
try {
|
|
415
|
-
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
416
|
-
if (pkg?.scripts?.test) return "npm test";
|
|
417
|
-
if (pkg?.scripts?.build) return "npm run build";
|
|
418
|
-
} catch { /* invalid JSON — skip */ }
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
if (existsSync(join(this.basePath, "Cargo.toml"))) {
|
|
422
|
-
return "cargo test";
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
const makefilePath = join(this.basePath, "Makefile");
|
|
426
|
-
if (existsSync(makefilePath)) {
|
|
427
|
-
try {
|
|
428
|
-
const content = readFileSync(makefilePath, "utf-8");
|
|
429
|
-
if (/^test\s*:/m.test(content)) return "make test";
|
|
430
|
-
} catch { /* skip */ }
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
if (existsSync(join(this.basePath, "pyproject.toml"))) {
|
|
434
|
-
return "python -m pytest";
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
return null;
|
|
438
|
-
}
|
|
439
|
-
|
|
440
395
|
// ─── Merge ─────────────────────────────────────────────────────────────
|
|
441
396
|
|
|
442
397
|
/**
|
|
@@ -522,7 +477,8 @@ export class GitServiceImpl {
|
|
|
522
477
|
);
|
|
523
478
|
}
|
|
524
479
|
|
|
525
|
-
// Snapshot the branch HEAD before merge (gated on prefs
|
|
480
|
+
// Snapshot the branch HEAD before merge (gated on prefs)
|
|
481
|
+
// We need to save the ref while the branch still exists
|
|
526
482
|
this.createSnapshot(branch);
|
|
527
483
|
|
|
528
484
|
// Build rich commit message before squash (needs branch history)
|
|
@@ -531,18 +487,20 @@ export class GitServiceImpl {
|
|
|
531
487
|
commitType, milestoneId, sliceId, sliceTitle, mainBranch, branch,
|
|
532
488
|
);
|
|
533
489
|
|
|
534
|
-
// Squash merge
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
//
|
|
541
|
-
this.git(["reset", "--hard", "HEAD"]);
|
|
542
|
-
const
|
|
543
|
-
const errInfo = checkResult.error ? `\n${checkResult.error}` : "";
|
|
490
|
+
// Squash merge — abort cleanly on conflict so the working tree is never
|
|
491
|
+
// left in a half-merged state (see: merge-bug-fix).
|
|
492
|
+
try {
|
|
493
|
+
this.git(["merge", "--squash", branch]);
|
|
494
|
+
} catch (mergeError) {
|
|
495
|
+
// git merge --squash exits non-zero on conflict. The working tree now
|
|
496
|
+
// has conflict markers and a dirty index. Reset to restore a clean state.
|
|
497
|
+
this.git(["reset", "--hard", "HEAD"], { allowFailure: true });
|
|
498
|
+
const msg = mergeError instanceof Error ? mergeError.message : String(mergeError);
|
|
544
499
|
throw new Error(
|
|
545
|
-
`
|
|
500
|
+
`Squash-merge of "${branch}" into "${mainBranch}" failed with conflicts. ` +
|
|
501
|
+
`Working tree has been reset to a clean state. ` +
|
|
502
|
+
`Resolve manually: git checkout ${mainBranch} && git merge --squash ${branch}\n` +
|
|
503
|
+
`Original error: ${msg}`,
|
|
546
504
|
);
|
|
547
505
|
}
|
|
548
506
|
|
|
@@ -555,7 +513,11 @@ export class GitServiceImpl {
|
|
|
555
513
|
// Auto-push to remote if enabled
|
|
556
514
|
if (this.prefs.auto_push === true) {
|
|
557
515
|
const remote = this.prefs.remote ?? "origin";
|
|
558
|
-
this.git(["push", remote, mainBranch], { allowFailure: true });
|
|
516
|
+
const pushResult = this.git(["push", remote, mainBranch], { allowFailure: true });
|
|
517
|
+
if (pushResult === "") {
|
|
518
|
+
// push succeeded (empty stdout is normal) or failed silently
|
|
519
|
+
// Verify by checking if remote is reachable — the allowFailure handles errors
|
|
520
|
+
}
|
|
559
521
|
}
|
|
560
522
|
|
|
561
523
|
return {
|
|
@@ -145,6 +145,7 @@ See \`~/.gsd/agent/extensions/gsd/docs/preferences-reference.md\` for full field
|
|
|
145
145
|
- \`models\`: Model preferences for specific task types
|
|
146
146
|
- \`skill_discovery\`: Automatic skill detection preferences
|
|
147
147
|
- \`auto_supervisor\`: Supervision and gating rules for autonomous modes
|
|
148
|
+
- \`git\`: Git preferences — \`main_branch\` (default branch name for new repos, e.g., "main", "master", "trunk"), \`auto_push\`, \`snapshots\`, etc.
|
|
148
149
|
|
|
149
150
|
## Examples
|
|
150
151
|
|
|
@@ -20,8 +20,9 @@ import {
|
|
|
20
20
|
} from "./paths.js";
|
|
21
21
|
import { join } from "node:path";
|
|
22
22
|
import { readFileSync, existsSync, mkdirSync, readdirSync } from "node:fs";
|
|
23
|
-
import { execSync } from "node:child_process";
|
|
23
|
+
import { execSync, execFileSync } from "node:child_process";
|
|
24
24
|
import { ensureGitignore, ensurePreferences } from "./gitignore.js";
|
|
25
|
+
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
25
26
|
|
|
26
27
|
// ─── Auto-start after discuss ─────────────────────────────────────────────────
|
|
27
28
|
|
|
@@ -444,7 +445,8 @@ export async function showSmartEntry(
|
|
|
444
445
|
try {
|
|
445
446
|
execSync("git rev-parse --git-dir", { cwd: basePath, stdio: "pipe" });
|
|
446
447
|
} catch {
|
|
447
|
-
|
|
448
|
+
const mainBranch = loadEffectiveGSDPreferences()?.preferences?.git?.main_branch || "main";
|
|
449
|
+
execFileSync("git", ["init", "-b", mainBranch], { cwd: basePath, stdio: "pipe" });
|
|
448
450
|
}
|
|
449
451
|
|
|
450
452
|
// ── Ensure .gitignore has baseline patterns ──────────────────────────
|
|
@@ -609,8 +611,9 @@ export async function showSmartEntry(
|
|
|
609
611
|
});
|
|
610
612
|
|
|
611
613
|
if (choice === "plan") {
|
|
614
|
+
const secretsOutputPath = relMilestoneFile(basePath, milestoneId, "SECRETS");
|
|
612
615
|
dispatchWorkflow(pi, loadPrompt("guided-plan-milestone", {
|
|
613
|
-
milestoneId, milestoneTitle,
|
|
616
|
+
milestoneId, milestoneTitle, secretsOutputPath,
|
|
614
617
|
}));
|
|
615
618
|
} else if (choice === "discuss") {
|
|
616
619
|
dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
|
|
@@ -3,6 +3,7 @@ import { homedir } from "node:os";
|
|
|
3
3
|
import { isAbsolute, join } from "node:path";
|
|
4
4
|
import { getAgentDir } from "@mariozechner/pi-coding-agent";
|
|
5
5
|
import type { GitPreferences } from "./git-service.ts";
|
|
6
|
+
import { VALID_BRANCH_NAME } from "./git-service.ts";
|
|
6
7
|
|
|
7
8
|
const GLOBAL_PREFERENCES_PATH = join(homedir(), ".gsd", "preferences.md");
|
|
8
9
|
const LEGACY_GLOBAL_PREFERENCES_PATH = join(homedir(), ".pi", "agent", "gsd-preferences.md");
|
|
@@ -639,6 +640,13 @@ function validatePreferences(preferences: GSDPreferences): {
|
|
|
639
640
|
errors.push(`git.commit_type must be one of: feat, fix, refactor, docs, test, chore, perf, ci, build, style`);
|
|
640
641
|
}
|
|
641
642
|
}
|
|
643
|
+
if (g.main_branch !== undefined) {
|
|
644
|
+
if (typeof g.main_branch === "string" && g.main_branch.trim() !== "" && VALID_BRANCH_NAME.test(g.main_branch)) {
|
|
645
|
+
git.main_branch = g.main_branch;
|
|
646
|
+
} else {
|
|
647
|
+
errors.push("git.main_branch must be a valid branch name (alphanumeric, _, -, /, .)");
|
|
648
|
+
}
|
|
649
|
+
}
|
|
642
650
|
|
|
643
651
|
if (Object.keys(git).length > 0) {
|
|
644
652
|
validated.git = git as GitPreferences;
|
|
@@ -6,17 +6,19 @@ All relevant context has been preloaded below — the slice plan, all task summa
|
|
|
6
6
|
|
|
7
7
|
{{inlinedContext}}
|
|
8
8
|
|
|
9
|
+
**Match effort to complexity.** A simple slice with 1-2 tasks needs a brief summary and lightweight verification. A complex slice with 5 tasks across multiple subsystems needs thorough verification and a detailed summary. Scale the work below accordingly.
|
|
10
|
+
|
|
9
11
|
Then:
|
|
10
12
|
1. Read the templates:
|
|
11
13
|
- `~/.gsd/agent/extensions/gsd/templates/slice-summary.md`
|
|
12
14
|
- `~/.gsd/agent/extensions/gsd/templates/uat.md`
|
|
13
15
|
2. If a `GSD Skill Preferences` block is present in system context, use it to decide which skills to load and follow during completion, without relaxing required verification or artifact rules
|
|
14
16
|
3. Run all slice-level verification checks defined in the slice plan. All must pass before marking the slice done. If any fail, fix them first.
|
|
15
|
-
4.
|
|
16
|
-
5. If `.gsd/REQUIREMENTS.md` exists, update it based on what this slice actually proved. Move requirements between Active, Validated, Deferred, Blocked, or Out of Scope only when the evidence from execution supports that change.
|
|
17
|
-
6. Write `{{sliceSummaryAbsPath}}` (compress all task summaries).
|
|
18
|
-
7. Write `{{sliceUatAbsPath}}`.
|
|
19
|
-
8. Review task summaries for `key_decisions`.
|
|
17
|
+
4. If the slice plan includes observability/diagnostic surfaces, confirm they work. Skip this for simple slices that don't have observability sections.
|
|
18
|
+
5. If `.gsd/REQUIREMENTS.md` exists, update it based on what this slice actually proved. Move requirements between Active, Validated, Deferred, Blocked, or Out of Scope only when the evidence from execution supports that change.
|
|
19
|
+
6. Write `{{sliceSummaryAbsPath}}` (compress all task summaries).
|
|
20
|
+
7. Write `{{sliceUatAbsPath}}`.
|
|
21
|
+
8. Review task summaries for `key_decisions`. Append any significant decisions to `.gsd/DECISIONS.md` if missing.
|
|
20
22
|
9. Mark {{sliceId}} done in `{{roadmapPath}}` (change `[ ]` to `[x]`)
|
|
21
23
|
10. Do not commit or squash-merge manually — the system auto-commits your changes and handles the merge after this unit succeeds.
|
|
22
24
|
11. Update `.gsd/PROJECT.md` if it exists — refresh current state if needed.
|
|
@@ -9,7 +9,7 @@ Special handling: if the user message is not a project description (for example,
|
|
|
9
9
|
After the user describes their idea, **do not ask questions yet**. First, prove you understood by reflecting back:
|
|
10
10
|
|
|
11
11
|
1. Summarize what you understood in your own words — concretely, not abstractly.
|
|
12
|
-
2.
|
|
12
|
+
2. Give an honest size read: roughly how many milestones, roughly how many slices in the first one. Base this on the actual work involved, not a classification label. A config change might be 1 milestone with 1 slice. A social network might be 5 milestones with 8+ slices each. Use your judgment.
|
|
13
13
|
3. Include scope honesty — a bullet list of the major capabilities you're hearing: "Here's what I'm hearing: [bullet list of major capabilities]."
|
|
14
14
|
4. Ask: "Did I get that right, or did I miss something?" — plain text, not `ask_user_questions`. Let them correct freely.
|
|
15
15
|
|
|
@@ -17,18 +17,14 @@ This prevents runaway questioning by forcing comprehension proof before anything
|
|
|
17
17
|
|
|
18
18
|
## Vision Mapping
|
|
19
19
|
|
|
20
|
-
After reflection is confirmed,
|
|
20
|
+
After reflection is confirmed, decide the approach based on the actual scope — not a label:
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
- **Project** — a coherent product with multiple major capabilities (multi-milestone likely)
|
|
24
|
-
- **Product/Platform** — a large vision with distinct phases, audiences, or systems (definitely multi-milestone)
|
|
25
|
-
|
|
26
|
-
**For Project or Product/Platform scale:** Before drilling into details, map the full landscape:
|
|
22
|
+
**If the work spans multiple milestones:** Before drilling into details, map the full landscape:
|
|
27
23
|
1. Propose a milestone sequence — names, one-line intents, rough dependencies
|
|
28
24
|
2. Present this to the user for confirmation or adjustment
|
|
29
25
|
3. Only then begin the deep Q&A — and scope the Q&A to the full vision, not just M001
|
|
30
26
|
|
|
31
|
-
**
|
|
27
|
+
**If the work fits in a single milestone:** Proceed directly to questioning.
|
|
32
28
|
|
|
33
29
|
**Anti-reduction rule:** If the user describes a big vision, plan the big vision. Do not ask "what's the minimum viable version?" or try to reduce scope unless the user explicitly asks for an MVP or minimal version. When something is complex or risky, phase it into a later milestone — do not cut it. The user's ambition is the target, and your job is to sequence it intelligently, not shrink it.
|
|
34
30
|
|
|
@@ -77,15 +73,13 @@ Do NOT offer to proceed until ALL of the following are satisfied. Track these in
|
|
|
77
73
|
- [ ] **The biggest technical unknowns / risks** — what could fail, what hasn't been proven
|
|
78
74
|
- [ ] **What external systems/services this touches** — APIs, databases, third-party services, hardware
|
|
79
75
|
|
|
80
|
-
**
|
|
81
|
-
- **Task scale:** at least 2 full rounds (6+ questions asked and answered)
|
|
82
|
-
- **Project/Product scale:** at least 4 full rounds (12+ questions asked and answered)
|
|
76
|
+
**Questioning depth should match scope.** Simple, well-defined work needs fewer rounds — maybe 1-2. Large, ambiguous visions need more — maybe 4+. Don't pad rounds to hit a number. Stop when the depth checklist is satisfied and you genuinely understand the work.
|
|
83
77
|
|
|
84
78
|
Do not count the reflection step as a question round. Rounds start after reflection is confirmed.
|
|
85
79
|
|
|
86
80
|
## Wrap-up Gate
|
|
87
81
|
|
|
88
|
-
Only after the depth checklist is fully satisfied
|
|
82
|
+
Only after the depth checklist is fully satisfied and you genuinely understand the work, offer to proceed.
|
|
89
83
|
|
|
90
84
|
The wrap-up gate must include a scope reflection:
|
|
91
85
|
"Here's what I'm planning to build: [list of capabilities with rough complexity]. Does this match your vision, or did I miss something?"
|
|
@@ -149,9 +143,7 @@ If the project is new or has no `REQUIREMENTS.md`, confirm candidate requirement
|
|
|
149
143
|
|
|
150
144
|
## Scope Assessment
|
|
151
145
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
If Vision Mapping classified the work as Task but discussion revealed Project-scale complexity, upgrade to multi-milestone and propose the split. If Vision Mapping classified it as Project but the scope narrowed to a single coherent body of work (roughly 2-12 slices), downgrade to single-milestone.
|
|
146
|
+
Before moving to output, confirm the size estimate from your reflection still holds. Discussion often reveals hidden complexity or simplifies things. If the scope grew or shrank significantly during Q&A, adjust the milestone and slice counts accordingly. Be honest — if something you thought was multi-milestone turns out to be 3 slices, plan 3 slices. If something you thought was simple turns out to need multiple milestones, say so.
|
|
155
147
|
|
|
156
148
|
## Output Phase
|
|
157
149
|
|
|
@@ -24,11 +24,7 @@ Then:
|
|
|
24
24
|
2. Execute the steps in the inlined task plan
|
|
25
25
|
3. Build the real thing. If the task plan says "create login endpoint", build an endpoint that actually authenticates against a real store, not one that returns a hardcoded success response. If the task plan says "create dashboard page", build a page that renders real data from the API, not a component with hardcoded props. Stubs and mocks are for tests, not for the shipped feature.
|
|
26
26
|
4. Write or update tests as part of execution — tests are verification, not an afterthought. If the slice plan defines test files in its Verification section and this is the first task, create them (they should initially fail).
|
|
27
|
-
5. When implementing non-trivial runtime behavior, add or preserve agent-usable observability
|
|
28
|
-
- Prefer structured logs/events, stable error codes/types, and explicit status surfaces over ad hoc console text
|
|
29
|
-
- Ensure failures are externally inspectable rather than swallowed or hidden
|
|
30
|
-
- Persist high-value failure state when it materially improves retries, recovery, or later debugging
|
|
31
|
-
- Never log secrets, tokens, or sensitive raw payloads unnecessarily
|
|
27
|
+
5. When implementing non-trivial runtime behavior (async flows, API boundaries, background processes, error paths), add or preserve agent-usable observability. Skip this for simple changes where it doesn't apply.
|
|
32
28
|
6. Verify must-haves are met by running concrete checks (tests, commands, observable behaviors)
|
|
33
29
|
7. Run the slice-level verification checks defined in the slice plan's Verification section. Track which pass. On the final task of the slice, all must pass before marking done. On intermediate tasks, partial passes are expected — note which ones pass in the summary.
|
|
34
30
|
8. If the task touches UI, browser flows, DOM behavior, or user-visible web state:
|
|
@@ -38,7 +34,7 @@ Then:
|
|
|
38
34
|
- use `browser_diff` when an action's effect is ambiguous
|
|
39
35
|
- use console/network/dialog diagnostics when validating async, stateful, or failure-prone UI
|
|
40
36
|
- record verification in terms of explicit checks passed/failed, not only prose interpretation
|
|
41
|
-
9. If
|
|
37
|
+
9. If the task plan includes an Observability Impact section, verify those signals directly. Skip this step if the task plan omits the section.
|
|
42
38
|
10. **If execution is running long or verification fails:**
|
|
43
39
|
|
|
44
40
|
**Context budget:** If you've used most of your context and haven't finished all steps, stop implementing and prioritize writing the task summary with clear notes on what's done and what remains. A partial summary that enables clean resumption is more valuable than one more half-finished step with no documentation. Never sacrifice summary quality for one more implementation step.
|
|
@@ -21,3 +21,7 @@ Plan milestone {{milestoneId}} ("{{milestoneTitle}}"). Read `.gsd/DECISIONS.md`
|
|
|
21
21
|
- **Don't invent risks.** If the project is straightforward, skip the proof strategy and just ship value in smart order. Not everything has major unknowns.
|
|
22
22
|
- **Ship features, not proofs.** A completed slice should leave the product in a state where the new capability is actually usable through its real interface. A login flow slice ends with a working login page, not a middleware function. An API slice ends with endpoints that return real data from a real store, not hardcoded fixtures. A dashboard slice ends with a real dashboard rendering real data, not a component that renders mock props. If a slice can't ship the real thing yet because a dependency isn't built, it should ship with realistic stubs that are clearly marked for replacement — but the user-facing surface must be real.
|
|
23
23
|
- **Ambition matches the milestone.** The number and depth of slices should match the milestone's ambition. A milestone promising "core platform with auth, data model, and primary user loop" should have enough slices to actually deliver all three as working features — not two proof-of-concept slices and a note that "the rest will come in the next milestone." If the milestone's context promises an outcome, the roadmap must deliver it.
|
|
24
|
+
|
|
25
|
+
## Secret Forecasting
|
|
26
|
+
|
|
27
|
+
After writing the roadmap, analyze the slices and their boundary maps for external service dependencies (third-party APIs, SaaS platforms, cloud providers, databases requiring credentials, OAuth providers, etc.). If this milestone requires any external API keys or secrets, read the template at `~/.gsd/agent/extensions/gsd/templates/secrets-manifest.md` for the expected format and write `{{secretsOutputPath}}` listing every predicted secret as an H3 section with the Service name, a direct Dashboard URL to the console page where the key is created, a Format hint showing what the key looks like, Status set to `pending`, and Destination (`dotenv`, `vercel`, or `convex`). Include numbered step-by-step guidance for obtaining each key. If this milestone does not require any external API keys or secrets, skip this step entirely — do not create an empty manifest.
|
|
@@ -12,7 +12,7 @@ Then:
|
|
|
12
12
|
1. Read the template at `~/.gsd/agent/extensions/gsd/templates/roadmap.md`
|
|
13
13
|
2. Read `.gsd/REQUIREMENTS.md` if it exists. Treat **Active** requirements as the capability contract for planning. If it does not exist, continue in legacy compatibility mode but explicitly note that requirement coverage is operating without a contract.
|
|
14
14
|
3. If a `GSD Skill Preferences` block is present in system context, use it to decide which skills to load and follow during planning, without overriding required roadmap formatting
|
|
15
|
-
4. Create the roadmap: decompose into demoable vertical slices — as many as the work needs, no more
|
|
15
|
+
4. Create the roadmap: decompose into demoable vertical slices — as many as the work genuinely needs, no more. A simple feature might be 1 slice. Don't decompose for decomposition's sake.
|
|
16
16
|
5. Order by risk (high-risk first)
|
|
17
17
|
6. Write `{{outputPath}}` with checkboxes, risk, depends, demo sentences, proof strategy, verification classes, milestone definition of done, **requirement coverage**, and a boundary map. Write success criteria as observable truths, not implementation tasks. If the milestone crosses multiple runtime boundaries, include an explicit final integration slice that proves the assembled system works end-to-end in a real environment
|
|
18
18
|
7. If planning produced structural decisions (e.g. slice ordering rationale, technology choices, scope exclusions), append them to `.gsd/DECISIONS.md` (read the template at `~/.gsd/agent/extensions/gsd/templates/decisions.md` if the file doesn't exist yet)
|
|
@@ -43,6 +43,38 @@ Apply these when decomposing and ordering slices:
|
|
|
43
43
|
- **Don't invent risks.** If the project is straightforward, skip the proof strategy and just ship value in smart order. Not everything has major unknowns.
|
|
44
44
|
- **Ship features, not proofs.** A completed slice should leave the product in a state where the new capability is actually usable through its real interface. A login flow slice ends with a working login page, not a middleware function. An API slice ends with endpoints that return real data from a real store, not hardcoded fixtures. A dashboard slice ends with a real dashboard rendering real data, not a component that renders mock props. If a slice can't ship the real thing yet because a dependency isn't built, it should ship with realistic stubs that are clearly marked for replacement — but the user-facing surface must be real.
|
|
45
45
|
- **Ambition matches the milestone.** The number and depth of slices should match the milestone's ambition. A milestone promising "core platform with auth, data model, and primary user loop" should have enough slices to actually deliver all three as working features — not two proof-of-concept slices and a note that "the rest will come in the next milestone." If the milestone's context promises an outcome, the roadmap must deliver it.
|
|
46
|
+
- **Right-size the decomposition.** Match slice count to actual complexity. If the work is small enough to build and verify in one pass, it's one slice — don't split it into three just because you can identify sub-steps. Multiple requirements can share a single slice. Conversely, don't cram genuinely independent capabilities into one slice just to keep the count low. Let the work dictate the structure.
|
|
47
|
+
|
|
48
|
+
## Single-Slice Fast Path
|
|
49
|
+
|
|
50
|
+
If the roadmap has only one slice, also write the slice plan and task plans inline during this unit — don't leave them for a separate planning session.
|
|
51
|
+
|
|
52
|
+
1. Read the templates:
|
|
53
|
+
- `~/.gsd/agent/extensions/gsd/templates/plan.md`
|
|
54
|
+
- `~/.gsd/agent/extensions/gsd/templates/task-plan.md`
|
|
55
|
+
2. `mkdir -p {{milestonePath}}/slices/S01/tasks`
|
|
56
|
+
3. Write the S01 plan file at `{{milestonePath}}/slices/S01/S01-PLAN.md`
|
|
57
|
+
4. Write individual task plans at `{{milestonePath}}/slices/S01/tasks/T01-PLAN.md`, etc.
|
|
58
|
+
5. For simple slices, keep the plan lean — omit Proof Level, Integration Closure, and Observability sections if they would all be "none". Executable verification commands are sufficient.
|
|
59
|
+
|
|
60
|
+
This eliminates a separate research-slice + plan-slice cycle when the work is straightforward.
|
|
61
|
+
|
|
62
|
+
## Secret Forecasting
|
|
63
|
+
|
|
64
|
+
After writing the roadmap, analyze the slices and their boundary maps for external service dependencies (third-party APIs, SaaS platforms, cloud providers, databases requiring credentials, OAuth providers, etc.).
|
|
65
|
+
|
|
66
|
+
If this milestone requires any external API keys or secrets:
|
|
67
|
+
|
|
68
|
+
1. Read the template at `~/.gsd/agent/extensions/gsd/templates/secrets-manifest.md` for the expected format
|
|
69
|
+
2. Write `{{secretsOutputPath}}` listing every predicted secret as an H3 section with:
|
|
70
|
+
- **Service** — the external service name
|
|
71
|
+
- **Dashboard** — direct URL to the console/dashboard page where the key is created (not a generic homepage)
|
|
72
|
+
- **Format hint** — what the key looks like (e.g. `sk-...`, `ghp_...`, 40-char hex, UUID)
|
|
73
|
+
- **Status** — always `pending` during planning
|
|
74
|
+
- **Destination** — `dotenv`, `vercel`, or `convex` depending on where the key will be consumed
|
|
75
|
+
- Numbered step-by-step guidance for obtaining the key (navigate to dashboard → create project → generate key → copy)
|
|
76
|
+
|
|
77
|
+
If this milestone does not require any external API keys or secrets, skip this step entirely — do not create an empty manifest.
|
|
46
78
|
|
|
47
79
|
**You MUST write the file `{{outputAbsPath}}` before finishing.**
|
|
48
80
|
|
|
@@ -12,7 +12,9 @@ Pay particular attention to **Forward Intelligence** sections — they contain h
|
|
|
12
12
|
|
|
13
13
|
{{dependencySummaries}}
|
|
14
14
|
|
|
15
|
-
Narrate your decomposition reasoning — why you're grouping work this way, what risks are driving the order, what verification strategy you're choosing and why.
|
|
15
|
+
Narrate your decomposition reasoning — why you're grouping work this way, what risks are driving the order, what verification strategy you're choosing and why. Keep the narration proportional to the work — a simple slice doesn't need a long justification.
|
|
16
|
+
|
|
17
|
+
**Right-size the plan.** If the slice is simple enough to be 1 task, plan 1 task. Don't split into multiple tasks just because you can identify sub-steps. Don't fill in sections with "None" when the section doesn't apply — omit them entirely. The plan's job is to guide execution, not to fill a template.
|
|
16
18
|
|
|
17
19
|
Then:
|
|
18
20
|
0. If `REQUIREMENTS.md` was preloaded above, identify which Active requirements the roadmap says this slice owns or supports. These are the requirements this plan must deliver — every owned requirement needs at least one task that directly advances it, and verification must prove the requirement is met.
|
|
@@ -20,43 +22,33 @@ Then:
|
|
|
20
22
|
- `~/.gsd/agent/extensions/gsd/templates/plan.md`
|
|
21
23
|
- `~/.gsd/agent/extensions/gsd/templates/task-plan.md`
|
|
22
24
|
2. If a `GSD Skill Preferences` block is present in system context, use it to decide which skills to load and follow during planning, without overriding required plan formatting
|
|
23
|
-
3. Define slice-level verification
|
|
24
|
-
- For non-trivial slices: plan actual test files with real assertions. Name the files.
|
|
25
|
+
3. Define slice-level verification — the objective stopping condition for this slice:
|
|
26
|
+
- For non-trivial slices: plan actual test files with real assertions. Name the files.
|
|
25
27
|
- For simple slices: executable commands or script assertions are fine.
|
|
26
28
|
- If the project is non-trivial and has no test framework, the first task should set one up.
|
|
27
29
|
- If this slice establishes a boundary contract, verification must exercise that contract.
|
|
28
|
-
4.
|
|
29
|
-
-
|
|
30
|
-
-
|
|
31
|
-
-
|
|
32
|
-
|
|
33
|
-
5. Fill the `Proof Level` and `Integration Closure` sections truthfully:
|
|
34
|
-
- State whether the slice proves contract, integration, operational, or final-assembly behavior.
|
|
35
|
-
- Say whether real runtime or human/UAT is required.
|
|
36
|
-
- Name the wiring introduced in this slice and what still remains before the milestone is truly usable end-to-end.
|
|
37
|
-
6. Decompose the slice into tasks, each fitting one context window
|
|
38
|
-
7. Every task in the slice plan should be written as an executable increment with:
|
|
30
|
+
4. **For non-trivial slices only** — plan observability, proof level, and integration closure:
|
|
31
|
+
- Include `Observability / Diagnostics` for backend, integration, async, stateful, or UI slices where failure diagnosis matters.
|
|
32
|
+
- Fill `Proof Level` and `Integration Closure` when the slice crosses runtime boundaries or has meaningful integration concerns.
|
|
33
|
+
- **Omit these sections entirely for simple slices** where they would all be "none" or trivially obvious.
|
|
34
|
+
5. Decompose the slice into tasks, each fitting one context window. Each task needs:
|
|
39
35
|
- a concrete, action-oriented title
|
|
40
36
|
- the inline task entry fields defined in the plan.md template (Why / Files / Do / Verify / Done when)
|
|
41
|
-
- a matching task plan
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
- **
|
|
48
|
-
- **
|
|
49
|
-
- **Task completeness:** Every task has steps, must-haves, verification, observability impact, inputs, and expected output — none are blank or vague.
|
|
37
|
+
- a matching task plan file with description, steps, must-haves, verification, inputs, and expected output
|
|
38
|
+
- Observability Impact section **only if the task touches runtime boundaries, async flows, or error paths** — omit it otherwise
|
|
39
|
+
6. Write `{{outputPath}}`
|
|
40
|
+
7. Write individual task plans in `{{sliceAbsPath}}/tasks/`: `T01-PLAN.md`, `T02-PLAN.md`, etc.
|
|
41
|
+
8. **Self-audit the plan.** Walk through each check — if any fail, fix the plan files before moving on:
|
|
42
|
+
- **Completion semantics:** If every task were completed exactly as written, the slice goal/demo should actually be true.
|
|
43
|
+
- **Requirement coverage:** Every must-have in the slice maps to at least one task. No must-have is orphaned. If `REQUIREMENTS.md` exists, every Active requirement this slice owns maps to at least one task.
|
|
44
|
+
- **Task completeness:** Every task has steps, must-haves, verification, inputs, and expected output — none are blank or vague.
|
|
50
45
|
- **Dependency correctness:** Task ordering is consistent. No task references work from a later task.
|
|
51
|
-
- **Key links planned:** For every pair of artifacts that must connect
|
|
52
|
-
- **Scope sanity:** Target 2–5 steps and 3–8 files per task.
|
|
53
|
-
- **
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
13. If planning produced structural decisions (e.g. verification strategy, observability strategy, technology choices, patterns to follow), append them to `.gsd/DECISIONS.md`
|
|
58
|
-
14. Commit: `docs({{sliceId}}): add slice plan`
|
|
59
|
-
15. Update `.gsd/STATE.md`
|
|
46
|
+
- **Key links planned:** For every pair of artifacts that must connect, there is an explicit step that wires them.
|
|
47
|
+
- **Scope sanity:** Target 2–5 steps and 3–8 files per task. 10+ steps or 12+ files — must split. Each task must be completable in a single fresh context window.
|
|
48
|
+
- **Feature completeness:** Every task produces real, user-facing progress — not just internal scaffolding.
|
|
49
|
+
9. If planning produced structural decisions, append them to `.gsd/DECISIONS.md`
|
|
50
|
+
10. Commit: `docs({{sliceId}}): add slice plan`
|
|
51
|
+
11. Update `.gsd/STATE.md`
|
|
60
52
|
|
|
61
53
|
The slice directory and tasks/ subdirectory already exist. Do NOT mkdir. You are on the slice branch; all work stays here.
|
|
62
54
|
|