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

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 (35) hide show
  1. package/dist/chunk-5JG4QJLO.js +64 -0
  2. package/dist/chunk-5SSNE5GM.js +301 -0
  3. package/dist/chunk-EOT63RDH.js +36 -0
  4. package/dist/{chunk-AOE6AYI7.js → chunk-F6ITRM7T.js} +2 -2
  5. package/dist/{chunk-WU6GAPKH.js → chunk-H3FE6VIK.js} +3 -5
  6. package/dist/chunk-XCBVSGCS.js +25 -0
  7. package/dist/{chunk-2R55HNVD.js → chunk-XHHCRDIR.js} +71 -6
  8. package/dist/{config-XYRBZJDU.js → config-VJMXCLXW.js} +1 -1
  9. package/dist/{doctor-YONYXDX6.js → doctor-U5W4CX5I.js} +118 -7
  10. package/dist/index.js +13 -12
  11. package/dist/{install-74ANPCCP.js → install-7XJ64WSC.js} +252 -246
  12. package/dist/{plan-context-hint-FC6P3WFE.js → plan-context-hint-CHVZGOZ5.js} +21 -8
  13. package/dist/{scope-explain-CDIZESP5.js → scope-explain-BWRWBCCP.js} +14 -4
  14. package/dist/{status-GLQWLWH6.js → status-7UFLWRX7.js} +17 -6
  15. package/dist/store-ZEZMQVG7.js +817 -0
  16. package/dist/{sync-UJ4BBCZJ.js → sync-EA5HZMXM.js} +165 -21
  17. package/dist/{uninstall-C3QXKOO6.js → uninstall-F75MPKQC.js} +27 -1
  18. package/dist/{whoami-2MLO4Y37.js → whoami-3FRWYGML.js} +16 -5
  19. package/package.json +3 -3
  20. package/templates/hooks/cite-policy-evict.cjs +412 -160
  21. package/templates/hooks/configs/claude-code.json +17 -2
  22. package/templates/hooks/configs/codex-hooks.json +14 -2
  23. package/templates/hooks/configs/cursor-hooks.json +14 -2
  24. package/templates/hooks/fabric-hint.cjs +151 -15
  25. package/templates/hooks/knowledge-hint-broad.cjs +12 -1
  26. package/templates/hooks/knowledge-hint-narrow.cjs +54 -1
  27. package/templates/hooks/post-tooluse-mutation.cjs +285 -0
  28. package/templates/hooks/session-end-marker.cjs +140 -0
  29. package/templates/skills/fabric-archive/SKILL.md +7 -1
  30. package/dist/chunk-4R2CYEA4.js +0 -116
  31. package/dist/chunk-L4Q55UC4.js +0 -52
  32. package/dist/chunk-LFIKMVY7.js +0 -27
  33. package/dist/chunk-RYAFBNES.js +0 -33
  34. package/dist/chunk-T5RPGCCM.js +0 -40
  35. package/dist/store-XB3ADT65.js +0 -144
@@ -13,6 +13,8 @@ import {
13
13
  installHookLibs,
14
14
  installKnowledgeHintBroadHook,
15
15
  installKnowledgeHintNarrowHook,
16
+ installPostTooluseMutationHook,
17
+ installSessionEndMarkerHook,
16
18
  installSharedSkillLib,
17
19
  mergeClaudeCodeHookConfig,
18
20
  mergeCodexHookConfig,
@@ -22,7 +24,7 @@ import {
22
24
  writeCodexBootstrapManagedBlock,
23
25
  writeCursorBootstrapManagedBlock,
24
26
  writeFabricAgentsSnapshot
25
- } from "./chunk-2R55HNVD.js";
27
+ } from "./chunk-XHHCRDIR.js";
26
28
  import {
27
29
  displayWidth,
28
30
  padEnd,
@@ -34,29 +36,40 @@ import {
34
36
  } from "./chunk-COI5VDFU.js";
35
37
  import {
36
38
  installMcpClients
37
- } from "./chunk-AOE6AYI7.js";
39
+ } from "./chunk-F6ITRM7T.js";
38
40
  import {
39
41
  detectClientSupports
40
42
  } from "./chunk-XC5RUHLK.js";
43
+ import {
44
+ regenerateBindingsSnapshot
45
+ } from "./chunk-H3FE6VIK.js";
46
+ import "./chunk-EOT63RDH.js";
41
47
  import {
42
48
  getProjectTranslator,
43
49
  t
44
50
  } from "./chunk-2CY4BMTH.js";
51
+ import {
52
+ storeBind,
53
+ storeCreate,
54
+ storeList,
55
+ storeSwitchWrite,
56
+ syncStoreAliasLinks,
57
+ unboundAvailableStores
58
+ } from "./chunk-5SSNE5GM.js";
45
59
  import {
46
60
  globalConfigPath,
47
61
  loadGlobalConfig,
62
+ loadProjectConfig,
48
63
  resolveGlobalRoot,
49
64
  saveGlobalConfig
50
- } from "./chunk-RYAFBNES.js";
65
+ } from "./chunk-XCBVSGCS.js";
51
66
 
52
67
  // src/commands/install.ts
53
68
  import { randomUUID as randomUUID2 } from "crypto";
54
- import { homedir } from "os";
55
69
  import * as childProcess from "child_process";
56
- import { appendFileSync, existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync3, rmSync as rmSync2, statSync as statSync4, writeFileSync } from "fs";
57
- import { dirname, isAbsolute as isAbsolute3, join as join6, resolve as resolve3 } from "path";
58
- import { cancel, confirm, group, intro, isCancel, log, note, outro, select } from "@clack/prompts";
59
- import { defaultAgentsMetaCounters } from "@fenglimg/fabric-shared";
70
+ import { appendFileSync, existsSync as existsSync5, mkdirSync as mkdirSync2, rmSync as rmSync2, statSync as statSync4, writeFileSync as writeFileSync2 } from "fs";
71
+ import { dirname, isAbsolute as isAbsolute3, join as join7, resolve as resolve3 } from "path";
72
+ import { cancel, confirm, group, intro, isCancel, log, note, outro, select, text } from "@clack/prompts";
60
73
  import { atomicWriteJson } from "@fenglimg/fabric-shared/node/atomic-write";
61
74
  import { defineCommand } from "citty";
62
75
 
@@ -79,6 +92,8 @@ async function installHooks(target, _options = {}) {
79
92
  results.push(...await runStep(() => installKnowledgeHintBroadHook(normalizedTarget)));
80
93
  results.push(...await runStep(() => installKnowledgeHintNarrowHook(normalizedTarget)));
81
94
  results.push(...await runStep(() => installCitePolicyEvictHook(normalizedTarget)));
95
+ results.push(...await runStep(() => installSessionEndMarkerHook(normalizedTarget)));
96
+ results.push(...await runStep(() => installPostTooluseMutationHook(normalizedTarget)));
82
97
  results.push(...await runStep(() => installHookLibs(normalizedTarget)));
83
98
  results.push(await runSingleStep("claude-hook-config", () => mergeClaudeCodeHookConfig(normalizedTarget)));
84
99
  results.push(await runSingleStep("codex-hook-config", () => mergeCodexHookConfig(normalizedTarget)));
@@ -94,7 +109,10 @@ function validateHookPaths(projectRoot) {
94
109
  const scripts = [
95
110
  { stepSuffix: "", hookFile: "fabric-hint.cjs" },
96
111
  { stepSuffix: "-broad", hookFile: "knowledge-hint-broad.cjs" },
97
- { stepSuffix: "-narrow", hookFile: "knowledge-hint-narrow.cjs" }
112
+ { stepSuffix: "-narrow", hookFile: "knowledge-hint-narrow.cjs" },
113
+ // lifecycle-refactor W2-T2/T3: SessionEnd + PostToolUse marker hooks.
114
+ { stepSuffix: "-session-end", hookFile: "session-end-marker.cjs" },
115
+ { stepSuffix: "-post-tooluse", hookFile: "post-tooluse-mutation.cjs" }
98
116
  ];
99
117
  const clients = [
100
118
  {
@@ -197,12 +215,50 @@ function assertExistingDirectory(target) {
197
215
  }
198
216
  }
199
217
 
218
+ // src/install/semantic-search.ts
219
+ import { existsSync as existsSync2, readFileSync, writeFileSync } from "fs";
220
+ import { join as join2 } from "path";
221
+ var DEFAULT_EMBED_MODEL_PIN = "fast-bge-small-zh-v1.5";
222
+ function enableSemanticSearch(projectRoot, opts = {}) {
223
+ const model = typeof opts.model === "string" && opts.model.length > 0 ? opts.model : DEFAULT_EMBED_MODEL_PIN;
224
+ const configPath = join2(projectRoot, "fabric.config.json");
225
+ let existing = {};
226
+ if (existsSync2(configPath)) {
227
+ try {
228
+ const parsed = JSON.parse(readFileSync(configPath, "utf8"));
229
+ if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
230
+ existing = parsed;
231
+ }
232
+ } catch {
233
+ existing = {};
234
+ }
235
+ }
236
+ const alreadyEnabled = existing.embed_enabled === true && existing.embed_model === model;
237
+ if (alreadyEnabled) {
238
+ return { configPath, model, alreadyEnabled: true, changed: false };
239
+ }
240
+ const merged = { ...existing, embed_enabled: true, embed_model: model };
241
+ writeFileSync(configPath, JSON.stringify(merged, null, 2) + "\n", "utf8");
242
+ return { configPath, model, alreadyEnabled: false, changed: true };
243
+ }
244
+ function renderSemanticSearchInstructions(model) {
245
+ return [
246
+ "\u8BED\u4E49\u641C\u7D22\u5DF2\u542F\u7528 (embed_enabled=true, embed_model=" + model + ")\u3002\u8FD8\u9700\u4E24\u6B65 (\u4E00\u6B21\u6027):",
247
+ " 1. \u5B89\u88C5\u53EF\u9009 embedder (\u88C5\u5230 MCP server \u89E3\u6790\u6A21\u5757\u7684\u4F4D\u7F6E \u2014 \u5168\u5C40\u5B89\u88C5\u5373\u5168\u5C40):",
248
+ " npm i -g fastembed",
249
+ " 2. \u9884\u70ED\u6A21\u578B\u7F13\u5B58 (\u9996\u8DD1\u4F1A\u8054\u7F51\u4E0B\u8F7D\u6A21\u578B\u6743\u91CD ~\u6570\u5341-\u6570\u767E MB, \u4E0D\u4E0A\u4F20\u4EFB\u4F55 KB \u6570\u636E):",
250
+ " export FABRIC_EMBED_CACHE_DIR=~/.cache/fabric-embed # \u4E25\u683C\u79BB\u7EBF\u8005\u9884\u5148\u653E\u597D\u6743\u91CD",
251
+ " \u6CE8: \u5207\u6362 embed_model \u540E\u5DF2\u6709\u5411\u91CF\u7EF4\u5EA6/\u8BED\u4E49\u53D8\u5316, \u4E0B\u6B21 recall \u4F1A\u6309\u65B0\u6A21\u578B\u91CD\u65B0\u5D4C\u5165 (doc \u5411\u91CF\u6309\u6587\u672C\u7F13\u5B58, \u81EA\u52A8\u5931\u914D\u91CD\u7B97)\u3002",
252
+ " \u5173\u95ED: \u7F16\u8F91 fabric.config.json \u8BBE embed_enabled=false\u3002"
253
+ ];
254
+ }
255
+
200
256
  // src/install/run-global-install.ts
201
257
  import { execFileSync as execFileSync2 } from "child_process";
202
258
  import { randomUUID } from "crypto";
203
259
  import { mkdirSync, mkdtempSync, renameSync } from "fs";
204
260
  import { tmpdir } from "os";
205
- import { join as join3 } from "path";
261
+ import { join as join4 } from "path";
206
262
  import { STORES_ROOT_DIR as STORES_ROOT_DIR2, addMountedStore, readStoreIdentity } from "@fenglimg/fabric-shared";
207
263
  import { GenericIOError } from "@fenglimg/fabric-shared/errors";
208
264
 
@@ -229,7 +285,7 @@ function deriveUid(opts = {}) {
229
285
 
230
286
  // src/install/install-global.ts
231
287
  import { rmSync } from "fs";
232
- import { join as join2 } from "path";
288
+ import { join as join3 } from "path";
233
289
  import {
234
290
  STORES_ROOT_DIR,
235
291
  globalConfigSchema,
@@ -288,7 +344,7 @@ async function installGlobalCore(options) {
288
344
  };
289
345
  }
290
346
  const alias = options.personalAlias ?? "personal";
291
- const personalDir = join2(options.globalRoot, STORES_ROOT_DIR, options.personalStoreUuid);
347
+ const personalDir = join3(options.globalRoot, STORES_ROOT_DIR, options.personalStoreUuid);
292
348
  let config = null;
293
349
  const receipt = await runInstallTransaction([
294
350
  {
@@ -339,10 +395,10 @@ function gitClone(url, dest) {
339
395
  }
340
396
  }
341
397
  function mountStoreFromRemote(url, globalRoot) {
342
- const storesRoot = join3(globalRoot, STORES_ROOT_DIR2);
398
+ const storesRoot = join4(globalRoot, STORES_ROOT_DIR2);
343
399
  mkdirSync(storesRoot, { recursive: true });
344
- const tmp = mkdtempSync(join3(tmpdir(), "fabric-clone-"));
345
- const cloneDest = join3(tmp, "store");
400
+ const tmp = mkdtempSync(join4(tmpdir(), "fabric-clone-"));
401
+ const cloneDest = join4(tmp, "store");
346
402
  gitClone(url, cloneDest);
347
403
  const identity = readStoreIdentity(cloneDest);
348
404
  if (identity === null) {
@@ -350,7 +406,7 @@ function mountStoreFromRemote(url, globalRoot) {
350
406
  actionHint: "verify the url points to a repository created by `fabric` (it must contain a store.json at its root); if you meant to mount a different store, re-run with the correct url"
351
407
  });
352
408
  }
353
- const finalDir = join3(storesRoot, identity.store_uuid);
409
+ const finalDir = join4(storesRoot, identity.store_uuid);
354
410
  renameSync(cloneDest, finalDir);
355
411
  const config = loadGlobalConfig(globalRoot);
356
412
  if (config === null) {
@@ -364,6 +420,7 @@ function mountStoreFromRemote(url, globalRoot) {
364
420
  globalRoot
365
421
  );
366
422
  console.log(`mounted store '${alias}' (${identity.store_uuid}) from ${url}`);
423
+ return { store_uuid: identity.store_uuid, alias };
367
424
  }
368
425
  async function runGlobalInstall(options = {}, globalRoot = resolveGlobalRoot()) {
369
426
  const uid = options.uid ?? deriveUid();
@@ -384,23 +441,24 @@ async function runGlobalInstall(options = {}, globalRoot = resolveGlobalRoot())
384
441
  if (options.url !== void 0) {
385
442
  mountStoreFromRemote(options.url, globalRoot);
386
443
  }
444
+ syncStoreAliasLinks(globalRoot);
387
445
  }
388
446
 
389
447
  // src/lib/detect-language.ts
390
- import { existsSync as existsSync2, readdirSync, readFileSync, statSync as statSync2 } from "fs";
391
- import { join as join4 } from "path";
448
+ import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync2, statSync as statSync2 } from "fs";
449
+ import { join as join5 } from "path";
392
450
  function detectExistingLanguage(target) {
393
451
  const ZH_CN_RATIO_THRESHOLD = 0.3;
394
452
  const samples = [];
395
- const readmePath = join4(target, "README.md");
396
- if (existsSync2(readmePath)) {
453
+ const readmePath = join5(target, "README.md");
454
+ if (existsSync3(readmePath)) {
397
455
  try {
398
- samples.push(readFileSync(readmePath, "utf8"));
456
+ samples.push(readFileSync2(readmePath, "utf8"));
399
457
  } catch {
400
458
  }
401
459
  }
402
- const docsDir = join4(target, "docs");
403
- if (existsSync2(docsDir)) {
460
+ const docsDir = join5(target, "docs");
461
+ if (existsSync3(docsDir)) {
404
462
  try {
405
463
  const stat = statSync2(docsDir);
406
464
  if (stat.isDirectory()) {
@@ -408,7 +466,7 @@ function detectExistingLanguage(target) {
408
466
  if (!entry.isFile()) continue;
409
467
  if (!/\.(md|mdx|txt)$/iu.test(entry.name)) continue;
410
468
  try {
411
- samples.push(readFileSync(join4(docsDir, entry.name), "utf8"));
469
+ samples.push(readFileSync2(join5(docsDir, entry.name), "utf8"));
412
470
  } catch {
413
471
  }
414
472
  }
@@ -441,9 +499,9 @@ function detectExistingLanguage(target) {
441
499
 
442
500
  // src/scanner/forensic.ts
443
501
  import { execFileSync as execFileSync3 } from "child_process";
444
- import { existsSync as existsSync3, readdirSync as readdirSync2, readFileSync as readFileSync2, statSync as statSync3 } from "fs";
502
+ import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync3, statSync as statSync3 } from "fs";
445
503
  import { createRequire } from "module";
446
- import { basename, extname, isAbsolute as isAbsolute2, join as join5, posix, relative, resolve as resolve2, sep } from "path";
504
+ import { basename, extname, isAbsolute as isAbsolute2, join as join6, posix, relative, resolve as resolve2, sep } from "path";
447
505
  import {
448
506
  buildScanRecommendations,
449
507
  forensicReportSchema
@@ -588,7 +646,7 @@ function buildTopology(root) {
588
646
  continue;
589
647
  }
590
648
  for (const entry of readdirSync2(current, { withFileTypes: true })) {
591
- const absolutePath = join5(current, entry.name);
649
+ const absolutePath = join6(current, entry.name);
592
650
  const relativePath = toPosixPath(relative(root, absolutePath));
593
651
  if (relativePath.length === 0) {
594
652
  continue;
@@ -627,7 +685,7 @@ function buildTopology(root) {
627
685
  };
628
686
  }
629
687
  function assertExistingDirectory2(target) {
630
- if (!existsSync3(target) || !statSync3(target).isDirectory()) {
688
+ if (!existsSync4(target) || !statSync3(target).isDirectory()) {
631
689
  throw new Error(`Target must be an existing directory: ${target}`);
632
690
  }
633
691
  }
@@ -676,7 +734,7 @@ function getEntryPointReason(relativePath) {
676
734
  async function buildCodeSamples(target, entryPoints, frameworkKind, topology, packageDependencies) {
677
735
  const samples = [];
678
736
  for (const entryPoint of entryPoints.slice(0, SAMPLE_LIMIT)) {
679
- const absolutePath = join5(target, ...entryPoint.path.split("/"));
737
+ const absolutePath = join6(target, ...entryPoint.path.split("/"));
680
738
  const sample = readFirstLines(absolutePath, SAMPLE_LINE_LIMIT);
681
739
  const patternAnalysis = await inferPatternHint(entryPoint.path, sample.snippet, {
682
740
  frameworkKind,
@@ -696,7 +754,7 @@ async function buildCodeSamples(target, entryPoints, frameworkKind, topology, pa
696
754
  }
697
755
  function readFirstLines(path, lineLimit) {
698
756
  try {
699
- const lines = readFileSync2(path, "utf8").split(/\r?\n/);
757
+ const lines = readFileSync3(path, "utf8").split(/\r?\n/);
700
758
  if (lines.at(-1) === "") {
701
759
  lines.pop();
702
760
  }
@@ -713,12 +771,12 @@ function readFirstLines(path, lineLimit) {
713
771
  }
714
772
  }
715
773
  function readPackageDependencies(target) {
716
- const packageJsonPath = join5(target, "package.json");
717
- if (!existsSync3(packageJsonPath)) {
774
+ const packageJsonPath = join6(target, "package.json");
775
+ if (!existsSync4(packageJsonPath)) {
718
776
  return /* @__PURE__ */ new Map();
719
777
  }
720
778
  try {
721
- const packageJson = JSON.parse(readFileSync2(packageJsonPath, "utf8"));
779
+ const packageJson = JSON.parse(readFileSync3(packageJsonPath, "utf8"));
722
780
  return new Map([
723
781
  ...Object.entries(packageJson.dependencies ?? {}),
724
782
  ...Object.entries(packageJson.devDependencies ?? {}),
@@ -1054,16 +1112,16 @@ function scoreFrameworkConfidence(input) {
1054
1112
  return input.configCount > 0 || input.packageCount > 0 ? "MEDIUM" : "LOW";
1055
1113
  }
1056
1114
  function readReadmeInfo(target) {
1057
- const readmePath = join5(target, "README.md");
1058
- const hasContributing = existsSync3(join5(target, "CONTRIBUTING.md"));
1059
- if (!existsSync3(readmePath)) {
1115
+ const readmePath = join6(target, "README.md");
1116
+ const hasContributing = existsSync4(join6(target, "CONTRIBUTING.md"));
1117
+ if (!existsSync4(readmePath)) {
1060
1118
  return {
1061
1119
  quality: "missing",
1062
1120
  line_count: 0,
1063
1121
  has_contributing: hasContributing
1064
1122
  };
1065
1123
  }
1066
- const readme = readFileSync2(readmePath, "utf8");
1124
+ const readme = readFileSync3(readmePath, "utf8");
1067
1125
  const wordCount = readme.trim().split(/\s+/).filter(Boolean).length;
1068
1126
  return {
1069
1127
  quality: wordCount >= 200 ? "ok" : "stub",
@@ -1529,10 +1587,10 @@ function buildSkillRecommendations(frameworkKind, topology, readme, projectRoot)
1529
1587
  );
1530
1588
  }
1531
1589
  function readProjectName(target) {
1532
- const packageJsonPath = join5(target, "package.json");
1533
- if (existsSync3(packageJsonPath)) {
1590
+ const packageJsonPath = join6(target, "package.json");
1591
+ if (existsSync4(packageJsonPath)) {
1534
1592
  try {
1535
- const packageJson = JSON.parse(readFileSync2(packageJsonPath, "utf8"));
1593
+ const packageJson = JSON.parse(readFileSync3(packageJsonPath, "utf8"));
1536
1594
  if (packageJson.name !== void 0 && packageJson.name.trim().length > 0) {
1537
1595
  return packageJson.name;
1538
1596
  }
@@ -1543,7 +1601,7 @@ function readProjectName(target) {
1543
1601
  return basename(target);
1544
1602
  }
1545
1603
  function getCliVersion() {
1546
- return true ? "2.2.0-rc.1" : "unknown";
1604
+ return true ? "2.2.0-rc.4" : "unknown";
1547
1605
  }
1548
1606
  function sortRecord(record) {
1549
1607
  return Object.fromEntries(Object.entries(record).sort(([left], [right]) => left.localeCompare(right)));
@@ -1553,7 +1611,7 @@ function toPosixPath(path) {
1553
1611
  }
1554
1612
 
1555
1613
  // src/commands/install.ts
1556
- var LOCAL_FABRIC_SERVER_PATH = join6("node_modules", "@fenglimg", "fabric-server", "dist", "index.js");
1614
+ var LOCAL_FABRIC_SERVER_PATH = join7("node_modules", "@fenglimg", "fabric-server", "dist", "index.js");
1557
1615
  var FABRIC_SERVER_PACKAGE = "@fenglimg/fabric-server";
1558
1616
  var INIT_WIZARD_GROUP_CANCELLED = /* @__PURE__ */ Symbol("init-wizard-group-cancelled");
1559
1617
  var installCommand = defineCommand({
@@ -1581,24 +1639,23 @@ var installCommand = defineCommand({
1581
1639
  description: t("cli.install.args.yes.description"),
1582
1640
  default: false
1583
1641
  },
1584
- "force-skills-only": {
1642
+ global: {
1585
1643
  type: "boolean",
1586
- description: t("cli.install.args.force-skills-only.description"),
1644
+ description: "Set up global Fabric (~/.fabric: uid + personal store + config)",
1587
1645
  default: false
1588
1646
  },
1589
- "force-hooks-only": {
1590
- type: "boolean",
1591
- description: t("cli.install.args.force-hooks-only.description"),
1592
- default: false
1647
+ url: {
1648
+ type: "string",
1649
+ description: "Clone + mount a shared store remote. In a project install: also binds it to this project and sets it as the write target. With --global: mounts it machine-wide only."
1593
1650
  },
1594
- global: {
1651
+ "enable-embed": {
1595
1652
  type: "boolean",
1596
- description: "Set up global Fabric (~/.fabric: uid + personal store + config)",
1653
+ description: t("cli.install.args.enable-embed.description"),
1597
1654
  default: false
1598
1655
  },
1599
- url: {
1656
+ "embed-model": {
1600
1657
  type: "string",
1601
- description: "With --global: clone + mount this shared store remote"
1658
+ description: t("cli.install.args.embed-model.description")
1602
1659
  }
1603
1660
  },
1604
1661
  async run({ args }) {
@@ -1606,83 +1663,6 @@ var installCommand = defineCommand({
1606
1663
  }
1607
1664
  });
1608
1665
  var install_default = installCommand;
1609
- async function runSkillsOnlyRefresh(targetInput) {
1610
- const target = normalizeTarget3(targetInput);
1611
- const metaPath = join6(target, ".fabric", "agents.meta.json");
1612
- if (!existsSync4(metaPath)) {
1613
- const message = t("cli.install.force-skills-only.uninitialised.message");
1614
- const hint = t("cli.install.force-skills-only.uninitialised.hint");
1615
- process.stderr.write(`${message}
1616
- ${hint}
1617
- `);
1618
- process.exitCode = 1;
1619
- return;
1620
- }
1621
- console.log(formatInitStageHeader(t("cli.install.force-skills-only.banner")));
1622
- const results = [];
1623
- results.push(...await cleanupDeprecatedSkills(target));
1624
- results.push(...await installFabricArchiveSkill(target));
1625
- results.push(...await installFabricReviewSkill(target));
1626
- results.push(...await installFabricImportSkill(target));
1627
- results.push(...await installFabricSyncSkill(target));
1628
- results.push(...await installFabricStoreSkill(target));
1629
- results.push(...await installFabricAuditSkill(target));
1630
- results.push(...await installFabricConnectSkill(target));
1631
- results.push(...await installSharedSkillLib(target));
1632
- let written = 0;
1633
- let skipped = 0;
1634
- let errors = 0;
1635
- for (const r of results) {
1636
- if (r.status === "written") written += 1;
1637
- else if (r.status === "skipped") skipped += 1;
1638
- else if (r.status === "error") errors += 1;
1639
- }
1640
- console.log(
1641
- t("cli.install.force-skills-only.summary", {
1642
- written: String(written),
1643
- skipped: String(skipped),
1644
- errors: String(errors)
1645
- })
1646
- );
1647
- if (errors > 0) {
1648
- for (const r of results) {
1649
- if (r.status === "error") {
1650
- process.stderr.write(` ${r.step} ${r.path}: ${r.message ?? "error"}
1651
- `);
1652
- }
1653
- }
1654
- process.exitCode = 1;
1655
- }
1656
- }
1657
- async function runHooksOnlyRefresh(targetInput) {
1658
- const target = normalizeTarget3(targetInput);
1659
- const metaPath = join6(target, ".fabric", "agents.meta.json");
1660
- if (!existsSync4(metaPath)) {
1661
- const message = t("cli.install.force-hooks-only.uninitialised.message");
1662
- const hint = t("cli.install.force-hooks-only.uninitialised.hint");
1663
- process.stderr.write(`${message}
1664
- ${hint}
1665
- `);
1666
- process.exitCode = 1;
1667
- return;
1668
- }
1669
- console.log(formatInitStageHeader(t("cli.install.force-hooks-only.banner")));
1670
- const result = await installHooks(target);
1671
- console.log(
1672
- t("cli.install.force-hooks-only.summary", {
1673
- written: String(result.installed.length),
1674
- skipped: String(result.skipped.length),
1675
- errors: String(result.errors.length)
1676
- })
1677
- );
1678
- if (result.errors.length > 0) {
1679
- for (const err of result.errors) {
1680
- process.stderr.write(` ${err}
1681
- `);
1682
- }
1683
- process.exitCode = 1;
1684
- }
1685
- }
1686
1666
  async function runInitCommand(args) {
1687
1667
  const logger = createDebugLogger(args.debug);
1688
1668
  if (args.global === true) {
@@ -1690,19 +1670,15 @@ async function runInitCommand(args) {
1690
1670
  return;
1691
1671
  }
1692
1672
  const resolution = resolveDevMode(args.target, process.cwd());
1693
- if (args["force-skills-only"] === true) {
1694
- await runSkillsOnlyRefresh(resolution.target);
1695
- return;
1696
- }
1697
- if (args["force-hooks-only"] === true) {
1698
- await runHooksOnlyRefresh(resolution.target);
1699
- return;
1700
- }
1701
1673
  const intent = resolveInitCliIntent(args, resolution.target);
1702
1674
  logger(`init target source: ${resolution.source}`);
1703
1675
  for (const step of resolution.chain) {
1704
1676
  logger(step);
1705
1677
  }
1678
+ if (loadGlobalConfig() === null) {
1679
+ logger("no global Fabric config found \u2014 minting ~/.fabric (uid + personal store)");
1680
+ await runGlobalInstall({});
1681
+ }
1706
1682
  const supports = detectClientSupports(intent.target);
1707
1683
  const basePlan = await buildInitExecutionPlan({
1708
1684
  target: intent.target,
@@ -1723,9 +1699,121 @@ async function runInitCommand(args) {
1723
1699
  console.log(t("cli.install.next-steps"));
1724
1700
  console.log("");
1725
1701
  console.log(paint.muted("More: docs/surfaces.md explains when to use CLI vs Skill vs MCP."));
1702
+ if (typeof args.url === "string" && args.url.length > 0) {
1703
+ bindRemoteStoreToProject(resolution.target, args.url);
1704
+ } else if (intent.wizardEnabled) {
1705
+ await promptStoreOnboarding(resolution.target);
1706
+ }
1707
+ const unboundStores = unboundAvailableStores(resolution.target);
1708
+ if (unboundStores.length > 0) {
1709
+ console.log("");
1710
+ console.log(
1711
+ t("cli.install.store-bind-nudge", {
1712
+ aliases: unboundStores.map((s) => `'${s.alias}'`).join(", "),
1713
+ first: unboundStores[0].alias
1714
+ })
1715
+ );
1716
+ }
1717
+ if (args["enable-embed"] === true) {
1718
+ enableSemanticSearchAndReport(resolution.target, args["embed-model"]);
1719
+ } else if (intent.wizardEnabled) {
1720
+ await promptSemanticSearch(resolution.target);
1721
+ }
1726
1722
  }
1727
1723
  return result;
1728
1724
  }
1725
+ function enableSemanticSearchAndReport(projectRoot, model) {
1726
+ const enabled = enableSemanticSearch(projectRoot, model === void 0 ? {} : { model });
1727
+ console.log("");
1728
+ if (enabled.alreadyEnabled) {
1729
+ console.log(paint.muted(`\u8BED\u4E49\u641C\u7D22\u5DF2\u662F\u542F\u7528\u72B6\u6001 (embed_model=${enabled.model})\uFF0C\u672A\u6539\u52A8 ${enabled.configPath}\u3002`));
1730
+ return;
1731
+ }
1732
+ for (const line of renderSemanticSearchInstructions(enabled.model)) {
1733
+ console.log(line);
1734
+ }
1735
+ }
1736
+ async function promptSemanticSearch(projectRoot) {
1737
+ const enable = await confirm({
1738
+ message: "Enable vector semantic search? (downloads an embedding model on first use)",
1739
+ initialValue: false
1740
+ });
1741
+ if (isCancel(enable) || !enable) {
1742
+ return;
1743
+ }
1744
+ enableSemanticSearchAndReport(projectRoot);
1745
+ }
1746
+ function bindRemoteStoreToProject(projectRoot, url, globalRoot = resolveGlobalRoot()) {
1747
+ const already = storeList(globalRoot).find((store) => store.remote === url);
1748
+ const mounted = already ?? mountStoreFromRemote(url, globalRoot);
1749
+ storeBind(projectRoot, { id: mounted.alias, suggested_remote: url });
1750
+ storeSwitchWrite(projectRoot, mounted.alias);
1751
+ regenerateBindingsSnapshot(projectRoot, { now: (/* @__PURE__ */ new Date()).toISOString(), globalRoot });
1752
+ console.log("");
1753
+ console.log(
1754
+ paint.success(
1755
+ `bound store '${mounted.alias}' to this project and set it as the write target.`
1756
+ )
1757
+ );
1758
+ }
1759
+ function bindCreatedStoreToProject(projectRoot, alias, options = {}) {
1760
+ const globalRoot = options.globalRoot ?? resolveGlobalRoot();
1761
+ storeCreate(alias, (/* @__PURE__ */ new Date()).toISOString(), {
1762
+ ...options.remote === void 0 ? {} : { remote: options.remote },
1763
+ globalRoot
1764
+ });
1765
+ storeBind(
1766
+ projectRoot,
1767
+ options.remote === void 0 ? { id: alias } : { id: alias, suggested_remote: options.remote }
1768
+ );
1769
+ storeSwitchWrite(projectRoot, alias);
1770
+ regenerateBindingsSnapshot(projectRoot, { now: (/* @__PURE__ */ new Date()).toISOString(), globalRoot });
1771
+ console.log("");
1772
+ console.log(
1773
+ paint.success(
1774
+ `created store '${alias}', bound it to this project, and set it as the write target.`
1775
+ )
1776
+ );
1777
+ }
1778
+ async function promptStoreOnboarding(projectRoot) {
1779
+ const config = loadProjectConfig(projectRoot);
1780
+ if (typeof config?.active_write_store === "string" && config.active_write_store.length > 0) {
1781
+ return;
1782
+ }
1783
+ const choice = await select({
1784
+ message: "Set up a team / shared knowledge store for this project?",
1785
+ initialValue: "skip",
1786
+ options: [
1787
+ { value: "skip", label: "skip", hint: "personal store only (default)" },
1788
+ { value: "join", label: "join existing", hint: "clone + bind a shared store from a git remote" },
1789
+ { value: "create", label: "create new", hint: "start a fresh local store (optionally remote-backed)" }
1790
+ ]
1791
+ });
1792
+ if (isCancel(choice) || choice === "skip") {
1793
+ return;
1794
+ }
1795
+ if (choice === "join") {
1796
+ const url = await text({
1797
+ message: "Shared store git remote (url):",
1798
+ placeholder: "git@github.com:org/knowledge.git"
1799
+ });
1800
+ if (isCancel(url) || typeof url !== "string" || url.length === 0) {
1801
+ return;
1802
+ }
1803
+ bindRemoteStoreToProject(projectRoot, url);
1804
+ return;
1805
+ }
1806
+ const alias = await text({ message: "Local alias for the new store:", initialValue: "team" });
1807
+ if (isCancel(alias) || typeof alias !== "string" || alias.length === 0) {
1808
+ return;
1809
+ }
1810
+ const remote = await text({
1811
+ message: "Git remote to back it (optional \u2014 leave blank to skip):",
1812
+ placeholder: "git@github.com:org/knowledge.git"
1813
+ });
1814
+ const remoteStr = !isCancel(remote) && typeof remote === "string" && remote.length > 0 ? remote : void 0;
1815
+ bindCreatedStoreToProject(projectRoot, alias, remoteStr === void 0 ? {} : { remote: remoteStr });
1816
+ }
1729
1817
  var FABRIC_GITIGNORE_CONTENT = [
1730
1818
  "# Fabric per-dev activity ledgers & caches \u2014 auto-generated, not shared.",
1731
1819
  "# Managed by `fabric install`; edit freely (re-install never overwrites this).",
@@ -1739,14 +1827,14 @@ var FABRIC_GITIGNORE_CONTENT = [
1739
1827
  ""
1740
1828
  ].join("\n");
1741
1829
  function writeDefaultGitignore(fabricDir) {
1742
- const target = join6(fabricDir, ".gitignore");
1743
- if (existsSync4(target)) return;
1830
+ const target = join7(fabricDir, ".gitignore");
1831
+ if (existsSync5(target)) return;
1744
1832
  mkdirSync2(fabricDir, { recursive: true });
1745
- writeFileSync(target, FABRIC_GITIGNORE_CONTENT, "utf8");
1833
+ writeFileSync2(target, FABRIC_GITIGNORE_CONTENT, "utf8");
1746
1834
  }
1747
1835
  function writeDefaultFabricConfig(fabricDir, targetRoot) {
1748
- const target = join6(fabricDir, "fabric-config.json");
1749
- if (existsSync4(target)) return;
1836
+ const target = join7(fabricDir, "fabric-config.json");
1837
+ if (existsSync5(target)) return;
1750
1838
  const detectedLanguage = detectExistingLanguage(targetRoot);
1751
1839
  const FABRIC_CONFIG_DEFAULTS = {
1752
1840
  // Scan/import language policy. Fixated at init time by probing
@@ -1808,7 +1896,7 @@ function writeDefaultFabricConfig(fabricDir, targetRoot) {
1808
1896
  review_stale_pending_days: 14
1809
1897
  };
1810
1898
  mkdirSync2(fabricDir, { recursive: true });
1811
- writeFileSync(target, JSON.stringify(FABRIC_CONFIG_DEFAULTS, null, 2) + "\n", "utf8");
1899
+ writeFileSync2(target, JSON.stringify(FABRIC_CONFIG_DEFAULTS, null, 2) + "\n", "utf8");
1812
1900
  log.info(
1813
1901
  `Detected and fixated fabric_language = ${detectedLanguage}; edit ${target} to override.`
1814
1902
  );
@@ -1871,7 +1959,6 @@ async function executeInitExecutionPlan(plan) {
1871
1959
  printInitPlanSummary(plan.target, plan.options, plan.mcpInstallMode, plan.supports);
1872
1960
  }
1873
1961
  const scaffoldStates = [
1874
- { path: plan.scaffold.metaPath, state: plan.scaffold.metaState },
1875
1962
  { path: plan.scaffold.eventsPath, state: plan.scaffold.eventsState },
1876
1963
  { path: plan.scaffold.forensicPath, state: plan.scaffold.forensicState }
1877
1964
  ];
@@ -1885,7 +1972,7 @@ async function executeInitExecutionPlan(plan) {
1885
1972
  finalSupports: plan.supports
1886
1973
  };
1887
1974
  }
1888
- if (existsSync4(plan.scaffold.fabricDir) && !statSync4(plan.scaffold.fabricDir).isDirectory()) {
1975
+ if (existsSync5(plan.scaffold.fabricDir) && !statSync4(plan.scaffold.fabricDir).isDirectory()) {
1889
1976
  throw new Error(
1890
1977
  t("cli.install.diff.drift-abort", { path: plan.scaffold.fabricDir })
1891
1978
  );
@@ -1932,26 +2019,16 @@ async function executeInitExecutionPlan(plan) {
1932
2019
  finalSupports
1933
2020
  };
1934
2021
  }
1935
- var KNOWLEDGE_SUBDIRS = ["decisions", "pitfalls", "guidelines", "models", "processes", "pending"];
1936
- function resolvePersonalFabricRoot() {
1937
- return process.env.FABRIC_HOME ?? homedir();
1938
- }
1939
2022
  async function buildInitFabricPlan(target, options) {
1940
2023
  assertExistingDirectory3(target);
1941
- const fabricDir = join6(target, ".fabric");
1942
- const agentsMdPath = join6(target, "AGENTS.md");
1943
- const agentsMdAction = existsSync4(agentsMdPath) ? "preserved" : "created";
1944
- const knowledgeDir = join6(fabricDir, "knowledge");
1945
- const personalKnowledgeDir = join6(resolvePersonalFabricRoot(), ".fabric", "knowledge");
1946
- const forensicPath = join6(fabricDir, "forensic.json");
1947
- const eventsPath = join6(fabricDir, "events.jsonl");
1948
- const metaPath = join6(fabricDir, "agents.meta.json");
2024
+ const fabricDir = join7(target, ".fabric");
2025
+ const agentsMdPath = join7(target, "AGENTS.md");
2026
+ const agentsMdAction = existsSync5(agentsMdPath) ? "preserved" : "created";
2027
+ const forensicPath = join7(fabricDir, "forensic.json");
2028
+ const eventsPath = join7(fabricDir, "events.jsonl");
1949
2029
  const replaceFabricDir = shouldReplaceWritableDirectory(fabricDir, options);
1950
- const knowledgeDirAction = existsSync4(knowledgeDir) ? "overwritten" : "created";
1951
- const metaClassification = classifyFreshPath(metaPath, "structural");
1952
2030
  const eventsClassification = classifyFreshPath(eventsPath, "presence");
1953
2031
  const forensicClassification = classifyFreshPath(forensicPath, "always-rewrite");
1954
- const metaAction = diffStateToWriteAction(metaClassification.state);
1955
2032
  const eventsAction = diffStateToWriteAction(eventsClassification.state);
1956
2033
  const forensicAction = diffStateToWriteAction(forensicClassification.state);
1957
2034
  const showScanProgress = process.stderr.isTTY === true;
@@ -1964,7 +2041,6 @@ async function buildInitFabricPlan(target, options) {
1964
2041
  process.stderr.write(`${t("cli.install.scan-complete")}
1965
2042
  `);
1966
2043
  }
1967
- const meta = createInitialMeta();
1968
2044
  return {
1969
2045
  target,
1970
2046
  options,
@@ -1972,18 +2048,11 @@ async function buildInitFabricPlan(target, options) {
1972
2048
  replaceFabricDir,
1973
2049
  agentsMdPath,
1974
2050
  agentsMdAction,
1975
- knowledgeDir,
1976
- knowledgeDirAction,
1977
- personalKnowledgeDir,
1978
- metaPath,
1979
- metaAction,
1980
- meta,
1981
2051
  eventsPath,
1982
2052
  eventsAction,
1983
2053
  forensicPath,
1984
2054
  forensicAction,
1985
2055
  forensicReport,
1986
- metaState: metaClassification.state,
1987
2056
  eventsState: eventsClassification.state,
1988
2057
  forensicState: forensicClassification.state
1989
2058
  };
@@ -1995,39 +2064,18 @@ async function executeInitFabricPlan(plan) {
1995
2064
  mkdirSync2(plan.fabricDir, { recursive: true });
1996
2065
  writeDefaultFabricConfig(plan.fabricDir, plan.target);
1997
2066
  writeDefaultGitignore(plan.fabricDir);
1998
- mkdirSync2(plan.knowledgeDir, { recursive: true });
1999
- for (const sub of KNOWLEDGE_SUBDIRS) {
2000
- const teamSubDir = join6(plan.knowledgeDir, sub);
2001
- mkdirSync2(teamSubDir, { recursive: true });
2002
- const teamGitkeep = join6(teamSubDir, ".gitkeep");
2003
- if (!existsSync4(teamGitkeep)) {
2004
- writeFileSync(teamGitkeep, "", "utf8");
2005
- }
2006
- }
2007
- try {
2008
- mkdirSync2(plan.personalKnowledgeDir, { recursive: true });
2009
- for (const sub of KNOWLEDGE_SUBDIRS) {
2010
- mkdirSync2(join6(plan.personalKnowledgeDir, sub), { recursive: true });
2011
- }
2012
- } catch {
2013
- }
2014
- if (plan.metaState === "missing") {
2015
- preparePlannedPath(plan.metaPath, plan.metaAction);
2016
- await atomicWriteJson(plan.metaPath, plan.meta);
2017
- }
2018
2067
  if (plan.eventsState === "missing") {
2019
2068
  preparePlannedPath(plan.eventsPath, plan.eventsAction);
2020
2069
  mkdirSync2(dirname(plan.eventsPath), { recursive: true });
2021
- writeFileSync(plan.eventsPath, "", "utf8");
2070
+ writeFileSync2(plan.eventsPath, "", "utf8");
2022
2071
  }
2023
2072
  preparePlannedPath(plan.forensicPath, plan.forensicAction);
2024
2073
  await atomicWriteJson(plan.forensicPath, plan.forensicReport);
2025
- if (existsSync4(plan.eventsPath)) {
2074
+ if (existsSync5(plan.eventsPath)) {
2026
2075
  const applied = [];
2027
2076
  const canonical = [];
2028
2077
  const drifted = [];
2029
2078
  for (const entry of [
2030
- { path: plan.metaPath, state: plan.metaState },
2031
2079
  { path: plan.eventsPath, state: plan.eventsState },
2032
2080
  { path: plan.forensicPath, state: plan.forensicState }
2033
2081
  ]) {
@@ -2044,11 +2092,6 @@ async function executeInitFabricPlan(plan) {
2044
2092
  return {
2045
2093
  agentsMdPath: plan.agentsMdPath,
2046
2094
  agentsMdAction: plan.agentsMdAction,
2047
- knowledgeDir: plan.knowledgeDir,
2048
- knowledgeDirAction: plan.knowledgeDirAction,
2049
- personalKnowledgeDir: plan.personalKnowledgeDir,
2050
- metaPath: plan.metaPath,
2051
- metaAction: plan.metaAction,
2052
2095
  eventsPath: plan.eventsPath,
2053
2096
  eventsAction: plan.eventsAction,
2054
2097
  forensicPath: plan.forensicPath,
@@ -2098,8 +2141,6 @@ function exhaustiveInitStagePlan(value) {
2098
2141
  }
2099
2142
  function printInitScaffoldResult(created) {
2100
2143
  console.log(formatAgentsMdAction(created.agentsMdPath, created.agentsMdAction));
2101
- console.log(formatInitPathAction(created.knowledgeDir, created.knowledgeDirAction));
2102
- console.log(formatInitPathAction(created.metaPath, created.metaAction));
2103
2144
  console.log(formatInitPathAction(created.eventsPath, created.eventsAction));
2104
2145
  console.log(formatInitPathAction(created.forensicPath, created.forensicAction));
2105
2146
  }
@@ -2146,11 +2187,6 @@ function buildPlanOnlyScaffoldResult(plan) {
2146
2187
  return {
2147
2188
  agentsMdPath: plan.agentsMdPath,
2148
2189
  agentsMdAction: plan.agentsMdAction,
2149
- knowledgeDir: plan.knowledgeDir,
2150
- knowledgeDirAction: plan.knowledgeDirAction,
2151
- personalKnowledgeDir: plan.personalKnowledgeDir,
2152
- metaPath: plan.metaPath,
2153
- metaAction: plan.metaAction,
2154
2190
  eventsPath: plan.eventsPath,
2155
2191
  eventsAction: plan.eventsAction,
2156
2192
  forensicPath: plan.forensicPath,
@@ -2183,6 +2219,8 @@ async function executeInitStagePlan(plan, stageName) {
2183
2219
  installResults.push(...await runBestEffort("hook-broad-script", () => installKnowledgeHintBroadHook(plan.target)));
2184
2220
  installResults.push(...await runBestEffort("hook-narrow-script", () => installKnowledgeHintNarrowHook(plan.target)));
2185
2221
  installResults.push(...await runBestEffort("hook-cite-policy-evict-script", () => installCitePolicyEvictHook(plan.target)));
2222
+ installResults.push(...await runBestEffort("hook-session-end-script", () => installSessionEndMarkerHook(plan.target)));
2223
+ installResults.push(...await runBestEffort("hook-post-tooluse-script", () => installPostTooluseMutationHook(plan.target)));
2186
2224
  installResults.push(...await runBestEffort("hook-lib", () => installHookLibs(plan.target)));
2187
2225
  installResults.push(await runBestEffortSingle("claude-hook-config", () => mergeClaudeCodeHookConfig(plan.target)));
2188
2226
  installResults.push(await runBestEffortSingle("codex-hook-config", () => mergeCodexHookConfig(plan.target)));
@@ -2238,7 +2276,7 @@ async function executeInitStagePlan(plan, stageName) {
2238
2276
  }
2239
2277
  }
2240
2278
  function shouldReplaceWritableDirectory(path, _options) {
2241
- if (!existsSync4(path)) {
2279
+ if (!existsSync5(path)) {
2242
2280
  return false;
2243
2281
  }
2244
2282
  if (statSync4(path).isDirectory()) {
@@ -2246,8 +2284,8 @@ function shouldReplaceWritableDirectory(path, _options) {
2246
2284
  }
2247
2285
  return false;
2248
2286
  }
2249
- function classifyFreshPath(path, strategy) {
2250
- if (!existsSync4(path)) {
2287
+ function classifyFreshPath(path, _strategy) {
2288
+ if (!existsSync5(path)) {
2251
2289
  return { path, state: "missing" };
2252
2290
  }
2253
2291
  let stat;
@@ -2263,30 +2301,7 @@ function classifyFreshPath(path, strategy) {
2263
2301
  if (!stat.isFile()) {
2264
2302
  return { path, state: "user-modified", reason: "expected a file" };
2265
2303
  }
2266
- if (strategy === "presence" || strategy === "always-rewrite") {
2267
- return { path, state: "present-canonical" };
2268
- }
2269
- try {
2270
- const raw = readFileSync3(path, "utf8");
2271
- const parsed = JSON.parse(raw);
2272
- if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
2273
- return { path, state: "user-modified", reason: "not a JSON object" };
2274
- }
2275
- const record = parsed;
2276
- const hasRevision = typeof record["revision"] === "string";
2277
- const hasNodes = record["nodes"] !== void 0 && record["nodes"] !== null && typeof record["nodes"] === "object" && !Array.isArray(record["nodes"]);
2278
- const hasCounters = record["counters"] !== void 0 && record["counters"] !== null && typeof record["counters"] === "object" && !Array.isArray(record["counters"]);
2279
- if (!hasRevision || !hasNodes || !hasCounters) {
2280
- return { path, state: "drifted", reason: "missing required AgentsMeta fields" };
2281
- }
2282
- return { path, state: "present-canonical" };
2283
- } catch (error) {
2284
- return {
2285
- path,
2286
- state: "user-modified",
2287
- reason: error instanceof Error ? error.message : String(error)
2288
- };
2289
- }
2304
+ return { path, state: "present-canonical" };
2290
2305
  }
2291
2306
  function diffStateToWriteAction(_state) {
2292
2307
  return "created";
@@ -2296,7 +2311,7 @@ function formatDiffFileState(state) {
2296
2311
  }
2297
2312
  function preparePlannedPath(path, action) {
2298
2313
  mkdirSync2(dirname(path), { recursive: true });
2299
- if (action === "overwritten" && existsSync4(path)) {
2314
+ if (action === "overwritten" && existsSync5(path)) {
2300
2315
  rmSync2(path, { recursive: true, force: true });
2301
2316
  }
2302
2317
  }
@@ -2450,19 +2465,19 @@ function normalizeTarget3(targetInput) {
2450
2465
  return isAbsolute3(targetInput) ? targetInput : resolve3(process.cwd(), targetInput);
2451
2466
  }
2452
2467
  function assertExistingDirectory3(target) {
2453
- if (!existsSync4(target) || !statSync4(target).isDirectory()) {
2468
+ if (!existsSync5(target) || !statSync4(target).isDirectory()) {
2454
2469
  throw new Error(`Target must be an existing directory: ${target}`);
2455
2470
  }
2456
2471
  }
2457
2472
  function detectPackageManager(cwd) {
2458
2473
  const workspaceRoot = resolve3(cwd);
2459
- if (existsSync4(join6(workspaceRoot, "pnpm-lock.yaml"))) {
2474
+ if (existsSync5(join7(workspaceRoot, "pnpm-lock.yaml"))) {
2460
2475
  return "pnpm";
2461
2476
  }
2462
- if (existsSync4(join6(workspaceRoot, "yarn.lock"))) {
2477
+ if (existsSync5(join7(workspaceRoot, "yarn.lock"))) {
2463
2478
  return "yarn";
2464
2479
  }
2465
- if (existsSync4(join6(workspaceRoot, "package-lock.json"))) {
2480
+ if (existsSync5(join7(workspaceRoot, "package-lock.json"))) {
2466
2481
  return "npm";
2467
2482
  }
2468
2483
  return "npm";
@@ -2475,13 +2490,6 @@ function installLocalFabricServer(target, manager) {
2475
2490
  shell: process.platform === "win32"
2476
2491
  });
2477
2492
  }
2478
- function createInitialMeta() {
2479
- return {
2480
- revision: "sha256:initial",
2481
- nodes: {},
2482
- counters: defaultAgentsMetaCounters()
2483
- };
2484
- }
2485
2493
  function appendInstallDiffLedgerEvent(eventsPath, payload) {
2486
2494
  const event = {
2487
2495
  kind: "fabric-event",
@@ -2573,8 +2581,6 @@ function printInitPlanSummary(target, options, mcpInstallMode, supports) {
2573
2581
  })
2574
2582
  );
2575
2583
  console.log(t("cli.install.plan.writes"));
2576
- console.log(` - ${target}/.fabric/knowledge/{decisions,pitfalls,guidelines,models,processes,pending}/`);
2577
- console.log(` - ${target}/.fabric/agents.meta.json`);
2578
2584
  console.log(` - ${target}/.fabric/events.jsonl`);
2579
2585
  console.log(` - ${target}/.fabric/forensic.json`);
2580
2586
  console.log(` - ${target}/.fabric/fabric-config.json`);
@@ -2720,6 +2726,8 @@ function writeStderr(message) {
2720
2726
  `);
2721
2727
  }
2722
2728
  export {
2729
+ bindCreatedStoreToProject,
2730
+ bindRemoteStoreToProject,
2723
2731
  buildInitExecutionPlan,
2724
2732
  buildInitFabricPlan,
2725
2733
  createDefaultInitWizardAdapter,
@@ -2730,8 +2738,6 @@ export {
2730
2738
  initFabric,
2731
2739
  installCommand,
2732
2740
  resolveInitExecutionPlanWithWizard,
2733
- runHooksOnlyRefresh,
2734
2741
  runInitCommand,
2735
- runSkillsOnlyRefresh,
2736
2742
  shouldUseInitWizard
2737
2743
  };