@fenglimg/fabric-cli 2.1.0-rc.2 → 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 (28) hide show
  1. package/dist/{chunk-PWLW3B57.js → chunk-2CY4BMTH.js} +5 -1
  2. package/dist/{chunk-F46ORPOA.js → chunk-2R55HNVD.js} +82 -5
  3. package/dist/{chunk-HFQVXY6P.js → chunk-4R2CYEA4.js} +31 -1
  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-MF3OTILQ.js → chunk-XC5RUHLK.js} +29 -8
  7. package/dist/{config-XJIPZNUP.js → config-XYRBZJDU.js} +3 -3
  8. package/dist/{doctor-QVNPHLJK.js → doctor-YONYXDX6.js} +39 -26
  9. package/dist/index.js +54 -14
  10. package/dist/{install-2HDO5FTQ.js → install-74ANPCCP.js} +88 -34
  11. package/dist/{metrics-ACEQFPDU.js → metrics-RER6NLFC.js} +22 -9
  12. package/dist/{onboard-coverage-MFCAEBDO.js → onboard-coverage-JWQWDZW7.js} +1 -1
  13. package/dist/{scope-explain-2F2R5URO.js → scope-explain-CDIZESP5.js} +6 -2
  14. package/dist/{store-XTSE5TY6.js → store-XB3ADT65.js} +50 -11
  15. package/dist/{sync-BJCWDPNC.js → sync-UJ4BBCZJ.js} +18 -12
  16. package/dist/{uninstall-TAXSUSKH.js → uninstall-C3QXKOO6.js} +35 -4
  17. package/dist/{whoami-B6AEMSEV.js → whoami-2MLO4Y37.js} +10 -5
  18. package/package.json +3 -3
  19. package/templates/hooks/fabric-hint.cjs +99 -7
  20. package/templates/hooks/knowledge-hint-broad.cjs +164 -9
  21. package/templates/hooks/knowledge-hint-narrow.cjs +10 -4
  22. package/templates/hooks/lib/injection-log.cjs +91 -0
  23. package/templates/hooks/lib/state-store.cjs +30 -11
  24. package/templates/skills/fabric-audit/SKILL.md +53 -0
  25. package/templates/skills/fabric-connect/SKILL.md +48 -0
  26. package/templates/skills/fabric-review/SKILL.md +2 -0
  27. package/templates/skills/fabric-review/ref/cite-contract.md +56 -0
  28. package/templates/skills/fabric-store/SKILL.md +44 -0
@@ -4,8 +4,11 @@ import {
4
4
  installArchiveHintHook,
5
5
  installCitePolicyEvictHook,
6
6
  installFabricArchiveSkill,
7
+ installFabricAuditSkill,
8
+ installFabricConnectSkill,
7
9
  installFabricImportSkill,
8
10
  installFabricReviewSkill,
11
+ installFabricStoreSkill,
9
12
  installFabricSyncSkill,
10
13
  installHookLibs,
11
14
  installKnowledgeHintBroadHook,
@@ -19,25 +22,26 @@ import {
19
22
  writeCodexBootstrapManagedBlock,
20
23
  writeCursorBootstrapManagedBlock,
21
24
  writeFabricAgentsSnapshot
22
- } from "./chunk-F46ORPOA.js";
25
+ } from "./chunk-2R55HNVD.js";
23
26
  import {
24
27
  displayWidth,
25
28
  padEnd,
26
29
  paint
27
- } from "./chunk-WWNXR34K.js";
30
+ } from "./chunk-BO4XIZWZ.js";
28
31
  import {
29
32
  createDebugLogger,
30
33
  resolveDevMode
31
34
  } from "./chunk-COI5VDFU.js";
32
35
  import {
33
36
  installMcpClients
34
- } from "./chunk-BATF4PEJ.js";
37
+ } from "./chunk-AOE6AYI7.js";
35
38
  import {
36
39
  detectClientSupports
37
- } from "./chunk-MF3OTILQ.js";
40
+ } from "./chunk-XC5RUHLK.js";
38
41
  import {
42
+ getProjectTranslator,
39
43
  t
40
- } from "./chunk-PWLW3B57.js";
44
+ } from "./chunk-2CY4BMTH.js";
41
45
  import {
42
46
  globalConfigPath,
43
47
  loadGlobalConfig,
@@ -67,6 +71,9 @@ async function installHooks(target, _options = {}) {
67
71
  results.push(...await runStep(() => installFabricReviewSkill(normalizedTarget)));
68
72
  results.push(...await runStep(() => installFabricImportSkill(normalizedTarget)));
69
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)));
70
77
  results.push(...await runStep(() => installSharedSkillLib(normalizedTarget)));
71
78
  results.push(...await runStep(() => installArchiveHintHook(normalizedTarget)));
72
79
  results.push(...await runStep(() => installKnowledgeHintBroadHook(normalizedTarget)));
@@ -197,11 +204,12 @@ import { mkdirSync, mkdtempSync, renameSync } from "fs";
197
204
  import { tmpdir } from "os";
198
205
  import { join as join3 } from "path";
199
206
  import { STORES_ROOT_DIR as STORES_ROOT_DIR2, addMountedStore, readStoreIdentity } from "@fenglimg/fabric-shared";
207
+ import { GenericIOError } from "@fenglimg/fabric-shared/errors";
200
208
 
201
209
  // src/store/uid.ts
202
210
  import { execFileSync } from "child_process";
203
211
  import { createHash } from "crypto";
204
- function deriveUid() {
212
+ function deriveUid(opts = {}) {
205
213
  let email = "";
206
214
  try {
207
215
  email = execFileSync("git", ["config", "user.email"], {
@@ -214,7 +222,8 @@ function deriveUid() {
214
222
  if (email === "") {
215
223
  return "u-anon";
216
224
  }
217
- const hash = createHash("sha256").update(email.toLowerCase()).digest("hex").slice(0, 12);
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);
218
227
  return `u-${hash}`;
219
228
  }
220
229
 
@@ -319,7 +328,15 @@ async function installGlobalCore(options) {
319
328
 
320
329
  // src/install/run-global-install.ts
321
330
  function gitClone(url, dest) {
322
- execFileSync2("git", ["clone", url, dest], { stdio: ["ignore", "ignore", "pipe"] });
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
+ }
323
340
  }
324
341
  function mountStoreFromRemote(url, globalRoot) {
325
342
  const storesRoot = join3(globalRoot, STORES_ROOT_DIR2);
@@ -329,13 +346,17 @@ function mountStoreFromRemote(url, globalRoot) {
329
346
  gitClone(url, cloneDest);
330
347
  const identity = readStoreIdentity(cloneDest);
331
348
  if (identity === null) {
332
- throw new Error(`cloned store at ${url} has no valid store.json (not a Fabric store)`);
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
+ });
333
352
  }
334
353
  const finalDir = join3(storesRoot, identity.store_uuid);
335
354
  renameSync(cloneDest, finalDir);
336
355
  const config = loadGlobalConfig(globalRoot);
337
356
  if (config === null) {
338
- throw new Error("global config missing after install");
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
+ });
339
360
  }
340
361
  const alias = identity.canonical_alias ?? "team";
341
362
  saveGlobalConfig(
@@ -350,7 +371,12 @@ async function runGlobalInstall(options = {}, globalRoot = resolveGlobalRoot())
350
371
  const now = options.now ?? (/* @__PURE__ */ new Date()).toISOString();
351
372
  const result = await installGlobalCore({ globalRoot, uid, personalStoreUuid, now });
352
373
  if (!result.receipt.ok) {
353
- throw new Error(`global install failed at step '${result.receipt.failedStep}': ${result.receipt.error}`);
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
+ );
354
380
  }
355
381
  console.log(
356
382
  result.alreadyInstalled ? "global Fabric already installed" : `installed global Fabric (uid ${uid})`
@@ -419,6 +445,7 @@ import { existsSync as existsSync3, readdirSync as readdirSync2, readFileSync as
419
445
  import { createRequire } from "module";
420
446
  import { basename, extname, isAbsolute as isAbsolute2, join as join5, posix, relative, resolve as resolve2, sep } from "path";
421
447
  import {
448
+ buildScanRecommendations,
422
449
  forensicReportSchema
423
450
  } from "@fenglimg/fabric-shared";
424
451
 
@@ -536,7 +563,7 @@ async function buildForensicReport(targetInput) {
536
563
  candidate_files: candidateFiles,
537
564
  sampling_budget: DEFAULT_SAMPLING_BUDGET,
538
565
  readme,
539
- recommendations_for_skill: buildSkillRecommendations(framework.kind, topology, readme)
566
+ recommendations_for_skill: buildSkillRecommendations(framework.kind, topology, readme, target)
540
567
  };
541
568
  const validation = forensicReportSchema.safeParse(report);
542
569
  if (!validation.success) {
@@ -1491,27 +1518,15 @@ function isDomainFile(relativePath) {
1491
1518
  }
1492
1519
  return !isConfigFile(relativePath) && !isTestFile(relativePath);
1493
1520
  }
1494
- function buildSkillRecommendations(frameworkKind, topology, readme) {
1495
- const recommendations = [];
1496
- if (frameworkKind === "cocos-creator") {
1497
- recommendations.push("\u5EFA\u8BAE\u5411\u7528\u6237\u786E\u8BA4 Cocos Creator Component \u751F\u547D\u5468\u671F(onLoad/onEnable/start)\u987A\u5E8F\u3002");
1498
- recommendations.push("\u5EFA\u8BAE\u8BE2\u95EE assets/prefabs \u548C assets/scenes \u662F\u5426\u5C5E\u4E8E @HUMAN \u4FDD\u62A4\u533A\u57DF\u3002");
1499
- if ((topology.by_ext[".meta"] ?? 0) > 0) {
1500
- recommendations.push("\u68C0\u6D4B\u5230 .meta \u6587\u4EF6,\u5EFA\u8BAE\u5728 @HUMAN \u9501\u5B9A .meta \u4E0D\u88AB AI \u6539\u52A8\u3002");
1501
- }
1502
- } else if (frameworkKind === "next") {
1503
- recommendations.push("\u5EFA\u8BAE\u786E\u8BA4 app/pages \u8DEF\u7531\u8FB9\u754C\u548C\u670D\u52A1\u7AEF\u7EC4\u4EF6\u7EA6\u675F\u3002");
1504
- } else if (frameworkKind === "vite") {
1505
- 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");
1506
- } else if (frameworkKind === "unknown") {
1507
- 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");
1508
- } else {
1509
- 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`);
1510
- }
1511
- if (readme.quality !== "ok") {
1512
- 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");
1513
- }
1514
- 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
+ );
1515
1530
  }
1516
1531
  function readProjectName(target) {
1517
1532
  const packageJsonPath = join5(target, "package.json");
@@ -1528,7 +1543,7 @@ function readProjectName(target) {
1528
1543
  return basename(target);
1529
1544
  }
1530
1545
  function getCliVersion() {
1531
- return true ? "2.1.0-rc.2" : "unknown";
1546
+ return true ? "2.2.0-rc.1" : "unknown";
1532
1547
  }
1533
1548
  function sortRecord(record) {
1534
1549
  return Object.fromEntries(Object.entries(record).sort(([left], [right]) => left.localeCompare(right)));
@@ -1609,6 +1624,11 @@ ${hint}
1609
1624
  results.push(...await installFabricArchiveSkill(target));
1610
1625
  results.push(...await installFabricReviewSkill(target));
1611
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));
1612
1632
  let written = 0;
1613
1633
  let skipped = 0;
1614
1634
  let errors = 0;
@@ -1706,6 +1726,24 @@ async function runInitCommand(args) {
1706
1726
  }
1707
1727
  return result;
1708
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
+ }
1709
1747
  function writeDefaultFabricConfig(fabricDir, targetRoot) {
1710
1748
  const target = join6(fabricDir, "fabric-config.json");
1711
1749
  if (existsSync4(target)) return;
@@ -1916,7 +1954,16 @@ async function buildInitFabricPlan(target, options) {
1916
1954
  const metaAction = diffStateToWriteAction(metaClassification.state);
1917
1955
  const eventsAction = diffStateToWriteAction(eventsClassification.state);
1918
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
+ }
1919
1962
  const forensicReport = await buildForensicReport(target);
1963
+ if (showScanProgress) {
1964
+ process.stderr.write(`${t("cli.install.scan-complete")}
1965
+ `);
1966
+ }
1920
1967
  const meta = createInitialMeta();
1921
1968
  return {
1922
1969
  target,
@@ -1947,6 +1994,7 @@ async function executeInitFabricPlan(plan) {
1947
1994
  }
1948
1995
  mkdirSync2(plan.fabricDir, { recursive: true });
1949
1996
  writeDefaultFabricConfig(plan.fabricDir, plan.target);
1997
+ writeDefaultGitignore(plan.fabricDir);
1950
1998
  mkdirSync2(plan.knowledgeDir, { recursive: true });
1951
1999
  for (const sub of KNOWLEDGE_SUBDIRS) {
1952
2000
  const teamSubDir = join6(plan.knowledgeDir, sub);
@@ -2126,9 +2174,15 @@ async function executeInitStagePlan(plan, stageName) {
2126
2174
  installResults.push(...await runBestEffort("skill-install", () => installFabricArchiveSkill(plan.target)));
2127
2175
  installResults.push(...await runBestEffort("skill-review-install", () => installFabricReviewSkill(plan.target)));
2128
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)));
2129
2182
  installResults.push(...await runBestEffort("hook-script", () => installArchiveHintHook(plan.target)));
2130
2183
  installResults.push(...await runBestEffort("hook-broad-script", () => installKnowledgeHintBroadHook(plan.target)));
2131
2184
  installResults.push(...await runBestEffort("hook-narrow-script", () => installKnowledgeHintNarrowHook(plan.target)));
2185
+ installResults.push(...await runBestEffort("hook-cite-policy-evict-script", () => installCitePolicyEvictHook(plan.target)));
2132
2186
  installResults.push(...await runBestEffort("hook-lib", () => installHookLibs(plan.target)));
2133
2187
  installResults.push(await runBestEffortSingle("claude-hook-config", () => mergeClaudeCodeHookConfig(plan.target)));
2134
2188
  installResults.push(await runBestEffortSingle("codex-hook-config", () => mergeCodexHookConfig(plan.target)));
@@ -1,14 +1,17 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ getProjectTranslator
4
+ } from "./chunk-2CY4BMTH.js";
2
5
 
3
6
  // src/commands/metrics.ts
4
7
  import { resolve } from "path";
5
8
  import { defineCommand } from "citty";
6
9
  import { readMetrics } from "@fenglimg/fabric-server";
7
- function parseSinceArg(raw) {
10
+ function parseSinceArg(raw, t) {
8
11
  if (raw === void 0 || raw.length === 0) return 0;
9
12
  const match = /^(\d+)([smhd]?)$/u.exec(raw);
10
13
  if (match === null) {
11
- throw new Error(`--since: invalid duration "${raw}" (expected e.g. 24h, 7d, 30m)`);
14
+ throw new Error(t("cli.metrics.invalid-since", { raw }));
12
15
  }
13
16
  const n = Number.parseInt(match[1], 10);
14
17
  const unit = match[2] ?? "s";
@@ -36,6 +39,8 @@ function aggregate(rows, sinceMs, now) {
36
39
  }
37
40
  }
38
41
  return {
42
+ // Stable token (NOT localized) so the --json contract is locale-independent;
43
+ // renderText localizes "all-time" at presentation time only.
39
44
  windowDescription: sinceMs > 0 ? formatDuration(sinceMs) : "all-time",
40
45
  rowCount: filtered.length,
41
46
  totals,
@@ -50,17 +55,24 @@ function formatDuration(ms) {
50
55
  if (ms >= 6e4) return `${Math.round(ms / 6e4)}m`;
51
56
  return `${Math.round(ms / 1e3)}s`;
52
57
  }
53
- function renderText(agg) {
58
+ function renderText(agg, t) {
54
59
  const lines = [];
55
- lines.push(`Fabric metrics \u2014 window: ${agg.windowDescription}`);
60
+ const windowDisplay = agg.windowDescription === "all-time" ? t("cli.metrics.window-all-time") : agg.windowDescription;
61
+ lines.push(t("cli.metrics.window", { window: windowDisplay }));
56
62
  if (agg.rangeStart && agg.rangeEnd) {
57
- lines.push(` rows: ${agg.rowCount} (${agg.rangeStart} \u2192 ${agg.rangeEnd})`);
63
+ lines.push(
64
+ t("cli.metrics.rows-range", {
65
+ count: String(agg.rowCount),
66
+ start: agg.rangeStart,
67
+ end: agg.rangeEnd
68
+ })
69
+ );
58
70
  } else {
59
- lines.push(` rows: ${agg.rowCount}`);
71
+ lines.push(t("cli.metrics.rows", { count: String(agg.rowCount) }));
60
72
  }
61
73
  lines.push("");
62
74
  if (Object.keys(agg.totals).length === 0) {
63
- lines.push(" (no counter activity in window \u2014 server may be idle or just started)");
75
+ lines.push(t("cli.metrics.no-activity"));
64
76
  return lines.join("\n");
65
77
  }
66
78
  lines.push(" counter total");
@@ -103,14 +115,15 @@ var metricsCommand = defineCommand({
103
115
  },
104
116
  async run({ args }) {
105
117
  const projectRoot = resolve(args.target ?? process.cwd());
106
- const sinceMs = parseSinceArg(args.since);
118
+ const t = getProjectTranslator(projectRoot);
119
+ const sinceMs = parseSinceArg(args.since, t);
107
120
  const rows = await readMetrics(projectRoot);
108
121
  const aggregated = aggregate(rows, sinceMs, /* @__PURE__ */ new Date());
109
122
  if (args.json === true) {
110
123
  process.stdout.write(`${JSON.stringify(aggregated)}
111
124
  `);
112
125
  } else {
113
- process.stdout.write(`${renderText(aggregated)}
126
+ process.stdout.write(`${renderText(aggregated, t)}
114
127
  `);
115
128
  }
116
129
  }
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  t
4
- } from "./chunk-PWLW3B57.js";
4
+ } from "./chunk-2CY4BMTH.js";
5
5
 
6
6
  // src/commands/onboard-coverage.ts
7
7
  import { existsSync, readdirSync, readFileSync } from "fs";
@@ -2,6 +2,9 @@
2
2
  import {
3
3
  scopeExplain
4
4
  } from "./chunk-L4Q55UC4.js";
5
+ import {
6
+ getProjectTranslator
7
+ } from "./chunk-2CY4BMTH.js";
5
8
  import "./chunk-LFIKMVY7.js";
6
9
  import "./chunk-RYAFBNES.js";
7
10
 
@@ -20,9 +23,10 @@ var scope_explain_default = defineCommand({
20
23
  }
21
24
  },
22
25
  run({ args }) {
23
- const result = scopeExplain(process.cwd(), args.scope);
26
+ const projectRoot = process.cwd();
27
+ const result = scopeExplain(projectRoot, args.scope);
24
28
  if (result === null) {
25
- console.log("no global Fabric config \u2014 run `fabric install --global <url>` first");
29
+ console.log(getProjectTranslator(projectRoot)("cli.cmd.no-global-config"));
26
30
  return;
27
31
  }
28
32
  console.log(JSON.stringify(result, null, 2));
@@ -1,16 +1,21 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ assertStoreMountable,
3
4
  storeAdd,
4
5
  storeBind,
6
+ storeCreate,
5
7
  storeExplain,
6
8
  storeList,
7
9
  storeRemove,
8
10
  storeSwitchWrite
9
- } from "./chunk-HFQVXY6P.js";
11
+ } from "./chunk-4R2CYEA4.js";
10
12
  import {
11
13
  regenerateBindingsSnapshot
12
14
  } from "./chunk-WU6GAPKH.js";
13
15
  import "./chunk-L4Q55UC4.js";
16
+ import {
17
+ getProjectTranslator
18
+ } from "./chunk-2CY4BMTH.js";
14
19
  import "./chunk-LFIKMVY7.js";
15
20
  import "./chunk-RYAFBNES.js";
16
21
 
@@ -19,13 +24,15 @@ import { defineCommand } from "citty";
19
24
  var listCommand = defineCommand({
20
25
  meta: { name: "list", description: "List mounted knowledge stores" },
21
26
  run() {
27
+ const t = getProjectTranslator();
22
28
  const stores = storeList();
23
29
  if (stores.length === 0) {
24
- console.log("(no stores mounted)");
30
+ console.log(t("cli.store.none-mounted"));
25
31
  return;
26
32
  }
33
+ const localOnly = t("cli.shared.local-only");
27
34
  for (const store of stores) {
28
- console.log(`${store.alias} ${store.store_uuid} ${store.remote ?? "(local-only)"}`);
35
+ console.log(`${store.alias} ${store.store_uuid} ${store.remote ?? localOnly}`);
29
36
  }
30
37
  }
31
38
  });
@@ -37,9 +44,32 @@ var addCommand = defineCommand({
37
44
  remote: { type: "string", description: "Git remote locator (omit for local-only)" }
38
45
  },
39
46
  run({ args }) {
47
+ assertStoreMountable(args.uuid);
40
48
  const store = args.remote === void 0 ? { store_uuid: args.uuid, alias: args.alias } : { store_uuid: args.uuid, alias: args.alias, remote: args.remote };
41
49
  const next = storeAdd(store);
42
- console.log(`mounted '${args.alias}' (${next.stores.length} store(s) total)`);
50
+ console.log(
51
+ getProjectTranslator()("cli.store.mounted", {
52
+ alias: args.alias,
53
+ count: String(next.stores.length)
54
+ })
55
+ );
56
+ }
57
+ });
58
+ var createCommand = defineCommand({
59
+ meta: { name: "create", description: "Create a brand-new local knowledge store and mount it" },
60
+ args: {
61
+ alias: { type: "string", required: true, description: "Local alias for the new store" },
62
+ remote: { type: "string", description: "Git remote to associate (push target; optional)" }
63
+ },
64
+ run({ args }) {
65
+ const result = storeCreate(args.alias, (/* @__PURE__ */ new Date()).toISOString(), {
66
+ ...args.remote === void 0 ? {} : { remote: args.remote }
67
+ });
68
+ const t = getProjectTranslator();
69
+ console.log(
70
+ t("cli.store.created", { alias: args.alias, uuid: result.store_uuid, dir: result.storeDir }) + (args.remote === void 0 ? `
71
+ ${t("cli.store.created-local-hint")}` : "")
72
+ );
43
73
  }
44
74
  });
45
75
  var removeCommand = defineCommand({
@@ -49,8 +79,9 @@ var removeCommand = defineCommand({
49
79
  },
50
80
  run({ args }) {
51
81
  const { detached } = storeRemove(args.alias);
82
+ const t = getProjectTranslator();
52
83
  console.log(
53
- detached === null ? `no store aliased '${args.alias}'` : `detached '${args.alias}' \u2014 on-disk store tree left intact (detach \u2260 delete)`
84
+ detached === null ? t("cli.store.no-alias", { alias: args.alias }) : t("cli.store.detached", { alias: args.alias })
54
85
  );
55
86
  }
56
87
  });
@@ -62,7 +93,7 @@ var explainCommand = defineCommand({
62
93
  run({ args }) {
63
94
  const explanation = storeExplain(args.alias);
64
95
  console.log(
65
- explanation === null ? `no store aliased '${args.alias}'` : JSON.stringify(explanation, null, 2)
96
+ explanation === null ? getProjectTranslator()("cli.store.no-alias", { alias: args.alias }) : JSON.stringify(explanation, null, 2)
66
97
  );
67
98
  }
68
99
  });
@@ -74,9 +105,15 @@ var bindCommand = defineCommand({
74
105
  },
75
106
  run({ args }) {
76
107
  const entry = args.remote === void 0 ? { id: args.id } : { id: args.id, suggested_remote: args.remote };
77
- const next = storeBind(process.cwd(), entry);
78
- console.log(`bound required store '${args.id}' (${next.required_stores?.length ?? 0} required)`);
79
- regenerateBindingsSnapshot(process.cwd(), { now: (/* @__PURE__ */ new Date()).toISOString() });
108
+ const projectRoot = process.cwd();
109
+ const next = storeBind(projectRoot, entry);
110
+ console.log(
111
+ getProjectTranslator(projectRoot)("cli.store.bound", {
112
+ id: args.id,
113
+ count: String(next.required_stores?.length ?? 0)
114
+ })
115
+ );
116
+ regenerateBindingsSnapshot(projectRoot, { now: (/* @__PURE__ */ new Date()).toISOString() });
80
117
  }
81
118
  });
82
119
  var switchWriteCommand = defineCommand({
@@ -85,14 +122,16 @@ var switchWriteCommand = defineCommand({
85
122
  alias: { type: "positional", required: true, description: "Alias of the store to write to" }
86
123
  },
87
124
  run({ args }) {
88
- storeSwitchWrite(process.cwd(), args.alias);
89
- console.log(`active write store set to '${args.alias}' for this project`);
125
+ const projectRoot = process.cwd();
126
+ storeSwitchWrite(projectRoot, args.alias);
127
+ console.log(getProjectTranslator(projectRoot)("cli.store.switch-write", { alias: args.alias }));
90
128
  }
91
129
  });
92
130
  var store_default = defineCommand({
93
131
  meta: { name: "store", description: "Manage mounted Fabric knowledge stores" },
94
132
  subCommands: {
95
133
  list: listCommand,
134
+ create: createCommand,
96
135
  add: addCommand,
97
136
  remove: removeCommand,
98
137
  explain: explainCommand,
@@ -3,6 +3,9 @@ import {
3
3
  regenerateBindingsSnapshot
4
4
  } from "./chunk-WU6GAPKH.js";
5
5
  import "./chunk-L4Q55UC4.js";
6
+ import {
7
+ getProjectTranslator
8
+ } from "./chunk-2CY4BMTH.js";
6
9
  import "./chunk-LFIKMVY7.js";
7
10
  import {
8
11
  loadGlobalConfig,
@@ -17,6 +20,7 @@ import { execFileSync } from "child_process";
17
20
  import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
18
21
  import { join } from "path";
19
22
  import { GLOBAL_STATE_DIR, storeRelativePath } from "@fenglimg/fabric-shared";
23
+ import { GenericIOError } from "@fenglimg/fabric-shared/errors";
20
24
 
21
25
  // src/sync/state-machine.ts
22
26
  function syncTransition(state, event) {
@@ -112,7 +116,11 @@ function defaultPull(storeDir) {
112
116
  )) {
113
117
  return "offline";
114
118
  }
115
- throw error;
119
+ const gitMessage = detail.trim().length > 0 ? detail.trim() : "unknown git error";
120
+ throw new GenericIOError(`git pull --rebase failed in ${storeDir}: ${gitMessage}`, {
121
+ actionHint: "resolve the git issue above (e.g. authentication, a dirty working tree, or a detached HEAD), then re-run `fabric sync`",
122
+ details: error
123
+ });
116
124
  }
117
125
  }
118
126
  function gitErrText(error, key) {
@@ -206,19 +214,16 @@ function runAbortSync(options) {
206
214
  }
207
215
 
208
216
  // src/commands/sync.ts
209
- function report(result) {
217
+ function report(result, projectRoot) {
218
+ const t = getProjectTranslator(projectRoot);
210
219
  for (const store of result.session.stores) {
211
220
  console.log(`${store.alias} ${store.state}`);
212
221
  }
213
222
  if (result.deferred.length > 0) {
214
- console.log(
215
- `${result.deferred.length} store(s) offline \u2014 push deferred; re-run \`fabric sync\` when online`
216
- );
223
+ console.log(t("cli.sync.deferred", { count: String(result.deferred.length) }));
217
224
  }
218
225
  if (!result.settled) {
219
- console.log(
220
- "sync paused on a conflict \u2014 resolve it, then run `fabric sync --continue` (or `--abort`)"
221
- );
226
+ console.log(t("cli.sync.paused"));
222
227
  }
223
228
  }
224
229
  var sync_default = defineCommand({
@@ -228,16 +233,17 @@ var sync_default = defineCommand({
228
233
  abort: { type: "boolean", description: "Abort the conflicted store's rebase" }
229
234
  },
230
235
  run({ args }) {
231
- const options = { projectRoot: process.cwd(), now: (/* @__PURE__ */ new Date()).toISOString() };
236
+ const projectRoot = process.cwd();
237
+ const options = { projectRoot, now: (/* @__PURE__ */ new Date()).toISOString() };
232
238
  if (args.continue === true) {
233
- report(runContinueSync(options));
239
+ report(runContinueSync(options), projectRoot);
234
240
  return;
235
241
  }
236
242
  if (args.abort === true) {
237
- report(runAbortSync(options));
243
+ report(runAbortSync(options), projectRoot);
238
244
  return;
239
245
  }
240
- report(runStartSync(options));
246
+ report(runStartSync(options), projectRoot);
241
247
  }
242
248
  });
243
249
  export {
@@ -7,10 +7,10 @@ import {
7
7
  HOOK_SCRIPT_DESTINATIONS,
8
8
  SKILL_DESTINATIONS,
9
9
  fabricAgentsSnapshotPath
10
- } from "./chunk-F46ORPOA.js";
10
+ } from "./chunk-2R55HNVD.js";
11
11
  import {
12
12
  paint
13
- } from "./chunk-WWNXR34K.js";
13
+ } from "./chunk-BO4XIZWZ.js";
14
14
  import {
15
15
  createDebugLogger,
16
16
  resolveDevMode
@@ -18,10 +18,10 @@ import {
18
18
  import {
19
19
  detectClientSupports,
20
20
  resolveClients
21
- } from "./chunk-MF3OTILQ.js";
21
+ } from "./chunk-XC5RUHLK.js";
22
22
  import {
23
23
  t
24
- } from "./chunk-PWLW3B57.js";
24
+ } from "./chunk-2CY4BMTH.js";
25
25
 
26
26
  // src/commands/uninstall.ts
27
27
  import { existsSync as existsSync2, statSync } from "fs";
@@ -49,6 +49,12 @@ async function uninstallFabricImportSkill(projectRoot) {
49
49
  async function uninstallFabricSyncSkill(projectRoot) {
50
50
  return removeSkill("skill-sync", SKILL_DESTINATIONS.fabricSync, projectRoot);
51
51
  }
52
+ async function uninstallFabricAuditSkill(projectRoot) {
53
+ return removeSkill("skill-audit", SKILL_DESTINATIONS.fabricAudit, projectRoot);
54
+ }
55
+ async function uninstallFabricConnectSkill(projectRoot) {
56
+ return removeSkill("skill-connect", SKILL_DESTINATIONS.fabricConnect, projectRoot);
57
+ }
52
58
  async function removeSkill(step, rels, projectRoot) {
53
59
  const results = [];
54
60
  for (const rel of rels) {
@@ -75,6 +81,13 @@ async function removeKnowledgeHintNarrowHook(projectRoot) {
75
81
  projectRoot
76
82
  );
77
83
  }
84
+ async function removeCitePolicyEvictHook(projectRoot) {
85
+ return removeHookScripts(
86
+ "hook-cite-policy-evict-script",
87
+ HOOK_SCRIPT_DESTINATIONS.citePolicyEvict,
88
+ projectRoot
89
+ );
90
+ }
78
91
  async function removeHookScripts(step, rels, projectRoot) {
79
92
  const results = [];
80
93
  for (const rel of rels) {
@@ -304,6 +317,24 @@ async function uninstallBootstrapStage(projectRoot, _opts = {}) {
304
317
  projectRoot,
305
318
  () => removeArchiveHintHook(projectRoot)
306
319
  );
320
+ await runAndCollect(
321
+ results,
322
+ "hook-cite-policy-evict-script",
323
+ projectRoot,
324
+ () => removeCitePolicyEvictHook(projectRoot)
325
+ );
326
+ await runAndCollect(
327
+ results,
328
+ "skill-connect",
329
+ projectRoot,
330
+ () => uninstallFabricConnectSkill(projectRoot)
331
+ );
332
+ await runAndCollect(
333
+ results,
334
+ "skill-audit",
335
+ projectRoot,
336
+ () => uninstallFabricAuditSkill(projectRoot)
337
+ );
307
338
  await runAndCollect(
308
339
  results,
309
340
  "skill-sync",