gsd-pi 2.17.0 → 2.19.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 +14 -2
- package/dist/resources/extensions/gsd/auto-prompts.ts +65 -16
- package/dist/resources/extensions/gsd/auto-worktree.ts +33 -4
- package/dist/resources/extensions/gsd/auto.ts +399 -29
- package/dist/resources/extensions/gsd/captures.ts +384 -0
- package/dist/resources/extensions/gsd/commands.ts +382 -23
- package/dist/resources/extensions/gsd/complexity-classifier.ts +322 -0
- package/dist/resources/extensions/gsd/dashboard-overlay.ts +10 -0
- package/dist/resources/extensions/gsd/dispatch-guard.ts +7 -19
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +201 -2
- package/dist/resources/extensions/gsd/files.ts +123 -1
- package/dist/resources/extensions/gsd/guided-flow.ts +237 -4
- package/dist/resources/extensions/gsd/index.ts +47 -3
- package/dist/resources/extensions/gsd/metrics.ts +48 -0
- package/dist/resources/extensions/gsd/model-cost-table.ts +65 -0
- package/dist/resources/extensions/gsd/model-router.ts +256 -0
- package/dist/resources/extensions/gsd/paths.ts +9 -0
- package/dist/resources/extensions/gsd/post-unit-hooks.ts +2 -1
- package/dist/resources/extensions/gsd/preferences.ts +132 -1
- package/dist/resources/extensions/gsd/prompt-loader.ts +45 -9
- package/dist/resources/extensions/gsd/prompts/execute-task.md +6 -5
- package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -0
- package/dist/resources/extensions/gsd/prompts/replan-slice.md +8 -0
- package/dist/resources/extensions/gsd/prompts/system.md +2 -0
- package/dist/resources/extensions/gsd/prompts/triage-captures.md +62 -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/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-worktree.test.ts +20 -0
- package/dist/resources/extensions/gsd/tests/captures.test.ts +438 -0
- package/dist/resources/extensions/gsd/tests/complexity-classifier.test.ts +181 -0
- package/dist/resources/extensions/gsd/tests/derive-state-deps.test.ts +99 -0
- package/dist/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +434 -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/milestone-transition-worktree.test.ts +144 -0
- package/dist/resources/extensions/gsd/tests/model-cost-table.test.ts +69 -0
- package/dist/resources/extensions/gsd/tests/model-router.test.ts +167 -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/remote-questions.test.ts +227 -1
- package/dist/resources/extensions/gsd/tests/routing-history.test.ts +215 -62
- package/dist/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +139 -0
- package/dist/resources/extensions/gsd/tests/triage-dispatch.test.ts +224 -0
- package/dist/resources/extensions/gsd/tests/triage-resolution.test.ts +215 -0
- package/dist/resources/extensions/gsd/tests/visualizer-data.test.ts +198 -0
- package/dist/resources/extensions/gsd/tests/visualizer-views.test.ts +255 -0
- package/dist/resources/extensions/gsd/triage-resolution.ts +200 -0
- package/dist/resources/extensions/gsd/triage-ui.ts +175 -0
- package/dist/resources/extensions/gsd/visualizer-data.ts +154 -0
- package/dist/resources/extensions/gsd/visualizer-overlay.ts +193 -0
- package/dist/resources/extensions/gsd/visualizer-views.ts +293 -0
- package/dist/resources/extensions/gsd/worktree-manager.ts +8 -5
- package/dist/resources/extensions/gsd/worktree.ts +22 -0
- package/dist/resources/extensions/remote-questions/discord-adapter.ts +33 -0
- package/dist/resources/extensions/remote-questions/format.ts +12 -6
- package/dist/resources/extensions/remote-questions/manager.ts +8 -0
- package/dist/resources/extensions/shared/next-action-ui.ts +16 -1
- package/package.json +1 -1
- 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/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/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/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/src/resources/extensions/gsd/activity-log.ts +37 -7
- package/src/resources/extensions/gsd/auto-dashboard.ts +14 -2
- package/src/resources/extensions/gsd/auto-prompts.ts +65 -16
- package/src/resources/extensions/gsd/auto-worktree.ts +33 -4
- package/src/resources/extensions/gsd/auto.ts +399 -29
- package/src/resources/extensions/gsd/captures.ts +384 -0
- package/src/resources/extensions/gsd/commands.ts +382 -23
- package/src/resources/extensions/gsd/complexity-classifier.ts +322 -0
- package/src/resources/extensions/gsd/dashboard-overlay.ts +10 -0
- package/src/resources/extensions/gsd/dispatch-guard.ts +7 -19
- package/src/resources/extensions/gsd/docs/preferences-reference.md +201 -2
- package/src/resources/extensions/gsd/files.ts +123 -1
- package/src/resources/extensions/gsd/guided-flow.ts +237 -4
- package/src/resources/extensions/gsd/index.ts +47 -3
- package/src/resources/extensions/gsd/metrics.ts +48 -0
- package/src/resources/extensions/gsd/model-cost-table.ts +65 -0
- package/src/resources/extensions/gsd/model-router.ts +256 -0
- package/src/resources/extensions/gsd/paths.ts +9 -0
- package/src/resources/extensions/gsd/post-unit-hooks.ts +2 -1
- package/src/resources/extensions/gsd/preferences.ts +132 -1
- package/src/resources/extensions/gsd/prompt-loader.ts +45 -9
- package/src/resources/extensions/gsd/prompts/execute-task.md +6 -5
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -0
- package/src/resources/extensions/gsd/prompts/replan-slice.md +8 -0
- package/src/resources/extensions/gsd/prompts/system.md +2 -0
- package/src/resources/extensions/gsd/prompts/triage-captures.md +62 -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/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-worktree.test.ts +20 -0
- package/src/resources/extensions/gsd/tests/captures.test.ts +438 -0
- package/src/resources/extensions/gsd/tests/complexity-classifier.test.ts +181 -0
- package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +99 -0
- package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +434 -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/milestone-transition-worktree.test.ts +144 -0
- package/src/resources/extensions/gsd/tests/model-cost-table.test.ts +69 -0
- package/src/resources/extensions/gsd/tests/model-router.test.ts +167 -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/remote-questions.test.ts +227 -1
- package/src/resources/extensions/gsd/tests/routing-history.test.ts +215 -62
- package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +139 -0
- package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +224 -0
- package/src/resources/extensions/gsd/tests/triage-resolution.test.ts +215 -0
- package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +198 -0
- package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +255 -0
- package/src/resources/extensions/gsd/triage-resolution.ts +200 -0
- package/src/resources/extensions/gsd/triage-ui.ts +175 -0
- package/src/resources/extensions/gsd/visualizer-data.ts +154 -0
- package/src/resources/extensions/gsd/visualizer-overlay.ts +193 -0
- package/src/resources/extensions/gsd/visualizer-views.ts +293 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +8 -5
- package/src/resources/extensions/gsd/worktree.ts +22 -0
- package/src/resources/extensions/remote-questions/discord-adapter.ts +33 -0
- package/src/resources/extensions/remote-questions/format.ts +12 -6
- package/src/resources/extensions/remote-questions/manager.ts +8 -0
- package/src/resources/extensions/shared/next-action-ui.ts +16 -1
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
// GSD Extension — Complexity Classifier
|
|
2
|
+
// Classifies unit complexity for dynamic model routing.
|
|
3
|
+
// Pure heuristics + adaptive learning — no LLM calls. Sub-millisecond classification.
|
|
4
|
+
|
|
5
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { gsdRoot } from "./paths.js";
|
|
8
|
+
import { getAdaptiveTierAdjustment } from "./routing-history.js";
|
|
9
|
+
|
|
10
|
+
// ─── Types ───────────────────────────────────────────────────────────────────
|
|
11
|
+
|
|
12
|
+
export type ComplexityTier = "light" | "standard" | "heavy";
|
|
13
|
+
|
|
14
|
+
export interface ClassificationResult {
|
|
15
|
+
tier: ComplexityTier;
|
|
16
|
+
reason: string;
|
|
17
|
+
downgraded: boolean; // true if budget pressure lowered the tier
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface TaskMetadata {
|
|
21
|
+
fileCount?: number;
|
|
22
|
+
dependencyCount?: number;
|
|
23
|
+
isNewFile?: boolean;
|
|
24
|
+
tags?: string[];
|
|
25
|
+
estimatedLines?: number;
|
|
26
|
+
codeBlockCount?: number; // number of fenced code blocks in plan
|
|
27
|
+
complexityKeywords?: string[]; // detected complexity signals
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// ─── Unit Type → Default Tier Mapping ────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
const UNIT_TYPE_TIERS: Record<string, ComplexityTier> = {
|
|
33
|
+
// Tier 1 — Light: structured summaries, completion, UAT
|
|
34
|
+
"complete-slice": "light",
|
|
35
|
+
"run-uat": "light",
|
|
36
|
+
|
|
37
|
+
// Tier 2 — Standard: research, routine planning
|
|
38
|
+
"research-milestone": "standard",
|
|
39
|
+
"research-slice": "standard",
|
|
40
|
+
"plan-milestone": "standard",
|
|
41
|
+
"plan-slice": "standard",
|
|
42
|
+
|
|
43
|
+
// Tier 3 — Heavy: execution, replanning (requires deep reasoning)
|
|
44
|
+
"execute-task": "standard", // default standard, upgraded by metadata
|
|
45
|
+
"replan-slice": "heavy",
|
|
46
|
+
"reassess-roadmap": "heavy",
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// ─── Public API ──────────────────────────────────────────────────────────────
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Classify unit complexity to determine which model tier to use.
|
|
53
|
+
*
|
|
54
|
+
* @param unitType The type of unit being dispatched
|
|
55
|
+
* @param unitId The unit ID (e.g. "M001/S01/T01")
|
|
56
|
+
* @param basePath Project base path (for reading task plans)
|
|
57
|
+
* @param budgetPct Current budget usage as fraction (0.0-1.0+), or undefined if no budget
|
|
58
|
+
* @param metadata Optional pre-parsed task metadata
|
|
59
|
+
*/
|
|
60
|
+
export function classifyUnitComplexity(
|
|
61
|
+
unitType: string,
|
|
62
|
+
unitId: string,
|
|
63
|
+
basePath: string,
|
|
64
|
+
budgetPct?: number,
|
|
65
|
+
metadata?: TaskMetadata,
|
|
66
|
+
): ClassificationResult {
|
|
67
|
+
// Hook units default to light
|
|
68
|
+
if (unitType.startsWith("hook/")) {
|
|
69
|
+
const result: ClassificationResult = { tier: "light", reason: "hook unit", downgraded: false };
|
|
70
|
+
return applyBudgetPressure(result, budgetPct);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Start with the default tier for this unit type
|
|
74
|
+
let tier = UNIT_TYPE_TIERS[unitType] ?? "standard";
|
|
75
|
+
let reason = `unit type: ${unitType}`;
|
|
76
|
+
|
|
77
|
+
// For execute-task, analyze task metadata for complexity signals
|
|
78
|
+
if (unitType === "execute-task") {
|
|
79
|
+
const taskAnalysis = analyzeTaskComplexity(unitId, basePath, metadata);
|
|
80
|
+
tier = taskAnalysis.tier;
|
|
81
|
+
reason = taskAnalysis.reason;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// For plan-slice, check if the slice has many tasks (complex planning)
|
|
85
|
+
if (unitType === "plan-slice" || unitType === "plan-milestone") {
|
|
86
|
+
const planAnalysis = analyzePlanComplexity(unitId, basePath);
|
|
87
|
+
if (planAnalysis) {
|
|
88
|
+
tier = planAnalysis.tier;
|
|
89
|
+
reason = planAnalysis.reason;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Adaptive learning: check if history suggests bumping the tier
|
|
94
|
+
const tags = metadata?.tags ?? extractTaskMetadata(unitId, basePath).tags;
|
|
95
|
+
const adaptiveAdjustment = getAdaptiveTierAdjustment(unitType, tier, tags);
|
|
96
|
+
if (adaptiveAdjustment && tierOrdinal(adaptiveAdjustment) > tierOrdinal(tier)) {
|
|
97
|
+
reason = `${reason} (adaptive: high failure rate at ${tier})`;
|
|
98
|
+
tier = adaptiveAdjustment;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const result: ClassificationResult = { tier, reason, downgraded: false };
|
|
102
|
+
return applyBudgetPressure(result, budgetPct);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Get a short label for the tier (for dashboard display).
|
|
107
|
+
*/
|
|
108
|
+
export function tierLabel(tier: ComplexityTier): string {
|
|
109
|
+
switch (tier) {
|
|
110
|
+
case "light": return "L";
|
|
111
|
+
case "standard": return "S";
|
|
112
|
+
case "heavy": return "H";
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Get the tier ordering value (for comparison).
|
|
118
|
+
*/
|
|
119
|
+
export function tierOrdinal(tier: ComplexityTier): number {
|
|
120
|
+
switch (tier) {
|
|
121
|
+
case "light": return 0;
|
|
122
|
+
case "standard": return 1;
|
|
123
|
+
case "heavy": return 2;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ─── Task Complexity Analysis ────────────────────────────────────────────────
|
|
128
|
+
|
|
129
|
+
interface TaskAnalysis {
|
|
130
|
+
tier: ComplexityTier;
|
|
131
|
+
reason: string;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function analyzeTaskComplexity(
|
|
135
|
+
unitId: string,
|
|
136
|
+
basePath: string,
|
|
137
|
+
metadata?: TaskMetadata,
|
|
138
|
+
): TaskAnalysis {
|
|
139
|
+
// Try to read task plan for complexity signals
|
|
140
|
+
const meta = metadata ?? extractTaskMetadata(unitId, basePath);
|
|
141
|
+
|
|
142
|
+
// Heavy signals
|
|
143
|
+
if (meta.dependencyCount && meta.dependencyCount >= 3) {
|
|
144
|
+
return { tier: "heavy", reason: `${meta.dependencyCount} dependencies` };
|
|
145
|
+
}
|
|
146
|
+
if (meta.fileCount && meta.fileCount >= 6) {
|
|
147
|
+
return { tier: "heavy", reason: `${meta.fileCount} files to modify` };
|
|
148
|
+
}
|
|
149
|
+
if (meta.estimatedLines && meta.estimatedLines >= 500) {
|
|
150
|
+
return { tier: "heavy", reason: `~${meta.estimatedLines} lines estimated` };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Heavy signals from complexity keywords (Phase 4)
|
|
154
|
+
if (meta.complexityKeywords && meta.complexityKeywords.length >= 2) {
|
|
155
|
+
return { tier: "heavy", reason: `complex: ${meta.complexityKeywords.join(", ")}` };
|
|
156
|
+
}
|
|
157
|
+
if (meta.codeBlockCount && meta.codeBlockCount >= 5) {
|
|
158
|
+
return { tier: "heavy", reason: `${meta.codeBlockCount} code blocks in plan` };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Standard signals from single complexity keyword
|
|
162
|
+
if (meta.complexityKeywords && meta.complexityKeywords.length === 1) {
|
|
163
|
+
return { tier: "standard", reason: `${meta.complexityKeywords[0]} task` };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Light signals (simple tasks)
|
|
167
|
+
if (meta.tags?.some(t => /^(docs?|readme|comment|config|typo|rename)$/i.test(t))) {
|
|
168
|
+
return { tier: "light", reason: `simple task: ${meta.tags.join(", ")}` };
|
|
169
|
+
}
|
|
170
|
+
if (meta.fileCount !== undefined && meta.fileCount <= 1 && !meta.isNewFile) {
|
|
171
|
+
return { tier: "light", reason: "single file modification" };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Standard by default
|
|
175
|
+
return { tier: "standard", reason: "standard execution task" };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function analyzePlanComplexity(
|
|
179
|
+
unitId: string,
|
|
180
|
+
basePath: string,
|
|
181
|
+
): TaskAnalysis | null {
|
|
182
|
+
// Check if this is a milestone-level plan (more complex) vs single slice
|
|
183
|
+
const parts = unitId.split("/");
|
|
184
|
+
if (parts.length === 1) {
|
|
185
|
+
// Milestone-level planning is always at least standard
|
|
186
|
+
return { tier: "standard", reason: "milestone-level planning" };
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// For slice planning, try to read the context/research to gauge complexity
|
|
190
|
+
// If research exists and is large, bump to heavy
|
|
191
|
+
const [mid, sid] = parts;
|
|
192
|
+
const researchPath = join(gsdRoot(basePath), mid, "slices", sid, "RESEARCH.md");
|
|
193
|
+
try {
|
|
194
|
+
if (existsSync(researchPath)) {
|
|
195
|
+
const content = readFileSync(researchPath, "utf-8");
|
|
196
|
+
const lineCount = content.split("\n").length;
|
|
197
|
+
if (lineCount > 200) {
|
|
198
|
+
return { tier: "heavy", reason: `complex slice: ${lineCount}-line research` };
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
} catch {
|
|
202
|
+
// Non-fatal
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return null; // Use default tier
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Extract task metadata from the task plan file on disk.
|
|
210
|
+
*/
|
|
211
|
+
function extractTaskMetadata(unitId: string, basePath: string): TaskMetadata {
|
|
212
|
+
const meta: TaskMetadata = {};
|
|
213
|
+
const parts = unitId.split("/");
|
|
214
|
+
if (parts.length !== 3) return meta;
|
|
215
|
+
|
|
216
|
+
const [mid, sid, tid] = parts;
|
|
217
|
+
const taskPlanPath = join(gsdRoot(basePath), mid, "slices", sid, "tasks", `${tid}-PLAN.md`);
|
|
218
|
+
|
|
219
|
+
try {
|
|
220
|
+
if (!existsSync(taskPlanPath)) return meta;
|
|
221
|
+
const content = readFileSync(taskPlanPath, "utf-8");
|
|
222
|
+
const lines = content.split("\n");
|
|
223
|
+
|
|
224
|
+
// Count files mentioned in "Files:" or "- Files:" lines
|
|
225
|
+
const fileLines = lines.filter(l => /^\s*-?\s*files?\s*:/i.test(l));
|
|
226
|
+
if (fileLines.length > 0) {
|
|
227
|
+
// Count comma-separated or bullet-pointed files
|
|
228
|
+
const allFiles = new Set<string>();
|
|
229
|
+
for (const line of fileLines) {
|
|
230
|
+
const filesStr = line.replace(/^\s*-?\s*files?\s*:\s*/i, "");
|
|
231
|
+
const files = filesStr.split(/[,;]/).map(f => f.trim()).filter(Boolean);
|
|
232
|
+
files.forEach(f => allFiles.add(f));
|
|
233
|
+
}
|
|
234
|
+
meta.fileCount = allFiles.size;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Check for "new file" or "create" keywords
|
|
238
|
+
meta.isNewFile = lines.some(l => /\b(create|new file|scaffold|bootstrap)\b/i.test(l));
|
|
239
|
+
|
|
240
|
+
// Look for tags/labels in frontmatter or content
|
|
241
|
+
const tags: string[] = [];
|
|
242
|
+
if (content.match(/\b(refactor|migration|architect)/i)) tags.push("refactor");
|
|
243
|
+
if (content.match(/\b(test|spec|coverage)\b/i)) tags.push("test");
|
|
244
|
+
if (content.match(/\b(doc|readme|comment|jsdoc)\b/i)) tags.push("docs");
|
|
245
|
+
if (content.match(/\b(config|env|setting)\b/i)) tags.push("config");
|
|
246
|
+
if (content.match(/\b(rename|typo|spelling)\b/i)) tags.push("rename");
|
|
247
|
+
meta.tags = tags;
|
|
248
|
+
|
|
249
|
+
// Try to extract estimated lines from content
|
|
250
|
+
const estimateMatch = content.match(/~?\s*(\d+)\s*lines?\b/i);
|
|
251
|
+
if (estimateMatch) {
|
|
252
|
+
meta.estimatedLines = parseInt(estimateMatch[1], 10);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Phase 4: Deeper introspection signals
|
|
256
|
+
|
|
257
|
+
// Count fenced code blocks (```) — more code blocks = more complex implementation
|
|
258
|
+
const codeBlockMatches = content.match(/^```/gm);
|
|
259
|
+
meta.codeBlockCount = codeBlockMatches ? Math.floor(codeBlockMatches.length / 2) : 0;
|
|
260
|
+
|
|
261
|
+
// Detect complexity keywords that suggest harder tasks
|
|
262
|
+
const complexityKeywords: string[] = [];
|
|
263
|
+
if (content.match(/\b(migration|migrate|schema change)\b/i)) complexityKeywords.push("migration");
|
|
264
|
+
if (content.match(/\b(architect|design pattern|system design)\b/i)) complexityKeywords.push("architecture");
|
|
265
|
+
if (content.match(/\b(security|auth|encrypt|credential|vulnerability)\b/i)) complexityKeywords.push("security");
|
|
266
|
+
if (content.match(/\b(performance|optimize|cache|index)\b/i)) complexityKeywords.push("performance");
|
|
267
|
+
if (content.match(/\b(concurrent|parallel|race condition|mutex|lock)\b/i)) complexityKeywords.push("concurrency");
|
|
268
|
+
if (content.match(/\b(backward.?compat|breaking change|deprecat)\b/i)) complexityKeywords.push("compatibility");
|
|
269
|
+
meta.complexityKeywords = complexityKeywords;
|
|
270
|
+
} catch {
|
|
271
|
+
// Non-fatal — metadata extraction is best-effort
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return meta;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// ─── Budget Pressure ─────────────────────────────────────────────────────────
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Apply budget pressure to a classification result.
|
|
281
|
+
* As budget usage increases, more aggressively downgrade tiers.
|
|
282
|
+
*
|
|
283
|
+
* - <50%: Normal classification (no change)
|
|
284
|
+
* - 50-75%: Tier 2 → Tier 1 where possible
|
|
285
|
+
* - 75-90%: Only heavy tasks keep configured model
|
|
286
|
+
* - >90%: Everything except replan-slice gets cheapest model
|
|
287
|
+
*/
|
|
288
|
+
function applyBudgetPressure(
|
|
289
|
+
result: ClassificationResult,
|
|
290
|
+
budgetPct?: number,
|
|
291
|
+
): ClassificationResult {
|
|
292
|
+
if (budgetPct === undefined || budgetPct < 0.5) return result;
|
|
293
|
+
|
|
294
|
+
const original = result.tier;
|
|
295
|
+
|
|
296
|
+
if (budgetPct >= 0.9) {
|
|
297
|
+
// >90%: almost everything goes to light
|
|
298
|
+
if (result.tier !== "heavy") {
|
|
299
|
+
result.tier = "light";
|
|
300
|
+
} else {
|
|
301
|
+
// Even heavy gets downgraded to standard
|
|
302
|
+
result.tier = "standard";
|
|
303
|
+
}
|
|
304
|
+
} else if (budgetPct >= 0.75) {
|
|
305
|
+
// 75-90%: only heavy stays, everything else goes to light
|
|
306
|
+
if (result.tier === "standard") {
|
|
307
|
+
result.tier = "light";
|
|
308
|
+
}
|
|
309
|
+
} else {
|
|
310
|
+
// 50-75%: standard → light
|
|
311
|
+
if (result.tier === "standard") {
|
|
312
|
+
result.tier = "light";
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (result.tier !== original) {
|
|
317
|
+
result.downgraded = true;
|
|
318
|
+
result.reason = `${result.reason} (budget pressure: ${Math.round(budgetPct * 100)}%)`;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return result;
|
|
322
|
+
}
|
|
@@ -39,6 +39,9 @@ function unitLabel(type: string): string {
|
|
|
39
39
|
case "execute-task": return "Execute";
|
|
40
40
|
case "complete-slice": return "Complete";
|
|
41
41
|
case "reassess-roadmap": return "Reassess";
|
|
42
|
+
case "triage-captures": return "Triage";
|
|
43
|
+
case "quick-task": return "Quick Task";
|
|
44
|
+
case "replan-slice": return "Replan";
|
|
42
45
|
default: return type;
|
|
43
46
|
}
|
|
44
47
|
}
|
|
@@ -345,6 +348,13 @@ export class GSDDashboardOverlay {
|
|
|
345
348
|
lines.push(blank());
|
|
346
349
|
}
|
|
347
350
|
|
|
351
|
+
// Pending captures badge — only shown when captures are waiting for triage
|
|
352
|
+
if (this.dashData.pendingCaptureCount > 0) {
|
|
353
|
+
const count = this.dashData.pendingCaptureCount;
|
|
354
|
+
lines.push(row(th.fg("warning", `📌 ${count} pending capture${count === 1 ? "" : "s"} awaiting triage`)));
|
|
355
|
+
lines.push(blank());
|
|
356
|
+
}
|
|
357
|
+
|
|
348
358
|
if (this.loading) {
|
|
349
359
|
lines.push(centered(th.fg("dim", "Loading dashboard…")));
|
|
350
360
|
return lines;
|
|
@@ -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,10 +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"`.
|
|
111
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`.
|
|
112
114
|
|
|
113
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`.
|
|
114
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
|
+
|
|
115
180
|
---
|
|
116
181
|
|
|
117
182
|
## Best Practices
|
|
@@ -277,3 +342,137 @@ git:
|
|
|
277
342
|
```
|
|
278
343
|
|
|
279
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.
|