@stcrft/statecraft 1.3.0 → 1.4.0
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 +4 -0
- package/dist/index.js +103 -13
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -21,6 +21,10 @@ Then run `statecraft <command>`. Or use `npx @stcrft/statecraft init` (etc.) wit
|
|
|
21
21
|
| **`statecraft summarize [path]`** | Print a short text summary of the board (name, columns, tasks, WIP/blocked). |
|
|
22
22
|
| **`statecraft render [path]`** | Serve the board in the browser (read-only UI). Options: `--port 3000` (default), `--open` (open browser). Starts a local server with `GET /api/board` and WebSocket for live updates when the file changes. |
|
|
23
23
|
|
|
24
|
+
## Upgrade notification
|
|
25
|
+
|
|
26
|
+
After each command, the CLI may check npm for a newer version and print a one-line message to stderr if an upgrade is available. To disable: set `STATECRAFT_NO_UPDATE_CHECK=1` or `CI=true`. When running from the repo (e.g. `pnpm cli validate`), the check is skipped. A cache in the system temp directory is used when writable (writes are best-effort and do not fail the command).
|
|
27
|
+
|
|
24
28
|
## Path handling
|
|
25
29
|
|
|
26
30
|
- **Single path only.** One board file per run. Passing multiple paths is not supported and will exit with an error.
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { createRequire } from "module";
|
|
5
|
+
import path6 from "path";
|
|
6
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
5
7
|
import { Command } from "commander";
|
|
6
8
|
|
|
7
9
|
// src/constants.ts
|
|
@@ -435,9 +437,9 @@ function startRenderServer(options) {
|
|
|
435
437
|
}
|
|
436
438
|
|
|
437
439
|
// src/executors/render.ts
|
|
438
|
-
function runRender(
|
|
440
|
+
function runRender(path7, options) {
|
|
439
441
|
startRenderServer({
|
|
440
|
-
boardPath:
|
|
442
|
+
boardPath: path7,
|
|
441
443
|
port: options.port,
|
|
442
444
|
openBrowser: options.open
|
|
443
445
|
});
|
|
@@ -494,9 +496,9 @@ function runSpec() {
|
|
|
494
496
|
|
|
495
497
|
// src/executors/summarize.ts
|
|
496
498
|
import { parseBoard, summarize } from "@stcrft/statecraft-core";
|
|
497
|
-
function runSummarize(
|
|
499
|
+
function runSummarize(path7) {
|
|
498
500
|
try {
|
|
499
|
-
const board = parseBoard(
|
|
501
|
+
const board = parseBoard(path7);
|
|
500
502
|
const summary = summarize(board);
|
|
501
503
|
process.stdout.write(summary);
|
|
502
504
|
process.exitCode = 0;
|
|
@@ -513,9 +515,9 @@ function formatValidationError(err) {
|
|
|
513
515
|
const prefix = err.path != null && err.path !== "" ? `${err.path}: ` : "";
|
|
514
516
|
return `${prefix}${err.message}`;
|
|
515
517
|
}
|
|
516
|
-
function runValidate(
|
|
518
|
+
function runValidate(path7) {
|
|
517
519
|
try {
|
|
518
|
-
const board = parseBoard2(
|
|
520
|
+
const board = parseBoard2(path7);
|
|
519
521
|
const result = validate(board);
|
|
520
522
|
if (!result.valid) {
|
|
521
523
|
for (const err of result.errors) {
|
|
@@ -532,9 +534,95 @@ function runValidate(path5) {
|
|
|
532
534
|
}
|
|
533
535
|
}
|
|
534
536
|
|
|
537
|
+
// src/update-check.ts
|
|
538
|
+
import fs5 from "fs";
|
|
539
|
+
import os from "os";
|
|
540
|
+
import path5 from "path";
|
|
541
|
+
import semver from "semver";
|
|
542
|
+
var REGISTRY_URL = "https://registry.npmjs.org/@stcrft/statecraft/latest";
|
|
543
|
+
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
544
|
+
var FETCH_TIMEOUT_MS = 3e3;
|
|
545
|
+
var CACHE_FILENAME = "statecraft-update-check.json";
|
|
546
|
+
function getCachePath() {
|
|
547
|
+
const tmp = os.tmpdir();
|
|
548
|
+
return path5.join(tmp, CACHE_FILENAME);
|
|
549
|
+
}
|
|
550
|
+
function readCache() {
|
|
551
|
+
try {
|
|
552
|
+
const p = getCachePath();
|
|
553
|
+
const raw = fs5.readFileSync(p, "utf-8");
|
|
554
|
+
const data = JSON.parse(raw);
|
|
555
|
+
if (typeof data.lastCheck === "number" && typeof data.latestVersion === "string") {
|
|
556
|
+
return data;
|
|
557
|
+
}
|
|
558
|
+
} catch {
|
|
559
|
+
}
|
|
560
|
+
return null;
|
|
561
|
+
}
|
|
562
|
+
function writeCache(latestVersion) {
|
|
563
|
+
try {
|
|
564
|
+
const p = getCachePath();
|
|
565
|
+
const data = { lastCheck: Date.now(), latestVersion };
|
|
566
|
+
fs5.writeFileSync(p, JSON.stringify(data), "utf-8");
|
|
567
|
+
} catch {
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
async function fetchLatestVersion() {
|
|
571
|
+
const controller = new AbortController();
|
|
572
|
+
const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
573
|
+
try {
|
|
574
|
+
const res = await fetch(REGISTRY_URL, { signal: controller.signal });
|
|
575
|
+
clearTimeout(timeout);
|
|
576
|
+
if (!res.ok) return null;
|
|
577
|
+
const json = await res.json();
|
|
578
|
+
return typeof json.version === "string" ? json.version : null;
|
|
579
|
+
} catch {
|
|
580
|
+
clearTimeout(timeout);
|
|
581
|
+
return null;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
function shouldSkip(options) {
|
|
585
|
+
if (process.env.CI === "true" || process.env.CI === "1") return true;
|
|
586
|
+
if (process.env.STATECRAFT_NO_UPDATE_CHECK === "1" || process.env.STATECRAFT_NO_UPDATE_CHECK === "true") return true;
|
|
587
|
+
if (options.isLocalDev) return true;
|
|
588
|
+
return false;
|
|
589
|
+
}
|
|
590
|
+
function runUpdateCheck(currentVersion, options) {
|
|
591
|
+
if (shouldSkip(options)) return;
|
|
592
|
+
void (async () => {
|
|
593
|
+
let latest = null;
|
|
594
|
+
const cached = readCache();
|
|
595
|
+
if (cached && Date.now() - cached.lastCheck < CACHE_TTL_MS) {
|
|
596
|
+
latest = cached.latestVersion;
|
|
597
|
+
}
|
|
598
|
+
if (latest === null) {
|
|
599
|
+
latest = await fetchLatestVersion();
|
|
600
|
+
if (latest !== null) writeCache(latest);
|
|
601
|
+
}
|
|
602
|
+
if (latest === null) return;
|
|
603
|
+
try {
|
|
604
|
+
if (semver.gt(latest, currentVersion)) {
|
|
605
|
+
process.stderr.write(
|
|
606
|
+
`A new version of Statecraft is available: ${latest} (you have ${currentVersion}). Upgrade: npm update -g @stcrft/statecraft
|
|
607
|
+
`
|
|
608
|
+
);
|
|
609
|
+
}
|
|
610
|
+
} catch {
|
|
611
|
+
}
|
|
612
|
+
})();
|
|
613
|
+
}
|
|
614
|
+
|
|
535
615
|
// src/index.ts
|
|
536
616
|
var require2 = createRequire(import.meta.url);
|
|
537
617
|
var { version } = require2("../package.json");
|
|
618
|
+
function isLocalDev() {
|
|
619
|
+
try {
|
|
620
|
+
const dir = path6.dirname(fileURLToPath3(import.meta.url));
|
|
621
|
+
return /packages[/\\]cli[/\\]/.test(dir) || dir.includes("statecraft" + path6.sep + "packages" + path6.sep + "cli");
|
|
622
|
+
} catch {
|
|
623
|
+
return false;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
538
626
|
function rejectMultiplePaths(extra) {
|
|
539
627
|
if (extra.length > 0) {
|
|
540
628
|
process.stderr.write("Only one board file per run. Multiple paths are not supported.\n");
|
|
@@ -552,17 +640,19 @@ program.command("init").description("Interactive setup: create board and configu
|
|
|
552
640
|
program.command("spec").description("Print the board format spec (for AI agents)").action(() => {
|
|
553
641
|
runSpec();
|
|
554
642
|
});
|
|
555
|
-
program.command("validate").description("Validate a board file (exit 0 if valid, 1 on errors)").argument("[path]", "path to board file", DEFAULT_BOARD_PATH).argument("[extra...]", "ignored (only one path allowed)").action((
|
|
643
|
+
program.command("validate").description("Validate a board file (exit 0 if valid, 1 on errors)").argument("[path]", "path to board file", DEFAULT_BOARD_PATH).argument("[extra...]", "ignored (only one path allowed)").action((path7, extra) => {
|
|
556
644
|
rejectMultiplePaths(extra);
|
|
557
|
-
if (process.exitCode !== 1) runValidate(
|
|
645
|
+
if (process.exitCode !== 1) runValidate(path7);
|
|
558
646
|
});
|
|
559
|
-
program.command("summarize").description("Print a short text summary of the board").argument("[path]", "path to board file", DEFAULT_BOARD_PATH).argument("[extra...]", "ignored (only one path allowed)").action((
|
|
647
|
+
program.command("summarize").description("Print a short text summary of the board").argument("[path]", "path to board file", DEFAULT_BOARD_PATH).argument("[extra...]", "ignored (only one path allowed)").action((path7, extra) => {
|
|
560
648
|
rejectMultiplePaths(extra);
|
|
561
|
-
if (process.exitCode !== 1) runSummarize(
|
|
649
|
+
if (process.exitCode !== 1) runSummarize(path7);
|
|
562
650
|
});
|
|
563
|
-
program.command("render").description("Serve the board in the browser (read-only UI)").argument("[path]", "path to board file", DEFAULT_BOARD_PATH).option("-p, --port <number>", "port for the server", String(DEFAULT_RENDER_PORT)).option("--open", "open browser after starting server").action((
|
|
651
|
+
program.command("render").description("Serve the board in the browser (read-only UI)").argument("[path]", "path to board file", DEFAULT_BOARD_PATH).option("-p, --port <number>", "port for the server", String(DEFAULT_RENDER_PORT)).option("--open", "open browser after starting server").action((path7, options) => {
|
|
564
652
|
const port = parseInt(options.port, 10) || DEFAULT_RENDER_PORT;
|
|
565
|
-
runRender(
|
|
653
|
+
runRender(path7, { port, open: options.open ?? false });
|
|
654
|
+
});
|
|
655
|
+
program.parseAsync().then(() => {
|
|
656
|
+
runUpdateCheck(version, { isLocalDev: isLocalDev() });
|
|
566
657
|
});
|
|
567
|
-
program.parseAsync();
|
|
568
658
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/executors/init.ts","../src/executors/rule-content.ts","../src/render-server.ts","../src/executors/render.ts","../src/executors/spec.ts","../src/utils.ts","../src/executors/summarize.ts","../src/executors/validate.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { createRequire } from \"node:module\";\nimport { Command } from \"commander\";\nimport { DEFAULT_BOARD_PATH, DEFAULT_RENDER_PORT } from \"./constants.js\";\nimport { runInit, runRender, runSpec, runSummarize, runValidate } from \"./executors/index.js\";\n\nconst require = createRequire(import.meta.url);\nconst { version } = require(\"../package.json\") as { version: string };\n\nfunction rejectMultiplePaths(extra: string[]): void {\n if (extra.length > 0) {\n process.stderr.write(\"Only one board file per run. Multiple paths are not supported.\\n\");\n process.exitCode = 1;\n }\n}\n\nconst program = new Command();\n\nprogram\n .name(\"statecraft\")\n .description(\"Validate, summarize, and render Statecraft board files\")\n .version(version);\n\nprogram\n .command(\"init\")\n .description(\"Interactive setup: create board and configure Statecraft for your workflow\")\n .action(async () => {\n await runInit().catch((err) => {\n process.stderr.write(err instanceof Error ? err.message : String(err) + \"\\n\");\n process.exitCode = 1;\n });\n });\n\nprogram\n .command(\"spec\")\n .description(\"Print the board format spec (for AI agents)\")\n .action(() => {\n runSpec();\n });\n\nprogram\n .command(\"validate\")\n .description(\"Validate a board file (exit 0 if valid, 1 on errors)\")\n .argument(\"[path]\", \"path to board file\", DEFAULT_BOARD_PATH)\n .argument(\"[extra...]\", \"ignored (only one path allowed)\")\n .action((path: string, extra: string[]) => {\n rejectMultiplePaths(extra);\n if (process.exitCode !== 1) runValidate(path);\n });\n\nprogram\n .command(\"summarize\")\n .description(\"Print a short text summary of the board\")\n .argument(\"[path]\", \"path to board file\", DEFAULT_BOARD_PATH)\n .argument(\"[extra...]\", \"ignored (only one path allowed)\")\n .action((path: string, extra: string[]) => {\n rejectMultiplePaths(extra);\n if (process.exitCode !== 1) runSummarize(path);\n });\n\nprogram\n .command(\"render\")\n .description(\"Serve the board in the browser (read-only UI)\")\n .argument(\"[path]\", \"path to board file\", DEFAULT_BOARD_PATH)\n .option(\"-p, --port <number>\", \"port for the server\", String(DEFAULT_RENDER_PORT))\n .option(\"--open\", \"open browser after starting server\")\n .action((path: string, options: { port: string; open: boolean }) => {\n const port = parseInt(options.port, 10) || DEFAULT_RENDER_PORT;\n runRender(path, { port, open: options.open ?? false });\n });\n\nprogram.parseAsync();\n","/** Default path to the board file when none is given (validate, summarize, render). */\nexport const DEFAULT_BOARD_PATH = \"./board.yaml\";\n\n/** Default port for the render server. */\nexport const DEFAULT_RENDER_PORT = 3000;\n\n/** Debounce delay (ms) for file watcher before broadcasting board updates to WebSocket clients. */\nexport const RENDER_WATCH_DEBOUNCE_MS = 100;\n\n/** Default board file path offered by init (cwd). */\nexport const INIT_DEFAULT_BOARD_PATH = \"board.yaml\";\n\n/** Default directory for task .md files (relative to board), offered by init. */\nexport const INIT_DEFAULT_TASKS_DIR = \"tasks\";\n\n/** Canonical column set per spec; init creates boards with these columns. */\nexport const CANONICAL_COLUMNS = [\"Backlog\", \"Ready\", \"In Progress\", \"Done\"] as const;\n\n/** Filename of the board format spec shipped with the CLI package (statecraft spec). */\nexport const SPEC_FILENAME = \"spec.md\";\n\n/** Init default: enforce \"create task before work\" workflow for all agents. */\nexport const INIT_STRICT_MODE_DEFAULT = true;\n\n/** Init default: require each task to have a spec .md file in the tasks directory. */\nexport const INIT_REQUIRE_SPEC_FILE_DEFAULT = true;\n\n/** Init default: include task spec .md format guidelines in generated rules. */\nexport const INIT_INCLUDE_TASK_SPEC_FORMAT_DEFAULT = true;\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport readline from \"node:readline\";\nimport { stringify } from \"yaml\";\nimport {\n CANONICAL_COLUMNS,\n INIT_DEFAULT_BOARD_PATH,\n INIT_DEFAULT_TASKS_DIR,\n INIT_INCLUDE_TASK_SPEC_FORMAT_DEFAULT,\n INIT_REQUIRE_SPEC_FILE_DEFAULT,\n INIT_STRICT_MODE_DEFAULT,\n} from \"../constants.js\";\nimport { buildStatecraftRuleBody, type RuleOptions } from \"./rule-content.js\";\n\nexport interface InitAnswers {\n boardName: string;\n columns: Array<{ name: string; limit?: number }>;\n boardPath: string;\n tasksDir: string;\n}\n\n/** Raw answers collected from prompts (before parsing WIP or booleans). */\ninterface CollectedInitAnswers {\n boardName: string;\n boardPath: string;\n tasksDir: string;\n wipStr: string;\n strictMode: boolean;\n requireSpecFile: boolean;\n includeTaskSpecFormat: boolean;\n generateCursorRule: boolean;\n generateClaudeRule: boolean;\n generateCodexAgents: boolean;\n}\n\nfunction question(rl: readline.Interface, prompt: string, defaultValue?: string): Promise<string> {\n const suffix = defaultValue !== undefined ? ` (default: ${defaultValue})` : \"\";\n return new Promise((resolve) => {\n rl.question(`${prompt}${suffix}: `, (answer) => {\n const trimmed = answer.trim();\n resolve(trimmed !== \"\" ? trimmed : (defaultValue ?? \"\"));\n });\n });\n}\n\nasync function getAnswer(\n rl: readline.Interface | null,\n answers: string[] | undefined,\n index: { current: number },\n prompt: string,\n defaultValue?: string\n): Promise<string> {\n if (answers !== undefined) {\n const raw = answers[index.current++] ?? \"\";\n const trimmed = raw.trim();\n return trimmed !== \"\" ? trimmed : (defaultValue ?? \"\");\n }\n return question(rl!, prompt, defaultValue);\n}\n\nfunction parseWipLimit(wipStr: string): number | undefined {\n if (wipStr === \"\") return undefined;\n const n = parseInt(wipStr, 10);\n return Number.isInteger(n) && n >= 1 ? n : undefined;\n}\n\n/** Collect all init prompts into a structured object. */\nasync function collectInitAnswers(\n rl: readline.Interface | null,\n answers: string[] | undefined,\n index: { current: number }\n): Promise<CollectedInitAnswers> {\n const boardName = await getAnswer(rl, answers, index, \"Board name\");\n const boardPath = await getAnswer(rl, answers, index, \"Path for board file\", INIT_DEFAULT_BOARD_PATH);\n const tasksDir = await getAnswer(\n rl,\n answers,\n index,\n \"Directory for task .md files (relative to board)\",\n INIT_DEFAULT_TASKS_DIR\n );\n const wipStr = await getAnswer(\n rl,\n answers,\n index,\n \"WIP limit for In Progress (optional, press Enter to skip)\",\n \"\"\n );\n const strictModeStr = await getAnswer(\n rl,\n answers,\n index,\n \"Enforce Statecraft workflow (create task before any work)? (Y/n)\",\n INIT_STRICT_MODE_DEFAULT ? \"Y\" : \"n\"\n );\n const requireSpecStr = await getAnswer(\n rl,\n answers,\n index,\n \"Require each task to have a spec .md file? (Y/n)\",\n INIT_REQUIRE_SPEC_FILE_DEFAULT ? \"Y\" : \"n\"\n );\n const includeFormatStr = await getAnswer(\n rl,\n answers,\n index,\n \"Include task spec .md format guidelines in rules? (Y/n)\",\n INIT_INCLUDE_TASK_SPEC_FORMAT_DEFAULT ? \"Y\" : \"n\"\n );\n const genCursor = await getAnswer(rl, answers, index, \"Generate Cursor rule? (Y/n)\", \"Y\");\n const genClaude = await getAnswer(rl, answers, index, \"Generate Claude Code rule? (y/n)\", \"n\");\n const genCodex = await getAnswer(rl, answers, index, \"Generate Codex instructions (AGENTS.md)? (y/n)\", \"n\");\n\n return {\n boardName,\n boardPath,\n tasksDir,\n wipStr,\n strictMode: /^y(es)?$/i.test(strictModeStr.trim()),\n requireSpecFile: /^y(es)?$/i.test(requireSpecStr.trim()),\n includeTaskSpecFormat: /^y(es)?$/i.test(includeFormatStr.trim()),\n generateCursorRule: /^y(es)?$/i.test(genCursor.trim()),\n generateClaudeRule: /^y(es)?$/i.test(genClaude.trim()),\n generateCodexAgents: /^y(es)?$/i.test(genCodex.trim()),\n };\n}\n\n/** Build the board object (name, columns, empty tasks) from collected answers. */\nfunction buildBoardFromAnswers(\n boardName: string,\n tasksDir: string,\n wipStr: string\n): { board: string; columns: Array<string | { name: string; limit: number }>; tasks: Record<string, never> } {\n const inProgressLimit = parseWipLimit(wipStr);\n const columns: Array<string | { name: string; limit: number }> = [\n CANONICAL_COLUMNS[0],\n CANONICAL_COLUMNS[1],\n inProgressLimit != null ? { name: CANONICAL_COLUMNS[2], limit: inProgressLimit } : CANONICAL_COLUMNS[2],\n CANONICAL_COLUMNS[3],\n ];\n return {\n board: boardName,\n columns,\n tasks: {},\n };\n}\n\n/** Ensure parent directory exists and write file. */\nfunction ensureDirAndWrite(filePath: string, content: string): void {\n const dir = path.dirname(filePath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n fs.writeFileSync(filePath, content, \"utf-8\");\n}\n\n/** Write board YAML to cwd-relative path. */\nfunction writeBoardFile(\n cwd: string,\n boardPath: string,\n board: { board: string; columns: unknown[]; tasks: object }\n): void {\n const absolutePath = path.resolve(cwd, boardPath);\n const yamlContent = stringify(board, { lineWidth: 0 });\n ensureDirAndWrite(absolutePath, yamlContent);\n}\n\n/** Write a rule file and optionally log. */\nfunction writeRuleFile(\n filePath: string,\n content: string,\n log: (msg: string) => void\n): void {\n ensureDirAndWrite(filePath, content);\n log(filePath);\n}\n\nconst CODECX_MARKER = \"## Statecraft (generated by statecraft init)\";\n\nfunction writeCursorRule(\n cwd: string,\n boardPath: string,\n specDir: string,\n options: Partial<RuleOptions>,\n log: (msg: string) => void\n): void {\n const cursorRulesDir = path.resolve(cwd, \".cursor\", \"rules\");\n const cursorRulePath = path.join(cursorRulesDir, \"statecraft.mdc\");\n const content = buildCursorRuleContent(boardPath, specDir, options);\n writeRuleFile(cursorRulePath, content, (p) => log(`Wrote Cursor rule to ${p}`));\n}\n\nfunction writeClaudeRule(\n cwd: string,\n boardPath: string,\n specDir: string,\n options: Partial<RuleOptions>,\n log: (msg: string) => void\n): void {\n const claudeRulesDir = path.resolve(cwd, \".claude\", \"rules\");\n const claudeRulePath = path.join(claudeRulesDir, \"statecraft.md\");\n const content = buildClaudeRuleContent(boardPath, specDir, options);\n writeRuleFile(claudeRulePath, content, (p) => log(`Wrote Claude Code rule to ${p}`));\n}\n\nfunction writeCodexRule(\n cwd: string,\n boardPath: string,\n specDir: string,\n options: Partial<RuleOptions>,\n log: (msg: string) => void\n): void {\n const codexAgentsPath = path.resolve(cwd, \"AGENTS.md\");\n const codexContent = buildCodexAgentsContent(boardPath, specDir, options);\n if (fs.existsSync(codexAgentsPath)) {\n const existing = fs.readFileSync(codexAgentsPath, \"utf-8\");\n if (existing.includes(CODECX_MARKER)) {\n log(\"AGENTS.md already contains Statecraft section; skipped.\");\n return;\n }\n fs.writeFileSync(codexAgentsPath, existing.trimEnd() + \"\\n\\n\" + codexContent + \"\\n\", \"utf-8\");\n log(`Appended Statecraft section to ${codexAgentsPath}`);\n } else {\n fs.writeFileSync(codexAgentsPath, codexContent + \"\\n\", \"utf-8\");\n log(`Wrote Codex instructions to ${codexAgentsPath}`);\n }\n}\n\n// --- Public rule content builders (used by init and by tests) ---\n\nexport function buildCursorRuleContent(\n boardPath: string,\n tasksDir: string,\n options?: Partial<RuleOptions>\n): string {\n return `---\ndescription: Statecraft board and task workflow; when to update the board and how to create tasks\nalwaysApply: true\n---\n${buildStatecraftRuleBody(boardPath, tasksDir, options)}`;\n}\n\n/** Claude Code: modular rule in .claude/rules/ (markdown, no frontmatter). */\nexport function buildClaudeRuleContent(\n boardPath: string,\n tasksDir: string,\n options?: Partial<RuleOptions>\n): string {\n return buildStatecraftRuleBody(boardPath, tasksDir, options);\n}\n\n/** Codex: section for AGENTS.md at project root. */\nexport function buildCodexAgentsContent(\n boardPath: string,\n tasksDir: string,\n options?: Partial<RuleOptions>\n): string {\n return `## Statecraft (generated by statecraft init)\n\n${buildStatecraftRuleBody(boardPath, tasksDir, options)}`;\n}\n\n// --- runInit ---\n\nexport interface RunInitOptions {\n /** For tests: pre-filled answers (board name, board path, tasks dir, WIP, strict mode Y/n, require spec Y/n, task spec format Y/n, Cursor y/n, Claude y/n, Codex y/n). */\n answers?: string[];\n /** For tests: working directory for writing board and rule files (default: process.cwd()). */\n cwd?: string;\n}\n\nexport async function runInit(options?: RunInitOptions): Promise<void> {\n const answers = options?.answers;\n const cwd = options?.cwd ?? process.cwd();\n const answerIndex = { current: 0 };\n const rl = answers === undefined ? readline.createInterface({ input: process.stdin, output: process.stdout }) : null;\n const log = (msg: string) => {\n if (rl) process.stdout.write(msg + \"\\n\");\n };\n\n try {\n if (rl) {\n process.stdout.write(\"\\nStatecraft init — create your board and connect it to your workflow.\\n\\n\");\n }\n\n const collected = await collectInitAnswers(rl, answers, answerIndex);\n if (!collected.boardName) {\n process.stderr.write(\"Board name is required.\\n\");\n process.exitCode = 1;\n return;\n }\n\n rl?.close();\n\n const board = buildBoardFromAnswers(collected.boardName, collected.tasksDir, collected.wipStr);\n writeBoardFile(cwd, collected.boardPath, board);\n\n const absolutePath = path.resolve(cwd, collected.boardPath);\n log(`\\nCreated board at ${absolutePath}`);\n log(`Task spec files: ${path.join(path.dirname(collected.boardPath), collected.tasksDir)}/<task-id>.md`);\n\n const specDir = path.join(path.dirname(collected.boardPath), collected.tasksDir);\n const ruleOptions: Partial<RuleOptions> = {\n strictMode: collected.strictMode,\n requireSpecFile: collected.requireSpecFile,\n includeTaskSpecFormat: collected.includeTaskSpecFormat,\n };\n\n if (collected.generateCursorRule) writeCursorRule(cwd, collected.boardPath, specDir, ruleOptions, log);\n if (collected.generateClaudeRule) writeClaudeRule(cwd, collected.boardPath, specDir, ruleOptions, log);\n if (collected.generateCodexAgents) writeCodexRule(cwd, collected.boardPath, specDir, ruleOptions, log);\n\n log(\"\\nRun `statecraft validate \" + collected.boardPath + \"` to validate, or `statecraft render \" + collected.boardPath + \"` to view.\");\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(message + \"\\n\");\n process.exitCode = 1;\n } finally {\n rl?.close();\n }\n}\n","import {\n INIT_INCLUDE_TASK_SPEC_FORMAT_DEFAULT,\n INIT_REQUIRE_SPEC_FILE_DEFAULT,\n INIT_STRICT_MODE_DEFAULT,\n} from \"../constants.js\";\n\n/**\n * Options for generated rule content. Used by init; defaults match init prompts (Y/n).\n */\nexport interface RuleOptions {\n /** Enforce \"create task before any work\" workflow (strict mode). Default true. */\n strictMode: boolean;\n /** Require each task to have a spec .md file. Default true. */\n requireSpecFile: boolean;\n /** Include task spec .md format guidelines in the rule. Default true. */\n includeTaskSpecFormat: boolean;\n}\n\nconst DEFAULT_RULE_OPTIONS: RuleOptions = {\n strictMode: INIT_STRICT_MODE_DEFAULT,\n requireSpecFile: INIT_REQUIRE_SPEC_FILE_DEFAULT,\n includeTaskSpecFormat: INIT_INCLUDE_TASK_SPEC_FORMAT_DEFAULT,\n};\n\n/**\n * Shared content for generated AI rules (Cursor, Claude Code, Codex).\n * Single source for the Statecraft rule body so init only orchestrates.\n */\nexport function buildStatecraftRuleBody(\n boardPath: string,\n tasksDir: string,\n options: Partial<RuleOptions> = {}\n): string {\n const opts = { ...DEFAULT_RULE_OPTIONS, ...options };\n\n const strictBlock =\n opts.strictMode &&\n `## ⚠️ REQUIRED BEFORE ANY OTHER ACTION (100% enforced)\n\n**Before** doing any substantive work (coding, fixing, refactoring, or answering the user's request with implementation), you **MUST**:\n\n1. **Ensure a task exists:** In \\`${boardPath}\\`, either **create** a new task under \\`tasks:\\` with \\`status: Backlog\\` (use a kebab-case id, e.g. \\`fix-header-import\\`), or use an existing task. New tasks start in **Backlog** (vague is OK).\n2. ${opts.requireSpecFile ? `**Spec file:** Create or update \\`${tasksDir}/<task-id>.md\\` (required for each task).` : `**Spec file:** Optionally create or update \\`${tasksDir}/<task-id>.md\\` with description and acceptance criteria.`}\n3. **Flow:** Do **not** put the task in In Progress until it has been refined. After discovery/planning, update the task's spec (description, acceptance criteria); when you know what to do and the spec is clear, set \\`status\\` to **Ready**. Only when you are **actually starting the work** (e.g. writing code), set \\`status\\` to **In Progress**. So: Backlog → Ready (after refinement) → In Progress (when working) → Done.\n\nYou **must not** start implementation (no code, no concrete changes) until the task is at least in **Ready** and you are about to work on it; then move to **In Progress**. If \\`${boardPath}\\` does not exist yet, create a minimal valid board file (board name, columns: Backlog, Ready, In Progress, Done, tasks: {}) and the task in Backlog before proceeding.\n\n---\n`;\n\n const taskSpecFormatSection = opts.includeTaskSpecFormat\n ? `\n## Task spec file format (\\`${tasksDir}/<task-id>.md\\`)\n\nFollow this structure so task specs are consistent and machine-friendly:\n\n- **Title:** First line or \\`# <Task title>\\` (match the board \\`title\\` or expand it).\n- **Description:** Short context or problem statement (optional \\`## Description\\`).\n- **Acceptance criteria / Definition of Done:** \\`## Acceptance criteria\\` or \\`## Definition of Done\\` with a checklist (e.g. \\`- [ ] item\\`) so completion is unambiguous. All items must be checked before moving the task to Done.\n- **Notes / Dependencies:** Optional \\`## Notes\\`, \\`## Dependencies\\` (for human context; \\`depends_on\\` in the board is the source of truth for task ordering).\n\nKeep each file focused on one task; use the task id in the filename (e.g. \\`fix-auth-timeout.md\\`).\n\n---\n`\n : \"\";\n\n const requireSpecBullet = opts.requireSpecFile\n ? `- **Spec required:** Every task must have a \\`spec\\` field pointing to \\`${tasksDir}/<task-id>.md\\`. Create the .md file when creating the task.\n`\n : \"\";\n\n return `# Statecraft\n${strictBlock || \"\"}\nThis project uses Statecraft for the task board.\n\n- **Board file:** \\`${boardPath}\\`\n- **Task spec files:** \\`${tasksDir}/<task-id>.md\\` (relative to board directory)\n- **Columns (canonical):** Backlog → Ready → In Progress → Done.\n\n## Commands\n\nUse \\`statecraft\\` if installed globally. If not in PATH, use \\`npx statecraft\\` (npm), \\`pnpm dlx statecraft\\` (pnpm), or \\`yarn dlx statecraft\\` (yarn)—e.g. \\`npx statecraft validate ${boardPath}\\`.\n\n- Get board format spec: \\`statecraft spec\\`\n- Validate board: \\`statecraft validate ${boardPath}\\`\n- View board in browser: \\`statecraft render ${boardPath}\\`\n\n## Task lifecycle (edit board and task files directly)\n\n**Flow:** Backlog → Ready → In Progress → Done. Do not skip Ready.\n\n- **Create task:** Add an entry under \\`tasks\\` with \\`status: Backlog\\` (id, title, optional description, spec, owner, priority, depends_on). ${opts.requireSpecFile ? `Create \\`${tasksDir}/<task-id>.md\\` for each task (required).` : `If needed, create \\`${tasksDir}/<task-id>.md\\` with description and DoD.`} Backlog = initial or vague; refinement comes next.\n- **Refine and move to Ready:** After discovery or planning, update the task's spec file (description, acceptance criteria). When the definition is clear and you know how to solve it, set \\`status\\` to **Ready**. Ready = \"ready to be worked on.\"\n- **Start work:** Only when you are about to do the actual work (code, fix, etc.), set the task's \\`status\\` to **In Progress**. Read the task's \\`spec\\` file if needed.\n- **Finish work:** Set the task's \\`status\\` to **Done** only when the task's acceptance criteria (in its spec file) are satisfied.\n${taskSpecFormatSection}\n## AI guidelines for creating tickets\n\n- **Keep the board in sync:** When doing substantive work, create a new task in **Backlog** (or use an existing one). After discovery/refinement, update the task spec and move to **Ready**; only when actually working move to **In Progress**, then **Done** when criteria are met. Do not skip Ready.\n${requireSpecBullet}- **Task naming:** kebab-case, verb or noun phrase (e.g. \\`fix-auth-timeout\\`).\n- **Description:** One line summary; optional markdown for context.\n- **Definition of Done:** Acceptance criteria in task spec; all checked before moving to Done.\n- **Task fields (from spec):** \\`title\\` (required), \\`status\\` (required), optional \\`description\\`, \\`spec\\` (path to .md), \\`owner\\`, \\`priority\\`, \\`depends_on\\`.\n- **Spec file:** Path relative to board directory, e.g. \\`${tasksDir}/<task-id>.md\\`.\n`;\n}\n","import express, { type Request, type Response } from \"express\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { createServer } from \"node:http\";\nimport { WebSocket, WebSocketServer } from \"ws\";\nimport { fileURLToPath } from \"node:url\";\nimport { DEFAULT_RENDER_PORT, RENDER_WATCH_DEBOUNCE_MS } from \"./constants.js\";\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\n/**\n * Resolve path to the renderer's static files.\n * - When installed from npm: bundled at package-root/renderer-dist.\n * - When running in monorepo: sibling package at packages/renderer/dist.\n */\nfunction getRendererDistPath(): string {\n const bundled = path.resolve(__dirname, \"..\", \"renderer-dist\");\n if (fs.existsSync(bundled) && fs.statSync(bundled).isDirectory()) {\n return bundled;\n }\n return path.resolve(__dirname, \"..\", \"..\", \"renderer\", \"dist\");\n}\n\n/**\n * Read board file and return content as UTF-8. Returns null on error.\n */\nfunction readBoardContent(boardPath: string): string | null {\n try {\n const resolved = path.resolve(process.cwd(), boardPath);\n return fs.readFileSync(resolved, \"utf-8\");\n } catch {\n return null;\n }\n}\n\nexport interface RenderServerOptions {\n boardPath: string;\n port?: number;\n openBrowser?: boolean;\n}\n\n/**\n * Start the render server: static app + GET /api/board + WebSocket /api/board/watch.\n * Watches the board file and broadcasts content to WS clients on change.\n */\nexport function startRenderServer(options: RenderServerOptions): void {\n const { boardPath, port = DEFAULT_RENDER_PORT, openBrowser = false } = options;\n const rendererDist = getRendererDistPath();\n\n if (!fs.existsSync(rendererDist) || !fs.statSync(rendererDist).isDirectory()) {\n process.stderr.write(\n \"Renderer build not found. Run: pnpm build\\n\"\n );\n process.exitCode = 1;\n return;\n }\n\n const app = express();\n\n // Board file directory (for resolving spec paths relative to board)\n const resolvedBoardPath = path.resolve(process.cwd(), boardPath);\n const boardDir = path.dirname(resolvedBoardPath);\n\n // API: board file content (raw YAML)\n app.get(\"/api/board\", (_req: Request, res: Response) => {\n const content = readBoardContent(boardPath);\n if (content === null) {\n res.status(404).type(\"text/plain\").send(\"Board file not found or unreadable.\");\n return;\n }\n res.type(\"text/yaml\").send(content);\n });\n\n // API: spec file content (path relative to board file directory; only .md files)\n app.get(\"/api/spec\", (req: Request, res: Response) => {\n const rawPath = typeof req.query.path === \"string\" ? req.query.path : \"\";\n if (!rawPath || rawPath.includes(\"..\")) {\n res.status(400).type(\"text/plain\").send(\"Invalid or missing path.\");\n return;\n }\n const ext = path.extname(rawPath).toLowerCase();\n if (ext !== \".md\") {\n res.status(400).type(\"text/plain\").send(\"Only .md spec files are allowed.\");\n return;\n }\n const resolved = path.resolve(boardDir, rawPath);\n const relative = path.relative(boardDir, resolved);\n if (relative.startsWith(\"..\") || path.isAbsolute(relative)) {\n res.status(400).type(\"text/plain\").send(\"Path must be under board directory.\");\n return;\n }\n try {\n const content = fs.readFileSync(resolved, \"utf-8\");\n res.type(\"text/markdown\").send(content);\n } catch {\n res.status(404).type(\"text/plain\").send(\"Spec file not found or unreadable.\");\n }\n });\n\n // Static files (must be after /api routes so they take precedence)\n app.use(express.static(rendererDist));\n\n // SPA fallback: serve index.html for GET requests not handled by static (Express 5 / path-to-regexp v8 reject bare '*')\n app.use((_req: Request, res: Response, next: express.NextFunction) => {\n if (_req.method !== \"GET\" || res.headersSent) return next();\n const indexHtml = path.join(rendererDist, \"index.html\");\n if (fs.existsSync(indexHtml)) {\n res.sendFile(indexHtml);\n } else {\n res.status(404).send(\"Not found\");\n }\n });\n\n const server = createServer(app);\n\n // WebSocket: /api/board/watch — broadcast board content on file change\n const wss = new WebSocketServer({ noServer: true });\n\n server.on(\"upgrade\", (request, socket, head) => {\n const url = new URL(request.url ?? \"\", `http://${request.headers.host}`);\n if (url.pathname === \"/api/board/watch\") {\n wss.handleUpgrade(request, socket, head, (ws: WebSocket) => {\n wss.emit(\"connection\", ws, request);\n });\n } else {\n socket.destroy();\n }\n });\n\n let debounceTimer: ReturnType<typeof setTimeout> | null = null;\n\n function broadcastBoard(): void {\n const content = readBoardContent(boardPath);\n const payload = content ?? \"\";\n wss.clients.forEach((client: WebSocket) => {\n if (client.readyState === 1) {\n client.send(payload);\n }\n });\n }\n\n wss.on(\"connection\", (ws: WebSocket) => {\n // Send current board on connect\n const content = readBoardContent(boardPath);\n if (content !== null) {\n ws.send(content);\n }\n });\n\n try {\n fs.watch(resolvedBoardPath, { persistent: false }, () => {\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n debounceTimer = null;\n broadcastBoard();\n }, RENDER_WATCH_DEBOUNCE_MS);\n });\n } catch {\n // File might not exist yet; watcher will not run\n }\n\n server.on(\"error\", (err: Error & { code?: string }) => {\n process.stderr.write(`Render server error: ${err.message}\\n`);\n if (err.code === \"EADDRINUSE\") {\n process.stderr.write(`Port ${port} is in use. Try --port <number>.\\n`);\n }\n process.exitCode = 1;\n });\n\n server.listen(port, () => {\n const url = `http://localhost:${port}`;\n process.stdout.write(`Open ${url}\\n`);\n if (openBrowser) {\n import(\"open\").then(({ default: open }) => {\n open(url).catch(() => {});\n });\n }\n });\n\n const shutdown = () => {\n server.close(() => {\n process.exit(process.exitCode ?? 0);\n });\n };\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n}\n","import { startRenderServer } from \"../render-server.js\";\n\nexport function runRender(path: string, options: { port: number; open: boolean }): void {\n startRenderServer({\n boardPath: path,\n port: options.port,\n openBrowser: options.open,\n });\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { SPEC_FILENAME } from \"../constants.js\";\nimport { findPackageRoot } from \"../utils.js\";\n\nexport function runSpec(): void {\n const thisFile = fileURLToPath(import.meta.url);\n const startDir = path.dirname(thisFile);\n const packageRoot = findPackageRoot(startDir);\n if (!packageRoot) {\n process.stderr.write(\"statecraft spec: could not find package root\\n\");\n process.exitCode = 1;\n return;\n }\n const specPath = path.join(packageRoot, SPEC_FILENAME);\n if (!fs.existsSync(specPath)) {\n process.stderr.write(`statecraft spec: spec file not found at ${specPath}\\n`);\n process.exitCode = 1;\n return;\n }\n try {\n const content = fs.readFileSync(specPath, \"utf-8\");\n process.stdout.write(content);\n if (!content.endsWith(\"\\n\")) process.stdout.write(\"\\n\");\n process.exitCode = 0;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(`statecraft spec: ${message}\\n`);\n process.exitCode = 1;\n }\n}\n","/**\n * Shared CLI utilities (path resolution, etc.).\n */\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n\n/**\n * Walk up from startDir until a directory containing package.json is found.\n * @returns Absolute path to package root, or null if not found.\n */\nexport function findPackageRoot(startDir: string): string | null {\n let dir = path.resolve(startDir);\n for (;;) {\n const pkgPath = path.join(dir, \"package.json\");\n if (fs.existsSync(pkgPath)) return dir;\n const parent = path.dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n}\n","import { parseBoard, summarize } from \"@stcrft/statecraft-core\";\n\nexport function runSummarize(path: string): void {\n try {\n const board = parseBoard(path);\n const summary = summarize(board);\n process.stdout.write(summary);\n process.exitCode = 0;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(message + \"\\n\");\n process.exitCode = 1;\n }\n}\n","import { parseBoard, validate } from \"@stcrft/statecraft-core\";\n\nfunction formatValidationError(err: { message: string; path?: string }): string {\n const prefix = err.path != null && err.path !== \"\" ? `${err.path}: ` : \"\";\n return `${prefix}${err.message}`;\n}\n\nexport function runValidate(path: string): void {\n try {\n const board = parseBoard(path);\n const result = validate(board);\n if (!result.valid) {\n for (const err of result.errors) {\n process.stderr.write(formatValidationError(err) + \"\\n\");\n }\n process.exitCode = 1;\n return;\n }\n process.exitCode = 0;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(message + \"\\n\");\n process.exitCode = 1;\n }\n}\n"],"mappings":"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","names":["fs","path","__dirname","path","fs","path","fs","path","fileURLToPath","fs","path","fileURLToPath","path","fs","path","parseBoard","path","require","path"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/executors/init.ts","../src/executors/rule-content.ts","../src/render-server.ts","../src/executors/render.ts","../src/executors/spec.ts","../src/utils.ts","../src/executors/summarize.ts","../src/executors/validate.ts","../src/update-check.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { createRequire } from \"node:module\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { Command } from \"commander\";\nimport { DEFAULT_BOARD_PATH, DEFAULT_RENDER_PORT } from \"./constants.js\";\nimport { runInit, runRender, runSpec, runSummarize, runValidate } from \"./executors/index.js\";\nimport { runUpdateCheck } from \"./update-check.js\";\n\nconst require = createRequire(import.meta.url);\nconst { version } = require(\"../package.json\") as { version: string };\n\n/** True when running from monorepo (e.g. pnpm cli validate). */\nfunction isLocalDev(): boolean {\n try {\n const dir = path.dirname(fileURLToPath(import.meta.url));\n return /packages[/\\\\]cli[/\\\\]/.test(dir) || dir.includes(\"statecraft\" + path.sep + \"packages\" + path.sep + \"cli\");\n } catch {\n return false;\n }\n}\n\nfunction rejectMultiplePaths(extra: string[]): void {\n if (extra.length > 0) {\n process.stderr.write(\"Only one board file per run. Multiple paths are not supported.\\n\");\n process.exitCode = 1;\n }\n}\n\nconst program = new Command();\n\nprogram\n .name(\"statecraft\")\n .description(\"Validate, summarize, and render Statecraft board files\")\n .version(version);\n\nprogram\n .command(\"init\")\n .description(\"Interactive setup: create board and configure Statecraft for your workflow\")\n .action(async () => {\n await runInit().catch((err) => {\n process.stderr.write(err instanceof Error ? err.message : String(err) + \"\\n\");\n process.exitCode = 1;\n });\n });\n\nprogram\n .command(\"spec\")\n .description(\"Print the board format spec (for AI agents)\")\n .action(() => {\n runSpec();\n });\n\nprogram\n .command(\"validate\")\n .description(\"Validate a board file (exit 0 if valid, 1 on errors)\")\n .argument(\"[path]\", \"path to board file\", DEFAULT_BOARD_PATH)\n .argument(\"[extra...]\", \"ignored (only one path allowed)\")\n .action((path: string, extra: string[]) => {\n rejectMultiplePaths(extra);\n if (process.exitCode !== 1) runValidate(path);\n });\n\nprogram\n .command(\"summarize\")\n .description(\"Print a short text summary of the board\")\n .argument(\"[path]\", \"path to board file\", DEFAULT_BOARD_PATH)\n .argument(\"[extra...]\", \"ignored (only one path allowed)\")\n .action((path: string, extra: string[]) => {\n rejectMultiplePaths(extra);\n if (process.exitCode !== 1) runSummarize(path);\n });\n\nprogram\n .command(\"render\")\n .description(\"Serve the board in the browser (read-only UI)\")\n .argument(\"[path]\", \"path to board file\", DEFAULT_BOARD_PATH)\n .option(\"-p, --port <number>\", \"port for the server\", String(DEFAULT_RENDER_PORT))\n .option(\"--open\", \"open browser after starting server\")\n .action((path: string, options: { port: string; open: boolean }) => {\n const port = parseInt(options.port, 10) || DEFAULT_RENDER_PORT;\n runRender(path, { port, open: options.open ?? false });\n });\n\nprogram.parseAsync().then(() => {\n runUpdateCheck(version, { isLocalDev: isLocalDev() });\n});\n","/** Default path to the board file when none is given (validate, summarize, render). */\nexport const DEFAULT_BOARD_PATH = \"./board.yaml\";\n\n/** Default port for the render server. */\nexport const DEFAULT_RENDER_PORT = 3000;\n\n/** Debounce delay (ms) for file watcher before broadcasting board updates to WebSocket clients. */\nexport const RENDER_WATCH_DEBOUNCE_MS = 100;\n\n/** Default board file path offered by init (cwd). */\nexport const INIT_DEFAULT_BOARD_PATH = \"board.yaml\";\n\n/** Default directory for task .md files (relative to board), offered by init. */\nexport const INIT_DEFAULT_TASKS_DIR = \"tasks\";\n\n/** Canonical column set per spec; init creates boards with these columns. */\nexport const CANONICAL_COLUMNS = [\"Backlog\", \"Ready\", \"In Progress\", \"Done\"] as const;\n\n/** Filename of the board format spec shipped with the CLI package (statecraft spec). */\nexport const SPEC_FILENAME = \"spec.md\";\n\n/** Init default: enforce \"create task before work\" workflow for all agents. */\nexport const INIT_STRICT_MODE_DEFAULT = true;\n\n/** Init default: require each task to have a spec .md file in the tasks directory. */\nexport const INIT_REQUIRE_SPEC_FILE_DEFAULT = true;\n\n/** Init default: include task spec .md format guidelines in generated rules. */\nexport const INIT_INCLUDE_TASK_SPEC_FORMAT_DEFAULT = true;\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport readline from \"node:readline\";\nimport { stringify } from \"yaml\";\nimport {\n CANONICAL_COLUMNS,\n INIT_DEFAULT_BOARD_PATH,\n INIT_DEFAULT_TASKS_DIR,\n INIT_INCLUDE_TASK_SPEC_FORMAT_DEFAULT,\n INIT_REQUIRE_SPEC_FILE_DEFAULT,\n INIT_STRICT_MODE_DEFAULT,\n} from \"../constants.js\";\nimport { buildStatecraftRuleBody, type RuleOptions } from \"./rule-content.js\";\n\nexport interface InitAnswers {\n boardName: string;\n columns: Array<{ name: string; limit?: number }>;\n boardPath: string;\n tasksDir: string;\n}\n\n/** Raw answers collected from prompts (before parsing WIP or booleans). */\ninterface CollectedInitAnswers {\n boardName: string;\n boardPath: string;\n tasksDir: string;\n wipStr: string;\n strictMode: boolean;\n requireSpecFile: boolean;\n includeTaskSpecFormat: boolean;\n generateCursorRule: boolean;\n generateClaudeRule: boolean;\n generateCodexAgents: boolean;\n}\n\nfunction question(rl: readline.Interface, prompt: string, defaultValue?: string): Promise<string> {\n const suffix = defaultValue !== undefined ? ` (default: ${defaultValue})` : \"\";\n return new Promise((resolve) => {\n rl.question(`${prompt}${suffix}: `, (answer) => {\n const trimmed = answer.trim();\n resolve(trimmed !== \"\" ? trimmed : (defaultValue ?? \"\"));\n });\n });\n}\n\nasync function getAnswer(\n rl: readline.Interface | null,\n answers: string[] | undefined,\n index: { current: number },\n prompt: string,\n defaultValue?: string\n): Promise<string> {\n if (answers !== undefined) {\n const raw = answers[index.current++] ?? \"\";\n const trimmed = raw.trim();\n return trimmed !== \"\" ? trimmed : (defaultValue ?? \"\");\n }\n return question(rl!, prompt, defaultValue);\n}\n\nfunction parseWipLimit(wipStr: string): number | undefined {\n if (wipStr === \"\") return undefined;\n const n = parseInt(wipStr, 10);\n return Number.isInteger(n) && n >= 1 ? n : undefined;\n}\n\n/** Collect all init prompts into a structured object. */\nasync function collectInitAnswers(\n rl: readline.Interface | null,\n answers: string[] | undefined,\n index: { current: number }\n): Promise<CollectedInitAnswers> {\n const boardName = await getAnswer(rl, answers, index, \"Board name\");\n const boardPath = await getAnswer(rl, answers, index, \"Path for board file\", INIT_DEFAULT_BOARD_PATH);\n const tasksDir = await getAnswer(\n rl,\n answers,\n index,\n \"Directory for task .md files (relative to board)\",\n INIT_DEFAULT_TASKS_DIR\n );\n const wipStr = await getAnswer(\n rl,\n answers,\n index,\n \"WIP limit for In Progress (optional, press Enter to skip)\",\n \"\"\n );\n const strictModeStr = await getAnswer(\n rl,\n answers,\n index,\n \"Enforce Statecraft workflow (create task before any work)? (Y/n)\",\n INIT_STRICT_MODE_DEFAULT ? \"Y\" : \"n\"\n );\n const requireSpecStr = await getAnswer(\n rl,\n answers,\n index,\n \"Require each task to have a spec .md file? (Y/n)\",\n INIT_REQUIRE_SPEC_FILE_DEFAULT ? \"Y\" : \"n\"\n );\n const includeFormatStr = await getAnswer(\n rl,\n answers,\n index,\n \"Include task spec .md format guidelines in rules? (Y/n)\",\n INIT_INCLUDE_TASK_SPEC_FORMAT_DEFAULT ? \"Y\" : \"n\"\n );\n const genCursor = await getAnswer(rl, answers, index, \"Generate Cursor rule? (Y/n)\", \"Y\");\n const genClaude = await getAnswer(rl, answers, index, \"Generate Claude Code rule? (y/n)\", \"n\");\n const genCodex = await getAnswer(rl, answers, index, \"Generate Codex instructions (AGENTS.md)? (y/n)\", \"n\");\n\n return {\n boardName,\n boardPath,\n tasksDir,\n wipStr,\n strictMode: /^y(es)?$/i.test(strictModeStr.trim()),\n requireSpecFile: /^y(es)?$/i.test(requireSpecStr.trim()),\n includeTaskSpecFormat: /^y(es)?$/i.test(includeFormatStr.trim()),\n generateCursorRule: /^y(es)?$/i.test(genCursor.trim()),\n generateClaudeRule: /^y(es)?$/i.test(genClaude.trim()),\n generateCodexAgents: /^y(es)?$/i.test(genCodex.trim()),\n };\n}\n\n/** Build the board object (name, columns, empty tasks) from collected answers. */\nfunction buildBoardFromAnswers(\n boardName: string,\n tasksDir: string,\n wipStr: string\n): { board: string; columns: Array<string | { name: string; limit: number }>; tasks: Record<string, never> } {\n const inProgressLimit = parseWipLimit(wipStr);\n const columns: Array<string | { name: string; limit: number }> = [\n CANONICAL_COLUMNS[0],\n CANONICAL_COLUMNS[1],\n inProgressLimit != null ? { name: CANONICAL_COLUMNS[2], limit: inProgressLimit } : CANONICAL_COLUMNS[2],\n CANONICAL_COLUMNS[3],\n ];\n return {\n board: boardName,\n columns,\n tasks: {},\n };\n}\n\n/** Ensure parent directory exists and write file. */\nfunction ensureDirAndWrite(filePath: string, content: string): void {\n const dir = path.dirname(filePath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n fs.writeFileSync(filePath, content, \"utf-8\");\n}\n\n/** Write board YAML to cwd-relative path. */\nfunction writeBoardFile(\n cwd: string,\n boardPath: string,\n board: { board: string; columns: unknown[]; tasks: object }\n): void {\n const absolutePath = path.resolve(cwd, boardPath);\n const yamlContent = stringify(board, { lineWidth: 0 });\n ensureDirAndWrite(absolutePath, yamlContent);\n}\n\n/** Write a rule file and optionally log. */\nfunction writeRuleFile(\n filePath: string,\n content: string,\n log: (msg: string) => void\n): void {\n ensureDirAndWrite(filePath, content);\n log(filePath);\n}\n\nconst CODECX_MARKER = \"## Statecraft (generated by statecraft init)\";\n\nfunction writeCursorRule(\n cwd: string,\n boardPath: string,\n specDir: string,\n options: Partial<RuleOptions>,\n log: (msg: string) => void\n): void {\n const cursorRulesDir = path.resolve(cwd, \".cursor\", \"rules\");\n const cursorRulePath = path.join(cursorRulesDir, \"statecraft.mdc\");\n const content = buildCursorRuleContent(boardPath, specDir, options);\n writeRuleFile(cursorRulePath, content, (p) => log(`Wrote Cursor rule to ${p}`));\n}\n\nfunction writeClaudeRule(\n cwd: string,\n boardPath: string,\n specDir: string,\n options: Partial<RuleOptions>,\n log: (msg: string) => void\n): void {\n const claudeRulesDir = path.resolve(cwd, \".claude\", \"rules\");\n const claudeRulePath = path.join(claudeRulesDir, \"statecraft.md\");\n const content = buildClaudeRuleContent(boardPath, specDir, options);\n writeRuleFile(claudeRulePath, content, (p) => log(`Wrote Claude Code rule to ${p}`));\n}\n\nfunction writeCodexRule(\n cwd: string,\n boardPath: string,\n specDir: string,\n options: Partial<RuleOptions>,\n log: (msg: string) => void\n): void {\n const codexAgentsPath = path.resolve(cwd, \"AGENTS.md\");\n const codexContent = buildCodexAgentsContent(boardPath, specDir, options);\n if (fs.existsSync(codexAgentsPath)) {\n const existing = fs.readFileSync(codexAgentsPath, \"utf-8\");\n if (existing.includes(CODECX_MARKER)) {\n log(\"AGENTS.md already contains Statecraft section; skipped.\");\n return;\n }\n fs.writeFileSync(codexAgentsPath, existing.trimEnd() + \"\\n\\n\" + codexContent + \"\\n\", \"utf-8\");\n log(`Appended Statecraft section to ${codexAgentsPath}`);\n } else {\n fs.writeFileSync(codexAgentsPath, codexContent + \"\\n\", \"utf-8\");\n log(`Wrote Codex instructions to ${codexAgentsPath}`);\n }\n}\n\n// --- Public rule content builders (used by init and by tests) ---\n\nexport function buildCursorRuleContent(\n boardPath: string,\n tasksDir: string,\n options?: Partial<RuleOptions>\n): string {\n return `---\ndescription: Statecraft board and task workflow; when to update the board and how to create tasks\nalwaysApply: true\n---\n${buildStatecraftRuleBody(boardPath, tasksDir, options)}`;\n}\n\n/** Claude Code: modular rule in .claude/rules/ (markdown, no frontmatter). */\nexport function buildClaudeRuleContent(\n boardPath: string,\n tasksDir: string,\n options?: Partial<RuleOptions>\n): string {\n return buildStatecraftRuleBody(boardPath, tasksDir, options);\n}\n\n/** Codex: section for AGENTS.md at project root. */\nexport function buildCodexAgentsContent(\n boardPath: string,\n tasksDir: string,\n options?: Partial<RuleOptions>\n): string {\n return `## Statecraft (generated by statecraft init)\n\n${buildStatecraftRuleBody(boardPath, tasksDir, options)}`;\n}\n\n// --- runInit ---\n\nexport interface RunInitOptions {\n /** For tests: pre-filled answers (board name, board path, tasks dir, WIP, strict mode Y/n, require spec Y/n, task spec format Y/n, Cursor y/n, Claude y/n, Codex y/n). */\n answers?: string[];\n /** For tests: working directory for writing board and rule files (default: process.cwd()). */\n cwd?: string;\n}\n\nexport async function runInit(options?: RunInitOptions): Promise<void> {\n const answers = options?.answers;\n const cwd = options?.cwd ?? process.cwd();\n const answerIndex = { current: 0 };\n const rl = answers === undefined ? readline.createInterface({ input: process.stdin, output: process.stdout }) : null;\n const log = (msg: string) => {\n if (rl) process.stdout.write(msg + \"\\n\");\n };\n\n try {\n if (rl) {\n process.stdout.write(\"\\nStatecraft init — create your board and connect it to your workflow.\\n\\n\");\n }\n\n const collected = await collectInitAnswers(rl, answers, answerIndex);\n if (!collected.boardName) {\n process.stderr.write(\"Board name is required.\\n\");\n process.exitCode = 1;\n return;\n }\n\n rl?.close();\n\n const board = buildBoardFromAnswers(collected.boardName, collected.tasksDir, collected.wipStr);\n writeBoardFile(cwd, collected.boardPath, board);\n\n const absolutePath = path.resolve(cwd, collected.boardPath);\n log(`\\nCreated board at ${absolutePath}`);\n log(`Task spec files: ${path.join(path.dirname(collected.boardPath), collected.tasksDir)}/<task-id>.md`);\n\n const specDir = path.join(path.dirname(collected.boardPath), collected.tasksDir);\n const ruleOptions: Partial<RuleOptions> = {\n strictMode: collected.strictMode,\n requireSpecFile: collected.requireSpecFile,\n includeTaskSpecFormat: collected.includeTaskSpecFormat,\n };\n\n if (collected.generateCursorRule) writeCursorRule(cwd, collected.boardPath, specDir, ruleOptions, log);\n if (collected.generateClaudeRule) writeClaudeRule(cwd, collected.boardPath, specDir, ruleOptions, log);\n if (collected.generateCodexAgents) writeCodexRule(cwd, collected.boardPath, specDir, ruleOptions, log);\n\n log(\"\\nRun `statecraft validate \" + collected.boardPath + \"` to validate, or `statecraft render \" + collected.boardPath + \"` to view.\");\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(message + \"\\n\");\n process.exitCode = 1;\n } finally {\n rl?.close();\n }\n}\n","import {\n INIT_INCLUDE_TASK_SPEC_FORMAT_DEFAULT,\n INIT_REQUIRE_SPEC_FILE_DEFAULT,\n INIT_STRICT_MODE_DEFAULT,\n} from \"../constants.js\";\n\n/**\n * Options for generated rule content. Used by init; defaults match init prompts (Y/n).\n */\nexport interface RuleOptions {\n /** Enforce \"create task before any work\" workflow (strict mode). Default true. */\n strictMode: boolean;\n /** Require each task to have a spec .md file. Default true. */\n requireSpecFile: boolean;\n /** Include task spec .md format guidelines in the rule. Default true. */\n includeTaskSpecFormat: boolean;\n}\n\nconst DEFAULT_RULE_OPTIONS: RuleOptions = {\n strictMode: INIT_STRICT_MODE_DEFAULT,\n requireSpecFile: INIT_REQUIRE_SPEC_FILE_DEFAULT,\n includeTaskSpecFormat: INIT_INCLUDE_TASK_SPEC_FORMAT_DEFAULT,\n};\n\n/**\n * Shared content for generated AI rules (Cursor, Claude Code, Codex).\n * Single source for the Statecraft rule body so init only orchestrates.\n */\nexport function buildStatecraftRuleBody(\n boardPath: string,\n tasksDir: string,\n options: Partial<RuleOptions> = {}\n): string {\n const opts = { ...DEFAULT_RULE_OPTIONS, ...options };\n\n const strictBlock =\n opts.strictMode &&\n `## ⚠️ REQUIRED BEFORE ANY OTHER ACTION (100% enforced)\n\n**Before** doing any substantive work (coding, fixing, refactoring, or answering the user's request with implementation), you **MUST**:\n\n1. **Ensure a task exists:** In \\`${boardPath}\\`, either **create** a new task under \\`tasks:\\` with \\`status: Backlog\\` (use a kebab-case id, e.g. \\`fix-header-import\\`), or use an existing task. New tasks start in **Backlog** (vague is OK).\n2. ${opts.requireSpecFile ? `**Spec file:** Create or update \\`${tasksDir}/<task-id>.md\\` (required for each task).` : `**Spec file:** Optionally create or update \\`${tasksDir}/<task-id>.md\\` with description and acceptance criteria.`}\n3. **Flow:** Do **not** put the task in In Progress until it has been refined. After discovery/planning, update the task's spec (description, acceptance criteria); when you know what to do and the spec is clear, set \\`status\\` to **Ready**. Only when you are **actually starting the work** (e.g. writing code), set \\`status\\` to **In Progress**. So: Backlog → Ready (after refinement) → In Progress (when working) → Done.\n\nYou **must not** start implementation (no code, no concrete changes) until the task is at least in **Ready** and you are about to work on it; then move to **In Progress**. If \\`${boardPath}\\` does not exist yet, create a minimal valid board file (board name, columns: Backlog, Ready, In Progress, Done, tasks: {}) and the task in Backlog before proceeding.\n\n---\n`;\n\n const taskSpecFormatSection = opts.includeTaskSpecFormat\n ? `\n## Task spec file format (\\`${tasksDir}/<task-id>.md\\`)\n\nFollow this structure so task specs are consistent and machine-friendly:\n\n- **Title:** First line or \\`# <Task title>\\` (match the board \\`title\\` or expand it).\n- **Description:** Short context or problem statement (optional \\`## Description\\`).\n- **Acceptance criteria / Definition of Done:** \\`## Acceptance criteria\\` or \\`## Definition of Done\\` with a checklist (e.g. \\`- [ ] item\\`) so completion is unambiguous. All items must be checked before moving the task to Done.\n- **Notes / Dependencies:** Optional \\`## Notes\\`, \\`## Dependencies\\` (for human context; \\`depends_on\\` in the board is the source of truth for task ordering).\n\nKeep each file focused on one task; use the task id in the filename (e.g. \\`fix-auth-timeout.md\\`).\n\n---\n`\n : \"\";\n\n const requireSpecBullet = opts.requireSpecFile\n ? `- **Spec required:** Every task must have a \\`spec\\` field pointing to \\`${tasksDir}/<task-id>.md\\`. Create the .md file when creating the task.\n`\n : \"\";\n\n return `# Statecraft\n${strictBlock || \"\"}\nThis project uses Statecraft for the task board.\n\n- **Board file:** \\`${boardPath}\\`\n- **Task spec files:** \\`${tasksDir}/<task-id>.md\\` (relative to board directory)\n- **Columns (canonical):** Backlog → Ready → In Progress → Done.\n\n## Commands\n\nUse \\`statecraft\\` if installed globally. If not in PATH, use \\`npx statecraft\\` (npm), \\`pnpm dlx statecraft\\` (pnpm), or \\`yarn dlx statecraft\\` (yarn)—e.g. \\`npx statecraft validate ${boardPath}\\`.\n\n- Get board format spec: \\`statecraft spec\\`\n- Validate board: \\`statecraft validate ${boardPath}\\`\n- View board in browser: \\`statecraft render ${boardPath}\\`\n\n## Task lifecycle (edit board and task files directly)\n\n**Flow:** Backlog → Ready → In Progress → Done. Do not skip Ready.\n\n- **Create task:** Add an entry under \\`tasks\\` with \\`status: Backlog\\` (id, title, optional description, spec, owner, priority, depends_on). ${opts.requireSpecFile ? `Create \\`${tasksDir}/<task-id>.md\\` for each task (required).` : `If needed, create \\`${tasksDir}/<task-id>.md\\` with description and DoD.`} Backlog = initial or vague; refinement comes next.\n- **Refine and move to Ready:** After discovery or planning, update the task's spec file (description, acceptance criteria). When the definition is clear and you know how to solve it, set \\`status\\` to **Ready**. Ready = \"ready to be worked on.\"\n- **Start work:** Only when you are about to do the actual work (code, fix, etc.), set the task's \\`status\\` to **In Progress**. Read the task's \\`spec\\` file if needed.\n- **Finish work:** Set the task's \\`status\\` to **Done** only when the task's acceptance criteria (in its spec file) are satisfied.\n${taskSpecFormatSection}\n## AI guidelines for creating tickets\n\n- **Keep the board in sync:** When doing substantive work, create a new task in **Backlog** (or use an existing one). After discovery/refinement, update the task spec and move to **Ready**; only when actually working move to **In Progress**, then **Done** when criteria are met. Do not skip Ready.\n${requireSpecBullet}- **Task naming:** kebab-case, verb or noun phrase (e.g. \\`fix-auth-timeout\\`).\n- **Description:** One line summary; optional markdown for context.\n- **Definition of Done:** Acceptance criteria in task spec; all checked before moving to Done.\n- **Task fields (from spec):** \\`title\\` (required), \\`status\\` (required), optional \\`description\\`, \\`spec\\` (path to .md), \\`owner\\`, \\`priority\\`, \\`depends_on\\`.\n- **Spec file:** Path relative to board directory, e.g. \\`${tasksDir}/<task-id>.md\\`.\n`;\n}\n","import express, { type Request, type Response } from \"express\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { createServer } from \"node:http\";\nimport { WebSocket, WebSocketServer } from \"ws\";\nimport { fileURLToPath } from \"node:url\";\nimport { DEFAULT_RENDER_PORT, RENDER_WATCH_DEBOUNCE_MS } from \"./constants.js\";\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\n/**\n * Resolve path to the renderer's static files.\n * - When installed from npm: bundled at package-root/renderer-dist.\n * - When running in monorepo: sibling package at packages/renderer/dist.\n */\nfunction getRendererDistPath(): string {\n const bundled = path.resolve(__dirname, \"..\", \"renderer-dist\");\n if (fs.existsSync(bundled) && fs.statSync(bundled).isDirectory()) {\n return bundled;\n }\n return path.resolve(__dirname, \"..\", \"..\", \"renderer\", \"dist\");\n}\n\n/**\n * Read board file and return content as UTF-8. Returns null on error.\n */\nfunction readBoardContent(boardPath: string): string | null {\n try {\n const resolved = path.resolve(process.cwd(), boardPath);\n return fs.readFileSync(resolved, \"utf-8\");\n } catch {\n return null;\n }\n}\n\nexport interface RenderServerOptions {\n boardPath: string;\n port?: number;\n openBrowser?: boolean;\n}\n\n/**\n * Start the render server: static app + GET /api/board + WebSocket /api/board/watch.\n * Watches the board file and broadcasts content to WS clients on change.\n */\nexport function startRenderServer(options: RenderServerOptions): void {\n const { boardPath, port = DEFAULT_RENDER_PORT, openBrowser = false } = options;\n const rendererDist = getRendererDistPath();\n\n if (!fs.existsSync(rendererDist) || !fs.statSync(rendererDist).isDirectory()) {\n process.stderr.write(\n \"Renderer build not found. Run: pnpm build\\n\"\n );\n process.exitCode = 1;\n return;\n }\n\n const app = express();\n\n // Board file directory (for resolving spec paths relative to board)\n const resolvedBoardPath = path.resolve(process.cwd(), boardPath);\n const boardDir = path.dirname(resolvedBoardPath);\n\n // API: board file content (raw YAML)\n app.get(\"/api/board\", (_req: Request, res: Response) => {\n const content = readBoardContent(boardPath);\n if (content === null) {\n res.status(404).type(\"text/plain\").send(\"Board file not found or unreadable.\");\n return;\n }\n res.type(\"text/yaml\").send(content);\n });\n\n // API: spec file content (path relative to board file directory; only .md files)\n app.get(\"/api/spec\", (req: Request, res: Response) => {\n const rawPath = typeof req.query.path === \"string\" ? req.query.path : \"\";\n if (!rawPath || rawPath.includes(\"..\")) {\n res.status(400).type(\"text/plain\").send(\"Invalid or missing path.\");\n return;\n }\n const ext = path.extname(rawPath).toLowerCase();\n if (ext !== \".md\") {\n res.status(400).type(\"text/plain\").send(\"Only .md spec files are allowed.\");\n return;\n }\n const resolved = path.resolve(boardDir, rawPath);\n const relative = path.relative(boardDir, resolved);\n if (relative.startsWith(\"..\") || path.isAbsolute(relative)) {\n res.status(400).type(\"text/plain\").send(\"Path must be under board directory.\");\n return;\n }\n try {\n const content = fs.readFileSync(resolved, \"utf-8\");\n res.type(\"text/markdown\").send(content);\n } catch {\n res.status(404).type(\"text/plain\").send(\"Spec file not found or unreadable.\");\n }\n });\n\n // Static files (must be after /api routes so they take precedence)\n app.use(express.static(rendererDist));\n\n // SPA fallback: serve index.html for GET requests not handled by static (Express 5 / path-to-regexp v8 reject bare '*')\n app.use((_req: Request, res: Response, next: express.NextFunction) => {\n if (_req.method !== \"GET\" || res.headersSent) return next();\n const indexHtml = path.join(rendererDist, \"index.html\");\n if (fs.existsSync(indexHtml)) {\n res.sendFile(indexHtml);\n } else {\n res.status(404).send(\"Not found\");\n }\n });\n\n const server = createServer(app);\n\n // WebSocket: /api/board/watch — broadcast board content on file change\n const wss = new WebSocketServer({ noServer: true });\n\n server.on(\"upgrade\", (request, socket, head) => {\n const url = new URL(request.url ?? \"\", `http://${request.headers.host}`);\n if (url.pathname === \"/api/board/watch\") {\n wss.handleUpgrade(request, socket, head, (ws: WebSocket) => {\n wss.emit(\"connection\", ws, request);\n });\n } else {\n socket.destroy();\n }\n });\n\n let debounceTimer: ReturnType<typeof setTimeout> | null = null;\n\n function broadcastBoard(): void {\n const content = readBoardContent(boardPath);\n const payload = content ?? \"\";\n wss.clients.forEach((client: WebSocket) => {\n if (client.readyState === 1) {\n client.send(payload);\n }\n });\n }\n\n wss.on(\"connection\", (ws: WebSocket) => {\n // Send current board on connect\n const content = readBoardContent(boardPath);\n if (content !== null) {\n ws.send(content);\n }\n });\n\n try {\n fs.watch(resolvedBoardPath, { persistent: false }, () => {\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n debounceTimer = null;\n broadcastBoard();\n }, RENDER_WATCH_DEBOUNCE_MS);\n });\n } catch {\n // File might not exist yet; watcher will not run\n }\n\n server.on(\"error\", (err: Error & { code?: string }) => {\n process.stderr.write(`Render server error: ${err.message}\\n`);\n if (err.code === \"EADDRINUSE\") {\n process.stderr.write(`Port ${port} is in use. Try --port <number>.\\n`);\n }\n process.exitCode = 1;\n });\n\n server.listen(port, () => {\n const url = `http://localhost:${port}`;\n process.stdout.write(`Open ${url}\\n`);\n if (openBrowser) {\n import(\"open\").then(({ default: open }) => {\n open(url).catch(() => {});\n });\n }\n });\n\n const shutdown = () => {\n server.close(() => {\n process.exit(process.exitCode ?? 0);\n });\n };\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n}\n","import { startRenderServer } from \"../render-server.js\";\n\nexport function runRender(path: string, options: { port: number; open: boolean }): void {\n startRenderServer({\n boardPath: path,\n port: options.port,\n openBrowser: options.open,\n });\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { SPEC_FILENAME } from \"../constants.js\";\nimport { findPackageRoot } from \"../utils.js\";\n\nexport function runSpec(): void {\n const thisFile = fileURLToPath(import.meta.url);\n const startDir = path.dirname(thisFile);\n const packageRoot = findPackageRoot(startDir);\n if (!packageRoot) {\n process.stderr.write(\"statecraft spec: could not find package root\\n\");\n process.exitCode = 1;\n return;\n }\n const specPath = path.join(packageRoot, SPEC_FILENAME);\n if (!fs.existsSync(specPath)) {\n process.stderr.write(`statecraft spec: spec file not found at ${specPath}\\n`);\n process.exitCode = 1;\n return;\n }\n try {\n const content = fs.readFileSync(specPath, \"utf-8\");\n process.stdout.write(content);\n if (!content.endsWith(\"\\n\")) process.stdout.write(\"\\n\");\n process.exitCode = 0;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(`statecraft spec: ${message}\\n`);\n process.exitCode = 1;\n }\n}\n","/**\n * Shared CLI utilities (path resolution, etc.).\n */\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n\n/**\n * Walk up from startDir until a directory containing package.json is found.\n * @returns Absolute path to package root, or null if not found.\n */\nexport function findPackageRoot(startDir: string): string | null {\n let dir = path.resolve(startDir);\n for (;;) {\n const pkgPath = path.join(dir, \"package.json\");\n if (fs.existsSync(pkgPath)) return dir;\n const parent = path.dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n}\n","import { parseBoard, summarize } from \"@stcrft/statecraft-core\";\n\nexport function runSummarize(path: string): void {\n try {\n const board = parseBoard(path);\n const summary = summarize(board);\n process.stdout.write(summary);\n process.exitCode = 0;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(message + \"\\n\");\n process.exitCode = 1;\n }\n}\n","import { parseBoard, validate } from \"@stcrft/statecraft-core\";\n\nfunction formatValidationError(err: { message: string; path?: string }): string {\n const prefix = err.path != null && err.path !== \"\" ? `${err.path}: ` : \"\";\n return `${prefix}${err.message}`;\n}\n\nexport function runValidate(path: string): void {\n try {\n const board = parseBoard(path);\n const result = validate(board);\n if (!result.valid) {\n for (const err of result.errors) {\n process.stderr.write(formatValidationError(err) + \"\\n\");\n }\n process.exitCode = 1;\n return;\n }\n process.exitCode = 0;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(message + \"\\n\");\n process.exitCode = 1;\n }\n}\n","/**\n * Optional update check: notify users when a newer version of Statecraft is available.\n * Runs fire-and-forget after commands; uses a cache in the temp directory when writable.\n */\n\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport semver from \"semver\";\n\nconst REGISTRY_URL = \"https://registry.npmjs.org/@stcrft/statecraft/latest\";\nconst CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours\nconst FETCH_TIMEOUT_MS = 3000;\nconst CACHE_FILENAME = \"statecraft-update-check.json\";\n\ninterface CacheShape {\n lastCheck: number;\n latestVersion: string;\n}\n\nfunction getCachePath(): string {\n const tmp = os.tmpdir();\n return path.join(tmp, CACHE_FILENAME);\n}\n\nfunction readCache(): CacheShape | null {\n try {\n const p = getCachePath();\n const raw = fs.readFileSync(p, \"utf-8\");\n const data = JSON.parse(raw) as CacheShape;\n if (typeof data.lastCheck === \"number\" && typeof data.latestVersion === \"string\") {\n return data;\n }\n } catch {\n // ignore\n }\n return null;\n}\n\nfunction writeCache(latestVersion: string): void {\n try {\n const p = getCachePath();\n const data: CacheShape = { lastCheck: Date.now(), latestVersion };\n fs.writeFileSync(p, JSON.stringify(data), \"utf-8\");\n } catch {\n // best-effort: do not fail if temp dir is read-only or other error\n }\n}\n\nasync function fetchLatestVersion(): Promise<string | null> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n try {\n const res = await fetch(REGISTRY_URL, { signal: controller.signal });\n clearTimeout(timeout);\n if (!res.ok) return null;\n const json = (await res.json()) as { version?: string };\n return typeof json.version === \"string\" ? json.version : null;\n } catch {\n clearTimeout(timeout);\n return null;\n }\n}\n\nfunction shouldSkip(options: { isLocalDev: boolean }): boolean {\n if (process.env.CI === \"true\" || process.env.CI === \"1\") return true;\n if (process.env.STATECRAFT_NO_UPDATE_CHECK === \"1\" || process.env.STATECRAFT_NO_UPDATE_CHECK === \"true\") return true;\n if (options.isLocalDev) return true;\n return false;\n}\n\n/**\n * Run the update check (fire-and-forget). Call after the command has finished.\n * Skips when CI, STATECRAFT_NO_UPDATE_CHECK, or running from local dev.\n * Uses cache in temp dir when writable; does not fail if cache write fails.\n */\nexport function runUpdateCheck(currentVersion: string, options: { isLocalDev: boolean }): void {\n if (shouldSkip(options)) return;\n\n void (async () => {\n let latest: string | null = null;\n const cached = readCache();\n if (cached && Date.now() - cached.lastCheck < CACHE_TTL_MS) {\n latest = cached.latestVersion;\n }\n if (latest === null) {\n latest = await fetchLatestVersion();\n if (latest !== null) writeCache(latest);\n }\n if (latest === null) return;\n try {\n if (semver.gt(latest, currentVersion)) {\n process.stderr.write(\n `A new version of Statecraft is available: ${latest} (you have ${currentVersion}). Upgrade: npm update -g @stcrft/statecraft\\n`\n );\n }\n } catch {\n // invalid semver or other compare error: do nothing\n }\n })();\n}\n"],"mappings":";;;AACA,SAAS,qBAAqB;AAC9B,OAAOA,WAAU;AACjB,SAAS,iBAAAC,sBAAqB;AAC9B,SAAS,eAAe;;;ACHjB,IAAM,qBAAqB;AAG3B,IAAM,sBAAsB;AAG5B,IAAM,2BAA2B;AAGjC,IAAM,0BAA0B;AAGhC,IAAM,yBAAyB;AAG/B,IAAM,oBAAoB,CAAC,WAAW,SAAS,eAAe,MAAM;AAGpE,IAAM,gBAAgB;AAGtB,IAAM,2BAA2B;AAGjC,IAAM,iCAAiC;AAGvC,IAAM,wCAAwC;;;AC5BrD,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,OAAO,cAAc;AACrB,SAAS,iBAAiB;;;ACe1B,IAAM,uBAAoC;AAAA,EACxC,YAAY;AAAA,EACZ,iBAAiB;AAAA,EACjB,uBAAuB;AACzB;AAMO,SAAS,wBACd,WACA,UACA,UAAgC,CAAC,GACzB;AACR,QAAM,OAAO,EAAE,GAAG,sBAAsB,GAAG,QAAQ;AAEnD,QAAM,cACJ,KAAK,cACL;AAAA;AAAA;AAAA;AAAA,oCAIgC,SAAS;AAAA,KACxC,KAAK,kBAAkB,qCAAqC,QAAQ,8CAA8C,gDAAgD,QAAQ,2DAA2D;AAAA;AAAA;AAAA,mLAGvD,SAAS;AAAA;AAAA;AAAA;AAK1L,QAAM,wBAAwB,KAAK,wBAC/B;AAAA,8BACwB,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAahC;AAEJ,QAAM,oBAAoB,KAAK,kBAC3B,4EAA4E,QAAQ;AAAA,IAEpF;AAEJ,SAAO;AAAA,EACP,eAAe,EAAE;AAAA;AAAA;AAAA,sBAGG,SAAS;AAAA,2BACJ,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA,gMAKwJ,SAAS;AAAA;AAAA;AAAA,0CAG1J,SAAS;AAAA,+CACJ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iJAMyF,KAAK,kBAAkB,YAAY,QAAQ,8CAA8C,uBAAuB,QAAQ,2CAA2C;AAAA;AAAA;AAAA;AAAA,EAIlT,qBAAqB;AAAA;AAAA;AAAA;AAAA,EAIrB,iBAAiB;AAAA;AAAA;AAAA;AAAA,4DAIyC,QAAQ;AAAA;AAEpE;;;ADvEA,SAAS,SAAS,IAAwB,QAAgB,cAAwC;AAChG,QAAM,SAAS,iBAAiB,SAAY,cAAc,YAAY,MAAM;AAC5E,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,OAAG,SAAS,GAAG,MAAM,GAAG,MAAM,MAAM,CAAC,WAAW;AAC9C,YAAM,UAAU,OAAO,KAAK;AAC5B,cAAQ,YAAY,KAAK,UAAW,gBAAgB,EAAG;AAAA,IACzD,CAAC;AAAA,EACH,CAAC;AACH;AAEA,eAAe,UACb,IACA,SACA,OACA,QACA,cACiB;AACjB,MAAI,YAAY,QAAW;AACzB,UAAM,MAAM,QAAQ,MAAM,SAAS,KAAK;AACxC,UAAM,UAAU,IAAI,KAAK;AACzB,WAAO,YAAY,KAAK,UAAW,gBAAgB;AAAA,EACrD;AACA,SAAO,SAAS,IAAK,QAAQ,YAAY;AAC3C;AAEA,SAAS,cAAc,QAAoC;AACzD,MAAI,WAAW,GAAI,QAAO;AAC1B,QAAM,IAAI,SAAS,QAAQ,EAAE;AAC7B,SAAO,OAAO,UAAU,CAAC,KAAK,KAAK,IAAI,IAAI;AAC7C;AAGA,eAAe,mBACb,IACA,SACA,OAC+B;AAC/B,QAAM,YAAY,MAAM,UAAU,IAAI,SAAS,OAAO,YAAY;AAClE,QAAM,YAAY,MAAM,UAAU,IAAI,SAAS,OAAO,uBAAuB,uBAAuB;AACpG,QAAM,WAAW,MAAM;AAAA,IACrB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,QAAM,SAAS,MAAM;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,QAAM,gBAAgB,MAAM;AAAA,IAC1B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,2BAA2B,MAAM;AAAA,EACnC;AACA,QAAM,iBAAiB,MAAM;AAAA,IAC3B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,iCAAiC,MAAM;AAAA,EACzC;AACA,QAAM,mBAAmB,MAAM;AAAA,IAC7B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,wCAAwC,MAAM;AAAA,EAChD;AACA,QAAM,YAAY,MAAM,UAAU,IAAI,SAAS,OAAO,+BAA+B,GAAG;AACxF,QAAM,YAAY,MAAM,UAAU,IAAI,SAAS,OAAO,oCAAoC,GAAG;AAC7F,QAAM,WAAW,MAAM,UAAU,IAAI,SAAS,OAAO,kDAAkD,GAAG;AAE1G,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY,YAAY,KAAK,cAAc,KAAK,CAAC;AAAA,IACjD,iBAAiB,YAAY,KAAK,eAAe,KAAK,CAAC;AAAA,IACvD,uBAAuB,YAAY,KAAK,iBAAiB,KAAK,CAAC;AAAA,IAC/D,oBAAoB,YAAY,KAAK,UAAU,KAAK,CAAC;AAAA,IACrD,oBAAoB,YAAY,KAAK,UAAU,KAAK,CAAC;AAAA,IACrD,qBAAqB,YAAY,KAAK,SAAS,KAAK,CAAC;AAAA,EACvD;AACF;AAGA,SAAS,sBACP,WACA,UACA,QAC2G;AAC3G,QAAM,kBAAkB,cAAc,MAAM;AAC5C,QAAM,UAA2D;AAAA,IAC/D,kBAAkB,CAAC;AAAA,IACnB,kBAAkB,CAAC;AAAA,IACnB,mBAAmB,OAAO,EAAE,MAAM,kBAAkB,CAAC,GAAG,OAAO,gBAAgB,IAAI,kBAAkB,CAAC;AAAA,IACtG,kBAAkB,CAAC;AAAA,EACrB;AACA,SAAO;AAAA,IACL,OAAO;AAAA,IACP;AAAA,IACA,OAAO,CAAC;AAAA,EACV;AACF;AAGA,SAAS,kBAAkB,UAAkB,SAAuB;AAClE,QAAM,MAAM,KAAK,QAAQ,QAAQ;AACjC,MAAI,CAAC,GAAG,WAAW,GAAG,GAAG;AACvB,OAAG,UAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAAA,EACvC;AACA,KAAG,cAAc,UAAU,SAAS,OAAO;AAC7C;AAGA,SAAS,eACP,KACA,WACA,OACM;AACN,QAAM,eAAe,KAAK,QAAQ,KAAK,SAAS;AAChD,QAAM,cAAc,UAAU,OAAO,EAAE,WAAW,EAAE,CAAC;AACrD,oBAAkB,cAAc,WAAW;AAC7C;AAGA,SAAS,cACP,UACA,SACA,KACM;AACN,oBAAkB,UAAU,OAAO;AACnC,MAAI,QAAQ;AACd;AAEA,IAAM,gBAAgB;AAEtB,SAAS,gBACP,KACA,WACA,SACA,SACA,KACM;AACN,QAAM,iBAAiB,KAAK,QAAQ,KAAK,WAAW,OAAO;AAC3D,QAAM,iBAAiB,KAAK,KAAK,gBAAgB,gBAAgB;AACjE,QAAM,UAAU,uBAAuB,WAAW,SAAS,OAAO;AAClE,gBAAc,gBAAgB,SAAS,CAAC,MAAM,IAAI,wBAAwB,CAAC,EAAE,CAAC;AAChF;AAEA,SAAS,gBACP,KACA,WACA,SACA,SACA,KACM;AACN,QAAM,iBAAiB,KAAK,QAAQ,KAAK,WAAW,OAAO;AAC3D,QAAM,iBAAiB,KAAK,KAAK,gBAAgB,eAAe;AAChE,QAAM,UAAU,uBAAuB,WAAW,SAAS,OAAO;AAClE,gBAAc,gBAAgB,SAAS,CAAC,MAAM,IAAI,6BAA6B,CAAC,EAAE,CAAC;AACrF;AAEA,SAAS,eACP,KACA,WACA,SACA,SACA,KACM;AACN,QAAM,kBAAkB,KAAK,QAAQ,KAAK,WAAW;AACrD,QAAM,eAAe,wBAAwB,WAAW,SAAS,OAAO;AACxE,MAAI,GAAG,WAAW,eAAe,GAAG;AAClC,UAAM,WAAW,GAAG,aAAa,iBAAiB,OAAO;AACzD,QAAI,SAAS,SAAS,aAAa,GAAG;AACpC,UAAI,yDAAyD;AAC7D;AAAA,IACF;AACA,OAAG,cAAc,iBAAiB,SAAS,QAAQ,IAAI,SAAS,eAAe,MAAM,OAAO;AAC5F,QAAI,kCAAkC,eAAe,EAAE;AAAA,EACzD,OAAO;AACL,OAAG,cAAc,iBAAiB,eAAe,MAAM,OAAO;AAC9D,QAAI,+BAA+B,eAAe,EAAE;AAAA,EACtD;AACF;AAIO,SAAS,uBACd,WACA,UACA,SACQ;AACR,SAAO;AAAA;AAAA;AAAA;AAAA,EAIP,wBAAwB,WAAW,UAAU,OAAO,CAAC;AACvD;AAGO,SAAS,uBACd,WACA,UACA,SACQ;AACR,SAAO,wBAAwB,WAAW,UAAU,OAAO;AAC7D;AAGO,SAAS,wBACd,WACA,UACA,SACQ;AACR,SAAO;AAAA;AAAA,EAEP,wBAAwB,WAAW,UAAU,OAAO,CAAC;AACvD;AAWA,eAAsB,QAAQ,SAAyC;AACrE,QAAM,UAAU,SAAS;AACzB,QAAM,MAAM,SAAS,OAAO,QAAQ,IAAI;AACxC,QAAM,cAAc,EAAE,SAAS,EAAE;AACjC,QAAM,KAAK,YAAY,SAAY,SAAS,gBAAgB,EAAE,OAAO,QAAQ,OAAO,QAAQ,QAAQ,OAAO,CAAC,IAAI;AAChH,QAAM,MAAM,CAAC,QAAgB;AAC3B,QAAI,GAAI,SAAQ,OAAO,MAAM,MAAM,IAAI;AAAA,EACzC;AAEA,MAAI;AACF,QAAI,IAAI;AACN,cAAQ,OAAO,MAAM,iFAA4E;AAAA,IACnG;AAEA,UAAM,YAAY,MAAM,mBAAmB,IAAI,SAAS,WAAW;AACnE,QAAI,CAAC,UAAU,WAAW;AACxB,cAAQ,OAAO,MAAM,2BAA2B;AAChD,cAAQ,WAAW;AACnB;AAAA,IACF;AAEA,QAAI,MAAM;AAEV,UAAM,QAAQ,sBAAsB,UAAU,WAAW,UAAU,UAAU,UAAU,MAAM;AAC7F,mBAAe,KAAK,UAAU,WAAW,KAAK;AAE9C,UAAM,eAAe,KAAK,QAAQ,KAAK,UAAU,SAAS;AAC1D,QAAI;AAAA,mBAAsB,YAAY,EAAE;AACxC,QAAI,oBAAoB,KAAK,KAAK,KAAK,QAAQ,UAAU,SAAS,GAAG,UAAU,QAAQ,CAAC,eAAe;AAEvG,UAAM,UAAU,KAAK,KAAK,KAAK,QAAQ,UAAU,SAAS,GAAG,UAAU,QAAQ;AAC/E,UAAM,cAAoC;AAAA,MACxC,YAAY,UAAU;AAAA,MACtB,iBAAiB,UAAU;AAAA,MAC3B,uBAAuB,UAAU;AAAA,IACnC;AAEA,QAAI,UAAU,mBAAoB,iBAAgB,KAAK,UAAU,WAAW,SAAS,aAAa,GAAG;AACrG,QAAI,UAAU,mBAAoB,iBAAgB,KAAK,UAAU,WAAW,SAAS,aAAa,GAAG;AACrG,QAAI,UAAU,oBAAqB,gBAAe,KAAK,UAAU,WAAW,SAAS,aAAa,GAAG;AAErG,QAAI,gCAAgC,UAAU,YAAY,0CAA0C,UAAU,YAAY,YAAY;AAAA,EACxI,SAAS,KAAK;AACZ,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,YAAQ,OAAO,MAAM,UAAU,IAAI;AACnC,YAAQ,WAAW;AAAA,EACrB,UAAE;AACA,QAAI,MAAM;AAAA,EACZ;AACF;;;AEhUA,OAAO,aAA8C;AACrD,OAAOC,SAAQ;AACf,OAAOC,WAAU;AACjB,SAAS,oBAAoB;AAC7B,SAAoB,uBAAuB;AAC3C,SAAS,qBAAqB;AAG9B,IAAMC,aAAYC,MAAK,QAAQ,cAAc,YAAY,GAAG,CAAC;AAO7D,SAAS,sBAA8B;AACrC,QAAM,UAAUA,MAAK,QAAQD,YAAW,MAAM,eAAe;AAC7D,MAAIE,IAAG,WAAW,OAAO,KAAKA,IAAG,SAAS,OAAO,EAAE,YAAY,GAAG;AAChE,WAAO;AAAA,EACT;AACA,SAAOD,MAAK,QAAQD,YAAW,MAAM,MAAM,YAAY,MAAM;AAC/D;AAKA,SAAS,iBAAiB,WAAkC;AAC1D,MAAI;AACF,UAAM,WAAWC,MAAK,QAAQ,QAAQ,IAAI,GAAG,SAAS;AACtD,WAAOC,IAAG,aAAa,UAAU,OAAO;AAAA,EAC1C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAYO,SAAS,kBAAkB,SAAoC;AACpE,QAAM,EAAE,WAAW,OAAO,qBAAqB,cAAc,MAAM,IAAI;AACvE,QAAM,eAAe,oBAAoB;AAEzC,MAAI,CAACA,IAAG,WAAW,YAAY,KAAK,CAACA,IAAG,SAAS,YAAY,EAAE,YAAY,GAAG;AAC5E,YAAQ,OAAO;AAAA,MACb;AAAA,IACF;AACA,YAAQ,WAAW;AACnB;AAAA,EACF;AAEA,QAAM,MAAM,QAAQ;AAGpB,QAAM,oBAAoBD,MAAK,QAAQ,QAAQ,IAAI,GAAG,SAAS;AAC/D,QAAM,WAAWA,MAAK,QAAQ,iBAAiB;AAG/C,MAAI,IAAI,cAAc,CAAC,MAAe,QAAkB;AACtD,UAAM,UAAU,iBAAiB,SAAS;AAC1C,QAAI,YAAY,MAAM;AACpB,UAAI,OAAO,GAAG,EAAE,KAAK,YAAY,EAAE,KAAK,qCAAqC;AAC7E;AAAA,IACF;AACA,QAAI,KAAK,WAAW,EAAE,KAAK,OAAO;AAAA,EACpC,CAAC;AAGD,MAAI,IAAI,aAAa,CAAC,KAAc,QAAkB;AACpD,UAAM,UAAU,OAAO,IAAI,MAAM,SAAS,WAAW,IAAI,MAAM,OAAO;AACtE,QAAI,CAAC,WAAW,QAAQ,SAAS,IAAI,GAAG;AACtC,UAAI,OAAO,GAAG,EAAE,KAAK,YAAY,EAAE,KAAK,0BAA0B;AAClE;AAAA,IACF;AACA,UAAM,MAAMA,MAAK,QAAQ,OAAO,EAAE,YAAY;AAC9C,QAAI,QAAQ,OAAO;AACjB,UAAI,OAAO,GAAG,EAAE,KAAK,YAAY,EAAE,KAAK,kCAAkC;AAC1E;AAAA,IACF;AACA,UAAM,WAAWA,MAAK,QAAQ,UAAU,OAAO;AAC/C,UAAM,WAAWA,MAAK,SAAS,UAAU,QAAQ;AACjD,QAAI,SAAS,WAAW,IAAI,KAAKA,MAAK,WAAW,QAAQ,GAAG;AAC1D,UAAI,OAAO,GAAG,EAAE,KAAK,YAAY,EAAE,KAAK,qCAAqC;AAC7E;AAAA,IACF;AACA,QAAI;AACF,YAAM,UAAUC,IAAG,aAAa,UAAU,OAAO;AACjD,UAAI,KAAK,eAAe,EAAE,KAAK,OAAO;AAAA,IACxC,QAAQ;AACN,UAAI,OAAO,GAAG,EAAE,KAAK,YAAY,EAAE,KAAK,oCAAoC;AAAA,IAC9E;AAAA,EACF,CAAC;AAGD,MAAI,IAAI,QAAQ,OAAO,YAAY,CAAC;AAGpC,MAAI,IAAI,CAAC,MAAe,KAAe,SAA+B;AACpE,QAAI,KAAK,WAAW,SAAS,IAAI,YAAa,QAAO,KAAK;AAC1D,UAAM,YAAYD,MAAK,KAAK,cAAc,YAAY;AACtD,QAAIC,IAAG,WAAW,SAAS,GAAG;AAC5B,UAAI,SAAS,SAAS;AAAA,IACxB,OAAO;AACL,UAAI,OAAO,GAAG,EAAE,KAAK,WAAW;AAAA,IAClC;AAAA,EACF,CAAC;AAED,QAAM,SAAS,aAAa,GAAG;AAG/B,QAAM,MAAM,IAAI,gBAAgB,EAAE,UAAU,KAAK,CAAC;AAElD,SAAO,GAAG,WAAW,CAAC,SAAS,QAAQ,SAAS;AAC9C,UAAM,MAAM,IAAI,IAAI,QAAQ,OAAO,IAAI,UAAU,QAAQ,QAAQ,IAAI,EAAE;AACvE,QAAI,IAAI,aAAa,oBAAoB;AACvC,UAAI,cAAc,SAAS,QAAQ,MAAM,CAAC,OAAkB;AAC1D,YAAI,KAAK,cAAc,IAAI,OAAO;AAAA,MACpC,CAAC;AAAA,IACH,OAAO;AACL,aAAO,QAAQ;AAAA,IACjB;AAAA,EACF,CAAC;AAED,MAAI,gBAAsD;AAE1D,WAAS,iBAAuB;AAC9B,UAAM,UAAU,iBAAiB,SAAS;AAC1C,UAAM,UAAU,WAAW;AAC3B,QAAI,QAAQ,QAAQ,CAAC,WAAsB;AACzC,UAAI,OAAO,eAAe,GAAG;AAC3B,eAAO,KAAK,OAAO;AAAA,MACrB;AAAA,IACF,CAAC;AAAA,EACH;AAEA,MAAI,GAAG,cAAc,CAAC,OAAkB;AAEtC,UAAM,UAAU,iBAAiB,SAAS;AAC1C,QAAI,YAAY,MAAM;AACpB,SAAG,KAAK,OAAO;AAAA,IACjB;AAAA,EACF,CAAC;AAED,MAAI;AACF,IAAAA,IAAG,MAAM,mBAAmB,EAAE,YAAY,MAAM,GAAG,MAAM;AACvD,UAAI,cAAe,cAAa,aAAa;AAC7C,sBAAgB,WAAW,MAAM;AAC/B,wBAAgB;AAChB,uBAAe;AAAA,MACjB,GAAG,wBAAwB;AAAA,IAC7B,CAAC;AAAA,EACH,QAAQ;AAAA,EAER;AAEA,SAAO,GAAG,SAAS,CAAC,QAAmC;AACrD,YAAQ,OAAO,MAAM,wBAAwB,IAAI,OAAO;AAAA,CAAI;AAC5D,QAAI,IAAI,SAAS,cAAc;AAC7B,cAAQ,OAAO,MAAM,QAAQ,IAAI;AAAA,CAAoC;AAAA,IACvE;AACA,YAAQ,WAAW;AAAA,EACrB,CAAC;AAED,SAAO,OAAO,MAAM,MAAM;AACxB,UAAM,MAAM,oBAAoB,IAAI;AACpC,YAAQ,OAAO,MAAM,QAAQ,GAAG;AAAA,CAAI;AACpC,QAAI,aAAa;AACf,aAAO,MAAM,EAAE,KAAK,CAAC,EAAE,SAAS,KAAK,MAAM;AACzC,aAAK,GAAG,EAAE,MAAM,MAAM;AAAA,QAAC,CAAC;AAAA,MAC1B,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AAED,QAAM,WAAW,MAAM;AACrB,WAAO,MAAM,MAAM;AACjB,cAAQ,KAAK,QAAQ,YAAY,CAAC;AAAA,IACpC,CAAC;AAAA,EACH;AACA,UAAQ,GAAG,UAAU,QAAQ;AAC7B,UAAQ,GAAG,WAAW,QAAQ;AAChC;;;ACxLO,SAAS,UAAUC,OAAc,SAAgD;AACtF,oBAAkB;AAAA,IAChB,WAAWA;AAAA,IACX,MAAM,QAAQ;AAAA,IACd,aAAa,QAAQ;AAAA,EACvB,CAAC;AACH;;;ACRA,OAAOC,SAAQ;AACf,OAAOC,WAAU;AACjB,SAAS,iBAAAC,sBAAqB;;;ACC9B,OAAOC,SAAQ;AACf,OAAOC,WAAU;AAMV,SAAS,gBAAgB,UAAiC;AAC/D,MAAI,MAAMA,MAAK,QAAQ,QAAQ;AAC/B,aAAS;AACP,UAAM,UAAUA,MAAK,KAAK,KAAK,cAAc;AAC7C,QAAID,IAAG,WAAW,OAAO,EAAG,QAAO;AACnC,UAAM,SAASC,MAAK,QAAQ,GAAG;AAC/B,QAAI,WAAW,IAAK,QAAO;AAC3B,UAAM;AAAA,EACR;AACF;;;ADbO,SAAS,UAAgB;AAC9B,QAAM,WAAWC,eAAc,YAAY,GAAG;AAC9C,QAAM,WAAWC,MAAK,QAAQ,QAAQ;AACtC,QAAM,cAAc,gBAAgB,QAAQ;AAC5C,MAAI,CAAC,aAAa;AAChB,YAAQ,OAAO,MAAM,gDAAgD;AACrE,YAAQ,WAAW;AACnB;AAAA,EACF;AACA,QAAM,WAAWA,MAAK,KAAK,aAAa,aAAa;AACrD,MAAI,CAACC,IAAG,WAAW,QAAQ,GAAG;AAC5B,YAAQ,OAAO,MAAM,2CAA2C,QAAQ;AAAA,CAAI;AAC5E,YAAQ,WAAW;AACnB;AAAA,EACF;AACA,MAAI;AACF,UAAM,UAAUA,IAAG,aAAa,UAAU,OAAO;AACjD,YAAQ,OAAO,MAAM,OAAO;AAC5B,QAAI,CAAC,QAAQ,SAAS,IAAI,EAAG,SAAQ,OAAO,MAAM,IAAI;AACtD,YAAQ,WAAW;AAAA,EACrB,SAAS,KAAK;AACZ,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,YAAQ,OAAO,MAAM,oBAAoB,OAAO;AAAA,CAAI;AACpD,YAAQ,WAAW;AAAA,EACrB;AACF;;;AE/BA,SAAS,YAAY,iBAAiB;AAE/B,SAAS,aAAaC,OAAoB;AAC/C,MAAI;AACF,UAAM,QAAQ,WAAWA,KAAI;AAC7B,UAAM,UAAU,UAAU,KAAK;AAC/B,YAAQ,OAAO,MAAM,OAAO;AAC5B,YAAQ,WAAW;AAAA,EACrB,SAAS,KAAK;AACZ,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,YAAQ,OAAO,MAAM,UAAU,IAAI;AACnC,YAAQ,WAAW;AAAA,EACrB;AACF;;;ACbA,SAAS,cAAAC,aAAY,gBAAgB;AAErC,SAAS,sBAAsB,KAAiD;AAC9E,QAAM,SAAS,IAAI,QAAQ,QAAQ,IAAI,SAAS,KAAK,GAAG,IAAI,IAAI,OAAO;AACvE,SAAO,GAAG,MAAM,GAAG,IAAI,OAAO;AAChC;AAEO,SAAS,YAAYC,OAAoB;AAC9C,MAAI;AACF,UAAM,QAAQD,YAAWC,KAAI;AAC7B,UAAM,SAAS,SAAS,KAAK;AAC7B,QAAI,CAAC,OAAO,OAAO;AACjB,iBAAW,OAAO,OAAO,QAAQ;AAC/B,gBAAQ,OAAO,MAAM,sBAAsB,GAAG,IAAI,IAAI;AAAA,MACxD;AACA,cAAQ,WAAW;AACnB;AAAA,IACF;AACA,YAAQ,WAAW;AAAA,EACrB,SAAS,KAAK;AACZ,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,YAAQ,OAAO,MAAM,UAAU,IAAI;AACnC,YAAQ,WAAW;AAAA,EACrB;AACF;;;ACnBA,OAAOC,SAAQ;AACf,OAAO,QAAQ;AACf,OAAOC,WAAU;AACjB,OAAO,YAAY;AAEnB,IAAM,eAAe;AACrB,IAAM,eAAe,KAAK,KAAK,KAAK;AACpC,IAAM,mBAAmB;AACzB,IAAM,iBAAiB;AAOvB,SAAS,eAAuB;AAC9B,QAAM,MAAM,GAAG,OAAO;AACtB,SAAOA,MAAK,KAAK,KAAK,cAAc;AACtC;AAEA,SAAS,YAA+B;AACtC,MAAI;AACF,UAAM,IAAI,aAAa;AACvB,UAAM,MAAMD,IAAG,aAAa,GAAG,OAAO;AACtC,UAAM,OAAO,KAAK,MAAM,GAAG;AAC3B,QAAI,OAAO,KAAK,cAAc,YAAY,OAAO,KAAK,kBAAkB,UAAU;AAChF,aAAO;AAAA,IACT;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAEA,SAAS,WAAW,eAA6B;AAC/C,MAAI;AACF,UAAM,IAAI,aAAa;AACvB,UAAM,OAAmB,EAAE,WAAW,KAAK,IAAI,GAAG,cAAc;AAChE,IAAAA,IAAG,cAAc,GAAG,KAAK,UAAU,IAAI,GAAG,OAAO;AAAA,EACnD,QAAQ;AAAA,EAER;AACF;AAEA,eAAe,qBAA6C;AAC1D,QAAM,aAAa,IAAI,gBAAgB;AACvC,QAAM,UAAU,WAAW,MAAM,WAAW,MAAM,GAAG,gBAAgB;AACrE,MAAI;AACF,UAAM,MAAM,MAAM,MAAM,cAAc,EAAE,QAAQ,WAAW,OAAO,CAAC;AACnE,iBAAa,OAAO;AACpB,QAAI,CAAC,IAAI,GAAI,QAAO;AACpB,UAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,WAAO,OAAO,KAAK,YAAY,WAAW,KAAK,UAAU;AAAA,EAC3D,QAAQ;AACN,iBAAa,OAAO;AACpB,WAAO;AAAA,EACT;AACF;AAEA,SAAS,WAAW,SAA2C;AAC7D,MAAI,QAAQ,IAAI,OAAO,UAAU,QAAQ,IAAI,OAAO,IAAK,QAAO;AAChE,MAAI,QAAQ,IAAI,+BAA+B,OAAO,QAAQ,IAAI,+BAA+B,OAAQ,QAAO;AAChH,MAAI,QAAQ,WAAY,QAAO;AAC/B,SAAO;AACT;AAOO,SAAS,eAAe,gBAAwB,SAAwC;AAC7F,MAAI,WAAW,OAAO,EAAG;AAEzB,QAAM,YAAY;AAChB,QAAI,SAAwB;AAC5B,UAAM,SAAS,UAAU;AACzB,QAAI,UAAU,KAAK,IAAI,IAAI,OAAO,YAAY,cAAc;AAC1D,eAAS,OAAO;AAAA,IAClB;AACA,QAAI,WAAW,MAAM;AACnB,eAAS,MAAM,mBAAmB;AAClC,UAAI,WAAW,KAAM,YAAW,MAAM;AAAA,IACxC;AACA,QAAI,WAAW,KAAM;AACrB,QAAI;AACF,UAAI,OAAO,GAAG,QAAQ,cAAc,GAAG;AACrC,gBAAQ,OAAO;AAAA,UACb,6CAA6C,MAAM,cAAc,cAAc;AAAA;AAAA,QACjF;AAAA,MACF;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF,GAAG;AACL;;;AV3FA,IAAME,WAAU,cAAc,YAAY,GAAG;AAC7C,IAAM,EAAE,QAAQ,IAAIA,SAAQ,iBAAiB;AAG7C,SAAS,aAAsB;AAC7B,MAAI;AACF,UAAM,MAAMC,MAAK,QAAQC,eAAc,YAAY,GAAG,CAAC;AACvD,WAAO,wBAAwB,KAAK,GAAG,KAAK,IAAI,SAAS,eAAeD,MAAK,MAAM,aAAaA,MAAK,MAAM,KAAK;AAAA,EAClH,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,oBAAoB,OAAuB;AAClD,MAAI,MAAM,SAAS,GAAG;AACpB,YAAQ,OAAO,MAAM,kEAAkE;AACvF,YAAQ,WAAW;AAAA,EACrB;AACF;AAEA,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,YAAY,EACjB,YAAY,wDAAwD,EACpE,QAAQ,OAAO;AAElB,QACG,QAAQ,MAAM,EACd,YAAY,4EAA4E,EACxF,OAAO,YAAY;AAClB,QAAM,QAAQ,EAAE,MAAM,CAAC,QAAQ;AAC7B,YAAQ,OAAO,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,IAAI,IAAI;AAC5E,YAAQ,WAAW;AAAA,EACrB,CAAC;AACH,CAAC;AAEH,QACG,QAAQ,MAAM,EACd,YAAY,6CAA6C,EACzD,OAAO,MAAM;AACZ,UAAQ;AACV,CAAC;AAEH,QACG,QAAQ,UAAU,EAClB,YAAY,sDAAsD,EAClE,SAAS,UAAU,sBAAsB,kBAAkB,EAC3D,SAAS,cAAc,iCAAiC,EACxD,OAAO,CAACA,OAAc,UAAoB;AACzC,sBAAoB,KAAK;AACzB,MAAI,QAAQ,aAAa,EAAG,aAAYA,KAAI;AAC9C,CAAC;AAEH,QACG,QAAQ,WAAW,EACnB,YAAY,yCAAyC,EACrD,SAAS,UAAU,sBAAsB,kBAAkB,EAC3D,SAAS,cAAc,iCAAiC,EACxD,OAAO,CAACA,OAAc,UAAoB;AACzC,sBAAoB,KAAK;AACzB,MAAI,QAAQ,aAAa,EAAG,cAAaA,KAAI;AAC/C,CAAC;AAEH,QACG,QAAQ,QAAQ,EAChB,YAAY,+CAA+C,EAC3D,SAAS,UAAU,sBAAsB,kBAAkB,EAC3D,OAAO,uBAAuB,uBAAuB,OAAO,mBAAmB,CAAC,EAChF,OAAO,UAAU,oCAAoC,EACrD,OAAO,CAACA,OAAc,YAA6C;AAClE,QAAM,OAAO,SAAS,QAAQ,MAAM,EAAE,KAAK;AAC3C,YAAUA,OAAM,EAAE,MAAM,MAAM,QAAQ,QAAQ,MAAM,CAAC;AACvD,CAAC;AAEH,QAAQ,WAAW,EAAE,KAAK,MAAM;AAC9B,iBAAe,SAAS,EAAE,YAAY,WAAW,EAAE,CAAC;AACtD,CAAC;","names":["path","fileURLToPath","fs","path","__dirname","path","fs","path","fs","path","fileURLToPath","fs","path","fileURLToPath","path","fs","path","parseBoard","path","fs","path","require","path","fileURLToPath"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stcrft/statecraft",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "CLI for Statecraft: validate, summarize, and render board-as-code",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
"open": "^11.0.0",
|
|
26
26
|
"ws": "^8.18.0",
|
|
27
27
|
"yaml": "^2.8.2",
|
|
28
|
-
"
|
|
28
|
+
"semver": "^7.6.3",
|
|
29
|
+
"@stcrft/statecraft-core": "1.4.0"
|
|
29
30
|
},
|
|
30
31
|
"devDependencies": {
|
|
31
32
|
"@types/express": "^5.0.0",
|