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.
@@ -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
- `templates/construction/bolt-types/{bolt_type}.md`
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 without a designer. Your goal is to create polished, professional interfaces that users love. Think like a designer, not a developer.
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
 
@@ -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
- return [
16
- new ClaudeInstaller(),
17
- new CursorInstaller(),
18
- new CopilotInstaller(),
19
- new AntigravityInstaller(),
20
- new WindsurfInstaller(),
21
- new ClineInstaller(),
22
- new RooInstaller(),
23
- new KiroInstaller(),
24
- new GeminiInstaller(),
25
- new CodexInstaller(),
26
- new OpenCodeInstaller()
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
- const installers = this.getInstallers();
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,6 @@
1
+ /**
2
+ * Type declarations for analytics index module
3
+ */
4
+
5
+ import tracker = require('./tracker');
6
+ export = tracker;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Type declarations for machine-id module
3
+ */
4
+
5
+ /**
6
+ * Generate a stable machine identifier
7
+ * @returns SHA-256 hash of salted hostname (64 hex characters)
8
+ */
9
+ export function getMachineId(): string;
@@ -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, 4, `Installing ${FLOWS[selectedFlow].name} flow...`);
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
+ };
@@ -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.5.1",
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",
@@ -128,57 +128,18 @@
128
128
 
129
129
  const fs = require('fs-extra');
130
130
  const path = require('path');
131
- const yaml = require('js-yaml');
132
-
133
- // Theme colors for output
134
- const colors = {
135
- reset: '\x1b[0m',
136
- green: '\x1b[32m',
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 lastStage = process.argv['--last-stage'] || null;
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 yaml = require('js-yaml');
19
-
20
- // Theme colors for output
21
- const colors = {
22
- reset: '\x1b[0m',
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
  */