pi-gsd 1.4.0 → 1.6.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.
@@ -503,7 +503,6 @@ export default function (pi: ExtensionAPI) {
503
503
  " /gsd-progress Progress + next steps",
504
504
  " /gsd-stats Full statistics",
505
505
  " /gsd-health [--repair] .planning/ integrity",
506
- " /gsd-milestone Milestone status dashboard",
507
506
  " /gsd-help This list",
508
507
  "",
509
508
  "Management:",
@@ -518,71 +517,6 @@ export default function (pi: ExtensionAPI) {
518
517
  },
519
518
  });
520
519
 
521
- pi.registerCommand("gsd-milestone", {
522
- description:
523
- "Milestone status dashboard — plan vs execute routing (instant)",
524
- handler: async (_args, ctx) => {
525
- const progress = runJson<GsdProgress>("progress json", ctx.cwd);
526
- if (!progress) {
527
- ctx.ui.notify(
528
- "❌ No GSD project found. Run /gsd-new-project to initialise.",
529
- "error",
530
- );
531
- ctx.ui.setEditorText("/gsd-new-project");
532
- return;
533
- }
534
-
535
- const phases = progress.phases;
536
- const total = phases.length;
537
- const done = phases.filter((p) => p.status === "Complete").length;
538
- const unplanned = phases.filter(
539
- (p) => p.status !== "Complete" && p.plans === 0,
540
- ).length;
541
- const planned = phases.filter(
542
- (p) => p.status !== "Complete" && p.plans > 0,
543
- ).length;
544
- const phasePct = total > 0 ? Math.round((done / total) * 100) : 0;
545
-
546
- // Determine recommended next milestone-level action
547
- let recommendation: string;
548
- let action: string;
549
- if (total === 0) {
550
- recommendation = "No phases defined yet";
551
- action = "/gsd-new-project";
552
- } else if (done === total) {
553
- recommendation = "All phases complete — ready to audit";
554
- action = "/gsd-audit-milestone";
555
- } else if (unplanned > 0 && planned === 0) {
556
- recommendation = `${unplanned} unplanned phase${unplanned > 1 ? "s" : ""} — plan the milestone first`;
557
- action = "/gsd-plan-milestone";
558
- } else if (unplanned > 0) {
559
- recommendation = `${planned} planned, ${unplanned} still unplanned — finish planning first`;
560
- action = "/gsd-plan-milestone";
561
- } else {
562
- recommendation = `${planned} phase${planned > 1 ? "s" : ""} ready to execute`;
563
- action = "/gsd-execute-milestone";
564
- }
565
-
566
- const lines = [
567
- `━━ GSD Milestone ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`,
568
- `🎯 ${progress.milestone_name} (${progress.milestone_version})`,
569
- ``,
570
- `Phases ${bar(phasePct)} ${done}/${total} (${phasePct}%)`,
571
- ``,
572
- `🟢 Complete: ${done}`,
573
- `🟡 Planned: ${planned}`,
574
- `🔴 Unplanned: ${unplanned}`,
575
- ``,
576
- `⚡ ${recommendation}`,
577
- `→ ${action}`,
578
- ``,
579
- `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`,
580
- ];
581
-
582
- ctx.ui.notify(lines.join("\n"), "info");
583
- ctx.ui.setEditorText(action);
584
- },
585
- });
586
520
 
587
521
  // ── tool_result: context usage monitor ───────────────────────────────────
588
522
  const WARNING_THRESHOLD = 35; // warn when remaining % ≤ 35
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/pi-gsd.svg)](https://www.npmjs.com/package/pi-gsd)
6
6
  [![license: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
7
- [![skills: 57](https://img.shields.io/badge/skills-57-orange.svg)](#skills)
7
+ [![skills: 60](https://img.shields.io/badge/skills-60-orange.svg)](#skills)
8
8
 
9
9
  GSD is a structured software-delivery framework for AI coding agents. It wraps any AI coding session with a six-step phase lifecycle, 57 slash commands, 18 specialized subagents, background hooks, and model profiles - all backed by a git-committed `.planning/` directory that survives context resets.
10
10
 
@@ -38,7 +38,7 @@ After install, run your first GSD command:
38
38
 
39
39
  | Artifact | Count | Description |
40
40
  | ---------- | ----: | ----------------------------------------------------------------- |
41
- | Skills | 57 | pi skill definitions (`/gsd-*`) loaded automatically |
41
+ | Skills | 60 | pi skill definitions (`/gsd-*`) loaded automatically |
42
42
  | CLI binary | 1 | `pi-gsd-tools` - state management, scaffolding, model routing |
43
43
  | Hooks | 5 | Background hooks (context monitor, workflow guard, statusline, …) |
44
44
 
@@ -123,8 +123,8 @@ Switch profile: `/gsd-set-profile <profile>`
123
123
  | Workstreams | ✔️ | ✔️ | Full workstream isolation |
124
124
  | 4 model profiles | ✔️ | ✔️ | quality / balanced / budget / inherit |
125
125
  | 18 subagents | ✔️ | ✔️ | Identical agent definitions |
126
- | 57 GSD skills | ✔️ | ✔️ | All commands available via pi prompt dispatcher (replaces skill system) |
127
- | Different skills paths for pi | ✔️ | ⚡ | All 57 skills moved to `.pi/gsd/` to enable advanced pi-gsd-tools integration |
126
+ | 60 GSD skills | ✔️ | ✔️ | All commands available via pi prompt dispatcher (replaces skill system) |
127
+ | Different skills paths for pi | ✔️ | ⚡ | All 60 skills moved to `.pi/gsd/` to enable advanced pi-gsd-tools integration |
128
128
  | pi harness (`.pi/`) | ❌ | ✔️ | New - GSD installs into pi's config dir |
129
129
  | Background hooks (pi) | ❌ | ✔️ | TypeScript extension (`gsd-hooks.ts`) installed via postinstall |
130
130
  | Pi session history ingestion | ❌ | ✔️ | `/gsd-profile-user` reads pi JSONL sessions from `~/.pi/agent/sessions/` |
@@ -138,12 +138,11 @@ Switch profile: `/gsd-set-profile <profile>`
138
138
  | Smarter `--repair` | ❌ | ✔️ | Schema defaults fill missing `config.json` fields; W011 STATE.md issues trigger regeneration |
139
139
  | Instant commands (no LLM cost) | ❌ | ✔️ | `/gsd-progress`, `/gsd-stats`, `/gsd-health`, `/gsd-help`, `/gsd-next` — zero LLM, editor pivot |
140
140
  | `/gsd-next` auto-advance | ❌ | ✔️ | Deterministic phase routing, pre-fills editor with the correct next command |
141
- | Prompt-dispatch for all skills | ❌ | ✔️ | 54 pi prompt templates — clean autocomplete, arg hints, direct workflow dispatch |
142
- | `/gsd-plan-milestone` command | ❌ | ✔️ | Plan all phases at once — one mode question, scope pre-check, context-safe checkpointing |
143
- | `/gsd-execute-milestone` command | ❌ | ✔️ | Execute all phases with scope guardian, UAT gates, recovery loop, worktree isolation |
144
- | `/gsd-milestone` instant command | ❌ | ✔️ | Milestone dashboard — plan vs execute routing, pre-fills editor with correct next command |
141
+ | Prompt-dispatch for all skills | ❌ | ✔️ | 57 pi prompt templates — clean autocomplete, arg hints, direct workflow dispatch |
142
+ | `/gsd-plan-milestone` command | ❌ | ✔️ | Plan all unplanned phases — one mode question, scope pre-check per phase, context-safe checkpoint |
143
+ | `/gsd-execute-milestone` command | ❌ | ✔️ | Execute all phases + scope guardian + auto gap/debt retry loop (insert-phase) + audit→complete→cleanup |
145
144
 
146
- Legend: ✔️ done · ⚡ enhanced · ⚠️ in progress · 📃 planned · ❌ not available
145
+ Legend: ✔️ done · ⚡ enhanced · ❌ not available
147
146
 
148
147
  ---
149
148
 
@@ -273,7 +273,7 @@ Plans:
273
273
 
274
274
  Plans:
275
275
  - [ ] TBD (run /gsd-plan-phase ${g} to break down)
276
- `,P=new RegExp(`(#{2,4}\\s*Phase\\s+0*${d}:[^\\n]*\\n)`,"i"),k=r.match(P);k||S(`Could not find Phase ${e} header`);let $=r.indexOf(k[0]),A=r.slice($+k[0].length).match(/\n#{2,4}\s+Phase\s+\d/i),O=A?$+k[0].length+A.index:r.length;j.default.writeFileSync(t,r.slice(0,O)+v+r.slice(O),"utf-8"),h({phase_number:g,after_phase:e,name:s,slug:a,directory:z(M.default.join(M.default.relative(n,F(n)),"phases",_))},i,g)}function Ls(n,e,s){let i=[],t=[],r=new RegExp(`^${e}\\.(\\d+)-(.+)$`),o=Oe(n,!0).map(a=>{let l=a.match(r);return l?{dir:a,oldDecimal:parseInt(l[1],10),slug:l[2]}:null}).filter(a=>a!==null&&a.oldDecimal>s).sort((a,l)=>l.oldDecimal-a.oldDecimal);for(let a of o){let l=a.oldDecimal-1,c=`${e}.${a.oldDecimal}`,d=`${e}.${l}`,u=`${e}.${l}-${a.slug}`;j.default.renameSync(M.default.join(n,a.dir),M.default.join(n,u)),i.push({from:a.dir,to:u});for(let f of j.default.readdirSync(M.default.join(n,u)))if(f.includes(c)){let m=f.replace(c,d);j.default.renameSync(M.default.join(n,u,f),M.default.join(n,u,m)),t.push({from:f,to:m})}}return{renamedDirs:i,renamedFiles:t}}function Us(n,e){let s=[],i=[],t=Oe(n,!0).map(r=>{let o=r.match(/^(\d+)([A-Z])?(?:\.(\d+))?-(.+)$/i);if(!o)return null;let a=parseInt(o[1],10);return a>e?{dir:r,oldInt:a,letter:o[2]?o[2].toUpperCase():"",decimal:o[3]?parseInt(o[3],10):null,slug:o[4]}:null}).filter(r=>r!==null).sort((r,o)=>r.oldInt!==o.oldInt?o.oldInt-r.oldInt:(o.decimal||0)-(r.decimal||0));for(let r of t){let o=r.oldInt-1,a=String(o).padStart(2,"0"),l=String(r.oldInt).padStart(2,"0"),c=r.letter||"",d=r.decimal!==null?`.${r.decimal}`:"",u=`${l}${c}${d}`,f=`${a}${c}${d}`,m=`${f}-${r.slug}`;j.default.renameSync(M.default.join(n,r.dir),M.default.join(n,m)),s.push({from:r.dir,to:m});for(let p of j.default.readdirSync(M.default.join(n,m)))if(p.startsWith(u)){let y=f+p.slice(u.length);j.default.renameSync(M.default.join(n,m,p),M.default.join(n,m,y)),i.push({from:p,to:y})}}return{renamedDirs:s,renamedFiles:i}}function Gs(n,e,s,i){let t=j.default.readFileSync(n,"utf-8"),r=Q(e);if(t=t.replace(new RegExp(`\\n?#{2,4}\\s*Phase\\s+${r}\\s*:[\\s\\S]*?(?=\\n#{2,4}\\s+Phase\\s+\\d|$)`,"i"),""),t=t.replace(new RegExp(`\\n?-\\s*\\[[ x]\\]\\s*.*Phase\\s+${r}[:\\s][^\\n]*`,"gi"),""),t=t.replace(new RegExp(`\\n?\\|\\s*${r}\\.?\\s[^|]*\\|[^\\n]*`,"gi"),""),!s)for(let o=99;o>i;o--){let a=o-1,l=String(o),c=String(a),d=l.padStart(2,"0"),u=c.padStart(2,"0");t=t.replace(new RegExp(`(#{2,4}\\s*Phase\\s+)${l}(\\s*:)`,"gi"),`$1${c}$2`),t=t.replace(new RegExp(`(Phase\\s+)${l}([:\\s])`,"g"),`$1${c}$2`),t=t.replace(new RegExp(`${d}-(\\d{2})`,"g"),`${u}-$1`),t=t.replace(new RegExp(`(\\|\\s*)${l}\\.\\s`,"g"),`$1${c}. `),t=t.replace(new RegExp(`(Depends on:\\*\\*\\s*Phase\\s+)${l}\\b`,"gi"),`$1${c}`)}j.default.writeFileSync(n,t,"utf-8")}function zs(n,e,s,i){e||S("phase number required for phase remove");let t=M.default.join(F(n),"ROADMAP.md"),r=M.default.join(F(n),"phases");j.default.existsSync(t)||S("ROADMAP.md not found");let o=ce(e),a=e.includes("."),l=s.force||!1,c=Oe(r,!0).find(m=>m.startsWith(o+"-")||m===o)||null;if(c&&!l){let m=j.default.readdirSync(M.default.join(r,c)).filter(p=>p.endsWith("-SUMMARY.md")||p==="SUMMARY.md");m.length>0&&S(`Phase ${e} has ${m.length} executed plan(s). Use --force to remove anyway.`)}c&&j.default.rmSync(M.default.join(r,c),{recursive:!0,force:!0});let d=[],u=[];try{let m=a?Ls(r,o.split(".")[0],parseInt(o.split(".")[1],10)):Us(r,parseInt(o,10));d=m.renamedDirs,u=m.renamedFiles}catch{}Gs(t,e,a,parseInt(o,10));let f=M.default.join(F(n),"STATE.md");if(j.default.existsSync(f)){let m=j.default.readFileSync(f,"utf-8"),p=V(m,"Total Phases");p&&(m=Se(m,"Total Phases",String(parseInt(p,10)-1))??m);let y=m.match(/(\bof\s+)(\d+)(\s*(?:\(|phases?))/i);y&&(m=m.replace(/(\bof\s+)(\d+)(\s*(?:\(|phases?))/i,`$1${parseInt(y[2],10)-1}$3`)),te(f,m,n)}h({removed:e,directory_deleted:c,renamed_directories:d,renamed_files:u,roadmap_updated:!0,state_updated:j.default.existsSync(f)},i)}function Vs(n,e,s){e||S("phase number required for phase complete");let i=M.default.join(F(n),"ROADMAP.md"),t=M.default.join(F(n),"STATE.md"),r=M.default.join(F(n),"phases"),o=ce(e),a=new Date().toISOString().split("T")[0],l=ue(n,e);l||S(`Phase ${e} not found`);let c=l.plans.length,d=l.summaries.length,u=!1,f=[];try{let g=M.default.join(n,l.directory),_=j.default.readdirSync(g);for(let b of _.filter(v=>v.includes("-UAT")&&v.endsWith(".md"))){let v=j.default.readFileSync(M.default.join(g,b),"utf-8");/result: pending/.test(v)&&f.push(`${b}: has pending tests`),/result: blocked/.test(v)&&f.push(`${b}: has blocked tests`),/status: partial/.test(v)&&f.push(`${b}: testing incomplete (partial)`),/status: diagnosed/.test(v)&&f.push(`${b}: has diagnosed gaps`)}for(let b of _.filter(v=>v.includes("-VERIFICATION")&&v.endsWith(".md"))){let v=j.default.readFileSync(M.default.join(g,b),"utf-8");/status: human_needed/.test(v)&&f.push(`${b}: needs human verification`),/status: gaps_found/.test(v)&&f.push(`${b}: has unresolved gaps`)}}catch{}if(j.default.existsSync(i)){let g=j.default.readFileSync(i,"utf-8"),_=new RegExp(`(-\\s*\\[)[ ](\\]\\s*.*Phase\\s+${Q(e)}[:\\s][^\\n]*)`,"i");g=Ze(g,_,`$1x$2 (completed ${a})`);let b=Q(e);g=g.replace(new RegExp(`^(\\|\\s*${b}\\.?\\s[^|]*(?:\\|[^\\n]*))$`,"im"),P=>{let k=P.split("|").slice(1,-1);return k.length===5?(k[3]=" Complete ",k[4]=` ${a} `):k.length===4&&(k[2]=" Complete ",k[3]=` ${a} `),"|"+k.join("|")+"|"}),g=Ze(g,new RegExp(`(#{2,4}\\s*Phase\\s+${b}[\\s\\S]*?\\*\\*Plans:\\*\\*\\s*)[^\\n]+`,"i"),`$1${d}/${c} plans complete`),j.default.writeFileSync(i,g,"utf-8");let v=M.default.join(F(n),"REQUIREMENTS.md");if(j.default.existsSync(v)){let k=be(g,n).match(new RegExp(`(#{2,4}\\s*Phase\\s+${Q(e)}[:\\s][\\s\\S]*?)(?=#{2,4}\\s*Phase\\s+|$)`,"i")),T=(k?k[1]:"").match(/\*\*Requirements:\*\*\s*([^\n]+)/i);if(T){let A=T[1].replace(/[[\]]/g,"").split(/[,\s]+/).map(N=>N.trim()).filter(Boolean),O=j.default.readFileSync(v,"utf-8");for(let N of A){let L=Q(N);O=O.replace(new RegExp(`(-\\s*\\[)[ ](\\]\\s*\\*\\*${L}\\*\\*)`,"gi"),"$1x$2"),O=O.replace(new RegExp(`(\\|\\s*${L}\\s*\\|[^|]+\\|)\\s*(?:Pending|In Progress)\\s*(\\|)`,"gi"),"$1 Complete $2")}j.default.writeFileSync(v,O,"utf-8"),u=!0}}}let m=null,p=null,y=!0;try{let g=Pe(n),_=j.default.readdirSync(r,{withFileTypes:!0}).filter(b=>b.isDirectory()).map(b=>b.name).filter(g).sort((b,v)=>ke(b,v));for(let b of _){let v=b.match(/^(\d+[A-Z]?(?:\.\d+)*)-?(.*)/i);if(v&&ke(v[1],e)>0){m=v[1],p=v[2]||null,y=!1;break}}}catch{}if(y&&j.default.existsSync(i))try{let g=be(j.default.readFileSync(i,"utf-8"),n),_=/#{2,4}\s*Phase\s+(\d+[A-Z]?(?:\.\d+)*)\s*:\s*([^\n]+)/gi,b;for(;(b=_.exec(g))!==null;)if(ke(b[1],e)>0){m=b[1],p=b[2].replace(/\(INSERTED\)/i,"").trim().toLowerCase().replace(/\s+/g,"-"),y=!1;break}}catch{}if(j.default.existsSync(t)){let g=j.default.readFileSync(t,"utf-8"),_=m||e,b=V(g,"Current Phase")||V(g,"Phase"),v=String(_);if(b){let k=b.match(/of\s+(\d+)/),$=b.match(/\(([^)]+)\)/);if(k){let T=p?` (${p.replace(/-/g," ")})`:$?` (${$[1]})`:"";v=`${_} of ${k[1]}${T}`}}g=he(g,"Current Phase","Phase",v),p&&(g=he(g,"Current Phase Name",null,p.replace(/-/g," "))),g=he(g,"Status",null,y?"Milestone complete":"Ready to plan"),g=he(g,"Current Plan","Plan","Not started"),g=he(g,"Last Activity","Last activity",a),g=he(g,"Last Activity Description",null,`Phase ${e} complete${m?`, transitioned to Phase ${m}`:""}`);let P=V(g,"Completed Phases");if(P){let k=parseInt(P,10)+1;g=Se(g,"Completed Phases",String(k))??g;let $=V(g,"Total Phases");if($){let T=parseInt($,10);if(T>0){let A=Math.round(k/T*100);g=Se(g,"Progress",`${A}%`)??g,g=g.replace(/(percent:\s*)\d+/,`$1${A}`)}}}te(t,g,n)}h({completed_phase:e,phase_name:l.phase_name,plans_executed:`${d}/${c}`,next_phase:m,next_phase_name:p,is_last_phase:y,date:a,roadmap_updated:j.default.existsSync(i),state_updated:j.default.existsSync(t),requirements_updated:u,warnings:f,has_warnings:f.length>0},s)}var j,M,ut=le(()=>{"use strict";j=W(require("fs")),M=W(require("path"));pe();Ue();Be()});var x,yn,Ir,Er,wr,Mr,Hs,Fr,Bs,Js,Ys,kt,Sn=le(()=>{"use strict";x=require("zod"),yn=x.z.object({milestone:x.z.string().optional(),milestone_name:x.z.string().optional(),current_phase:x.z.string().optional(),current_phase_name:x.z.string().optional(),current_plan:x.z.string().optional(),total_phases:x.z.coerce.number().int().nonnegative().optional(),total_plans_in_phase:x.z.coerce.number().int().nonnegative().optional(),status:x.z.string().optional(),progress:x.z.string().optional(),last_activity:x.z.string().optional(),paused_at:x.z.string().optional(),stopped_at:x.z.string().optional()}).passthrough(),Ir=x.z.object({found:x.z.literal(!0),phase_number:x.z.string(),phase_name:x.z.string(),goal:x.z.string().nullable(),success_criteria:x.z.array(x.z.string()).default([]),section:x.z.string().optional()}).passthrough(),Er=x.z.object({phase:x.z.union([x.z.string(),x.z.number()]),plan:x.z.union([x.z.string(),x.z.number()]),type:x.z.string(),wave:x.z.union([x.z.string(),x.z.number()]),depends_on:x.z.union([x.z.string(),x.z.array(x.z.string())]),files_modified:x.z.union([x.z.string(),x.z.array(x.z.string())]),autonomous:x.z.union([x.z.boolean(),x.z.string()]),must_haves:x.z.union([x.z.string(),x.z.array(x.z.string())])}).passthrough(),wr=x.z.object({phase:x.z.union([x.z.string(),x.z.number()]),plan:x.z.union([x.z.string(),x.z.number()]),subsystem:x.z.string(),tags:x.z.union([x.z.string(),x.z.array(x.z.string())]),duration:x.z.string(),completed:x.z.string()}).passthrough(),Mr=x.z.object({phase:x.z.union([x.z.string(),x.z.number()]),verified:x.z.union([x.z.boolean(),x.z.string()]),status:x.z.string(),score:x.z.union([x.z.number(),x.z.string()])}).passthrough(),Hs=x.z.object({test:x.z.number().int().positive().optional(),name:x.z.string(),expected:x.z.string().optional(),result:x.z.string(),category:x.z.string(),reason:x.z.string().optional(),blocked_by:x.z.string().optional()}).passthrough(),Fr=x.z.object({phase:x.z.string(),phase_dir:x.z.string(),file:x.z.string(),file_path:x.z.string(),type:x.z.literal("uat"),status:x.z.string(),items:x.z.array(Hs)}).passthrough(),Bs=x.z.object({branching_strategy:x.z.enum(["none","phase","milestone","workstream"]).default("none"),phase_branch_template:x.z.string().default("gsd/phase-{phase}-{slug}"),milestone_branch_template:x.z.string().default("gsd/{milestone}-{slug}"),quick_branch_template:x.z.string().nullable().default(null)}).passthrough(),Js=x.z.object({research:x.z.boolean().default(!0),plan_check:x.z.boolean().default(!0),verifier:x.z.boolean().default(!0),nyquist_validation:x.z.boolean().default(!0),auto_advance:x.z.boolean().default(!1),node_repair:x.z.boolean().default(!0),node_repair_budget:x.z.number().int().nonnegative().default(2),ui_phase:x.z.boolean().default(!0),ui_safety_gate:x.z.boolean().default(!0),text_mode:x.z.boolean().default(!1),research_before_questions:x.z.boolean().default(!1),discuss_mode:x.z.string().default("discuss"),skip_discuss:x.z.boolean().default(!1),_auto_chain_active:x.z.boolean().default(!1)}).passthrough(),Ys=x.z.object({context_warnings:x.z.boolean().default(!0),workflow_guard:x.z.boolean().default(!1)}).passthrough(),kt=x.z.object({model_profile:x.z.enum(["quality","balanced","budget","inherit"]).default("balanced"),commit_docs:x.z.boolean().default(!0),parallelization:x.z.boolean().default(!0),search_gitignored:x.z.boolean().default(!1),brave_search:x.z.boolean().default(!1),firecrawl:x.z.boolean().default(!1),exa_search:x.z.boolean().default(!1),git:Bs.default({}),workflow:Js.default({}),hooks:Ys.default({}),agent_skills:x.z.record(x.z.string(),x.z.unknown()).default({})}).passthrough()});var Pt={};fe(Pt,{cmdValidateAgents:()=>oi,cmdValidateConsistency:()=>ii,cmdValidateHealth:()=>ri,cmdVerifyArtifacts:()=>ni,cmdVerifyCommits:()=>ti,cmdVerifyKeyLinks:()=>si,cmdVerifyPhaseCompleteness:()=>Xs,cmdVerifyPlanStructure:()=>Qs,cmdVerifyReferences:()=>ei,cmdVerifySummary:()=>Zs});function Ks(n,e){let s=n;for(let i of e){if(s==null||typeof s!="object")return;s=s[i]}return s}function Zs(n,e,s,i){e||S("summary-path required");let t=Y.default.join(n,e),r=s||2;if(!K.default.existsSync(t)){h({passed:!1,checks:{summary_exists:!1,files_created:{checked:0,found:0,missing:[]},commits_exist:!1,self_check:"not_found"},errors:["SUMMARY.md not found"]},i,"failed");return}let o=K.default.readFileSync(t,"utf-8"),a=[],l=new Set;for(let y of[/`([^`]+\.[a-zA-Z]+)`/g,/(?:Created|Modified|Added|Updated|Edited):\s*`?([^\s`]+\.[a-zA-Z]+)`?/gi]){let g;for(;(g=y.exec(o))!==null;)g[1]&&!g[1].startsWith("http")&&g[1].includes("/")&&l.add(g[1])}let c=Array.from(l).slice(0,r),d=c.filter(y=>!K.default.existsSync(Y.default.join(n,y))),u=o.match(/\b[0-9a-f]{7,40}\b/g)||[],f=!1;for(let y of u.slice(0,3))if(oe(n,["cat-file","-t",y]).stdout==="commit"){f=!0;break}let m="not_found";if(/##\s*(?:Self[- ]?Check|Verification|Quality Check)/i.test(o)){let y=o.slice(o.search(/##\s*(?:Self[- ]?Check|Verification|Quality Check)/i));/(?:fail|✗|❌|incomplete|blocked)/i.test(y)?m="failed":/(?:all\s+)?(?:pass|✓|✅|complete|succeeded)/i.test(y)&&(m="passed")}d.length>0&&a.push("Missing files: "+d.join(", ")),!f&&u.length>0&&a.push("Referenced commit hashes not found in git history"),m==="failed"&&a.push("Self-check section indicates failure");let p=d.length===0&&m!=="failed";h({passed:p,checks:{summary_exists:!0,files_created:{checked:c.length,found:c.length-d.length,missing:d},commits_exist:f,self_check:m},errors:a},i,p?"passed":"failed")}function Qs(n,e,s){e||S("file path required");let i=Y.default.isAbsolute(e)?e:Y.default.join(n,e),t=we(i);if(!t){h({error:"File not found",path:e},s);return}let r=ee(t),o=[],a=[];for(let f of["phase","plan","type","wave","depends_on","files_modified","autonomous","must_haves"])r[f]===void 0&&o.push(`Missing required frontmatter field: ${f}`);let l=/<task[^>]*>([\s\S]*?)<\/task>/g,c=[],d;for(;(d=l.exec(t))!==null;){let f=d[1],m=f.match(/<name>([\s\S]*?)<\/name>/),p=m?m[1].trim():"unnamed",y=/<files>/.test(f),g=/<action>/.test(f),_=/<verify>/.test(f),b=/<done>/.test(f);m||o.push("Task missing <name> element"),g||o.push(`Task '${p}' missing <action>`),_||a.push(`Task '${p}' missing <verify>`),b||a.push(`Task '${p}' missing <done>`),y||a.push(`Task '${p}' missing <files>`),c.push({name:p,hasFiles:y,hasAction:g,hasVerify:_,hasDone:b})}c.length===0&&a.push("No <task> elements found"),r.wave&&parseInt(String(r.wave))>1&&(!r.depends_on||Array.isArray(r.depends_on)&&r.depends_on.length===0)&&a.push("Wave > 1 but depends_on is empty"),/<task\s+type=["']?checkpoint/.test(t)&&r.autonomous!=="false"&&r.autonomous!==!1&&o.push("Has checkpoint tasks but autonomous is not false"),h({valid:o.length===0,errors:o,warnings:a,task_count:c.length,tasks:c,frontmatter_fields:Object.keys(r)},s,o.length===0?"valid":"invalid")}function Xs(n,e,s){e||S("phase required");let i=ue(n,e);if(!i?.found){h({error:"Phase not found",phase:e},s);return}let t=Y.default.join(n,i.directory),r=[],o=[],a;try{a=K.default.readdirSync(t)}catch{h({error:"Cannot read phase directory"},s);return}let l=a.filter(p=>p.match(/-PLAN\.md$/i)),c=a.filter(p=>p.match(/-SUMMARY\.md$/i)),d=new Set(l.map(p=>p.replace(/-PLAN\.md$/i,""))),u=new Set(c.map(p=>p.replace(/-SUMMARY\.md$/i,""))),f=[...d].filter(p=>!u.has(p)),m=[...u].filter(p=>!d.has(p));f.length>0&&r.push(`Plans without summaries: ${f.join(", ")}`),m.length>0&&o.push(`Summaries without plans: ${m.join(", ")}`),h({complete:r.length===0,phase:i.phase_number,plan_count:l.length,summary_count:c.length,incomplete_plans:f,orphan_summaries:m,errors:r,warnings:o},s,r.length===0?"complete":"incomplete")}function ei(n,e,s){e||S("file path required");let i=Y.default.isAbsolute(e)?e:Y.default.join(n,e),t=we(i);if(!t){h({error:"File not found",path:e},s);return}let r=[],o=[];for(let a of t.match(/@([^\s\n,)]+\/[^\s\n,)]+)/g)||[]){let l=a.slice(1),c=l.startsWith("~/")?Y.default.join(process.env.HOME??"",l.slice(2)):Y.default.join(n,l);(K.default.existsSync(c)?r:o).push(l)}for(let a of t.match(/`([^`]+\/[^`]+\.[a-zA-Z]{1,10})`/g)||[]){let l=a.slice(1,-1);l.startsWith("http")||l.includes("${")||l.includes("{{")||r.includes(l)||o.includes(l)||(K.default.existsSync(Y.default.join(n,l))?r:o).push(l)}h({valid:o.length===0,found:r.length,missing:o,total:r.length+o.length},s,o.length===0?"valid":"invalid")}function ti(n,e,s){(!e||e.length===0)&&S("At least one commit hash required");let i=[],t=[];for(let r of e)(oe(n,["cat-file","-t",r]).stdout.trim()==="commit"?i:t).push(r);h({all_valid:t.length===0,valid:i,invalid:t,total:e.length},s,t.length===0?"valid":"invalid")}function ni(n,e,s){e||S("plan file path required");let i=Y.default.isAbsolute(e)?e:Y.default.join(n,e),t=we(i);if(!t){h({error:"File not found",path:e},s);return}let r=St(t,"artifacts");if(r.length===0){h({error:"No must_haves.artifacts found in frontmatter",path:e},s);return}let o=[];for(let l of r){if(typeof l=="string")continue;let c=l;if(!c.path)continue;let d=Y.default.join(n,c.path),u=K.default.existsSync(d),f={path:c.path,exists:u,issues:[],passed:!1};if(u){let m=we(d)??"",p=m.split(`
276
+ `,P=new RegExp(`(#{2,4}\\s*Phase\\s+0*${d}:[^\\n]*\\n)`,"i"),k=r.match(P);k||S(`Could not find Phase ${e} header`);let $=r.indexOf(k[0]),A=r.slice($+k[0].length).match(/\n#{2,4}\s+Phase\s+\d/i),O=A?$+k[0].length+A.index:r.length;j.default.writeFileSync(t,r.slice(0,O)+v+r.slice(O),"utf-8"),h({phase_number:g,after_phase:e,name:s,slug:a,directory:z(M.default.join(M.default.relative(n,F(n)),"phases",_))},i,g)}function Ls(n,e,s){let i=[],t=[],r=new RegExp(`^${e}\\.(\\d+)-(.+)$`),o=Oe(n,!0).map(a=>{let l=a.match(r);return l?{dir:a,oldDecimal:parseInt(l[1],10),slug:l[2]}:null}).filter(a=>a!==null&&a.oldDecimal>s).sort((a,l)=>l.oldDecimal-a.oldDecimal);for(let a of o){let l=a.oldDecimal-1,c=`${e}.${a.oldDecimal}`,d=`${e}.${l}`,u=`${e}.${l}-${a.slug}`;j.default.renameSync(M.default.join(n,a.dir),M.default.join(n,u)),i.push({from:a.dir,to:u});for(let f of j.default.readdirSync(M.default.join(n,u)))if(f.includes(c)){let m=f.replace(c,d);j.default.renameSync(M.default.join(n,u,f),M.default.join(n,u,m)),t.push({from:f,to:m})}}return{renamedDirs:i,renamedFiles:t}}function Us(n,e){let s=[],i=[],t=Oe(n,!0).map(r=>{let o=r.match(/^(\d+)([A-Z])?(?:\.(\d+))?-(.+)$/i);if(!o)return null;let a=parseInt(o[1],10);return a>e?{dir:r,oldInt:a,letter:o[2]?o[2].toUpperCase():"",decimal:o[3]?parseInt(o[3],10):null,slug:o[4]}:null}).filter(r=>r!==null).sort((r,o)=>r.oldInt!==o.oldInt?o.oldInt-r.oldInt:(o.decimal||0)-(r.decimal||0));for(let r of t){let o=r.oldInt-1,a=String(o).padStart(2,"0"),l=String(r.oldInt).padStart(2,"0"),c=r.letter||"",d=r.decimal!==null?`.${r.decimal}`:"",u=`${l}${c}${d}`,f=`${a}${c}${d}`,m=`${f}-${r.slug}`;j.default.renameSync(M.default.join(n,r.dir),M.default.join(n,m)),s.push({from:r.dir,to:m});for(let p of j.default.readdirSync(M.default.join(n,m)))if(p.startsWith(u)){let y=f+p.slice(u.length);j.default.renameSync(M.default.join(n,m,p),M.default.join(n,m,y)),i.push({from:p,to:y})}}return{renamedDirs:s,renamedFiles:i}}function Gs(n,e,s,i){let t=j.default.readFileSync(n,"utf-8"),r=Q(e);if(t=t.replace(new RegExp(`\\n?#{2,4}\\s*Phase\\s+${r}\\s*:[\\s\\S]*?(?=\\n#{2,4}\\s+Phase\\s+\\d|$)`,"i"),""),t=t.replace(new RegExp(`\\n?-\\s*\\[[ x]\\]\\s*.*Phase\\s+${r}[:\\s][^\\n]*`,"gi"),""),t=t.replace(new RegExp(`\\n?\\|\\s*${r}\\.?\\s[^|]*\\|[^\\n]*`,"gi"),""),!s)for(let o=99;o>i;o--){let a=o-1,l=String(o),c=String(a),d=l.padStart(2,"0"),u=c.padStart(2,"0");t=t.replace(new RegExp(`(#{2,4}\\s*Phase\\s+)${l}(\\s*:)`,"gi"),`$1${c}$2`),t=t.replace(new RegExp(`(Phase\\s+)${l}([:\\s])`,"g"),`$1${c}$2`),t=t.replace(new RegExp(`${d}-(\\d{2})`,"g"),`${u}-$1`),t=t.replace(new RegExp(`(\\|\\s*)${l}\\.\\s`,"g"),`$1${c}. `),t=t.replace(new RegExp(`(Depends on:\\*\\*\\s*Phase\\s+)${l}\\b`,"gi"),`$1${c}`)}j.default.writeFileSync(n,t,"utf-8")}function zs(n,e,s,i){e||S("phase number required for phase remove");let t=M.default.join(F(n),"ROADMAP.md"),r=M.default.join(F(n),"phases");j.default.existsSync(t)||S("ROADMAP.md not found");let o=ce(e),a=e.includes("."),l=s.force||!1,c=Oe(r,!0).find(m=>m.startsWith(o+"-")||m===o)||null;if(c&&!l){let m=j.default.readdirSync(M.default.join(r,c)).filter(p=>p.endsWith("-SUMMARY.md")||p==="SUMMARY.md");m.length>0&&S(`Phase ${e} has ${m.length} executed plan(s). Use --force to remove anyway.`)}c&&j.default.rmSync(M.default.join(r,c),{recursive:!0,force:!0});let d=[],u=[];try{let m=a?Ls(r,o.split(".")[0],parseInt(o.split(".")[1],10)):Us(r,parseInt(o,10));d=m.renamedDirs,u=m.renamedFiles}catch{}Gs(t,e,a,parseInt(o,10));let f=M.default.join(F(n),"STATE.md");if(j.default.existsSync(f)){let m=j.default.readFileSync(f,"utf-8"),p=V(m,"Total Phases");p&&(m=Se(m,"Total Phases",String(parseInt(p,10)-1))??m);let y=m.match(/(\bof\s+)(\d+)(\s*(?:\(|phases?))/i);y&&(m=m.replace(/(\bof\s+)(\d+)(\s*(?:\(|phases?))/i,`$1${parseInt(y[2],10)-1}$3`)),te(f,m,n)}h({removed:e,directory_deleted:c,renamed_directories:d,renamed_files:u,roadmap_updated:!0,state_updated:j.default.existsSync(f)},i)}function Vs(n,e,s){e||S("phase number required for phase complete");let i=M.default.join(F(n),"ROADMAP.md"),t=M.default.join(F(n),"STATE.md"),r=M.default.join(F(n),"phases"),o=ce(e),a=new Date().toISOString().split("T")[0],l=ue(n,e);l||S(`Phase ${e} not found`);let c=l.plans.length,d=l.summaries.length,u=!1,f=[];try{let g=M.default.join(n,l.directory),_=j.default.readdirSync(g);for(let b of _.filter(v=>v.includes("-UAT")&&v.endsWith(".md"))){let v=j.default.readFileSync(M.default.join(g,b),"utf-8");/result: pending/.test(v)&&f.push(`${b}: has pending tests`),/result: blocked/.test(v)&&f.push(`${b}: has blocked tests`),/status: partial/.test(v)&&f.push(`${b}: testing incomplete (partial)`),/status: diagnosed/.test(v)&&f.push(`${b}: has diagnosed gaps`)}for(let b of _.filter(v=>v.includes("-VERIFICATION")&&v.endsWith(".md"))){let v=j.default.readFileSync(M.default.join(g,b),"utf-8");/status: human_needed/.test(v)&&f.push(`${b}: needs human verification`),/status: gaps_found/.test(v)&&f.push(`${b}: has unresolved gaps`)}}catch{}if(j.default.existsSync(i)){let g=j.default.readFileSync(i,"utf-8"),_=new RegExp(`(-\\s*\\[)[ ](\\]\\s*.*Phase\\s+${Q(e)}[:\\s][^\\n]*)`,"i");g=Ze(g,_,`$1x$2 (completed ${a})`);let b=Q(e);g=g.replace(new RegExp(`^(\\|\\s*${b}\\.?\\s[^|]*(?:\\|[^\\n]*))$`,"im"),P=>{let k=P.split("|").slice(1,-1);return k.length===5?(k[3]=" Complete ",k[4]=` ${a} `):k.length===4&&(k[2]=" Complete ",k[3]=` ${a} `),"|"+k.join("|")+"|"}),g=Ze(g,new RegExp(`(#{2,4}\\s*Phase\\s+${b}[\\s\\S]*?\\*\\*Plans:\\*\\*\\s*)[^\\n]+`,"i"),`$1${d}/${c} plans complete`),j.default.writeFileSync(i,g,"utf-8");let v=M.default.join(F(n),"REQUIREMENTS.md");if(j.default.existsSync(v)){let k=be(g,n).match(new RegExp(`(#{2,4}\\s*Phase\\s+${Q(e)}[:\\s][\\s\\S]*?)(?=#{2,4}\\s*Phase\\s+|$)`,"i")),T=(k?k[1]:"").match(/\*\*Requirements:\*\*\s*([^\n]+)/i);if(T){let A=T[1].replace(/[[\]]/g,"").split(/[,\s]+/).map(N=>N.trim()).filter(Boolean),O=j.default.readFileSync(v,"utf-8");for(let N of A){let L=Q(N);O=O.replace(new RegExp(`(-\\s*\\[)[ ](\\]\\s*\\*\\*${L}\\*\\*)`,"gi"),"$1x$2"),O=O.replace(new RegExp(`(\\|\\s*${L}\\s*\\|[^|]+\\|)\\s*(?:Pending|In Progress)\\s*(\\|)`,"gi"),"$1 Complete $2")}j.default.writeFileSync(v,O,"utf-8"),u=!0}}}let m=null,p=null,y=!0;try{let g=Pe(n),_=j.default.readdirSync(r,{withFileTypes:!0}).filter(b=>b.isDirectory()).map(b=>b.name).filter(g).sort((b,v)=>ke(b,v));for(let b of _){let v=b.match(/^(\d+[A-Z]?(?:\.\d+)*)-?(.*)/i);if(v&&ke(v[1],e)>0){m=v[1],p=v[2]||null,y=!1;break}}}catch{}if(y&&j.default.existsSync(i))try{let g=be(j.default.readFileSync(i,"utf-8"),n),_=/#{2,4}\s*Phase\s+(\d+[A-Z]?(?:\.\d+)*)\s*:\s*([^\n]+)/gi,b;for(;(b=_.exec(g))!==null;)if(ke(b[1],e)>0){m=b[1],p=b[2].replace(/\(INSERTED\)/i,"").trim().toLowerCase().replace(/\s+/g,"-"),y=!1;break}}catch{}if(j.default.existsSync(t)){let g=j.default.readFileSync(t,"utf-8"),_=m||e,b=V(g,"Current Phase")||V(g,"Phase"),v=String(_);if(b){let k=b.match(/of\s+(\d+)/),$=b.match(/\(([^)]+)\)/);if(k){let T=p?` (${p.replace(/-/g," ")})`:$?` (${$[1]})`:"";v=`${_} of ${k[1]}${T}`}}g=he(g,"Current Phase","Phase",v),p&&(g=he(g,"Current Phase Name",null,p.replace(/-/g," "))),g=he(g,"Status",null,y?"Milestone complete":"Ready to plan"),g=he(g,"Current Plan","Plan","Not started"),g=he(g,"Last Activity","Last activity",a),g=he(g,"Last Activity Description",null,`Phase ${e} complete${m?`, transitioned to Phase ${m}`:""}`);let P=V(g,"Completed Phases");if(P){let k=parseInt(P,10)+1;g=Se(g,"Completed Phases",String(k))??g;let $=V(g,"Total Phases");if($){let T=parseInt($,10);if(T>0){let A=Math.round(k/T*100);g=Se(g,"Progress",`${A}%`)??g,g=g.replace(/(percent:\s*)\d+/,`$1${A}`)}}}te(t,g,n)}h({completed_phase:e,phase_name:l.phase_name,plans_executed:`${d}/${c}`,next_phase:m,next_phase_name:p,is_last_phase:y,date:a,roadmap_updated:j.default.existsSync(i),state_updated:j.default.existsSync(t),requirements_updated:u,warnings:f,has_warnings:f.length>0},s)}var j,M,ut=le(()=>{"use strict";j=W(require("fs")),M=W(require("path"));pe();Ue();Be()});var x,yn,Ir,Er,wr,Mr,Hs,Fr,Bs,Js,Ys,kt,Sn=le(()=>{"use strict";x=require("zod"),yn=x.z.object({milestone:x.z.string().optional(),milestone_name:x.z.string().optional(),current_phase:x.z.string().optional(),current_phase_name:x.z.string().optional(),current_plan:x.z.string().optional(),total_phases:x.z.coerce.number().int().nonnegative().optional(),total_plans_in_phase:x.z.coerce.number().int().nonnegative().optional(),status:x.z.string().optional(),progress:x.z.string().optional(),last_activity:x.z.string().optional(),paused_at:x.z.string().optional(),stopped_at:x.z.string().optional()}).passthrough(),Ir=x.z.object({found:x.z.literal(!0),phase_number:x.z.string(),phase_name:x.z.string(),goal:x.z.string().nullable(),success_criteria:x.z.array(x.z.string()).default([]),section:x.z.string().optional()}).passthrough(),Er=x.z.object({phase:x.z.union([x.z.string(),x.z.number()]),plan:x.z.union([x.z.string(),x.z.number()]),type:x.z.string(),wave:x.z.union([x.z.string(),x.z.number()]),depends_on:x.z.union([x.z.string(),x.z.array(x.z.string())]),files_modified:x.z.union([x.z.string(),x.z.array(x.z.string())]),autonomous:x.z.union([x.z.boolean(),x.z.string()]),must_haves:x.z.union([x.z.string(),x.z.array(x.z.string())])}).passthrough(),wr=x.z.object({phase:x.z.union([x.z.string(),x.z.number()]),plan:x.z.union([x.z.string(),x.z.number()]),subsystem:x.z.string(),tags:x.z.union([x.z.string(),x.z.array(x.z.string())]),duration:x.z.string(),completed:x.z.string()}).passthrough(),Mr=x.z.object({phase:x.z.union([x.z.string(),x.z.number()]),verified:x.z.union([x.z.boolean(),x.z.string()]),status:x.z.string(),score:x.z.union([x.z.number(),x.z.string()])}).passthrough(),Hs=x.z.object({test:x.z.number().int().positive().optional(),name:x.z.string(),expected:x.z.string().optional(),result:x.z.string(),category:x.z.string(),reason:x.z.string().optional(),blocked_by:x.z.string().optional()}).passthrough(),Fr=x.z.object({phase:x.z.string(),phase_dir:x.z.string(),file:x.z.string(),file_path:x.z.string(),type:x.z.literal("uat"),status:x.z.string(),items:x.z.array(Hs)}).passthrough(),Bs=x.z.object({branching_strategy:x.z.enum(["none","phase","milestone","workstream"]).default("none"),phase_branch_template:x.z.string().default("gsd/phase-{phase}-{slug}"),milestone_branch_template:x.z.string().default("gsd/{milestone}-{slug}"),quick_branch_template:x.z.string().nullable().default(null)}).passthrough(),Js=x.z.object({research:x.z.boolean().default(!0),plan_check:x.z.boolean().default(!0),verifier:x.z.boolean().default(!0),nyquist_validation:x.z.boolean().default(!0),auto_advance:x.z.boolean().default(!1),node_repair:x.z.boolean().default(!0),node_repair_budget:x.z.number().int().nonnegative().default(2),auto_retry_audit:x.z.boolean().default(!0),auto_retry_audit_budget:x.z.number().int().nonnegative().default(1),auto_retry_tech_debt:x.z.boolean().default(!0),auto_retry_tech_debt_budget:x.z.number().int().nonnegative().default(1),ui_phase:x.z.boolean().default(!0),ui_safety_gate:x.z.boolean().default(!0),text_mode:x.z.boolean().default(!1),research_before_questions:x.z.boolean().default(!1),discuss_mode:x.z.string().default("discuss"),skip_discuss:x.z.boolean().default(!1),_auto_chain_active:x.z.boolean().default(!1)}).passthrough(),Ys=x.z.object({context_warnings:x.z.boolean().default(!0),workflow_guard:x.z.boolean().default(!1)}).passthrough(),kt=x.z.object({model_profile:x.z.enum(["quality","balanced","budget","inherit"]).default("balanced"),commit_docs:x.z.boolean().default(!0),parallelization:x.z.boolean().default(!0),search_gitignored:x.z.boolean().default(!1),brave_search:x.z.boolean().default(!1),firecrawl:x.z.boolean().default(!1),exa_search:x.z.boolean().default(!1),git:Bs.default({}),workflow:Js.default({}),hooks:Ys.default({}),agent_skills:x.z.record(x.z.string(),x.z.unknown()).default({})}).passthrough()});var Pt={};fe(Pt,{cmdValidateAgents:()=>oi,cmdValidateConsistency:()=>ii,cmdValidateHealth:()=>ri,cmdVerifyArtifacts:()=>ni,cmdVerifyCommits:()=>ti,cmdVerifyKeyLinks:()=>si,cmdVerifyPhaseCompleteness:()=>Xs,cmdVerifyPlanStructure:()=>Qs,cmdVerifyReferences:()=>ei,cmdVerifySummary:()=>Zs});function Ks(n,e){let s=n;for(let i of e){if(s==null||typeof s!="object")return;s=s[i]}return s}function Zs(n,e,s,i){e||S("summary-path required");let t=Y.default.join(n,e),r=s||2;if(!K.default.existsSync(t)){h({passed:!1,checks:{summary_exists:!1,files_created:{checked:0,found:0,missing:[]},commits_exist:!1,self_check:"not_found"},errors:["SUMMARY.md not found"]},i,"failed");return}let o=K.default.readFileSync(t,"utf-8"),a=[],l=new Set;for(let y of[/`([^`]+\.[a-zA-Z]+)`/g,/(?:Created|Modified|Added|Updated|Edited):\s*`?([^\s`]+\.[a-zA-Z]+)`?/gi]){let g;for(;(g=y.exec(o))!==null;)g[1]&&!g[1].startsWith("http")&&g[1].includes("/")&&l.add(g[1])}let c=Array.from(l).slice(0,r),d=c.filter(y=>!K.default.existsSync(Y.default.join(n,y))),u=o.match(/\b[0-9a-f]{7,40}\b/g)||[],f=!1;for(let y of u.slice(0,3))if(oe(n,["cat-file","-t",y]).stdout==="commit"){f=!0;break}let m="not_found";if(/##\s*(?:Self[- ]?Check|Verification|Quality Check)/i.test(o)){let y=o.slice(o.search(/##\s*(?:Self[- ]?Check|Verification|Quality Check)/i));/(?:fail|✗|❌|incomplete|blocked)/i.test(y)?m="failed":/(?:all\s+)?(?:pass|✓|✅|complete|succeeded)/i.test(y)&&(m="passed")}d.length>0&&a.push("Missing files: "+d.join(", ")),!f&&u.length>0&&a.push("Referenced commit hashes not found in git history"),m==="failed"&&a.push("Self-check section indicates failure");let p=d.length===0&&m!=="failed";h({passed:p,checks:{summary_exists:!0,files_created:{checked:c.length,found:c.length-d.length,missing:d},commits_exist:f,self_check:m},errors:a},i,p?"passed":"failed")}function Qs(n,e,s){e||S("file path required");let i=Y.default.isAbsolute(e)?e:Y.default.join(n,e),t=we(i);if(!t){h({error:"File not found",path:e},s);return}let r=ee(t),o=[],a=[];for(let f of["phase","plan","type","wave","depends_on","files_modified","autonomous","must_haves"])r[f]===void 0&&o.push(`Missing required frontmatter field: ${f}`);let l=/<task[^>]*>([\s\S]*?)<\/task>/g,c=[],d;for(;(d=l.exec(t))!==null;){let f=d[1],m=f.match(/<name>([\s\S]*?)<\/name>/),p=m?m[1].trim():"unnamed",y=/<files>/.test(f),g=/<action>/.test(f),_=/<verify>/.test(f),b=/<done>/.test(f);m||o.push("Task missing <name> element"),g||o.push(`Task '${p}' missing <action>`),_||a.push(`Task '${p}' missing <verify>`),b||a.push(`Task '${p}' missing <done>`),y||a.push(`Task '${p}' missing <files>`),c.push({name:p,hasFiles:y,hasAction:g,hasVerify:_,hasDone:b})}c.length===0&&a.push("No <task> elements found"),r.wave&&parseInt(String(r.wave))>1&&(!r.depends_on||Array.isArray(r.depends_on)&&r.depends_on.length===0)&&a.push("Wave > 1 but depends_on is empty"),/<task\s+type=["']?checkpoint/.test(t)&&r.autonomous!=="false"&&r.autonomous!==!1&&o.push("Has checkpoint tasks but autonomous is not false"),h({valid:o.length===0,errors:o,warnings:a,task_count:c.length,tasks:c,frontmatter_fields:Object.keys(r)},s,o.length===0?"valid":"invalid")}function Xs(n,e,s){e||S("phase required");let i=ue(n,e);if(!i?.found){h({error:"Phase not found",phase:e},s);return}let t=Y.default.join(n,i.directory),r=[],o=[],a;try{a=K.default.readdirSync(t)}catch{h({error:"Cannot read phase directory"},s);return}let l=a.filter(p=>p.match(/-PLAN\.md$/i)),c=a.filter(p=>p.match(/-SUMMARY\.md$/i)),d=new Set(l.map(p=>p.replace(/-PLAN\.md$/i,""))),u=new Set(c.map(p=>p.replace(/-SUMMARY\.md$/i,""))),f=[...d].filter(p=>!u.has(p)),m=[...u].filter(p=>!d.has(p));f.length>0&&r.push(`Plans without summaries: ${f.join(", ")}`),m.length>0&&o.push(`Summaries without plans: ${m.join(", ")}`),h({complete:r.length===0,phase:i.phase_number,plan_count:l.length,summary_count:c.length,incomplete_plans:f,orphan_summaries:m,errors:r,warnings:o},s,r.length===0?"complete":"incomplete")}function ei(n,e,s){e||S("file path required");let i=Y.default.isAbsolute(e)?e:Y.default.join(n,e),t=we(i);if(!t){h({error:"File not found",path:e},s);return}let r=[],o=[];for(let a of t.match(/@([^\s\n,)]+\/[^\s\n,)]+)/g)||[]){let l=a.slice(1),c=l.startsWith("~/")?Y.default.join(process.env.HOME??"",l.slice(2)):Y.default.join(n,l);(K.default.existsSync(c)?r:o).push(l)}for(let a of t.match(/`([^`]+\/[^`]+\.[a-zA-Z]{1,10})`/g)||[]){let l=a.slice(1,-1);l.startsWith("http")||l.includes("${")||l.includes("{{")||r.includes(l)||o.includes(l)||(K.default.existsSync(Y.default.join(n,l))?r:o).push(l)}h({valid:o.length===0,found:r.length,missing:o,total:r.length+o.length},s,o.length===0?"valid":"invalid")}function ti(n,e,s){(!e||e.length===0)&&S("At least one commit hash required");let i=[],t=[];for(let r of e)(oe(n,["cat-file","-t",r]).stdout.trim()==="commit"?i:t).push(r);h({all_valid:t.length===0,valid:i,invalid:t,total:e.length},s,t.length===0?"valid":"invalid")}function ni(n,e,s){e||S("plan file path required");let i=Y.default.isAbsolute(e)?e:Y.default.join(n,e),t=we(i);if(!t){h({error:"File not found",path:e},s);return}let r=St(t,"artifacts");if(r.length===0){h({error:"No must_haves.artifacts found in frontmatter",path:e},s);return}let o=[];for(let l of r){if(typeof l=="string")continue;let c=l;if(!c.path)continue;let d=Y.default.join(n,c.path),u=K.default.existsSync(d),f={path:c.path,exists:u,issues:[],passed:!1};if(u){let m=we(d)??"",p=m.split(`
277
277
  `).length;if(c.min_lines&&p<c.min_lines&&f.issues.push(`Only ${p} lines, need ${c.min_lines}`),c.contains&&!m.includes(c.contains)&&f.issues.push(`Missing pattern: ${c.contains}`),c.exports){let y=Array.isArray(c.exports)?c.exports:[c.exports];for(let g of y)m.includes(g)||f.issues.push(`Missing export: ${g}`)}f.passed=f.issues.length===0}else f.issues.push("File not found");o.push(f)}let a=o.filter(l=>l.passed).length;h({all_passed:a===o.length,passed:a,total:o.length,artifacts:o},s,a===o.length?"valid":"invalid")}function si(n,e,s){e||S("plan file path required");let i=Y.default.isAbsolute(e)?e:Y.default.join(n,e),t=we(i);if(!t){h({error:"File not found",path:e},s);return}let r=St(t,"key_links");if(r.length===0){h({error:"No must_haves.key_links found in frontmatter",path:e},s);return}let o=[];for(let l of r){if(typeof l=="string")continue;let c=l,d={from:c.from??"",to:c.to??"",via:c.via||"",verified:!1,detail:""},u=we(Y.default.join(n,c.from||""));if(!u)d.detail="Source file not found";else if(c.pattern)try{let f=new RegExp(c.pattern);if(f.test(u))d.verified=!0,d.detail="Pattern found in source";else{let m=we(Y.default.join(n,c.to||""));m&&f.test(m)?(d.verified=!0,d.detail="Pattern found in target"):d.detail=`Pattern "${c.pattern}" not found in source or target`}}catch{d.detail=`Invalid regex pattern: ${c.pattern}`}else u.includes(c.to||"")?(d.verified=!0,d.detail="Target referenced in source"):d.detail="Target not referenced in source";o.push(d)}let a=o.filter(l=>l.verified).length;h({all_verified:a===o.length,verified:a,total:o.length,links:o},s,a===o.length?"valid":"invalid")}function ii(n,e){let s=Y.default.join(F(n),"ROADMAP.md"),i=Y.default.join(F(n),"phases"),t=[],r=[];if(!K.default.existsSync(s)){t.push("ROADMAP.md not found"),h({passed:!1,errors:t,warnings:r},e,"failed");return}let o=be(K.default.readFileSync(s,"utf-8"),n),a=new Set,l=/#{2,4}\s*Phase\s+(\d+[A-Z]?(?:\.\d+)*)\s*:/gi,c;for(;(c=l.exec(o))!==null;)a.add(c[1]);let d=new Set;try{K.default.readdirSync(i,{withFileTypes:!0}).filter(m=>m.isDirectory()).map(m=>m.name).forEach(m=>{let p=m.match(/^(\d+[A-Z]?(?:\.\d+)*)/i);p&&d.add(p[1])})}catch{}for(let m of a)!d.has(m)&&!d.has(ce(m))&&r.push(`Phase ${m} in ROADMAP.md but no directory on disk`);for(let m of d){let p=String(parseInt(m,10));!a.has(m)&&!a.has(p)&&r.push(`Phase ${m} exists on disk but not in ROADMAP.md`)}if(B(n).phase_naming!=="custom"){let m=[...d].filter(p=>!p.includes(".")).map(p=>parseInt(p,10)).sort((p,y)=>p-y);for(let p=1;p<m.length;p++)m[p]!==m[p-1]+1&&r.push(`Gap in phase numbering: ${m[p-1]} \u2192 ${m[p]}`)}let f=t.length===0;h({passed:f,errors:t,warnings:r,warning_count:r.length},e,f?"passed":"failed")}function ri(n,e,s){let i=Y.default.resolve(n);if(i===_n.default.homedir()){h({status:"error",errors:[{code:"E010",message:"CWD is home directory - health check would read the wrong .planning/ directory.",fix:"cd into your project directory and retry"}],warnings:[],info:[{code:"I010",message:`Resolved CWD: ${i}`}],repairable_count:0},s);return}let t=F(n),r=J(n),o=Y.default.join(r,"PROJECT.md"),a=Y.default.join(t,"ROADMAP.md"),l=Y.default.join(t,"STATE.md"),c=Y.default.join(r,"config.json"),d=Y.default.join(t,"phases"),u=[],f=[],m=[],p=[],y=(b,v,P,k,$=!1,T)=>{let A={code:v,message:P,fix:k,repairable:$,...T};b==="error"?u.push(A):b==="warning"?f.push(A):m.push(A)};if(!K.default.existsSync(t)){y("error","E001",".planning/ directory not found","Run /gsd-new-project to initialize"),h({status:"broken",errors:u,warnings:f,info:m,repairable_count:0},s);return}if(!K.default.existsSync(o))y("error","E002","PROJECT.md not found","Run /gsd-new-project to create");else{let b=K.default.readFileSync(o,"utf-8");for(let v of["## What This Is","## Core Value","## Requirements"])b.includes(v)||y("warning","W001",`PROJECT.md missing section: ${v}`,"Add section manually")}if(K.default.existsSync(a)||y("error","E003","ROADMAP.md not found","Run /gsd-new-milestone to create roadmap"),!K.default.existsSync(l))y("error","E004","STATE.md not found","Run /gsd-health --repair to regenerate",!0),p.push("regenerateState");else try{let b=K.default.readFileSync(l,"utf-8"),v=ee(b),P=yn.safeParse(v);if(!P.success){for(let k of P.error.issues){let $=k.path.join(".")||"(root)";y("warning","W011",`STATE.md frontmatter: field "${$}" \u2014 ${k.message}`,"Check STATE.md frontmatter manually or run /gsd-health --repair to regenerate",!0)}p.includes("regenerateState")||p.push("regenerateState")}}catch{}if(!K.default.existsSync(c))y("warning","W003","config.json not found","Run /gsd-health --repair to create with defaults",!0),p.push("createConfig");else try{let b=JSON.parse(K.default.readFileSync(c,"utf-8")),v=kt.safeParse(b);if(!v.success){for(let P of v.error.issues){let k=P.path.join(".")||"(root)",$=Ks(b,P.path);y("warning","W005",`config.json: field "${k}" - ${P.message}`,"Run pi-gsd-tools validate health --repair to fix using schema defaults",!0,{field:k,expected:P.message,actual:$})}p.includes("fixSchemaDefaults")||p.push("fixSchemaDefaults")}}catch(b){y("error","E005",`config.json: JSON parse error - ${b.message}`,"Run pi-gsd-tools validate health --repair to reset to defaults",!0),p.push("resetConfig")}try{let b=ot();b.agents_installed||y("warning","W010",b.installed_agents.length===0?`No GSD agents found in ${b.agents_dir}`:`Missing ${b.missing_agents.length} GSD agents: ${b.missing_agents.join(", ")}`,"Run the GSD installer: pi install npm:pi-gsd")}catch{}let g=[];if(e.repair&&p.length>0)for(let b of p)try{if(b==="createConfig"||b==="resetConfig"){let v=kt.parse({});K.default.writeFileSync(c,JSON.stringify(v,null,2),"utf-8"),g.push({action:b,success:!0,path:"config.json"})}else if(b==="fixSchemaDefaults"&&K.default.existsSync(c)){let v=JSON.parse(K.default.readFileSync(c,"utf-8")),P=kt.parse(v);K.default.writeFileSync(c,JSON.stringify(P,null,2),"utf-8"),g.push({action:b,success:!0,path:"config.json"})}else if(b==="regenerateState"){if(K.default.existsSync(l)){let P=new Date().toISOString().replace(/[:.]/g,"-").slice(0,19),k=`${l}.bak-${P}`;K.default.copyFileSync(l,k),g.push({action:"backupState",success:!0,path:k})}let v=ie(n);te(l,`# Session State
278
278
 
279
279
  ## Project Reference
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-gsd",
3
- "version": "1.4.0",
3
+ "version": "1.6.0",
4
4
  "description": "Get Shit Done - Unofficial port of the renowned AI-native project-planning spec-driven toolkit",
5
5
  "main": "dist/pi-gsd-tools.js",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: gsd-execute-milestone
3
- description: Execute all planned phases in the milestone scope guardian, UAT gates, recovery loop
3
+ description: Execute all planned phases + full milestone lifecycle (audit complete cleanup)
4
4
  ---
5
5
 
6
6
  <objective>