specsmd 0.1.0 → 0.1.2
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/README.md +20 -0
- package/flows/aidlc/skills/construction/bolt-list.md +14 -14
- package/flows/aidlc/skills/construction/bolt-replan.md +37 -35
- package/flows/aidlc/skills/construction/bolt-start.md +5 -5
- package/flows/aidlc/skills/construction/navigator.md +7 -7
- package/flows/aidlc/skills/inception/bolt-plan.md +49 -29
- package/flows/aidlc/templates/construction/bolt-types/ddd-construction-bolt/adr-template.md +1 -1
- package/flows/aidlc/templates/construction/bolt-types/ddd-construction-bolt/ddd-01-domain-model-template.md +1 -1
- package/flows/aidlc/templates/construction/bolt-types/ddd-construction-bolt/ddd-02-technical-design-template.md +1 -1
- package/flows/aidlc/templates/construction/bolt-types/ddd-construction-bolt/ddd-03-test-report-template.md +1 -1
- package/flows/aidlc/templates/construction/bolt-types/ddd-construction-bolt.md +3 -3
- package/flows/aidlc/templates/construction/bolt-types/simple-construction-bolt.md +3 -3
- package/flows/aidlc/templates/construction/bolt-types/spike-bolt.md +2 -2
- package/flows/aidlc/templates/construction/construction-log-template.md +2 -2
- package/flows/aidlc/templates/inception/inception-log-template.md +2 -2
- package/flows/aidlc/templates/inception/requirements-template.md +2 -2
- package/flows/aidlc/templates/inception/stories-template.md +1 -1
- package/flows/aidlc/templates/inception/story-template.md +2 -2
- package/flows/aidlc/templates/inception/system-context-template.md +1 -1
- package/flows/aidlc/templates/inception/unit-brief-template.md +2 -2
- package/flows/aidlc/templates/inception/units-template.md +1 -1
- package/lib/analytics/env-detector.js +92 -0
- package/lib/analytics/index.js +22 -0
- package/lib/analytics/machine-id.js +33 -0
- package/lib/analytics/tracker.js +227 -0
- package/lib/installer.js +75 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# specs.md
|
|
2
2
|
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="images/specs_md_pixel_logo.png" alt="specs.md logo" width="400" />
|
|
5
|
+
</p>
|
|
6
|
+
|
|
3
7
|
**AI-native software development with multi-agent orchestration.**
|
|
4
8
|
|
|
5
9
|
specsmd implements the [AI-Driven Development Lifecycle (AI-DLC)](https://aws.amazon.com/blogs/devops/ai-driven-development-life-cycle/) methodology as a set of markdown-based agents that work with your favorite AI coding tools.
|
|
@@ -14,6 +18,22 @@ specsmd implements the [AI-Driven Development Lifecycle (AI-DLC)](https://aws.am
|
|
|
14
18
|
|
|
15
19
|
---
|
|
16
20
|
|
|
21
|
+
## VS Code Extension
|
|
22
|
+
|
|
23
|
+
Track your AI-DLC progress with our sidebar extension for VS Code and compatible IDEs.
|
|
24
|
+
|
|
25
|
+
<p align="center">
|
|
26
|
+
<img src="vs-code-extension/resources/extension-preview.png" alt="VS Code Extension Preview" width="800" />
|
|
27
|
+
</p>
|
|
28
|
+
|
|
29
|
+
> **Note:** Works with any VS Code-based IDE including [Cursor](https://cursor.sh), [Google Antigravity](https://antigravity.google), [Windsurf](https://codeium.com/windsurf), and others.
|
|
30
|
+
|
|
31
|
+
**Install from:**
|
|
32
|
+
- [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=fabriqaai.specsmd)
|
|
33
|
+
- [GitHub Releases (VSIX)](https://github.com/fabriqaai/specs.md/releases)
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
17
37
|
## Quick Start
|
|
18
38
|
|
|
19
39
|
### Prerequisites
|
|
@@ -73,20 +73,20 @@ Sort bolts by:
|
|
|
73
73
|
|
|
74
74
|
### Active Bolts
|
|
75
75
|
|
|
76
|
-
- ⏳ **
|
|
76
|
+
- ⏳ **002-auth-service** (auth-service, {bolt-type}) - Stage: {current-stage}, 2/4 (50%) ← working
|
|
77
77
|
|
|
78
78
|
### Planned Bolts
|
|
79
79
|
|
|
80
|
-
- [ ] **
|
|
81
|
-
- [ ] **
|
|
80
|
+
- [ ] **003-auth-service** (auth-service) - Stories: 005-*, 006-* - Ready ✅
|
|
81
|
+
- [ ] **004-payment-api** (payment-api) - Stories: 001-*, 002-*, 003-* - Ready ✅
|
|
82
82
|
|
|
83
83
|
### Blocked Bolts
|
|
84
84
|
|
|
85
|
-
- 🚫 **
|
|
85
|
+
- 🚫 **005-api-gateway** - Waiting for auth (since 2024-12-04)
|
|
86
86
|
|
|
87
87
|
### Completed Bolts
|
|
88
88
|
|
|
89
|
-
- ✅ **
|
|
89
|
+
- ✅ **001-auth-service** (auth-service) - Completed 2024-12-05 (4 hours)
|
|
90
90
|
|
|
91
91
|
### Summary
|
|
92
92
|
- **Total**: {n} bolts
|
|
@@ -97,8 +97,8 @@ Sort bolts by:
|
|
|
97
97
|
|
|
98
98
|
### Actions
|
|
99
99
|
|
|
100
|
-
1 - **Continue active bolt**: Resume `
|
|
101
|
-
2 - **Start planned bolt**: Begin `
|
|
100
|
+
1 - **Continue active bolt**: Resume `002-auth-service`
|
|
101
|
+
2 - **Start planned bolt**: Begin `003-auth-service`
|
|
102
102
|
3 - **View bolt status**: Check detailed status
|
|
103
103
|
4 - **Plan new bolts**: Create additional bolts
|
|
104
104
|
|
|
@@ -112,14 +112,14 @@ Sort bolts by:
|
|
|
112
112
|
```markdown
|
|
113
113
|
## Bolts for Unit: {unit-name}
|
|
114
114
|
|
|
115
|
-
- ✅ **
|
|
116
|
-
- ⏳ **
|
|
117
|
-
- [ ] **
|
|
115
|
+
- ✅ **001-{unit-name}** ({bolt-type}) - Completed 100% - Stories: 001-*, 002-*
|
|
116
|
+
- ⏳ **002-{unit-name}** ({bolt-type}) - In progress 50% - Stories: 003-*, 004-* ← current
|
|
117
|
+
- [ ] **003-{unit-name}** ({bolt-type}) - Planned 0% - Stories: 005-*
|
|
118
118
|
|
|
119
119
|
### Quick Actions
|
|
120
120
|
|
|
121
|
-
1 - **Continue
|
|
122
|
-
2 - **View
|
|
121
|
+
1 - **Continue 002-{unit-name}**: Resume current work
|
|
122
|
+
2 - **View 001-{unit-name} status**: Review completed bolt
|
|
123
123
|
|
|
124
124
|
**Type a number to continue.**
|
|
125
125
|
```
|
|
@@ -133,8 +133,8 @@ Sort bolts by:
|
|
|
133
133
|
```text
|
|
134
134
|
### Available Bolts
|
|
135
135
|
|
|
136
|
-
1 -
|
|
137
|
-
2 -
|
|
136
|
+
1 - 001-{unit-name} (planned) - Stories: 001-*, 002-*
|
|
137
|
+
2 - 002-{unit-name} (planned) - Stories: 003-*, 004-*
|
|
138
138
|
|
|
139
139
|
Which bolt would you like to work on?
|
|
140
140
|
```
|
|
@@ -33,9 +33,9 @@ Replan bolts during Construction phase - add new bolts, split existing ones, or
|
|
|
33
33
|
```markdown
|
|
34
34
|
## Current Bolt Status: {unit-name}
|
|
35
35
|
|
|
36
|
-
- ✅ **
|
|
37
|
-
- ⏳ **
|
|
38
|
-
- [ ] **
|
|
36
|
+
- ✅ **001-auth-service** ({bolt-type}): 001-user-signup, 002-user-login - completed - No dependencies
|
|
37
|
+
- ⏳ **002-auth-service** ({bolt-type}): 003-password-reset, 004-email-verify - in-progress - requires 001-auth-service
|
|
38
|
+
- [ ] **003-auth-service** ({bolt-type}): 005-mfa-setup - planned - requires 002-auth-service
|
|
39
39
|
|
|
40
40
|
### Summary
|
|
41
41
|
|
|
@@ -86,8 +86,10 @@ Select an option (1-4):
|
|
|
86
86
|
```
|
|
87
87
|
|
|
88
88
|
2. **Determine next bolt ID**:
|
|
89
|
-
-
|
|
90
|
-
-
|
|
89
|
+
- List all directories in `memory-bank/bolts/`
|
|
90
|
+
- Extract the 3-digit prefix from each (e.g., `015` from `015-auth-service`)
|
|
91
|
+
- Find the highest number
|
|
92
|
+
- Next bolt: `{next-BBB}-{unit-name}` (e.g., if highest is `015`, next is `016-auth-service`)
|
|
91
93
|
|
|
92
94
|
3. **Create new bolt(s)**:
|
|
93
95
|
- Use template: `.specsmd/aidlc/templates/construction/bolt-template.md`
|
|
@@ -103,12 +105,12 @@ Select an option (1-4):
|
|
|
103
105
|
|
|
104
106
|
Created 1 new bolt:
|
|
105
107
|
|
|
106
|
-
- [ ] **
|
|
108
|
+
- [ ] **004-auth-service** ({bolt-type}): 006-session-mgmt, 007-api-keys - requires 003-auth-service
|
|
107
109
|
|
|
108
110
|
Updated dependency graph:
|
|
109
|
-
|
|
111
|
+
001-auth-service → 002-auth-service → 003-auth-service → 004-auth-service (NEW)
|
|
110
112
|
|
|
111
|
-
File created: `memory-bank/bolts/
|
|
113
|
+
File created: `memory-bank/bolts/004-auth-service/bolt.md`
|
|
112
114
|
```
|
|
113
115
|
|
|
114
116
|
---
|
|
@@ -136,8 +138,8 @@ Select an option (1-4):
|
|
|
136
138
|
|
|
137
139
|
Splittable bolts (planned or in-progress):
|
|
138
140
|
|
|
139
|
-
- ⏳ **
|
|
140
|
-
- [ ] **
|
|
141
|
+
- ⏳ **002-auth-service** ({bolt-type}): 003-password-reset, 004-email-verify, 005-mfa-setup - in-progress - ⚠️ Confirm to split
|
|
142
|
+
- [ ] **003-auth-service** ({bolt-type}): 006-session-mgmt, 007-api-keys, 008-rate-limit - planned - ✅ Can split
|
|
141
143
|
|
|
142
144
|
Enter bolt ID to split:
|
|
143
145
|
```
|
|
@@ -145,21 +147,21 @@ Select an option (1-4):
|
|
|
145
147
|
2. **Propose split**:
|
|
146
148
|
|
|
147
149
|
```markdown
|
|
148
|
-
## Split Proposal:
|
|
150
|
+
## Split Proposal: 003-auth-service
|
|
149
151
|
|
|
150
152
|
Current: 3 stories (006-session-mgmt, 007-api-keys, 008-rate-limit)
|
|
151
153
|
|
|
152
154
|
Proposed split:
|
|
153
155
|
|
|
154
|
-
- **
|
|
155
|
-
- **
|
|
156
|
+
- **003-auth-service**: 006-session-mgmt - Core feature
|
|
157
|
+
- **004-auth-service**: 007-api-keys, 008-rate-limit - Related edge cases
|
|
156
158
|
|
|
157
159
|
Accept this split? (yes/no/customize)
|
|
158
160
|
```
|
|
159
161
|
|
|
160
162
|
3. **Execute split**:
|
|
161
163
|
- Archive or update original bolt file
|
|
162
|
-
- Create new bolt files with
|
|
164
|
+
- Create new bolt files with next sequence number
|
|
163
165
|
- Update dependencies on dependent bolts
|
|
164
166
|
- Update `enables_bolts` on prerequisite bolts
|
|
165
167
|
|
|
@@ -168,17 +170,17 @@ Select an option (1-4):
|
|
|
168
170
|
```markdown
|
|
169
171
|
## Bolt Split Complete
|
|
170
172
|
|
|
171
|
-
Original:
|
|
173
|
+
Original: 003-auth-service (updated)
|
|
172
174
|
|
|
173
175
|
Created:
|
|
174
176
|
|
|
175
|
-
- [ ] **
|
|
176
|
-
- [ ] **
|
|
177
|
+
- [ ] **003-auth-service** ({bolt-type}): 006-session-mgmt - requires 002-auth-service
|
|
178
|
+
- [ ] **004-auth-service** ({bolt-type}): 007-api-keys, 008-rate-limit - requires 003-auth-service
|
|
177
179
|
|
|
178
180
|
Updated files:
|
|
179
|
-
- `memory-bank/bolts/
|
|
180
|
-
- `memory-bank/bolts/
|
|
181
|
-
- `memory-bank/bolts/
|
|
181
|
+
- `memory-bank/bolts/003-auth-service/bolt.md` (updated)
|
|
182
|
+
- `memory-bank/bolts/004-auth-service/bolt.md` (created)
|
|
183
|
+
- `memory-bank/bolts/002-auth-service/bolt.md` (updated enables_bolts)
|
|
182
184
|
```
|
|
183
185
|
|
|
184
186
|
---
|
|
@@ -204,21 +206,21 @@ Select an option (1-4):
|
|
|
204
206
|
```markdown
|
|
205
207
|
## Current Execution Order
|
|
206
208
|
|
|
207
|
-
|
|
209
|
+
001-auth-service (completed) → 002-auth-service (in-progress) → 003-auth-service (planned) → 004-auth-service (planned)
|
|
208
210
|
|
|
209
|
-
- 1 - **
|
|
210
|
-
- 2 - **
|
|
211
|
-
- 3 - **
|
|
212
|
-
- 4 - **
|
|
211
|
+
- 1 - **001-auth-service**: completed - ❌ Cannot move
|
|
212
|
+
- 2 - **002-auth-service**: in-progress - ⚠️ Will pause if moved
|
|
213
|
+
- 3 - **003-auth-service**: planned - ✅ Can move
|
|
214
|
+
- 4 - **004-auth-service**: planned - ✅ Can move
|
|
213
215
|
```
|
|
214
216
|
|
|
215
217
|
2. **Get new order**:
|
|
216
218
|
|
|
217
219
|
```markdown
|
|
218
220
|
Enter new order for planned bolts (comma-separated IDs):
|
|
219
|
-
Example:
|
|
221
|
+
Example: 004-auth-service, 003-auth-service
|
|
220
222
|
|
|
221
|
-
This will execute
|
|
223
|
+
This will execute 004-auth-service before 003-auth-service.
|
|
222
224
|
```
|
|
223
225
|
|
|
224
226
|
3. **Validate dependencies**:
|
|
@@ -236,11 +238,11 @@ Select an option (1-4):
|
|
|
236
238
|
|
|
237
239
|
New execution order:
|
|
238
240
|
|
|
239
|
-
|
|
241
|
+
001-auth-service (completed) → 002-auth-service (in-progress) → 004-auth-service (planned) → 003-auth-service (planned)
|
|
240
242
|
|
|
241
243
|
Updated files:
|
|
242
|
-
- `memory-bank/bolts/
|
|
243
|
-
- `memory-bank/bolts/
|
|
244
|
+
- `memory-bank/bolts/003-auth-service/bolt.md` (requires_bolts updated)
|
|
245
|
+
- `memory-bank/bolts/004-auth-service/bolt.md` (requires_bolts updated)
|
|
244
246
|
```
|
|
245
247
|
|
|
246
248
|
---
|
|
@@ -253,10 +255,10 @@ When modifying bolts, always update dependencies:
|
|
|
253
255
|
|
|
254
256
|
```yaml
|
|
255
257
|
---
|
|
256
|
-
id:
|
|
258
|
+
id: 003-auth-service
|
|
257
259
|
type: ddd-construction-bolt
|
|
258
|
-
requires_bolts: [
|
|
259
|
-
enables_bolts: [
|
|
260
|
+
requires_bolts: [002-auth-service]
|
|
261
|
+
enables_bolts: [004-auth-service]
|
|
260
262
|
requires_units: []
|
|
261
263
|
complexity:
|
|
262
264
|
avg_complexity: 2
|
|
@@ -314,12 +316,12 @@ Update Current Bolt Structure to reflect changes.
|
|
|
314
316
|
|
|
315
317
|
### Example
|
|
316
318
|
|
|
317
|
-
After splitting
|
|
319
|
+
After splitting 002-auth-service:
|
|
318
320
|
|
|
319
321
|
```markdown
|
|
320
322
|
## Replanning History
|
|
321
323
|
|
|
322
|
-
- **2025-12-07**: split -
|
|
324
|
+
- **2025-12-07**: split - 002-auth-service → 002-auth-service, 003-auth-service - Scope too large - ✅ Approved
|
|
323
325
|
```
|
|
324
326
|
|
|
325
327
|
---
|
|
@@ -255,7 +255,7 @@ Status changes cascade upward: Bolt → Story → Unit → Intent.
|
|
|
255
255
|
**On Bolt Completion** (after updating stories):
|
|
256
256
|
|
|
257
257
|
1. **Check Unit Completion**:
|
|
258
|
-
- Find all bolts for this unit: `memory-bank/bolts
|
|
258
|
+
- Find all bolts for this unit: scan `memory-bank/bolts/*/bolt.md` and match `unit: {unit-name}` in frontmatter
|
|
259
259
|
- If ALL bolts have `status: complete` → update unit-brief to `status: complete`
|
|
260
260
|
|
|
261
261
|
2. **Check Intent Completion**:
|
|
@@ -270,13 +270,13 @@ Unit: draft → stories-defined → in-progress → complete
|
|
|
270
270
|
Story: draft → in-progress → complete
|
|
271
271
|
```
|
|
272
272
|
|
|
273
|
-
**Example** (
|
|
273
|
+
**Example** (001-artifact-parser completes):
|
|
274
274
|
|
|
275
275
|
```text
|
|
276
276
|
1. Stories updated: 001, 002, 003, 004 → complete
|
|
277
277
|
2. Check unit bolts:
|
|
278
|
-
-
|
|
279
|
-
-
|
|
278
|
+
- 001-artifact-parser: complete ✓
|
|
279
|
+
- 005-artifact-parser: planned ✗
|
|
280
280
|
→ Unit stays in-progress (not all bolts complete)
|
|
281
281
|
3. Intent stays construction (unit not complete)
|
|
282
282
|
```
|
|
@@ -286,7 +286,7 @@ Story: draft → in-progress → complete
|
|
|
286
286
|
```text
|
|
287
287
|
1. Stories updated: 001, 002 → complete
|
|
288
288
|
2. Check unit bolts:
|
|
289
|
-
-
|
|
289
|
+
- 003-file-watcher: complete ✓
|
|
290
290
|
→ Only bolt for unit, all complete!
|
|
291
291
|
→ Update unit-brief: status: complete
|
|
292
292
|
3. Check intent units:
|
|
@@ -134,18 +134,18 @@ When user selects an option:
|
|
|
134
134
|
|
|
135
135
|
### Available Bolts
|
|
136
136
|
|
|
137
|
-
- [ ] `
|
|
138
|
-
- [ ] `
|
|
139
|
-
- [ ] `
|
|
137
|
+
- [ ] `001-auth-service` (auth-service, DDD) - planned
|
|
138
|
+
- [ ] `002-auth-service` (auth-service, DDD) - planned
|
|
139
|
+
- [ ] `003-api-gateway` (api-gateway, Simple) - planned
|
|
140
140
|
|
|
141
141
|
### Quick Actions
|
|
142
142
|
|
|
143
|
-
1 - **Start
|
|
143
|
+
1 - **Start 001-auth-service**: Begin first bolt
|
|
144
144
|
2 - **List all bolts**: View with details
|
|
145
145
|
3 - **View bolt status**: Check specific bolt
|
|
146
146
|
|
|
147
147
|
### Suggested Next Step
|
|
148
|
-
→ Start construction with `
|
|
148
|
+
→ Start construction with `001-auth-service`
|
|
149
149
|
|
|
150
150
|
**Type a number or enter a bolt ID.**
|
|
151
151
|
```
|
|
@@ -161,8 +161,8 @@ When user selects an option:
|
|
|
161
161
|
|
|
162
162
|
All {n} bolts have been completed:
|
|
163
163
|
|
|
164
|
-
- ✅ `
|
|
165
|
-
- ✅ `
|
|
164
|
+
- ✅ `001-{unit-name}` - Completed 2024-12-05 (3h)
|
|
165
|
+
- ✅ `005-{unit-name}` - Completed 2024-12-06 (4h)
|
|
166
166
|
|
|
167
167
|
### Summary
|
|
168
168
|
- Stories delivered: {n}
|
|
@@ -190,35 +190,51 @@ Establish execution order based on dependencies:
|
|
|
190
190
|
1. **Read Path**: Check `schema.bolts` from `.specsmd/aidlc/memory-bank.yaml`
|
|
191
191
|
*(Default: `memory-bank/bolts/{bolt-id}/`)*
|
|
192
192
|
|
|
193
|
-
2. **
|
|
193
|
+
2. **Determine Bolt ID**:
|
|
194
|
+
- List all directories in `memory-bank/bolts/`
|
|
195
|
+
- Extract the 3-digit prefix from each (e.g., `015` from `015-auth-service`)
|
|
196
|
+
- Find the highest number
|
|
197
|
+
- Next bolt uses the next available number (e.g., if highest is `015`, next is `016`)
|
|
198
|
+
|
|
199
|
+
**⚠️ CRITICAL**: The `{BBB}` prefix is a **GLOBAL** sequence across ALL bolts in `memory-bank/bolts/`, NOT per-unit.
|
|
200
|
+
|
|
201
|
+
3. **Create Directory + File Per Bolt**:
|
|
194
202
|
For EACH bolt in the plan:
|
|
195
|
-
- Create directory: `memory-bank/bolts/
|
|
196
|
-
- Create file inside: `memory-bank/bolts/
|
|
203
|
+
- Create directory: `memory-bank/bolts/{BBB}-{unit-name}/`
|
|
204
|
+
- Create file inside: `memory-bank/bolts/{BBB}-{unit-name}/bolt.md`
|
|
197
205
|
- Use template: `.specsmd/aidlc/templates/construction/bolt-template.md`
|
|
198
206
|
|
|
199
|
-
**
|
|
207
|
+
**Naming Convention** (from `memory-bank.yaml`):
|
|
208
|
+
- Format: `{BBB}-{unit-name}/` where BBB is a **GLOBAL** 3-digit sequence
|
|
209
|
+
- The number is global across ALL bolts in `memory-bank/bolts/` (not per-unit)
|
|
210
|
+
- Example sequence: `001-auth-service/`, `002-auth-service/`, `003-payment-service/`, `004-auth-service/`
|
|
211
|
+
|
|
212
|
+
**Example**: Planning bolts across multiple units (global numbering):
|
|
200
213
|
|
|
201
214
|
```text
|
|
202
|
-
memory-bank/bolts/
|
|
203
|
-
|
|
204
|
-
|
|
215
|
+
memory-bank/bolts/
|
|
216
|
+
├── 001-auth-service/bolt.md ← First bolt ever created
|
|
217
|
+
├── 002-auth-service/bolt.md ← Second bolt (same unit, continues sequence)
|
|
218
|
+
├── 003-payment-service/bolt.md ← Third bolt (different unit)
|
|
219
|
+
├── 004-auth-service/bolt.md ← Fourth bolt (back to auth-service)
|
|
220
|
+
└── 005-api-gateway/bolt.md ← Fifth bolt (another unit)
|
|
205
221
|
```
|
|
206
222
|
|
|
207
223
|
**Stage artifacts will be added to same directory during construction:**
|
|
208
224
|
|
|
209
225
|
```text
|
|
210
|
-
memory-bank/bolts/
|
|
226
|
+
memory-bank/bolts/001-auth-service/
|
|
211
227
|
├── bolt.md ← You create this now
|
|
212
228
|
└── {stage-artifacts} ← Created during construction (varies by bolt type)
|
|
213
229
|
```
|
|
214
230
|
|
|
215
231
|
*Note: Artifact names depend on bolt type (e.g., DDD bolts create `ddd-01-domain-model.md`, simple bolts create `implementation-plan.md`).*
|
|
216
232
|
|
|
217
|
-
|
|
233
|
+
4. **Bolt File Structure** (CRITICAL: Include all dependencies in frontmatter):
|
|
218
234
|
|
|
219
235
|
```markdown
|
|
220
236
|
---
|
|
221
|
-
id:
|
|
237
|
+
id: {BBB}-{unit-name}
|
|
222
238
|
unit: {unit-name}
|
|
223
239
|
intent: {intent-name}
|
|
224
240
|
type: {bolt-type} # From unit-brief.md or default (ddd-construction-bolt, simple-construction-bolt)
|
|
@@ -227,8 +243,8 @@ Establish execution order based on dependencies:
|
|
|
227
243
|
created: {date}
|
|
228
244
|
|
|
229
245
|
# Dependency Tracking (REQUIRED)
|
|
230
|
-
requires_bolts: [
|
|
231
|
-
enables_bolts: [
|
|
246
|
+
requires_bolts: [001-auth-service] # Bolts that must complete first
|
|
247
|
+
enables_bolts: [003-auth-service, 001-api-service] # Bolts that depend on this
|
|
232
248
|
requires_units: [auth-service] # Units that must be complete
|
|
233
249
|
blocks: false # true if waiting on dependency
|
|
234
250
|
|
|
@@ -240,7 +256,7 @@ Establish execution order based on dependencies:
|
|
|
240
256
|
testing_scope: 2 # 1=Unit, 2=Integration, 3=E2E
|
|
241
257
|
---
|
|
242
258
|
|
|
243
|
-
## Bolt: {
|
|
259
|
+
## Bolt: {BBB}-{unit-name}
|
|
244
260
|
|
|
245
261
|
### Objective
|
|
246
262
|
{What this bolt will accomplish}
|
|
@@ -257,19 +273,19 @@ Establish execution order based on dependencies:
|
|
|
257
273
|
|
|
258
274
|
#### Bolt Dependencies (within intent)
|
|
259
275
|
|
|
260
|
-
- **
|
|
261
|
-
- **
|
|
276
|
+
- **001-auth-service** (Required): Completed
|
|
277
|
+
- **002-auth-service** (Optional): In Progress
|
|
262
278
|
|
|
263
279
|
#### Unit Dependencies (cross-unit)
|
|
264
280
|
|
|
265
281
|
- **auth-service**: Needs auth tokens - Completed
|
|
266
282
|
|
|
267
283
|
#### Enables (other bolts waiting on this)
|
|
268
|
-
-
|
|
269
|
-
-
|
|
284
|
+
- 003-auth-service
|
|
285
|
+
- 001-api-service
|
|
270
286
|
```
|
|
271
287
|
|
|
272
|
-
###
|
|
288
|
+
### 9. Validate Plan
|
|
273
289
|
|
|
274
290
|
Check the plan against:
|
|
275
291
|
|
|
@@ -291,16 +307,20 @@ Check the plan against:
|
|
|
291
307
|
**⚠️ YOU MUST CREATE THESE DIRECTORIES AND FILES:**
|
|
292
308
|
|
|
293
309
|
```text
|
|
294
|
-
memory-bank/bolts/
|
|
295
|
-
memory-bank/bolts/bolt-{unit}-2/bolt.md ← CREATE THIS DIRECTORY AND FILE
|
|
296
|
-
memory-bank/bolts/bolt-{unit}-3/bolt.md ← CREATE THIS DIRECTORY AND FILE
|
|
310
|
+
memory-bank/bolts/{BBB}-{unit-name}/bolt.md ← CREATE THIS DIRECTORY AND FILE
|
|
297
311
|
```
|
|
298
312
|
|
|
313
|
+
**Naming Convention** (from `memory-bank.yaml`):
|
|
314
|
+
|
|
315
|
+
- Format: `{BBB}-{unit-name}/` where BBB is a global 3-digit sequence
|
|
316
|
+
- Example: `001-auth-service/`, `002-auth-service/`, `016-analytics-tracker/`
|
|
317
|
+
|
|
299
318
|
**⚠️ DO NOT CREATE:**
|
|
300
319
|
|
|
301
320
|
- `bolt-plan.md` (summary doc)
|
|
302
321
|
- `README.md` files
|
|
303
|
-
- Flat files like `
|
|
322
|
+
- Flat files like `001-auth-service.md` (must be in directory)
|
|
323
|
+
- Old format like `bolt-{unit}-1/` (incorrect)
|
|
304
324
|
|
|
305
325
|
### Summary (displayed to user)
|
|
306
326
|
|
|
@@ -309,17 +329,17 @@ memory-bank/bolts/bolt-{unit}-3/bolt.md ← CREATE THIS DIRECTORY AND FILE
|
|
|
309
329
|
|
|
310
330
|
### Bolts Created
|
|
311
331
|
|
|
312
|
-
- [ ] **
|
|
313
|
-
- [ ] **
|
|
314
|
-
- [ ] **
|
|
332
|
+
- [ ] **001-auth-service** ({bolt-type}): 001-user-signup, 002-user-login
|
|
333
|
+
- [ ] **002-auth-service** ({bolt-type}): 003-password-reset, 004-email-verify
|
|
334
|
+
- [ ] **003-auth-service** ({bolt-type}): 005-mfa-setup
|
|
315
335
|
|
|
316
336
|
### Dependency Graph
|
|
317
|
-
|
|
337
|
+
001-auth-service ──► 002-auth-service ──► 003-auth-service
|
|
318
338
|
|
|
319
339
|
### Directories Created
|
|
320
|
-
✅ `memory-bank/bolts/
|
|
321
|
-
✅ `memory-bank/bolts/
|
|
322
|
-
✅ `memory-bank/bolts/
|
|
340
|
+
✅ `memory-bank/bolts/001-auth-service/bolt.md`
|
|
341
|
+
✅ `memory-bank/bolts/002-auth-service/bolt.md`
|
|
342
|
+
✅ `memory-bank/bolts/003-auth-service/bolt.md`
|
|
323
343
|
|
|
324
344
|
### Total
|
|
325
345
|
- {n} bolts created
|
|
@@ -109,7 +109,7 @@ This bolt type implements Domain-Driven Design (DDD) methodology through five se
|
|
|
109
109
|
---
|
|
110
110
|
stage: model
|
|
111
111
|
bolt: {bolt-id}
|
|
112
|
-
created: {
|
|
112
|
+
created: {YYYY-MM-DDTHH:MM:SSZ}
|
|
113
113
|
---
|
|
114
114
|
|
|
115
115
|
## Static Model: {unit-name}
|
|
@@ -189,7 +189,7 @@ created: {timestamp}
|
|
|
189
189
|
---
|
|
190
190
|
stage: design
|
|
191
191
|
bolt: {bolt-id}
|
|
192
|
-
created: {
|
|
192
|
+
created: {YYYY-MM-DDTHH:MM:SSZ}
|
|
193
193
|
---
|
|
194
194
|
|
|
195
195
|
## Technical Design: {unit-name}
|
|
@@ -440,7 +440,7 @@ tests/
|
|
|
440
440
|
---
|
|
441
441
|
stage: test
|
|
442
442
|
bolt: {bolt-id}
|
|
443
|
-
created: {
|
|
443
|
+
created: {YYYY-MM-DDTHH:MM:SSZ}
|
|
444
444
|
---
|
|
445
445
|
|
|
446
446
|
## Test Report: {unit-name}
|
|
@@ -107,7 +107,7 @@ This bolt type provides a lightweight construction process for work that doesn't
|
|
|
107
107
|
---
|
|
108
108
|
stage: plan
|
|
109
109
|
bolt: {bolt-id}
|
|
110
|
-
created: {
|
|
110
|
+
created: {YYYY-MM-DDTHH:MM:SSZ}
|
|
111
111
|
---
|
|
112
112
|
|
|
113
113
|
## Implementation Plan: {unit-name}
|
|
@@ -175,7 +175,7 @@ created: {timestamp}
|
|
|
175
175
|
---
|
|
176
176
|
stage: implement
|
|
177
177
|
bolt: {bolt-id}
|
|
178
|
-
created: {
|
|
178
|
+
created: {YYYY-MM-DDTHH:MM:SSZ}
|
|
179
179
|
---
|
|
180
180
|
|
|
181
181
|
## Implementation Walkthrough: {unit-name}
|
|
@@ -260,7 +260,7 @@ created: {timestamp}
|
|
|
260
260
|
---
|
|
261
261
|
stage: test
|
|
262
262
|
bolt: {bolt-id}
|
|
263
|
-
created: {
|
|
263
|
+
created: {YYYY-MM-DDTHH:MM:SSZ}
|
|
264
264
|
---
|
|
265
265
|
|
|
266
266
|
## Test Report: {unit-name}
|
|
@@ -80,7 +80,7 @@ This bolt type is for research and proof-of-concept work when there are unknowns
|
|
|
80
80
|
---
|
|
81
81
|
stage: explore
|
|
82
82
|
bolt: {bolt-id}
|
|
83
|
-
created: {
|
|
83
|
+
created: {YYYY-MM-DDTHH:MM:SSZ}
|
|
84
84
|
time_box: {hours}
|
|
85
85
|
---
|
|
86
86
|
|
|
@@ -142,7 +142,7 @@ time_box: {hours}
|
|
|
142
142
|
---
|
|
143
143
|
stage: document
|
|
144
144
|
bolt: {bolt-id}
|
|
145
|
-
created: {
|
|
145
|
+
created: {YYYY-MM-DDTHH:MM:SSZ}
|
|
146
146
|
---
|
|
147
147
|
|
|
148
148
|
## Spike Report: {topic}
|
|
@@ -16,8 +16,8 @@ Use this template to track construction progress and replanning decisions for a
|
|
|
16
16
|
---
|
|
17
17
|
unit: {unit-name}
|
|
18
18
|
intent: {intent-name}
|
|
19
|
-
created: {YYYY-MM-
|
|
20
|
-
last_updated: {YYYY-MM-
|
|
19
|
+
created: {YYYY-MM-DDTHH:MM:SSZ}
|
|
20
|
+
last_updated: {YYYY-MM-DDTHH:MM:SSZ}
|
|
21
21
|
---
|
|
22
22
|
|
|
23
23
|
# Construction Log: {unit-name}
|
|
@@ -15,8 +15,8 @@ Use this template to track inception progress and decisions for an intent.
|
|
|
15
15
|
```markdown
|
|
16
16
|
---
|
|
17
17
|
intent: {intent-name}
|
|
18
|
-
created: {YYYY-MM-
|
|
19
|
-
completed: {YYYY-MM-
|
|
18
|
+
created: {YYYY-MM-DDTHH:MM:SSZ}
|
|
19
|
+
completed: {YYYY-MM-DDTHH:MM:SSZ or null}
|
|
20
20
|
status: {in-progress | complete}
|
|
21
21
|
---
|
|
22
22
|
|
|
@@ -11,8 +11,8 @@ Use this template when documenting requirements for an intent.
|
|
|
11
11
|
intent: {intent-name}
|
|
12
12
|
phase: inception
|
|
13
13
|
status: draft|in-progress|complete
|
|
14
|
-
created: {YYYY-MM-
|
|
15
|
-
updated: {YYYY-MM-
|
|
14
|
+
created: {YYYY-MM-DDTHH:MM:SSZ}
|
|
15
|
+
updated: {YYYY-MM-DDTHH:MM:SSZ}
|
|
16
16
|
---
|
|
17
17
|
```
|
|
18
18
|
|
|
@@ -13,7 +13,7 @@ unit: {unit-name}
|
|
|
13
13
|
intent: {intent-name}
|
|
14
14
|
status: draft
|
|
15
15
|
priority: must|should|could
|
|
16
|
-
created: {YYYY-MM-
|
|
16
|
+
created: {YYYY-MM-DDTHH:MM:SSZ}
|
|
17
17
|
assigned_bolt: null
|
|
18
18
|
implemented: false
|
|
19
19
|
---
|
|
@@ -96,7 +96,7 @@ unit: auth-service
|
|
|
96
96
|
intent: user-authentication
|
|
97
97
|
status: ready
|
|
98
98
|
priority: must
|
|
99
|
-
created: 2024-12-
|
|
99
|
+
created: 2024-12-05T10:00:00Z
|
|
100
100
|
assigned_bolt: bolt-auth-service-1
|
|
101
101
|
implemented: false
|
|
102
102
|
---
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment Detection
|
|
3
|
+
*
|
|
4
|
+
* Detects shell environment and telemetry opt-out settings.
|
|
5
|
+
* Used to enrich analytics events and respect user privacy preferences.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Detect the user's shell/terminal environment
|
|
10
|
+
*
|
|
11
|
+
* @returns {string} Shell name (zsh, bash, powershell, cmd, fish, etc.) or 'unknown'
|
|
12
|
+
*/
|
|
13
|
+
function detectShell() {
|
|
14
|
+
if (process.platform === 'win32') {
|
|
15
|
+
const comspec = (process.env.ComSpec || '').toLowerCase();
|
|
16
|
+
if (comspec.includes('powershell') || comspec.includes('pwsh')) {
|
|
17
|
+
return 'powershell';
|
|
18
|
+
}
|
|
19
|
+
if (comspec.includes('cmd')) {
|
|
20
|
+
return 'cmd';
|
|
21
|
+
}
|
|
22
|
+
return 'unknown';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Unix-like systems (macOS, Linux)
|
|
26
|
+
const shell = process.env.SHELL || '';
|
|
27
|
+
const basename = shell.split('/').pop() || 'unknown';
|
|
28
|
+
return basename;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Check if telemetry is disabled via environment variables or CLI flag
|
|
33
|
+
*
|
|
34
|
+
* Respects:
|
|
35
|
+
* - SPECSMD_TELEMETRY_DISABLED=1
|
|
36
|
+
* - DO_NOT_TRACK=1
|
|
37
|
+
* - CI environments (CI, GITHUB_ACTIONS, GITLAB_CI, CIRCLECI, JENKINS_URL)
|
|
38
|
+
* - --no-telemetry CLI flag
|
|
39
|
+
*
|
|
40
|
+
* @param {Object} options - Optional overrides
|
|
41
|
+
* @param {boolean} options.noTelemetryFlag - CLI flag state
|
|
42
|
+
* @returns {boolean} True if telemetry should be disabled
|
|
43
|
+
*/
|
|
44
|
+
function isTelemetryDisabled(options = {}) {
|
|
45
|
+
// Check CLI flag first (highest priority)
|
|
46
|
+
if (options.noTelemetryFlag === true) {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Check process.argv for --no-telemetry flag
|
|
51
|
+
if (process.argv.includes('--no-telemetry')) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Check explicit opt-out environment variables
|
|
56
|
+
if (process.env.SPECSMD_TELEMETRY_DISABLED === '1') {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Respect DO_NOT_TRACK standard (https://consoledonottrack.com/)
|
|
61
|
+
if (process.env.DO_NOT_TRACK === '1') {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Auto-disable in CI environments
|
|
66
|
+
if (process.env.CI === 'true') {
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (process.env.GITHUB_ACTIONS === 'true') {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (process.env.GITLAB_CI === 'true') {
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (process.env.CIRCLECI === 'true') {
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (process.env.JENKINS_URL !== undefined) {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
module.exports = {
|
|
90
|
+
detectShell,
|
|
91
|
+
isTelemetryDisabled
|
|
92
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analytics Module
|
|
3
|
+
*
|
|
4
|
+
* Exports the analytics tracker singleton for use throughout the installer.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* const analytics = require('./analytics');
|
|
8
|
+
*
|
|
9
|
+
* // Initialize at startup
|
|
10
|
+
* analytics.init();
|
|
11
|
+
*
|
|
12
|
+
* // Track events
|
|
13
|
+
* analytics.trackInstallerStarted();
|
|
14
|
+
* analytics.trackIdesConfirmed(['claude-code', 'cursor']);
|
|
15
|
+
* analytics.trackFlowSelected('aidlc');
|
|
16
|
+
* analytics.trackInstallationCompleted('claude-code', 'aidlc', 1500, 12);
|
|
17
|
+
* analytics.trackInstallationFailed('cursor', 'file_permission', 'aidlc');
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const tracker = require('./tracker');
|
|
21
|
+
|
|
22
|
+
module.exports = tracker;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Machine ID Generation
|
|
3
|
+
*
|
|
4
|
+
* Generates a stable, anonymous machine identifier using a salted SHA-256 hash
|
|
5
|
+
* of the hostname. This ensures:
|
|
6
|
+
* - Same machine always produces same ID
|
|
7
|
+
* - Cannot reverse-lookup the hostname from the hash
|
|
8
|
+
* - No PII is stored or transmitted
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const crypto = require('crypto');
|
|
12
|
+
const os = require('os');
|
|
13
|
+
|
|
14
|
+
// Constant salt prevents rainbow table attacks
|
|
15
|
+
// Do not change this value - it would break ID consistency
|
|
16
|
+
const SALT = 'specsmd-analytics-v1';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Generate a stable machine identifier
|
|
20
|
+
*
|
|
21
|
+
* @returns {string} SHA-256 hash of salted hostname (64 hex characters)
|
|
22
|
+
*/
|
|
23
|
+
function getMachineId() {
|
|
24
|
+
const hostname = os.hostname();
|
|
25
|
+
return crypto
|
|
26
|
+
.createHash('sha256')
|
|
27
|
+
.update(SALT + hostname)
|
|
28
|
+
.digest('hex');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = {
|
|
32
|
+
getMachineId
|
|
33
|
+
};
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analytics Tracker
|
|
3
|
+
*
|
|
4
|
+
* Mixpanel-based analytics for the specsmd installer.
|
|
5
|
+
* Tracks anonymous usage patterns while respecting privacy.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Fire-and-forget event delivery (non-blocking)
|
|
9
|
+
* - Silent failures (never breaks installation)
|
|
10
|
+
* - Privacy-first (no PII, respects opt-out)
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const crypto = require('crypto');
|
|
14
|
+
const { getMachineId } = require('./machine-id');
|
|
15
|
+
const { detectShell, isTelemetryDisabled } = require('./env-detector');
|
|
16
|
+
|
|
17
|
+
// Mixpanel project token
|
|
18
|
+
// This is safe to embed - analytics tokens are public by design
|
|
19
|
+
const MIXPANEL_TOKEN = 'f405d1fa631f91137f9bb8e0a0277653';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* AnalyticsTracker - Singleton class for event tracking
|
|
23
|
+
*/
|
|
24
|
+
class AnalyticsTracker {
|
|
25
|
+
constructor() {
|
|
26
|
+
this.mixpanel = null;
|
|
27
|
+
this.enabled = false;
|
|
28
|
+
this.machineId = null;
|
|
29
|
+
this.sessionId = null;
|
|
30
|
+
this.baseProperties = null;
|
|
31
|
+
this.initialized = false;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Initialize the analytics tracker
|
|
36
|
+
*
|
|
37
|
+
* @param {Object} options - Initialization options
|
|
38
|
+
* @param {boolean} options.noTelemetry - CLI flag to disable telemetry
|
|
39
|
+
* @returns {boolean} True if analytics is enabled
|
|
40
|
+
*/
|
|
41
|
+
init(options = {}) {
|
|
42
|
+
if (this.initialized) {
|
|
43
|
+
return this.enabled;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
this.initialized = true;
|
|
47
|
+
|
|
48
|
+
// Check if telemetry is disabled
|
|
49
|
+
if (isTelemetryDisabled({ noTelemetryFlag: options.noTelemetry })) {
|
|
50
|
+
this.enabled = false;
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
// Lazy-load Mixpanel to avoid blocking if not needed
|
|
56
|
+
const Mixpanel = require('mixpanel');
|
|
57
|
+
this.mixpanel = Mixpanel.init(MIXPANEL_TOKEN, {
|
|
58
|
+
protocol: 'https',
|
|
59
|
+
host: 'api-eu.mixpanel.com' // EU endpoint for GDPR compliance
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Generate IDs
|
|
63
|
+
this.machineId = getMachineId();
|
|
64
|
+
this.sessionId = crypto.randomUUID();
|
|
65
|
+
|
|
66
|
+
// Build base properties included with every event
|
|
67
|
+
this.baseProperties = {
|
|
68
|
+
distinct_id: this.machineId,
|
|
69
|
+
session_id: this.sessionId,
|
|
70
|
+
$os: process.platform,
|
|
71
|
+
shell: detectShell(),
|
|
72
|
+
node_version: process.version,
|
|
73
|
+
specsmd_version: this._getVersion()
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
this.enabled = true;
|
|
77
|
+
return true;
|
|
78
|
+
} catch (error) {
|
|
79
|
+
// Silent failure - analytics should never break installation
|
|
80
|
+
this.enabled = false;
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Get specsmd version from package.json
|
|
87
|
+
* @private
|
|
88
|
+
*/
|
|
89
|
+
_getVersion() {
|
|
90
|
+
try {
|
|
91
|
+
const pkg = require('../../package.json');
|
|
92
|
+
return pkg.version || 'unknown';
|
|
93
|
+
} catch {
|
|
94
|
+
return 'unknown';
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Track an event (fire-and-forget)
|
|
100
|
+
* @private
|
|
101
|
+
*
|
|
102
|
+
* @param {string} eventName - Name of the event
|
|
103
|
+
* @param {Object} properties - Additional event properties
|
|
104
|
+
* @param {boolean} waitForDelivery - Wait for event to be sent
|
|
105
|
+
* @returns {Promise<void>} Resolves when event is sent (if waitForDelivery is true)
|
|
106
|
+
*/
|
|
107
|
+
track(eventName, properties = {}, waitForDelivery = false) {
|
|
108
|
+
if (!this.enabled || !this.mixpanel) {
|
|
109
|
+
return waitForDelivery ? Promise.resolve() : undefined;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const eventData = {
|
|
113
|
+
...this.baseProperties,
|
|
114
|
+
...properties
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
if (waitForDelivery) {
|
|
118
|
+
return new Promise((resolve) => {
|
|
119
|
+
try {
|
|
120
|
+
this.mixpanel.track(eventName, eventData, (err) => {
|
|
121
|
+
// Resolve regardless of error - silent failure
|
|
122
|
+
resolve();
|
|
123
|
+
});
|
|
124
|
+
} catch {
|
|
125
|
+
resolve();
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
this.mixpanel.track(eventName, eventData);
|
|
132
|
+
} catch {
|
|
133
|
+
// Silent failure
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Track installer_started event
|
|
139
|
+
* Called when the installer begins
|
|
140
|
+
* @returns {Promise<void>} Resolves when event is sent
|
|
141
|
+
*/
|
|
142
|
+
trackInstallerStarted() {
|
|
143
|
+
// Wait for delivery - critical funnel event
|
|
144
|
+
return this.track('installer_started', {}, true);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Track ides_confirmed event
|
|
149
|
+
* Called after user confirms IDE/tool selection
|
|
150
|
+
*
|
|
151
|
+
* @param {string[]} ides - Array of selected IDE keys (e.g., ['claude-code', 'cursor'])
|
|
152
|
+
* @returns {Promise<void>} Resolves when event is sent
|
|
153
|
+
*/
|
|
154
|
+
trackIdesConfirmed(ides) {
|
|
155
|
+
// Wait for delivery - critical funnel event
|
|
156
|
+
return this.track('ides_confirmed', {
|
|
157
|
+
ide_count: ides.length,
|
|
158
|
+
ides: ides
|
|
159
|
+
}, true);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Track flow_selected event
|
|
164
|
+
* Called after user selects an SDLC flow
|
|
165
|
+
*
|
|
166
|
+
* @param {string} flow - Flow key (e.g., 'aidlc', 'agile')
|
|
167
|
+
* @returns {Promise<void>} Resolves when event is sent
|
|
168
|
+
*/
|
|
169
|
+
trackFlowSelected(flow) {
|
|
170
|
+
// Wait for delivery - critical funnel event
|
|
171
|
+
return this.track('flow_selected', {
|
|
172
|
+
flow: flow
|
|
173
|
+
}, true);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Track installation_completed event
|
|
178
|
+
* Called after successful installation for an IDE
|
|
179
|
+
*
|
|
180
|
+
* @param {string} ide - IDE key (e.g., 'claude-code')
|
|
181
|
+
* @param {string} flow - Flow key (e.g., 'aidlc')
|
|
182
|
+
* @param {number} durationMs - Installation duration in milliseconds
|
|
183
|
+
* @param {number} filesCreated - Number of files created
|
|
184
|
+
*/
|
|
185
|
+
trackInstallationCompleted(ide, flow, durationMs, filesCreated) {
|
|
186
|
+
this.track('installation_completed', {
|
|
187
|
+
ide: ide,
|
|
188
|
+
flow: flow,
|
|
189
|
+
duration_ms: durationMs,
|
|
190
|
+
files_created: filesCreated
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Track installation_failed event
|
|
196
|
+
* Called after failed installation for an IDE
|
|
197
|
+
*
|
|
198
|
+
* @param {string} ide - IDE key (e.g., 'claude-code')
|
|
199
|
+
* @param {string} errorCategory - Error category (e.g., 'file_permission', 'network', 'unknown')
|
|
200
|
+
* @param {string} [flow] - Flow key (optional, may not be selected yet)
|
|
201
|
+
*/
|
|
202
|
+
trackInstallationFailed(ide, errorCategory, flow) {
|
|
203
|
+
const properties = {
|
|
204
|
+
ide: ide,
|
|
205
|
+
error_category: errorCategory
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
if (flow) {
|
|
209
|
+
properties.flow = flow;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
this.track('installation_failed', properties);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Check if analytics is enabled
|
|
217
|
+
* @returns {boolean}
|
|
218
|
+
*/
|
|
219
|
+
isEnabled() {
|
|
220
|
+
return this.enabled;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Export singleton instance
|
|
225
|
+
const tracker = new AnalyticsTracker();
|
|
226
|
+
|
|
227
|
+
module.exports = tracker;
|
package/lib/installer.js
CHANGED
|
@@ -5,10 +5,56 @@ const yaml = require('js-yaml');
|
|
|
5
5
|
const CLIUtils = require('./cli-utils');
|
|
6
6
|
const InstallerFactory = require('./InstallerFactory');
|
|
7
7
|
const { FLOWS } = require('./constants');
|
|
8
|
+
const analytics = require('./analytics');
|
|
8
9
|
|
|
9
10
|
// Use theme from CLIUtils for consistent styling
|
|
10
11
|
const { theme } = CLIUtils;
|
|
11
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Categorize an error for analytics tracking
|
|
15
|
+
* @param {Error} error - The error to categorize
|
|
16
|
+
* @returns {string} Error category
|
|
17
|
+
*/
|
|
18
|
+
function categorizeError(error) {
|
|
19
|
+
const message = (error.message || '').toLowerCase();
|
|
20
|
+
|
|
21
|
+
if (message.includes('permission') || message.includes('eacces')) {
|
|
22
|
+
return 'file_permission';
|
|
23
|
+
}
|
|
24
|
+
if (message.includes('enoent') || message.includes('not found')) {
|
|
25
|
+
return 'file_not_found';
|
|
26
|
+
}
|
|
27
|
+
if (message.includes('network') || message.includes('enotfound') || message.includes('timeout')) {
|
|
28
|
+
return 'network';
|
|
29
|
+
}
|
|
30
|
+
if (message.includes('enospc') || message.includes('disk')) {
|
|
31
|
+
return 'disk_space';
|
|
32
|
+
}
|
|
33
|
+
return 'unknown';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Count files in a directory recursively
|
|
38
|
+
* @param {string} dir - Directory path
|
|
39
|
+
* @returns {Promise<number>} File count
|
|
40
|
+
*/
|
|
41
|
+
async function countFiles(dir) {
|
|
42
|
+
let count = 0;
|
|
43
|
+
try {
|
|
44
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
45
|
+
for (const entry of entries) {
|
|
46
|
+
if (entry.isDirectory()) {
|
|
47
|
+
count += await countFiles(path.join(dir, entry.name));
|
|
48
|
+
} else {
|
|
49
|
+
count++;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
} catch {
|
|
53
|
+
// Ignore errors (directory might not exist)
|
|
54
|
+
}
|
|
55
|
+
return count;
|
|
56
|
+
}
|
|
57
|
+
|
|
12
58
|
async function detectTools() {
|
|
13
59
|
const detected = [];
|
|
14
60
|
const installers = InstallerFactory.getInstallers();
|
|
@@ -22,6 +68,12 @@ async function detectTools() {
|
|
|
22
68
|
}
|
|
23
69
|
|
|
24
70
|
async function install() {
|
|
71
|
+
// Initialize analytics (respects opt-out env vars)
|
|
72
|
+
analytics.init();
|
|
73
|
+
await analytics.trackInstallerStarted();
|
|
74
|
+
|
|
75
|
+
const installStartTime = Date.now();
|
|
76
|
+
|
|
25
77
|
await CLIUtils.displayLogo();
|
|
26
78
|
CLIUtils.displayHeader('Installation', '');
|
|
27
79
|
|
|
@@ -67,6 +119,9 @@ async function install() {
|
|
|
67
119
|
process.exit(1);
|
|
68
120
|
}
|
|
69
121
|
|
|
122
|
+
// Track IDE selection (await to ensure delivery before potential cancel)
|
|
123
|
+
await analytics.trackIdesConfirmed(selectedToolKeys);
|
|
124
|
+
|
|
70
125
|
// Step 3: Select Flow
|
|
71
126
|
console.log('');
|
|
72
127
|
CLIUtils.displayStep(3, 4, 'Select SDLC flow');
|
|
@@ -88,12 +143,21 @@ async function install() {
|
|
|
88
143
|
process.exit(1);
|
|
89
144
|
}
|
|
90
145
|
|
|
146
|
+
// Track flow selection (await to ensure delivery before potential cancel)
|
|
147
|
+
await analytics.trackFlowSelected(selectedFlow);
|
|
148
|
+
|
|
91
149
|
// Step 4: Install flow files
|
|
92
150
|
console.log('');
|
|
93
151
|
CLIUtils.displayStep(4, 4, `Installing ${FLOWS[selectedFlow].name} flow...`);
|
|
94
152
|
|
|
95
153
|
try {
|
|
96
|
-
await installFlow(selectedFlow, selectedToolKeys);
|
|
154
|
+
const filesCreated = await installFlow(selectedFlow, selectedToolKeys);
|
|
155
|
+
|
|
156
|
+
// Track successful installation for each tool
|
|
157
|
+
const durationMs = Date.now() - installStartTime;
|
|
158
|
+
for (const toolKey of selectedToolKeys) {
|
|
159
|
+
analytics.trackInstallationCompleted(toolKey, selectedFlow, durationMs, filesCreated);
|
|
160
|
+
}
|
|
97
161
|
|
|
98
162
|
CLIUtils.displaySuccess(`${FLOWS[selectedFlow].name} flow installed successfully!`, 'Installation Complete');
|
|
99
163
|
|
|
@@ -108,6 +172,12 @@ async function install() {
|
|
|
108
172
|
];
|
|
109
173
|
CLIUtils.displayNextSteps(nextSteps);
|
|
110
174
|
} catch (error) {
|
|
175
|
+
// Track installation failure
|
|
176
|
+
const errorCategory = categorizeError(error);
|
|
177
|
+
for (const toolKey of selectedToolKeys) {
|
|
178
|
+
analytics.trackInstallationFailed(toolKey, errorCategory, selectedFlow);
|
|
179
|
+
}
|
|
180
|
+
|
|
111
181
|
CLIUtils.displayError(`Installation failed: ${error.message}`);
|
|
112
182
|
console.log(theme.dim('\nRolling back changes...'));
|
|
113
183
|
await rollback(selectedFlow, selectedToolKeys);
|
|
@@ -199,6 +269,10 @@ async function installFlow(flowKey, toolKeys) {
|
|
|
199
269
|
);
|
|
200
270
|
|
|
201
271
|
CLIUtils.displayStatus('', 'Created installation manifest', 'success');
|
|
272
|
+
|
|
273
|
+
// Count files created for analytics
|
|
274
|
+
const filesCreated = await countFiles(specsmdDir);
|
|
275
|
+
return filesCreated;
|
|
202
276
|
}
|
|
203
277
|
|
|
204
278
|
async function rollback(flowKey, toolKeys) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specsmd",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
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": {
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"fs-extra": "^11.1.1",
|
|
46
46
|
"gradient-string": "^2.0.2",
|
|
47
47
|
"js-yaml": "^4.1.0",
|
|
48
|
+
"mixpanel": "^0.18.0",
|
|
48
49
|
"oh-my-logo": "^0.4.0",
|
|
49
50
|
"prompts": "^2.4.2"
|
|
50
51
|
},
|