@fenglimg/fabric-cli 2.0.1 → 2.2.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 (40) hide show
  1. package/dist/{chunk-PWLW3B57.js → chunk-2CY4BMTH.js} +5 -1
  2. package/dist/{chunk-D25XJ4BC.js → chunk-2R55HNVD.js} +105 -5
  3. package/dist/chunk-4R2CYEA4.js +116 -0
  4. package/dist/{chunk-BATF4PEJ.js → chunk-AOE6AYI7.js} +2 -2
  5. package/dist/{chunk-WWNXR34K.js → chunk-BO4XIZWZ.js} +8 -1
  6. package/dist/chunk-L4Q55UC4.js +52 -0
  7. package/dist/chunk-LFIKMVY7.js +27 -0
  8. package/dist/chunk-RYAFBNES.js +33 -0
  9. package/dist/chunk-T5RPGCCM.js +40 -0
  10. package/dist/chunk-WU6GAPKH.js +36 -0
  11. package/dist/{chunk-MF3OTILQ.js → chunk-XC5RUHLK.js} +29 -8
  12. package/dist/{config-XJIPZNUP.js → config-XYRBZJDU.js} +3 -3
  13. package/dist/{doctor-EJDSEJSS.js → doctor-YONYXDX6.js} +147 -24
  14. package/dist/index.js +58 -10
  15. package/dist/{install-EKWMFLUU.js → install-74ANPCCP.js} +320 -75
  16. package/dist/{metrics-ACEQFPDU.js → metrics-RER6NLFC.js} +22 -9
  17. package/dist/{onboard-coverage-MFCAEBDO.js → onboard-coverage-JWQWDZW7.js} +1 -1
  18. package/dist/scope-explain-CDIZESP5.js +37 -0
  19. package/dist/status-GLQWLWH6.js +23 -0
  20. package/dist/store-XB3ADT65.js +144 -0
  21. package/dist/sync-UJ4BBCZJ.js +251 -0
  22. package/dist/{uninstall-MH7ZIB6M.js → uninstall-C3QXKOO6.js} +47 -7
  23. package/dist/whoami-2MLO4Y37.js +36 -0
  24. package/package.json +3 -3
  25. package/templates/hooks/fabric-hint.cjs +139 -7
  26. package/templates/hooks/knowledge-hint-broad.cjs +204 -9
  27. package/templates/hooks/knowledge-hint-narrow.cjs +49 -4
  28. package/templates/hooks/lib/bindings-snapshot-reader.cjs +81 -0
  29. package/templates/hooks/lib/cite-contract-reminder.cjs +15 -9
  30. package/templates/hooks/lib/cite-line-parser.cjs +48 -26
  31. package/templates/hooks/lib/injection-log.cjs +91 -0
  32. package/templates/hooks/lib/state-store.cjs +30 -11
  33. package/templates/skills/fabric-archive/SKILL.md +4 -0
  34. package/templates/skills/fabric-audit/SKILL.md +53 -0
  35. package/templates/skills/fabric-connect/SKILL.md +48 -0
  36. package/templates/skills/fabric-import/SKILL.md +4 -0
  37. package/templates/skills/fabric-review/SKILL.md +6 -0
  38. package/templates/skills/fabric-review/ref/cite-contract.md +56 -0
  39. package/templates/skills/fabric-store/SKILL.md +44 -0
  40. package/templates/skills/fabric-sync/SKILL.md +46 -0
@@ -1,14 +1,15 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- installMcpClients
4
- } from "./chunk-BATF4PEJ.js";
5
2
  import {
6
3
  cleanupDeprecatedSkills,
7
4
  installArchiveHintHook,
8
5
  installCitePolicyEvictHook,
9
6
  installFabricArchiveSkill,
7
+ installFabricAuditSkill,
8
+ installFabricConnectSkill,
10
9
  installFabricImportSkill,
11
10
  installFabricReviewSkill,
11
+ installFabricStoreSkill,
12
+ installFabricSyncSkill,
12
13
  installHookLibs,
13
14
  installKnowledgeHintBroadHook,
14
15
  installKnowledgeHintNarrowHook,
@@ -21,29 +22,39 @@ import {
21
22
  writeCodexBootstrapManagedBlock,
22
23
  writeCursorBootstrapManagedBlock,
23
24
  writeFabricAgentsSnapshot
24
- } from "./chunk-D25XJ4BC.js";
25
- import {
26
- detectClientSupports
27
- } from "./chunk-MF3OTILQ.js";
25
+ } from "./chunk-2R55HNVD.js";
28
26
  import {
29
27
  displayWidth,
30
28
  padEnd,
31
29
  paint
32
- } from "./chunk-WWNXR34K.js";
30
+ } from "./chunk-BO4XIZWZ.js";
33
31
  import {
34
32
  createDebugLogger,
35
33
  resolveDevMode
36
34
  } from "./chunk-COI5VDFU.js";
37
35
  import {
36
+ installMcpClients
37
+ } from "./chunk-AOE6AYI7.js";
38
+ import {
39
+ detectClientSupports
40
+ } from "./chunk-XC5RUHLK.js";
41
+ import {
42
+ getProjectTranslator,
38
43
  t
39
- } from "./chunk-PWLW3B57.js";
44
+ } from "./chunk-2CY4BMTH.js";
45
+ import {
46
+ globalConfigPath,
47
+ loadGlobalConfig,
48
+ resolveGlobalRoot,
49
+ saveGlobalConfig
50
+ } from "./chunk-RYAFBNES.js";
40
51
 
41
52
  // src/commands/install.ts
42
- import { randomUUID } from "crypto";
53
+ import { randomUUID as randomUUID2 } from "crypto";
43
54
  import { homedir } from "os";
44
55
  import * as childProcess from "child_process";
45
- import { appendFileSync, existsSync as existsSync4, mkdirSync, readFileSync as readFileSync3, rmSync, statSync as statSync4, writeFileSync } from "fs";
46
- import { dirname, isAbsolute as isAbsolute3, join as join4, resolve as resolve3 } from "path";
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";
47
58
  import { cancel, confirm, group, intro, isCancel, log, note, outro, select } from "@clack/prompts";
48
59
  import { defaultAgentsMetaCounters } from "@fenglimg/fabric-shared";
49
60
  import { atomicWriteJson } from "@fenglimg/fabric-shared/node/atomic-write";
@@ -59,6 +70,10 @@ async function installHooks(target, _options = {}) {
59
70
  results.push(...await runStep(() => installFabricArchiveSkill(normalizedTarget)));
60
71
  results.push(...await runStep(() => installFabricReviewSkill(normalizedTarget)));
61
72
  results.push(...await runStep(() => installFabricImportSkill(normalizedTarget)));
73
+ results.push(...await runStep(() => installFabricSyncSkill(normalizedTarget)));
74
+ results.push(...await runStep(() => installFabricStoreSkill(normalizedTarget)));
75
+ results.push(...await runStep(() => installFabricAuditSkill(normalizedTarget)));
76
+ results.push(...await runStep(() => installFabricConnectSkill(normalizedTarget)));
62
77
  results.push(...await runStep(() => installSharedSkillLib(normalizedTarget)));
63
78
  results.push(...await runStep(() => installArchiveHintHook(normalizedTarget)));
64
79
  results.push(...await runStep(() => installKnowledgeHintBroadHook(normalizedTarget)));
@@ -182,20 +197,209 @@ function assertExistingDirectory(target) {
182
197
  }
183
198
  }
184
199
 
200
+ // src/install/run-global-install.ts
201
+ import { execFileSync as execFileSync2 } from "child_process";
202
+ import { randomUUID } from "crypto";
203
+ import { mkdirSync, mkdtempSync, renameSync } from "fs";
204
+ import { tmpdir } from "os";
205
+ import { join as join3 } from "path";
206
+ import { STORES_ROOT_DIR as STORES_ROOT_DIR2, addMountedStore, readStoreIdentity } from "@fenglimg/fabric-shared";
207
+ import { GenericIOError } from "@fenglimg/fabric-shared/errors";
208
+
209
+ // src/store/uid.ts
210
+ import { execFileSync } from "child_process";
211
+ import { createHash } from "crypto";
212
+ function deriveUid(opts = {}) {
213
+ let email = "";
214
+ try {
215
+ email = execFileSync("git", ["config", "user.email"], {
216
+ encoding: "utf8",
217
+ stdio: ["ignore", "pipe", "ignore"]
218
+ }).trim();
219
+ } catch {
220
+ email = "";
221
+ }
222
+ if (email === "") {
223
+ return "u-anon";
224
+ }
225
+ const material = opts.salt !== void 0 && opts.salt.length > 0 ? `${opts.salt}:${email.toLowerCase()}` : email.toLowerCase();
226
+ const hash = createHash("sha256").update(material).digest("hex").slice(0, 12);
227
+ return `u-${hash}`;
228
+ }
229
+
230
+ // src/install/install-global.ts
231
+ import { rmSync } from "fs";
232
+ import { join as join2 } from "path";
233
+ import {
234
+ STORES_ROOT_DIR,
235
+ globalConfigSchema,
236
+ initStore
237
+ } from "@fenglimg/fabric-shared";
238
+
239
+ // src/install/transaction.ts
240
+ function errorMessage(error) {
241
+ return error instanceof Error ? error.message : String(error);
242
+ }
243
+ async function runInstallTransaction(steps) {
244
+ const receipt = { ok: true, steps: [] };
245
+ const applied = [];
246
+ for (let i = 0; i < steps.length; i++) {
247
+ const step = steps[i];
248
+ try {
249
+ await step.apply();
250
+ applied.push(step);
251
+ receipt.steps.push({ name: step.name, status: "applied" });
252
+ } catch (error) {
253
+ receipt.ok = false;
254
+ receipt.failedStep = step.name;
255
+ receipt.error = errorMessage(error);
256
+ receipt.steps.push({ name: step.name, status: "failed", error: errorMessage(error) });
257
+ for (let j = i + 1; j < steps.length; j++) {
258
+ receipt.steps.push({ name: steps[j].name, status: "skipped" });
259
+ }
260
+ for (const done of [...applied].reverse()) {
261
+ const entry = receipt.steps.find((s) => s.name === done.name);
262
+ try {
263
+ await done.rollback();
264
+ if (entry !== void 0) {
265
+ entry.status = "rolled_back";
266
+ }
267
+ } catch (rollbackError) {
268
+ if (entry !== void 0) {
269
+ entry.status = "rollback_failed";
270
+ entry.error = errorMessage(rollbackError);
271
+ }
272
+ }
273
+ }
274
+ return receipt;
275
+ }
276
+ }
277
+ return receipt;
278
+ }
279
+
280
+ // src/install/install-global.ts
281
+ async function installGlobalCore(options) {
282
+ const existing = loadGlobalConfig(options.globalRoot);
283
+ if (existing !== null) {
284
+ return {
285
+ receipt: { ok: true, steps: [{ name: "already-installed", status: "applied" }] },
286
+ config: existing,
287
+ alreadyInstalled: true
288
+ };
289
+ }
290
+ const alias = options.personalAlias ?? "personal";
291
+ const personalDir = join2(options.globalRoot, STORES_ROOT_DIR, options.personalStoreUuid);
292
+ let config = null;
293
+ const receipt = await runInstallTransaction([
294
+ {
295
+ name: "init-personal-store",
296
+ apply: () => {
297
+ initStore(
298
+ personalDir,
299
+ {
300
+ store_uuid: options.personalStoreUuid,
301
+ created_at: options.now,
302
+ canonical_alias: alias
303
+ },
304
+ { git: options.git }
305
+ );
306
+ },
307
+ rollback: () => {
308
+ rmSync(personalDir, { recursive: true, force: true });
309
+ }
310
+ },
311
+ {
312
+ name: "write-global-config",
313
+ apply: () => {
314
+ const next = globalConfigSchema.parse({
315
+ uid: options.uid,
316
+ stores: [{ store_uuid: options.personalStoreUuid, alias, personal: true }]
317
+ });
318
+ saveGlobalConfig(next, options.globalRoot);
319
+ config = next;
320
+ },
321
+ rollback: () => {
322
+ rmSync(globalConfigPath(options.globalRoot), { force: true });
323
+ }
324
+ }
325
+ ]);
326
+ return { receipt, config, alreadyInstalled: false };
327
+ }
328
+
329
+ // src/install/run-global-install.ts
330
+ function gitClone(url, dest) {
331
+ console.log(`cloning store from ${url} (this may take a while)\u2026`);
332
+ try {
333
+ execFileSync2("git", ["clone", "--", url, dest], { stdio: ["ignore", "ignore", "inherit"] });
334
+ } catch (error) {
335
+ throw new GenericIOError(`git clone of ${url} failed`, {
336
+ actionHint: "check the url is reachable and points to a Fabric store git repo (the git error above shows the cause), then re-run `fabric install --global <url>`",
337
+ details: error
338
+ });
339
+ }
340
+ }
341
+ function mountStoreFromRemote(url, globalRoot) {
342
+ const storesRoot = join3(globalRoot, STORES_ROOT_DIR2);
343
+ mkdirSync(storesRoot, { recursive: true });
344
+ const tmp = mkdtempSync(join3(tmpdir(), "fabric-clone-"));
345
+ const cloneDest = join3(tmp, "store");
346
+ gitClone(url, cloneDest);
347
+ const identity = readStoreIdentity(cloneDest);
348
+ if (identity === null) {
349
+ throw new GenericIOError(`cloned store at ${url} has no valid store.json (not a Fabric store)`, {
350
+ 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
+ });
352
+ }
353
+ const finalDir = join3(storesRoot, identity.store_uuid);
354
+ renameSync(cloneDest, finalDir);
355
+ const config = loadGlobalConfig(globalRoot);
356
+ if (config === null) {
357
+ throw new GenericIOError("global config missing after install", {
358
+ actionHint: "re-run `fabric install --global` to (re)create the global config, then retry mounting the store; if it persists, inspect ~/.fabric for a partial install"
359
+ });
360
+ }
361
+ const alias = identity.canonical_alias ?? "team";
362
+ saveGlobalConfig(
363
+ addMountedStore(config, { store_uuid: identity.store_uuid, alias, remote: url }),
364
+ globalRoot
365
+ );
366
+ console.log(`mounted store '${alias}' (${identity.store_uuid}) from ${url}`);
367
+ }
368
+ async function runGlobalInstall(options = {}, globalRoot = resolveGlobalRoot()) {
369
+ const uid = options.uid ?? deriveUid();
370
+ const personalStoreUuid = options.personalStoreUuid ?? randomUUID();
371
+ const now = options.now ?? (/* @__PURE__ */ new Date()).toISOString();
372
+ const result = await installGlobalCore({ globalRoot, uid, personalStoreUuid, now });
373
+ if (!result.receipt.ok) {
374
+ throw new GenericIOError(
375
+ `global install failed at step '${result.receipt.failedStep}': ${result.receipt.error}`,
376
+ {
377
+ actionHint: "check write permissions and free space under ~/.fabric, then re-run `fabric install --global` (the install is transactional and rolls back partial state)"
378
+ }
379
+ );
380
+ }
381
+ console.log(
382
+ result.alreadyInstalled ? "global Fabric already installed" : `installed global Fabric (uid ${uid})`
383
+ );
384
+ if (options.url !== void 0) {
385
+ mountStoreFromRemote(options.url, globalRoot);
386
+ }
387
+ }
388
+
185
389
  // src/lib/detect-language.ts
186
390
  import { existsSync as existsSync2, readdirSync, readFileSync, statSync as statSync2 } from "fs";
187
- import { join as join2 } from "path";
391
+ import { join as join4 } from "path";
188
392
  function detectExistingLanguage(target) {
189
393
  const ZH_CN_RATIO_THRESHOLD = 0.3;
190
394
  const samples = [];
191
- const readmePath = join2(target, "README.md");
395
+ const readmePath = join4(target, "README.md");
192
396
  if (existsSync2(readmePath)) {
193
397
  try {
194
398
  samples.push(readFileSync(readmePath, "utf8"));
195
399
  } catch {
196
400
  }
197
401
  }
198
- const docsDir = join2(target, "docs");
402
+ const docsDir = join4(target, "docs");
199
403
  if (existsSync2(docsDir)) {
200
404
  try {
201
405
  const stat = statSync2(docsDir);
@@ -204,7 +408,7 @@ function detectExistingLanguage(target) {
204
408
  if (!entry.isFile()) continue;
205
409
  if (!/\.(md|mdx|txt)$/iu.test(entry.name)) continue;
206
410
  try {
207
- samples.push(readFileSync(join2(docsDir, entry.name), "utf8"));
411
+ samples.push(readFileSync(join4(docsDir, entry.name), "utf8"));
208
412
  } catch {
209
413
  }
210
414
  }
@@ -236,11 +440,12 @@ function detectExistingLanguage(target) {
236
440
  }
237
441
 
238
442
  // src/scanner/forensic.ts
239
- import { execFileSync } from "child_process";
443
+ import { execFileSync as execFileSync3 } from "child_process";
240
444
  import { existsSync as existsSync3, readdirSync as readdirSync2, readFileSync as readFileSync2, statSync as statSync3 } from "fs";
241
445
  import { createRequire } from "module";
242
- import { basename, extname, isAbsolute as isAbsolute2, join as join3, posix, relative, resolve as resolve2, sep } from "path";
446
+ import { basename, extname, isAbsolute as isAbsolute2, join as join5, posix, relative, resolve as resolve2, sep } from "path";
243
447
  import {
448
+ buildScanRecommendations,
244
449
  forensicReportSchema
245
450
  } from "@fenglimg/fabric-shared";
246
451
 
@@ -358,7 +563,7 @@ async function buildForensicReport(targetInput) {
358
563
  candidate_files: candidateFiles,
359
564
  sampling_budget: DEFAULT_SAMPLING_BUDGET,
360
565
  readme,
361
- recommendations_for_skill: buildSkillRecommendations(framework.kind, topology, readme)
566
+ recommendations_for_skill: buildSkillRecommendations(framework.kind, topology, readme, target)
362
567
  };
363
568
  const validation = forensicReportSchema.safeParse(report);
364
569
  if (!validation.success) {
@@ -383,7 +588,7 @@ function buildTopology(root) {
383
588
  continue;
384
589
  }
385
590
  for (const entry of readdirSync2(current, { withFileTypes: true })) {
386
- const absolutePath = join3(current, entry.name);
591
+ const absolutePath = join5(current, entry.name);
387
592
  const relativePath = toPosixPath(relative(root, absolutePath));
388
593
  if (relativePath.length === 0) {
389
594
  continue;
@@ -471,7 +676,7 @@ function getEntryPointReason(relativePath) {
471
676
  async function buildCodeSamples(target, entryPoints, frameworkKind, topology, packageDependencies) {
472
677
  const samples = [];
473
678
  for (const entryPoint of entryPoints.slice(0, SAMPLE_LIMIT)) {
474
- const absolutePath = join3(target, ...entryPoint.path.split("/"));
679
+ const absolutePath = join5(target, ...entryPoint.path.split("/"));
475
680
  const sample = readFirstLines(absolutePath, SAMPLE_LINE_LIMIT);
476
681
  const patternAnalysis = await inferPatternHint(entryPoint.path, sample.snippet, {
477
682
  frameworkKind,
@@ -508,7 +713,7 @@ function readFirstLines(path, lineLimit) {
508
713
  }
509
714
  }
510
715
  function readPackageDependencies(target) {
511
- const packageJsonPath = join3(target, "package.json");
716
+ const packageJsonPath = join5(target, "package.json");
512
717
  if (!existsSync3(packageJsonPath)) {
513
718
  return /* @__PURE__ */ new Map();
514
719
  }
@@ -526,7 +731,7 @@ function readPackageDependencies(target) {
526
731
  }
527
732
  function readGitChurnWeight(target, relativePath) {
528
733
  try {
529
- const output = execFileSync("git", ["log", "--follow", "--oneline", "-20", "--", relativePath], {
734
+ const output = execFileSync3("git", ["log", "--follow", "--oneline", "-20", "--", relativePath], {
530
735
  cwd: target,
531
736
  encoding: "utf8",
532
737
  stdio: ["ignore", "pipe", "ignore"],
@@ -849,8 +1054,8 @@ function scoreFrameworkConfidence(input) {
849
1054
  return input.configCount > 0 || input.packageCount > 0 ? "MEDIUM" : "LOW";
850
1055
  }
851
1056
  function readReadmeInfo(target) {
852
- const readmePath = join3(target, "README.md");
853
- const hasContributing = existsSync3(join3(target, "CONTRIBUTING.md"));
1057
+ const readmePath = join5(target, "README.md");
1058
+ const hasContributing = existsSync3(join5(target, "CONTRIBUTING.md"));
854
1059
  if (!existsSync3(readmePath)) {
855
1060
  return {
856
1061
  quality: "missing",
@@ -1313,30 +1518,18 @@ function isDomainFile(relativePath) {
1313
1518
  }
1314
1519
  return !isConfigFile(relativePath) && !isTestFile(relativePath);
1315
1520
  }
1316
- function buildSkillRecommendations(frameworkKind, topology, readme) {
1317
- const recommendations = [];
1318
- if (frameworkKind === "cocos-creator") {
1319
- recommendations.push("\u5EFA\u8BAE\u5411\u7528\u6237\u786E\u8BA4 Cocos Creator Component \u751F\u547D\u5468\u671F(onLoad/onEnable/start)\u987A\u5E8F\u3002");
1320
- recommendations.push("\u5EFA\u8BAE\u8BE2\u95EE assets/prefabs \u548C assets/scenes \u662F\u5426\u5C5E\u4E8E @HUMAN \u4FDD\u62A4\u533A\u57DF\u3002");
1321
- if ((topology.by_ext[".meta"] ?? 0) > 0) {
1322
- recommendations.push("\u68C0\u6D4B\u5230 .meta \u6587\u4EF6,\u5EFA\u8BAE\u5728 @HUMAN \u9501\u5B9A .meta \u4E0D\u88AB AI \u6539\u52A8\u3002");
1323
- }
1324
- } else if (frameworkKind === "next") {
1325
- recommendations.push("\u5EFA\u8BAE\u786E\u8BA4 app/pages \u8DEF\u7531\u8FB9\u754C\u548C\u670D\u52A1\u7AEF\u7EC4\u4EF6\u7EA6\u675F\u3002");
1326
- } else if (frameworkKind === "vite") {
1327
- recommendations.push("\u5EFA\u8BAE\u786E\u8BA4 src/main \u5165\u53E3\u3001\u7EC4\u4EF6\u76EE\u5F55\u548C\u6784\u5EFA\u811A\u672C\u7684\u7EF4\u62A4\u8FB9\u754C\u3002");
1328
- } else if (frameworkKind === "unknown") {
1329
- recommendations.push("\u672A\u68C0\u6D4B\u5230\u660E\u786E\u6846\u67B6,\u5EFA\u8BAE\u5148\u8BA9\u7528\u6237\u786E\u8BA4\u6280\u672F\u6808\u548C\u4E3B\u8981\u5165\u53E3\u3002");
1330
- } else {
1331
- recommendations.push(`\u5EFA\u8BAE\u56F4\u7ED5 ${frameworkKind} \u7684\u4E3B\u8981\u5165\u53E3\u548C\u751F\u6210\u76EE\u5F55\u786E\u8BA4 AGENTS.md \u5206\u5C42\u8FB9\u754C\u3002`);
1332
- }
1333
- if (readme.quality !== "ok") {
1334
- recommendations.push("README \u4FE1\u606F\u4E0D\u8DB3,\u5EFA\u8BAE\u5728\u521D\u59CB\u5316\u8BBF\u8C08\u4E2D\u8865\u9F50\u9879\u76EE\u76EE\u6807\u3001\u8FD0\u884C\u65B9\u5F0F\u548C\u7981\u6539\u533A\u57DF\u3002");
1335
- }
1336
- return recommendations;
1521
+ function buildSkillRecommendations(frameworkKind, topology, readme, projectRoot) {
1522
+ return buildScanRecommendations(
1523
+ {
1524
+ frameworkKind,
1525
+ hasMeta: (topology.by_ext[".meta"] ?? 0) > 0,
1526
+ readmeOk: readme.quality === "ok"
1527
+ },
1528
+ getProjectTranslator(projectRoot)
1529
+ );
1337
1530
  }
1338
1531
  function readProjectName(target) {
1339
- const packageJsonPath = join3(target, "package.json");
1532
+ const packageJsonPath = join5(target, "package.json");
1340
1533
  if (existsSync3(packageJsonPath)) {
1341
1534
  try {
1342
1535
  const packageJson = JSON.parse(readFileSync2(packageJsonPath, "utf8"));
@@ -1350,7 +1543,7 @@ function readProjectName(target) {
1350
1543
  return basename(target);
1351
1544
  }
1352
1545
  function getCliVersion() {
1353
- return true ? "2.0.1" : "unknown";
1546
+ return true ? "2.2.0-rc.1" : "unknown";
1354
1547
  }
1355
1548
  function sortRecord(record) {
1356
1549
  return Object.fromEntries(Object.entries(record).sort(([left], [right]) => left.localeCompare(right)));
@@ -1360,7 +1553,7 @@ function toPosixPath(path) {
1360
1553
  }
1361
1554
 
1362
1555
  // src/commands/install.ts
1363
- var LOCAL_FABRIC_SERVER_PATH = join4("node_modules", "@fenglimg", "fabric-server", "dist", "index.js");
1556
+ var LOCAL_FABRIC_SERVER_PATH = join6("node_modules", "@fenglimg", "fabric-server", "dist", "index.js");
1364
1557
  var FABRIC_SERVER_PACKAGE = "@fenglimg/fabric-server";
1365
1558
  var INIT_WIZARD_GROUP_CANCELLED = /* @__PURE__ */ Symbol("init-wizard-group-cancelled");
1366
1559
  var installCommand = defineCommand({
@@ -1397,6 +1590,15 @@ var installCommand = defineCommand({
1397
1590
  type: "boolean",
1398
1591
  description: t("cli.install.args.force-hooks-only.description"),
1399
1592
  default: false
1593
+ },
1594
+ global: {
1595
+ type: "boolean",
1596
+ description: "Set up global Fabric (~/.fabric: uid + personal store + config)",
1597
+ default: false
1598
+ },
1599
+ url: {
1600
+ type: "string",
1601
+ description: "With --global: clone + mount this shared store remote"
1400
1602
  }
1401
1603
  },
1402
1604
  async run({ args }) {
@@ -1406,7 +1608,7 @@ var installCommand = defineCommand({
1406
1608
  var install_default = installCommand;
1407
1609
  async function runSkillsOnlyRefresh(targetInput) {
1408
1610
  const target = normalizeTarget3(targetInput);
1409
- const metaPath = join4(target, ".fabric", "agents.meta.json");
1611
+ const metaPath = join6(target, ".fabric", "agents.meta.json");
1410
1612
  if (!existsSync4(metaPath)) {
1411
1613
  const message = t("cli.install.force-skills-only.uninitialised.message");
1412
1614
  const hint = t("cli.install.force-skills-only.uninitialised.hint");
@@ -1422,6 +1624,11 @@ ${hint}
1422
1624
  results.push(...await installFabricArchiveSkill(target));
1423
1625
  results.push(...await installFabricReviewSkill(target));
1424
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));
1425
1632
  let written = 0;
1426
1633
  let skipped = 0;
1427
1634
  let errors = 0;
@@ -1449,7 +1656,7 @@ ${hint}
1449
1656
  }
1450
1657
  async function runHooksOnlyRefresh(targetInput) {
1451
1658
  const target = normalizeTarget3(targetInput);
1452
- const metaPath = join4(target, ".fabric", "agents.meta.json");
1659
+ const metaPath = join6(target, ".fabric", "agents.meta.json");
1453
1660
  if (!existsSync4(metaPath)) {
1454
1661
  const message = t("cli.install.force-hooks-only.uninitialised.message");
1455
1662
  const hint = t("cli.install.force-hooks-only.uninitialised.hint");
@@ -1478,6 +1685,10 @@ ${hint}
1478
1685
  }
1479
1686
  async function runInitCommand(args) {
1480
1687
  const logger = createDebugLogger(args.debug);
1688
+ if (args.global === true) {
1689
+ await runGlobalInstall({ url: args.url });
1690
+ return;
1691
+ }
1481
1692
  const resolution = resolveDevMode(args.target, process.cwd());
1482
1693
  if (args["force-skills-only"] === true) {
1483
1694
  await runSkillsOnlyRefresh(resolution.target);
@@ -1515,8 +1726,26 @@ async function runInitCommand(args) {
1515
1726
  }
1516
1727
  return result;
1517
1728
  }
1729
+ var FABRIC_GITIGNORE_CONTENT = [
1730
+ "# Fabric per-dev activity ledgers & caches \u2014 auto-generated, not shared.",
1731
+ "# Managed by `fabric install`; edit freely (re-install never overwrites this).",
1732
+ "events.jsonl",
1733
+ "metrics.jsonl",
1734
+ "cite-rollup.jsonl",
1735
+ "injections.jsonl",
1736
+ ".cache/",
1737
+ "*.lock",
1738
+ "*.corrupted.*",
1739
+ ""
1740
+ ].join("\n");
1741
+ function writeDefaultGitignore(fabricDir) {
1742
+ const target = join6(fabricDir, ".gitignore");
1743
+ if (existsSync4(target)) return;
1744
+ mkdirSync2(fabricDir, { recursive: true });
1745
+ writeFileSync(target, FABRIC_GITIGNORE_CONTENT, "utf8");
1746
+ }
1518
1747
  function writeDefaultFabricConfig(fabricDir, targetRoot) {
1519
- const target = join4(fabricDir, "fabric-config.json");
1748
+ const target = join6(fabricDir, "fabric-config.json");
1520
1749
  if (existsSync4(target)) return;
1521
1750
  const detectedLanguage = detectExistingLanguage(targetRoot);
1522
1751
  const FABRIC_CONFIG_DEFAULTS = {
@@ -1578,7 +1807,7 @@ function writeDefaultFabricConfig(fabricDir, targetRoot) {
1578
1807
  // is considered stale by fabric-review. Default 14.
1579
1808
  review_stale_pending_days: 14
1580
1809
  };
1581
- mkdirSync(fabricDir, { recursive: true });
1810
+ mkdirSync2(fabricDir, { recursive: true });
1582
1811
  writeFileSync(target, JSON.stringify(FABRIC_CONFIG_DEFAULTS, null, 2) + "\n", "utf8");
1583
1812
  log.info(
1584
1813
  `Detected and fixated fabric_language = ${detectedLanguage}; edit ${target} to override.`
@@ -1709,14 +1938,14 @@ function resolvePersonalFabricRoot() {
1709
1938
  }
1710
1939
  async function buildInitFabricPlan(target, options) {
1711
1940
  assertExistingDirectory3(target);
1712
- const fabricDir = join4(target, ".fabric");
1713
- const agentsMdPath = join4(target, "AGENTS.md");
1941
+ const fabricDir = join6(target, ".fabric");
1942
+ const agentsMdPath = join6(target, "AGENTS.md");
1714
1943
  const agentsMdAction = existsSync4(agentsMdPath) ? "preserved" : "created";
1715
- const knowledgeDir = join4(fabricDir, "knowledge");
1716
- const personalKnowledgeDir = join4(resolvePersonalFabricRoot(), ".fabric", "knowledge");
1717
- const forensicPath = join4(fabricDir, "forensic.json");
1718
- const eventsPath = join4(fabricDir, "events.jsonl");
1719
- const metaPath = join4(fabricDir, "agents.meta.json");
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");
1720
1949
  const replaceFabricDir = shouldReplaceWritableDirectory(fabricDir, options);
1721
1950
  const knowledgeDirAction = existsSync4(knowledgeDir) ? "overwritten" : "created";
1722
1951
  const metaClassification = classifyFreshPath(metaPath, "structural");
@@ -1725,7 +1954,16 @@ async function buildInitFabricPlan(target, options) {
1725
1954
  const metaAction = diffStateToWriteAction(metaClassification.state);
1726
1955
  const eventsAction = diffStateToWriteAction(eventsClassification.state);
1727
1956
  const forensicAction = diffStateToWriteAction(forensicClassification.state);
1957
+ const showScanProgress = process.stderr.isTTY === true;
1958
+ if (showScanProgress) {
1959
+ process.stderr.write(`${t("cli.install.scanning")}
1960
+ `);
1961
+ }
1728
1962
  const forensicReport = await buildForensicReport(target);
1963
+ if (showScanProgress) {
1964
+ process.stderr.write(`${t("cli.install.scan-complete")}
1965
+ `);
1966
+ }
1729
1967
  const meta = createInitialMeta();
1730
1968
  return {
1731
1969
  target,
@@ -1752,23 +1990,24 @@ async function buildInitFabricPlan(target, options) {
1752
1990
  }
1753
1991
  async function executeInitFabricPlan(plan) {
1754
1992
  if (plan.replaceFabricDir) {
1755
- rmSync(plan.fabricDir, { force: true });
1993
+ rmSync2(plan.fabricDir, { force: true });
1756
1994
  }
1757
- mkdirSync(plan.fabricDir, { recursive: true });
1995
+ mkdirSync2(plan.fabricDir, { recursive: true });
1758
1996
  writeDefaultFabricConfig(plan.fabricDir, plan.target);
1759
- mkdirSync(plan.knowledgeDir, { recursive: true });
1997
+ writeDefaultGitignore(plan.fabricDir);
1998
+ mkdirSync2(plan.knowledgeDir, { recursive: true });
1760
1999
  for (const sub of KNOWLEDGE_SUBDIRS) {
1761
- const teamSubDir = join4(plan.knowledgeDir, sub);
1762
- mkdirSync(teamSubDir, { recursive: true });
1763
- const teamGitkeep = join4(teamSubDir, ".gitkeep");
2000
+ const teamSubDir = join6(plan.knowledgeDir, sub);
2001
+ mkdirSync2(teamSubDir, { recursive: true });
2002
+ const teamGitkeep = join6(teamSubDir, ".gitkeep");
1764
2003
  if (!existsSync4(teamGitkeep)) {
1765
2004
  writeFileSync(teamGitkeep, "", "utf8");
1766
2005
  }
1767
2006
  }
1768
2007
  try {
1769
- mkdirSync(plan.personalKnowledgeDir, { recursive: true });
2008
+ mkdirSync2(plan.personalKnowledgeDir, { recursive: true });
1770
2009
  for (const sub of KNOWLEDGE_SUBDIRS) {
1771
- mkdirSync(join4(plan.personalKnowledgeDir, sub), { recursive: true });
2010
+ mkdirSync2(join6(plan.personalKnowledgeDir, sub), { recursive: true });
1772
2011
  }
1773
2012
  } catch {
1774
2013
  }
@@ -1778,7 +2017,7 @@ async function executeInitFabricPlan(plan) {
1778
2017
  }
1779
2018
  if (plan.eventsState === "missing") {
1780
2019
  preparePlannedPath(plan.eventsPath, plan.eventsAction);
1781
- mkdirSync(dirname(plan.eventsPath), { recursive: true });
2020
+ mkdirSync2(dirname(plan.eventsPath), { recursive: true });
1782
2021
  writeFileSync(plan.eventsPath, "", "utf8");
1783
2022
  }
1784
2023
  preparePlannedPath(plan.forensicPath, plan.forensicAction);
@@ -1935,9 +2174,15 @@ async function executeInitStagePlan(plan, stageName) {
1935
2174
  installResults.push(...await runBestEffort("skill-install", () => installFabricArchiveSkill(plan.target)));
1936
2175
  installResults.push(...await runBestEffort("skill-review-install", () => installFabricReviewSkill(plan.target)));
1937
2176
  installResults.push(...await runBestEffort("skill-import-install", () => installFabricImportSkill(plan.target)));
2177
+ installResults.push(...await runBestEffort("skill-sync-install", () => installFabricSyncSkill(plan.target)));
2178
+ installResults.push(...await runBestEffort("skill-store-install", () => installFabricStoreSkill(plan.target)));
2179
+ installResults.push(...await runBestEffort("skill-audit-install", () => installFabricAuditSkill(plan.target)));
2180
+ installResults.push(...await runBestEffort("skill-connect-install", () => installFabricConnectSkill(plan.target)));
2181
+ installResults.push(...await runBestEffort("skill-shared-lib", () => installSharedSkillLib(plan.target)));
1938
2182
  installResults.push(...await runBestEffort("hook-script", () => installArchiveHintHook(plan.target)));
1939
2183
  installResults.push(...await runBestEffort("hook-broad-script", () => installKnowledgeHintBroadHook(plan.target)));
1940
2184
  installResults.push(...await runBestEffort("hook-narrow-script", () => installKnowledgeHintNarrowHook(plan.target)));
2185
+ installResults.push(...await runBestEffort("hook-cite-policy-evict-script", () => installCitePolicyEvictHook(plan.target)));
1941
2186
  installResults.push(...await runBestEffort("hook-lib", () => installHookLibs(plan.target)));
1942
2187
  installResults.push(await runBestEffortSingle("claude-hook-config", () => mergeClaudeCodeHookConfig(plan.target)));
1943
2188
  installResults.push(await runBestEffortSingle("codex-hook-config", () => mergeCodexHookConfig(plan.target)));
@@ -2050,9 +2295,9 @@ function formatDiffFileState(state) {
2050
2295
  return t(`cli.install.diff.state.${state}`);
2051
2296
  }
2052
2297
  function preparePlannedPath(path, action) {
2053
- mkdirSync(dirname(path), { recursive: true });
2298
+ mkdirSync2(dirname(path), { recursive: true });
2054
2299
  if (action === "overwritten" && existsSync4(path)) {
2055
- rmSync(path, { recursive: true, force: true });
2300
+ rmSync2(path, { recursive: true, force: true });
2056
2301
  }
2057
2302
  }
2058
2303
  function createDefaultInitWizardAdapter() {
@@ -2211,13 +2456,13 @@ function assertExistingDirectory3(target) {
2211
2456
  }
2212
2457
  function detectPackageManager(cwd) {
2213
2458
  const workspaceRoot = resolve3(cwd);
2214
- if (existsSync4(join4(workspaceRoot, "pnpm-lock.yaml"))) {
2459
+ if (existsSync4(join6(workspaceRoot, "pnpm-lock.yaml"))) {
2215
2460
  return "pnpm";
2216
2461
  }
2217
- if (existsSync4(join4(workspaceRoot, "yarn.lock"))) {
2462
+ if (existsSync4(join6(workspaceRoot, "yarn.lock"))) {
2218
2463
  return "yarn";
2219
2464
  }
2220
- if (existsSync4(join4(workspaceRoot, "package-lock.json"))) {
2465
+ if (existsSync4(join6(workspaceRoot, "package-lock.json"))) {
2221
2466
  return "npm";
2222
2467
  }
2223
2468
  return "npm";
@@ -2240,7 +2485,7 @@ function createInitialMeta() {
2240
2485
  function appendInstallDiffLedgerEvent(eventsPath, payload) {
2241
2486
  const event = {
2242
2487
  kind: "fabric-event",
2243
- id: `event:${randomUUID()}`,
2488
+ id: `event:${randomUUID2()}`,
2244
2489
  ts: Date.now(),
2245
2490
  schema_version: 1,
2246
2491
  event_type: "install_diff_applied",