@fenglimg/fabric-cli 2.1.0-rc.2 → 2.2.0-rc.10

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 (88) hide show
  1. package/README.md +8 -5
  2. package/dist/chunk-27HK6H5Y.js +69 -0
  3. package/dist/{chunk-BATF4PEJ.js → chunk-2KBCTMID.js} +31 -8
  4. package/dist/chunk-3D7B2UAZ.js +149 -0
  5. package/dist/{chunk-MF3OTILQ.js → chunk-3IOLS5EK.js} +48 -42
  6. package/dist/{plan-context-hint-FC6P3WFE.js → chunk-722JU5BP.js} +52 -12
  7. package/dist/{chunk-F46ORPOA.js → chunk-7ZDXBOOU.js} +271 -166
  8. package/dist/{doctor-QVNPHLJK.js → chunk-E7HJUU34.js} +248 -72
  9. package/dist/chunk-EOT63RDH.js +36 -0
  10. package/dist/chunk-FNHDQTPC.js +16 -0
  11. package/dist/chunk-HORSMSZL.js +26 -0
  12. package/dist/chunk-NLNH64A3.js +43 -0
  13. package/dist/{chunk-WU6GAPKH.js → chunk-PTGQAZEW.js} +12 -4
  14. package/dist/chunk-QFIVFZRH.js +13 -0
  15. package/dist/chunk-QPAW6IYT.js +387 -0
  16. package/dist/{chunk-COI5VDFU.js → chunk-WA3DYGSY.js} +1 -2
  17. package/dist/{config-XJIPZNUP.js → config-A3LTECAY.js} +4 -3
  18. package/dist/context-UJCGYOT6.js +117 -0
  19. package/dist/doctor-MDTZWKBK.js +24 -0
  20. package/dist/index.d.ts +2 -2
  21. package/dist/index.js +167 -16
  22. package/dist/info-7FKBTMVO.js +139 -0
  23. package/dist/install-v2-RINEA24K.js +3279 -0
  24. package/dist/{metrics-ACEQFPDU.js → metrics-HMFH4YHK.js} +22 -9
  25. package/dist/{onboard-coverage-MFCAEBDO.js → onboard-coverage-XSG77LL3.js} +48 -27
  26. package/dist/plan-context-hint-5TNGH3R4.js +12 -0
  27. package/dist/scope-explain-HLJZ2M33.js +48 -0
  28. package/dist/status-4R3TM4FJ.js +37 -0
  29. package/dist/store-HOCORVL3.js +563 -0
  30. package/dist/sync-DT5UJMMR.js +418 -0
  31. package/dist/{uninstall-TAXSUSKH.js → uninstall-IFN2KYBK.js} +128 -140
  32. package/dist/whoami-ITGEFWH4.js +49 -0
  33. package/package.json +7 -5
  34. package/templates/hooks/cite-policy-evict.cjs +412 -160
  35. package/templates/hooks/configs/README.md +14 -27
  36. package/templates/hooks/configs/claude-code.json +17 -2
  37. package/templates/hooks/configs/codex-hooks.json +15 -3
  38. package/templates/hooks/fabric-hint.cjs +573 -180
  39. package/templates/hooks/knowledge-hint-broad.cjs +648 -190
  40. package/templates/hooks/knowledge-hint-narrow.cjs +123 -77
  41. package/templates/hooks/lib/banner-i18n.cjs +31 -0
  42. package/templates/hooks/lib/bindings-snapshot-reader.cjs +118 -7
  43. package/templates/hooks/lib/cite-line-parser.cjs +12 -20
  44. package/templates/hooks/lib/client-adapter.cjs +66 -7
  45. package/templates/hooks/lib/injection-log.cjs +91 -0
  46. package/templates/hooks/lib/nudge-policy.cjs +117 -0
  47. package/templates/hooks/lib/state-store.cjs +90 -11
  48. package/templates/hooks/post-tooluse-mutation.cjs +386 -0
  49. package/templates/hooks/session-end-marker.cjs +140 -0
  50. package/templates/skills/fabric/SKILL.md +100 -0
  51. package/templates/skills/fabric-archive/SKILL.md +35 -24
  52. package/templates/skills/fabric-archive/ref/dry-run-scope.md +1 -1
  53. package/templates/skills/fabric-archive/ref/i18n-policy.md +2 -3
  54. package/templates/skills/fabric-archive/ref/phase-1-5-onboard.md +2 -3
  55. package/templates/skills/fabric-archive/ref/phase-1-cross-session.md +1 -1
  56. package/templates/skills/fabric-archive/ref/phase-2-5-viability.md +1 -1
  57. package/templates/skills/fabric-archive/ref/phase-3-6-related-edges.md +18 -0
  58. package/templates/skills/fabric-archive/ref/phase-3-7-semantic-scope.md +47 -0
  59. package/templates/skills/fabric-audit/SKILL.md +63 -0
  60. package/templates/skills/fabric-connect/SKILL.md +48 -0
  61. package/templates/skills/fabric-import/SKILL.md +7 -7
  62. package/templates/skills/fabric-import/ref/i18n-policy.md +2 -3
  63. package/templates/skills/fabric-import/ref/state-recovery.md +1 -2
  64. package/templates/skills/fabric-review/SKILL.md +16 -5
  65. package/templates/skills/fabric-review/ref/cite-contract.md +56 -0
  66. package/templates/skills/fabric-review/ref/i18n-policy.md +2 -3
  67. package/templates/skills/fabric-review/ref/output-contract.md +1 -1
  68. package/templates/skills/fabric-review/ref/per-mode-flows.md +2 -2
  69. package/templates/skills/fabric-review/ref/worked-examples.md +1 -1
  70. package/templates/skills/fabric-store/SKILL.md +44 -0
  71. package/templates/skills/fabric-sync/SKILL.md +1 -1
  72. package/templates/skills/lib/shared-policy.md +2 -2
  73. package/dist/chunk-HFQVXY6P.js +0 -86
  74. package/dist/chunk-L4Q55UC4.js +0 -52
  75. package/dist/chunk-LFIKMVY7.js +0 -27
  76. package/dist/chunk-PWLW3B57.js +0 -18
  77. package/dist/chunk-RYAFBNES.js +0 -33
  78. package/dist/chunk-T5RPGCCM.js +0 -40
  79. package/dist/chunk-WWNXR34K.js +0 -49
  80. package/dist/install-2HDO5FTQ.js +0 -2683
  81. package/dist/scope-explain-2F2R5URO.js +0 -33
  82. package/dist/status-GLQWLWH6.js +0 -23
  83. package/dist/store-XTSE5TY6.js +0 -105
  84. package/dist/sync-BJCWDPNC.js +0 -245
  85. package/dist/whoami-B6AEMSEV.js +0 -31
  86. package/templates/hooks/configs/cursor-hooks.json +0 -18
  87. package/templates/hooks/lib/cite-contract-reminder.cjs +0 -179
  88. package/templates/hooks/lib/summary-fallback.cjs +0 -210
@@ -7,28 +7,28 @@ import {
7
7
  HOOK_SCRIPT_DESTINATIONS,
8
8
  SKILL_DESTINATIONS,
9
9
  fabricAgentsSnapshotPath
10
- } from "./chunk-F46ORPOA.js";
11
- import {
12
- paint
13
- } from "./chunk-WWNXR34K.js";
10
+ } from "./chunk-7ZDXBOOU.js";
14
11
  import {
15
12
  createDebugLogger,
16
13
  resolveDevMode
17
- } from "./chunk-COI5VDFU.js";
14
+ } from "./chunk-WA3DYGSY.js";
18
15
  import {
19
16
  detectClientSupports,
20
17
  resolveClients
21
- } from "./chunk-MF3OTILQ.js";
18
+ } from "./chunk-3IOLS5EK.js";
19
+ import {
20
+ paint
21
+ } from "./chunk-NLNH64A3.js";
22
22
  import {
23
23
  t
24
- } from "./chunk-PWLW3B57.js";
24
+ } from "./chunk-HORSMSZL.js";
25
25
 
26
26
  // src/commands/uninstall.ts
27
27
  import { existsSync as existsSync2, statSync } from "fs";
28
28
  import { rm as rm2 } from "fs/promises";
29
29
  import { homedir } from "os";
30
30
  import { isAbsolute, join as join2, relative, resolve, sep } from "path";
31
- import { cancel, confirm, group, intro, isCancel, log, note, outro } from "@clack/prompts";
31
+ import { cancel, confirm, intro, isCancel, multiselect, note, outro } from "@clack/prompts";
32
32
  import { defineCommand } from "citty";
33
33
 
34
34
  // src/install/uninstall-skills-and-hooks.ts
@@ -37,6 +37,9 @@ import { readdir, readFile, rm, rmdir } from "fs/promises";
37
37
  import { dirname, join } from "path";
38
38
  import { atomicWriteJson, atomicWriteText } from "@fenglimg/fabric-shared/node/atomic-write";
39
39
  import { BOOTSTRAP_REGEX } from "@fenglimg/fabric-shared/templates/bootstrap-canonical";
40
+ async function uninstallFabricRouterSkill(projectRoot) {
41
+ return removeSkill("skill-router", SKILL_DESTINATIONS.fabricRouter, projectRoot);
42
+ }
40
43
  async function uninstallFabricArchiveSkill(projectRoot) {
41
44
  return removeSkill("skill", SKILL_DESTINATIONS.fabricArchive, projectRoot);
42
45
  }
@@ -49,6 +52,12 @@ async function uninstallFabricImportSkill(projectRoot) {
49
52
  async function uninstallFabricSyncSkill(projectRoot) {
50
53
  return removeSkill("skill-sync", SKILL_DESTINATIONS.fabricSync, projectRoot);
51
54
  }
55
+ async function uninstallFabricAuditSkill(projectRoot) {
56
+ return removeSkill("skill-audit", SKILL_DESTINATIONS.fabricAudit, projectRoot);
57
+ }
58
+ async function uninstallFabricConnectSkill(projectRoot) {
59
+ return removeSkill("skill-connect", SKILL_DESTINATIONS.fabricConnect, projectRoot);
60
+ }
52
61
  async function removeSkill(step, rels, projectRoot) {
53
62
  const results = [];
54
63
  for (const rel of rels) {
@@ -75,6 +84,27 @@ async function removeKnowledgeHintNarrowHook(projectRoot) {
75
84
  projectRoot
76
85
  );
77
86
  }
87
+ async function removeCitePolicyEvictHook(projectRoot) {
88
+ return removeHookScripts(
89
+ "hook-cite-policy-evict-script",
90
+ HOOK_SCRIPT_DESTINATIONS.citePolicyEvict,
91
+ projectRoot
92
+ );
93
+ }
94
+ async function removeSessionEndMarkerHook(projectRoot) {
95
+ return removeHookScripts(
96
+ "hook-session-end-script",
97
+ HOOK_SCRIPT_DESTINATIONS.sessionEndMarker,
98
+ projectRoot
99
+ );
100
+ }
101
+ async function removePostTooluseMutationHook(projectRoot) {
102
+ return removeHookScripts(
103
+ "hook-post-tooluse-script",
104
+ HOOK_SCRIPT_DESTINATIONS.postTooluseMutation,
105
+ projectRoot
106
+ );
107
+ }
78
108
  async function removeHookScripts(step, rels, projectRoot) {
79
109
  const results = [];
80
110
  for (const rel of rels) {
@@ -131,25 +161,10 @@ async function unmergeCodexHookConfig(projectRoot) {
131
161
  extractCommands: extractFlatCommands
132
162
  });
133
163
  }
134
- async function unmergeCursorHookConfig(projectRoot) {
135
- return unmergeHookConfig({
136
- step: "cursor-hook-config",
137
- projectRoot,
138
- configRel: HOOK_CONFIG_TARGETS.cursor,
139
- arrayPaths: [...HOOK_CONFIG_ARRAY_PATHS.cursor],
140
- fabricCommands: Object.values(FABRIC_HOOK_COMMAND_PATHS.cursor),
141
- extractCommands: extractFlatCommands
142
- });
143
- }
144
164
  async function stripFabricBootstrapBlocks(projectRoot) {
145
165
  const results = [];
146
166
  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
- );
167
+ results.push(await stripManagedBlock(projectRoot, "AGENTS.md"));
153
168
  return results;
154
169
  }
155
170
  async function stripClaudeBootstrapImports(projectRoot) {
@@ -194,8 +209,8 @@ async function stripClaudeBootstrapImports(projectRoot) {
194
209
  };
195
210
  }
196
211
  }
197
- async function stripManagedBlock(projectRoot, relPath, options) {
198
- const step = relPath.endsWith(".mdc") ? "bootstrap-cursor" : "bootstrap-codex";
212
+ async function stripManagedBlock(projectRoot, relPath) {
213
+ const step = "bootstrap-codex";
199
214
  const target = join(projectRoot, relPath);
200
215
  if (!existsSync(target)) {
201
216
  return { step, path: target, status: "skipped", message: "absent" };
@@ -218,19 +233,6 @@ async function stripManagedBlock(projectRoot, relPath, options) {
218
233
  const before = existing.slice(0, match.index ?? 0);
219
234
  const after = existing.slice((match.index ?? 0) + match[0].length);
220
235
  const filtered = `${before}${after.replace(/^\r?\n/, "")}`;
221
- if (options.deleteWhenEmpty && isFrontMatterOnly(filtered)) {
222
- try {
223
- await rm(target, { force: true });
224
- return { step, path: target, status: "removed", message: "front-matter-only" };
225
- } catch (error) {
226
- return {
227
- step,
228
- path: target,
229
- status: "error",
230
- message: error instanceof Error ? error.message : String(error)
231
- };
232
- }
233
- }
234
236
  try {
235
237
  await atomicWriteText(target, filtered);
236
238
  return { step, path: target, status: "removed" };
@@ -243,12 +245,6 @@ async function stripManagedBlock(projectRoot, relPath, options) {
243
245
  };
244
246
  }
245
247
  }
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;
251
- }
252
248
  async function deleteFabricAgentsSnapshot(projectRoot) {
253
249
  const target = fabricAgentsSnapshotPath(projectRoot);
254
250
  return rmIfExists("bootstrap-snapshot", target);
@@ -267,12 +263,6 @@ async function uninstallBootstrapStage(projectRoot, _opts = {}) {
267
263
  projectRoot,
268
264
  () => deleteFabricAgentsSnapshot(projectRoot)
269
265
  );
270
- await runAndCollectOne(
271
- results,
272
- "cursor-hook-config",
273
- projectRoot,
274
- () => unmergeCursorHookConfig(projectRoot)
275
- );
276
266
  await runAndCollectOne(
277
267
  results,
278
268
  "codex-hook-config",
@@ -304,6 +294,36 @@ async function uninstallBootstrapStage(projectRoot, _opts = {}) {
304
294
  projectRoot,
305
295
  () => removeArchiveHintHook(projectRoot)
306
296
  );
297
+ await runAndCollect(
298
+ results,
299
+ "hook-cite-policy-evict-script",
300
+ projectRoot,
301
+ () => removeCitePolicyEvictHook(projectRoot)
302
+ );
303
+ await runAndCollect(
304
+ results,
305
+ "hook-session-end-script",
306
+ projectRoot,
307
+ () => removeSessionEndMarkerHook(projectRoot)
308
+ );
309
+ await runAndCollect(
310
+ results,
311
+ "hook-post-tooluse-script",
312
+ projectRoot,
313
+ () => removePostTooluseMutationHook(projectRoot)
314
+ );
315
+ await runAndCollect(
316
+ results,
317
+ "skill-connect",
318
+ projectRoot,
319
+ () => uninstallFabricConnectSkill(projectRoot)
320
+ );
321
+ await runAndCollect(
322
+ results,
323
+ "skill-audit",
324
+ projectRoot,
325
+ () => uninstallFabricAuditSkill(projectRoot)
326
+ );
307
327
  await runAndCollect(
308
328
  results,
309
329
  "skill-sync",
@@ -328,6 +348,12 @@ async function uninstallBootstrapStage(projectRoot, _opts = {}) {
328
348
  projectRoot,
329
349
  () => uninstallFabricArchiveSkill(projectRoot)
330
350
  );
351
+ await runAndCollect(
352
+ results,
353
+ "skill-router",
354
+ projectRoot,
355
+ () => uninstallFabricRouterSkill(projectRoot)
356
+ );
331
357
  return results;
332
358
  }
333
359
  async function runAndCollect(results, step, projectRoot, fn) {
@@ -527,15 +553,6 @@ function jsonEqual(a, b) {
527
553
  }
528
554
 
529
555
  // src/commands/uninstall.ts
530
- var UNINSTALL_WIZARD_GROUP_CANCELLED = /* @__PURE__ */ Symbol("uninstall-wizard-group-cancelled");
531
- var KNOWLEDGE_SUBDIRS = [
532
- "decisions",
533
- "pitfalls",
534
- "guidelines",
535
- "models",
536
- "processes",
537
- "pending"
538
- ];
539
556
  var FABRIC_STATE_FILES = ["agents.meta.json", "events.jsonl", "forensic.json"];
540
557
  var uninstallCommand = defineCommand({
541
558
  meta: {
@@ -650,21 +667,17 @@ async function buildUninstallExecutionPlan(target, options = {}) {
650
667
  function buildUninstallFabricPlan(target, options = {}) {
651
668
  const absTarget = normalizeTarget(target);
652
669
  const fabricDir = join2(absTarget, ".fabric");
653
- const personalKnowledgeDir = resolve(resolvePersonalFabricRoot(), ".fabric", "knowledge");
670
+ const globalStoresDir = join2(resolvePersonalFabricRoot(), ".fabric", "stores");
654
671
  const entries = [];
655
672
  for (const name of FABRIC_STATE_FILES) {
656
673
  const p = join2(fabricDir, name);
657
674
  entries.push({ path: p, kind: "state-file", absent: !existsSync2(p) });
658
675
  }
659
- for (const sub of KNOWLEDGE_SUBDIRS) {
660
- const gk = join2(fabricDir, "knowledge", sub, ".gitkeep");
661
- entries.push({ path: gk, kind: "gitkeep", absent: !existsSync2(gk) });
662
- }
663
- const safeEntries = entries.filter((entry) => !isInsidePersonalRoot(entry.path, personalKnowledgeDir));
676
+ const safeEntries = entries.filter((entry) => !isInsideGlobalStoresRoot(entry.path, globalStoresDir));
664
677
  return {
665
678
  target: absTarget,
666
679
  fabricDir,
667
- personalKnowledgeDir,
680
+ globalStoresDir,
668
681
  options,
669
682
  entries: safeEntries
670
683
  };
@@ -699,8 +712,6 @@ function scaffoldStepLabel(kind) {
699
712
  switch (kind) {
700
713
  case "state-file":
701
714
  return "scaffold-state";
702
- case "gitkeep":
703
- return "scaffold-gitkeep";
704
715
  }
705
716
  }
706
717
  async function uninstallMcpClients(target, options = {}) {
@@ -774,12 +785,17 @@ async function uninstallMcpClients(target, options = {}) {
774
785
  }
775
786
  async function executeUninstallExecutionPlan(plan) {
776
787
  const stageResults = [];
788
+ const totalStages = plan.stages.length;
789
+ console.log(t("cli.uninstall.plan.phase-banner", { total: String(totalStages) }));
790
+ let stepNum = 0;
777
791
  for (const stage of plan.stages) {
792
+ stepNum += 1;
778
793
  if (stage.skipped) {
794
+ console.log(formatUninstallStageHeader(stage.name, stepNum, totalStages, true));
779
795
  stageResults.push({ name: stage.name, disposition: "skipped", steps: [] });
780
796
  continue;
781
797
  }
782
- console.log(formatUninstallStageHeader(stage.name));
798
+ console.log(formatUninstallStageHeader(stage.name, stepNum, totalStages));
783
799
  try {
784
800
  const steps = await executeUninstallStage(plan, stage.name);
785
801
  const disposition = steps.some((s) => s.status === "error") ? "failed" : "ran";
@@ -848,66 +864,36 @@ function createDefaultUninstallWizardAdapter() {
848
864
  return {
849
865
  async run(context) {
850
866
  intro(t("cli.uninstall.wizard.intro"));
851
- note(
852
- t("cli.uninstall.wizard.overview.body", {
853
- target: context.target
854
- }),
855
- t("cli.uninstall.wizard.overview.title")
867
+ const available = UNINSTALL_STAGE_KEYS.filter(
868
+ (key) => !context.lockedStages.includes(key)
856
869
  );
857
- printUninstallPlanSummary(context.target, context.options, context.supports);
858
- log.step(t("cli.uninstall.wizard.step.target"));
859
- const continueWithTarget = await confirm({
860
- message: t("cli.uninstall.wizard.target.confirm", { target: context.target }),
861
- initialValue: true
870
+ const initialValues = available.filter((key) => !isStageSkipped(context.options, key));
871
+ const picked = await multiselect({
872
+ message: t("cli.uninstall.wizard.select.prompt", { target: context.target }),
873
+ options: available.map((key) => ({
874
+ value: key,
875
+ label: t(`cli.uninstall.wizard.select.${key}.label`),
876
+ hint: t(`cli.uninstall.wizard.select.${key}.hint`)
877
+ })),
878
+ initialValues,
879
+ required: false
862
880
  });
863
- if (isCancel(continueWithTarget) || !continueWithTarget) {
881
+ if (isCancel(picked)) {
864
882
  emitUninstallWizardCancellation();
865
883
  return null;
866
884
  }
867
- log.step(t("cli.uninstall.wizard.step.plan"));
868
- let groupedSelection;
869
- try {
870
- groupedSelection = await group(
871
- {
872
- scaffold: async () => context.lockedStages.includes("scaffold") ? false : confirmInGroup({
873
- message: t("cli.uninstall.wizard.stage.scaffold", {
874
- defaultValue: formatPromptDefault(!context.options.skipScaffold)
875
- }),
876
- initialValue: !context.options.skipScaffold
877
- }),
878
- bootstrap: async () => context.lockedStages.includes("bootstrap") ? false : confirmInGroup({
879
- message: t("cli.uninstall.wizard.stage.bootstrap", {
880
- defaultValue: formatPromptDefault(!context.options.skipBootstrap)
881
- }),
882
- initialValue: !context.options.skipBootstrap
883
- }),
884
- mcp: async () => context.lockedStages.includes("mcp") ? false : confirmInGroup({
885
- message: t("cli.uninstall.wizard.stage.mcp", {
886
- defaultValue: formatPromptDefault(!context.options.skipMcp)
887
- }),
888
- initialValue: !context.options.skipMcp
889
- })
890
- },
891
- {
892
- onCancel() {
893
- throw UNINSTALL_WIZARD_GROUP_CANCELLED;
894
- }
895
- }
896
- );
897
- } catch (error) {
898
- if (error === UNINSTALL_WIZARD_GROUP_CANCELLED) {
899
- emitUninstallWizardCancellation();
900
- return null;
901
- }
902
- throw error;
903
- }
885
+ const selected = new Set(picked);
886
+ const selection = {
887
+ scaffold: selected.has("scaffold"),
888
+ bootstrap: selected.has("bootstrap"),
889
+ mcp: selected.has("mcp")
890
+ };
904
891
  const previewOptions = {
905
892
  ...context.options,
906
- skipScaffold: !groupedSelection.scaffold,
907
- skipBootstrap: !groupedSelection.bootstrap,
908
- skipMcp: !groupedSelection.mcp
893
+ skipScaffold: !selection.scaffold,
894
+ skipBootstrap: !selection.bootstrap,
895
+ skipMcp: !selection.mcp
909
896
  };
910
- log.step(t("cli.uninstall.wizard.step.review"));
911
897
  printUninstallPlanSummary(context.target, previewOptions, context.supports);
912
898
  const confirmed = await confirm({
913
899
  message: t("cli.uninstall.wizard.execute.confirm"),
@@ -918,20 +904,24 @@ function createDefaultUninstallWizardAdapter() {
918
904
  return null;
919
905
  }
920
906
  outro(t("cli.uninstall.wizard.outro"));
921
- return groupedSelection;
907
+ return selection;
922
908
  }
923
909
  };
924
910
  }
911
+ var UNINSTALL_STAGE_KEYS = ["scaffold", "bootstrap", "mcp"];
912
+ function isStageSkipped(options, key) {
913
+ switch (key) {
914
+ case "scaffold":
915
+ return Boolean(options.skipScaffold);
916
+ case "bootstrap":
917
+ return Boolean(options.skipBootstrap);
918
+ case "mcp":
919
+ return Boolean(options.skipMcp);
920
+ }
921
+ }
925
922
  function emitUninstallWizardCancellation() {
926
923
  cancel(t("cli.uninstall.wizard.cancelled"));
927
924
  }
928
- async function confirmInGroup(options) {
929
- const result = await confirm(options);
930
- if (isCancel(result)) {
931
- throw UNINSTALL_WIZARD_GROUP_CANCELLED;
932
- }
933
- return result;
934
- }
935
925
  async function confirmDestructive(plan) {
936
926
  printUninstallPlanSummary(plan.target, plan.options, plan.supports);
937
927
  const answer = await confirm({
@@ -978,8 +968,7 @@ function printUninstallPlanSummary(target, options, supports) {
978
968
  })
979
969
  );
980
970
  console.log(t("cli.uninstall.plan.preserves"));
981
- console.log(` - ${target}/.fabric/knowledge/ ${paint.muted(t("cli.uninstall.plan.preserves.knowledge"))}`);
982
- console.log(` - ~/.fabric/knowledge/ ${paint.muted(t("cli.uninstall.plan.preserves.personal"))}`);
971
+ console.log(` - ~/.fabric/stores/ ${paint.muted(t("cli.uninstall.plan.preserves.stores"))}`);
983
972
  }
984
973
  function printUninstallSummary(result) {
985
974
  const removed = result.stageResults.flatMap(
@@ -1007,8 +996,10 @@ function printUninstallSummary(result) {
1007
996
  }
1008
997
  }
1009
998
  }
1010
- function formatUninstallStageHeader(stageName) {
1011
- return `${paint.ai(t("cli.shared.next"))} ${paint.muted(t(`cli.uninstall.stages.${stageName}`))}`;
999
+ function formatUninstallStageHeader(stageName, stepNum, total, skipped = false) {
1000
+ const label = t(`cli.uninstall.stages.${stageName}`);
1001
+ const head = `[${stepNum}/${total}] ${label}`;
1002
+ return skipped ? paint.muted(`${head} (${t("cli.shared.skipped")})`) : head;
1012
1003
  }
1013
1004
  function formatUninstallStageResult(stageName, steps) {
1014
1005
  const removedCount = steps.filter((s) => s.status === "removed").length;
@@ -1025,9 +1016,9 @@ function formatUninstallStageFailure(stage, error) {
1025
1016
  function resolvePersonalFabricRoot() {
1026
1017
  return process.env.FABRIC_HOME ?? homedir();
1027
1018
  }
1028
- function isInsidePersonalRoot(candidate, personalKnowledgeDir) {
1019
+ function isInsideGlobalStoresRoot(candidate, globalStoresDir) {
1029
1020
  const candidateAbs = resolve(candidate);
1030
- const rootAbs = resolve(personalKnowledgeDir);
1021
+ const rootAbs = resolve(globalStoresDir);
1031
1022
  if (candidateAbs === rootAbs) {
1032
1023
  return true;
1033
1024
  }
@@ -1045,9 +1036,6 @@ function assertExistingDirectory(target) {
1045
1036
  function isInteractiveUninstall() {
1046
1037
  return Boolean(process.stdin.isTTY) && Boolean(process.stdout.isTTY) && Boolean(process.stderr.isTTY);
1047
1038
  }
1048
- function formatPromptDefault(value) {
1049
- return value ? "Y/n" : "y/N";
1050
- }
1051
1039
  function yesNoLabel(value) {
1052
1040
  return value ? t("cli.shared.yes") : t("cli.shared.no");
1053
1041
  }
@@ -1063,7 +1051,7 @@ export {
1063
1051
  uninstall_default as default,
1064
1052
  executeUninstallExecutionPlan,
1065
1053
  executeUninstallFabricPlan,
1066
- isInsidePersonalRoot,
1054
+ isInsideGlobalStoresRoot,
1067
1055
  resolveUninstallExecutionPlanWithWizard,
1068
1056
  runUninstallCommand,
1069
1057
  shouldUseUninstallWizard,
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ warnUnknownFlags,
4
+ whoami
5
+ } from "./chunk-27HK6H5Y.js";
6
+ import "./chunk-QPAW6IYT.js";
7
+ import "./chunk-QFIVFZRH.js";
8
+ import "./chunk-FNHDQTPC.js";
9
+ import {
10
+ getProjectTranslator
11
+ } from "./chunk-HORSMSZL.js";
12
+
13
+ // src/commands/whoami.ts
14
+ import { defineCommand } from "citty";
15
+ var whoami_default = defineCommand({
16
+ meta: { name: "whoami", description: "[DEPRECATED] Use 'fabric info --global' instead" },
17
+ args: {
18
+ // F27: `--json` machine-readable output (was silently ignored — the command
19
+ // declared no args, so citty swallowed the flag and still printed text).
20
+ json: { type: "boolean", description: "Emit machine-readable JSON instead of text" }
21
+ },
22
+ run({ args }) {
23
+ warnUnknownFlags(["json"]);
24
+ console.error("\u26A0\uFE0F DEPRECATED: 'fabric whoami' is deprecated. Use 'fabric info --global' instead.");
25
+ const info = whoami();
26
+ if (args.json === true) {
27
+ console.log(JSON.stringify(info, null, 2));
28
+ return;
29
+ }
30
+ const t = getProjectTranslator();
31
+ if (info === null) {
32
+ console.log(t("cli.cmd.no-global-config"));
33
+ return;
34
+ }
35
+ console.log(t("cli.whoami.uid", { uid: info.uid }));
36
+ if (info.stores.length === 0) {
37
+ console.log(t("cli.whoami.stores-none"));
38
+ return;
39
+ }
40
+ console.log(t("cli.whoami.stores-label"));
41
+ const localOnly = t("cli.shared.local-only");
42
+ for (const store of info.stores) {
43
+ console.log(` ${store.alias} ${store.store_uuid}${store.local_only ? ` ${localOnly}` : ""}`);
44
+ }
45
+ }
46
+ });
47
+ export {
48
+ whoami_default as default
49
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@fenglimg/fabric-cli",
3
- "version": "2.1.0-rc.2",
4
- "description": "Fabric CLI — installs the MCP server + skills + hooks for Claude Code, Cursor, and Codex CLI; runs doctor / knowledge maintenance.",
3
+ "version": "2.2.0-rc.10",
4
+ "description": "Fabric CLI — installs the MCP server + skills + hooks for Claude Code and Codex CLI; runs doctor / knowledge maintenance.",
5
5
  "license": "MIT",
6
6
  "author": "wangzhichao <fenglimg90@gmail.com>",
7
7
  "homepage": "https://github.com/fenglimg/fabric-v2#readme",
@@ -18,7 +18,6 @@
18
18
  "mcp",
19
19
  "cli",
20
20
  "claude-code",
21
- "cursor",
22
21
  "codex-cli",
23
22
  "ai-knowledge-management"
24
23
  ],
@@ -40,16 +39,19 @@
40
39
  "dependencies": {
41
40
  "@clack/prompts": "^1.2.0",
42
41
  "citty": "^0.2.2",
42
+ "ink": "^4.4.1",
43
43
  "picocolors": "^1.1.1",
44
+ "react": "^18.3.1",
44
45
  "string-width": "^7.2.0",
45
46
  "tree-sitter-javascript": "^0.25.0",
46
47
  "tree-sitter-typescript": "^0.23.2",
47
48
  "web-tree-sitter": "^0.26.8",
48
- "@fenglimg/fabric-server": "2.1.0-rc.2",
49
- "@fenglimg/fabric-shared": "2.1.0-rc.2"
49
+ "@fenglimg/fabric-server": "2.2.0-rc.10",
50
+ "@fenglimg/fabric-shared": "2.2.0-rc.10"
50
51
  },
51
52
  "devDependencies": {
52
53
  "@types/node": "^22.15.0",
54
+ "@types/react": "^18.3.12",
53
55
  "tsup": "^8.5.0",
54
56
  "typescript": "^5.8.3"
55
57
  },