gsd-pi 2.16.0 → 2.18.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 +39 -0
- package/dist/onboarding.js +2 -2
- package/dist/remote-questions-config.d.ts +10 -0
- package/dist/remote-questions-config.js +36 -0
- package/dist/resources/extensions/gsd/activity-log.ts +37 -7
- 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 +91 -42
- package/dist/resources/extensions/gsd/auto-recovery.ts +7 -2
- package/dist/resources/extensions/gsd/auto-worktree.ts +33 -4
- package/dist/resources/extensions/gsd/auto.ts +177 -25
- package/dist/resources/extensions/gsd/commands.ts +264 -23
- package/dist/resources/extensions/gsd/complexity.ts +236 -0
- package/dist/resources/extensions/gsd/dispatch-guard.ts +7 -19
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +202 -2
- package/dist/resources/extensions/gsd/files.ts +129 -3
- 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 +247 -10
- package/dist/resources/extensions/gsd/index.ts +47 -3
- 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/paths.ts +9 -0
- package/dist/resources/extensions/gsd/preferences.ts +181 -2
- package/dist/resources/extensions/gsd/prompts/execute-task.md +6 -5
- package/dist/resources/extensions/gsd/prompts/system.md +2 -0
- package/dist/resources/extensions/gsd/queue-order.ts +231 -0
- package/dist/resources/extensions/gsd/queue-reorder-ui.ts +263 -0
- package/dist/resources/extensions/gsd/routing-history.ts +290 -0
- package/dist/resources/extensions/gsd/state.ts +15 -3
- package/dist/resources/extensions/gsd/templates/knowledge.md +19 -0
- package/dist/resources/extensions/gsd/templates/preferences.md +14 -0
- package/dist/resources/extensions/gsd/tests/auto-recovery.test.ts +50 -0
- package/dist/resources/extensions/gsd/tests/auto-worktree.test.ts +20 -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/derive-state-deps.test.ts +99 -0
- package/dist/resources/extensions/gsd/tests/git-service.test.ts +132 -0
- package/dist/resources/extensions/gsd/tests/in-flight-tool-tracking.test.ts +79 -0
- package/dist/resources/extensions/gsd/tests/knowledge.test.ts +161 -0
- package/dist/resources/extensions/gsd/tests/memory-leak-guards.test.ts +87 -0
- package/dist/resources/extensions/gsd/tests/preferences-git.test.ts +28 -0
- package/dist/resources/extensions/gsd/tests/preferences-wizard-fields.test.ts +168 -0
- package/dist/resources/extensions/gsd/tests/queue-order.test.ts +204 -0
- package/dist/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +281 -0
- package/dist/resources/extensions/gsd/tests/routing-history.test.ts +87 -0
- package/dist/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +139 -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-manager.ts +8 -5
- package/dist/resources/extensions/gsd/worktree.ts +24 -2
- package/dist/resources/extensions/shared/next-action-ui.ts +16 -1
- 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/cli/args.d.ts +5 -0
- package/packages/pi-coding-agent/dist/cli/args.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/cli/args.js +21 -0
- package/packages/pi-coding-agent/dist/cli/args.js.map +1 -1
- package/packages/pi-coding-agent/dist/cli/list-models.d.ts +14 -3
- package/packages/pi-coding-agent/dist/cli/list-models.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/cli/list-models.js +52 -17
- package/packages/pi-coding-agent/dist/cli/list-models.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/discovery-cache.d.ts +27 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.js +79 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.test.js +140 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.d.ts +35 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.js +162 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.test.js +100 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.js +113 -0
- package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts +26 -0
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +98 -0
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/models-json-writer.d.ts +62 -0
- package/packages/pi-coding-agent/dist/core/models-json-writer.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/models-json-writer.js +145 -0
- package/packages/pi-coding-agent/dist/core/models-json-writer.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/models-json-writer.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/models-json-writer.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/models-json-writer.test.js +118 -0
- package/packages/pi-coding-agent/dist/core/models-json-writer.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +9 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +11 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/slash-commands.js +1 -0
- package/packages/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
- 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/index.d.ts +5 -1
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js +4 -1
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/main.js +17 -2
- package/packages/pi-coding-agent/dist/main.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/index.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/index.js +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts +25 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +121 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +32 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- 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/cli/args.ts +21 -0
- package/packages/pi-coding-agent/src/cli/list-models.ts +70 -17
- package/packages/pi-coding-agent/src/core/discovery-cache.test.ts +170 -0
- package/packages/pi-coding-agent/src/core/discovery-cache.ts +97 -0
- package/packages/pi-coding-agent/src/core/model-discovery.test.ts +125 -0
- package/packages/pi-coding-agent/src/core/model-discovery.ts +231 -0
- package/packages/pi-coding-agent/src/core/model-registry-discovery.test.ts +135 -0
- package/packages/pi-coding-agent/src/core/model-registry.ts +107 -0
- package/packages/pi-coding-agent/src/core/models-json-writer.test.ts +145 -0
- package/packages/pi-coding-agent/src/core/models-json-writer.ts +188 -0
- package/packages/pi-coding-agent/src/core/settings-manager.ts +21 -0
- package/packages/pi-coding-agent/src/core/slash-commands.ts +1 -0
- 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/index.ts +5 -0
- package/packages/pi-coding-agent/src/main.ts +19 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/index.ts +1 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/model-selector.ts +1 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +163 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +37 -0
- 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/activity-log.ts +37 -7
- 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 +91 -42
- package/src/resources/extensions/gsd/auto-recovery.ts +7 -2
- package/src/resources/extensions/gsd/auto-worktree.ts +33 -4
- package/src/resources/extensions/gsd/auto.ts +177 -25
- package/src/resources/extensions/gsd/commands.ts +264 -23
- package/src/resources/extensions/gsd/complexity.ts +236 -0
- package/src/resources/extensions/gsd/dispatch-guard.ts +7 -19
- package/src/resources/extensions/gsd/docs/preferences-reference.md +202 -2
- package/src/resources/extensions/gsd/files.ts +129 -3
- 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 +247 -10
- package/src/resources/extensions/gsd/index.ts +47 -3
- 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/paths.ts +9 -0
- package/src/resources/extensions/gsd/preferences.ts +181 -2
- package/src/resources/extensions/gsd/prompts/execute-task.md +6 -5
- package/src/resources/extensions/gsd/prompts/system.md +2 -0
- package/src/resources/extensions/gsd/queue-order.ts +231 -0
- package/src/resources/extensions/gsd/queue-reorder-ui.ts +263 -0
- package/src/resources/extensions/gsd/routing-history.ts +290 -0
- package/src/resources/extensions/gsd/state.ts +15 -3
- package/src/resources/extensions/gsd/templates/knowledge.md +19 -0
- package/src/resources/extensions/gsd/templates/preferences.md +14 -0
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +20 -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/derive-state-deps.test.ts +99 -0
- package/src/resources/extensions/gsd/tests/git-service.test.ts +132 -0
- package/src/resources/extensions/gsd/tests/in-flight-tool-tracking.test.ts +79 -0
- package/src/resources/extensions/gsd/tests/knowledge.test.ts +161 -0
- package/src/resources/extensions/gsd/tests/memory-leak-guards.test.ts +87 -0
- package/src/resources/extensions/gsd/tests/preferences-git.test.ts +28 -0
- package/src/resources/extensions/gsd/tests/preferences-wizard-fields.test.ts +168 -0
- package/src/resources/extensions/gsd/tests/queue-order.test.ts +204 -0
- package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +281 -0
- package/src/resources/extensions/gsd/tests/routing-history.test.ts +87 -0
- package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +139 -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-manager.ts +8 -5
- package/src/resources/extensions/gsd/worktree.ts +24 -2
- package/src/resources/extensions/shared/next-action-ui.ts +16 -1
|
@@ -5,7 +5,7 @@ import { readFileSync } from "node:fs";
|
|
|
5
5
|
import { readdirSync } from "node:fs";
|
|
6
6
|
import { resolveMilestoneFile, milestonesDir } from "./paths.js";
|
|
7
7
|
import { parseRoadmapSlices } from "./roadmap-slices.js";
|
|
8
|
-
import {
|
|
8
|
+
import { findMilestoneIds } from "./guided-flow.js";
|
|
9
9
|
|
|
10
10
|
const SLICE_DISPATCH_TYPES = new Set([
|
|
11
11
|
"research-slice",
|
|
@@ -43,24 +43,12 @@ export function getPriorSliceCompletionBlocker(base: string, _mainBranch: string
|
|
|
43
43
|
const [targetMid, targetSid] = unitId.split("/");
|
|
44
44
|
if (!targetMid || !targetSid) return null;
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
milestoneIds = readdirSync(milestonesDir(base), { withFileTypes: true })
|
|
53
|
-
.filter(d => d.isDirectory())
|
|
54
|
-
.map(d => {
|
|
55
|
-
const match = d.name.match(/^(M\d+(?:-[a-z0-9]{6})?)/);
|
|
56
|
-
return match ? match[1] : null;
|
|
57
|
-
})
|
|
58
|
-
.filter((id): id is string => id !== null)
|
|
59
|
-
.sort(milestoneIdSort)
|
|
60
|
-
.filter(id => extractMilestoneSeq(id) <= targetSeq);
|
|
61
|
-
} catch {
|
|
62
|
-
return null;
|
|
63
|
-
}
|
|
46
|
+
// Use findMilestoneIds to respect custom queue order.
|
|
47
|
+
// Only check milestones that come BEFORE the target in queue order.
|
|
48
|
+
const allIds = findMilestoneIds(base);
|
|
49
|
+
const targetIdx = allIds.indexOf(targetMid);
|
|
50
|
+
if (targetIdx < 0) return null;
|
|
51
|
+
const milestoneIds = allIds.slice(0, targetIdx + 1);
|
|
64
52
|
|
|
65
53
|
for (const mid of milestoneIds) {
|
|
66
54
|
// Read from disk (working tree) — always has the latest state
|
|
@@ -80,9 +80,9 @@ Setting `prefer_skills: []` does **not** disable skill discovery — it just mea
|
|
|
80
80
|
|
|
81
81
|
- `skill_rules`: situational rules with a human-readable `when` trigger and one or more of `use`, `prefer`, or `avoid`.
|
|
82
82
|
|
|
83
|
-
- `custom_instructions`: extra durable instructions related to skill use.
|
|
83
|
+
- `custom_instructions`: extra durable instructions related to skill use. For operational project knowledge (recurring rules, gotchas, patterns), use `.gsd/KNOWLEDGE.md` instead — it's injected into every agent prompt automatically and agents can append to it during execution.
|
|
84
84
|
|
|
85
|
-
- `models`: per-stage model selection for auto-mode. Keys: `research`, `planning`, `execution`, `completion`. Values can be:
|
|
85
|
+
- `models`: per-stage model selection for auto-mode. Keys: `research`, `planning`, `execution`, `execution_simple`, `completion`, `subagent`. Values can be:
|
|
86
86
|
- Simple string: `"claude-sonnet-4-6"` — single model, no fallbacks
|
|
87
87
|
- Provider-qualified string: `"bedrock/claude-sonnet-4-6"` — targets a specific provider when the same model ID exists across multiple providers
|
|
88
88
|
- Object with fallbacks: `{ model: "claude-opus-4-6", fallbacks: ["glm-5", "minimax-m2.5"] }` — tries fallbacks in order if primary fails
|
|
@@ -108,9 +108,75 @@ Setting `prefer_skills: []` does **not** disable skill discovery — it just mea
|
|
|
108
108
|
- `pre_merge_check`: boolean or `"auto"` — run pre-merge checks before merging a worktree back to the integration branch. `true` always runs, `false` never runs, `"auto"` runs when CI is detected. Default: `false`.
|
|
109
109
|
- `commit_type`: string — override the conventional commit type prefix. Must be one of: `feat`, `fix`, `refactor`, `docs`, `test`, `chore`, `perf`, `ci`, `build`, `style`. Default: inferred from diff content.
|
|
110
110
|
- `main_branch`: string — the primary branch name for new git repos (e.g., `"main"`, `"master"`, `"trunk"`). Also used by `getMainBranch()` as the preferred branch when auto-detection is ambiguous. Default: `"main"`.
|
|
111
|
+
- `merge_strategy`: `"squash"` or `"merge"` — controls how worktree branches are merged back. `"squash"` combines all commits into one; `"merge"` preserves individual commits. Default: `"squash"`.
|
|
112
|
+
- `isolation`: `"worktree"` or `"branch"` — controls auto-mode git isolation strategy. `"worktree"` creates a milestone worktree for isolated work; `"branch"` works directly in the project root (useful for submodule-heavy repos). Default: `"worktree"`.
|
|
113
|
+
- `commit_docs`: boolean — when `false`, prevents GSD from committing `.gsd/` planning artifacts to git. The `.gsd/` folder is added to `.gitignore` and kept local-only. Useful for teams where only some members use GSD, or when company policy requires a clean repository. Default: `true`.
|
|
111
114
|
|
|
112
115
|
- `unique_milestone_ids`: boolean — when `true`, generates milestone IDs in `M{seq}-{rand6}` format (e.g. `M001-eh88as`) instead of plain sequential `M001`. Prevents ID collisions in team workflows where multiple contributors create milestones concurrently. Both formats coexist — existing `M001`-style milestones remain valid. Default: `false`.
|
|
113
116
|
|
|
117
|
+
- `budget_ceiling`: number — maximum dollar amount to spend on auto-mode. When reached, behavior is controlled by `budget_enforcement`. Default: no limit.
|
|
118
|
+
|
|
119
|
+
- `budget_enforcement`: `"warn"`, `"pause"`, or `"halt"` — action taken when `budget_ceiling` is reached.
|
|
120
|
+
- `warn` — log a warning but continue execution.
|
|
121
|
+
- `pause` — pause auto-mode and wait for user confirmation.
|
|
122
|
+
- `halt` — stop auto-mode immediately.
|
|
123
|
+
- Default: `"pause"`.
|
|
124
|
+
|
|
125
|
+
- `context_pause_threshold`: number (0-100) — context window usage percentage at which auto-mode should pause to suggest checkpointing. Set to `0` to disable. Default: `0` (disabled).
|
|
126
|
+
|
|
127
|
+
- `token_profile`: `"budget"`, `"balanced"`, or `"quality"` — coordinates model selection, phase skipping, and context compression. `budget` skips research/reassessment and uses cheaper models; `balanced` (default) runs all phases; `quality` prefers higher-quality models. See token-optimization docs.
|
|
128
|
+
|
|
129
|
+
- `phases`: fine-grained control over which phases run. Usually set by `token_profile`, but can be overridden. Keys:
|
|
130
|
+
- `skip_research`: boolean — skip milestone-level research. Default: `false`.
|
|
131
|
+
- `skip_reassess`: boolean — skip roadmap reassessment after each slice. Default: `false`.
|
|
132
|
+
- `skip_slice_research`: boolean — skip per-slice research. Default: `false`.
|
|
133
|
+
|
|
134
|
+
- `remote_questions`: route interactive questions to Slack/Discord for headless auto-mode. Keys:
|
|
135
|
+
- `channel`: `"slack"` or `"discord"` — channel type.
|
|
136
|
+
- `channel_id`: string or number — channel ID.
|
|
137
|
+
- `timeout_minutes`: number — question timeout in minutes (clamped 1-30).
|
|
138
|
+
- `poll_interval_seconds`: number — poll interval in seconds (clamped 2-30).
|
|
139
|
+
|
|
140
|
+
- `notifications`: configures desktop notification behavior during auto-mode. Keys:
|
|
141
|
+
- `enabled`: boolean — master toggle for all notifications. Default: `true`.
|
|
142
|
+
- `on_complete`: boolean — notify when a unit completes. Default: `true`.
|
|
143
|
+
- `on_error`: boolean — notify on errors. Default: `true`.
|
|
144
|
+
- `on_budget`: boolean — notify when budget thresholds are reached. Default: `true`.
|
|
145
|
+
- `on_milestone`: boolean — notify when a milestone finishes. Default: `true`.
|
|
146
|
+
- `on_attention`: boolean — notify when manual attention is needed. Default: `true`.
|
|
147
|
+
|
|
148
|
+
- `uat_dispatch`: boolean — when `true`, enables UAT (User Acceptance Testing) dispatch mode. Default: `false`.
|
|
149
|
+
|
|
150
|
+
- `post_unit_hooks`: array — hooks that fire after a unit completes. Each entry has:
|
|
151
|
+
- `name`: string — unique hook identifier.
|
|
152
|
+
- `after`: string[] — unit types that trigger this hook (e.g., `["execute-task"]`).
|
|
153
|
+
- `prompt`: string — prompt sent to the LLM. Supports `{milestoneId}`, `{sliceId}`, `{taskId}` substitutions.
|
|
154
|
+
- `max_cycles`: number — max times this hook fires per trigger (default: 1, max: 10).
|
|
155
|
+
- `model`: string — optional model override.
|
|
156
|
+
- `artifact`: string — expected output file name (relative to task/slice dir). Hook is skipped if file already exists (idempotent).
|
|
157
|
+
- `retry_on`: string — if this file is produced instead of the artifact, re-run the trigger unit then re-run hooks.
|
|
158
|
+
- `agent`: string — agent definition file to use for hook execution.
|
|
159
|
+
- `enabled`: boolean — toggle without removing (default: `true`).
|
|
160
|
+
|
|
161
|
+
- `pre_dispatch_hooks`: array — hooks that fire before a unit is dispatched. Each entry has:
|
|
162
|
+
- `name`: string — unique hook identifier.
|
|
163
|
+
- `before`: string[] — unit types to intercept.
|
|
164
|
+
- `action`: `"modify"`, `"skip"`, or `"replace"` — what to do with the unit.
|
|
165
|
+
- `prepend`: string — text prepended to unit prompt (for `"modify"` action).
|
|
166
|
+
- `append`: string — text appended to unit prompt (for `"modify"` action).
|
|
167
|
+
- `prompt`: string — replacement prompt (for `"replace"` action; required when action is `"replace"`).
|
|
168
|
+
- `unit_type`: string — override unit type label (for `"replace"` action).
|
|
169
|
+
- `skip_if`: string — for `"skip"` action: only skip if this file exists (relative to unit dir).
|
|
170
|
+
- `model`: string — optional model override when this hook fires.
|
|
171
|
+
- `enabled`: boolean — toggle without removing (default: `true`).
|
|
172
|
+
|
|
173
|
+
**Action validation:**
|
|
174
|
+
- `"modify"` requires at least one of `prepend` or `append`.
|
|
175
|
+
- `"replace"` requires `prompt`.
|
|
176
|
+
- `"skip"` is valid with no additional fields.
|
|
177
|
+
|
|
178
|
+
**Known unit types for `before`/`after`:** `research-milestone`, `plan-milestone`, `research-slice`, `plan-slice`, `execute-task`, `complete-slice`, `replan-slice`, `reassess-roadmap`, `run-uat`.
|
|
179
|
+
|
|
114
180
|
---
|
|
115
181
|
|
|
116
182
|
## Best Practices
|
|
@@ -276,3 +342,137 @@ git:
|
|
|
276
342
|
```
|
|
277
343
|
|
|
278
344
|
All git fields are optional. Omit any field to use the default behavior. Project-level preferences override global preferences on a per-field basis.
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## Budget & Cost Control Example
|
|
349
|
+
|
|
350
|
+
```yaml
|
|
351
|
+
---
|
|
352
|
+
version: 1
|
|
353
|
+
budget_ceiling: 10.00
|
|
354
|
+
budget_enforcement: pause
|
|
355
|
+
context_pause_threshold: 80
|
|
356
|
+
---
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
Sets a $10 budget ceiling. Auto-mode pauses when the ceiling is reached. Context window pauses at 80% usage for checkpointing.
|
|
360
|
+
|
|
361
|
+
---
|
|
362
|
+
|
|
363
|
+
## Notifications Example
|
|
364
|
+
|
|
365
|
+
```yaml
|
|
366
|
+
---
|
|
367
|
+
version: 1
|
|
368
|
+
notifications:
|
|
369
|
+
enabled: true
|
|
370
|
+
on_complete: false
|
|
371
|
+
on_error: true
|
|
372
|
+
on_budget: true
|
|
373
|
+
on_milestone: true
|
|
374
|
+
on_attention: true
|
|
375
|
+
---
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
Disables per-unit completion notifications (noisy in long runs) while keeping error, budget, milestone, and attention notifications enabled.
|
|
379
|
+
|
|
380
|
+
---
|
|
381
|
+
|
|
382
|
+
## Post-Unit Hooks Example
|
|
383
|
+
|
|
384
|
+
```yaml
|
|
385
|
+
---
|
|
386
|
+
version: 1
|
|
387
|
+
post_unit_hooks:
|
|
388
|
+
- name: code-review
|
|
389
|
+
after:
|
|
390
|
+
- execute-task
|
|
391
|
+
prompt: "Review the code changes in {sliceId}/{taskId} for quality, security, and test coverage."
|
|
392
|
+
max_cycles: 1
|
|
393
|
+
artifact: REVIEW.md
|
|
394
|
+
---
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
Runs an automated code review after each task execution. Skips if `REVIEW.md` already exists (idempotent).
|
|
398
|
+
|
|
399
|
+
---
|
|
400
|
+
|
|
401
|
+
## Pre-Dispatch Hooks Examples
|
|
402
|
+
|
|
403
|
+
**Modify — inject instructions before every task:**
|
|
404
|
+
|
|
405
|
+
```yaml
|
|
406
|
+
---
|
|
407
|
+
version: 1
|
|
408
|
+
pre_dispatch_hooks:
|
|
409
|
+
- name: enforce-standards
|
|
410
|
+
before:
|
|
411
|
+
- execute-task
|
|
412
|
+
action: modify
|
|
413
|
+
prepend: "Follow our TypeScript coding standards and always run linting."
|
|
414
|
+
---
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
**Skip — skip per-slice research when a research file already exists:**
|
|
418
|
+
|
|
419
|
+
```yaml
|
|
420
|
+
---
|
|
421
|
+
version: 1
|
|
422
|
+
pre_dispatch_hooks:
|
|
423
|
+
- name: skip-existing-research
|
|
424
|
+
before:
|
|
425
|
+
- research-slice
|
|
426
|
+
action: skip
|
|
427
|
+
skip_if: RESEARCH.md
|
|
428
|
+
---
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
**Replace — substitute a custom prompt for task execution:**
|
|
432
|
+
|
|
433
|
+
```yaml
|
|
434
|
+
---
|
|
435
|
+
version: 1
|
|
436
|
+
pre_dispatch_hooks:
|
|
437
|
+
- name: tdd-execute
|
|
438
|
+
before:
|
|
439
|
+
- execute-task
|
|
440
|
+
action: replace
|
|
441
|
+
prompt: "Implement the task using strict TDD. Write failing tests first, then implement, then refactor."
|
|
442
|
+
model: claude-opus-4-6
|
|
443
|
+
---
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
---
|
|
447
|
+
|
|
448
|
+
## Token Profile & Phases Example
|
|
449
|
+
|
|
450
|
+
```yaml
|
|
451
|
+
---
|
|
452
|
+
version: 1
|
|
453
|
+
token_profile: budget
|
|
454
|
+
phases:
|
|
455
|
+
skip_research: true
|
|
456
|
+
skip_reassess: true
|
|
457
|
+
skip_slice_research: false
|
|
458
|
+
---
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
Uses the `budget` profile to minimize token usage, with explicit override to keep slice-level research enabled.
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
## Remote Questions Example
|
|
466
|
+
|
|
467
|
+
```yaml
|
|
468
|
+
---
|
|
469
|
+
version: 1
|
|
470
|
+
remote_questions:
|
|
471
|
+
channel: slack
|
|
472
|
+
channel_id: "C0123456789"
|
|
473
|
+
timeout_minutes: 15
|
|
474
|
+
poll_interval_seconds: 10
|
|
475
|
+
---
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
Routes interactive questions to a Slack channel for headless auto-mode sessions. Questions time out after 15 minutes if unanswered.
|
|
@@ -26,12 +26,16 @@ import { nativeParseRoadmap, nativeExtractSection, nativeParsePlanFile, nativePa
|
|
|
26
26
|
|
|
27
27
|
const CACHE_MAX = 50;
|
|
28
28
|
|
|
29
|
-
/** Fast composite key: length + first/last 100 chars.
|
|
29
|
+
/** Fast composite key: length + first/mid/last 100 chars. The middle sample
|
|
30
|
+
* prevents collisions when only a few characters change in the interior of
|
|
31
|
+
* a file (e.g., a checkbox [ ] → [x] that doesn't alter length or endpoints). */
|
|
30
32
|
function cacheKey(content: string): string {
|
|
31
33
|
const len = content.length;
|
|
32
34
|
const head = content.slice(0, 100);
|
|
35
|
+
const midStart = Math.max(0, Math.floor(len / 2) - 50);
|
|
36
|
+
const mid = len > 200 ? content.slice(midStart, midStart + 100) : '';
|
|
33
37
|
const tail = len > 100 ? content.slice(-100) : '';
|
|
34
|
-
return `${len}:${head}:${tail}`;
|
|
38
|
+
return `${len}:${head}:${mid}:${tail}`;
|
|
35
39
|
}
|
|
36
40
|
|
|
37
41
|
const _parseCache = new Map<string, unknown>();
|
|
@@ -845,7 +849,7 @@ export function parseContextDependsOn(content: string | null): string[] {
|
|
|
845
849
|
const fm = parseFrontmatterMap(fmLines);
|
|
846
850
|
const raw = fm['depends_on'];
|
|
847
851
|
if (!Array.isArray(raw) || raw.length === 0) return [];
|
|
848
|
-
return (raw as string[]).map(s => String(s).
|
|
852
|
+
return (raw as string[]).map(s => String(s).trim()).filter(Boolean);
|
|
849
853
|
}
|
|
850
854
|
|
|
851
855
|
/**
|
|
@@ -947,6 +951,128 @@ export async function appendOverride(basePath: string, change: string, appliedAt
|
|
|
947
951
|
}
|
|
948
952
|
}
|
|
949
953
|
|
|
954
|
+
export async function appendKnowledge(
|
|
955
|
+
basePath: string,
|
|
956
|
+
type: "rule" | "pattern" | "lesson",
|
|
957
|
+
entry: string,
|
|
958
|
+
scope: string,
|
|
959
|
+
): Promise<void> {
|
|
960
|
+
const knowledgePath = resolveGsdRootFile(basePath, "KNOWLEDGE");
|
|
961
|
+
const existing = await loadFile(knowledgePath);
|
|
962
|
+
|
|
963
|
+
if (existing) {
|
|
964
|
+
// Find the next ID for this type
|
|
965
|
+
const prefix = type === "rule" ? "K" : type === "pattern" ? "P" : "L";
|
|
966
|
+
const idPattern = new RegExp(`^\\| ${prefix}(\\d+)`, "gm");
|
|
967
|
+
let maxId = 0;
|
|
968
|
+
let match;
|
|
969
|
+
while ((match = idPattern.exec(existing)) !== null) {
|
|
970
|
+
const num = parseInt(match[1], 10);
|
|
971
|
+
if (num > maxId) maxId = num;
|
|
972
|
+
}
|
|
973
|
+
const nextId = `${prefix}${String(maxId + 1).padStart(3, "0")}`;
|
|
974
|
+
|
|
975
|
+
// Build the table row
|
|
976
|
+
let row: string;
|
|
977
|
+
if (type === "rule") {
|
|
978
|
+
row = `| ${nextId} | ${scope} | ${entry} | — | manual |`;
|
|
979
|
+
} else if (type === "pattern") {
|
|
980
|
+
row = `| ${nextId} | ${entry} | — | ${scope} |`;
|
|
981
|
+
} else {
|
|
982
|
+
row = `| ${nextId} | ${entry} | — | — | ${scope} |`;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
// Find the right section and append after the table header
|
|
986
|
+
const sectionHeading = type === "rule" ? "## Rules" : type === "pattern" ? "## Patterns" : "## Lessons Learned";
|
|
987
|
+
const sectionIdx = existing.indexOf(sectionHeading);
|
|
988
|
+
if (sectionIdx !== -1) {
|
|
989
|
+
// Find the end of the table header row (the |---|...| line)
|
|
990
|
+
const afterHeading = existing.indexOf("\n", sectionIdx);
|
|
991
|
+
// Find the next section or end
|
|
992
|
+
const nextSection = existing.indexOf("\n## ", afterHeading + 1);
|
|
993
|
+
const insertPoint = nextSection !== -1 ? nextSection : existing.length;
|
|
994
|
+
|
|
995
|
+
// Insert row before the next section (or at end)
|
|
996
|
+
const before = existing.slice(0, insertPoint).trimEnd();
|
|
997
|
+
const after = existing.slice(insertPoint);
|
|
998
|
+
await saveFile(knowledgePath, before + "\n" + row + "\n" + after);
|
|
999
|
+
} else {
|
|
1000
|
+
// Section not found — append at end
|
|
1001
|
+
await saveFile(knowledgePath, existing.trimEnd() + "\n\n" + row + "\n");
|
|
1002
|
+
}
|
|
1003
|
+
} else {
|
|
1004
|
+
// Create file from scratch with template header
|
|
1005
|
+
const header = [
|
|
1006
|
+
"# Project Knowledge",
|
|
1007
|
+
"",
|
|
1008
|
+
"Append-only register of project-specific rules, patterns, and lessons learned.",
|
|
1009
|
+
"Agents read this before every unit. Add entries when you discover something worth remembering.",
|
|
1010
|
+
"",
|
|
1011
|
+
].join("\n");
|
|
1012
|
+
|
|
1013
|
+
let content: string;
|
|
1014
|
+
if (type === "rule") {
|
|
1015
|
+
content = header + [
|
|
1016
|
+
"## Rules",
|
|
1017
|
+
"",
|
|
1018
|
+
"| # | Scope | Rule | Why | Added |",
|
|
1019
|
+
"|---|-------|------|-----|-------|",
|
|
1020
|
+
`| K001 | ${scope} | ${entry} | — | manual |`,
|
|
1021
|
+
"",
|
|
1022
|
+
"## Patterns",
|
|
1023
|
+
"",
|
|
1024
|
+
"| # | Pattern | Where | Notes |",
|
|
1025
|
+
"|---|---------|-------|-------|",
|
|
1026
|
+
"",
|
|
1027
|
+
"## Lessons Learned",
|
|
1028
|
+
"",
|
|
1029
|
+
"| # | What Happened | Root Cause | Fix | Scope |",
|
|
1030
|
+
"|---|--------------|------------|-----|-------|",
|
|
1031
|
+
"",
|
|
1032
|
+
].join("\n");
|
|
1033
|
+
} else if (type === "pattern") {
|
|
1034
|
+
content = header + [
|
|
1035
|
+
"## Rules",
|
|
1036
|
+
"",
|
|
1037
|
+
"| # | Scope | Rule | Why | Added |",
|
|
1038
|
+
"|---|-------|------|-----|-------|",
|
|
1039
|
+
"",
|
|
1040
|
+
"## Patterns",
|
|
1041
|
+
"",
|
|
1042
|
+
"| # | Pattern | Where | Notes |",
|
|
1043
|
+
"|---|---------|-------|-------|",
|
|
1044
|
+
`| P001 | ${entry} | — | ${scope} |`,
|
|
1045
|
+
"",
|
|
1046
|
+
"## Lessons Learned",
|
|
1047
|
+
"",
|
|
1048
|
+
"| # | What Happened | Root Cause | Fix | Scope |",
|
|
1049
|
+
"|---|--------------|------------|-----|-------|",
|
|
1050
|
+
"",
|
|
1051
|
+
].join("\n");
|
|
1052
|
+
} else {
|
|
1053
|
+
content = header + [
|
|
1054
|
+
"## Rules",
|
|
1055
|
+
"",
|
|
1056
|
+
"| # | Scope | Rule | Why | Added |",
|
|
1057
|
+
"|---|-------|------|-----|-------|",
|
|
1058
|
+
"",
|
|
1059
|
+
"## Patterns",
|
|
1060
|
+
"",
|
|
1061
|
+
"| # | Pattern | Where | Notes |",
|
|
1062
|
+
"|---|---------|-------|-------|",
|
|
1063
|
+
"",
|
|
1064
|
+
"## Lessons Learned",
|
|
1065
|
+
"",
|
|
1066
|
+
"| # | What Happened | Root Cause | Fix | Scope |",
|
|
1067
|
+
"|---|--------------|------------|-----|-------|",
|
|
1068
|
+
`| L001 | ${entry} | — | — | ${scope} |`,
|
|
1069
|
+
"",
|
|
1070
|
+
].join("\n");
|
|
1071
|
+
}
|
|
1072
|
+
await saveFile(knowledgePath, content);
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
|
|
950
1076
|
export async function loadActiveOverrides(basePath: string): Promise<Override[]> {
|
|
951
1077
|
const overridesPath = resolveGsdRootFile(basePath, "OVERRIDES");
|
|
952
1078
|
const content = await loadFile(overridesPath);
|
|
@@ -47,6 +47,11 @@ export interface GitPreferences {
|
|
|
47
47
|
* - "branch": works directly in the project root (for submodule-heavy repos)
|
|
48
48
|
*/
|
|
49
49
|
isolation?: "worktree" | "branch";
|
|
50
|
+
/** When false, prevents GSD from committing .gsd/ planning artifacts to git.
|
|
51
|
+
* The .gsd/ folder is added to .gitignore and kept local-only.
|
|
52
|
+
* Default: true (planning docs are tracked in git).
|
|
53
|
+
*/
|
|
54
|
+
commit_docs?: boolean;
|
|
50
55
|
}
|
|
51
56
|
|
|
52
57
|
export const VALID_BRANCH_NAME = /^[a-zA-Z0-9_\-\/.]+$/;
|
|
@@ -152,7 +157,7 @@ export function readIntegrationBranch(basePath: string, milestoneId: string): st
|
|
|
152
157
|
*
|
|
153
158
|
* The file is committed immediately so the metadata is persisted in git.
|
|
154
159
|
*/
|
|
155
|
-
export function writeIntegrationBranch(basePath: string, milestoneId: string, branch: string): void {
|
|
160
|
+
export function writeIntegrationBranch(basePath: string, milestoneId: string, branch: string, options?: { commitDocs?: boolean }): void {
|
|
156
161
|
// Don't record slice branches as the integration target
|
|
157
162
|
if (SLICE_BRANCH_RE.test(branch)) return;
|
|
158
163
|
// Validate
|
|
@@ -178,12 +183,15 @@ export function writeIntegrationBranch(basePath: string, milestoneId: string, br
|
|
|
178
183
|
writeFileSync(metaFile, JSON.stringify(existing, null, 2) + "\n", "utf-8");
|
|
179
184
|
|
|
180
185
|
// Commit immediately so the metadata is persisted in git.
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
186
|
+
// Skip when commit_docs is explicitly false — .gsd/ is local-only.
|
|
187
|
+
if (options?.commitDocs !== false) {
|
|
188
|
+
try {
|
|
189
|
+
nativeAddPaths(basePath, [metaFile]);
|
|
190
|
+
nativeCommit(basePath, `chore(${milestoneId}): record integration branch`, { allowEmpty: false });
|
|
191
|
+
} catch {
|
|
192
|
+
// Non-fatal — file is on disk even if commit fails (e.g. nothing to commit
|
|
193
|
+
// because the file was already tracked with identical content)
|
|
194
|
+
}
|
|
187
195
|
}
|
|
188
196
|
}
|
|
189
197
|
|
|
@@ -284,7 +292,10 @@ export class GitServiceImpl {
|
|
|
284
292
|
* @param extraExclusions Additional pathspec exclusions beyond RUNTIME_EXCLUSION_PATHS.
|
|
285
293
|
*/
|
|
286
294
|
private smartStage(extraExclusions: readonly string[] = []): void {
|
|
287
|
-
|
|
295
|
+
// When commit_docs is false, exclude the entire .gsd/ directory from staging
|
|
296
|
+
const commitDocsDisabled = this.prefs.commit_docs === false;
|
|
297
|
+
const gsdExclusion = commitDocsDisabled ? [".gsd/"] : [];
|
|
298
|
+
const allExclusions = [...RUNTIME_EXCLUSION_PATHS, ...gsdExclusion, ...extraExclusions];
|
|
288
299
|
|
|
289
300
|
// One-time cleanup: if runtime files are already tracked in the index
|
|
290
301
|
// (from older versions where the fallback bug staged them), untrack them
|
|
@@ -78,15 +78,26 @@ const BASELINE_PATTERNS = [
|
|
|
78
78
|
* Ensure basePath/.gitignore contains all baseline patterns.
|
|
79
79
|
* Creates the file if missing; appends only missing lines if it exists.
|
|
80
80
|
* Returns true if the file was created or modified, false if already complete.
|
|
81
|
+
*
|
|
82
|
+
* When `commitDocs` is false, the entire `.gsd/` directory is added to
|
|
83
|
+
* .gitignore instead of individual runtime patterns, keeping all GSD
|
|
84
|
+
* artifacts local-only.
|
|
81
85
|
*/
|
|
82
|
-
export function ensureGitignore(basePath: string): boolean {
|
|
86
|
+
export function ensureGitignore(basePath: string, options?: { commitDocs?: boolean }): boolean {
|
|
83
87
|
const gitignorePath = join(basePath, ".gitignore");
|
|
88
|
+
const commitDocs = options?.commitDocs !== false; // default true
|
|
84
89
|
|
|
85
90
|
let existing = "";
|
|
86
91
|
if (existsSync(gitignorePath)) {
|
|
87
92
|
existing = readFileSync(gitignorePath, "utf-8");
|
|
88
93
|
}
|
|
89
94
|
|
|
95
|
+
// When commit_docs is false, ensure blanket ".gsd/" is in .gitignore
|
|
96
|
+
// and skip the self-heal that would remove it.
|
|
97
|
+
if (!commitDocs) {
|
|
98
|
+
return ensureBlanketGsdIgnore(gitignorePath, existing);
|
|
99
|
+
}
|
|
100
|
+
|
|
90
101
|
// Self-heal: remove blanket ".gsd/" lines from pre-v2.14.0 projects.
|
|
91
102
|
// The blanket ignore prevented planning artifacts (.gsd/milestones/) from
|
|
92
103
|
// being tracked in git, causing artifacts to vanish in worktrees and
|
|
@@ -203,7 +214,7 @@ See \`~/.gsd/agent/extensions/gsd/docs/preferences-reference.md\` for full field
|
|
|
203
214
|
- \`models\`: Model preferences for specific task types
|
|
204
215
|
- \`skill_discovery\`: Automatic skill detection preferences
|
|
205
216
|
- \`auto_supervisor\`: Supervision and gating rules for autonomous modes
|
|
206
|
-
- \`git\`: Git preferences — \`main_branch\` (default branch name for new repos, e.g., "main", "master", "trunk"), \`auto_push\`, \`snapshots\`, etc.
|
|
217
|
+
- \`git\`: Git preferences — \`main_branch\` (default branch name for new repos, e.g., "main", "master", "trunk"), \`auto_push\`, \`snapshots\`, \`commit_docs\` (set to \`false\` to keep .gsd/ local-only), etc.
|
|
207
218
|
|
|
208
219
|
## Examples
|
|
209
220
|
|
|
@@ -224,3 +235,31 @@ custom_instructions:
|
|
|
224
235
|
return true;
|
|
225
236
|
}
|
|
226
237
|
|
|
238
|
+
/**
|
|
239
|
+
* When commit_docs is false, ensure `.gsd/` is in .gitignore as a blanket
|
|
240
|
+
* pattern. This keeps all GSD artifacts local-only.
|
|
241
|
+
* Returns true if the file was modified, false if already complete.
|
|
242
|
+
*/
|
|
243
|
+
function ensureBlanketGsdIgnore(gitignorePath: string, existing: string): boolean {
|
|
244
|
+
const existingLines = new Set(
|
|
245
|
+
existing
|
|
246
|
+
.split("\n")
|
|
247
|
+
.map((l) => l.trim())
|
|
248
|
+
.filter((l) => l && !l.startsWith("#")),
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
// Already has blanket .gsd/ ignore
|
|
252
|
+
if (existingLines.has(".gsd/") || existingLines.has(".gsd")) return false;
|
|
253
|
+
|
|
254
|
+
const block = [
|
|
255
|
+
"",
|
|
256
|
+
"# ── GSD (local-only, commit_docs: false) ──",
|
|
257
|
+
".gsd/",
|
|
258
|
+
"",
|
|
259
|
+
].join("\n");
|
|
260
|
+
|
|
261
|
+
const prefix = existing && !existing.endsWith("\n") ? "\n" : "";
|
|
262
|
+
writeFileSync(gitignorePath, existing + prefix + block, "utf-8");
|
|
263
|
+
return true;
|
|
264
|
+
}
|
|
265
|
+
|