@champpaba/claude-agent-kit 2.0.1 ā 2.1.1
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.
- package/.claude/CLAUDE.md +57 -0
- package/.claude/commands/csetup.md +75 -0
- package/.claude/commands/cstatus.md +97 -4
- package/.claude/commands/pstatus.md +431 -0
- package/.claude/contexts/patterns/change-workflow.md +11 -43
- package/.claude/lib/README.md +1 -1
- package/.claude/lib/agent-router.md +1 -1
- package/.claude/lib/context-loading-protocol.md +4 -4
- package/.claude/lib/detailed-guides/agent-system.md +1 -1
- package/.claude/lib/detailed-guides/best-practices-system.md +4 -6
- package/.claude/lib/detailed-guides/design-system.md +2 -3
- package/.claude/templates/PROJECT_STATUS.template.yml +105 -0
- package/.claude/templates/design-context-template.md +2 -2
- package/README.md +20 -1567
- package/lib/init.js +30 -2
- package/package.json +1 -1
- package/.claude/CHANGELOG-v1.1.1.md +0 -259
package/.claude/CLAUDE.md
CHANGED
|
@@ -103,6 +103,13 @@ Universal, framework-agnostic template for AI-assisted development.
|
|
|
103
103
|
- `/cdev {change-id}` - Start/continue multi-agent development
|
|
104
104
|
- `/cview {change-id}` - View detailed progress for a change
|
|
105
105
|
- `/cstatus {change-id}` - Quick progress status for a change
|
|
106
|
+
- `/pstatus` - Update PROJECT_STATUS.yml (cross-session context)
|
|
107
|
+
|
|
108
|
+
**Cross-Session Context (v2.1.0):**
|
|
109
|
+
- `PROJECT_STATUS.yml` (project root) - Quick context snapshot for new sessions
|
|
110
|
+
- Contains: infrastructure state, blockers, completed work, next priorities
|
|
111
|
+
- Created by: `cak init` (optional) or manually
|
|
112
|
+
- Updated by: `/pstatus` command or Main Claude prompts
|
|
106
113
|
|
|
107
114
|
**Best Practices (Dynamic):**
|
|
108
115
|
- `.claude/contexts/domain/project/best-practices/` (auto-generated by `/csetup`)
|
|
@@ -378,4 +385,54 @@ Beyond the 6 agent files, these supporting files were also updated:
|
|
|
378
385
|
|
|
379
386
|
---
|
|
380
387
|
|
|
388
|
+
## š PROJECT_STATUS.yml Protocol (v2.1.0)
|
|
389
|
+
|
|
390
|
+
**WHY this exists:** New Claude sessions lose context about infrastructure state, blockers, and priorities. This file provides a quick snapshot.
|
|
391
|
+
|
|
392
|
+
### Session Start Behavior
|
|
393
|
+
|
|
394
|
+
If `PROJECT_STATUS.yml` exists in project root:
|
|
395
|
+
1. Read it first before other files
|
|
396
|
+
2. Note: `current_focus`, `blockers`, `infrastructure` state
|
|
397
|
+
3. If `last_updated` > 7 days (or `_config.stale_warning_days`) ā Suggest: "PROJECT_STATUS.yml may be outdated. Run /pstatus?"
|
|
398
|
+
|
|
399
|
+
### Intelligent Update Prompts
|
|
400
|
+
|
|
401
|
+
Prompt "Update PROJECT_STATUS.yml?" when detecting these patterns:
|
|
402
|
+
|
|
403
|
+
| Event Detected | What to Update |
|
|
404
|
+
|----------------|----------------|
|
|
405
|
+
| After `/openspec:archive` completes | Add to `completed_changes` |
|
|
406
|
+
| User says "waiting for...", "need X from...", "blocked by..." | Add to `blockers` |
|
|
407
|
+
| User mentions blocker resolved | Remove from `blockers` |
|
|
408
|
+
| Infrastructure change (deploy, tunnel, DB migration) | Update `infrastructure` |
|
|
409
|
+
| User discusses priority shift | Update `next_priorities` |
|
|
410
|
+
| `/csetup {change-id}` started | Update `current_focus.active_change` |
|
|
411
|
+
|
|
412
|
+
### Update Protocol
|
|
413
|
+
|
|
414
|
+
- Always ask before modifying (user confirms)
|
|
415
|
+
- Use `/pstatus` for comprehensive review
|
|
416
|
+
- Keep updates atomic (one section at a time during work)
|
|
417
|
+
- Auto-detect archived changes not listed and suggest additions
|
|
418
|
+
|
|
419
|
+
### Example Interaction
|
|
420
|
+
|
|
421
|
+
```
|
|
422
|
+
# Session start
|
|
423
|
+
Claude: *reads PROJECT_STATUS.yml*
|
|
424
|
+
Claude: "I see auth-system is active, tunnel running but waiting for domain."
|
|
425
|
+
|
|
426
|
+
# During work
|
|
427
|
+
User: "Domain is now configured"
|
|
428
|
+
Claude: "Update PROJECT_STATUS.yml?
|
|
429
|
+
- Remove 'domain' from blockers
|
|
430
|
+
- Update cloudflare_tunnel.waiting_for to null"
|
|
431
|
+
|
|
432
|
+
# After archiving
|
|
433
|
+
Claude: "Auth-system archived. Add to completed_changes?"
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
---
|
|
437
|
+
|
|
381
438
|
**š” Remember:** This template is universal. Use Context7 for framework-specific docs!
|
|
@@ -35,6 +35,81 @@ If not found:
|
|
|
35
35
|
Please create the change with OpenSpec first
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
+
### Step 1.5: Read PROJECT_STATUS.yml (v2.1.0)
|
|
39
|
+
|
|
40
|
+
**WHY:** Cross-session context helps understand blockers and infrastructure state before starting work.
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
const projectStatusPath = 'PROJECT_STATUS.yml'
|
|
44
|
+
|
|
45
|
+
if (fileExists(projectStatusPath)) {
|
|
46
|
+
const projectStatus = parseYaml(Read(projectStatusPath))
|
|
47
|
+
|
|
48
|
+
output(`\nš Project Context (from PROJECT_STATUS.yml)`)
|
|
49
|
+
|
|
50
|
+
// Show current focus
|
|
51
|
+
if (projectStatus.current_focus?.description) {
|
|
52
|
+
output(` Focus: ${projectStatus.current_focus.description}`)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Check for blockers that might affect this change
|
|
56
|
+
if (projectStatus.blockers?.length > 0) {
|
|
57
|
+
const relevantBlockers = projectStatus.blockers.filter(b =>
|
|
58
|
+
b.blocks?.some(blocked =>
|
|
59
|
+
blocked.toLowerCase().includes(changeId.toLowerCase()) ||
|
|
60
|
+
changeId.toLowerCase().includes(blocked.toLowerCase())
|
|
61
|
+
)
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
if (relevantBlockers.length > 0) {
|
|
65
|
+
output(`\n ā ļø Potential blockers for this change:`)
|
|
66
|
+
relevantBlockers.forEach(b => {
|
|
67
|
+
output(` - ${b.id}: ${b.description}`)
|
|
68
|
+
})
|
|
69
|
+
output(`\n Consider resolving blockers before starting.`)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Show infrastructure status summary
|
|
74
|
+
if (projectStatus.infrastructure) {
|
|
75
|
+
const downServices = Object.entries(projectStatus.infrastructure)
|
|
76
|
+
.filter(([_, info]) => info.status === 'down' || info.status === 'degraded')
|
|
77
|
+
|
|
78
|
+
if (downServices.length > 0) {
|
|
79
|
+
output(`\n ā ļø Infrastructure issues:`)
|
|
80
|
+
downServices.forEach(([service, info]) => {
|
|
81
|
+
output(` - ${service}: ${info.status}${info.notes ? ` (${info.notes})` : ''}`)
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Check stale status
|
|
87
|
+
const lastUpdated = new Date(projectStatus.last_updated)
|
|
88
|
+
const daysSinceUpdate = Math.floor((Date.now() - lastUpdated) / (1000 * 60 * 60 * 24))
|
|
89
|
+
const staleThreshold = projectStatus._config?.stale_warning_days || 7
|
|
90
|
+
|
|
91
|
+
if (daysSinceUpdate > staleThreshold) {
|
|
92
|
+
output(`\n ā¹ļø PROJECT_STATUS.yml last updated ${daysSinceUpdate} days ago.`)
|
|
93
|
+
output(` Consider running /pstatus to refresh.`)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Update active change
|
|
97
|
+
if (projectStatus.current_focus?.active_change !== changeId) {
|
|
98
|
+
output(`\n š Update current_focus.active_change to "${changeId}"? (yes/no)`)
|
|
99
|
+
const updateFocus = await askUser()
|
|
100
|
+
if (updateFocus) {
|
|
101
|
+
projectStatus.current_focus = projectStatus.current_focus || {}
|
|
102
|
+
projectStatus.current_focus.active_change = changeId
|
|
103
|
+
projectStatus.last_updated = new Date().toISOString().split('T')[0]
|
|
104
|
+
Write(projectStatusPath, toYaml(projectStatus))
|
|
105
|
+
output(` ā
Updated active_change to "${changeId}"`)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
output(``) // Blank line
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
38
113
|
### Step 2: Read OpenSpec Files
|
|
39
114
|
|
|
40
115
|
Read in order:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: Change Status
|
|
3
|
-
description: Quick progress status for a change
|
|
3
|
+
description: Quick progress status for a change (with project context)
|
|
4
4
|
category: Multi-Agent
|
|
5
5
|
tags: [status, progress, quick]
|
|
6
6
|
---
|
|
@@ -9,20 +9,88 @@ tags: [status, progress, quick]
|
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
11
|
/cstatus {change-id}
|
|
12
|
+
/cstatus # Shows project status only (no change-id)
|
|
12
13
|
```
|
|
13
14
|
|
|
14
15
|
## What It Does
|
|
15
16
|
|
|
16
17
|
Shows quick progress summary:
|
|
18
|
+
- **Project context** (from PROJECT_STATUS.yml if exists)
|
|
17
19
|
- Progress percentage with bar
|
|
18
20
|
- Current phase
|
|
19
21
|
- Time spent/remaining
|
|
20
22
|
- Quick stats
|
|
21
23
|
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Step 1: Show Project Context (v2.1.0)
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
const projectStatusPath = 'PROJECT_STATUS.yml'
|
|
30
|
+
|
|
31
|
+
if (fileExists(projectStatusPath)) {
|
|
32
|
+
const projectStatus = parseYaml(Read(projectStatusPath))
|
|
33
|
+
|
|
34
|
+
output(`
|
|
35
|
+
š Project Status
|
|
36
|
+
āā Focus: ${projectStatus.current_focus?.description || 'Not set'}
|
|
37
|
+
āā Active change: ${projectStatus.current_focus?.active_change || 'None'}
|
|
38
|
+
āā Blockers: ${projectStatus.blockers?.length || 0}
|
|
39
|
+
āā Infra: ${summarizeInfraStatus(projectStatus.infrastructure)}
|
|
40
|
+
āā Updated: ${projectStatus.last_updated}
|
|
41
|
+
`)
|
|
42
|
+
|
|
43
|
+
// Show blockers if any
|
|
44
|
+
if (projectStatus.blockers?.length > 0) {
|
|
45
|
+
output(`
|
|
46
|
+
š§ Active Blockers:
|
|
47
|
+
${projectStatus.blockers.map(b => ` - ${b.id}: ${b.description}`).join('\n')}
|
|
48
|
+
`)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// If no change-id provided, stop here
|
|
53
|
+
if (!changeId) {
|
|
54
|
+
output(`
|
|
55
|
+
Commands:
|
|
56
|
+
ā Update project status: /pstatus
|
|
57
|
+
ā View change status: /cstatus {change-id}
|
|
58
|
+
`)
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Step 2: Show Change Status
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
// Read change flags
|
|
69
|
+
const flagsPath = `openspec/changes/${changeId}/.claude/flags.json`
|
|
70
|
+
|
|
71
|
+
if (!fileExists(flagsPath)) {
|
|
72
|
+
return output(`ā Change ${changeId} not found or not set up. Run /csetup ${changeId} first.`)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const flags = JSON.parse(Read(flagsPath))
|
|
76
|
+
```
|
|
77
|
+
|
|
22
78
|
## Output Format
|
|
23
79
|
|
|
24
80
|
```
|
|
25
|
-
š
|
|
81
|
+
š Project Status
|
|
82
|
+
āā Focus: Building authentication system
|
|
83
|
+
āā Active change: auth-system
|
|
84
|
+
āā Blockers: 1
|
|
85
|
+
āā Infra: DB ā
| API ā
| Tunnel ā³
|
|
86
|
+
āā Updated: 2025-11-30
|
|
87
|
+
|
|
88
|
+
š§ Active Blockers:
|
|
89
|
+
- domain-config: Need domain for Cloudflare public routes
|
|
90
|
+
|
|
91
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
92
|
+
|
|
93
|
+
š¦ CHANGE-{id}: {type} | {template}
|
|
26
94
|
|
|
27
95
|
Progress: [āāāāāāāāāā] 64% (7/11 phases)
|
|
28
96
|
|
|
@@ -51,10 +119,35 @@ Current Phase: #8 Refactor (test-debug)
|
|
|
51
119
|
3. Documentation (15 min)
|
|
52
120
|
|
|
53
121
|
Commands:
|
|
122
|
+
ā Update project status: /pstatus
|
|
54
123
|
ā Detailed view: /cview {change-id}
|
|
55
124
|
ā Continue dev: /cdev {change-id}
|
|
56
125
|
```
|
|
57
126
|
|
|
58
|
-
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Helper: summarizeInfraStatus()
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
function summarizeInfraStatus(infrastructure) {
|
|
133
|
+
if (!infrastructure) return 'Not configured'
|
|
134
|
+
|
|
135
|
+
return Object.entries(infrastructure)
|
|
136
|
+
.map(([service, info]) => {
|
|
137
|
+
const icon = info.status === 'healthy' ? 'ā
' :
|
|
138
|
+
info.status === 'degraded' ? 'ā ļø' :
|
|
139
|
+
info.status === 'down' ? 'ā' :
|
|
140
|
+
info.status === 'waiting' ? 'ā³' : 'ā'
|
|
141
|
+
return `${service} ${icon}`
|
|
142
|
+
})
|
|
143
|
+
.join(' | ')
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Implementation Notes
|
|
59
150
|
|
|
60
|
-
|
|
151
|
+
1. **Always show project status first** (if PROJECT_STATUS.yml exists)
|
|
152
|
+
2. **Then show change status** (if change-id provided)
|
|
153
|
+
3. **Include /pstatus in commands** to encourage keeping status updated
|
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Project Status
|
|
3
|
+
description: Update PROJECT_STATUS.yml for cross-session context
|
|
4
|
+
category: Multi-Agent
|
|
5
|
+
tags: [status, context, cross-session]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Usage
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
/pstatus
|
|
12
|
+
/pstatus quick # Quick update (outdated sections only)
|
|
13
|
+
/pstatus full # Full review (all sections)
|
|
14
|
+
/pstatus blockers # Update blockers section only
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## What It Does
|
|
18
|
+
|
|
19
|
+
Interactive update of `PROJECT_STATUS.yml` - the cross-session context file that helps Claude understand project state in new sessions.
|
|
20
|
+
|
|
21
|
+
**WHY this exists:** New Claude sessions lose context about infrastructure, blockers, and priorities. This command helps maintain that context.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Step 1: Check File Exists
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
const statusPath = 'PROJECT_STATUS.yml'
|
|
29
|
+
|
|
30
|
+
if (!fileExists(statusPath)) {
|
|
31
|
+
output(`
|
|
32
|
+
š PROJECT_STATUS.yml not found
|
|
33
|
+
|
|
34
|
+
This file provides cross-session context for Claude.
|
|
35
|
+
It helps new sessions understand:
|
|
36
|
+
- Infrastructure state (DB, API, tunnels)
|
|
37
|
+
- Blockers (waiting for domain, API keys)
|
|
38
|
+
- Completed work & next priorities
|
|
39
|
+
|
|
40
|
+
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
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Step 2: Read Current Status
|
|
58
|
+
|
|
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))
|
|
63
|
+
|
|
64
|
+
output(`
|
|
65
|
+
š PROJECT_STATUS.yml
|
|
66
|
+
|
|
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
|
+
`)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Step 3: Select Update Mode
|
|
78
|
+
|
|
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
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Step 4: Section-by-Section Updates
|
|
100
|
+
|
|
101
|
+
### 4.1 Update `last_updated`
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
// Always update timestamp
|
|
105
|
+
status.last_updated = new Date().toISOString().split('T')[0]
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### 4.2 Update `current_focus`
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
output(`
|
|
112
|
+
š 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...
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### 4.3 Update `completed_changes`
|
|
135
|
+
|
|
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
|
+
`)
|
|
141
|
+
|
|
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
|
+
})
|
|
148
|
+
|
|
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')}
|
|
153
|
+
|
|
154
|
+
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
|
+
```
|
|
181
|
+
|
|
182
|
+
### 4.4 Update `infrastructure`
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
output(`
|
|
186
|
+
šļø 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
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### 4.5 Update `blockers`
|
|
245
|
+
|
|
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...
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### 4.6 Update `next_priorities`
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
output(`
|
|
273
|
+
šÆ 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...
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### 4.7 Update `notes`
|
|
295
|
+
|
|
296
|
+
```typescript
|
|
297
|
+
output(`
|
|
298
|
+
š 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...
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
321
|
+
## Step 5: Write Changes
|
|
322
|
+
|
|
323
|
+
```typescript
|
|
324
|
+
// Show diff
|
|
325
|
+
output(`
|
|
326
|
+
š Changes to be written:
|
|
327
|
+
|
|
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(`
|
|
346
|
+
ā
PROJECT_STATUS.yml updated!
|
|
347
|
+
|
|
348
|
+
Last updated: ${status.last_updated}
|
|
349
|
+
Changes saved: ${countChanges(originalStatus, status)} section(s)
|
|
350
|
+
`)
|
|
351
|
+
} else {
|
|
352
|
+
output('Changes discarded.')
|
|
353
|
+
}
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## Quick Mode Behavior
|
|
359
|
+
|
|
360
|
+
When `/pstatus quick` is used:
|
|
361
|
+
|
|
362
|
+
1. Check `last_updated` - if > 7 days, suggest full review
|
|
363
|
+
2. Auto-detect archived changes not in `completed_changes`
|
|
364
|
+
3. Show current blockers - ask if any resolved
|
|
365
|
+
4. Skip unchanged sections
|
|
366
|
+
5. Update timestamp and save
|
|
367
|
+
|
|
368
|
+
---
|
|
369
|
+
|
|
370
|
+
## Output Example
|
|
371
|
+
|
|
372
|
+
```
|
|
373
|
+
š PROJECT_STATUS.yml Review
|
|
374
|
+
|
|
375
|
+
Last updated: 2025-11-25 (6 days ago)
|
|
376
|
+
Current focus: "Building authentication system"
|
|
377
|
+
Active change: auth-system
|
|
378
|
+
|
|
379
|
+
š¦ Found 1 archived change not in completed_changes:
|
|
380
|
+
- infrastructure-cicd
|
|
381
|
+
|
|
382
|
+
Add it? (yes)
|
|
383
|
+
ā
Added: infrastructure-cicd
|
|
384
|
+
|
|
385
|
+
š§ Blockers (1)
|
|
386
|
+
- domain-config: Need domain for Cloudflare (blocks: production-launch)
|
|
387
|
+
|
|
388
|
+
Any blockers resolved? (no)
|
|
389
|
+
|
|
390
|
+
šÆ Priorities look current.
|
|
391
|
+
|
|
392
|
+
š Changes to be written:
|
|
393
|
+
+ completed_changes: infrastructure-cicd
|
|
394
|
+
~ last_updated: 2025-11-25 ā 2025-12-01
|
|
395
|
+
|
|
396
|
+
Save changes? (yes)
|
|
397
|
+
|
|
398
|
+
ā
PROJECT_STATUS.yml updated!
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
---
|
|
402
|
+
|
|
403
|
+
## Integration with Other Commands
|
|
404
|
+
|
|
405
|
+
| Command | Integration |
|
|
406
|
+
|---------|-------------|
|
|
407
|
+
| `/csetup` | Reads PROJECT_STATUS.yml for context, updates `current_focus.active_change` |
|
|
408
|
+
| `/cstatus` | Shows both change status AND project status summary |
|
|
409
|
+
| `/openspec:archive` | Prompts to add to `completed_changes` |
|
|
410
|
+
|
|
411
|
+
---
|
|
412
|
+
|
|
413
|
+
## Blocker Detection Patterns
|
|
414
|
+
|
|
415
|
+
Main Claude should recognize these phrases and suggest adding blockers:
|
|
416
|
+
|
|
417
|
+
| Pattern | Example |
|
|
418
|
+
|---------|---------|
|
|
419
|
+
| "waiting for..." | "waiting for domain configuration" |
|
|
420
|
+
| "need X from..." | "need API key from client" |
|
|
421
|
+
| "blocked by..." | "blocked by DevOps team" |
|
|
422
|
+
| "pending..." | "pending approval" |
|
|
423
|
+
| "can't proceed until..." | "can't proceed until payment gateway ready" |
|
|
424
|
+
|
|
425
|
+
When detected, prompt:
|
|
426
|
+
```
|
|
427
|
+
This sounds like an external blocker. Add to PROJECT_STATUS.yml?
|
|
428
|
+
- id: {suggested-id}
|
|
429
|
+
- description: {extracted-description}
|
|
430
|
+
- blocks: [{related-work}]
|
|
431
|
+
```
|