@fenglimg/fabric-cli 2.1.0-rc.2 → 2.2.0-rc.10
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-27HK6H5Y.js +69 -0
- package/dist/{chunk-BATF4PEJ.js → chunk-2KBCTMID.js} +31 -8
- package/dist/chunk-3D7B2UAZ.js +149 -0
- package/dist/{chunk-MF3OTILQ.js → chunk-3IOLS5EK.js} +48 -42
- package/dist/{plan-context-hint-FC6P3WFE.js → chunk-722JU5BP.js} +52 -12
- package/dist/{chunk-F46ORPOA.js → chunk-7ZDXBOOU.js} +271 -166
- package/dist/{doctor-QVNPHLJK.js → chunk-E7HJUU34.js} +248 -72
- package/dist/chunk-EOT63RDH.js +36 -0
- package/dist/chunk-FNHDQTPC.js +16 -0
- package/dist/chunk-HORSMSZL.js +26 -0
- package/dist/chunk-NLNH64A3.js +43 -0
- package/dist/{chunk-WU6GAPKH.js → chunk-PTGQAZEW.js} +12 -4
- package/dist/chunk-QFIVFZRH.js +13 -0
- package/dist/chunk-QPAW6IYT.js +387 -0
- package/dist/{chunk-COI5VDFU.js → chunk-WA3DYGSY.js} +1 -2
- package/dist/{config-XJIPZNUP.js → config-A3LTECAY.js} +4 -3
- package/dist/context-UJCGYOT6.js +117 -0
- package/dist/doctor-MDTZWKBK.js +24 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +167 -16
- package/dist/info-7FKBTMVO.js +139 -0
- package/dist/install-v2-RINEA24K.js +3279 -0
- package/dist/{metrics-ACEQFPDU.js → metrics-HMFH4YHK.js} +22 -9
- package/dist/{onboard-coverage-MFCAEBDO.js → onboard-coverage-XSG77LL3.js} +48 -27
- package/dist/plan-context-hint-5TNGH3R4.js +12 -0
- package/dist/scope-explain-HLJZ2M33.js +48 -0
- package/dist/status-4R3TM4FJ.js +37 -0
- package/dist/store-HOCORVL3.js +563 -0
- package/dist/sync-DT5UJMMR.js +418 -0
- package/dist/{uninstall-TAXSUSKH.js → uninstall-IFN2KYBK.js} +128 -140
- package/dist/whoami-ITGEFWH4.js +49 -0
- package/package.json +7 -5
- package/templates/hooks/cite-policy-evict.cjs +412 -160
- package/templates/hooks/configs/README.md +14 -27
- package/templates/hooks/configs/claude-code.json +17 -2
- package/templates/hooks/configs/codex-hooks.json +15 -3
- package/templates/hooks/fabric-hint.cjs +573 -180
- package/templates/hooks/knowledge-hint-broad.cjs +648 -190
- package/templates/hooks/knowledge-hint-narrow.cjs +123 -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/injection-log.cjs +91 -0
- package/templates/hooks/lib/nudge-policy.cjs +117 -0
- package/templates/hooks/lib/state-store.cjs +90 -11
- package/templates/hooks/post-tooluse-mutation.cjs +386 -0
- package/templates/hooks/session-end-marker.cjs +140 -0
- package/templates/skills/fabric/SKILL.md +100 -0
- package/templates/skills/fabric-archive/SKILL.md +35 -24
- 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 +63 -0
- package/templates/skills/fabric-connect/SKILL.md +48 -0
- 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 +16 -5
- package/templates/skills/fabric-review/ref/cite-contract.md +56 -0
- 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 +44 -0
- package/templates/skills/fabric-sync/SKILL.md +1 -1
- package/templates/skills/lib/shared-policy.md +2 -2
- package/dist/chunk-HFQVXY6P.js +0 -86
- package/dist/chunk-L4Q55UC4.js +0 -52
- package/dist/chunk-LFIKMVY7.js +0 -27
- package/dist/chunk-PWLW3B57.js +0 -18
- package/dist/chunk-RYAFBNES.js +0 -33
- package/dist/chunk-T5RPGCCM.js +0 -40
- package/dist/chunk-WWNXR34K.js +0 -49
- package/dist/install-2HDO5FTQ.js +0 -2683
- package/dist/scope-explain-2F2R5URO.js +0 -33
- package/dist/status-GLQWLWH6.js +0 -23
- package/dist/store-XTSE5TY6.js +0 -105
- package/dist/sync-BJCWDPNC.js +0 -245
- package/dist/whoami-B6AEMSEV.js +0 -31
- package/templates/hooks/configs/cursor-hooks.json +0 -18
- package/templates/hooks/lib/cite-contract-reminder.cjs +0 -179
- package/templates/hooks/lib/summary-fallback.cjs +0 -210
|
@@ -0,0 +1,563 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
regenerateBindingsSnapshot
|
|
4
|
+
} from "./chunk-PTGQAZEW.js";
|
|
5
|
+
import "./chunk-EOT63RDH.js";
|
|
6
|
+
import {
|
|
7
|
+
assertStoreMountable,
|
|
8
|
+
resolveStoreDir,
|
|
9
|
+
storeAdd,
|
|
10
|
+
storeBind,
|
|
11
|
+
storeCreate,
|
|
12
|
+
storeExplain,
|
|
13
|
+
storeGitRemote,
|
|
14
|
+
storeList,
|
|
15
|
+
storeProjectCreate,
|
|
16
|
+
storeProjectList,
|
|
17
|
+
storeRemove,
|
|
18
|
+
storeSetWriteRoute,
|
|
19
|
+
storeSwitchWrite
|
|
20
|
+
} from "./chunk-QPAW6IYT.js";
|
|
21
|
+
import {
|
|
22
|
+
loadProjectConfig
|
|
23
|
+
} from "./chunk-QFIVFZRH.js";
|
|
24
|
+
import "./chunk-FNHDQTPC.js";
|
|
25
|
+
import {
|
|
26
|
+
getProjectTranslator
|
|
27
|
+
} from "./chunk-HORSMSZL.js";
|
|
28
|
+
|
|
29
|
+
// src/commands/store.ts
|
|
30
|
+
import { defineCommand } from "citty";
|
|
31
|
+
import { join as join3 } from "path";
|
|
32
|
+
|
|
33
|
+
// src/store/scope-backfill.ts
|
|
34
|
+
import { existsSync, readFileSync, readdirSync, writeFileSync } from "fs";
|
|
35
|
+
import { join } from "path";
|
|
36
|
+
import {
|
|
37
|
+
STORE_KNOWLEDGE_TYPE_DIRS,
|
|
38
|
+
isPersonalScope,
|
|
39
|
+
parseKnowledgeId
|
|
40
|
+
} from "@fenglimg/fabric-shared";
|
|
41
|
+
var FRONTMATTER_RE = /^(?:)?---\r?\n([\s\S]*?)\r?\n---/u;
|
|
42
|
+
function readKey(block, key) {
|
|
43
|
+
const m = new RegExp(`^${key}:\\s*"?([^"\\n]+?)"?\\s*$`, "mu").exec(block);
|
|
44
|
+
return m?.[1];
|
|
45
|
+
}
|
|
46
|
+
function setKey(block, key, value, anchorKey) {
|
|
47
|
+
const lines = block.split(/\r?\n/u);
|
|
48
|
+
const idx = lines.findIndex((l) => new RegExp(`^${key}:`).test(l));
|
|
49
|
+
if (idx !== -1) {
|
|
50
|
+
lines[idx] = `${key}: ${value}`;
|
|
51
|
+
return lines.join("\n");
|
|
52
|
+
}
|
|
53
|
+
const anchorIdx = lines.findIndex((l) => new RegExp(`^${anchorKey}:`).test(l));
|
|
54
|
+
const at = anchorIdx === -1 ? lines.length - 1 : anchorIdx + 1;
|
|
55
|
+
lines.splice(at, 0, `${key}: ${value}`);
|
|
56
|
+
return lines.join("\n");
|
|
57
|
+
}
|
|
58
|
+
function backfillEntryContent(content, visibilityStore) {
|
|
59
|
+
const match = FRONTMATTER_RE.exec(content);
|
|
60
|
+
if (match === null) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
const block = match[1] ?? "";
|
|
64
|
+
const id = readKey(block, "id") ?? null;
|
|
65
|
+
const parsed = id === null ? null : parseKnowledgeId(id);
|
|
66
|
+
const declaredLayer = readKey(block, "layer");
|
|
67
|
+
const layer = parsed?.layer ?? (declaredLayer === "personal" ? "personal" : "team");
|
|
68
|
+
const semanticScope = layer === "personal" ? "personal" : "team";
|
|
69
|
+
const visibility = isPersonalScope(semanticScope) ? "personal" : visibilityStore;
|
|
70
|
+
const changed = [];
|
|
71
|
+
let newBlock = block;
|
|
72
|
+
if (declaredLayer !== layer) {
|
|
73
|
+
newBlock = setKey(newBlock, "layer", layer, "maturity");
|
|
74
|
+
changed.push("layer");
|
|
75
|
+
}
|
|
76
|
+
if (readKey(block, "semantic_scope") !== semanticScope) {
|
|
77
|
+
newBlock = setKey(newBlock, "semantic_scope", semanticScope, "layer");
|
|
78
|
+
changed.push("semantic_scope");
|
|
79
|
+
}
|
|
80
|
+
if (readKey(block, "visibility_store") !== visibility) {
|
|
81
|
+
newBlock = setKey(newBlock, "visibility_store", `"${visibility}"`, "semantic_scope");
|
|
82
|
+
changed.push("visibility_store");
|
|
83
|
+
}
|
|
84
|
+
if (changed.length === 0) {
|
|
85
|
+
return { content, change: { file: "", id, changed } };
|
|
86
|
+
}
|
|
87
|
+
const before = content.slice(0, match.index);
|
|
88
|
+
const after = content.slice(match.index + match[0].length);
|
|
89
|
+
return {
|
|
90
|
+
content: `${before}---
|
|
91
|
+
${newBlock}
|
|
92
|
+
---${after}`,
|
|
93
|
+
change: { file: "", id, changed }
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function backfillKnowledgeDir(knowledgeDir, options) {
|
|
97
|
+
const report = { dryRun: options.dryRun === true, unchanged: 0, changes: [], skipped: [] };
|
|
98
|
+
for (const type of STORE_KNOWLEDGE_TYPE_DIRS) {
|
|
99
|
+
const dir = join(knowledgeDir, type);
|
|
100
|
+
if (!existsSync(dir)) {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
for (const name of readdirSync(dir).filter((n) => n.endsWith(".md")).sort()) {
|
|
104
|
+
const file = join(dir, name);
|
|
105
|
+
const result = backfillEntryContent(readFileSync(file, "utf8"), options.visibilityStore);
|
|
106
|
+
if (result === null) {
|
|
107
|
+
report.skipped.push(file);
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (result.change.changed.length === 0) {
|
|
111
|
+
report.unchanged += 1;
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
result.change.file = file;
|
|
115
|
+
report.changes.push(result.change);
|
|
116
|
+
if (options.dryRun !== true) {
|
|
117
|
+
writeFileSync(file, result.content, "utf8");
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return report;
|
|
122
|
+
}
|
|
123
|
+
|
|
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
|
+
import {
|
|
128
|
+
STORE_KNOWLEDGE_TYPE_DIRS as STORE_KNOWLEDGE_TYPE_DIRS2,
|
|
129
|
+
STORE_LAYOUT,
|
|
130
|
+
isPersonalScope as isPersonalScope2,
|
|
131
|
+
readStoreProjects,
|
|
132
|
+
scopeCoordinateSchema,
|
|
133
|
+
scopeRoot
|
|
134
|
+
} from "@fenglimg/fabric-shared";
|
|
135
|
+
async function validateToScope(toScope, storeDir, storeVisibility) {
|
|
136
|
+
if (!scopeCoordinateSchema.safeParse(toScope).success) {
|
|
137
|
+
return `invalid scope coordinate '${toScope}'`;
|
|
138
|
+
}
|
|
139
|
+
if (isPersonalScope2(toScope) && storeVisibility === "shared") {
|
|
140
|
+
return "refusing personal scope in a shared store (R5#3 privacy boundary)";
|
|
141
|
+
}
|
|
142
|
+
if (scopeRoot(toScope) === "project") {
|
|
143
|
+
const projectId = toScope.split(":")[1] ?? "";
|
|
144
|
+
if (projectId.length === 0 || !(await readStoreProjects(storeDir)).some((p) => p.id === projectId)) {
|
|
145
|
+
return `project '${projectId}' is not registered in this store (run \`fabric store project add ${projectId}\` first)`;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
function matchesSelection(options, id, currentScope) {
|
|
151
|
+
if (options.id !== void 0 && id !== options.id) {
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
if (options.fromScope !== void 0 && currentScope !== options.fromScope) {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
if (options.fromScopeRoot !== void 0 && (currentScope === void 0 || scopeRoot(currentScope) !== options.fromScopeRoot)) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
async function rescopeStore(storeDir, toScope, options) {
|
|
163
|
+
const report = {
|
|
164
|
+
dryRun: options.dryRun === true,
|
|
165
|
+
toScope,
|
|
166
|
+
changes: [],
|
|
167
|
+
refusals: [],
|
|
168
|
+
unchanged: 0,
|
|
169
|
+
skipped: []
|
|
170
|
+
};
|
|
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)) {
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
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");
|
|
180
|
+
const match = FRONTMATTER_RE.exec(content);
|
|
181
|
+
if (match === null) {
|
|
182
|
+
report.skipped.push(file);
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
const block = match[1] ?? "";
|
|
186
|
+
const id = readKey(block, "id") ?? null;
|
|
187
|
+
const currentScope = readKey(block, "semantic_scope");
|
|
188
|
+
if (!matchesSelection(options, id, currentScope)) {
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
if (currentScope === toScope) {
|
|
192
|
+
report.unchanged += 1;
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
if (toScopeError !== null) {
|
|
196
|
+
report.refusals.push({ file, id, reason: toScopeError });
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
const newBlock = setKey(block, "semantic_scope", toScope, "layer");
|
|
200
|
+
const before = content.slice(0, match.index);
|
|
201
|
+
const after = content.slice(match.index + match[0].length);
|
|
202
|
+
report.changes.push({ file, id, fromScope: currentScope, toScope });
|
|
203
|
+
if (options.dryRun !== true) {
|
|
204
|
+
writeFileSync2(file, `${before}---
|
|
205
|
+
${newBlock}
|
|
206
|
+
---${after}`, "utf8");
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return report;
|
|
211
|
+
}
|
|
212
|
+
async function promoteProjectToTeam(storeDir, options) {
|
|
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 };
|
|
214
|
+
return rescopeStore(storeDir, "team", selection);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// src/commands/store.ts
|
|
218
|
+
import {
|
|
219
|
+
STORE_LAYOUT as STORE_LAYOUT2,
|
|
220
|
+
loadGlobalConfig,
|
|
221
|
+
resolveGlobalRoot
|
|
222
|
+
} from "@fenglimg/fabric-shared";
|
|
223
|
+
var listCommand = defineCommand({
|
|
224
|
+
meta: { name: "list", description: "List mounted knowledge stores" },
|
|
225
|
+
run() {
|
|
226
|
+
const t = getProjectTranslator();
|
|
227
|
+
const stores = storeList();
|
|
228
|
+
if (stores.length === 0) {
|
|
229
|
+
console.log(t("cli.store.none-mounted"));
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
const localOnly = t("cli.shared.local-only");
|
|
233
|
+
for (const store of stores) {
|
|
234
|
+
const realRemote = storeGitRemote(store.alias);
|
|
235
|
+
console.log(
|
|
236
|
+
`${store.alias} ${store.mount_name ?? store.store_uuid} ${store.store_uuid} ${realRemote ?? localOnly}`
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
var addCommand = defineCommand({
|
|
242
|
+
meta: { name: "add", description: "Mount a knowledge store into the global registry" },
|
|
243
|
+
args: {
|
|
244
|
+
uuid: { type: "string", required: true, description: "Intrinsic store UUID" },
|
|
245
|
+
alias: { type: "string", required: true, description: "Local alias for this store" },
|
|
246
|
+
"mount-name": { type: "string", description: "Stable local directory under ~/.fabric/stores/" },
|
|
247
|
+
remote: { type: "string", description: "Git remote locator (omit for local-only)" }
|
|
248
|
+
},
|
|
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
|
+
};
|
|
261
|
+
const next = storeAdd(store);
|
|
262
|
+
const t = getProjectTranslator();
|
|
263
|
+
console.log(
|
|
264
|
+
t("cli.store.mounted", {
|
|
265
|
+
alias: args.alias,
|
|
266
|
+
count: String(next.stores.length)
|
|
267
|
+
})
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
var createCommand = defineCommand({
|
|
272
|
+
meta: { name: "create", description: "Create a brand-new local knowledge store and mount it" },
|
|
273
|
+
args: {
|
|
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/" },
|
|
276
|
+
remote: { type: "string", description: "Git remote to associate (push target; optional)" }
|
|
277
|
+
},
|
|
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"] },
|
|
281
|
+
...args.remote === void 0 ? {} : { remote: args.remote }
|
|
282
|
+
});
|
|
283
|
+
const t = getProjectTranslator();
|
|
284
|
+
console.log(
|
|
285
|
+
t("cli.store.created", { alias: args.alias, uuid: result.store_uuid, dir: result.storeDir }) + (args.remote === void 0 ? `
|
|
286
|
+
${t("cli.store.created-local-hint")}` : "")
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
var removeCommand = defineCommand({
|
|
291
|
+
meta: { name: "remove", description: "Detach a store from the registry (does NOT delete it)" },
|
|
292
|
+
args: {
|
|
293
|
+
alias: { type: "positional", required: true, description: "Alias to detach" }
|
|
294
|
+
},
|
|
295
|
+
async run({ args }) {
|
|
296
|
+
const { detached } = storeRemove(args.alias);
|
|
297
|
+
const t = getProjectTranslator();
|
|
298
|
+
if (detached === null) {
|
|
299
|
+
process.exitCode = 1;
|
|
300
|
+
}
|
|
301
|
+
console.log(
|
|
302
|
+
detached === null ? t("cli.store.no-alias", { alias: args.alias }) : t("cli.store.detached", { alias: args.alias })
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
var explainCommand = defineCommand({
|
|
307
|
+
meta: { name: "explain", description: "Explain how a store alias resolves" },
|
|
308
|
+
args: {
|
|
309
|
+
alias: { type: "positional", required: true, description: "Alias to explain" }
|
|
310
|
+
},
|
|
311
|
+
run({ args }) {
|
|
312
|
+
const explanation = storeExplain(args.alias);
|
|
313
|
+
if (explanation === null) {
|
|
314
|
+
process.exitCode = 1;
|
|
315
|
+
}
|
|
316
|
+
console.log(
|
|
317
|
+
explanation === null ? getProjectTranslator()("cli.store.no-alias", { alias: args.alias }) : JSON.stringify(explanation, null, 2)
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
var bindCommand = defineCommand({
|
|
322
|
+
meta: { name: "bind", description: "Declare a required store on this project's config" },
|
|
323
|
+
args: {
|
|
324
|
+
id: { type: "positional", required: true, description: "Store alias/UUID to require" },
|
|
325
|
+
remote: { type: "string", description: "Suggested remote for clone onboarding" },
|
|
326
|
+
project: {
|
|
327
|
+
type: "string",
|
|
328
|
+
description: "Bind this repo to a project:<id> in the store (must already exist)"
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
async run({ args }) {
|
|
332
|
+
const entry = args.remote === void 0 ? { id: args.id } : { id: args.id, suggested_remote: args.remote };
|
|
333
|
+
const projectRoot = process.cwd();
|
|
334
|
+
const next = await storeBind(
|
|
335
|
+
projectRoot,
|
|
336
|
+
entry,
|
|
337
|
+
args.project === void 0 ? {} : { project: args.project }
|
|
338
|
+
);
|
|
339
|
+
console.log(
|
|
340
|
+
getProjectTranslator(projectRoot)("cli.store.bound", {
|
|
341
|
+
id: args.id,
|
|
342
|
+
count: String(next.required_stores?.length ?? 0)
|
|
343
|
+
})
|
|
344
|
+
);
|
|
345
|
+
regenerateBindingsSnapshot(projectRoot, { now: (/* @__PURE__ */ new Date()).toISOString() });
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
var switchWriteCommand = defineCommand({
|
|
349
|
+
meta: { name: "switch-write", description: "Set the default write store for non-personal scopes" },
|
|
350
|
+
args: {
|
|
351
|
+
alias: { type: "positional", required: true, description: "Alias of the store to write to" }
|
|
352
|
+
},
|
|
353
|
+
async run({ args }) {
|
|
354
|
+
const projectRoot = process.cwd();
|
|
355
|
+
storeSwitchWrite(projectRoot, args.alias);
|
|
356
|
+
regenerateBindingsSnapshot(projectRoot, { now: (/* @__PURE__ */ new Date()).toISOString() });
|
|
357
|
+
console.log(getProjectTranslator(projectRoot)("cli.store.switch-write", { alias: args.alias }));
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
var routeWriteCommand = defineCommand({
|
|
361
|
+
meta: { name: "route-write", description: "Route a semantic scope to a writable shared store" },
|
|
362
|
+
args: {
|
|
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" }
|
|
365
|
+
},
|
|
366
|
+
run({ args }) {
|
|
367
|
+
const projectRoot = process.cwd();
|
|
368
|
+
storeSetWriteRoute(projectRoot, args.scope, args.alias);
|
|
369
|
+
console.log(`write route: ${args.scope} -> ${args.alias}`);
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
var projectListCommand = defineCommand({
|
|
373
|
+
meta: { name: "list", description: "List projects registered in a store" },
|
|
374
|
+
args: {
|
|
375
|
+
store: { type: "positional", required: true, description: "Store alias/UUID" }
|
|
376
|
+
},
|
|
377
|
+
async run({ args }) {
|
|
378
|
+
const projects = await storeProjectList(args.store);
|
|
379
|
+
if (projects.length === 0) {
|
|
380
|
+
console.log(`store '${args.store}' has no registered projects.`);
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
for (const p of projects) {
|
|
384
|
+
console.log(`${p.id}${p.name === void 0 ? "" : ` ${p.name}`}`);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
var projectCreateCommand = defineCommand({
|
|
389
|
+
meta: { name: "create", description: "Register a new project in a store" },
|
|
390
|
+
args: {
|
|
391
|
+
store: { type: "positional", required: true, description: "Store alias/UUID" },
|
|
392
|
+
id: { type: "positional", required: true, description: "Project id (single [a-z0-9_-] segment)" },
|
|
393
|
+
name: { type: "string", description: "Optional human-facing label" }
|
|
394
|
+
},
|
|
395
|
+
async run({ args }) {
|
|
396
|
+
const project = await storeProjectCreate(args.store, args.id, (/* @__PURE__ */ new Date()).toISOString(), {
|
|
397
|
+
...args.name === void 0 ? {} : { name: args.name }
|
|
398
|
+
});
|
|
399
|
+
console.log(`registered project '${project.id}' in store '${args.store}'.`);
|
|
400
|
+
}
|
|
401
|
+
});
|
|
402
|
+
var projectCommand = defineCommand({
|
|
403
|
+
meta: { name: "project", description: "Manage the projects a store serves" },
|
|
404
|
+
subCommands: {
|
|
405
|
+
list: projectListCommand,
|
|
406
|
+
create: projectCreateCommand
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
var backfillScopeCommand = defineCommand({
|
|
410
|
+
meta: {
|
|
411
|
+
name: "backfill-scope",
|
|
412
|
+
description: "Backfill semantic_scope + visibility_store on existing knowledge (repairs dirty layer)"
|
|
413
|
+
},
|
|
414
|
+
args: {
|
|
415
|
+
store: { type: "string", description: "Backfill a mounted store's knowledge" },
|
|
416
|
+
"dry-run": { type: "boolean", description: "Preview changes without writing" }
|
|
417
|
+
},
|
|
418
|
+
run({ args }) {
|
|
419
|
+
const dryRun = args["dry-run"] === true;
|
|
420
|
+
let knowledgeDir;
|
|
421
|
+
let visibilityStore;
|
|
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);
|
|
432
|
+
if (storeDir === null) {
|
|
433
|
+
console.error(`no mounted store '${selectedStore}'`);
|
|
434
|
+
process.exitCode = 1;
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
knowledgeDir = join3(storeDir, STORE_LAYOUT2.knowledgeDir);
|
|
438
|
+
visibilityStore = selectedStore;
|
|
439
|
+
}
|
|
440
|
+
const report = backfillKnowledgeDir(knowledgeDir, { visibilityStore, dryRun });
|
|
441
|
+
if (report.changes.length === 0) {
|
|
442
|
+
console.log(`scope backfill: nothing to do (${report.unchanged} already consistent).`);
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
console.log(
|
|
446
|
+
`${dryRun ? "[dry-run] " : ""}scope backfill: ${report.changes.length} entr${report.changes.length === 1 ? "y" : "ies"} updated, ${report.unchanged} unchanged.`
|
|
447
|
+
);
|
|
448
|
+
for (const c of report.changes) {
|
|
449
|
+
console.log(` ${c.id ?? "(no id)"} [${c.changed.join(", ")}]`);
|
|
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
|
+
}
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
function resolveStoreDirAndVisibility(aliasOrUuid) {
|
|
460
|
+
const dir = resolveStoreDir(aliasOrUuid);
|
|
461
|
+
if (dir === null) {
|
|
462
|
+
return null;
|
|
463
|
+
}
|
|
464
|
+
const store = loadGlobalConfig(resolveGlobalRoot())?.stores.find(
|
|
465
|
+
(s) => s.alias === aliasOrUuid || s.store_uuid === aliasOrUuid
|
|
466
|
+
);
|
|
467
|
+
return { dir, visibility: store?.personal === true ? "personal" : "shared" };
|
|
468
|
+
}
|
|
469
|
+
function printRescopeReport(report) {
|
|
470
|
+
const prefix = report.dryRun ? "[dry-run] " : "";
|
|
471
|
+
if (report.changes.length === 0 && report.refusals.length === 0) {
|
|
472
|
+
console.log(`re-scope: nothing to do (${report.unchanged} already at '${report.toScope}').`);
|
|
473
|
+
} else if (report.changes.length > 0) {
|
|
474
|
+
console.log(
|
|
475
|
+
`${prefix}re-scope \u2192 ${report.toScope}: ${report.changes.length} entr${report.changes.length === 1 ? "y" : "ies"} updated, ${report.unchanged} unchanged.`
|
|
476
|
+
);
|
|
477
|
+
for (const c of report.changes) {
|
|
478
|
+
console.log(` ${c.id ?? "(no id)"} ${c.fromScope ?? "(none)"} \u2192 ${c.toScope}`);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
if (report.refusals.length > 0) {
|
|
482
|
+
console.error(`${report.refusals.length} entr${report.refusals.length === 1 ? "y" : "ies"} refused:`);
|
|
483
|
+
for (const r of report.refusals) {
|
|
484
|
+
console.error(` ${r.id ?? "(no id)"}: ${r.reason}`);
|
|
485
|
+
}
|
|
486
|
+
process.exitCode = 1;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
var rescopeCommand = defineCommand({
|
|
490
|
+
meta: {
|
|
491
|
+
name: "re-scope",
|
|
492
|
+
description: "Rewrite knowledge entries' semantic_scope coordinate in a store"
|
|
493
|
+
},
|
|
494
|
+
args: {
|
|
495
|
+
store: { type: "positional", required: true, description: "Target store alias or uuid" },
|
|
496
|
+
to: { type: "string", required: true, description: "New semantic_scope (e.g. team, project:alpha)" },
|
|
497
|
+
id: { type: "string", description: "Only the entry with this stable_id" },
|
|
498
|
+
from: { type: "string", description: "Only entries currently at this semantic_scope" },
|
|
499
|
+
"dry-run": { type: "boolean", description: "Preview changes without writing" }
|
|
500
|
+
},
|
|
501
|
+
async run({ args }) {
|
|
502
|
+
const resolved = resolveStoreDirAndVisibility(args.store);
|
|
503
|
+
if (resolved === null) {
|
|
504
|
+
console.error(`no mounted store '${args.store}'`);
|
|
505
|
+
process.exitCode = 1;
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
printRescopeReport(
|
|
509
|
+
await rescopeStore(resolved.dir, args.to, {
|
|
510
|
+
id: args.id,
|
|
511
|
+
fromScope: args.from,
|
|
512
|
+
storeVisibility: resolved.visibility,
|
|
513
|
+
dryRun: args["dry-run"] === true
|
|
514
|
+
})
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
});
|
|
518
|
+
var promoteCommand = defineCommand({
|
|
519
|
+
meta: {
|
|
520
|
+
name: "promote",
|
|
521
|
+
description: "Promote project-scoped entries to team scope (project absorption)"
|
|
522
|
+
},
|
|
523
|
+
args: {
|
|
524
|
+
store: { type: "positional", required: true, description: "Target store alias or uuid" },
|
|
525
|
+
project: { type: "string", description: "Only this project's entries (default: all project:*)" },
|
|
526
|
+
"dry-run": { type: "boolean", description: "Preview changes without writing" }
|
|
527
|
+
},
|
|
528
|
+
async run({ args }) {
|
|
529
|
+
const resolved = resolveStoreDirAndVisibility(args.store);
|
|
530
|
+
if (resolved === null) {
|
|
531
|
+
console.error(`no mounted store '${args.store}'`);
|
|
532
|
+
process.exitCode = 1;
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
printRescopeReport(
|
|
536
|
+
await promoteProjectToTeam(resolved.dir, {
|
|
537
|
+
projectId: args.project,
|
|
538
|
+
storeVisibility: resolved.visibility,
|
|
539
|
+
dryRun: args["dry-run"] === true
|
|
540
|
+
})
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
});
|
|
544
|
+
var store_default = defineCommand({
|
|
545
|
+
meta: { name: "store", description: "Manage mounted Fabric knowledge stores" },
|
|
546
|
+
subCommands: {
|
|
547
|
+
list: listCommand,
|
|
548
|
+
create: createCommand,
|
|
549
|
+
add: addCommand,
|
|
550
|
+
remove: removeCommand,
|
|
551
|
+
explain: explainCommand,
|
|
552
|
+
bind: bindCommand,
|
|
553
|
+
"switch-write": switchWriteCommand,
|
|
554
|
+
"route-write": routeWriteCommand,
|
|
555
|
+
"backfill-scope": backfillScopeCommand,
|
|
556
|
+
"re-scope": rescopeCommand,
|
|
557
|
+
promote: promoteCommand,
|
|
558
|
+
project: projectCommand
|
|
559
|
+
}
|
|
560
|
+
});
|
|
561
|
+
export {
|
|
562
|
+
store_default as default
|
|
563
|
+
};
|