@fenglimg/fabric-cli 2.2.0-rc.4 → 2.2.0-rc.9
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.
- package/README.md +8 -5
- package/dist/{chunk-5JG4QJLO.js → chunk-27HK6H5Y.js} +10 -5
- package/dist/{chunk-F6ITRM7T.js → chunk-2KBCTMID.js} +29 -6
- package/dist/chunk-3D7B2UAZ.js +149 -0
- package/dist/{chunk-XC5RUHLK.js → chunk-3IOLS5EK.js} +23 -38
- package/dist/{chunk-XHHCRDIR.js → chunk-7ZDXBOOU.js} +174 -211
- package/dist/{doctor-U5W4CX5I.js → chunk-E7HJUU34.js} +103 -51
- package/dist/{chunk-XCBVSGCS.js → chunk-FNHDQTPC.js} +1 -10
- package/dist/{chunk-2CY4BMTH.js → chunk-HORSMSZL.js} +9 -5
- package/dist/{chunk-BO4XIZWZ.js → chunk-NLNH64A3.js} +5 -18
- package/dist/{chunk-H3FE6VIK.js → chunk-PTGQAZEW.js} +13 -3
- package/dist/chunk-QFIVFZRH.js +13 -0
- package/dist/{chunk-5SSNE5GM.js → chunk-QPAW6IYT.js} +125 -39
- package/dist/{chunk-COI5VDFU.js → chunk-WA3DYGSY.js} +1 -2
- package/dist/{plan-context-hint-CHVZGOZ5.js → chunk-YM4XATJF.js} +29 -4
- package/dist/{config-VJMXCLXW.js → config-A3LTECAY.js} +4 -3
- package/dist/context-7NUKXDB6.js +117 -0
- package/dist/doctor-MDTZWKBK.js +24 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +131 -21
- package/dist/info-7FKBTMVO.js +139 -0
- package/dist/install-v2-I6PJ6IFT.js +3279 -0
- package/dist/{metrics-RER6NLFC.js → metrics-HMFH4YHK.js} +1 -1
- package/dist/{onboard-coverage-JWQWDZW7.js → onboard-coverage-XSG77LL3.js} +48 -27
- package/dist/plan-context-hint-G75R4P4J.js +12 -0
- package/dist/{scope-explain-BWRWBCCP.js → scope-explain-HLJZ2M33.js} +3 -2
- package/dist/{status-7UFLWRX7.js → status-4R3TM4FJ.js} +8 -5
- package/dist/{store-ZEZMQVG7.js → store-HOCORVL3.js} +96 -350
- package/dist/{sync-EA5HZMXM.js → sync-DT5UJMMR.js} +36 -13
- package/dist/{uninstall-F75MPKQC.js → uninstall-IFN2KYBK.js} +71 -140
- package/dist/{whoami-3FRWYGML.js → whoami-ITGEFWH4.js} +9 -7
- package/package.json +7 -5
- package/templates/hooks/cite-policy-evict.cjs +5 -5
- package/templates/hooks/configs/README.md +14 -27
- package/templates/hooks/configs/claude-code.json +1 -1
- package/templates/hooks/configs/codex-hooks.json +3 -3
- package/templates/hooks/fabric-hint.cjs +326 -161
- package/templates/hooks/knowledge-hint-broad.cjs +431 -271
- package/templates/hooks/knowledge-hint-narrow.cjs +64 -77
- package/templates/hooks/lib/banner-i18n.cjs +31 -0
- package/templates/hooks/lib/bindings-snapshot-reader.cjs +118 -7
- package/templates/hooks/lib/cite-line-parser.cjs +12 -20
- package/templates/hooks/lib/client-adapter.cjs +66 -7
- package/templates/hooks/lib/nudge-policy.cjs +117 -0
- package/templates/hooks/lib/state-store.cjs +60 -0
- package/templates/hooks/post-tooluse-mutation.cjs +112 -11
- package/templates/skills/fabric/SKILL.md +100 -0
- package/templates/skills/fabric-archive/SKILL.md +29 -26
- package/templates/skills/fabric-archive/ref/dry-run-scope.md +1 -1
- package/templates/skills/fabric-archive/ref/i18n-policy.md +2 -3
- package/templates/skills/fabric-archive/ref/phase-1-5-onboard.md +2 -3
- package/templates/skills/fabric-archive/ref/phase-1-cross-session.md +1 -1
- package/templates/skills/fabric-archive/ref/phase-2-5-viability.md +1 -1
- package/templates/skills/fabric-archive/ref/phase-3-6-related-edges.md +18 -0
- package/templates/skills/fabric-archive/ref/phase-3-7-semantic-scope.md +47 -0
- package/templates/skills/fabric-audit/SKILL.md +13 -3
- package/templates/skills/fabric-connect/SKILL.md +3 -3
- package/templates/skills/fabric-import/SKILL.md +7 -7
- package/templates/skills/fabric-import/ref/i18n-policy.md +2 -3
- package/templates/skills/fabric-import/ref/state-recovery.md +1 -2
- package/templates/skills/fabric-review/SKILL.md +5 -5
- package/templates/skills/fabric-review/ref/cite-contract.md +1 -1
- package/templates/skills/fabric-review/ref/i18n-policy.md +2 -3
- package/templates/skills/fabric-review/ref/output-contract.md +1 -1
- package/templates/skills/fabric-review/ref/per-mode-flows.md +2 -2
- package/templates/skills/fabric-review/ref/worked-examples.md +1 -1
- package/templates/skills/fabric-store/SKILL.md +1 -1
- package/templates/skills/fabric-sync/SKILL.md +1 -1
- package/templates/skills/lib/shared-policy.md +2 -2
- package/dist/install-7XJ64WSC.js +0 -2743
- package/templates/hooks/configs/cursor-hooks.json +0 -30
- package/templates/hooks/lib/cite-contract-reminder.cjs +0 -179
- package/templates/hooks/lib/summary-fallback.cjs +0 -210
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
regenerateBindingsSnapshot
|
|
4
|
-
} from "./chunk-
|
|
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-
|
|
20
|
+
} from "./chunk-QPAW6IYT.js";
|
|
23
21
|
import {
|
|
24
|
-
loadGlobalConfig,
|
|
25
22
|
loadProjectConfig
|
|
26
|
-
} from "./chunk-
|
|
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
|
|
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-
|
|
124
|
-
import {
|
|
125
|
-
import {
|
|
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
|
|
427
|
-
const dir =
|
|
428
|
-
if (!
|
|
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
|
|
432
|
-
const file =
|
|
433
|
-
const content =
|
|
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
|
-
|
|
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
|
|
474
|
-
loadGlobalConfig
|
|
475
|
-
resolveGlobalRoot
|
|
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.
|
|
489
|
-
console.log(
|
|
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 ? {
|
|
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
|
|
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
|
|
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
|
-
"
|
|
600
|
-
|
|
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
|
-
|
|
607
|
-
|
|
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
|
|
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
|
-
|
|
687
|
-
|
|
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 '${
|
|
433
|
+
console.error(`no mounted store '${selectedStore}'`);
|
|
690
434
|
process.exitCode = 1;
|
|
691
435
|
return;
|
|
692
436
|
}
|
|
693
|
-
knowledgeDir =
|
|
694
|
-
visibilityStore =
|
|
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 =
|
|
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
|
-
|
|
554
|
+
"route-write": routeWriteCommand,
|
|
809
555
|
"backfill-scope": backfillScopeCommand,
|
|
810
556
|
"re-scope": rescopeCommand,
|
|
811
557
|
promote: promoteCommand,
|