@champpaba/claude-agent-kit 3.3.0 → 3.4.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.
@@ -24,11 +24,12 @@ Interactive update of `PROJECT_STATUS.yml` - the cross-session context file that
24
24
 
25
25
  ## Step 1: Check File Exists
26
26
 
27
- ```typescript
28
- const statusPath = 'PROJECT_STATUS.yml'
27
+ 1. Check if `PROJECT_STATUS.yml` exists in project root
29
28
 
30
- if (!fileExists(statusPath)) {
31
- output(`
29
+ **If file does NOT exist:**
30
+
31
+ 2. Display message:
32
+ ```
32
33
  📊 PROJECT_STATUS.yml not found
33
34
 
34
35
  This file provides cross-session context for Claude.
@@ -38,61 +39,77 @@ It helps new sessions understand:
38
39
  - Completed work & next priorities
39
40
 
40
41
  Create it now? (yes/no)
41
- `)
42
-
43
- const answer = await askUser()
44
- if (answer === 'yes') {
45
- // Copy from template
46
- copy('.claude/templates/PROJECT_STATUS.template.yml', statusPath)
47
- output('✅ Created PROJECT_STATUS.yml - please fill in your project details')
48
- return
49
- } else {
50
- return output('Skipped. Run /pstatus again when ready.')
51
- }
52
- }
53
42
  ```
54
43
 
44
+ 3. Ask user to confirm creation
45
+
46
+ **If user says "yes":**
47
+ - Copy `.claude/templates/PROJECT_STATUS.template.yml` to project root as `PROJECT_STATUS.yml`
48
+ - Display: `✅ Created PROJECT_STATUS.yml - please fill in your project details`
49
+ - Stop execution (user needs to fill template first)
50
+
51
+ **If user says "no":**
52
+ - Display: `Skipped. Run /pstatus again when ready.`
53
+ - Stop execution
54
+
55
+ **If file EXISTS:**
56
+ - Continue to Step 2
57
+
55
58
  ---
56
59
 
57
60
  ## Step 2: Read Current Status
58
61
 
59
- ```typescript
60
- const status = parseYaml(Read(statusPath))
61
- const lastUpdated = new Date(status.last_updated)
62
- const daysSinceUpdate = Math.floor((Date.now() - lastUpdated) / (1000 * 60 * 60 * 24))
62
+ 1. Read `PROJECT_STATUS.yml` and parse as YAML
63
+
64
+ 2. Extract `last_updated` field
65
+
66
+ 3. Calculate days since update:
67
+ - Parse `last_updated` as date
68
+ - Calculate difference between today and last_updated in days
69
+ - Round down to whole number
70
+
71
+ 4. Display status summary:
72
+ ```
73
+ 📊 PROJECT_STATUS.yml
74
+
75
+ Last updated: [YYYY-MM-DD] ([N] days ago)
76
+ Current focus: [description or 'Not set']
77
+ Active change: [change-id or 'None']
78
+ Blockers: [count]
79
+ Completed changes: [count]
80
+ ```
63
81
 
64
- output(`
82
+ **Example:**
83
+ ```
65
84
  📊 PROJECT_STATUS.yml
66
85
 
67
- Last updated: ${status.last_updated} (${daysSinceUpdate} days ago)
68
- Current focus: ${status.current_focus?.description || 'Not set'}
69
- Active change: ${status.current_focus?.active_change || 'None'}
70
- Blockers: ${status.blockers?.length || 0}
71
- Completed changes: ${status.completed_changes?.length || 0}
72
- `)
86
+ Last updated: 2025-11-25 (6 days ago)
87
+ Current focus: Building authentication system
88
+ Active change: auth-system
89
+ Blockers: 1
90
+ Completed changes: 3
73
91
  ```
74
92
 
75
93
  ---
76
94
 
77
95
  ## Step 3: Select Update Mode
78
96
 
79
- ```typescript
80
- // If mode provided via argument, use it
81
- // Otherwise, ask user
82
-
83
- const mode = args[0] || await askUserQuestion({
84
- questions: [{
85
- question: 'What would you like to update?',
86
- header: 'Mode',
87
- options: [
88
- { label: 'Quick Update', description: 'Only sections that seem outdated' },
89
- { label: 'Full Review', description: 'Walk through all sections' },
90
- { label: 'Specific Section', description: 'Update one section only' }
91
- ],
92
- multiSelect: false
93
- }]
94
- })
95
- ```
97
+ 1. Check if mode was provided as argument:
98
+ - `/pstatus quick` mode = "quick"
99
+ - `/pstatus full` → mode = "full"
100
+ - `/pstatus blockers` → mode = "blockers" (or any specific section)
101
+ - `/pstatus` (no args) ask user
102
+
103
+ **If no mode specified:**
104
+
105
+ 2. Ask user: "What would you like to update?"
106
+
107
+ Display options:
108
+ - **Quick Update** - Only sections that seem outdated
109
+ - **Full Review** - Walk through all sections
110
+ - **Specific Section** - Update one section only
111
+
112
+ 3. Store selected mode for Step 4
96
113
 
97
114
  ---
98
115
 
@@ -100,259 +117,310 @@ const mode = args[0] || await askUserQuestion({
100
117
 
101
118
  ### 4.1 Update `last_updated`
102
119
 
103
- ```typescript
104
- // Always update timestamp
105
- status.last_updated = new Date().toISOString().split('T')[0]
106
- ```
120
+ Always update timestamp to today's date in YYYY-MM-DD format.
121
+
122
+ **Example:** `2025-12-20`
107
123
 
108
124
  ### 4.2 Update `current_focus`
109
125
 
110
- ```typescript
111
- output(`
126
+ 1. Display current focus:
127
+ ```
112
128
  📍 Current Focus
113
- Description: "${status.current_focus?.description || 'Not set'}"
114
- Active change: ${status.current_focus?.active_change || 'None'}
115
- `)
116
-
117
- const updateFocus = await askUserQuestion({
118
- questions: [{
119
- question: 'Update current focus?',
120
- header: 'Focus',
121
- options: [
122
- { label: 'Keep as is', description: 'No changes needed' },
123
- { label: 'Update description', description: 'Change what you are working on' },
124
- { label: 'Set active change', description: 'Link to OpenSpec change' },
125
- { label: 'Clear active change', description: 'Not working on a change' }
126
- ],
127
- multiSelect: false
128
- }]
129
- })
130
-
131
- // Handle user selection...
129
+ Description: "[description or 'Not set']"
130
+ Active change: [change-id or 'None']
132
131
  ```
133
132
 
133
+ 2. Ask user: "Update current focus?"
134
+
135
+ Display options:
136
+ - **Keep as is** - No changes needed
137
+ - **Update description** - Change what you are working on
138
+ - **Set active change** - Link to OpenSpec change
139
+ - **Clear active change** - Not working on a change
140
+
141
+ **If user selects "Update description":**
142
+ - Ask: "What are you working on?"
143
+ - Update `current_focus.description` with user's answer
144
+
145
+ **If user selects "Set active change":**
146
+ - Ask: "Which change ID?"
147
+ - Update `current_focus.active_change` with change ID
148
+
149
+ **If user selects "Clear active change":**
150
+ - Set `current_focus.active_change` to null
151
+
152
+ **If user selects "Keep as is":**
153
+ - Skip to next section
154
+
134
155
  ### 4.3 Update `completed_changes`
135
156
 
136
- ```typescript
137
- output(`
138
- ✅ Completed Changes (${status.completed_changes?.length || 0})
139
- ${status.completed_changes?.map(c => ` - ${c.id} (${c.date}): ${c.summary}`).join('\n') || ' (none)'}
140
- `)
157
+ 1. Display completed changes:
158
+ ```
159
+ ✅ Completed Changes ([count])
160
+ - [id] ([date]): [summary]
161
+ - ...
162
+ (or "none" if empty)
163
+ ```
141
164
 
142
- // Auto-detect archived changes not in list
143
- const archivedChanges = listFiles('openspec/changes/archive/')
144
- const missingChanges = archivedChanges.filter(dir => {
145
- const id = path.basename(dir)
146
- return !status.completed_changes?.some(c => c.id === id)
147
- })
165
+ 2. Auto-detect archived changes not in list:
166
+ - List all directories in `openspec/changes/archive/`
167
+ - For each archived change, check if its ID exists in `completed_changes`
168
+ - Collect missing changes
148
169
 
149
- if (missingChanges.length > 0) {
150
- output(`
151
- 📦 Found ${missingChanges.length} archived change(s) not in completed_changes:
152
- ${missingChanges.map(c => ` - ${path.basename(c)}`).join('\n')}
170
+ **If missing changes found:**
171
+
172
+ 3. Display:
173
+ ```
174
+ 📦 Found [N] archived change(s) not in completed_changes:
175
+ - [change-id-1]
176
+ - [change-id-2]
177
+ ...
153
178
 
154
179
  Add them? (yes/no)
155
- `)
156
-
157
- const addMissing = await askUser()
158
- if (addMissing) {
159
- for (const changePath of missingChanges) {
160
- const id = path.basename(changePath)
161
- // Try to read proposal.md for summary
162
- const proposalPath = `${changePath}/proposal.md`
163
- let summary = 'No summary available'
164
- if (fileExists(proposalPath)) {
165
- const proposal = Read(proposalPath)
166
- // Extract first sentence or title
167
- summary = extractSummary(proposal)
168
- }
169
-
170
- status.completed_changes = status.completed_changes || []
171
- status.completed_changes.push({
172
- id,
173
- date: new Date().toISOString().split('T')[0],
174
- summary
175
- })
176
- output(` ✅ Added: ${id}`)
177
- }
178
- }
179
- }
180
180
  ```
181
181
 
182
+ 4. Ask user to confirm
183
+
184
+ **If user says "yes":**
185
+
186
+ For each missing change:
187
+ - Extract change ID from directory name
188
+ - Try to read `{change-path}/proposal.md`
189
+ - Extract summary from proposal (first H1 title or first sentence)
190
+ - If proposal not found, use "No summary available"
191
+ - Add to `completed_changes`:
192
+ ```yaml
193
+ - id: change-id
194
+ date: YYYY-MM-DD (today)
195
+ summary: extracted summary
196
+ ```
197
+ - Display: `✅ Added: {change-id}`
198
+
199
+ **If user says "no":**
200
+ - Skip to next section
201
+
182
202
  ### 4.4 Update `infrastructure`
183
203
 
184
- ```typescript
185
- output(`
204
+ 1. Display infrastructure status:
205
+ ```
186
206
  🏗️ Infrastructure Status
187
- ${Object.entries(status.infrastructure || {}).map(([service, info]) =>
188
- ` ${service}: ${info.status}${info.waiting_for ? ` (waiting: ${info.waiting_for})` : ''}${info.notes ? ` - ${info.notes}` : ''}`
189
- ).join('\n') || ' (none configured)'}
190
- `)
191
-
192
- const updateInfra = await askUserQuestion({
193
- questions: [{
194
- question: 'Update infrastructure status?',
195
- header: 'Infra',
196
- options: [
197
- { label: 'Keep as is', description: 'No changes needed' },
198
- { label: 'Update status', description: 'Change service status' },
199
- { label: 'Add service', description: 'Track new infrastructure' },
200
- { label: 'Remove service', description: 'Stop tracking a service' }
201
- ],
202
- multiSelect: false
203
- }]
204
- })
205
-
206
- // Handle user selection...
207
- // For status update, walk through each service:
208
- if (updateInfra === 'Update status') {
209
- for (const [service, info] of Object.entries(status.infrastructure || {})) {
210
- output(`\n${service}: Currently "${info.status}"`)
211
- const newStatus = await askUserQuestion({
212
- questions: [{
213
- question: `Update ${service} status?`,
214
- header: service,
215
- options: [
216
- { label: 'healthy', description: 'Working normally' },
217
- { label: 'degraded', description: 'Working with issues' },
218
- { label: 'down', description: 'Not working' },
219
- { label: 'waiting', description: 'Pending external action' },
220
- { label: 'Keep current', description: `Stay as "${info.status}"` }
221
- ],
222
- multiSelect: false
223
- }]
224
- })
225
-
226
- if (newStatus !== 'Keep current') {
227
- status.infrastructure[service].status = newStatus
228
-
229
- // If changed to healthy, clear waiting_for
230
- if (newStatus === 'healthy') {
231
- status.infrastructure[service].waiting_for = null
232
- }
233
-
234
- // If changed to waiting, ask what for
235
- if (newStatus === 'waiting') {
236
- output('What is it waiting for?')
237
- status.infrastructure[service].waiting_for = await askUser()
238
- }
239
- }
240
- }
241
- }
207
+ [service]: [status] (waiting: [reason]) - [notes]
208
+ ...
209
+ (or "none configured" if empty)
242
210
  ```
243
211
 
212
+ 2. Ask user: "Update infrastructure status?"
213
+
214
+ Display options:
215
+ - **Keep as is** - No changes needed
216
+ - **Update status** - Change service status
217
+ - **Add service** - Track new infrastructure
218
+ - **Remove service** - Stop tracking a service
219
+
220
+ **If user selects "Update status":**
221
+
222
+ For each service in infrastructure:
223
+
224
+ 3. Display current status:
225
+ ```
226
+ [service]: Currently "[status]"
227
+ ```
228
+
229
+ 4. Ask user to select new status:
230
+ - **healthy** - Working normally
231
+ - **degraded** - Working with issues
232
+ - **down** - Not working
233
+ - **waiting** - Pending external action
234
+ - **Keep current** - Stay as "[current status]"
235
+
236
+ 5. Handle status change:
237
+
238
+ **If new status = "healthy":**
239
+ - Update `infrastructure[service].status` to "healthy"
240
+ - Clear `infrastructure[service].waiting_for` (set to null)
241
+
242
+ **If new status = "waiting":**
243
+ - Update `infrastructure[service].status` to "waiting"
244
+ - Ask: "What is it waiting for?"
245
+ - Update `infrastructure[service].waiting_for` with user's answer
246
+
247
+ **If new status = "degraded" or "down":**
248
+ - Update `infrastructure[service].status` to selected value
249
+
250
+ **If user selects "Keep current":**
251
+ - Skip this service
252
+
253
+ **If user selects "Add service":**
254
+ - Ask: "Service name?"
255
+ - Ask: "Status?" (healthy/degraded/down/waiting)
256
+ - If waiting, ask: "Waiting for what?"
257
+ - Add new service to `infrastructure`
258
+
259
+ **If user selects "Remove service":**
260
+ - Ask: "Which service to remove?"
261
+ - Remove service from `infrastructure`
262
+
263
+ **If user selects "Keep as is":**
264
+ - Skip to next section
265
+
244
266
  ### 4.5 Update `blockers`
245
267
 
246
- ```typescript
247
- output(`
248
- 🚧 Blockers (${status.blockers?.length || 0})
249
- ${status.blockers?.map(b => ` - ${b.id}: ${b.description} (blocks: ${b.blocks?.join(', ') || 'nothing specified'})`).join('\n') || ' (none)'}
250
- `)
251
-
252
- const updateBlockers = await askUserQuestion({
253
- questions: [{
254
- question: 'Update blockers?',
255
- header: 'Blockers',
256
- options: [
257
- { label: 'Keep as is', description: 'No changes needed' },
258
- { label: 'Add blocker', description: 'New external dependency' },
259
- { label: 'Remove blocker', description: 'Blocker resolved' },
260
- { label: 'Update blocker', description: 'Change existing blocker' }
261
- ],
262
- multiSelect: false
263
- }]
264
- })
265
-
266
- // Handle user selection...
268
+ 1. Display blockers:
269
+ ```
270
+ 🚧 Blockers ([count])
271
+ - [id]: [description] (blocks: [what it blocks])
272
+ ...
273
+ (or "none" if empty)
267
274
  ```
268
275
 
276
+ 2. Ask user: "Update blockers?"
277
+
278
+ Display options:
279
+ - **Keep as is** - No changes needed
280
+ - **Add blocker** - New external dependency
281
+ - **Remove blocker** - Blocker resolved
282
+ - **Update blocker** - Change existing blocker
283
+
284
+ **If user selects "Add blocker":**
285
+ - Ask: "Blocker ID?" (e.g., "domain-config")
286
+ - Ask: "What's blocked?" (description)
287
+ - Ask: "What does it block?" (e.g., "production-launch")
288
+ - Add new blocker:
289
+ ```yaml
290
+ - id: blocker-id
291
+ description: user's description
292
+ blocks: [what-it-blocks]
293
+ ```
294
+
295
+ **If user selects "Remove blocker":**
296
+ - Display list of current blockers with numbers
297
+ - Ask: "Which blocker to remove?" (user selects by number or ID)
298
+ - Remove blocker from list
299
+
300
+ **If user selects "Update blocker":**
301
+ - Display list of current blockers with numbers
302
+ - Ask: "Which blocker to update?" (user selects by number or ID)
303
+ - Ask: "Update description? (yes/no)"
304
+ - If yes, ask for new description
305
+ - Ask: "Update what it blocks? (yes/no)"
306
+ - If yes, ask for new value
307
+
308
+ **If user selects "Keep as is":**
309
+ - Skip to next section
310
+
269
311
  ### 4.6 Update `next_priorities`
270
312
 
271
- ```typescript
272
- output(`
313
+ 1. Display priorities (in order):
314
+ ```
273
315
  🎯 Next Priorities
274
- ${status.next_priorities?.map((p, i) => ` ${i + 1}. ${p.id}: ${p.reason}`).join('\n') || ' (none set)'}
275
- `)
276
-
277
- const updatePriorities = await askUserQuestion({
278
- questions: [{
279
- question: 'Update priorities?',
280
- header: 'Priorities',
281
- options: [
282
- { label: 'Keep as is', description: 'No changes needed' },
283
- { label: 'Add priority', description: 'New item to work on' },
284
- { label: 'Remove priority', description: 'Completed or deprioritized' },
285
- { label: 'Reorder', description: 'Change priority order' }
286
- ],
287
- multiSelect: false
288
- }]
289
- })
290
-
291
- // Handle user selection...
316
+ 1. [id]: [reason]
317
+ 2. [id]: [reason]
318
+ ...
319
+ (or "none set" if empty)
292
320
  ```
293
321
 
322
+ 2. Ask user: "Update priorities?"
323
+
324
+ Display options:
325
+ - **Keep as is** - No changes needed
326
+ - **Add priority** - New item to work on
327
+ - **Remove priority** - Completed or deprioritized
328
+ - **Reorder** - Change priority order
329
+
330
+ **If user selects "Add priority":**
331
+ - Ask: "Priority ID?" (e.g., "auth-system")
332
+ - Ask: "Why is this a priority?" (reason)
333
+ - Ask: "Add at position?" (1 = highest priority, or append to end)
334
+ - Insert new priority at specified position:
335
+ ```yaml
336
+ - id: priority-id
337
+ reason: user's reason
338
+ ```
339
+
340
+ **If user selects "Remove priority":**
341
+ - Display numbered list of priorities
342
+ - Ask: "Which priority to remove?" (user selects by number)
343
+ - Remove priority from list
344
+
345
+ **If user selects "Reorder":**
346
+ - Display numbered list of priorities
347
+ - Ask: "Which priority to move?" (user selects by number)
348
+ - Ask: "New position?" (1 = highest)
349
+ - Move priority to new position
350
+
351
+ **If user selects "Keep as is":**
352
+ - Skip to next section
353
+
294
354
  ### 4.7 Update `notes`
295
355
 
296
- ```typescript
297
- output(`
356
+ 1. Display notes:
357
+ ```
298
358
  📝 Notes
299
- ${status.notes || ' (empty)'}
300
- `)
301
-
302
- const updateNotes = await askUserQuestion({
303
- questions: [{
304
- question: 'Update notes?',
305
- header: 'Notes',
306
- options: [
307
- { label: 'Keep as is', description: 'No changes needed' },
308
- { label: 'Replace', description: 'Replace all notes' },
309
- { label: 'Append', description: 'Add to existing notes' },
310
- { label: 'Clear', description: 'Remove all notes' }
311
- ],
312
- multiSelect: false
313
- }]
314
- })
315
-
316
- // Handle user selection...
359
+ [notes content or "(empty)"]
317
360
  ```
318
361
 
362
+ 2. Ask user: "Update notes?"
363
+
364
+ Display options:
365
+ - **Keep as is** - No changes needed
366
+ - **Replace** - Replace all notes
367
+ - **Append** - Add to existing notes
368
+ - **Clear** - Remove all notes
369
+
370
+ **If user selects "Replace":**
371
+ - Ask: "New notes?"
372
+ - Replace entire `notes` field with user's input
373
+
374
+ **If user selects "Append":**
375
+ - Ask: "What to add?"
376
+ - Append user's input to existing notes (add newline separator)
377
+
378
+ **If user selects "Clear":**
379
+ - Set `notes` to empty string or null
380
+
381
+ **If user selects "Keep as is":**
382
+ - Continue to Step 5
383
+
319
384
  ---
320
385
 
321
386
  ## Step 5: Write Changes
322
387
 
323
- ```typescript
324
- // Show diff
325
- output(`
388
+ 1. Generate diff between original and updated status
389
+
390
+ 2. Display changes summary:
391
+ ```
326
392
  📝 Changes to be written:
327
393
 
328
- ${generateYamlDiff(originalStatus, status)}
329
- `)
330
-
331
- const confirm = await askUserQuestion({
332
- questions: [{
333
- question: 'Save changes?',
334
- header: 'Confirm',
335
- options: [
336
- { label: 'Yes', description: 'Write changes to PROJECT_STATUS.yml' },
337
- { label: 'No', description: 'Discard changes' }
338
- ],
339
- multiSelect: false
340
- }]
341
- })
342
-
343
- if (confirm === 'Yes') {
344
- Write(statusPath, toYaml(status))
345
- output(`
394
+ + completed_changes: [new items]
395
+ ~ current_focus: [old] → [new]
396
+ - blockers: [removed items]
397
+ ...
398
+ ```
399
+
400
+ 3. Ask user: "Save changes?"
401
+
402
+ Display options:
403
+ - **Yes** - Write changes to PROJECT_STATUS.yml
404
+ - **No** - Discard changes
405
+
406
+ **If user selects "Yes":**
407
+
408
+ 4. Convert updated status object to YAML format
409
+
410
+ 5. Write to `PROJECT_STATUS.yml` (overwrite entire file)
411
+
412
+ 6. Display confirmation:
413
+ ```
346
414
  ✅ PROJECT_STATUS.yml updated!
347
415
 
348
- Last updated: ${status.last_updated}
349
- Changes saved: ${countChanges(originalStatus, status)} section(s)
350
- `)
351
- } else {
352
- output('Changes discarded.')
353
- }
416
+ Last updated: [YYYY-MM-DD]
417
+ Changes saved: [N] section(s)
354
418
  ```
355
419
 
420
+ **If user selects "No":**
421
+ - Display: `Changes discarded.`
422
+ - Do not modify file
423
+
356
424
  ---
357
425
 
358
426
  ## Quick Mode Behavior
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@champpaba/claude-agent-kit",
3
- "version": "3.3.0",
3
+ "version": "3.4.0",
4
4
  "description": "Universal multi-agent template for Claude Code - AI-assisted development with specialized agents",
5
5
  "main": "bin/cli.js",
6
6
  "bin": {