stoa-mcp 0.1.1 → 0.1.3

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 CHANGED
@@ -32,48 +32,60 @@ This creates a `.stoa/` folder with everything Stoa needs:
32
32
  ```
33
33
  .stoa/
34
34
  moodboard/notes.md ← your design direction (colors, layout, style)
35
+ moodboard/tokens.json ← auto-generated machine-readable design tokens
35
36
  context.md ← dependencies, conventions, brand voice
36
37
  lessons.md ← project memory (grows automatically)
37
38
  guardrails/ ← rules the AI must follow
38
39
  roles/ ← AI personas (Builder, Fixer, Planner)
40
+ presets/ ← your saved custom style presets
39
41
  specs/ ← saved specifications
40
42
  ```
41
43
 
44
+ Every new project starts with the **Clean** style preset (white, minimal, Linear-style). Every spec you generate will include these design tokens automatically.
45
+
42
46
  ### 2. Set up your moodboard (optional but powerful)
43
47
 
44
- Open the moodboard file:
48
+ Your project already has a working design system. Check it:
45
49
 
46
50
  ```bash
47
- stoa edit moodboard
51
+ stoa moodboard
48
52
  ```
49
53
 
50
- Replace the template with your design preferences:
54
+ **Want a different style?** Pick a preset:
51
55
 
52
- ```markdown
53
- # Design Direction
54
- Minimal and dark like Linear
56
+ ```bash
57
+ stoa moodboard preset
58
+ ```
55
59
 
56
- # Colors
57
- Primary: #E8C872
58
- Background: #1A1A1A
59
- Text: #F5F5F5
60
+ Use arrow keys to browse 4 built-in presets with live previews:
61
+ - **Clean** — White, minimal, Linear/Vercel style
62
+ - **Dark** — Dark background, muted accents, GitHub/Raycast style
63
+ - **Warm** — Cream tones, friendly SaaS feel
64
+ - **Bold** — High contrast, sharp corners, brutalist
60
65
 
61
- # Layout
62
- Sidebar navigation left, main content right
66
+ Or apply directly: `stoa moodboard preset dark`
63
67
 
64
- # Typography
65
- Clean sans-serif, large headings
68
+ **Want to customize?** Edit interactively in the terminal:
69
+
70
+ ```bash
71
+ stoa moodboard edit
66
72
  ```
67
73
 
68
- That's it. Four lines. Every spec you generate will include these design tokens.
74
+ Walks you through each field (colors, typography, layout) one by one. Press Enter to keep, type a new value to change, or `q` to quit anytime.
69
75
 
70
- **Have a screenshot of a design you like?** Drop it in `.stoa/moodboard/` and run:
76
+ **Have a screenshot of a design you like?** Run:
71
77
 
72
78
  ```bash
73
79
  stoa moodboard describe
74
80
  ```
75
81
 
76
- If you have an Anthropic API key, Stoa will analyze the image and write the design system for you. If not, it prints a prompt you can paste into any Claude chat along with your screenshot.
82
+ This opens the moodboard folder in Finder — drop your screenshots in, press Enter. If you have an Anthropic API key, Stoa analyzes the images and writes the design system for you. If not, it prints a prompt you can paste into any Claude chat.
83
+
84
+ **Save your custom style** for reuse across projects:
85
+
86
+ ```bash
87
+ stoa moodboard save-preset my-brand
88
+ ```
77
89
 
78
90
  ### 3. Refine your idea
79
91
 
@@ -92,34 +104,46 @@ Stoa runs 5 stages:
92
104
  After refining, Stoa:
93
105
  - Saves the spec as readable markdown files in `.stoa/specs/`
94
106
  - Copies Stage 1 to your clipboard automatically
95
- - Prints a build command you can use with Claude Code
107
+ - Shows an interactive menu:
96
108
 
97
109
  ```
98
110
  Spec saved to .stoa/specs/personal-finance-tracker/
99
- Score: 5/5 Executable
111
+ Spec Score: 5 / 5
100
112
 
101
113
  → Stage 1 description copied to clipboard
102
114
  Paste into Lovable, Bolt, v0, or any AI tool
103
115
 
104
- Build prompt for Claude Code:
105
- Read the spec in .stoa/specs/personal-finance-tracker/ and build it.
106
-
107
- Scenarios saved. Run: stoa scenarios run
116
+ What next?
117
+ [b] Build with Claude Code
118
+ [c] Copy spec to clipboard
119
+ [e] Export as single markdown
120
+ [v] View spec files
121
+ [q] Done
108
122
  ```
109
123
 
124
+ - **[b] Build** — launches Claude Code with the spec pre-loaded
125
+ - **[c] Copy** — re-copies the full spec to clipboard (useful if you copied something else)
126
+ - **[e] Export** — writes all 5 stages as a single markdown to `specs/<slug>/spec.md` in your project root (visible, not hidden inside `.stoa/`)
127
+ - **[v] View** — opens the spec directory in Finder
128
+ - **[q] Done** — exits
129
+
110
130
  ### 4. Build
111
131
 
112
- **Option A — Paste into any AI tool:**
132
+ **Option A — Press [b] after refine:**
133
+
134
+ The fastest path. Press `b` in the post-refine menu and Claude Code starts building immediately.
135
+
136
+ **Option B — Paste into any AI tool:**
113
137
 
114
138
  Open Lovable, Bolt, v0, or any AI coding tool. Press Cmd+V. The spec is already on your clipboard. The AI builds exactly what you specified.
115
139
 
116
- **Option B — Use Claude Code:**
140
+ **Option C — Use Claude Code manually:**
117
141
 
118
142
  ```bash
119
143
  claude "Read the spec in .stoa/specs/personal-finance-tracker/ and build it. Follow all constraints and subtasks."
120
144
  ```
121
145
 
122
- **Option C — Use Cursor with Claude Code extension:**
146
+ **Option D — Use Cursor with Claude Code extension:**
123
147
 
124
148
  Open the project folder in Cursor. Open Claude Code from the sidebar. Paste the build prompt.
125
149
 
@@ -173,12 +197,11 @@ Stage 1 will reference your existing files, components, and design system. The s
173
197
 
174
198
  ## Changing the Design
175
199
 
176
- Update your moodboard, then refine:
200
+ Switch preset, edit interactively, or both:
177
201
 
178
202
  ```bash
179
- stoa edit moodboard
180
- # Change colors, layout, style...
181
-
203
+ stoa moodboard preset dark # switch to dark theme
204
+ stoa moodboard edit # tweak individual values
182
205
  stoa refine "Redesign the app to match the updated design system"
183
206
  ```
184
207
 
@@ -194,6 +217,7 @@ All files in `.stoa/` are optional. Use what you need, ignore what you don't.
194
217
  |------|-------------|----------|
195
218
  | `moodboard/notes.md` | Design direction: colors, layout, typography | Web apps, UI projects |
196
219
  | `moodboard/tokens.json` | Auto-generated machine-readable design values | Generated by `stoa moodboard sync` |
220
+ | `presets/*.json` | Custom saved style presets | Reuse across projects |
197
221
  | `context.md` | Dependencies, conventions, brand voice | All projects |
198
222
  | `lessons.md` | Past mistakes — auto-grows, prevents repeats | All projects (grows over time) |
199
223
  | `guardrails/*.md` | Rules the AI must follow (e.g. "don't delete code") | All projects |
@@ -241,60 +265,190 @@ Every future refine includes past lessons as failure modes to avoid. Your projec
241
265
 
242
266
  ## CLI Reference
243
267
 
268
+ ### Setup
269
+
244
270
  ```bash
245
- # Setup
246
- stoa init # Create .stoa/ folder with templates
247
- stoa edit moodboard # Open moodboard in your editor
248
- stoa edit context # Open context.md in your editor
249
- stoa edit lessons # Open lessons.md in your editor
250
-
251
- # Moodboard
252
- stoa moodboard sync # Generate tokens.json from notes.md
253
- stoa moodboard describe # AI-analyze screenshots in moodboard/
254
- stoa moodboard describe --overwrite # Overwrite existing notes.md
255
-
256
- # Refine
257
- stoa refine "your task" # Run 5-stage pipeline
258
- stoa refine "task" --mode api # Force API mode (needs API key)
259
- stoa refine "task" --mode clipboard # Get prompts without AI calls
260
-
261
- # Specs
262
- stoa specs list # List all saved specs
263
- stoa specs show <name> # View a spec's contents
264
-
265
- # Scenarios
266
- stoa scenarios list # List scenarios for latest spec
271
+ stoa init
272
+ ```
273
+ Creates `.stoa/` in the current directory with the Clean style preset, 5 guardrails, and 3 roles. Run this once per project.
274
+
275
+ ```bash
276
+ stoa edit moodboard # open moodboard in VS Code/Cursor
277
+ stoa edit context # open context.md
278
+ stoa edit lessons # open lessons.md
279
+ ```
280
+ Opens files in the best available editor. Detection order: Cursor → VS Code → `$EDITOR` → macOS default → nano.
281
+
282
+ ---
283
+
284
+ ### Moodboard
285
+
286
+ ```bash
287
+ stoa moodboard
288
+ ```
289
+ Shows current moodboard status: active style, color count, image count, and available commands.
290
+
291
+ ```bash
292
+ stoa moodboard preset
293
+ ```
294
+ **Interactive.** Browse 4 built-in presets (Clean, Dark, Warm, Bold) + any custom presets with arrow keys. Shows a live preview with colors, typography, and references. Press **Enter** to apply, **q** to cancel.
295
+
296
+ You can also apply directly without the picker:
297
+ ```bash
298
+ stoa moodboard preset dark
299
+ ```
300
+
301
+ ```bash
302
+ stoa moodboard edit
303
+ ```
304
+ **Interactive.** Walks through each field one by one: Design Direction → Colors (each individually) → Typography → Layout → Component Style → References. Press **Enter** to keep current value. Type a new value to replace. Type **q** to quit at any point.
305
+
306
+ ```bash
307
+ stoa moodboard describe
308
+ ```
309
+ **Interactive.** Opens the `.stoa/moodboard/` folder in Finder so you can drag screenshots in. Press **Enter** when ready. If you have an API key, Stoa analyzes the images with AI and writes the design system automatically. Without an API key, it prints a prompt you can paste into any Claude chat alongside your screenshot.
310
+
311
+ ```bash
312
+ stoa moodboard sync
313
+ ```
314
+ Regenerates `tokens.json` from `notes.md`. Usually happens automatically after preset or edit, but run this if you edited `notes.md` by hand.
315
+
316
+ ```bash
317
+ stoa moodboard save-preset my-brand
318
+ ```
319
+ Saves the current moodboard as `.stoa/presets/my-brand.json`. Reusable across projects — shows up in `stoa moodboard preset` picker.
320
+
321
+ ---
322
+
323
+ ### Refine
324
+
325
+ ```bash
326
+ stoa refine "Build a waitlist page with email signup and referral system"
327
+ ```
328
+ Runs the 5-stage pipeline. After completion, shows an interactive menu:
329
+
330
+ | Key | Action | Notes |
331
+ |-----|--------|-------|
332
+ | **b** | Build with Claude Code | Launches Claude Code with the spec |
333
+ | **c** | Copy spec to clipboard | Re-copy (Stage 1 is auto-copied on finish) |
334
+ | **e** | Export as markdown | Writes to `specs/<slug>/spec.md` in project root |
335
+ | **v** | View spec files | Opens spec directory in Finder |
336
+ | **q** | Done | Exits |
337
+
338
+ **Options:**
339
+ ```bash
340
+ stoa refine "task" --mode api # Force Anthropic API (needs key)
341
+ stoa refine "task" --mode claude-code # Force Claude Code CLI
342
+ stoa refine "task" --mode clipboard # Get prompts without AI calls (free)
343
+ stoa refine "task" --role planner # Use a specific role
344
+ stoa refine "task" --stages clarify,structure # Run specific stages only
345
+ ```
346
+
347
+ ---
348
+
349
+ ### Specs
350
+
351
+ ```bash
352
+ stoa specs list # List all saved specs with dates and stage count
353
+ stoa specs show <name> # Print a spec's contents to terminal
354
+ ```
355
+
356
+ Specs are saved in `.stoa/specs/<slug>/` with one markdown file per stage. The `[e]` export writes a combined `spec.md` to the visible `specs/` folder in your project root.
357
+
358
+ ---
359
+
360
+ ### Scenarios
361
+
362
+ ```bash
363
+ stoa scenarios list # List scenarios for the latest spec
364
+ stoa scenarios list <name> # List scenarios for a specific spec
267
365
  stoa scenarios run # Walk through scenarios interactively
268
366
  stoa scenarios run <name> # Run scenarios for a specific spec
367
+ ```
368
+
369
+ **Interactive.** Each scenario shows GIVEN (what to set up) and EXPECTED (what to check). Press **y** for pass, **n** for fail, **s** to skip. Shows a summary at the end.
370
+
371
+ ---
372
+
373
+ ### Review
374
+
375
+ ```bash
376
+ stoa review # Review the latest spec
377
+ stoa review <name> # Review a specific spec
378
+ ```
379
+
380
+ **Interactive.** Opens each stage for review. Accept, edit, or skip. After editing, optionally re-runs affected pipeline stages.
381
+
382
+ ---
269
383
 
270
- # Guardrails & Roles
271
- stoa guardrails list # List active guardrails
272
- stoa guardrails show <name> # View a guardrail
273
- stoa roles list # List available roles
274
- stoa roles show <name> # View a role
384
+ ### Build & Verify
275
385
 
276
- # Config
277
- stoa config # View current config
386
+ ```bash
387
+ stoa build # Build the latest spec with Claude Code
388
+ stoa build <name> # Build a specific spec
389
+ stoa verify # Run blind test verification
390
+ stoa verify <name> # Verify a specific spec
391
+ ```
392
+
393
+ Build gives you a choice: build all at once or subtask by subtask. Verify runs the scenarios interactively after the build.
394
+
395
+ ---
396
+
397
+ ### Guardrails & Roles
398
+
399
+ ```bash
400
+ stoa guardrails list # List active guardrails
401
+ stoa guardrails show <name> # View a guardrail's content
402
+ stoa guardrails add <name> # Add a new guardrail
403
+ stoa guardrails remove <name> # Remove a guardrail
404
+ stoa roles list # List available roles
405
+ stoa roles show <name> # View a role's content
406
+ stoa roles add <name> # Add a new role
407
+ stoa roles remove <name> # Remove a role
408
+ ```
409
+
410
+ Guardrails are rules injected into every refine (e.g. "don't delete existing code"). Roles are AI personas used via `--role` flag.
411
+
412
+ ---
413
+
414
+ ### Config
415
+
416
+ ```bash
417
+ stoa config # View current settings
278
418
  stoa config set apiKey <key> # Set Anthropic API key
279
- stoa config set model <model> # Set model (default: claude-sonnet-4-20250514)
280
- stoa config set mode <mode> # Set mode: api, claude-code, clipboard
419
+ stoa config set model <model> # Set model (default: claude-sonnet-4-6)
420
+ stoa config set mode <mode> # Set default mode: api, claude-code, clipboard
281
421
  ```
282
422
 
283
423
  ---
284
424
 
285
425
  ## Execution Modes
286
426
 
287
- Stoa has three ways to run the AI pipeline:
427
+ | Mode | How it works | You need | Cost |
428
+ |------|-------------|----------|------|
429
+ | `api` | Direct Anthropic API call | API key (`stoa config set apiKey`) | ~$0.05/refine |
430
+ | `claude-code` | Pipes to Claude Code CLI | Claude Code installed + subscription | Included in subscription |
431
+ | `clipboard` | Returns prompts — no AI calls | Nothing | Free |
288
432
 
289
- | Mode | How it works | You need |
290
- |------|-------------|----------|
291
- | `api` | Direct Anthropic API call | API key (`stoa config set apiKey`) |
292
- | `claude-code` | Pipes to Claude Code CLI | Claude Code installed + subscription |
293
- | `clipboard` | Returns prompts — no AI calls | Nothing (free) |
433
+ Stoa auto-detects: API key `api`, Claude Code in PATH → `claude-code`, otherwise → `clipboard`.
434
+
435
+ In clipboard mode, Stoa prints each stage's prompt. Paste into any AI chat (Claude, ChatGPT, Cursor) and copy the response back. Same pipeline, just manual.
436
+
437
+ ---
438
+
439
+ ## Keyboard Shortcuts
294
440
 
295
- Stoa auto-detects: if you have an API key it uses `api`, if Claude Code is installed it uses `claude-code`, otherwise `clipboard`.
441
+ All interactive commands support these:
296
442
 
297
- In clipboard mode, Stoa prints each stage's prompt. You paste it into any AI chat (Claude, ChatGPT, Cursor) and copy the response back. Everything works — just manually.
443
+ | Context | Key | Action |
444
+ |---------|-----|--------|
445
+ | Any prompt | `q` / `quit` / `exit` | Cancel and go back |
446
+ | Arrow key menus | `↑` `↓` or `k` `j` | Navigate |
447
+ | Arrow key menus | `Enter` | Select |
448
+ | Arrow key menus | `q` | Cancel |
449
+ | Post-refine menu | `b` `c` `e` `v` `q` | See table above |
450
+ | Scenario runner | `y` `n` `s` | Pass / Fail / Skip |
451
+ | Ctrl+C | Always | Force quit |
298
452
 
299
453
  ---
300
454
 
@@ -313,18 +467,24 @@ Add Stoa as an MCP server in Cursor. Create or edit `.cursor/mcp.json` in your p
313
467
  }
314
468
  ```
315
469
 
316
- Find the global path with:
317
-
470
+ Find the global path:
318
471
  ```bash
319
- npm root -g
472
+ echo "$(npm root -g)/stoa-mcp/dist/index.js"
320
473
  ```
321
474
 
322
475
  Then in Cursor's Agent chat:
323
-
324
476
  ```
325
477
  Use the refine_task tool with title: "My App" and description: "description of what I want"
326
478
  ```
327
479
 
480
+ ## Use with Claude Code
481
+
482
+ Stoa works directly with Claude Code. After refining:
483
+
484
+ 1. Press `[b]` in the post-refine menu — launches Claude Code automatically
485
+ 2. Or copy the spec and paste it: `claude "Read the spec in .stoa/specs/<name>/ and build it"`
486
+ 3. Or use `stoa build` for the full guided experience
487
+
328
488
  ---
329
489
 
330
490
  ## How It Works
@@ -375,6 +535,12 @@ The spec references existing files by name and says "add to" instead of "rebuild
375
535
  - `Fixer` — fixes bugs from failure context
376
536
  - `Planner` — breaks down large tasks
377
537
 
538
+ **4 Style Presets:**
539
+ - `Clean` — white, minimal, Linear/Vercel (applied by default)
540
+ - `Dark` — dark background, violet accents, GitHub/Raycast
541
+ - `Warm` — cream tones, amber accents, Cal.com/Stripe
542
+ - `Bold` — high contrast, sharp corners, brutalist
543
+
378
544
  ---
379
545
 
380
546
  ## The Stoa Desktop App
@@ -388,7 +554,7 @@ The CLI is the free version. The full loop lives in the Stoa desktop app:
388
554
  - Task hierarchy (parent → subtask → fix task)
389
555
  - Dashboard with spec scores across all tasks
390
556
 
391
- **Same pipeline, full GUI.** Coming soon at [stoafactory.com](https://stoafactory.com).
557
+ **Same pipeline, full GUI.** Coming soon at [stoafactory.com](https://stoafactory.dev).
392
558
 
393
559
  ---
394
560
 
@@ -0,0 +1 @@
1
+ export declare function runMoodboardEdit(projectDir: string): Promise<void>;
@@ -0,0 +1,245 @@
1
+ import chalk from "chalk";
2
+ import { readFileSync, writeFileSync, existsSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { createInterface } from "node:readline";
5
+ import { syncMoodboard } from "../storage/moodboard-sync.js";
6
+ function writeln(text = "") {
7
+ process.stdout.write(text + "\n");
8
+ }
9
+ const HTML_COMMENT_RE = /<!--[\s\S]*?-->/g;
10
+ function parseMoodboard(notesPath) {
11
+ const defaults = {
12
+ designDirection: "",
13
+ colors: {},
14
+ layout: "",
15
+ typography: "",
16
+ componentStyle: "",
17
+ references: "",
18
+ };
19
+ if (!existsSync(notesPath))
20
+ return defaults;
21
+ const raw = readFileSync(notesPath, "utf-8");
22
+ const sections = {};
23
+ const parts = raw.split(/^# /m);
24
+ for (const part of parts) {
25
+ const trimmed = part.trim();
26
+ if (!trimmed)
27
+ continue;
28
+ const newlineIdx = trimmed.indexOf("\n");
29
+ if (newlineIdx === -1)
30
+ continue;
31
+ const heading = trimmed.slice(0, newlineIdx).trim();
32
+ const body = trimmed.slice(newlineIdx + 1).replace(HTML_COMMENT_RE, "").trim();
33
+ if (body)
34
+ sections[heading] = body;
35
+ }
36
+ // Parse colors
37
+ const colors = {};
38
+ if (sections["Colors"]) {
39
+ const lines = sections["Colors"].split("\n");
40
+ for (const line of lines) {
41
+ const match = line.match(/^([A-Za-z][A-Za-z0-9 /]*?)\s*:\s*(#[0-9A-Fa-f]{6})\b/);
42
+ if (match) {
43
+ colors[match[1].trim()] = match[2];
44
+ }
45
+ }
46
+ }
47
+ return {
48
+ designDirection: sections["Design Direction"] ?? "",
49
+ colors,
50
+ layout: sections["Layout"] ?? "",
51
+ typography: sections["Typography"] ?? "",
52
+ componentStyle: sections["Component Style"] ?? "",
53
+ references: sections["References"] ?? "",
54
+ };
55
+ }
56
+ const QUIT_COMMANDS = new Set(["q", "quit", "exit"]);
57
+ function prompt(rl, question) {
58
+ return new Promise((resolve) => {
59
+ rl.question(question, (answer) => {
60
+ if (QUIT_COMMANDS.has(answer.trim().toLowerCase())) {
61
+ resolve(null);
62
+ }
63
+ else {
64
+ resolve(answer);
65
+ }
66
+ });
67
+ });
68
+ }
69
+ export async function runMoodboardEdit(projectDir) {
70
+ const notesPath = join(projectDir, ".stoa", "moodboard", "notes.md");
71
+ const current = parseMoodboard(notesPath);
72
+ const rl = createInterface({
73
+ input: process.stdin,
74
+ output: process.stdout,
75
+ });
76
+ writeln();
77
+ writeln(chalk.bold("Edit Moodboard"));
78
+ writeln(chalk.dim("Enter to keep current value. Type new value to replace. Type 'q' to quit."));
79
+ writeln();
80
+ let changed = false;
81
+ // Helper to handle a text field
82
+ async function editField(label, currentValue) {
83
+ writeln(chalk.bold(label));
84
+ if (currentValue) {
85
+ writeln(chalk.dim(` Current: ${currentValue}`));
86
+ }
87
+ const answer = await prompt(rl, chalk.cyan(" New: "));
88
+ if (answer === null)
89
+ return "quit";
90
+ if (answer.trim()) {
91
+ writeln(chalk.green(" ✓ Updated"));
92
+ return answer.trim();
93
+ }
94
+ writeln(chalk.dim(" ✓ Kept"));
95
+ return currentValue;
96
+ }
97
+ // Design Direction
98
+ const dir = await editField("Design Direction", current.designDirection);
99
+ if (dir === "quit") {
100
+ rl.close();
101
+ writeln(chalk.dim("\nCancelled."));
102
+ return;
103
+ }
104
+ if (dir !== current.designDirection) {
105
+ current.designDirection = dir;
106
+ changed = true;
107
+ }
108
+ writeln();
109
+ // Colors
110
+ writeln(chalk.bold("Colors"));
111
+ const colorKeys = Object.keys(current.colors);
112
+ if (colorKeys.length > 0) {
113
+ for (const key of colorKeys) {
114
+ const hex = current.colors[key];
115
+ const swatch = chalk.hex(hex)("██");
116
+ writeln(` ${chalk.dim(key)}: ${swatch} ${chalk.dim(hex)}`);
117
+ const newColor = await prompt(rl, chalk.cyan(` New ${key}: `));
118
+ if (newColor === null) {
119
+ rl.close();
120
+ writeln(chalk.dim("\nCancelled."));
121
+ return;
122
+ }
123
+ if (newColor.trim()) {
124
+ if (/^#[0-9A-Fa-f]{6}$/.test(newColor.trim())) {
125
+ current.colors[key] = newColor.trim();
126
+ changed = true;
127
+ writeln(chalk.green(" ✓ Updated"));
128
+ }
129
+ else {
130
+ writeln(chalk.yellow(" ✗ Invalid hex (use #RRGGBB). Kept original."));
131
+ }
132
+ }
133
+ else {
134
+ writeln(chalk.dim(" ✓ Kept"));
135
+ }
136
+ }
137
+ }
138
+ else {
139
+ writeln(chalk.dim(" No colors defined. Add them in format: Label: #HEXVAL"));
140
+ }
141
+ // Add new color?
142
+ const addColor = await prompt(rl, chalk.cyan(" Add new color? (name: #hex or Enter to skip): "));
143
+ if (addColor === null) {
144
+ rl.close();
145
+ writeln(chalk.dim("\nCancelled."));
146
+ return;
147
+ }
148
+ if (addColor.trim()) {
149
+ const match = addColor.match(/^([A-Za-z][A-Za-z0-9 ]*?)\s*:\s*(#[0-9A-Fa-f]{6})$/);
150
+ if (match) {
151
+ current.colors[match[1].trim()] = match[2];
152
+ changed = true;
153
+ writeln(chalk.green(` ✓ Added ${match[1].trim()}`));
154
+ }
155
+ else {
156
+ writeln(chalk.yellow(" ✗ Format: Name: #HEXVAL"));
157
+ }
158
+ }
159
+ writeln();
160
+ // Typography
161
+ const typo = await editField("Typography", current.typography);
162
+ if (typo === "quit") {
163
+ rl.close();
164
+ writeln(chalk.dim("\nCancelled."));
165
+ return;
166
+ }
167
+ if (typo !== current.typography) {
168
+ current.typography = typo;
169
+ changed = true;
170
+ }
171
+ writeln();
172
+ // Layout
173
+ const layout = await editField("Layout", current.layout);
174
+ if (layout === "quit") {
175
+ rl.close();
176
+ writeln(chalk.dim("\nCancelled."));
177
+ return;
178
+ }
179
+ if (layout !== current.layout) {
180
+ current.layout = layout;
181
+ changed = true;
182
+ }
183
+ writeln();
184
+ // Component Style
185
+ const style = await editField("Component Style", current.componentStyle);
186
+ if (style === "quit") {
187
+ rl.close();
188
+ writeln(chalk.dim("\nCancelled."));
189
+ return;
190
+ }
191
+ if (style !== current.componentStyle) {
192
+ current.componentStyle = style;
193
+ changed = true;
194
+ }
195
+ writeln();
196
+ // References
197
+ const refs = await editField("References", current.references);
198
+ if (refs === "quit") {
199
+ rl.close();
200
+ writeln(chalk.dim("\nCancelled."));
201
+ return;
202
+ }
203
+ if (refs !== current.references) {
204
+ current.references = refs;
205
+ changed = true;
206
+ }
207
+ rl.close();
208
+ writeln();
209
+ if (!changed) {
210
+ writeln(chalk.dim("No changes made."));
211
+ return;
212
+ }
213
+ // Write notes.md
214
+ const colorLines = Object.entries(current.colors)
215
+ .map(([key, value]) => `${key}: ${value}`)
216
+ .join("\n");
217
+ const markdown = `# Design Direction
218
+ ${current.designDirection || ""}
219
+
220
+ # Colors
221
+ ${colorLines || ""}
222
+
223
+ # Layout
224
+ ${current.layout || ""}
225
+
226
+ # Typography
227
+ ${current.typography || ""}
228
+
229
+ # Component Style
230
+ ${current.componentStyle || ""}
231
+
232
+ # References
233
+ ${current.references || ""}
234
+ `;
235
+ writeFileSync(notesPath, markdown, "utf-8");
236
+ writeln(chalk.green("✓ Saved to .stoa/moodboard/notes.md"));
237
+ // Auto-sync tokens
238
+ try {
239
+ syncMoodboard(projectDir);
240
+ writeln(chalk.green("✓ Synced tokens.json"));
241
+ }
242
+ catch {
243
+ // Non-critical
244
+ }
245
+ }
@@ -0,0 +1,2 @@
1
+ import type { PresetEntry } from "../storage/moodboard-presets.js";
2
+ export declare function pickPreset(entries: PresetEntry[]): Promise<PresetEntry | null>;