@ydtb/specsmd 0.1.22
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 +322 -0
- package/bin/cli.js +21 -0
- package/flows/aidlc/README.md +372 -0
- package/flows/aidlc/agents/construction-agent.md +80 -0
- package/flows/aidlc/agents/inception-agent.md +97 -0
- package/flows/aidlc/agents/master-agent.md +61 -0
- package/flows/aidlc/agents/operations-agent.md +89 -0
- package/flows/aidlc/commands/construction-agent.md +67 -0
- package/flows/aidlc/commands/inception-agent.md +59 -0
- package/flows/aidlc/commands/master-agent.md +51 -0
- package/flows/aidlc/commands/operations-agent.md +81 -0
- package/flows/aidlc/context-config.yaml +67 -0
- package/flows/aidlc/memory-bank.yaml +105 -0
- package/flows/aidlc/quick-start.md +322 -0
- package/flows/aidlc/scripts/artifact-validator.cjs +594 -0
- package/flows/aidlc/scripts/bolt-complete.cjs +606 -0
- package/flows/aidlc/scripts/status-integrity.cjs +598 -0
- package/flows/aidlc/skills/construction/bolt-list.md +163 -0
- package/flows/aidlc/skills/construction/bolt-replan.md +345 -0
- package/flows/aidlc/skills/construction/bolt-start.md +442 -0
- package/flows/aidlc/skills/construction/bolt-status.md +185 -0
- package/flows/aidlc/skills/construction/navigator.md +196 -0
- package/flows/aidlc/skills/construction/prototype-apply.md +311 -0
- package/flows/aidlc/skills/inception/bolt-plan.md +372 -0
- package/flows/aidlc/skills/inception/context.md +171 -0
- package/flows/aidlc/skills/inception/intent-create.md +211 -0
- package/flows/aidlc/skills/inception/intent-list.md +124 -0
- package/flows/aidlc/skills/inception/navigator.md +207 -0
- package/flows/aidlc/skills/inception/requirements.md +227 -0
- package/flows/aidlc/skills/inception/review.md +248 -0
- package/flows/aidlc/skills/inception/story-create.md +304 -0
- package/flows/aidlc/skills/inception/units.md +278 -0
- package/flows/aidlc/skills/inception/vibe-to-spec.md +410 -0
- package/flows/aidlc/skills/master/analyze-context.md +239 -0
- package/flows/aidlc/skills/master/answer-question.md +141 -0
- package/flows/aidlc/skills/master/explain-flow.md +158 -0
- package/flows/aidlc/skills/master/project-init.md +281 -0
- package/flows/aidlc/skills/master/route-request.md +126 -0
- package/flows/aidlc/skills/operations/build.md +237 -0
- package/flows/aidlc/skills/operations/deploy.md +259 -0
- package/flows/aidlc/skills/operations/monitor.md +265 -0
- package/flows/aidlc/skills/operations/navigator.md +209 -0
- package/flows/aidlc/skills/operations/verify.md +224 -0
- package/flows/aidlc/templates/construction/bolt-template.md +226 -0
- package/flows/aidlc/templates/construction/bolt-types/ddd-construction-bolt/adr-template.md +49 -0
- package/flows/aidlc/templates/construction/bolt-types/ddd-construction-bolt/ddd-01-domain-model-template.md +55 -0
- package/flows/aidlc/templates/construction/bolt-types/ddd-construction-bolt/ddd-02-technical-design-template.md +67 -0
- package/flows/aidlc/templates/construction/bolt-types/ddd-construction-bolt/ddd-03-test-report-template.md +62 -0
- package/flows/aidlc/templates/construction/bolt-types/ddd-construction-bolt.md +590 -0
- package/flows/aidlc/templates/construction/bolt-types/simple-construction-bolt.md +347 -0
- package/flows/aidlc/templates/construction/bolt-types/spike-bolt.md +240 -0
- package/flows/aidlc/templates/construction/construction-log-template.md +129 -0
- package/flows/aidlc/templates/construction/standards/coding-standards.md +29 -0
- package/flows/aidlc/templates/construction/standards/system-architecture.md +22 -0
- package/flows/aidlc/templates/construction/standards/tech-stack.md +19 -0
- package/flows/aidlc/templates/inception/inception-log-template.md +134 -0
- package/flows/aidlc/templates/inception/project/README.md +55 -0
- package/flows/aidlc/templates/inception/requirements-template.md +144 -0
- package/flows/aidlc/templates/inception/stories-template.md +38 -0
- package/flows/aidlc/templates/inception/story-template.md +147 -0
- package/flows/aidlc/templates/inception/system-context-template.md +29 -0
- package/flows/aidlc/templates/inception/unit-brief-template.md +177 -0
- package/flows/aidlc/templates/inception/units-template.md +52 -0
- package/flows/aidlc/templates/standards/catalog.yaml +345 -0
- package/flows/aidlc/templates/standards/coding-standards.guide.md +553 -0
- package/flows/aidlc/templates/standards/data-stack.guide.md +162 -0
- package/flows/aidlc/templates/standards/decision-index-template.md +32 -0
- package/flows/aidlc/templates/standards/tech-stack.guide.md +280 -0
- package/flows/fire/README.md +19 -0
- package/flows/fire/agents/builder/agent.md +254 -0
- package/flows/fire/agents/builder/skills/code-review/SKILL.md +257 -0
- package/flows/fire/agents/builder/skills/code-review/references/auto-fix-rules.md +218 -0
- package/flows/fire/agents/builder/skills/code-review/references/review-categories.md +154 -0
- package/flows/fire/agents/builder/skills/code-review/templates/review-report.md.hbs +120 -0
- package/flows/fire/agents/builder/skills/commit-changes/SKILL.md +232 -0
- package/flows/fire/agents/builder/skills/commit-changes/scripts/commit-changes.cjs +447 -0
- package/flows/fire/agents/builder/skills/run-execute/SKILL.md +700 -0
- package/flows/fire/agents/builder/skills/run-execute/scripts/complete-run.cjs +748 -0
- package/flows/fire/agents/builder/skills/run-execute/scripts/init-run.cjs +457 -0
- package/flows/fire/agents/builder/skills/run-execute/scripts/update-phase.cjs +239 -0
- package/flows/fire/agents/builder/skills/run-execute/templates/plan.md.hbs +61 -0
- package/flows/fire/agents/builder/skills/run-execute/templates/test-report.md.hbs +81 -0
- package/flows/fire/agents/builder/skills/run-plan/SKILL.md +366 -0
- package/flows/fire/agents/builder/skills/run-status/SKILL.md +96 -0
- package/flows/fire/agents/builder/skills/walkthrough-generate/SKILL.md +181 -0
- package/flows/fire/agents/builder/skills/walkthrough-generate/templates/walkthrough.md.hbs +108 -0
- package/flows/fire/agents/orchestrator/agent.md +144 -0
- package/flows/fire/agents/orchestrator/skills/project-init/SKILL.md +226 -0
- package/flows/fire/agents/orchestrator/skills/project-init/templates/coding-standards.md.hbs +149 -0
- package/flows/fire/agents/orchestrator/skills/project-init/templates/constitution.md.hbs +43 -0
- package/flows/fire/agents/orchestrator/skills/project-init/templates/system-architecture.md.hbs +101 -0
- package/flows/fire/agents/orchestrator/skills/project-init/templates/tech-stack.md.hbs +136 -0
- package/flows/fire/agents/orchestrator/skills/project-init/templates/testing-standards.md.hbs +94 -0
- package/flows/fire/agents/orchestrator/skills/route/SKILL.md +146 -0
- package/flows/fire/agents/orchestrator/skills/status/SKILL.md +696 -0
- package/flows/fire/agents/planner/agent.md +143 -0
- package/flows/fire/agents/planner/skills/design-doc-generate/SKILL.md +156 -0
- package/flows/fire/agents/planner/skills/design-doc-generate/templates/design.md.hbs +124 -0
- package/flows/fire/agents/planner/skills/intent-capture/SKILL.md +125 -0
- package/flows/fire/agents/planner/skills/intent-capture/templates/brief.md.hbs +40 -0
- package/flows/fire/agents/planner/skills/work-item-decompose/SKILL.md +166 -0
- package/flows/fire/agents/planner/skills/work-item-decompose/templates/work-item.md.hbs +40 -0
- package/flows/fire/commands/fire-builder.md +56 -0
- package/flows/fire/commands/fire-planner.md +48 -0
- package/flows/fire/commands/fire.md +46 -0
- package/flows/fire/memory-bank.yaml +240 -0
- package/flows/fire/quick-start.md +146 -0
- package/flows/simple/README.md +190 -0
- package/flows/simple/agents/agent.md +404 -0
- package/flows/simple/commands/agent.md +60 -0
- package/flows/simple/context-config.yaml +34 -0
- package/flows/simple/memory-bank.yaml +66 -0
- package/flows/simple/quick-start.md +231 -0
- package/flows/simple/skills/design.md +96 -0
- package/flows/simple/skills/execute.md +190 -0
- package/flows/simple/skills/requirements.md +94 -0
- package/flows/simple/skills/tasks.md +136 -0
- package/flows/simple/templates/design-template.md +138 -0
- package/flows/simple/templates/requirements-template.md +85 -0
- package/flows/simple/templates/tasks-template.md +104 -0
- package/lib/InstallerFactory.js +36 -0
- 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 +232 -0
- package/lib/cli-utils.js +342 -0
- package/lib/constants.js +44 -0
- package/lib/installer.js +406 -0
- package/lib/installers/AntigravityInstaller.js +22 -0
- package/lib/installers/ClaudeInstaller.js +85 -0
- package/lib/installers/ClineInstaller.js +21 -0
- package/lib/installers/CodexInstaller.js +21 -0
- package/lib/installers/CopilotInstaller.js +113 -0
- package/lib/installers/CursorInstaller.js +63 -0
- package/lib/installers/GeminiInstaller.js +75 -0
- package/lib/installers/KiroInstaller.js +77 -0
- package/lib/installers/OpenCodeInstaller.js +30 -0
- package/lib/installers/RooInstaller.js +22 -0
- package/lib/installers/ToolInstaller.js +76 -0
- package/lib/installers/WindsurfInstaller.js +22 -0
- package/lib/markdown-validator.ts +175 -0
- package/lib/yaml-validator.ts +99 -0
- package/package.json +69 -0
|
@@ -0,0 +1,232 @@
|
|
|
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
|
+
// Note: geolocate: true enables IP-based geolocation for analytics.
|
|
61
|
+
// This data is used solely for aggregate usage insights (e.g., country-level
|
|
62
|
+
// adoption patterns). No personal identifiers are collected. Users can
|
|
63
|
+
// opt out via the --no-telemetry flag or SPECSMD_NO_TELEMETRY env var.
|
|
64
|
+
geolocate: true
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Generate IDs
|
|
68
|
+
this.machineId = getMachineId();
|
|
69
|
+
this.sessionId = crypto.randomUUID();
|
|
70
|
+
|
|
71
|
+
// Build base properties included with every event
|
|
72
|
+
this.baseProperties = {
|
|
73
|
+
distinct_id: this.machineId,
|
|
74
|
+
session_id: this.sessionId,
|
|
75
|
+
$os: process.platform,
|
|
76
|
+
shell: detectShell(),
|
|
77
|
+
node_version: process.version,
|
|
78
|
+
specsmd_version: this._getVersion()
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
this.enabled = true;
|
|
82
|
+
return true;
|
|
83
|
+
} catch (error) {
|
|
84
|
+
// Silent failure - analytics should never break installation
|
|
85
|
+
this.enabled = false;
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Get specsmd version from package.json
|
|
92
|
+
* @private
|
|
93
|
+
*/
|
|
94
|
+
_getVersion() {
|
|
95
|
+
try {
|
|
96
|
+
const pkg = require('../../package.json');
|
|
97
|
+
return pkg.version || 'unknown';
|
|
98
|
+
} catch {
|
|
99
|
+
return 'unknown';
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Track an event (fire-and-forget)
|
|
105
|
+
* @private
|
|
106
|
+
*
|
|
107
|
+
* @param {string} eventName - Name of the event
|
|
108
|
+
* @param {Object} properties - Additional event properties
|
|
109
|
+
* @param {boolean} waitForDelivery - Wait for event to be sent
|
|
110
|
+
* @returns {Promise<void>} Resolves when event is sent (if waitForDelivery is true)
|
|
111
|
+
*/
|
|
112
|
+
track(eventName, properties = {}, waitForDelivery = false) {
|
|
113
|
+
if (!this.enabled || !this.mixpanel) {
|
|
114
|
+
return waitForDelivery ? Promise.resolve() : undefined;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const eventData = {
|
|
118
|
+
...this.baseProperties,
|
|
119
|
+
...properties
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
if (waitForDelivery) {
|
|
123
|
+
return new Promise((resolve) => {
|
|
124
|
+
try {
|
|
125
|
+
this.mixpanel.track(eventName, eventData, () => {
|
|
126
|
+
// Resolve regardless of error - silent failure
|
|
127
|
+
resolve();
|
|
128
|
+
});
|
|
129
|
+
} catch {
|
|
130
|
+
resolve();
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
this.mixpanel.track(eventName, eventData);
|
|
137
|
+
} catch {
|
|
138
|
+
// Silent failure
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Track installer_started event
|
|
144
|
+
* Called when the installer begins
|
|
145
|
+
* @returns {Promise<void>} Resolves when event is sent
|
|
146
|
+
*/
|
|
147
|
+
trackInstallerStarted() {
|
|
148
|
+
// Wait for delivery - critical funnel event
|
|
149
|
+
return this.track('installer_started', {}, true);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Track ides_confirmed event
|
|
154
|
+
* Called after user confirms IDE/tool selection
|
|
155
|
+
*
|
|
156
|
+
* @param {string[]} ides - Array of selected IDE keys (e.g., ['claude-code', 'cursor'])
|
|
157
|
+
* @returns {Promise<void>} Resolves when event is sent
|
|
158
|
+
*/
|
|
159
|
+
trackIdesConfirmed(ides) {
|
|
160
|
+
// Wait for delivery - critical funnel event
|
|
161
|
+
return this.track('ides_confirmed', {
|
|
162
|
+
ide_count: ides.length,
|
|
163
|
+
ides: ides
|
|
164
|
+
}, true);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Track flow_selected event
|
|
169
|
+
* Called after user selects an SDLC flow
|
|
170
|
+
*
|
|
171
|
+
* @param {string} flow - Flow key (e.g., 'aidlc', 'agile')
|
|
172
|
+
* @returns {Promise<void>} Resolves when event is sent
|
|
173
|
+
*/
|
|
174
|
+
trackFlowSelected(flow) {
|
|
175
|
+
// Wait for delivery - critical funnel event
|
|
176
|
+
return this.track('flow_selected', {
|
|
177
|
+
flow: flow
|
|
178
|
+
}, true);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Track installation_completed event
|
|
183
|
+
* Called after successful installation for an IDE
|
|
184
|
+
*
|
|
185
|
+
* @param {string} ide - IDE key (e.g., 'claude-code')
|
|
186
|
+
* @param {string} flow - Flow key (e.g., 'aidlc')
|
|
187
|
+
* @param {number} durationMs - Installation duration in milliseconds
|
|
188
|
+
* @param {number} filesCreated - Number of files created
|
|
189
|
+
*/
|
|
190
|
+
trackInstallationCompleted(ide, flow, durationMs, filesCreated) {
|
|
191
|
+
this.track('installation_completed', {
|
|
192
|
+
ide: ide,
|
|
193
|
+
flow: flow,
|
|
194
|
+
duration_ms: durationMs,
|
|
195
|
+
files_created: filesCreated
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Track installation_failed event
|
|
201
|
+
* Called after failed installation for an IDE
|
|
202
|
+
*
|
|
203
|
+
* @param {string} ide - IDE key (e.g., 'claude-code')
|
|
204
|
+
* @param {string} errorCategory - Error category (e.g., 'file_permission', 'network', 'unknown')
|
|
205
|
+
* @param {string} [flow] - Flow key (optional, may not be selected yet)
|
|
206
|
+
*/
|
|
207
|
+
trackInstallationFailed(ide, errorCategory, flow) {
|
|
208
|
+
const properties = {
|
|
209
|
+
ide: ide,
|
|
210
|
+
error_category: errorCategory
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
if (flow) {
|
|
214
|
+
properties.flow = flow;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
this.track('installation_failed', properties);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Check if analytics is enabled
|
|
222
|
+
* @returns {boolean}
|
|
223
|
+
*/
|
|
224
|
+
isEnabled() {
|
|
225
|
+
return this.enabled;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Export singleton instance
|
|
230
|
+
const tracker = new AnalyticsTracker();
|
|
231
|
+
|
|
232
|
+
module.exports = tracker;
|
package/lib/cli-utils.js
ADDED
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Utilities for specsmd
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const chalk = require('chalk');
|
|
6
|
+
const gradient = require('gradient-string');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
const { THEME_COLORS } = require('./constants');
|
|
10
|
+
|
|
11
|
+
const THEME = THEME_COLORS;
|
|
12
|
+
|
|
13
|
+
// Create gradient for logo
|
|
14
|
+
const logoGradient = gradient([THEME.primary, THEME.secondary]);
|
|
15
|
+
|
|
16
|
+
// Pixel-art style logo (brick blocks with 3D effect)
|
|
17
|
+
const PIXEL_LOGO = `
|
|
18
|
+
███████╗ ██████╗ ███████╗ ██████╗ ███████╗ ███╗ ███╗ ██████╗
|
|
19
|
+
██╔════╝ ██╔══██╗ ██╔════╝ ██╔════╝ ██╔════╝ ████╗ ████║ ██╔══██╗
|
|
20
|
+
███████╗ ██████╔╝ █████╗ ██║ ███████╗ ██╔████╔██║ ██║ ██║
|
|
21
|
+
╚════██║ ██╔═══╝ ██╔══╝ ██║ ╚════██║ ██║╚██╔╝██║ ██║ ██║
|
|
22
|
+
███████║ ██║ ███████╗ ╚██████╗ ███████║ ██╗ ██║ ╚═╝ ██║ ██████╔╝
|
|
23
|
+
╚══════╝ ╚═╝ ╚══════╝ ╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝
|
|
24
|
+
`;
|
|
25
|
+
|
|
26
|
+
// Theme chalk instances
|
|
27
|
+
const theme = {
|
|
28
|
+
primary: chalk.hex(THEME.primary),
|
|
29
|
+
secondary: chalk.hex(THEME.secondary),
|
|
30
|
+
success: chalk.hex(THEME.success),
|
|
31
|
+
error: chalk.hex(THEME.error),
|
|
32
|
+
warning: chalk.hex(THEME.warning),
|
|
33
|
+
info: chalk.hex(THEME.info),
|
|
34
|
+
dim: chalk.hex(THEME.dim)
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Box drawing characters (Unicode)
|
|
38
|
+
const BOX = {
|
|
39
|
+
topLeft: '╭',
|
|
40
|
+
topRight: '╮',
|
|
41
|
+
bottomLeft: '╰',
|
|
42
|
+
bottomRight: '╯',
|
|
43
|
+
horizontal: '─',
|
|
44
|
+
vertical: '│',
|
|
45
|
+
heavyHorizontal: '═',
|
|
46
|
+
heavyVertical: '║',
|
|
47
|
+
heavyTopLeft: '╔',
|
|
48
|
+
heavyTopRight: '╗',
|
|
49
|
+
heavyBottomLeft: '╚',
|
|
50
|
+
heavyBottomRight: '╝'
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* CLIUtils -
|
|
55
|
+
*/
|
|
56
|
+
const CLIUtils = {
|
|
57
|
+
/**
|
|
58
|
+
* Get package version
|
|
59
|
+
*/
|
|
60
|
+
getVersion() {
|
|
61
|
+
try {
|
|
62
|
+
const packageJson = require(path.join(__dirname, '..', 'package.json'));
|
|
63
|
+
return packageJson.version || 'Unknown';
|
|
64
|
+
} catch {
|
|
65
|
+
return 'Unknown';
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Get terminal width (capped at 80)
|
|
71
|
+
*/
|
|
72
|
+
getWidth() {
|
|
73
|
+
return Math.min(process.stdout.columns || 80, 80);
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Display the specs.md logo with ASCII art
|
|
78
|
+
* @param {boolean} clearScreen - Whether to clear screen first
|
|
79
|
+
*/
|
|
80
|
+
async displayLogo(clearScreen = true) {
|
|
81
|
+
if (clearScreen) {
|
|
82
|
+
console.clear();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const version = this.getVersion();
|
|
86
|
+
|
|
87
|
+
console.log('');
|
|
88
|
+
console.log(logoGradient(PIXEL_LOGO));
|
|
89
|
+
|
|
90
|
+
// Tagline with version
|
|
91
|
+
console.log(theme.primary(' AI-native development orchestration') + theme.primary.bold(` v${version}`) + theme.dim(' https://specs.md') + '\n');
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Display the specsmd logo synchronously with ASCII art
|
|
96
|
+
* @param {boolean} clearScreen - Whether to clear screen first
|
|
97
|
+
*/
|
|
98
|
+
displayLogoSync(clearScreen = true) {
|
|
99
|
+
if (clearScreen) {
|
|
100
|
+
console.clear();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const version = this.getVersion();
|
|
104
|
+
|
|
105
|
+
console.log('');
|
|
106
|
+
console.log(logoGradient(PIXEL_LOGO));
|
|
107
|
+
|
|
108
|
+
// Tagline with version
|
|
109
|
+
console.log(theme.primary(' AI-native development orchestration') + theme.primary.bold(` v${version}`) + theme.dim(' https://specs.md') + '\n');
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Display the logo using pixel-art style (alternative/legacy method name)
|
|
114
|
+
* @param {boolean} clearScreen - Whether to clear screen first
|
|
115
|
+
*/
|
|
116
|
+
displayFigletLogo(clearScreen = true) {
|
|
117
|
+
if (clearScreen) {
|
|
118
|
+
console.clear();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const version = this.getVersion();
|
|
122
|
+
|
|
123
|
+
console.log(logoGradient(PIXEL_LOGO));
|
|
124
|
+
console.log(theme.dim(' AI-native development orchestration') + theme.primary.bold(` v${version}`) + theme.dim(' https://specs.md') + '\n');
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Display a section header with separator lines
|
|
129
|
+
* @param {string} title - Section title
|
|
130
|
+
* @param {string} subtitle - Optional subtitle
|
|
131
|
+
*/
|
|
132
|
+
displaySection(title, subtitle = null) {
|
|
133
|
+
const width = this.getWidth();
|
|
134
|
+
const line = BOX.heavyHorizontal.repeat(width);
|
|
135
|
+
|
|
136
|
+
console.log('\n' + theme.primary(line));
|
|
137
|
+
console.log(theme.primary.bold(` ${title}`));
|
|
138
|
+
if (subtitle) {
|
|
139
|
+
console.log(theme.dim(` ${subtitle}`));
|
|
140
|
+
}
|
|
141
|
+
console.log(theme.primary(line) + '\n');
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Display a header (lighter than section)
|
|
146
|
+
* @param {string} title - Header title
|
|
147
|
+
* @param {string} icon - Optional icon prefix
|
|
148
|
+
*/
|
|
149
|
+
displayHeader(title, icon = '') {
|
|
150
|
+
const prefix = icon ? `${icon} ` : '';
|
|
151
|
+
console.log('\n' + theme.primary.bold(`${prefix}${title}`));
|
|
152
|
+
console.log(theme.dim(BOX.horizontal.repeat(title.length + prefix.length)) + '\n');
|
|
153
|
+
},
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Display a boxed message
|
|
157
|
+
* @param {string} content - Box content
|
|
158
|
+
* @param {object} options - Box options
|
|
159
|
+
*/
|
|
160
|
+
displayBox(content, options = {}) {
|
|
161
|
+
const {
|
|
162
|
+
title = null,
|
|
163
|
+
borderColor = 'primary',
|
|
164
|
+
padding = 1,
|
|
165
|
+
width = null
|
|
166
|
+
} = options;
|
|
167
|
+
|
|
168
|
+
const boxWidth = width || Math.min(this.getWidth() - 4, 76);
|
|
169
|
+
const innerWidth = boxWidth - 2;
|
|
170
|
+
const colorFn = theme[borderColor] || theme.primary;
|
|
171
|
+
|
|
172
|
+
// Top border
|
|
173
|
+
let topBorder;
|
|
174
|
+
if (title) {
|
|
175
|
+
const titleStr = ` ${title} `;
|
|
176
|
+
const remainingWidth = boxWidth - titleStr.length - 2;
|
|
177
|
+
const leftPad = Math.floor(remainingWidth / 2);
|
|
178
|
+
const rightPad = remainingWidth - leftPad;
|
|
179
|
+
topBorder = colorFn(BOX.topLeft + BOX.horizontal.repeat(leftPad) + titleStr + BOX.horizontal.repeat(rightPad) + BOX.topRight);
|
|
180
|
+
} else {
|
|
181
|
+
topBorder = colorFn(BOX.topLeft + BOX.horizontal.repeat(boxWidth - 2) + BOX.topRight);
|
|
182
|
+
}
|
|
183
|
+
console.log(topBorder);
|
|
184
|
+
|
|
185
|
+
// Padding top
|
|
186
|
+
for (let i = 0; i < padding; i++) {
|
|
187
|
+
console.log(colorFn(BOX.vertical) + ' '.repeat(innerWidth) + colorFn(BOX.vertical));
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Content lines
|
|
191
|
+
const lines = content.split('\n');
|
|
192
|
+
for (const line of lines) {
|
|
193
|
+
const stripped = line.replace(/\u001b\[[0-9;]*m/g, ''); // Strip ANSI codes for length calculation
|
|
194
|
+
const paddedLine = line + ' '.repeat(Math.max(0, innerWidth - stripped.length));
|
|
195
|
+
console.log(colorFn(BOX.vertical) + paddedLine.slice(0, innerWidth) + colorFn(BOX.vertical));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Padding bottom
|
|
199
|
+
for (let i = 0; i < padding; i++) {
|
|
200
|
+
console.log(colorFn(BOX.vertical) + ' '.repeat(innerWidth) + colorFn(BOX.vertical));
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Bottom border
|
|
204
|
+
console.log(colorFn(BOX.bottomLeft + BOX.horizontal.repeat(boxWidth - 2) + BOX.bottomRight));
|
|
205
|
+
},
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Display a success message in a green box
|
|
209
|
+
* @param {string} message - Success message
|
|
210
|
+
* @param {string} title - Optional title
|
|
211
|
+
*/
|
|
212
|
+
displaySuccess(message, title = 'Success') {
|
|
213
|
+
console.log('');
|
|
214
|
+
this.displayBox(theme.success(message), {
|
|
215
|
+
title,
|
|
216
|
+
borderColor: 'success',
|
|
217
|
+
padding: 0
|
|
218
|
+
});
|
|
219
|
+
},
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Display an error message in a red box
|
|
223
|
+
* @param {string} message - Error message
|
|
224
|
+
* @param {string} title - Optional title
|
|
225
|
+
*/
|
|
226
|
+
displayError(message, title = 'Error') {
|
|
227
|
+
console.log('');
|
|
228
|
+
this.displayBox(theme.error(message), {
|
|
229
|
+
title,
|
|
230
|
+
borderColor: 'error',
|
|
231
|
+
padding: 0
|
|
232
|
+
});
|
|
233
|
+
},
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Display a warning message in an amber box
|
|
237
|
+
* @param {string} message - Warning message
|
|
238
|
+
* @param {string} title - Optional title
|
|
239
|
+
*/
|
|
240
|
+
displayWarning(message, title = 'Warning') {
|
|
241
|
+
console.log('');
|
|
242
|
+
this.displayBox(theme.warning(message), {
|
|
243
|
+
title,
|
|
244
|
+
borderColor: 'warning',
|
|
245
|
+
padding: 0
|
|
246
|
+
});
|
|
247
|
+
},
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Display an info message in a blue box
|
|
251
|
+
* @param {string} message - Info message
|
|
252
|
+
* @param {string} title - Optional title
|
|
253
|
+
*/
|
|
254
|
+
displayInfo(message, title = 'Info') {
|
|
255
|
+
console.log('');
|
|
256
|
+
this.displayBox(theme.info(message), {
|
|
257
|
+
title,
|
|
258
|
+
borderColor: 'info',
|
|
259
|
+
padding: 0
|
|
260
|
+
});
|
|
261
|
+
},
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Display current step progress
|
|
265
|
+
* @param {number} current - Current step
|
|
266
|
+
* @param {number} total - Total steps
|
|
267
|
+
* @param {string} description - Step description
|
|
268
|
+
*/
|
|
269
|
+
displayStep(current, total, description) {
|
|
270
|
+
const progress = theme.primary.bold(`[${current}/${total}]`);
|
|
271
|
+
console.log(`${progress} ${description}`);
|
|
272
|
+
},
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Display a completion message
|
|
276
|
+
* @param {string} message - Completion message
|
|
277
|
+
*/
|
|
278
|
+
displayComplete(message) {
|
|
279
|
+
this.displaySuccess(message, 'Complete');
|
|
280
|
+
},
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Display a simple status line
|
|
284
|
+
* @param {string} icon - Status icon
|
|
285
|
+
* @param {string} message - Status message
|
|
286
|
+
* @param {string} color - Color name
|
|
287
|
+
*/
|
|
288
|
+
displayStatus(icon, message, color = 'primary') {
|
|
289
|
+
const colorFn = theme[color] || theme.primary;
|
|
290
|
+
console.log(`${colorFn(icon)} ${message}`);
|
|
291
|
+
},
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Display a list of items
|
|
295
|
+
* @param {string[]} items - List items
|
|
296
|
+
* @param {string} bullet - Bullet character
|
|
297
|
+
*/
|
|
298
|
+
displayList(items, bullet = ' -') {
|
|
299
|
+
for (const item of items) {
|
|
300
|
+
console.log(theme.dim(bullet) + ' ' + item);
|
|
301
|
+
}
|
|
302
|
+
},
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Display next steps
|
|
306
|
+
* @param {string[]} steps - Array of step descriptions
|
|
307
|
+
*/
|
|
308
|
+
displayNextSteps(steps) {
|
|
309
|
+
console.log(theme.dim('\nNext steps:'));
|
|
310
|
+
steps.forEach((step, index) => {
|
|
311
|
+
console.log(theme.dim(` ${index + 1}. ${step}`));
|
|
312
|
+
});
|
|
313
|
+
console.log('');
|
|
314
|
+
},
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Display a separator line
|
|
318
|
+
* @param {string} style - 'light' or 'heavy'
|
|
319
|
+
*/
|
|
320
|
+
displaySeparator(style = 'light') {
|
|
321
|
+
const width = this.getWidth();
|
|
322
|
+
const char = style === 'heavy' ? BOX.heavyHorizontal : BOX.horizontal;
|
|
323
|
+
console.log(theme.dim(char.repeat(width)));
|
|
324
|
+
},
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Theme colors for external use
|
|
328
|
+
*/
|
|
329
|
+
theme,
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Logo gradient for external use
|
|
333
|
+
*/
|
|
334
|
+
logoGradient,
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Box characters for external use
|
|
338
|
+
*/
|
|
339
|
+
BOX
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
module.exports = CLIUtils;
|
package/lib/constants.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
|
|
2
|
+
// Theme Colors (dark red)
|
|
3
|
+
const THEME_COLORS = {
|
|
4
|
+
primary: '#A83232', // Dark brick red
|
|
5
|
+
secondary: '#C04545', // Medium red
|
|
6
|
+
accent: '#D85858', // Coral red
|
|
7
|
+
success: '#22c55e', // Green
|
|
8
|
+
error: '#ef4444', // Red
|
|
9
|
+
warning: '#f59e0b', // Amber
|
|
10
|
+
info: '#3b82f6', // Blue
|
|
11
|
+
dim: '#666666' // Gray shadow (visible on dark/light terminals)
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const FLOWS = {
|
|
15
|
+
fire: {
|
|
16
|
+
name: 'FIRE',
|
|
17
|
+
description: 'Adaptive execution - Brownfield/monorepo ready, right-sizes rigor to complexity',
|
|
18
|
+
path: 'fire'
|
|
19
|
+
},
|
|
20
|
+
aidlc: {
|
|
21
|
+
name: 'AI-DLC',
|
|
22
|
+
description: 'Full methodology - Comprehensive traceability, DDD or Simple bolt types',
|
|
23
|
+
path: 'aidlc'
|
|
24
|
+
},
|
|
25
|
+
simple: {
|
|
26
|
+
name: 'Simple',
|
|
27
|
+
description: 'Spec generation only (Kiro style) - Creates requirement/design/task docs, no execution tracking',
|
|
28
|
+
path: 'simple'
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const LINKS = {
|
|
33
|
+
website: 'https://specs.md',
|
|
34
|
+
flows: 'https://specs.md/architecture/flows',
|
|
35
|
+
ideExtension: 'https://specs.md/getting-started/ide-extension',
|
|
36
|
+
vscodeMarketplace: 'https://marketplace.visualstudio.com/items?itemName=fabriqaai.specsmd',
|
|
37
|
+
openVsx: 'https://open-vsx.org/extension/fabriqaai/specsmd'
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
module.exports = {
|
|
41
|
+
THEME_COLORS,
|
|
42
|
+
FLOWS,
|
|
43
|
+
LINKS
|
|
44
|
+
};
|