gsd-pi 2.16.0 → 2.17.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/dist/resources/extensions/gsd/auto-dashboard.ts +4 -0
- package/dist/resources/extensions/gsd/auto-dispatch.ts +9 -3
- package/dist/resources/extensions/gsd/auto-prompts.ts +71 -41
- package/dist/resources/extensions/gsd/auto-recovery.ts +7 -2
- package/dist/resources/extensions/gsd/auto.ts +54 -15
- package/dist/resources/extensions/gsd/commands.ts +20 -2
- package/dist/resources/extensions/gsd/complexity.ts +236 -0
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -0
- package/dist/resources/extensions/gsd/files.ts +6 -2
- package/dist/resources/extensions/gsd/git-service.ts +19 -8
- package/dist/resources/extensions/gsd/gitignore.ts +41 -2
- package/dist/resources/extensions/gsd/guided-flow.ts +10 -6
- package/dist/resources/extensions/gsd/metrics.ts +44 -0
- package/dist/resources/extensions/gsd/native-git-bridge.ts +5 -0
- package/dist/resources/extensions/gsd/native-parser-bridge.ts +5 -0
- package/dist/resources/extensions/gsd/preferences.ts +122 -1
- package/dist/resources/extensions/gsd/routing-history.ts +290 -0
- package/dist/resources/extensions/gsd/tests/auto-recovery.test.ts +50 -0
- package/dist/resources/extensions/gsd/tests/budget-prediction.test.ts +220 -0
- package/dist/resources/extensions/gsd/tests/complexity-routing.test.ts +294 -0
- package/dist/resources/extensions/gsd/tests/context-compression.test.ts +180 -0
- package/dist/resources/extensions/gsd/tests/git-service.test.ts +132 -0
- package/dist/resources/extensions/gsd/tests/preferences-git.test.ts +28 -0
- package/dist/resources/extensions/gsd/tests/routing-history.test.ts +87 -0
- package/dist/resources/extensions/gsd/tests/stop-auto-remote.test.ts +130 -0
- package/dist/resources/extensions/gsd/tests/token-profile.test.ts +263 -0
- package/dist/resources/extensions/gsd/types.ts +28 -0
- package/dist/resources/extensions/gsd/worktree.ts +2 -2
- package/package.json +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +493 -13
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +422 -62
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/dist/providers/google-shared.d.ts +12 -0
- package/packages/pi-ai/dist/providers/google-shared.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/google-shared.js +9 -22
- package/packages/pi-ai/dist/providers/google-shared.js.map +1 -1
- package/packages/pi-ai/dist/providers/google-shared.test.d.ts +2 -0
- package/packages/pi-ai/dist/providers/google-shared.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/google-shared.test.js +125 -0
- package/packages/pi-ai/dist/providers/google-shared.test.js.map +1 -0
- package/packages/pi-ai/src/models.generated.ts +422 -62
- package/packages/pi-ai/src/providers/google-shared.test.ts +137 -0
- package/packages/pi-ai/src/providers/google-shared.ts +10 -19
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.d.ts +7 -7
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.js +209 -13
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +67 -0
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +10 -0
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
- package/packages/pi-coding-agent/src/core/tools/edit-diff.test.ts +85 -0
- package/packages/pi-coding-agent/src/core/tools/edit-diff.ts +245 -17
- package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +13 -0
- package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/pkg/dist/modes/interactive/theme/theme.js +10 -0
- package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
- package/src/resources/extensions/gsd/auto-dashboard.ts +4 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +9 -3
- package/src/resources/extensions/gsd/auto-prompts.ts +71 -41
- package/src/resources/extensions/gsd/auto-recovery.ts +7 -2
- package/src/resources/extensions/gsd/auto.ts +54 -15
- package/src/resources/extensions/gsd/commands.ts +20 -2
- package/src/resources/extensions/gsd/complexity.ts +236 -0
- package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -0
- package/src/resources/extensions/gsd/files.ts +6 -2
- package/src/resources/extensions/gsd/git-service.ts +19 -8
- package/src/resources/extensions/gsd/gitignore.ts +41 -2
- package/src/resources/extensions/gsd/guided-flow.ts +10 -6
- package/src/resources/extensions/gsd/metrics.ts +44 -0
- package/src/resources/extensions/gsd/native-git-bridge.ts +5 -0
- package/src/resources/extensions/gsd/native-parser-bridge.ts +5 -0
- package/src/resources/extensions/gsd/preferences.ts +122 -1
- package/src/resources/extensions/gsd/routing-history.ts +290 -0
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/budget-prediction.test.ts +220 -0
- package/src/resources/extensions/gsd/tests/complexity-routing.test.ts +294 -0
- package/src/resources/extensions/gsd/tests/context-compression.test.ts +180 -0
- package/src/resources/extensions/gsd/tests/git-service.test.ts +132 -0
- package/src/resources/extensions/gsd/tests/preferences-git.test.ts +28 -0
- package/src/resources/extensions/gsd/tests/routing-history.test.ts +87 -0
- package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +130 -0
- package/src/resources/extensions/gsd/tests/token-profile.test.ts +263 -0
- package/src/resources/extensions/gsd/types.ts +28 -0
- package/src/resources/extensions/gsd/worktree.ts +2 -2
|
@@ -35,6 +35,10 @@ export interface AutoDashboardData {
|
|
|
35
35
|
/** Running cost and token totals from metrics ledger */
|
|
36
36
|
totalCost: number;
|
|
37
37
|
totalTokens: number;
|
|
38
|
+
/** Projected remaining cost based on unit-type averages (undefined if insufficient data) */
|
|
39
|
+
projectedRemainingCost?: number;
|
|
40
|
+
/** Whether token profile has been auto-downgraded due to budget prediction */
|
|
41
|
+
profileDowngraded?: boolean;
|
|
38
42
|
}
|
|
39
43
|
|
|
40
44
|
// ─── Unit Description Helpers ─────────────────────────────────────────────────
|
|
@@ -122,7 +122,9 @@ const DISPATCH_RULES: DispatchRule[] = [
|
|
|
122
122
|
},
|
|
123
123
|
{
|
|
124
124
|
name: "reassess-roadmap (post-completion)",
|
|
125
|
-
match: async ({ state, mid, midTitle, basePath }) => {
|
|
125
|
+
match: async ({ state, mid, midTitle, basePath, prefs }) => {
|
|
126
|
+
// Phase skip: skip reassess when preference or profile says so
|
|
127
|
+
if (prefs?.phases?.skip_reassess) return null;
|
|
126
128
|
const needsReassess = await checkNeedsReassessment(basePath, mid, state);
|
|
127
129
|
if (!needsReassess) return null;
|
|
128
130
|
return {
|
|
@@ -160,8 +162,10 @@ const DISPATCH_RULES: DispatchRule[] = [
|
|
|
160
162
|
},
|
|
161
163
|
{
|
|
162
164
|
name: "pre-planning (no research) → research-milestone",
|
|
163
|
-
match: async ({ state, mid, midTitle, basePath }) => {
|
|
165
|
+
match: async ({ state, mid, midTitle, basePath, prefs }) => {
|
|
164
166
|
if (state.phase !== "pre-planning") return null;
|
|
167
|
+
// Phase skip: skip research when preference or profile says so
|
|
168
|
+
if (prefs?.phases?.skip_research) return null;
|
|
165
169
|
const researchFile = resolveMilestoneFile(basePath, mid, "RESEARCH");
|
|
166
170
|
if (researchFile) return null; // has research, fall through
|
|
167
171
|
return {
|
|
@@ -186,8 +190,10 @@ const DISPATCH_RULES: DispatchRule[] = [
|
|
|
186
190
|
},
|
|
187
191
|
{
|
|
188
192
|
name: "planning (no research, not S01) → research-slice",
|
|
189
|
-
match: async ({ state, mid, midTitle, basePath }) => {
|
|
193
|
+
match: async ({ state, mid, midTitle, basePath, prefs }) => {
|
|
190
194
|
if (state.phase !== "planning") return null;
|
|
195
|
+
// Phase skip: skip research when preference or profile says so
|
|
196
|
+
if (prefs?.phases?.skip_research || prefs?.phases?.skip_slice_research) return null;
|
|
191
197
|
const sid = state.activeSlice!.id;
|
|
192
198
|
const sTitle = state.activeSlice!.title;
|
|
193
199
|
const researchFile = resolveSliceFile(basePath, mid, sid, "RESEARCH");
|
|
@@ -15,8 +15,8 @@ import {
|
|
|
15
15
|
relMilestoneFile, relSliceFile, relSlicePath, relMilestonePath,
|
|
16
16
|
resolveGsdRootFile, relGsdRootFile,
|
|
17
17
|
} from "./paths.js";
|
|
18
|
-
import { resolveSkillDiscoveryMode } from "./preferences.js";
|
|
19
|
-
import type { GSDState } from "./types.js";
|
|
18
|
+
import { resolveSkillDiscoveryMode, resolveInlineLevel } from "./preferences.js";
|
|
19
|
+
import type { GSDState, InlineLevel } from "./types.js";
|
|
20
20
|
import type { GSDPreferences } from "./preferences.js";
|
|
21
21
|
import { join } from "node:path";
|
|
22
22
|
import { existsSync } from "node:fs";
|
|
@@ -393,7 +393,8 @@ export async function buildResearchMilestonePrompt(mid: string, midTitle: string
|
|
|
393
393
|
});
|
|
394
394
|
}
|
|
395
395
|
|
|
396
|
-
export async function buildPlanMilestonePrompt(mid: string, midTitle: string, base: string): Promise<string> {
|
|
396
|
+
export async function buildPlanMilestonePrompt(mid: string, midTitle: string, base: string, level?: InlineLevel): Promise<string> {
|
|
397
|
+
const inlineLevel = level ?? resolveInlineLevel();
|
|
397
398
|
const contextPath = resolveMilestoneFile(base, mid, "CONTEXT");
|
|
398
399
|
const contextRel = relMilestoneFile(base, mid, "CONTEXT");
|
|
399
400
|
const researchPath = resolveMilestoneFile(base, mid, "RESEARCH");
|
|
@@ -406,17 +407,23 @@ export async function buildPlanMilestonePrompt(mid: string, midTitle: string, ba
|
|
|
406
407
|
const { inlinePriorMilestoneSummary } = await import("./files.js");
|
|
407
408
|
const priorSummaryInline = await inlinePriorMilestoneSummary(mid, base);
|
|
408
409
|
if (priorSummaryInline) inlined.push(priorSummaryInline);
|
|
409
|
-
const projectInline = await inlineGsdRootFile(base, "project.md", "Project");
|
|
410
|
+
const projectInline = inlineLevel !== "minimal" ? await inlineGsdRootFile(base, "project.md", "Project") : null;
|
|
410
411
|
if (projectInline) inlined.push(projectInline);
|
|
411
|
-
const requirementsInline = await inlineGsdRootFile(base, "requirements.md", "Requirements");
|
|
412
|
+
const requirementsInline = inlineLevel !== "minimal" ? await inlineGsdRootFile(base, "requirements.md", "Requirements") : null;
|
|
412
413
|
if (requirementsInline) inlined.push(requirementsInline);
|
|
413
|
-
const decisionsInline = await inlineGsdRootFile(base, "decisions.md", "Decisions");
|
|
414
|
+
const decisionsInline = inlineLevel !== "minimal" ? await inlineGsdRootFile(base, "decisions.md", "Decisions") : null;
|
|
414
415
|
if (decisionsInline) inlined.push(decisionsInline);
|
|
415
416
|
inlined.push(inlineTemplate("roadmap", "Roadmap"));
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
417
|
+
if (inlineLevel === "full") {
|
|
418
|
+
inlined.push(inlineTemplate("decisions", "Decisions"));
|
|
419
|
+
inlined.push(inlineTemplate("plan", "Slice Plan"));
|
|
420
|
+
inlined.push(inlineTemplate("task-plan", "Task Plan"));
|
|
421
|
+
inlined.push(inlineTemplate("secrets-manifest", "Secrets Manifest"));
|
|
422
|
+
} else if (inlineLevel === "standard") {
|
|
423
|
+
inlined.push(inlineTemplate("decisions", "Decisions"));
|
|
424
|
+
inlined.push(inlineTemplate("plan", "Slice Plan"));
|
|
425
|
+
inlined.push(inlineTemplate("task-plan", "Task Plan"));
|
|
426
|
+
}
|
|
420
427
|
|
|
421
428
|
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`;
|
|
422
429
|
|
|
@@ -479,8 +486,9 @@ export async function buildResearchSlicePrompt(
|
|
|
479
486
|
}
|
|
480
487
|
|
|
481
488
|
export async function buildPlanSlicePrompt(
|
|
482
|
-
mid: string, _midTitle: string, sid: string, sTitle: string, base: string,
|
|
489
|
+
mid: string, _midTitle: string, sid: string, sTitle: string, base: string, level?: InlineLevel,
|
|
483
490
|
): Promise<string> {
|
|
491
|
+
const inlineLevel = level ?? resolveInlineLevel();
|
|
484
492
|
const roadmapPath = resolveMilestoneFile(base, mid, "ROADMAP");
|
|
485
493
|
const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
|
|
486
494
|
const researchPath = resolveSliceFile(base, mid, sid, "RESEARCH");
|
|
@@ -490,12 +498,16 @@ export async function buildPlanSlicePrompt(
|
|
|
490
498
|
inlined.push(await inlineFile(roadmapPath, roadmapRel, "Milestone Roadmap"));
|
|
491
499
|
const researchInline = await inlineFileOptional(researchPath, researchRel, "Slice Research");
|
|
492
500
|
if (researchInline) inlined.push(researchInline);
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
501
|
+
if (inlineLevel !== "minimal") {
|
|
502
|
+
const decisionsInline = await inlineGsdRootFile(base, "decisions.md", "Decisions");
|
|
503
|
+
if (decisionsInline) inlined.push(decisionsInline);
|
|
504
|
+
const requirementsInline = await inlineGsdRootFile(base, "requirements.md", "Requirements");
|
|
505
|
+
if (requirementsInline) inlined.push(requirementsInline);
|
|
506
|
+
}
|
|
497
507
|
inlined.push(inlineTemplate("plan", "Slice Plan"));
|
|
498
|
-
|
|
508
|
+
if (inlineLevel === "full") {
|
|
509
|
+
inlined.push(inlineTemplate("task-plan", "Task Plan"));
|
|
510
|
+
}
|
|
499
511
|
|
|
500
512
|
const depContent = await inlineDependencySummaries(mid, sid, base);
|
|
501
513
|
const planActiveOverrides = await loadActiveOverrides(base);
|
|
@@ -519,8 +531,9 @@ export async function buildPlanSlicePrompt(
|
|
|
519
531
|
|
|
520
532
|
export async function buildExecuteTaskPrompt(
|
|
521
533
|
mid: string, sid: string, sTitle: string,
|
|
522
|
-
tid: string, tTitle: string, base: string,
|
|
534
|
+
tid: string, tTitle: string, base: string, level?: InlineLevel,
|
|
523
535
|
): Promise<string> {
|
|
536
|
+
const inlineLevel = level ?? resolveInlineLevel();
|
|
524
537
|
|
|
525
538
|
const priorSummaries = await getPriorTaskSummaryPaths(mid, sid, tid, base);
|
|
526
539
|
const priorLines = priorSummaries.length > 0
|
|
@@ -560,11 +573,17 @@ export async function buildExecuteTaskPrompt(
|
|
|
560
573
|
legacyContinuePath ? `${relSlicePath(base, mid, sid)}/continue.md` : null,
|
|
561
574
|
);
|
|
562
575
|
|
|
563
|
-
|
|
564
|
-
const
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
576
|
+
// For minimal inline level, only carry forward the most recent prior summary
|
|
577
|
+
const effectivePriorSummaries = inlineLevel === "minimal" && priorSummaries.length > 1
|
|
578
|
+
? priorSummaries.slice(-1)
|
|
579
|
+
: priorSummaries;
|
|
580
|
+
const carryForwardSection = await buildCarryForwardSection(effectivePriorSummaries, base);
|
|
581
|
+
const inlinedTemplates = inlineLevel === "minimal"
|
|
582
|
+
? inlineTemplate("task-summary", "Task Summary")
|
|
583
|
+
: [
|
|
584
|
+
inlineTemplate("task-summary", "Task Summary"),
|
|
585
|
+
inlineTemplate("decisions", "Decisions"),
|
|
586
|
+
].join("\n\n---\n\n");
|
|
568
587
|
|
|
569
588
|
const taskSummaryPath = `${relSlicePath(base, mid, sid)}/tasks/${tid}-SUMMARY.md`;
|
|
570
589
|
|
|
@@ -589,8 +608,9 @@ export async function buildExecuteTaskPrompt(
|
|
|
589
608
|
}
|
|
590
609
|
|
|
591
610
|
export async function buildCompleteSlicePrompt(
|
|
592
|
-
mid: string, _midTitle: string, sid: string, sTitle: string, base: string,
|
|
611
|
+
mid: string, _midTitle: string, sid: string, sTitle: string, base: string, level?: InlineLevel,
|
|
593
612
|
): Promise<string> {
|
|
613
|
+
const inlineLevel = level ?? resolveInlineLevel();
|
|
594
614
|
|
|
595
615
|
const roadmapPath = resolveMilestoneFile(base, mid, "ROADMAP");
|
|
596
616
|
const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
|
|
@@ -600,8 +620,10 @@ export async function buildCompleteSlicePrompt(
|
|
|
600
620
|
const inlined: string[] = [];
|
|
601
621
|
inlined.push(await inlineFile(roadmapPath, roadmapRel, "Milestone Roadmap"));
|
|
602
622
|
inlined.push(await inlineFile(slicePlanPath, slicePlanRel, "Slice Plan"));
|
|
603
|
-
|
|
604
|
-
|
|
623
|
+
if (inlineLevel !== "minimal") {
|
|
624
|
+
const requirementsInline = await inlineGsdRootFile(base, "requirements.md", "Requirements");
|
|
625
|
+
if (requirementsInline) inlined.push(requirementsInline);
|
|
626
|
+
}
|
|
605
627
|
|
|
606
628
|
// Inline all task summaries for this slice
|
|
607
629
|
const tDir = resolveTasksDir(base, mid, sid);
|
|
@@ -618,7 +640,9 @@ export async function buildCompleteSlicePrompt(
|
|
|
618
640
|
}
|
|
619
641
|
}
|
|
620
642
|
inlined.push(inlineTemplate("slice-summary", "Slice Summary"));
|
|
621
|
-
|
|
643
|
+
if (inlineLevel !== "minimal") {
|
|
644
|
+
inlined.push(inlineTemplate("uat", "UAT"));
|
|
645
|
+
}
|
|
622
646
|
const completeActiveOverrides = await loadActiveOverrides(base);
|
|
623
647
|
const completeOverridesInline = formatOverridesSection(completeActiveOverrides);
|
|
624
648
|
if (completeOverridesInline) inlined.unshift(completeOverridesInline);
|
|
@@ -641,8 +665,9 @@ export async function buildCompleteSlicePrompt(
|
|
|
641
665
|
}
|
|
642
666
|
|
|
643
667
|
export async function buildCompleteMilestonePrompt(
|
|
644
|
-
mid: string, midTitle: string, base: string,
|
|
668
|
+
mid: string, midTitle: string, base: string, level?: InlineLevel,
|
|
645
669
|
): Promise<string> {
|
|
670
|
+
const inlineLevel = level ?? resolveInlineLevel();
|
|
646
671
|
const roadmapPath = resolveMilestoneFile(base, mid, "ROADMAP");
|
|
647
672
|
const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
|
|
648
673
|
|
|
@@ -663,13 +688,15 @@ export async function buildCompleteMilestonePrompt(
|
|
|
663
688
|
}
|
|
664
689
|
}
|
|
665
690
|
|
|
666
|
-
// Inline root GSD files
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
691
|
+
// Inline root GSD files (skip for minimal — completion can read these if needed)
|
|
692
|
+
if (inlineLevel !== "minimal") {
|
|
693
|
+
const requirementsInline = await inlineGsdRootFile(base, "requirements.md", "Requirements");
|
|
694
|
+
if (requirementsInline) inlined.push(requirementsInline);
|
|
695
|
+
const decisionsInline = await inlineGsdRootFile(base, "decisions.md", "Decisions");
|
|
696
|
+
if (decisionsInline) inlined.push(decisionsInline);
|
|
697
|
+
const projectInline = await inlineGsdRootFile(base, "project.md", "Project");
|
|
698
|
+
if (projectInline) inlined.push(projectInline);
|
|
699
|
+
}
|
|
673
700
|
// Inline milestone context file (milestone-level, not GSD root)
|
|
674
701
|
const contextPath = resolveMilestoneFile(base, mid, "CONTEXT");
|
|
675
702
|
const contextRel = relMilestoneFile(base, mid, "CONTEXT");
|
|
@@ -779,8 +806,9 @@ export async function buildRunUatPrompt(
|
|
|
779
806
|
}
|
|
780
807
|
|
|
781
808
|
export async function buildReassessRoadmapPrompt(
|
|
782
|
-
mid: string, midTitle: string, completedSliceId: string, base: string,
|
|
809
|
+
mid: string, midTitle: string, completedSliceId: string, base: string, level?: InlineLevel,
|
|
783
810
|
): Promise<string> {
|
|
811
|
+
const inlineLevel = level ?? resolveInlineLevel();
|
|
784
812
|
const roadmapPath = resolveMilestoneFile(base, mid, "ROADMAP");
|
|
785
813
|
const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
|
|
786
814
|
const summaryPath = resolveSliceFile(base, mid, completedSliceId, "SUMMARY");
|
|
@@ -789,12 +817,14 @@ export async function buildReassessRoadmapPrompt(
|
|
|
789
817
|
const inlined: string[] = [];
|
|
790
818
|
inlined.push(await inlineFile(roadmapPath, roadmapRel, "Current Roadmap"));
|
|
791
819
|
inlined.push(await inlineFile(summaryPath, summaryRel, `${completedSliceId} Summary`));
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
820
|
+
if (inlineLevel !== "minimal") {
|
|
821
|
+
const projectInline = await inlineGsdRootFile(base, "project.md", "Project");
|
|
822
|
+
if (projectInline) inlined.push(projectInline);
|
|
823
|
+
const requirementsInline = await inlineGsdRootFile(base, "requirements.md", "Requirements");
|
|
824
|
+
if (requirementsInline) inlined.push(requirementsInline);
|
|
825
|
+
const decisionsInline = await inlineGsdRootFile(base, "decisions.md", "Decisions");
|
|
826
|
+
if (decisionsInline) inlined.push(decisionsInline);
|
|
827
|
+
}
|
|
798
828
|
|
|
799
829
|
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`;
|
|
800
830
|
|
|
@@ -11,6 +11,7 @@ import type { ExtensionContext } from "@gsd/pi-coding-agent";
|
|
|
11
11
|
import {
|
|
12
12
|
clearUnitRuntimeRecord,
|
|
13
13
|
} from "./unit-runtime.js";
|
|
14
|
+
import { clearParseCache } from "./files.js";
|
|
14
15
|
import {
|
|
15
16
|
nativeConflictFiles,
|
|
16
17
|
nativeCommit,
|
|
@@ -107,9 +108,13 @@ export function verifyExpectedArtifact(unitType: string, unitId: string, base: s
|
|
|
107
108
|
// is managed by the hook engine, not the artifact verification system.
|
|
108
109
|
if (unitType.startsWith("hook/")) return true;
|
|
109
110
|
|
|
110
|
-
// Clear stale directory listing cache so artifact checks see
|
|
111
|
-
//
|
|
111
|
+
// Clear stale directory listing cache AND parse cache so artifact checks see
|
|
112
|
+
// fresh disk state (#431). The parse cache must also be cleared because
|
|
113
|
+
// cacheKey() uses length + first/last 100 chars — when a checkbox changes
|
|
114
|
+
// from [ ] to [x], the key collides with the pre-edit version, returning
|
|
115
|
+
// stale parsed results (e.g., slice.done = false when it's actually true).
|
|
112
116
|
clearPathCache();
|
|
117
|
+
clearParseCache();
|
|
113
118
|
|
|
114
119
|
if (unitType === "rewrite-docs") {
|
|
115
120
|
const overridesPath = resolveGsdRootFile(base, "OVERRIDES");
|
|
@@ -293,6 +293,41 @@ export function isAutoPaused(): boolean {
|
|
|
293
293
|
return paused;
|
|
294
294
|
}
|
|
295
295
|
|
|
296
|
+
/**
|
|
297
|
+
* Return the base path to use for the auto.lock file.
|
|
298
|
+
* Always uses the original project root (not the worktree) so that
|
|
299
|
+
* a second terminal can discover and stop a running auto-mode session.
|
|
300
|
+
*/
|
|
301
|
+
function lockBase(): string {
|
|
302
|
+
return originalBasePath || basePath;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Attempt to stop a running auto-mode session from a different process.
|
|
307
|
+
* Reads the lock file at the project root, checks if the PID is alive,
|
|
308
|
+
* and sends SIGTERM to gracefully stop it.
|
|
309
|
+
*
|
|
310
|
+
* Returns true if a remote session was found and signaled, false otherwise.
|
|
311
|
+
*/
|
|
312
|
+
export function stopAutoRemote(projectRoot: string): { found: boolean; pid?: number; error?: string } {
|
|
313
|
+
const lock = readCrashLock(projectRoot);
|
|
314
|
+
if (!lock) return { found: false };
|
|
315
|
+
|
|
316
|
+
if (!isLockProcessAlive(lock)) {
|
|
317
|
+
// Stale lock — clean it up
|
|
318
|
+
clearLock(projectRoot);
|
|
319
|
+
return { found: false };
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Send SIGTERM — the auto-mode process has a handler that clears the lock and exits
|
|
323
|
+
try {
|
|
324
|
+
process.kill(lock.pid, "SIGTERM");
|
|
325
|
+
return { found: true, pid: lock.pid };
|
|
326
|
+
} catch (err) {
|
|
327
|
+
return { found: false, error: (err as Error).message };
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
296
331
|
export function isStepMode(): boolean {
|
|
297
332
|
return stepMode;
|
|
298
333
|
}
|
|
@@ -371,7 +406,7 @@ function startDispatchGapWatchdog(ctx: ExtensionContext, pi: ExtensionAPI): void
|
|
|
371
406
|
export async function stopAuto(ctx?: ExtensionContext, pi?: ExtensionAPI): Promise<void> {
|
|
372
407
|
if (!active && !paused) return;
|
|
373
408
|
clearUnitTimeout();
|
|
374
|
-
if (
|
|
409
|
+
if (lockBase()) clearLock(lockBase());
|
|
375
410
|
clearSkillSnapshot();
|
|
376
411
|
_dispatching = false;
|
|
377
412
|
_skipDepth = 0;
|
|
@@ -454,7 +489,7 @@ export async function stopAuto(ctx?: ExtensionContext, pi?: ExtensionAPI): Promi
|
|
|
454
489
|
export async function pauseAuto(ctx?: ExtensionContext, _pi?: ExtensionAPI): Promise<void> {
|
|
455
490
|
if (!active) return;
|
|
456
491
|
clearUnitTimeout();
|
|
457
|
-
if (
|
|
492
|
+
if (lockBase()) clearLock(lockBase());
|
|
458
493
|
|
|
459
494
|
// Remove SIGTERM handler registered at auto-mode start
|
|
460
495
|
deregisterSigtermHandler();
|
|
@@ -527,8 +562,8 @@ export async function startAuto(
|
|
|
527
562
|
}
|
|
528
563
|
}
|
|
529
564
|
|
|
530
|
-
// Re-register SIGTERM handler for the resumed session
|
|
531
|
-
registerSigtermHandler(
|
|
565
|
+
// Re-register SIGTERM handler for the resumed session (use original base for lock)
|
|
566
|
+
registerSigtermHandler(lockBase());
|
|
532
567
|
|
|
533
568
|
ctx.ui.setStatus("gsd-auto", stepMode ? "next" : "auto");
|
|
534
569
|
ctx.ui.setFooter(hideFooter);
|
|
@@ -557,17 +592,21 @@ export async function startAuto(
|
|
|
557
592
|
}
|
|
558
593
|
|
|
559
594
|
// Ensure .gitignore has baseline patterns
|
|
560
|
-
|
|
595
|
+
const commitDocs = loadEffectiveGSDPreferences()?.preferences?.git?.commit_docs;
|
|
596
|
+
ensureGitignore(base, { commitDocs });
|
|
561
597
|
untrackRuntimeFiles(base);
|
|
562
598
|
|
|
563
599
|
// Bootstrap .gsd/ if it doesn't exist
|
|
564
600
|
const gsdDir = join(base, ".gsd");
|
|
565
601
|
if (!existsSync(gsdDir)) {
|
|
566
602
|
mkdirSync(join(gsdDir, "milestones"), { recursive: true });
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
603
|
+
// Only commit .gsd/ init when commit_docs is not explicitly false
|
|
604
|
+
if (commitDocs !== false) {
|
|
605
|
+
try {
|
|
606
|
+
nativeAddPaths(base, [".gsd", ".gitignore"]);
|
|
607
|
+
nativeCommit(base, "chore: init gsd");
|
|
608
|
+
} catch { /* nothing to commit */ }
|
|
609
|
+
}
|
|
571
610
|
}
|
|
572
611
|
|
|
573
612
|
// Initialize GitServiceImpl — basePath is set and git repo confirmed
|
|
@@ -658,7 +697,7 @@ export async function startAuto(
|
|
|
658
697
|
// of the repo's default (main/master). Idempotent when the branch is the
|
|
659
698
|
// same; updates the record when started from a different branch (#300).
|
|
660
699
|
if (currentMilestoneId) {
|
|
661
|
-
captureIntegrationBranch(base, currentMilestoneId);
|
|
700
|
+
captureIntegrationBranch(base, currentMilestoneId, { commitDocs });
|
|
662
701
|
setActiveMilestoneId(base, currentMilestoneId);
|
|
663
702
|
}
|
|
664
703
|
|
|
@@ -695,8 +734,8 @@ export async function startAuto(
|
|
|
695
734
|
gitService = new GitServiceImpl(basePath, loadEffectiveGSDPreferences()?.preferences?.git ?? {});
|
|
696
735
|
ctx.ui.notify(`Created auto-worktree at ${wtPath}`, "info");
|
|
697
736
|
}
|
|
698
|
-
// Re-register SIGTERM handler with the
|
|
699
|
-
registerSigtermHandler(
|
|
737
|
+
// Re-register SIGTERM handler with the original basePath (lock lives there)
|
|
738
|
+
registerSigtermHandler(originalBasePath);
|
|
700
739
|
} catch (err) {
|
|
701
740
|
// Worktree creation is non-fatal — continue in the project root.
|
|
702
741
|
ctx.ui.notify(
|
|
@@ -952,7 +991,7 @@ export async function handleAgentEnd(
|
|
|
952
991
|
return;
|
|
953
992
|
}
|
|
954
993
|
const sessionFile = ctx.sessionManager.getSessionFile();
|
|
955
|
-
writeLock(
|
|
994
|
+
writeLock(lockBase(), hookUnit.unitType, hookUnit.unitId, completedUnits.length, sessionFile);
|
|
956
995
|
// Persist hook state so cycle counts survive crashes
|
|
957
996
|
persistHookState(basePath);
|
|
958
997
|
|
|
@@ -1211,7 +1250,7 @@ async function dispatchNextUnit(
|
|
|
1211
1250
|
unitRecoveryCount.clear();
|
|
1212
1251
|
unitLifetimeDispatches.clear();
|
|
1213
1252
|
// Capture integration branch for the new milestone and update git service
|
|
1214
|
-
captureIntegrationBranch(originalBasePath || basePath, mid);
|
|
1253
|
+
captureIntegrationBranch(originalBasePath || basePath, mid, { commitDocs: loadEffectiveGSDPreferences()?.preferences?.git?.commit_docs });
|
|
1215
1254
|
}
|
|
1216
1255
|
if (mid) {
|
|
1217
1256
|
currentMilestoneId = mid;
|
|
@@ -1758,7 +1797,7 @@ async function dispatchNextUnit(
|
|
|
1758
1797
|
// Pi appends entries incrementally via appendFileSync, so on crash the
|
|
1759
1798
|
// session file survives with every tool call up to the crash point.
|
|
1760
1799
|
const sessionFile = ctx.sessionManager.getSessionFile();
|
|
1761
|
-
writeLock(
|
|
1800
|
+
writeLock(lockBase(), unitType, unitId, completedUnits.length, sessionFile);
|
|
1762
1801
|
|
|
1763
1802
|
// On crash recovery, prepend the full recovery briefing
|
|
1764
1803
|
// On retry (stuck detection), prepend deep diagnostic from last attempt
|
|
@@ -12,7 +12,7 @@ import { fileURLToPath } from "node:url";
|
|
|
12
12
|
import { deriveState } from "./state.js";
|
|
13
13
|
import { GSDDashboardOverlay } from "./dashboard-overlay.js";
|
|
14
14
|
import { showQueue, showDiscuss } from "./guided-flow.js";
|
|
15
|
-
import { startAuto, stopAuto, pauseAuto, isAutoActive, isAutoPaused, isStepMode } from "./auto.js";
|
|
15
|
+
import { startAuto, stopAuto, pauseAuto, isAutoActive, isAutoPaused, isStepMode, stopAutoRemote } from "./auto.js";
|
|
16
16
|
import {
|
|
17
17
|
getGlobalGSDPreferencesPath,
|
|
18
18
|
getLegacyGlobalGSDPreferencesPath,
|
|
@@ -178,7 +178,15 @@ export function registerGSDCommand(pi: ExtensionAPI): void {
|
|
|
178
178
|
|
|
179
179
|
if (trimmed === "stop") {
|
|
180
180
|
if (!isAutoActive() && !isAutoPaused()) {
|
|
181
|
-
|
|
181
|
+
// Not running in this process — check for a remote auto-mode session
|
|
182
|
+
const result = stopAutoRemote(process.cwd());
|
|
183
|
+
if (result.found) {
|
|
184
|
+
ctx.ui.notify(`Sent stop signal to auto-mode session (PID ${result.pid}). It will shut down gracefully.`, "info");
|
|
185
|
+
} else if (result.error) {
|
|
186
|
+
ctx.ui.notify(`Failed to stop remote auto-mode: ${result.error}`, "error");
|
|
187
|
+
} else {
|
|
188
|
+
ctx.ui.notify("Auto-mode is not running.", "info");
|
|
189
|
+
}
|
|
182
190
|
return;
|
|
183
191
|
}
|
|
184
192
|
await stopAuto(ctx, pi);
|
|
@@ -502,6 +510,16 @@ async function handlePrefsWizard(
|
|
|
502
510
|
delete git.main_branch;
|
|
503
511
|
}
|
|
504
512
|
}
|
|
513
|
+
// ─── Git commit_docs ────────────────────────────────────────────────────
|
|
514
|
+
const currentCommitDocs = git.commit_docs;
|
|
515
|
+
const commitDocsChoice = await ctx.ui.select(
|
|
516
|
+
`Track .gsd/ planning docs in git${currentCommitDocs !== undefined ? ` (current: ${currentCommitDocs})` : ""}:`,
|
|
517
|
+
["true", "false", "(keep current)"],
|
|
518
|
+
);
|
|
519
|
+
if (commitDocsChoice && commitDocsChoice !== "(keep current)") {
|
|
520
|
+
git.commit_docs = commitDocsChoice === "true";
|
|
521
|
+
}
|
|
522
|
+
|
|
505
523
|
if (Object.keys(git).length > 0) {
|
|
506
524
|
prefs.git = git;
|
|
507
525
|
}
|