pi-gsd 1.3.0 → 1.3.1
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/.gsd/extensions/gsd-hooks.ts +94 -13
- package/package.json +1 -1
|
@@ -254,18 +254,15 @@ export default function (pi: ExtensionAPI) {
|
|
|
254
254
|
return lines;
|
|
255
255
|
};
|
|
256
256
|
|
|
257
|
-
const formatProgress = (cwd: string): string => {
|
|
257
|
+
const formatProgress = (cwd: string): { text: string; data: GsdProgress | null } => {
|
|
258
258
|
const data = runJson<GsdProgress>("progress json", cwd);
|
|
259
259
|
if (!data)
|
|
260
|
-
return "❌ No GSD project found. Run /gsd-new-project to initialise.";
|
|
260
|
+
return { text: "❌ No GSD project found. Run /gsd-new-project to initialise.", data: null };
|
|
261
261
|
|
|
262
|
-
const phasePct = data.percent ?? 0;
|
|
263
|
-
const planPct =
|
|
264
|
-
data.total_plans > 0
|
|
265
|
-
? Math.round((data.total_summaries / data.total_plans) * 100)
|
|
266
|
-
: 0;
|
|
267
262
|
const done = data.phases.filter((p) => p.status === "Complete").length;
|
|
268
263
|
const total = data.phases.length;
|
|
264
|
+
const phasePct = total > 0 ? Math.round((done / total) * 100) : 0;
|
|
265
|
+
const planPct =
|
|
269
266
|
|
|
270
267
|
const lines = [
|
|
271
268
|
`━━ GSD Progress ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`,
|
|
@@ -279,13 +276,13 @@ export default function (pi: ExtensionAPI) {
|
|
|
279
276
|
``,
|
|
280
277
|
`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`,
|
|
281
278
|
];
|
|
282
|
-
return lines.join("\n");
|
|
279
|
+
return { text: lines.join("\n"), data };
|
|
283
280
|
};
|
|
284
281
|
|
|
285
|
-
const formatStats = (cwd: string): string => {
|
|
282
|
+
const formatStats = (cwd: string): { text: string; data: GsdStats | null } => {
|
|
286
283
|
const data = runJson<GsdStats>("stats json", cwd);
|
|
287
284
|
if (!data)
|
|
288
|
-
return "❌ No GSD project found. Run /gsd-new-project to initialise.";
|
|
285
|
+
return { text: "❌ No GSD project found. Run /gsd-new-project to initialise.", data: null };
|
|
289
286
|
|
|
290
287
|
const reqPct =
|
|
291
288
|
data.requirements_total > 0
|
|
@@ -311,7 +308,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
311
308
|
``,
|
|
312
309
|
`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`,
|
|
313
310
|
];
|
|
314
|
-
return lines.join("\n");
|
|
311
|
+
return { text: lines.join("\n"), data };
|
|
315
312
|
};
|
|
316
313
|
|
|
317
314
|
const formatHealth = (cwd: string, repair: boolean): string => {
|
|
@@ -349,17 +346,40 @@ export default function (pi: ExtensionAPI) {
|
|
|
349
346
|
return lines.join("\n");
|
|
350
347
|
};
|
|
351
348
|
|
|
349
|
+
/** Derive the suggested next command string from phase data. */
|
|
350
|
+
const nextCommand = (phases: GsdPhase[]): string | null => {
|
|
351
|
+
const pending = phases.filter((p) => p.status !== "Complete");
|
|
352
|
+
if (pending.length === 0) return "/gsd-audit-milestone";
|
|
353
|
+
const next = pending[0];
|
|
354
|
+
const n = next.number;
|
|
355
|
+
if (next.plans === 0) return `/gsd-discuss-phase ${n}`;
|
|
356
|
+
if (next.summaries < next.plans) return `/gsd-execute-phase ${n}`;
|
|
357
|
+
return `/gsd-verify-work ${n}`;
|
|
358
|
+
};
|
|
359
|
+
|
|
352
360
|
pi.registerCommand("gsd-progress", {
|
|
353
361
|
description: "Show project progress with next steps (instant)",
|
|
354
362
|
handler: async (_args, ctx) => {
|
|
355
|
-
|
|
363
|
+
const { text, data } = formatProgress(ctx.cwd);
|
|
364
|
+
ctx.ui.notify(text, "info");
|
|
365
|
+
// Pivot affordance: pre-fill the editor with the most relevant next action
|
|
366
|
+
// so the user can run it, modify it, or just type something else entirely
|
|
367
|
+
if (data) {
|
|
368
|
+
const cmd = nextCommand(data.phases);
|
|
369
|
+
if (cmd) ctx.ui.setEditorText(cmd);
|
|
370
|
+
}
|
|
356
371
|
},
|
|
357
372
|
});
|
|
358
373
|
|
|
359
374
|
pi.registerCommand("gsd-stats", {
|
|
360
375
|
description: "Show project statistics (instant)",
|
|
361
376
|
handler: async (_args, ctx) => {
|
|
362
|
-
|
|
377
|
+
const { text, data } = formatStats(ctx.cwd);
|
|
378
|
+
ctx.ui.notify(text, "info");
|
|
379
|
+
if (data) {
|
|
380
|
+
const cmd = nextCommand(data.phases);
|
|
381
|
+
if (cmd) ctx.ui.setEditorText(cmd);
|
|
382
|
+
}
|
|
363
383
|
},
|
|
364
384
|
});
|
|
365
385
|
|
|
@@ -374,6 +394,67 @@ export default function (pi: ExtensionAPI) {
|
|
|
374
394
|
},
|
|
375
395
|
});
|
|
376
396
|
|
|
397
|
+
pi.registerCommand("gsd-next", {
|
|
398
|
+
description: "Auto-advance to the next GSD action (instant, no LLM)",
|
|
399
|
+
handler: async (_args, ctx) => {
|
|
400
|
+
const data = runJson<GsdProgress>("progress json", ctx.cwd);
|
|
401
|
+
if (!data) {
|
|
402
|
+
ctx.ui.notify(
|
|
403
|
+
"❌ No GSD project found. Run /gsd-new-project to initialise.",
|
|
404
|
+
"error",
|
|
405
|
+
);
|
|
406
|
+
ctx.ui.setEditorText("/gsd-new-project");
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
const pending = data.phases.filter((p) => p.status !== "Complete");
|
|
411
|
+
|
|
412
|
+
if (pending.length === 0) {
|
|
413
|
+
ctx.ui.notify(
|
|
414
|
+
[
|
|
415
|
+
`━━ GSD Next ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`,
|
|
416
|
+
`✅ All phases complete!`,
|
|
417
|
+
`→ /gsd-audit-milestone`,
|
|
418
|
+
`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`,
|
|
419
|
+
].join("\n"),
|
|
420
|
+
"info",
|
|
421
|
+
);
|
|
422
|
+
ctx.ui.setEditorText("/gsd-audit-milestone");
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const next = pending[0];
|
|
427
|
+
const n = next.number;
|
|
428
|
+
let action: string;
|
|
429
|
+
let reason: string;
|
|
430
|
+
|
|
431
|
+
if (next.plans === 0) {
|
|
432
|
+
action = `/gsd-discuss-phase ${n}`;
|
|
433
|
+
reason = `Phase ${n} has no plans yet — start with discussion`;
|
|
434
|
+
} else if (next.summaries < next.plans) {
|
|
435
|
+
action = `/gsd-execute-phase ${n}`;
|
|
436
|
+
reason = `Phase ${n}: ${next.summaries}/${next.plans} plans done — continue execution`;
|
|
437
|
+
} else {
|
|
438
|
+
action = `/gsd-verify-work ${n}`;
|
|
439
|
+
reason = `Phase ${n}: all plans done — verify UAT`;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
ctx.ui.notify(
|
|
443
|
+
[
|
|
444
|
+
`━━ GSD Next ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`,
|
|
445
|
+
`⏩ ${reason}`,
|
|
446
|
+
`→ ${action}`,
|
|
447
|
+
...(pending.length > 1
|
|
448
|
+
? [` (${pending.length - 1} more phase${pending.length > 2 ? "s" : ""} pending after this)`]
|
|
449
|
+
: []),
|
|
450
|
+
`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`,
|
|
451
|
+
].join("\n"),
|
|
452
|
+
"info",
|
|
453
|
+
);
|
|
454
|
+
ctx.ui.setEditorText(action);
|
|
455
|
+
},
|
|
456
|
+
});
|
|
457
|
+
|
|
377
458
|
pi.registerCommand("gsd-help", {
|
|
378
459
|
description: "List all GSD commands (instant)",
|
|
379
460
|
handler: async (_args, ctx) => {
|