specsmd 0.0.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 +300 -0
- package/bin/cli.js +21 -0
- package/flows/aidlc/README.md +372 -0
- package/flows/aidlc/agents/construction-agent.md +81 -0
- package/flows/aidlc/agents/inception-agent.md +95 -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 +63 -0
- package/flows/aidlc/commands/inception-agent.md +55 -0
- package/flows/aidlc/commands/master-agent.md +47 -0
- package/flows/aidlc/commands/operations-agent.md +77 -0
- package/flows/aidlc/context-config.yaml +41 -0
- package/flows/aidlc/memory-bank.yaml +104 -0
- package/flows/aidlc/quick-start.md +315 -0
- package/flows/aidlc/skills/construction/bolt-list.md +163 -0
- package/flows/aidlc/skills/construction/bolt-replan.md +343 -0
- package/flows/aidlc/skills/construction/bolt-start.md +289 -0
- package/flows/aidlc/skills/construction/bolt-status.md +185 -0
- package/flows/aidlc/skills/construction/navigator.md +196 -0
- package/flows/aidlc/skills/inception/bolt-plan.md +338 -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 +271 -0
- package/flows/aidlc/skills/master/analyze-context.md +132 -0
- package/flows/aidlc/skills/master/answer-question.md +141 -0
- package/flows/aidlc/skills/master/explain-flow.md +146 -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 +193 -0
- package/flows/aidlc/templates/construction/bolt-types/bdd-construction-bolt.md +250 -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 +528 -0
- package/flows/aidlc/templates/construction/bolt-types/simple-construction-bolt.md +273 -0
- package/flows/aidlc/templates/construction/bolt-types/spike-bolt.md +240 -0
- package/flows/aidlc/templates/construction/bolt-types/tdd-construction-bolt.md +259 -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/tech-stack.guide.md +280 -0
- package/lib/InstallerFactory.js +36 -0
- package/lib/cli-utils.js +372 -0
- package/lib/constants.js +31 -0
- package/lib/installer.js +314 -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 +22 -0
- package/lib/installers/OpenCodeInstaller.js +22 -0
- package/lib/installers/RooInstaller.js +22 -0
- package/lib/installers/ToolInstaller.js +73 -0
- package/lib/installers/WindsurfInstaller.js +76 -0
- package/lib/markdown-validator.ts +175 -0
- package/lib/yaml-validator.ts +99 -0
- package/package.json +65 -0
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
# Tech Stack Facilitation Guide
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Define the technology choices that will guide code generation, architecture decisions, and ensure consistency across all AI agents working on the project.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Facilitation Approach
|
|
10
|
+
|
|
11
|
+
You are collaborating with a peer to discover their tech stack preferences. This is a conversation, not a form to fill out.
|
|
12
|
+
|
|
13
|
+
**Adapt your style:**
|
|
14
|
+
|
|
15
|
+
- If they mention specific technologies confidently → treat them as experienced, be concise
|
|
16
|
+
- If they seem uncertain → provide more context, examples, and recommendations
|
|
17
|
+
- If they have strong preferences → respect them, ask about tradeoffs they've considered
|
|
18
|
+
|
|
19
|
+
**Your role:**
|
|
20
|
+
|
|
21
|
+
- Guide discovery, don't dictate choices
|
|
22
|
+
- Surface tradeoffs they may not have considered
|
|
23
|
+
- Ensure choices are coherent (no conflicting technologies)
|
|
24
|
+
- Capture rationale, not just the choice
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Discovery Areas
|
|
29
|
+
|
|
30
|
+
### 1. Languages
|
|
31
|
+
|
|
32
|
+
**Goal**: Understand what programming language(s) they'll use.
|
|
33
|
+
|
|
34
|
+
**Open with context:**
|
|
35
|
+
> "Let's start with languages. This affects everything else - framework options, available libraries, team hiring, and performance characteristics."
|
|
36
|
+
|
|
37
|
+
**Explore:**
|
|
38
|
+
|
|
39
|
+
- What languages is the team already comfortable with?
|
|
40
|
+
- Are there languages they want to learn vs. use productively?
|
|
41
|
+
- Is type safety important? (catches bugs early vs. development speed)
|
|
42
|
+
- What's the runtime environment? (Browser, Node.js, Edge functions, Native)
|
|
43
|
+
- Any organizational standards or constraints?
|
|
44
|
+
|
|
45
|
+
**If they're unsure, guide by use case:**
|
|
46
|
+
|
|
47
|
+
| Use Case | Recommendation | Why |
|
|
48
|
+
|----------|----------------|-----|
|
|
49
|
+
| Web app (full-stack) | TypeScript | Type safety, React/Next.js ecosystem, great tooling |
|
|
50
|
+
| API service | TypeScript or Go | TS for ecosystem, Go for performance |
|
|
51
|
+
| ML/AI/Data | Python | Libraries (PyTorch, pandas), community |
|
|
52
|
+
| High-performance systems | Go or Rust | Go for simplicity, Rust for safety |
|
|
53
|
+
| Scripts/automation | Python or TypeScript | Readability, quick iteration |
|
|
54
|
+
|
|
55
|
+
**Common signals to listen for:**
|
|
56
|
+
|
|
57
|
+
- "We're a React shop" → TypeScript
|
|
58
|
+
- "We do ML/AI" → Python, possibly with TypeScript frontend
|
|
59
|
+
- "Performance is critical" → Go, Rust, or optimized Node.js
|
|
60
|
+
- "Small team, move fast" → TypeScript or Python
|
|
61
|
+
- "Enterprise environment" → Java/Kotlin or TypeScript
|
|
62
|
+
|
|
63
|
+
**Validate before moving on:**
|
|
64
|
+
> "So we're going with {language}. This means {implication}. Sound right?"
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
### 2. Framework
|
|
69
|
+
|
|
70
|
+
**Goal**: Understand their application framework choice.
|
|
71
|
+
|
|
72
|
+
**Context to share:**
|
|
73
|
+
> "Your framework shapes project structure, available patterns, and deployment options. It's one of the hardest things to change later."
|
|
74
|
+
|
|
75
|
+
**Explore:**
|
|
76
|
+
|
|
77
|
+
- What type of application? (Web app, API only, CLI, Mobile)
|
|
78
|
+
- Do they need server-side rendering (SSR) or static generation (SSG)?
|
|
79
|
+
- Is this a new project or adding to existing code?
|
|
80
|
+
- Where will it be deployed? (This affects framework choice)
|
|
81
|
+
- Any real-time requirements? (WebSockets, subscriptions)
|
|
82
|
+
|
|
83
|
+
**Guide by language and use case:**
|
|
84
|
+
|
|
85
|
+
**TypeScript - Web Applications:**
|
|
86
|
+
|
|
87
|
+
| Framework | Best For | Tradeoffs |
|
|
88
|
+
|-----------|----------|-----------|
|
|
89
|
+
| Next.js | Full-stack, SSR/SSG, Vercel deployment | Opinionated, tied to React |
|
|
90
|
+
| Remix | Web standards, nested routing, great DX | Smaller ecosystem |
|
|
91
|
+
| Astro | Content-heavy sites, partial hydration | Less suited for highly interactive apps |
|
|
92
|
+
| SvelteKit | Performance, smaller bundle, great DX | Smaller ecosystem than React |
|
|
93
|
+
|
|
94
|
+
**TypeScript - API Only:**
|
|
95
|
+
|
|
96
|
+
| Framework | Best For | Tradeoffs |
|
|
97
|
+
|-----------|----------|-----------|
|
|
98
|
+
| Fastify | Performance, low overhead | Less opinionated |
|
|
99
|
+
| NestJS | Enterprise, structured, dependency injection | More boilerplate |
|
|
100
|
+
| Hono | Edge functions, ultra-lightweight | Newer, smaller ecosystem |
|
|
101
|
+
| Express | Simple, huge middleware ecosystem | Older patterns, callback-heavy |
|
|
102
|
+
|
|
103
|
+
**Python - APIs:**
|
|
104
|
+
|
|
105
|
+
| Framework | Best For | Tradeoffs |
|
|
106
|
+
|-----------|----------|-----------|
|
|
107
|
+
| FastAPI | Modern async, auto-docs, type hints | Async complexity |
|
|
108
|
+
| Django | Batteries-included, ORM, admin | Heavier, monolithic |
|
|
109
|
+
| Flask | Minimal, flexible | Need to add everything yourself |
|
|
110
|
+
|
|
111
|
+
**Go - APIs:**
|
|
112
|
+
|
|
113
|
+
| Framework | Best For | Tradeoffs |
|
|
114
|
+
|-----------|----------|-----------|
|
|
115
|
+
| Gin | Fast, popular, good docs | |
|
|
116
|
+
| Echo | Similar to Gin, slightly different API | |
|
|
117
|
+
| Chi | Lightweight, idiomatic Go | Less batteries included |
|
|
118
|
+
| Standard library | Maximum control | More code to write |
|
|
119
|
+
|
|
120
|
+
**Questions to surface tradeoffs:**
|
|
121
|
+
|
|
122
|
+
- "Next.js is great but ties you to React. Is that okay?"
|
|
123
|
+
- "FastAPI requires understanding async Python. Is the team comfortable with that?"
|
|
124
|
+
- "NestJS has more structure but also more boilerplate. Do you prefer convention or flexibility?"
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
### 3. Authentication
|
|
129
|
+
|
|
130
|
+
**Goal**: Understand authentication needs.
|
|
131
|
+
|
|
132
|
+
**Optional - some projects don't need auth initially.**
|
|
133
|
+
|
|
134
|
+
**Explore:**
|
|
135
|
+
|
|
136
|
+
- Do they need user authentication?
|
|
137
|
+
- What methods? (Email/password, social login, magic links, SSO)
|
|
138
|
+
- Is this B2C (end users) or B2B (enterprise SSO)?
|
|
139
|
+
- Any compliance requirements? (MFA, audit logs)
|
|
140
|
+
|
|
141
|
+
**Options:**
|
|
142
|
+
|
|
143
|
+
| Solution | Best For | Tradeoffs |
|
|
144
|
+
|----------|----------|-----------|
|
|
145
|
+
| Supabase Auth | Supabase users, quick setup | Tied to Supabase |
|
|
146
|
+
| NextAuth.js | Next.js apps, flexible | Configuration complexity |
|
|
147
|
+
| Clerk | Quick setup, beautiful UI | Cost at scale |
|
|
148
|
+
| Auth0 | Enterprise, SSO | Cost, complexity |
|
|
149
|
+
| Lucia | Lightweight, self-hosted | More DIY |
|
|
150
|
+
| Custom | Full control | Security responsibility |
|
|
151
|
+
|
|
152
|
+
**If using Supabase for database:**
|
|
153
|
+
> "Since you're using Supabase for the database, Supabase Auth integrates seamlessly. It handles email/password, social providers, and row-level security. Worth considering unless you have specific needs."
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
### 4. Infrastructure & Deployment
|
|
158
|
+
|
|
159
|
+
**Goal**: Understand how they'll deploy and operate.
|
|
160
|
+
|
|
161
|
+
**Explore:**
|
|
162
|
+
|
|
163
|
+
- Where do they want to deploy? (Cloud provider preference?)
|
|
164
|
+
- Serverless vs. containers vs. VMs?
|
|
165
|
+
- Who manages infrastructure? (Solo dev, dedicated DevOps, managed service)
|
|
166
|
+
- Budget constraints?
|
|
167
|
+
- Any existing infrastructure to integrate with?
|
|
168
|
+
|
|
169
|
+
**Guide by context:**
|
|
170
|
+
|
|
171
|
+
| Context | Recommendation | Why |
|
|
172
|
+
|---------|----------------|-----|
|
|
173
|
+
| Solo dev, startup | Vercel, Railway, Render | Minimal ops, fast deployment |
|
|
174
|
+
| Next.js app | Vercel | Optimized for Next.js |
|
|
175
|
+
| Need containers | Fly.io, Railway, Render | Simple container hosting |
|
|
176
|
+
| Enterprise/compliance | AWS, GCP, Azure | Full control, compliance certifications |
|
|
177
|
+
| Cost-sensitive at scale | Fly.io, self-managed k8s | Lower costs, more ops |
|
|
178
|
+
| Already in AWS | AWS ecosystem | Consistency, existing knowledge |
|
|
179
|
+
|
|
180
|
+
**Serverless vs. Containers:**
|
|
181
|
+
|
|
182
|
+
- **Serverless** (Vercel, Lambda): Pay-per-use, auto-scaling, cold starts
|
|
183
|
+
- **Containers** (Fly.io, ECS): Predictable performance, more control
|
|
184
|
+
- **VMs** (EC2): Full control, predictable costs at scale
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
### 5. Package Manager
|
|
189
|
+
|
|
190
|
+
**Goal**: Quick decision on package management.
|
|
191
|
+
|
|
192
|
+
**For JavaScript/TypeScript:**
|
|
193
|
+
|
|
194
|
+
| Manager | Best For |
|
|
195
|
+
|---------|----------|
|
|
196
|
+
| pnpm | Monorepos, disk efficiency, speed |
|
|
197
|
+
| npm | Default, widest compatibility |
|
|
198
|
+
| yarn | Existing yarn projects |
|
|
199
|
+
| bun | Experimental, very fast |
|
|
200
|
+
|
|
201
|
+
**Default recommendation:** pnpm (fast, efficient, great monorepo support)
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Completing the Discovery
|
|
206
|
+
|
|
207
|
+
Once you've explored all relevant areas, summarize:
|
|
208
|
+
|
|
209
|
+
```markdown
|
|
210
|
+
## Tech Stack Summary
|
|
211
|
+
|
|
212
|
+
Based on our conversation, here's what I understand:
|
|
213
|
+
|
|
214
|
+
**Languages**: {choice}
|
|
215
|
+
{brief rationale}
|
|
216
|
+
|
|
217
|
+
**Framework**: {choice}
|
|
218
|
+
{brief rationale}
|
|
219
|
+
|
|
220
|
+
**Authentication**: {choice or "TBD"}
|
|
221
|
+
{brief rationale if applicable}
|
|
222
|
+
|
|
223
|
+
**Infrastructure**: {choice}
|
|
224
|
+
{brief rationale}
|
|
225
|
+
|
|
226
|
+
**Package Manager**: {choice}
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
Does this capture your tech stack accurately? Any adjustments needed?
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## Output Generation
|
|
236
|
+
|
|
237
|
+
After confirmation, create `standards/tech-stack.md`:
|
|
238
|
+
|
|
239
|
+
```markdown
|
|
240
|
+
# Tech Stack
|
|
241
|
+
|
|
242
|
+
## Overview
|
|
243
|
+
{1-2 sentence summary of the stack and why it fits the project}
|
|
244
|
+
|
|
245
|
+
## Languages
|
|
246
|
+
{language(s)}
|
|
247
|
+
|
|
248
|
+
{Rationale - why this choice, what it enables}
|
|
249
|
+
|
|
250
|
+
## Framework
|
|
251
|
+
{framework}
|
|
252
|
+
|
|
253
|
+
{Rationale - why this choice, deployment implications}
|
|
254
|
+
|
|
255
|
+
## Authentication
|
|
256
|
+
{auth solution or "TBD"}
|
|
257
|
+
|
|
258
|
+
{Rationale if selected}
|
|
259
|
+
|
|
260
|
+
## Infrastructure & Deployment
|
|
261
|
+
{infrastructure choice}
|
|
262
|
+
|
|
263
|
+
{Rationale - deployment strategy, scaling approach}
|
|
264
|
+
|
|
265
|
+
## Package Manager
|
|
266
|
+
{package manager}
|
|
267
|
+
|
|
268
|
+
## Decision Relationships
|
|
269
|
+
{Note any important connections between choices}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## Notes for Agent
|
|
275
|
+
|
|
276
|
+
- **Don't ask all questions linearly** - adapt based on what they've already told you
|
|
277
|
+
- **Skip irrelevant decisions** - CLI tools don't need authentication discussion
|
|
278
|
+
- **Capture rationale** - "why" is as important as "what"
|
|
279
|
+
- **Respect existing choices** - if they say "we're using X", don't try to change their mind unless there's a real issue
|
|
280
|
+
- **It's okay to leave things TBD** - not every decision needs to be made upfront
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const ClaudeInstaller = require('./installers/ClaudeInstaller');
|
|
2
|
+
const CursorInstaller = require('./installers/CursorInstaller');
|
|
3
|
+
const CopilotInstaller = require('./installers/CopilotInstaller');
|
|
4
|
+
const AntigravityInstaller = require('./installers/AntigravityInstaller');
|
|
5
|
+
const WindsurfInstaller = require('./installers/WindsurfInstaller');
|
|
6
|
+
const ClineInstaller = require('./installers/ClineInstaller');
|
|
7
|
+
const RooInstaller = require('./installers/RooInstaller');
|
|
8
|
+
const KiroInstaller = require('./installers/KiroInstaller');
|
|
9
|
+
const GeminiInstaller = require('./installers/GeminiInstaller');
|
|
10
|
+
const CodexInstaller = require('./installers/CodexInstaller');
|
|
11
|
+
const OpenCodeInstaller = require('./installers/OpenCodeInstaller');
|
|
12
|
+
|
|
13
|
+
class InstallerFactory {
|
|
14
|
+
static getInstallers() {
|
|
15
|
+
return [
|
|
16
|
+
new ClaudeInstaller(),
|
|
17
|
+
new CursorInstaller(),
|
|
18
|
+
new CopilotInstaller(),
|
|
19
|
+
new AntigravityInstaller(),
|
|
20
|
+
new WindsurfInstaller(),
|
|
21
|
+
new ClineInstaller(),
|
|
22
|
+
new RooInstaller(),
|
|
23
|
+
new KiroInstaller(),
|
|
24
|
+
new GeminiInstaller(),
|
|
25
|
+
new CodexInstaller(),
|
|
26
|
+
new OpenCodeInstaller()
|
|
27
|
+
];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
static getInstaller(key) {
|
|
31
|
+
const installers = this.getInstallers();
|
|
32
|
+
return installers.find(i => i.key === key);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = InstallerFactory;
|
package/lib/cli-utils.js
ADDED
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Utilities for specsmd
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const chalk = require('chalk');
|
|
6
|
+
const figlet = require('figlet');
|
|
7
|
+
const gradient = require('gradient-string');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
// Lazy load oh-my-logo (ESM module) via dynamic import
|
|
11
|
+
let ohMyLogo = null;
|
|
12
|
+
const getOhMyLogo = async () => {
|
|
13
|
+
if (!ohMyLogo) {
|
|
14
|
+
ohMyLogo = await import('oh-my-logo');
|
|
15
|
+
}
|
|
16
|
+
return ohMyLogo;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const { THEME_COLORS } = require('./constants');
|
|
20
|
+
|
|
21
|
+
// Theme Colors (Terracotta/Orange inspired by Claude Code)
|
|
22
|
+
const THEME = THEME_COLORS;
|
|
23
|
+
|
|
24
|
+
// Create gradient for logo
|
|
25
|
+
const logoGradient = gradient([THEME.primary, THEME.secondary]);
|
|
26
|
+
|
|
27
|
+
// Theme chalk instances
|
|
28
|
+
const theme = {
|
|
29
|
+
primary: chalk.hex(THEME.primary),
|
|
30
|
+
secondary: chalk.hex(THEME.secondary),
|
|
31
|
+
success: chalk.hex(THEME.success),
|
|
32
|
+
error: chalk.hex(THEME.error),
|
|
33
|
+
warning: chalk.hex(THEME.warning),
|
|
34
|
+
info: chalk.hex(THEME.info),
|
|
35
|
+
dim: chalk.hex(THEME.dim)
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Box drawing characters (Unicode)
|
|
39
|
+
const BOX = {
|
|
40
|
+
topLeft: '╭',
|
|
41
|
+
topRight: '╮',
|
|
42
|
+
bottomLeft: '╰',
|
|
43
|
+
bottomRight: '╯',
|
|
44
|
+
horizontal: '─',
|
|
45
|
+
vertical: '│',
|
|
46
|
+
heavyHorizontal: '═',
|
|
47
|
+
heavyVertical: '║',
|
|
48
|
+
heavyTopLeft: '╔',
|
|
49
|
+
heavyTopRight: '╗',
|
|
50
|
+
heavyBottomLeft: '╚',
|
|
51
|
+
heavyBottomRight: '╝'
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* CLIUtils -
|
|
56
|
+
*/
|
|
57
|
+
const CLIUtils = {
|
|
58
|
+
/**
|
|
59
|
+
* Get package version
|
|
60
|
+
*/
|
|
61
|
+
getVersion() {
|
|
62
|
+
try {
|
|
63
|
+
const packageJson = require(path.join(__dirname, '..', 'package.json'));
|
|
64
|
+
return packageJson.version || 'Unknown';
|
|
65
|
+
} catch {
|
|
66
|
+
return 'Unknown';
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Get terminal width (capped at 80)
|
|
72
|
+
*/
|
|
73
|
+
getWidth() {
|
|
74
|
+
return Math.min(process.stdout.columns || 80, 80);
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Display the specs.md logo with gradient ASCII art (lowercase with shadow)
|
|
79
|
+
* Uses oh-my-logo for beautiful gradient rendering
|
|
80
|
+
* @param {boolean} clearScreen - Whether to clear screen first
|
|
81
|
+
*/
|
|
82
|
+
async displayLogo(clearScreen = true) {
|
|
83
|
+
if (clearScreen) {
|
|
84
|
+
console.clear();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const version = this.getVersion();
|
|
88
|
+
|
|
89
|
+
console.log('');
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
// Use oh-my-logo with custom orange palette and Standard font (lowercase)
|
|
93
|
+
const { render } = await getOhMyLogo();
|
|
94
|
+
const logo = await render('specs.md', {
|
|
95
|
+
palette: [THEME.primary, THEME.secondary, '#ffb380'],
|
|
96
|
+
font: 'Standard',
|
|
97
|
+
direction: 'horizontal'
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
console.log(logo);
|
|
101
|
+
} catch (err) {
|
|
102
|
+
// Fallback to figlet + gradient-string if oh-my-logo fails
|
|
103
|
+
const figletArt = figlet.textSync('specs.md', {
|
|
104
|
+
font: 'Standard',
|
|
105
|
+
horizontalLayout: 'default'
|
|
106
|
+
});
|
|
107
|
+
console.log(logoGradient(figletArt));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Tagline with version
|
|
111
|
+
console.log(theme.primary(' AI-native development orchestration') + theme.primary.bold(` v${version}`) + '\n');
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Display the specsmd logo synchronously using figlet (fallback)
|
|
116
|
+
* @param {boolean} clearScreen - Whether to clear screen first
|
|
117
|
+
*/
|
|
118
|
+
displayLogoSync(clearScreen = true) {
|
|
119
|
+
if (clearScreen) {
|
|
120
|
+
console.clear();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const version = this.getVersion();
|
|
124
|
+
|
|
125
|
+
// Generate ASCII art with figlet
|
|
126
|
+
const figletArt = figlet.textSync('specs.md', {
|
|
127
|
+
font: 'Big',
|
|
128
|
+
horizontalLayout: 'default'
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
console.log('');
|
|
132
|
+
console.log(logoGradient(figletArt));
|
|
133
|
+
|
|
134
|
+
// Tagline with version
|
|
135
|
+
console.log(theme.primary(' AI-native development orchestration') + theme.primary.bold(` v${version}`) + '\n');
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Display the logo using figlet (alternative style)
|
|
140
|
+
* @param {boolean} clearScreen - Whether to clear screen first
|
|
141
|
+
*/
|
|
142
|
+
displayFigletLogo(clearScreen = true) {
|
|
143
|
+
if (clearScreen) {
|
|
144
|
+
console.clear();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const version = this.getVersion();
|
|
148
|
+
const art = figlet.textSync('specs.md', {
|
|
149
|
+
font: 'Big',
|
|
150
|
+
horizontalLayout: 'full'
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
console.log(logoGradient(art));
|
|
154
|
+
console.log(theme.dim(' AI-native development orchestration') + theme.primary.bold(` v${version}`) + '\n');
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Display a section header with separator lines
|
|
159
|
+
* @param {string} title - Section title
|
|
160
|
+
* @param {string} subtitle - Optional subtitle
|
|
161
|
+
*/
|
|
162
|
+
displaySection(title, subtitle = null) {
|
|
163
|
+
const width = this.getWidth();
|
|
164
|
+
const line = BOX.heavyHorizontal.repeat(width);
|
|
165
|
+
|
|
166
|
+
console.log('\n' + theme.primary(line));
|
|
167
|
+
console.log(theme.primary.bold(` ${title}`));
|
|
168
|
+
if (subtitle) {
|
|
169
|
+
console.log(theme.dim(` ${subtitle}`));
|
|
170
|
+
}
|
|
171
|
+
console.log(theme.primary(line) + '\n');
|
|
172
|
+
},
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Display a header (lighter than section)
|
|
176
|
+
* @param {string} title - Header title
|
|
177
|
+
* @param {string} icon - Optional icon prefix
|
|
178
|
+
*/
|
|
179
|
+
displayHeader(title, icon = '') {
|
|
180
|
+
const prefix = icon ? `${icon} ` : '';
|
|
181
|
+
console.log('\n' + theme.primary.bold(`${prefix}${title}`));
|
|
182
|
+
console.log(theme.dim(BOX.horizontal.repeat(title.length + prefix.length)) + '\n');
|
|
183
|
+
},
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Display a boxed message
|
|
187
|
+
* @param {string} content - Box content
|
|
188
|
+
* @param {object} options - Box options
|
|
189
|
+
*/
|
|
190
|
+
displayBox(content, options = {}) {
|
|
191
|
+
const {
|
|
192
|
+
title = null,
|
|
193
|
+
borderColor = 'primary',
|
|
194
|
+
padding = 1,
|
|
195
|
+
width = null
|
|
196
|
+
} = options;
|
|
197
|
+
|
|
198
|
+
const boxWidth = width || Math.min(this.getWidth() - 4, 76);
|
|
199
|
+
const innerWidth = boxWidth - 2;
|
|
200
|
+
const colorFn = theme[borderColor] || theme.primary;
|
|
201
|
+
|
|
202
|
+
// Top border
|
|
203
|
+
let topBorder;
|
|
204
|
+
if (title) {
|
|
205
|
+
const titleStr = ` ${title} `;
|
|
206
|
+
const remainingWidth = boxWidth - titleStr.length - 2;
|
|
207
|
+
const leftPad = Math.floor(remainingWidth / 2);
|
|
208
|
+
const rightPad = remainingWidth - leftPad;
|
|
209
|
+
topBorder = colorFn(BOX.topLeft + BOX.horizontal.repeat(leftPad) + titleStr + BOX.horizontal.repeat(rightPad) + BOX.topRight);
|
|
210
|
+
} else {
|
|
211
|
+
topBorder = colorFn(BOX.topLeft + BOX.horizontal.repeat(boxWidth - 2) + BOX.topRight);
|
|
212
|
+
}
|
|
213
|
+
console.log(topBorder);
|
|
214
|
+
|
|
215
|
+
// Padding top
|
|
216
|
+
for (let i = 0; i < padding; i++) {
|
|
217
|
+
console.log(colorFn(BOX.vertical) + ' '.repeat(innerWidth) + colorFn(BOX.vertical));
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Content lines
|
|
221
|
+
const lines = content.split('\n');
|
|
222
|
+
for (const line of lines) {
|
|
223
|
+
const stripped = line.replace(/\u001b\[[0-9;]*m/g, ''); // Strip ANSI codes for length calculation
|
|
224
|
+
const paddedLine = line + ' '.repeat(Math.max(0, innerWidth - stripped.length));
|
|
225
|
+
console.log(colorFn(BOX.vertical) + paddedLine.slice(0, innerWidth) + colorFn(BOX.vertical));
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Padding bottom
|
|
229
|
+
for (let i = 0; i < padding; i++) {
|
|
230
|
+
console.log(colorFn(BOX.vertical) + ' '.repeat(innerWidth) + colorFn(BOX.vertical));
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Bottom border
|
|
234
|
+
console.log(colorFn(BOX.bottomLeft + BOX.horizontal.repeat(boxWidth - 2) + BOX.bottomRight));
|
|
235
|
+
},
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Display a success message in a green box
|
|
239
|
+
* @param {string} message - Success message
|
|
240
|
+
* @param {string} title - Optional title
|
|
241
|
+
*/
|
|
242
|
+
displaySuccess(message, title = 'Success') {
|
|
243
|
+
console.log('');
|
|
244
|
+
this.displayBox(theme.success(message), {
|
|
245
|
+
title,
|
|
246
|
+
borderColor: 'success',
|
|
247
|
+
padding: 0
|
|
248
|
+
});
|
|
249
|
+
},
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Display an error message in a red box
|
|
253
|
+
* @param {string} message - Error message
|
|
254
|
+
* @param {string} title - Optional title
|
|
255
|
+
*/
|
|
256
|
+
displayError(message, title = 'Error') {
|
|
257
|
+
console.log('');
|
|
258
|
+
this.displayBox(theme.error(message), {
|
|
259
|
+
title,
|
|
260
|
+
borderColor: 'error',
|
|
261
|
+
padding: 0
|
|
262
|
+
});
|
|
263
|
+
},
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Display a warning message in an amber box
|
|
267
|
+
* @param {string} message - Warning message
|
|
268
|
+
* @param {string} title - Optional title
|
|
269
|
+
*/
|
|
270
|
+
displayWarning(message, title = 'Warning') {
|
|
271
|
+
console.log('');
|
|
272
|
+
this.displayBox(theme.warning(message), {
|
|
273
|
+
title,
|
|
274
|
+
borderColor: 'warning',
|
|
275
|
+
padding: 0
|
|
276
|
+
});
|
|
277
|
+
},
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Display an info message in a blue box
|
|
281
|
+
* @param {string} message - Info message
|
|
282
|
+
* @param {string} title - Optional title
|
|
283
|
+
*/
|
|
284
|
+
displayInfo(message, title = 'Info') {
|
|
285
|
+
console.log('');
|
|
286
|
+
this.displayBox(theme.info(message), {
|
|
287
|
+
title,
|
|
288
|
+
borderColor: 'info',
|
|
289
|
+
padding: 0
|
|
290
|
+
});
|
|
291
|
+
},
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Display current step progress
|
|
295
|
+
* @param {number} current - Current step
|
|
296
|
+
* @param {number} total - Total steps
|
|
297
|
+
* @param {string} description - Step description
|
|
298
|
+
*/
|
|
299
|
+
displayStep(current, total, description) {
|
|
300
|
+
const progress = theme.primary.bold(`[${current}/${total}]`);
|
|
301
|
+
console.log(`${progress} ${description}`);
|
|
302
|
+
},
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Display a completion message
|
|
306
|
+
* @param {string} message - Completion message
|
|
307
|
+
*/
|
|
308
|
+
displayComplete(message) {
|
|
309
|
+
this.displaySuccess(message, 'Complete');
|
|
310
|
+
},
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Display a simple status line
|
|
314
|
+
* @param {string} icon - Status icon
|
|
315
|
+
* @param {string} message - Status message
|
|
316
|
+
* @param {string} color - Color name
|
|
317
|
+
*/
|
|
318
|
+
displayStatus(icon, message, color = 'primary') {
|
|
319
|
+
const colorFn = theme[color] || theme.primary;
|
|
320
|
+
console.log(`${colorFn(icon)} ${message}`);
|
|
321
|
+
},
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Display a list of items
|
|
325
|
+
* @param {string[]} items - List items
|
|
326
|
+
* @param {string} bullet - Bullet character
|
|
327
|
+
*/
|
|
328
|
+
displayList(items, bullet = ' -') {
|
|
329
|
+
for (const item of items) {
|
|
330
|
+
console.log(theme.dim(bullet) + ' ' + item);
|
|
331
|
+
}
|
|
332
|
+
},
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Display next steps
|
|
336
|
+
* @param {string[]} steps - Array of step descriptions
|
|
337
|
+
*/
|
|
338
|
+
displayNextSteps(steps) {
|
|
339
|
+
console.log(theme.dim('\nNext steps:'));
|
|
340
|
+
steps.forEach((step, index) => {
|
|
341
|
+
console.log(theme.dim(` ${index + 1}. ${step}`));
|
|
342
|
+
});
|
|
343
|
+
console.log('');
|
|
344
|
+
},
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Display a separator line
|
|
348
|
+
* @param {string} style - 'light' or 'heavy'
|
|
349
|
+
*/
|
|
350
|
+
displaySeparator(style = 'light') {
|
|
351
|
+
const width = this.getWidth();
|
|
352
|
+
const char = style === 'heavy' ? BOX.heavyHorizontal : BOX.horizontal;
|
|
353
|
+
console.log(theme.dim(char.repeat(width)));
|
|
354
|
+
},
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Theme colors for external use
|
|
358
|
+
*/
|
|
359
|
+
theme,
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Logo gradient for external use
|
|
363
|
+
*/
|
|
364
|
+
logoGradient,
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Box characters for external use
|
|
368
|
+
*/
|
|
369
|
+
BOX
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
module.exports = CLIUtils;
|