@champpaba/claude-agent-kit 3.2.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.
- package/.claude/CHANGELOG.md +129 -0
- package/.claude/CLAUDE.md +57 -2
- package/.claude/agents/02-uxui-frontend.md +99 -16
- package/.claude/agents/07-ux-tester.md +307 -108
- package/.claude/commands/cdev.md +506 -496
- package/.claude/commands/csetup.md +985 -1062
- package/.claude/commands/cstatus.md +62 -53
- package/.claude/commands/cview.md +49 -50
- package/.claude/commands/designsetup.md +958 -1910
- package/.claude/commands/extract.md +480 -743
- package/.claude/commands/pageplan.md +155 -153
- package/.claude/commands/pstatus.md +322 -254
- package/.claude/lib/README.md +8 -0
- package/.claude/lib/design-validator.md +330 -0
- package/package.json +1 -1
|
@@ -43,162 +43,163 @@ Please create the change with OpenSpec first
|
|
|
43
43
|
|
|
44
44
|
**WHY:** Cross-session context helps understand blockers and infrastructure state before starting work.
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
const projectStatusPath = 'PROJECT_STATUS.yml'
|
|
46
|
+
1. Check if `PROJECT_STATUS.yml` exists in project root
|
|
48
47
|
|
|
49
|
-
|
|
50
|
-
const projectStatus = parseYaml(Read(projectStatusPath))
|
|
48
|
+
**If file exists:**
|
|
51
49
|
|
|
52
|
-
|
|
50
|
+
2. Read the file `PROJECT_STATUS.yml`
|
|
53
51
|
|
|
54
|
-
|
|
55
|
-
if (projectStatus.current_focus?.description) {
|
|
56
|
-
output(` Focus: ${projectStatus.current_focus.description}`)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Check for blockers that might affect this change
|
|
60
|
-
if (projectStatus.blockers?.length > 0) {
|
|
61
|
-
const relevantBlockers = projectStatus.blockers.filter(b =>
|
|
62
|
-
b.blocks?.some(blocked =>
|
|
63
|
-
blocked.toLowerCase().includes(changeId.toLowerCase()) ||
|
|
64
|
-
changeId.toLowerCase().includes(blocked.toLowerCase())
|
|
65
|
-
)
|
|
66
|
-
)
|
|
67
|
-
|
|
68
|
-
if (relevantBlockers.length > 0) {
|
|
69
|
-
output(`\n ā ļø Potential blockers for this change:`)
|
|
70
|
-
relevantBlockers.forEach(b => {
|
|
71
|
-
output(` - ${b.id}: ${b.description}`)
|
|
72
|
-
})
|
|
73
|
-
output(`\n Consider resolving blockers before starting.`)
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Show infrastructure status summary
|
|
78
|
-
if (projectStatus.infrastructure) {
|
|
79
|
-
const downServices = Object.entries(projectStatus.infrastructure)
|
|
80
|
-
.filter(([_, info]) => info.status === 'down' || info.status === 'degraded')
|
|
81
|
-
|
|
82
|
-
if (downServices.length > 0) {
|
|
83
|
-
output(`\n ā ļø Infrastructure issues:`)
|
|
84
|
-
downServices.forEach(([service, info]) => {
|
|
85
|
-
output(` - ${service}: ${info.status}${info.notes ? ` (${info.notes})` : ''}`)
|
|
86
|
-
})
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Check pending follow-ups that might affect this change (v2.1.6)
|
|
91
|
-
if (projectStatus.pending_followups?.length > 0) {
|
|
92
|
-
const proposalPath = `openspec/changes/${changeId}/proposal.md`
|
|
93
|
-
const proposal = fileExists(proposalPath) ? Read(proposalPath).toLowerCase() : ''
|
|
94
|
-
|
|
95
|
-
const relatedPending = projectStatus.pending_followups.filter(p => {
|
|
96
|
-
const affects = p.affects || []
|
|
97
|
-
return affects.some(pattern => {
|
|
98
|
-
const patternLower = pattern.toLowerCase()
|
|
99
|
-
return changeId.toLowerCase().includes(patternLower) ||
|
|
100
|
-
proposal.includes(patternLower) ||
|
|
101
|
-
(patternLower.includes('db') && proposal.includes('table')) ||
|
|
102
|
-
(patternLower.includes('schema') && proposal.includes('model')) ||
|
|
103
|
-
(patternLower.includes('migration') && proposal.includes('database'))
|
|
104
|
-
})
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
if (relatedPending.length > 0) {
|
|
108
|
-
output(`\n ā ļø Found related pending follow-ups:`)
|
|
109
|
-
relatedPending.forEach(p => {
|
|
110
|
-
output(` - "${p.item}" (from ${p.from_change})`)
|
|
111
|
-
output(` Reason: ${p.reason}`)
|
|
112
|
-
if (p.affects) output(` Affects: ${p.affects.join(', ')}`)
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
output(`\n This change may be affected by unresolved follow-ups.`)
|
|
116
|
-
output(` Options:`)
|
|
117
|
-
output(` 1. Continue anyway (risk: issues like schema sync)`)
|
|
118
|
-
output(` 2. Address follow-up first (create separate proposal)`)
|
|
119
|
-
output(` 3. Include follow-up in this change's scope`)
|
|
120
|
-
|
|
121
|
-
const choice = await askUser(`\n How to proceed? (1/2/3)`)
|
|
122
|
-
|
|
123
|
-
if (choice === '2') {
|
|
124
|
-
output(`\n ā Setup paused. Create proposal for pending follow-up first.`)
|
|
125
|
-
return
|
|
126
|
-
} else if (choice === '3') {
|
|
127
|
-
output(`\n ā¹ļø Remember to include follow-up items in tasks.md`)
|
|
128
|
-
} else {
|
|
129
|
-
output(`\n ā ļø Continuing with caution. Monitor for related issues.`)
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Check stale status
|
|
135
|
-
const lastUpdated = new Date(projectStatus.last_updated)
|
|
136
|
-
const daysSinceUpdate = Math.floor((Date.now() - lastUpdated) / (1000 * 60 * 60 * 24))
|
|
137
|
-
const staleThreshold = projectStatus._config?.stale_warning_days || 7
|
|
138
|
-
|
|
139
|
-
if (daysSinceUpdate > staleThreshold) {
|
|
140
|
-
output(`\n ā¹ļø PROJECT_STATUS.yml last updated ${daysSinceUpdate} days ago.`)
|
|
141
|
-
output(` Consider running /pstatus to refresh.`)
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// Update active change
|
|
145
|
-
if (projectStatus.current_focus?.active_change !== changeId) {
|
|
146
|
-
output(`\n š Update current_focus.active_change to "${changeId}"? (yes/no)`)
|
|
147
|
-
const updateFocus = await askUser()
|
|
148
|
-
if (updateFocus) {
|
|
149
|
-
projectStatus.current_focus = projectStatus.current_focus || {}
|
|
150
|
-
projectStatus.current_focus.active_change = changeId
|
|
151
|
-
projectStatus.last_updated = new Date().toISOString().split('T')[0]
|
|
152
|
-
Write(projectStatusPath, toYaml(projectStatus))
|
|
153
|
-
output(` ā
Updated active_change to "${changeId}"`)
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
output(``) // Blank line
|
|
158
|
-
}
|
|
52
|
+
3. Output header:
|
|
159
53
|
```
|
|
54
|
+
š Project Context (from PROJECT_STATUS.yml)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
4. **If `current_focus.description` exists:**
|
|
58
|
+
- Output: ` Focus: {description}`
|
|
59
|
+
|
|
60
|
+
5. **Check for blockers that might affect this change:**
|
|
61
|
+
- Look at `blockers` array
|
|
62
|
+
- For each blocker, check if `blocks` field contains:
|
|
63
|
+
- The change-id (case-insensitive)
|
|
64
|
+
- OR any keyword from change-id
|
|
65
|
+
- OR vice versa
|
|
66
|
+
|
|
67
|
+
**If relevant blockers found:**
|
|
68
|
+
```
|
|
69
|
+
ā ļø Potential blockers for this change:
|
|
70
|
+
- {blocker.id}: {blocker.description}
|
|
71
|
+
... (for each relevant blocker)
|
|
72
|
+
|
|
73
|
+
Consider resolving blockers before starting.
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
6. **Check infrastructure status:**
|
|
77
|
+
- Look at `infrastructure` section
|
|
78
|
+
- Find services where `status` is 'down' or 'degraded'
|
|
79
|
+
|
|
80
|
+
**If degraded services found:**
|
|
81
|
+
```
|
|
82
|
+
ā ļø Infrastructure issues:
|
|
83
|
+
- {service}: {status} ({notes if present})
|
|
84
|
+
... (for each down/degraded service)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
7. **Check pending follow-ups (v2.1.6):**
|
|
88
|
+
- Read `openspec/changes/{changeId}/proposal.md` (if exists)
|
|
89
|
+
- Look at `pending_followups` array
|
|
90
|
+
- For each pending item, check if `affects` field matches:
|
|
91
|
+
- Change-id contains pattern
|
|
92
|
+
- OR proposal contains pattern
|
|
93
|
+
- OR special database patterns (dbātable, schemaāmodel, migrationādatabase)
|
|
94
|
+
|
|
95
|
+
**If related pending items found:**
|
|
96
|
+
```
|
|
97
|
+
ā ļø Found related pending follow-ups:
|
|
98
|
+
- "{item}" (from {from_change})
|
|
99
|
+
Reason: {reason}
|
|
100
|
+
Affects: {affects list}
|
|
101
|
+
... (for each related pending)
|
|
102
|
+
|
|
103
|
+
This change may be affected by unresolved follow-ups.
|
|
104
|
+
Options:
|
|
105
|
+
1. Continue anyway (risk: issues like schema sync)
|
|
106
|
+
2. Address follow-up first (create separate proposal)
|
|
107
|
+
3. Include follow-up in this change's scope
|
|
108
|
+
|
|
109
|
+
How to proceed? (1/2/3)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
ā Wait for user input
|
|
113
|
+
|
|
114
|
+
**If user chose 2:**
|
|
115
|
+
```
|
|
116
|
+
ā Setup paused. Create proposal for pending follow-up first.
|
|
117
|
+
```
|
|
118
|
+
ā STOP execution, exit command
|
|
119
|
+
|
|
120
|
+
**If user chose 3:**
|
|
121
|
+
```
|
|
122
|
+
ā¹ļø Remember to include follow-up items in tasks.md
|
|
123
|
+
```
|
|
124
|
+
ā Continue
|
|
125
|
+
|
|
126
|
+
**If user chose 1 or other:**
|
|
127
|
+
```
|
|
128
|
+
ā ļø Continuing with caution. Monitor for related issues.
|
|
129
|
+
```
|
|
130
|
+
ā Continue
|
|
131
|
+
|
|
132
|
+
8. **Check if status is stale:**
|
|
133
|
+
- Calculate days since `last_updated`
|
|
134
|
+
- Compare with `_config.stale_warning_days` (default: 7)
|
|
135
|
+
|
|
136
|
+
**If days > threshold:**
|
|
137
|
+
```
|
|
138
|
+
ā¹ļø PROJECT_STATUS.yml last updated {days} days ago.
|
|
139
|
+
Consider running /pstatus to refresh.
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
9. **Update active change if needed:**
|
|
143
|
+
- Check if `current_focus.active_change` equals current changeId
|
|
144
|
+
|
|
145
|
+
**If NOT equal:**
|
|
146
|
+
- Ask user:
|
|
147
|
+
```
|
|
148
|
+
š Update current_focus.active_change to "{changeId}"? (yes/no)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**If user approves:**
|
|
152
|
+
- Set `current_focus.active_change = changeId`
|
|
153
|
+
- Set `last_updated = today's date (YYYY-MM-DD)`
|
|
154
|
+
- Write updated YAML back to `PROJECT_STATUS.yml`
|
|
155
|
+
- Output: ` ā
Updated active_change to "{changeId}"`
|
|
156
|
+
|
|
157
|
+
10. Output blank line
|
|
158
|
+
|
|
159
|
+
**If file does not exist:**
|
|
160
|
+
- Skip to next step (no output needed)
|
|
160
161
|
|
|
161
162
|
### Step 1.6: Memory Context Query (v2.2.0 - claude-mem Integration)
|
|
162
163
|
|
|
163
164
|
**WHY:** Query past work to leverage decisions, avoid repeating mistakes, and maintain consistency.
|
|
164
165
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const proposalPath = `openspec/changes/${changeId}/proposal.md`
|
|
169
|
-
const proposalContent = fileExists(proposalPath) ? Read(proposalPath) : ''
|
|
170
|
-
const proposalTitle = proposalContent.match(/^#\s+(.+)/m)?.[1] || changeId
|
|
171
|
-
|
|
172
|
-
output(`\nš§ Querying claude-mem for related past work...`)
|
|
166
|
+
1. Extract keywords from change-id:
|
|
167
|
+
- Replace hyphens with spaces
|
|
168
|
+
- Example: `add-auth-system` ā `add auth system`
|
|
173
169
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
`decisions about ${changeKeywords}`,
|
|
178
|
-
`bugs related to ${changeKeywords}`,
|
|
179
|
-
`implementations of ${changeKeywords}`
|
|
180
|
-
]
|
|
170
|
+
2. Read `openspec/changes/{changeId}/proposal.md` (if exists)
|
|
171
|
+
- Extract first heading line (proposal title)
|
|
172
|
+
- If no file or no heading, use changeId as title
|
|
181
173
|
|
|
182
|
-
|
|
183
|
-
|
|
174
|
+
3. Output:
|
|
175
|
+
```
|
|
176
|
+
š§ Querying claude-mem for related past work...
|
|
177
|
+
```
|
|
184
178
|
|
|
185
|
-
|
|
179
|
+
4. Query claude-mem using natural language questions:
|
|
180
|
+
- "decisions about {keywords}"
|
|
181
|
+
- "bugs related to {keywords}"
|
|
182
|
+
- "implementations of {keywords}"
|
|
186
183
|
|
|
187
|
-
|
|
188
|
-
// and mem-search skill returns relevant observations
|
|
184
|
+
**Note:** The mem-search skill will auto-invoke when you ask these questions naturally.
|
|
189
185
|
|
|
190
|
-
|
|
191
|
-
output(` (Results will be included in research-checklist.md if relevant)`)
|
|
192
|
-
output(``)
|
|
186
|
+
5. Store results in memory for later use
|
|
193
187
|
|
|
194
|
-
|
|
195
|
-
|
|
188
|
+
6. Output:
|
|
189
|
+
```
|
|
190
|
+
Searched for: {keywords}
|
|
191
|
+
(Results will be included in pre-work-context.md if relevant)
|
|
196
192
|
```
|
|
197
193
|
|
|
198
|
-
|
|
194
|
+
7. Output blank line
|
|
199
195
|
|
|
200
|
-
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
**Integration with pre-work-context.md:**
|
|
201
199
|
|
|
200
|
+
When generating `pre-work-context.md` (Step 2.6.7), include a "Past Learnings" section:
|
|
201
|
+
|
|
202
|
+
**If relevant observations found:**
|
|
202
203
|
```markdown
|
|
203
204
|
## Past Learnings (from claude-mem)
|
|
204
205
|
|
|
@@ -206,15 +207,15 @@ When generating `research-checklist.md` (Step 2.6), include a "Past Learnings" s
|
|
|
206
207
|
|
|
207
208
|
| ID | Type | Summary | Relevance |
|
|
208
209
|
|----|------|---------|-----------|
|
|
209
|
-
| #
|
|
210
|
-
|
|
210
|
+
| #{id} | {type} | {summary} | {HIGH/MEDIUM/LOW} |
|
|
211
|
+
... (for each observation)
|
|
211
212
|
|
|
212
213
|
### Key Takeaways:
|
|
213
|
-
-
|
|
214
|
-
-
|
|
214
|
+
- {takeaway 1}
|
|
215
|
+
- {takeaway 2}
|
|
215
216
|
```
|
|
216
217
|
|
|
217
|
-
If no relevant observations found
|
|
218
|
+
**If no relevant observations found:**
|
|
218
219
|
```markdown
|
|
219
220
|
## Past Learnings (from claude-mem)
|
|
220
221
|
|
|
@@ -236,86 +237,98 @@ Read in order:
|
|
|
236
237
|
|
|
237
238
|
> **Updated v2.0.0:** Validate design files + read page-plan.md if exists
|
|
238
239
|
|
|
239
|
-
|
|
240
|
-
// Detect if change involves UI/frontend work
|
|
241
|
-
const tasksContent = Read('openspec/changes/{change-id}/tasks.md')
|
|
242
|
-
const hasFrontend = tasksContent.toLowerCase().match(/(ui|component|page|frontend|design|responsive)/i)
|
|
243
|
-
|
|
244
|
-
let tokens = null
|
|
245
|
-
let pagePlan = null
|
|
246
|
-
let pageType = 'generic'
|
|
247
|
-
|
|
248
|
-
if (hasFrontend) {
|
|
249
|
-
output(`\nšØ UI work detected - validating design system...`)
|
|
250
|
-
|
|
251
|
-
const tokensPath = 'design-system/data.yaml' // v2.0 tokens
|
|
252
|
-
const readmePath = 'design-system/README.md'
|
|
253
|
-
const pagePlanPath = `openspec/changes/${changeId}/page-plan.md`
|
|
254
|
-
|
|
255
|
-
const hasTokens = fileExists(tokensPath)
|
|
256
|
-
const hasReadme = fileExists(readmePath)
|
|
257
|
-
const hasPagePlan = fileExists(pagePlanPath)
|
|
258
|
-
|
|
259
|
-
// ========== LOAD data.yaml (v2.0 structure) ==========
|
|
260
|
-
if (hasTokens) {
|
|
261
|
-
tokens = parseYaml(Read(tokensPath))
|
|
262
|
-
output(`ā
data.yaml Loaded:`)
|
|
263
|
-
output(` - Style: ${tokens.style.name}`)
|
|
264
|
-
output(` - Theme: ${tokens.theme.name}`)
|
|
265
|
-
output(` - Animations: ${tokens.animations.enabled ? 'Enabled' : 'Disabled'}`)
|
|
266
|
-
}
|
|
240
|
+
1. Read `openspec/changes/{change-id}/tasks.md`
|
|
267
241
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
// Extract page type from page-plan.md
|
|
274
|
-
const pageTypeMatch = pagePlan.match(/Page Type:\*\*\s*(.*)/i)
|
|
275
|
-
if (pageTypeMatch) {
|
|
276
|
-
pageType = pageTypeMatch[1].trim().toLowerCase()
|
|
277
|
-
output(` - Page Type: ${pageType}`)
|
|
278
|
-
}
|
|
279
|
-
} else {
|
|
280
|
-
output(`ā¹ļø page-plan.md not found (optional)`)
|
|
281
|
-
output(` ā Run /pageplan first for better component planning`)
|
|
282
|
-
}
|
|
242
|
+
2. **Check if change involves UI/frontend work:**
|
|
243
|
+
- Search tasks.md (case-insensitive) for keywords:
|
|
244
|
+
- ui, component, page, frontend, design, responsive
|
|
245
|
+
- Store result as `hasFrontend`
|
|
283
246
|
|
|
284
|
-
|
|
285
|
-
warn(`
|
|
286
|
-
ā ļø WARNING: UI work detected but design system incomplete!
|
|
247
|
+
**If hasFrontend is TRUE:**
|
|
287
248
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
${hasPagePlan ? 'ā
' : 'ā'} page-plan.md
|
|
292
|
-
|
|
293
|
-
This may result in:
|
|
294
|
-
- Inconsistent colors (random hex codes)
|
|
295
|
-
- Arbitrary spacing (p-5, gap-7)
|
|
296
|
-
- Duplicate components
|
|
297
|
-
|
|
298
|
-
Recommendation:
|
|
299
|
-
1. Run: /designsetup
|
|
300
|
-
2. Run: /pageplan @prd.md (optional but recommended)
|
|
301
|
-
3. Then: /csetup ${changeId}
|
|
302
|
-
|
|
303
|
-
Continue anyway? (yes/no)
|
|
304
|
-
`)
|
|
305
|
-
|
|
306
|
-
const answer = await askUser()
|
|
307
|
-
if (answer === 'no') {
|
|
308
|
-
return error('Setup cancelled. Run /designsetup first.')
|
|
309
|
-
}
|
|
310
|
-
} else {
|
|
311
|
-
output(`ā
Design System Ready`)
|
|
312
|
-
output(` - README.md ā (human-readable)`)
|
|
313
|
-
output(` - data.yaml ā`)
|
|
314
|
-
if (hasPagePlan) output(` - page-plan.md ā`)
|
|
315
|
-
}
|
|
316
|
-
}
|
|
249
|
+
3. Output:
|
|
250
|
+
```
|
|
251
|
+
šØ UI work detected - validating design system...
|
|
317
252
|
```
|
|
318
253
|
|
|
254
|
+
4. Check if these files exist:
|
|
255
|
+
- `design-system/data.yaml`
|
|
256
|
+
- `design-system/README.md`
|
|
257
|
+
- `openspec/changes/{changeId}/page-plan.md`
|
|
258
|
+
|
|
259
|
+
5. **If `data.yaml` exists:**
|
|
260
|
+
- Read and parse `design-system/data.yaml`
|
|
261
|
+
- Output:
|
|
262
|
+
```
|
|
263
|
+
ā
data.yaml Loaded:
|
|
264
|
+
- Style: {tokens.style.name}
|
|
265
|
+
- Theme: {tokens.theme.name}
|
|
266
|
+
- Animations: {Enabled/Disabled based on tokens.animations.enabled}
|
|
267
|
+
```
|
|
268
|
+
- Store parsed tokens for later use
|
|
269
|
+
|
|
270
|
+
6. **If `page-plan.md` exists:**
|
|
271
|
+
- Read `openspec/changes/{changeId}/page-plan.md`
|
|
272
|
+
- Output:
|
|
273
|
+
```
|
|
274
|
+
ā
page-plan.md Found
|
|
275
|
+
```
|
|
276
|
+
- Search for line matching pattern: `Page Type:**` or `**Page Type:**`
|
|
277
|
+
- Extract page type value (trim whitespace, convert to lowercase)
|
|
278
|
+
- Output: ` - Page Type: {pageType}`
|
|
279
|
+
- Store page type for later use
|
|
280
|
+
|
|
281
|
+
**If `page-plan.md` does NOT exist:**
|
|
282
|
+
```
|
|
283
|
+
ā¹ļø page-plan.md not found (optional)
|
|
284
|
+
ā Run /pageplan first for better component planning
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
7. **Check completeness:**
|
|
288
|
+
|
|
289
|
+
**If `data.yaml` OR `README.md` is missing:**
|
|
290
|
+
- Output warning:
|
|
291
|
+
```
|
|
292
|
+
ā ļø WARNING: UI work detected but design system incomplete!
|
|
293
|
+
|
|
294
|
+
Found:
|
|
295
|
+
{ā
/ā} README.md (human-readable)
|
|
296
|
+
{ā
/ā} data.yaml
|
|
297
|
+
{ā
/ā} page-plan.md
|
|
298
|
+
|
|
299
|
+
This may result in:
|
|
300
|
+
- Inconsistent colors (random hex codes)
|
|
301
|
+
- Arbitrary spacing (p-5, gap-7)
|
|
302
|
+
- Duplicate components
|
|
303
|
+
|
|
304
|
+
Recommendation:
|
|
305
|
+
1. Run: /designsetup
|
|
306
|
+
2. Run: /pageplan @prd.md (optional but recommended)
|
|
307
|
+
3. Then: /csetup {changeId}
|
|
308
|
+
|
|
309
|
+
Continue anyway? (yes/no)
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
ā Wait for user input
|
|
313
|
+
|
|
314
|
+
**If user answered 'no':**
|
|
315
|
+
- Output: `Setup cancelled. Run /designsetup first.`
|
|
316
|
+
- STOP execution, exit command
|
|
317
|
+
|
|
318
|
+
**If user answered 'yes' or other:**
|
|
319
|
+
- Continue to next step
|
|
320
|
+
|
|
321
|
+
**If both `data.yaml` AND `README.md` exist:**
|
|
322
|
+
```
|
|
323
|
+
ā
Design System Ready
|
|
324
|
+
- README.md ā (human-readable)
|
|
325
|
+
- data.yaml ā
|
|
326
|
+
- page-plan.md ā (only if exists)
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
**If hasFrontend is FALSE:**
|
|
330
|
+
- Skip all steps above, continue to Step 2.6
|
|
331
|
+
|
|
319
332
|
---
|
|
320
333
|
|
|
321
334
|
### Step 2.6: Generate Pre-Work Context (v3.2.0 - Consolidated)
|
|
@@ -626,237 +639,199 @@ Do NOT treat this as pseudocode. EXECUTE these instructions.
|
|
|
626
639
|
> **NEW:** Verify chosen libraries support ALL spec requirements before proceeding
|
|
627
640
|
> **WHY:** Prevents spec drift - discovering during implementation that library doesn't support requirements
|
|
628
641
|
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
let detectedLibraries = []
|
|
634
|
-
let specRequirements = []
|
|
635
|
-
let capabilityGaps = []
|
|
636
|
-
let customImplementationRequired = []
|
|
637
|
-
|
|
638
|
-
// 1. Extract spec requirements from design.md
|
|
639
|
-
const designPath = `openspec/changes/${changeId}/design.md`
|
|
640
|
-
if (!fileExists(designPath)) {
|
|
641
|
-
output(` ā ļø No design.md found - skipping library validation`)
|
|
642
|
-
} else {
|
|
643
|
-
const designContent = Read(designPath)
|
|
644
|
-
|
|
645
|
-
// 2. Find library mentions in spec
|
|
646
|
-
const libraryPatterns = {
|
|
647
|
-
'better-auth': {
|
|
648
|
-
patterns: ['better-auth', 'betterauth'],
|
|
649
|
-
context7Id: null, // No Context7 mapping yet
|
|
650
|
-
knownLimitations: [
|
|
651
|
-
{ feature: 'refresh token rotation', supported: false },
|
|
652
|
-
{ feature: 'redis session storage', supported: false },
|
|
653
|
-
{ feature: 'jwt plugin', supported: true },
|
|
654
|
-
{ feature: 'bearer plugin', supported: true },
|
|
655
|
-
{ feature: 'session-based auth', supported: true }
|
|
656
|
-
]
|
|
657
|
-
},
|
|
658
|
-
'nextauth': {
|
|
659
|
-
patterns: ['next-auth', 'nextauth', 'authjs'],
|
|
660
|
-
context7Id: '/nextauthjs/next-auth',
|
|
661
|
-
knownLimitations: []
|
|
662
|
-
},
|
|
663
|
-
'lucia': {
|
|
664
|
-
patterns: ['lucia', 'lucia-auth'],
|
|
665
|
-
context7Id: '/lucia-auth/lucia',
|
|
666
|
-
knownLimitations: []
|
|
667
|
-
},
|
|
668
|
-
'prisma': {
|
|
669
|
-
patterns: ['prisma'],
|
|
670
|
-
context7Id: '/prisma/prisma',
|
|
671
|
-
knownLimitations: []
|
|
672
|
-
},
|
|
673
|
-
'drizzle': {
|
|
674
|
-
patterns: ['drizzle'],
|
|
675
|
-
context7Id: '/drizzle-team/drizzle-orm',
|
|
676
|
-
knownLimitations: []
|
|
677
|
-
}
|
|
678
|
-
}
|
|
642
|
+
1. Output:
|
|
643
|
+
```
|
|
644
|
+
š Validating Library Capabilities...
|
|
645
|
+
```
|
|
679
646
|
|
|
680
|
-
|
|
681
|
-
for (const [libName, config] of Object.entries(libraryPatterns)) {
|
|
682
|
-
if (config.patterns.some(p => designContent.toLowerCase().includes(p))) {
|
|
683
|
-
detectedLibraries.push({ name: libName, ...config })
|
|
684
|
-
}
|
|
685
|
-
}
|
|
647
|
+
2. Check if `openspec/changes/{changeId}/design.md` exists
|
|
686
648
|
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
649
|
+
**If design.md does NOT exist:**
|
|
650
|
+
```
|
|
651
|
+
ā ļø No design.md found - skipping library validation
|
|
652
|
+
```
|
|
653
|
+
ā Skip to Step 3
|
|
654
|
+
|
|
655
|
+
**If design.md exists:**
|
|
656
|
+
|
|
657
|
+
3. Read `openspec/changes/{changeId}/design.md`
|
|
658
|
+
|
|
659
|
+
4. **Detect libraries mentioned in design.md:**
|
|
660
|
+
|
|
661
|
+
Search for these library patterns (case-insensitive):
|
|
662
|
+
|
|
663
|
+
| Library | Search Patterns | Context7 ID | Known Limitations |
|
|
664
|
+
|---------|----------------|-------------|-------------------|
|
|
665
|
+
| better-auth | better-auth, betterauth | (none) | ā refresh token rotation, ā redis session, ā
jwt plugin, ā
bearer plugin, ā
session-based auth |
|
|
666
|
+
| nextauth | next-auth, nextauth, authjs | /nextauthjs/next-auth | (none) |
|
|
667
|
+
| lucia | lucia, lucia-auth | /lucia-auth/lucia | (none) |
|
|
668
|
+
| prisma | prisma | /prisma/prisma | (none) |
|
|
669
|
+
| drizzle | drizzle | /drizzle-team/drizzle-orm | (none) |
|
|
670
|
+
|
|
671
|
+
**If libraries found:**
|
|
672
|
+
```
|
|
673
|
+
š Libraries in Spec:
|
|
674
|
+
- {library1}
|
|
675
|
+
- {library2}
|
|
676
|
+
...
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
5. **Extract requirements from design.md:**
|
|
680
|
+
|
|
681
|
+
Search for these requirement patterns:
|
|
682
|
+
|
|
683
|
+
| Requirement | Search Pattern |
|
|
684
|
+
|-------------|----------------|
|
|
685
|
+
| JWT access token | jwt + (access OR token) + (number + min) |
|
|
686
|
+
| Refresh token | refresh token |
|
|
687
|
+
| Token rotation | rotation OR rotate |
|
|
688
|
+
| Redis session | redis + session (any order) |
|
|
689
|
+
| Bearer token | bearer + (token OR auth) |
|
|
690
|
+
| OAuth providers | oauth OR google OR github OR social login |
|
|
691
|
+
| Rate limiting | rate limit |
|
|
692
|
+
| Account lockout | lockout OR lock account |
|
|
693
|
+
|
|
694
|
+
**If requirements found:**
|
|
695
|
+
```
|
|
696
|
+
š Spec Requirements Found:
|
|
697
|
+
- {requirement1}
|
|
698
|
+
- {requirement2}
|
|
699
|
+
...
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
6. **Check each library's capabilities:**
|
|
703
|
+
|
|
704
|
+
For each detected library:
|
|
705
|
+
```
|
|
706
|
+
š Checking {library} capabilities...
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
For each requirement:
|
|
710
|
+
- Check known limitations database
|
|
711
|
+
- Match requirement with limitation feature (partial match OK)
|
|
712
|
+
|
|
713
|
+
**If known limitation says NOT supported:**
|
|
714
|
+
```
|
|
715
|
+
ā {requirement} - NOT SUPPORTED
|
|
716
|
+
```
|
|
717
|
+
ā Add to capability gaps list
|
|
718
|
+
|
|
719
|
+
**If known limitation says supported:**
|
|
720
|
+
```
|
|
721
|
+
ā
{requirement} - Supported
|
|
722
|
+
```
|
|
723
|
+
|
|
724
|
+
**If unknown (not in limitations database):**
|
|
725
|
+
- **If library has Context7 ID:**
|
|
726
|
+
```
|
|
727
|
+
š {requirement} - Checking Context7...
|
|
728
|
+
ā ļø {requirement} - Verify manually
|
|
729
|
+
```
|
|
730
|
+
- **If NO Context7 ID:**
|
|
731
|
+
```
|
|
732
|
+
ā ļø {requirement} - Verify manually (no Context7 mapping)
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
7. **Report capability gaps (if any):**
|
|
736
|
+
|
|
737
|
+
**If gaps found:**
|
|
738
|
+
```
|
|
739
|
+
ā ļø Library Capability Gaps Detected!
|
|
740
|
+
|
|
741
|
+
The following spec requirements are NOT supported by chosen libraries:
|
|
742
|
+
|
|
743
|
+
{library1}:
|
|
744
|
+
- {requirement1}
|
|
745
|
+
- {requirement2}
|
|
746
|
+
{library2}:
|
|
747
|
+
- {requirement3}
|
|
748
|
+
|
|
749
|
+
This will cause spec drift during implementation!
|
|
750
|
+
|
|
751
|
+
Options:
|
|
752
|
+
A) Change library - Use a library that supports these features
|
|
753
|
+
B) Downgrade spec - Remove unsupported requirements (must document trade-off)
|
|
754
|
+
C) Custom implementation - Build missing features on top of library
|
|
755
|
+
D) Continue anyway - Proceed and let agent handle at implementation time
|
|
756
|
+
```
|
|
757
|
+
|
|
758
|
+
ā Ask user: "How would you like to handle the capability gaps?"
|
|
759
|
+
|
|
760
|
+
**If user chose A (Change library):**
|
|
761
|
+
```
|
|
762
|
+
š Suggested alternative libraries:
|
|
763
|
+
```
|
|
764
|
+
- If better-auth has gaps:
|
|
765
|
+
```
|
|
766
|
+
Instead of better-auth, consider:
|
|
767
|
+
- lucia-auth (supports custom session storage)
|
|
768
|
+
- NextAuth.js (supports refresh token rotation with JWT strategy)
|
|
769
|
+
- Custom implementation with jose + Redis
|
|
770
|
+
|
|
771
|
+
Please update design.md with new library choice and re-run /csetup.
|
|
772
|
+
```
|
|
773
|
+
ā STOP execution, exit command
|
|
774
|
+
|
|
775
|
+
**If user chose B (Downgrade spec):**
|
|
776
|
+
```
|
|
777
|
+
š Update design.md to remove unsupported requirements:
|
|
778
|
+
|
|
779
|
+
```markdown
|
|
780
|
+
### D{n}: Library Capability Alignment
|
|
781
|
+
|
|
782
|
+
**Changed requirements to match {library} capabilities:**
|
|
783
|
+
|
|
784
|
+
- ~~{requirement1}~~ ā Use {library}'s default approach instead
|
|
785
|
+
- ~~{requirement2}~~ ā Use {library}'s default approach instead
|
|
786
|
+
|
|
787
|
+
**Reason:** Library limitation
|
|
788
|
+
**Trade-off:** {requirement1}, {requirement2} not available
|
|
789
|
+
**Date:** {today's date YYYY-MM-DD}
|
|
790
|
+
```
|
|
791
|
+
|
|
792
|
+
Please update design.md and re-run /csetup.
|
|
793
|
+
```
|
|
794
|
+
ā STOP execution, exit command
|
|
795
|
+
|
|
796
|
+
**If user chose C (Custom implementation):**
|
|
797
|
+
```
|
|
798
|
+
š Custom implementation notes for agents:
|
|
799
|
+
|
|
800
|
+
Add to context.md:
|
|
801
|
+
```markdown
|
|
802
|
+
## Custom Implementation Required
|
|
803
|
+
|
|
804
|
+
The following features need custom implementation:
|
|
805
|
+
- {requirement1} (not supported by {library1})
|
|
806
|
+
- {requirement2} (not supported by {library2})
|
|
807
|
+
|
|
808
|
+
Agents should implement these on top of the base library.
|
|
809
|
+
```
|
|
810
|
+
```
|
|
811
|
+
ā Store gaps for context.md generation (Step 7)
|
|
812
|
+
ā Continue to Step 3
|
|
813
|
+
|
|
814
|
+
**If user chose D (Continue anyway):**
|
|
815
|
+
ā Store gaps for agent awareness
|
|
816
|
+
ā Continue to Step 3
|
|
817
|
+
|
|
818
|
+
**If NO gaps found:**
|
|
819
|
+
```
|
|
820
|
+
ā
All spec requirements supported by chosen libraries
|
|
821
|
+
```
|
|
822
|
+
|
|
823
|
+
**If no libraries detected:**
|
|
824
|
+
```
|
|
825
|
+
ā¹ļø No specific libraries detected in spec
|
|
826
|
+
```
|
|
709
827
|
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
for (const lib of detectedLibraries) {
|
|
716
|
-
output(`\nš Checking ${lib.name} capabilities...`)
|
|
717
|
-
|
|
718
|
-
for (const req of specRequirements) {
|
|
719
|
-
// Check known limitations first
|
|
720
|
-
const known = lib.knownLimitations.find(l =>
|
|
721
|
-
req.toLowerCase().includes(l.feature.toLowerCase()) ||
|
|
722
|
-
l.feature.toLowerCase().includes(req.toLowerCase())
|
|
723
|
-
)
|
|
724
|
-
|
|
725
|
-
if (known && !known.supported) {
|
|
726
|
-
output(` ā ${req} - NOT SUPPORTED`)
|
|
727
|
-
capabilityGaps.push({
|
|
728
|
-
library: lib.name,
|
|
729
|
-
requirement: req,
|
|
730
|
-
supported: false,
|
|
731
|
-
note: `${lib.name} does not have built-in support for ${req}`
|
|
732
|
-
})
|
|
733
|
-
} else if (known && known.supported) {
|
|
734
|
-
output(` ā
${req} - Supported`)
|
|
735
|
-
} else {
|
|
736
|
-
// Unknown - query Context7 if available
|
|
737
|
-
if (lib.context7Id) {
|
|
738
|
-
output(` š ${req} - Checking Context7...`)
|
|
739
|
-
// Note: In actual implementation, this would call Context7
|
|
740
|
-
// For now, mark as unknown
|
|
741
|
-
output(` ā ļø ${req} - Verify manually`)
|
|
742
|
-
} else {
|
|
743
|
-
output(` ā ļø ${req} - Verify manually (no Context7 mapping)`)
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
// 6. Report gaps if any
|
|
750
|
-
if (capabilityGaps.length > 0) {
|
|
751
|
-
output(`\nā ļø Library Capability Gaps Detected!`)
|
|
752
|
-
output(``)
|
|
753
|
-
output(`The following spec requirements are NOT supported by chosen libraries:`)
|
|
754
|
-
output(``)
|
|
755
|
-
|
|
756
|
-
// Group by library
|
|
757
|
-
const byLibrary = {}
|
|
758
|
-
capabilityGaps.forEach(g => {
|
|
759
|
-
if (!byLibrary[g.library]) byLibrary[g.library] = []
|
|
760
|
-
byLibrary[g.library].push(g.requirement)
|
|
761
|
-
})
|
|
762
|
-
|
|
763
|
-
for (const [library, reqs] of Object.entries(byLibrary)) {
|
|
764
|
-
output(` ${library}:`)
|
|
765
|
-
reqs.forEach(r => output(` - ${r}`))
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
output(``)
|
|
769
|
-
output(`This will cause spec drift during implementation!`)
|
|
770
|
-
output(``)
|
|
771
|
-
output(`Options:`)
|
|
772
|
-
output(` A) Change library - Use a library that supports these features`)
|
|
773
|
-
output(` B) Downgrade spec - Remove unsupported requirements (must document trade-off)`)
|
|
774
|
-
output(` C) Custom implementation - Build missing features on top of library`)
|
|
775
|
-
output(` D) Continue anyway - Proceed and let agent handle at implementation time`)
|
|
776
|
-
output(``)
|
|
777
|
-
|
|
778
|
-
const decision = await askUserQuestion({
|
|
779
|
-
questions: [{
|
|
780
|
-
question: 'How would you like to handle the capability gaps?',
|
|
781
|
-
header: 'Lib Gaps',
|
|
782
|
-
options: [
|
|
783
|
-
{ label: 'A) Change library', description: 'Switch to a library that supports requirements' },
|
|
784
|
-
{ label: 'B) Downgrade spec', description: 'Update design.md to use what library supports' },
|
|
785
|
-
{ label: 'C) Custom implementation', description: 'Build on top of library (more work)' },
|
|
786
|
-
{ label: 'D) Continue anyway', description: 'Let agent handle during implementation' }
|
|
787
|
-
],
|
|
788
|
-
multiSelect: false
|
|
789
|
-
}]
|
|
790
|
-
})
|
|
791
|
-
|
|
792
|
-
if (decision.includes('A')) {
|
|
793
|
-
output(`\nš Suggested alternative libraries:`)
|
|
794
|
-
for (const [library, reqs] of Object.entries(byLibrary)) {
|
|
795
|
-
if (library === 'better-auth') {
|
|
796
|
-
output(` Instead of ${library}, consider:`)
|
|
797
|
-
output(` - lucia-auth (supports custom session storage)`)
|
|
798
|
-
output(` - NextAuth.js (supports refresh token rotation with JWT strategy)`)
|
|
799
|
-
output(` - Custom implementation with jose + Redis`)
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
output(``)
|
|
803
|
-
output(`Please update design.md with new library choice and re-run /csetup.`)
|
|
804
|
-
return
|
|
805
|
-
} else if (decision.includes('B')) {
|
|
806
|
-
output(`\nš Update design.md to remove unsupported requirements:`)
|
|
807
|
-
output(``)
|
|
808
|
-
output(`\`\`\`markdown`)
|
|
809
|
-
output(`### D{n}: Library Capability Alignment`)
|
|
810
|
-
output(``)
|
|
811
|
-
output(`**Changed requirements to match ${Object.keys(byLibrary).join(', ')} capabilities:**`)
|
|
812
|
-
output(``)
|
|
813
|
-
for (const gap of capabilityGaps) {
|
|
814
|
-
output(`- ~~${gap.requirement}~~ ā Use ${gap.library}'s default approach instead`)
|
|
815
|
-
}
|
|
816
|
-
output(``)
|
|
817
|
-
output(`**Reason:** Library limitation`)
|
|
818
|
-
output(`**Trade-off:** ${capabilityGaps.map(g => g.requirement).join(', ')} not available`)
|
|
819
|
-
output(`**Date:** ${new Date().toISOString().split('T')[0]}`)
|
|
820
|
-
output(`\`\`\``)
|
|
821
|
-
output(``)
|
|
822
|
-
output(`Please update design.md and re-run /csetup.`)
|
|
823
|
-
return
|
|
824
|
-
} else if (decision.includes('C')) {
|
|
825
|
-
output(`\nš Custom implementation notes for agents:`)
|
|
826
|
-
output(``)
|
|
827
|
-
output(`Add to context.md:`)
|
|
828
|
-
output(`\`\`\`markdown`)
|
|
829
|
-
output(`## Custom Implementation Required`)
|
|
830
|
-
output(``)
|
|
831
|
-
output(`The following features need custom implementation:`)
|
|
832
|
-
for (const gap of capabilityGaps) {
|
|
833
|
-
output(`- ${gap.requirement} (not supported by ${gap.library})`)
|
|
834
|
-
}
|
|
835
|
-
output(``)
|
|
836
|
-
output(`Agents should implement these on top of the base library.`)
|
|
837
|
-
output(`\`\`\``)
|
|
838
|
-
|
|
839
|
-
// Store for context.md generation
|
|
840
|
-
customImplementationRequired = capabilityGaps
|
|
841
|
-
}
|
|
842
|
-
// If D, continue with gaps logged for agent awareness
|
|
843
|
-
} else {
|
|
844
|
-
output(`\nā
All spec requirements supported by chosen libraries`)
|
|
845
|
-
}
|
|
846
|
-
}
|
|
847
|
-
} else {
|
|
848
|
-
output(` ā¹ļø No specific libraries detected in spec`)
|
|
849
|
-
}
|
|
850
|
-
}
|
|
828
|
+
8. Store capability analysis for later use:
|
|
829
|
+
- Detected libraries
|
|
830
|
+
- Spec requirements
|
|
831
|
+
- Capability gaps
|
|
832
|
+
- Custom implementation needs
|
|
851
833
|
|
|
852
|
-
|
|
853
|
-
const capabilityAnalysis = {
|
|
854
|
-
libraries: detectedLibraries,
|
|
855
|
-
requirements: specRequirements,
|
|
856
|
-
gaps: capabilityGaps,
|
|
857
|
-
customRequired: customImplementationRequired
|
|
858
|
-
}
|
|
859
|
-
```
|
|
834
|
+
---
|
|
860
835
|
|
|
861
836
|
|
|
862
837
|
---
|
|
@@ -866,161 +841,138 @@ const capabilityAnalysis = {
|
|
|
866
841
|
> **NEW in v2.0:** No templates, no keyword matching. AI analyzes tasks and makes decisions.
|
|
867
842
|
> **See:** `.claude/lib/task-analyzer.md` for complete analysis logic
|
|
868
843
|
|
|
869
|
-
|
|
870
|
-
const tasksContent = Read(`openspec/changes/${changeId}/tasks.md`)
|
|
871
|
-
|
|
872
|
-
output(`\nš Task Analyzer v2.0 (Template-Free)...`)
|
|
873
|
-
|
|
874
|
-
// ========== 3.1 Parse ALL Tasks ==========
|
|
875
|
-
// Extract EVERY task from tasks.md - nothing is filtered out
|
|
876
|
-
const allTasks = parseAllTasks(tasksContent)
|
|
877
|
-
output(` Found: ${allTasks.length} tasks from tasks.md`)
|
|
878
|
-
|
|
879
|
-
// ========== 3.2 AI-Driven Analysis ==========
|
|
880
|
-
// Claude analyzes each task and decides:
|
|
881
|
-
// - complexity (1-10)
|
|
882
|
-
// - risk (LOW/MEDIUM/HIGH)
|
|
883
|
-
// - agent (based on context, NOT keywords)
|
|
884
|
-
// - dependencies (blocked_by, blocks)
|
|
885
|
-
// - needsIncremental (boolean)
|
|
886
|
-
|
|
887
|
-
const analyzedTasks = []
|
|
888
|
-
|
|
889
|
-
output(`\nš Analyzing each task...`)
|
|
844
|
+
1. Read `openspec/changes/${changeId}/tasks.md`
|
|
890
845
|
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
// Complexity: How many operations? Multiple systems? Business logic?
|
|
896
|
-
complexity: /* AI determines 1-10 based on task scope */,
|
|
897
|
-
|
|
898
|
-
// Risk: What if this fails? Security/money/data involved?
|
|
899
|
-
risk: /* AI determines LOW/MEDIUM/HIGH */,
|
|
900
|
-
|
|
901
|
-
// Agent: Read the full context and decide which agent
|
|
902
|
-
// DO NOT use keyword matching - understand the task
|
|
903
|
-
agent: /* AI decides: uxui-frontend, backend, database, frontend, test-debug, integration */,
|
|
904
|
-
agentReason: /* Brief explanation why this agent */,
|
|
905
|
-
|
|
906
|
-
// Dependencies: What must complete first? What's waiting for this?
|
|
907
|
-
dependencies: {
|
|
908
|
-
blockedBy: /* AI identifies blocking tasks */,
|
|
909
|
-
blocks: /* AI identifies tasks this blocks */,
|
|
910
|
-
canParallelize: /* Tasks with no shared dependencies */
|
|
911
|
-
},
|
|
912
|
-
|
|
913
|
-
// Incremental: Does this need milestone-based execution?
|
|
914
|
-
// YES if: batch processing, external API, data transformation,
|
|
915
|
-
// multiple methods, complex form, HIGH risk, complexity >= 7
|
|
916
|
-
needsIncremental: /* AI determines based on task nature */
|
|
917
|
-
}
|
|
918
|
-
|
|
919
|
-
analyzedTasks.push({ ...task, ...analysis })
|
|
920
|
-
}
|
|
921
|
-
|
|
922
|
-
// Report analysis
|
|
923
|
-
output(`\nš Analysis Results:`)
|
|
924
|
-
output(` Complexity: avg ${avgComplexity}/10`)
|
|
925
|
-
output(` Risk: ${highRiskCount} HIGH, ${mediumRiskCount} MEDIUM, ${lowRiskCount} LOW`)
|
|
926
|
-
output(` Agents: ${agentBreakdown}`)
|
|
927
|
-
|
|
928
|
-
// ========== 3.3 Auto-Add Best Practices ==========
|
|
929
|
-
// No warnings - just add what's needed automatically
|
|
930
|
-
|
|
931
|
-
const additions = []
|
|
932
|
-
|
|
933
|
-
for (const task of analyzedTasks) {
|
|
934
|
-
// Rule 1: HIGH Risk ā Add checkpoint
|
|
935
|
-
if (task.risk === 'HIGH') {
|
|
936
|
-
additions.push({
|
|
937
|
-
id: `${task.id}.verify`,
|
|
938
|
-
description: `Checkpoint: Verify ${task.description} before proceeding`,
|
|
939
|
-
type: 'verification',
|
|
940
|
-
autoAdded: true,
|
|
941
|
-
reason: 'HIGH risk task requires verification checkpoint',
|
|
942
|
-
phase: task.phase
|
|
943
|
-
})
|
|
944
|
-
}
|
|
945
|
-
|
|
946
|
-
// Rule 2: External API ā Add error handling
|
|
947
|
-
if (task.hasExternalAPI) {
|
|
948
|
-
if (!hasRelatedTask(allTasks, 'error handling')) {
|
|
949
|
-
additions.push({
|
|
950
|
-
id: `${task.id}.errors`,
|
|
951
|
-
description: `Add error handling for external API`,
|
|
952
|
-
type: 'implementation',
|
|
953
|
-
autoAdded: true,
|
|
954
|
-
reason: 'External APIs require error handling',
|
|
955
|
-
phase: task.phase
|
|
956
|
-
})
|
|
957
|
-
}
|
|
958
|
-
}
|
|
959
|
-
|
|
960
|
-
// Rule 3: Security-Critical ā Add security review
|
|
961
|
-
if (task.isSecurityCritical) {
|
|
962
|
-
additions.push({
|
|
963
|
-
id: `${task.id}.security`,
|
|
964
|
-
description: `Security review: ${task.description}`,
|
|
965
|
-
type: 'verification',
|
|
966
|
-
autoAdded: true,
|
|
967
|
-
reason: 'Security-critical tasks require review',
|
|
968
|
-
phase: task.phase
|
|
969
|
-
})
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
// Rule 4: Database Changes ā Add migration safety
|
|
973
|
-
if (task.involvesDatabaseChange) {
|
|
974
|
-
additions.push({
|
|
975
|
-
id: `${task.id}.backup`,
|
|
976
|
-
description: `Backup affected tables before ${task.description}`,
|
|
977
|
-
type: 'safety',
|
|
978
|
-
autoAdded: true,
|
|
979
|
-
reason: 'Database changes require backup',
|
|
980
|
-
phase: task.phase
|
|
981
|
-
})
|
|
982
|
-
}
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
output(` Auto-added: ${additions.length} best practice tasks`)
|
|
986
|
-
|
|
987
|
-
// ========== 3.4 Generate Incremental Milestones ==========
|
|
988
|
-
// For tasks that need milestone-based execution
|
|
989
|
-
|
|
990
|
-
for (const task of analyzedTasks) {
|
|
991
|
-
if (task.needsIncremental) {
|
|
992
|
-
// AI generates appropriate milestones based on task type:
|
|
993
|
-
// - Repository/Service: method-by-method
|
|
994
|
-
// - External API: mock ā single ā errors ā scale
|
|
995
|
-
// - Batch Processing: 1 ā 5 ā 20 ā 100
|
|
996
|
-
// - Complex Form: architecture ā e2e ā all fields
|
|
997
|
-
|
|
998
|
-
task.milestones = generateMilestones(task)
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
|
-
|
|
1002
|
-
const incrementalCount = analyzedTasks.filter(t => t.milestones).length
|
|
1003
|
-
const totalMilestones = analyzedTasks.reduce((sum, t) => sum + (t.milestones?.length || 0), 0)
|
|
1004
|
-
output(` Incremental: ${incrementalCount} tasks with ${totalMilestones} milestones`)
|
|
1005
|
-
|
|
1006
|
-
// ========== 3.5 Sort by Priority ==========
|
|
1007
|
-
// Respect original phase order, then sort within phases
|
|
846
|
+
2. Output:
|
|
847
|
+
```
|
|
848
|
+
š Task Analyzer v2.0 (Template-Free)...
|
|
849
|
+
```
|
|
1008
850
|
|
|
1009
|
-
|
|
851
|
+
3. **Parse ALL tasks from tasks.md:**
|
|
852
|
+
- Extract EVERY checkbox item
|
|
853
|
+
- Pattern: `- [ ] {id} {description}`
|
|
854
|
+
- Nothing is filtered out
|
|
855
|
+
- Output: ` Found: {count} tasks from tasks.md`
|
|
1010
856
|
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
// b. HIGH risk early (fail fast)
|
|
1016
|
-
// c. Foundation before features
|
|
1017
|
-
// d. Lower complexity first (quick wins)
|
|
857
|
+
4. Output:
|
|
858
|
+
```
|
|
859
|
+
š Analyzing each task...
|
|
860
|
+
```
|
|
1018
861
|
|
|
1019
|
-
|
|
1020
|
-
|
|
862
|
+
5. **AI-Driven Analysis - For EACH task, determine:**
|
|
863
|
+
|
|
864
|
+
**a) Complexity (1-10):**
|
|
865
|
+
- Consider: number of operations, systems involved, business logic
|
|
866
|
+
- 1-3: Simple CRUD
|
|
867
|
+
- 4-6: Multiple operations, some logic
|
|
868
|
+
- 7-8: Complex logic, multiple systems
|
|
869
|
+
- 9-10: High complexity, many dependencies
|
|
870
|
+
|
|
871
|
+
**b) Risk (LOW/MEDIUM/HIGH):**
|
|
872
|
+
- Consider: What happens if this fails?
|
|
873
|
+
- HIGH: Security, money, data loss
|
|
874
|
+
- MEDIUM: User experience, performance
|
|
875
|
+
- LOW: UI tweaks, minor features
|
|
876
|
+
|
|
877
|
+
**c) Agent Assignment:**
|
|
878
|
+
- **DO NOT use keyword matching**
|
|
879
|
+
- Read full task description and context
|
|
880
|
+
- Decide which agent: uxui-frontend, backend, database, frontend, test-debug, integration
|
|
881
|
+
- Write brief reason why this agent
|
|
882
|
+
|
|
883
|
+
**d) Dependencies:**
|
|
884
|
+
- Identify which tasks must complete first (blockedBy)
|
|
885
|
+
- Identify which tasks depend on this (blocks)
|
|
886
|
+
- Identify tasks that can run in parallel (no shared dependencies)
|
|
887
|
+
|
|
888
|
+
**e) Incremental Testing Needed:**
|
|
889
|
+
- YES if ANY of these:
|
|
890
|
+
- Batch processing
|
|
891
|
+
- External API integration
|
|
892
|
+
- Data transformation
|
|
893
|
+
- Multiple methods to implement
|
|
894
|
+
- Complex form
|
|
895
|
+
- Risk = HIGH
|
|
896
|
+
- Complexity >= 7
|
|
897
|
+
|
|
898
|
+
Store all analysis results with each task.
|
|
899
|
+
|
|
900
|
+
6. **Calculate and output analysis summary:**
|
|
901
|
+
- Calculate average complexity
|
|
902
|
+
- Count tasks by risk level
|
|
903
|
+
- Count tasks by agent
|
|
904
|
+
|
|
905
|
+
Output:
|
|
906
|
+
```
|
|
907
|
+
š Analysis Results:
|
|
908
|
+
Complexity: avg {average}/10
|
|
909
|
+
Risk: {HIGH count} HIGH, {MEDIUM count} MEDIUM, {LOW count} LOW
|
|
910
|
+
Agents: {agent1} ({count1}), {agent2} ({count2}), ...
|
|
911
|
+
```
|
|
912
|
+
|
|
913
|
+
7. **Auto-Add Best Practice Tasks:**
|
|
914
|
+
|
|
915
|
+
For each analyzed task, apply these rules:
|
|
916
|
+
|
|
917
|
+
**Rule 1: HIGH Risk ā Add checkpoint**
|
|
918
|
+
- If risk = HIGH
|
|
919
|
+
- Add new task: `{task.id}.verify - Checkpoint: Verify {description} before proceeding`
|
|
920
|
+
- Mark as autoAdded, type: verification
|
|
921
|
+
|
|
922
|
+
**Rule 2: External API ā Add error handling**
|
|
923
|
+
- If task involves external API
|
|
924
|
+
- Check if error handling task already exists
|
|
925
|
+
- If not, add: `{task.id}.errors - Add error handling for external API`
|
|
926
|
+
- Mark as autoAdded, type: implementation
|
|
927
|
+
|
|
928
|
+
**Rule 3: Security-Critical ā Add security review**
|
|
929
|
+
- If task is security-critical (auth, payment, data access)
|
|
930
|
+
- Add: `{task.id}.security - Security review: {description}`
|
|
931
|
+
- Mark as autoAdded, type: verification
|
|
932
|
+
|
|
933
|
+
**Rule 4: Database Changes ā Add migration safety**
|
|
934
|
+
- If task involves database schema changes
|
|
935
|
+
- Add: `{task.id}.backup - Backup affected tables before {description}`
|
|
936
|
+
- Mark as autoAdded, type: safety
|
|
937
|
+
|
|
938
|
+
Output: ` Auto-added: {count} best practice tasks`
|
|
939
|
+
|
|
940
|
+
8. **Generate Incremental Milestones:**
|
|
941
|
+
|
|
942
|
+
For each task where needsIncremental = true:
|
|
943
|
+
- Determine task type (Repository/API/Batch/Form)
|
|
944
|
+
- Generate appropriate milestones:
|
|
945
|
+
- **Repository/Service:** method-by-method implementation
|
|
946
|
+
- **External API:** mock ā single ā errors ā scale
|
|
947
|
+
- **Batch Processing:** 1 record ā 5 ā 20 ā 100
|
|
948
|
+
- **Complex Form:** architecture ā e2e ā all fields
|
|
949
|
+
|
|
950
|
+
Count totals and output:
|
|
951
|
+
```
|
|
952
|
+
Incremental: {count} tasks with {total milestones} milestones
|
|
953
|
+
```
|
|
954
|
+
|
|
955
|
+
9. **Sort Tasks by Priority:**
|
|
956
|
+
|
|
957
|
+
Combine analyzed tasks + auto-added tasks
|
|
958
|
+
|
|
959
|
+
Sort using these rules:
|
|
960
|
+
1. Preserve original phase order from tasks.md
|
|
961
|
+
2. Within each phase:
|
|
962
|
+
- Dependencies first (tasks with no blockers)
|
|
963
|
+
- HIGH risk early (fail fast principle)
|
|
964
|
+
- Foundation before features
|
|
965
|
+
- Lower complexity first (quick wins)
|
|
966
|
+
|
|
967
|
+
10. Output:
|
|
968
|
+
```
|
|
969
|
+
ā
Task Analysis Complete
|
|
970
|
+
Total: {original count} original + {auto-added count} auto-added = {total count} tasks
|
|
1021
971
|
```
|
|
1022
972
|
|
|
1023
|
-
|
|
973
|
+
---
|
|
974
|
+
|
|
975
|
+
**Expected Output Example:**
|
|
1024
976
|
```
|
|
1025
977
|
š Task Analyzer v2.0 (Template-Free)...
|
|
1026
978
|
Found: 47 tasks from tasks.md
|
|
@@ -1037,79 +989,82 @@ output(` Total: ${allTasks.length} original + ${additions.length} auto-added =
|
|
|
1037
989
|
|
|
1038
990
|
ā
Task Analysis Complete
|
|
1039
991
|
Total: 47 original + 12 auto-added = 59 tasks
|
|
1040
|
-
|
|
1041
|
-
š§Ŗ UX Testing Injection...
|
|
1042
|
-
Injected Phase 1.5 (ux-tester) after Phase 1
|
|
1043
|
-
ā
1 UX approval gate(s) added
|
|
1044
992
|
```
|
|
1045
993
|
|
|
1046
994
|
---
|
|
1047
995
|
|
|
1048
996
|
### Step 4: Create .claude Directory
|
|
1049
997
|
|
|
1050
|
-
**
|
|
1051
|
-
```typescript
|
|
1052
|
-
// Create .claude directory for change-specific files
|
|
1053
|
-
const claudeDir = `openspec/changes/${changeId}/.claude`
|
|
998
|
+
**WHY:** `/cdev` expects files at `openspec/changes/{id}/.claude/` - creating the directory first ensures consistent file paths.
|
|
1054
999
|
|
|
1055
|
-
if
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1000
|
+
1. Check if directory exists: `openspec/changes/{changeId}/.claude`
|
|
1001
|
+
|
|
1002
|
+
**If directory does NOT exist:**
|
|
1003
|
+
- Create the directory
|
|
1004
|
+
- Output: `š Created: openspec/changes/{changeId}/.claude`
|
|
1060
1005
|
|
|
1061
|
-
|
|
1006
|
+
**If directory exists:**
|
|
1007
|
+
- Skip (no output needed)
|
|
1008
|
+
|
|
1009
|
+
---
|
|
1062
1010
|
|
|
1063
1011
|
### Step 4.5: Inject UX Testing Phases (v2.7.0)
|
|
1064
1012
|
|
|
1065
1013
|
> **CRITICAL:** Auto-inject Phase X.5 (ux-tester) after EVERY uxui-frontend phase
|
|
1066
1014
|
> **Purpose:** User approval gate before proceeding to backend development
|
|
1067
1015
|
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
// Check if any phase has uxui-frontend agent
|
|
1073
|
-
const hasUIWork = phases.some(p => {
|
|
1074
|
-
const phaseTasks = sortedTasks.filter(t => t.phase?.number === p.number)
|
|
1075
|
-
return getMostCommonAgent(phaseTasks) === 'uxui-frontend'
|
|
1076
|
-
})
|
|
1077
|
-
|
|
1078
|
-
if (hasUIWork) {
|
|
1079
|
-
output(`\nš§Ŗ UX Testing Injection...`)
|
|
1080
|
-
|
|
1081
|
-
// Find all uxui-frontend phases
|
|
1082
|
-
const uiFrontendPhases = phases.filter(p => {
|
|
1083
|
-
const phaseTasks = sortedTasks.filter(t => t.phase?.number === p.number)
|
|
1084
|
-
return getMostCommonAgent(phaseTasks) === 'uxui-frontend'
|
|
1085
|
-
})
|
|
1086
|
-
|
|
1087
|
-
// Inject .5 phase after each uxui-frontend phase
|
|
1088
|
-
uiFrontendPhases.forEach(uiPhase => {
|
|
1089
|
-
const uxTestingPhase = {
|
|
1090
|
-
number: `${uiPhase.number}.5`,
|
|
1091
|
-
name: 'UX Testing (Approval Gate)',
|
|
1092
|
-
agent: 'ux-tester',
|
|
1093
|
-
isApprovalGate: true,
|
|
1094
|
-
strategy: 'approval-required',
|
|
1095
|
-
tasks: [
|
|
1096
|
-
{ id: `${uiPhase.number}.5.1`, description: 'Generate personas from product context', autoAdded: true },
|
|
1097
|
-
{ id: `${uiPhase.number}.5.2`, description: 'Test UI from each persona perspective', autoAdded: true },
|
|
1098
|
-
{ id: `${uiPhase.number}.5.3`, description: 'Generate UX test report with conversion prediction', autoAdded: true },
|
|
1099
|
-
{ id: `${uiPhase.number}.5.4`, description: 'āøļø PAUSE: Wait for user approval', autoAdded: true }
|
|
1100
|
-
]
|
|
1101
|
-
}
|
|
1016
|
+
1. **Group tasks by phase:**
|
|
1017
|
+
- Use sorted tasks from Step 3
|
|
1018
|
+
- Group by phase number
|
|
1019
|
+
- Create phase objects with number, name, tasks list
|
|
1102
1020
|
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1021
|
+
2. **Check if ANY phase has uxui-frontend work:**
|
|
1022
|
+
- For each phase, count tasks by agent
|
|
1023
|
+
- Find most common agent for that phase
|
|
1024
|
+
- Check if most common agent = 'uxui-frontend'
|
|
1025
|
+
- Store result as `hasUIWork`
|
|
1106
1026
|
|
|
1107
|
-
|
|
1108
|
-
})
|
|
1027
|
+
**If hasUIWork is TRUE:**
|
|
1109
1028
|
|
|
1110
|
-
|
|
1111
|
-
}
|
|
1029
|
+
3. Output:
|
|
1112
1030
|
```
|
|
1031
|
+
š§Ŗ UX Testing Injection...
|
|
1032
|
+
```
|
|
1033
|
+
|
|
1034
|
+
4. **Find all uxui-frontend phases:**
|
|
1035
|
+
- Filter phases where dominant agent = 'uxui-frontend'
|
|
1036
|
+
- Store as `uiFrontendPhases`
|
|
1037
|
+
|
|
1038
|
+
5. **For EACH uxui-frontend phase:**
|
|
1039
|
+
|
|
1040
|
+
a) Create new UX Testing phase object:
|
|
1041
|
+
- Phase number: `{uiPhase.number}.5` (e.g., if UI phase is 1, UX phase is 1.5)
|
|
1042
|
+
- Phase name: `UX Testing (Approval Gate)`
|
|
1043
|
+
- Agent: `ux-tester`
|
|
1044
|
+
- Mark as approval gate: `isApprovalGate: true`
|
|
1045
|
+
- Strategy: `approval-required`
|
|
1046
|
+
|
|
1047
|
+
b) Add these tasks to UX Testing phase:
|
|
1048
|
+
- `{phaseNum}.5.1 - Generate personas from product context` (autoAdded)
|
|
1049
|
+
- `{phaseNum}.5.2 - Test UI from each persona perspective` (autoAdded)
|
|
1050
|
+
- `{phaseNum}.5.3 - Generate UX test report with conversion prediction` (autoAdded)
|
|
1051
|
+
- `{phaseNum}.5.4 - āøļø PAUSE: Wait for user approval` (autoAdded)
|
|
1052
|
+
|
|
1053
|
+
c) Insert UX Testing phase into phases array:
|
|
1054
|
+
- Find position of UI phase
|
|
1055
|
+
- Insert UX Testing phase RIGHT AFTER (position + 1)
|
|
1056
|
+
|
|
1057
|
+
d) Output: ` Injected Phase {uiPhase.number}.5 (ux-tester) after Phase {uiPhase.number}`
|
|
1058
|
+
|
|
1059
|
+
6. Output:
|
|
1060
|
+
```
|
|
1061
|
+
ā
{count} UX approval gate(s) added
|
|
1062
|
+
```
|
|
1063
|
+
|
|
1064
|
+
**If hasUIWork is FALSE:**
|
|
1065
|
+
- Skip all steps above
|
|
1066
|
+
|
|
1067
|
+
---
|
|
1113
1068
|
|
|
1114
1069
|
**Workflow with UX Testing:**
|
|
1115
1070
|
```
|
|
@@ -1132,486 +1087,454 @@ Phase 1.5: ux-tester (APPROVAL GATE)
|
|
|
1132
1087
|
> **v2.0:** No templates loaded. Phases generated directly from analyzed tasks.
|
|
1133
1088
|
> **v2.7.0:** UX Testing phases already injected in Step 4.5
|
|
1134
1089
|
|
|
1135
|
-
|
|
1136
|
-
|
|
1090
|
+
**Prepare data:**
|
|
1091
|
+
|
|
1092
|
+
1. Extract proposal title from `openspec/changes/{changeId}/proposal.md`
|
|
1093
|
+
- Get first heading line
|
|
1094
|
+
- If not found, use changeId
|
|
1095
|
+
|
|
1096
|
+
2. Get current timestamp (ISO format)
|
|
1097
|
+
|
|
1098
|
+
3. Calculate totals from sorted tasks:
|
|
1099
|
+
- Original tasks count (where autoAdded is false)
|
|
1100
|
+
- Auto-added tasks count (where autoAdded is true)
|
|
1101
|
+
- Incremental tasks count (where milestones exist)
|
|
1102
|
+
- Total milestones (sum of all milestone arrays)
|
|
1137
1103
|
|
|
1138
|
-
|
|
1104
|
+
**Generate overview table:**
|
|
1139
1105
|
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1106
|
+
4. For each phase:
|
|
1107
|
+
- Get tasks for this phase
|
|
1108
|
+
- Find dominant agent (most common agent in phase tasks)
|
|
1109
|
+
- Check if phase has incremental tasks
|
|
1110
|
+
- Get max risk level in phase
|
|
1111
|
+
- Create table row: `| {number} | {name} | {task count} | {agent} | {strategy} | {risk} |`
|
|
1143
1112
|
|
|
1144
|
-
|
|
1145
|
-
const originalCount = tasks.filter(t => !t.autoAdded).length
|
|
1146
|
-
const autoAddedCount = tasks.filter(t => t.autoAdded).length
|
|
1147
|
-
const incrementalCount = tasks.filter(t => t.milestones).length
|
|
1148
|
-
const totalMilestones = tasks.reduce((sum, t) => sum + (t.milestones?.length || 0), 0)
|
|
1113
|
+
**Generate phase sections:**
|
|
1149
1114
|
|
|
1150
|
-
|
|
1151
|
-
const overviewRows = phases.map(phase => {
|
|
1152
|
-
const phaseTasks = tasks.filter(t => t.phase?.number === phase.number)
|
|
1153
|
-
const dominantAgent = getMostCommonAgent(phaseTasks)
|
|
1154
|
-
const hasIncremental = phaseTasks.some(t => t.milestones)
|
|
1155
|
-
const maxRisk = getMaxRisk(phaseTasks)
|
|
1115
|
+
5. For each phase, generate phase section:
|
|
1156
1116
|
|
|
1157
|
-
|
|
1158
|
-
|
|
1117
|
+
a) Header:
|
|
1118
|
+
```markdown
|
|
1119
|
+
## Phase {number}: {name}
|
|
1159
1120
|
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1121
|
+
**Agent:** {dominantAgent}
|
|
1122
|
+
**Strategy:** {š INCREMENTAL or Standard}
|
|
1123
|
+
**Risk:** {maxRisk}
|
|
1124
|
+
```
|
|
1164
1125
|
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1126
|
+
b) **If phase has TDD tasks (v3.1.0):**
|
|
1127
|
+
- Filter tasks where tdd.tdd_required = true
|
|
1128
|
+
- Collect unique TDD reasons
|
|
1129
|
+
- Add:
|
|
1130
|
+
```markdown
|
|
1131
|
+
**TDD Required:** ā
YES
|
|
1132
|
+
**TDD Reason:** {reasons, max 2}
|
|
1133
|
+
**TDD Workflow:** red-green-refactor
|
|
1169
1134
|
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1135
|
+
ā ļø **TDD WORKFLOW REQUIRED:**
|
|
1136
|
+
1. š“ RED: Write tests FIRST (they should fail)
|
|
1137
|
+
2. ā
GREEN: Write minimal implementation to pass tests
|
|
1138
|
+
3. š§ REFACTOR: Improve code quality while keeping tests green
|
|
1139
|
+
```
|
|
1174
1140
|
|
|
1175
|
-
|
|
1141
|
+
c) **Standard tasks list:**
|
|
1142
|
+
- Filter tasks without milestones
|
|
1143
|
+
- For each task:
|
|
1144
|
+
```markdown
|
|
1145
|
+
- [ ] {⨠if autoAdded}{task.id} {task.description}
|
|
1146
|
+
```
|
|
1176
1147
|
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1148
|
+
d) **Incremental tasks with milestones:**
|
|
1149
|
+
- Filter tasks with milestones
|
|
1150
|
+
- For each incremental task:
|
|
1151
|
+
```markdown
|
|
1152
|
+
### Task {id}: {description}
|
|
1153
|
+
**Complexity:** {complexity}/10 | **Why Agent:** {agentReason}
|
|
1154
|
+
```
|
|
1181
1155
|
|
|
1182
|
-
|
|
1156
|
+
For each milestone:
|
|
1157
|
+
```markdown
|
|
1158
|
+
#### Milestone {milestone.id}/{total milestones}: {milestone.name}
|
|
1159
|
+
**Goal:** {milestone.goal}
|
|
1183
1160
|
|
|
1184
|
-
|
|
1161
|
+
{milestone tasks as checkboxes}
|
|
1185
1162
|
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
${overviewRows}
|
|
1163
|
+
**Exit Criteria:**
|
|
1164
|
+
{exit criteria as checkboxes}
|
|
1189
1165
|
|
|
1190
|
-
**
|
|
1191
|
-
**Incremental Tasks:** ${incrementalCount} tasks with ${totalMilestones} milestones
|
|
1166
|
+
**CHECKPOINT:** Report results before {next milestone or next phase}
|
|
1192
1167
|
|
|
1193
|
-
---
|
|
1168
|
+
---
|
|
1169
|
+
```
|
|
1194
1170
|
|
|
1195
|
-
|
|
1171
|
+
e) **Phase exit criteria:**
|
|
1172
|
+
```markdown
|
|
1173
|
+
### Phase {number} Exit Criteria
|
|
1174
|
+
- [ ] All tasks completed
|
|
1175
|
+
- [ ] All tests pass
|
|
1176
|
+
- [ ] No regression in existing functionality
|
|
1177
|
+
```
|
|
1196
1178
|
|
|
1197
|
-
|
|
1179
|
+
**Generate auto-added summary:**
|
|
1198
1180
|
|
|
1199
|
-
|
|
1181
|
+
6. **If auto-added tasks exist:**
|
|
1182
|
+
```markdown
|
|
1183
|
+
## Auto-Added Tasks (Best Practices)
|
|
1200
1184
|
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
}
|
|
1185
|
+
| Task | Reason | Phase |
|
|
1186
|
+
|------|--------|-------|
|
|
1187
|
+
| {description} | {reason} | {phase number} |
|
|
1188
|
+
... (for each auto-added task)
|
|
1189
|
+
```
|
|
1204
1190
|
|
|
1205
|
-
|
|
1206
|
-
const dominantAgent = getMostCommonAgent(phaseTasks)
|
|
1207
|
-
const hasIncremental = phaseTasks.some(t => t.milestones)
|
|
1208
|
-
const maxRisk = getMaxRisk(phaseTasks)
|
|
1209
|
-
|
|
1210
|
-
// v3.1.0: Use TDD classification from task-analyzer.md (Step 2.6)
|
|
1211
|
-
// Each task now has task.tdd = { tdd_required, workflow, reason, confidence }
|
|
1212
|
-
const tddTasks = phaseTasks.filter(t => t.tdd?.tdd_required === true)
|
|
1213
|
-
const needsTDD = tddTasks.length > 0
|
|
1214
|
-
const tddReasons = [...new Set(tddTasks.map(t => t.tdd?.reason).filter(Boolean))]
|
|
1215
|
-
|
|
1216
|
-
let section = `## Phase ${phase.number}: ${phase.name}
|
|
1217
|
-
|
|
1218
|
-
**Agent:** ${dominantAgent}
|
|
1219
|
-
**Strategy:** ${hasIncremental ? 'š INCREMENTAL' : 'Standard'}
|
|
1220
|
-
**Risk:** ${maxRisk}
|
|
1221
|
-
${needsTDD ? `**TDD Required:** ā
YES
|
|
1222
|
-
**TDD Reason:** ${tddReasons.slice(0, 2).join('; ')}
|
|
1223
|
-
**TDD Workflow:** red-green-refactor
|
|
1224
|
-
|
|
1225
|
-
ā ļø **TDD WORKFLOW REQUIRED:**
|
|
1226
|
-
1. š“ RED: Write tests FIRST (they should fail)
|
|
1227
|
-
2. ā
GREEN: Write minimal implementation to pass tests
|
|
1228
|
-
3. š§ REFACTOR: Improve code quality while keeping tests green
|
|
1229
|
-
` : ''}
|
|
1230
|
-
`
|
|
1231
|
-
|
|
1232
|
-
// Group tasks: incremental tasks get milestone sections, others get simple list
|
|
1233
|
-
const incrementalTasks = phaseTasks.filter(t => t.milestones)
|
|
1234
|
-
const standardTasks = phaseTasks.filter(t => !t.milestones)
|
|
1235
|
-
|
|
1236
|
-
// Standard tasks section
|
|
1237
|
-
if (standardTasks.length > 0) {
|
|
1238
|
-
section += `### Tasks\n\n`
|
|
1239
|
-
standardTasks.forEach(task => {
|
|
1240
|
-
const prefix = task.autoAdded ? '⨠' : ''
|
|
1241
|
-
section += `- [ ] ${prefix}${task.id} ${task.description}\n`
|
|
1242
|
-
})
|
|
1243
|
-
section += '\n'
|
|
1244
|
-
}
|
|
1191
|
+
**Assemble final content:**
|
|
1245
1192
|
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
**Complexity:** ${task.complexity}/10 | **Why Agent:** ${task.agentReason}
|
|
1193
|
+
7. Combine all sections:
|
|
1194
|
+
```markdown
|
|
1195
|
+
# Phases: {title}
|
|
1250
1196
|
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
**
|
|
1197
|
+
> **Generated by:** Task Analyzer v2.0 (Template-Free)
|
|
1198
|
+
> **Source:** tasks.md (Single Source of Truth)
|
|
1199
|
+
> **Strategy:** Incremental development (small ā large)
|
|
1200
|
+
> **Generated:** {timestamp}
|
|
1255
1201
|
|
|
1256
|
-
|
|
1202
|
+
---
|
|
1257
1203
|
|
|
1258
|
-
|
|
1259
|
-
${milestone.exitCriteria.map(c => `- [ ] ${c}`).join('\n')}
|
|
1204
|
+
## Overview
|
|
1260
1205
|
|
|
1261
|
-
|
|
1206
|
+
| Phase | Name | Tasks | Agent | Strategy | Risk |
|
|
1207
|
+
|-------|------|-------|-------|----------|------|
|
|
1208
|
+
{overview rows}
|
|
1262
1209
|
|
|
1263
|
-
|
|
1210
|
+
**Total Tasks:** {original} original + {auto-added} auto-added = {total}
|
|
1211
|
+
**Incremental Tasks:** {incremental count} tasks with {total milestones} milestones
|
|
1264
1212
|
|
|
1265
|
-
|
|
1266
|
-
})
|
|
1267
|
-
})
|
|
1213
|
+
---
|
|
1268
1214
|
|
|
1269
|
-
|
|
1270
|
-
section += `### Phase ${phase.number} Exit Criteria
|
|
1271
|
-
- [ ] All tasks completed
|
|
1272
|
-
- [ ] All tests pass
|
|
1273
|
-
- [ ] No regression in existing functionality
|
|
1274
|
-
`
|
|
1215
|
+
{all phase sections separated by ---}
|
|
1275
1216
|
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1217
|
+
{auto-added summary if exists}
|
|
1218
|
+
|
|
1219
|
+
---
|
|
1220
|
+
|
|
1221
|
+
**End of phases.md**
|
|
1222
|
+
```
|
|
1279
1223
|
|
|
1280
|
-
Write to: `openspec/changes/{
|
|
1224
|
+
8. Write to: `openspec/changes/{changeId}/.claude/phases.md`
|
|
1225
|
+
|
|
1226
|
+
---
|
|
1281
1227
|
|
|
1282
1228
|
### Step 6: Generate flags.json (Template-Free)
|
|
1283
1229
|
|
|
1284
1230
|
> **v2.0:** Flags generated from analyzed tasks, not templates.
|
|
1285
1231
|
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1232
|
+
1. **Group tasks by phase** (use phases from Step 4.5 which includes UX Testing phases)
|
|
1233
|
+
|
|
1234
|
+
2. **Detect change type from tasks:**
|
|
1235
|
+
- Check agents assigned to tasks
|
|
1236
|
+
- Rules:
|
|
1237
|
+
- If has uxui-frontend + backend + database ā `full-stack`
|
|
1238
|
+
- If has uxui-frontend but NO backend ā `frontend-only`
|
|
1239
|
+
- If has backend but NO uxui-frontend ā `backend-only`
|
|
1240
|
+
- If has test-debug and total tasks <= 5 ā `bug-fix`
|
|
1241
|
+
- Otherwise ā `feature`
|
|
1242
|
+
|
|
1243
|
+
3. **Create flags.json structure:**
|
|
1244
|
+
|
|
1245
|
+
```json
|
|
1246
|
+
{
|
|
1247
|
+
"version": "2.0.0",
|
|
1248
|
+
"change_id": "{changeId}",
|
|
1249
|
+
"change_type": "{detected type}",
|
|
1250
|
+
"created_at": "{current timestamp ISO}",
|
|
1251
|
+
"updated_at": "{current timestamp ISO}",
|
|
1252
|
+
"current_phase": "{first phase number or 1}",
|
|
1253
|
+
"meta": {
|
|
1254
|
+
"total_phases": {total phase count},
|
|
1255
|
+
"pending_phases": {total phase count},
|
|
1256
|
+
"completed_phases": 0,
|
|
1257
|
+
"total_tasks": {total sorted tasks count},
|
|
1258
|
+
"original_tasks": {count where autoAdded = false},
|
|
1259
|
+
"auto_added_tasks": {count where autoAdded = true},
|
|
1260
|
+
"incremental_tasks": {count where milestones exist},
|
|
1261
|
+
"total_milestones": {sum of all milestone counts}
|
|
1307
1262
|
},
|
|
1308
|
-
phases: {
|
|
1309
|
-
}
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
name: phase.name,
|
|
1320
|
-
status: 'pending',
|
|
1321
|
-
agent: dominantAgent,
|
|
1322
|
-
task_count: phaseTasks.length,
|
|
1323
|
-
strategy: hasIncremental ? 'incremental' : 'standard',
|
|
1324
|
-
milestones: hasIncremental ? phaseTasks.filter(t => t.milestones).reduce((sum, t) => sum + t.milestones.length, 0) : 0
|
|
1263
|
+
"phases": {
|
|
1264
|
+
"{phase.number}": {
|
|
1265
|
+
"phase_number": {sequential index starting from 1},
|
|
1266
|
+
"name": "{phase.name}",
|
|
1267
|
+
"status": "pending",
|
|
1268
|
+
"agent": "{dominant agent in phase}",
|
|
1269
|
+
"task_count": {tasks in this phase},
|
|
1270
|
+
"strategy": "{incremental or standard}",
|
|
1271
|
+
"milestones": {milestone count or 0}
|
|
1272
|
+
}
|
|
1273
|
+
... (for each phase)
|
|
1325
1274
|
}
|
|
1326
|
-
}
|
|
1275
|
+
}
|
|
1327
1276
|
```
|
|
1328
1277
|
|
|
1329
|
-
|
|
1278
|
+
4. **For each phase, add to phases object:**
|
|
1279
|
+
- Get tasks for this phase
|
|
1280
|
+
- Find dominant agent (most common agent)
|
|
1281
|
+
- Check if any task has milestones
|
|
1282
|
+
- Count total milestones in phase
|
|
1283
|
+
- Create phase entry with structure above
|
|
1284
|
+
|
|
1285
|
+
5. Write JSON to: `openspec/changes/{changeId}/.claude/flags.json`
|
|
1286
|
+
- Format with proper indentation (2 spaces)
|
|
1287
|
+
|
|
1288
|
+
---
|
|
1330
1289
|
|
|
1331
1290
|
### Step 7: Generate context.md
|
|
1332
1291
|
|
|
1333
1292
|
**Load template and populate:**
|
|
1334
|
-
```typescript
|
|
1335
|
-
// Load template
|
|
1336
|
-
let contextTemplate = Read('.claude/templates/context-template.md')
|
|
1337
|
-
|
|
1338
|
-
// Load project tech stack
|
|
1339
|
-
const projectTech = Read('.claude/contexts/domain/project/tech-stack.md')
|
|
1340
|
-
|
|
1341
|
-
// Detect additional tech from proposal/tasks
|
|
1342
|
-
const additionalTech = detectAdditionalTech(proposalContent, tasksContent)
|
|
1343
|
-
|
|
1344
|
-
// š Load design info (if UI work) - v2.0.0
|
|
1345
|
-
let designInfo = ''
|
|
1346
|
-
if (hasFrontend && tokens) {
|
|
1347
|
-
designInfo = `
|
|
1348
|
-
## šØ Design System (v2.0.0)
|
|
1349
|
-
|
|
1350
|
-
**Design Files:**
|
|
1351
|
-
- data.yaml: \`design-system/data.yaml\` (~800 tokens)
|
|
1352
|
-
- patterns/: \`design-system/patterns/*.md\` (selective loading)
|
|
1353
|
-
- README.md: \`design-system/README.md\` (human-readable, ~100 lines)
|
|
1354
|
-
${pagePlan ? `- page-plan.md: \`openspec/changes/${changeId}/page-plan.md\` ā
` : ''}
|
|
1355
|
-
|
|
1356
|
-
**Style Direction:**
|
|
1357
|
-
- Style: ${tokens.style.name}
|
|
1358
|
-
- Theme: ${tokens.theme.name}
|
|
1359
|
-
- Feel: ${tokens.style.feel}
|
|
1360
|
-
|
|
1361
|
-
**Design Tokens:**
|
|
1362
|
-
- Primary Color: ${tokens.colors.primary.DEFAULT}
|
|
1363
|
-
- Component Library: ${tokens.component_library.name}
|
|
1364
|
-
- Spacing Scale: ${tokens.spacing.scale.join(', ')}px
|
|
1365
|
-
- Animations: ${tokens.animations.enabled ? 'Enabled' : 'Disabled'}
|
|
1366
|
-
|
|
1367
|
-
**Theme & Decorations:**
|
|
1368
|
-
${pageType.includes('landing') || pageType.includes('marketing') ? `
|
|
1369
|
-
- Decorations: ā
Enabled
|
|
1370
|
-
- USE: ${tokens.theme.decorative_elements.use.slice(0, 3).join(', ')}
|
|
1371
|
-
- AVOID: ${tokens.theme.decorative_elements.avoid.slice(0, 2).join(', ') || '(none)'}
|
|
1372
|
-
- Scroll Animations: ā
Enabled
|
|
1373
|
-
` : `
|
|
1374
|
-
- Decorations: ā Disabled (${pageType} page)
|
|
1375
|
-
- Scroll Animations: ā Disabled
|
|
1376
|
-
`}
|
|
1377
|
-
|
|
1378
|
-
**Pattern Files to Load:**
|
|
1379
|
-
${pageType.includes('landing') || pageType.includes('marketing') ?
|
|
1380
|
-
`- patterns/buttons.md ā
|
|
1381
|
-
- patterns/cards.md ā
|
|
1382
|
-
- patterns/scroll-animations.md ā
|
|
1383
|
-
- patterns/decorations.md ā
` :
|
|
1384
|
-
pageType.includes('auth') ?
|
|
1385
|
-
`- patterns/buttons.md ā
|
|
1386
|
-
- patterns/forms.md ā
` :
|
|
1387
|
-
`- patterns/buttons.md ā
|
|
1388
|
-
- patterns/cards.md ā
|
|
1389
|
-
- patterns/forms.md ā
`}
|
|
1390
|
-
|
|
1391
|
-
**Agent Loading (STEP 0.5 for uxui-frontend):**
|
|
1392
|
-
1. Read: data.yaml (~800 tokens)
|
|
1393
|
-
2. Read: page-plan.md (if exists)
|
|
1394
|
-
3. Load patterns selectively based on page type
|
|
1395
|
-
4. Report: Design tokens + page type extracted
|
|
1396
|
-
|
|
1397
|
-
**Style Guidelines:**
|
|
1398
|
-
|
|
1399
|
-
| Instead of | Use | WHY |
|
|
1400
|
-
|------------|-----|-----|
|
|
1401
|
-
| text-gray-500 | text-foreground/70 | Theme-aware |
|
|
1402
|
-
| p-5 | p-4 or p-6 | Spacing scale |
|
|
1403
|
-
| ${pageType.includes('landing') ? 'ā
Apply decorations from theme' : 'ā Skip decorations for this page type'} | | |
|
|
1404
|
-
`
|
|
1405
|
-
}
|
|
1406
|
-
|
|
1407
|
-
// Replace placeholders (v2.0: use phases from Task Analyzer, not templates)
|
|
1408
|
-
const phases = groupTasksByPhase(sortedTasks)
|
|
1409
|
-
const totalPhases = phases.length
|
|
1410
|
-
|
|
1411
|
-
contextTemplate = contextTemplate
|
|
1412
|
-
.replace('{CHANGE_ID}', changeId)
|
|
1413
|
-
.replace('{CHANGE_TITLE}', extractTitle(proposalContent))
|
|
1414
|
-
.replace('{CHANGE_TYPE}', detectChangeType(sortedTasks))
|
|
1415
|
-
.replace('{CURRENT_PHASE_NUMBER}', '1')
|
|
1416
|
-
.replace('{TOTAL_PHASES}', totalPhases.toString())
|
|
1417
|
-
.replace('{CREATED_DATE}', new Date().toISOString())
|
|
1418
|
-
.replace('{CORE_TECH_LIST}', generateCoreTechList(projectTech))
|
|
1419
|
-
.replace('{ADDITIONAL_TECH_LIST}', generateAdditionalTechList(additionalTech))
|
|
1420
|
-
.replace('{CURRENT_PHASE}', phases[0]?.name || 'Phase 1')
|
|
1421
|
-
.replace('{STATUS}', 'pending')
|
|
1422
|
-
.replace('{DESIGN_SYSTEM}', designInfo) // š Add design section
|
|
1423
|
-
```
|
|
1424
1293
|
|
|
1425
|
-
|
|
1294
|
+
1. **Read the context template:**
|
|
1295
|
+
- Load: `.claude/templates/context-template.md`
|
|
1296
|
+
|
|
1297
|
+
2. **Load project tech stack:**
|
|
1298
|
+
- Read: `.claude/contexts/domain/project/tech-stack.md`
|
|
1299
|
+
- This contains the core technologies used in the project
|
|
1300
|
+
|
|
1301
|
+
3. **Detect additional technologies:**
|
|
1302
|
+
- Use Step 2.7's library detection results from pre-work-context.md
|
|
1303
|
+
- Extract libraries mentioned in proposal.md and tasks.md
|
|
1304
|
+
|
|
1305
|
+
4. **Load design information (if UI work detected):**
|
|
1306
|
+
- Check if any task has `agent: uxui-frontend`
|
|
1307
|
+
- If yes and `design-system/data.yaml` exists:
|
|
1308
|
+
- Read design tokens from `design-system/data.yaml`
|
|
1309
|
+
- Check for page-plan.md at `openspec/changes/{changeId}/page-plan.md`
|
|
1310
|
+
- Determine page type from tasks (landing/marketing/auth/dashboard)
|
|
1311
|
+
- Build design system section with:
|
|
1312
|
+
- Design file paths (data.yaml, patterns/, README.md, page-plan.md)
|
|
1313
|
+
- Style direction (style name, theme name, feel)
|
|
1314
|
+
- Design tokens (primary color, component library, spacing scale, animations status)
|
|
1315
|
+
- Theme & decorations (enabled for landing/marketing, disabled for others)
|
|
1316
|
+
- Pattern files to load (selective based on page type)
|
|
1317
|
+
- Agent loading instructions (STEP 0.5 checklist)
|
|
1318
|
+
- Style guidelines table
|
|
1319
|
+
|
|
1320
|
+
5. **Group tasks by phase:**
|
|
1321
|
+
- Use the groupTasksByPhase helper (see Helper Functions section below)
|
|
1322
|
+
- Count total phases
|
|
1323
|
+
|
|
1324
|
+
6. **Replace template placeholders:**
|
|
1325
|
+
- `{CHANGE_ID}` ā changeId parameter
|
|
1326
|
+
- `{CHANGE_TITLE}` ā Extract title from proposal.md (first heading)
|
|
1327
|
+
- `{CHANGE_TYPE}` ā Use detectChangeType helper (full-stack/frontend-only/backend-only/bug-fix/feature)
|
|
1328
|
+
- `{CURRENT_PHASE_NUMBER}` ā "1"
|
|
1329
|
+
- `{TOTAL_PHASES}` ā Total phase count
|
|
1330
|
+
- `{CREATED_DATE}` ā Current timestamp in ISO format
|
|
1331
|
+
- `{CORE_TECH_LIST}` ā Markdown list from tech-stack.md
|
|
1332
|
+
- `{ADDITIONAL_TECH_LIST}` ā Markdown list from detected libraries
|
|
1333
|
+
- `{CURRENT_PHASE}` ā First phase name or "Phase 1"
|
|
1334
|
+
- `{STATUS}` ā "pending"
|
|
1335
|
+
- `{DESIGN_SYSTEM}` ā Design info section (from step 4)
|
|
1336
|
+
|
|
1337
|
+
7. **Write to file:**
|
|
1338
|
+
- Write final content to: `openspec/changes/{changeId}/.claude/context.md`
|
|
1426
1339
|
|
|
1427
1340
|
### Step 8: Output Summary (v2.0.0 - Template-Free)
|
|
1428
1341
|
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1342
|
+
**Calculate statistics from analyzed tasks:**
|
|
1343
|
+
|
|
1344
|
+
1. **Group tasks and count:**
|
|
1345
|
+
- Use groupTasksByPhase helper to organize tasks
|
|
1346
|
+
- Count total phases
|
|
1347
|
+
- Count tasks with incremental milestones
|
|
1348
|
+
- Sum all milestones across tasks
|
|
1349
|
+
- Count auto-added tasks
|
|
1350
|
+
|
|
1351
|
+
2. **Check for UI work:**
|
|
1352
|
+
- Check if any task has `agent: uxui-frontend`
|
|
1353
|
+
- If yes, check if `openspec/changes/{changeId}/page-plan.md` exists
|
|
1354
|
+
|
|
1355
|
+
3. **Calculate agent breakdown:**
|
|
1356
|
+
- Count tasks per agent
|
|
1357
|
+
- Format as: "agent1 (count1), agent2 (count2), ..."
|
|
1358
|
+
|
|
1359
|
+
4. **Build output message with these sections:**
|
|
1360
|
+
|
|
1361
|
+
**Header:**
|
|
1362
|
+
```
|
|
1363
|
+
ā
Change setup complete!
|
|
1364
|
+
|
|
1365
|
+
š¦ Change: {changeId}
|
|
1366
|
+
š Architecture: Task Analyzer v2.0 (Template-Free)
|
|
1367
|
+
š ļø Agents: {agent summary}
|
|
1368
|
+
```
|
|
1369
|
+
|
|
1370
|
+
**Files created:**
|
|
1371
|
+
```
|
|
1372
|
+
š Files created:
|
|
1373
|
+
ā openspec/changes/{changeId}/.claude/phases.md
|
|
1374
|
+
ā openspec/changes/{changeId}/.claude/flags.json
|
|
1375
|
+
ā openspec/changes/{changeId}/.claude/context.md
|
|
1376
|
+
```
|
|
1377
|
+
|
|
1378
|
+
**Task analysis:**
|
|
1379
|
+
```
|
|
1380
|
+
š Task Analysis:
|
|
1381
|
+
Total: X tasks (Y original + Z auto-added)
|
|
1382
|
+
Incremental: X tasks with Y milestones
|
|
1383
|
+
Phases: X
|
|
1384
|
+
UX Approval Gates: X
|
|
1385
|
+
```
|
|
1386
|
+
|
|
1387
|
+
**Phase overview:**
|
|
1388
|
+
```
|
|
1389
|
+
š Phase Overview:
|
|
1390
|
+
Phase 1: {name} ({agent}, {count} tasks)
|
|
1391
|
+
Phase 2: {name} ({agent}, {count} tasks)
|
|
1392
|
+
...
|
|
1393
|
+
```
|
|
1394
|
+
|
|
1395
|
+
5. **Add UI work recommendation (if applicable):**
|
|
1396
|
+
- If UI work detected AND page-plan.md exists:
|
|
1397
|
+
- Show: "ā
page-plan.md found: {path}"
|
|
1398
|
+
- Note: "uxui-frontend will use this for component planning"
|
|
1399
|
+
|
|
1400
|
+
- If UI work detected AND page-plan.md missing:
|
|
1401
|
+
- Show banner: "šØ UI Work Detected!"
|
|
1402
|
+
- List phases with UI work
|
|
1403
|
+
- Explain benefits: Content variants, component index, asset checklist, approval process
|
|
1404
|
+
- Show recommended steps (4 steps with /pageplan workflow)
|
|
1405
|
+
|
|
1406
|
+
6. **Add next steps:**
|
|
1407
|
+
- If UI work without page-plan:
|
|
1408
|
+
```
|
|
1409
|
+
š Ready to start development!
|
|
1410
|
+
|
|
1411
|
+
Next steps:
|
|
1412
|
+
1. (Recommended) Run: /pageplan @prd.md
|
|
1413
|
+
2. Edit page-plan.md (content, assets, approval)
|
|
1414
|
+
3. Review workflow: openspec/changes/{changeId}/.claude/phases.md
|
|
1415
|
+
4. Start development: /cdev {changeId}
|
|
1416
|
+
5. View progress: /cview {changeId}
|
|
1417
|
+
```
|
|
1418
|
+
|
|
1419
|
+
- Otherwise:
|
|
1420
|
+
```
|
|
1421
|
+
š Ready to start development!
|
|
1422
|
+
|
|
1423
|
+
Next steps:
|
|
1424
|
+
1. Review workflow: openspec/changes/{changeId}/.claude/phases.md
|
|
1425
|
+
2. Start development: /cdev {changeId}
|
|
1426
|
+
3. View progress: /cview {changeId}
|
|
1427
|
+
```
|
|
1428
|
+
|
|
1429
|
+
7. **Display the complete output message**
|
|
1456
1430
|
|
|
1457
|
-
|
|
1458
|
-
š Architecture: Task Analyzer v2.0 (Template-Free)
|
|
1459
|
-
š ļø Agents: ${agentSummary}
|
|
1431
|
+
---
|
|
1460
1432
|
|
|
1461
|
-
|
|
1462
|
-
ā openspec/changes/${changeId}/.claude/phases.md
|
|
1463
|
-
ā openspec/changes/${changeId}/.claude/flags.json
|
|
1464
|
-
ā openspec/changes/${changeId}/.claude/context.md
|
|
1433
|
+
## Helper Functions
|
|
1465
1434
|
|
|
1466
|
-
|
|
1467
|
-
Total: ${sortedTasks.length} tasks (${sortedTasks.filter(t => !t.autoAdded).length} original + ${autoAddedCount} auto-added)
|
|
1468
|
-
Incremental: ${incrementalCount} tasks with ${totalMilestones} milestones
|
|
1469
|
-
Phases: ${totalPhases}
|
|
1470
|
-
UX Approval Gates: ${phases.filter(p => p.agent === 'ux-tester').length}
|
|
1435
|
+
### extractTaskIds()
|
|
1471
1436
|
|
|
1472
|
-
|
|
1473
|
-
${phases.map((p, i) => {
|
|
1474
|
-
const phaseTasks = sortedTasks.filter(t => t.phase?.number === p.number)
|
|
1475
|
-
const agent = getMostCommonAgent(phaseTasks)
|
|
1476
|
-
return ` Phase ${p.number}: ${p.name} (${agent}, ${phaseTasks.length} tasks)`
|
|
1477
|
-
}).join('\n')}
|
|
1478
|
-
`
|
|
1479
|
-
|
|
1480
|
-
// š v2.6.0: Recommend /pageplan if UI work detected
|
|
1481
|
-
if (hasUIWork) {
|
|
1482
|
-
if (hasPagePlan) {
|
|
1483
|
-
output += `
|
|
1484
|
-
ā
page-plan.md found: ${pagePlanPath}
|
|
1485
|
-
ā uxui-frontend will use this for component planning
|
|
1486
|
-
`
|
|
1487
|
-
} else {
|
|
1488
|
-
output += `
|
|
1489
|
-
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
1490
|
-
šØ UI Work Detected!
|
|
1491
|
-
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
1492
|
-
|
|
1493
|
-
Phases with UI work:
|
|
1494
|
-
${uiPhases.map(p => \` ⢠Phase \${p.number}: \${p.name} (\${p.agent})\`).join('\\n')}
|
|
1495
|
-
|
|
1496
|
-
š” RECOMMENDED: Run /pageplan before /cdev
|
|
1497
|
-
|
|
1498
|
-
Why?
|
|
1499
|
-
āāā Content variants (3 options per element - user picks A/B/C)
|
|
1500
|
-
āāā Component index (auto-generated, prevents duplicates)
|
|
1501
|
-
āāā Asset checklist (images, icons with specs)
|
|
1502
|
-
āāā Approval process (user reviews before implementation)
|
|
1503
|
-
|
|
1504
|
-
š Recommended Steps:
|
|
1505
|
-
1. /pageplan @prd.md ā Generate page plan
|
|
1506
|
-
2. Edit page-plan.md ā Pick A/B/C content, prepare assets
|
|
1507
|
-
3. Mark APPROVED in Section 6 ā Sign-off before implementation
|
|
1508
|
-
4. /cdev ${changeId} ā Implement with real content
|
|
1509
|
-
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
1510
|
-
`
|
|
1511
|
-
}
|
|
1512
|
-
}
|
|
1437
|
+
To extract task IDs from tasks.md content:
|
|
1513
1438
|
|
|
1514
|
-
|
|
1515
|
-
|
|
1439
|
+
1. Search for patterns matching: `- [ ] X.X` (checkbox followed by number.number)
|
|
1440
|
+
- Pattern: `-\s*\[\s*\]\s*(\d+\.\d+)`
|
|
1441
|
+
- Matches: `- [ ] 1.1`, `- [ ] 2.3`, etc.
|
|
1516
1442
|
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
if (hasUIWork && !hasPagePlan) {
|
|
1520
|
-
output += `
|
|
1521
|
-
1. (Recommended) Run: /pageplan @prd.md
|
|
1522
|
-
2. Edit page-plan.md (content, assets, approval)
|
|
1523
|
-
3. Review workflow: openspec/changes/${changeId}/.claude/phases.md
|
|
1524
|
-
4. Start development: /cdev ${changeId}
|
|
1525
|
-
5. View progress: /cview ${changeId}`
|
|
1526
|
-
} else {
|
|
1527
|
-
output += `
|
|
1528
|
-
1. Review workflow: openspec/changes/${changeId}/.claude/phases.md
|
|
1529
|
-
2. Start development: /cdev ${changeId}
|
|
1530
|
-
3. View progress: /cview ${changeId}`
|
|
1531
|
-
}
|
|
1443
|
+
2. Extract the number portion (e.g., "1.1", "1.2", "2.1")
|
|
1532
1444
|
|
|
1533
|
-
|
|
1534
|
-
|
|
1445
|
+
3. Return all found task IDs as a list
|
|
1446
|
+
|
|
1447
|
+
**Example:**
|
|
1448
|
+
- Input: `- [ ] 1.1 Setup database\n- [ ] 1.2 Create schema`
|
|
1449
|
+
- Output: `["1.1", "1.2"]`
|
|
1535
1450
|
|
|
1536
1451
|
---
|
|
1537
1452
|
|
|
1538
|
-
|
|
1453
|
+
### getMostCommonAgent() (v2.0 - Template-Free)
|
|
1539
1454
|
|
|
1540
|
-
|
|
1541
|
-
```typescript
|
|
1542
|
-
// Extract task IDs like "1.1", "1.2", "2.1" from tasks.md
|
|
1543
|
-
function extractTaskIds(content: string): string[] {
|
|
1544
|
-
const regex = /-\s*\[\s*\]\s*(\d+\.\d+)/g
|
|
1545
|
-
const matches = [...content.matchAll(regex)]
|
|
1546
|
-
return matches.map(m => m[1])
|
|
1547
|
-
}
|
|
1548
|
-
```
|
|
1455
|
+
> **v2.0:** Agent determined by AI analysis of tasks, not phase templates
|
|
1549
1456
|
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1457
|
+
To find the most common agent in a list of tasks:
|
|
1458
|
+
|
|
1459
|
+
1. **Handle empty list:**
|
|
1460
|
+
- If tasks list is empty, return `'integration'` as default
|
|
1461
|
+
|
|
1462
|
+
2. **Count agents:**
|
|
1463
|
+
- Create a count map for each agent
|
|
1464
|
+
- Loop through tasks and increment count for each task's agent
|
|
1465
|
+
|
|
1466
|
+
3. **Find the most common:**
|
|
1467
|
+
- Sort agents by count (descending)
|
|
1468
|
+
- Return the agent with highest count
|
|
1469
|
+
|
|
1470
|
+
**Example:**
|
|
1471
|
+
- Input: `[{agent: 'backend'}, {agent: 'backend'}, {agent: 'test-debug'}]`
|
|
1472
|
+
- Output: `'backend'` (appears 2 times)
|
|
1473
|
+
|
|
1474
|
+
---
|
|
1565
1475
|
|
|
1566
1476
|
### groupTasksByPhase()
|
|
1567
|
-
```typescript
|
|
1568
|
-
function groupTasksByPhase(tasks: AnalyzedTask[]): Phase[] {
|
|
1569
|
-
const phaseMap = new Map()
|
|
1570
|
-
|
|
1571
|
-
tasks.forEach(task => {
|
|
1572
|
-
const phaseNum = task.phase?.number || 1
|
|
1573
|
-
const phaseName = task.phase?.name || `Phase ${phaseNum}`
|
|
1574
|
-
|
|
1575
|
-
if (!phaseMap.has(phaseNum)) {
|
|
1576
|
-
phaseMap.set(phaseNum, {
|
|
1577
|
-
number: phaseNum,
|
|
1578
|
-
name: phaseName,
|
|
1579
|
-
tasks: []
|
|
1580
|
-
})
|
|
1581
|
-
}
|
|
1582
|
-
phaseMap.get(phaseNum).tasks.push(task)
|
|
1583
|
-
})
|
|
1584
1477
|
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1478
|
+
To group tasks into phases:
|
|
1479
|
+
|
|
1480
|
+
1. **Create a phase map** (keyed by phase number)
|
|
1481
|
+
|
|
1482
|
+
2. **For each task:**
|
|
1483
|
+
- Get phase number from `task.phase.number` (default to 1 if not set)
|
|
1484
|
+
- Get phase name from `task.phase.name` (default to `"Phase {number}"` if not set)
|
|
1485
|
+
|
|
1486
|
+
3. **Add to map:**
|
|
1487
|
+
- If phase number not in map yet:
|
|
1488
|
+
- Create new phase entry with: number, name, empty tasks array
|
|
1489
|
+
- Add task to that phase's tasks array
|
|
1490
|
+
|
|
1491
|
+
4. **Convert map to sorted array:**
|
|
1492
|
+
- Convert map values to array
|
|
1493
|
+
- Sort by phase number (ascending)
|
|
1494
|
+
- Return sorted phases
|
|
1495
|
+
|
|
1496
|
+
**Example:**
|
|
1497
|
+
- Input: `[{phase: {number: 2, name: "Backend"}}, {phase: {number: 1, name: "UI"}}]`
|
|
1498
|
+
- Output: `[{number: 1, name: "UI", tasks: [...]}, {number: 2, name: "Backend", tasks: [...]}]`
|
|
1499
|
+
|
|
1500
|
+
---
|
|
1588
1501
|
|
|
1589
1502
|
### getMaxRisk()
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1503
|
+
|
|
1504
|
+
To find the highest risk level across tasks:
|
|
1505
|
+
|
|
1506
|
+
1. Check if any task has `risk: 'HIGH'` ā return `'HIGH'`
|
|
1507
|
+
2. Otherwise, check if any task has `risk: 'MEDIUM'` ā return `'MEDIUM'`
|
|
1508
|
+
3. Otherwise, return `'LOW'`
|
|
1509
|
+
|
|
1510
|
+
**Example:**
|
|
1511
|
+
- Input: `[{risk: 'LOW'}, {risk: 'MEDIUM'}, {risk: 'LOW'}]`
|
|
1512
|
+
- Output: `'MEDIUM'`
|
|
1513
|
+
|
|
1514
|
+
---
|
|
1597
1515
|
|
|
1598
1516
|
### detectChangeType() (v2.0 - AI-Driven)
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1517
|
+
|
|
1518
|
+
> **v2.0:** Detect from analyzed tasks, not keywords
|
|
1519
|
+
|
|
1520
|
+
To detect change type from task agents:
|
|
1521
|
+
|
|
1522
|
+
1. **Check which agents are present:**
|
|
1523
|
+
- `hasUI`: Any task with `agent: 'uxui-frontend'`
|
|
1524
|
+
- `hasBackend`: Any task with `agent: 'backend'`
|
|
1525
|
+
- `hasDatabase`: Any task with `agent: 'database'`
|
|
1526
|
+
- `hasTests`: Any task with `agent: 'test-debug'`
|
|
1527
|
+
|
|
1528
|
+
2. **Determine type based on combination:**
|
|
1529
|
+
- If `hasUI` AND `hasBackend` AND `hasDatabase` ā `'full-stack'`
|
|
1530
|
+
- If `hasUI` AND NOT `hasBackend` ā `'frontend-only'`
|
|
1531
|
+
- If `hasBackend` AND NOT `hasUI` ā `'backend-only'`
|
|
1532
|
+
- If `hasTests` AND total tasks <= 5 ā `'bug-fix'`
|
|
1533
|
+
- Otherwise ā `'feature'`
|
|
1534
|
+
|
|
1535
|
+
**Example:**
|
|
1536
|
+
- Input: `[{agent: 'uxui-frontend'}, {agent: 'backend'}, {agent: 'database'}]`
|
|
1537
|
+
- Output: `'full-stack'`
|
|
1615
1538
|
|
|
1616
1539
|
### detectAdditionalTech() - REMOVED (v3.1.0)
|
|
1617
1540
|
|