@fenglimg/fabric-cli 2.0.1 → 2.2.0-rc.1
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/dist/{chunk-PWLW3B57.js → chunk-2CY4BMTH.js} +5 -1
- package/dist/{chunk-D25XJ4BC.js → chunk-2R55HNVD.js} +105 -5
- package/dist/chunk-4R2CYEA4.js +116 -0
- package/dist/{chunk-BATF4PEJ.js → chunk-AOE6AYI7.js} +2 -2
- package/dist/{chunk-WWNXR34K.js → chunk-BO4XIZWZ.js} +8 -1
- package/dist/chunk-L4Q55UC4.js +52 -0
- package/dist/chunk-LFIKMVY7.js +27 -0
- package/dist/chunk-RYAFBNES.js +33 -0
- package/dist/chunk-T5RPGCCM.js +40 -0
- package/dist/chunk-WU6GAPKH.js +36 -0
- package/dist/{chunk-MF3OTILQ.js → chunk-XC5RUHLK.js} +29 -8
- package/dist/{config-XJIPZNUP.js → config-XYRBZJDU.js} +3 -3
- package/dist/{doctor-EJDSEJSS.js → doctor-YONYXDX6.js} +147 -24
- package/dist/index.js +58 -10
- package/dist/{install-EKWMFLUU.js → install-74ANPCCP.js} +320 -75
- package/dist/{metrics-ACEQFPDU.js → metrics-RER6NLFC.js} +22 -9
- package/dist/{onboard-coverage-MFCAEBDO.js → onboard-coverage-JWQWDZW7.js} +1 -1
- package/dist/scope-explain-CDIZESP5.js +37 -0
- package/dist/status-GLQWLWH6.js +23 -0
- package/dist/store-XB3ADT65.js +144 -0
- package/dist/sync-UJ4BBCZJ.js +251 -0
- package/dist/{uninstall-MH7ZIB6M.js → uninstall-C3QXKOO6.js} +47 -7
- package/dist/whoami-2MLO4Y37.js +36 -0
- package/package.json +3 -3
- package/templates/hooks/fabric-hint.cjs +139 -7
- package/templates/hooks/knowledge-hint-broad.cjs +204 -9
- package/templates/hooks/knowledge-hint-narrow.cjs +49 -4
- package/templates/hooks/lib/bindings-snapshot-reader.cjs +81 -0
- package/templates/hooks/lib/cite-contract-reminder.cjs +15 -9
- package/templates/hooks/lib/cite-line-parser.cjs +48 -26
- package/templates/hooks/lib/injection-log.cjs +91 -0
- package/templates/hooks/lib/state-store.cjs +30 -11
- package/templates/skills/fabric-archive/SKILL.md +4 -0
- package/templates/skills/fabric-audit/SKILL.md +53 -0
- package/templates/skills/fabric-connect/SKILL.md +48 -0
- package/templates/skills/fabric-import/SKILL.md +4 -0
- package/templates/skills/fabric-review/SKILL.md +6 -0
- package/templates/skills/fabric-review/ref/cite-contract.md +56 -0
- package/templates/skills/fabric-store/SKILL.md +44 -0
- package/templates/skills/fabric-sync/SKILL.md +46 -0
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
installMcpClients
|
|
4
|
-
} from "./chunk-BATF4PEJ.js";
|
|
5
2
|
import {
|
|
6
3
|
cleanupDeprecatedSkills,
|
|
7
4
|
installArchiveHintHook,
|
|
8
5
|
installCitePolicyEvictHook,
|
|
9
6
|
installFabricArchiveSkill,
|
|
7
|
+
installFabricAuditSkill,
|
|
8
|
+
installFabricConnectSkill,
|
|
10
9
|
installFabricImportSkill,
|
|
11
10
|
installFabricReviewSkill,
|
|
11
|
+
installFabricStoreSkill,
|
|
12
|
+
installFabricSyncSkill,
|
|
12
13
|
installHookLibs,
|
|
13
14
|
installKnowledgeHintBroadHook,
|
|
14
15
|
installKnowledgeHintNarrowHook,
|
|
@@ -21,29 +22,39 @@ import {
|
|
|
21
22
|
writeCodexBootstrapManagedBlock,
|
|
22
23
|
writeCursorBootstrapManagedBlock,
|
|
23
24
|
writeFabricAgentsSnapshot
|
|
24
|
-
} from "./chunk-
|
|
25
|
-
import {
|
|
26
|
-
detectClientSupports
|
|
27
|
-
} from "./chunk-MF3OTILQ.js";
|
|
25
|
+
} from "./chunk-2R55HNVD.js";
|
|
28
26
|
import {
|
|
29
27
|
displayWidth,
|
|
30
28
|
padEnd,
|
|
31
29
|
paint
|
|
32
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-BO4XIZWZ.js";
|
|
33
31
|
import {
|
|
34
32
|
createDebugLogger,
|
|
35
33
|
resolveDevMode
|
|
36
34
|
} from "./chunk-COI5VDFU.js";
|
|
37
35
|
import {
|
|
36
|
+
installMcpClients
|
|
37
|
+
} from "./chunk-AOE6AYI7.js";
|
|
38
|
+
import {
|
|
39
|
+
detectClientSupports
|
|
40
|
+
} from "./chunk-XC5RUHLK.js";
|
|
41
|
+
import {
|
|
42
|
+
getProjectTranslator,
|
|
38
43
|
t
|
|
39
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-2CY4BMTH.js";
|
|
45
|
+
import {
|
|
46
|
+
globalConfigPath,
|
|
47
|
+
loadGlobalConfig,
|
|
48
|
+
resolveGlobalRoot,
|
|
49
|
+
saveGlobalConfig
|
|
50
|
+
} from "./chunk-RYAFBNES.js";
|
|
40
51
|
|
|
41
52
|
// src/commands/install.ts
|
|
42
|
-
import { randomUUID } from "crypto";
|
|
53
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
43
54
|
import { homedir } from "os";
|
|
44
55
|
import * as childProcess from "child_process";
|
|
45
|
-
import { appendFileSync, existsSync as existsSync4, mkdirSync, readFileSync as readFileSync3, rmSync, statSync as statSync4, writeFileSync } from "fs";
|
|
46
|
-
import { dirname, isAbsolute as isAbsolute3, join as
|
|
56
|
+
import { appendFileSync, existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync3, rmSync as rmSync2, statSync as statSync4, writeFileSync } from "fs";
|
|
57
|
+
import { dirname, isAbsolute as isAbsolute3, join as join6, resolve as resolve3 } from "path";
|
|
47
58
|
import { cancel, confirm, group, intro, isCancel, log, note, outro, select } from "@clack/prompts";
|
|
48
59
|
import { defaultAgentsMetaCounters } from "@fenglimg/fabric-shared";
|
|
49
60
|
import { atomicWriteJson } from "@fenglimg/fabric-shared/node/atomic-write";
|
|
@@ -59,6 +70,10 @@ async function installHooks(target, _options = {}) {
|
|
|
59
70
|
results.push(...await runStep(() => installFabricArchiveSkill(normalizedTarget)));
|
|
60
71
|
results.push(...await runStep(() => installFabricReviewSkill(normalizedTarget)));
|
|
61
72
|
results.push(...await runStep(() => installFabricImportSkill(normalizedTarget)));
|
|
73
|
+
results.push(...await runStep(() => installFabricSyncSkill(normalizedTarget)));
|
|
74
|
+
results.push(...await runStep(() => installFabricStoreSkill(normalizedTarget)));
|
|
75
|
+
results.push(...await runStep(() => installFabricAuditSkill(normalizedTarget)));
|
|
76
|
+
results.push(...await runStep(() => installFabricConnectSkill(normalizedTarget)));
|
|
62
77
|
results.push(...await runStep(() => installSharedSkillLib(normalizedTarget)));
|
|
63
78
|
results.push(...await runStep(() => installArchiveHintHook(normalizedTarget)));
|
|
64
79
|
results.push(...await runStep(() => installKnowledgeHintBroadHook(normalizedTarget)));
|
|
@@ -182,20 +197,209 @@ function assertExistingDirectory(target) {
|
|
|
182
197
|
}
|
|
183
198
|
}
|
|
184
199
|
|
|
200
|
+
// src/install/run-global-install.ts
|
|
201
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
202
|
+
import { randomUUID } from "crypto";
|
|
203
|
+
import { mkdirSync, mkdtempSync, renameSync } from "fs";
|
|
204
|
+
import { tmpdir } from "os";
|
|
205
|
+
import { join as join3 } from "path";
|
|
206
|
+
import { STORES_ROOT_DIR as STORES_ROOT_DIR2, addMountedStore, readStoreIdentity } from "@fenglimg/fabric-shared";
|
|
207
|
+
import { GenericIOError } from "@fenglimg/fabric-shared/errors";
|
|
208
|
+
|
|
209
|
+
// src/store/uid.ts
|
|
210
|
+
import { execFileSync } from "child_process";
|
|
211
|
+
import { createHash } from "crypto";
|
|
212
|
+
function deriveUid(opts = {}) {
|
|
213
|
+
let email = "";
|
|
214
|
+
try {
|
|
215
|
+
email = execFileSync("git", ["config", "user.email"], {
|
|
216
|
+
encoding: "utf8",
|
|
217
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
218
|
+
}).trim();
|
|
219
|
+
} catch {
|
|
220
|
+
email = "";
|
|
221
|
+
}
|
|
222
|
+
if (email === "") {
|
|
223
|
+
return "u-anon";
|
|
224
|
+
}
|
|
225
|
+
const material = opts.salt !== void 0 && opts.salt.length > 0 ? `${opts.salt}:${email.toLowerCase()}` : email.toLowerCase();
|
|
226
|
+
const hash = createHash("sha256").update(material).digest("hex").slice(0, 12);
|
|
227
|
+
return `u-${hash}`;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// src/install/install-global.ts
|
|
231
|
+
import { rmSync } from "fs";
|
|
232
|
+
import { join as join2 } from "path";
|
|
233
|
+
import {
|
|
234
|
+
STORES_ROOT_DIR,
|
|
235
|
+
globalConfigSchema,
|
|
236
|
+
initStore
|
|
237
|
+
} from "@fenglimg/fabric-shared";
|
|
238
|
+
|
|
239
|
+
// src/install/transaction.ts
|
|
240
|
+
function errorMessage(error) {
|
|
241
|
+
return error instanceof Error ? error.message : String(error);
|
|
242
|
+
}
|
|
243
|
+
async function runInstallTransaction(steps) {
|
|
244
|
+
const receipt = { ok: true, steps: [] };
|
|
245
|
+
const applied = [];
|
|
246
|
+
for (let i = 0; i < steps.length; i++) {
|
|
247
|
+
const step = steps[i];
|
|
248
|
+
try {
|
|
249
|
+
await step.apply();
|
|
250
|
+
applied.push(step);
|
|
251
|
+
receipt.steps.push({ name: step.name, status: "applied" });
|
|
252
|
+
} catch (error) {
|
|
253
|
+
receipt.ok = false;
|
|
254
|
+
receipt.failedStep = step.name;
|
|
255
|
+
receipt.error = errorMessage(error);
|
|
256
|
+
receipt.steps.push({ name: step.name, status: "failed", error: errorMessage(error) });
|
|
257
|
+
for (let j = i + 1; j < steps.length; j++) {
|
|
258
|
+
receipt.steps.push({ name: steps[j].name, status: "skipped" });
|
|
259
|
+
}
|
|
260
|
+
for (const done of [...applied].reverse()) {
|
|
261
|
+
const entry = receipt.steps.find((s) => s.name === done.name);
|
|
262
|
+
try {
|
|
263
|
+
await done.rollback();
|
|
264
|
+
if (entry !== void 0) {
|
|
265
|
+
entry.status = "rolled_back";
|
|
266
|
+
}
|
|
267
|
+
} catch (rollbackError) {
|
|
268
|
+
if (entry !== void 0) {
|
|
269
|
+
entry.status = "rollback_failed";
|
|
270
|
+
entry.error = errorMessage(rollbackError);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return receipt;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return receipt;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// src/install/install-global.ts
|
|
281
|
+
async function installGlobalCore(options) {
|
|
282
|
+
const existing = loadGlobalConfig(options.globalRoot);
|
|
283
|
+
if (existing !== null) {
|
|
284
|
+
return {
|
|
285
|
+
receipt: { ok: true, steps: [{ name: "already-installed", status: "applied" }] },
|
|
286
|
+
config: existing,
|
|
287
|
+
alreadyInstalled: true
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
const alias = options.personalAlias ?? "personal";
|
|
291
|
+
const personalDir = join2(options.globalRoot, STORES_ROOT_DIR, options.personalStoreUuid);
|
|
292
|
+
let config = null;
|
|
293
|
+
const receipt = await runInstallTransaction([
|
|
294
|
+
{
|
|
295
|
+
name: "init-personal-store",
|
|
296
|
+
apply: () => {
|
|
297
|
+
initStore(
|
|
298
|
+
personalDir,
|
|
299
|
+
{
|
|
300
|
+
store_uuid: options.personalStoreUuid,
|
|
301
|
+
created_at: options.now,
|
|
302
|
+
canonical_alias: alias
|
|
303
|
+
},
|
|
304
|
+
{ git: options.git }
|
|
305
|
+
);
|
|
306
|
+
},
|
|
307
|
+
rollback: () => {
|
|
308
|
+
rmSync(personalDir, { recursive: true, force: true });
|
|
309
|
+
}
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
name: "write-global-config",
|
|
313
|
+
apply: () => {
|
|
314
|
+
const next = globalConfigSchema.parse({
|
|
315
|
+
uid: options.uid,
|
|
316
|
+
stores: [{ store_uuid: options.personalStoreUuid, alias, personal: true }]
|
|
317
|
+
});
|
|
318
|
+
saveGlobalConfig(next, options.globalRoot);
|
|
319
|
+
config = next;
|
|
320
|
+
},
|
|
321
|
+
rollback: () => {
|
|
322
|
+
rmSync(globalConfigPath(options.globalRoot), { force: true });
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
]);
|
|
326
|
+
return { receipt, config, alreadyInstalled: false };
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// src/install/run-global-install.ts
|
|
330
|
+
function gitClone(url, dest) {
|
|
331
|
+
console.log(`cloning store from ${url} (this may take a while)\u2026`);
|
|
332
|
+
try {
|
|
333
|
+
execFileSync2("git", ["clone", "--", url, dest], { stdio: ["ignore", "ignore", "inherit"] });
|
|
334
|
+
} catch (error) {
|
|
335
|
+
throw new GenericIOError(`git clone of ${url} failed`, {
|
|
336
|
+
actionHint: "check the url is reachable and points to a Fabric store git repo (the git error above shows the cause), then re-run `fabric install --global <url>`",
|
|
337
|
+
details: error
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
function mountStoreFromRemote(url, globalRoot) {
|
|
342
|
+
const storesRoot = join3(globalRoot, STORES_ROOT_DIR2);
|
|
343
|
+
mkdirSync(storesRoot, { recursive: true });
|
|
344
|
+
const tmp = mkdtempSync(join3(tmpdir(), "fabric-clone-"));
|
|
345
|
+
const cloneDest = join3(tmp, "store");
|
|
346
|
+
gitClone(url, cloneDest);
|
|
347
|
+
const identity = readStoreIdentity(cloneDest);
|
|
348
|
+
if (identity === null) {
|
|
349
|
+
throw new GenericIOError(`cloned store at ${url} has no valid store.json (not a Fabric store)`, {
|
|
350
|
+
actionHint: "verify the url points to a repository created by `fabric` (it must contain a store.json at its root); if you meant to mount a different store, re-run with the correct url"
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
const finalDir = join3(storesRoot, identity.store_uuid);
|
|
354
|
+
renameSync(cloneDest, finalDir);
|
|
355
|
+
const config = loadGlobalConfig(globalRoot);
|
|
356
|
+
if (config === null) {
|
|
357
|
+
throw new GenericIOError("global config missing after install", {
|
|
358
|
+
actionHint: "re-run `fabric install --global` to (re)create the global config, then retry mounting the store; if it persists, inspect ~/.fabric for a partial install"
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
const alias = identity.canonical_alias ?? "team";
|
|
362
|
+
saveGlobalConfig(
|
|
363
|
+
addMountedStore(config, { store_uuid: identity.store_uuid, alias, remote: url }),
|
|
364
|
+
globalRoot
|
|
365
|
+
);
|
|
366
|
+
console.log(`mounted store '${alias}' (${identity.store_uuid}) from ${url}`);
|
|
367
|
+
}
|
|
368
|
+
async function runGlobalInstall(options = {}, globalRoot = resolveGlobalRoot()) {
|
|
369
|
+
const uid = options.uid ?? deriveUid();
|
|
370
|
+
const personalStoreUuid = options.personalStoreUuid ?? randomUUID();
|
|
371
|
+
const now = options.now ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
372
|
+
const result = await installGlobalCore({ globalRoot, uid, personalStoreUuid, now });
|
|
373
|
+
if (!result.receipt.ok) {
|
|
374
|
+
throw new GenericIOError(
|
|
375
|
+
`global install failed at step '${result.receipt.failedStep}': ${result.receipt.error}`,
|
|
376
|
+
{
|
|
377
|
+
actionHint: "check write permissions and free space under ~/.fabric, then re-run `fabric install --global` (the install is transactional and rolls back partial state)"
|
|
378
|
+
}
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
console.log(
|
|
382
|
+
result.alreadyInstalled ? "global Fabric already installed" : `installed global Fabric (uid ${uid})`
|
|
383
|
+
);
|
|
384
|
+
if (options.url !== void 0) {
|
|
385
|
+
mountStoreFromRemote(options.url, globalRoot);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
185
389
|
// src/lib/detect-language.ts
|
|
186
390
|
import { existsSync as existsSync2, readdirSync, readFileSync, statSync as statSync2 } from "fs";
|
|
187
|
-
import { join as
|
|
391
|
+
import { join as join4 } from "path";
|
|
188
392
|
function detectExistingLanguage(target) {
|
|
189
393
|
const ZH_CN_RATIO_THRESHOLD = 0.3;
|
|
190
394
|
const samples = [];
|
|
191
|
-
const readmePath =
|
|
395
|
+
const readmePath = join4(target, "README.md");
|
|
192
396
|
if (existsSync2(readmePath)) {
|
|
193
397
|
try {
|
|
194
398
|
samples.push(readFileSync(readmePath, "utf8"));
|
|
195
399
|
} catch {
|
|
196
400
|
}
|
|
197
401
|
}
|
|
198
|
-
const docsDir =
|
|
402
|
+
const docsDir = join4(target, "docs");
|
|
199
403
|
if (existsSync2(docsDir)) {
|
|
200
404
|
try {
|
|
201
405
|
const stat = statSync2(docsDir);
|
|
@@ -204,7 +408,7 @@ function detectExistingLanguage(target) {
|
|
|
204
408
|
if (!entry.isFile()) continue;
|
|
205
409
|
if (!/\.(md|mdx|txt)$/iu.test(entry.name)) continue;
|
|
206
410
|
try {
|
|
207
|
-
samples.push(readFileSync(
|
|
411
|
+
samples.push(readFileSync(join4(docsDir, entry.name), "utf8"));
|
|
208
412
|
} catch {
|
|
209
413
|
}
|
|
210
414
|
}
|
|
@@ -236,11 +440,12 @@ function detectExistingLanguage(target) {
|
|
|
236
440
|
}
|
|
237
441
|
|
|
238
442
|
// src/scanner/forensic.ts
|
|
239
|
-
import { execFileSync } from "child_process";
|
|
443
|
+
import { execFileSync as execFileSync3 } from "child_process";
|
|
240
444
|
import { existsSync as existsSync3, readdirSync as readdirSync2, readFileSync as readFileSync2, statSync as statSync3 } from "fs";
|
|
241
445
|
import { createRequire } from "module";
|
|
242
|
-
import { basename, extname, isAbsolute as isAbsolute2, join as
|
|
446
|
+
import { basename, extname, isAbsolute as isAbsolute2, join as join5, posix, relative, resolve as resolve2, sep } from "path";
|
|
243
447
|
import {
|
|
448
|
+
buildScanRecommendations,
|
|
244
449
|
forensicReportSchema
|
|
245
450
|
} from "@fenglimg/fabric-shared";
|
|
246
451
|
|
|
@@ -358,7 +563,7 @@ async function buildForensicReport(targetInput) {
|
|
|
358
563
|
candidate_files: candidateFiles,
|
|
359
564
|
sampling_budget: DEFAULT_SAMPLING_BUDGET,
|
|
360
565
|
readme,
|
|
361
|
-
recommendations_for_skill: buildSkillRecommendations(framework.kind, topology, readme)
|
|
566
|
+
recommendations_for_skill: buildSkillRecommendations(framework.kind, topology, readme, target)
|
|
362
567
|
};
|
|
363
568
|
const validation = forensicReportSchema.safeParse(report);
|
|
364
569
|
if (!validation.success) {
|
|
@@ -383,7 +588,7 @@ function buildTopology(root) {
|
|
|
383
588
|
continue;
|
|
384
589
|
}
|
|
385
590
|
for (const entry of readdirSync2(current, { withFileTypes: true })) {
|
|
386
|
-
const absolutePath =
|
|
591
|
+
const absolutePath = join5(current, entry.name);
|
|
387
592
|
const relativePath = toPosixPath(relative(root, absolutePath));
|
|
388
593
|
if (relativePath.length === 0) {
|
|
389
594
|
continue;
|
|
@@ -471,7 +676,7 @@ function getEntryPointReason(relativePath) {
|
|
|
471
676
|
async function buildCodeSamples(target, entryPoints, frameworkKind, topology, packageDependencies) {
|
|
472
677
|
const samples = [];
|
|
473
678
|
for (const entryPoint of entryPoints.slice(0, SAMPLE_LIMIT)) {
|
|
474
|
-
const absolutePath =
|
|
679
|
+
const absolutePath = join5(target, ...entryPoint.path.split("/"));
|
|
475
680
|
const sample = readFirstLines(absolutePath, SAMPLE_LINE_LIMIT);
|
|
476
681
|
const patternAnalysis = await inferPatternHint(entryPoint.path, sample.snippet, {
|
|
477
682
|
frameworkKind,
|
|
@@ -508,7 +713,7 @@ function readFirstLines(path, lineLimit) {
|
|
|
508
713
|
}
|
|
509
714
|
}
|
|
510
715
|
function readPackageDependencies(target) {
|
|
511
|
-
const packageJsonPath =
|
|
716
|
+
const packageJsonPath = join5(target, "package.json");
|
|
512
717
|
if (!existsSync3(packageJsonPath)) {
|
|
513
718
|
return /* @__PURE__ */ new Map();
|
|
514
719
|
}
|
|
@@ -526,7 +731,7 @@ function readPackageDependencies(target) {
|
|
|
526
731
|
}
|
|
527
732
|
function readGitChurnWeight(target, relativePath) {
|
|
528
733
|
try {
|
|
529
|
-
const output =
|
|
734
|
+
const output = execFileSync3("git", ["log", "--follow", "--oneline", "-20", "--", relativePath], {
|
|
530
735
|
cwd: target,
|
|
531
736
|
encoding: "utf8",
|
|
532
737
|
stdio: ["ignore", "pipe", "ignore"],
|
|
@@ -849,8 +1054,8 @@ function scoreFrameworkConfidence(input) {
|
|
|
849
1054
|
return input.configCount > 0 || input.packageCount > 0 ? "MEDIUM" : "LOW";
|
|
850
1055
|
}
|
|
851
1056
|
function readReadmeInfo(target) {
|
|
852
|
-
const readmePath =
|
|
853
|
-
const hasContributing = existsSync3(
|
|
1057
|
+
const readmePath = join5(target, "README.md");
|
|
1058
|
+
const hasContributing = existsSync3(join5(target, "CONTRIBUTING.md"));
|
|
854
1059
|
if (!existsSync3(readmePath)) {
|
|
855
1060
|
return {
|
|
856
1061
|
quality: "missing",
|
|
@@ -1313,30 +1518,18 @@ function isDomainFile(relativePath) {
|
|
|
1313
1518
|
}
|
|
1314
1519
|
return !isConfigFile(relativePath) && !isTestFile(relativePath);
|
|
1315
1520
|
}
|
|
1316
|
-
function buildSkillRecommendations(frameworkKind, topology, readme) {
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
recommendations.push("\u5EFA\u8BAE\u786E\u8BA4 app/pages \u8DEF\u7531\u8FB9\u754C\u548C\u670D\u52A1\u7AEF\u7EC4\u4EF6\u7EA6\u675F\u3002");
|
|
1326
|
-
} else if (frameworkKind === "vite") {
|
|
1327
|
-
recommendations.push("\u5EFA\u8BAE\u786E\u8BA4 src/main \u5165\u53E3\u3001\u7EC4\u4EF6\u76EE\u5F55\u548C\u6784\u5EFA\u811A\u672C\u7684\u7EF4\u62A4\u8FB9\u754C\u3002");
|
|
1328
|
-
} else if (frameworkKind === "unknown") {
|
|
1329
|
-
recommendations.push("\u672A\u68C0\u6D4B\u5230\u660E\u786E\u6846\u67B6,\u5EFA\u8BAE\u5148\u8BA9\u7528\u6237\u786E\u8BA4\u6280\u672F\u6808\u548C\u4E3B\u8981\u5165\u53E3\u3002");
|
|
1330
|
-
} else {
|
|
1331
|
-
recommendations.push(`\u5EFA\u8BAE\u56F4\u7ED5 ${frameworkKind} \u7684\u4E3B\u8981\u5165\u53E3\u548C\u751F\u6210\u76EE\u5F55\u786E\u8BA4 AGENTS.md \u5206\u5C42\u8FB9\u754C\u3002`);
|
|
1332
|
-
}
|
|
1333
|
-
if (readme.quality !== "ok") {
|
|
1334
|
-
recommendations.push("README \u4FE1\u606F\u4E0D\u8DB3,\u5EFA\u8BAE\u5728\u521D\u59CB\u5316\u8BBF\u8C08\u4E2D\u8865\u9F50\u9879\u76EE\u76EE\u6807\u3001\u8FD0\u884C\u65B9\u5F0F\u548C\u7981\u6539\u533A\u57DF\u3002");
|
|
1335
|
-
}
|
|
1336
|
-
return recommendations;
|
|
1521
|
+
function buildSkillRecommendations(frameworkKind, topology, readme, projectRoot) {
|
|
1522
|
+
return buildScanRecommendations(
|
|
1523
|
+
{
|
|
1524
|
+
frameworkKind,
|
|
1525
|
+
hasMeta: (topology.by_ext[".meta"] ?? 0) > 0,
|
|
1526
|
+
readmeOk: readme.quality === "ok"
|
|
1527
|
+
},
|
|
1528
|
+
getProjectTranslator(projectRoot)
|
|
1529
|
+
);
|
|
1337
1530
|
}
|
|
1338
1531
|
function readProjectName(target) {
|
|
1339
|
-
const packageJsonPath =
|
|
1532
|
+
const packageJsonPath = join5(target, "package.json");
|
|
1340
1533
|
if (existsSync3(packageJsonPath)) {
|
|
1341
1534
|
try {
|
|
1342
1535
|
const packageJson = JSON.parse(readFileSync2(packageJsonPath, "utf8"));
|
|
@@ -1350,7 +1543,7 @@ function readProjectName(target) {
|
|
|
1350
1543
|
return basename(target);
|
|
1351
1544
|
}
|
|
1352
1545
|
function getCliVersion() {
|
|
1353
|
-
return true ? "2.0.1" : "unknown";
|
|
1546
|
+
return true ? "2.2.0-rc.1" : "unknown";
|
|
1354
1547
|
}
|
|
1355
1548
|
function sortRecord(record) {
|
|
1356
1549
|
return Object.fromEntries(Object.entries(record).sort(([left], [right]) => left.localeCompare(right)));
|
|
@@ -1360,7 +1553,7 @@ function toPosixPath(path) {
|
|
|
1360
1553
|
}
|
|
1361
1554
|
|
|
1362
1555
|
// src/commands/install.ts
|
|
1363
|
-
var LOCAL_FABRIC_SERVER_PATH =
|
|
1556
|
+
var LOCAL_FABRIC_SERVER_PATH = join6("node_modules", "@fenglimg", "fabric-server", "dist", "index.js");
|
|
1364
1557
|
var FABRIC_SERVER_PACKAGE = "@fenglimg/fabric-server";
|
|
1365
1558
|
var INIT_WIZARD_GROUP_CANCELLED = /* @__PURE__ */ Symbol("init-wizard-group-cancelled");
|
|
1366
1559
|
var installCommand = defineCommand({
|
|
@@ -1397,6 +1590,15 @@ var installCommand = defineCommand({
|
|
|
1397
1590
|
type: "boolean",
|
|
1398
1591
|
description: t("cli.install.args.force-hooks-only.description"),
|
|
1399
1592
|
default: false
|
|
1593
|
+
},
|
|
1594
|
+
global: {
|
|
1595
|
+
type: "boolean",
|
|
1596
|
+
description: "Set up global Fabric (~/.fabric: uid + personal store + config)",
|
|
1597
|
+
default: false
|
|
1598
|
+
},
|
|
1599
|
+
url: {
|
|
1600
|
+
type: "string",
|
|
1601
|
+
description: "With --global: clone + mount this shared store remote"
|
|
1400
1602
|
}
|
|
1401
1603
|
},
|
|
1402
1604
|
async run({ args }) {
|
|
@@ -1406,7 +1608,7 @@ var installCommand = defineCommand({
|
|
|
1406
1608
|
var install_default = installCommand;
|
|
1407
1609
|
async function runSkillsOnlyRefresh(targetInput) {
|
|
1408
1610
|
const target = normalizeTarget3(targetInput);
|
|
1409
|
-
const metaPath =
|
|
1611
|
+
const metaPath = join6(target, ".fabric", "agents.meta.json");
|
|
1410
1612
|
if (!existsSync4(metaPath)) {
|
|
1411
1613
|
const message = t("cli.install.force-skills-only.uninitialised.message");
|
|
1412
1614
|
const hint = t("cli.install.force-skills-only.uninitialised.hint");
|
|
@@ -1422,6 +1624,11 @@ ${hint}
|
|
|
1422
1624
|
results.push(...await installFabricArchiveSkill(target));
|
|
1423
1625
|
results.push(...await installFabricReviewSkill(target));
|
|
1424
1626
|
results.push(...await installFabricImportSkill(target));
|
|
1627
|
+
results.push(...await installFabricSyncSkill(target));
|
|
1628
|
+
results.push(...await installFabricStoreSkill(target));
|
|
1629
|
+
results.push(...await installFabricAuditSkill(target));
|
|
1630
|
+
results.push(...await installFabricConnectSkill(target));
|
|
1631
|
+
results.push(...await installSharedSkillLib(target));
|
|
1425
1632
|
let written = 0;
|
|
1426
1633
|
let skipped = 0;
|
|
1427
1634
|
let errors = 0;
|
|
@@ -1449,7 +1656,7 @@ ${hint}
|
|
|
1449
1656
|
}
|
|
1450
1657
|
async function runHooksOnlyRefresh(targetInput) {
|
|
1451
1658
|
const target = normalizeTarget3(targetInput);
|
|
1452
|
-
const metaPath =
|
|
1659
|
+
const metaPath = join6(target, ".fabric", "agents.meta.json");
|
|
1453
1660
|
if (!existsSync4(metaPath)) {
|
|
1454
1661
|
const message = t("cli.install.force-hooks-only.uninitialised.message");
|
|
1455
1662
|
const hint = t("cli.install.force-hooks-only.uninitialised.hint");
|
|
@@ -1478,6 +1685,10 @@ ${hint}
|
|
|
1478
1685
|
}
|
|
1479
1686
|
async function runInitCommand(args) {
|
|
1480
1687
|
const logger = createDebugLogger(args.debug);
|
|
1688
|
+
if (args.global === true) {
|
|
1689
|
+
await runGlobalInstall({ url: args.url });
|
|
1690
|
+
return;
|
|
1691
|
+
}
|
|
1481
1692
|
const resolution = resolveDevMode(args.target, process.cwd());
|
|
1482
1693
|
if (args["force-skills-only"] === true) {
|
|
1483
1694
|
await runSkillsOnlyRefresh(resolution.target);
|
|
@@ -1515,8 +1726,26 @@ async function runInitCommand(args) {
|
|
|
1515
1726
|
}
|
|
1516
1727
|
return result;
|
|
1517
1728
|
}
|
|
1729
|
+
var FABRIC_GITIGNORE_CONTENT = [
|
|
1730
|
+
"# Fabric per-dev activity ledgers & caches \u2014 auto-generated, not shared.",
|
|
1731
|
+
"# Managed by `fabric install`; edit freely (re-install never overwrites this).",
|
|
1732
|
+
"events.jsonl",
|
|
1733
|
+
"metrics.jsonl",
|
|
1734
|
+
"cite-rollup.jsonl",
|
|
1735
|
+
"injections.jsonl",
|
|
1736
|
+
".cache/",
|
|
1737
|
+
"*.lock",
|
|
1738
|
+
"*.corrupted.*",
|
|
1739
|
+
""
|
|
1740
|
+
].join("\n");
|
|
1741
|
+
function writeDefaultGitignore(fabricDir) {
|
|
1742
|
+
const target = join6(fabricDir, ".gitignore");
|
|
1743
|
+
if (existsSync4(target)) return;
|
|
1744
|
+
mkdirSync2(fabricDir, { recursive: true });
|
|
1745
|
+
writeFileSync(target, FABRIC_GITIGNORE_CONTENT, "utf8");
|
|
1746
|
+
}
|
|
1518
1747
|
function writeDefaultFabricConfig(fabricDir, targetRoot) {
|
|
1519
|
-
const target =
|
|
1748
|
+
const target = join6(fabricDir, "fabric-config.json");
|
|
1520
1749
|
if (existsSync4(target)) return;
|
|
1521
1750
|
const detectedLanguage = detectExistingLanguage(targetRoot);
|
|
1522
1751
|
const FABRIC_CONFIG_DEFAULTS = {
|
|
@@ -1578,7 +1807,7 @@ function writeDefaultFabricConfig(fabricDir, targetRoot) {
|
|
|
1578
1807
|
// is considered stale by fabric-review. Default 14.
|
|
1579
1808
|
review_stale_pending_days: 14
|
|
1580
1809
|
};
|
|
1581
|
-
|
|
1810
|
+
mkdirSync2(fabricDir, { recursive: true });
|
|
1582
1811
|
writeFileSync(target, JSON.stringify(FABRIC_CONFIG_DEFAULTS, null, 2) + "\n", "utf8");
|
|
1583
1812
|
log.info(
|
|
1584
1813
|
`Detected and fixated fabric_language = ${detectedLanguage}; edit ${target} to override.`
|
|
@@ -1709,14 +1938,14 @@ function resolvePersonalFabricRoot() {
|
|
|
1709
1938
|
}
|
|
1710
1939
|
async function buildInitFabricPlan(target, options) {
|
|
1711
1940
|
assertExistingDirectory3(target);
|
|
1712
|
-
const fabricDir =
|
|
1713
|
-
const agentsMdPath =
|
|
1941
|
+
const fabricDir = join6(target, ".fabric");
|
|
1942
|
+
const agentsMdPath = join6(target, "AGENTS.md");
|
|
1714
1943
|
const agentsMdAction = existsSync4(agentsMdPath) ? "preserved" : "created";
|
|
1715
|
-
const knowledgeDir =
|
|
1716
|
-
const personalKnowledgeDir =
|
|
1717
|
-
const forensicPath =
|
|
1718
|
-
const eventsPath =
|
|
1719
|
-
const metaPath =
|
|
1944
|
+
const knowledgeDir = join6(fabricDir, "knowledge");
|
|
1945
|
+
const personalKnowledgeDir = join6(resolvePersonalFabricRoot(), ".fabric", "knowledge");
|
|
1946
|
+
const forensicPath = join6(fabricDir, "forensic.json");
|
|
1947
|
+
const eventsPath = join6(fabricDir, "events.jsonl");
|
|
1948
|
+
const metaPath = join6(fabricDir, "agents.meta.json");
|
|
1720
1949
|
const replaceFabricDir = shouldReplaceWritableDirectory(fabricDir, options);
|
|
1721
1950
|
const knowledgeDirAction = existsSync4(knowledgeDir) ? "overwritten" : "created";
|
|
1722
1951
|
const metaClassification = classifyFreshPath(metaPath, "structural");
|
|
@@ -1725,7 +1954,16 @@ async function buildInitFabricPlan(target, options) {
|
|
|
1725
1954
|
const metaAction = diffStateToWriteAction(metaClassification.state);
|
|
1726
1955
|
const eventsAction = diffStateToWriteAction(eventsClassification.state);
|
|
1727
1956
|
const forensicAction = diffStateToWriteAction(forensicClassification.state);
|
|
1957
|
+
const showScanProgress = process.stderr.isTTY === true;
|
|
1958
|
+
if (showScanProgress) {
|
|
1959
|
+
process.stderr.write(`${t("cli.install.scanning")}
|
|
1960
|
+
`);
|
|
1961
|
+
}
|
|
1728
1962
|
const forensicReport = await buildForensicReport(target);
|
|
1963
|
+
if (showScanProgress) {
|
|
1964
|
+
process.stderr.write(`${t("cli.install.scan-complete")}
|
|
1965
|
+
`);
|
|
1966
|
+
}
|
|
1729
1967
|
const meta = createInitialMeta();
|
|
1730
1968
|
return {
|
|
1731
1969
|
target,
|
|
@@ -1752,23 +1990,24 @@ async function buildInitFabricPlan(target, options) {
|
|
|
1752
1990
|
}
|
|
1753
1991
|
async function executeInitFabricPlan(plan) {
|
|
1754
1992
|
if (plan.replaceFabricDir) {
|
|
1755
|
-
|
|
1993
|
+
rmSync2(plan.fabricDir, { force: true });
|
|
1756
1994
|
}
|
|
1757
|
-
|
|
1995
|
+
mkdirSync2(plan.fabricDir, { recursive: true });
|
|
1758
1996
|
writeDefaultFabricConfig(plan.fabricDir, plan.target);
|
|
1759
|
-
|
|
1997
|
+
writeDefaultGitignore(plan.fabricDir);
|
|
1998
|
+
mkdirSync2(plan.knowledgeDir, { recursive: true });
|
|
1760
1999
|
for (const sub of KNOWLEDGE_SUBDIRS) {
|
|
1761
|
-
const teamSubDir =
|
|
1762
|
-
|
|
1763
|
-
const teamGitkeep =
|
|
2000
|
+
const teamSubDir = join6(plan.knowledgeDir, sub);
|
|
2001
|
+
mkdirSync2(teamSubDir, { recursive: true });
|
|
2002
|
+
const teamGitkeep = join6(teamSubDir, ".gitkeep");
|
|
1764
2003
|
if (!existsSync4(teamGitkeep)) {
|
|
1765
2004
|
writeFileSync(teamGitkeep, "", "utf8");
|
|
1766
2005
|
}
|
|
1767
2006
|
}
|
|
1768
2007
|
try {
|
|
1769
|
-
|
|
2008
|
+
mkdirSync2(plan.personalKnowledgeDir, { recursive: true });
|
|
1770
2009
|
for (const sub of KNOWLEDGE_SUBDIRS) {
|
|
1771
|
-
|
|
2010
|
+
mkdirSync2(join6(plan.personalKnowledgeDir, sub), { recursive: true });
|
|
1772
2011
|
}
|
|
1773
2012
|
} catch {
|
|
1774
2013
|
}
|
|
@@ -1778,7 +2017,7 @@ async function executeInitFabricPlan(plan) {
|
|
|
1778
2017
|
}
|
|
1779
2018
|
if (plan.eventsState === "missing") {
|
|
1780
2019
|
preparePlannedPath(plan.eventsPath, plan.eventsAction);
|
|
1781
|
-
|
|
2020
|
+
mkdirSync2(dirname(plan.eventsPath), { recursive: true });
|
|
1782
2021
|
writeFileSync(plan.eventsPath, "", "utf8");
|
|
1783
2022
|
}
|
|
1784
2023
|
preparePlannedPath(plan.forensicPath, plan.forensicAction);
|
|
@@ -1935,9 +2174,15 @@ async function executeInitStagePlan(plan, stageName) {
|
|
|
1935
2174
|
installResults.push(...await runBestEffort("skill-install", () => installFabricArchiveSkill(plan.target)));
|
|
1936
2175
|
installResults.push(...await runBestEffort("skill-review-install", () => installFabricReviewSkill(plan.target)));
|
|
1937
2176
|
installResults.push(...await runBestEffort("skill-import-install", () => installFabricImportSkill(plan.target)));
|
|
2177
|
+
installResults.push(...await runBestEffort("skill-sync-install", () => installFabricSyncSkill(plan.target)));
|
|
2178
|
+
installResults.push(...await runBestEffort("skill-store-install", () => installFabricStoreSkill(plan.target)));
|
|
2179
|
+
installResults.push(...await runBestEffort("skill-audit-install", () => installFabricAuditSkill(plan.target)));
|
|
2180
|
+
installResults.push(...await runBestEffort("skill-connect-install", () => installFabricConnectSkill(plan.target)));
|
|
2181
|
+
installResults.push(...await runBestEffort("skill-shared-lib", () => installSharedSkillLib(plan.target)));
|
|
1938
2182
|
installResults.push(...await runBestEffort("hook-script", () => installArchiveHintHook(plan.target)));
|
|
1939
2183
|
installResults.push(...await runBestEffort("hook-broad-script", () => installKnowledgeHintBroadHook(plan.target)));
|
|
1940
2184
|
installResults.push(...await runBestEffort("hook-narrow-script", () => installKnowledgeHintNarrowHook(plan.target)));
|
|
2185
|
+
installResults.push(...await runBestEffort("hook-cite-policy-evict-script", () => installCitePolicyEvictHook(plan.target)));
|
|
1941
2186
|
installResults.push(...await runBestEffort("hook-lib", () => installHookLibs(plan.target)));
|
|
1942
2187
|
installResults.push(await runBestEffortSingle("claude-hook-config", () => mergeClaudeCodeHookConfig(plan.target)));
|
|
1943
2188
|
installResults.push(await runBestEffortSingle("codex-hook-config", () => mergeCodexHookConfig(plan.target)));
|
|
@@ -2050,9 +2295,9 @@ function formatDiffFileState(state) {
|
|
|
2050
2295
|
return t(`cli.install.diff.state.${state}`);
|
|
2051
2296
|
}
|
|
2052
2297
|
function preparePlannedPath(path, action) {
|
|
2053
|
-
|
|
2298
|
+
mkdirSync2(dirname(path), { recursive: true });
|
|
2054
2299
|
if (action === "overwritten" && existsSync4(path)) {
|
|
2055
|
-
|
|
2300
|
+
rmSync2(path, { recursive: true, force: true });
|
|
2056
2301
|
}
|
|
2057
2302
|
}
|
|
2058
2303
|
function createDefaultInitWizardAdapter() {
|
|
@@ -2211,13 +2456,13 @@ function assertExistingDirectory3(target) {
|
|
|
2211
2456
|
}
|
|
2212
2457
|
function detectPackageManager(cwd) {
|
|
2213
2458
|
const workspaceRoot = resolve3(cwd);
|
|
2214
|
-
if (existsSync4(
|
|
2459
|
+
if (existsSync4(join6(workspaceRoot, "pnpm-lock.yaml"))) {
|
|
2215
2460
|
return "pnpm";
|
|
2216
2461
|
}
|
|
2217
|
-
if (existsSync4(
|
|
2462
|
+
if (existsSync4(join6(workspaceRoot, "yarn.lock"))) {
|
|
2218
2463
|
return "yarn";
|
|
2219
2464
|
}
|
|
2220
|
-
if (existsSync4(
|
|
2465
|
+
if (existsSync4(join6(workspaceRoot, "package-lock.json"))) {
|
|
2221
2466
|
return "npm";
|
|
2222
2467
|
}
|
|
2223
2468
|
return "npm";
|
|
@@ -2240,7 +2485,7 @@ function createInitialMeta() {
|
|
|
2240
2485
|
function appendInstallDiffLedgerEvent(eventsPath, payload) {
|
|
2241
2486
|
const event = {
|
|
2242
2487
|
kind: "fabric-event",
|
|
2243
|
-
id: `event:${
|
|
2488
|
+
id: `event:${randomUUID2()}`,
|
|
2244
2489
|
ts: Date.now(),
|
|
2245
2490
|
schema_version: 1,
|
|
2246
2491
|
event_type: "install_diff_applied",
|