project-iris 0.5.1 → 0.6.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/flows/aidlc/memory-bank.yaml +21 -0
- package/flows/aidlc/skills/construction/bolt-start.md +217 -1
- package/flows/aidlc/skills/inception/story-create.md +115 -0
- package/flows/aidlc/templates/construction/ui-patterns.md +30 -1
- package/flows/aidlc/templates/inception/story-template.md +73 -0
- package/lib/InstallerFactory.js +19 -15
- package/lib/analytics/env-detector.d.ts +16 -0
- package/lib/analytics/index.d.ts +6 -0
- package/lib/analytics/machine-id.d.ts +9 -0
- package/lib/analytics/tracker.d.ts +70 -0
- package/lib/installer.js +48 -1
- package/lib/mcp/FigmaMCPProvider.js +70 -0
- package/lib/mcp/constants.js +15 -0
- package/lib/mcp/index.js +35 -0
- package/package.json +2 -1
- package/scripts/bolt-complete.js +8 -56
- package/scripts/lib/frontmatter-utils.js +75 -0
- package/scripts/status-integrity.js +5 -55
|
@@ -67,6 +67,27 @@ naming:
|
|
|
67
67
|
example: "001-user-signup.md"
|
|
68
68
|
note: "3-digit story number + kebab-case story title"
|
|
69
69
|
full_path_example: "memory-bank/intents/001-user-authentication/units/001-auth-service/stories/001-user-signup.md"
|
|
70
|
+
optional_fields:
|
|
71
|
+
figma_frames:
|
|
72
|
+
description: "Optional array of Figma frame URLs for UI stories"
|
|
73
|
+
type: "array of URLs"
|
|
74
|
+
formats:
|
|
75
|
+
- "https://www.figma.com/design/{file-id}/{file-name}?node-id={node-id}&t={token}"
|
|
76
|
+
- "https://www.figma.com/make/{file-id}/{file-name}?p=f&t={token}"
|
|
77
|
+
note: "Construction Agent uses Figma MCP to fetch design specs. Prototype connections extracted if available."
|
|
78
|
+
collected_by: "Inception Agent during story creation (Step 4a in story-create skill)"
|
|
79
|
+
interaction_flows:
|
|
80
|
+
description: "Manual interaction flow definition (fallback when Figma has no prototype)"
|
|
81
|
+
type: "array of screen interaction definitions"
|
|
82
|
+
structure: |
|
|
83
|
+
- screen: {screen-name}
|
|
84
|
+
interactions:
|
|
85
|
+
- element: "{Button/Link text}"
|
|
86
|
+
action: navigate | overlay | close | submit
|
|
87
|
+
target: {destination-screen}
|
|
88
|
+
note: "Only needed if Figma file has designs but no prototype connections. Construction Agent uses this to wire navigation."
|
|
89
|
+
collected_by: "Inception Agent during story creation (Step 4a-2 in story-create skill)"
|
|
90
|
+
priority: "Figma prototype > interaction_flows > infer from labels > ask user"
|
|
70
91
|
|
|
71
92
|
bolts:
|
|
72
93
|
format: "{BBB}-{unit-name}/"
|
|
@@ -184,6 +184,222 @@ Scan the bolt's unit name, story titles, and story descriptions for these signal
|
|
|
184
184
|
|
|
185
185
|
This is not a hard requirement - use judgment on which principles apply to the current task.
|
|
186
186
|
|
|
187
|
+
### 4c. Figma Frames (Conditional - Per Story)
|
|
188
|
+
|
|
189
|
+
**Check each story's `figma_frames` field in frontmatter.**
|
|
190
|
+
|
|
191
|
+
If a story has `figma_frames` (array of Figma URLs), use Figma MCP to fetch design specs and interaction flows before implementing.
|
|
192
|
+
|
|
193
|
+
**Supported Figma URL formats:**
|
|
194
|
+
|
|
195
|
+
- Design file: `https://www.figma.com/design/{file-id}/{file-name}?node-id={node-id}&t={token}`
|
|
196
|
+
- Make file: `https://www.figma.com/make/{file-id}/{file-name}?p=f&t={token}`
|
|
197
|
+
|
|
198
|
+
**When `figma_frames` is present:**
|
|
199
|
+
|
|
200
|
+
```text
|
|
201
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
202
|
+
│ FIGMA FRAMES DETECTED │
|
|
203
|
+
│ │
|
|
204
|
+
│ Story: {story-id} │
|
|
205
|
+
│ Frames: {count} Figma frame(s) │
|
|
206
|
+
│ │
|
|
207
|
+
│ Action: Use Figma MCP to fetch design and flow data │
|
|
208
|
+
└─────────────────────────────────────────────────────────────┘
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**Figma MCP Integration:**
|
|
212
|
+
|
|
213
|
+
Use the Figma MCP tool to read design specifications. The MCP server is configured via:
|
|
214
|
+
`claude mcp add figma --transport http --url https://mcp.figma.com/mcp`
|
|
215
|
+
|
|
216
|
+
### 4c-1. Extract Design Specs (Per Frame)
|
|
217
|
+
|
|
218
|
+
For EACH Figma frame URL, extract:
|
|
219
|
+
|
|
220
|
+
```text
|
|
221
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
222
|
+
│ DESIGN SPECS EXTRACTION │
|
|
223
|
+
│ │
|
|
224
|
+
│ For each frame, extract: │
|
|
225
|
+
│ │
|
|
226
|
+
│ 1. Layout & Structure │
|
|
227
|
+
│ - Component hierarchy (parent/child relationships) │
|
|
228
|
+
│ - Spacing values (padding, margins, gaps) │
|
|
229
|
+
│ - Alignment and positioning │
|
|
230
|
+
│ │
|
|
231
|
+
│ 2. Visual Styling │
|
|
232
|
+
│ - Colors (exact hex values, opacity) │
|
|
233
|
+
│ - Typography (font family, size, weight, line height) │
|
|
234
|
+
│ - Border radius, shadows, effects │
|
|
235
|
+
│ │
|
|
236
|
+
│ 3. Component States │
|
|
237
|
+
│ - Default, hover, active, disabled, focus states │
|
|
238
|
+
│ - Loading states and skeletons │
|
|
239
|
+
│ - Error and success states │
|
|
240
|
+
│ │
|
|
241
|
+
│ 4. Responsive Variants (if defined) │
|
|
242
|
+
│ - Mobile, tablet, desktop variations │
|
|
243
|
+
│ - Breakpoint-specific layouts │
|
|
244
|
+
└─────────────────────────────────────────────────────────────┘
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### 4c-2. Extract Interaction Flows
|
|
248
|
+
|
|
249
|
+
**Figma prototype connections MAY define interaction flows - but many designs don't have them.**
|
|
250
|
+
|
|
251
|
+
**Step 1: Check for prototype connections via Figma MCP**
|
|
252
|
+
|
|
253
|
+
```text
|
|
254
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
255
|
+
│ PROTOTYPE CONNECTION CHECK │
|
|
256
|
+
│ │
|
|
257
|
+
│ Query Figma MCP for prototype data on each frame. │
|
|
258
|
+
│ Prototype connections include: │
|
|
259
|
+
│ - Navigation triggers (which element → which frame) │
|
|
260
|
+
│ - Transition types (push, overlay, swap) │
|
|
261
|
+
│ - Overlay settings (dismiss on click outside, etc.) │
|
|
262
|
+
└─────────────────────────────────────────────────────────────┘
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
**Step 2: Determine flow source (in priority order)**
|
|
266
|
+
|
|
267
|
+
```text
|
|
268
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
269
|
+
│ FLOW SOURCE PRIORITY │
|
|
270
|
+
│ │
|
|
271
|
+
│ 1. Figma prototype connections (if they exist) │
|
|
272
|
+
│ → Extract automatically from Figma MCP │
|
|
273
|
+
│ │
|
|
274
|
+
│ 2. Manual flow definition in story (if provided) │
|
|
275
|
+
│ → Read from story's `interaction_flows` field │
|
|
276
|
+
│ │
|
|
277
|
+
│ 3. No flow information available │
|
|
278
|
+
│ → Ask user OR infer from button labels │
|
|
279
|
+
└─────────────────────────────────────────────────────────────┘
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### 4c-2a. If Prototype Connections Exist in Figma
|
|
283
|
+
|
|
284
|
+
Extract interaction data:
|
|
285
|
+
|
|
286
|
+
- Navigation actions (which element → destination frame)
|
|
287
|
+
- Overlay triggers (modals, bottom sheets, tooltips)
|
|
288
|
+
- Transition types and animations
|
|
289
|
+
- Dismiss behaviors
|
|
290
|
+
|
|
291
|
+
### 4c-2b. If NO Prototype Connections in Figma
|
|
292
|
+
|
|
293
|
+
**Check story for manual `interaction_flows` definition:**
|
|
294
|
+
|
|
295
|
+
```yaml
|
|
296
|
+
# In story frontmatter or body
|
|
297
|
+
interaction_flows:
|
|
298
|
+
- screen: login-form
|
|
299
|
+
interactions:
|
|
300
|
+
- element: "Sign Up button"
|
|
301
|
+
action: navigate
|
|
302
|
+
target: registration-form
|
|
303
|
+
- element: "Forgot Password link"
|
|
304
|
+
action: overlay
|
|
305
|
+
target: forgot-password-modal
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
**If manual flows exist:** Use them to build the interaction map.
|
|
309
|
+
|
|
310
|
+
**If NO flows defined anywhere:**
|
|
311
|
+
|
|
312
|
+
```text
|
|
313
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
314
|
+
│ NO INTERACTION FLOW DEFINED │
|
|
315
|
+
│ │
|
|
316
|
+
│ Story: {story-id} │
|
|
317
|
+
│ Figma frames: {count} (no prototype connections found) │
|
|
318
|
+
│ Manual flows: Not defined │
|
|
319
|
+
│ │
|
|
320
|
+
│ Options: │
|
|
321
|
+
│ 1 - Define interactions now (I'll ask about each button) │
|
|
322
|
+
│ 2 - Infer from button labels (best guess) │
|
|
323
|
+
│ 3 - Implement as static screens (no navigation wiring) │
|
|
324
|
+
└─────────────────────────────────────────────────────────────┘
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
**Option 1 - Define interactions:** Ask user about each interactive element:
|
|
328
|
+
|
|
329
|
+
```text
|
|
330
|
+
I found these interactive elements in the Figma frames:
|
|
331
|
+
- "Sign Up" button
|
|
332
|
+
- "Forgot Password" link
|
|
333
|
+
- "Login" button
|
|
334
|
+
|
|
335
|
+
For each element, what should happen on click?
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
**Option 2 - Infer from labels:** Make reasonable assumptions:
|
|
339
|
+
|
|
340
|
+
- "Submit", "Save", "Login" → form submission
|
|
341
|
+
- "Cancel", "Close", "X" → close/go back
|
|
342
|
+
- "Sign Up", "Register" → navigate to registration
|
|
343
|
+
- "Learn More", "View Details" → navigate to detail screen
|
|
344
|
+
|
|
345
|
+
**Option 3 - Static screens:** Implement visuals only, user wires navigation later.
|
|
346
|
+
|
|
347
|
+
### 4c-2c. Output: Interaction Map
|
|
348
|
+
|
|
349
|
+
After determining flows (from any source), build the interaction map:
|
|
350
|
+
|
|
351
|
+
```markdown
|
|
352
|
+
### Interaction Map: {story-id}
|
|
353
|
+
|
|
354
|
+
**Flow Source:** {Figma prototype | Manual definition | Inferred | Static}
|
|
355
|
+
|
|
356
|
+
#### Screen: {frame-name-1}
|
|
357
|
+
| Element | Action | Target | Type |
|
|
358
|
+
|---------|--------|--------|------|
|
|
359
|
+
| "Sign Up" button | on-click | registration-form | navigate |
|
|
360
|
+
| "Forgot Password" link | on-click | forgot-password-modal | overlay |
|
|
361
|
+
|
|
362
|
+
#### Navigation Flow
|
|
363
|
+
|
|
364
|
+
login-screen
|
|
365
|
+
├── "Sign Up" → registration-form
|
|
366
|
+
├── "Forgot Password" → forgot-password-modal (overlay)
|
|
367
|
+
└── "Login" → dashboard (on success)
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
### 4c-3. If Figma MCP is not available
|
|
371
|
+
|
|
372
|
+
```text
|
|
373
|
+
⚠️ Figma MCP not configured - cannot fetch design specs
|
|
374
|
+
Story {story-id} has figma_frames but Figma MCP is unavailable.
|
|
375
|
+
|
|
376
|
+
Options:
|
|
377
|
+
1 - Continue using ui-patterns.md guidelines (design may not match)
|
|
378
|
+
2 - Stop and configure Figma MCP first
|
|
379
|
+
|
|
380
|
+
To configure: Run `claude mcp add figma --transport http --url https://mcp.figma.com/mcp`
|
|
381
|
+
Then authenticate via `/mcp` in Claude Code.
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
### 4c-4. Design-First Implementation Rule
|
|
385
|
+
|
|
386
|
+
When a story has `figma_frames`:
|
|
387
|
+
|
|
388
|
+
1. **Fetch ALL frames FIRST** before writing any UI code
|
|
389
|
+
2. **Build the interaction map** to understand the complete flow
|
|
390
|
+
3. **Extract exact values** - don't approximate colors, spacing, or typography
|
|
391
|
+
4. **Match the design** - Figma specs override ui-patterns.md defaults
|
|
392
|
+
5. **Implement navigation** - wire up all interactions as defined in Figma
|
|
393
|
+
6. **Note deviations** - if you must deviate from design, document why in code comments
|
|
394
|
+
|
|
395
|
+
**When NO `figma_frames` exists:**
|
|
396
|
+
|
|
397
|
+
Fall back to ui-patterns.md guidelines (Step 4b). The agent should generate UI based on:
|
|
398
|
+
|
|
399
|
+
1. Acceptance criteria in the story
|
|
400
|
+
2. Project's ux-guide.md (component library, styling approach)
|
|
401
|
+
3. ui-patterns.md design principles
|
|
402
|
+
|
|
187
403
|
### 5. Determine Current Stage
|
|
188
404
|
|
|
189
405
|
Based on bolt state:
|
|
@@ -306,7 +522,7 @@ For the current stage, follow the bolt type definition:
|
|
|
306
522
|
**Required artifacts are defined by the bolt type definition file.**
|
|
307
523
|
|
|
308
524
|
Each bolt type specifies its own stages and required artifacts. Refer to:
|
|
309
|
-
|
|
525
|
+
`.iris/aidlc/templates/construction/bolt-types/{bolt_type}.md`
|
|
310
526
|
|
|
311
527
|
**If the bolt type definition specifies an artifact for a stage, you MUST create it.**
|
|
312
528
|
|
|
@@ -148,6 +148,121 @@ Organize stories for bolt planning:
|
|
|
148
148
|
- **Should**: Important but not blocking (Error handling, validation)
|
|
149
149
|
- **Could**: Nice to have (Advanced features, optimizations)
|
|
150
150
|
|
|
151
|
+
### 4a. Collect Figma References (UI Stories Only)
|
|
152
|
+
|
|
153
|
+
**For stories that involve UI screens, ask the user for Figma frame URLs.**
|
|
154
|
+
|
|
155
|
+
**Detection:** Check if the story involves UI by looking for these signals in the user story or acceptance criteria:
|
|
156
|
+
|
|
157
|
+
- Screen, page, view, layout
|
|
158
|
+
- Form, button, modal, dialog, bottom sheet
|
|
159
|
+
- Navigation, menu, sidebar
|
|
160
|
+
- Component, widget, card, table
|
|
161
|
+
|
|
162
|
+
**If UI signals detected:**
|
|
163
|
+
|
|
164
|
+
```text
|
|
165
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
166
|
+
│ FIGMA REFERENCE COLLECTION │
|
|
167
|
+
│ │
|
|
168
|
+
│ Story: {story-id} - {title} │
|
|
169
|
+
│ UI Detected: {signal found} │
|
|
170
|
+
│ │
|
|
171
|
+
│ Do you have Figma designs for this story? │
|
|
172
|
+
│ │
|
|
173
|
+
│ 1 - Yes, I'll provide Figma frame URLs │
|
|
174
|
+
│ 2 - No, use ui-patterns.md guidelines │
|
|
175
|
+
│ 3 - Skip for now (add later) │
|
|
176
|
+
└─────────────────────────────────────────────────────────────┘
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**If user selects "Yes":**
|
|
180
|
+
|
|
181
|
+
```text
|
|
182
|
+
Please provide Figma frame URLs for this story.
|
|
183
|
+
You can provide multiple URLs (one per screen/component).
|
|
184
|
+
|
|
185
|
+
Supported formats:
|
|
186
|
+
- Design file: https://www.figma.com/design/{file-id}/{name}?node-id={node-id}&t={token}
|
|
187
|
+
- Make file: https://www.figma.com/make/{file-id}/{name}?p=f&t={token}
|
|
188
|
+
|
|
189
|
+
Enter URLs (one per line, empty line to finish):
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**Store in story frontmatter:**
|
|
193
|
+
|
|
194
|
+
```yaml
|
|
195
|
+
figma_frames:
|
|
196
|
+
- https://www.figma.com/design/xxx/Project?node-id=1:100&t=abc
|
|
197
|
+
- https://www.figma.com/design/xxx/Project?node-id=1:200&t=abc
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
**If user selects "No" or "Skip":**
|
|
201
|
+
|
|
202
|
+
Leave `figma_frames: []` in frontmatter. Construction Agent will use ui-patterns.md guidelines.
|
|
203
|
+
|
|
204
|
+
**Batch Collection Option:**
|
|
205
|
+
|
|
206
|
+
If multiple UI stories are being created, offer batch collection:
|
|
207
|
+
|
|
208
|
+
```text
|
|
209
|
+
I detected {n} stories with UI components. Would you like to:
|
|
210
|
+
|
|
211
|
+
1 - Provide Figma URLs for each story individually
|
|
212
|
+
2 - Provide a single Figma file URL (I'll ask which frames map to which stories)
|
|
213
|
+
3 - Skip Figma references for all (use ui-patterns.md)
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### 4a-2. Collect Interaction Flows (If No Prototype in Figma)
|
|
217
|
+
|
|
218
|
+
**After collecting Figma URLs, ask about prototype connections:**
|
|
219
|
+
|
|
220
|
+
```text
|
|
221
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
222
|
+
│ INTERACTION FLOW CHECK │
|
|
223
|
+
│ │
|
|
224
|
+
│ Does your Figma file have prototype connections defined? │
|
|
225
|
+
│ (Click interactions that link frames together) │
|
|
226
|
+
│ │
|
|
227
|
+
│ 1 - Yes, prototype is set up in Figma │
|
|
228
|
+
│ 2 - No, I'll define interactions manually │
|
|
229
|
+
│ 3 - Not sure / I'll handle it during construction │
|
|
230
|
+
└─────────────────────────────────────────────────────────────┘
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
**If user selects "No" (manual definition):**
|
|
234
|
+
|
|
235
|
+
```text
|
|
236
|
+
Let's define the interactions for each screen.
|
|
237
|
+
|
|
238
|
+
For screen: {screen-name} (from Figma frame)
|
|
239
|
+
|
|
240
|
+
What interactive elements are on this screen?
|
|
241
|
+
(List buttons, links, or other clickable elements)
|
|
242
|
+
|
|
243
|
+
For each element, I'll ask:
|
|
244
|
+
- What action does it trigger? (navigate, overlay, close, submit)
|
|
245
|
+
- What is the target? (destination screen name)
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
**Store in story frontmatter:**
|
|
249
|
+
|
|
250
|
+
```yaml
|
|
251
|
+
interaction_flows:
|
|
252
|
+
- screen: login-form
|
|
253
|
+
interactions:
|
|
254
|
+
- element: "Sign Up button"
|
|
255
|
+
action: navigate
|
|
256
|
+
target: registration-form
|
|
257
|
+
- element: "Forgot Password link"
|
|
258
|
+
action: overlay
|
|
259
|
+
target: forgot-password-modal
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
**If user selects "Yes" or "Not sure":**
|
|
263
|
+
|
|
264
|
+
Leave `interaction_flows: []` - Construction Agent will extract from Figma or ask during implementation.
|
|
265
|
+
|
|
151
266
|
### 4b. Validate FR-to-Story Coverage (CRITICAL - HARD GATE)
|
|
152
267
|
|
|
153
268
|
**⛔ HARD GATE**: Before creating story files, validate that ALL assigned FRs have stories.
|
|
@@ -1,6 +1,35 @@
|
|
|
1
1
|
# UI Design Guide
|
|
2
2
|
|
|
3
|
-
You are building UI for a project
|
|
3
|
+
You are building UI for a project. Your goal is to create polished, professional interfaces that users love. Think like a designer, not a developer.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Figma Design Integration
|
|
8
|
+
|
|
9
|
+
**If a story has `figma_frames` (array of Figma URLs), use Figma MCP to fetch design specs and interaction flows.**
|
|
10
|
+
|
|
11
|
+
### When Figma Frames Exist
|
|
12
|
+
|
|
13
|
+
1. **Fetch ALL frames first** via Figma MCP before writing any code
|
|
14
|
+
2. **Extract design specs** from each frame:
|
|
15
|
+
- Colors (exact hex values, opacity)
|
|
16
|
+
- Typography (font family, size, weight, line height)
|
|
17
|
+
- Spacing (padding, margins, gaps)
|
|
18
|
+
- Border radius, shadows, effects
|
|
19
|
+
- Component dimensions and states
|
|
20
|
+
3. **Extract interaction flows** (prototype connections):
|
|
21
|
+
- Navigation actions (which button goes where)
|
|
22
|
+
- Overlay triggers (modals, bottom sheets, tooltips)
|
|
23
|
+
- State changes and toggle behaviors
|
|
24
|
+
- Scroll and gesture behaviors
|
|
25
|
+
4. **Build an interaction map** showing the complete flow between screens
|
|
26
|
+
5. **Match the design exactly** - Figma specs override the guidelines below
|
|
27
|
+
6. **Implement all interactions** as defined in Figma's prototype connections
|
|
28
|
+
7. **Document deviations** - if you must deviate, explain why in code comments
|
|
29
|
+
|
|
30
|
+
### When No Figma Frames Exist
|
|
31
|
+
|
|
32
|
+
Follow the guidelines in this document to create professional UI without a designer.
|
|
4
33
|
|
|
5
34
|
---
|
|
6
35
|
|
|
@@ -16,6 +16,8 @@ priority: must|should|could
|
|
|
16
16
|
created: {YYYY-MM-DDTHH:MM:SSZ}
|
|
17
17
|
assigned_bolt: null
|
|
18
18
|
implemented: false
|
|
19
|
+
figma_frames: [] # Optional: Array of Figma frame URLs (design specs extracted via MCP)
|
|
20
|
+
interaction_flows: [] # Optional: Manual flow definition (only if Figma has no prototype connections)
|
|
19
21
|
---
|
|
20
22
|
```
|
|
21
23
|
|
|
@@ -38,6 +40,71 @@ implemented: false
|
|
|
38
40
|
- [ ] **Given** {precondition}, **When** {action}, **Then** {expected outcome}
|
|
39
41
|
- [ ] **Given** {precondition}, **When** {action}, **Then** {expected outcome}
|
|
40
42
|
|
|
43
|
+
## Figma Frames
|
|
44
|
+
|
|
45
|
+
{Optional: List Figma frame URLs for screens in this story.}
|
|
46
|
+
|
|
47
|
+
**Figma URLs:**
|
|
48
|
+
|
|
49
|
+
- {figma-frame-url-1}
|
|
50
|
+
- {figma-frame-url-2}
|
|
51
|
+
|
|
52
|
+
**Supported URL formats:**
|
|
53
|
+
|
|
54
|
+
- Design file: `https://www.figma.com/design/{file-id}/{file-name}?node-id={node-id}&t={token}`
|
|
55
|
+
- Make file: `https://www.figma.com/make/{file-id}/{file-name}?p=f&t={token}`
|
|
56
|
+
|
|
57
|
+
**What the Construction Agent extracts via Figma MCP:**
|
|
58
|
+
|
|
59
|
+
1. **Design specs** (always available): colors, typography, spacing, components
|
|
60
|
+
2. **Prototype connections** (if defined in Figma): navigation flows, overlays, transitions
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Interaction Flows (Optional - Only if Figma has no prototype)
|
|
65
|
+
|
|
66
|
+
{If your Figma file has prototype connections defined, skip this section - flows are extracted automatically.}
|
|
67
|
+
|
|
68
|
+
{If your Figma has designs but NO prototype connections, define interactions manually here:}
|
|
69
|
+
|
|
70
|
+
```yaml
|
|
71
|
+
interaction_flows:
|
|
72
|
+
- screen: {screen-name}
|
|
73
|
+
interactions:
|
|
74
|
+
- element: "{Button/Link text}"
|
|
75
|
+
action: navigate | overlay | close | submit
|
|
76
|
+
target: {destination-screen-name}
|
|
77
|
+
- element: "{Another element}"
|
|
78
|
+
action: overlay
|
|
79
|
+
target: {modal-or-bottomsheet-name}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**Action types:**
|
|
83
|
+
|
|
84
|
+
- `navigate` - Go to another screen (replaces current)
|
|
85
|
+
- `overlay` - Open modal, bottom sheet, tooltip, dropdown
|
|
86
|
+
- `close` - Close current overlay / go back
|
|
87
|
+
- `submit` - Form submission (may navigate on success/failure)
|
|
88
|
+
|
|
89
|
+
**Example:**
|
|
90
|
+
|
|
91
|
+
```yaml
|
|
92
|
+
interaction_flows:
|
|
93
|
+
- screen: login-form
|
|
94
|
+
interactions:
|
|
95
|
+
- element: "Sign Up button"
|
|
96
|
+
action: navigate
|
|
97
|
+
target: registration-form
|
|
98
|
+
- element: "Forgot Password link"
|
|
99
|
+
action: overlay
|
|
100
|
+
target: forgot-password-modal
|
|
101
|
+
- element: "Login button"
|
|
102
|
+
action: submit
|
|
103
|
+
target: dashboard # on success
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
{If no Figma design exists at all, describe the expected screens and interactions in the Technical Notes section.}
|
|
107
|
+
|
|
41
108
|
## Technical Notes
|
|
42
109
|
|
|
43
110
|
{Implementation hints, constraints, or considerations}
|
|
@@ -45,9 +112,11 @@ implemented: false
|
|
|
45
112
|
## Dependencies
|
|
46
113
|
|
|
47
114
|
### Requires
|
|
115
|
+
|
|
48
116
|
- {Other stories this depends on, or "None"}
|
|
49
117
|
|
|
50
118
|
### Enables
|
|
119
|
+
|
|
51
120
|
- {Stories that depend on this, or "None"}
|
|
52
121
|
|
|
53
122
|
## Edge Cases
|
|
@@ -60,6 +129,7 @@ implemented: false
|
|
|
60
129
|
## Out of Scope
|
|
61
130
|
|
|
62
131
|
- {What this story does NOT cover}
|
|
132
|
+
|
|
63
133
|
```
|
|
64
134
|
|
|
65
135
|
---
|
|
@@ -99,6 +169,9 @@ priority: must
|
|
|
99
169
|
created: 2024-12-05T10:00:00Z
|
|
100
170
|
assigned_bolt: 001-auth-service
|
|
101
171
|
implemented: false
|
|
172
|
+
figma_frames:
|
|
173
|
+
- https://www.figma.com/design/{file-id}/{file-name}?node-id={node-id-1}&t={token}
|
|
174
|
+
- https://www.figma.com/design/{file-id}/{file-name}?node-id={node-id-2}&t={token}
|
|
102
175
|
---
|
|
103
176
|
```
|
|
104
177
|
|
package/lib/InstallerFactory.js
CHANGED
|
@@ -11,25 +11,29 @@ const CodexInstaller = require('./installers/CodexInstaller');
|
|
|
11
11
|
const OpenCodeInstaller = require('./installers/OpenCodeInstaller');
|
|
12
12
|
|
|
13
13
|
class InstallerFactory {
|
|
14
|
+
static _installers = null;
|
|
15
|
+
|
|
14
16
|
static getInstallers() {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
17
|
+
if (!this._installers) {
|
|
18
|
+
this._installers = [
|
|
19
|
+
new ClaudeInstaller(),
|
|
20
|
+
new CursorInstaller(),
|
|
21
|
+
new CopilotInstaller(),
|
|
22
|
+
new AntigravityInstaller(),
|
|
23
|
+
new WindsurfInstaller(),
|
|
24
|
+
new ClineInstaller(),
|
|
25
|
+
new RooInstaller(),
|
|
26
|
+
new KiroInstaller(),
|
|
27
|
+
new GeminiInstaller(),
|
|
28
|
+
new CodexInstaller(),
|
|
29
|
+
new OpenCodeInstaller()
|
|
30
|
+
];
|
|
31
|
+
}
|
|
32
|
+
return this._installers;
|
|
28
33
|
}
|
|
29
34
|
|
|
30
35
|
static getInstaller(key) {
|
|
31
|
-
|
|
32
|
-
return installers.find(i => i.key === key);
|
|
36
|
+
return this.getInstallers().find(i => i.key === key);
|
|
33
37
|
}
|
|
34
38
|
}
|
|
35
39
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type declarations for env-detector module
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Detect the user's shell/terminal environment
|
|
7
|
+
* @returns Shell name (zsh, bash, powershell, cmd, fish, etc.) or 'unknown'
|
|
8
|
+
*/
|
|
9
|
+
export function detectShell(): string;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Check if telemetry is disabled via environment variables or CLI flag
|
|
13
|
+
* @param options - Optional overrides
|
|
14
|
+
* @returns True if telemetry should be disabled
|
|
15
|
+
*/
|
|
16
|
+
export function isTelemetryDisabled(options?: { noTelemetryFlag?: boolean }): boolean;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type declarations for tracker module
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
interface InitOptions {
|
|
6
|
+
noTelemetry?: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface AnalyticsTracker {
|
|
10
|
+
/**
|
|
11
|
+
* Initialize the analytics tracker
|
|
12
|
+
* @param options - Initialization options
|
|
13
|
+
* @returns True if analytics is enabled
|
|
14
|
+
*/
|
|
15
|
+
init(options?: InitOptions): boolean;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Track an event (fire-and-forget)
|
|
19
|
+
* @param eventName - Name of the event
|
|
20
|
+
* @param properties - Additional event properties
|
|
21
|
+
* @param waitForDelivery - Wait for event to be sent
|
|
22
|
+
* @returns Resolves when event is sent (if waitForDelivery is true)
|
|
23
|
+
*/
|
|
24
|
+
track(eventName: string, properties?: Record<string, unknown>, waitForDelivery?: boolean): Promise<void> | undefined;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Track installer_started event
|
|
28
|
+
* @returns Resolves when event is sent
|
|
29
|
+
*/
|
|
30
|
+
trackInstallerStarted(): Promise<void>;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Track ides_confirmed event
|
|
34
|
+
* @param ides - Array of selected IDE keys
|
|
35
|
+
* @returns Resolves when event is sent
|
|
36
|
+
*/
|
|
37
|
+
trackIdesConfirmed(ides: string[]): Promise<void>;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Track flow_selected event
|
|
41
|
+
* @param flow - Flow key
|
|
42
|
+
* @returns Resolves when event is sent
|
|
43
|
+
*/
|
|
44
|
+
trackFlowSelected(flow: string): Promise<void>;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Track installation_completed event
|
|
48
|
+
* @param ide - IDE key
|
|
49
|
+
* @param flow - Flow key
|
|
50
|
+
* @param durationMs - Installation duration in milliseconds
|
|
51
|
+
* @param filesCreated - Number of files created
|
|
52
|
+
*/
|
|
53
|
+
trackInstallationCompleted(ide: string, flow: string, durationMs: number, filesCreated: number): void;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Track installation_failed event
|
|
57
|
+
* @param ide - IDE key
|
|
58
|
+
* @param errorCategory - Error category
|
|
59
|
+
* @param flow - Flow key (optional)
|
|
60
|
+
*/
|
|
61
|
+
trackInstallationFailed(ide: string, errorCategory: string, flow?: string): void;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Check if analytics is enabled
|
|
65
|
+
*/
|
|
66
|
+
isEnabled(): boolean;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
declare const tracker: AnalyticsTracker;
|
|
70
|
+
export = tracker;
|
package/lib/installer.js
CHANGED
|
@@ -6,6 +6,7 @@ const CLIUtils = require('./cli-utils');
|
|
|
6
6
|
const InstallerFactory = require('./InstallerFactory');
|
|
7
7
|
const { FLOWS } = require('./constants');
|
|
8
8
|
const analytics = require('./analytics');
|
|
9
|
+
const mcpManager = require('./mcp');
|
|
9
10
|
|
|
10
11
|
// Use theme from CLIUtils for consistent styling
|
|
11
12
|
const { theme } = CLIUtils;
|
|
@@ -108,6 +109,29 @@ async function installClaudeMd() {
|
|
|
108
109
|
return true;
|
|
109
110
|
}
|
|
110
111
|
|
|
112
|
+
/**
|
|
113
|
+
* Configure Figma MCP for Claude Code
|
|
114
|
+
*/
|
|
115
|
+
async function configureFigmaMCP() {
|
|
116
|
+
const figmaProvider = mcpManager.getProvider('figma');
|
|
117
|
+
|
|
118
|
+
console.log(theme.dim(' Checking for Claude Code CLI...'));
|
|
119
|
+
|
|
120
|
+
const result = await figmaProvider.configureForClaudeCode();
|
|
121
|
+
|
|
122
|
+
if (result.success) {
|
|
123
|
+
if (result.alreadyExists) {
|
|
124
|
+
CLIUtils.displayStatus('', 'Figma MCP: already configured', 'info');
|
|
125
|
+
} else {
|
|
126
|
+
CLIUtils.displayStatus('', 'Figma MCP: configured', 'success');
|
|
127
|
+
}
|
|
128
|
+
console.log('');
|
|
129
|
+
console.log(theme.dim(' Tip: Run /mcp in Claude Code to authenticate with Figma'));
|
|
130
|
+
} else {
|
|
131
|
+
CLIUtils.displayStatus('', `Figma MCP: ${result.message}`, 'warning');
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
111
135
|
async function install() {
|
|
112
136
|
// Initialize analytics (respects opt-out env vars)
|
|
113
137
|
analytics.init();
|
|
@@ -187,9 +211,13 @@ async function install() {
|
|
|
187
211
|
// Track flow selection (await to ensure delivery before potential cancel)
|
|
188
212
|
await analytics.trackFlowSelected(selectedFlow);
|
|
189
213
|
|
|
214
|
+
// Determine total steps (5 if Claude selected for MCP option, otherwise 4)
|
|
215
|
+
const hasClaude = selectedToolKeys.includes('claude');
|
|
216
|
+
const totalSteps = hasClaude ? 5 : 4;
|
|
217
|
+
|
|
190
218
|
// Step 4: Install flow files
|
|
191
219
|
console.log('');
|
|
192
|
-
CLIUtils.displayStep(4,
|
|
220
|
+
CLIUtils.displayStep(4, totalSteps, `Installing ${FLOWS[selectedFlow].name} flow...`);
|
|
193
221
|
|
|
194
222
|
try {
|
|
195
223
|
const filesCreated = await installFlow(selectedFlow, selectedToolKeys);
|
|
@@ -197,6 +225,25 @@ async function install() {
|
|
|
197
225
|
// Install CLAUDE.md with quality guidelines
|
|
198
226
|
await installClaudeMd();
|
|
199
227
|
|
|
228
|
+
// Step 5: MCP Integrations (only if Claude is selected)
|
|
229
|
+
if (hasClaude) {
|
|
230
|
+
console.log('');
|
|
231
|
+
CLIUtils.displayStep(5, totalSteps, 'MCP Integrations (Optional)');
|
|
232
|
+
|
|
233
|
+
const { configureFigma } = await prompts({
|
|
234
|
+
type: 'confirm',
|
|
235
|
+
name: 'configureFigma',
|
|
236
|
+
message: 'Configure Figma MCP? (enables AI to read Figma designs)',
|
|
237
|
+
initial: false
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
if (configureFigma) {
|
|
241
|
+
await configureFigmaMCP();
|
|
242
|
+
} else {
|
|
243
|
+
console.log(theme.dim(' Skipped MCP configuration'));
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
200
247
|
// Track successful installation for each tool
|
|
201
248
|
const durationMs = Date.now() - installStartTime;
|
|
202
249
|
for (const toolKey of selectedToolKeys) {
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Figma MCP Provider
|
|
3
|
+
*
|
|
4
|
+
* Configures Figma MCP server for Claude Code.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { execSync } = require('child_process');
|
|
8
|
+
const { FIGMA_MCP } = require('./constants');
|
|
9
|
+
|
|
10
|
+
class FigmaMCPProvider {
|
|
11
|
+
constructor() {
|
|
12
|
+
this.name = 'figma';
|
|
13
|
+
this.config = FIGMA_MCP;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Check if Claude Code CLI is available in PATH
|
|
18
|
+
* @returns {boolean}
|
|
19
|
+
*/
|
|
20
|
+
isClaudeCodeAvailable() {
|
|
21
|
+
try {
|
|
22
|
+
const cmd = process.platform === 'win32' ? 'where claude' : 'which claude';
|
|
23
|
+
execSync(cmd, { stdio: 'ignore' });
|
|
24
|
+
return true;
|
|
25
|
+
} catch {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Configure Figma MCP for Claude Code using CLI
|
|
32
|
+
* @returns {Promise<{success: boolean, message: string, alreadyExists?: boolean}>}
|
|
33
|
+
*/
|
|
34
|
+
async configureForClaudeCode() {
|
|
35
|
+
if (!this.isClaudeCodeAvailable()) {
|
|
36
|
+
return {
|
|
37
|
+
success: false,
|
|
38
|
+
message: 'Claude Code CLI not found in PATH'
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
const cmd = `claude mcp add ${this.config.name} --transport ${this.config.transport} --url ${this.config.url}`;
|
|
44
|
+
execSync(cmd, { stdio: 'pipe' });
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
success: true,
|
|
48
|
+
message: 'Figma MCP configured for Claude Code'
|
|
49
|
+
};
|
|
50
|
+
} catch (error) {
|
|
51
|
+
const errorMessage = error.stderr ? error.stderr.toString() : error.message;
|
|
52
|
+
|
|
53
|
+
// Check if already exists
|
|
54
|
+
if (errorMessage.includes('already') || errorMessage.includes('exists')) {
|
|
55
|
+
return {
|
|
56
|
+
success: true,
|
|
57
|
+
message: 'Figma MCP already configured',
|
|
58
|
+
alreadyExists: true
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
success: false,
|
|
64
|
+
message: `Failed to configure: ${errorMessage.trim()}`
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
module.exports = FigmaMCPProvider;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Configuration Constants
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Figma MCP server configuration
|
|
6
|
+
const FIGMA_MCP = {
|
|
7
|
+
name: 'figma',
|
|
8
|
+
url: 'https://mcp.figma.com/mcp',
|
|
9
|
+
transport: 'http',
|
|
10
|
+
description: 'Figma design collaboration tool'
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
module.exports = {
|
|
14
|
+
FIGMA_MCP
|
|
15
|
+
};
|
package/lib/mcp/index.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Manager
|
|
3
|
+
*
|
|
4
|
+
* Coordinates MCP server configuration providers.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const FigmaMCPProvider = require('./FigmaMCPProvider');
|
|
8
|
+
|
|
9
|
+
class MCPManager {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.providers = {
|
|
12
|
+
figma: new FigmaMCPProvider()
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Get a specific MCP provider by name
|
|
18
|
+
* @param {string} name - Provider name (e.g., 'figma')
|
|
19
|
+
* @returns {FigmaMCPProvider|undefined}
|
|
20
|
+
*/
|
|
21
|
+
getProvider(name) {
|
|
22
|
+
return this.providers[name];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Get list of available MCP provider names
|
|
27
|
+
* @returns {string[]}
|
|
28
|
+
*/
|
|
29
|
+
getAvailableProviders() {
|
|
30
|
+
return Object.keys(this.providers);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Export singleton instance
|
|
35
|
+
module.exports = new MCPManager();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "project-iris",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Multi-agent orchestration system for AI-native software development. Delivers AI-DLC, Agile, and custom SDLC flows as markdown-based agent systems.",
|
|
5
5
|
"main": "lib/installer.js",
|
|
6
6
|
"bin": {
|
|
@@ -56,6 +56,7 @@
|
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
58
|
"@semantic-release/git": "^10.0.1",
|
|
59
|
+
"@types/fs-extra": "^11.0.4",
|
|
59
60
|
"@types/js-yaml": "^4.0.9",
|
|
60
61
|
"@types/node": "^24.10.2",
|
|
61
62
|
"glob": "^13.0.0",
|
package/scripts/bolt-complete.js
CHANGED
|
@@ -128,57 +128,18 @@
|
|
|
128
128
|
|
|
129
129
|
const fs = require('fs-extra');
|
|
130
130
|
const path = require('path');
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
red: '\x1b[31m',
|
|
138
|
-
yellow: '\x1b[33m',
|
|
139
|
-
blue: '\x1b[34m',
|
|
140
|
-
dim: '\x1b[90m',
|
|
141
|
-
bright: '\x1b[1m'
|
|
142
|
-
};
|
|
131
|
+
const {
|
|
132
|
+
colors,
|
|
133
|
+
extractFrontmatter,
|
|
134
|
+
updateFrontmatter,
|
|
135
|
+
getTimestamp
|
|
136
|
+
} = require('./lib/frontmatter-utils');
|
|
143
137
|
|
|
144
138
|
// Memory bank paths (relative to project root)
|
|
145
139
|
const MEMORY_BANK_DIR = 'memory-bank';
|
|
146
140
|
const BOLTS_DIR = path.join(MEMORY_BANK_DIR, 'bolts');
|
|
147
141
|
const INTENTS_DIR = path.join(MEMORY_BANK_DIR, 'intents');
|
|
148
142
|
|
|
149
|
-
/**
|
|
150
|
-
* Extract frontmatter from a markdown file
|
|
151
|
-
*/
|
|
152
|
-
function extractFrontmatter(content) {
|
|
153
|
-
const match = content.match(/^---\n([\s\S]+?)\n---/);
|
|
154
|
-
if (!match) return null;
|
|
155
|
-
|
|
156
|
-
try {
|
|
157
|
-
return yaml.load(match[1]);
|
|
158
|
-
} catch (error) {
|
|
159
|
-
console.error(`${colors.red}Error parsing YAML frontmatter:${colors.reset}`, error.message);
|
|
160
|
-
return null;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Update frontmatter in a markdown file
|
|
166
|
-
*/
|
|
167
|
-
function updateFrontmatter(content, newFrontmatter) {
|
|
168
|
-
const match = content.match(/^---\n([\s\S]+?)\n---/);
|
|
169
|
-
if (!match) return null;
|
|
170
|
-
|
|
171
|
-
const newYaml = yaml.dump(newFrontmatter, {
|
|
172
|
-
lineWidth: -1,
|
|
173
|
-
noRefs: true,
|
|
174
|
-
quotingType: '"',
|
|
175
|
-
forceQuotes: false,
|
|
176
|
-
sortKeys: false
|
|
177
|
-
}).trim();
|
|
178
|
-
|
|
179
|
-
return `---\n${newYaml}\n---${content.slice(match[0].length)}`;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
143
|
/**
|
|
183
144
|
* Check off all acceptance criteria checkboxes in markdown content
|
|
184
145
|
* Converts "- [ ]" to "- [x]" in the Acceptance Criteria section
|
|
@@ -223,16 +184,6 @@ function checkAcceptanceCriteria(content) {
|
|
|
223
184
|
};
|
|
224
185
|
}
|
|
225
186
|
|
|
226
|
-
/**
|
|
227
|
-
* Format timestamp as ISO 8601
|
|
228
|
-
* Format: YYYY-MM-DDTHH:MM:SSZ (no milliseconds, per memory-bank.yaml convention)
|
|
229
|
-
*/
|
|
230
|
-
function getTimestamp() {
|
|
231
|
-
const date = new Date();
|
|
232
|
-
// Format to ISO 8601 without milliseconds
|
|
233
|
-
return date.toISOString().replace(/\.\d+Z$/, 'Z');
|
|
234
|
-
}
|
|
235
|
-
|
|
236
187
|
/**
|
|
237
188
|
* Read a bolt file and extract metadata
|
|
238
189
|
*/
|
|
@@ -658,7 +609,8 @@ async function boltMarkComplete(boltId, lastStage) {
|
|
|
658
609
|
|
|
659
610
|
// CLI entry point
|
|
660
611
|
const boltId = process.argv[2];
|
|
661
|
-
const
|
|
612
|
+
const lastStageIndex = process.argv.indexOf('--last-stage');
|
|
613
|
+
const lastStage = lastStageIndex !== -1 ? process.argv[lastStageIndex + 1] : null;
|
|
662
614
|
|
|
663
615
|
if (!boltId) {
|
|
664
616
|
console.error(`${colors.red}Error:${colors.reset} Bolt ID required`);
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Frontmatter Utilities
|
|
3
|
+
*
|
|
4
|
+
* Common functions for parsing and updating YAML frontmatter in markdown files.
|
|
5
|
+
* Used by bolt-complete.js and status-integrity.js scripts.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const yaml = require('js-yaml');
|
|
9
|
+
|
|
10
|
+
// Theme colors for CLI output
|
|
11
|
+
const colors = {
|
|
12
|
+
reset: '\x1b[0m',
|
|
13
|
+
green: '\x1b[32m',
|
|
14
|
+
red: '\x1b[31m',
|
|
15
|
+
yellow: '\x1b[33m',
|
|
16
|
+
blue: '\x1b[34m',
|
|
17
|
+
dim: '\x1b[90m',
|
|
18
|
+
bright: '\x1b[1m'
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Extract frontmatter from a markdown file
|
|
23
|
+
* @param {string} content - Markdown file content
|
|
24
|
+
* @returns {object|null} Parsed frontmatter object or null if invalid
|
|
25
|
+
*/
|
|
26
|
+
function extractFrontmatter(content) {
|
|
27
|
+
const match = content.match(/^---\n([\s\S]+?)\n---/);
|
|
28
|
+
if (!match) return null;
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
return yaml.load(match[1]);
|
|
32
|
+
} catch (error) {
|
|
33
|
+
console.error(`${colors.red}Error parsing YAML frontmatter:${colors.reset}`, error.message);
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Update frontmatter in a markdown file
|
|
40
|
+
* @param {string} content - Original markdown content
|
|
41
|
+
* @param {object} newFrontmatter - New frontmatter object
|
|
42
|
+
* @returns {string|null} Updated content or null if failed
|
|
43
|
+
*/
|
|
44
|
+
function updateFrontmatter(content, newFrontmatter) {
|
|
45
|
+
const match = content.match(/^---\n([\s\S]+?)\n---/);
|
|
46
|
+
if (!match) return null;
|
|
47
|
+
|
|
48
|
+
const newYaml = yaml.dump(newFrontmatter, {
|
|
49
|
+
lineWidth: -1,
|
|
50
|
+
noRefs: true,
|
|
51
|
+
quotingType: '"',
|
|
52
|
+
forceQuotes: false,
|
|
53
|
+
sortKeys: false
|
|
54
|
+
}).trim();
|
|
55
|
+
|
|
56
|
+
return `---\n${newYaml}\n---${content.slice(match[0].length)}`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Format timestamp as ISO 8601
|
|
61
|
+
* Format: YYYY-MM-DDTHH:MM:SSZ (no milliseconds, per memory-bank.yaml convention)
|
|
62
|
+
* @returns {string} ISO 8601 formatted timestamp
|
|
63
|
+
*/
|
|
64
|
+
function getTimestamp() {
|
|
65
|
+
const date = new Date();
|
|
66
|
+
// Format to ISO 8601 without milliseconds
|
|
67
|
+
return date.toISOString().replace(/\.\d+Z$/, 'Z');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
module.exports = {
|
|
71
|
+
colors,
|
|
72
|
+
extractFrontmatter,
|
|
73
|
+
updateFrontmatter,
|
|
74
|
+
getTimestamp
|
|
75
|
+
};
|
|
@@ -15,18 +15,11 @@
|
|
|
15
15
|
|
|
16
16
|
const fs = require('fs-extra');
|
|
17
17
|
const path = require('path');
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
green: '\x1b[32m',
|
|
24
|
-
red: '\x1b[31m',
|
|
25
|
-
yellow: '\x1b[33m',
|
|
26
|
-
blue: '\x1b[34m',
|
|
27
|
-
dim: '\x1b[90m',
|
|
28
|
-
bright: '\x1b[1m'
|
|
29
|
-
};
|
|
18
|
+
const {
|
|
19
|
+
colors,
|
|
20
|
+
extractFrontmatter,
|
|
21
|
+
updateFrontmatter
|
|
22
|
+
} = require('./lib/frontmatter-utils');
|
|
30
23
|
|
|
31
24
|
// Memory bank paths (relative to project root)
|
|
32
25
|
const MEMORY_BANK_DIR = 'memory-bank';
|
|
@@ -34,49 +27,6 @@ const BOLTS_DIR = path.join(MEMORY_BANK_DIR, 'bolts');
|
|
|
34
27
|
const INTENTS_DIR = path.join(MEMORY_BANK_DIR, 'intents');
|
|
35
28
|
const MAINTENANCE_LOG = path.join(MEMORY_BANK_DIR, 'maintenance-log.md');
|
|
36
29
|
|
|
37
|
-
/**
|
|
38
|
-
* Extract frontmatter from a markdown file
|
|
39
|
-
*/
|
|
40
|
-
function extractFrontmatter(content) {
|
|
41
|
-
const match = content.match(/^---\n([\s\S]+?)\n---/);
|
|
42
|
-
if (!match) return null;
|
|
43
|
-
|
|
44
|
-
try {
|
|
45
|
-
return yaml.load(match[1]);
|
|
46
|
-
} catch (error) {
|
|
47
|
-
console.error(`${colors.red}Error parsing YAML frontmatter:${colors.reset}`, error.message);
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Update frontmatter in a markdown file
|
|
54
|
-
*/
|
|
55
|
-
function updateFrontmatter(content, newFrontmatter) {
|
|
56
|
-
const match = content.match(/^---\n([\s\S]+?)\n---/);
|
|
57
|
-
if (!match) return null;
|
|
58
|
-
|
|
59
|
-
const newYaml = yaml.dump(newFrontmatter, {
|
|
60
|
-
lineWidth: -1,
|
|
61
|
-
noRefs: true,
|
|
62
|
-
quotingType: '"',
|
|
63
|
-
forceQuotes: false,
|
|
64
|
-
sortKeys: false
|
|
65
|
-
}).trim();
|
|
66
|
-
|
|
67
|
-
return `---\n${newYaml}\n---${content.slice(match[0].length)}`;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Format timestamp as ISO 8601
|
|
72
|
-
* Format: YYYY-MM-DDTHH:MM:SSZ (no milliseconds, per memory-bank.yaml convention)
|
|
73
|
-
*/
|
|
74
|
-
function getTimestamp() {
|
|
75
|
-
const date = new Date();
|
|
76
|
-
// Format to ISO 8601 without milliseconds
|
|
77
|
-
return date.toISOString().replace(/\.\d+Z$/, 'Z');
|
|
78
|
-
}
|
|
79
|
-
|
|
80
30
|
/**
|
|
81
31
|
* Read a bolt file and extract metadata
|
|
82
32
|
*/
|