@fenglimg/fabric-cli 2.2.0-rc.4 → 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 (73) hide show
  1. package/README.md +8 -5
  2. package/dist/{chunk-5JG4QJLO.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-U5W4CX5I.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-5SSNE5GM.js → chunk-QPAW6IYT.js} +125 -39
  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-7UFLWRX7.js → status-4R3TM4FJ.js} +8 -5
  28. package/dist/{store-ZEZMQVG7.js → store-HOCORVL3.js} +96 -350
  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-3FRWYGML.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/install-7XJ64WSC.js +0 -2743
  72. package/templates/hooks/configs/cursor-hooks.json +0 -30
  73. package/templates/hooks/lib/cite-contract-reminder.cjs +0 -179
@@ -1,11 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  regenerateBindingsSnapshot
4
- } from "./chunk-H3FE6VIK.js";
4
+ } from "./chunk-PTGQAZEW.js";
5
5
  import "./chunk-EOT63RDH.js";
6
- import {
7
- getProjectTranslator
8
- } from "./chunk-2CY4BMTH.js";
9
6
  import {
10
7
  assertStoreMountable,
11
8
  resolveStoreDir,
@@ -18,16 +15,20 @@ import {
18
15
  storeProjectCreate,
19
16
  storeProjectList,
20
17
  storeRemove,
18
+ storeSetWriteRoute,
21
19
  storeSwitchWrite
22
- } from "./chunk-5SSNE5GM.js";
20
+ } from "./chunk-QPAW6IYT.js";
23
21
  import {
24
- loadGlobalConfig,
25
22
  loadProjectConfig
26
- } from "./chunk-XCBVSGCS.js";
23
+ } from "./chunk-QFIVFZRH.js";
24
+ import "./chunk-FNHDQTPC.js";
25
+ import {
26
+ getProjectTranslator
27
+ } from "./chunk-HORSMSZL.js";
27
28
 
28
29
  // src/commands/store.ts
29
30
  import { defineCommand } from "citty";
30
- import { join as join4 } from "path";
31
+ import { join as join3 } from "path";
31
32
 
32
33
  // src/store/scope-backfill.ts
33
34
  import { existsSync, readFileSync, readdirSync, writeFileSync } from "fs";
@@ -120,273 +121,18 @@ function backfillKnowledgeDir(knowledgeDir, options) {
120
121
  return report;
121
122
  }
122
123
 
123
- // src/store/store-migrate.ts
124
- import { execFileSync } from "child_process";
125
- import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, readdirSync as readdirSync2, rmSync, writeFileSync as writeFileSync2 } from "fs";
126
- import { basename, join as join2 } from "path";
124
+ // src/store/store-rescope.ts
125
+ import { existsSync as existsSync2, readFileSync as readFileSync2, readdirSync as readdirSync2, writeFileSync as writeFileSync2 } from "fs";
126
+ import { join as join2 } from "path";
127
127
  import {
128
128
  STORE_KNOWLEDGE_TYPE_DIRS as STORE_KNOWLEDGE_TYPE_DIRS2,
129
129
  STORE_LAYOUT,
130
- STORE_PENDING_DIR,
131
- buildStoreResolveInput,
132
- createStoreResolver,
133
- formatKnowledgeId,
134
- parseKnowledgeId as parseKnowledgeId2,
135
- reconcileStoreCounters,
136
- resolveGlobalRoot,
137
- storeRelativePath
138
- } from "@fenglimg/fabric-shared";
139
- function resolveTargetStore(layer, projectRoot, globalRoot) {
140
- const input = buildStoreResolveInput(projectRoot, globalRoot);
141
- if (input === null) {
142
- return null;
143
- }
144
- const scope = layer === "personal" ? "personal" : "team";
145
- const { target } = createStoreResolver().resolveWriteTarget(input, scope);
146
- if (target === null) {
147
- return null;
148
- }
149
- const alias = loadGlobalConfig(globalRoot)?.stores.find((s) => s.store_uuid === target.store_uuid)?.alias ?? target.store_uuid;
150
- return {
151
- uuid: target.store_uuid,
152
- alias,
153
- dir: join2(globalRoot, storeRelativePath(target.store_uuid))
154
- };
155
- }
156
- function listMd(dir) {
157
- if (!existsSync2(dir)) {
158
- return [];
159
- }
160
- return readdirSync2(dir).filter((name) => name.endsWith(".md")).sort();
161
- }
162
- function readId(content) {
163
- const match = content.match(/^id:\s*(\S+)\s*$/mu);
164
- return match ? match[1] : null;
165
- }
166
- function slugSuffix(fileName, oldId) {
167
- const stem = fileName.replace(/\.md$/u, "");
168
- if (oldId !== null && stem.startsWith(`${oldId}--`)) {
169
- return stem.slice(oldId.length);
170
- }
171
- return "";
172
- }
173
- function buildStoreIdIndex(storeDir) {
174
- const existing = /* @__PURE__ */ new Set();
175
- const maxCounter = /* @__PURE__ */ new Map();
176
- for (const type of STORE_KNOWLEDGE_TYPE_DIRS2) {
177
- const dir = join2(storeDir, STORE_LAYOUT.knowledgeDir, type);
178
- for (const file of listMd(dir)) {
179
- const content = readFileSync2(join2(dir, file), "utf8");
180
- const id = readId(content) ?? file.replace(/\.md$/u, "").split("--")[0];
181
- const parsed = parseKnowledgeId2(id);
182
- if (parsed === null) {
183
- continue;
184
- }
185
- existing.add(id);
186
- const key = id.slice(0, id.lastIndexOf("-"));
187
- maxCounter.set(key, Math.max(maxCounter.get(key) ?? 0, parsed.counter));
188
- }
189
- }
190
- return { existing, maxCounter };
191
- }
192
- function nextId(index, layer, type) {
193
- const probe = formatKnowledgeId(layer, type, 1);
194
- const key = probe.slice(0, probe.lastIndexOf("-"));
195
- let counter = (index.maxCounter.get(key) ?? 0) + 1;
196
- let id = formatKnowledgeId(layer, type, counter);
197
- while (index.existing.has(id)) {
198
- counter += 1;
199
- id = formatKnowledgeId(layer, type, counter);
200
- }
201
- index.existing.add(id);
202
- index.maxCounter.set(key, counter);
203
- return id;
204
- }
205
- function typeDirToKnowledgeType(typeDir) {
206
- return STORE_KNOWLEDGE_TYPE_DIRS2.includes(typeDir) ? typeDir : null;
207
- }
208
- function migrateProjectKnowledge(projectRoot, options = {}) {
209
- const dryRun = options.dryRun ?? false;
210
- const globalRoot = options.globalRoot ?? resolveGlobalRoot();
211
- const runGit = options.git ?? true;
212
- const items = [];
213
- const skips = [];
214
- const remap = {};
215
- const targets = {};
216
- const sourceRoots = {
217
- team: join2(projectRoot, ".fabric", "knowledge"),
218
- personal: join2(globalRoot, "knowledge")
219
- };
220
- const layerState = {};
221
- for (const layer of ["team", "personal"]) {
222
- if (!existsSync2(sourceRoots[layer])) {
223
- continue;
224
- }
225
- const target = resolveTargetStore(layer, projectRoot, globalRoot);
226
- if (target === null) {
227
- continue;
228
- }
229
- targets[layer] = { uuid: target.uuid, dir: target.dir };
230
- layerState[layer] = { target, index: buildStoreIdIndex(target.dir) };
231
- }
232
- for (const layer of ["team", "personal"]) {
233
- const root = sourceRoots[layer];
234
- if (!existsSync2(root)) {
235
- continue;
236
- }
237
- const state = layerState[layer];
238
- for (const typeDir of STORE_KNOWLEDGE_TYPE_DIRS2) {
239
- const dir = join2(root, typeDir);
240
- for (const file of listMd(dir)) {
241
- const source = join2(dir, file);
242
- if (state === void 0) {
243
- skips.push({
244
- source,
245
- reason: `no ${layer} write-target store \u2014 run \`fabric install --global\` then \`fabric store bind <alias>\`${layer === "team" ? " + `fabric store switch-write <alias>`" : ""}`
246
- });
247
- continue;
248
- }
249
- const content = readFileSync2(source, "utf8");
250
- const oldId = readId(content);
251
- const knowledgeType = typeDirToKnowledgeType(typeDir);
252
- let newId = null;
253
- if (oldId !== null && state.index.existing.has(oldId) && knowledgeType !== null) {
254
- const parsed = parseKnowledgeId2(oldId);
255
- const idLayer = parsed?.layer ?? layer;
256
- newId = nextId(state.index, idLayer, knowledgeType);
257
- remap[oldId] = newId;
258
- } else if (oldId !== null) {
259
- state.index.existing.add(oldId);
260
- const parsed = parseKnowledgeId2(oldId);
261
- if (parsed !== null) {
262
- const key = oldId.slice(0, oldId.lastIndexOf("-"));
263
- state.index.maxCounter.set(
264
- key,
265
- Math.max(state.index.maxCounter.get(key) ?? 0, parsed.counter)
266
- );
267
- }
268
- }
269
- const effectiveId = newId ?? oldId;
270
- const targetName = newId !== null && effectiveId !== null ? `${effectiveId}${slugSuffix(file, oldId)}.md` : file;
271
- const targetFile = join2(state.target.dir, STORE_LAYOUT.knowledgeDir, typeDir, targetName);
272
- items.push({
273
- source,
274
- layer,
275
- type: typeDir,
276
- oldId,
277
- newId,
278
- target: targetFile,
279
- storeUuid: state.target.uuid,
280
- alias: state.target.alias
281
- });
282
- }
283
- }
284
- const pendingRoot = join2(root, STORE_PENDING_DIR);
285
- for (const sub of [".", "decisions", "guidelines", "pitfalls", "models", "processes"]) {
286
- const dir = sub === "." ? pendingRoot : join2(pendingRoot, sub);
287
- for (const file of listMd(dir)) {
288
- const source = join2(dir, file);
289
- if (state === void 0) {
290
- skips.push({ source, reason: `no ${layer} write-target store` });
291
- continue;
292
- }
293
- const rel = sub === "." ? file : join2(sub, file);
294
- const targetFile = join2(
295
- state.target.dir,
296
- STORE_LAYOUT.knowledgeDir,
297
- STORE_PENDING_DIR,
298
- rel
299
- );
300
- if (existsSync2(targetFile)) {
301
- skips.push({ source, reason: `pending already present in store: ${basename(targetFile)}` });
302
- continue;
303
- }
304
- items.push({
305
- source,
306
- layer,
307
- type: STORE_PENDING_DIR,
308
- oldId: null,
309
- newId: null,
310
- target: targetFile,
311
- storeUuid: state.target.uuid,
312
- alias: state.target.alias
313
- });
314
- }
315
- }
316
- }
317
- if (dryRun || items.length === 0) {
318
- return { dryRun, committed: false, items, skips, remap, targets };
319
- }
320
- for (const item of items) {
321
- let content = readFileSync2(item.source, "utf8");
322
- if (item.newId !== null && item.oldId !== null) {
323
- content = content.replace(/^id:\s*\S+\s*$/mu, `id: ${item.newId}`);
324
- }
325
- content = rewriteRelated(content, remap);
326
- mkdirSync(join2(item.target, ".."), { recursive: true });
327
- writeFileSync2(item.target, content, "utf8");
328
- }
329
- for (const item of items) {
330
- rmSync(item.source, { force: true });
331
- }
332
- for (const info of Object.values(targets)) {
333
- if (items.some((i) => i.storeUuid === info.uuid)) {
334
- reconcileStoreCounters(info.dir);
335
- }
336
- }
337
- let committed = false;
338
- if (runGit) {
339
- for (const [layer, info] of Object.entries(targets)) {
340
- const moved = items.filter((i) => i.layer === layer).length;
341
- if (moved === 0) {
342
- continue;
343
- }
344
- committed = gitCommitStore(info.dir, moved) || committed;
345
- }
346
- }
347
- return { dryRun, committed, items, skips, remap, targets };
348
- }
349
- function rewriteRelated(content, remap) {
350
- if (Object.keys(remap).length === 0) {
351
- return content;
352
- }
353
- return content.replace(/^related:\s*\[(.*)\]\s*$/mu, (line, inner) => {
354
- const rewritten = inner.split(",").map((token) => {
355
- const trimmed = token.trim();
356
- return remap[trimmed] ?? trimmed;
357
- }).join(", ");
358
- return `related: [${rewritten}]`;
359
- });
360
- }
361
- function gitCommitStore(storeDir, count) {
362
- if (!existsSync2(join2(storeDir, ".git"))) {
363
- return false;
364
- }
365
- try {
366
- execFileSync("git", ["add", "-A"], { cwd: storeDir, stdio: ["ignore", "ignore", "pipe"] });
367
- execFileSync(
368
- "git",
369
- ["commit", "-m", `chore(migrate): import ${count} entries from project dual-root`],
370
- { cwd: storeDir, stdio: ["ignore", "ignore", "pipe"] }
371
- );
372
- return true;
373
- } catch {
374
- return false;
375
- }
376
- }
377
-
378
- // src/store/store-rescope.ts
379
- import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync as readdirSync3, writeFileSync as writeFileSync3 } from "fs";
380
- import { join as join3 } from "path";
381
- import {
382
- STORE_KNOWLEDGE_TYPE_DIRS as STORE_KNOWLEDGE_TYPE_DIRS3,
383
- STORE_LAYOUT as STORE_LAYOUT2,
384
130
  isPersonalScope as isPersonalScope2,
385
131
  readStoreProjects,
386
132
  scopeCoordinateSchema,
387
133
  scopeRoot
388
134
  } from "@fenglimg/fabric-shared";
389
- function validateToScope(toScope, storeDir, storeVisibility) {
135
+ async function validateToScope(toScope, storeDir, storeVisibility) {
390
136
  if (!scopeCoordinateSchema.safeParse(toScope).success) {
391
137
  return `invalid scope coordinate '${toScope}'`;
392
138
  }
@@ -395,7 +141,7 @@ function validateToScope(toScope, storeDir, storeVisibility) {
395
141
  }
396
142
  if (scopeRoot(toScope) === "project") {
397
143
  const projectId = toScope.split(":")[1] ?? "";
398
- if (projectId.length === 0 || !readStoreProjects(storeDir).some((p) => p.id === projectId)) {
144
+ if (projectId.length === 0 || !(await readStoreProjects(storeDir)).some((p) => p.id === projectId)) {
399
145
  return `project '${projectId}' is not registered in this store (run \`fabric store project add ${projectId}\` first)`;
400
146
  }
401
147
  }
@@ -413,7 +159,7 @@ function matchesSelection(options, id, currentScope) {
413
159
  }
414
160
  return true;
415
161
  }
416
- function rescopeStore(storeDir, toScope, options) {
162
+ async function rescopeStore(storeDir, toScope, options) {
417
163
  const report = {
418
164
  dryRun: options.dryRun === true,
419
165
  toScope,
@@ -422,15 +168,15 @@ function rescopeStore(storeDir, toScope, options) {
422
168
  unchanged: 0,
423
169
  skipped: []
424
170
  };
425
- const toScopeError = validateToScope(toScope, storeDir, options.storeVisibility);
426
- for (const type of STORE_KNOWLEDGE_TYPE_DIRS3) {
427
- const dir = join3(storeDir, STORE_LAYOUT2.knowledgeDir, type);
428
- if (!existsSync3(dir)) {
171
+ const toScopeError = await validateToScope(toScope, storeDir, options.storeVisibility);
172
+ for (const type of STORE_KNOWLEDGE_TYPE_DIRS2) {
173
+ const dir = join2(storeDir, STORE_LAYOUT.knowledgeDir, type);
174
+ if (!existsSync2(dir)) {
429
175
  continue;
430
176
  }
431
- for (const name of readdirSync3(dir).filter((n) => n.endsWith(".md")).sort()) {
432
- const file = join3(dir, name);
433
- const content = readFileSync3(file, "utf8");
177
+ for (const name of readdirSync2(dir).filter((n) => n.endsWith(".md")).sort()) {
178
+ const file = join2(dir, name);
179
+ const content = readFileSync2(file, "utf8");
434
180
  const match = FRONTMATTER_RE.exec(content);
435
181
  if (match === null) {
436
182
  report.skipped.push(file);
@@ -455,7 +201,7 @@ function rescopeStore(storeDir, toScope, options) {
455
201
  const after = content.slice(match.index + match[0].length);
456
202
  report.changes.push({ file, id, fromScope: currentScope, toScope });
457
203
  if (options.dryRun !== true) {
458
- writeFileSync3(file, `${before}---
204
+ writeFileSync2(file, `${before}---
459
205
  ${newBlock}
460
206
  ---${after}`, "utf8");
461
207
  }
@@ -463,16 +209,16 @@ ${newBlock}
463
209
  }
464
210
  return report;
465
211
  }
466
- function promoteProjectToTeam(storeDir, options) {
212
+ async function promoteProjectToTeam(storeDir, options) {
467
213
  const selection = options.projectId !== void 0 ? { fromScope: `project:${options.projectId}`, storeVisibility: options.storeVisibility, dryRun: options.dryRun } : { fromScopeRoot: "project", storeVisibility: options.storeVisibility, dryRun: options.dryRun };
468
214
  return rescopeStore(storeDir, "team", selection);
469
215
  }
470
216
 
471
217
  // src/commands/store.ts
472
218
  import {
473
- STORE_LAYOUT as STORE_LAYOUT3,
474
- loadGlobalConfig as loadGlobalConfig2,
475
- resolveGlobalRoot as resolveGlobalRoot2
219
+ STORE_LAYOUT as STORE_LAYOUT2,
220
+ loadGlobalConfig,
221
+ resolveGlobalRoot
476
222
  } from "@fenglimg/fabric-shared";
477
223
  var listCommand = defineCommand({
478
224
  meta: { name: "list", description: "List mounted knowledge stores" },
@@ -485,8 +231,10 @@ var listCommand = defineCommand({
485
231
  }
486
232
  const localOnly = t("cli.shared.local-only");
487
233
  for (const store of stores) {
488
- const realRemote = storeGitRemote(store.store_uuid);
489
- console.log(`${store.alias} ${store.store_uuid} ${realRemote ?? localOnly}`);
234
+ const realRemote = storeGitRemote(store.alias);
235
+ console.log(
236
+ `${store.alias} ${store.mount_name ?? store.store_uuid} ${store.store_uuid} ${realRemote ?? localOnly}`
237
+ );
490
238
  }
491
239
  }
492
240
  });
@@ -495,11 +243,21 @@ var addCommand = defineCommand({
495
243
  args: {
496
244
  uuid: { type: "string", required: true, description: "Intrinsic store UUID" },
497
245
  alias: { type: "string", required: true, description: "Local alias for this store" },
246
+ "mount-name": { type: "string", description: "Stable local directory under ~/.fabric/stores/" },
498
247
  remote: { type: "string", description: "Git remote locator (omit for local-only)" }
499
248
  },
500
- run({ args }) {
501
- assertStoreMountable(args.uuid);
502
- const store = args.remote === void 0 ? { store_uuid: args.uuid, alias: args.alias } : { store_uuid: args.uuid, alias: args.alias, remote: args.remote };
249
+ async run({ args }) {
250
+ assertStoreMountable(args.uuid, void 0, args["mount-name"]);
251
+ const store = args.remote === void 0 ? {
252
+ store_uuid: args.uuid,
253
+ alias: args.alias,
254
+ ...args["mount-name"] === void 0 ? {} : { mount_name: args["mount-name"] }
255
+ } : {
256
+ store_uuid: args.uuid,
257
+ alias: args.alias,
258
+ ...args["mount-name"] === void 0 ? {} : { mount_name: args["mount-name"] },
259
+ remote: args.remote
260
+ };
503
261
  const next = storeAdd(store);
504
262
  const t = getProjectTranslator();
505
263
  console.log(
@@ -514,10 +272,12 @@ var createCommand = defineCommand({
514
272
  meta: { name: "create", description: "Create a brand-new local knowledge store and mount it" },
515
273
  args: {
516
274
  alias: { type: "string", required: true, description: "Local alias for the new store" },
275
+ "mount-name": { type: "string", description: "Stable local directory under ~/.fabric/stores/" },
517
276
  remote: { type: "string", description: "Git remote to associate (push target; optional)" }
518
277
  },
519
- run({ args }) {
520
- const result = storeCreate(args.alias, (/* @__PURE__ */ new Date()).toISOString(), {
278
+ async run({ args }) {
279
+ const result = await storeCreate(args.alias, (/* @__PURE__ */ new Date()).toISOString(), {
280
+ ...args["mount-name"] === void 0 ? {} : { mountName: args["mount-name"] },
521
281
  ...args.remote === void 0 ? {} : { remote: args.remote }
522
282
  });
523
283
  const t = getProjectTranslator();
@@ -532,9 +292,12 @@ var removeCommand = defineCommand({
532
292
  args: {
533
293
  alias: { type: "positional", required: true, description: "Alias to detach" }
534
294
  },
535
- run({ args }) {
295
+ async run({ args }) {
536
296
  const { detached } = storeRemove(args.alias);
537
297
  const t = getProjectTranslator();
298
+ if (detached === null) {
299
+ process.exitCode = 1;
300
+ }
538
301
  console.log(
539
302
  detached === null ? t("cli.store.no-alias", { alias: args.alias }) : t("cli.store.detached", { alias: args.alias })
540
303
  );
@@ -547,6 +310,9 @@ var explainCommand = defineCommand({
547
310
  },
548
311
  run({ args }) {
549
312
  const explanation = storeExplain(args.alias);
313
+ if (explanation === null) {
314
+ process.exitCode = 1;
315
+ }
550
316
  console.log(
551
317
  explanation === null ? getProjectTranslator()("cli.store.no-alias", { alias: args.alias }) : JSON.stringify(explanation, null, 2)
552
318
  );
@@ -562,10 +328,10 @@ var bindCommand = defineCommand({
562
328
  description: "Bind this repo to a project:<id> in the store (must already exist)"
563
329
  }
564
330
  },
565
- run({ args }) {
331
+ async run({ args }) {
566
332
  const entry = args.remote === void 0 ? { id: args.id } : { id: args.id, suggested_remote: args.remote };
567
333
  const projectRoot = process.cwd();
568
- const next = storeBind(
334
+ const next = await storeBind(
569
335
  projectRoot,
570
336
  entry,
571
337
  args.project === void 0 ? {} : { project: args.project }
@@ -580,57 +346,27 @@ var bindCommand = defineCommand({
580
346
  }
581
347
  });
582
348
  var switchWriteCommand = defineCommand({
583
- meta: { name: "switch-write", description: "Set the active write store for non-personal scopes" },
349
+ meta: { name: "switch-write", description: "Set the default write store for non-personal scopes" },
584
350
  args: {
585
351
  alias: { type: "positional", required: true, description: "Alias of the store to write to" }
586
352
  },
587
- run({ args }) {
353
+ async run({ args }) {
588
354
  const projectRoot = process.cwd();
589
355
  storeSwitchWrite(projectRoot, args.alias);
356
+ regenerateBindingsSnapshot(projectRoot, { now: (/* @__PURE__ */ new Date()).toISOString() });
590
357
  console.log(getProjectTranslator(projectRoot)("cli.store.switch-write", { alias: args.alias }));
591
358
  }
592
359
  });
593
- var migrateCommand = defineCommand({
594
- meta: {
595
- name: "migrate",
596
- description: "Move project-local (dual-root) knowledge into the resolved write-target stores"
597
- },
360
+ var routeWriteCommand = defineCommand({
361
+ meta: { name: "route-write", description: "Route a semantic scope to a writable shared store" },
598
362
  args: {
599
- "dry-run": {
600
- type: "boolean",
601
- description: "Preview the move without writing anything"
602
- }
363
+ scope: { type: "positional", required: true, description: "Semantic scope, e.g. team or project:fabric-v2" },
364
+ alias: { type: "positional", required: true, description: "Alias of the shared store to write to" }
603
365
  },
604
366
  run({ args }) {
605
367
  const projectRoot = process.cwd();
606
- const t = getProjectTranslator(projectRoot);
607
- const dryRun = args["dry-run"] === true;
608
- const report = migrateProjectKnowledge(projectRoot, { dryRun });
609
- if (report.items.length === 0 && report.skips.length === 0) {
610
- console.log(t("cli.store.migrate.none"));
611
- return;
612
- }
613
- console.log(
614
- dryRun ? t("cli.store.migrate.dry-run-header") : t("cli.store.migrate.applied-header", { count: String(report.items.length) })
615
- );
616
- for (const item of report.items) {
617
- const id = item.newId ?? item.oldId ?? "(draft)";
618
- console.log(` ${item.layer}/${item.type} ${id} \u2192 ${item.alias}`);
619
- if (item.newId !== null && item.oldId !== null) {
620
- console.log(
621
- t("cli.store.migrate.remap-note", { oldId: item.oldId, newId: item.newId })
622
- );
623
- }
624
- }
625
- if (report.skips.length > 0) {
626
- console.log(t("cli.store.migrate.skips-header", { count: String(report.skips.length) }));
627
- for (const skip of report.skips) {
628
- console.log(` ${skip.source}: ${skip.reason}`);
629
- }
630
- }
631
- if (report.committed) {
632
- console.log(t("cli.store.migrate.committed"));
633
- }
368
+ storeSetWriteRoute(projectRoot, args.scope, args.alias);
369
+ console.log(`write route: ${args.scope} -> ${args.alias}`);
634
370
  }
635
371
  });
636
372
  var projectListCommand = defineCommand({
@@ -638,8 +374,8 @@ var projectListCommand = defineCommand({
638
374
  args: {
639
375
  store: { type: "positional", required: true, description: "Store alias/UUID" }
640
376
  },
641
- run({ args }) {
642
- const projects = storeProjectList(args.store);
377
+ async run({ args }) {
378
+ const projects = await storeProjectList(args.store);
643
379
  if (projects.length === 0) {
644
380
  console.log(`store '${args.store}' has no registered projects.`);
645
381
  return;
@@ -656,8 +392,8 @@ var projectCreateCommand = defineCommand({
656
392
  id: { type: "positional", required: true, description: "Project id (single [a-z0-9_-] segment)" },
657
393
  name: { type: "string", description: "Optional human-facing label" }
658
394
  },
659
- run({ args }) {
660
- const project = storeProjectCreate(args.store, args.id, (/* @__PURE__ */ new Date()).toISOString(), {
395
+ async run({ args }) {
396
+ const project = await storeProjectCreate(args.store, args.id, (/* @__PURE__ */ new Date()).toISOString(), {
661
397
  ...args.name === void 0 ? {} : { name: args.name }
662
398
  });
663
399
  console.log(`registered project '${project.id}' in store '${args.store}'.`);
@@ -676,26 +412,30 @@ var backfillScopeCommand = defineCommand({
676
412
  description: "Backfill semantic_scope + visibility_store on existing knowledge (repairs dirty layer)"
677
413
  },
678
414
  args: {
679
- store: { type: "string", description: "Backfill a mounted store's knowledge instead of the project" },
415
+ store: { type: "string", description: "Backfill a mounted store's knowledge" },
680
416
  "dry-run": { type: "boolean", description: "Preview changes without writing" }
681
417
  },
682
418
  run({ args }) {
683
419
  const dryRun = args["dry-run"] === true;
684
420
  let knowledgeDir;
685
421
  let visibilityStore;
686
- if (typeof args.store === "string" && args.store.length > 0) {
687
- const storeDir = resolveStoreDir(args.store);
422
+ const selectedStore = typeof args.store === "string" && args.store.length > 0 ? args.store : loadProjectConfig(process.cwd())?.active_write_store;
423
+ if (typeof selectedStore !== "string" || selectedStore.length === 0) {
424
+ console.error(
425
+ "no store selected for scope backfill; pass --store <alias> or run `fabric store switch-write <alias>`"
426
+ );
427
+ process.exitCode = 1;
428
+ return;
429
+ }
430
+ {
431
+ const storeDir = resolveStoreDir(selectedStore);
688
432
  if (storeDir === null) {
689
- console.error(`no mounted store '${args.store}'`);
433
+ console.error(`no mounted store '${selectedStore}'`);
690
434
  process.exitCode = 1;
691
435
  return;
692
436
  }
693
- knowledgeDir = join4(storeDir, STORE_LAYOUT3.knowledgeDir);
694
- visibilityStore = args.store;
695
- } else {
696
- const projectRoot = process.cwd();
697
- knowledgeDir = join4(projectRoot, ".fabric", "knowledge");
698
- visibilityStore = loadProjectConfig(projectRoot)?.active_write_store ?? "team";
437
+ knowledgeDir = join3(storeDir, STORE_LAYOUT2.knowledgeDir);
438
+ visibilityStore = selectedStore;
699
439
  }
700
440
  const report = backfillKnowledgeDir(knowledgeDir, { visibilityStore, dryRun });
701
441
  if (report.changes.length === 0) {
@@ -708,6 +448,12 @@ var backfillScopeCommand = defineCommand({
708
448
  for (const c of report.changes) {
709
449
  console.log(` ${c.id ?? "(no id)"} [${c.changed.join(", ")}]`);
710
450
  }
451
+ const scopeAssigned = report.changes.filter((c) => c.changed.includes("semantic_scope")).length;
452
+ if (scopeAssigned > 0) {
453
+ console.error(
454
+ `${dryRun ? "[dry-run] " : ""}note: ${scopeAssigned} entr${scopeAssigned === 1 ? "y" : "ies"} defaulted to semantic_scope: team. Demote project-specific ones with \`fabric store re-scope <store> --to project:<id> --id <id>\`.`
455
+ );
456
+ }
711
457
  }
712
458
  });
713
459
  function resolveStoreDirAndVisibility(aliasOrUuid) {
@@ -715,7 +461,7 @@ function resolveStoreDirAndVisibility(aliasOrUuid) {
715
461
  if (dir === null) {
716
462
  return null;
717
463
  }
718
- const store = loadGlobalConfig2(resolveGlobalRoot2())?.stores.find(
464
+ const store = loadGlobalConfig(resolveGlobalRoot())?.stores.find(
719
465
  (s) => s.alias === aliasOrUuid || s.store_uuid === aliasOrUuid
720
466
  );
721
467
  return { dir, visibility: store?.personal === true ? "personal" : "shared" };
@@ -752,7 +498,7 @@ var rescopeCommand = defineCommand({
752
498
  from: { type: "string", description: "Only entries currently at this semantic_scope" },
753
499
  "dry-run": { type: "boolean", description: "Preview changes without writing" }
754
500
  },
755
- run({ args }) {
501
+ async run({ args }) {
756
502
  const resolved = resolveStoreDirAndVisibility(args.store);
757
503
  if (resolved === null) {
758
504
  console.error(`no mounted store '${args.store}'`);
@@ -760,7 +506,7 @@ var rescopeCommand = defineCommand({
760
506
  return;
761
507
  }
762
508
  printRescopeReport(
763
- rescopeStore(resolved.dir, args.to, {
509
+ await rescopeStore(resolved.dir, args.to, {
764
510
  id: args.id,
765
511
  fromScope: args.from,
766
512
  storeVisibility: resolved.visibility,
@@ -779,7 +525,7 @@ var promoteCommand = defineCommand({
779
525
  project: { type: "string", description: "Only this project's entries (default: all project:*)" },
780
526
  "dry-run": { type: "boolean", description: "Preview changes without writing" }
781
527
  },
782
- run({ args }) {
528
+ async run({ args }) {
783
529
  const resolved = resolveStoreDirAndVisibility(args.store);
784
530
  if (resolved === null) {
785
531
  console.error(`no mounted store '${args.store}'`);
@@ -787,7 +533,7 @@ var promoteCommand = defineCommand({
787
533
  return;
788
534
  }
789
535
  printRescopeReport(
790
- promoteProjectToTeam(resolved.dir, {
536
+ await promoteProjectToTeam(resolved.dir, {
791
537
  projectId: args.project,
792
538
  storeVisibility: resolved.visibility,
793
539
  dryRun: args["dry-run"] === true
@@ -805,7 +551,7 @@ var store_default = defineCommand({
805
551
  explain: explainCommand,
806
552
  bind: bindCommand,
807
553
  "switch-write": switchWriteCommand,
808
- migrate: migrateCommand,
554
+ "route-write": routeWriteCommand,
809
555
  "backfill-scope": backfillScopeCommand,
810
556
  "re-scope": rescopeCommand,
811
557
  promote: promoteCommand,