@cubedot/cli 0.1.2 → 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 +454 -130
  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";
@@ -178,17 +181,18 @@ var GITIGNORE_ENTRIES = [
178
181
  function updateGitignore(cwd) {
179
182
  const path = join(cwd, ".gitignore");
180
183
  let content = existsSync(path) ? readFileSync(path, "utf8") : "";
181
- const lines = content.split("\n");
182
- let changed = false;
184
+ const existing = content.split("\n");
185
+ const added = [];
183
186
  for (const entry of GITIGNORE_ENTRIES) {
184
- if (!lines.some((l) => l.trim() === entry)) {
187
+ if (!existing.some((l) => l.trim() === entry)) {
185
188
  content = content.endsWith("\n") ? content + entry + "\n" : content + "\n" + entry + "\n";
186
- changed = true;
189
+ added.push(entry);
187
190
  }
188
191
  }
189
- if (changed) {
192
+ if (added.length > 0) {
190
193
  writeFileSync(path, content, "utf8");
191
194
  }
195
+ return added;
192
196
  }
193
197
  function readHashes(cwd) {
194
198
  const path = join(cwd, ".cubedot", "sync", "hashes.json");
@@ -208,8 +212,9 @@ function enableMcpServerInSettings(cwd, serverName = "cubedot") {
208
212
  const dir = join(cwd, ".claude");
209
213
  if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
210
214
  const path = join(dir, "settings.json");
215
+ const existed = existsSync(path);
211
216
  let settings = {};
212
- if (existsSync(path)) {
217
+ if (existed) {
213
218
  try {
214
219
  settings = JSON.parse(readFileSync(path, "utf8"));
215
220
  } catch {
@@ -220,6 +225,7 @@ function enableMcpServerInSettings(cwd, serverName = "cubedot") {
220
225
  settings["enabledMcpjsonServers"] = [...cur, serverName];
221
226
  }
222
227
  writeFileSync(path, JSON.stringify(settings, null, 2) + "\n", "utf8");
228
+ return { created: !existed };
223
229
  }
224
230
  function readLinks(cwd) {
225
231
  const path = join(cwd, ".cubedot", "links.json");
@@ -230,6 +236,76 @@ function readLinks(cwd) {
230
236
  return {};
231
237
  }
232
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
+ }
233
309
 
234
310
  // src/hotmemory.ts
235
311
  import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
@@ -396,6 +472,25 @@ function decodeToken(token) {
396
472
  }
397
473
  async function initCommand(opts) {
398
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"));
399
494
  let projectId;
400
495
  let key;
401
496
  let mcpUrl;
@@ -485,7 +580,7 @@ async function initCommand(opts) {
485
580
  await client.close();
486
581
  }
487
582
  writeMcpJson(cwd, mcpUrl, projectId, key);
488
- enableMcpServerInSettings(cwd);
583
+ const { created: settingsCreated } = enableMcpServerInSettings(cwd);
489
584
  writeCubedotConfig(cwd, {
490
585
  projectId,
491
586
  mcpUrl,
@@ -498,7 +593,33 @@ async function initCommand(opts) {
498
593
  generateMemoryFiles(cwd, identity, conventions, registry);
499
594
  installAgentAssets(cwd);
500
595
  scaffoldCubedotDirs(cwd);
501
- 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);
502
623
  const projectName = orientText.split("\n").find((l) => l.trim())?.replace(/^#+\s*/, "") ?? projectId;
503
624
  console.log("\n" + pc.green("\u2713") + " Cubedot connected: " + pc.bold(projectName));
504
625
  console.log(pc.dim(` Project ID : ${projectId}`));
@@ -510,6 +631,8 @@ async function initCommand(opts) {
510
631
  const written = [
511
632
  ".mcp.json (gitignored \u2014 holds your key)",
512
633
  ".cubedot/config.json (gitignored)",
634
+ ".cubedot/install-manifest.json",
635
+ ".claude/settings.json (enabledMcpjsonServers)",
513
636
  "AGENTS.md",
514
637
  "CLAUDE.md",
515
638
  ".claude/agents/CubedotVerifier.md",
@@ -523,24 +646,224 @@ async function initCommand(opts) {
523
646
  console.log("\nOpen Claude Code in this directory to start building. \u2728");
524
647
  }
525
648
 
526
- // src/commands/sync.ts
649
+ // src/commands/uninstall.ts
527
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";
528
851
  import { createHash } from "crypto";
529
- import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
530
- import { join as join5 } from "path";
852
+ import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
853
+ import { join as join6 } from "path";
531
854
  import { fileURLToPath as fileURLToPath3 } from "url";
532
855
  import { dirname as dirname3 } from "path";
533
856
 
534
857
  // src/ledger.ts
535
858
  import {
536
- existsSync as existsSync4,
859
+ existsSync as existsSync5,
537
860
  mkdirSync as mkdirSync2,
538
- readFileSync as readFileSync4,
539
- readdirSync,
861
+ readFileSync as readFileSync5,
862
+ readdirSync as readdirSync2,
540
863
  renameSync,
541
864
  writeFileSync as writeFileSync3
542
865
  } from "fs";
543
- import { join as join4 } from "path";
866
+ import { join as join5 } from "path";
544
867
  import { randomUUID } from "crypto";
545
868
  var FN_CODE_REGEX = /^FN-\d{3}-\d{2,}$/;
546
869
  function validateFnCode(code) {
@@ -551,13 +874,13 @@ function validateFnCode(code) {
551
874
  }
552
875
  }
553
876
  function getLedgerPath(cwd, code) {
554
- return join4(cwd, ".cubedot", "progress", `${code}.json`);
877
+ return join5(cwd, ".cubedot", "progress", `${code}.json`);
555
878
  }
556
879
  function readLedger(cwd, code) {
557
880
  const path = getLedgerPath(cwd, code);
558
- if (!existsSync4(path)) return null;
881
+ if (!existsSync5(path)) return null;
559
882
  try {
560
- return JSON.parse(readFileSync4(path, "utf8"));
883
+ return JSON.parse(readFileSync5(path, "utf8"));
561
884
  } catch {
562
885
  throw new Error(
563
886
  `Failed to read ledger for ${code}: file exists but contains malformed JSON.`
@@ -565,11 +888,11 @@ function readLedger(cwd, code) {
565
888
  }
566
889
  }
567
890
  function readAllLedgers(cwd) {
568
- const dir = join4(cwd, ".cubedot", "progress");
569
- if (!existsSync4(dir)) return [];
570
- 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) => {
571
894
  try {
572
- return JSON.parse(readFileSync4(join4(dir, f), "utf8"));
895
+ return JSON.parse(readFileSync5(join5(dir, f), "utf8"));
573
896
  } catch {
574
897
  return null;
575
898
  }
@@ -577,8 +900,8 @@ function readAllLedgers(cwd) {
577
900
  }
578
901
  function writeLedger(cwd, record) {
579
902
  const path = getLedgerPath(cwd, record.code);
580
- const dir = join4(cwd, ".cubedot", "progress");
581
- if (!existsSync4(dir)) mkdirSync2(dir, { recursive: true });
903
+ const dir = join5(cwd, ".cubedot", "progress");
904
+ if (!existsSync5(dir)) mkdirSync2(dir, { recursive: true });
582
905
  const tmp = `${path}.${randomUUID()}.tmp`;
583
906
  writeFileSync3(tmp, JSON.stringify(record, null, 2) + "\n", "utf8");
584
907
  renameSync(tmp, path);
@@ -671,12 +994,12 @@ function doneFn(cwd, code, actor = "human", force = false, discrepancies = []) {
671
994
  var __filename2 = fileURLToPath3(import.meta.url);
672
995
  var __dirname3 = dirname3(__filename2);
673
996
  var pkg2 = JSON.parse(
674
- readFileSync5(join5(__dirname3, "../package.json"), "utf8")
997
+ readFileSync6(join6(__dirname3, "../package.json"), "utf8")
675
998
  );
676
999
  function fileHash(filePath) {
677
- if (!existsSync5(filePath)) return "";
1000
+ if (!existsSync6(filePath)) return "";
678
1001
  try {
679
- return createHash("sha256").update(readFileSync5(filePath)).digest("hex");
1002
+ return createHash("sha256").update(readFileSync6(filePath)).digest("hex");
680
1003
  } catch {
681
1004
  return "";
682
1005
  }
@@ -686,7 +1009,7 @@ async function syncCommand() {
686
1009
  const config = readCubedotConfig(cwd);
687
1010
  if (!config) {
688
1011
  console.error(
689
- pc2.red(
1012
+ pc3.red(
690
1013
  "Error: .cubedot/config.json not found. Run `cubedot init` first."
691
1014
  )
692
1015
  );
@@ -697,26 +1020,26 @@ async function syncCommand() {
697
1020
  const ageDays = (Date.now() - new Date(config.lastSynced).getTime()) / 864e5;
698
1021
  if (ageDays >= STALE_DAYS) {
699
1022
  console.log(
700
- pc2.yellow(
1023
+ pc3.yellow(
701
1024
  `\u26A0 Last synced ${Math.floor(ageDays)} day(s) ago \u2014 refreshing now.`
702
1025
  )
703
1026
  );
704
1027
  }
705
1028
  }
706
1029
  const connectUrl = buildConnectUrl(config.mcpUrl, config.projectId);
707
- console.log(pc2.dim(`Connecting to ${connectUrl} \u2026`));
1030
+ console.log(pc3.dim(`Connecting to ${connectUrl} \u2026`));
708
1031
  let client;
709
1032
  try {
710
1033
  client = await createMcpClient(connectUrl, await readKeyFromMcpJson(cwd));
711
1034
  } catch (err) {
712
- console.error(pc2.red(`Error: ${categorizeError(err)}`));
1035
+ console.error(pc3.red(`Error: ${categorizeError(err)}`));
713
1036
  process.exit(1);
714
1037
  }
715
1038
  try {
716
1039
  await validateTools(client);
717
1040
  } catch (err) {
718
1041
  await client.close();
719
- console.error(pc2.red(`Error: ${err.message}`));
1042
+ console.error(pc3.red(`Error: ${err.message}`));
720
1043
  process.exit(1);
721
1044
  }
722
1045
  let orientText = "";
@@ -734,7 +1057,7 @@ async function syncCommand() {
734
1057
  } catch (err) {
735
1058
  await client.close();
736
1059
  console.error(
737
- pc2.red(`Error fetching project data: ${err.message}`)
1060
+ pc3.red(`Error fetching project data: ${err.message}`)
738
1061
  );
739
1062
  process.exit(1);
740
1063
  } finally {
@@ -761,7 +1084,7 @@ async function syncCommand() {
761
1084
  const skipOrphanDetection = specFnCodes.size === 0 && ledgers.length > 0;
762
1085
  if (skipOrphanDetection) {
763
1086
  console.log(
764
- pc2.yellow(
1087
+ pc3.yellow(
765
1088
  "\u26A0 Spec tree returned no functionalities \u2014 skipping orphan detection this sync."
766
1089
  )
767
1090
  );
@@ -777,7 +1100,7 @@ async function syncCommand() {
777
1100
  if (record.state === "done") {
778
1101
  const files = links[record.code] ?? [];
779
1102
  for (const filePath of files) {
780
- const absPath = join5(cwd, filePath);
1103
+ const absPath = join6(cwd, filePath);
781
1104
  const newHash = fileHash(absPath);
782
1105
  const oldHash = prevHashes[filePath] ?? "";
783
1106
  nextHashes[filePath] = newHash;
@@ -795,35 +1118,35 @@ async function syncCommand() {
795
1118
  }
796
1119
  for (const [code, files] of Object.entries(links)) {
797
1120
  for (const filePath of files) {
798
- const absPath = join5(cwd, filePath);
1121
+ const absPath = join6(cwd, filePath);
799
1122
  nextHashes[filePath] = fileHash(absPath);
800
1123
  }
801
1124
  }
802
1125
  writeHashes(cwd, nextHashes);
803
- console.log(pc2.green("\u2713") + " Sync complete.");
1126
+ console.log(pc3.green("\u2713") + " Sync complete.");
804
1127
  if (orphaned.length > 0) {
805
1128
  console.log(
806
- pc2.yellow(` Orphaned (removed from spec): ${orphaned.join(", ")}`)
1129
+ pc3.yellow(` Orphaned (removed from spec): ${orphaned.join(", ")}`)
807
1130
  );
808
1131
  }
809
1132
  if (needsReverification.length > 0) {
810
1133
  console.log(
811
- pc2.yellow(
1134
+ pc3.yellow(
812
1135
  ` Needs re-verification (spec changed after done): ${needsReverification.join(", ")}`
813
1136
  )
814
1137
  );
815
1138
  }
816
1139
  if (orphaned.length === 0 && needsReverification.length === 0) {
817
- console.log(pc2.dim(" No drift detected."));
1140
+ console.log(pc3.dim(" No drift detected."));
818
1141
  }
819
- console.log(pc2.dim(" AGENTS.md and CLAUDE.md updated."));
1142
+ console.log(pc3.dim(" AGENTS.md and CLAUDE.md updated."));
820
1143
  }
821
1144
  async function readKeyFromMcpJson(cwd) {
822
- const mcpPath = join5(cwd, ".mcp.json");
823
- if (!existsSync5(mcpPath)) {
1145
+ const mcpPath = join6(cwd, ".mcp.json");
1146
+ if (!existsSync6(mcpPath)) {
824
1147
  throw new Error(".mcp.json not found. Run `cubedot init` first.");
825
1148
  }
826
- const mcp = JSON.parse(readFileSync5(mcpPath, "utf8"));
1149
+ const mcp = JSON.parse(readFileSync6(mcpPath, "utf8"));
827
1150
  const headers = mcp.mcpServers?.["cubedot"]?.headers ?? {};
828
1151
  const authHeader = headers["Authorization"] ?? headers["authorization"] ?? "";
829
1152
  const key = authHeader.replace(/^ApiKey\s+/i, "").trim();
@@ -836,90 +1159,90 @@ async function readKeyFromMcpJson(cwd) {
836
1159
  }
837
1160
 
838
1161
  // src/commands/status.ts
839
- import pc3 from "picocolors";
840
- import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
841
- 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";
842
1165
  async function statusCommand() {
843
1166
  const cwd = process.cwd();
844
1167
  let ok = true;
845
1168
  const config = readCubedotConfig(cwd);
846
1169
  if (!config) {
847
- 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`)");
848
1171
  ok = false;
849
1172
  } else {
850
- console.log(pc3.green("\u2713") + " .cubedot/config.json");
851
- console.log(pc3.dim(` projectId: ${config.projectId}`));
1173
+ console.log(pc4.green("\u2713") + " .cubedot/config.json");
1174
+ console.log(pc4.dim(` projectId: ${config.projectId}`));
852
1175
  if (config.lastSynced) {
853
1176
  const ageDays = (Date.now() - new Date(config.lastSynced).getTime()) / 864e5;
854
1177
  const ageStr = ageDays < 1 ? "today" : `${Math.floor(ageDays)} day(s) ago`;
855
1178
  const stale = ageDays >= 3;
856
1179
  console.log(
857
- (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`" : ""}`
858
1181
  );
859
1182
  }
860
1183
  }
861
- const mcpPath = join6(cwd, ".mcp.json");
862
- if (!existsSync6(mcpPath)) {
863
- 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");
864
1187
  ok = false;
865
1188
  } else {
866
1189
  try {
867
- const mcp = JSON.parse(readFileSync6(mcpPath, "utf8"));
1190
+ const mcp = JSON.parse(readFileSync7(mcpPath, "utf8"));
868
1191
  const headers = mcp.mcpServers?.["cubedot"]?.headers ?? {};
869
1192
  const authHeader = headers["Authorization"] ?? headers["authorization"] ?? "";
870
1193
  const keyRaw = authHeader.replace(/^ApiKey\s+/i, "").trim();
871
1194
  const keyTail = keyRaw ? keyRaw.slice(-8) : "(empty)";
872
- 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}`);
873
1196
  } catch {
874
- console.log(pc3.red("\u2717") + " .mcp.json \u2014 malformed JSON");
1197
+ console.log(pc4.red("\u2717") + " .mcp.json \u2014 malformed JSON");
875
1198
  ok = false;
876
1199
  }
877
1200
  }
878
1201
  for (const file of ["AGENTS.md", "CLAUDE.md"]) {
879
- const path = join6(cwd, file);
880
- if (!existsSync6(path)) {
881
- 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`);
882
1205
  ok = false;
883
1206
  } else {
884
- const content = readFileSync6(path, "utf8");
1207
+ const content = readFileSync7(path, "utf8");
885
1208
  const markers = hasAllMarkers(content);
886
1209
  console.log(
887
- (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`)"}`
888
1211
  );
889
1212
  }
890
1213
  }
891
- const verifier = join6(cwd, ".claude", "agents", "CubedotVerifier.md");
892
- 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");
893
1216
  console.log(
894
- (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"
895
1218
  );
896
1219
  console.log(
897
- (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"
898
1221
  );
899
1222
  const ledgers = readAllLedgers(cwd);
900
1223
  if (ledgers.length === 0) {
901
- console.log(pc3.dim(" Ledger: no entries yet"));
1224
+ console.log(pc4.dim(" Ledger: no entries yet"));
902
1225
  } else {
903
1226
  const counts = {};
904
1227
  for (const l of ledgers) counts[l.state] = (counts[l.state] ?? 0) + 1;
905
1228
  const summary = Object.entries(counts).map(([s, n]) => `${n} ${s}`).join(", ");
906
- console.log(pc3.dim(` Ledger: ${ledgers.length} entries (${summary})`));
1229
+ console.log(pc4.dim(` Ledger: ${ledgers.length} entries (${summary})`));
907
1230
  }
908
1231
  if (config) {
909
- console.log(pc3.dim("\nChecking MCP connectivity \u2026"));
1232
+ console.log(pc4.dim("\nChecking MCP connectivity \u2026"));
910
1233
  try {
911
1234
  const key = await readKeyFromMcpJson(cwd);
912
1235
  const connectUrl = buildConnectUrl(config.mcpUrl, config.projectId);
913
1236
  const client = await createMcpClient(connectUrl, key);
914
1237
  try {
915
1238
  await validateTools(client);
916
- 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");
917
1240
  } finally {
918
1241
  await client.close();
919
1242
  }
920
1243
  } catch (err) {
921
1244
  console.log(
922
- pc3.red("\u2717") + " MCP server: " + categorizeError(err)
1245
+ pc4.red("\u2717") + " MCP server: " + categorizeError(err)
923
1246
  );
924
1247
  ok = false;
925
1248
  }
@@ -928,9 +1251,9 @@ async function statusCommand() {
928
1251
  }
929
1252
 
930
1253
  // src/commands/check.ts
931
- import pc4 from "picocolors";
932
- import { existsSync as existsSync7, readFileSync as readFileSync7 } from "fs";
933
- 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";
934
1257
  async function checkCommand() {
935
1258
  const cwd = process.cwd();
936
1259
  const issues = [];
@@ -938,12 +1261,12 @@ async function checkCommand() {
938
1261
  if (!config) {
939
1262
  issues.push(".cubedot/config.json missing \u2014 run `cubedot init`");
940
1263
  }
941
- const mcpPath = join7(cwd, ".mcp.json");
942
- if (!existsSync7(mcpPath)) {
1264
+ const mcpPath = join8(cwd, ".mcp.json");
1265
+ if (!existsSync8(mcpPath)) {
943
1266
  issues.push(".mcp.json missing \u2014 run `cubedot init`");
944
1267
  } else {
945
1268
  try {
946
- const mcp = JSON.parse(readFileSync7(mcpPath, "utf8"));
1269
+ const mcp = JSON.parse(readFileSync8(mcpPath, "utf8"));
947
1270
  if (!mcp.mcpServers?.["cubedot"]) {
948
1271
  issues.push(
949
1272
  ".mcp.json missing mcpServers.cubedot \u2014 run `cubedot init`"
@@ -953,106 +1276,106 @@ async function checkCommand() {
953
1276
  issues.push(".mcp.json contains malformed JSON");
954
1277
  }
955
1278
  }
956
- const agentsPath = join7(cwd, "AGENTS.md");
957
- if (!existsSync7(agentsPath)) {
1279
+ const agentsPath = join8(cwd, "AGENTS.md");
1280
+ if (!existsSync8(agentsPath)) {
958
1281
  issues.push("AGENTS.md missing \u2014 run `cubedot init`");
959
1282
  } else {
960
- const content = readFileSync7(agentsPath, "utf8");
1283
+ const content = readFileSync8(agentsPath, "utf8");
961
1284
  if (!hasAllMarkers(content)) {
962
1285
  issues.push(
963
1286
  "AGENTS.md is missing or has malformed CUBEDOT markers \u2014 run `cubedot sync`"
964
1287
  );
965
1288
  }
966
1289
  }
967
- const claudePath = join7(cwd, "CLAUDE.md");
968
- if (!existsSync7(claudePath)) {
1290
+ const claudePath = join8(cwd, "CLAUDE.md");
1291
+ if (!existsSync8(claudePath)) {
969
1292
  issues.push("CLAUDE.md missing \u2014 run `cubedot init`");
970
1293
  } else {
971
- const content = readFileSync7(claudePath, "utf8");
1294
+ const content = readFileSync8(claudePath, "utf8");
972
1295
  if (!hasAllMarkers(content)) {
973
1296
  issues.push(
974
1297
  "CLAUDE.md is missing or has malformed CUBEDOT markers \u2014 run `cubedot sync`"
975
1298
  );
976
1299
  }
977
1300
  }
978
- const verifier = join7(cwd, ".claude", "agents", "CubedotVerifier.md");
979
- if (!existsSync7(verifier)) {
1301
+ const verifier = join8(cwd, ".claude", "agents", "CubedotVerifier.md");
1302
+ if (!existsSync8(verifier)) {
980
1303
  issues.push(
981
1304
  ".claude/agents/CubedotVerifier.md missing \u2014 run `cubedot init`"
982
1305
  );
983
1306
  }
984
- const manual = join7(cwd, ".cubedot", "fia-operating-manual.md");
985
- if (!existsSync7(manual)) {
1307
+ const manual = join8(cwd, ".cubedot", "fia-operating-manual.md");
1308
+ if (!existsSync8(manual)) {
986
1309
  issues.push(
987
1310
  ".cubedot/fia-operating-manual.md missing \u2014 run `cubedot init`"
988
1311
  );
989
1312
  }
990
1313
  for (const dir of [".cubedot/progress", ".cubedot/reports"]) {
991
- if (!existsSync7(join7(cwd, dir))) {
1314
+ if (!existsSync8(join8(cwd, dir))) {
992
1315
  issues.push(`${dir}/ missing \u2014 run 'cubedot init'`);
993
1316
  }
994
1317
  }
995
- if (existsSync7(claudePath)) {
996
- const content = readFileSync7(claudePath, "utf8");
997
- 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"))) {
998
1321
  issues.push(
999
1322
  "CLAUDE.md references .cubedot/fia-operating-manual.md but it is missing"
1000
1323
  );
1001
1324
  }
1002
1325
  }
1003
1326
  if (issues.length === 0) {
1004
- console.log(pc4.green("\u2713") + " All checks passed.");
1327
+ console.log(pc5.green("\u2713") + " All checks passed.");
1005
1328
  process.exit(0);
1006
1329
  } else {
1007
- console.log(pc4.red(`\u2717 ${issues.length} issue(s) found:
1330
+ console.log(pc5.red(`\u2717 ${issues.length} issue(s) found:
1008
1331
  `));
1009
1332
  for (const issue of issues) {
1010
- console.log(" " + pc4.red("\u2022") + " " + issue);
1333
+ console.log(" " + pc5.red("\u2022") + " " + issue);
1011
1334
  }
1012
1335
  process.exit(1);
1013
1336
  }
1014
1337
  }
1015
1338
 
1016
1339
  // src/commands/start.ts
1017
- import pc5 from "picocolors";
1340
+ import pc6 from "picocolors";
1018
1341
  function startCommand(fn, opts) {
1019
1342
  const cwd = process.cwd();
1020
1343
  const actor = opts.actor === "human" ? "human" : "agent";
1021
1344
  try {
1022
1345
  const record = startFn(cwd, fn, actor);
1023
1346
  console.log(
1024
- 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})`
1025
1348
  );
1026
- console.log(pc5.dim(` Ledger: .cubedot/progress/${fn}.json`));
1349
+ console.log(pc6.dim(` Ledger: .cubedot/progress/${fn}.json`));
1027
1350
  } catch (err) {
1028
- console.error(pc5.red(`Error: ${err.message}`));
1351
+ console.error(pc6.red(`Error: ${err.message}`));
1029
1352
  process.exit(1);
1030
1353
  }
1031
1354
  }
1032
1355
 
1033
1356
  // src/commands/complete.ts
1034
- import pc6 from "picocolors";
1357
+ import pc7 from "picocolors";
1035
1358
  function completeCommand(fn, opts) {
1036
1359
  const cwd = process.cwd();
1037
1360
  const actor = opts.actor === "human" ? "human" : "agent";
1038
1361
  try {
1039
1362
  const record = completeFn(cwd, fn, actor);
1040
1363
  console.log(
1041
- 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})`
1042
1365
  );
1043
1366
  console.log(
1044
- pc6.dim(
1367
+ pc7.dim(
1045
1368
  ` Next: spawn CubedotVerifier, then ask the developer to run \`cubedot done ${fn}\`.`
1046
1369
  )
1047
1370
  );
1048
1371
  } catch (err) {
1049
- console.error(pc6.red(`Error: ${err.message}`));
1372
+ console.error(pc7.red(`Error: ${err.message}`));
1050
1373
  process.exit(1);
1051
1374
  }
1052
1375
  }
1053
1376
 
1054
1377
  // src/commands/done.ts
1055
- import pc7 from "picocolors";
1378
+ import pc8 from "picocolors";
1056
1379
  function doneCommand(fn, opts) {
1057
1380
  const cwd = process.cwd();
1058
1381
  const actor = opts.actor === "agent" ? "agent" : "human";
@@ -1064,43 +1387,43 @@ function doneCommand(fn, opts) {
1064
1387
  try {
1065
1388
  const record = doneFn(cwd, fn, actor, force, discrepancies);
1066
1389
  console.log(
1067
- 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})`
1068
1391
  );
1069
1392
  if (record.overrides.length > 0) {
1070
1393
  console.log(
1071
- pc7.yellow(
1394
+ pc8.yellow(
1072
1395
  ` \u26A0 Override recorded: ${record.overrides[record.overrides.length - 1].condition}`
1073
1396
  )
1074
1397
  );
1075
1398
  }
1076
1399
  if (discrepancies.length > 0) {
1077
1400
  console.log(
1078
- pc7.yellow(
1401
+ pc8.yellow(
1079
1402
  ` \u26A0 ${discrepancies.length} discrepancy(ies) recorded (criteria accepted unmet):`
1080
1403
  )
1081
1404
  );
1082
1405
  for (const d of discrepancies) {
1083
- console.log(pc7.yellow(` - ${d.criterion}`));
1406
+ console.log(pc8.yellow(` - ${d.criterion}`));
1084
1407
  }
1085
1408
  }
1086
1409
  } catch (err) {
1087
- console.error(pc7.red(`Error: ${err.message}`));
1410
+ console.error(pc8.red(`Error: ${err.message}`));
1088
1411
  process.exit(1);
1089
1412
  }
1090
1413
  }
1091
1414
 
1092
1415
  // src/commands/progress.ts
1093
- import pc8 from "picocolors";
1094
- import { existsSync as existsSync8, readFileSync as readFileSync8 } from "fs";
1095
- 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";
1096
1419
  function progressCommand() {
1097
1420
  const cwd = process.cwd();
1098
1421
  const ledgers = readAllLedgers(cwd);
1099
1422
  let fnNames = /* @__PURE__ */ new Map();
1100
1423
  for (const file of ["AGENTS.md", "CLAUDE.md"]) {
1101
- const path = join8(cwd, file);
1102
- if (existsSync8(path)) {
1103
- fnNames = parseRegistryBlock(readFileSync8(path, "utf8"));
1424
+ const path = join9(cwd, file);
1425
+ if (existsSync9(path)) {
1426
+ fnNames = parseRegistryBlock(readFileSync9(path, "utf8"));
1104
1427
  if (fnNames.size > 0) break;
1105
1428
  }
1106
1429
  }
@@ -1119,24 +1442,24 @@ function progressCommand() {
1119
1442
  const ccCount = byState["code-complete"].length;
1120
1443
  const ipCount = byState["in_progress"].length;
1121
1444
  const todoCount = Math.max(0, total - doneCount - ccCount - ipCount);
1122
- console.log("\n" + pc8.bold("Progress Summary"));
1445
+ console.log("\n" + pc9.bold("Progress Summary"));
1123
1446
  console.log("\u2501".repeat(40));
1124
- console.log(` Done: ${pc8.green(String(doneCount))}/${total}`);
1447
+ console.log(` Done: ${pc9.green(String(doneCount))}/${total}`);
1125
1448
  if (ccCount > 0)
1126
- console.log(` Code-complete: ${pc8.cyan(String(ccCount))}/${total}`);
1449
+ console.log(` Code-complete: ${pc9.cyan(String(ccCount))}/${total}`);
1127
1450
  if (ipCount > 0)
1128
- console.log(` In progress: ${pc8.yellow(String(ipCount))}/${total}`);
1451
+ console.log(` In progress: ${pc9.yellow(String(ipCount))}/${total}`);
1129
1452
  if (todoCount > 0)
1130
- console.log(` Todo: ${pc8.dim(String(todoCount))}/${total}`);
1453
+ console.log(` Todo: ${pc9.dim(String(todoCount))}/${total}`);
1131
1454
  const active = [
1132
1455
  ...byState["in_progress"],
1133
1456
  ...byState["code-complete"]
1134
1457
  ];
1135
1458
  if (active.length > 0) {
1136
- console.log("\n" + pc8.bold("Active:"));
1459
+ console.log("\n" + pc9.bold("Active:"));
1137
1460
  for (const l of active) {
1138
1461
  const name = fnNames.get(l.code) ? ` \u2014 ${fnNames.get(l.code)}` : "";
1139
- 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");
1140
1463
  console.log(` ${l.code}${name} [${stateLabel}]`);
1141
1464
  }
1142
1465
  }
@@ -1145,21 +1468,21 @@ function progressCommand() {
1145
1468
  const registryOrder = Array.from(fnNames.keys());
1146
1469
  const nextCode = registryOrder.find((code) => !ledgerCodes.has(code));
1147
1470
  if (nextCode) {
1148
- console.log("\n" + pc8.bold("Next unblocked:"));
1471
+ console.log("\n" + pc9.bold("Next unblocked:"));
1149
1472
  console.log(` ${nextCode} \u2014 ${fnNames.get(nextCode) ?? ""}`);
1150
1473
  }
1151
1474
  }
1152
1475
  if (flagged.length > 0) {
1153
- console.log("\n" + pc8.bold(pc8.yellow("Flagged:")));
1476
+ console.log("\n" + pc9.bold(pc9.yellow("Flagged:")));
1154
1477
  for (const l of flagged) {
1155
1478
  console.log(` ${l.code} [${l.flags.join(", ")}]`);
1156
1479
  }
1157
1480
  }
1158
1481
  if (byState["done"].length > 0) {
1159
- console.log("\n" + pc8.bold("Done:"));
1482
+ console.log("\n" + pc9.bold("Done:"));
1160
1483
  for (const l of byState["done"]) {
1161
1484
  const name = fnNames.get(l.code) ? ` \u2014 ${fnNames.get(l.code)}` : "";
1162
- console.log(` ${pc8.green("\u2713")} ${l.code}${name}`);
1485
+ console.log(` ${pc9.green("\u2713")} ${l.code}${name}`);
1163
1486
  }
1164
1487
  }
1165
1488
  console.log("");
@@ -1169,15 +1492,16 @@ function progressCommand() {
1169
1492
  var __filename3 = fileURLToPath4(import.meta.url);
1170
1493
  var __dirname4 = dirname4(__filename3);
1171
1494
  var pkg3 = JSON.parse(
1172
- readFileSync9(join9(__dirname4, "../package.json"), "utf8")
1495
+ readFileSync10(join10(__dirname4, "../package.json"), "utf8")
1173
1496
  );
1174
1497
  var program = new Command();
1175
1498
  var collect = (value, prev) => [...prev, value];
1176
1499
  program.name("cubedot").description(
1177
1500
  "Connect your repo to the Cubedot MCP and drive the spec-first build loop."
1178
1501
  ).version(pkg3.version);
1179
- 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);
1180
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);
1181
1505
  program.command("status").description("Connectivity ping + file-health check (read-only).").action(statusCommand);
1182
1506
  program.command("check").description("Validate markers, config, and server name (read-only; CI-friendly).").action(checkCommand);
1183
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.2",
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",