@grainulation/wheat 1.0.3 → 1.0.5

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.
Files changed (43) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +32 -31
  3. package/bin/wheat.js +63 -40
  4. package/compiler/detect-sprints.js +108 -66
  5. package/compiler/generate-manifest.js +116 -69
  6. package/compiler/wheat-compiler.js +763 -471
  7. package/lib/compiler.js +11 -6
  8. package/lib/connect.js +273 -134
  9. package/lib/defaults.js +32 -0
  10. package/lib/disconnect.js +61 -40
  11. package/lib/guard.js +20 -17
  12. package/lib/index.js +8 -8
  13. package/lib/init.js +260 -142
  14. package/lib/install-prompt.js +26 -26
  15. package/lib/load-claims.js +88 -0
  16. package/lib/quickstart.js +225 -111
  17. package/lib/serve-mcp.js +495 -180
  18. package/lib/server.js +198 -111
  19. package/lib/stats.js +65 -39
  20. package/lib/status.js +65 -34
  21. package/lib/update.js +13 -11
  22. package/package.json +8 -4
  23. package/templates/claude.md +31 -17
  24. package/templates/commands/blind-spot.md +9 -2
  25. package/templates/commands/brief.md +11 -1
  26. package/templates/commands/calibrate.md +3 -1
  27. package/templates/commands/challenge.md +4 -1
  28. package/templates/commands/connect.md +12 -1
  29. package/templates/commands/evaluate.md +4 -0
  30. package/templates/commands/feedback.md +3 -1
  31. package/templates/commands/handoff.md +11 -7
  32. package/templates/commands/init.md +4 -1
  33. package/templates/commands/merge.md +4 -1
  34. package/templates/commands/next.md +1 -0
  35. package/templates/commands/present.md +3 -0
  36. package/templates/commands/prototype.md +2 -0
  37. package/templates/commands/pull.md +103 -0
  38. package/templates/commands/replay.md +8 -0
  39. package/templates/commands/research.md +1 -0
  40. package/templates/commands/resolve.md +4 -1
  41. package/templates/commands/status.md +4 -0
  42. package/templates/commands/sync.md +94 -0
  43. package/templates/commands/witness.md +6 -2
package/lib/server.js CHANGED
@@ -9,45 +9,78 @@
9
9
  * wheat serve [--port 9092] [--dir /path/to/sprint]
10
10
  */
11
11
 
12
- import http from 'node:http';
13
- import fs from 'node:fs';
14
- import path from 'node:path';
15
- import { execFileSync } from 'node:child_process';
16
- import { fileURLToPath } from 'node:url';
12
+ import http from "node:http";
13
+ import fs from "node:fs";
14
+ import path from "node:path";
15
+ import { execFileSync } from "node:child_process";
16
+ import { fileURLToPath } from "node:url";
17
17
 
18
18
  const __filename = fileURLToPath(import.meta.url);
19
19
  const __dirname = path.dirname(__filename);
20
20
 
21
21
  // ── Crash handlers ──
22
- process.on('uncaughtException', (err) => {
23
- process.stderr.write(`[${new Date().toISOString()}] FATAL: ${err.stack || err}\n`);
22
+ process.on("uncaughtException", (err) => {
23
+ process.stderr.write(
24
+ `[${new Date().toISOString()}] FATAL: ${err.stack || err}\n`
25
+ );
24
26
  process.exit(1);
25
27
  });
26
- process.on('unhandledRejection', (reason) => {
27
- process.stderr.write(`[${new Date().toISOString()}] WARN unhandledRejection: ${reason}\n`);
28
+ process.on("unhandledRejection", (reason) => {
29
+ process.stderr.write(
30
+ `[${new Date().toISOString()}] WARN unhandledRejection: ${reason}\n`
31
+ );
28
32
  });
29
33
 
30
- const PUBLIC_DIR = path.join(__dirname, '..', 'public');
34
+ const PUBLIC_DIR = path.join(__dirname, "..", "public");
31
35
 
32
36
  // ── Verbose logging ──────────────────────────────────────────────────────────
33
37
 
34
- const verbose = process.argv.includes('--verbose');
38
+ const verbose = process.argv.includes("--verbose");
35
39
  function vlog(...a) {
36
40
  if (!verbose) return;
37
41
  const ts = new Date().toISOString();
38
- process.stderr.write(`[${ts}] wheat: ${a.join(' ')}\n`);
42
+ process.stderr.write(`[${ts}] wheat: ${a.join(" ")}\n`);
39
43
  }
40
44
 
41
45
  // ── Routes manifest ──────────────────────────────────────────────────────────
42
46
 
43
47
  const ROUTES = [
44
- { method: 'GET', path: '/events', description: 'SSE event stream for live sprint updates' },
45
- { method: 'GET', path: '/api/state', description: 'Current sprint state (claims, compilation, sprints)' },
46
- { method: 'GET', path: '/api/claims', description: 'Claims list with optional ?topic, ?type, ?evidence, ?status filters' },
47
- { method: 'GET', path: '/api/coverage', description: 'Compilation coverage data' },
48
- { method: 'GET', path: '/api/compilation', description: 'Full compilation result' },
49
- { method: 'POST', path: '/api/compile', description: 'Trigger recompilation of claims' },
50
- { method: 'GET', path: '/api/docs', description: 'This API documentation page' },
48
+ {
49
+ method: "GET",
50
+ path: "/events",
51
+ description: "SSE event stream for live sprint updates",
52
+ },
53
+ {
54
+ method: "GET",
55
+ path: "/api/state",
56
+ description: "Current sprint state (claims, compilation, sprints)",
57
+ },
58
+ {
59
+ method: "GET",
60
+ path: "/api/claims",
61
+ description:
62
+ "Claims list with optional ?topic, ?type, ?evidence, ?status filters",
63
+ },
64
+ {
65
+ method: "GET",
66
+ path: "/api/coverage",
67
+ description: "Compilation coverage data",
68
+ },
69
+ {
70
+ method: "GET",
71
+ path: "/api/compilation",
72
+ description: "Full compilation result",
73
+ },
74
+ {
75
+ method: "POST",
76
+ path: "/api/compile",
77
+ description: "Trigger recompilation of claims",
78
+ },
79
+ {
80
+ method: "GET",
81
+ path: "/api/docs",
82
+ description: "This API documentation page",
83
+ },
51
84
  ];
52
85
 
53
86
  // ── State ─────────────────────────────────────────────────────────────────────
@@ -65,18 +98,22 @@ const sseClients = new Set();
65
98
  function broadcast(event) {
66
99
  const data = `data: ${JSON.stringify(event)}\n\n`;
67
100
  for (const res of sseClients) {
68
- try { res.write(data); } catch { sseClients.delete(res); }
101
+ try {
102
+ res.write(data);
103
+ } catch {
104
+ sseClients.delete(res);
105
+ }
69
106
  }
70
107
  }
71
108
 
72
109
  // ── Data loading ──────────────────────────────────────────────────────────────
73
110
 
74
111
  function loadClaims(root) {
75
- const claimsPath = path.join(root, 'claims.json');
76
- vlog('read', claimsPath);
112
+ const claimsPath = path.join(root, "claims.json");
113
+ vlog("read", claimsPath);
77
114
  if (!fs.existsSync(claimsPath)) return { meta: null, claims: [] };
78
115
  try {
79
- const data = JSON.parse(fs.readFileSync(claimsPath, 'utf8'));
116
+ const data = JSON.parse(fs.readFileSync(claimsPath, "utf8"));
80
117
  return { meta: data.meta || null, claims: data.claims || [] };
81
118
  } catch {
82
119
  return { meta: null, claims: [] };
@@ -84,11 +121,11 @@ function loadClaims(root) {
84
121
  }
85
122
 
86
123
  function loadCompilation(root) {
87
- const compilationPath = path.join(root, 'compilation.json');
88
- vlog('read', compilationPath);
124
+ const compilationPath = path.join(root, "compilation.json");
125
+ vlog("read", compilationPath);
89
126
  if (!fs.existsSync(compilationPath)) return null;
90
127
  try {
91
- return JSON.parse(fs.readFileSync(compilationPath, 'utf8'));
128
+ return JSON.parse(fs.readFileSync(compilationPath, "utf8"));
92
129
  } catch {
93
130
  return null;
94
131
  }
@@ -96,17 +133,18 @@ function loadCompilation(root) {
96
133
 
97
134
  function loadSprints(root) {
98
135
  try {
99
- const compilerDir = path.join(__dirname, '..', 'compiler');
100
- const mod = path.join(compilerDir, 'detect-sprints.js');
136
+ const compilerDir = path.join(__dirname, "..", "compiler");
137
+ const mod = path.join(compilerDir, "detect-sprints.js");
101
138
  if (!fs.existsSync(mod)) return { sprints: [], active: null };
102
139
 
103
- const result = execFileSync('node', [mod, '--json', '--root', root], {
104
- timeout: 10000, stdio: ['ignore', 'pipe', 'pipe'],
140
+ const result = execFileSync("node", [mod, "--json", "--root", root], {
141
+ timeout: 10000,
142
+ stdio: ["ignore", "pipe", "pipe"],
105
143
  });
106
144
  const data = JSON.parse(result.toString());
107
145
  return {
108
146
  sprints: data.sprints || [],
109
- active: (data.sprints || []).find(s => s.status === 'active') || null,
147
+ active: (data.sprints || []).find((s) => s.status === "active") || null,
110
148
  };
111
149
  } catch {
112
150
  return { sprints: [], active: null };
@@ -115,10 +153,16 @@ function loadSprints(root) {
115
153
 
116
154
  function runCompile(root) {
117
155
  try {
118
- const compiler = path.join(__dirname, '..', 'compiler', 'wheat-compiler.js');
156
+ const compiler = path.join(
157
+ __dirname,
158
+ "..",
159
+ "compiler",
160
+ "wheat-compiler.js"
161
+ );
119
162
  if (!fs.existsSync(compiler)) return null;
120
- execFileSync('node', [compiler, '--root', root], {
121
- timeout: 30000, stdio: ['ignore', 'pipe', 'pipe'],
163
+ execFileSync("node", [compiler, "--root", root], {
164
+ timeout: 30000,
165
+ stdio: ["ignore", "pipe", "pipe"],
122
166
  cwd: root,
123
167
  });
124
168
  return loadCompilation(root);
@@ -133,14 +177,14 @@ function refreshState(viewRoot, scanRoot) {
133
177
  state.sprints = sprintData.sprints;
134
178
  state.activeSprint = sprintData.active;
135
179
 
136
- if (viewRoot === '__all') {
180
+ if (viewRoot === "__all") {
137
181
  // Merge claims from all sprints
138
182
  let allClaims = [];
139
183
  let meta = null;
140
184
  for (const s of sprintData.sprints) {
141
185
  const sprintRoot = path.resolve(sr, s.path);
142
186
  const d = loadClaims(sprintRoot);
143
- if (s.status === 'active' && !meta) meta = d.meta;
187
+ if (s.status === "active" && !meta) meta = d.meta;
144
188
  for (const c of d.claims) {
145
189
  c._sprint = s.name;
146
190
  allClaims.push(c);
@@ -155,18 +199,18 @@ function refreshState(viewRoot, scanRoot) {
155
199
  state.claims = claimsData.claims;
156
200
  state.compilation = loadCompilation(viewRoot);
157
201
  }
158
- broadcast({ type: 'state', data: state });
202
+ broadcast({ type: "state", data: state });
159
203
  }
160
204
 
161
205
  // ── MIME types ────────────────────────────────────────────────────────────────
162
206
 
163
207
  const MIME = {
164
- '.html': 'text/html; charset=utf-8',
165
- '.css': 'text/css; charset=utf-8',
166
- '.js': 'application/javascript; charset=utf-8',
167
- '.json': 'application/json; charset=utf-8',
168
- '.svg': 'image/svg+xml',
169
- '.png': 'image/png',
208
+ ".html": "text/html; charset=utf-8",
209
+ ".css": "text/css; charset=utf-8",
210
+ ".js": "application/javascript; charset=utf-8",
211
+ ".json": "application/json; charset=utf-8",
212
+ ".svg": "image/svg+xml",
213
+ ".png": "image/png",
170
214
  };
171
215
 
172
216
  // ── HTTP server ───────────────────────────────────────────────────────────────
@@ -178,110 +222,129 @@ function createWheatServer(root, port, corsOrigin) {
178
222
 
179
223
  // CORS (only when --cors is passed)
180
224
  if (corsOrigin) {
181
- res.setHeader('Access-Control-Allow-Origin', corsOrigin);
182
- res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
183
- res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
225
+ res.setHeader("Access-Control-Allow-Origin", corsOrigin);
226
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
227
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type");
184
228
  }
185
229
 
186
- if (req.method === 'OPTIONS' && corsOrigin) {
187
- res.writeHead(204); res.end(); return;
230
+ if (req.method === "OPTIONS" && corsOrigin) {
231
+ res.writeHead(204);
232
+ res.end();
233
+ return;
188
234
  }
189
235
 
190
- vlog('request', req.method, url.pathname);
236
+ vlog("request", req.method, url.pathname);
191
237
 
192
238
  // ── API: docs ──
193
- if (req.method === 'GET' && url.pathname === '/api/docs') {
239
+ if (req.method === "GET" && url.pathname === "/api/docs") {
194
240
  const html = `<!DOCTYPE html><html><head><title>wheat API</title>
195
241
  <style>body{font-family:system-ui;background:#0a0e1a;color:#e8ecf1;max-width:800px;margin:40px auto;padding:0 20px}
196
242
  table{width:100%;border-collapse:collapse}th,td{padding:8px 12px;border-bottom:1px solid #1e293b;text-align:left}
197
243
  th{color:#9ca3af}code{background:#1e293b;padding:2px 6px;border-radius:4px;font-size:13px}</style></head>
198
244
  <body><h1>wheat API</h1><p>${ROUTES.length} endpoints</p>
199
245
  <table><tr><th>Method</th><th>Path</th><th>Description</th></tr>
200
- ${ROUTES.map(r => '<tr><td><code>'+r.method+'</code></td><td><code>'+r.path+'</code></td><td>'+r.description+'</td></tr>').join('')}
246
+ ${ROUTES.map(
247
+ (r) =>
248
+ "<tr><td><code>" +
249
+ r.method +
250
+ "</code></td><td><code>" +
251
+ r.path +
252
+ "</code></td><td>" +
253
+ r.description +
254
+ "</td></tr>"
255
+ ).join("")}
201
256
  </table></body></html>`;
202
- res.writeHead(200, { 'Content-Type': 'text/html' });
257
+ res.writeHead(200, { "Content-Type": "text/html" });
203
258
  res.end(html);
204
259
  return;
205
260
  }
206
261
 
207
262
  // ── SSE ──
208
- if (req.method === 'GET' && url.pathname === '/events') {
263
+ if (req.method === "GET" && url.pathname === "/events") {
209
264
  res.writeHead(200, {
210
- 'Content-Type': 'text/event-stream',
211
- 'Cache-Control': 'no-cache',
212
- 'Connection': 'keep-alive',
265
+ "Content-Type": "text/event-stream",
266
+ "Cache-Control": "no-cache",
267
+ Connection: "keep-alive",
213
268
  });
214
- res.write(`data: ${JSON.stringify({ type: 'state', data: state })}\n\n`);
269
+ res.write(`data: ${JSON.stringify({ type: "state", data: state })}\n\n`);
215
270
  const heartbeat = setInterval(() => {
216
- try { res.write(': heartbeat\n\n'); } catch { clearInterval(heartbeat); }
271
+ try {
272
+ res.write(": heartbeat\n\n");
273
+ } catch {
274
+ clearInterval(heartbeat);
275
+ }
217
276
  }, 15000);
218
277
  sseClients.add(res);
219
- vlog('sse', `client connected (${sseClients.size} total)`);
220
- req.on('close', () => { clearInterval(heartbeat); sseClients.delete(res); vlog('sse', `client disconnected (${sseClients.size} total)`); });
278
+ vlog("sse", `client connected (${sseClients.size} total)`);
279
+ req.on("close", () => {
280
+ clearInterval(heartbeat);
281
+ sseClients.delete(res);
282
+ vlog("sse", `client disconnected (${sseClients.size} total)`);
283
+ });
221
284
  return;
222
285
  }
223
286
 
224
287
  // ── API: state ──
225
- if (req.method === 'GET' && url.pathname === '/api/state') {
226
- res.writeHead(200, { 'Content-Type': 'application/json' });
288
+ if (req.method === "GET" && url.pathname === "/api/state") {
289
+ res.writeHead(200, { "Content-Type": "application/json" });
227
290
  res.end(JSON.stringify(state));
228
291
  return;
229
292
  }
230
293
 
231
294
  // ── API: claims (with optional filters) ──
232
- if (req.method === 'GET' && url.pathname === '/api/claims') {
295
+ if (req.method === "GET" && url.pathname === "/api/claims") {
233
296
  let claims = state.claims;
234
- const topic = url.searchParams.get('topic');
235
- const evidence = url.searchParams.get('evidence');
236
- const type = url.searchParams.get('type');
237
- const status = url.searchParams.get('status');
238
- if (topic) claims = claims.filter(c => c.topic === topic);
239
- if (evidence) claims = claims.filter(c => c.evidence === evidence);
240
- if (type) claims = claims.filter(c => c.type === type);
241
- if (status) claims = claims.filter(c => c.status === status);
242
- res.writeHead(200, { 'Content-Type': 'application/json' });
297
+ const topic = url.searchParams.get("topic");
298
+ const evidence = url.searchParams.get("evidence");
299
+ const type = url.searchParams.get("type");
300
+ const status = url.searchParams.get("status");
301
+ if (topic) claims = claims.filter((c) => c.topic === topic);
302
+ if (evidence) claims = claims.filter((c) => c.evidence === evidence);
303
+ if (type) claims = claims.filter((c) => c.type === type);
304
+ if (status) claims = claims.filter((c) => c.status === status);
305
+ res.writeHead(200, { "Content-Type": "application/json" });
243
306
  res.end(JSON.stringify(claims));
244
307
  return;
245
308
  }
246
309
 
247
310
  // ── API: coverage ──
248
- if (req.method === 'GET' && url.pathname === '/api/coverage') {
311
+ if (req.method === "GET" && url.pathname === "/api/coverage") {
249
312
  const coverage = state.compilation?.coverage || {};
250
- res.writeHead(200, { 'Content-Type': 'application/json' });
313
+ res.writeHead(200, { "Content-Type": "application/json" });
251
314
  res.end(JSON.stringify(coverage));
252
315
  return;
253
316
  }
254
317
 
255
318
  // ── API: compilation ──
256
- if (req.method === 'GET' && url.pathname === '/api/compilation') {
257
- res.writeHead(200, { 'Content-Type': 'application/json' });
319
+ if (req.method === "GET" && url.pathname === "/api/compilation") {
320
+ res.writeHead(200, { "Content-Type": "application/json" });
258
321
  res.end(JSON.stringify(state.compilation));
259
322
  return;
260
323
  }
261
324
 
262
325
  // ── API: compile (trigger recompilation) ──
263
- if (req.method === 'POST' && url.pathname === '/api/compile') {
264
- const compileRoot = activeRoot === '__all' ? root : activeRoot;
326
+ if (req.method === "POST" && url.pathname === "/api/compile") {
327
+ const compileRoot = activeRoot === "__all" ? root : activeRoot;
265
328
  state.compilation = runCompile(compileRoot);
266
329
  refreshState(activeRoot, root);
267
- res.writeHead(200, { 'Content-Type': 'application/json' });
330
+ res.writeHead(200, { "Content-Type": "application/json" });
268
331
  res.end(JSON.stringify(state));
269
332
  return;
270
333
  }
271
334
 
272
335
  // ── API: switch sprint ──
273
- if (req.method === 'POST' && url.pathname === '/api/switch') {
274
- let body = '';
275
- req.on('data', chunk => body += chunk);
276
- req.on('end', () => {
336
+ if (req.method === "POST" && url.pathname === "/api/switch") {
337
+ let body = "";
338
+ req.on("data", (chunk) => (body += chunk));
339
+ req.on("end", () => {
277
340
  try {
278
341
  const { sprint } = JSON.parse(body);
279
- if (sprint === '__all') {
280
- activeRoot = '__all';
342
+ if (sprint === "__all") {
343
+ activeRoot = "__all";
281
344
  } else if (!sprint) {
282
345
  activeRoot = root;
283
346
  } else {
284
- const s = state.sprints.find(sp => sp.name === sprint);
347
+ const s = state.sprints.find((sp) => sp.name === sprint);
285
348
  if (s) {
286
349
  activeRoot = path.resolve(root, s.path);
287
350
  } else {
@@ -289,40 +352,50 @@ ${ROUTES.map(r => '<tr><td><code>'+r.method+'</code></td><td><code>'+r.path+'</c
289
352
  }
290
353
  }
291
354
  refreshState(activeRoot, root);
292
- res.writeHead(200, { 'Content-Type': 'application/json' });
355
+ res.writeHead(200, { "Content-Type": "application/json" });
293
356
  res.end(JSON.stringify(state));
294
357
  } catch {
295
- res.writeHead(400); res.end('bad request');
358
+ res.writeHead(400);
359
+ res.end("bad request");
296
360
  }
297
361
  });
298
362
  return;
299
363
  }
300
364
 
301
365
  // ── Static files ──
302
- let filePath = url.pathname === '/' ? '/index.html' : url.pathname;
303
- const resolved = path.resolve(PUBLIC_DIR, '.' + filePath);
366
+ let filePath = url.pathname === "/" ? "/index.html" : url.pathname;
367
+ const resolved = path.resolve(PUBLIC_DIR, "." + filePath);
304
368
  if (!resolved.startsWith(PUBLIC_DIR)) {
305
- res.writeHead(403); res.end('forbidden'); return;
369
+ res.writeHead(403);
370
+ res.end("forbidden");
371
+ return;
306
372
  }
307
373
 
308
374
  if (fs.existsSync(resolved) && fs.statSync(resolved).isFile()) {
309
375
  const ext = path.extname(resolved);
310
- res.writeHead(200, { 'Content-Type': MIME[ext] || 'application/octet-stream' });
376
+ res.writeHead(200, {
377
+ "Content-Type": MIME[ext] || "application/octet-stream",
378
+ });
311
379
  res.end(fs.readFileSync(resolved));
312
380
  return;
313
381
  }
314
382
 
315
- res.writeHead(404); res.end('not found');
383
+ res.writeHead(404);
384
+ res.end("not found");
316
385
  });
317
386
 
318
387
  // ── File watching ──
319
- const claimsPath = path.join(root, 'claims.json');
320
- const compilationPath = path.join(root, 'compilation.json');
388
+ const claimsPath = path.join(root, "claims.json");
389
+ const compilationPath = path.join(root, "compilation.json");
321
390
  if (fs.existsSync(claimsPath)) {
322
- fs.watchFile(claimsPath, { interval: 2000 }, () => refreshState(activeRoot, root));
391
+ fs.watchFile(claimsPath, { interval: 2000 }, () =>
392
+ refreshState(activeRoot, root)
393
+ );
323
394
  }
324
395
  if (fs.existsSync(compilationPath)) {
325
- fs.watchFile(compilationPath, { interval: 2000 }, () => refreshState(activeRoot, root));
396
+ fs.watchFile(compilationPath, { interval: 2000 }, () =>
397
+ refreshState(activeRoot, root)
398
+ );
326
399
  }
327
400
 
328
401
  // ── Start ──
@@ -331,16 +404,20 @@ ${ROUTES.map(r => '<tr><td><code>'+r.method+'</code></td><td><code>'+r.path+'</c
331
404
  // ── Graceful shutdown ──
332
405
  const shutdown = (signal) => {
333
406
  console.log(`\nwheat: ${signal} received, shutting down...`);
334
- for (const res of sseClients) { try { res.end(); } catch {} }
407
+ for (const res of sseClients) {
408
+ try {
409
+ res.end();
410
+ } catch {}
411
+ }
335
412
  sseClients.clear();
336
413
  server.close(() => process.exit(0));
337
414
  setTimeout(() => process.exit(1), 5000);
338
415
  };
339
- process.on('SIGTERM', () => shutdown('SIGTERM'));
340
- process.on('SIGINT', () => shutdown('SIGINT'));
416
+ process.on("SIGTERM", () => shutdown("SIGTERM"));
417
+ process.on("SIGINT", () => shutdown("SIGINT"));
341
418
 
342
- server.on('error', (err) => {
343
- if (err.code === 'EADDRINUSE') {
419
+ server.on("error", (err) => {
420
+ if (err.code === "EADDRINUSE") {
344
421
  console.error(`\nwheat: port ${port} is already in use.`);
345
422
  console.error(` Try: wheat serve --port ${Number(port) + 1}`);
346
423
  console.error(` Or stop the process using port ${port}.\n`);
@@ -349,14 +426,20 @@ ${ROUTES.map(r => '<tr><td><code>'+r.method+'</code></td><td><code>'+r.path+'</c
349
426
  throw err;
350
427
  });
351
428
 
352
- server.listen(port, '127.0.0.1', () => {
353
- vlog('listen', `port=${port}`, `root=${root}`);
429
+ server.listen(port, "127.0.0.1", () => {
430
+ vlog("listen", `port=${port}`, `root=${root}`);
354
431
  console.log(`wheat: serving on http://localhost:${port}`);
355
432
  console.log(` claims: ${state.claims.length} loaded`);
356
- console.log(` compilation: ${state.compilation ? state.compilation.status : 'not found'}`);
433
+ console.log(
434
+ ` compilation: ${
435
+ state.compilation ? state.compilation.status : "not found"
436
+ }`
437
+ );
357
438
  console.log(` sprints: ${state.sprints.length} detected`);
358
439
  if (state.activeSprint) {
359
- console.log(` active: ${state.activeSprint.name} (${state.activeSprint.phase})`);
440
+ console.log(
441
+ ` active: ${state.activeSprint.name} (${state.activeSprint.phase})`
442
+ );
360
443
  }
361
444
  console.log(` root: ${root}`);
362
445
  });
@@ -368,22 +451,26 @@ ${ROUTES.map(r => '<tr><td><code>'+r.method+'</code></td><td><code>'+r.path+'</c
368
451
 
369
452
  export async function run(targetDir, subArgs) {
370
453
  let port = 9092;
371
- const portIdx = subArgs.indexOf('--port');
454
+ const portIdx = subArgs.indexOf("--port");
372
455
  if (portIdx !== -1 && subArgs[portIdx + 1]) {
373
456
  port = parseInt(subArgs[portIdx + 1], 10);
374
457
  }
375
- const corsIdx = subArgs.indexOf('--cors');
376
- const corsOrigin = (corsIdx !== -1 && subArgs[corsIdx + 1]) ? subArgs[corsIdx + 1] : null;
458
+ const corsIdx = subArgs.indexOf("--cors");
459
+ const corsOrigin =
460
+ corsIdx !== -1 && subArgs[corsIdx + 1] ? subArgs[corsIdx + 1] : null;
377
461
 
378
462
  // Walk up to find project root if no claims.json in targetDir
379
463
  let root = targetDir;
380
- if (!fs.existsSync(path.join(root, 'claims.json'))) {
464
+ if (!fs.existsSync(path.join(root, "claims.json"))) {
381
465
  let dir = path.resolve(root);
382
466
  for (let i = 0; i < 5; i++) {
383
467
  const parent = path.dirname(dir);
384
468
  if (parent === dir) break;
385
469
  dir = parent;
386
- if (fs.existsSync(path.join(dir, 'claims.json'))) { root = dir; break; }
470
+ if (fs.existsSync(path.join(dir, "claims.json"))) {
471
+ root = dir;
472
+ break;
473
+ }
387
474
  }
388
475
  }
389
476