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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/chunk-5JG4QJLO.js +64 -0
  2. package/dist/chunk-5SSNE5GM.js +301 -0
  3. package/dist/chunk-EOT63RDH.js +36 -0
  4. package/dist/{chunk-AOE6AYI7.js → chunk-F6ITRM7T.js} +2 -2
  5. package/dist/{chunk-WU6GAPKH.js → chunk-H3FE6VIK.js} +3 -5
  6. package/dist/chunk-XCBVSGCS.js +25 -0
  7. package/dist/{chunk-2R55HNVD.js → chunk-XHHCRDIR.js} +71 -6
  8. package/dist/{config-XYRBZJDU.js → config-VJMXCLXW.js} +1 -1
  9. package/dist/{doctor-YONYXDX6.js → doctor-U5W4CX5I.js} +118 -7
  10. package/dist/index.js +13 -12
  11. package/dist/{install-74ANPCCP.js → install-7XJ64WSC.js} +252 -246
  12. package/dist/{plan-context-hint-FC6P3WFE.js → plan-context-hint-CHVZGOZ5.js} +21 -8
  13. package/dist/{scope-explain-CDIZESP5.js → scope-explain-BWRWBCCP.js} +14 -4
  14. package/dist/{status-GLQWLWH6.js → status-7UFLWRX7.js} +17 -6
  15. package/dist/store-ZEZMQVG7.js +817 -0
  16. package/dist/{sync-UJ4BBCZJ.js → sync-EA5HZMXM.js} +165 -21
  17. package/dist/{uninstall-C3QXKOO6.js → uninstall-F75MPKQC.js} +27 -1
  18. package/dist/{whoami-2MLO4Y37.js → whoami-3FRWYGML.js} +16 -5
  19. package/package.json +3 -3
  20. package/templates/hooks/cite-policy-evict.cjs +412 -160
  21. package/templates/hooks/configs/claude-code.json +17 -2
  22. package/templates/hooks/configs/codex-hooks.json +14 -2
  23. package/templates/hooks/configs/cursor-hooks.json +14 -2
  24. package/templates/hooks/fabric-hint.cjs +151 -15
  25. package/templates/hooks/knowledge-hint-broad.cjs +12 -1
  26. package/templates/hooks/knowledge-hint-narrow.cjs +54 -1
  27. package/templates/hooks/post-tooluse-mutation.cjs +285 -0
  28. package/templates/hooks/session-end-marker.cjs +140 -0
  29. package/templates/skills/fabric-archive/SKILL.md +7 -1
  30. package/dist/chunk-4R2CYEA4.js +0 -116
  31. package/dist/chunk-L4Q55UC4.js +0 -52
  32. package/dist/chunk-LFIKMVY7.js +0 -27
  33. package/dist/chunk-RYAFBNES.js +0 -33
  34. package/dist/chunk-T5RPGCCM.js +0 -40
  35. package/dist/store-XB3ADT65.js +0 -144
@@ -0,0 +1,817 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ regenerateBindingsSnapshot
4
+ } from "./chunk-H3FE6VIK.js";
5
+ import "./chunk-EOT63RDH.js";
6
+ import {
7
+ getProjectTranslator
8
+ } from "./chunk-2CY4BMTH.js";
9
+ import {
10
+ assertStoreMountable,
11
+ resolveStoreDir,
12
+ storeAdd,
13
+ storeBind,
14
+ storeCreate,
15
+ storeExplain,
16
+ storeGitRemote,
17
+ storeList,
18
+ storeProjectCreate,
19
+ storeProjectList,
20
+ storeRemove,
21
+ storeSwitchWrite
22
+ } from "./chunk-5SSNE5GM.js";
23
+ import {
24
+ loadGlobalConfig,
25
+ loadProjectConfig
26
+ } from "./chunk-XCBVSGCS.js";
27
+
28
+ // src/commands/store.ts
29
+ import { defineCommand } from "citty";
30
+ import { join as join4 } from "path";
31
+
32
+ // src/store/scope-backfill.ts
33
+ import { existsSync, readFileSync, readdirSync, writeFileSync } from "fs";
34
+ import { join } from "path";
35
+ import {
36
+ STORE_KNOWLEDGE_TYPE_DIRS,
37
+ isPersonalScope,
38
+ parseKnowledgeId
39
+ } from "@fenglimg/fabric-shared";
40
+ var FRONTMATTER_RE = /^(?:)?---\r?\n([\s\S]*?)\r?\n---/u;
41
+ function readKey(block, key) {
42
+ const m = new RegExp(`^${key}:\\s*"?([^"\\n]+?)"?\\s*$`, "mu").exec(block);
43
+ return m?.[1];
44
+ }
45
+ function setKey(block, key, value, anchorKey) {
46
+ const lines = block.split(/\r?\n/u);
47
+ const idx = lines.findIndex((l) => new RegExp(`^${key}:`).test(l));
48
+ if (idx !== -1) {
49
+ lines[idx] = `${key}: ${value}`;
50
+ return lines.join("\n");
51
+ }
52
+ const anchorIdx = lines.findIndex((l) => new RegExp(`^${anchorKey}:`).test(l));
53
+ const at = anchorIdx === -1 ? lines.length - 1 : anchorIdx + 1;
54
+ lines.splice(at, 0, `${key}: ${value}`);
55
+ return lines.join("\n");
56
+ }
57
+ function backfillEntryContent(content, visibilityStore) {
58
+ const match = FRONTMATTER_RE.exec(content);
59
+ if (match === null) {
60
+ return null;
61
+ }
62
+ const block = match[1] ?? "";
63
+ const id = readKey(block, "id") ?? null;
64
+ const parsed = id === null ? null : parseKnowledgeId(id);
65
+ const declaredLayer = readKey(block, "layer");
66
+ const layer = parsed?.layer ?? (declaredLayer === "personal" ? "personal" : "team");
67
+ const semanticScope = layer === "personal" ? "personal" : "team";
68
+ const visibility = isPersonalScope(semanticScope) ? "personal" : visibilityStore;
69
+ const changed = [];
70
+ let newBlock = block;
71
+ if (declaredLayer !== layer) {
72
+ newBlock = setKey(newBlock, "layer", layer, "maturity");
73
+ changed.push("layer");
74
+ }
75
+ if (readKey(block, "semantic_scope") !== semanticScope) {
76
+ newBlock = setKey(newBlock, "semantic_scope", semanticScope, "layer");
77
+ changed.push("semantic_scope");
78
+ }
79
+ if (readKey(block, "visibility_store") !== visibility) {
80
+ newBlock = setKey(newBlock, "visibility_store", `"${visibility}"`, "semantic_scope");
81
+ changed.push("visibility_store");
82
+ }
83
+ if (changed.length === 0) {
84
+ return { content, change: { file: "", id, changed } };
85
+ }
86
+ const before = content.slice(0, match.index);
87
+ const after = content.slice(match.index + match[0].length);
88
+ return {
89
+ content: `${before}---
90
+ ${newBlock}
91
+ ---${after}`,
92
+ change: { file: "", id, changed }
93
+ };
94
+ }
95
+ function backfillKnowledgeDir(knowledgeDir, options) {
96
+ const report = { dryRun: options.dryRun === true, unchanged: 0, changes: [], skipped: [] };
97
+ for (const type of STORE_KNOWLEDGE_TYPE_DIRS) {
98
+ const dir = join(knowledgeDir, type);
99
+ if (!existsSync(dir)) {
100
+ continue;
101
+ }
102
+ for (const name of readdirSync(dir).filter((n) => n.endsWith(".md")).sort()) {
103
+ const file = join(dir, name);
104
+ const result = backfillEntryContent(readFileSync(file, "utf8"), options.visibilityStore);
105
+ if (result === null) {
106
+ report.skipped.push(file);
107
+ continue;
108
+ }
109
+ if (result.change.changed.length === 0) {
110
+ report.unchanged += 1;
111
+ continue;
112
+ }
113
+ result.change.file = file;
114
+ report.changes.push(result.change);
115
+ if (options.dryRun !== true) {
116
+ writeFileSync(file, result.content, "utf8");
117
+ }
118
+ }
119
+ }
120
+ return report;
121
+ }
122
+
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";
127
+ import {
128
+ STORE_KNOWLEDGE_TYPE_DIRS as STORE_KNOWLEDGE_TYPE_DIRS2,
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
+ isPersonalScope as isPersonalScope2,
385
+ readStoreProjects,
386
+ scopeCoordinateSchema,
387
+ scopeRoot
388
+ } from "@fenglimg/fabric-shared";
389
+ function validateToScope(toScope, storeDir, storeVisibility) {
390
+ if (!scopeCoordinateSchema.safeParse(toScope).success) {
391
+ return `invalid scope coordinate '${toScope}'`;
392
+ }
393
+ if (isPersonalScope2(toScope) && storeVisibility === "shared") {
394
+ return "refusing personal scope in a shared store (R5#3 privacy boundary)";
395
+ }
396
+ if (scopeRoot(toScope) === "project") {
397
+ const projectId = toScope.split(":")[1] ?? "";
398
+ if (projectId.length === 0 || !readStoreProjects(storeDir).some((p) => p.id === projectId)) {
399
+ return `project '${projectId}' is not registered in this store (run \`fabric store project add ${projectId}\` first)`;
400
+ }
401
+ }
402
+ return null;
403
+ }
404
+ function matchesSelection(options, id, currentScope) {
405
+ if (options.id !== void 0 && id !== options.id) {
406
+ return false;
407
+ }
408
+ if (options.fromScope !== void 0 && currentScope !== options.fromScope) {
409
+ return false;
410
+ }
411
+ if (options.fromScopeRoot !== void 0 && (currentScope === void 0 || scopeRoot(currentScope) !== options.fromScopeRoot)) {
412
+ return false;
413
+ }
414
+ return true;
415
+ }
416
+ function rescopeStore(storeDir, toScope, options) {
417
+ const report = {
418
+ dryRun: options.dryRun === true,
419
+ toScope,
420
+ changes: [],
421
+ refusals: [],
422
+ unchanged: 0,
423
+ skipped: []
424
+ };
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)) {
429
+ continue;
430
+ }
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");
434
+ const match = FRONTMATTER_RE.exec(content);
435
+ if (match === null) {
436
+ report.skipped.push(file);
437
+ continue;
438
+ }
439
+ const block = match[1] ?? "";
440
+ const id = readKey(block, "id") ?? null;
441
+ const currentScope = readKey(block, "semantic_scope");
442
+ if (!matchesSelection(options, id, currentScope)) {
443
+ continue;
444
+ }
445
+ if (currentScope === toScope) {
446
+ report.unchanged += 1;
447
+ continue;
448
+ }
449
+ if (toScopeError !== null) {
450
+ report.refusals.push({ file, id, reason: toScopeError });
451
+ continue;
452
+ }
453
+ const newBlock = setKey(block, "semantic_scope", toScope, "layer");
454
+ const before = content.slice(0, match.index);
455
+ const after = content.slice(match.index + match[0].length);
456
+ report.changes.push({ file, id, fromScope: currentScope, toScope });
457
+ if (options.dryRun !== true) {
458
+ writeFileSync3(file, `${before}---
459
+ ${newBlock}
460
+ ---${after}`, "utf8");
461
+ }
462
+ }
463
+ }
464
+ return report;
465
+ }
466
+ function promoteProjectToTeam(storeDir, options) {
467
+ 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
+ return rescopeStore(storeDir, "team", selection);
469
+ }
470
+
471
+ // src/commands/store.ts
472
+ import {
473
+ STORE_LAYOUT as STORE_LAYOUT3,
474
+ loadGlobalConfig as loadGlobalConfig2,
475
+ resolveGlobalRoot as resolveGlobalRoot2
476
+ } from "@fenglimg/fabric-shared";
477
+ var listCommand = defineCommand({
478
+ meta: { name: "list", description: "List mounted knowledge stores" },
479
+ run() {
480
+ const t = getProjectTranslator();
481
+ const stores = storeList();
482
+ if (stores.length === 0) {
483
+ console.log(t("cli.store.none-mounted"));
484
+ return;
485
+ }
486
+ const localOnly = t("cli.shared.local-only");
487
+ for (const store of stores) {
488
+ const realRemote = storeGitRemote(store.store_uuid);
489
+ console.log(`${store.alias} ${store.store_uuid} ${realRemote ?? localOnly}`);
490
+ }
491
+ }
492
+ });
493
+ var addCommand = defineCommand({
494
+ meta: { name: "add", description: "Mount a knowledge store into the global registry" },
495
+ args: {
496
+ uuid: { type: "string", required: true, description: "Intrinsic store UUID" },
497
+ alias: { type: "string", required: true, description: "Local alias for this store" },
498
+ remote: { type: "string", description: "Git remote locator (omit for local-only)" }
499
+ },
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 };
503
+ const next = storeAdd(store);
504
+ const t = getProjectTranslator();
505
+ console.log(
506
+ t("cli.store.mounted", {
507
+ alias: args.alias,
508
+ count: String(next.stores.length)
509
+ })
510
+ );
511
+ }
512
+ });
513
+ var createCommand = defineCommand({
514
+ meta: { name: "create", description: "Create a brand-new local knowledge store and mount it" },
515
+ args: {
516
+ alias: { type: "string", required: true, description: "Local alias for the new store" },
517
+ remote: { type: "string", description: "Git remote to associate (push target; optional)" }
518
+ },
519
+ run({ args }) {
520
+ const result = storeCreate(args.alias, (/* @__PURE__ */ new Date()).toISOString(), {
521
+ ...args.remote === void 0 ? {} : { remote: args.remote }
522
+ });
523
+ const t = getProjectTranslator();
524
+ console.log(
525
+ t("cli.store.created", { alias: args.alias, uuid: result.store_uuid, dir: result.storeDir }) + (args.remote === void 0 ? `
526
+ ${t("cli.store.created-local-hint")}` : "")
527
+ );
528
+ }
529
+ });
530
+ var removeCommand = defineCommand({
531
+ meta: { name: "remove", description: "Detach a store from the registry (does NOT delete it)" },
532
+ args: {
533
+ alias: { type: "positional", required: true, description: "Alias to detach" }
534
+ },
535
+ run({ args }) {
536
+ const { detached } = storeRemove(args.alias);
537
+ const t = getProjectTranslator();
538
+ console.log(
539
+ detached === null ? t("cli.store.no-alias", { alias: args.alias }) : t("cli.store.detached", { alias: args.alias })
540
+ );
541
+ }
542
+ });
543
+ var explainCommand = defineCommand({
544
+ meta: { name: "explain", description: "Explain how a store alias resolves" },
545
+ args: {
546
+ alias: { type: "positional", required: true, description: "Alias to explain" }
547
+ },
548
+ run({ args }) {
549
+ const explanation = storeExplain(args.alias);
550
+ console.log(
551
+ explanation === null ? getProjectTranslator()("cli.store.no-alias", { alias: args.alias }) : JSON.stringify(explanation, null, 2)
552
+ );
553
+ }
554
+ });
555
+ var bindCommand = defineCommand({
556
+ meta: { name: "bind", description: "Declare a required store on this project's config" },
557
+ args: {
558
+ id: { type: "positional", required: true, description: "Store alias/UUID to require" },
559
+ remote: { type: "string", description: "Suggested remote for clone onboarding" },
560
+ project: {
561
+ type: "string",
562
+ description: "Bind this repo to a project:<id> in the store (must already exist)"
563
+ }
564
+ },
565
+ run({ args }) {
566
+ const entry = args.remote === void 0 ? { id: args.id } : { id: args.id, suggested_remote: args.remote };
567
+ const projectRoot = process.cwd();
568
+ const next = storeBind(
569
+ projectRoot,
570
+ entry,
571
+ args.project === void 0 ? {} : { project: args.project }
572
+ );
573
+ console.log(
574
+ getProjectTranslator(projectRoot)("cli.store.bound", {
575
+ id: args.id,
576
+ count: String(next.required_stores?.length ?? 0)
577
+ })
578
+ );
579
+ regenerateBindingsSnapshot(projectRoot, { now: (/* @__PURE__ */ new Date()).toISOString() });
580
+ }
581
+ });
582
+ var switchWriteCommand = defineCommand({
583
+ meta: { name: "switch-write", description: "Set the active write store for non-personal scopes" },
584
+ args: {
585
+ alias: { type: "positional", required: true, description: "Alias of the store to write to" }
586
+ },
587
+ run({ args }) {
588
+ const projectRoot = process.cwd();
589
+ storeSwitchWrite(projectRoot, args.alias);
590
+ console.log(getProjectTranslator(projectRoot)("cli.store.switch-write", { alias: args.alias }));
591
+ }
592
+ });
593
+ var migrateCommand = defineCommand({
594
+ meta: {
595
+ name: "migrate",
596
+ description: "Move project-local (dual-root) knowledge into the resolved write-target stores"
597
+ },
598
+ args: {
599
+ "dry-run": {
600
+ type: "boolean",
601
+ description: "Preview the move without writing anything"
602
+ }
603
+ },
604
+ run({ args }) {
605
+ 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
+ }
634
+ }
635
+ });
636
+ var projectListCommand = defineCommand({
637
+ meta: { name: "list", description: "List projects registered in a store" },
638
+ args: {
639
+ store: { type: "positional", required: true, description: "Store alias/UUID" }
640
+ },
641
+ run({ args }) {
642
+ const projects = storeProjectList(args.store);
643
+ if (projects.length === 0) {
644
+ console.log(`store '${args.store}' has no registered projects.`);
645
+ return;
646
+ }
647
+ for (const p of projects) {
648
+ console.log(`${p.id}${p.name === void 0 ? "" : ` ${p.name}`}`);
649
+ }
650
+ }
651
+ });
652
+ var projectCreateCommand = defineCommand({
653
+ meta: { name: "create", description: "Register a new project in a store" },
654
+ args: {
655
+ store: { type: "positional", required: true, description: "Store alias/UUID" },
656
+ id: { type: "positional", required: true, description: "Project id (single [a-z0-9_-] segment)" },
657
+ name: { type: "string", description: "Optional human-facing label" }
658
+ },
659
+ run({ args }) {
660
+ const project = storeProjectCreate(args.store, args.id, (/* @__PURE__ */ new Date()).toISOString(), {
661
+ ...args.name === void 0 ? {} : { name: args.name }
662
+ });
663
+ console.log(`registered project '${project.id}' in store '${args.store}'.`);
664
+ }
665
+ });
666
+ var projectCommand = defineCommand({
667
+ meta: { name: "project", description: "Manage the projects a store serves" },
668
+ subCommands: {
669
+ list: projectListCommand,
670
+ create: projectCreateCommand
671
+ }
672
+ });
673
+ var backfillScopeCommand = defineCommand({
674
+ meta: {
675
+ name: "backfill-scope",
676
+ description: "Backfill semantic_scope + visibility_store on existing knowledge (repairs dirty layer)"
677
+ },
678
+ args: {
679
+ store: { type: "string", description: "Backfill a mounted store's knowledge instead of the project" },
680
+ "dry-run": { type: "boolean", description: "Preview changes without writing" }
681
+ },
682
+ run({ args }) {
683
+ const dryRun = args["dry-run"] === true;
684
+ let knowledgeDir;
685
+ let visibilityStore;
686
+ if (typeof args.store === "string" && args.store.length > 0) {
687
+ const storeDir = resolveStoreDir(args.store);
688
+ if (storeDir === null) {
689
+ console.error(`no mounted store '${args.store}'`);
690
+ process.exitCode = 1;
691
+ return;
692
+ }
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";
699
+ }
700
+ const report = backfillKnowledgeDir(knowledgeDir, { visibilityStore, dryRun });
701
+ if (report.changes.length === 0) {
702
+ console.log(`scope backfill: nothing to do (${report.unchanged} already consistent).`);
703
+ return;
704
+ }
705
+ console.log(
706
+ `${dryRun ? "[dry-run] " : ""}scope backfill: ${report.changes.length} entr${report.changes.length === 1 ? "y" : "ies"} updated, ${report.unchanged} unchanged.`
707
+ );
708
+ for (const c of report.changes) {
709
+ console.log(` ${c.id ?? "(no id)"} [${c.changed.join(", ")}]`);
710
+ }
711
+ }
712
+ });
713
+ function resolveStoreDirAndVisibility(aliasOrUuid) {
714
+ const dir = resolveStoreDir(aliasOrUuid);
715
+ if (dir === null) {
716
+ return null;
717
+ }
718
+ const store = loadGlobalConfig2(resolveGlobalRoot2())?.stores.find(
719
+ (s) => s.alias === aliasOrUuid || s.store_uuid === aliasOrUuid
720
+ );
721
+ return { dir, visibility: store?.personal === true ? "personal" : "shared" };
722
+ }
723
+ function printRescopeReport(report) {
724
+ const prefix = report.dryRun ? "[dry-run] " : "";
725
+ if (report.changes.length === 0 && report.refusals.length === 0) {
726
+ console.log(`re-scope: nothing to do (${report.unchanged} already at '${report.toScope}').`);
727
+ } else if (report.changes.length > 0) {
728
+ console.log(
729
+ `${prefix}re-scope \u2192 ${report.toScope}: ${report.changes.length} entr${report.changes.length === 1 ? "y" : "ies"} updated, ${report.unchanged} unchanged.`
730
+ );
731
+ for (const c of report.changes) {
732
+ console.log(` ${c.id ?? "(no id)"} ${c.fromScope ?? "(none)"} \u2192 ${c.toScope}`);
733
+ }
734
+ }
735
+ if (report.refusals.length > 0) {
736
+ console.error(`${report.refusals.length} entr${report.refusals.length === 1 ? "y" : "ies"} refused:`);
737
+ for (const r of report.refusals) {
738
+ console.error(` ${r.id ?? "(no id)"}: ${r.reason}`);
739
+ }
740
+ process.exitCode = 1;
741
+ }
742
+ }
743
+ var rescopeCommand = defineCommand({
744
+ meta: {
745
+ name: "re-scope",
746
+ description: "Rewrite knowledge entries' semantic_scope coordinate in a store"
747
+ },
748
+ args: {
749
+ store: { type: "positional", required: true, description: "Target store alias or uuid" },
750
+ to: { type: "string", required: true, description: "New semantic_scope (e.g. team, project:alpha)" },
751
+ id: { type: "string", description: "Only the entry with this stable_id" },
752
+ from: { type: "string", description: "Only entries currently at this semantic_scope" },
753
+ "dry-run": { type: "boolean", description: "Preview changes without writing" }
754
+ },
755
+ run({ args }) {
756
+ const resolved = resolveStoreDirAndVisibility(args.store);
757
+ if (resolved === null) {
758
+ console.error(`no mounted store '${args.store}'`);
759
+ process.exitCode = 1;
760
+ return;
761
+ }
762
+ printRescopeReport(
763
+ rescopeStore(resolved.dir, args.to, {
764
+ id: args.id,
765
+ fromScope: args.from,
766
+ storeVisibility: resolved.visibility,
767
+ dryRun: args["dry-run"] === true
768
+ })
769
+ );
770
+ }
771
+ });
772
+ var promoteCommand = defineCommand({
773
+ meta: {
774
+ name: "promote",
775
+ description: "Promote project-scoped entries to team scope (project absorption)"
776
+ },
777
+ args: {
778
+ store: { type: "positional", required: true, description: "Target store alias or uuid" },
779
+ project: { type: "string", description: "Only this project's entries (default: all project:*)" },
780
+ "dry-run": { type: "boolean", description: "Preview changes without writing" }
781
+ },
782
+ run({ args }) {
783
+ const resolved = resolveStoreDirAndVisibility(args.store);
784
+ if (resolved === null) {
785
+ console.error(`no mounted store '${args.store}'`);
786
+ process.exitCode = 1;
787
+ return;
788
+ }
789
+ printRescopeReport(
790
+ promoteProjectToTeam(resolved.dir, {
791
+ projectId: args.project,
792
+ storeVisibility: resolved.visibility,
793
+ dryRun: args["dry-run"] === true
794
+ })
795
+ );
796
+ }
797
+ });
798
+ var store_default = defineCommand({
799
+ meta: { name: "store", description: "Manage mounted Fabric knowledge stores" },
800
+ subCommands: {
801
+ list: listCommand,
802
+ create: createCommand,
803
+ add: addCommand,
804
+ remove: removeCommand,
805
+ explain: explainCommand,
806
+ bind: bindCommand,
807
+ "switch-write": switchWriteCommand,
808
+ migrate: migrateCommand,
809
+ "backfill-scope": backfillScopeCommand,
810
+ "re-scope": rescopeCommand,
811
+ promote: promoteCommand,
812
+ project: projectCommand
813
+ }
814
+ });
815
+ export {
816
+ store_default as default
817
+ };