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

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 (74) hide show
  1. package/README.md +2 -2
  2. package/dist/audit-PURSJJFH.js +734 -0
  3. package/dist/{chunk-QPAW6IYT.js → chunk-7V4XMLQ2.js} +3 -3
  4. package/dist/{chunk-7ZDXBOOU.js → chunk-ACSMNX3V.js} +44 -128
  5. package/dist/{chunk-PTGQAZEW.js → chunk-GGDVZCD6.js} +2 -4
  6. package/dist/{chunk-EOT63RDH.js → chunk-I5F5BHWI.js} +9 -0
  7. package/dist/chunk-PP7QVRXH.js +565 -0
  8. package/dist/chunk-SL77FXX7.js +54 -0
  9. package/dist/{chunk-3D7B2UAZ.js → chunk-VQKXTMWH.js} +44 -4
  10. package/dist/doctor-S6KPGS35.js +27 -0
  11. package/dist/index.js +90 -80
  12. package/dist/{info-7FKBTMVO.js → info-NJEY26H6.js} +91 -46
  13. package/dist/{context-UJCGYOT6.js → inspect-5YZMJPFM.js} +10 -10
  14. package/dist/{install-v2-3KJX3YRO.js → install-v2-KGIDII4H.js} +163 -364
  15. package/dist/{store-HOCORVL3.js → store-GF4SFBMJ.js} +155 -57
  16. package/dist/{sync-DT5UJMMR.js → sync-3XCIRDPK.js} +3 -4
  17. package/dist/{uninstall-IFN2KYBK.js → uninstall-BG4ML4FC.js} +39 -10
  18. package/package.json +3 -7
  19. package/templates/hooks/cite-policy-evict.cjs +1 -1
  20. package/templates/hooks/configs/claude-code.json +1 -5
  21. package/templates/hooks/configs/codex-hooks.json +1 -5
  22. package/templates/hooks/fabric-hint.cjs +67 -41
  23. package/templates/hooks/knowledge-hint-broad.cjs +82 -24
  24. package/templates/hooks/knowledge-hint-narrow.cjs +3 -3
  25. package/templates/hooks/knowledge-pretooluse.cjs +111 -0
  26. package/templates/hooks/lib/banner-i18n.cjs +12 -11
  27. package/templates/hooks/lib/bindings-snapshot-reader.cjs +1 -1
  28. package/templates/hooks/lib/event-writer.cjs +79 -0
  29. package/templates/hooks/lib/nudge-policy.cjs +11 -0
  30. package/templates/hooks/lib/theme.cjs +62 -0
  31. package/templates/hooks/post-tooluse-mutation.cjs +28 -39
  32. package/templates/skills/fabric-archive/SKILL.md +29 -12
  33. package/templates/skills/fabric-archive/ref/dry-run-scope.md +1 -1
  34. package/templates/skills/fabric-archive/ref/i18n-policy.md +1 -1
  35. package/templates/skills/fabric-archive/ref/phase-1-5-onboard.md +5 -5
  36. package/templates/skills/fabric-archive/ref/phase-1-cross-session.md +2 -2
  37. package/templates/skills/fabric-archive/ref/phase-2-5-viability.md +1 -1
  38. package/templates/skills/fabric-archive/ref/phase-3-5-scope.md +1 -1
  39. package/templates/skills/fabric-archive/ref/phase-3-6-related-edges.md +1 -1
  40. package/templates/skills/fabric-archive/ref/phase-3-classify.md +1 -1
  41. package/templates/skills/fabric-archive/ref/phase-4-5-emit.md +1 -1
  42. package/templates/skills/fabric-archive/ref/phase-4-mcp-persist.md +6 -5
  43. package/templates/skills/{fabric-import/ref/checkpoint-state.md → fabric-archive/ref/source-checkpoint.md} +3 -3
  44. package/templates/skills/{fabric-import/ref/phase-3-dedup.md → fabric-archive/ref/source-dedup.md} +4 -4
  45. package/templates/skills/{fabric-import/ref/phase-2-mining.md → fabric-archive/ref/source-mining.md} +20 -20
  46. package/templates/skills/{fabric-import/ref/output-contract.md → fabric-archive/ref/source-output-contract.md} +3 -3
  47. package/templates/skills/{fabric-import/ref/state-recovery.md → fabric-archive/ref/source-state-recovery.md} +2 -2
  48. package/templates/skills/{fabric-import/ref/worked-examples.md → fabric-archive/ref/source-worked-examples.md} +10 -10
  49. package/templates/skills/fabric-archive/ref/worked-examples.md +3 -3
  50. package/templates/skills/fabric-review/SKILL.md +19 -15
  51. package/templates/skills/fabric-review/ref/cite-contract.md +2 -2
  52. package/templates/skills/fabric-review/ref/modify-flow.md +13 -1
  53. package/templates/skills/fabric-review/ref/per-mode-flows.md +5 -5
  54. package/templates/skills/fabric-review/ref/relate-mode.md +33 -0
  55. package/templates/skills/fabric-review/ref/retire-mode.md +47 -0
  56. package/templates/skills/fabric-review/ref/semantic-check.md +1 -1
  57. package/templates/skills/fabric-review/ref/worked-examples.md +5 -5
  58. package/templates/skills/fabric-store/SKILL.md +12 -27
  59. package/templates/skills/fabric-sync/SKILL.md +16 -35
  60. package/templates/skills/lib/shared-policy.md +6 -4
  61. package/dist/chunk-27HK6H5Y.js +0 -69
  62. package/dist/chunk-E7HJUU34.js +0 -1096
  63. package/dist/chunk-NLNH64A3.js +0 -43
  64. package/dist/chunk-QFIVFZRH.js +0 -13
  65. package/dist/doctor-MDTZWKBK.js +0 -24
  66. package/dist/metrics-HMFH4YHK.js +0 -135
  67. package/dist/scope-explain-HLJZ2M33.js +0 -48
  68. package/dist/status-4R3TM4FJ.js +0 -37
  69. package/dist/whoami-ITGEFWH4.js +0 -49
  70. package/templates/skills/fabric/SKILL.md +0 -100
  71. package/templates/skills/fabric-audit/SKILL.md +0 -63
  72. package/templates/skills/fabric-connect/SKILL.md +0 -48
  73. package/templates/skills/fabric-import/SKILL.md +0 -151
  74. package/templates/skills/fabric-import/ref/i18n-policy.md +0 -78
@@ -1,8 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  regenerateBindingsSnapshot
4
- } from "./chunk-PTGQAZEW.js";
5
- import "./chunk-EOT63RDH.js";
4
+ } from "./chunk-GGDVZCD6.js";
6
5
  import {
7
6
  assertStoreMountable,
8
7
  resolveStoreDir,
@@ -17,16 +16,17 @@ import {
17
16
  storeRemove,
18
17
  storeSetWriteRoute,
19
18
  storeSwitchWrite
20
- } from "./chunk-QPAW6IYT.js";
19
+ } from "./chunk-7V4XMLQ2.js";
21
20
  import {
22
21
  loadProjectConfig
23
- } from "./chunk-QFIVFZRH.js";
22
+ } from "./chunk-I5F5BHWI.js";
24
23
  import "./chunk-FNHDQTPC.js";
25
24
  import {
26
25
  getProjectTranslator
27
26
  } from "./chunk-HORSMSZL.js";
28
27
 
29
28
  // src/commands/store.ts
29
+ import { confirm, isCancel } from "@clack/prompts";
30
30
  import { defineCommand } from "citty";
31
31
  import { join as join3 } from "path";
32
32
 
@@ -220,6 +220,47 @@ import {
220
220
  loadGlobalConfig,
221
221
  resolveGlobalRoot
222
222
  } from "@fenglimg/fabric-shared";
223
+ async function resolveStoreMutationConsent(opts) {
224
+ if (opts.yes || process.env.FABRIC_NONINTERACTIVE === "1") {
225
+ return "proceed";
226
+ }
227
+ if (process.stdin.isTTY !== true) {
228
+ console.error(
229
+ `store ${opts.label}: stdin is not a TTY and neither --yes nor FABRIC_NONINTERACTIVE=1 is set. Refusing to mutate.`
230
+ );
231
+ return "abort";
232
+ }
233
+ const answer = await confirm({
234
+ message: `About to rewrite ${opts.count} knowledge entr${opts.count === 1 ? "y" : "ies"} (store ${opts.label}). Proceed?`,
235
+ initialValue: false
236
+ });
237
+ if (isCancel(answer) || answer !== true) {
238
+ console.error(`store ${opts.label}: aborted by user.`);
239
+ return "abort";
240
+ }
241
+ return "proceed";
242
+ }
243
+ async function runGatedMigrate(opts) {
244
+ if (opts.dryRun) {
245
+ opts.print(await opts.run(true));
246
+ return;
247
+ }
248
+ const preview = await opts.run(true);
249
+ if (preview.changes.length === 0) {
250
+ opts.print(preview);
251
+ return;
252
+ }
253
+ const decision = await resolveStoreMutationConsent({
254
+ label: opts.label,
255
+ count: preview.changes.length,
256
+ yes: opts.yes
257
+ });
258
+ if (decision === "abort") {
259
+ process.exitCode = 1;
260
+ return;
261
+ }
262
+ opts.print(await opts.run(false));
263
+ }
223
264
  var listCommand = defineCommand({
224
265
  meta: { name: "list", description: "List mounted knowledge stores" },
225
266
  run() {
@@ -230,16 +271,24 @@ var listCommand = defineCommand({
230
271
  return;
231
272
  }
232
273
  const localOnly = t("cli.shared.local-only");
233
- for (const store of stores) {
234
- const realRemote = storeGitRemote(store.alias);
274
+ const rows = stores.map((store) => ({
275
+ alias: store.alias,
276
+ name: store.mount_name ?? store.store_uuid,
277
+ uuid: store.store_uuid,
278
+ remote: storeGitRemote(store.alias) ?? localOnly
279
+ }));
280
+ const aliasW = Math.max(...rows.map((r) => r.alias.length));
281
+ const nameW = Math.max(...rows.map((r) => r.name.length));
282
+ const uuidW = Math.max(...rows.map((r) => r.uuid.length));
283
+ for (const r of rows) {
235
284
  console.log(
236
- `${store.alias} ${store.mount_name ?? store.store_uuid} ${store.store_uuid} ${realRemote ?? localOnly}`
285
+ `${r.alias.padEnd(aliasW)} ${r.name.padEnd(nameW)} ${r.uuid.padEnd(uuidW)} ${r.remote}`
237
286
  );
238
287
  }
239
288
  }
240
289
  });
241
- var addCommand = defineCommand({
242
- meta: { name: "add", description: "Mount a knowledge store into the global registry" },
290
+ var mountCommand = defineCommand({
291
+ meta: { name: "mount", description: "Mount a knowledge store into the global registry" },
243
292
  args: {
244
293
  uuid: { type: "string", required: true, description: "Intrinsic store UUID" },
245
294
  alias: { type: "string", required: true, description: "Local alias for this store" },
@@ -346,29 +395,35 @@ var bindCommand = defineCommand({
346
395
  }
347
396
  });
348
397
  var switchWriteCommand = defineCommand({
349
- meta: { name: "switch-write", description: "Set the default write store for non-personal scopes" },
398
+ meta: {
399
+ name: "switch-write",
400
+ description: "Set the default write store, or route one semantic scope with --scope"
401
+ },
350
402
  args: {
351
- alias: { type: "positional", required: true, description: "Alias of the store to write to" }
403
+ alias: { type: "positional", required: true, description: "Alias of the store to write to" },
404
+ scope: {
405
+ type: "string",
406
+ description: "Route only this semantic scope (e.g. team or project:fabric-v2) to the store"
407
+ }
352
408
  },
353
409
  async run({ args }) {
354
410
  const projectRoot = process.cwd();
411
+ if (typeof args.scope === "string" && args.scope.length > 0) {
412
+ storeSetWriteRoute(projectRoot, args.scope, args.alias);
413
+ regenerateBindingsSnapshot(projectRoot, { now: (/* @__PURE__ */ new Date()).toISOString() });
414
+ console.log(
415
+ getProjectTranslator(projectRoot)("cli.store.routed", {
416
+ scope: args.scope,
417
+ alias: args.alias
418
+ })
419
+ );
420
+ return;
421
+ }
355
422
  storeSwitchWrite(projectRoot, args.alias);
356
423
  regenerateBindingsSnapshot(projectRoot, { now: (/* @__PURE__ */ new Date()).toISOString() });
357
424
  console.log(getProjectTranslator(projectRoot)("cli.store.switch-write", { alias: args.alias }));
358
425
  }
359
426
  });
360
- var routeWriteCommand = defineCommand({
361
- meta: { name: "route-write", description: "Route a semantic scope to a writable shared store" },
362
- args: {
363
- scope: { type: "positional", required: true, description: "Semantic scope, e.g. team or project:fabric-v2" },
364
- alias: { type: "positional", required: true, description: "Alias of the shared store to write to" }
365
- },
366
- run({ args }) {
367
- const projectRoot = process.cwd();
368
- storeSetWriteRoute(projectRoot, args.scope, args.alias);
369
- console.log(`write route: ${args.scope} -> ${args.alias}`);
370
- }
371
- });
372
427
  var projectListCommand = defineCommand({
373
428
  meta: { name: "list", description: "List projects registered in a store" },
374
429
  args: {
@@ -408,14 +463,15 @@ var projectCommand = defineCommand({
408
463
  });
409
464
  var backfillScopeCommand = defineCommand({
410
465
  meta: {
411
- name: "backfill-scope",
466
+ name: "backfill",
412
467
  description: "Backfill semantic_scope + visibility_store on existing knowledge (repairs dirty layer)"
413
468
  },
414
469
  args: {
415
470
  store: { type: "string", description: "Backfill a mounted store's knowledge" },
416
- "dry-run": { type: "boolean", description: "Preview changes without writing" }
471
+ "dry-run": { type: "boolean", description: "Preview changes without writing" },
472
+ yes: { type: "boolean", description: "Skip the confirm-before-mutate prompt (CI / non-interactive)" }
417
473
  },
418
- run({ args }) {
474
+ async run({ args }) {
419
475
  const dryRun = args["dry-run"] === true;
420
476
  let knowledgeDir;
421
477
  let visibilityStore;
@@ -437,23 +493,43 @@ var backfillScopeCommand = defineCommand({
437
493
  knowledgeDir = join3(storeDir, STORE_LAYOUT2.knowledgeDir);
438
494
  visibilityStore = selectedStore;
439
495
  }
440
- const report = backfillKnowledgeDir(knowledgeDir, { visibilityStore, dryRun });
441
- if (report.changes.length === 0) {
442
- console.log(`scope backfill: nothing to do (${report.unchanged} already consistent).`);
496
+ const printReport = (report, preview2) => {
497
+ if (report.changes.length === 0) {
498
+ console.log(`scope backfill: nothing to do (${report.unchanged} already consistent).`);
499
+ return;
500
+ }
501
+ console.log(
502
+ `${preview2 ? "[dry-run] " : ""}scope backfill: ${report.changes.length} entr${report.changes.length === 1 ? "y" : "ies"} updated, ${report.unchanged} unchanged.`
503
+ );
504
+ for (const c of report.changes) {
505
+ console.log(` ${c.id ?? "(no id)"} [${c.changed.join(", ")}]`);
506
+ }
507
+ const scopeAssigned = report.changes.filter((c) => c.changed.includes("semantic_scope")).length;
508
+ if (scopeAssigned > 0) {
509
+ console.error(
510
+ `${preview2 ? "[dry-run] " : ""}note: ${scopeAssigned} entr${scopeAssigned === 1 ? "y" : "ies"} defaulted to semantic_scope: team. Demote project-specific ones with \`fabric store migrate scope <store> --to project:<id> --id <id>\`.`
511
+ );
512
+ }
513
+ };
514
+ if (dryRun) {
515
+ printReport(backfillKnowledgeDir(knowledgeDir, { visibilityStore, dryRun: true }), true);
443
516
  return;
444
517
  }
445
- console.log(
446
- `${dryRun ? "[dry-run] " : ""}scope backfill: ${report.changes.length} entr${report.changes.length === 1 ? "y" : "ies"} updated, ${report.unchanged} unchanged.`
447
- );
448
- for (const c of report.changes) {
449
- console.log(` ${c.id ?? "(no id)"} [${c.changed.join(", ")}]`);
518
+ const preview = backfillKnowledgeDir(knowledgeDir, { visibilityStore, dryRun: true });
519
+ if (preview.changes.length === 0) {
520
+ printReport(preview, false);
521
+ return;
450
522
  }
451
- const scopeAssigned = report.changes.filter((c) => c.changed.includes("semantic_scope")).length;
452
- if (scopeAssigned > 0) {
453
- console.error(
454
- `${dryRun ? "[dry-run] " : ""}note: ${scopeAssigned} entr${scopeAssigned === 1 ? "y" : "ies"} defaulted to semantic_scope: team. Demote project-specific ones with \`fabric store re-scope <store> --to project:<id> --id <id>\`.`
455
- );
523
+ const decision = await resolveStoreMutationConsent({
524
+ label: "migrate backfill",
525
+ count: preview.changes.length,
526
+ yes: args.yes === true
527
+ });
528
+ if (decision === "abort") {
529
+ process.exitCode = 1;
530
+ return;
456
531
  }
532
+ printReport(backfillKnowledgeDir(knowledgeDir, { visibilityStore, dryRun: false }), false);
457
533
  }
458
534
  });
459
535
  function resolveStoreDirAndVisibility(aliasOrUuid) {
@@ -488,7 +564,7 @@ function printRescopeReport(report) {
488
564
  }
489
565
  var rescopeCommand = defineCommand({
490
566
  meta: {
491
- name: "re-scope",
567
+ name: "scope",
492
568
  description: "Rewrite knowledge entries' semantic_scope coordinate in a store"
493
569
  },
494
570
  args: {
@@ -496,7 +572,8 @@ var rescopeCommand = defineCommand({
496
572
  to: { type: "string", required: true, description: "New semantic_scope (e.g. team, project:alpha)" },
497
573
  id: { type: "string", description: "Only the entry with this stable_id" },
498
574
  from: { type: "string", description: "Only entries currently at this semantic_scope" },
499
- "dry-run": { type: "boolean", description: "Preview changes without writing" }
575
+ "dry-run": { type: "boolean", description: "Preview changes without writing" },
576
+ yes: { type: "boolean", description: "Skip the confirm-before-mutate prompt (CI / non-interactive)" }
500
577
  },
501
578
  async run({ args }) {
502
579
  const resolved = resolveStoreDirAndVisibility(args.store);
@@ -505,14 +582,18 @@ var rescopeCommand = defineCommand({
505
582
  process.exitCode = 1;
506
583
  return;
507
584
  }
508
- printRescopeReport(
509
- await rescopeStore(resolved.dir, args.to, {
585
+ await runGatedMigrate({
586
+ label: "migrate scope",
587
+ dryRun: args["dry-run"] === true,
588
+ yes: args.yes === true,
589
+ run: (dryRun) => rescopeStore(resolved.dir, args.to, {
510
590
  id: args.id,
511
591
  fromScope: args.from,
512
592
  storeVisibility: resolved.visibility,
513
- dryRun: args["dry-run"] === true
514
- })
515
- );
593
+ dryRun
594
+ }),
595
+ print: printRescopeReport
596
+ });
516
597
  }
517
598
  });
518
599
  var promoteCommand = defineCommand({
@@ -523,7 +604,8 @@ var promoteCommand = defineCommand({
523
604
  args: {
524
605
  store: { type: "positional", required: true, description: "Target store alias or uuid" },
525
606
  project: { type: "string", description: "Only this project's entries (default: all project:*)" },
526
- "dry-run": { type: "boolean", description: "Preview changes without writing" }
607
+ "dry-run": { type: "boolean", description: "Preview changes without writing" },
608
+ yes: { type: "boolean", description: "Skip the confirm-before-mutate prompt (CI / non-interactive)" }
527
609
  },
528
610
  async run({ args }) {
529
611
  const resolved = resolveStoreDirAndVisibility(args.store);
@@ -532,29 +614,45 @@ var promoteCommand = defineCommand({
532
614
  process.exitCode = 1;
533
615
  return;
534
616
  }
535
- printRescopeReport(
536
- await promoteProjectToTeam(resolved.dir, {
617
+ await runGatedMigrate({
618
+ label: "migrate promote",
619
+ dryRun: args["dry-run"] === true,
620
+ yes: args.yes === true,
621
+ run: (dryRun) => promoteProjectToTeam(resolved.dir, {
537
622
  projectId: args.project,
538
623
  storeVisibility: resolved.visibility,
539
- dryRun: args["dry-run"] === true
540
- })
541
- );
624
+ dryRun
625
+ }),
626
+ print: printRescopeReport
627
+ });
628
+ }
629
+ });
630
+ var migrateCommand = defineCommand({
631
+ meta: { name: "migrate", description: "Rewrite knowledge entries' scope coordinates in a store" },
632
+ subCommands: {
633
+ scope: rescopeCommand,
634
+ promote: promoteCommand,
635
+ backfill: backfillScopeCommand
542
636
  }
543
637
  });
544
638
  var store_default = defineCommand({
545
639
  meta: { name: "store", description: "Manage mounted Fabric knowledge stores" },
640
+ // W3-E: subCommands ordered by value axis — registry (which stores exist) →
641
+ // project wiring (where this repo reads/writes) → knowledge migration →
642
+ // store-internal project registry.
546
643
  subCommands: {
644
+ // registry
645
+ mount: mountCommand,
547
646
  list: listCommand,
548
647
  create: createCommand,
549
- add: addCommand,
550
648
  remove: removeCommand,
551
649
  explain: explainCommand,
650
+ // project wiring
552
651
  bind: bindCommand,
553
652
  "switch-write": switchWriteCommand,
554
- "route-write": routeWriteCommand,
555
- "backfill-scope": backfillScopeCommand,
556
- "re-scope": rescopeCommand,
557
- promote: promoteCommand,
653
+ // knowledge migration
654
+ migrate: migrateCommand,
655
+ // store-internal projects
558
656
  project: projectCommand
559
657
  }
560
658
  });
@@ -1,12 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  paint
4
- } from "./chunk-NLNH64A3.js";
4
+ } from "./chunk-SL77FXX7.js";
5
5
  import {
6
6
  regenerateBindingsSnapshot
7
- } from "./chunk-PTGQAZEW.js";
8
- import "./chunk-EOT63RDH.js";
9
- import "./chunk-QFIVFZRH.js";
7
+ } from "./chunk-GGDVZCD6.js";
8
+ import "./chunk-I5F5BHWI.js";
10
9
  import {
11
10
  loadGlobalConfig,
12
11
  resolveGlobalRoot
@@ -7,18 +7,18 @@ import {
7
7
  HOOK_SCRIPT_DESTINATIONS,
8
8
  SKILL_DESTINATIONS,
9
9
  fabricAgentsSnapshotPath
10
- } from "./chunk-7ZDXBOOU.js";
11
- import {
12
- createDebugLogger,
13
- resolveDevMode
14
- } from "./chunk-WA3DYGSY.js";
10
+ } from "./chunk-ACSMNX3V.js";
15
11
  import {
16
12
  detectClientSupports,
17
13
  resolveClients
18
14
  } from "./chunk-3IOLS5EK.js";
15
+ import {
16
+ createDebugLogger,
17
+ resolveDevMode
18
+ } from "./chunk-WA3DYGSY.js";
19
19
  import {
20
20
  paint
21
- } from "./chunk-NLNH64A3.js";
21
+ } from "./chunk-SL77FXX7.js";
22
22
  import {
23
23
  t
24
24
  } from "./chunk-HORSMSZL.js";
@@ -38,7 +38,11 @@ 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
40
  async function uninstallFabricRouterSkill(projectRoot) {
41
- return removeSkill("skill-router", SKILL_DESTINATIONS.fabricRouter, projectRoot);
41
+ return removeSkill(
42
+ "skill-router",
43
+ [".claude/skills/fabric/SKILL.md", ".codex/skills/fabric/SKILL.md"],
44
+ projectRoot
45
+ );
42
46
  }
43
47
  async function uninstallFabricArchiveSkill(projectRoot) {
44
48
  return removeSkill("skill", SKILL_DESTINATIONS.fabricArchive, projectRoot);
@@ -47,16 +51,28 @@ async function uninstallFabricReviewSkill(projectRoot) {
47
51
  return removeSkill("skill-review", SKILL_DESTINATIONS.fabricReview, projectRoot);
48
52
  }
49
53
  async function uninstallFabricImportSkill(projectRoot) {
50
- return removeSkill("skill-import", SKILL_DESTINATIONS.fabricImport, projectRoot);
54
+ return removeSkill(
55
+ "skill-import",
56
+ [".claude/skills/fabric-import/SKILL.md", ".codex/skills/fabric-import/SKILL.md"],
57
+ projectRoot
58
+ );
51
59
  }
52
60
  async function uninstallFabricSyncSkill(projectRoot) {
53
61
  return removeSkill("skill-sync", SKILL_DESTINATIONS.fabricSync, projectRoot);
54
62
  }
55
63
  async function uninstallFabricAuditSkill(projectRoot) {
56
- return removeSkill("skill-audit", SKILL_DESTINATIONS.fabricAudit, projectRoot);
64
+ return removeSkill(
65
+ "skill-audit",
66
+ [".claude/skills/fabric-audit/SKILL.md", ".codex/skills/fabric-audit/SKILL.md"],
67
+ projectRoot
68
+ );
57
69
  }
58
70
  async function uninstallFabricConnectSkill(projectRoot) {
59
- return removeSkill("skill-connect", SKILL_DESTINATIONS.fabricConnect, projectRoot);
71
+ return removeSkill(
72
+ "skill-connect",
73
+ [".claude/skills/fabric-connect/SKILL.md", ".codex/skills/fabric-connect/SKILL.md"],
74
+ projectRoot
75
+ );
60
76
  }
61
77
  async function removeSkill(step, rels, projectRoot) {
62
78
  const results = [];
@@ -91,6 +107,13 @@ async function removeCitePolicyEvictHook(projectRoot) {
91
107
  projectRoot
92
108
  );
93
109
  }
110
+ async function removeKnowledgePretoolUseHook(projectRoot) {
111
+ return removeHookScripts(
112
+ "hook-pretooluse-script",
113
+ HOOK_SCRIPT_DESTINATIONS.knowledgePretoolUse,
114
+ projectRoot
115
+ );
116
+ }
94
117
  async function removeSessionEndMarkerHook(projectRoot) {
95
118
  return removeHookScripts(
96
119
  "hook-session-end-script",
@@ -300,6 +323,12 @@ async function uninstallBootstrapStage(projectRoot, _opts = {}) {
300
323
  projectRoot,
301
324
  () => removeCitePolicyEvictHook(projectRoot)
302
325
  );
326
+ await runAndCollect(
327
+ results,
328
+ "hook-pretooluse-script",
329
+ projectRoot,
330
+ () => removeKnowledgePretoolUseHook(projectRoot)
331
+ );
303
332
  await runAndCollect(
304
333
  results,
305
334
  "hook-session-end-script",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fenglimg/fabric-cli",
3
- "version": "2.2.0",
3
+ "version": "2.3.0-rc.1",
4
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>",
@@ -39,19 +39,15 @@
39
39
  "dependencies": {
40
40
  "@clack/prompts": "^1.2.0",
41
41
  "citty": "^0.2.2",
42
- "ink": "^4.4.1",
43
- "picocolors": "^1.1.1",
44
- "react": "^18.3.1",
45
42
  "string-width": "^7.2.0",
46
43
  "tree-sitter-javascript": "^0.25.0",
47
44
  "tree-sitter-typescript": "^0.23.2",
48
45
  "web-tree-sitter": "^0.26.8",
49
- "@fenglimg/fabric-server": "2.2.0",
50
- "@fenglimg/fabric-shared": "2.2.0"
46
+ "@fenglimg/fabric-server": "2.3.0-rc.1",
47
+ "@fenglimg/fabric-shared": "2.3.0-rc.1"
51
48
  },
52
49
  "devDependencies": {
53
50
  "@types/node": "^22.15.0",
54
- "@types/react": "^18.3.12",
55
51
  "tsup": "^8.5.0",
56
52
  "typescript": "^5.8.3"
57
53
  },
@@ -385,7 +385,7 @@ function renderNudge(editPaths) {
385
385
  `[fabric cite] 改 ${target} 前未检测到相关 fab_recall —`,
386
386
  "建议先调 fab_recall(paths=[<被改文件>]) 让系统自动记账引用的 KB(无需手写首行 KB:)。",
387
387
  "已 recall 过可忽略本提示。仍可手写首行 `KB: <id> [applied]` 显式 override。",
388
- "(nudge only — 不阻塞本次编辑;cite 覆盖率见 fabric doctor --cite-coverage)",
388
+ "(nudge only — 不阻塞本次编辑;cite 覆盖率见 fabric audit cite)",
389
389
  ].join("\n");
390
390
  }
391
391
 
@@ -28,11 +28,7 @@
28
28
  "hooks": [
29
29
  {
30
30
  "type": "command",
31
- "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/knowledge-hint-narrow.cjs"
32
- },
33
- {
34
- "type": "command",
35
- "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/cite-policy-evict.cjs"
31
+ "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/knowledge-pretooluse.cjs"
36
32
  }
37
33
  ]
38
34
  }
@@ -13,11 +13,7 @@
13
13
  "PreToolUse": [
14
14
  {
15
15
  "matcher": "Edit|Write|MultiEdit|apply_patch",
16
- "command": "\"$(git rev-parse --show-toplevel)/.codex/hooks/knowledge-hint-narrow.cjs\""
17
- },
18
- {
19
- "matcher": "Edit|Write|MultiEdit|apply_patch",
20
- "command": "\"$(git rev-parse --show-toplevel)/.codex/hooks/cite-policy-evict.cjs\""
16
+ "command": "\"$(git rev-parse --show-toplevel)/.codex/hooks/knowledge-pretooluse.cjs\""
21
17
  }
22
18
  ],
23
19
  "PostToolUse": [