@fenglimg/fabric-cli 2.2.0-rc.1 → 2.2.0-rc.11

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 (83) hide show
  1. package/README.md +8 -5
  2. package/dist/chunk-27HK6H5Y.js +69 -0
  3. package/dist/{chunk-AOE6AYI7.js → chunk-2KBCTMID.js} +31 -8
  4. package/dist/chunk-3D7B2UAZ.js +149 -0
  5. package/dist/{chunk-XC5RUHLK.js → chunk-3IOLS5EK.js} +23 -38
  6. package/dist/{plan-context-hint-FC6P3WFE.js → chunk-722JU5BP.js} +52 -12
  7. package/dist/{chunk-2R55HNVD.js → chunk-7ZDXBOOU.js} +234 -206
  8. package/dist/{doctor-YONYXDX6.js → chunk-E7HJUU34.js} +215 -52
  9. package/dist/chunk-EOT63RDH.js +36 -0
  10. package/dist/chunk-FNHDQTPC.js +16 -0
  11. package/dist/{chunk-2CY4BMTH.js → chunk-HORSMSZL.js} +9 -5
  12. package/dist/{chunk-BO4XIZWZ.js → chunk-NLNH64A3.js} +5 -18
  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-XYRBZJDU.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 +133 -22
  22. package/dist/info-7FKBTMVO.js +139 -0
  23. package/dist/install-v2-WLEJ5XHT.js +3279 -0
  24. package/dist/{metrics-RER6NLFC.js → metrics-HMFH4YHK.js} +1 -1
  25. package/dist/{onboard-coverage-JWQWDZW7.js → onboard-coverage-XSG77LL3.js} +48 -27
  26. package/dist/plan-context-hint-5TNGH3R4.js +12 -0
  27. package/dist/{scope-explain-CDIZESP5.js → scope-explain-HLJZ2M33.js} +17 -6
  28. package/dist/status-4R3TM4FJ.js +37 -0
  29. package/dist/store-HOCORVL3.js +563 -0
  30. package/dist/{sync-UJ4BBCZJ.js → sync-DT5UJMMR.js} +197 -30
  31. package/dist/{uninstall-C3QXKOO6.js → uninstall-IFN2KYBK.js} +97 -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 +742 -259
  39. package/templates/hooks/knowledge-hint-broad.cjs +577 -274
  40. package/templates/hooks/knowledge-hint-narrow.cjs +113 -73
  41. package/templates/hooks/lib/banner-i18n.cjs +50 -1
  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/nudge-policy.cjs +117 -0
  46. package/templates/hooks/lib/state-store.cjs +60 -0
  47. package/templates/hooks/post-tooluse-mutation.cjs +386 -0
  48. package/templates/hooks/session-end-marker.cjs +140 -0
  49. package/templates/skills/fabric/SKILL.md +100 -0
  50. package/templates/skills/fabric-archive/SKILL.md +47 -24
  51. package/templates/skills/fabric-archive/ref/dry-run-scope.md +1 -1
  52. package/templates/skills/fabric-archive/ref/i18n-policy.md +2 -3
  53. package/templates/skills/fabric-archive/ref/phase-1-5-onboard.md +2 -3
  54. package/templates/skills/fabric-archive/ref/phase-1-cross-session.md +1 -1
  55. package/templates/skills/fabric-archive/ref/phase-2-5-viability.md +1 -1
  56. package/templates/skills/fabric-archive/ref/phase-3-6-related-edges.md +18 -0
  57. package/templates/skills/fabric-archive/ref/phase-3-7-semantic-scope.md +47 -0
  58. package/templates/skills/fabric-audit/SKILL.md +13 -3
  59. package/templates/skills/fabric-connect/SKILL.md +3 -3
  60. package/templates/skills/fabric-import/SKILL.md +7 -7
  61. package/templates/skills/fabric-import/ref/i18n-policy.md +2 -3
  62. package/templates/skills/fabric-import/ref/state-recovery.md +1 -2
  63. package/templates/skills/fabric-review/SKILL.md +14 -5
  64. package/templates/skills/fabric-review/ref/cite-contract.md +1 -1
  65. package/templates/skills/fabric-review/ref/i18n-policy.md +2 -3
  66. package/templates/skills/fabric-review/ref/output-contract.md +1 -1
  67. package/templates/skills/fabric-review/ref/per-mode-flows.md +2 -2
  68. package/templates/skills/fabric-review/ref/worked-examples.md +1 -1
  69. package/templates/skills/fabric-store/SKILL.md +1 -1
  70. package/templates/skills/fabric-sync/SKILL.md +1 -1
  71. package/templates/skills/lib/shared-policy.md +2 -2
  72. package/dist/chunk-4R2CYEA4.js +0 -116
  73. package/dist/chunk-L4Q55UC4.js +0 -52
  74. package/dist/chunk-LFIKMVY7.js +0 -27
  75. package/dist/chunk-RYAFBNES.js +0 -33
  76. package/dist/chunk-T5RPGCCM.js +0 -40
  77. package/dist/install-74ANPCCP.js +0 -2737
  78. package/dist/status-GLQWLWH6.js +0 -23
  79. package/dist/store-XB3ADT65.js +0 -144
  80. package/dist/whoami-2MLO4Y37.js +0 -36
  81. package/templates/hooks/configs/cursor-hooks.json +0 -18
  82. package/templates/hooks/lib/cite-contract-reminder.cjs +0 -179
  83. 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-2R55HNVD.js";
11
- import {
12
- paint
13
- } from "./chunk-BO4XIZWZ.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-XC5RUHLK.js";
18
+ } from "./chunk-3IOLS5EK.js";
19
+ import {
20
+ paint
21
+ } from "./chunk-NLNH64A3.js";
22
22
  import {
23
23
  t
24
- } from "./chunk-2CY4BMTH.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
  }
@@ -88,6 +91,20 @@ async function removeCitePolicyEvictHook(projectRoot) {
88
91
  projectRoot
89
92
  );
90
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
+ }
91
108
  async function removeHookScripts(step, rels, projectRoot) {
92
109
  const results = [];
93
110
  for (const rel of rels) {
@@ -144,25 +161,10 @@ async function unmergeCodexHookConfig(projectRoot) {
144
161
  extractCommands: extractFlatCommands
145
162
  });
146
163
  }
147
- async function unmergeCursorHookConfig(projectRoot) {
148
- return unmergeHookConfig({
149
- step: "cursor-hook-config",
150
- projectRoot,
151
- configRel: HOOK_CONFIG_TARGETS.cursor,
152
- arrayPaths: [...HOOK_CONFIG_ARRAY_PATHS.cursor],
153
- fabricCommands: Object.values(FABRIC_HOOK_COMMAND_PATHS.cursor),
154
- extractCommands: extractFlatCommands
155
- });
156
- }
157
164
  async function stripFabricBootstrapBlocks(projectRoot) {
158
165
  const results = [];
159
166
  results.push(await stripClaudeBootstrapImports(projectRoot));
160
- results.push(await stripManagedBlock(projectRoot, "AGENTS.md", { deleteWhenEmpty: false }));
161
- results.push(
162
- await stripManagedBlock(projectRoot, join(".cursor", "rules", "fabric-bootstrap.mdc"), {
163
- deleteWhenEmpty: true
164
- })
165
- );
167
+ results.push(await stripManagedBlock(projectRoot, "AGENTS.md"));
166
168
  return results;
167
169
  }
168
170
  async function stripClaudeBootstrapImports(projectRoot) {
@@ -207,8 +209,8 @@ async function stripClaudeBootstrapImports(projectRoot) {
207
209
  };
208
210
  }
209
211
  }
210
- async function stripManagedBlock(projectRoot, relPath, options) {
211
- const step = relPath.endsWith(".mdc") ? "bootstrap-cursor" : "bootstrap-codex";
212
+ async function stripManagedBlock(projectRoot, relPath) {
213
+ const step = "bootstrap-codex";
212
214
  const target = join(projectRoot, relPath);
213
215
  if (!existsSync(target)) {
214
216
  return { step, path: target, status: "skipped", message: "absent" };
@@ -231,19 +233,6 @@ async function stripManagedBlock(projectRoot, relPath, options) {
231
233
  const before = existing.slice(0, match.index ?? 0);
232
234
  const after = existing.slice((match.index ?? 0) + match[0].length);
233
235
  const filtered = `${before}${after.replace(/^\r?\n/, "")}`;
234
- if (options.deleteWhenEmpty && isFrontMatterOnly(filtered)) {
235
- try {
236
- await rm(target, { force: true });
237
- return { step, path: target, status: "removed", message: "front-matter-only" };
238
- } catch (error) {
239
- return {
240
- step,
241
- path: target,
242
- status: "error",
243
- message: error instanceof Error ? error.message : String(error)
244
- };
245
- }
246
- }
247
236
  try {
248
237
  await atomicWriteText(target, filtered);
249
238
  return { step, path: target, status: "removed" };
@@ -256,12 +245,6 @@ async function stripManagedBlock(projectRoot, relPath, options) {
256
245
  };
257
246
  }
258
247
  }
259
- function isFrontMatterOnly(content) {
260
- const trimmed = content.replace(/^\s+/, "");
261
- const match = trimmed.match(/^---\n[\s\S]*?\n---\s*$/);
262
- if (match === null) return trimmed.length === 0;
263
- return true;
264
- }
265
248
  async function deleteFabricAgentsSnapshot(projectRoot) {
266
249
  const target = fabricAgentsSnapshotPath(projectRoot);
267
250
  return rmIfExists("bootstrap-snapshot", target);
@@ -280,12 +263,6 @@ async function uninstallBootstrapStage(projectRoot, _opts = {}) {
280
263
  projectRoot,
281
264
  () => deleteFabricAgentsSnapshot(projectRoot)
282
265
  );
283
- await runAndCollectOne(
284
- results,
285
- "cursor-hook-config",
286
- projectRoot,
287
- () => unmergeCursorHookConfig(projectRoot)
288
- );
289
266
  await runAndCollectOne(
290
267
  results,
291
268
  "codex-hook-config",
@@ -323,6 +300,18 @@ async function uninstallBootstrapStage(projectRoot, _opts = {}) {
323
300
  projectRoot,
324
301
  () => removeCitePolicyEvictHook(projectRoot)
325
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
+ );
326
315
  await runAndCollect(
327
316
  results,
328
317
  "skill-connect",
@@ -359,6 +348,12 @@ async function uninstallBootstrapStage(projectRoot, _opts = {}) {
359
348
  projectRoot,
360
349
  () => uninstallFabricArchiveSkill(projectRoot)
361
350
  );
351
+ await runAndCollect(
352
+ results,
353
+ "skill-router",
354
+ projectRoot,
355
+ () => uninstallFabricRouterSkill(projectRoot)
356
+ );
362
357
  return results;
363
358
  }
364
359
  async function runAndCollect(results, step, projectRoot, fn) {
@@ -558,15 +553,6 @@ function jsonEqual(a, b) {
558
553
  }
559
554
 
560
555
  // src/commands/uninstall.ts
561
- var UNINSTALL_WIZARD_GROUP_CANCELLED = /* @__PURE__ */ Symbol("uninstall-wizard-group-cancelled");
562
- var KNOWLEDGE_SUBDIRS = [
563
- "decisions",
564
- "pitfalls",
565
- "guidelines",
566
- "models",
567
- "processes",
568
- "pending"
569
- ];
570
556
  var FABRIC_STATE_FILES = ["agents.meta.json", "events.jsonl", "forensic.json"];
571
557
  var uninstallCommand = defineCommand({
572
558
  meta: {
@@ -681,21 +667,17 @@ async function buildUninstallExecutionPlan(target, options = {}) {
681
667
  function buildUninstallFabricPlan(target, options = {}) {
682
668
  const absTarget = normalizeTarget(target);
683
669
  const fabricDir = join2(absTarget, ".fabric");
684
- const personalKnowledgeDir = resolve(resolvePersonalFabricRoot(), ".fabric", "knowledge");
670
+ const globalStoresDir = join2(resolvePersonalFabricRoot(), ".fabric", "stores");
685
671
  const entries = [];
686
672
  for (const name of FABRIC_STATE_FILES) {
687
673
  const p = join2(fabricDir, name);
688
674
  entries.push({ path: p, kind: "state-file", absent: !existsSync2(p) });
689
675
  }
690
- for (const sub of KNOWLEDGE_SUBDIRS) {
691
- const gk = join2(fabricDir, "knowledge", sub, ".gitkeep");
692
- entries.push({ path: gk, kind: "gitkeep", absent: !existsSync2(gk) });
693
- }
694
- const safeEntries = entries.filter((entry) => !isInsidePersonalRoot(entry.path, personalKnowledgeDir));
676
+ const safeEntries = entries.filter((entry) => !isInsideGlobalStoresRoot(entry.path, globalStoresDir));
695
677
  return {
696
678
  target: absTarget,
697
679
  fabricDir,
698
- personalKnowledgeDir,
680
+ globalStoresDir,
699
681
  options,
700
682
  entries: safeEntries
701
683
  };
@@ -730,8 +712,6 @@ function scaffoldStepLabel(kind) {
730
712
  switch (kind) {
731
713
  case "state-file":
732
714
  return "scaffold-state";
733
- case "gitkeep":
734
- return "scaffold-gitkeep";
735
715
  }
736
716
  }
737
717
  async function uninstallMcpClients(target, options = {}) {
@@ -805,12 +785,17 @@ async function uninstallMcpClients(target, options = {}) {
805
785
  }
806
786
  async function executeUninstallExecutionPlan(plan) {
807
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;
808
791
  for (const stage of plan.stages) {
792
+ stepNum += 1;
809
793
  if (stage.skipped) {
794
+ console.log(formatUninstallStageHeader(stage.name, stepNum, totalStages, true));
810
795
  stageResults.push({ name: stage.name, disposition: "skipped", steps: [] });
811
796
  continue;
812
797
  }
813
- console.log(formatUninstallStageHeader(stage.name));
798
+ console.log(formatUninstallStageHeader(stage.name, stepNum, totalStages));
814
799
  try {
815
800
  const steps = await executeUninstallStage(plan, stage.name);
816
801
  const disposition = steps.some((s) => s.status === "error") ? "failed" : "ran";
@@ -879,66 +864,36 @@ function createDefaultUninstallWizardAdapter() {
879
864
  return {
880
865
  async run(context) {
881
866
  intro(t("cli.uninstall.wizard.intro"));
882
- note(
883
- t("cli.uninstall.wizard.overview.body", {
884
- target: context.target
885
- }),
886
- t("cli.uninstall.wizard.overview.title")
867
+ const available = UNINSTALL_STAGE_KEYS.filter(
868
+ (key) => !context.lockedStages.includes(key)
887
869
  );
888
- printUninstallPlanSummary(context.target, context.options, context.supports);
889
- log.step(t("cli.uninstall.wizard.step.target"));
890
- const continueWithTarget = await confirm({
891
- message: t("cli.uninstall.wizard.target.confirm", { target: context.target }),
892
- 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
893
880
  });
894
- if (isCancel(continueWithTarget) || !continueWithTarget) {
881
+ if (isCancel(picked)) {
895
882
  emitUninstallWizardCancellation();
896
883
  return null;
897
884
  }
898
- log.step(t("cli.uninstall.wizard.step.plan"));
899
- let groupedSelection;
900
- try {
901
- groupedSelection = await group(
902
- {
903
- scaffold: async () => context.lockedStages.includes("scaffold") ? false : confirmInGroup({
904
- message: t("cli.uninstall.wizard.stage.scaffold", {
905
- defaultValue: formatPromptDefault(!context.options.skipScaffold)
906
- }),
907
- initialValue: !context.options.skipScaffold
908
- }),
909
- bootstrap: async () => context.lockedStages.includes("bootstrap") ? false : confirmInGroup({
910
- message: t("cli.uninstall.wizard.stage.bootstrap", {
911
- defaultValue: formatPromptDefault(!context.options.skipBootstrap)
912
- }),
913
- initialValue: !context.options.skipBootstrap
914
- }),
915
- mcp: async () => context.lockedStages.includes("mcp") ? false : confirmInGroup({
916
- message: t("cli.uninstall.wizard.stage.mcp", {
917
- defaultValue: formatPromptDefault(!context.options.skipMcp)
918
- }),
919
- initialValue: !context.options.skipMcp
920
- })
921
- },
922
- {
923
- onCancel() {
924
- throw UNINSTALL_WIZARD_GROUP_CANCELLED;
925
- }
926
- }
927
- );
928
- } catch (error) {
929
- if (error === UNINSTALL_WIZARD_GROUP_CANCELLED) {
930
- emitUninstallWizardCancellation();
931
- return null;
932
- }
933
- throw error;
934
- }
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
+ };
935
891
  const previewOptions = {
936
892
  ...context.options,
937
- skipScaffold: !groupedSelection.scaffold,
938
- skipBootstrap: !groupedSelection.bootstrap,
939
- skipMcp: !groupedSelection.mcp
893
+ skipScaffold: !selection.scaffold,
894
+ skipBootstrap: !selection.bootstrap,
895
+ skipMcp: !selection.mcp
940
896
  };
941
- log.step(t("cli.uninstall.wizard.step.review"));
942
897
  printUninstallPlanSummary(context.target, previewOptions, context.supports);
943
898
  const confirmed = await confirm({
944
899
  message: t("cli.uninstall.wizard.execute.confirm"),
@@ -949,20 +904,24 @@ function createDefaultUninstallWizardAdapter() {
949
904
  return null;
950
905
  }
951
906
  outro(t("cli.uninstall.wizard.outro"));
952
- return groupedSelection;
907
+ return selection;
953
908
  }
954
909
  };
955
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
+ }
956
922
  function emitUninstallWizardCancellation() {
957
923
  cancel(t("cli.uninstall.wizard.cancelled"));
958
924
  }
959
- async function confirmInGroup(options) {
960
- const result = await confirm(options);
961
- if (isCancel(result)) {
962
- throw UNINSTALL_WIZARD_GROUP_CANCELLED;
963
- }
964
- return result;
965
- }
966
925
  async function confirmDestructive(plan) {
967
926
  printUninstallPlanSummary(plan.target, plan.options, plan.supports);
968
927
  const answer = await confirm({
@@ -1009,8 +968,7 @@ function printUninstallPlanSummary(target, options, supports) {
1009
968
  })
1010
969
  );
1011
970
  console.log(t("cli.uninstall.plan.preserves"));
1012
- console.log(` - ${target}/.fabric/knowledge/ ${paint.muted(t("cli.uninstall.plan.preserves.knowledge"))}`);
1013
- 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"))}`);
1014
972
  }
1015
973
  function printUninstallSummary(result) {
1016
974
  const removed = result.stageResults.flatMap(
@@ -1038,8 +996,10 @@ function printUninstallSummary(result) {
1038
996
  }
1039
997
  }
1040
998
  }
1041
- function formatUninstallStageHeader(stageName) {
1042
- 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;
1043
1003
  }
1044
1004
  function formatUninstallStageResult(stageName, steps) {
1045
1005
  const removedCount = steps.filter((s) => s.status === "removed").length;
@@ -1056,9 +1016,9 @@ function formatUninstallStageFailure(stage, error) {
1056
1016
  function resolvePersonalFabricRoot() {
1057
1017
  return process.env.FABRIC_HOME ?? homedir();
1058
1018
  }
1059
- function isInsidePersonalRoot(candidate, personalKnowledgeDir) {
1019
+ function isInsideGlobalStoresRoot(candidate, globalStoresDir) {
1060
1020
  const candidateAbs = resolve(candidate);
1061
- const rootAbs = resolve(personalKnowledgeDir);
1021
+ const rootAbs = resolve(globalStoresDir);
1062
1022
  if (candidateAbs === rootAbs) {
1063
1023
  return true;
1064
1024
  }
@@ -1076,9 +1036,6 @@ function assertExistingDirectory(target) {
1076
1036
  function isInteractiveUninstall() {
1077
1037
  return Boolean(process.stdin.isTTY) && Boolean(process.stdout.isTTY) && Boolean(process.stderr.isTTY);
1078
1038
  }
1079
- function formatPromptDefault(value) {
1080
- return value ? "Y/n" : "y/N";
1081
- }
1082
1039
  function yesNoLabel(value) {
1083
1040
  return value ? t("cli.shared.yes") : t("cli.shared.no");
1084
1041
  }
@@ -1094,7 +1051,7 @@ export {
1094
1051
  uninstall_default as default,
1095
1052
  executeUninstallExecutionPlan,
1096
1053
  executeUninstallFabricPlan,
1097
- isInsidePersonalRoot,
1054
+ isInsideGlobalStoresRoot,
1098
1055
  resolveUninstallExecutionPlanWithWizard,
1099
1056
  runUninstallCommand,
1100
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.2.0-rc.1",
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.11",
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.2.0-rc.1",
49
- "@fenglimg/fabric-shared": "2.2.0-rc.1"
49
+ "@fenglimg/fabric-server": "2.2.0-rc.11",
50
+ "@fenglimg/fabric-shared": "2.2.0-rc.11"
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
  },