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.
Files changed (66) hide show
  1. package/dist/pi-gsd-hooks.js +1533 -0
  2. package/dist/pi-gsd-tools.js +53 -52
  3. package/package.json +3 -5
  4. package/.gsd/extensions/pi-gsd-hooks.ts +0 -973
  5. package/src/cli.ts +0 -644
  6. package/src/commands/base.ts +0 -67
  7. package/src/commands/commit.ts +0 -22
  8. package/src/commands/config.ts +0 -71
  9. package/src/commands/frontmatter.ts +0 -51
  10. package/src/commands/index.ts +0 -76
  11. package/src/commands/init.ts +0 -43
  12. package/src/commands/milestone.ts +0 -37
  13. package/src/commands/phase.ts +0 -92
  14. package/src/commands/progress.ts +0 -71
  15. package/src/commands/roadmap.ts +0 -40
  16. package/src/commands/scaffold.ts +0 -19
  17. package/src/commands/state.ts +0 -102
  18. package/src/commands/template.ts +0 -52
  19. package/src/commands/verify.ts +0 -70
  20. package/src/commands/workstream.ts +0 -98
  21. package/src/commands/wxp.ts +0 -65
  22. package/src/lib/commands.ts +0 -1040
  23. package/src/lib/config.ts +0 -385
  24. package/src/lib/core.ts +0 -1167
  25. package/src/lib/frontmatter.ts +0 -462
  26. package/src/lib/init.ts +0 -517
  27. package/src/lib/milestone.ts +0 -290
  28. package/src/lib/model-profiles.ts +0 -272
  29. package/src/lib/phase.ts +0 -1012
  30. package/src/lib/profile-output.ts +0 -237
  31. package/src/lib/profile-pipeline.ts +0 -556
  32. package/src/lib/roadmap.ts +0 -378
  33. package/src/lib/schemas.ts +0 -290
  34. package/src/lib/security.ts +0 -176
  35. package/src/lib/state.ts +0 -1175
  36. package/src/lib/template.ts +0 -246
  37. package/src/lib/uat.ts +0 -289
  38. package/src/lib/verify.ts +0 -879
  39. package/src/lib/workstream.ts +0 -524
  40. package/src/output.ts +0 -45
  41. package/src/schemas/pi-gsd-settings.schema.json +0 -80
  42. package/src/schemas/wxp.xsd +0 -619
  43. package/src/schemas/wxp.zod.ts +0 -318
  44. package/src/wxp/__tests__/arguments.test.ts +0 -86
  45. package/src/wxp/__tests__/conditions.test.ts +0 -106
  46. package/src/wxp/__tests__/executor.test.ts +0 -95
  47. package/src/wxp/__tests__/helpers.ts +0 -26
  48. package/src/wxp/__tests__/integration.test.ts +0 -166
  49. package/src/wxp/__tests__/new-features.test.ts +0 -222
  50. package/src/wxp/__tests__/parser.test.ts +0 -159
  51. package/src/wxp/__tests__/paste.test.ts +0 -66
  52. package/src/wxp/__tests__/schema.test.ts +0 -120
  53. package/src/wxp/__tests__/security.test.ts +0 -87
  54. package/src/wxp/__tests__/shell.test.ts +0 -85
  55. package/src/wxp/__tests__/string-ops.test.ts +0 -25
  56. package/src/wxp/__tests__/variables.test.ts +0 -65
  57. package/src/wxp/arguments.ts +0 -89
  58. package/src/wxp/conditions.ts +0 -78
  59. package/src/wxp/executor.ts +0 -191
  60. package/src/wxp/index.ts +0 -191
  61. package/src/wxp/parser.ts +0 -198
  62. package/src/wxp/paste.ts +0 -51
  63. package/src/wxp/security.ts +0 -102
  64. package/src/wxp/shell.ts +0 -81
  65. package/src/wxp/string-ops.ts +0 -44
  66. 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
- }