pi-gsd 2.0.1 → 2.0.3
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/pi-gsd-hooks.js +1533 -0
- package/dist/pi-gsd-tools.js +53 -52
- package/package.json +3 -5
- package/.gsd/extensions/pi-gsd-hooks.ts +0 -973
- package/src/cli.ts +0 -644
- package/src/commands/base.ts +0 -67
- package/src/commands/commit.ts +0 -22
- package/src/commands/config.ts +0 -71
- package/src/commands/frontmatter.ts +0 -51
- package/src/commands/index.ts +0 -76
- package/src/commands/init.ts +0 -43
- package/src/commands/milestone.ts +0 -37
- package/src/commands/phase.ts +0 -92
- package/src/commands/progress.ts +0 -71
- package/src/commands/roadmap.ts +0 -40
- package/src/commands/scaffold.ts +0 -19
- package/src/commands/state.ts +0 -102
- package/src/commands/template.ts +0 -52
- package/src/commands/verify.ts +0 -70
- package/src/commands/workstream.ts +0 -98
- package/src/commands/wxp.ts +0 -65
- package/src/lib/commands.ts +0 -1040
- package/src/lib/config.ts +0 -385
- package/src/lib/core.ts +0 -1167
- package/src/lib/frontmatter.ts +0 -462
- package/src/lib/init.ts +0 -517
- package/src/lib/milestone.ts +0 -290
- package/src/lib/model-profiles.ts +0 -272
- package/src/lib/phase.ts +0 -1012
- package/src/lib/profile-output.ts +0 -237
- package/src/lib/profile-pipeline.ts +0 -556
- package/src/lib/roadmap.ts +0 -378
- package/src/lib/schemas.ts +0 -290
- package/src/lib/security.ts +0 -176
- package/src/lib/state.ts +0 -1175
- package/src/lib/template.ts +0 -246
- package/src/lib/uat.ts +0 -289
- package/src/lib/verify.ts +0 -879
- package/src/lib/workstream.ts +0 -524
- package/src/output.ts +0 -45
- package/src/schemas/pi-gsd-settings.schema.json +0 -80
- package/src/schemas/wxp.xsd +0 -619
- package/src/schemas/wxp.zod.ts +0 -318
- package/src/wxp/__tests__/arguments.test.ts +0 -86
- package/src/wxp/__tests__/conditions.test.ts +0 -106
- package/src/wxp/__tests__/executor.test.ts +0 -95
- package/src/wxp/__tests__/helpers.ts +0 -26
- package/src/wxp/__tests__/integration.test.ts +0 -166
- package/src/wxp/__tests__/new-features.test.ts +0 -222
- package/src/wxp/__tests__/parser.test.ts +0 -159
- package/src/wxp/__tests__/paste.test.ts +0 -66
- package/src/wxp/__tests__/schema.test.ts +0 -120
- package/src/wxp/__tests__/security.test.ts +0 -87
- package/src/wxp/__tests__/shell.test.ts +0 -85
- package/src/wxp/__tests__/string-ops.test.ts +0 -25
- package/src/wxp/__tests__/variables.test.ts +0 -65
- package/src/wxp/arguments.ts +0 -89
- package/src/wxp/conditions.ts +0 -78
- package/src/wxp/executor.ts +0 -191
- package/src/wxp/index.ts +0 -191
- package/src/wxp/parser.ts +0 -198
- package/src/wxp/paste.ts +0 -51
- package/src/wxp/security.ts +0 -102
- package/src/wxp/shell.ts +0 -81
- package/src/wxp/string-ops.ts +0 -44
- package/src/wxp/variables.ts +0 -109
package/src/lib/config.ts
DELETED
|
@@ -1,385 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* config.ts - Planning config CRUD operations.
|
|
3
|
-
*
|
|
4
|
-
* Ported from lib/config.cjs. All command signatures preserved.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import fs from "fs";
|
|
8
|
-
import os from "os";
|
|
9
|
-
import path from "path";
|
|
10
|
-
import { gsdError, output, planningRoot } from "./core.js";
|
|
11
|
-
import {
|
|
12
|
-
formatAgentToModelMapAsTable,
|
|
13
|
-
getAgentToModelMapForProfile,
|
|
14
|
-
type ProfileKey,
|
|
15
|
-
VALID_PROFILES,
|
|
16
|
-
} from "./model-profiles.js";
|
|
17
|
-
import type { PlanningConfig } from "./schemas.js";
|
|
18
|
-
|
|
19
|
-
// ─── Valid config keys ────────────────────────────────────────────────────────
|
|
20
|
-
|
|
21
|
-
const VALID_CONFIG_KEYS = new Set([
|
|
22
|
-
"mode",
|
|
23
|
-
"granularity",
|
|
24
|
-
"parallelization",
|
|
25
|
-
"commit_docs",
|
|
26
|
-
"model_profile",
|
|
27
|
-
"search_gitignored",
|
|
28
|
-
"brave_search",
|
|
29
|
-
"firecrawl",
|
|
30
|
-
"exa_search",
|
|
31
|
-
"workflow.research",
|
|
32
|
-
"workflow.plan_check",
|
|
33
|
-
"workflow.verifier",
|
|
34
|
-
"workflow.nyquist_validation",
|
|
35
|
-
"workflow.ui_phase",
|
|
36
|
-
"workflow.ui_safety_gate",
|
|
37
|
-
"workflow.auto_advance",
|
|
38
|
-
"workflow.node_repair",
|
|
39
|
-
"workflow.node_repair_budget",
|
|
40
|
-
"workflow.text_mode",
|
|
41
|
-
"workflow.research_before_questions",
|
|
42
|
-
"workflow.discuss_mode",
|
|
43
|
-
"workflow.skip_discuss",
|
|
44
|
-
"workflow._auto_chain_active",
|
|
45
|
-
"git.branching_strategy",
|
|
46
|
-
"git.phase_branch_template",
|
|
47
|
-
"git.milestone_branch_template",
|
|
48
|
-
"git.quick_branch_template",
|
|
49
|
-
"planning.commit_docs",
|
|
50
|
-
"planning.search_gitignored",
|
|
51
|
-
"hooks.context_warnings",
|
|
52
|
-
]);
|
|
53
|
-
|
|
54
|
-
const CONFIG_KEY_SUGGESTIONS: Record<string, string> = {
|
|
55
|
-
"workflow.nyquist_validation_enabled": "workflow.nyquist_validation",
|
|
56
|
-
"agents.nyquist_validation_enabled": "workflow.nyquist_validation",
|
|
57
|
-
"nyquist.validation_enabled": "workflow.nyquist_validation",
|
|
58
|
-
"hooks.research_questions": "workflow.research_before_questions",
|
|
59
|
-
"workflow.research_questions": "workflow.research_before_questions",
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
function isValidConfigKey(keyPath: string): boolean {
|
|
63
|
-
if (VALID_CONFIG_KEYS.has(keyPath)) return true;
|
|
64
|
-
if (/^agent_skills\.[a-zA-Z0-9_-]+$/.test(keyPath)) return true;
|
|
65
|
-
return false;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function validateKnownConfigKeyPath(keyPath: string): void {
|
|
69
|
-
const suggested = CONFIG_KEY_SUGGESTIONS[keyPath];
|
|
70
|
-
if (suggested)
|
|
71
|
-
gsdError(`Unknown config key: ${keyPath}. Did you mean ${suggested}?`);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// ─── New project config builder ───────────────────────────────────────────────
|
|
75
|
-
|
|
76
|
-
function buildNewProjectConfig(
|
|
77
|
-
userChoices: Partial<PlanningConfig>,
|
|
78
|
-
): Record<string, unknown> {
|
|
79
|
-
const choices = userChoices || {};
|
|
80
|
-
const homedir = os.homedir();
|
|
81
|
-
|
|
82
|
-
const braveKeyFile = path.join(homedir, ".gsd", "brave_api_key");
|
|
83
|
-
const hasBraveSearch = !!(
|
|
84
|
-
process.env["BRAVE_API_KEY"] || fs.existsSync(braveKeyFile)
|
|
85
|
-
);
|
|
86
|
-
const firecrawlKeyFile = path.join(homedir, ".gsd", "firecrawl_api_key");
|
|
87
|
-
const hasFirecrawl = !!(
|
|
88
|
-
process.env["FIRECRAWL_API_KEY"] || fs.existsSync(firecrawlKeyFile)
|
|
89
|
-
);
|
|
90
|
-
const exaKeyFile = path.join(homedir, ".gsd", "exa_api_key");
|
|
91
|
-
const hasExaSearch = !!(
|
|
92
|
-
process.env["EXA_API_KEY"] || fs.existsSync(exaKeyFile)
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
const globalDefaultsPath = path.join(homedir, ".gsd", "defaults.json");
|
|
96
|
-
let userDefaults: Record<string, unknown> = {};
|
|
97
|
-
try {
|
|
98
|
-
if (fs.existsSync(globalDefaultsPath)) {
|
|
99
|
-
userDefaults = JSON.parse(fs.readFileSync(globalDefaultsPath, "utf-8"));
|
|
100
|
-
if ("depth" in userDefaults && !("granularity" in userDefaults)) {
|
|
101
|
-
const m: Record<string, string> = {
|
|
102
|
-
quick: "coarse",
|
|
103
|
-
standard: "standard",
|
|
104
|
-
comprehensive: "fine",
|
|
105
|
-
};
|
|
106
|
-
userDefaults.granularity =
|
|
107
|
-
m[userDefaults.depth as string] || (userDefaults.depth as string);
|
|
108
|
-
delete userDefaults.depth;
|
|
109
|
-
try {
|
|
110
|
-
fs.writeFileSync(
|
|
111
|
-
globalDefaultsPath,
|
|
112
|
-
JSON.stringify(userDefaults, null, 2),
|
|
113
|
-
"utf-8",
|
|
114
|
-
);
|
|
115
|
-
} catch {
|
|
116
|
-
/* ok */
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
} catch {
|
|
121
|
-
/* ok */
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const hardcoded = {
|
|
125
|
-
model_profile: "balanced",
|
|
126
|
-
commit_docs: true,
|
|
127
|
-
parallelization: true,
|
|
128
|
-
search_gitignored: false,
|
|
129
|
-
brave_search: hasBraveSearch,
|
|
130
|
-
firecrawl: hasFirecrawl,
|
|
131
|
-
exa_search: hasExaSearch,
|
|
132
|
-
git: {
|
|
133
|
-
branching_strategy: "none",
|
|
134
|
-
phase_branch_template: "gsd/phase-{phase}-{slug}",
|
|
135
|
-
milestone_branch_template: "gsd/{milestone}-{slug}",
|
|
136
|
-
quick_branch_template: null,
|
|
137
|
-
},
|
|
138
|
-
workflow: {
|
|
139
|
-
research: true,
|
|
140
|
-
plan_check: true,
|
|
141
|
-
verifier: true,
|
|
142
|
-
nyquist_validation: true,
|
|
143
|
-
auto_advance: false,
|
|
144
|
-
node_repair: true,
|
|
145
|
-
node_repair_budget: 2,
|
|
146
|
-
ui_phase: true,
|
|
147
|
-
ui_safety_gate: true,
|
|
148
|
-
text_mode: false,
|
|
149
|
-
research_before_questions: false,
|
|
150
|
-
discuss_mode: "discuss",
|
|
151
|
-
skip_discuss: false,
|
|
152
|
-
},
|
|
153
|
-
hooks: { context_warnings: true },
|
|
154
|
-
agent_skills: {},
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
return {
|
|
158
|
-
...hardcoded,
|
|
159
|
-
...userDefaults,
|
|
160
|
-
...choices,
|
|
161
|
-
git: {
|
|
162
|
-
...hardcoded.git,
|
|
163
|
-
...(userDefaults.git || {}),
|
|
164
|
-
...(choices.git || {}),
|
|
165
|
-
},
|
|
166
|
-
workflow: {
|
|
167
|
-
...hardcoded.workflow,
|
|
168
|
-
...(userDefaults.workflow || {}),
|
|
169
|
-
...(choices.workflow || {}),
|
|
170
|
-
},
|
|
171
|
-
hooks: {
|
|
172
|
-
...hardcoded.hooks,
|
|
173
|
-
...(userDefaults.hooks || {}),
|
|
174
|
-
...(choices.hooks || {}),
|
|
175
|
-
},
|
|
176
|
-
agent_skills: {
|
|
177
|
-
...hardcoded.agent_skills,
|
|
178
|
-
...(userDefaults.agent_skills || {}),
|
|
179
|
-
...(choices.agent_skills || {}),
|
|
180
|
-
},
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// ─── ensureConfigFile ─────────────────────────────────────────────────────────
|
|
185
|
-
|
|
186
|
-
export function ensureConfigFile(cwd: string): {
|
|
187
|
-
created: boolean;
|
|
188
|
-
reason?: string;
|
|
189
|
-
path?: string;
|
|
190
|
-
} {
|
|
191
|
-
const planningBase = planningRoot(cwd);
|
|
192
|
-
const configPath = path.join(planningBase, "config.json");
|
|
193
|
-
try {
|
|
194
|
-
if (!fs.existsSync(planningBase))
|
|
195
|
-
fs.mkdirSync(planningBase, { recursive: true });
|
|
196
|
-
} catch (err) {
|
|
197
|
-
gsdError("Failed to create .planning directory: " + (err as Error).message);
|
|
198
|
-
}
|
|
199
|
-
if (fs.existsSync(configPath))
|
|
200
|
-
return { created: false, reason: "already_exists" };
|
|
201
|
-
const config = buildNewProjectConfig({});
|
|
202
|
-
try {
|
|
203
|
-
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
204
|
-
return { created: true, path: ".planning/config.json" };
|
|
205
|
-
} catch (err) {
|
|
206
|
-
gsdError("Failed to create config.json: " + (err as Error).message);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
export function setConfigValue(
|
|
211
|
-
cwd: string,
|
|
212
|
-
keyPath: string,
|
|
213
|
-
parsedValue: unknown,
|
|
214
|
-
):
|
|
215
|
-
| { updated: boolean; key: string; value: unknown; previousValue: unknown }
|
|
216
|
-
| undefined {
|
|
217
|
-
const configPath = path.join(planningRoot(cwd), "config.json");
|
|
218
|
-
let config: Record<string, unknown> = {};
|
|
219
|
-
try {
|
|
220
|
-
if (fs.existsSync(configPath))
|
|
221
|
-
config = JSON.parse(fs.readFileSync(configPath, "utf-8")) as Record<
|
|
222
|
-
string,
|
|
223
|
-
unknown
|
|
224
|
-
>;
|
|
225
|
-
} catch (err) {
|
|
226
|
-
gsdError("Failed to read config.json: " + (err as Error).message);
|
|
227
|
-
}
|
|
228
|
-
const keys = keyPath.split(".");
|
|
229
|
-
let current: Record<string, unknown> = config;
|
|
230
|
-
for (let i = 0; i < keys.length - 1; i++) {
|
|
231
|
-
const key = keys[i];
|
|
232
|
-
if (current[key] === undefined || typeof current[key] !== "object")
|
|
233
|
-
current[key] = {};
|
|
234
|
-
current = current[key] as Record<string, unknown>;
|
|
235
|
-
}
|
|
236
|
-
const previousValue = current[keys[keys.length - 1]];
|
|
237
|
-
current[keys[keys.length - 1]] = parsedValue;
|
|
238
|
-
try {
|
|
239
|
-
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
240
|
-
return { updated: true, key: keyPath, value: parsedValue, previousValue };
|
|
241
|
-
} catch (err) {
|
|
242
|
-
gsdError("Failed to write config.json: " + (err as Error).message);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// ─── Commands ─────────────────────────────────────────────────────────────────
|
|
247
|
-
|
|
248
|
-
export function cmdConfigNewProject(
|
|
249
|
-
cwd: string,
|
|
250
|
-
choicesJson: string | undefined,
|
|
251
|
-
raw: boolean,
|
|
252
|
-
): void {
|
|
253
|
-
const planningBase = planningRoot(cwd);
|
|
254
|
-
const configPath = path.join(planningBase, "config.json");
|
|
255
|
-
if (fs.existsSync(configPath)) {
|
|
256
|
-
output({ created: false, reason: "already_exists" }, raw, "exists");
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
let userChoices: Partial<PlanningConfig> = {};
|
|
260
|
-
if (choicesJson && choicesJson.trim()) {
|
|
261
|
-
try {
|
|
262
|
-
userChoices = JSON.parse(choicesJson) as Partial<PlanningConfig>;
|
|
263
|
-
} catch (err) {
|
|
264
|
-
gsdError(
|
|
265
|
-
"Invalid JSON for config-new-project: " + (err as Error).message,
|
|
266
|
-
);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
try {
|
|
270
|
-
if (!fs.existsSync(planningBase))
|
|
271
|
-
fs.mkdirSync(planningBase, { recursive: true });
|
|
272
|
-
} catch (err) {
|
|
273
|
-
gsdError("Failed to create .planning directory: " + (err as Error).message);
|
|
274
|
-
}
|
|
275
|
-
const config = buildNewProjectConfig(userChoices);
|
|
276
|
-
try {
|
|
277
|
-
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
278
|
-
output({ created: true, path: ".planning/config.json" }, raw, "created");
|
|
279
|
-
} catch (err) {
|
|
280
|
-
gsdError("Failed to write config.json: " + (err as Error).message);
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
export function cmdConfigEnsureSection(cwd: string, raw: boolean): void {
|
|
285
|
-
const result = ensureConfigFile(cwd);
|
|
286
|
-
output(result, raw, result?.created ? "created" : "exists");
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
export function cmdConfigSet(
|
|
290
|
-
cwd: string,
|
|
291
|
-
keyPath: string | undefined,
|
|
292
|
-
value: string | undefined,
|
|
293
|
-
raw: boolean,
|
|
294
|
-
): void {
|
|
295
|
-
if (!keyPath) gsdError("Usage: config-set <key.path> <value>");
|
|
296
|
-
validateKnownConfigKeyPath(keyPath);
|
|
297
|
-
if (!isValidConfigKey(keyPath))
|
|
298
|
-
gsdError(
|
|
299
|
-
`Unknown config key: "${keyPath}". Valid keys: ${[...VALID_CONFIG_KEYS].sort().join(", ")}, agent_skills.<agent-type>`,
|
|
300
|
-
);
|
|
301
|
-
let parsedValue: unknown = value;
|
|
302
|
-
if (value === "true") parsedValue = true;
|
|
303
|
-
else if (value === "false") parsedValue = false;
|
|
304
|
-
else if (value !== undefined && !isNaN(Number(value)) && value !== "")
|
|
305
|
-
parsedValue = Number(value);
|
|
306
|
-
else if (
|
|
307
|
-
typeof value === "string" &&
|
|
308
|
-
(value.startsWith("[") || value.startsWith("{"))
|
|
309
|
-
) {
|
|
310
|
-
try {
|
|
311
|
-
parsedValue = JSON.parse(value);
|
|
312
|
-
} catch {
|
|
313
|
-
/* keep as string */
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
const result = setConfigValue(cwd, keyPath, parsedValue);
|
|
317
|
-
output(result, raw, `${keyPath}=${parsedValue}`);
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
export function cmdConfigGet(
|
|
321
|
-
cwd: string,
|
|
322
|
-
keyPath: string | undefined,
|
|
323
|
-
raw: boolean,
|
|
324
|
-
): void {
|
|
325
|
-
if (!keyPath) gsdError("Usage: config-get <key.path>");
|
|
326
|
-
const configPath = path.join(planningRoot(cwd), "config.json");
|
|
327
|
-
let config: Record<string, unknown> = {};
|
|
328
|
-
try {
|
|
329
|
-
if (fs.existsSync(configPath))
|
|
330
|
-
config = JSON.parse(fs.readFileSync(configPath, "utf-8")) as Record<
|
|
331
|
-
string,
|
|
332
|
-
unknown
|
|
333
|
-
>;
|
|
334
|
-
else gsdError("No config.json found at " + configPath);
|
|
335
|
-
} catch (err) {
|
|
336
|
-
if ((err as Error).message.startsWith("Error:")) throw err;
|
|
337
|
-
gsdError("Failed to read config.json: " + (err as Error).message);
|
|
338
|
-
}
|
|
339
|
-
const keys = keyPath.split(".");
|
|
340
|
-
let current: unknown = config;
|
|
341
|
-
for (const key of keys) {
|
|
342
|
-
if (
|
|
343
|
-
current === undefined ||
|
|
344
|
-
current === null ||
|
|
345
|
-
typeof current !== "object"
|
|
346
|
-
)
|
|
347
|
-
gsdError(`Key not found: ${keyPath}`);
|
|
348
|
-
current = (current as Record<string, unknown>)[key];
|
|
349
|
-
}
|
|
350
|
-
if (current === undefined) gsdError(`Key not found: ${keyPath}`);
|
|
351
|
-
output(current, raw, String(current));
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
export function cmdConfigSetModelProfile(
|
|
355
|
-
cwd: string,
|
|
356
|
-
profile: string | undefined,
|
|
357
|
-
raw: boolean,
|
|
358
|
-
): void {
|
|
359
|
-
if (!profile)
|
|
360
|
-
gsdError(`Usage: config-set-model-profile <${VALID_PROFILES.join("|")}>`);
|
|
361
|
-
const normalizedProfile = profile.toLowerCase().trim() as ProfileKey;
|
|
362
|
-
if (!VALID_PROFILES.includes(normalizedProfile))
|
|
363
|
-
gsdError(
|
|
364
|
-
`Invalid profile '${profile}'. Valid profiles: ${VALID_PROFILES.join(", ")}`,
|
|
365
|
-
);
|
|
366
|
-
ensureConfigFile(cwd);
|
|
367
|
-
const result = setConfigValue(cwd, "model_profile", normalizedProfile);
|
|
368
|
-
const previousProfile = (result?.previousValue as string) || "balanced";
|
|
369
|
-
const agentToModelMap = getAgentToModelMapForProfile(normalizedProfile);
|
|
370
|
-
const table = formatAgentToModelMapAsTable(agentToModelMap);
|
|
371
|
-
const didChange = previousProfile !== normalizedProfile;
|
|
372
|
-
const rawValue = didChange
|
|
373
|
-
? `✓ Model profile set to: ${normalizedProfile} (was: ${previousProfile})\n\nAgents will now use:\n\n${table}\nNext spawned agents will use the new profile.`
|
|
374
|
-
: `✓ Model profile is already set to: ${normalizedProfile}\n\nAgents are using:\n\n${table}`;
|
|
375
|
-
output(
|
|
376
|
-
{
|
|
377
|
-
updated: true,
|
|
378
|
-
profile: normalizedProfile,
|
|
379
|
-
previousProfile,
|
|
380
|
-
agentToModelMap,
|
|
381
|
-
},
|
|
382
|
-
raw,
|
|
383
|
-
rawValue,
|
|
384
|
-
);
|
|
385
|
-
}
|