prjct-cli 0.55.1 → 0.55.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.
@@ -4,36 +4,133 @@ allowed-tools: [Read, Write, Bash, AskUserQuestion]
4
4
 
5
5
  # p. pause "$ARGUMENTS"
6
6
 
7
+ ## Step 1: Resolve Project Paths
8
+
7
9
  ```bash
8
- prjct context pause
10
+ # Get projectId from local config
11
+ cat .prjct/prjct.config.json | grep -o '"projectId"[[:space:]]*:[[:space:]]*"[^"]*"' | cut -d'"' -f4
12
+ ```
13
+
14
+ Set `globalPath = ~/.prjct-cli/projects/{projectId}`
15
+
16
+ ## Step 2: Read Current State
17
+
18
+ READ `{globalPath}/storage/state.json`
19
+
9
20
  ```
21
+ IF no currentTask OR currentTask is null:
22
+ OUTPUT: "No active task to pause."
23
+ STOP
10
24
 
11
- IF no `currentTask` "No active task to pause"
12
- IF already paused → Show paused state, STOP
25
+ IF currentTask.status == "paused":
26
+ OUTPUT:
27
+ """
28
+ ⏸️ Already paused: {currentTask.description}
13
29
 
14
- IF no reason provided, Ask: "Why pausing?"
15
- - Blocked (waiting on external)
16
- - Switching task
17
- - Taking a break
18
- - Researching
30
+ To resume: `p. resume`
31
+ """
32
+ STOP
33
+ ```
19
34
 
20
- IF blocked Ask what's blocking
35
+ ## Step 3: Get Pause Reason
21
36
 
22
- Calculate duration since `startedAt`
37
+ ```
38
+ IF $ARGUMENTS is empty:
39
+ AskUserQuestion:
40
+ question: "Why are you pausing?"
41
+ header: "Pause"
42
+ options:
43
+ - label: "Blocked (waiting on external)"
44
+ description: "Waiting for review, dependency, or other team"
45
+ - label: "Switching task"
46
+ description: "Need to work on something else"
47
+ - label: "Taking a break"
48
+ description: "Stepping away temporarily"
49
+ - label: "Researching"
50
+ description: "Need to investigate before continuing"
51
+
52
+ SET pauseReason = selected option
53
+
54
+ IF pauseReason == "Blocked":
55
+ ASK: "What's blocking you?"
56
+ SET blockingReason = response
57
+ ELSE:
58
+ SET pauseReason = $ARGUMENTS
59
+ ```
60
+
61
+ ## Step 4: Calculate Duration
62
+
63
+ ```bash
64
+ # Get current timestamp
65
+ node -e "console.log(new Date().toISOString())"
66
+ ```
67
+
68
+ Calculate time elapsed since `currentTask.startedAt` (or `resumedAt` if it exists).
69
+
70
+ ## Step 5: Update State
71
+
72
+ READ current state, then update:
73
+
74
+ ```
75
+ pausedTask = currentTask
76
+ pausedTask.status = "paused"
77
+ pausedTask.pausedAt = "{timestamp}"
78
+ pausedTask.pauseReason = "{pauseReason}"
79
+ pausedTask.blockingReason = "{blockingReason if set}"
80
+ pausedTask.activeTime = "{duration worked so far}"
81
+
82
+ state.pausedTasks = state.pausedTasks || []
83
+ state.pausedTasks.unshift(pausedTask) # Add to front
84
+ state.currentTask = null
85
+ ```
86
+
87
+ WRITE `{globalPath}/storage/state.json`:
88
+ ```json
89
+ {
90
+ "currentTask": null,
91
+ "pausedTasks": [
92
+ {
93
+ "id": "{task.id}",
94
+ "description": "{task.description}",
95
+ "type": "{task.type}",
96
+ "status": "paused",
97
+ "startedAt": "{task.startedAt}",
98
+ "pausedAt": "{timestamp}",
99
+ "pauseReason": "{pauseReason}",
100
+ "blockingReason": "{blockingReason or null}",
101
+ "activeTime": "{duration}",
102
+ "subtasks": [...],
103
+ "currentSubtaskIndex": {task.currentSubtaskIndex},
104
+ "parentDescription": "{task.parentDescription}",
105
+ "branch": "{task.branch}",
106
+ "linearId": "{task.linearId or null}"
107
+ },
108
+ ...existing paused tasks
109
+ ],
110
+ "previousTask": {...}
111
+ }
112
+ ```
113
+
114
+ ## Step 6: Log Event
115
+
116
+ APPEND to `{globalPath}/memory/events.jsonl`:
117
+ ```json
118
+ {"type":"task_paused","taskId":"{id}","description":"{description}","reason":"{pauseReason}","timestamp":"{timestamp}","activeTime":"{duration}"}
119
+ ```
120
+
121
+ ---
23
122
 
24
- UPDATE `{globalPath}/storage/state.json`:
25
- - `pausedTask` = currentTask with `status: "paused"`, `pausedAt`, `pauseReason`
26
- - `currentTask` = null
123
+ ## Output
27
124
 
28
- **Output**:
29
125
  ```
30
- ⏸️ Paused: {task}
126
+ ⏸️ Paused: {task description}
31
127
 
32
- Duration: {time}
33
- Reason: {reason}
128
+ Duration: {time worked}
129
+ Reason: {pauseReason}
130
+ {IF blockingReason: "Blocked by: {blockingReason}"}
34
131
 
35
132
  Next:
36
133
  - Resume → `p. resume`
37
134
  - New task → `p. task "description"`
38
- - Complete without resuming → `p. done`
135
+ - Fix bug → `p. bug "description"`
39
136
  ```
@@ -4,31 +4,168 @@ allowed-tools: [Read, Write, Bash, Glob, AskUserQuestion]
4
4
 
5
5
  # p. resume
6
6
 
7
+ ## Step 1: Resolve Project Paths
8
+
9
+ ```bash
10
+ # Get projectId from local config
11
+ cat .prjct/prjct.config.json | grep -o '"projectId"[[:space:]]*:[[:space:]]*"[^"]*"' | cut -d'"' -f4
12
+ ```
13
+
14
+ Set `globalPath = ~/.prjct-cli/projects/{projectId}`
15
+
16
+ ## Step 2: Read Current State
17
+
18
+ READ `{globalPath}/storage/state.json`
19
+
20
+ ```
21
+ IF currentTask exists AND currentTask.status == "active":
22
+ OUTPUT:
23
+ """
24
+ Already active: {currentTask.description}
25
+
26
+ To pause: `p. pause`
27
+ To complete: `p. done`
28
+ """
29
+ STOP
30
+
31
+ IF (no pausedTasks OR pausedTasks is empty):
32
+ OUTPUT: "Nothing to resume. Use `p. task` to start a new task."
33
+ STOP
34
+ ```
35
+
36
+ ## Step 3: Select Task to Resume
37
+
38
+ ```
39
+ IF pausedTasks.length == 1:
40
+ taskToResume = pausedTasks[0]
41
+ ELSE:
42
+ # Multiple paused tasks - let user choose
43
+ Show list:
44
+ """
45
+ Multiple paused tasks:
46
+
47
+ 1. {pausedTasks[0].description} (paused {time ago})
48
+ Reason: {pauseReason}
49
+ 2. {pausedTasks[1].description} (paused {time ago})
50
+ Reason: {pauseReason}
51
+ ...
52
+ """
53
+
54
+ AskUserQuestion:
55
+ question: "Which task do you want to resume?"
56
+ header: "Resume"
57
+ options:
58
+ - label: "{pausedTasks[0].description}"
59
+ description: "Paused {time ago} - {pauseReason}"
60
+ - label: "{pausedTasks[1].description}"
61
+ description: "Paused {time ago} - {pauseReason}"
62
+ ... (up to 4 options)
63
+
64
+ taskToResume = selected task
65
+ ```
66
+
67
+ ## Step 4: Calculate Away Duration
68
+
69
+ ```bash
70
+ # Get current timestamp
71
+ node -e "console.log(new Date().toISOString())"
72
+ ```
73
+
74
+ Calculate time since `taskToResume.pausedAt`.
75
+
76
+ ## Step 5: Switch to Task Branch (if needed)
77
+
7
78
  ```bash
8
- prjct context resume
79
+ git branch --show-current
80
+ ```
81
+
82
+ ```
83
+ IF taskToResume.branch exists AND currentBranch != taskToResume.branch:
84
+ # Check for uncommitted changes first
85
+ git status --porcelain
86
+
87
+ IF uncommitted changes:
88
+ AskUserQuestion:
89
+ question: "You have uncommitted changes. What should we do?"
90
+ header: "Git"
91
+ options:
92
+ - label: "Stash changes"
93
+ description: "Save changes and switch branches"
94
+ - label: "Commit changes"
95
+ description: "Commit before switching"
96
+ - label: "Cancel resume"
97
+ description: "Stay on current branch"
98
+
99
+ Handle response appropriately
100
+
101
+ git checkout {taskToResume.branch}
102
+ ```
103
+
104
+ ## Step 6: Update State
105
+
106
+ ```
107
+ resumedTask = taskToResume
108
+ resumedTask.status = "active"
109
+ resumedTask.resumedAt = "{timestamp}"
110
+ resumedTask.pausedDuration = "{away duration}"
111
+
112
+ # Remove from pausedTasks
113
+ state.pausedTasks = state.pausedTasks.filter(t => t.id != resumedTask.id)
114
+ state.currentTask = resumedTask
9
115
  ```
10
116
 
11
- IF `currentTask` active → "Already active: {task}", STOP
12
- IF no `pausedTask` AND no `interruptedTask` → "Nothing to resume", STOP
117
+ WRITE `{globalPath}/storage/state.json`:
118
+ ```json
119
+ {
120
+ "currentTask": {
121
+ "id": "{task.id}",
122
+ "description": "{task.description}",
123
+ "type": "{task.type}",
124
+ "status": "active",
125
+ "startedAt": "{task.startedAt}",
126
+ "resumedAt": "{timestamp}",
127
+ "pausedDuration": "{away duration}",
128
+ "subtasks": [...],
129
+ "currentSubtaskIndex": {task.currentSubtaskIndex},
130
+ "parentDescription": "{task.parentDescription}",
131
+ "branch": "{task.branch}",
132
+ "linearId": "{task.linearId or null}"
133
+ },
134
+ "pausedTasks": [...remaining paused tasks],
135
+ "previousTask": {...}
136
+ }
137
+ ```
13
138
 
14
- IF both exist Ask which to resume
139
+ ## Step 7: Load Context
15
140
 
16
- Calculate away duration
141
+ Load agents for context:
142
+ ```
143
+ GLOB: {globalPath}/agents/*.md
17
144
 
18
- UPDATE `{globalPath}/storage/state.json`:
19
- - `currentTask` = resumed task with `status: "active"`, `resumedAt`
20
- - Clear `pausedTask` or `interruptedTask`
145
+ FOR each agent file:
146
+ READ agent content for domain patterns
147
+ ```
148
+
149
+ ## Step 8: Log Event
150
+
151
+ APPEND to `{globalPath}/memory/events.jsonl`:
152
+ ```json
153
+ {"type":"task_resumed","taskId":"{id}","description":"{description}","timestamp":"{timestamp}","pausedDuration":"{away duration}"}
154
+ ```
155
+
156
+ ---
21
157
 
22
- Load agents from `{globalPath}/agents/` for context
158
+ ## Output
23
159
 
24
- **Output**:
25
160
  ```
26
- ▶️ Resumed: {task}
161
+ ▶️ Resumed: {task.parentDescription}
27
162
 
28
- Was paused: {duration}
29
- {IF workflow: Phase: {phase}}
163
+ Was paused: {pausedDuration}
164
+ {IF task.subtasks: "Current subtask: {current subtask}"}
165
+ Branch: {task.branch}
30
166
 
31
167
  Next:
32
- - Finish work → `p. done`
168
+ - Continue work → make changes
169
+ - Finish subtask → `p. done`
33
170
  - Pause again → `p. pause`
34
171
  ```
@@ -89,15 +89,6 @@ IF integrations.linear.enabled:
89
89
  OUTPUT: "Linear: {fetched} issues synced"
90
90
  ```
91
91
 
92
- ## Cursor Router Regeneration
93
-
94
- If `.cursor/` exists but `.cursor/rules/prjct.mdc` is missing:
95
- 1. Get npm root: `npm root -g`
96
- 2. Create `.cursor/rules/` directory if needed
97
- 3. Copy router: `{npmRoot}/prjct-cli/templates/cursor/router.mdc` → `.cursor/rules/prjct.mdc`
98
- 4. Copy commands: `{npmRoot}/prjct-cli/templates/cursor/p.md` → `.cursor/commands/p.md`
99
- 5. Report: "Cursor routers regenerated"
100
-
101
92
  ## Output
102
93
 
103
94
  ```
@@ -106,7 +97,6 @@ If `.cursor/` exists but `.cursor/rules/prjct.mdc` is missing:
106
97
  Ecosystem: {ecosystem}
107
98
  Agents: {count} generated
108
99
  Linear: {issueCount} issues synced (or "not enabled")
109
- Cursor: {regenerated/ready/not detected}
110
100
 
111
101
  Next:
112
102
  - Start work → `p. task "description"`
@@ -17,7 +17,16 @@ IF $ARGUMENTS is empty:
17
17
  DO NOT proceed with empty task
18
18
  ```
19
19
 
20
- ### Check 2: Check for Active Task
20
+ ### Check 2: Resolve Project Paths
21
+
22
+ ```bash
23
+ # Get projectId from local config
24
+ cat .prjct/prjct.config.json | grep -o '"projectId"[[:space:]]*:[[:space:]]*"[^"]*"' | cut -d'"' -f4
25
+ ```
26
+
27
+ Set `globalPath = ~/.prjct-cli/projects/{projectId}`
28
+
29
+ ### Check 3: Check for Active Task
21
30
 
22
31
  ```
23
32
  READ: {globalPath}/storage/state.json
@@ -38,7 +47,7 @@ IF currentTask exists AND currentTask.status == "active":
38
47
  DO NOT automatically switch tasks
39
48
  ```
40
49
 
41
- ### Check 3: Validate Git State
50
+ ### Check 4: Validate Git State
42
51
 
43
52
  ```bash
44
53
  git status --porcelain
@@ -77,9 +86,9 @@ IF integrations.linear.enabled:
77
86
  IF issue found in local cache:
78
87
  USE cached issue data (no API call needed)
79
88
  ELSE:
80
- # Fallback to API
81
- IMPORT: linearSync from core/integrations/linear
82
- CALL: linearSync.getIssue(projectId, "$ARGUMENTS") # Fetches + caches
89
+ # Fallback to API - sync and retry
90
+ RUN: bun $PRJCT_CLI/core/cli/linear.ts --project {projectId} sync
91
+ RUN: bun $PRJCT_CLI/core/cli/linear.ts --project {projectId} get-local "$ARGUMENTS"
83
92
 
84
93
  IF issue found:
85
94
  SET: task.linearId = issue.identifier # "PRJ-123"
@@ -87,17 +96,15 @@ IF integrations.linear.enabled:
87
96
  SET: task.description = issue.title
88
97
  SET: $ARGUMENTS = issue.title # Use title for task
89
98
 
90
- # Mark issue as In Progress in Linear (pushes status change)
91
- IMPORT: linearSync from core/integrations/linear
92
- CALL: linearSync.pushStatus(projectId, issue.identifier, 'in_progress')
99
+ # Mark issue as In Progress in Linear
100
+ RUN: bun $PRJCT_CLI/core/cli/linear.ts --project {projectId} status "$ARGUMENTS" "in_progress"
93
101
 
94
102
  OUTPUT: "Linked to Linear: {issue.identifier} - {issue.title}"
95
103
  OUTPUT: "Linear: In Progress"
96
104
 
97
105
  ELSE IF integrations.jira.enabled:
98
106
  # JIRA issue detected
99
- IMPORT: jiraService from core/integrations/jira
100
- CALL: jiraService.fetchIssue("$ARGUMENTS")
107
+ RUN: bun $PRJCT_CLI/core/cli/jira.ts --project {projectId} get "$ARGUMENTS"
101
108
 
102
109
  IF issue found:
103
110
  SET: task.externalId = issue.externalId
@@ -106,7 +113,7 @@ ELSE IF integrations.jira.enabled:
106
113
  SET: $ARGUMENTS = issue.title # Use title for task
107
114
 
108
115
  # Mark issue as In Progress in JIRA
109
- CALL: jiraService.markInProgress(issue.externalId)
116
+ RUN: bun $PRJCT_CLI/core/cli/jira.ts --project {projectId} status "$ARGUMENTS" "in_progress"
110
117
 
111
118
  OUTPUT: "Linked to JIRA: {issue.externalId} - {issue.title}"
112
119
  OUTPUT: "JIRA: In Progress"
@@ -119,7 +126,29 @@ ELSE:
119
126
 
120
127
  ## Main Flow
121
128
 
122
- ### Step A: Show Plan and Get Approval (BLOCKING)
129
+ ### Step A: Explore Codebase (for context)
130
+
131
+ ```
132
+ USE Task(Explore) → find similar code, affected files
133
+ READ {globalPath}/agents/*.md → get domain patterns (if they exist)
134
+ ```
135
+
136
+ ### Step B: Classify Task
137
+
138
+ Determine type based on keywords:
139
+ - `add`, `create`, `implement`, `new` → **feature**
140
+ - `fix`, `repair`, `broken`, `error` → **bug**
141
+ - `improve`, `enhance`, `optimize` → **improvement**
142
+ - `refactor`, `clean`, `reorganize` → **refactor**
143
+ - `update`, `upgrade`, `migrate` → **chore**
144
+
145
+ Default: **feature**
146
+
147
+ ### Step C: Break Down into Subtasks
148
+
149
+ Create 2-5 actionable subtasks based on the task description.
150
+
151
+ ### Step D: Show Plan and Get Approval (BLOCKING)
123
152
 
124
153
  **⛔ DO NOT create branches or modify state without user approval.**
125
154
 
@@ -131,11 +160,16 @@ Description: $ARGUMENTS
131
160
  Type: {classified type}
132
161
  Branch: {type}/{slug}
133
162
 
163
+ Subtasks:
164
+ 1. {subtask 1}
165
+ 2. {subtask 2}
166
+ ...
167
+
134
168
  Will do:
135
169
  1. Create feature branch from current branch
136
170
  2. Initialize task tracking in state.json
137
- 3. Break down into subtasks
138
- 4. {If Linear: Update issue status to In Progress}
171
+ 3. Begin work on first subtask
172
+ {If Linear: 4. Update issue status to In Progress}
139
173
  ```
140
174
 
141
175
  Then ask for confirmation:
@@ -167,27 +201,16 @@ STOP - Do not continue
167
201
  ```
168
202
 
169
203
  **If "Yes, start task":**
170
- CONTINUE to Step B
204
+ CONTINUE to Step E
171
205
 
172
- ### Step B: Explore Codebase
173
-
174
- ```
175
- USE Task(Explore) → find similar code, affected files
176
- READ agents/*.md → get domain patterns
177
- ```
178
-
179
- ### Step C: Classify Task
180
-
181
- Determine type: feature | bug | improvement | refactor | chore
182
-
183
- ### Step D: Create Branch (if needed)
206
+ ### Step E: Create Branch (if needed)
184
207
 
185
208
  ```bash
186
- CURRENT_BRANCH=$(git branch --show-current)
209
+ git branch --show-current
187
210
  ```
188
211
 
189
212
  ```
190
- IF CURRENT_BRANCH == "main" OR CURRENT_BRANCH == "master":
213
+ IF current branch == "main" OR "master":
191
214
  OUTPUT: "Creating feature branch: {type}/{slug}"
192
215
 
193
216
  git checkout -b {type}/{slug}
@@ -197,8 +220,15 @@ IF CURRENT_BRANCH == "main" OR CURRENT_BRANCH == "master":
197
220
  STOP
198
221
  ```
199
222
 
223
+ ### Step F: Write State
224
+
225
+ Generate UUID and timestamp:
200
226
  ```bash
201
- prjct work "$ARGUMENTS"
227
+ # UUID
228
+ node -e "console.log(require('crypto').randomUUID())"
229
+
230
+ # Timestamp
231
+ node -e "console.log(new Date().toISOString())"
202
232
  ```
203
233
 
204
234
  WRITE `{globalPath}/storage/state.json`:
@@ -206,25 +236,43 @@ WRITE `{globalPath}/storage/state.json`:
206
236
  {
207
237
  "currentTask": {
208
238
  "id": "{uuid}",
209
- "description": "{subtask}",
239
+ "description": "{first subtask description}",
210
240
  "type": "{type}",
211
241
  "status": "active",
212
- "startedAt": "{now}",
213
- "subtasks": [...],
242
+ "startedAt": "{timestamp}",
243
+ "subtasks": [
244
+ {"description": "{subtask 1}", "status": "active"},
245
+ {"description": "{subtask 2}", "status": "pending"},
246
+ {"description": "{subtask 3}", "status": "pending"}
247
+ ],
214
248
  "currentSubtaskIndex": 0,
215
249
  "parentDescription": "$ARGUMENTS",
216
- "linearId": "{identifier or null}", // "PRJ-123" - Linear identifier
217
- "linearUuid": "{uuid or null}" // Linear internal UUID
218
- }
250
+ "branch": "{type}/{slug}",
251
+ "linearId": "{identifier or null}",
252
+ "linearUuid": "{uuid or null}"
253
+ },
254
+ "pausedTasks": []
219
255
  }
220
256
  ```
221
257
 
222
- **Output**:
258
+ ### Step G: Log Event
259
+
260
+ APPEND to `{globalPath}/memory/events.jsonl`:
261
+ ```json
262
+ {"type":"task_started","taskId":"{uuid}","description":"$ARGUMENTS","timestamp":"{timestamp}","branch":"{branch}"}
263
+ ```
264
+
265
+ ---
266
+
267
+ ## Output
268
+
223
269
  ```
224
- {type}: $ARGUMENTS
270
+ {type}: $ARGUMENTS
225
271
 
226
272
  Branch: {branch} | Subtasks: {count}
227
273
  {linearId ? "Linear: {linearId} → In Progress" : ""}
228
274
 
275
+ Current: {first subtask}
276
+
229
277
  Next: Work on subtask, then `p. done`
230
278
  ```
@@ -4,47 +4,90 @@ allowed-tools: [Bash, Read, Write]
4
4
 
5
5
  # p. test
6
6
 
7
+ ## Step 1: Detect Test Runner
8
+
9
+ Check project files to determine the test runner:
10
+
7
11
  ```bash
8
- prjct context test
12
+ # Check for package.json with test script
13
+ if [ -f package.json ]; then
14
+ cat package.json | grep -o '"test"[[:space:]]*:' && echo "node"
15
+ fi
16
+
17
+ # Check for pytest
18
+ if [ -f pytest.ini ] || [ -f pyproject.toml ]; then
19
+ echo "pytest"
20
+ fi
21
+
22
+ # Check for Cargo (Rust)
23
+ if [ -f Cargo.toml ]; then
24
+ echo "cargo"
25
+ fi
26
+
27
+ # Check for Go
28
+ if [ -f go.mod ]; then
29
+ echo "go"
30
+ fi
31
+
32
+ # Check for .NET
33
+ if ls *.sln *.csproj 2>/dev/null | head -1; then
34
+ echo "dotnet"
35
+ fi
9
36
  ```
10
37
 
11
- Detect test runner from project files:
12
- - package.json with scripts.test → npm/pnpm/yarn/bun test
13
- - pytest.ini/pyproject.toml pytest
14
- - Cargo.toml cargo test
15
- - go.mod go test ./...
16
- - *.sln/*.csproj dotnet test
38
+ | Detected | Runner Command |
39
+ |----------|----------------|
40
+ | package.json with scripts.test | `npm test` or `bun test` or `pnpm test` |
41
+ | pytest.ini / pyproject.toml | `pytest` |
42
+ | Cargo.toml | `cargo test` |
43
+ | go.mod | `go test ./...` |
44
+ | *.sln / *.csproj | `dotnet test` |
45
+
46
+ ## Step 2: Run Tests
17
47
 
18
- Run tests:
19
48
  ```bash
20
49
  {runnerCmd} 2>&1
21
50
  ```
22
51
 
23
- Parse results: passed/failed count
52
+ Parse results to count passed/failed tests.
24
53
 
25
- IF failed AND mode == "fix":
26
- Try `{runnerCmd} -- -u` (update snapshots)
27
- Re-run tests
54
+ ## Step 3: Handle Results
55
+
56
+ **IF all tests pass:**
28
57
 
29
- **Output (pass)**:
30
58
  ```
31
59
  ✅ Tests passing
32
60
 
33
61
  Passed: {count}
34
- Coverage: {%}
62
+ Coverage: {%} (if available)
35
63
 
36
64
  Next:
37
65
  - Code review → `p. review`
38
66
  - Ship → `p. ship`
39
67
  ```
40
68
 
41
- **Output (fail)**:
69
+ **IF tests fail:**
70
+
42
71
  ```
43
72
  ❌ {failed} tests failing
44
73
 
45
- {test output}
74
+ {test output - last 50 lines}
46
75
 
47
76
  Next:
48
- - Auto-fix → `p. test fix`
77
+ - Auto-fix snapshots → `p. test fix`
49
78
  - Fix manually and re-run
50
79
  ```
80
+
81
+ ---
82
+
83
+ ## Fix Mode (`p. test fix`)
84
+
85
+ IF mode == "fix":
86
+ Try updating snapshots:
87
+ ```bash
88
+ {runnerCmd} -- -u
89
+ # or for jest: npm test -- -u
90
+ # or for vitest: npx vitest --update
91
+ ```
92
+
93
+ Re-run tests to verify fix.