@fenglimg/fabric-cli 2.0.0-rc.13 → 2.0.0-rc.21

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 (29) hide show
  1. package/README.md +4 -2
  2. package/dist/{chunk-X7QPY5KH.js → chunk-4HC5ZK7H.js} +296 -301
  3. package/dist/{chunk-FDRLV5PL.js → chunk-FNO7CQDG.js} +5 -213
  4. package/dist/{chunk-WWNXR34K.js → chunk-G2CIOLD4.js} +16 -1
  5. package/dist/chunk-KZ2YITOS.js +225 -0
  6. package/dist/{chunk-OHWQNSLH.js → chunk-MF3OTILQ.js} +267 -44
  7. package/dist/{chunk-OBQU6NHO.js → chunk-ZSESMG6L.js} +0 -6
  8. package/dist/config-AYP5F72E.js +13 -0
  9. package/dist/doctor-L6TIXXIX.js +425 -0
  10. package/dist/index.js +11 -9
  11. package/dist/{install-SLS5W27W.js → install-DNZXGFHJ.js} +344 -359
  12. package/dist/{plan-context-hint-QMUPAXIB.js → plan-context-hint-CFDGXHCA.js} +10 -5
  13. package/dist/{serve-NGLXHDYC.js → serve-6PPQX7AW.js} +16 -11
  14. package/dist/{uninstall-JHUSFENL.js → uninstall-L2HEEOU3.js} +200 -215
  15. package/package.json +3 -3
  16. package/templates/hooks/configs/README.md +9 -5
  17. package/templates/hooks/configs/cursor-hooks.json +7 -10
  18. package/templates/hooks/fabric-hint.cjs +350 -21
  19. package/templates/hooks/knowledge-hint-broad.cjs +39 -14
  20. package/templates/hooks/knowledge-hint-narrow.cjs +31 -7
  21. package/templates/hooks/lib/banner-i18n.cjs +252 -0
  22. package/dist/chunk-Q72D24BG.js +0 -186
  23. package/dist/doctor-RILCO5OG.js +0 -282
  24. package/dist/hooks-HIWYI3VG.js +0 -13
  25. package/dist/scan-VHKZPT2W.js +0 -24
  26. package/templates/agents-md/AGENTS.md.template +0 -59
  27. package/templates/bootstrap/CLAUDE.md +0 -8
  28. package/templates/bootstrap/codex-AGENTS-header.md +0 -6
  29. package/templates/bootstrap/cursor-fabric-bootstrap.mdc +0 -10
@@ -1,31 +1,33 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- detectClientSupports,
4
- resolveClients
5
- } from "./chunk-OHWQNSLH.js";
6
2
  import {
7
3
  FABRIC_HOOK_COMMAND_PATHS,
8
- FABRIC_SECTION_REGEX,
9
4
  HOOK_CONFIG_ARRAY_PATHS,
10
5
  HOOK_CONFIG_TARGETS,
6
+ HOOK_LIB_DESTINATIONS,
11
7
  HOOK_SCRIPT_DESTINATIONS,
12
- SECTION_TARGETS,
13
- SKILL_DESTINATIONS
14
- } from "./chunk-X7QPY5KH.js";
8
+ SKILL_DESTINATIONS,
9
+ fabricAgentsSnapshotPath
10
+ } from "./chunk-4HC5ZK7H.js";
15
11
  import {
16
- paint
17
- } from "./chunk-WWNXR34K.js";
12
+ detectClientSupports,
13
+ resolveClients
14
+ } from "./chunk-MF3OTILQ.js";
18
15
  import {
19
- createDebugLogger,
20
- resolveDevMode
21
- } from "./chunk-OBQU6NHO.js";
16
+ hasActionHint,
17
+ paint,
18
+ renderFabricError
19
+ } from "./chunk-G2CIOLD4.js";
22
20
  import {
23
21
  t
24
22
  } from "./chunk-6ICJICVU.js";
23
+ import {
24
+ createDebugLogger,
25
+ resolveDevMode
26
+ } from "./chunk-ZSESMG6L.js";
25
27
 
26
28
  // src/commands/uninstall.ts
27
29
  import { existsSync as existsSync2, statSync } from "fs";
28
- import { readdir as readdir2, rm as rm2 } from "fs/promises";
30
+ import { rm as rm2 } from "fs/promises";
29
31
  import { homedir } from "os";
30
32
  import { isAbsolute, join as join2, relative, resolve, sep } from "path";
31
33
  import { cancel, confirm, group, intro, isCancel, log, note, outro } from "@clack/prompts";
@@ -37,6 +39,7 @@ import { existsSync } from "fs";
37
39
  import { readdir, readFile, rm, rmdir } from "fs/promises";
38
40
  import { dirname, join } from "path";
39
41
  import { atomicWriteJson, atomicWriteText } from "@fenglimg/fabric-shared/node/atomic-write";
42
+ import { BOOTSTRAP_REGEX } from "@fenglimg/fabric-shared/templates/bootstrap-canonical";
40
43
  async function uninstallFabricArchiveSkill(projectRoot) {
41
44
  return removeSkill("skill", SKILL_DESTINATIONS.fabricArchive, projectRoot);
42
45
  }
@@ -80,121 +83,209 @@ async function removeHookScripts(step, rels, projectRoot) {
80
83
  }
81
84
  return results;
82
85
  }
83
- async function unmergeClaudeCodeHookConfig(projectRoot, opts = {}) {
86
+ async function removeHookLibs(projectRoot) {
87
+ const results = [];
88
+ for (const dirRel of HOOK_LIB_DESTINATIONS) {
89
+ const dirAbs = join(projectRoot, dirRel);
90
+ if (!existsSync(dirAbs)) {
91
+ results.push({ step: "hook-lib", path: dirAbs, status: "skipped", message: "absent" });
92
+ continue;
93
+ }
94
+ let entries;
95
+ try {
96
+ entries = await readdir(dirAbs);
97
+ } catch (error) {
98
+ results.push({
99
+ step: "hook-lib",
100
+ path: dirAbs,
101
+ status: "error",
102
+ message: error instanceof Error ? error.message : String(error)
103
+ });
104
+ continue;
105
+ }
106
+ for (const entry of entries) {
107
+ if (!entry.endsWith(".cjs")) continue;
108
+ results.push(await rmIfExists("hook-lib", join(dirAbs, entry)));
109
+ }
110
+ results.push(await rmDirIfEmpty("hook-lib-dir", dirAbs));
111
+ }
112
+ return results;
113
+ }
114
+ async function unmergeClaudeCodeHookConfig(projectRoot) {
84
115
  return unmergeHookConfig({
85
116
  step: "claude-hook-config",
86
117
  projectRoot,
87
118
  configRel: HOOK_CONFIG_TARGETS.claudeCode,
88
119
  arrayPaths: [...HOOK_CONFIG_ARRAY_PATHS.claudeCode],
89
120
  fabricCommands: Object.values(FABRIC_HOOK_COMMAND_PATHS.claudeCode),
90
- extractCommands: extractClaudeCommands,
91
- cleanEmpties: opts.cleanEmpties === true
121
+ extractCommands: extractClaudeCommands
92
122
  });
93
123
  }
94
- async function unmergeCodexHookConfig(projectRoot, opts = {}) {
124
+ async function unmergeCodexHookConfig(projectRoot) {
95
125
  return unmergeHookConfig({
96
126
  step: "codex-hook-config",
97
127
  projectRoot,
98
128
  configRel: HOOK_CONFIG_TARGETS.codex,
99
129
  arrayPaths: [...HOOK_CONFIG_ARRAY_PATHS.codex],
100
130
  fabricCommands: Object.values(FABRIC_HOOK_COMMAND_PATHS.codex),
101
- extractCommands: extractFlatCommands,
102
- cleanEmpties: opts.cleanEmpties === true
131
+ extractCommands: extractFlatCommands
103
132
  });
104
133
  }
105
- async function unmergeCursorHookConfig(projectRoot, opts = {}) {
134
+ async function unmergeCursorHookConfig(projectRoot) {
106
135
  return unmergeHookConfig({
107
136
  step: "cursor-hook-config",
108
137
  projectRoot,
109
138
  configRel: HOOK_CONFIG_TARGETS.cursor,
110
139
  arrayPaths: [...HOOK_CONFIG_ARRAY_PATHS.cursor],
111
140
  fabricCommands: Object.values(FABRIC_HOOK_COMMAND_PATHS.cursor),
112
- extractCommands: extractFlatCommands,
113
- cleanEmpties: opts.cleanEmpties === true
141
+ extractCommands: extractFlatCommands
114
142
  });
115
143
  }
116
- async function stripFabricKnowledgeBaseSection(projectRoot) {
144
+ async function stripFabricBootstrapBlocks(projectRoot) {
117
145
  const results = [];
118
- for (const rel of SECTION_TARGETS) {
119
- const target = join(projectRoot, rel);
120
- if (!existsSync(target)) {
121
- results.push({ step: "section", path: target, status: "skipped", message: "absent" });
122
- continue;
123
- }
124
- let existing;
125
- try {
126
- existing = await readFile(target, "utf8");
127
- } catch (error) {
128
- results.push({
129
- step: "section",
130
- path: target,
131
- status: "error",
132
- message: error instanceof Error ? error.message : String(error)
133
- });
134
- continue;
135
- }
136
- const match = existing.match(FABRIC_SECTION_REGEX);
137
- if (match === null) {
138
- results.push({
139
- step: "section",
140
- path: target,
141
- status: "skipped",
142
- message: "no-fabric-section"
143
- });
144
- continue;
145
- }
146
- const before = existing.slice(0, match.index ?? 0);
147
- const after = existing.slice((match.index ?? 0) + match[0].length);
148
- const filtered = `${before}${after.replace(/^\r?\n/, "")}`;
149
- if (filtered === existing) {
150
- results.push({
151
- step: "section",
152
- path: target,
153
- status: "skipped",
154
- message: "no-fabric-section"
155
- });
156
- continue;
157
- }
146
+ results.push(await stripClaudeBootstrapImports(projectRoot));
147
+ results.push(await stripManagedBlock(projectRoot, "AGENTS.md", { deleteWhenEmpty: false }));
148
+ results.push(
149
+ await stripManagedBlock(projectRoot, join(".cursor", "rules", "fabric-bootstrap.mdc"), {
150
+ deleteWhenEmpty: true
151
+ })
152
+ );
153
+ return results;
154
+ }
155
+ async function stripClaudeBootstrapImports(projectRoot) {
156
+ const step = "bootstrap-claude";
157
+ const target = join(projectRoot, "CLAUDE.md");
158
+ if (!existsSync(target)) {
159
+ return { step, path: target, status: "skipped", message: "absent" };
160
+ }
161
+ let existing;
162
+ try {
163
+ existing = await readFile(target, "utf8");
164
+ } catch (error) {
165
+ return {
166
+ step,
167
+ path: target,
168
+ status: "error",
169
+ message: error instanceof Error ? error.message : String(error)
170
+ };
171
+ }
172
+ const managedLines = /* @__PURE__ */ new Set(["@.fabric/AGENTS.md", "@.fabric/project-rules.md"]);
173
+ const lines = existing.split(/\r?\n/);
174
+ const filtered = lines.filter((l) => !managedLines.has(l.replace(/\s+$/, "")));
175
+ if (filtered.length === lines.length) {
176
+ return { step, path: target, status: "skipped", message: "no-fabric-section" };
177
+ }
178
+ while (filtered.length > 1 && filtered[filtered.length - 1] === "" && filtered[filtered.length - 2] === "") {
179
+ filtered.pop();
180
+ }
181
+ const next = filtered.join("\n");
182
+ if (next === existing) {
183
+ return { step, path: target, status: "skipped", message: "no-fabric-section" };
184
+ }
185
+ try {
186
+ await atomicWriteText(target, next);
187
+ return { step, path: target, status: "removed" };
188
+ } catch (error) {
189
+ return {
190
+ step,
191
+ path: target,
192
+ status: "error",
193
+ message: error instanceof Error ? error.message : String(error)
194
+ };
195
+ }
196
+ }
197
+ async function stripManagedBlock(projectRoot, relPath, options) {
198
+ const step = relPath.endsWith(".mdc") ? "bootstrap-cursor" : "bootstrap-codex";
199
+ const target = join(projectRoot, relPath);
200
+ if (!existsSync(target)) {
201
+ return { step, path: target, status: "skipped", message: "absent" };
202
+ }
203
+ let existing;
204
+ try {
205
+ existing = await readFile(target, "utf8");
206
+ } catch (error) {
207
+ return {
208
+ step,
209
+ path: target,
210
+ status: "error",
211
+ message: error instanceof Error ? error.message : String(error)
212
+ };
213
+ }
214
+ const match = existing.match(BOOTSTRAP_REGEX);
215
+ if (match === null) {
216
+ return { step, path: target, status: "skipped", message: "no-fabric-section" };
217
+ }
218
+ const before = existing.slice(0, match.index ?? 0);
219
+ const after = existing.slice((match.index ?? 0) + match[0].length);
220
+ const filtered = `${before}${after.replace(/^\r?\n/, "")}`;
221
+ if (options.deleteWhenEmpty && isFrontMatterOnly(filtered)) {
158
222
  try {
159
- await atomicWriteText(target, filtered);
160
- results.push({ step: "section", path: target, status: "removed" });
223
+ await rm(target, { force: true });
224
+ return { step, path: target, status: "removed", message: "front-matter-only" };
161
225
  } catch (error) {
162
- results.push({
163
- step: "section",
226
+ return {
227
+ step,
164
228
  path: target,
165
229
  status: "error",
166
230
  message: error instanceof Error ? error.message : String(error)
167
- });
231
+ };
168
232
  }
169
233
  }
170
- return results;
234
+ try {
235
+ await atomicWriteText(target, filtered);
236
+ return { step, path: target, status: "removed" };
237
+ } catch (error) {
238
+ return {
239
+ step,
240
+ path: target,
241
+ status: "error",
242
+ message: error instanceof Error ? error.message : String(error)
243
+ };
244
+ }
245
+ }
246
+ function isFrontMatterOnly(content) {
247
+ const trimmed = content.replace(/^\s+/, "");
248
+ const match = trimmed.match(/^---\n[\s\S]*?\n---\s*$/);
249
+ if (match === null) return trimmed.length === 0;
250
+ return true;
171
251
  }
172
- async function uninstallBootstrapStage(projectRoot, opts = {}) {
252
+ async function deleteFabricAgentsSnapshot(projectRoot) {
253
+ const target = fabricAgentsSnapshotPath(projectRoot);
254
+ return rmIfExists("bootstrap-snapshot", target);
255
+ }
256
+ async function uninstallBootstrapStage(projectRoot, _opts = {}) {
173
257
  const results = [];
174
258
  await runAndCollect(
175
259
  results,
176
- "section",
260
+ "bootstrap-blocks",
177
261
  projectRoot,
178
- () => stripFabricKnowledgeBaseSection(projectRoot)
262
+ () => stripFabricBootstrapBlocks(projectRoot)
263
+ );
264
+ await runAndCollectOne(
265
+ results,
266
+ "bootstrap-snapshot",
267
+ projectRoot,
268
+ () => deleteFabricAgentsSnapshot(projectRoot)
179
269
  );
180
270
  await runAndCollectOne(
181
271
  results,
182
272
  "cursor-hook-config",
183
273
  projectRoot,
184
- () => unmergeCursorHookConfig(projectRoot, opts)
274
+ () => unmergeCursorHookConfig(projectRoot)
185
275
  );
186
276
  await runAndCollectOne(
187
277
  results,
188
278
  "codex-hook-config",
189
279
  projectRoot,
190
- () => unmergeCodexHookConfig(projectRoot, opts)
280
+ () => unmergeCodexHookConfig(projectRoot)
191
281
  );
192
282
  await runAndCollectOne(
193
283
  results,
194
284
  "claude-hook-config",
195
285
  projectRoot,
196
- () => unmergeClaudeCodeHookConfig(projectRoot, opts)
286
+ () => unmergeClaudeCodeHookConfig(projectRoot)
197
287
  );
288
+ await runAndCollect(results, "hook-lib", projectRoot, () => removeHookLibs(projectRoot));
198
289
  await runAndCollect(
199
290
  results,
200
291
  "hook-narrow-script",
@@ -339,7 +430,7 @@ async function unmergeHookConfig(args) {
339
430
  }
340
431
  const next = JSON.parse(JSON.stringify(parsed));
341
432
  for (const dotted of args.arrayPaths) {
342
- pruneArrayAtPath(next, dotted, args.fabricCommands, args.extractCommands, args.cleanEmpties);
433
+ pruneArrayAtPath(next, dotted, args.fabricCommands, args.extractCommands);
343
434
  }
344
435
  if (jsonEqual(parsed, next)) {
345
436
  return { step: args.step, path: target, status: "skipped", message: "no-fabric-entries" };
@@ -356,7 +447,7 @@ async function unmergeHookConfig(args) {
356
447
  };
357
448
  }
358
449
  }
359
- function pruneArrayAtPath(root, path, fabricCommands, extractCommands, cleanEmpties) {
450
+ function pruneArrayAtPath(root, path, fabricCommands, extractCommands) {
360
451
  const keys = path.split(".");
361
452
  const chain = [];
362
453
  let cursor = root;
@@ -384,7 +475,7 @@ function pruneArrayAtPath(root, path, fabricCommands, extractCommands, cleanEmpt
384
475
  });
385
476
  const leaf = chain[chain.length - 1];
386
477
  leaf.parent[leaf.key] = filtered;
387
- if (!cleanEmpties || filtered.length > 0) {
478
+ if (filtered.length > 0) {
388
479
  return;
389
480
  }
390
481
  for (let i = chain.length - 1; i >= 0; i--) {
@@ -446,59 +537,24 @@ var uninstallCommand = defineCommand({
446
537
  description: t("cli.uninstall.description")
447
538
  },
448
539
  args: {
449
- target: {
450
- type: "string",
451
- description: t("cli.uninstall.args.target.description")
452
- },
453
540
  debug: {
454
541
  type: "boolean",
455
542
  description: t("cli.uninstall.args.debug.description"),
456
543
  default: false
457
544
  },
458
- force: {
545
+ "dry-run": {
459
546
  type: "boolean",
460
- description: t("cli.uninstall.args.force.description"),
547
+ description: t("cli.uninstall.args.dry-run.description"),
461
548
  default: false
462
549
  },
550
+ target: {
551
+ type: "string",
552
+ description: t("cli.uninstall.args.target.description")
553
+ },
463
554
  yes: {
464
555
  type: "boolean",
465
556
  description: t("cli.uninstall.args.yes.description"),
466
557
  default: false
467
- },
468
- plan: {
469
- type: "boolean",
470
- description: t("cli.uninstall.args.plan.description"),
471
- default: false
472
- },
473
- bootstrap: {
474
- type: "boolean",
475
- default: true,
476
- negativeDescription: t("cli.uninstall.flags.no-bootstrap")
477
- },
478
- mcp: {
479
- type: "boolean",
480
- default: true,
481
- negativeDescription: t("cli.uninstall.flags.no-mcp")
482
- },
483
- scaffold: {
484
- type: "boolean",
485
- default: true,
486
- negativeDescription: t("cli.uninstall.flags.no-scaffold")
487
- },
488
- interactive: {
489
- type: "boolean",
490
- description: t("cli.uninstall.flags.interactive"),
491
- default: true
492
- },
493
- purge: {
494
- type: "boolean",
495
- description: t("cli.uninstall.flags.purge"),
496
- default: false
497
- },
498
- "clean-empties": {
499
- type: "boolean",
500
- description: t("cli.uninstall.flags.clean-empties"),
501
- default: false
502
558
  }
503
559
  },
504
560
  async run({ args }) {
@@ -514,7 +570,15 @@ async function runUninstallCommand(args) {
514
570
  for (const step of resolution.chain) {
515
571
  logger(step);
516
572
  }
517
- checkLockOrThrow(intent.target, { force: args.force });
573
+ try {
574
+ checkLockOrThrow(intent.target);
575
+ } catch (err) {
576
+ if (hasActionHint(err)) {
577
+ renderFabricError(err);
578
+ process.exit(1);
579
+ }
580
+ throw err;
581
+ }
518
582
  const supports = detectClientSupports(intent.target);
519
583
  const basePlan = await buildUninstallExecutionPlan(intent.target, {
520
584
  ...intent.options
@@ -525,7 +589,7 @@ async function runUninstallCommand(args) {
525
589
  interactive: intent.interactiveSummary && !intent.wizardEnabled,
526
590
  supports
527
591
  };
528
- const finalPlan = intent.wizardEnabled ? await resolveUninstallExecutionPlanWithWizard(planWithSupports, args, createDefaultUninstallWizardAdapter()) : planWithSupports;
592
+ const finalPlan = intent.wizardEnabled ? await resolveUninstallExecutionPlanWithWizard(planWithSupports, createDefaultUninstallWizardAdapter()) : planWithSupports;
529
593
  if (finalPlan === null) {
530
594
  process.exitCode = 130;
531
595
  return;
@@ -541,7 +605,7 @@ async function runUninstallCommand(args) {
541
605
  }))
542
606
  };
543
607
  }
544
- if (intent.interactiveSummary && !intent.wizardEnabled && args.yes !== true && args.force !== true) {
608
+ if (intent.interactiveSummary && !intent.wizardEnabled && args.yes !== true) {
545
609
  const proceed = await confirmDestructive(finalPlan);
546
610
  if (!proceed) {
547
611
  process.exitCode = 130;
@@ -555,25 +619,19 @@ async function runUninstallCommand(args) {
555
619
  function resolveUninstallCliIntent(args, targetInput) {
556
620
  const target = normalizeTarget(targetInput);
557
621
  const terminalInteractive = isInteractiveUninstall();
558
- const planOnly = args.plan === true;
622
+ const planOnly = args["dry-run"] === true;
559
623
  const options = {
560
- force: args.force,
561
- skipBootstrap: args.bootstrap === false,
562
- skipMcp: args.mcp === false,
563
- skipScaffold: args.scaffold === false,
564
- planOnly,
565
- purge: args.purge === true,
566
- cleanEmpties: args["clean-empties"] === true
624
+ planOnly
567
625
  };
568
626
  return {
569
627
  target,
570
628
  options,
571
- interactiveSummary: args.interactive !== false && terminalInteractive,
629
+ interactiveSummary: terminalInteractive,
572
630
  wizardEnabled: shouldUseUninstallWizard(args, terminalInteractive) && !planOnly
573
631
  };
574
632
  }
575
633
  function shouldUseUninstallWizard(args, terminalInteractive = isInteractiveUninstall()) {
576
- return terminalInteractive && args.interactive !== false && args.yes !== true;
634
+ return terminalInteractive && args.yes !== true;
577
635
  }
578
636
  async function buildUninstallExecutionPlan(target, options = {}) {
579
637
  const scaffold = buildUninstallFabricPlan(target, options);
@@ -605,13 +663,6 @@ function buildUninstallFabricPlan(target, options = {}) {
605
663
  const gk = join2(fabricDir, "knowledge", sub, ".gitkeep");
606
664
  entries.push({ path: gk, kind: "gitkeep", absent: !existsSync2(gk) });
607
665
  }
608
- if (options.purge === true) {
609
- for (const sub of KNOWLEDGE_SUBDIRS) {
610
- const subdir = join2(fabricDir, "knowledge", sub);
611
- entries.push({ path: subdir, kind: "knowledge-subdir", absent: !existsSync2(subdir) });
612
- }
613
- entries.push({ path: fabricDir, kind: "fabric-dir", absent: !existsSync2(fabricDir) });
614
- }
615
666
  const safeEntries = entries.filter((entry) => !isInsidePersonalRoot(entry.path, personalKnowledgeDir));
616
667
  return {
617
668
  target: absTarget,
@@ -623,9 +674,7 @@ function buildUninstallFabricPlan(target, options = {}) {
623
674
  }
624
675
  async function executeUninstallFabricPlan(plan) {
625
676
  const results = [];
626
- const fabricDirEntry = plan.entries.find((entry) => entry.kind === "fabric-dir");
627
- const otherEntries = plan.entries.filter((entry) => entry.kind !== "fabric-dir");
628
- for (const entry of otherEntries) {
677
+ for (const entry of plan.entries) {
629
678
  if (entry.absent) {
630
679
  results.push({
631
680
  step: scaffoldStepLabel(entry.kind),
@@ -636,7 +685,7 @@ async function executeUninstallFabricPlan(plan) {
636
685
  continue;
637
686
  }
638
687
  try {
639
- await rm2(entry.path, { recursive: entry.kind === "knowledge-subdir", force: true });
688
+ await rm2(entry.path, { force: true });
640
689
  results.push({ step: scaffoldStepLabel(entry.kind), path: entry.path, status: "removed" });
641
690
  } catch (error) {
642
691
  results.push({
@@ -647,39 +696,6 @@ async function executeUninstallFabricPlan(plan) {
647
696
  });
648
697
  }
649
698
  }
650
- if (fabricDirEntry !== void 0) {
651
- const path = fabricDirEntry.path;
652
- if (!existsSync2(path)) {
653
- results.push({
654
- step: "fabric-dir",
655
- path,
656
- status: "skipped",
657
- message: "absent"
658
- });
659
- } else {
660
- try {
661
- const entries = await readdir2(path);
662
- if (entries.length > 0) {
663
- results.push({
664
- step: "fabric-dir",
665
- path,
666
- status: "skipped",
667
- message: "not-empty"
668
- });
669
- } else {
670
- await rm2(path, { recursive: true, force: true });
671
- results.push({ step: "fabric-dir", path, status: "removed" });
672
- }
673
- } catch (error) {
674
- results.push({
675
- step: "fabric-dir",
676
- path,
677
- status: "error",
678
- message: error instanceof Error ? error.message : String(error)
679
- });
680
- }
681
- }
682
- }
683
699
  return results;
684
700
  }
685
701
  function scaffoldStepLabel(kind) {
@@ -688,10 +704,6 @@ function scaffoldStepLabel(kind) {
688
704
  return "scaffold-state";
689
705
  case "gitkeep":
690
706
  return "scaffold-gitkeep";
691
- case "knowledge-subdir":
692
- return "scaffold-knowledge";
693
- case "fabric-dir":
694
- return "fabric-dir";
695
707
  }
696
708
  }
697
709
  async function uninstallMcpClients(target, options = {}) {
@@ -799,7 +811,7 @@ async function executeUninstallStage(plan, stageName) {
799
811
  case "scaffold":
800
812
  return executeUninstallFabricPlan(plan.scaffold);
801
813
  case "bootstrap": {
802
- const opts = { cleanEmpties: plan.options.cleanEmpties === true };
814
+ const opts = {};
803
815
  return uninstallBootstrapStage(plan.target, opts);
804
816
  }
805
817
  case "mcp": {
@@ -812,12 +824,12 @@ async function uninstallFabric(target, options = {}) {
812
824
  const plan = await buildUninstallExecutionPlan(target, options);
813
825
  return executeUninstallExecutionPlan(plan);
814
826
  }
815
- async function resolveUninstallExecutionPlanWithWizard(basePlan, args, wizardAdapter) {
827
+ async function resolveUninstallExecutionPlanWithWizard(basePlan, wizardAdapter) {
816
828
  const selection = await wizardAdapter.run({
817
829
  target: basePlan.target,
818
830
  options: basePlan.options,
819
831
  supports: basePlan.supports,
820
- lockedStages: collectLockedWizardStages(args)
832
+ lockedStages: []
821
833
  });
822
834
  if (selection === null) {
823
835
  return null;
@@ -826,9 +838,7 @@ async function resolveUninstallExecutionPlanWithWizard(basePlan, args, wizardAda
826
838
  ...basePlan.options,
827
839
  skipScaffold: !selection.scaffold,
828
840
  skipBootstrap: !selection.bootstrap,
829
- skipMcp: !selection.mcp,
830
- purge: selection.purge,
831
- cleanEmpties: selection.cleanEmpties
841
+ skipMcp: !selection.mcp
832
842
  };
833
843
  const rebuilt = await buildUninstallExecutionPlan(basePlan.target, nextOptions);
834
844
  return {
@@ -879,18 +889,6 @@ function createDefaultUninstallWizardAdapter() {
879
889
  defaultValue: formatPromptDefault(!context.options.skipMcp)
880
890
  }),
881
891
  initialValue: !context.options.skipMcp
882
- }),
883
- purge: async () => confirmInGroup({
884
- message: t("cli.uninstall.wizard.purge", {
885
- defaultValue: formatPromptDefault(context.options.purge === true)
886
- }),
887
- initialValue: context.options.purge === true
888
- }),
889
- cleanEmpties: async () => confirmInGroup({
890
- message: t("cli.uninstall.wizard.clean-empties", {
891
- defaultValue: formatPromptDefault(context.options.cleanEmpties === true)
892
- }),
893
- initialValue: context.options.cleanEmpties === true
894
892
  })
895
893
  },
896
894
  {
@@ -910,9 +908,7 @@ function createDefaultUninstallWizardAdapter() {
910
908
  ...context.options,
911
909
  skipScaffold: !groupedSelection.scaffold,
912
910
  skipBootstrap: !groupedSelection.bootstrap,
913
- skipMcp: !groupedSelection.mcp,
914
- purge: groupedSelection.purge,
915
- cleanEmpties: groupedSelection.cleanEmpties
911
+ skipMcp: !groupedSelection.mcp
916
912
  };
917
913
  log.step(t("cli.uninstall.wizard.step.review"));
918
914
  printUninstallPlanSummary(context.target, previewOptions, context.supports);
@@ -939,13 +935,6 @@ async function confirmInGroup(options) {
939
935
  }
940
936
  return result;
941
937
  }
942
- function collectLockedWizardStages(args) {
943
- const locked = [];
944
- if (args.scaffold === false) locked.push("scaffold");
945
- if (args.bootstrap === false) locked.push("bootstrap");
946
- if (args.mcp === false) locked.push("mcp");
947
- return locked;
948
- }
949
938
  async function confirmDestructive(plan) {
950
939
  printUninstallPlanSummary(plan.target, plan.options, plan.supports);
951
940
  const answer = await confirm({
@@ -964,9 +953,7 @@ function printUninstallPlanPreview(plan) {
964
953
  t("cli.uninstall.plan.preview-result", {
965
954
  scaffold: yesNoLabel(!plan.options.skipScaffold),
966
955
  bootstrap: yesNoLabel(!plan.options.skipBootstrap),
967
- mcp: yesNoLabel(!plan.options.skipMcp),
968
- purge: yesNoLabel(plan.options.purge === true),
969
- cleanEmpties: yesNoLabel(plan.options.cleanEmpties === true)
956
+ mcp: yesNoLabel(!plan.options.skipMcp)
970
957
  })
971
958
  );
972
959
  if (!plan.options.skipScaffold && plan.scaffold.entries.length > 0) {
@@ -984,9 +971,7 @@ function printUninstallPlanSummary(target, options, supports) {
984
971
  t("cli.uninstall.plan.actions", {
985
972
  scaffold: yesNoLabel(!options.skipScaffold),
986
973
  bootstrap: yesNoLabel(!options.skipBootstrap),
987
- mcp: yesNoLabel(!options.skipMcp),
988
- purge: yesNoLabel(options.purge === true),
989
- cleanEmpties: yesNoLabel(options.cleanEmpties === true)
974
+ mcp: yesNoLabel(!options.skipMcp)
990
975
  })
991
976
  );
992
977
  const detected = supports.filter((support) => support.detected);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fenglimg/fabric-cli",
3
- "version": "2.0.0-rc.13",
3
+ "version": "2.0.0-rc.21",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "fab": "dist/index.js",
@@ -20,8 +20,8 @@
20
20
  "tree-sitter-javascript": "^0.25.0",
21
21
  "tree-sitter-typescript": "^0.23.2",
22
22
  "web-tree-sitter": "^0.26.8",
23
- "@fenglimg/fabric-shared": "2.0.0-rc.13",
24
- "@fenglimg/fabric-server": "2.0.0-rc.13"
23
+ "@fenglimg/fabric-server": "2.0.0-rc.21",
24
+ "@fenglimg/fabric-shared": "2.0.0-rc.21"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@types/node": "^22.15.0",