prjct-cli 0.25.2 → 0.27.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.
@@ -1,11 +1,16 @@
1
1
  ---
2
- allowed-tools: [Read, Write, Bash]
3
- description: 'Complete current task with session metrics'
2
+ allowed-tools: [Read, Write, Bash, AskUserQuestion]
3
+ description: 'Complete subtask or mark implement phase done'
4
4
  ---
5
5
 
6
- # /p:done - Complete Task
6
+ # /p:done - Complete Subtask
7
7
 
8
- Mark current task as complete with metrics.
8
+ Mark current subtask as complete and progress to next. Does NOT complete the workflow - use p. verify after shipping.
9
+
10
+ **Note**: In workflow mode, p. done advances subtasks within the implement phase. To complete the full workflow:
11
+ ```
12
+ p. task → (implement) → p. done (subtasks) → p. test → p. review → p. merge → p. ship → p. verify
13
+ ```
9
14
 
10
15
  ## Usage
11
16
  ```
@@ -22,7 +27,7 @@ SET: `{globalPath}` = `~/.prjct-cli/projects/{projectId}`
22
27
 
23
28
  READ: `{globalPath}/storage/state.json`
24
29
  IF no `currentTask` OR status != "active":
25
- OUTPUT: "No active task. Use /p:now to start one."
30
+ OUTPUT: "No active task. Use p. task to start one."
26
31
  STOP
27
32
 
28
33
  EXTRACT: `{task}` = currentTask.description
@@ -30,6 +35,80 @@ EXTRACT: `{startedAt}` = currentTask.startedAt
30
35
  EXTRACT: `{sessionId}` = currentTask.sessionId
31
36
  EXTRACT: `{estimate}` = currentTask.estimate (optional)
32
37
 
38
+ ### Step 1.5: Handle Subtask Completion (AGENTIC)
39
+
40
+ IF currentTask.subtasks exists AND currentTask.subtasks.length > 0:
41
+ SET: {currentIndex} = currentTask.currentSubtaskIndex
42
+ SET: {currentSubtask} = currentTask.subtasks[{currentIndex}]
43
+ SET: {totalSubtasks} = currentTask.subtasks.length
44
+ SET: {nextIndex} = {currentIndex} + 1
45
+ SET: {hasMoreSubtasks} = {nextIndex} < {totalSubtasks}
46
+
47
+ IF {hasMoreSubtasks}:
48
+ SET: {nextSubtask} = currentTask.subtasks[{nextIndex}]
49
+ SET: {completedCount} = subtasks where status == "completed" + 1
50
+ SET: {remainingCount} = {totalSubtasks} - {completedCount}
51
+
52
+ USE AskUserQuestion:
53
+ ```
54
+ question: "Subtask '{currentSubtask.description}' done. What's next?"
55
+ header: "Subtask Complete"
56
+ options:
57
+ - label: "Next: {nextSubtask.description}"
58
+ description: "{remainingCount} subtasks remaining"
59
+ - label: "Complete entire feature"
60
+ description: "Finish '{currentTask.parentDescription}' now"
61
+ - label: "More work needed"
62
+ description: "Continue on this subtask"
63
+ ```
64
+
65
+ IF choice starts with "Next:":
66
+ SET: {now} = GetTimestamp()
67
+ # Mark current subtask complete
68
+ SET: currentTask.subtasks[{currentIndex}].status = "completed"
69
+ SET: currentTask.subtasks[{currentIndex}].completedAt = "{now}"
70
+ # Start next subtask
71
+ SET: currentTask.subtasks[{nextIndex}].status = "active"
72
+ SET: currentTask.currentSubtaskIndex = {nextIndex}
73
+ SET: currentTask.description = "{nextSubtask.description}"
74
+
75
+ WRITE: `{globalPath}/storage/state.json`
76
+
77
+ APPEND to `{globalPath}/memory/events.jsonl`:
78
+ {"timestamp":"{now}","action":"subtask_completed","subtaskId":"{currentSubtask.id}","nextSubtaskId":"{nextSubtask.id}"}
79
+
80
+ OUTPUT:
81
+ ```
82
+ ✅ {currentSubtask.description}
83
+
84
+ Now: {nextSubtask.description}
85
+ Progress: {completedCount}/{totalSubtasks} subtasks
86
+
87
+ p. done when finished
88
+ ```
89
+ STOP
90
+
91
+ IF choice == "Complete entire feature":
92
+ # Mark all remaining as skipped, continue to normal done flow
93
+ FOR each subtask in currentTask.subtasks:
94
+ IF subtask.status == "pending":
95
+ SET: subtask.status = "skipped"
96
+ SET: currentTask.subtasks[{currentIndex}].status = "completed"
97
+ OUTPUT: "Completing feature: {currentTask.parentDescription}"
98
+ # Continue to Step 2
99
+
100
+ IF choice == "More work needed":
101
+ OUTPUT: "Continuing: {currentSubtask.description}"
102
+ STOP
103
+
104
+ ELSE:
105
+ # Last subtask - mark complete and continue to normal done flow
106
+ SET: {now} = GetTimestamp()
107
+ SET: currentTask.subtasks[{currentIndex}].status = "completed"
108
+ SET: currentTask.subtasks[{currentIndex}].completedAt = "{now}"
109
+ OUTPUT: "Final subtask done. Completing feature..."
110
+ # Continue to Step 2
111
+
33
112
  ### Step 2: Calculate Metrics
34
113
  GET timestamp: `bun -e "console.log(new Date().toISOString())" 2>/dev/null || node -e "console.log(new Date().toISOString())"`
35
114
  SET: `{now}` = result
@@ -82,7 +161,76 @@ APPEND to `{globalPath}/progress/sessions/{YYYY-MM}/{date}.jsonl`:
82
161
  {"ts":"{now}","type":"task_complete","task":"{task}","duration":"{duration}"}
83
162
  ```
84
163
 
85
- ### Step 6: Output
164
+ ### Step 5.5: Handle Bug Completion Resume (AGENTIC)
165
+
166
+ IF currentTask.type == "bug" AND state.interruptedTask exists:
167
+ USE AskUserQuestion:
168
+ ```
169
+ question: "Bug fixed! Resume '{state.interruptedTask.description}'?"
170
+ header: "Bug Complete"
171
+ options:
172
+ - label: "Resume previous task"
173
+ description: "Return to '{state.interruptedTask.description}'"
174
+ - label: "Pick from queue"
175
+ description: "See what else is waiting"
176
+ - label: "Done for now"
177
+ description: "No active task"
178
+ ```
179
+
180
+ IF choice == "Resume previous task":
181
+ SET: {now} = GetTimestamp()
182
+ SET: currentTask = state.interruptedTask
183
+ SET: currentTask.status = "active"
184
+ SET: currentTask.resumedAt = "{now}"
185
+ SET: state.interruptedTask = null
186
+
187
+ WRITE: `{globalPath}/storage/state.json`
188
+
189
+ APPEND to `{globalPath}/memory/events.jsonl`:
190
+ {"timestamp":"{now}","action":"task_resumed_from_interrupt","taskId":"{currentTask.id}"}
191
+
192
+ OUTPUT:
193
+ ```
194
+ ✅ Bug fixed ({duration})
195
+
196
+ Resumed: {currentTask.description}
197
+
198
+ p. done when finished
199
+ ```
200
+ STOP
201
+
202
+ IF choice == "Pick from queue":
203
+ SET: state.interruptedTask = null
204
+ READ: `{globalPath}/storage/queue.json`
205
+ SET: {topTasks} = first 3 from queue.tasks
206
+
207
+ OUTPUT:
208
+ ```
209
+ ✅ Bug fixed ({duration})
210
+
211
+ Previous task saved. Queue:
212
+ {FOR each in topTasks: "- {task.description}"}
213
+
214
+ p. task <description> to start
215
+ ```
216
+ STOP
217
+
218
+ IF choice == "Done for now":
219
+ OUTPUT:
220
+ ```
221
+ ✅ Bug fixed ({duration})
222
+
223
+ '{state.interruptedTask.description}' saved for later.
224
+
225
+ p. resume to return | p. task for new work
226
+ ```
227
+ STOP
228
+
229
+ ### Step 6: Output with Queue Suggestion
230
+
231
+ READ: `{globalPath}/storage/queue.json`
232
+ SET: {queueCount} = queue.tasks.length
233
+ SET: {topTask} = queue.tasks[0] if exists
86
234
 
87
235
  WITH estimate:
88
236
  ```
@@ -90,8 +238,9 @@ WITH estimate:
90
238
 
91
239
  Estimate: {estimate} | Actual: {duration} | Accuracy: {accuracy}%
92
240
  Files: {filesChanged} | +{linesAdded}/-{linesRemoved} | Commits: {commits}
241
+ {IF queueCount > 0: "Queue: {queueCount} tasks | Next: {topTask.description}"}
93
242
 
94
- Next: /p:now | /p:ship | /p:progress
243
+ p. task | p. ship | p. next
95
244
  ```
96
245
 
97
246
  WITHOUT estimate:
@@ -99,8 +248,9 @@ WITHOUT estimate:
99
248
  ✅ {task} ({duration})
100
249
 
101
250
  Files: {filesChanged} | +{linesAdded}/-{linesRemoved} | Commits: {commits}
251
+ {IF queueCount > 0: "Queue: {queueCount} tasks | Next: {topTask.description}"}
102
252
 
103
- Next: /p:now | /p:ship | /p:progress
253
+ p. task | p. ship | p. next
104
254
  ```
105
255
 
106
256
  ## Error Handling
@@ -97,16 +97,32 @@ IF {currentBranch} == "main" OR {currentBranch} == "master":
97
97
  2. Resolve: conflicts if any
98
98
  3. Git: `push`
99
99
 
100
- ## Commit Message Format
100
+ ## Commit Message Format (CRITICAL - ALWAYS USE)
101
+
102
+ **Every commit MUST include the prjct signature:**
103
+
104
+ ```
105
+ {type}: {description}
106
+
107
+ {details if any}
108
+
109
+ 🤖 Generated with [p/](https://www.prjct.app/)
110
+ Designed for [Claude](https://www.anthropic.com/claude)
101
111
 
102
112
  ```
113
+
114
+ **NON-NEGOTIABLE: The `🤖 Generated with [p/]` line MUST appear in ALL commits.**
115
+
116
+ Use HEREDOC for proper formatting:
117
+ ```bash
118
+ git commit -m "$(cat <<'EOF'
103
119
  {type}: {description}
104
120
 
105
- Agent: {agent}
106
- Dev: @{github_dev}
121
+ 🤖 Generated with [p/](https://www.prjct.app/)
122
+ Designed for [Claude](https://www.anthropic.com/claude)
107
123
 
108
- Generated-by: prjct/cli
109
- Co-Authored-By: @{github_dev}
124
+ EOF
125
+ )"
110
126
  ```
111
127
 
112
128
  ## Response
@@ -0,0 +1,202 @@
1
+ ---
2
+ allowed-tools: [Bash, Read, Write, AskUserQuestion]
3
+ description: 'Merge PR to main branch'
4
+ timestamp-rule: 'GetTimestamp() for ALL timestamps'
5
+ architecture: 'Write-Through (JSON -> MD -> Events)'
6
+ storage-layer: true
7
+ source-of-truth: 'storage/state.json'
8
+ ---
9
+
10
+ # /p:merge
11
+
12
+ Merge approved PR to main branch.
13
+
14
+ ## Usage
15
+
16
+ ```
17
+ /p:merge [--squash|--rebase|--merge] # Merge strategy (default: squash)
18
+ [--delete-branch] # Delete branch after merge
19
+ ```
20
+
21
+ ## Context Variables
22
+ - `{projectId}`: From `.prjct/prjct.config.json`
23
+ - `{globalPath}`: `~/.prjct-cli/projects/{projectId}`
24
+ - `{statePath}`: `{globalPath}/storage/state.json`
25
+ - `{memoryPath}`: `{globalPath}/memory/events.jsonl`
26
+ - `{syncPath}`: `{globalPath}/sync/pending.json`
27
+
28
+ ## Step 1: Validate Project
29
+
30
+ READ: `.prjct/prjct.config.json`
31
+ EXTRACT: `projectId`
32
+
33
+ IF file not found:
34
+ OUTPUT: "No prjct project. Run /p:init first."
35
+ STOP
36
+
37
+ ## Step 2: Validate Workflow Phase
38
+
39
+ READ: `{globalPath}/storage/state.json`
40
+
41
+ IF currentTask is null:
42
+ OUTPUT: "No active task. Use p. task to start one."
43
+ STOP
44
+
45
+ IF currentTask.workflow exists:
46
+ IF currentTask.workflow.phase != "review":
47
+ OUTPUT:
48
+ ```
49
+ Cannot merge. Current phase: {currentTask.workflow.phase}
50
+
51
+ Required phase: review
52
+
53
+ Workflow: analyze → branch → implement → test → review → merge → ship → verify
54
+
55
+ Complete code review first with p. review
56
+ ```
57
+ STOP
58
+
59
+ ## Step 3: Verify PR Status
60
+
61
+ SET: {prNumber} = currentTask.branch.prNumber
62
+
63
+ IF {prNumber} is null:
64
+ OUTPUT: "No PR found. Run p. review first to create PR."
65
+ STOP
66
+
67
+ ### Check PR is approved
68
+ BASH: `gh pr view {prNumber} --json reviewDecision,mergeable,state`
69
+ SET: {decision} = result.reviewDecision
70
+ SET: {mergeable} = result.mergeable
71
+ SET: {state} = result.state
72
+
73
+ IF {state} == "MERGED":
74
+ OUTPUT: "PR already merged."
75
+ -> Skip to Step 5 (update workflow)
76
+
77
+ IF {decision} != "APPROVED":
78
+ OUTPUT:
79
+ ```
80
+ ⚠️ PR not approved yet
81
+
82
+ Current status: {decision}
83
+
84
+ Get approvals and run p. merge again.
85
+ ```
86
+ STOP
87
+
88
+ IF {mergeable} != "MERGEABLE":
89
+ OUTPUT:
90
+ ```
91
+ ⚠️ PR has conflicts or is not mergeable
92
+
93
+ Status: {mergeable}
94
+
95
+ Resolve conflicts and run p. merge again.
96
+ ```
97
+ STOP
98
+
99
+ ## Step 4: Merge PR
100
+
101
+ ### Parse merge strategy
102
+ SET: {strategy} = "squash" # default
103
+ IF --rebase: {strategy} = "rebase"
104
+ IF --merge: {strategy} = "merge"
105
+
106
+ OUTPUT: "Merging PR #{prNumber} with {strategy} strategy..."
107
+
108
+ BASH: `gh pr merge {prNumber} --{strategy} --auto 2>&1`
109
+
110
+ IF command fails:
111
+ OUTPUT: "Merge failed. Check PR status on GitHub."
112
+ STOP
113
+
114
+ ### Delete branch if requested
115
+ IF --delete-branch OR currentTask.branch.createdByPrjct:
116
+ BASH: `git branch -d {currentTask.branch.name} 2>/dev/null`
117
+ BASH: `git push origin --delete {currentTask.branch.name} 2>/dev/null`
118
+ OUTPUT: "Deleted branch: {currentTask.branch.name}"
119
+
120
+ ### Switch to base branch
121
+ BASH: `git checkout {currentTask.branch.baseBranch}`
122
+ BASH: `git pull origin {currentTask.branch.baseBranch}`
123
+
124
+ ## Step 5: Update Workflow Phase
125
+
126
+ SET: {now} = GetTimestamp()
127
+
128
+ ### Get merge commit
129
+ BASH: `git rev-parse HEAD`
130
+ SET: {mergeCommit} = result
131
+
132
+ SET: currentTask.workflow.phase = "merge"
133
+ SET: currentTask.workflow.checkpoints.merge = {
134
+ "completedAt": "{now}",
135
+ "data": {
136
+ "mergeCommit": "{mergeCommit}",
137
+ "prNumber": {prNumber},
138
+ "strategy": "{strategy}"
139
+ }
140
+ }
141
+ SET: currentTask.workflow.lastCheckpoint = "merge"
142
+
143
+ WRITE: `{statePath}`
144
+
145
+ ## Step 6: Log Events
146
+
147
+ APPEND to `{memoryPath}`:
148
+ ```json
149
+ {"timestamp":"{now}","action":"phase_advanced","taskId":"{currentTask.id}","from":"review","to":"merge"}
150
+ {"timestamp":"{now}","action":"checkpoint_completed","taskId":"{currentTask.id}","checkpoint":"merge","data":{"mergeCommit":"{mergeCommit}"}}
151
+ ```
152
+
153
+ APPEND to `{syncPath}`:
154
+ ```json
155
+ {"type":"workflow.phase_advanced","data":{"taskId":"{currentTask.id}","from":"review","to":"merge","mergeCommit":"{mergeCommit}"},"timestamp":"{now}"}
156
+ ```
157
+
158
+ ## Output
159
+
160
+ ```
161
+ ✓ PR Merged
162
+
163
+ Task: {currentTask.description}
164
+ PR: #{prNumber}
165
+ Merge commit: {mergeCommit}
166
+ Strategy: {strategy}
167
+
168
+ Phase: merge (6/11 checkpoints)
169
+
170
+ Workflow:
171
+ 1. analyze ✓
172
+ 2. branch ✓
173
+ 3. implement ✓
174
+ 4. test ✓
175
+ 5. review ✓
176
+ 6. merge ✓
177
+ 7-10. ship ← next
178
+
179
+ Next: p. ship to release
180
+ ```
181
+
182
+ ## Error Handling
183
+
184
+ | Error | Response | Action |
185
+ |-------|----------|--------|
186
+ | No project | "No prjct project" | STOP |
187
+ | No active task | "No active task" | STOP |
188
+ | Wrong phase | Show required phase | STOP |
189
+ | No PR | "Run p. review first" | STOP |
190
+ | Not approved | "Get approvals first" | STOP |
191
+ | Conflicts | "Resolve conflicts" | STOP |
192
+ | Merge fails | Show error | STOP |
193
+
194
+ ## Natural Language Triggers
195
+
196
+ - `p. merge` -> /p:merge
197
+ - `p. merge pr` -> /p:merge
198
+
199
+ ## References
200
+
201
+ - Architecture: `~/.prjct-cli/docs/architecture.md`
202
+ - Workflow: `~/.prjct-cli/docs/workflow.md`
@@ -0,0 +1,32 @@
1
+ ---
2
+ description: 'prjct CLI - Developer momentum tool'
3
+ allowed-tools: [Read, Write, Edit, Bash, Glob, Grep, Task, AskUserQuestion, TodoWrite]
4
+ ---
5
+
6
+ # prjct Command Router
7
+
8
+ Route `p. <command>` to the appropriate prjct command.
9
+
10
+ ## Instructions
11
+
12
+ **ARGUMENTS**: $ARGUMENTS
13
+
14
+ 1. Parse ARGUMENTS: first word = `command`, rest = `commandArgs`
15
+ 2. Read the template file: `~/.claude/commands/p/{command}.md`
16
+ 3. Execute that template with `commandArgs` as the task description
17
+
18
+ ## Example
19
+
20
+ If ARGUMENTS = "task fix the login bug":
21
+ - command = "task"
22
+ - commandArgs = "fix the login bug"
23
+ - Read: `~/.claude/commands/p/task.md`
24
+ - Execute with: "fix the login bug"
25
+
26
+ ## Available Commands
27
+
28
+ `task` `done` `ship` `sync` `init` `idea` `dash` `next` `pause` `resume` `bug` `spec` `suggest` `git` `test` `cleanup` `design` `analyze` `undo` `redo`
29
+
30
+ ## Action
31
+
32
+ NOW read the command template and execute it.
@@ -1,5 +1,5 @@
1
1
  ---
2
- allowed-tools: [Read, Write, Bash]
2
+ allowed-tools: [Read, Write, Bash, AskUserQuestion]
3
3
  description: 'Pause current session with reason'
4
4
  timestamp-rule: 'GetTimestamp() for all timestamps'
5
5
  architecture: 'Write-Through (JSON → MD → Events)'
@@ -89,15 +89,42 @@ IF {currentTask} is null OR {currentTask.status} != "active":
89
89
  OUTPUT: "⚠️ No active session to pause."
90
90
  STOP
91
91
 
92
- ## Step 3: Get Pause Reason
92
+ ## Step 3: Get Pause Reason (AGENTIC)
93
93
 
94
94
  IF {reason} not provided:
95
- ASK user to select reason (blocked, switch, break, research)
96
- OR auto-detect "break" as default
95
+ USE AskUserQuestion:
96
+ ```
97
+ question: "Why are you pausing '{currentTask.description}'?"
98
+ header: "Pause Reason"
99
+ options:
100
+ - label: "Blocked"
101
+ description: "Waiting on external dependency"
102
+ - label: "Switching task"
103
+ description: "Starting a different, higher priority task"
104
+ - label: "Taking a break"
105
+ description: "Stepping away temporarily"
106
+ - label: "Researching"
107
+ description: "Need to investigate more before continuing"
108
+ ```
109
+
110
+ SET: {reason} = choice mapped to (blocked, switch, break, research)
97
111
 
98
112
  IF {reason} == "blocked":
99
- ASK for blocker note: "What's blocking you?"
100
- SET: {blockerNote} = user response
113
+ USE AskUserQuestion:
114
+ ```
115
+ question: "What's blocking you?"
116
+ header: "Blocker"
117
+ options:
118
+ - label: "Waiting for review"
119
+ description: "Code review or approval needed"
120
+ - label: "Waiting for API/credentials"
121
+ description: "External access or keys needed"
122
+ - label: "Waiting for clarification"
123
+ description: "Need more info on requirements"
124
+ - label: "Technical blocker"
125
+ description: "Dependency or infrastructure issue"
126
+ ```
127
+ SET: {blockerNote} = choice (user can also provide custom text)
101
128
 
102
129
  ## Step 4: Calculate Duration So Far
103
130
 
@@ -120,10 +147,16 @@ SET: {durationFormatted} = format as "Xh Ym" or "Xm"
120
147
  "pauseReason": "{reason}",
121
148
  "pauseNote": "{blockerNote}",
122
149
  "estimate": "{currentTask.estimate}",
123
- "estimateSeconds": {currentTask.estimateSeconds}
150
+ "estimateSeconds": {currentTask.estimateSeconds},
151
+ "workflow": "{currentTask.workflow}",
152
+ "subtasks": "{currentTask.subtasks}",
153
+ "currentSubtaskIndex": {currentTask.currentSubtaskIndex},
154
+ "branch": "{currentTask.branch}"
124
155
  }
125
156
  ```
126
157
 
158
+ **Note**: The workflow field is preserved so the task can resume at the correct phase.
159
+
127
160
  ### Update state.json
128
161
  READ: `{statePath}`
129
162
  SET: state.pausedTask = paused task object