specsmd 0.0.34 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -1
- package/flows/aidlc/agents/inception-agent.md +4 -2
- package/flows/aidlc/context-config.yaml +28 -2
- package/flows/aidlc/memory-bank.yaml +7 -7
- 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 +133 -8
- package/flows/aidlc/skills/construction/navigator.md +7 -7
- package/flows/aidlc/skills/inception/bolt-plan.md +50 -30
- package/flows/aidlc/skills/master/analyze-context.md +107 -0
- package/flows/aidlc/skills/master/explain-flow.md +13 -1
- package/flows/aidlc/templates/construction/bolt-template.md +13 -1
- 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 +92 -23
- 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 +205 -0
- package/lib/installer.js +75 -1
- package/package.json +4 -3
|
@@ -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,205 @@
|
|
|
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
|
+
*/
|
|
105
|
+
track(eventName, properties = {}) {
|
|
106
|
+
if (!this.enabled || !this.mixpanel) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
this.mixpanel.track(eventName, {
|
|
112
|
+
...this.baseProperties,
|
|
113
|
+
...properties
|
|
114
|
+
});
|
|
115
|
+
// No await - fire and forget
|
|
116
|
+
} catch {
|
|
117
|
+
// Silent failure
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Track installer_started event
|
|
123
|
+
* Called when the installer begins
|
|
124
|
+
*/
|
|
125
|
+
trackInstallerStarted() {
|
|
126
|
+
this.track('installer_started');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Track ides_confirmed event
|
|
131
|
+
* Called after user confirms IDE/tool selection
|
|
132
|
+
*
|
|
133
|
+
* @param {string[]} ides - Array of selected IDE keys (e.g., ['claude-code', 'cursor'])
|
|
134
|
+
*/
|
|
135
|
+
trackIdesConfirmed(ides) {
|
|
136
|
+
this.track('ides_confirmed', {
|
|
137
|
+
ide_count: ides.length,
|
|
138
|
+
ides: ides
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Track flow_selected event
|
|
144
|
+
* Called after user selects an SDLC flow
|
|
145
|
+
*
|
|
146
|
+
* @param {string} flow - Flow key (e.g., 'aidlc', 'agile')
|
|
147
|
+
*/
|
|
148
|
+
trackFlowSelected(flow) {
|
|
149
|
+
this.track('flow_selected', {
|
|
150
|
+
flow: flow
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Track installation_completed event
|
|
156
|
+
* Called after successful installation for an IDE
|
|
157
|
+
*
|
|
158
|
+
* @param {string} ide - IDE key (e.g., 'claude-code')
|
|
159
|
+
* @param {string} flow - Flow key (e.g., 'aidlc')
|
|
160
|
+
* @param {number} durationMs - Installation duration in milliseconds
|
|
161
|
+
* @param {number} filesCreated - Number of files created
|
|
162
|
+
*/
|
|
163
|
+
trackInstallationCompleted(ide, flow, durationMs, filesCreated) {
|
|
164
|
+
this.track('installation_completed', {
|
|
165
|
+
ide: ide,
|
|
166
|
+
flow: flow,
|
|
167
|
+
duration_ms: durationMs,
|
|
168
|
+
files_created: filesCreated
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Track installation_failed event
|
|
174
|
+
* Called after failed installation for an IDE
|
|
175
|
+
*
|
|
176
|
+
* @param {string} ide - IDE key (e.g., 'claude-code')
|
|
177
|
+
* @param {string} errorCategory - Error category (e.g., 'file_permission', 'network', 'unknown')
|
|
178
|
+
* @param {string} [flow] - Flow key (optional, may not be selected yet)
|
|
179
|
+
*/
|
|
180
|
+
trackInstallationFailed(ide, errorCategory, flow) {
|
|
181
|
+
const properties = {
|
|
182
|
+
ide: ide,
|
|
183
|
+
error_category: errorCategory
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
if (flow) {
|
|
187
|
+
properties.flow = flow;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
this.track('installation_failed', properties);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Check if analytics is enabled
|
|
195
|
+
* @returns {boolean}
|
|
196
|
+
*/
|
|
197
|
+
isEnabled() {
|
|
198
|
+
return this.enabled;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Export singleton instance
|
|
203
|
+
const tracker = new AnalyticsTracker();
|
|
204
|
+
|
|
205
|
+
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
|
+
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
|
|
123
|
+
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
|
|
147
|
+
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.
|
|
3
|
+
"version": "0.1.1",
|
|
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": {
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
"test": "vitest run",
|
|
11
11
|
"test:watch": "vitest",
|
|
12
12
|
"test:schema": "vitest run __tests__/unit/schema-validation/",
|
|
13
|
-
"lint:md": "markdownlint '
|
|
14
|
-
"lint:md:fix": "markdownlint '
|
|
13
|
+
"lint:md": "markdownlint 'flows/**/*.md' --config ../.markdownlint.yaml",
|
|
14
|
+
"lint:md:fix": "markdownlint 'flows/**/*.md' --config ../.markdownlint.yaml --fix",
|
|
15
15
|
"validate:all": "npm run test && npm run lint:md"
|
|
16
16
|
},
|
|
17
17
|
"keywords": [
|
|
@@ -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
|
},
|