learnship 1.9.20 → 1.9.22

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 (47) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.cursor-plugin/plugin.json +1 -1
  3. package/README.md +1 -1
  4. package/agents/learnship-debugger.md +1 -0
  5. package/agents/learnship-verifier.md +1 -0
  6. package/cursor-rules/learnship.mdc +48 -0
  7. package/gemini-extension.json +1 -1
  8. package/learnship/agents/debugger.md +81 -25
  9. package/learnship/agents/verifier.md +80 -24
  10. package/learnship/references/planning-config.md +2 -2
  11. package/learnship/workflows/add-phase.md +5 -2
  12. package/learnship/workflows/add-tests.md +2 -0
  13. package/learnship/workflows/add-todo.md +1 -1
  14. package/learnship/workflows/audit-milestone.md +1 -0
  15. package/learnship/workflows/cleanup.md +1 -1
  16. package/learnship/workflows/complete-milestone.md +2 -1
  17. package/learnship/workflows/debug.md +2 -2
  18. package/learnship/workflows/diagnose-issues.md +1 -0
  19. package/learnship/workflows/discovery-phase.md +6 -0
  20. package/learnship/workflows/discuss-milestone.md +2 -1
  21. package/learnship/workflows/discuss-phase.md +2 -1
  22. package/learnship/workflows/execute-plan.md +1 -0
  23. package/learnship/workflows/health.md +40 -34
  24. package/learnship/workflows/insert-phase.md +2 -2
  25. package/learnship/workflows/ls.md +2 -2
  26. package/learnship/workflows/map-codebase.md +1 -1
  27. package/learnship/workflows/milestone-retrospective.md +2 -0
  28. package/learnship/workflows/new-milestone.md +1 -0
  29. package/learnship/workflows/new-project.md +37 -15
  30. package/learnship/workflows/next.md +1 -1
  31. package/learnship/workflows/pause-work.md +1 -1
  32. package/learnship/workflows/plan-milestone-gaps.md +3 -1
  33. package/learnship/workflows/plan-phase.md +1 -1
  34. package/learnship/workflows/progress.md +2 -2
  35. package/learnship/workflows/quick.md +5 -3
  36. package/learnship/workflows/release.md +3 -2
  37. package/learnship/workflows/remove-phase.md +1 -1
  38. package/learnship/workflows/research-phase.md +1 -1
  39. package/learnship/workflows/resume-work.md +3 -3
  40. package/learnship/workflows/set-profile.md +6 -6
  41. package/learnship/workflows/settings.md +1 -1
  42. package/learnship/workflows/sync-upstream-skills.md +1 -1
  43. package/learnship/workflows/transition.md +1 -1
  44. package/learnship/workflows/update.md +2 -1
  45. package/learnship/workflows/validate-phase.md +2 -1
  46. package/package.json +1 -1
  47. package/references/planning-config.md +2 -2
@@ -13,6 +13,7 @@ Execute a single PLAN.md file in isolation. Useful when one plan in a phase fail
13
13
  Find the phase directory:
14
14
  ```bash
15
15
  ls .planning/phases/ | grep -E "^0*[phase]-" | head -1
16
+ # PowerShell: Get-ChildItem .planning/phases/ | Where-Object { $_.Name -match "^0*[phase]-" } | Select-Object -First 1 -ExpandProperty Name
16
17
  PHASE_DIR=".planning/phases/[matched]"
17
18
  ```
18
19
 
@@ -15,7 +15,7 @@ Check if `--repair` flag is present.
15
15
  ## Step 2: Check Project Exists
16
16
 
17
17
  ```bash
18
- python3 -c "import os; print('OK' if os.path.isdir('.planning') else 'MISSING')"
18
+ node -e "const fs=require('fs'); console.log(fs.existsSync('.planning') ? 'OK' : 'MISSING')"
19
19
  ```
20
20
 
21
21
  If `.planning/` doesn't exist:
@@ -32,40 +32,49 @@ Run the following checks and classify each as error, warning, or info:
32
32
 
33
33
  ### Required Files
34
34
  ```bash
35
- python3 -c "import os; print('E002: PROJECT.md not found') if not os.path.exists('.planning/PROJECT.md') else None"
36
- python3 -c "import os; print('E003: ROADMAP.md not found') if not os.path.exists('.planning/ROADMAP.md') else None"
37
- python3 -c "import os; print('E004: STATE.md not found (repairable)') if not os.path.exists('.planning/STATE.md') else None"
38
- python3 -c "import os; print('W003: config.json not found (repairable)') if not os.path.exists('.planning/config.json') else None"
35
+ node -e "const fs=require('fs'); if(!fs.existsSync('.planning/PROJECT.md')) console.log('E002: PROJECT.md not found')"
36
+ node -e "const fs=require('fs'); if(!fs.existsSync('.planning/ROADMAP.md')) console.log('E003: ROADMAP.md not found')"
37
+ node -e "const fs=require('fs'); if(!fs.existsSync('.planning/STATE.md')) console.log('E004: STATE.md not found (repairable)')"
38
+ node -e "const fs=require('fs'); if(!fs.existsSync('.planning/config.json')) console.log('W003: config.json not found (repairable)')"
39
39
  ```
40
40
 
41
41
  ### Config Validity
42
42
  ```bash
43
- cat .planning/config.json | python3 -c "import sys,json; json.load(sys.stdin)" 2>&1 || echo "E005: config.json parse error (repairable)"
43
+ node -e "try{JSON.parse(require('fs').readFileSync('.planning/config.json','utf8'))}catch(e){console.log('E005: config.json parse error (repairable)')}"
44
44
  ```
45
45
 
46
46
  ### State / Roadmap Consistency
47
47
  ```bash
48
- # Check if STATE.md references a phase that exists in ROADMAP.md
49
- CURRENT_PHASE=$(grep -E "^Phase:" .planning/STATE.md 2>/dev/null | head -1 | grep -oE "[0-9]+")
50
- if [ -n "$CURRENT_PHASE" ]; then
51
- grep -q "Phase ${CURRENT_PHASE}:" .planning/ROADMAP.md || echo "W002: STATE.md references phase ${CURRENT_PHASE} not found in roadmap (repairable)"
52
- fi
48
+ node -e "
49
+ const fs=require('fs');
50
+ if(!fs.existsSync('.planning/STATE.md')||!fs.existsSync('.planning/ROADMAP.md'))process.exit(0);
51
+ const state=fs.readFileSync('.planning/STATE.md','utf8');
52
+ const m=state.match(/^Phase:\s*(\d+)/m);
53
+ if(m){
54
+ const roadmap=fs.readFileSync('.planning/ROADMAP.md','utf8');
55
+ if(!roadmap.includes('Phase '+m[1]+':'))console.log('W002: STATE.md references phase '+m[1]+' not found in roadmap (repairable)');
56
+ }
57
+ "
53
58
  ```
54
59
 
55
60
  ### Phase Directory Checks
56
61
  ```bash
57
- # Phases in ROADMAP but no directory
58
- grep -oE "Phase [0-9]+:" .planning/ROADMAP.md | while read phase; do
59
- num=$(echo "$phase" | grep -oE "[0-9]+")
60
- padded=$(printf "%02d" $num)
61
- ls .planning/phases/${padded}-* 2>/dev/null | head -1 || echo "W006: Phase ${num} in roadmap but no directory"
62
- done
63
-
64
- # Phase directories not in ROADMAP
65
- for dir in .planning/phases/*/; do
66
- slug=$(basename "$dir" | sed 's/^[0-9]*-//')
67
- grep -q "$slug" .planning/ROADMAP.md || echo "W007: Directory $(basename $dir) not in roadmap"
68
- done
62
+ node -e "
63
+ const fs=require('fs'),path=require('path');
64
+ if(!fs.existsSync('.planning/ROADMAP.md'))process.exit(0);
65
+ const roadmap=fs.readFileSync('.planning/ROADMAP.md','utf8');
66
+ const phases=[...roadmap.matchAll(/^## Phase (\d+):/mg)].map(m=>m[1]);
67
+ const phasesDir='.planning/phases';
68
+ const dirs=fs.existsSync(phasesDir)?fs.readdirSync(phasesDir):[];
69
+ for(const n of phases){
70
+ const pad=n.padStart(2,'0');
71
+ if(!dirs.some(d=>d.startsWith(pad+'-')))console.log('W006: Phase '+n+' in roadmap but no directory');
72
+ }
73
+ for(const d of dirs){
74
+ const slug=d.replace(/^\d+-/,'');
75
+ if(!roadmap.includes(slug))console.log('W007: Directory '+d+' not in roadmap');
76
+ }
77
+ "
69
78
  ```
70
79
 
71
80
  ### Plans Without Summaries
@@ -79,21 +88,18 @@ done
79
88
  ### Uncommitted Changes
80
89
  ```bash
81
90
  git status --short .planning/ 2>/dev/null | head -10
91
+ # PowerShell: git status --short .planning/ 2>$null | Select-Object -First 10
82
92
  ```
83
93
 
84
94
  ### Config Fields
85
95
  ```bash
86
- # Check for required config keys
87
- python3 -c "
88
- import json
89
- cfg = json.load(open('.planning/config.json'))
90
- missing = []
91
- for key in ['mode', 'granularity', 'model_profile', 'learning_mode']:
92
- if key not in cfg:
93
- missing.append(key)
94
- if missing:
95
- print('W004: config.json missing fields: ' + ', '.join(missing))
96
- " 2>/dev/null
96
+ node -e "
97
+ try{
98
+ const cfg=JSON.parse(require('fs').readFileSync('.planning/config.json','utf8'));
99
+ const missing=['mode','granularity','model_profile','learning_mode'].filter(k=>!(k in cfg));
100
+ if(missing.length)console.log('W004: config.json missing fields: '+missing.join(', '));
101
+ }catch(e){}
102
+ "
97
103
  ```
98
104
 
99
105
  ## Step 4: Format Output
@@ -31,7 +31,7 @@ Validate that the after-phase number is an integer.
31
31
  ## Step 2: Validate
32
32
 
33
33
  ```bash
34
- python3 -c "import os; print('OK' if os.path.exists('.planning/ROADMAP.md') else 'MISSING')"
34
+ node -e "const fs=require('fs'); console.log(fs.existsSync('.planning/ROADMAP.md') ? 'OK' : 'MISSING')"
35
35
  ```
36
36
 
37
37
  Check that phase `[N]` exists in ROADMAP.md:
@@ -56,7 +56,7 @@ Generate slug from description (lowercase, hyphens, max 40 chars).
56
56
  ## Step 4: Create Phase Directory
57
57
 
58
58
  ```bash
59
- mkdir -p ".planning/phases/[N].[M]-[SLUG]"
59
+ node -e "require('fs').mkdirSync('.planning/phases/[N].[M]-[SLUG]',{recursive:true})"
60
60
  ```
61
61
 
62
62
  ## Step 5: Update ROADMAP.md
@@ -13,7 +13,7 @@ The quickest way to answer "where am I and what do I do next?" Works for new use
13
13
  ## Step 1: Check for Project
14
14
 
15
15
  ```bash
16
- python3 -c "import os; print('EXISTS' if os.path.exists('.planning/PROJECT.md') else 'MISSING')"
16
+ node -e "const fs=require('fs'); console.log(fs.existsSync('.planning/PROJECT.md') ? 'EXISTS' : 'MISSING')"
17
17
  ```
18
18
 
19
19
  **If MISSING** — no project initialized yet. Display:
@@ -50,7 +50,7 @@ cat .planning/ROADMAP.md
50
50
  Find the 2–3 most recent SUMMARY.md files:
51
51
 
52
52
  ```bash
53
- find .planning -name "*-SUMMARY.md" -type f 2>/dev/null | xargs ls -t 2>/dev/null | head -3
53
+ node -e "const fs=require('fs'),path=require('path');function find(d){let r=[];try{for(const e of fs.readdirSync(d,{withFileTypes:true})){const f=path.join(d,e.name);r=r.concat(e.isDirectory()?find(f):e.name.endsWith('-SUMMARY.md')?[f]:[]);}}catch(e){}return r;}const files=find('.planning').map(f=>({f,t:fs.statSync(f).mtimeMs})).sort((a,b)=>b.t-a.t).slice(0,3).map(x=>x.f);files.forEach(f=>console.log(f));"
54
54
  ```
55
55
 
56
56
  Read each for a one-liner summary of what was accomplished.
@@ -33,7 +33,7 @@ Wait for response before continuing.
33
33
  ## Step 2: Create Output Directory
34
34
 
35
35
  ```bash
36
- mkdir -p .planning/codebase
36
+ node -e "require('fs').mkdirSync('.planning/codebase',{recursive:true})"
37
37
  ```
38
38
 
39
39
  Expected output files:
@@ -15,7 +15,9 @@ A structured learning retrospective after a milestone ships. Five focused questi
15
15
  Read the milestone that just shipped:
16
16
  ```bash
17
17
  ls .planning/milestones/ | sort -V | tail -3
18
+ # PowerShell: Get-ChildItem .planning/milestones/ | Sort-Object Name | Select-Object -Last 3
18
19
  cat .planning/milestones/[VERSION]-ROADMAP.md 2>/dev/null | head -60
20
+ # PowerShell: Get-Content .planning/milestones/[VERSION]-ROADMAP.md -ErrorAction SilentlyContinue | Select-Object -First 60
19
21
  ```
20
22
 
21
23
  Read all phase SUMMARY.md files from this milestone:
@@ -48,6 +48,7 @@ Follow the thread. When you have enough to write clear goals, ask for confirmati
48
48
  Read the last version from `.planning/milestones/`:
49
49
  ```bash
50
50
  ls .planning/milestones/ | grep -E "^v[0-9]" | sort -V | tail -3
51
+ # PowerShell: Get-ChildItem .planning/milestones/ | Where-Object { $_.Name -match '^v[0-9]' } | Sort-Object Name | Select-Object -Last 3
51
52
  ```
52
53
 
53
54
  Propose the next version (e.g., `v1.0 → v1.1`, or `v2.0` for a major scope change). Confirm with user or let them specify.
@@ -6,16 +6,29 @@ description: Initialize a new project — questioning → research → requireme
6
6
 
7
7
  Initialize a new project with full context gathering, optional research, requirements scoping, and roadmap creation. This is the most leveraged moment in any project — deep questioning now means better plans, better execution, better outcomes.
8
8
 
9
+ > **This workflow has 9 mandatory steps. You must complete every step in order. Do not skip, defer, or abbreviate any step. Check each one off as you complete it:**
10
+ >
11
+ > - [ ] Step 1 — Setup & codebase check
12
+ > - [ ] Step 1b — Existing codebase scan (if applicable)
13
+ > - [ ] Step 2 — Configuration questions
14
+ > - [ ] Step 3 — Deep questioning (4 exchanges)
15
+ > - [ ] Step 4 — Write and confirm PROJECT.md
16
+ > - [ ] Step 5 — Research decision (ask user, wait for answer)
17
+ > - [ ] Step 6 — Define requirements (interactive)
18
+ > - [ ] Step 7 — Create and approve roadmap
19
+ > - [ ] Step 8 — Generate AGENTS.md ← **mandatory, never skip**
20
+ > - [ ] Step 9 — Done banner + next step
21
+
9
22
  ## Step 1: Setup
10
23
 
11
- <!-- LEARNSHIP_PLATFORM_LABEL -->
24
+ You are running on **Windsurf**. Platform config directory: `.windsurf/`
12
25
 
13
26
  > **Routing protocol suspended.** While this workflow is running, every user message is an answer to a workflow question — not a task to route. Do NOT apply the request routing protocol until `/new-project` is fully complete and `.planning/PROJECT.md` exists.
14
27
 
15
28
  Check if `.planning/PROJECT.md` already exists:
16
29
 
17
30
  ```bash
18
- python3 -c "import os; print('EXISTS' if os.path.exists('.planning/PROJECT.md') else 'NEW')"
31
+ node -e "const fs=require('fs'); console.log(fs.existsSync('.planning/PROJECT.md') ? 'EXISTS' : 'NEW')"
19
32
  ```
20
33
 
21
34
  **If EXISTS:** Stop. Project already initialized. Use the `progress` workflow to see where you are.
@@ -23,11 +36,11 @@ python3 -c "import os; print('EXISTS' if os.path.exists('.planning/PROJECT.md')
23
36
  **Check for an existing codebase:**
24
37
 
25
38
  ```bash
26
- python3 -c "
27
- import os, pathlib
28
- files = [p for p in pathlib.Path('.').rglob('*') if p.is_file() and not any(x in p.parts for x in ['.git', 'node_modules', '.planning', '__pycache__', '.venv'])]
29
- print('HAS_CODE' if len(files) > 2 else 'BLANK')
30
- print(f'{len(files)} files')
39
+ node -e "
40
+ const fs=require('fs'),path=require('path');
41
+ function walk(dir,skip){let n=0;try{for(const e of fs.readdirSync(dir,{withFileTypes:true})){const f=path.join(dir,e.name);if(skip.some(s=>f.includes(s)))continue;n+=e.isDirectory()?walk(f,skip):1;}}catch(e){}return n;}
42
+ const n=walk('.',['/.git/','node_modules','.planning','__pycache__','.venv']);
43
+ console.log(n>2?'HAS_CODE':'BLANK');console.log(n+' files');
31
44
  "
32
45
  ```
33
46
 
@@ -36,7 +49,7 @@ print(f'{len(files)} files')
36
49
  Check if git is initialized:
37
50
 
38
51
  ```bash
39
- python3 -c "import os; print('HAS_GIT' if os.path.isdir('.git') else 'NO_GIT')"
52
+ node -e "const fs=require('fs'); console.log(fs.existsSync('.git') ? 'HAS_GIT' : 'NO_GIT')"
40
53
  ```
41
54
 
42
55
  **If NO_GIT:**
@@ -46,12 +59,12 @@ git init
46
59
 
47
60
  Add the platform config directory to `.gitignore` so AI platform files are not tracked in the project repo:
48
61
  ```bash
49
- <!-- LEARNSHIP_GITIGNORE_CMD -->
62
+ grep -q '.windsurf/' .gitignore 2>/dev/null || echo '.windsurf/' >> .gitignore
50
63
  ```
51
64
 
52
65
  Create the planning directory:
53
66
  ```bash
54
- mkdir -p .planning/research
67
+ node -e "require('fs').mkdirSync('.planning/research',{recursive:true})"
55
68
  ```
56
69
 
57
70
  ## Step 1b: Existing Codebase Scan (only if EXISTING_CODEBASE = true)
@@ -60,6 +73,7 @@ If `EXISTING_CODEBASE = true`, do a quick structural scan before questioning so
60
73
 
61
74
  ```bash
62
75
  find . -maxdepth 3 -not -path './.git/*' -not -path './node_modules/*' -not -path './.planning/*' -not -path './__pycache__/*' -not -path './.venv/*' | sort | head -40
76
+ # PowerShell: Get-ChildItem -Recurse -Depth 3 | Where-Object { $_.FullName -notmatch '\.git|node_modules|\.planning|__pycache__|\.venv' } | Select-Object -First 40
63
77
  ```
64
78
 
65
79
  Note the tech stack, key directories, and any README content internally. Use this ONLY to ask sharper follow-up questions — never to infer the user's intent or skip ceremony steps.
@@ -92,7 +106,9 @@ Ask: "Which workflow agents should be enabled?"
92
106
  - **Plan Check** (recommended) — Verify plans achieve their goals before execution
93
107
  - **Verifier** (recommended) — Confirm deliverables match phase goals after execution
94
108
 
95
- <!-- LEARNSHIP_PARALLEL_BLOCK -->
109
+ **Group D — Parallel execution:**
110
+
111
+ Windsurf does not support real subagents. Parallelization is automatically set to `false`.
96
112
 
97
113
  Ask: "Commit planning docs to git?"
98
114
  - **Yes** (recommended) — Planning docs tracked in version control
@@ -208,15 +224,15 @@ If user requests changes: update PROJECT.md, show the full file again, re-ask th
208
224
  git add .planning/PROJECT.md && git commit -m "docs: initialize project"
209
225
  ```
210
226
 
211
- > 🛑 STOP. Do not proceed to Step 5 until you have asked the research question below AND received the user's explicit answer to it.
227
+ > 🛑 STOP — **Step 4 complete. You MUST now ask the research question (Step 5) before writing any other file.** Do not write REQUIREMENTS.md. Do not write ROADMAP.md. Do not proceed to any other step. The next action is to ask the user exactly one question: whether to research the domain first.
212
228
 
213
229
  ## Step 5: Research Decision
214
230
 
215
- Ask: "Research the domain ecosystem before defining requirements?"
231
+ Ask: **"Before I write the requirements — do you want me to research the domain ecosystem first?"**
216
232
  - **Research first** (recommended) — Discover standard stacks, expected features, architecture patterns
217
233
  - **Skip research** — I know this domain well, go straight to requirements
218
234
 
219
- > 🛑 STOP. Wait for the user's explicit choice before continuing. Do not default to "Research first" wait for the user's actual reply.
235
+ > 🛑 STOP. Wait for the user's explicit choice. Do not default to either option. Do not write REQUIREMENTS.md yet. Do not proceed until the user replies.
220
236
 
221
237
  **If Research first:**
222
238
 
@@ -325,6 +341,8 @@ Ask for approval:
325
341
  git add .planning/ROADMAP.md .planning/STATE.md .planning/REQUIREMENTS.md && git commit -m "docs: create roadmap ([N] phases)"
326
342
  ```
327
343
 
344
+ > 🛑 STOP — **Step 7 complete. You MUST now generate AGENTS.md (Step 8) before anything else.** Do not display the done banner. Do not suggest next steps. Do not end the workflow. The roadmap is approved — AGENTS.md is next.
345
+
328
346
  ## Step 8: Generate AGENTS.md
329
347
 
330
348
  > **🔴 MANDATORY — This step must always be completed. Do not skip it, do not defer it, do not move to Step 9 without writing AGENTS.md to the project root. AGENTS.md is the persistent memory file that every future session depends on.**
@@ -336,6 +354,7 @@ Fill in the placeholder sections using information gathered in this session:
336
354
  **Project Structure** — derive from the project description and any existing directories:
337
355
  ```bash
338
356
  find . -maxdepth 2 -not -path './.git/*' -not -path './node_modules/*' -not -path './.planning/*' -type d | sort | head -20
357
+ # PowerShell: Get-ChildItem -Directory -Recurse -Depth 2 | Where-Object { $_.FullName -notmatch '\.git|node_modules|\.planning' } | Select-Object -First 20
339
358
  ```
340
359
 
341
360
  Populate the `## Project Structure` tree with real directories and one-line descriptions.
@@ -378,7 +397,10 @@ Files created:
378
397
  - .planning/config.json
379
398
  [- .planning/research/ (if research was run)]
380
399
 
381
- ▶ Next: discuss-phase 1 plan-phase 1 execute-phase 1
400
+ ▶ Next: `/discuss-phase 1` **start here, not `/plan-phase`**
401
+
402
+ The phase loop is: `discuss-phase` → `plan-phase` → `execute-phase` → `verify-work`
403
+ `discuss-phase` is mandatory before planning — it captures your intent and writes the CONTEXT.md that plan-phase depends on. Skipping it means planning without context.
382
404
 
383
405
  > **Platform detected:** `[PLATFORM]` — parallelization is `[true/false]`
384
406
  ```
@@ -13,7 +13,7 @@ Reads project state and runs the right next workflow automatically. No need to r
13
13
  ## Step 1: Check for Project
14
14
 
15
15
  ```bash
16
- python3 -c "import os; print('EXISTS' if os.path.exists('.planning/PROJECT.md') else 'MISSING')"
16
+ node -e "const fs=require('fs'); console.log(fs.existsSync('.planning/PROJECT.md') ? 'EXISTS' : 'MISSING')"
17
17
  ```
18
18
 
19
19
  **If MISSING:**
@@ -12,7 +12,7 @@ Create a `.continue-here.md` handoff file that captures complete work state. Ena
12
12
 
13
13
  Find the most recently active phase:
14
14
  ```bash
15
- find .planning/phases -name "*-PLAN.md" -type f 2>/dev/null | xargs ls -t 2>/dev/null | head -1
15
+ node -e "const fs=require('fs'),path=require('path');function find(d){let r=[];try{for(const e of fs.readdirSync(d,{withFileTypes:true})){const f=path.join(d,e.name);r=r.concat(e.isDirectory()?find(f):e.name.endsWith('-PLAN.md')?[f]:[]);}}catch(e){}return r;}const files=find('.planning/phases').map(f=>({f,t:fs.statSync(f).mtimeMs})).sort((a,b)=>b.t-a.t);if(files[0])console.log(files[0].f);"
16
16
  ```
17
17
 
18
18
  Extract the phase directory name from the result.
@@ -13,6 +13,7 @@ Create all phases needed to close gaps identified by `audit-milestone`. One work
13
13
  Find the most recent audit file:
14
14
  ```bash
15
15
  ls -t .planning/*-MILESTONE-AUDIT.md 2>/dev/null | head -1
16
+ # PowerShell: Get-ChildItem .planning/*-MILESTONE-AUDIT.md -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 1
16
17
  ```
17
18
 
18
19
  If no audit file exists or status is `passed`:
@@ -74,6 +75,7 @@ Gap: Flow "User stays logged in" broken
74
75
  Find the highest existing phase number:
75
76
  ```bash
76
77
  ls .planning/phases/ | grep -E "^[0-9]" | sort -V | tail -1
78
+ # PowerShell: Get-ChildItem .planning/phases/ | Where-Object { $_.Name -match '^[0-9]' } | Sort-Object Name | Select-Object -Last 1
77
79
  ```
78
80
 
79
81
  Gap closure phases continue from the highest existing phase + 1.
@@ -131,7 +133,7 @@ For each unsatisfied requirement being addressed:
131
133
 
132
134
  ```bash
133
135
  for each gap closure phase:
134
- mkdir -p ".planning/phases/[NN]-[slug]"
136
+ node -e "require('fs').mkdirSync('.planning/phases/[NN]-[slug]',{recursive:true})"
135
137
  done
136
138
  ```
137
139
 
@@ -25,7 +25,7 @@ cat .planning/config.json
25
25
 
26
26
  Create the phase directory if it doesn't exist:
27
27
  ```bash
28
- mkdir -p ".planning/phases/[padded_phase]-[phase_slug]"
28
+ node -e "require('fs').mkdirSync('.planning/phases/[padded_phase]-[phase_slug]',{recursive:true})"
29
29
  ```
30
30
 
31
31
  Check what already exists:
@@ -9,7 +9,7 @@ Check where you are in the project, what's been done, and what comes next.
9
9
  ## Step 1: Check for Planning Structure
10
10
 
11
11
  ```bash
12
- python3 -c "import os; print('EXISTS' if os.path.exists('.planning/PROJECT.md') else 'MISSING')"
12
+ node -e "const fs=require('fs'); console.log(fs.existsSync('.planning/PROJECT.md') ? 'EXISTS' : 'MISSING')"
13
13
  ```
14
14
 
15
15
  If `.planning/` doesn't exist: stop — run `new-project` to initialize.
@@ -24,7 +24,7 @@ cat .planning/ROADMAP.md
24
24
 
25
25
  Find the 2-3 most recent SUMMARY.md files:
26
26
  ```bash
27
- find .planning -name "*-SUMMARY.md" -type f 2>/dev/null | xargs ls -t 2>/dev/null | head -3
27
+ node -e "const fs=require('fs'),path=require('path');function find(d){let r=[];try{for(const e of fs.readdirSync(d,{withFileTypes:true})){const f=path.join(d,e.name);r=r.concat(e.isDirectory()?find(f):e.name.endsWith('-SUMMARY.md')?[f]:[]);}}catch(e){}return r;}const files=find('.planning').map(f=>({f,t:fs.statSync(f).mtimeMs})).sort((a,b)=>b.t-a.t).slice(0,3).map(x=>x.f);files.forEach(f=>console.log(f));"
28
28
  ```
29
29
 
30
30
  Read each to extract what was recently accomplished (one-liner per plan).
@@ -34,7 +34,7 @@ Display banner based on active flags:
34
34
 
35
35
  Check that a project exists:
36
36
  ```bash
37
- python3 -c "import os; print('OK' if os.path.exists('.planning/PROJECT.md') else 'MISSING')"
37
+ node -e "const fs=require('fs'); console.log(fs.existsSync('.planning/PROJECT.md') ? 'OK' : 'MISSING')"
38
38
  ```
39
39
 
40
40
  If PROJECT.md missing: stop — run `new-project` first. Quick tasks require an active project.
@@ -46,13 +46,15 @@ Generate a slug from the description (lowercase, hyphens, max 40 chars).
46
46
  Find the next task number:
47
47
  ```bash
48
48
  ls .planning/quick/ 2>/dev/null | grep -E "^[0-9]+" | sort -n | tail -1
49
+ # PowerShell: Get-ChildItem .planning/quick/ -ErrorAction SilentlyContinue | Where-Object { $_.Name -match '^[0-9]+' } | Sort-Object Name | Select-Object -Last 1
49
50
  ```
50
51
 
51
52
  Set `NEXT_NUM` to the next available number (001, 002, etc.).
52
53
 
53
54
  Create task directory:
54
55
  ```bash
55
- mkdir -p ".planning/quick/${NEXT_NUM}-${SLUG}"
56
+ node -e "require('fs').mkdirSync('.planning/quick/${NEXT_NUM}-${SLUG}',{recursive:true})"
57
+ # PowerShell: New-Item -ItemType Directory -Force -Path ".planning/quick/${NEXT_NUM}-${SLUG}"
56
58
  ```
57
59
 
58
60
  Report: "Creating quick task ${NEXT_NUM}: ${DESCRIPTION}"
@@ -130,7 +132,7 @@ If `--full`: also include `must_haves` in plan frontmatter (truths, artifacts, k
130
132
 
131
133
  Verify plan was created (substitute actual NEXT_NUM and SLUG values):
132
134
  ```bash
133
- python3 -c "import os; print('OK' if os.path.exists('.planning/quick/NEXT_NUM-SLUG/NEXT_NUM-PLAN.md') else 'MISSING')"
135
+ node -e "const fs=require('fs'); console.log(fs.existsSync('.planning/quick/NEXT_NUM-SLUG/NEXT_NUM-PLAN.md') ? 'OK' : 'MISSING')"
134
136
  ```
135
137
 
136
138
  ## Step 5: Plan Check (only with `--full`)
@@ -168,10 +168,10 @@ curl -s -X POST \
168
168
  \"tag_name\": \"vX.Y.Z\",
169
169
  \"target_commitish\": \"main\",
170
170
  \"name\": \"vX.Y.Z — [Short title]\",
171
- \"body\": $(echo "$RELEASE_NOTES" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read()))'),
171
+ \"body\": $(node -e "process.stdout.write(JSON.stringify(require('fs').readFileSync('/dev/stdin','utf8')))" <<< "$RELEASE_NOTES"),
172
172
  \"draft\": false,
173
173
  \"prerelease\": false
174
- }" | python3 -c "import sys,json; r=json.load(sys.stdin); print(r.get('html_url', r.get('message','ERROR')))"
174
+ }" | node -e "let d='';process.stdin.on('data',c=>d+=c).on('end',()=>{const r=JSON.parse(d);console.log(r.html_url||r.message||'ERROR');})"
175
175
  ```
176
176
 
177
177
  If successful, the release URL is printed.
@@ -198,6 +198,7 @@ git remote -v
198
198
  ```bash
199
199
  git log --oneline public-main -3 # new release commit at top
200
200
  git tag --sort=-version:refname | head -5 # new tag at top
201
+ # PowerShell: git tag --sort=-version:refname | Select-Object -First 5
201
202
  ```
202
203
 
203
204
  Open the release URL printed in Step 11 to confirm it looks correct on GitHub.
@@ -22,7 +22,7 @@ Example: remove-phase 7
22
22
 
23
23
  Check roadmap exists:
24
24
  ```bash
25
- python3 -c "import os; print('OK' if os.path.exists('.planning/ROADMAP.md') else 'MISSING')"
25
+ node -e "const fs=require('fs'); console.log(fs.existsSync('.planning/ROADMAP.md') ? 'OK' : 'MISSING')"
26
26
  ```
27
27
 
28
28
  ## Step 2: Verify the Phase is Future
@@ -13,7 +13,7 @@ Run standalone domain research for a phase. Useful when the domain is unfamiliar
13
13
  ## Step 1: Validate Phase
14
14
 
15
15
  ```bash
16
- python3 -c "import os; print('OK' if os.path.exists('.planning/ROADMAP.md') else 'MISSING')"
16
+ node -e "const fs=require('fs'); console.log(fs.existsSync('.planning/ROADMAP.md') ? 'OK' : 'MISSING')"
17
17
  ```
18
18
 
19
19
  Find phase `[N]` in ROADMAP.md:
@@ -9,9 +9,9 @@ Instantly restore full project context. Use when starting a new session, returni
9
9
  ## Step 1: Check Planning Structure
10
10
 
11
11
  ```bash
12
- python3 -c "import os; print('HAS_STATE' if os.path.exists('.planning/STATE.md') else 'NO_STATE')"
13
- python3 -c "import os; print('HAS_PROJECT' if os.path.exists('.planning/PROJECT.md') else 'NO_PROJECT')"
14
- python3 -c "import os; print('HAS_ROADMAP' if os.path.exists('.planning/ROADMAP.md') else 'NO_ROADMAP')"
12
+ node -e "const fs=require('fs'); console.log(fs.existsSync('.planning/STATE.md') ? 'HAS_STATE' : 'NO_STATE')"
13
+ node -e "const fs=require('fs'); console.log(fs.existsSync('.planning/PROJECT.md') ? 'HAS_PROJECT' : 'NO_PROJECT')"
14
+ node -e "const fs=require('fs'); console.log(fs.existsSync('.planning/ROADMAP.md') ? 'HAS_ROADMAP' : 'NO_ROADMAP')"
15
15
  ```
16
16
 
17
17
  If nothing exists: stop — run `new-project` to start a project.
@@ -51,12 +51,12 @@ Stop.
51
51
  Update the `model_profile` field in `.planning/config.json`:
52
52
 
53
53
  ```bash
54
- python3 -c "
55
- import json
56
- cfg = json.load(open('.planning/config.json'))
57
- cfg['model_profile'] = '[profile]'
58
- json.dump(cfg, open('.planning/config.json', 'w'), indent=2)
59
- print('Updated.')
54
+ node -e "
55
+ const fs=require('fs');
56
+ const cfg=JSON.parse(fs.readFileSync('.planning/config.json','utf8'));
57
+ cfg.model_profile='[profile]';
58
+ fs.writeFileSync('.planning/config.json',JSON.stringify(cfg,null,2));
59
+ console.log('Updated.');
60
60
  "
61
61
  ```
62
62
 
@@ -11,7 +11,7 @@ Interactive configuration editor for the current project. Updates `.planning/con
11
11
  ## Step 1: Ensure Config Exists
12
12
 
13
13
  ```bash
14
- python3 -c "import os; print('exists' if os.path.exists('.planning/config.json') else 'missing')"
14
+ node -e "const fs=require('fs'); console.log(fs.existsSync('.planning/config.json') ? 'exists' : 'missing')"
15
15
  ```
16
16
 
17
17
  If missing, create from template:
@@ -97,7 +97,7 @@ Confirm both clones succeeded — `SKILL.md` must exist in `$AGENTIC_LEARN_TMP`
97
97
 
98
98
  ```bash
99
99
  BACKUP_DIR="$(pwd)/.windsurf/skills/.upstream-backup-$(date +%Y%m%d-%H%M%S)"
100
- mkdir -p "$BACKUP_DIR"
100
+ node -e "require('fs').mkdirSync('$BACKUP_DIR',{recursive:true})"
101
101
 
102
102
  cp -r "$(pwd)/.windsurf/skills/agentic-learning" "$BACKUP_DIR/agentic-learning"
103
103
  cp -r "$(pwd)/.windsurf/skills/impeccable" "$BACKUP_DIR/impeccable"
@@ -27,7 +27,7 @@ find .planning/ -name "*.md" | sort
27
27
 
28
28
  Read the most recent SUMMARY.md files (last 3 phases):
29
29
  ```bash
30
- ls -t .planning/phases/*/*-SUMMARY.md 2>/dev/null | head -6
30
+ node -e "const fs=require('fs'),path=require('path');function find(d){let r=[];try{for(const e of fs.readdirSync(d,{withFileTypes:true})){const f=path.join(d,e.name);r=r.concat(e.isDirectory()?find(f):e.name.endsWith('-SUMMARY.md')?[f]:[]);}}catch(e){}return r;}const files=find('.planning/phases').map(f=>({f,t:fs.statSync(f).mtimeMs})).sort((a,b)=>b.t-a.t).slice(0,6).map(x=>x.f);files.forEach(f=>console.log(f));"
31
31
  ```
32
32
 
33
33
  Check git status:
@@ -35,6 +35,7 @@ for loc in \
35
35
  "$HOME/favio/learnship" \
36
36
  "$HOME/learnship" \
37
37
  "$(find $HOME -name "install.sh" -path "*/learnship/*" 2>/dev/null | head -1 | xargs dirname 2>/dev/null)"; do
38
+ # PowerShell: (Get-ChildItem $HOME -Recurse -Filter install.sh -ErrorAction SilentlyContinue | Where-Object { $_.FullName -match 'learnship' } | Select-Object -First 1).DirectoryName
38
39
  test -d "$loc/.windsurf/workflows" && SOURCE_DIR="$loc" && break
39
40
  done
40
41
  ```
@@ -93,7 +94,7 @@ Stop.
93
94
  For any workflow file that exists in install dir AND differs from source AND differs from the current source (meaning you modified it):
94
95
 
95
96
  ```bash
96
- mkdir -p "$INSTALL_DIR/local-patches"
97
+ node -e "require('fs').mkdirSync('$INSTALL_DIR/local-patches',{recursive:true})"
97
98
  ```
98
99
 
99
100
  For each locally modified file:
@@ -22,7 +22,7 @@ If `nyquist_validation: false`: stop — "Validation is disabled. Enable it in `
22
22
  ## Step 2: Validate Phase
23
23
 
24
24
  ```bash
25
- python3 -c "import os; print('OK' if os.path.exists('.planning/ROADMAP.md') else 'MISSING')"
25
+ node -e "const fs=require('fs'); console.log(fs.existsSync('.planning/ROADMAP.md') ? 'OK' : 'MISSING')"
26
26
  ```
27
27
 
28
28
  Determine the phase directory:
@@ -61,6 +61,7 @@ Extract:
61
61
  find . \( -name "jest.config.*" -o -name "vitest.config.*" -o -name "pytest.ini" -o -name "pyproject.toml" \) -not -path "*/node_modules/*" 2>/dev/null
62
62
 
63
63
  find . \( -name "*.test.*" -o -name "*.spec.*" -o -name "test_*.py" \) -not -path "*/node_modules/*" 2>/dev/null | head -20
64
+ # PowerShell: Get-ChildItem -Recurse | Where-Object { $_.Name -match '\.test\.|spec\.|^test_.*\.py' -and $_.FullName -notmatch 'node_modules' } | Select-Object -First 20
64
65
  ```
65
66
 
66
67
  Identify: test framework, how to run tests, existing test file patterns.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "learnship",
3
- "version": "1.9.20",
3
+ "version": "1.9.22",
4
4
  "description": "Learn as you build. Build with intent. — A multi-platform agentic engineering system for Windsurf, Claude Code, Cursor, OpenCode, Gemini CLI, and Codex: spec-driven workflows, integrated learning, and production-grade design.",
5
5
  "keywords": [
6
6
  "agentic",
@@ -40,7 +40,7 @@ Configuration options for `.planning/` directory behavior.
40
40
 
41
41
  ```bash
42
42
  # Read commit_docs from config.json
43
- COMMIT_DOCS=$(python3 -c "import json; c=json.load(open('.planning/config.json')); print(c.get('planning',{}).get('commit_docs','true'))" 2>/dev/null || echo 'true')
43
+ COMMIT_DOCS=$(node -e "try{const c=JSON.parse(require('fs').readFileSync('.planning/config.json','utf8'));process.stdout.write(String((c.planning||{}).commit_docs??'true'));}catch(e){process.stdout.write('true');}" 2>/dev/null || echo 'true')
44
44
  ```
45
45
 
46
46
  **Auto-detection:** If `.planning/` is gitignored, treat `commit_docs` as `false` regardless of config.json. This prevents git errors.
@@ -139,7 +139,7 @@ To use uncommitted mode:
139
139
 
140
140
  Read config directly:
141
141
  ```bash
142
- python3 -c "import json; c=json.load(open('.planning/config.json')); g=c.get('git',{}); print(g.get('branching_strategy','none'), g.get('phase_branch_template','phase-{phase}-{slug}'), g.get('milestone_branch_template','{milestone}-{slug}'))"
142
+ node -e "const c=JSON.parse(require('fs').readFileSync('.planning/config.json','utf8')),g=c.git||{};console.log(g.branching_strategy||'none',g.phase_branch_template||'phase-{phase}-{slug}',g.milestone_branch_template||'{milestone}-{slug}')"
143
143
  ```
144
144
 
145
145
  **Branch creation:**