stoa-mcp 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -92,34 +92,46 @@ Stoa runs 5 stages:
92
92
  After refining, Stoa:
93
93
  - Saves the spec as readable markdown files in `.stoa/specs/`
94
94
  - Copies Stage 1 to your clipboard automatically
95
- - Prints a build command you can use with Claude Code
95
+ - Shows an interactive menu:
96
96
 
97
97
  ```
98
98
  Spec saved to .stoa/specs/personal-finance-tracker/
99
- Score: 5/5 Executable
99
+ Spec Score: 5 / 5
100
100
 
101
101
  → Stage 1 description copied to clipboard
102
102
  Paste into Lovable, Bolt, v0, or any AI tool
103
103
 
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
104
+ What next?
105
+ [b] Build with Claude Code
106
+ [c] Copy spec to clipboard
107
+ [e] Export as single markdown
108
+ [v] View spec files
109
+ [q] Done
108
110
  ```
109
111
 
112
+ - **[b] Build** — launches Claude Code with the spec pre-loaded
113
+ - **[c] Copy** — re-copies the full spec to clipboard (useful if you copied something else)
114
+ - **[e] Export** — writes all 5 stages as a single markdown to `specs/<slug>/spec.md` in your project root (visible, not hidden inside `.stoa/`)
115
+ - **[v] View** — opens the spec directory in Finder
116
+ - **[q] Done** — exits
117
+
110
118
  ### 4. Build
111
119
 
112
- **Option A — Paste into any AI tool:**
120
+ **Option A — Press [b] after refine:**
121
+
122
+ The fastest path. Press `b` in the post-refine menu and Claude Code starts building immediately.
123
+
124
+ **Option B — Paste into any AI tool:**
113
125
 
114
126
  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
127
 
116
- **Option B — Use Claude Code:**
128
+ **Option C — Use Claude Code manually:**
117
129
 
118
130
  ```bash
119
131
  claude "Read the spec in .stoa/specs/personal-finance-tracker/ and build it. Follow all constraints and subtasks."
120
132
  ```
121
133
 
122
- **Option C — Use Cursor with Claude Code extension:**
134
+ **Option D — Use Cursor with Claude Code extension:**
123
135
 
124
136
  Open the project folder in Cursor. Open Claude Code from the sidebar. Paste the build prompt.
125
137
 
@@ -261,6 +273,8 @@ stoa refine "task" --mode clipboard # Get prompts without AI calls
261
273
  # Specs
262
274
  stoa specs list # List all saved specs
263
275
  stoa specs show <name> # View a spec's contents
276
+ # Export (also available as [e] in the post-refine menu)
277
+ # Writes to specs/<slug>/spec.md in your project root
264
278
 
265
279
  # Scenarios
266
280
  stoa scenarios list # List scenarios for latest spec
package/dist/cli.js CHANGED
@@ -14,7 +14,7 @@ import { listGuardrails, addGuardrail, showGuardrail, removeGuardrail, } from ".
14
14
  import { refinePipeline as refineGuardrail } from "./guardrails/refine.js";
15
15
  import { refinePipeline as refineRole } from "./storage/roles-refine.js";
16
16
  import { toSlug, resolveSlug } from "./utils/slug.js";
17
- import { readMoodboard, writeSpecFiles, writeRefineMeta, listSpecs, showSpec } from "./storage/index.js";
17
+ import { readMoodboard, writeSpecFiles, writeRefineMeta, listSpecs, showSpec, STAGE_FILENAMES } from "./storage/index.js";
18
18
  import { listRoles, addRole, showRole, removeRole, loadRole, } from "./storage/roles.js";
19
19
  import { listScenarios, showScenarios, addScenario, removeScenario, } from "./storage/scenarios.js";
20
20
  import { generateScenarios } from "./storage/scenarios-refine.js";
@@ -130,11 +130,130 @@ function copyToClipboard(text) {
130
130
  // Clipboard unavailable — skip silently
131
131
  }
132
132
  }
133
+ async function buildExportMarkdown(specsDir, slug) {
134
+ const specDir = join(specsDir, slug);
135
+ const sections = [];
136
+ for (const stageNum of [1, 2, 3, 4, 5]) {
137
+ const filename = STAGE_FILENAMES[stageNum];
138
+ if (!filename)
139
+ continue;
140
+ try {
141
+ const content = await readFile(join(specDir, filename), "utf-8");
142
+ const displayName = STAGE_DISPLAY_NAMES[stageNum] ?? `Stage ${stageNum}`;
143
+ sections.push(`## Stage ${stageNum}: ${displayName}\n\n${content.trimEnd()}`);
144
+ }
145
+ catch {
146
+ // stage file missing — skip
147
+ }
148
+ }
149
+ return sections.join("\n\n") + "\n";
150
+ }
151
+ function waitForKeypress() {
152
+ return new Promise((resolve) => {
153
+ const { stdin } = process;
154
+ if (!stdin.isTTY || typeof stdin.setRawMode !== "function") {
155
+ resolve("q");
156
+ return;
157
+ }
158
+ const wasRaw = stdin.isRaw;
159
+ stdin.setRawMode(true);
160
+ stdin.resume();
161
+ stdin.once("data", (data) => {
162
+ stdin.setRawMode(wasRaw);
163
+ stdin.pause();
164
+ resolve(data.toString());
165
+ });
166
+ });
167
+ }
168
+ async function showPostRefineMenu(slug) {
169
+ const specsDir = join(process.cwd(), ".stoa", "specs");
170
+ const specDir = join(specsDir, slug);
171
+ const printMenu = () => {
172
+ writeln();
173
+ writeln(chalk.bold("What next?"));
174
+ writeln(` ${chalk.cyan("[b]")} Build with Claude Code`);
175
+ writeln(` ${chalk.cyan("[c]")} Copy spec to clipboard`);
176
+ writeln(` ${chalk.cyan("[e]")} Export as single markdown`);
177
+ writeln(` ${chalk.cyan("[v]")} View spec files`);
178
+ writeln(` ${chalk.cyan("[q]")} Done`);
179
+ writeln();
180
+ };
181
+ if (!process.stdin.isTTY) {
182
+ writeln();
183
+ writeln(chalk.bold("Next steps:"));
184
+ writeln(` ${chalk.cyan("stoa export")} ${slug} — export spec as markdown`);
185
+ writeln(` ${chalk.cyan("stoa build")} ${slug} — build with Claude Code`);
186
+ writeln();
187
+ return;
188
+ }
189
+ printMenu();
190
+ while (true) {
191
+ const key = await waitForKeypress();
192
+ // Handle Ctrl+C
193
+ if (key === "\x03") {
194
+ process.exit(0);
195
+ }
196
+ switch (key.toLowerCase()) {
197
+ case "b": {
198
+ // Verify spec dir exists
199
+ try {
200
+ await access(specDir);
201
+ }
202
+ catch {
203
+ writeln(chalk.red(`Error: spec directory not found: .stoa/specs/${slug}/`));
204
+ printMenu();
205
+ break;
206
+ }
207
+ const buildPrompt = `Read the spec in .stoa/specs/${slug}/ and build it. Follow all constraints and subtasks. Do not modify or delete the .stoa/ directory — it is not part of the project.`;
208
+ spawn("claude", [buildPrompt], { stdio: "inherit", detached: true });
209
+ return;
210
+ }
211
+ case "c": {
212
+ const markdown = await buildExportMarkdown(specsDir, slug);
213
+ copyToClipboard(markdown);
214
+ writeln(chalk.green("Spec copied to clipboard."));
215
+ printMenu();
216
+ break;
217
+ }
218
+ case "e": {
219
+ const markdown = await buildExportMarkdown(specsDir, slug);
220
+ const exportDir = join(process.cwd(), "specs", slug);
221
+ await mkdir(exportDir, { recursive: true });
222
+ const exportPath = join(exportDir, "spec.md");
223
+ await writeFile(exportPath, markdown, "utf-8");
224
+ writeln(chalk.green(`Exported to specs/${slug}/spec.md`));
225
+ printMenu();
226
+ break;
227
+ }
228
+ case "v": {
229
+ // Verify spec dir exists
230
+ try {
231
+ await access(specDir);
232
+ }
233
+ catch {
234
+ writeln(chalk.red(`Error: spec directory not found: .stoa/specs/${slug}/`));
235
+ printMenu();
236
+ break;
237
+ }
238
+ const opener = process.platform === "darwin" ? "open" : "xdg-open";
239
+ spawn(opener, [specDir], { stdio: "ignore", detached: true });
240
+ writeln(chalk.dim(specDir));
241
+ printMenu();
242
+ break;
243
+ }
244
+ case "q": {
245
+ process.exit(0);
246
+ }
247
+ default:
248
+ // Ignore unrecognized keys
249
+ break;
250
+ }
251
+ }
252
+ }
133
253
  async function printPostRefineOutput(slug, specScore) {
134
254
  const specsDir = join(process.cwd(), ".stoa", "specs");
135
255
  const descPath = join(specsDir, slug, "01-problem-statement.md");
136
- const scenariosPath = join(specsDir, slug, "05-evaluation-design.md");
137
- // Copy Stage 1 to clipboard
256
+ // Copy Stage 1 to clipboard (preserved behavior)
138
257
  let copiedToClipboard = false;
139
258
  try {
140
259
  const descContent = await readFile(descPath, "utf-8");
@@ -144,15 +263,6 @@ async function printPostRefineOutput(slug, specScore) {
144
263
  catch {
145
264
  // description file missing — skip
146
265
  }
147
- // Check if scenarios exist
148
- let hasScenarios = false;
149
- try {
150
- const scenariosContent = await readFile(scenariosPath, "utf-8");
151
- hasScenarios = scenariosContent.trim().length > 0;
152
- }
153
- catch {
154
- // no scenarios file
155
- }
156
266
  writeln();
157
267
  writeln(chalk.cyan(`Spec saved to .stoa/specs/${slug}/`));
158
268
  printScoreBadge(specScore);
@@ -161,13 +271,7 @@ async function printPostRefineOutput(slug, specScore) {
161
271
  writeln(chalk.green("→ Stage 1 description copied to clipboard"));
162
272
  writeln(chalk.dim(" Paste into Lovable, Bolt, v0, or any AI tool"));
163
273
  }
164
- writeln();
165
- writeln(chalk.green("→ Build prompt for Claude Code:"));
166
- writeln(chalk.dim(` Read the spec in .stoa/specs/${slug}/ and build it. Follow all constraints and subtasks.`));
167
- if (hasScenarios) {
168
- writeln();
169
- writeln(chalk.green(`→ Scenarios saved. Run: ${chalk.white(`stoa scenarios run ${slug}`)}`));
170
- }
274
+ await showPostRefineMenu(slug);
171
275
  }
172
276
  function resolveStages(stagesArg) {
173
277
  const names = stagesArg.split(",").map((s) => s.trim());
package/dist/index.js CHANGED
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stoa-mcp",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "The Specification Compiler for AI Agents. Transform vague tasks into executable specs with blind test scenarios.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",