@fenglimg/fabric-cli 2.2.0-rc.3 → 2.2.0-rc.8

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 (75) hide show
  1. package/README.md +8 -5
  2. package/dist/{chunk-5LQIHYFC.js → chunk-27HK6H5Y.js} +10 -5
  3. package/dist/{chunk-F6ITRM7T.js → chunk-2KBCTMID.js} +29 -6
  4. package/dist/{chunk-XC5RUHLK.js → chunk-3IOLS5EK.js} +23 -38
  5. package/dist/{chunk-XHHCRDIR.js → chunk-CMDW3PYK.js} +105 -220
  6. package/dist/chunk-FEOPLBGA.js +150 -0
  7. package/dist/{chunk-XCBVSGCS.js → chunk-FNHDQTPC.js} +1 -10
  8. package/dist/{chunk-2CY4BMTH.js → chunk-HORSMSZL.js} +9 -5
  9. package/dist/{doctor-J4O3X54I.js → chunk-JTHWLUD3.js} +103 -51
  10. package/dist/{chunk-BO4XIZWZ.js → chunk-NLNH64A3.js} +5 -18
  11. package/dist/{chunk-H3FE6VIK.js → chunk-PTGQAZEW.js} +13 -3
  12. package/dist/chunk-QFIVFZRH.js +13 -0
  13. package/dist/chunk-QPAW6IYT.js +387 -0
  14. package/dist/{chunk-COI5VDFU.js → chunk-WA3DYGSY.js} +1 -2
  15. package/dist/{plan-context-hint-CHVZGOZ5.js → chunk-YM4XATJF.js} +29 -4
  16. package/dist/{config-VJMXCLXW.js → config-A3LTECAY.js} +4 -3
  17. package/dist/context-7NUKXDB6.js +117 -0
  18. package/dist/doctor-REZDNH4A.js +24 -0
  19. package/dist/index.d.ts +2 -2
  20. package/dist/index.js +131 -21
  21. package/dist/info-7FKBTMVO.js +139 -0
  22. package/dist/install-v2-2COC3DO3.js +3277 -0
  23. package/dist/{metrics-RER6NLFC.js → metrics-HMFH4YHK.js} +1 -1
  24. package/dist/{onboard-coverage-JWQWDZW7.js → onboard-coverage-XSG77LL3.js} +48 -27
  25. package/dist/plan-context-hint-G75R4P4J.js +12 -0
  26. package/dist/{scope-explain-BWRWBCCP.js → scope-explain-HLJZ2M33.js} +3 -2
  27. package/dist/{status-PANEGKU2.js → status-4R3TM4FJ.js} +8 -5
  28. package/dist/store-HOCORVL3.js +563 -0
  29. package/dist/{sync-EA5HZMXM.js → sync-DT5UJMMR.js} +36 -13
  30. package/dist/{uninstall-F75MPKQC.js → uninstall-62F4LNKI.js} +62 -140
  31. package/dist/{whoami-66YKY5DZ.js → whoami-ITGEFWH4.js} +9 -7
  32. package/package.json +7 -5
  33. package/templates/hooks/cite-policy-evict.cjs +5 -5
  34. package/templates/hooks/configs/README.md +14 -27
  35. package/templates/hooks/configs/claude-code.json +1 -1
  36. package/templates/hooks/configs/codex-hooks.json +3 -3
  37. package/templates/hooks/fabric-hint.cjs +301 -161
  38. package/templates/hooks/knowledge-hint-broad.cjs +426 -207
  39. package/templates/hooks/knowledge-hint-narrow.cjs +56 -56
  40. package/templates/hooks/lib/banner-i18n.cjs +31 -0
  41. package/templates/hooks/lib/bindings-snapshot-reader.cjs +117 -7
  42. package/templates/hooks/lib/cite-line-parser.cjs +12 -20
  43. package/templates/hooks/lib/client-adapter.cjs +66 -7
  44. package/templates/hooks/lib/nudge-policy.cjs +117 -0
  45. package/templates/hooks/lib/state-store.cjs +60 -0
  46. package/templates/hooks/lib/summary-fallback.cjs +82 -19
  47. package/templates/hooks/post-tooluse-mutation.cjs +112 -11
  48. package/templates/skills/fabric/SKILL.md +94 -0
  49. package/templates/skills/fabric-archive/SKILL.md +29 -26
  50. package/templates/skills/fabric-archive/ref/dry-run-scope.md +1 -1
  51. package/templates/skills/fabric-archive/ref/i18n-policy.md +2 -3
  52. package/templates/skills/fabric-archive/ref/phase-1-5-onboard.md +2 -3
  53. package/templates/skills/fabric-archive/ref/phase-1-cross-session.md +1 -1
  54. package/templates/skills/fabric-archive/ref/phase-2-5-viability.md +1 -1
  55. package/templates/skills/fabric-archive/ref/phase-3-6-related-edges.md +18 -0
  56. package/templates/skills/fabric-archive/ref/phase-3-7-semantic-scope.md +47 -0
  57. package/templates/skills/fabric-audit/SKILL.md +13 -3
  58. package/templates/skills/fabric-connect/SKILL.md +3 -3
  59. package/templates/skills/fabric-import/SKILL.md +7 -7
  60. package/templates/skills/fabric-import/ref/i18n-policy.md +2 -3
  61. package/templates/skills/fabric-import/ref/state-recovery.md +1 -2
  62. package/templates/skills/fabric-review/SKILL.md +5 -5
  63. package/templates/skills/fabric-review/ref/cite-contract.md +1 -1
  64. package/templates/skills/fabric-review/ref/i18n-policy.md +2 -3
  65. package/templates/skills/fabric-review/ref/output-contract.md +1 -1
  66. package/templates/skills/fabric-review/ref/per-mode-flows.md +2 -2
  67. package/templates/skills/fabric-review/ref/worked-examples.md +1 -1
  68. package/templates/skills/fabric-store/SKILL.md +1 -1
  69. package/templates/skills/fabric-sync/SKILL.md +1 -1
  70. package/templates/skills/lib/shared-policy.md +2 -2
  71. package/dist/chunk-5ZUMLCD5.js +0 -248
  72. package/dist/install-BULNDUIM.js +0 -2816
  73. package/dist/store-66NK2FTQ.js +0 -443
  74. package/templates/hooks/configs/cursor-hooks.json +0 -30
  75. package/templates/hooks/lib/cite-contract-reminder.cjs +0 -179
package/README.md CHANGED
@@ -7,19 +7,22 @@
7
7
  1. 在 monorepo 根目录运行 `pnpm install`。
8
8
  2. 用 `pnpm --filter @fenglimg/fabric-cli build` 构建 CLI。
9
9
  3. 在目标项目运行 `fabric install`,完成一站式安装。
10
- 4. 启动 `fabric serve`,再去客户端里验证 `fab_plan_context` `fab_get_knowledge_sections`。
10
+ 4. 重启 Claude Code / Codex CLI,在客户端里验证 `fab_recall`。
11
11
 
12
- `fabric install` 会自动准备 bootstrap、MCP 配置和 git hooks。公共命令面只保留 `install`、`doctor`、`serve`、`uninstall`、`config`(rc.23 起移除了 baseline scan 机制,知识库唯一合法来源是 Skill 路径:`fabric-archive` / `fabric-import` / `fabric-review`)。
12
+ `fabric install` 会自动准备 bootstrap、MCP stdio 配置和 git hooks。当前公共命令面包括 `install`、`store`、`sync`、`info`、`doctor`、`uninstall`、`config`;`metrics`、`plan-context-hint`、`onboard-coverage` hidden/internal 命令;`whoami` / `status` / `scope-explain` 作为 deprecated alias 保留到 v3。`fabric serve` 已 quarantine 到 `packages/server-http-experimental/`,主线不再注册。
13
13
 
14
14
  ## 常用命令
15
15
 
16
16
  - `fabric install`
17
17
  - `fabric doctor`
18
18
  - `fabric doctor --json`
19
- - `fabric doctor --strict`
20
19
  - `fabric doctor --fix`
21
- - `fabric serve`
20
+ - `fabric doctor --fix-knowledge`
21
+ - `fabric store list`
22
+ - `fabric sync`
23
+ - `fabric info`
24
+ - `fabric metrics`(hidden/internal)
22
25
  - `fabric uninstall`
23
26
  - `fabric config`(rc.16 起将提供配置面板;当前为占位提示)
24
27
 
25
- `fabric doctor --fix` 只修复确定性的派生状态,例如 `.fabric/agents.meta.json`、`.fabric/.cache/knowledge-test.index.json`、缺失的 `.fabric/events.jsonl` 和 stale hashes;语义冲突、缺失 rule section、未完成的初始化确认仍需要人工处理。
28
+ `fabric doctor --fix` 只修复确定性的派生状态,例如 `.fabric/agents.meta.json`、`.fabric/.cache/knowledge-test.index.json`、缺失的 `.fabric/events.jsonl` 和 stale hashes。知识条目的 demote/archive/default backfill 走 `fabric doctor --fix-knowledge` 或 `fabric-review`;语义冲突、未完成的初始化确认和本地客户端配置问题仍需要人工处理。
@@ -1,12 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  storeGitRemote
4
- } from "./chunk-5ZUMLCD5.js";
4
+ } from "./chunk-QPAW6IYT.js";
5
+ import {
6
+ loadProjectConfig
7
+ } from "./chunk-QFIVFZRH.js";
5
8
  import {
6
9
  loadGlobalConfig,
7
- loadProjectConfig,
8
10
  resolveGlobalRoot
9
- } from "./chunk-XCBVSGCS.js";
11
+ } from "./chunk-FNHDQTPC.js";
10
12
 
11
13
  // src/lib/unknown-flags.ts
12
14
  function warnUnknownFlags(known) {
@@ -34,13 +36,14 @@ function whoami(globalRoot = resolveGlobalRoot()) {
34
36
  uid: config.uid,
35
37
  stores: config.stores.map((s) => ({
36
38
  alias: s.alias,
39
+ mount_name: s.mount_name ?? null,
37
40
  store_uuid: s.store_uuid,
38
41
  // F4: parity with `fabric store list` — local-only reflects the store
39
42
  // repo's TRUE git remote (what sync actually pushes to), not the registry
40
43
  // metadata. A store with a physical `origin` but no registry `remote`
41
44
  // (e.g. the personal store) was misreported as local-only by whoami while
42
45
  // `store list` honestly showed its remote. Both now read the same source.
43
- local_only: storeGitRemote(s.store_uuid, globalRoot) === void 0
46
+ local_only: storeGitRemote(s.alias, globalRoot) === void 0
44
47
  }))
45
48
  };
46
49
  }
@@ -53,7 +56,9 @@ function projectStatus(projectRoot, globalRoot = resolveGlobalRoot()) {
53
56
  project_id: project?.project_id ?? null,
54
57
  is_fabric_project: project !== null,
55
58
  required: (project?.required_stores ?? []).map((r) => r.id),
56
- active_write_store: project?.active_write_store ?? null
59
+ active_write_store: project?.active_write_store ?? null,
60
+ default_write_store: project?.default_write_store ?? null,
61
+ write_routes: project?.write_routes ?? []
57
62
  };
58
63
  }
59
64
 
@@ -1,10 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  resolveClients
4
- } from "./chunk-XC5RUHLK.js";
4
+ } from "./chunk-3IOLS5EK.js";
5
+ import {
6
+ loadGlobalConfig,
7
+ resolveGlobalRoot,
8
+ saveGlobalConfig
9
+ } from "./chunk-FNHDQTPC.js";
5
10
  import {
6
11
  t
7
- } from "./chunk-2CY4BMTH.js";
12
+ } from "./chunk-HORSMSZL.js";
8
13
 
9
14
  // src/commands/config.ts
10
15
  import { existsSync, statSync } from "fs";
@@ -18,8 +23,9 @@ import {
18
23
  } from "@fenglimg/fabric-shared";
19
24
  import { atomicWriteJson } from "@fenglimg/fabric-shared/node/atomic-write";
20
25
  import { defineCommand } from "citty";
26
+ var LANGUAGE_FIELD_KEY = "fabric_language";
21
27
  async function loadFabricConfig(workspaceRoot) {
22
- const configPath = resolve(workspaceRoot, "fabric.config.json");
28
+ const configPath = resolve(workspaceRoot, ".fabric", "fabric-config.json");
23
29
  if (!existsSync(configPath)) {
24
30
  return {};
25
31
  }
@@ -201,6 +207,10 @@ var configCmd = defineCommand({
201
207
  let edited = false;
202
208
  while (true) {
203
209
  const current = await readPanelConfig(configPath);
210
+ const globalLanguage = loadGlobalConfig(resolveGlobalRoot())?.language;
211
+ if (globalLanguage !== void 0) {
212
+ current[LANGUAGE_FIELD_KEY] = globalLanguage;
213
+ }
204
214
  const fields = getPanelFields();
205
215
  const fieldChoice = await select({
206
216
  message: t("cli.config.menu.field-select"),
@@ -234,9 +244,22 @@ var configCmd = defineCommand({
234
244
  continue;
235
245
  }
236
246
  try {
237
- const refreshed = await readPanelConfig(configPath);
238
- const merged = { ...refreshed, [field.key]: newValue };
239
- await atomicWriteJson(configPath, merged);
247
+ if (field.key === LANGUAGE_FIELD_KEY) {
248
+ const globalRoot = resolveGlobalRoot();
249
+ const globalConfig = loadGlobalConfig(globalRoot);
250
+ if (globalConfig === null) {
251
+ log.error(t("cli.config.errors.uninit-workspace.message"));
252
+ continue;
253
+ }
254
+ saveGlobalConfig(
255
+ { ...globalConfig, language: newValue },
256
+ globalRoot
257
+ );
258
+ } else {
259
+ const refreshed = await readPanelConfig(configPath);
260
+ const merged = { ...refreshed, [field.key]: newValue };
261
+ await atomicWriteJson(configPath, merged);
262
+ }
240
263
  edited = true;
241
264
  log.success(
242
265
  t("cli.config.write.success", {
@@ -231,16 +231,6 @@ var ClaudeCodeCLIWriter = class extends JsonClientConfigWriter {
231
231
  return this.scope === "user" ? join(homedir(), ".claude.json") : join(workspaceRoot, ".mcp.json");
232
232
  }
233
233
  };
234
- var CursorWriter = class extends JsonClientConfigWriter {
235
- clientKind = "Cursor";
236
- constructor(configuredPath) {
237
- super(configuredPath);
238
- }
239
- defaultPath(workspaceRoot) {
240
- const cursorDir = join(workspaceRoot, ".cursor");
241
- return existsSync(cursorDir) ? join(cursorDir, "mcp.json") : null;
242
- }
243
- };
244
234
 
245
235
  // src/config/claude-code.ts
246
236
  function getClaudeDesktopConfigPath() {
@@ -342,16 +332,11 @@ function trimTrailingBlankLines(value) {
342
332
  function removeCodexServerBlock(rawConfig, serverName) {
343
333
  const normalized = rawConfig.replace(/\r\n/g, "\n");
344
334
  const escaped = serverName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
345
- const legacyPattern = new RegExp(
346
- String.raw`\n?\[mcp\.servers\.${escaped}\]\n[\s\S]*?(?=\n\[[^\n]+\]\n|$)`,
347
- "g"
348
- );
349
335
  const currentPattern = new RegExp(
350
336
  String.raw`\n?\[mcp_servers\.${escaped}\]\n[\s\S]*?(?=\n\[[^\n]+\]\n|$)`,
351
337
  "g"
352
338
  );
353
- const withoutLegacy = normalized.replace(legacyPattern, "");
354
- const withoutCurrent = withoutLegacy.replace(currentPattern, "");
339
+ const withoutCurrent = normalized.replace(currentPattern, "");
355
340
  const changed = withoutCurrent !== normalized;
356
341
  const text = changed ? `${trimTrailingBlankLines(withoutCurrent)}
357
342
  ` : rawConfig;
@@ -360,7 +345,6 @@ function removeCodexServerBlock(rawConfig, serverName) {
360
345
  function upsertCodexServerBlock(rawConfig, serverName, serverEntry) {
361
346
  const normalized = rawConfig.replace(/\r\n/g, "\n");
362
347
  const escaped = serverName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
363
- const legacyPattern = new RegExp(String.raw`\n?\[mcp\.servers\.${escaped}\]\n[\s\S]*?(?=\n\[[^\n]+\]\n|$)`, "g");
364
348
  const currentPattern = new RegExp(
365
349
  String.raw`\n?\[mcp_servers\.${escaped}\]\n[\s\S]*?(?=\n\[[^\n]+\]\n|$)`,
366
350
  "g"
@@ -368,8 +352,7 @@ function upsertCodexServerBlock(rawConfig, serverName, serverEntry) {
368
352
  const existingMatch = normalized.match(currentPattern);
369
353
  const preservedUserLines = existingMatch !== null && existingMatch.length > 0 ? extractCodexBlockUserLines(existingMatch[0]) : [];
370
354
  const block = serializeCodexServerBlock(serverName, serverEntry, preservedUserLines);
371
- const withoutLegacy = normalized.replace(legacyPattern, "");
372
- const withoutExisting = withoutLegacy.replace(currentPattern, "");
355
+ const withoutExisting = normalized.replace(currentPattern, "");
373
356
  const trimmed = trimTrailingBlankLines(withoutExisting);
374
357
  if (trimmed.length === 0) {
375
358
  return block;
@@ -474,12 +457,6 @@ function resolveClients(workspaceRoot, fabricConfig = {}, opts = {}) {
474
457
  (configuredPath) => new ClaudeCodeDesktopWriter(configuredPath),
475
458
  hasExplicitPath(clientPaths, "claudeCodeDesktop") ? clientPaths.claudeCodeDesktop : void 0
476
459
  );
477
- addIfDetected(
478
- writers,
479
- existsSync4(join4(workspaceRoot, ".cursor")),
480
- (configuredPath) => new CursorWriter(configuredPath),
481
- hasExplicitPath(clientPaths, "cursor") ? clientPaths.cursor : void 0
482
- );
483
460
  addIfDetected(
484
461
  writers,
485
462
  existsSync4(join4(homedir4(), ".codex")),
@@ -492,7 +469,6 @@ function detectClientSupports(workspaceRoot, fabricConfig = {}) {
492
469
  const clientPaths = fabricConfig.clientPaths;
493
470
  const claudeDetected = existsSync4(join4(homedir4(), ".claude")) || existsSync4(join4(workspaceRoot, ".claude"));
494
471
  const claudeDesktopDetected = existsSync4(getClaudeDesktopConfigPath());
495
- const cursorDetected = existsSync4(join4(workspaceRoot, ".cursor"));
496
472
  const codexDetected = existsSync4(join4(homedir4(), ".codex"));
497
473
  return [
498
474
  {
@@ -523,18 +499,6 @@ function detectClientSupports(workspaceRoot, fabricConfig = {}) {
523
499
  skill: false
524
500
  }
525
501
  },
526
- {
527
- clientKind: "Cursor",
528
- label: "Cursor",
529
- detected: cursorDetected || hasExplicitPath(clientPaths, "cursor"),
530
- configPath: ".cursor/mcp.json",
531
- capabilities: {
532
- bootstrap: true,
533
- mcp: true,
534
- hook: false,
535
- skill: false
536
- }
537
- },
538
502
  {
539
503
  clientKind: "CodexCLI",
540
504
  label: "Codex CLI",
@@ -554,6 +518,27 @@ function detectClientSupports(workspaceRoot, fabricConfig = {}) {
554
518
  // skills as uninstalled even right after installing them).
555
519
  skill: existsSync4(join4(workspaceRoot, ".codex", "skills"))
556
520
  }
521
+ },
522
+ {
523
+ clientKind: "CodexDesktop",
524
+ label: "Codex Desktop",
525
+ // Codex Desktop shares the same ~/.codex config as Codex CLI — there is no
526
+ // separate adapter work: installing the Codex CLI assets (MCP config /
527
+ // hooks / skills) makes Desktop ready too. Display-only row mirroring Codex
528
+ // CLI's detection + installed state; no dedicated writer (CodexTOMLConfigWriter
529
+ // already targets the shared config, so removing/adding it once covers both).
530
+ detected: codexDetected || hasExplicitPath(clientPaths, "codexCLI"),
531
+ configPath: "~/.codex/config.toml (shared with Codex CLI)",
532
+ capabilities: {
533
+ bootstrap: true,
534
+ mcp: true,
535
+ hook: true,
536
+ skill: true
537
+ },
538
+ installedCapabilities: {
539
+ hook: existsSync4(join4(workspaceRoot, ".codex", "hooks.json")),
540
+ skill: existsSync4(join4(workspaceRoot, ".codex", "skills"))
541
+ }
557
542
  }
558
543
  ];
559
544
  }