runcastle 0.2.0 → 1.0.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.
Files changed (80) hide show
  1. package/README.md +47 -0
  2. package/bin/runcastle.js +5580 -0
  3. package/drizzle/0000_amused_bruce_banner.sql +74 -0
  4. package/drizzle/0001_mapped_feature_flag.sql +1 -0
  5. package/drizzle/0002_waypoints.sql +14 -0
  6. package/drizzle/0003_project_closed_at.sql +1 -0
  7. package/drizzle/0004_events_project_id.sql +28 -0
  8. package/drizzle/0005_project_setting_overrides.sql +2 -0
  9. package/drizzle/meta/0000_snapshot.json +466 -0
  10. package/drizzle/meta/0001_snapshot.json +474 -0
  11. package/drizzle/meta/0002_snapshot.json +568 -0
  12. package/drizzle/meta/0003_snapshot.json +575 -0
  13. package/drizzle/meta/0004_snapshot.json +582 -0
  14. package/drizzle/meta/0005_snapshot.json +596 -0
  15. package/drizzle/meta/_journal.json +48 -0
  16. package/hook-client.ts +67 -0
  17. package/index.js +5424 -0
  18. package/package.json +24 -30
  19. package/pty-host.cjs +186 -0
  20. package/sandcastle-template/Containerfile +33 -0
  21. package/sandcastle-template/Dockerfile +33 -0
  22. package/skills/burner/implement-ticket.md +42 -0
  23. package/skills/burner/research-waypoint.md +45 -0
  24. package/skills/packs/README.md +50 -0
  25. package/skills/packs/runcastle/.claude-plugin/plugin.json +6 -0
  26. package/skills/packs/runcastle/skills/converge/SKILL.md +51 -0
  27. package/skills/packs/runcastle/skills/ideate/SKILL.md +81 -0
  28. package/skills/packs/runcastle/skills/qa/SKILL.md +30 -0
  29. package/skills/packs/runcastle/skills/spec/SKILL.md +43 -0
  30. package/skills/packs/runcastle/skills/tickets/SKILL.md +52 -0
  31. package/skills/packs/runcastle/skills/waypoint/SKILL.md +54 -0
  32. package/web/assets/index-CKZ5Z7vc.css +1 -0
  33. package/web/assets/index-YBuT7tTz.js +108 -0
  34. package/web/index.html +13 -0
  35. package/dist/add-DD7G73HH.js +0 -315
  36. package/dist/add-DD7G73HH.js.map +0 -1
  37. package/dist/chunk-44KDPUYJ.js +0 -49
  38. package/dist/chunk-44KDPUYJ.js.map +0 -1
  39. package/dist/chunk-5VPKMXSV.js +0 -190
  40. package/dist/chunk-5VPKMXSV.js.map +0 -1
  41. package/dist/chunk-6CWCR5SO.js +0 -24
  42. package/dist/chunk-6CWCR5SO.js.map +0 -1
  43. package/dist/chunk-7CHGGMP7.js +0 -73
  44. package/dist/chunk-7CHGGMP7.js.map +0 -1
  45. package/dist/chunk-AHRTPV2C.js +0 -1082
  46. package/dist/chunk-AHRTPV2C.js.map +0 -1
  47. package/dist/chunk-GU4LRYYH.js +0 -41
  48. package/dist/chunk-GU4LRYYH.js.map +0 -1
  49. package/dist/chunk-LP5MV76P.js +0 -33
  50. package/dist/chunk-LP5MV76P.js.map +0 -1
  51. package/dist/chunk-OJ356IMJ.js +0 -136
  52. package/dist/chunk-OJ356IMJ.js.map +0 -1
  53. package/dist/chunk-PRNBOQHW.js +0 -32
  54. package/dist/chunk-PRNBOQHW.js.map +0 -1
  55. package/dist/chunk-QTC5Y3LU.js +0 -146
  56. package/dist/chunk-QTC5Y3LU.js.map +0 -1
  57. package/dist/chunk-SNUH2UT6.js +0 -95
  58. package/dist/chunk-SNUH2UT6.js.map +0 -1
  59. package/dist/chunk-UG25BZUN.js +0 -59
  60. package/dist/chunk-UG25BZUN.js.map +0 -1
  61. package/dist/chunk-VWAIXSB6.js +0 -78
  62. package/dist/chunk-VWAIXSB6.js.map +0 -1
  63. package/dist/index.js +0 -99
  64. package/dist/index.js.map +0 -1
  65. package/dist/info-3PW6A6JA.js +0 -80
  66. package/dist/info-3PW6A6JA.js.map +0 -1
  67. package/dist/init-MOJNLWXK.js +0 -212
  68. package/dist/init-MOJNLWXK.js.map +0 -1
  69. package/dist/logout-YPGZ6BB6.js +0 -27
  70. package/dist/logout-YPGZ6BB6.js.map +0 -1
  71. package/dist/publish-W37Y6TO6.js +0 -211
  72. package/dist/publish-W37Y6TO6.js.map +0 -1
  73. package/dist/run-M4XXNPGD.js +0 -291
  74. package/dist/run-M4XXNPGD.js.map +0 -1
  75. package/dist/search-2KNAXPM5.js +0 -55
  76. package/dist/search-2KNAXPM5.js.map +0 -1
  77. package/dist/update-UCT4K6IQ.js +0 -125
  78. package/dist/update-UCT4K6IQ.js.map +0 -1
  79. package/dist/validate-GFDSEVPV.js +0 -12
  80. package/dist/validate-GFDSEVPV.js.map +0 -1
@@ -1,95 +0,0 @@
1
- import {
2
- PAYLOAD_DIR,
3
- normalizePath,
4
- validatePackage
5
- } from "./chunk-AHRTPV2C.js";
6
- import {
7
- CliError
8
- } from "./chunk-6CWCR5SO.js";
9
- import {
10
- emitJson,
11
- isJsonMode,
12
- log,
13
- pc
14
- } from "./chunk-PRNBOQHW.js";
15
-
16
- // src/lib/package-loader.ts
17
- import fsp from "fs/promises";
18
- import path from "path";
19
- async function walk(dir, root, out) {
20
- let entries;
21
- try {
22
- entries = await fsp.readdir(dir, { withFileTypes: true });
23
- } catch {
24
- return;
25
- }
26
- for (const entry of entries) {
27
- const abs = path.join(dir, entry.name);
28
- if (entry.isDirectory()) {
29
- await walk(abs, root, out);
30
- } else if (entry.isFile()) {
31
- out.push(normalizePath(path.relative(root, abs)));
32
- }
33
- }
34
- }
35
- async function loadPackage(root) {
36
- const abs = path.resolve(root);
37
- const files = /* @__PURE__ */ new Map();
38
- const bytes = /* @__PURE__ */ new Map();
39
- const candidates = [];
40
- for (const top of ["workflow.json", "README.md"]) {
41
- try {
42
- const st = await fsp.stat(path.join(abs, top));
43
- if (st.isFile()) candidates.push(top);
44
- } catch {
45
- }
46
- }
47
- await walk(path.join(abs, PAYLOAD_DIR), abs, candidates);
48
- for (const rel of candidates) {
49
- const buf = await fsp.readFile(path.join(abs, ...rel.split("/")));
50
- const bytesForFile = new Uint8Array(buf);
51
- bytes.set(rel, bytesForFile);
52
- files.set(rel, { content: bytesForFile, sizeBytes: bytesForFile.byteLength });
53
- }
54
- if (!files.has("workflow.json")) {
55
- throw new CliError(`no workflow.json found in ${abs}`, {
56
- code: "E_NO_MANIFEST",
57
- details: ["Run `runcastle init` to scaffold one, or pass the correct package path."]
58
- });
59
- }
60
- return { pkg: { files }, bytes };
61
- }
62
-
63
- // src/commands/validate.ts
64
- async function runValidation(pkgPath) {
65
- const { pkg } = await loadPackage(pkgPath);
66
- return validatePackage(pkg);
67
- }
68
- async function validateCommand(opts) {
69
- const pkgPath = opts.path ?? ".";
70
- const result = await runValidation(pkgPath);
71
- if (isJsonMode()) {
72
- emitJson({ ok: result.ok, errors: result.errors, warnings: result.warnings });
73
- if (!result.ok) throw new CliError("", { exitCode: 1, code: "E_VALIDATION" });
74
- return;
75
- }
76
- for (const w of result.warnings) {
77
- log(`${pc.yellow("warning")} ${pc.dim(`[${w.code}]`)} ${w.message}${w.file ? pc.dim(` (${w.file})`) : ""}`);
78
- }
79
- for (const e of result.errors) {
80
- log(`${pc.red("error")} ${pc.dim(`[${e.code}]`)} ${e.message}${e.file ? pc.dim(` (${e.file})`) : ""}`);
81
- }
82
- if (result.ok) {
83
- log(pc.green(`\u2713 valid workflow package${result.warnings.length ? ` (${result.warnings.length} warning(s))` : ""}`));
84
- } else {
85
- log("");
86
- log(pc.red(`\u2717 ${result.errors.length} error(s), ${result.warnings.length} warning(s)`));
87
- throw new CliError("", { exitCode: 1, code: "E_VALIDATION" });
88
- }
89
- }
90
-
91
- export {
92
- runValidation,
93
- validateCommand
94
- };
95
- //# sourceMappingURL=chunk-SNUH2UT6.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/lib/package-loader.ts","../src/commands/validate.ts"],"sourcesContent":["// Load an on-disk workflow package into the schema's in-memory PackageInput so\n// the EXACT same validation engine runs locally (spec §10). Reads workflow.json,\n// README.md, and the entire .sandcastle/ payload as raw bytes (byte-accurate for\n// binary detection and size limits). Paths are POSIX-normalized on the way in.\n\nimport fsp from \"node:fs/promises\";\nimport path from \"node:path\";\nimport {\n PAYLOAD_DIR,\n normalizePath,\n type PackageInput,\n type PackageFile,\n} from \"@castellan/schema\";\nimport { CliError } from \"./errors.js\";\n\n/** Recursively list files under `dir`, returned as POSIX paths relative to `root`. */\nasync function walk(dir: string, root: string, out: string[]): Promise<void> {\n let entries: import(\"node:fs\").Dirent[];\n try {\n entries = await fsp.readdir(dir, { withFileTypes: true });\n } catch {\n return;\n }\n for (const entry of entries) {\n const abs = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n await walk(abs, root, out);\n } else if (entry.isFile()) {\n out.push(normalizePath(path.relative(root, abs)));\n }\n }\n}\n\n/**\n * Build a PackageInput from a package root directory. Includes workflow.json,\n * README.md (if present), and everything under .sandcastle/. Returns the raw\n * bytes per file keyed by POSIX relative path.\n */\nexport async function loadPackage(root: string): Promise<{ pkg: PackageInput; bytes: Map<string, Uint8Array> }> {\n const abs = path.resolve(root);\n const files = new Map<string, PackageFile>();\n const bytes = new Map<string, Uint8Array>();\n\n const candidates: string[] = [];\n for (const top of [\"workflow.json\", \"README.md\"]) {\n try {\n const st = await fsp.stat(path.join(abs, top));\n if (st.isFile()) candidates.push(top);\n } catch {\n /* absent */\n }\n }\n await walk(path.join(abs, PAYLOAD_DIR), abs, candidates);\n\n for (const rel of candidates) {\n const buf = await fsp.readFile(path.join(abs, ...rel.split(\"/\")));\n const bytesForFile = new Uint8Array(buf);\n bytes.set(rel, bytesForFile);\n files.set(rel, { content: bytesForFile, sizeBytes: bytesForFile.byteLength });\n }\n\n if (!files.has(\"workflow.json\")) {\n throw new CliError(`no workflow.json found in ${abs}`, {\n code: \"E_NO_MANIFEST\",\n details: [\"Run `runcastle init` to scaffold one, or pass the correct package path.\"],\n });\n }\n\n return { pkg: { files }, bytes };\n}\n","// `runcastle validate [path]` (spec §5.2). Runs the full §4.3 engine locally via\n// the shared @castellan/schema validator. Exit 0 on ok, 1 on errors. `--json`\n// emits `{ ok, errors, warnings }` with the frozen codes.\n\nimport { validatePackage, type ValidationResult } from \"@castellan/schema\";\nimport { loadPackage } from \"../lib/package-loader.js\";\nimport { emitJson, isJsonMode, log, pc } from \"../lib/output.js\";\nimport { CliError } from \"../lib/errors.js\";\n\nexport interface ValidateOptions {\n path?: string;\n json?: boolean;\n}\n\n/** Run validation and return the result (shared with `publish`). */\nexport async function runValidation(pkgPath: string): Promise<ValidationResult> {\n const { pkg } = await loadPackage(pkgPath);\n return validatePackage(pkg);\n}\n\nexport async function validateCommand(opts: ValidateOptions): Promise<void> {\n const pkgPath = opts.path ?? \".\";\n const result = await runValidation(pkgPath);\n\n if (isJsonMode()) {\n emitJson({ ok: result.ok, errors: result.errors, warnings: result.warnings });\n if (!result.ok) throw new CliError(\"\", { exitCode: 1, code: \"E_VALIDATION\" });\n return;\n }\n\n for (const w of result.warnings) {\n log(`${pc.yellow(\"warning\")} ${pc.dim(`[${w.code}]`)} ${w.message}${w.file ? pc.dim(` (${w.file})`) : \"\"}`);\n }\n for (const e of result.errors) {\n log(`${pc.red(\"error\")} ${pc.dim(`[${e.code}]`)} ${e.message}${e.file ? pc.dim(` (${e.file})`) : \"\"}`);\n }\n\n if (result.ok) {\n log(pc.green(`✓ valid workflow package${result.warnings.length ? ` (${result.warnings.length} warning(s))` : \"\"}`));\n } else {\n log(\"\");\n log(pc.red(`✗ ${result.errors.length} error(s), ${result.warnings.length} warning(s)`));\n throw new CliError(\"\", { exitCode: 1, code: \"E_VALIDATION\" });\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,59 +0,0 @@
1
- import {
2
- SANDCASTLE_PACKAGE_NAME
3
- } from "./chunk-AHRTPV2C.js";
4
-
5
- // src/lib/sandcastle-version.ts
6
- import fsp from "fs/promises";
7
- import path from "path";
8
- import semver from "semver";
9
- async function installedVersion(cwd) {
10
- const pkgJson = path.join(cwd, "node_modules", ...SANDCASTLE_PACKAGE_NAME.split("/"), "package.json");
11
- try {
12
- const raw = await fsp.readFile(pkgJson, "utf8");
13
- const v = JSON.parse(raw).version;
14
- if (v && semver.valid(v)) return v;
15
- } catch {
16
- }
17
- return void 0;
18
- }
19
- async function declaredRange(cwd) {
20
- try {
21
- const raw = await fsp.readFile(path.join(cwd, "package.json"), "utf8");
22
- const pkg = JSON.parse(raw);
23
- for (const field of ["dependencies", "devDependencies", "peerDependencies"]) {
24
- const range = pkg[field]?.[SANDCASTLE_PACKAGE_NAME];
25
- if (range) return range;
26
- }
27
- } catch {
28
- }
29
- return void 0;
30
- }
31
- async function detectSandcastle(cwd) {
32
- const [version, range] = await Promise.all([installedVersion(cwd), declaredRange(cwd)]);
33
- return {
34
- ...version ? { version } : {},
35
- ...range ? { range } : {}
36
- };
37
- }
38
- function compatibilityNote(workflowRange, detected) {
39
- const { version, range } = detected;
40
- if (version && semver.valid(version) && semver.validRange(workflowRange)) {
41
- if (!semver.satisfies(version, workflowRange)) {
42
- return `Your installed ${SANDCASTLE_PACKAGE_NAME} is ${version}, outside this workflow's tested range ${workflowRange}.`;
43
- }
44
- return void 0;
45
- }
46
- if (range && semver.validRange(range) && semver.validRange(workflowRange)) {
47
- const min = semver.minVersion(range);
48
- if (min && !semver.satisfies(min, workflowRange)) {
49
- return `Your ${SANDCASTLE_PACKAGE_NAME} dependency (${range}) may fall outside this workflow's tested range ${workflowRange}.`;
50
- }
51
- }
52
- return void 0;
53
- }
54
-
55
- export {
56
- detectSandcastle,
57
- compatibilityNote
58
- };
59
- //# sourceMappingURL=chunk-UG25BZUN.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/lib/sandcastle-version.ts"],"sourcesContent":["// Best-effort detection of the user's installed @ai-hero/sandcastle version so\n// `add` can warn when it falls outside the workflow's tested versionRange\n// (spec §5.4.3a). Reads (in order) the installed package's package.json, then\n// the consumer's own package.json dependency ranges.\n\nimport fsp from \"node:fs/promises\";\nimport path from \"node:path\";\nimport semver from \"semver\";\nimport { SANDCASTLE_PACKAGE_NAME } from \"@castellan/schema\";\n\n/** Return the concrete installed version, if resolvable. */\nasync function installedVersion(cwd: string): Promise<string | undefined> {\n const pkgJson = path.join(cwd, \"node_modules\", ...SANDCASTLE_PACKAGE_NAME.split(\"/\"), \"package.json\");\n try {\n const raw = await fsp.readFile(pkgJson, \"utf8\");\n const v = (JSON.parse(raw) as { version?: string }).version;\n if (v && semver.valid(v)) return v;\n } catch {\n /* not installed */\n }\n return undefined;\n}\n\n/** Return the declared dependency range from the consumer's package.json. */\nasync function declaredRange(cwd: string): Promise<string | undefined> {\n try {\n const raw = await fsp.readFile(path.join(cwd, \"package.json\"), \"utf8\");\n const pkg = JSON.parse(raw) as Record<string, Record<string, string> | undefined>;\n for (const field of [\"dependencies\", \"devDependencies\", \"peerDependencies\"] as const) {\n const range = pkg[field]?.[SANDCASTLE_PACKAGE_NAME];\n if (range) return range;\n }\n } catch {\n /* no package.json */\n }\n return undefined;\n}\n\nexport interface SandcastleDetection {\n /** Concrete installed version, if found in node_modules. */\n version?: string;\n /** Declared dependency range from package.json, if present. */\n range?: string;\n}\n\nexport async function detectSandcastle(cwd: string): Promise<SandcastleDetection> {\n const [version, range] = await Promise.all([installedVersion(cwd), declaredRange(cwd)]);\n return {\n ...(version ? { version } : {}),\n ...(range ? { range } : {}),\n };\n}\n\n/**\n * Decide whether the user's Sandcastle is compatible with a workflow's\n * versionRange. Returns a human note when there is a mismatch, else undefined.\n */\nexport function compatibilityNote(\n workflowRange: string,\n detected: SandcastleDetection,\n): string | undefined {\n const { version, range } = detected;\n if (version && semver.valid(version) && semver.validRange(workflowRange)) {\n if (!semver.satisfies(version, workflowRange)) {\n return `Your installed ${SANDCASTLE_PACKAGE_NAME} is ${version}, outside this workflow's tested range ${workflowRange}.`;\n }\n return undefined;\n }\n if (range && semver.validRange(range) && semver.validRange(workflowRange)) {\n // Compare ranges via their lowest satisfying versions — coarse but useful.\n const min = semver.minVersion(range);\n if (min && !semver.satisfies(min, workflowRange)) {\n return `Your ${SANDCASTLE_PACKAGE_NAME} dependency (${range}) may fall outside this workflow's tested range ${workflowRange}.`;\n }\n }\n return undefined;\n}\n"],"mappings":";;;;;AAKA,OAAO,SAAS;AAChB,OAAO,UAAU;AACjB,OAAO,YAAY;AAInB,eAAe,iBAAiB,KAA0C;AACxE,QAAM,UAAU,KAAK,KAAK,KAAK,gBAAgB,GAAG,wBAAwB,MAAM,GAAG,GAAG,cAAc;AACpG,MAAI;AACF,UAAM,MAAM,MAAM,IAAI,SAAS,SAAS,MAAM;AAC9C,UAAM,IAAK,KAAK,MAAM,GAAG,EAA2B;AACpD,QAAI,KAAK,OAAO,MAAM,CAAC,EAAG,QAAO;AAAA,EACnC,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAGA,eAAe,cAAc,KAA0C;AACrE,MAAI;AACF,UAAM,MAAM,MAAM,IAAI,SAAS,KAAK,KAAK,KAAK,cAAc,GAAG,MAAM;AACrE,UAAM,MAAM,KAAK,MAAM,GAAG;AAC1B,eAAW,SAAS,CAAC,gBAAgB,mBAAmB,kBAAkB,GAAY;AACpF,YAAM,QAAQ,IAAI,KAAK,IAAI,uBAAuB;AAClD,UAAI,MAAO,QAAO;AAAA,IACpB;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AASA,eAAsB,iBAAiB,KAA2C;AAChF,QAAM,CAAC,SAAS,KAAK,IAAI,MAAM,QAAQ,IAAI,CAAC,iBAAiB,GAAG,GAAG,cAAc,GAAG,CAAC,CAAC;AACtF,SAAO;AAAA,IACL,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC7B,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC;AAAA,EAC3B;AACF;AAMO,SAAS,kBACd,eACA,UACoB;AACpB,QAAM,EAAE,SAAS,MAAM,IAAI;AAC3B,MAAI,WAAW,OAAO,MAAM,OAAO,KAAK,OAAO,WAAW,aAAa,GAAG;AACxE,QAAI,CAAC,OAAO,UAAU,SAAS,aAAa,GAAG;AAC7C,aAAO,kBAAkB,uBAAuB,OAAO,OAAO,0CAA0C,aAAa;AAAA,IACvH;AACA,WAAO;AAAA,EACT;AACA,MAAI,SAAS,OAAO,WAAW,KAAK,KAAK,OAAO,WAAW,aAAa,GAAG;AAEzE,UAAM,MAAM,OAAO,WAAW,KAAK;AACnC,QAAI,OAAO,CAAC,OAAO,UAAU,KAAK,aAAa,GAAG;AAChD,aAAO,QAAQ,uBAAuB,gBAAgB,KAAK,mDAAmD,aAAa;AAAA,IAC7H;AAAA,EACF;AACA,SAAO;AACT;","names":[]}
@@ -1,78 +0,0 @@
1
- // src/lib/preview.ts
2
- import pc from "picocolors";
3
- function pad(s, width) {
4
- return s.length >= width ? s : s + " ".repeat(width - s.length);
5
- }
6
- function renderPreview(input) {
7
- const { manifest, author, version } = input;
8
- const c = input.colorize ? pc : {
9
- bold: (s) => s,
10
- dim: (s) => s,
11
- yellow: (s) => s,
12
- green: (s) => s,
13
- red: (s) => s,
14
- cyan: (s) => s
15
- };
16
- const lines = [];
17
- const rule = "\u2500".repeat(60);
18
- lines.push(c.bold(`${manifest.name}`));
19
- lines.push(`${c.dim("by")} ${author} ${c.dim("\xB7")} v${version} ${c.dim("\xB7")} ${manifest.license}`);
20
- if (manifest.description) lines.push(c.dim(manifest.description));
21
- lines.push("");
22
- lines.push(`${c.dim("Sandcastle range:")} ${manifest.sandcastle.versionRange}`);
23
- if (input.compatibilityNote) {
24
- lines.push(c.yellow(`\u26A0 ${input.compatibilityNote}`));
25
- }
26
- if (manifest.agents.length > 0) {
27
- const agents = manifest.agents.map((a) => `${a.role} (${a.provider} ${a.model}${a.parallel ? ", parallel" : ""})`).join(", ");
28
- lines.push(`${c.dim("Agents:")} ${agents}`);
29
- }
30
- if (manifest.sandboxProviders.length > 0) {
31
- lines.push(`${c.dim("Sandbox:")} ${manifest.sandboxProviders.join(", ")}`);
32
- }
33
- if (manifest.env.length > 0) {
34
- lines.push("");
35
- lines.push(c.bold("Environment variables"));
36
- const nameW = Math.max(4, ...manifest.env.map((e) => e.name.length));
37
- for (const e of manifest.env) {
38
- const req = e.required ? c.red("required") : c.dim("optional");
39
- const desc = e.description ? ` ${c.dim(e.description)}` : "";
40
- lines.push(` ${pad(e.name, nameW)} ${req}${desc}`);
41
- }
42
- }
43
- lines.push("");
44
- lines.push(c.yellow(c.bold("\u26A0 Disclosures \u2014 what this workflow can do")));
45
- lines.push(c.yellow(rule));
46
- const d = manifest.disclosures;
47
- lines.push(
48
- ` ${c.yellow("Host hooks:")} ${d.hostHooks.length ? d.hostHooks.map((h) => `\`${h}\``).join(", ") : c.dim("none")}`
49
- );
50
- lines.push(
51
- ` ${c.yellow("Sandbox hooks:")} ${d.sandboxHooks.length ? d.sandboxHooks.map((h) => `\`${h}\``).join(", ") : c.dim("none")}`
52
- );
53
- lines.push(` ${c.yellow("Network access:")} ${d.networkAccess ?? c.dim("none declared")}`);
54
- lines.push(
55
- ` ${c.yellow("Shell expansion:")} ${d.usesShellExpansion ? c.red("yes \u2014 prompt files run shell commands at load time") : c.dim("no")}`
56
- );
57
- lines.push(c.yellow(rule));
58
- lines.push("");
59
- lines.push(c.bold(`Files (into ${input.installDir}/)`));
60
- for (const f of input.filePlan) {
61
- const tag = f.action === "create" ? c.green("create ") : f.action === "overwrite" ? c.yellow("overwrite") : c.dim("identical");
62
- lines.push(` ${tag} ${f.target}`);
63
- }
64
- if (input.githubMode) {
65
- lines.push("");
66
- lines.push(
67
- c.dim(
68
- "Note: installing from github: \u2014 checksums are computed client-side from the fetched files (no registry tamper-evidence)."
69
- )
70
- );
71
- }
72
- return lines.join("\n");
73
- }
74
-
75
- export {
76
- renderPreview
77
- };
78
- //# sourceMappingURL=chunk-VWAIXSB6.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/lib/preview.ts"],"sourcesContent":["// The install preview (spec §5.4.3) — a SECURITY SURFACE. Snapshot-tested;\n// changes require review. Pure and deterministic: given a manifest and a file\n// plan, it returns the exact text shown before any bytes are written. Color is\n// applied only when `colorize` is true so snapshots stay stable.\n\nimport type { Manifest } from \"@castellan/schema\";\nimport pc from \"picocolors\";\n\nexport type FileAction = \"create\" | \"overwrite\" | \"identical\";\n\nexport interface PreviewFilePlanEntry {\n /** Target path relative to the project root (post-namespacing). */\n target: string;\n action: FileAction;\n}\n\nexport interface PreviewInput {\n manifest: Manifest;\n author: string;\n version: string;\n /** Compatibility warning about the user's installed Sandcastle, if any. */\n compatibilityNote?: string | undefined;\n filePlan: PreviewFilePlanEntry[];\n /** Install destination dir relative to project root (e.g. \".sandcastle/<slug>\"). */\n installDir: string;\n /** True for `github:` installs — checksums are client-side, no tamper evidence (M1). */\n githubMode?: boolean;\n colorize?: boolean;\n}\n\nfunction pad(s: string, width: number): string {\n return s.length >= width ? s : s + \" \".repeat(width - s.length);\n}\n\nexport function renderPreview(input: PreviewInput): string {\n const { manifest, author, version } = input;\n const c = input.colorize\n ? pc\n : ({\n bold: (s: string) => s,\n dim: (s: string) => s,\n yellow: (s: string) => s,\n green: (s: string) => s,\n red: (s: string) => s,\n cyan: (s: string) => s,\n } as unknown as typeof pc);\n\n const lines: string[] = [];\n const rule = \"─\".repeat(60);\n\n // --- a. Summary -----------------------------------------------------------\n lines.push(c.bold(`${manifest.name}`));\n lines.push(`${c.dim(\"by\")} ${author} ${c.dim(\"·\")} v${version} ${c.dim(\"·\")} ${manifest.license}`);\n if (manifest.description) lines.push(c.dim(manifest.description));\n lines.push(\"\");\n lines.push(`${c.dim(\"Sandcastle range:\")} ${manifest.sandcastle.versionRange}`);\n if (input.compatibilityNote) {\n lines.push(c.yellow(`⚠ ${input.compatibilityNote}`));\n }\n if (manifest.agents.length > 0) {\n const agents = manifest.agents\n .map((a) => `${a.role} (${a.provider} ${a.model}${a.parallel ? \", parallel\" : \"\"})`)\n .join(\", \");\n lines.push(`${c.dim(\"Agents:\")} ${agents}`);\n }\n if (manifest.sandboxProviders.length > 0) {\n lines.push(`${c.dim(\"Sandbox:\")} ${manifest.sandboxProviders.join(\", \")}`);\n }\n\n // --- b. Required env vars -------------------------------------------------\n if (manifest.env.length > 0) {\n lines.push(\"\");\n lines.push(c.bold(\"Environment variables\"));\n const nameW = Math.max(4, ...manifest.env.map((e) => e.name.length));\n for (const e of manifest.env) {\n const req = e.required ? c.red(\"required\") : c.dim(\"optional\");\n const desc = e.description ? ` ${c.dim(e.description)}` : \"\";\n lines.push(` ${pad(e.name, nameW)} ${req}${desc}`);\n }\n }\n\n // --- c. Disclosures block (prominent / yellow) ----------------------------\n lines.push(\"\");\n lines.push(c.yellow(c.bold(\"⚠ Disclosures — what this workflow can do\")));\n lines.push(c.yellow(rule));\n const d = manifest.disclosures;\n lines.push(\n ` ${c.yellow(\"Host hooks:\")} ${d.hostHooks.length ? d.hostHooks.map((h) => `\\`${h}\\``).join(\", \") : c.dim(\"none\")}`,\n );\n lines.push(\n ` ${c.yellow(\"Sandbox hooks:\")} ${d.sandboxHooks.length ? d.sandboxHooks.map((h) => `\\`${h}\\``).join(\", \") : c.dim(\"none\")}`,\n );\n lines.push(` ${c.yellow(\"Network access:\")} ${d.networkAccess ?? c.dim(\"none declared\")}`);\n lines.push(\n ` ${c.yellow(\"Shell expansion:\")} ${d.usesShellExpansion ? c.red(\"yes — prompt files run shell commands at load time\") : c.dim(\"no\")}`,\n );\n lines.push(c.yellow(rule));\n\n // --- d. File plan ---------------------------------------------------------\n lines.push(\"\");\n lines.push(c.bold(`Files (into ${input.installDir}/)`));\n for (const f of input.filePlan) {\n const tag =\n f.action === \"create\"\n ? c.green(\"create \")\n : f.action === \"overwrite\"\n ? c.yellow(\"overwrite\")\n : c.dim(\"identical\");\n lines.push(` ${tag} ${f.target}`);\n }\n\n if (input.githubMode) {\n lines.push(\"\");\n lines.push(\n c.dim(\n \"Note: installing from github: — checksums are computed client-side from the fetched files (no registry tamper-evidence).\",\n ),\n );\n }\n\n return lines.join(\"\\n\");\n}\n"],"mappings":"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","names":[]}
package/dist/index.js DELETED
@@ -1,99 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- isCliError
4
- } from "./chunk-6CWCR5SO.js";
5
- import {
6
- logErr,
7
- pc,
8
- setJsonMode
9
- } from "./chunk-PRNBOQHW.js";
10
-
11
- // src/index.ts
12
- import { Command } from "commander";
13
- function fail(e) {
14
- if (isCliError(e)) {
15
- if (e.message) logErr(`${pc.red("error")} ${e.message}`);
16
- for (const d of e.details) if (d) logErr(` ${pc.dim(d)}`);
17
- process.exitCode = e.exitCode;
18
- return;
19
- }
20
- logErr(`${pc.red("error")} ${e?.message ?? String(e)}`);
21
- process.exitCode = 1;
22
- }
23
- function ctx(command) {
24
- const opts = command.optsWithGlobals();
25
- setJsonMode(Boolean(opts.json));
26
- return opts;
27
- }
28
- async function main() {
29
- const program = new Command();
30
- program.name("runcastle").description("Install and publish Sandcastle workflow packages").version("0.2.0").option("--json", "machine-readable JSON output").option("--registry <url>", "registry base URL").option("-y, --yes", "skip confirmation prompts");
31
- program.command("init").description("Scaffold a workflow.json from an existing .sandcastle/ directory").action(async (_o, cmd) => {
32
- const g = ctx(cmd);
33
- const { initCommand } = await import("./init-MOJNLWXK.js");
34
- await initCommand({ ...g.yes ? { yes: true } : {} });
35
- });
36
- program.command("validate").description("Validate a workflow package (exit 0/1)").argument("[path]", "package directory", ".").action(async (pathArg, _o, cmd) => {
37
- ctx(cmd);
38
- const { validateCommand } = await import("./validate-GFDSEVPV.js");
39
- await validateCommand({ path: pathArg });
40
- });
41
- program.command("add").description("Install a workflow (author/slug[@version] or github:owner/repo[/dir][@sha])").argument("<spec>", "workflow reference").option("--dir <subdir>", "install into .sandcastle/<subdir>/").option("--dry-run", "resolve + preview only; write nothing").action(async (spec, o, cmd) => {
42
- const g = ctx(cmd);
43
- const { addCommand } = await import("./add-DD7G73HH.js");
44
- await addCommand(spec, {
45
- ...o.dir ? { dir: o.dir } : {},
46
- ...o.dryRun ? { dryRun: true } : {},
47
- ...g.yes ? { yes: true } : {},
48
- ...g.json ? { json: true } : {},
49
- ...g.registry ? { registry: g.registry } : {}
50
- });
51
- });
52
- program.command("run").description("Preflight-check and run an installed workflow (builds the sandbox image on first run)").argument("[dir]", "install directory when multiple workflows are installed (e.g. .sandcastle/<name>)").action(async (dirArg, _o, cmd) => {
53
- const g = ctx(cmd);
54
- const { runCommand } = await import("./run-M4XXNPGD.js");
55
- await runCommand(dirArg, { ...g.yes ? { yes: true } : {} });
56
- });
57
- program.command("info").description("Show a workflow's preview without installing").argument("<spec>", "author/slug[@version]").action(async (spec, _o, cmd) => {
58
- const g = ctx(cmd);
59
- const { infoCommand } = await import("./info-3PW6A6JA.js");
60
- await infoCommand(spec, {
61
- ...g.json ? { json: true } : {},
62
- ...g.registry ? { registry: g.registry } : {}
63
- });
64
- });
65
- program.command("search").description("Search the registry").argument("[query]", "search text", "").option("--sort <sort>", "downloads | newest | name").action(async (query, o, cmd) => {
66
- const g = ctx(cmd);
67
- const { searchCommand } = await import("./search-2KNAXPM5.js");
68
- await searchCommand(query, {
69
- ...o.sort ? { sort: o.sort } : {},
70
- ...g.json ? { json: true } : {},
71
- ...g.registry ? { registry: g.registry } : {}
72
- });
73
- });
74
- program.command("publish").description("Validate, authenticate, and register the workflow at the current commit").action(async (_o, cmd) => {
75
- const g = ctx(cmd);
76
- const { publishCommand } = await import("./publish-W37Y6TO6.js");
77
- await publishCommand({
78
- ...g.yes ? { yes: true } : {},
79
- ...g.json ? { json: true } : {},
80
- ...g.registry ? { registry: g.registry } : {}
81
- });
82
- });
83
- program.command("update").description("Update installed workflows to newer versions (unmodified files only)").action(async (_o, cmd) => {
84
- const g = ctx(cmd);
85
- const { updateCommand } = await import("./update-UCT4K6IQ.js");
86
- await updateCommand({
87
- ...g.json ? { json: true } : {},
88
- ...g.registry ? { registry: g.registry } : {}
89
- });
90
- });
91
- program.command("logout").description("Remove the cached GitHub token").action(async (_o, cmd) => {
92
- ctx(cmd);
93
- const { logoutCommand } = await import("./logout-YPGZ6BB6.js");
94
- await logoutCommand();
95
- });
96
- await program.parseAsync(process.argv);
97
- }
98
- main().catch(fail);
99
- //# sourceMappingURL=index.js.map
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\n// runcastle CLI entrypoint. Kept intentionally light: commander + the output\n// helpers only. Every command module (and the heavy schema/analyzer code it may\n// pull) is loaded LAZILY via dynamic import so cold `npx` start stays < 3s\n// (spec §5.7) — the hot path `add` never touches ts-morph.\n\nimport { Command } from \"commander\";\nimport { isCliError } from \"./lib/errors.js\";\nimport { setJsonMode, logErr, pc } from \"./lib/output.js\";\n\ninterface GlobalOpts {\n json?: boolean;\n registry?: string;\n yes?: boolean;\n}\n\nfunction fail(e: unknown): void {\n // Set process.exitCode and let the event loop drain rather than calling\n // process.exit() synchronously. On Windows, forcing exit while undici's\n // keep-alive sockets (opened by `add`'s multi-file download) are still\n // closing trips a libuv assertion (`UV_HANDLE_CLOSING`) and yields exit 127\n // instead of the intended code. The success path already exits naturally, so\n // the error path does the same. Commands throw CliError; nothing runs after.\n if (isCliError(e)) {\n if (e.message) logErr(`${pc.red(\"error\")} ${e.message}`);\n for (const d of e.details) if (d) logErr(` ${pc.dim(d)}`);\n process.exitCode = e.exitCode;\n return;\n }\n logErr(`${pc.red(\"error\")} ${(e as Error)?.message ?? String(e)}`);\n process.exitCode = 1;\n}\n\n/** Merge global + command options and set json mode before running. */\nfunction ctx(command: Command): GlobalOpts {\n const opts = command.optsWithGlobals() as GlobalOpts;\n setJsonMode(Boolean(opts.json));\n return opts;\n}\n\nasync function main(): Promise<void> {\n const program = new Command();\n program\n .name(\"runcastle\")\n .description(\"Install and publish Sandcastle workflow packages\")\n .version(\"0.2.0\")\n .option(\"--json\", \"machine-readable JSON output\")\n .option(\"--registry <url>\", \"registry base URL\")\n .option(\"-y, --yes\", \"skip confirmation prompts\");\n\n program\n .command(\"init\")\n .description(\"Scaffold a workflow.json from an existing .sandcastle/ directory\")\n .action(async (_o, cmd: Command) => {\n const g = ctx(cmd);\n const { initCommand } = await import(\"./commands/init.js\");\n await initCommand({ ...(g.yes ? { yes: true } : {}) });\n });\n\n program\n .command(\"validate\")\n .description(\"Validate a workflow package (exit 0/1)\")\n .argument(\"[path]\", \"package directory\", \".\")\n .action(async (pathArg: string, _o, cmd: Command) => {\n ctx(cmd);\n const { validateCommand } = await import(\"./commands/validate.js\");\n await validateCommand({ path: pathArg });\n });\n\n program\n .command(\"add\")\n .description(\"Install a workflow (author/slug[@version] or github:owner/repo[/dir][@sha])\")\n .argument(\"<spec>\", \"workflow reference\")\n .option(\"--dir <subdir>\", \"install into .sandcastle/<subdir>/\")\n .option(\"--dry-run\", \"resolve + preview only; write nothing\")\n .action(async (spec: string, o: { dir?: string; dryRun?: boolean }, cmd: Command) => {\n const g = ctx(cmd);\n const { addCommand } = await import(\"./commands/add.js\");\n await addCommand(spec, {\n ...(o.dir ? { dir: o.dir } : {}),\n ...(o.dryRun ? { dryRun: true } : {}),\n ...(g.yes ? { yes: true } : {}),\n ...(g.json ? { json: true } : {}),\n ...(g.registry ? { registry: g.registry } : {}),\n });\n });\n\n program\n .command(\"run\")\n .description(\"Preflight-check and run an installed workflow (builds the sandbox image on first run)\")\n .argument(\"[dir]\", \"install directory when multiple workflows are installed (e.g. .sandcastle/<name>)\")\n .action(async (dirArg: string | undefined, _o, cmd: Command) => {\n const g = ctx(cmd);\n const { runCommand } = await import(\"./commands/run.js\");\n await runCommand(dirArg, { ...(g.yes ? { yes: true } : {}) });\n });\n\n program\n .command(\"info\")\n .description(\"Show a workflow's preview without installing\")\n .argument(\"<spec>\", \"author/slug[@version]\")\n .action(async (spec: string, _o, cmd: Command) => {\n const g = ctx(cmd);\n const { infoCommand } = await import(\"./commands/info.js\");\n await infoCommand(spec, {\n ...(g.json ? { json: true } : {}),\n ...(g.registry ? { registry: g.registry } : {}),\n });\n });\n\n program\n .command(\"search\")\n .description(\"Search the registry\")\n .argument(\"[query]\", \"search text\", \"\")\n .option(\"--sort <sort>\", \"downloads | newest | name\")\n .action(async (query: string, o: { sort?: string }, cmd: Command) => {\n const g = ctx(cmd);\n const { searchCommand } = await import(\"./commands/search.js\");\n await searchCommand(query, {\n ...(o.sort ? { sort: o.sort } : {}),\n ...(g.json ? { json: true } : {}),\n ...(g.registry ? { registry: g.registry } : {}),\n });\n });\n\n program\n .command(\"publish\")\n .description(\"Validate, authenticate, and register the workflow at the current commit\")\n .action(async (_o, cmd: Command) => {\n const g = ctx(cmd);\n const { publishCommand } = await import(\"./commands/publish.js\");\n await publishCommand({\n ...(g.yes ? { yes: true } : {}),\n ...(g.json ? { json: true } : {}),\n ...(g.registry ? { registry: g.registry } : {}),\n });\n });\n\n program\n .command(\"update\")\n .description(\"Update installed workflows to newer versions (unmodified files only)\")\n .action(async (_o, cmd: Command) => {\n const g = ctx(cmd);\n const { updateCommand } = await import(\"./commands/update.js\");\n await updateCommand({\n ...(g.json ? { json: true } : {}),\n ...(g.registry ? { registry: g.registry } : {}),\n });\n });\n\n program\n .command(\"logout\")\n .description(\"Remove the cached GitHub token\")\n .action(async (_o, cmd: Command) => {\n ctx(cmd);\n const { logoutCommand } = await import(\"./commands/logout.js\");\n await logoutCommand();\n });\n\n await program.parseAsync(process.argv);\n}\n\nmain().catch(fail);\n"],"mappings":"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","names":[]}
@@ -1,80 +0,0 @@
1
- import {
2
- renderPreview
3
- } from "./chunk-VWAIXSB6.js";
4
- import {
5
- mapTarget
6
- } from "./chunk-QTC5Y3LU.js";
7
- import {
8
- compatibilityNote,
9
- detectSandcastle
10
- } from "./chunk-UG25BZUN.js";
11
- import {
12
- RegistryClient
13
- } from "./chunk-5VPKMXSV.js";
14
- import {
15
- resolveRegistry
16
- } from "./chunk-LP5MV76P.js";
17
- import {
18
- PAYLOAD_DIR
19
- } from "./chunk-AHRTPV2C.js";
20
- import {
21
- CliError
22
- } from "./chunk-6CWCR5SO.js";
23
- import {
24
- emitJson,
25
- isJsonMode,
26
- log,
27
- pc
28
- } from "./chunk-PRNBOQHW.js";
29
-
30
- // src/commands/info.ts
31
- import fs from "fs";
32
- import path from "path";
33
- async function infoCommand(spec, opts) {
34
- const atIdx = spec.indexOf("@");
35
- const namepart = atIdx === -1 ? spec : spec.slice(0, atIdx);
36
- const inlineVersion = atIdx === -1 ? "" : spec.slice(atIdx + 1);
37
- const segs = namepart.split("/").filter(Boolean);
38
- if (segs.length !== 2) {
39
- throw new CliError(`invalid spec "${spec}" \u2014 expected author/slug[@version]`, { code: "E_BAD_SPEC" });
40
- }
41
- const [author, slug] = segs;
42
- const version = opts.version ?? inlineVersion;
43
- const registry = new RegistryClient({
44
- baseUrl: resolveRegistry(opts.registry),
45
- ...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
46
- });
47
- const record = await registry.getWorkflow(author, slug, version);
48
- if (isJsonMode()) {
49
- emitJson(record);
50
- return;
51
- }
52
- const cwd = process.cwd();
53
- const filePlan = record.files.map((f) => {
54
- const target = mapTarget(f.path, PAYLOAD_DIR);
55
- const exists = fs.existsSync(path.join(cwd, ...target.split("/")));
56
- return { target, action: exists ? "overwrite" : "create" };
57
- });
58
- const detected = await detectSandcastle(cwd);
59
- const note = compatibilityNote(record.manifest.sandcastle.versionRange, detected);
60
- log(
61
- renderPreview({
62
- manifest: record.manifest,
63
- author: record.author,
64
- version: record.version,
65
- compatibilityNote: note,
66
- filePlan,
67
- installDir: PAYLOAD_DIR,
68
- colorize: pc.isColorSupported
69
- })
70
- );
71
- log("");
72
- log(pc.dim(`Install: npx runcastle add ${record.author}/${record.slug}${version ? `@${record.version}` : ""}`));
73
- if (record.versions.length > 1) {
74
- log(pc.dim(`Versions: ${record.versions.join(", ")}`));
75
- }
76
- }
77
- export {
78
- infoCommand
79
- };
80
- //# sourceMappingURL=info-3PW6A6JA.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/info.ts"],"sourcesContent":["// `runcastle info <author/slug>` (spec §5.5). Prints the §5.4.3 preview WITHOUT\n// installing. Resolves the record from the registry and renders the same preview\n// surface `add` uses; the file plan reflects what an install into `.sandcastle/`\n// would do (create vs overwrite) without downloading file contents.\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { emitJson, isJsonMode, log, pc } from \"../lib/output.js\";\nimport { resolveRegistry, PAYLOAD_DIR } from \"../lib/config.js\";\nimport { RegistryClient } from \"../lib/registry.js\";\nimport { renderPreview, type PreviewFilePlanEntry } from \"../lib/preview.js\";\nimport { detectSandcastle, compatibilityNote } from \"../lib/sandcastle-version.js\";\nimport { mapTarget } from \"../lib/installer.js\";\nimport { CliError } from \"../lib/errors.js\";\nimport type { FetchLike } from \"../lib/http.js\";\n\nexport interface InfoOptions {\n registry?: string;\n json?: boolean;\n version?: string;\n fetchImpl?: FetchLike;\n}\n\nexport async function infoCommand(spec: string, opts: InfoOptions): Promise<void> {\n const atIdx = spec.indexOf(\"@\");\n const namepart = atIdx === -1 ? spec : spec.slice(0, atIdx);\n const inlineVersion = atIdx === -1 ? \"\" : spec.slice(atIdx + 1);\n const segs = namepart.split(\"/\").filter(Boolean);\n if (segs.length !== 2) {\n throw new CliError(`invalid spec \"${spec}\" — expected author/slug[@version]`, { code: \"E_BAD_SPEC\" });\n }\n const [author, slug] = segs as [string, string];\n const version = opts.version ?? inlineVersion;\n\n const registry = new RegistryClient({\n baseUrl: resolveRegistry(opts.registry),\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n const record = await registry.getWorkflow(author, slug, version);\n\n if (isJsonMode()) {\n emitJson(record);\n return;\n }\n\n const cwd = process.cwd();\n const filePlan: PreviewFilePlanEntry[] = record.files.map((f) => {\n const target = mapTarget(f.path, PAYLOAD_DIR);\n const exists = fs.existsSync(path.join(cwd, ...target.split(\"/\")));\n return { target, action: exists ? \"overwrite\" : \"create\" };\n });\n\n const detected = await detectSandcastle(cwd);\n const note = compatibilityNote(record.manifest.sandcastle.versionRange, detected);\n\n log(\n renderPreview({\n manifest: record.manifest,\n author: record.author,\n version: record.version,\n compatibilityNote: note,\n filePlan,\n installDir: PAYLOAD_DIR,\n colorize: pc.isColorSupported,\n }),\n );\n log(\"\");\n log(pc.dim(`Install: npx runcastle add ${record.author}/${record.slug}${version ? `@${record.version}` : \"\"}`));\n if (record.versions.length > 1) {\n log(pc.dim(`Versions: ${record.versions.join(\", \")}`));\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAKA,OAAO,QAAQ;AACf,OAAO,UAAU;AAiBjB,eAAsB,YAAY,MAAc,MAAkC;AAChF,QAAM,QAAQ,KAAK,QAAQ,GAAG;AAC9B,QAAM,WAAW,UAAU,KAAK,OAAO,KAAK,MAAM,GAAG,KAAK;AAC1D,QAAM,gBAAgB,UAAU,KAAK,KAAK,KAAK,MAAM,QAAQ,CAAC;AAC9D,QAAM,OAAO,SAAS,MAAM,GAAG,EAAE,OAAO,OAAO;AAC/C,MAAI,KAAK,WAAW,GAAG;AACrB,UAAM,IAAI,SAAS,iBAAiB,IAAI,2CAAsC,EAAE,MAAM,aAAa,CAAC;AAAA,EACtG;AACA,QAAM,CAAC,QAAQ,IAAI,IAAI;AACvB,QAAM,UAAU,KAAK,WAAW;AAEhC,QAAM,WAAW,IAAI,eAAe;AAAA,IAClC,SAAS,gBAAgB,KAAK,QAAQ;AAAA,IACtC,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,UAAU,IAAI,CAAC;AAAA,EACxD,CAAC;AACD,QAAM,SAAS,MAAM,SAAS,YAAY,QAAQ,MAAM,OAAO;AAE/D,MAAI,WAAW,GAAG;AAChB,aAAS,MAAM;AACf;AAAA,EACF;AAEA,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,WAAmC,OAAO,MAAM,IAAI,CAAC,MAAM;AAC/D,UAAM,SAAS,UAAU,EAAE,MAAM,WAAW;AAC5C,UAAM,SAAS,GAAG,WAAW,KAAK,KAAK,KAAK,GAAG,OAAO,MAAM,GAAG,CAAC,CAAC;AACjE,WAAO,EAAE,QAAQ,QAAQ,SAAS,cAAc,SAAS;AAAA,EAC3D,CAAC;AAED,QAAM,WAAW,MAAM,iBAAiB,GAAG;AAC3C,QAAM,OAAO,kBAAkB,OAAO,SAAS,WAAW,cAAc,QAAQ;AAEhF;AAAA,IACE,cAAc;AAAA,MACZ,UAAU,OAAO;AAAA,MACjB,QAAQ,OAAO;AAAA,MACf,SAAS,OAAO;AAAA,MAChB,mBAAmB;AAAA,MACnB;AAAA,MACA,YAAY;AAAA,MACZ,UAAU,GAAG;AAAA,IACf,CAAC;AAAA,EACH;AACA,MAAI,EAAE;AACN,MAAI,GAAG,IAAI,+BAA+B,OAAO,MAAM,IAAI,OAAO,IAAI,GAAG,UAAU,IAAI,OAAO,OAAO,KAAK,EAAE,EAAE,CAAC;AAC/G,MAAI,OAAO,SAAS,SAAS,GAAG;AAC9B,QAAI,GAAG,IAAI,aAAa,OAAO,SAAS,KAAK,IAAI,CAAC,EAAE,CAAC;AAAA,EACvD;AACF;","names":[]}