@cubedot/cli 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.js +457 -131
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -3,12 +3,12 @@
3
3
  // src/cli.ts
4
4
  import { Command } from "commander";
5
5
  import { fileURLToPath as fileURLToPath4 } from "url";
6
- import { dirname as dirname4, join as join9 } from "path";
7
- import { readFileSync as readFileSync9 } from "fs";
6
+ import { dirname as dirname4, join as join10 } from "path";
7
+ import { readFileSync as readFileSync10 } from "fs";
8
8
 
9
9
  // src/commands/init.ts
10
10
  import pc from "picocolors";
11
- import { readFileSync as readFileSync3 } from "fs";
11
+ import { readFileSync as readFileSync3, existsSync as existsSync3 } from "fs";
12
12
  import { join as join3 } from "path";
13
13
 
14
14
  // src/mcp.ts
@@ -83,7 +83,10 @@ import {
83
83
  copyFileSync,
84
84
  existsSync,
85
85
  mkdirSync,
86
+ readdirSync,
86
87
  readFileSync,
88
+ rmdirSync,
89
+ unlinkSync,
87
90
  writeFileSync
88
91
  } from "fs";
89
92
  import { dirname, join } from "path";
@@ -101,13 +104,15 @@ function writeMcpJson(cwd, mcpUrl, projectId, key) {
101
104
  } catch {
102
105
  }
103
106
  }
107
+ const mcpEndpoint = new URL(mcpUrl);
108
+ mcpEndpoint.searchParams.set("project", projectId);
104
109
  const config = {
105
110
  ...existing,
106
111
  mcpServers: {
107
112
  ...existing.mcpServers ?? {},
108
113
  cubedot: {
109
114
  type: "http",
110
- url: `${mcpUrl}?project=${encodeURIComponent(projectId)}`,
115
+ url: mcpEndpoint.toString(),
111
116
  headers: { Authorization: `ApiKey ${key}` }
112
117
  }
113
118
  }
@@ -176,17 +181,18 @@ var GITIGNORE_ENTRIES = [
176
181
  function updateGitignore(cwd) {
177
182
  const path = join(cwd, ".gitignore");
178
183
  let content = existsSync(path) ? readFileSync(path, "utf8") : "";
179
- const lines = content.split("\n");
180
- let changed = false;
184
+ const existing = content.split("\n");
185
+ const added = [];
181
186
  for (const entry of GITIGNORE_ENTRIES) {
182
- if (!lines.some((l) => l.trim() === entry)) {
187
+ if (!existing.some((l) => l.trim() === entry)) {
183
188
  content = content.endsWith("\n") ? content + entry + "\n" : content + "\n" + entry + "\n";
184
- changed = true;
189
+ added.push(entry);
185
190
  }
186
191
  }
187
- if (changed) {
192
+ if (added.length > 0) {
188
193
  writeFileSync(path, content, "utf8");
189
194
  }
195
+ return added;
190
196
  }
191
197
  function readHashes(cwd) {
192
198
  const path = join(cwd, ".cubedot", "sync", "hashes.json");
@@ -206,8 +212,9 @@ function enableMcpServerInSettings(cwd, serverName = "cubedot") {
206
212
  const dir = join(cwd, ".claude");
207
213
  if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
208
214
  const path = join(dir, "settings.json");
215
+ const existed = existsSync(path);
209
216
  let settings = {};
210
- if (existsSync(path)) {
217
+ if (existed) {
211
218
  try {
212
219
  settings = JSON.parse(readFileSync(path, "utf8"));
213
220
  } catch {
@@ -218,6 +225,7 @@ function enableMcpServerInSettings(cwd, serverName = "cubedot") {
218
225
  settings["enabledMcpjsonServers"] = [...cur, serverName];
219
226
  }
220
227
  writeFileSync(path, JSON.stringify(settings, null, 2) + "\n", "utf8");
228
+ return { created: !existed };
221
229
  }
222
230
  function readLinks(cwd) {
223
231
  const path = join(cwd, ".cubedot", "links.json");
@@ -228,6 +236,76 @@ function readLinks(cwd) {
228
236
  return {};
229
237
  }
230
238
  }
239
+ var MANIFEST_PATH = ".cubedot/install-manifest.json";
240
+ function readInstallManifest(cwd) {
241
+ const path = join(cwd, MANIFEST_PATH);
242
+ if (!existsSync(path)) return null;
243
+ try {
244
+ return JSON.parse(readFileSync(path, "utf8"));
245
+ } catch {
246
+ return null;
247
+ }
248
+ }
249
+ function writeInstallManifest(cwd, manifest) {
250
+ writeFileSync(join(cwd, MANIFEST_PATH), JSON.stringify(manifest, null, 2) + "\n", "utf8");
251
+ }
252
+ function stripGitignoreLines(cwd, lines) {
253
+ const path = join(cwd, ".gitignore");
254
+ if (!existsSync(path)) return;
255
+ const content = readFileSync(path, "utf8");
256
+ const kept = content.split("\n").filter((l) => !lines.includes(l.trim()));
257
+ while (kept.length > 0 && !kept[kept.length - 1]?.trim()) kept.pop();
258
+ if (kept.length === 0) {
259
+ unlinkSync(path);
260
+ } else {
261
+ writeFileSync(path, kept.join("\n") + "\n", "utf8");
262
+ }
263
+ }
264
+ function removeCubedotFromSettings(cwd) {
265
+ const path = join(cwd, ".claude", "settings.json");
266
+ if (!existsSync(path)) return;
267
+ let settings;
268
+ try {
269
+ settings = JSON.parse(readFileSync(path, "utf8"));
270
+ } catch {
271
+ return;
272
+ }
273
+ if (!Array.isArray(settings["enabledMcpjsonServers"])) return;
274
+ const filtered = settings["enabledMcpjsonServers"].filter(
275
+ (s) => s !== "cubedot"
276
+ );
277
+ if (filtered.length === 0) {
278
+ delete settings["enabledMcpjsonServers"];
279
+ } else {
280
+ settings["enabledMcpjsonServers"] = filtered;
281
+ }
282
+ if (Object.keys(settings).length === 0) {
283
+ unlinkSync(path);
284
+ } else {
285
+ writeFileSync(path, JSON.stringify(settings, null, 2) + "\n", "utf8");
286
+ }
287
+ }
288
+ function stripCubedotMarkers(filePath) {
289
+ if (!existsSync(filePath)) return;
290
+ const content = readFileSync(filePath, "utf8");
291
+ const stripped = content.replace(
292
+ /<!-- CUBEDOT:START:(\w+) -->[\s\S]*?<!-- CUBEDOT:END:\1 -->\n?/g,
293
+ ""
294
+ );
295
+ const cleaned = stripped.replace(/\n{3,}/g, "\n\n").trimEnd();
296
+ writeFileSync(filePath, cleaned ? cleaned + "\n" : "", "utf8");
297
+ }
298
+ function tryRmdir(dirPath) {
299
+ if (!existsSync(dirPath)) return false;
300
+ try {
301
+ const entries = readdirSync(dirPath);
302
+ if (entries.length > 0) return false;
303
+ rmdirSync(dirPath);
304
+ return true;
305
+ } catch {
306
+ return false;
307
+ }
308
+ }
231
309
 
232
310
  // src/hotmemory.ts
233
311
  import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
@@ -394,6 +472,25 @@ function decodeToken(token) {
394
472
  }
395
473
  async function initCommand(opts) {
396
474
  const cwd = process.cwd();
475
+ const ownPkgPath = join3(cwd, "package.json");
476
+ if (existsSync3(ownPkgPath) && !opts.force) {
477
+ try {
478
+ const ownPkg = JSON.parse(readFileSync3(ownPkgPath, "utf8"));
479
+ if (ownPkg.name === "@cubedot/cli") {
480
+ console.error(
481
+ pc.red(
482
+ "Error: This looks like the Cubedot CLI source repo, not a project to connect.\nRun `cubedot init` inside your target project directory, or pass --force to override."
483
+ )
484
+ );
485
+ process.exit(1);
486
+ }
487
+ } catch {
488
+ }
489
+ }
490
+ const claudeMdExisted = existsSync3(join3(cwd, "CLAUDE.md"));
491
+ const agentsMdExisted = existsSync3(join3(cwd, "AGENTS.md"));
492
+ const fieldNotesExisted = existsSync3(join3(cwd, ".cubedot", "field-notes.md"));
493
+ const linksJsonExisted = existsSync3(join3(cwd, ".cubedot", "links.json"));
397
494
  let projectId;
398
495
  let key;
399
496
  let mcpUrl;
@@ -483,7 +580,7 @@ async function initCommand(opts) {
483
580
  await client.close();
484
581
  }
485
582
  writeMcpJson(cwd, mcpUrl, projectId, key);
486
- enableMcpServerInSettings(cwd);
583
+ const { created: settingsCreated } = enableMcpServerInSettings(cwd);
487
584
  writeCubedotConfig(cwd, {
488
585
  projectId,
489
586
  mcpUrl,
@@ -496,7 +593,33 @@ async function initCommand(opts) {
496
593
  generateMemoryFiles(cwd, identity, conventions, registry);
497
594
  installAgentAssets(cwd);
498
595
  scaffoldCubedotDirs(cwd);
499
- updateGitignore(cwd);
596
+ const gitignoreAdded = updateGitignore(cwd);
597
+ const manifest = {
598
+ cliVersion: pkg.version,
599
+ at: (/* @__PURE__ */ new Date()).toISOString(),
600
+ createdFiles: [
601
+ ".mcp.json",
602
+ ".cubedot/config.json",
603
+ ".cubedot/fia-operating-manual.md",
604
+ ".claude/agents/CubedotVerifier.md",
605
+ ...!fieldNotesExisted ? [".cubedot/field-notes.md"] : [],
606
+ ...!linksJsonExisted ? [".cubedot/links.json"] : []
607
+ ],
608
+ createdDirs: [
609
+ ".cubedot/progress",
610
+ ".cubedot/reports",
611
+ ".cubedot/docs",
612
+ ".cubedot/sync",
613
+ ".cubedot",
614
+ ".claude/agents",
615
+ ".claude"
616
+ ],
617
+ claudeMd: claudeMdExisted ? "inserted" : "created",
618
+ agentsMd: agentsMdExisted ? "inserted" : "created",
619
+ settingsCreated,
620
+ gitignoreAdded
621
+ };
622
+ writeInstallManifest(cwd, manifest);
500
623
  const projectName = orientText.split("\n").find((l) => l.trim())?.replace(/^#+\s*/, "") ?? projectId;
501
624
  console.log("\n" + pc.green("\u2713") + " Cubedot connected: " + pc.bold(projectName));
502
625
  console.log(pc.dim(` Project ID : ${projectId}`));
@@ -508,6 +631,8 @@ async function initCommand(opts) {
508
631
  const written = [
509
632
  ".mcp.json (gitignored \u2014 holds your key)",
510
633
  ".cubedot/config.json (gitignored)",
634
+ ".cubedot/install-manifest.json",
635
+ ".claude/settings.json (enabledMcpjsonServers)",
511
636
  "AGENTS.md",
512
637
  "CLAUDE.md",
513
638
  ".claude/agents/CubedotVerifier.md",
@@ -521,24 +646,224 @@ async function initCommand(opts) {
521
646
  console.log("\nOpen Claude Code in this directory to start building. \u2728");
522
647
  }
523
648
 
524
- // src/commands/sync.ts
649
+ // src/commands/uninstall.ts
525
650
  import pc2 from "picocolors";
651
+ import { existsSync as existsSync4, readFileSync as readFileSync4, rmSync as rmSync2, unlinkSync as unlinkSync2 } from "fs";
652
+ import { join as join4 } from "path";
653
+ async function uninstallCommand(opts) {
654
+ const cwd = process.cwd();
655
+ const hasCubedot = existsSync4(join4(cwd, ".mcp.json")) || existsSync4(join4(cwd, ".cubedot")) || existsSync4(join4(cwd, ".claude", "agents", "CubedotVerifier.md"));
656
+ if (!hasCubedot) {
657
+ console.log("Nothing to uninstall here.");
658
+ return;
659
+ }
660
+ const manifest = readInstallManifest(cwd);
661
+ if (manifest) {
662
+ await runManifestUninstall(cwd, manifest, opts.yes ?? false);
663
+ } else {
664
+ await runHeuristicUninstall(cwd, opts.yes ?? false);
665
+ }
666
+ }
667
+ async function confirm(skipConfirm) {
668
+ if (skipConfirm) return true;
669
+ if (!process.stdin.isTTY) {
670
+ console.error(pc2.red("Non-interactive shell: pass --yes / -y to skip confirmation."));
671
+ process.exit(1);
672
+ }
673
+ const { default: input } = await import("@inquirer/input");
674
+ const answer = await input({ message: "Continue? [y/N]" });
675
+ return answer.trim().toLowerCase() === "y";
676
+ }
677
+ function execute(actions) {
678
+ for (const a of actions) {
679
+ try {
680
+ a.fn();
681
+ } catch (err) {
682
+ console.warn(pc2.yellow(` \u26A0 Could not complete "${a.desc}": ${err.message}`));
683
+ }
684
+ }
685
+ }
686
+ async function runManifestUninstall(cwd, manifest, skipConfirm) {
687
+ const actions = [];
688
+ for (const rel of manifest.createdFiles) {
689
+ const abs = join4(cwd, rel);
690
+ if (existsSync4(abs)) {
691
+ actions.push({ desc: `delete ${rel}`, fn: () => unlinkSync2(abs) });
692
+ }
693
+ }
694
+ const claudePath = join4(cwd, "CLAUDE.md");
695
+ if (existsSync4(claudePath)) {
696
+ if (manifest.claudeMd === "created") {
697
+ actions.push({
698
+ desc: "delete CLAUDE.md (created by cubedot init)",
699
+ fn: () => unlinkSync2(claudePath)
700
+ });
701
+ } else {
702
+ actions.push({
703
+ desc: "CLAUDE.md \u2014 remove CUBEDOT marker blocks (preserves your content)",
704
+ fn: () => stripCubedotMarkers(claudePath)
705
+ });
706
+ }
707
+ }
708
+ const agentsPath = join4(cwd, "AGENTS.md");
709
+ if (existsSync4(agentsPath)) {
710
+ if (manifest.agentsMd === "created") {
711
+ actions.push({
712
+ desc: "delete AGENTS.md (created by cubedot init)",
713
+ fn: () => unlinkSync2(agentsPath)
714
+ });
715
+ } else {
716
+ actions.push({
717
+ desc: "AGENTS.md \u2014 remove CUBEDOT marker blocks (preserves your content)",
718
+ fn: () => stripCubedotMarkers(agentsPath)
719
+ });
720
+ }
721
+ }
722
+ const settingsPath = join4(cwd, ".claude", "settings.json");
723
+ if (existsSync4(settingsPath)) {
724
+ if (manifest.settingsCreated) {
725
+ actions.push({
726
+ desc: "delete .claude/settings.json (created by cubedot init)",
727
+ fn: () => unlinkSync2(settingsPath)
728
+ });
729
+ } else {
730
+ actions.push({
731
+ desc: '.claude/settings.json \u2014 remove "cubedot" from enabledMcpjsonServers',
732
+ fn: () => removeCubedotFromSettings(cwd)
733
+ });
734
+ }
735
+ }
736
+ if (manifest.gitignoreAdded.length > 0 && existsSync4(join4(cwd, ".gitignore"))) {
737
+ actions.push({
738
+ desc: `.gitignore \u2014 remove lines: ${manifest.gitignoreAdded.join(", ")}`,
739
+ fn: () => stripGitignoreLines(cwd, manifest.gitignoreAdded)
740
+ });
741
+ }
742
+ const manifestAbs = join4(cwd, MANIFEST_PATH);
743
+ if (existsSync4(manifestAbs)) {
744
+ actions.push({
745
+ desc: `delete ${MANIFEST_PATH}`,
746
+ fn: () => unlinkSync2(manifestAbs)
747
+ });
748
+ }
749
+ if (actions.length === 0) {
750
+ console.log("Nothing to uninstall here.");
751
+ return;
752
+ }
753
+ printPlan(actions, true);
754
+ if (!await confirm(skipConfirm)) {
755
+ console.log("Aborted.");
756
+ return;
757
+ }
758
+ execute(actions);
759
+ const dirsToTry = [...manifest.createdDirs].reverse();
760
+ for (const rel of dirsToTry) {
761
+ tryRmdir(join4(cwd, rel));
762
+ }
763
+ console.log("\n" + pc2.green("\u2713") + " Uninstall complete. This folder is no longer connected to Cubedot.");
764
+ }
765
+ async function runHeuristicUninstall(cwd, skipConfirm) {
766
+ const actions = [];
767
+ const mcpPath = join4(cwd, ".mcp.json");
768
+ if (existsSync4(mcpPath)) {
769
+ actions.push({ desc: "delete .mcp.json", fn: () => unlinkSync2(mcpPath) });
770
+ }
771
+ const cubedotDir = join4(cwd, ".cubedot");
772
+ if (existsSync4(cubedotDir)) {
773
+ actions.push({
774
+ desc: "delete .cubedot/ (entire directory)",
775
+ fn: () => rmSync2(cubedotDir, { recursive: true, force: true })
776
+ });
777
+ }
778
+ const verifierPath = join4(cwd, ".claude", "agents", "CubedotVerifier.md");
779
+ if (existsSync4(verifierPath)) {
780
+ actions.push({
781
+ desc: "delete .claude/agents/CubedotVerifier.md",
782
+ fn: () => unlinkSync2(verifierPath)
783
+ });
784
+ }
785
+ const settingsPath = join4(cwd, ".claude", "settings.json");
786
+ if (existsSync4(settingsPath)) {
787
+ actions.push({
788
+ desc: '.claude/settings.json \u2014 remove "cubedot" from enabledMcpjsonServers',
789
+ fn: () => removeCubedotFromSettings(cwd)
790
+ });
791
+ }
792
+ const gitignorePath = join4(cwd, ".gitignore");
793
+ if (existsSync4(gitignorePath)) {
794
+ const content = readFileSync4(gitignorePath, "utf8");
795
+ const lines = content.split("\n");
796
+ if (GITIGNORE_ENTRIES.some((e) => lines.some((l) => l.trim() === e))) {
797
+ actions.push({
798
+ desc: ".gitignore \u2014 remove cubedot lines",
799
+ fn: () => stripGitignoreLines(cwd, GITIGNORE_ENTRIES)
800
+ });
801
+ }
802
+ }
803
+ const claudePath = join4(cwd, "CLAUDE.md");
804
+ if (existsSync4(claudePath)) {
805
+ const content = readFileSync4(claudePath, "utf8");
806
+ if (content.includes("<!-- CUBEDOT:START:")) {
807
+ actions.push({
808
+ desc: "delete CLAUDE.md (contains cubedot markers \u2014 treated as generated)",
809
+ fn: () => unlinkSync2(claudePath)
810
+ });
811
+ }
812
+ }
813
+ const agentsPath = join4(cwd, "AGENTS.md");
814
+ if (existsSync4(agentsPath)) {
815
+ const content = readFileSync4(agentsPath, "utf8");
816
+ if (content.includes("<!-- CUBEDOT:START:")) {
817
+ actions.push({
818
+ desc: "delete AGENTS.md (contains cubedot markers \u2014 treated as generated)",
819
+ fn: () => unlinkSync2(agentsPath)
820
+ });
821
+ }
822
+ }
823
+ if (actions.length === 0) {
824
+ console.log("Nothing to uninstall here.");
825
+ return;
826
+ }
827
+ printPlan(actions, false);
828
+ if (!await confirm(skipConfirm)) {
829
+ console.log("Aborted.");
830
+ return;
831
+ }
832
+ execute(actions);
833
+ tryRmdir(join4(cwd, ".claude", "agents"));
834
+ tryRmdir(join4(cwd, ".claude"));
835
+ console.log("\n" + pc2.green("\u2713") + " Uninstall complete. This folder is no longer connected to Cubedot.");
836
+ }
837
+ function printPlan(actions, hasManifest) {
838
+ const mode = hasManifest ? "manifest-based (precise)" : "heuristic (no manifest found)";
839
+ console.log(pc2.bold(`
840
+ Cubedot uninstall \u2014 ${mode}
841
+ `));
842
+ console.log("The following changes will be made:\n");
843
+ for (const a of actions) {
844
+ console.log(" " + pc2.dim("\u2022") + " " + a.desc);
845
+ }
846
+ console.log("");
847
+ }
848
+
849
+ // src/commands/sync.ts
850
+ import pc3 from "picocolors";
526
851
  import { createHash } from "crypto";
527
- import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
528
- import { join as join5 } from "path";
852
+ import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
853
+ import { join as join6 } from "path";
529
854
  import { fileURLToPath as fileURLToPath3 } from "url";
530
855
  import { dirname as dirname3 } from "path";
531
856
 
532
857
  // src/ledger.ts
533
858
  import {
534
- existsSync as existsSync4,
859
+ existsSync as existsSync5,
535
860
  mkdirSync as mkdirSync2,
536
- readFileSync as readFileSync4,
537
- readdirSync,
861
+ readFileSync as readFileSync5,
862
+ readdirSync as readdirSync2,
538
863
  renameSync,
539
864
  writeFileSync as writeFileSync3
540
865
  } from "fs";
541
- import { join as join4 } from "path";
866
+ import { join as join5 } from "path";
542
867
  import { randomUUID } from "crypto";
543
868
  var FN_CODE_REGEX = /^FN-\d{3}-\d{2,}$/;
544
869
  function validateFnCode(code) {
@@ -549,13 +874,13 @@ function validateFnCode(code) {
549
874
  }
550
875
  }
551
876
  function getLedgerPath(cwd, code) {
552
- return join4(cwd, ".cubedot", "progress", `${code}.json`);
877
+ return join5(cwd, ".cubedot", "progress", `${code}.json`);
553
878
  }
554
879
  function readLedger(cwd, code) {
555
880
  const path = getLedgerPath(cwd, code);
556
- if (!existsSync4(path)) return null;
881
+ if (!existsSync5(path)) return null;
557
882
  try {
558
- return JSON.parse(readFileSync4(path, "utf8"));
883
+ return JSON.parse(readFileSync5(path, "utf8"));
559
884
  } catch {
560
885
  throw new Error(
561
886
  `Failed to read ledger for ${code}: file exists but contains malformed JSON.`
@@ -563,11 +888,11 @@ function readLedger(cwd, code) {
563
888
  }
564
889
  }
565
890
  function readAllLedgers(cwd) {
566
- const dir = join4(cwd, ".cubedot", "progress");
567
- if (!existsSync4(dir)) return [];
568
- return readdirSync(dir).filter((f) => f.endsWith(".json")).map((f) => {
891
+ const dir = join5(cwd, ".cubedot", "progress");
892
+ if (!existsSync5(dir)) return [];
893
+ return readdirSync2(dir).filter((f) => f.endsWith(".json")).map((f) => {
569
894
  try {
570
- return JSON.parse(readFileSync4(join4(dir, f), "utf8"));
895
+ return JSON.parse(readFileSync5(join5(dir, f), "utf8"));
571
896
  } catch {
572
897
  return null;
573
898
  }
@@ -575,8 +900,8 @@ function readAllLedgers(cwd) {
575
900
  }
576
901
  function writeLedger(cwd, record) {
577
902
  const path = getLedgerPath(cwd, record.code);
578
- const dir = join4(cwd, ".cubedot", "progress");
579
- if (!existsSync4(dir)) mkdirSync2(dir, { recursive: true });
903
+ const dir = join5(cwd, ".cubedot", "progress");
904
+ if (!existsSync5(dir)) mkdirSync2(dir, { recursive: true });
580
905
  const tmp = `${path}.${randomUUID()}.tmp`;
581
906
  writeFileSync3(tmp, JSON.stringify(record, null, 2) + "\n", "utf8");
582
907
  renameSync(tmp, path);
@@ -669,12 +994,12 @@ function doneFn(cwd, code, actor = "human", force = false, discrepancies = []) {
669
994
  var __filename2 = fileURLToPath3(import.meta.url);
670
995
  var __dirname3 = dirname3(__filename2);
671
996
  var pkg2 = JSON.parse(
672
- readFileSync5(join5(__dirname3, "../package.json"), "utf8")
997
+ readFileSync6(join6(__dirname3, "../package.json"), "utf8")
673
998
  );
674
999
  function fileHash(filePath) {
675
- if (!existsSync5(filePath)) return "";
1000
+ if (!existsSync6(filePath)) return "";
676
1001
  try {
677
- return createHash("sha256").update(readFileSync5(filePath)).digest("hex");
1002
+ return createHash("sha256").update(readFileSync6(filePath)).digest("hex");
678
1003
  } catch {
679
1004
  return "";
680
1005
  }
@@ -684,7 +1009,7 @@ async function syncCommand() {
684
1009
  const config = readCubedotConfig(cwd);
685
1010
  if (!config) {
686
1011
  console.error(
687
- pc2.red(
1012
+ pc3.red(
688
1013
  "Error: .cubedot/config.json not found. Run `cubedot init` first."
689
1014
  )
690
1015
  );
@@ -695,26 +1020,26 @@ async function syncCommand() {
695
1020
  const ageDays = (Date.now() - new Date(config.lastSynced).getTime()) / 864e5;
696
1021
  if (ageDays >= STALE_DAYS) {
697
1022
  console.log(
698
- pc2.yellow(
1023
+ pc3.yellow(
699
1024
  `\u26A0 Last synced ${Math.floor(ageDays)} day(s) ago \u2014 refreshing now.`
700
1025
  )
701
1026
  );
702
1027
  }
703
1028
  }
704
1029
  const connectUrl = buildConnectUrl(config.mcpUrl, config.projectId);
705
- console.log(pc2.dim(`Connecting to ${connectUrl} \u2026`));
1030
+ console.log(pc3.dim(`Connecting to ${connectUrl} \u2026`));
706
1031
  let client;
707
1032
  try {
708
1033
  client = await createMcpClient(connectUrl, await readKeyFromMcpJson(cwd));
709
1034
  } catch (err) {
710
- console.error(pc2.red(`Error: ${categorizeError(err)}`));
1035
+ console.error(pc3.red(`Error: ${categorizeError(err)}`));
711
1036
  process.exit(1);
712
1037
  }
713
1038
  try {
714
1039
  await validateTools(client);
715
1040
  } catch (err) {
716
1041
  await client.close();
717
- console.error(pc2.red(`Error: ${err.message}`));
1042
+ console.error(pc3.red(`Error: ${err.message}`));
718
1043
  process.exit(1);
719
1044
  }
720
1045
  let orientText = "";
@@ -732,7 +1057,7 @@ async function syncCommand() {
732
1057
  } catch (err) {
733
1058
  await client.close();
734
1059
  console.error(
735
- pc2.red(`Error fetching project data: ${err.message}`)
1060
+ pc3.red(`Error fetching project data: ${err.message}`)
736
1061
  );
737
1062
  process.exit(1);
738
1063
  } finally {
@@ -759,7 +1084,7 @@ async function syncCommand() {
759
1084
  const skipOrphanDetection = specFnCodes.size === 0 && ledgers.length > 0;
760
1085
  if (skipOrphanDetection) {
761
1086
  console.log(
762
- pc2.yellow(
1087
+ pc3.yellow(
763
1088
  "\u26A0 Spec tree returned no functionalities \u2014 skipping orphan detection this sync."
764
1089
  )
765
1090
  );
@@ -775,7 +1100,7 @@ async function syncCommand() {
775
1100
  if (record.state === "done") {
776
1101
  const files = links[record.code] ?? [];
777
1102
  for (const filePath of files) {
778
- const absPath = join5(cwd, filePath);
1103
+ const absPath = join6(cwd, filePath);
779
1104
  const newHash = fileHash(absPath);
780
1105
  const oldHash = prevHashes[filePath] ?? "";
781
1106
  nextHashes[filePath] = newHash;
@@ -793,35 +1118,35 @@ async function syncCommand() {
793
1118
  }
794
1119
  for (const [code, files] of Object.entries(links)) {
795
1120
  for (const filePath of files) {
796
- const absPath = join5(cwd, filePath);
1121
+ const absPath = join6(cwd, filePath);
797
1122
  nextHashes[filePath] = fileHash(absPath);
798
1123
  }
799
1124
  }
800
1125
  writeHashes(cwd, nextHashes);
801
- console.log(pc2.green("\u2713") + " Sync complete.");
1126
+ console.log(pc3.green("\u2713") + " Sync complete.");
802
1127
  if (orphaned.length > 0) {
803
1128
  console.log(
804
- pc2.yellow(` Orphaned (removed from spec): ${orphaned.join(", ")}`)
1129
+ pc3.yellow(` Orphaned (removed from spec): ${orphaned.join(", ")}`)
805
1130
  );
806
1131
  }
807
1132
  if (needsReverification.length > 0) {
808
1133
  console.log(
809
- pc2.yellow(
1134
+ pc3.yellow(
810
1135
  ` Needs re-verification (spec changed after done): ${needsReverification.join(", ")}`
811
1136
  )
812
1137
  );
813
1138
  }
814
1139
  if (orphaned.length === 0 && needsReverification.length === 0) {
815
- console.log(pc2.dim(" No drift detected."));
1140
+ console.log(pc3.dim(" No drift detected."));
816
1141
  }
817
- console.log(pc2.dim(" AGENTS.md and CLAUDE.md updated."));
1142
+ console.log(pc3.dim(" AGENTS.md and CLAUDE.md updated."));
818
1143
  }
819
1144
  async function readKeyFromMcpJson(cwd) {
820
- const mcpPath = join5(cwd, ".mcp.json");
821
- if (!existsSync5(mcpPath)) {
1145
+ const mcpPath = join6(cwd, ".mcp.json");
1146
+ if (!existsSync6(mcpPath)) {
822
1147
  throw new Error(".mcp.json not found. Run `cubedot init` first.");
823
1148
  }
824
- const mcp = JSON.parse(readFileSync5(mcpPath, "utf8"));
1149
+ const mcp = JSON.parse(readFileSync6(mcpPath, "utf8"));
825
1150
  const headers = mcp.mcpServers?.["cubedot"]?.headers ?? {};
826
1151
  const authHeader = headers["Authorization"] ?? headers["authorization"] ?? "";
827
1152
  const key = authHeader.replace(/^ApiKey\s+/i, "").trim();
@@ -834,90 +1159,90 @@ async function readKeyFromMcpJson(cwd) {
834
1159
  }
835
1160
 
836
1161
  // src/commands/status.ts
837
- import pc3 from "picocolors";
838
- import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
839
- import { join as join6 } from "path";
1162
+ import pc4 from "picocolors";
1163
+ import { existsSync as existsSync7, readFileSync as readFileSync7 } from "fs";
1164
+ import { join as join7 } from "path";
840
1165
  async function statusCommand() {
841
1166
  const cwd = process.cwd();
842
1167
  let ok = true;
843
1168
  const config = readCubedotConfig(cwd);
844
1169
  if (!config) {
845
- console.log(pc3.red("\u2717") + " .cubedot/config.json \u2014 missing (run `cubedot init`)");
1170
+ console.log(pc4.red("\u2717") + " .cubedot/config.json \u2014 missing (run `cubedot init`)");
846
1171
  ok = false;
847
1172
  } else {
848
- console.log(pc3.green("\u2713") + " .cubedot/config.json");
849
- console.log(pc3.dim(` projectId: ${config.projectId}`));
1173
+ console.log(pc4.green("\u2713") + " .cubedot/config.json");
1174
+ console.log(pc4.dim(` projectId: ${config.projectId}`));
850
1175
  if (config.lastSynced) {
851
1176
  const ageDays = (Date.now() - new Date(config.lastSynced).getTime()) / 864e5;
852
1177
  const ageStr = ageDays < 1 ? "today" : `${Math.floor(ageDays)} day(s) ago`;
853
1178
  const stale = ageDays >= 3;
854
1179
  console.log(
855
- (stale ? pc3.yellow("\u26A0 ") : pc3.dim(" ")) + ` Last synced: ${ageStr}${stale ? " \u2014 consider running `cubedot sync`" : ""}`
1180
+ (stale ? pc4.yellow("\u26A0 ") : pc4.dim(" ")) + ` Last synced: ${ageStr}${stale ? " \u2014 consider running `cubedot sync`" : ""}`
856
1181
  );
857
1182
  }
858
1183
  }
859
- const mcpPath = join6(cwd, ".mcp.json");
860
- if (!existsSync6(mcpPath)) {
861
- console.log(pc3.red("\u2717") + " .mcp.json \u2014 missing");
1184
+ const mcpPath = join7(cwd, ".mcp.json");
1185
+ if (!existsSync7(mcpPath)) {
1186
+ console.log(pc4.red("\u2717") + " .mcp.json \u2014 missing");
862
1187
  ok = false;
863
1188
  } else {
864
1189
  try {
865
- const mcp = JSON.parse(readFileSync6(mcpPath, "utf8"));
1190
+ const mcp = JSON.parse(readFileSync7(mcpPath, "utf8"));
866
1191
  const headers = mcp.mcpServers?.["cubedot"]?.headers ?? {};
867
1192
  const authHeader = headers["Authorization"] ?? headers["authorization"] ?? "";
868
1193
  const keyRaw = authHeader.replace(/^ApiKey\s+/i, "").trim();
869
1194
  const keyTail = keyRaw ? keyRaw.slice(-8) : "(empty)";
870
- console.log(pc3.green("\u2713") + ` .mcp.json \u2014 key tail: \u2026${keyTail}`);
1195
+ console.log(pc4.green("\u2713") + ` .mcp.json \u2014 key tail: \u2026${keyTail}`);
871
1196
  } catch {
872
- console.log(pc3.red("\u2717") + " .mcp.json \u2014 malformed JSON");
1197
+ console.log(pc4.red("\u2717") + " .mcp.json \u2014 malformed JSON");
873
1198
  ok = false;
874
1199
  }
875
1200
  }
876
1201
  for (const file of ["AGENTS.md", "CLAUDE.md"]) {
877
- const path = join6(cwd, file);
878
- if (!existsSync6(path)) {
879
- console.log(pc3.red("\u2717") + ` ${file} \u2014 missing`);
1202
+ const path = join7(cwd, file);
1203
+ if (!existsSync7(path)) {
1204
+ console.log(pc4.red("\u2717") + ` ${file} \u2014 missing`);
880
1205
  ok = false;
881
1206
  } else {
882
- const content = readFileSync6(path, "utf8");
1207
+ const content = readFileSync7(path, "utf8");
883
1208
  const markers = hasAllMarkers(content);
884
1209
  console.log(
885
- (markers ? pc3.green("\u2713") : pc3.yellow("\u26A0")) + ` ${file}${markers ? "" : " \u2014 markers missing or malformed (run `cubedot sync`)"}`
1210
+ (markers ? pc4.green("\u2713") : pc4.yellow("\u26A0")) + ` ${file}${markers ? "" : " \u2014 markers missing or malformed (run `cubedot sync`)"}`
886
1211
  );
887
1212
  }
888
1213
  }
889
- const verifier = join6(cwd, ".claude", "agents", "CubedotVerifier.md");
890
- const manual = join6(cwd, ".cubedot", "fia-operating-manual.md");
1214
+ const verifier = join7(cwd, ".claude", "agents", "CubedotVerifier.md");
1215
+ const manual = join7(cwd, ".cubedot", "fia-operating-manual.md");
891
1216
  console.log(
892
- (existsSync6(verifier) ? pc3.green("\u2713") : pc3.red("\u2717")) + " .claude/agents/CubedotVerifier.md"
1217
+ (existsSync7(verifier) ? pc4.green("\u2713") : pc4.red("\u2717")) + " .claude/agents/CubedotVerifier.md"
893
1218
  );
894
1219
  console.log(
895
- (existsSync6(manual) ? pc3.green("\u2713") : pc3.red("\u2717")) + " .cubedot/fia-operating-manual.md"
1220
+ (existsSync7(manual) ? pc4.green("\u2713") : pc4.red("\u2717")) + " .cubedot/fia-operating-manual.md"
896
1221
  );
897
1222
  const ledgers = readAllLedgers(cwd);
898
1223
  if (ledgers.length === 0) {
899
- console.log(pc3.dim(" Ledger: no entries yet"));
1224
+ console.log(pc4.dim(" Ledger: no entries yet"));
900
1225
  } else {
901
1226
  const counts = {};
902
1227
  for (const l of ledgers) counts[l.state] = (counts[l.state] ?? 0) + 1;
903
1228
  const summary = Object.entries(counts).map(([s, n]) => `${n} ${s}`).join(", ");
904
- console.log(pc3.dim(` Ledger: ${ledgers.length} entries (${summary})`));
1229
+ console.log(pc4.dim(` Ledger: ${ledgers.length} entries (${summary})`));
905
1230
  }
906
1231
  if (config) {
907
- console.log(pc3.dim("\nChecking MCP connectivity \u2026"));
1232
+ console.log(pc4.dim("\nChecking MCP connectivity \u2026"));
908
1233
  try {
909
1234
  const key = await readKeyFromMcpJson(cwd);
910
1235
  const connectUrl = buildConnectUrl(config.mcpUrl, config.projectId);
911
1236
  const client = await createMcpClient(connectUrl, key);
912
1237
  try {
913
1238
  await validateTools(client);
914
- console.log(pc3.green("\u2713") + " MCP server reachable \u2014 all 7 tools present");
1239
+ console.log(pc4.green("\u2713") + " MCP server reachable \u2014 all 7 tools present");
915
1240
  } finally {
916
1241
  await client.close();
917
1242
  }
918
1243
  } catch (err) {
919
1244
  console.log(
920
- pc3.red("\u2717") + " MCP server: " + categorizeError(err)
1245
+ pc4.red("\u2717") + " MCP server: " + categorizeError(err)
921
1246
  );
922
1247
  ok = false;
923
1248
  }
@@ -926,9 +1251,9 @@ async function statusCommand() {
926
1251
  }
927
1252
 
928
1253
  // src/commands/check.ts
929
- import pc4 from "picocolors";
930
- import { existsSync as existsSync7, readFileSync as readFileSync7 } from "fs";
931
- import { join as join7 } from "path";
1254
+ import pc5 from "picocolors";
1255
+ import { existsSync as existsSync8, readFileSync as readFileSync8 } from "fs";
1256
+ import { join as join8 } from "path";
932
1257
  async function checkCommand() {
933
1258
  const cwd = process.cwd();
934
1259
  const issues = [];
@@ -936,12 +1261,12 @@ async function checkCommand() {
936
1261
  if (!config) {
937
1262
  issues.push(".cubedot/config.json missing \u2014 run `cubedot init`");
938
1263
  }
939
- const mcpPath = join7(cwd, ".mcp.json");
940
- if (!existsSync7(mcpPath)) {
1264
+ const mcpPath = join8(cwd, ".mcp.json");
1265
+ if (!existsSync8(mcpPath)) {
941
1266
  issues.push(".mcp.json missing \u2014 run `cubedot init`");
942
1267
  } else {
943
1268
  try {
944
- const mcp = JSON.parse(readFileSync7(mcpPath, "utf8"));
1269
+ const mcp = JSON.parse(readFileSync8(mcpPath, "utf8"));
945
1270
  if (!mcp.mcpServers?.["cubedot"]) {
946
1271
  issues.push(
947
1272
  ".mcp.json missing mcpServers.cubedot \u2014 run `cubedot init`"
@@ -951,106 +1276,106 @@ async function checkCommand() {
951
1276
  issues.push(".mcp.json contains malformed JSON");
952
1277
  }
953
1278
  }
954
- const agentsPath = join7(cwd, "AGENTS.md");
955
- if (!existsSync7(agentsPath)) {
1279
+ const agentsPath = join8(cwd, "AGENTS.md");
1280
+ if (!existsSync8(agentsPath)) {
956
1281
  issues.push("AGENTS.md missing \u2014 run `cubedot init`");
957
1282
  } else {
958
- const content = readFileSync7(agentsPath, "utf8");
1283
+ const content = readFileSync8(agentsPath, "utf8");
959
1284
  if (!hasAllMarkers(content)) {
960
1285
  issues.push(
961
1286
  "AGENTS.md is missing or has malformed CUBEDOT markers \u2014 run `cubedot sync`"
962
1287
  );
963
1288
  }
964
1289
  }
965
- const claudePath = join7(cwd, "CLAUDE.md");
966
- if (!existsSync7(claudePath)) {
1290
+ const claudePath = join8(cwd, "CLAUDE.md");
1291
+ if (!existsSync8(claudePath)) {
967
1292
  issues.push("CLAUDE.md missing \u2014 run `cubedot init`");
968
1293
  } else {
969
- const content = readFileSync7(claudePath, "utf8");
1294
+ const content = readFileSync8(claudePath, "utf8");
970
1295
  if (!hasAllMarkers(content)) {
971
1296
  issues.push(
972
1297
  "CLAUDE.md is missing or has malformed CUBEDOT markers \u2014 run `cubedot sync`"
973
1298
  );
974
1299
  }
975
1300
  }
976
- const verifier = join7(cwd, ".claude", "agents", "CubedotVerifier.md");
977
- if (!existsSync7(verifier)) {
1301
+ const verifier = join8(cwd, ".claude", "agents", "CubedotVerifier.md");
1302
+ if (!existsSync8(verifier)) {
978
1303
  issues.push(
979
1304
  ".claude/agents/CubedotVerifier.md missing \u2014 run `cubedot init`"
980
1305
  );
981
1306
  }
982
- const manual = join7(cwd, ".cubedot", "fia-operating-manual.md");
983
- if (!existsSync7(manual)) {
1307
+ const manual = join8(cwd, ".cubedot", "fia-operating-manual.md");
1308
+ if (!existsSync8(manual)) {
984
1309
  issues.push(
985
1310
  ".cubedot/fia-operating-manual.md missing \u2014 run `cubedot init`"
986
1311
  );
987
1312
  }
988
1313
  for (const dir of [".cubedot/progress", ".cubedot/reports"]) {
989
- if (!existsSync7(join7(cwd, dir))) {
1314
+ if (!existsSync8(join8(cwd, dir))) {
990
1315
  issues.push(`${dir}/ missing \u2014 run 'cubedot init'`);
991
1316
  }
992
1317
  }
993
- if (existsSync7(claudePath)) {
994
- const content = readFileSync7(claudePath, "utf8");
995
- if (content.includes("fia-operating-manual") && !existsSync7(join7(cwd, ".cubedot", "fia-operating-manual.md"))) {
1318
+ if (existsSync8(claudePath)) {
1319
+ const content = readFileSync8(claudePath, "utf8");
1320
+ if (content.includes("fia-operating-manual") && !existsSync8(join8(cwd, ".cubedot", "fia-operating-manual.md"))) {
996
1321
  issues.push(
997
1322
  "CLAUDE.md references .cubedot/fia-operating-manual.md but it is missing"
998
1323
  );
999
1324
  }
1000
1325
  }
1001
1326
  if (issues.length === 0) {
1002
- console.log(pc4.green("\u2713") + " All checks passed.");
1327
+ console.log(pc5.green("\u2713") + " All checks passed.");
1003
1328
  process.exit(0);
1004
1329
  } else {
1005
- console.log(pc4.red(`\u2717 ${issues.length} issue(s) found:
1330
+ console.log(pc5.red(`\u2717 ${issues.length} issue(s) found:
1006
1331
  `));
1007
1332
  for (const issue of issues) {
1008
- console.log(" " + pc4.red("\u2022") + " " + issue);
1333
+ console.log(" " + pc5.red("\u2022") + " " + issue);
1009
1334
  }
1010
1335
  process.exit(1);
1011
1336
  }
1012
1337
  }
1013
1338
 
1014
1339
  // src/commands/start.ts
1015
- import pc5 from "picocolors";
1340
+ import pc6 from "picocolors";
1016
1341
  function startCommand(fn, opts) {
1017
1342
  const cwd = process.cwd();
1018
1343
  const actor = opts.actor === "human" ? "human" : "agent";
1019
1344
  try {
1020
1345
  const record = startFn(cwd, fn, actor);
1021
1346
  console.log(
1022
- pc5.green("\u2713") + ` ${fn} \u2192 in_progress (actor: ${record.actor}, at: ${record.updatedAt})`
1347
+ pc6.green("\u2713") + ` ${fn} \u2192 in_progress (actor: ${record.actor}, at: ${record.updatedAt})`
1023
1348
  );
1024
- console.log(pc5.dim(` Ledger: .cubedot/progress/${fn}.json`));
1349
+ console.log(pc6.dim(` Ledger: .cubedot/progress/${fn}.json`));
1025
1350
  } catch (err) {
1026
- console.error(pc5.red(`Error: ${err.message}`));
1351
+ console.error(pc6.red(`Error: ${err.message}`));
1027
1352
  process.exit(1);
1028
1353
  }
1029
1354
  }
1030
1355
 
1031
1356
  // src/commands/complete.ts
1032
- import pc6 from "picocolors";
1357
+ import pc7 from "picocolors";
1033
1358
  function completeCommand(fn, opts) {
1034
1359
  const cwd = process.cwd();
1035
1360
  const actor = opts.actor === "human" ? "human" : "agent";
1036
1361
  try {
1037
1362
  const record = completeFn(cwd, fn, actor);
1038
1363
  console.log(
1039
- pc6.green("\u2713") + ` ${fn} \u2192 code-complete (actor: ${record.actor}, at: ${record.updatedAt})`
1364
+ pc7.green("\u2713") + ` ${fn} \u2192 code-complete (actor: ${record.actor}, at: ${record.updatedAt})`
1040
1365
  );
1041
1366
  console.log(
1042
- pc6.dim(
1367
+ pc7.dim(
1043
1368
  ` Next: spawn CubedotVerifier, then ask the developer to run \`cubedot done ${fn}\`.`
1044
1369
  )
1045
1370
  );
1046
1371
  } catch (err) {
1047
- console.error(pc6.red(`Error: ${err.message}`));
1372
+ console.error(pc7.red(`Error: ${err.message}`));
1048
1373
  process.exit(1);
1049
1374
  }
1050
1375
  }
1051
1376
 
1052
1377
  // src/commands/done.ts
1053
- import pc7 from "picocolors";
1378
+ import pc8 from "picocolors";
1054
1379
  function doneCommand(fn, opts) {
1055
1380
  const cwd = process.cwd();
1056
1381
  const actor = opts.actor === "agent" ? "agent" : "human";
@@ -1062,43 +1387,43 @@ function doneCommand(fn, opts) {
1062
1387
  try {
1063
1388
  const record = doneFn(cwd, fn, actor, force, discrepancies);
1064
1389
  console.log(
1065
- pc7.green("\u2713") + ` ${fn} \u2192 done (actor: ${record.actor}, at: ${record.updatedAt})`
1390
+ pc8.green("\u2713") + ` ${fn} \u2192 done (actor: ${record.actor}, at: ${record.updatedAt})`
1066
1391
  );
1067
1392
  if (record.overrides.length > 0) {
1068
1393
  console.log(
1069
- pc7.yellow(
1394
+ pc8.yellow(
1070
1395
  ` \u26A0 Override recorded: ${record.overrides[record.overrides.length - 1].condition}`
1071
1396
  )
1072
1397
  );
1073
1398
  }
1074
1399
  if (discrepancies.length > 0) {
1075
1400
  console.log(
1076
- pc7.yellow(
1401
+ pc8.yellow(
1077
1402
  ` \u26A0 ${discrepancies.length} discrepancy(ies) recorded (criteria accepted unmet):`
1078
1403
  )
1079
1404
  );
1080
1405
  for (const d of discrepancies) {
1081
- console.log(pc7.yellow(` - ${d.criterion}`));
1406
+ console.log(pc8.yellow(` - ${d.criterion}`));
1082
1407
  }
1083
1408
  }
1084
1409
  } catch (err) {
1085
- console.error(pc7.red(`Error: ${err.message}`));
1410
+ console.error(pc8.red(`Error: ${err.message}`));
1086
1411
  process.exit(1);
1087
1412
  }
1088
1413
  }
1089
1414
 
1090
1415
  // src/commands/progress.ts
1091
- import pc8 from "picocolors";
1092
- import { existsSync as existsSync8, readFileSync as readFileSync8 } from "fs";
1093
- import { join as join8 } from "path";
1416
+ import pc9 from "picocolors";
1417
+ import { existsSync as existsSync9, readFileSync as readFileSync9 } from "fs";
1418
+ import { join as join9 } from "path";
1094
1419
  function progressCommand() {
1095
1420
  const cwd = process.cwd();
1096
1421
  const ledgers = readAllLedgers(cwd);
1097
1422
  let fnNames = /* @__PURE__ */ new Map();
1098
1423
  for (const file of ["AGENTS.md", "CLAUDE.md"]) {
1099
- const path = join8(cwd, file);
1100
- if (existsSync8(path)) {
1101
- fnNames = parseRegistryBlock(readFileSync8(path, "utf8"));
1424
+ const path = join9(cwd, file);
1425
+ if (existsSync9(path)) {
1426
+ fnNames = parseRegistryBlock(readFileSync9(path, "utf8"));
1102
1427
  if (fnNames.size > 0) break;
1103
1428
  }
1104
1429
  }
@@ -1117,24 +1442,24 @@ function progressCommand() {
1117
1442
  const ccCount = byState["code-complete"].length;
1118
1443
  const ipCount = byState["in_progress"].length;
1119
1444
  const todoCount = Math.max(0, total - doneCount - ccCount - ipCount);
1120
- console.log("\n" + pc8.bold("Progress Summary"));
1445
+ console.log("\n" + pc9.bold("Progress Summary"));
1121
1446
  console.log("\u2501".repeat(40));
1122
- console.log(` Done: ${pc8.green(String(doneCount))}/${total}`);
1447
+ console.log(` Done: ${pc9.green(String(doneCount))}/${total}`);
1123
1448
  if (ccCount > 0)
1124
- console.log(` Code-complete: ${pc8.cyan(String(ccCount))}/${total}`);
1449
+ console.log(` Code-complete: ${pc9.cyan(String(ccCount))}/${total}`);
1125
1450
  if (ipCount > 0)
1126
- console.log(` In progress: ${pc8.yellow(String(ipCount))}/${total}`);
1451
+ console.log(` In progress: ${pc9.yellow(String(ipCount))}/${total}`);
1127
1452
  if (todoCount > 0)
1128
- console.log(` Todo: ${pc8.dim(String(todoCount))}/${total}`);
1453
+ console.log(` Todo: ${pc9.dim(String(todoCount))}/${total}`);
1129
1454
  const active = [
1130
1455
  ...byState["in_progress"],
1131
1456
  ...byState["code-complete"]
1132
1457
  ];
1133
1458
  if (active.length > 0) {
1134
- console.log("\n" + pc8.bold("Active:"));
1459
+ console.log("\n" + pc9.bold("Active:"));
1135
1460
  for (const l of active) {
1136
1461
  const name = fnNames.get(l.code) ? ` \u2014 ${fnNames.get(l.code)}` : "";
1137
- const stateLabel = l.state === "in_progress" ? pc8.yellow("in_progress") : pc8.cyan("code-complete");
1462
+ const stateLabel = l.state === "in_progress" ? pc9.yellow("in_progress") : pc9.cyan("code-complete");
1138
1463
  console.log(` ${l.code}${name} [${stateLabel}]`);
1139
1464
  }
1140
1465
  }
@@ -1143,21 +1468,21 @@ function progressCommand() {
1143
1468
  const registryOrder = Array.from(fnNames.keys());
1144
1469
  const nextCode = registryOrder.find((code) => !ledgerCodes.has(code));
1145
1470
  if (nextCode) {
1146
- console.log("\n" + pc8.bold("Next unblocked:"));
1471
+ console.log("\n" + pc9.bold("Next unblocked:"));
1147
1472
  console.log(` ${nextCode} \u2014 ${fnNames.get(nextCode) ?? ""}`);
1148
1473
  }
1149
1474
  }
1150
1475
  if (flagged.length > 0) {
1151
- console.log("\n" + pc8.bold(pc8.yellow("Flagged:")));
1476
+ console.log("\n" + pc9.bold(pc9.yellow("Flagged:")));
1152
1477
  for (const l of flagged) {
1153
1478
  console.log(` ${l.code} [${l.flags.join(", ")}]`);
1154
1479
  }
1155
1480
  }
1156
1481
  if (byState["done"].length > 0) {
1157
- console.log("\n" + pc8.bold("Done:"));
1482
+ console.log("\n" + pc9.bold("Done:"));
1158
1483
  for (const l of byState["done"]) {
1159
1484
  const name = fnNames.get(l.code) ? ` \u2014 ${fnNames.get(l.code)}` : "";
1160
- console.log(` ${pc8.green("\u2713")} ${l.code}${name}`);
1485
+ console.log(` ${pc9.green("\u2713")} ${l.code}${name}`);
1161
1486
  }
1162
1487
  }
1163
1488
  console.log("");
@@ -1167,15 +1492,16 @@ function progressCommand() {
1167
1492
  var __filename3 = fileURLToPath4(import.meta.url);
1168
1493
  var __dirname4 = dirname4(__filename3);
1169
1494
  var pkg3 = JSON.parse(
1170
- readFileSync9(join9(__dirname4, "../package.json"), "utf8")
1495
+ readFileSync10(join10(__dirname4, "../package.json"), "utf8")
1171
1496
  );
1172
1497
  var program = new Command();
1173
1498
  var collect = (value, prev) => [...prev, value];
1174
1499
  program.name("cubedot").description(
1175
1500
  "Connect your repo to the Cubedot MCP and drive the spec-first build loop."
1176
1501
  ).version(pkg3.version);
1177
- program.command("init").description("Connect and scaffold the repo (validates MCP, writes config + hot-memory files).").option("--token <token>", "Base64url connect token {v:1, projectId, key, mcpUrl}").option("--project <projectId>", "Project ID (alternative to --token)").option("--key <key>", "MCP API key (alternative to --token)").option("--url <url>", "MCP URL (alternative to --token)").action(initCommand);
1502
+ program.command("init").description("Connect and scaffold the repo (validates MCP, writes config + hot-memory files).").option("--token <token>", "Base64url connect token {v:1, projectId, key, mcpUrl}").option("--project <projectId>", "Project ID (alternative to --token)").option("--key <key>", "MCP API key (alternative to --token)").option("--url <url>", "MCP URL (alternative to --token)").option("--force", "Override the self-install guard (run init inside the CLI source repo).").action(initCommand);
1178
1503
  program.command("sync").description("Refresh local projection (AGENTS.md/CLAUDE.md markers) and detect drift.").action(syncCommand);
1504
+ program.command("uninstall").description("Remove all cubedot scaffold files from this repo and disconnect from the MCP.").option("-y, --yes", "Skip the confirmation prompt.").action(uninstallCommand);
1179
1505
  program.command("status").description("Connectivity ping + file-health check (read-only).").action(statusCommand);
1180
1506
  program.command("check").description("Validate markers, config, and server name (read-only; CI-friendly).").action(checkCommand);
1181
1507
  program.command("start <fn>").description("Set a functionality to in_progress.").option("--actor <actor>", "Actor performing the action", "agent").action(startCommand);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cubedot/cli",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Cubedot CLI — connect your repo to the Cubedot MCP and drive the build loop",
5
5
  "license": "UNLICENSED",
6
6
  "homepage": "https://cubedot.ai",