@fenglimg/fabric-cli 2.2.0-rc.1 → 2.2.0-rc.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-5JG4QJLO.js +64 -0
- package/dist/chunk-5SSNE5GM.js +301 -0
- package/dist/chunk-EOT63RDH.js +36 -0
- package/dist/{chunk-AOE6AYI7.js → chunk-F6ITRM7T.js} +2 -2
- package/dist/{chunk-WU6GAPKH.js → chunk-H3FE6VIK.js} +3 -5
- package/dist/chunk-XCBVSGCS.js +25 -0
- package/dist/{chunk-2R55HNVD.js → chunk-XHHCRDIR.js} +71 -6
- package/dist/{config-XYRBZJDU.js → config-VJMXCLXW.js} +1 -1
- package/dist/{doctor-YONYXDX6.js → doctor-U5W4CX5I.js} +118 -7
- package/dist/index.js +13 -12
- package/dist/{install-74ANPCCP.js → install-7XJ64WSC.js} +252 -246
- package/dist/{plan-context-hint-FC6P3WFE.js → plan-context-hint-CHVZGOZ5.js} +21 -8
- package/dist/{scope-explain-CDIZESP5.js → scope-explain-BWRWBCCP.js} +14 -4
- package/dist/{status-GLQWLWH6.js → status-7UFLWRX7.js} +17 -6
- package/dist/store-ZEZMQVG7.js +817 -0
- package/dist/{sync-UJ4BBCZJ.js → sync-EA5HZMXM.js} +165 -21
- package/dist/{uninstall-C3QXKOO6.js → uninstall-F75MPKQC.js} +27 -1
- package/dist/{whoami-2MLO4Y37.js → whoami-3FRWYGML.js} +16 -5
- package/package.json +3 -3
- package/templates/hooks/cite-policy-evict.cjs +412 -160
- package/templates/hooks/configs/claude-code.json +17 -2
- package/templates/hooks/configs/codex-hooks.json +14 -2
- package/templates/hooks/configs/cursor-hooks.json +14 -2
- package/templates/hooks/fabric-hint.cjs +151 -15
- package/templates/hooks/knowledge-hint-broad.cjs +12 -1
- package/templates/hooks/knowledge-hint-narrow.cjs +54 -1
- package/templates/hooks/post-tooluse-mutation.cjs +285 -0
- package/templates/hooks/session-end-marker.cjs +140 -0
- package/templates/skills/fabric-archive/SKILL.md +7 -1
- package/dist/chunk-4R2CYEA4.js +0 -116
- package/dist/chunk-L4Q55UC4.js +0 -52
- package/dist/chunk-LFIKMVY7.js +0 -27
- package/dist/chunk-RYAFBNES.js +0 -33
- package/dist/chunk-T5RPGCCM.js +0 -40
- package/dist/store-XB3ADT65.js +0 -144
|
@@ -13,6 +13,8 @@ import {
|
|
|
13
13
|
installHookLibs,
|
|
14
14
|
installKnowledgeHintBroadHook,
|
|
15
15
|
installKnowledgeHintNarrowHook,
|
|
16
|
+
installPostTooluseMutationHook,
|
|
17
|
+
installSessionEndMarkerHook,
|
|
16
18
|
installSharedSkillLib,
|
|
17
19
|
mergeClaudeCodeHookConfig,
|
|
18
20
|
mergeCodexHookConfig,
|
|
@@ -22,7 +24,7 @@ import {
|
|
|
22
24
|
writeCodexBootstrapManagedBlock,
|
|
23
25
|
writeCursorBootstrapManagedBlock,
|
|
24
26
|
writeFabricAgentsSnapshot
|
|
25
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-XHHCRDIR.js";
|
|
26
28
|
import {
|
|
27
29
|
displayWidth,
|
|
28
30
|
padEnd,
|
|
@@ -34,29 +36,40 @@ import {
|
|
|
34
36
|
} from "./chunk-COI5VDFU.js";
|
|
35
37
|
import {
|
|
36
38
|
installMcpClients
|
|
37
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-F6ITRM7T.js";
|
|
38
40
|
import {
|
|
39
41
|
detectClientSupports
|
|
40
42
|
} from "./chunk-XC5RUHLK.js";
|
|
43
|
+
import {
|
|
44
|
+
regenerateBindingsSnapshot
|
|
45
|
+
} from "./chunk-H3FE6VIK.js";
|
|
46
|
+
import "./chunk-EOT63RDH.js";
|
|
41
47
|
import {
|
|
42
48
|
getProjectTranslator,
|
|
43
49
|
t
|
|
44
50
|
} from "./chunk-2CY4BMTH.js";
|
|
51
|
+
import {
|
|
52
|
+
storeBind,
|
|
53
|
+
storeCreate,
|
|
54
|
+
storeList,
|
|
55
|
+
storeSwitchWrite,
|
|
56
|
+
syncStoreAliasLinks,
|
|
57
|
+
unboundAvailableStores
|
|
58
|
+
} from "./chunk-5SSNE5GM.js";
|
|
45
59
|
import {
|
|
46
60
|
globalConfigPath,
|
|
47
61
|
loadGlobalConfig,
|
|
62
|
+
loadProjectConfig,
|
|
48
63
|
resolveGlobalRoot,
|
|
49
64
|
saveGlobalConfig
|
|
50
|
-
} from "./chunk-
|
|
65
|
+
} from "./chunk-XCBVSGCS.js";
|
|
51
66
|
|
|
52
67
|
// src/commands/install.ts
|
|
53
68
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
54
|
-
import { homedir } from "os";
|
|
55
69
|
import * as childProcess from "child_process";
|
|
56
|
-
import { appendFileSync, existsSync as
|
|
57
|
-
import { dirname, isAbsolute as isAbsolute3, join as
|
|
58
|
-
import { cancel, confirm, group, intro, isCancel, log, note, outro, select } from "@clack/prompts";
|
|
59
|
-
import { defaultAgentsMetaCounters } from "@fenglimg/fabric-shared";
|
|
70
|
+
import { appendFileSync, existsSync as existsSync5, mkdirSync as mkdirSync2, rmSync as rmSync2, statSync as statSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
71
|
+
import { dirname, isAbsolute as isAbsolute3, join as join7, resolve as resolve3 } from "path";
|
|
72
|
+
import { cancel, confirm, group, intro, isCancel, log, note, outro, select, text } from "@clack/prompts";
|
|
60
73
|
import { atomicWriteJson } from "@fenglimg/fabric-shared/node/atomic-write";
|
|
61
74
|
import { defineCommand } from "citty";
|
|
62
75
|
|
|
@@ -79,6 +92,8 @@ async function installHooks(target, _options = {}) {
|
|
|
79
92
|
results.push(...await runStep(() => installKnowledgeHintBroadHook(normalizedTarget)));
|
|
80
93
|
results.push(...await runStep(() => installKnowledgeHintNarrowHook(normalizedTarget)));
|
|
81
94
|
results.push(...await runStep(() => installCitePolicyEvictHook(normalizedTarget)));
|
|
95
|
+
results.push(...await runStep(() => installSessionEndMarkerHook(normalizedTarget)));
|
|
96
|
+
results.push(...await runStep(() => installPostTooluseMutationHook(normalizedTarget)));
|
|
82
97
|
results.push(...await runStep(() => installHookLibs(normalizedTarget)));
|
|
83
98
|
results.push(await runSingleStep("claude-hook-config", () => mergeClaudeCodeHookConfig(normalizedTarget)));
|
|
84
99
|
results.push(await runSingleStep("codex-hook-config", () => mergeCodexHookConfig(normalizedTarget)));
|
|
@@ -94,7 +109,10 @@ function validateHookPaths(projectRoot) {
|
|
|
94
109
|
const scripts = [
|
|
95
110
|
{ stepSuffix: "", hookFile: "fabric-hint.cjs" },
|
|
96
111
|
{ stepSuffix: "-broad", hookFile: "knowledge-hint-broad.cjs" },
|
|
97
|
-
{ stepSuffix: "-narrow", hookFile: "knowledge-hint-narrow.cjs" }
|
|
112
|
+
{ stepSuffix: "-narrow", hookFile: "knowledge-hint-narrow.cjs" },
|
|
113
|
+
// lifecycle-refactor W2-T2/T3: SessionEnd + PostToolUse marker hooks.
|
|
114
|
+
{ stepSuffix: "-session-end", hookFile: "session-end-marker.cjs" },
|
|
115
|
+
{ stepSuffix: "-post-tooluse", hookFile: "post-tooluse-mutation.cjs" }
|
|
98
116
|
];
|
|
99
117
|
const clients = [
|
|
100
118
|
{
|
|
@@ -197,12 +215,50 @@ function assertExistingDirectory(target) {
|
|
|
197
215
|
}
|
|
198
216
|
}
|
|
199
217
|
|
|
218
|
+
// src/install/semantic-search.ts
|
|
219
|
+
import { existsSync as existsSync2, readFileSync, writeFileSync } from "fs";
|
|
220
|
+
import { join as join2 } from "path";
|
|
221
|
+
var DEFAULT_EMBED_MODEL_PIN = "fast-bge-small-zh-v1.5";
|
|
222
|
+
function enableSemanticSearch(projectRoot, opts = {}) {
|
|
223
|
+
const model = typeof opts.model === "string" && opts.model.length > 0 ? opts.model : DEFAULT_EMBED_MODEL_PIN;
|
|
224
|
+
const configPath = join2(projectRoot, "fabric.config.json");
|
|
225
|
+
let existing = {};
|
|
226
|
+
if (existsSync2(configPath)) {
|
|
227
|
+
try {
|
|
228
|
+
const parsed = JSON.parse(readFileSync(configPath, "utf8"));
|
|
229
|
+
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
230
|
+
existing = parsed;
|
|
231
|
+
}
|
|
232
|
+
} catch {
|
|
233
|
+
existing = {};
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
const alreadyEnabled = existing.embed_enabled === true && existing.embed_model === model;
|
|
237
|
+
if (alreadyEnabled) {
|
|
238
|
+
return { configPath, model, alreadyEnabled: true, changed: false };
|
|
239
|
+
}
|
|
240
|
+
const merged = { ...existing, embed_enabled: true, embed_model: model };
|
|
241
|
+
writeFileSync(configPath, JSON.stringify(merged, null, 2) + "\n", "utf8");
|
|
242
|
+
return { configPath, model, alreadyEnabled: false, changed: true };
|
|
243
|
+
}
|
|
244
|
+
function renderSemanticSearchInstructions(model) {
|
|
245
|
+
return [
|
|
246
|
+
"\u8BED\u4E49\u641C\u7D22\u5DF2\u542F\u7528 (embed_enabled=true, embed_model=" + model + ")\u3002\u8FD8\u9700\u4E24\u6B65 (\u4E00\u6B21\u6027):",
|
|
247
|
+
" 1. \u5B89\u88C5\u53EF\u9009 embedder (\u88C5\u5230 MCP server \u89E3\u6790\u6A21\u5757\u7684\u4F4D\u7F6E \u2014 \u5168\u5C40\u5B89\u88C5\u5373\u5168\u5C40):",
|
|
248
|
+
" npm i -g fastembed",
|
|
249
|
+
" 2. \u9884\u70ED\u6A21\u578B\u7F13\u5B58 (\u9996\u8DD1\u4F1A\u8054\u7F51\u4E0B\u8F7D\u6A21\u578B\u6743\u91CD ~\u6570\u5341-\u6570\u767E MB, \u4E0D\u4E0A\u4F20\u4EFB\u4F55 KB \u6570\u636E):",
|
|
250
|
+
" export FABRIC_EMBED_CACHE_DIR=~/.cache/fabric-embed # \u4E25\u683C\u79BB\u7EBF\u8005\u9884\u5148\u653E\u597D\u6743\u91CD",
|
|
251
|
+
" \u6CE8: \u5207\u6362 embed_model \u540E\u5DF2\u6709\u5411\u91CF\u7EF4\u5EA6/\u8BED\u4E49\u53D8\u5316, \u4E0B\u6B21 recall \u4F1A\u6309\u65B0\u6A21\u578B\u91CD\u65B0\u5D4C\u5165 (doc \u5411\u91CF\u6309\u6587\u672C\u7F13\u5B58, \u81EA\u52A8\u5931\u914D\u91CD\u7B97)\u3002",
|
|
252
|
+
" \u5173\u95ED: \u7F16\u8F91 fabric.config.json \u8BBE embed_enabled=false\u3002"
|
|
253
|
+
];
|
|
254
|
+
}
|
|
255
|
+
|
|
200
256
|
// src/install/run-global-install.ts
|
|
201
257
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
202
258
|
import { randomUUID } from "crypto";
|
|
203
259
|
import { mkdirSync, mkdtempSync, renameSync } from "fs";
|
|
204
260
|
import { tmpdir } from "os";
|
|
205
|
-
import { join as
|
|
261
|
+
import { join as join4 } from "path";
|
|
206
262
|
import { STORES_ROOT_DIR as STORES_ROOT_DIR2, addMountedStore, readStoreIdentity } from "@fenglimg/fabric-shared";
|
|
207
263
|
import { GenericIOError } from "@fenglimg/fabric-shared/errors";
|
|
208
264
|
|
|
@@ -229,7 +285,7 @@ function deriveUid(opts = {}) {
|
|
|
229
285
|
|
|
230
286
|
// src/install/install-global.ts
|
|
231
287
|
import { rmSync } from "fs";
|
|
232
|
-
import { join as
|
|
288
|
+
import { join as join3 } from "path";
|
|
233
289
|
import {
|
|
234
290
|
STORES_ROOT_DIR,
|
|
235
291
|
globalConfigSchema,
|
|
@@ -288,7 +344,7 @@ async function installGlobalCore(options) {
|
|
|
288
344
|
};
|
|
289
345
|
}
|
|
290
346
|
const alias = options.personalAlias ?? "personal";
|
|
291
|
-
const personalDir =
|
|
347
|
+
const personalDir = join3(options.globalRoot, STORES_ROOT_DIR, options.personalStoreUuid);
|
|
292
348
|
let config = null;
|
|
293
349
|
const receipt = await runInstallTransaction([
|
|
294
350
|
{
|
|
@@ -339,10 +395,10 @@ function gitClone(url, dest) {
|
|
|
339
395
|
}
|
|
340
396
|
}
|
|
341
397
|
function mountStoreFromRemote(url, globalRoot) {
|
|
342
|
-
const storesRoot =
|
|
398
|
+
const storesRoot = join4(globalRoot, STORES_ROOT_DIR2);
|
|
343
399
|
mkdirSync(storesRoot, { recursive: true });
|
|
344
|
-
const tmp = mkdtempSync(
|
|
345
|
-
const cloneDest =
|
|
400
|
+
const tmp = mkdtempSync(join4(tmpdir(), "fabric-clone-"));
|
|
401
|
+
const cloneDest = join4(tmp, "store");
|
|
346
402
|
gitClone(url, cloneDest);
|
|
347
403
|
const identity = readStoreIdentity(cloneDest);
|
|
348
404
|
if (identity === null) {
|
|
@@ -350,7 +406,7 @@ function mountStoreFromRemote(url, globalRoot) {
|
|
|
350
406
|
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
407
|
});
|
|
352
408
|
}
|
|
353
|
-
const finalDir =
|
|
409
|
+
const finalDir = join4(storesRoot, identity.store_uuid);
|
|
354
410
|
renameSync(cloneDest, finalDir);
|
|
355
411
|
const config = loadGlobalConfig(globalRoot);
|
|
356
412
|
if (config === null) {
|
|
@@ -364,6 +420,7 @@ function mountStoreFromRemote(url, globalRoot) {
|
|
|
364
420
|
globalRoot
|
|
365
421
|
);
|
|
366
422
|
console.log(`mounted store '${alias}' (${identity.store_uuid}) from ${url}`);
|
|
423
|
+
return { store_uuid: identity.store_uuid, alias };
|
|
367
424
|
}
|
|
368
425
|
async function runGlobalInstall(options = {}, globalRoot = resolveGlobalRoot()) {
|
|
369
426
|
const uid = options.uid ?? deriveUid();
|
|
@@ -384,23 +441,24 @@ async function runGlobalInstall(options = {}, globalRoot = resolveGlobalRoot())
|
|
|
384
441
|
if (options.url !== void 0) {
|
|
385
442
|
mountStoreFromRemote(options.url, globalRoot);
|
|
386
443
|
}
|
|
444
|
+
syncStoreAliasLinks(globalRoot);
|
|
387
445
|
}
|
|
388
446
|
|
|
389
447
|
// src/lib/detect-language.ts
|
|
390
|
-
import { existsSync as
|
|
391
|
-
import { join as
|
|
448
|
+
import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync2, statSync as statSync2 } from "fs";
|
|
449
|
+
import { join as join5 } from "path";
|
|
392
450
|
function detectExistingLanguage(target) {
|
|
393
451
|
const ZH_CN_RATIO_THRESHOLD = 0.3;
|
|
394
452
|
const samples = [];
|
|
395
|
-
const readmePath =
|
|
396
|
-
if (
|
|
453
|
+
const readmePath = join5(target, "README.md");
|
|
454
|
+
if (existsSync3(readmePath)) {
|
|
397
455
|
try {
|
|
398
|
-
samples.push(
|
|
456
|
+
samples.push(readFileSync2(readmePath, "utf8"));
|
|
399
457
|
} catch {
|
|
400
458
|
}
|
|
401
459
|
}
|
|
402
|
-
const docsDir =
|
|
403
|
-
if (
|
|
460
|
+
const docsDir = join5(target, "docs");
|
|
461
|
+
if (existsSync3(docsDir)) {
|
|
404
462
|
try {
|
|
405
463
|
const stat = statSync2(docsDir);
|
|
406
464
|
if (stat.isDirectory()) {
|
|
@@ -408,7 +466,7 @@ function detectExistingLanguage(target) {
|
|
|
408
466
|
if (!entry.isFile()) continue;
|
|
409
467
|
if (!/\.(md|mdx|txt)$/iu.test(entry.name)) continue;
|
|
410
468
|
try {
|
|
411
|
-
samples.push(
|
|
469
|
+
samples.push(readFileSync2(join5(docsDir, entry.name), "utf8"));
|
|
412
470
|
} catch {
|
|
413
471
|
}
|
|
414
472
|
}
|
|
@@ -441,9 +499,9 @@ function detectExistingLanguage(target) {
|
|
|
441
499
|
|
|
442
500
|
// src/scanner/forensic.ts
|
|
443
501
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
444
|
-
import { existsSync as
|
|
502
|
+
import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync3, statSync as statSync3 } from "fs";
|
|
445
503
|
import { createRequire } from "module";
|
|
446
|
-
import { basename, extname, isAbsolute as isAbsolute2, join as
|
|
504
|
+
import { basename, extname, isAbsolute as isAbsolute2, join as join6, posix, relative, resolve as resolve2, sep } from "path";
|
|
447
505
|
import {
|
|
448
506
|
buildScanRecommendations,
|
|
449
507
|
forensicReportSchema
|
|
@@ -588,7 +646,7 @@ function buildTopology(root) {
|
|
|
588
646
|
continue;
|
|
589
647
|
}
|
|
590
648
|
for (const entry of readdirSync2(current, { withFileTypes: true })) {
|
|
591
|
-
const absolutePath =
|
|
649
|
+
const absolutePath = join6(current, entry.name);
|
|
592
650
|
const relativePath = toPosixPath(relative(root, absolutePath));
|
|
593
651
|
if (relativePath.length === 0) {
|
|
594
652
|
continue;
|
|
@@ -627,7 +685,7 @@ function buildTopology(root) {
|
|
|
627
685
|
};
|
|
628
686
|
}
|
|
629
687
|
function assertExistingDirectory2(target) {
|
|
630
|
-
if (!
|
|
688
|
+
if (!existsSync4(target) || !statSync3(target).isDirectory()) {
|
|
631
689
|
throw new Error(`Target must be an existing directory: ${target}`);
|
|
632
690
|
}
|
|
633
691
|
}
|
|
@@ -676,7 +734,7 @@ function getEntryPointReason(relativePath) {
|
|
|
676
734
|
async function buildCodeSamples(target, entryPoints, frameworkKind, topology, packageDependencies) {
|
|
677
735
|
const samples = [];
|
|
678
736
|
for (const entryPoint of entryPoints.slice(0, SAMPLE_LIMIT)) {
|
|
679
|
-
const absolutePath =
|
|
737
|
+
const absolutePath = join6(target, ...entryPoint.path.split("/"));
|
|
680
738
|
const sample = readFirstLines(absolutePath, SAMPLE_LINE_LIMIT);
|
|
681
739
|
const patternAnalysis = await inferPatternHint(entryPoint.path, sample.snippet, {
|
|
682
740
|
frameworkKind,
|
|
@@ -696,7 +754,7 @@ async function buildCodeSamples(target, entryPoints, frameworkKind, topology, pa
|
|
|
696
754
|
}
|
|
697
755
|
function readFirstLines(path, lineLimit) {
|
|
698
756
|
try {
|
|
699
|
-
const lines =
|
|
757
|
+
const lines = readFileSync3(path, "utf8").split(/\r?\n/);
|
|
700
758
|
if (lines.at(-1) === "") {
|
|
701
759
|
lines.pop();
|
|
702
760
|
}
|
|
@@ -713,12 +771,12 @@ function readFirstLines(path, lineLimit) {
|
|
|
713
771
|
}
|
|
714
772
|
}
|
|
715
773
|
function readPackageDependencies(target) {
|
|
716
|
-
const packageJsonPath =
|
|
717
|
-
if (!
|
|
774
|
+
const packageJsonPath = join6(target, "package.json");
|
|
775
|
+
if (!existsSync4(packageJsonPath)) {
|
|
718
776
|
return /* @__PURE__ */ new Map();
|
|
719
777
|
}
|
|
720
778
|
try {
|
|
721
|
-
const packageJson = JSON.parse(
|
|
779
|
+
const packageJson = JSON.parse(readFileSync3(packageJsonPath, "utf8"));
|
|
722
780
|
return new Map([
|
|
723
781
|
...Object.entries(packageJson.dependencies ?? {}),
|
|
724
782
|
...Object.entries(packageJson.devDependencies ?? {}),
|
|
@@ -1054,16 +1112,16 @@ function scoreFrameworkConfidence(input) {
|
|
|
1054
1112
|
return input.configCount > 0 || input.packageCount > 0 ? "MEDIUM" : "LOW";
|
|
1055
1113
|
}
|
|
1056
1114
|
function readReadmeInfo(target) {
|
|
1057
|
-
const readmePath =
|
|
1058
|
-
const hasContributing =
|
|
1059
|
-
if (!
|
|
1115
|
+
const readmePath = join6(target, "README.md");
|
|
1116
|
+
const hasContributing = existsSync4(join6(target, "CONTRIBUTING.md"));
|
|
1117
|
+
if (!existsSync4(readmePath)) {
|
|
1060
1118
|
return {
|
|
1061
1119
|
quality: "missing",
|
|
1062
1120
|
line_count: 0,
|
|
1063
1121
|
has_contributing: hasContributing
|
|
1064
1122
|
};
|
|
1065
1123
|
}
|
|
1066
|
-
const readme =
|
|
1124
|
+
const readme = readFileSync3(readmePath, "utf8");
|
|
1067
1125
|
const wordCount = readme.trim().split(/\s+/).filter(Boolean).length;
|
|
1068
1126
|
return {
|
|
1069
1127
|
quality: wordCount >= 200 ? "ok" : "stub",
|
|
@@ -1529,10 +1587,10 @@ function buildSkillRecommendations(frameworkKind, topology, readme, projectRoot)
|
|
|
1529
1587
|
);
|
|
1530
1588
|
}
|
|
1531
1589
|
function readProjectName(target) {
|
|
1532
|
-
const packageJsonPath =
|
|
1533
|
-
if (
|
|
1590
|
+
const packageJsonPath = join6(target, "package.json");
|
|
1591
|
+
if (existsSync4(packageJsonPath)) {
|
|
1534
1592
|
try {
|
|
1535
|
-
const packageJson = JSON.parse(
|
|
1593
|
+
const packageJson = JSON.parse(readFileSync3(packageJsonPath, "utf8"));
|
|
1536
1594
|
if (packageJson.name !== void 0 && packageJson.name.trim().length > 0) {
|
|
1537
1595
|
return packageJson.name;
|
|
1538
1596
|
}
|
|
@@ -1543,7 +1601,7 @@ function readProjectName(target) {
|
|
|
1543
1601
|
return basename(target);
|
|
1544
1602
|
}
|
|
1545
1603
|
function getCliVersion() {
|
|
1546
|
-
return true ? "2.2.0-rc.
|
|
1604
|
+
return true ? "2.2.0-rc.4" : "unknown";
|
|
1547
1605
|
}
|
|
1548
1606
|
function sortRecord(record) {
|
|
1549
1607
|
return Object.fromEntries(Object.entries(record).sort(([left], [right]) => left.localeCompare(right)));
|
|
@@ -1553,7 +1611,7 @@ function toPosixPath(path) {
|
|
|
1553
1611
|
}
|
|
1554
1612
|
|
|
1555
1613
|
// src/commands/install.ts
|
|
1556
|
-
var LOCAL_FABRIC_SERVER_PATH =
|
|
1614
|
+
var LOCAL_FABRIC_SERVER_PATH = join7("node_modules", "@fenglimg", "fabric-server", "dist", "index.js");
|
|
1557
1615
|
var FABRIC_SERVER_PACKAGE = "@fenglimg/fabric-server";
|
|
1558
1616
|
var INIT_WIZARD_GROUP_CANCELLED = /* @__PURE__ */ Symbol("init-wizard-group-cancelled");
|
|
1559
1617
|
var installCommand = defineCommand({
|
|
@@ -1581,24 +1639,23 @@ var installCommand = defineCommand({
|
|
|
1581
1639
|
description: t("cli.install.args.yes.description"),
|
|
1582
1640
|
default: false
|
|
1583
1641
|
},
|
|
1584
|
-
|
|
1642
|
+
global: {
|
|
1585
1643
|
type: "boolean",
|
|
1586
|
-
description:
|
|
1644
|
+
description: "Set up global Fabric (~/.fabric: uid + personal store + config)",
|
|
1587
1645
|
default: false
|
|
1588
1646
|
},
|
|
1589
|
-
|
|
1590
|
-
type: "
|
|
1591
|
-
description:
|
|
1592
|
-
default: false
|
|
1647
|
+
url: {
|
|
1648
|
+
type: "string",
|
|
1649
|
+
description: "Clone + mount a shared store remote. In a project install: also binds it to this project and sets it as the write target. With --global: mounts it machine-wide only."
|
|
1593
1650
|
},
|
|
1594
|
-
|
|
1651
|
+
"enable-embed": {
|
|
1595
1652
|
type: "boolean",
|
|
1596
|
-
description: "
|
|
1653
|
+
description: t("cli.install.args.enable-embed.description"),
|
|
1597
1654
|
default: false
|
|
1598
1655
|
},
|
|
1599
|
-
|
|
1656
|
+
"embed-model": {
|
|
1600
1657
|
type: "string",
|
|
1601
|
-
description: "
|
|
1658
|
+
description: t("cli.install.args.embed-model.description")
|
|
1602
1659
|
}
|
|
1603
1660
|
},
|
|
1604
1661
|
async run({ args }) {
|
|
@@ -1606,83 +1663,6 @@ var installCommand = defineCommand({
|
|
|
1606
1663
|
}
|
|
1607
1664
|
});
|
|
1608
1665
|
var install_default = installCommand;
|
|
1609
|
-
async function runSkillsOnlyRefresh(targetInput) {
|
|
1610
|
-
const target = normalizeTarget3(targetInput);
|
|
1611
|
-
const metaPath = join6(target, ".fabric", "agents.meta.json");
|
|
1612
|
-
if (!existsSync4(metaPath)) {
|
|
1613
|
-
const message = t("cli.install.force-skills-only.uninitialised.message");
|
|
1614
|
-
const hint = t("cli.install.force-skills-only.uninitialised.hint");
|
|
1615
|
-
process.stderr.write(`${message}
|
|
1616
|
-
${hint}
|
|
1617
|
-
`);
|
|
1618
|
-
process.exitCode = 1;
|
|
1619
|
-
return;
|
|
1620
|
-
}
|
|
1621
|
-
console.log(formatInitStageHeader(t("cli.install.force-skills-only.banner")));
|
|
1622
|
-
const results = [];
|
|
1623
|
-
results.push(...await cleanupDeprecatedSkills(target));
|
|
1624
|
-
results.push(...await installFabricArchiveSkill(target));
|
|
1625
|
-
results.push(...await installFabricReviewSkill(target));
|
|
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));
|
|
1632
|
-
let written = 0;
|
|
1633
|
-
let skipped = 0;
|
|
1634
|
-
let errors = 0;
|
|
1635
|
-
for (const r of results) {
|
|
1636
|
-
if (r.status === "written") written += 1;
|
|
1637
|
-
else if (r.status === "skipped") skipped += 1;
|
|
1638
|
-
else if (r.status === "error") errors += 1;
|
|
1639
|
-
}
|
|
1640
|
-
console.log(
|
|
1641
|
-
t("cli.install.force-skills-only.summary", {
|
|
1642
|
-
written: String(written),
|
|
1643
|
-
skipped: String(skipped),
|
|
1644
|
-
errors: String(errors)
|
|
1645
|
-
})
|
|
1646
|
-
);
|
|
1647
|
-
if (errors > 0) {
|
|
1648
|
-
for (const r of results) {
|
|
1649
|
-
if (r.status === "error") {
|
|
1650
|
-
process.stderr.write(` ${r.step} ${r.path}: ${r.message ?? "error"}
|
|
1651
|
-
`);
|
|
1652
|
-
}
|
|
1653
|
-
}
|
|
1654
|
-
process.exitCode = 1;
|
|
1655
|
-
}
|
|
1656
|
-
}
|
|
1657
|
-
async function runHooksOnlyRefresh(targetInput) {
|
|
1658
|
-
const target = normalizeTarget3(targetInput);
|
|
1659
|
-
const metaPath = join6(target, ".fabric", "agents.meta.json");
|
|
1660
|
-
if (!existsSync4(metaPath)) {
|
|
1661
|
-
const message = t("cli.install.force-hooks-only.uninitialised.message");
|
|
1662
|
-
const hint = t("cli.install.force-hooks-only.uninitialised.hint");
|
|
1663
|
-
process.stderr.write(`${message}
|
|
1664
|
-
${hint}
|
|
1665
|
-
`);
|
|
1666
|
-
process.exitCode = 1;
|
|
1667
|
-
return;
|
|
1668
|
-
}
|
|
1669
|
-
console.log(formatInitStageHeader(t("cli.install.force-hooks-only.banner")));
|
|
1670
|
-
const result = await installHooks(target);
|
|
1671
|
-
console.log(
|
|
1672
|
-
t("cli.install.force-hooks-only.summary", {
|
|
1673
|
-
written: String(result.installed.length),
|
|
1674
|
-
skipped: String(result.skipped.length),
|
|
1675
|
-
errors: String(result.errors.length)
|
|
1676
|
-
})
|
|
1677
|
-
);
|
|
1678
|
-
if (result.errors.length > 0) {
|
|
1679
|
-
for (const err of result.errors) {
|
|
1680
|
-
process.stderr.write(` ${err}
|
|
1681
|
-
`);
|
|
1682
|
-
}
|
|
1683
|
-
process.exitCode = 1;
|
|
1684
|
-
}
|
|
1685
|
-
}
|
|
1686
1666
|
async function runInitCommand(args) {
|
|
1687
1667
|
const logger = createDebugLogger(args.debug);
|
|
1688
1668
|
if (args.global === true) {
|
|
@@ -1690,19 +1670,15 @@ async function runInitCommand(args) {
|
|
|
1690
1670
|
return;
|
|
1691
1671
|
}
|
|
1692
1672
|
const resolution = resolveDevMode(args.target, process.cwd());
|
|
1693
|
-
if (args["force-skills-only"] === true) {
|
|
1694
|
-
await runSkillsOnlyRefresh(resolution.target);
|
|
1695
|
-
return;
|
|
1696
|
-
}
|
|
1697
|
-
if (args["force-hooks-only"] === true) {
|
|
1698
|
-
await runHooksOnlyRefresh(resolution.target);
|
|
1699
|
-
return;
|
|
1700
|
-
}
|
|
1701
1673
|
const intent = resolveInitCliIntent(args, resolution.target);
|
|
1702
1674
|
logger(`init target source: ${resolution.source}`);
|
|
1703
1675
|
for (const step of resolution.chain) {
|
|
1704
1676
|
logger(step);
|
|
1705
1677
|
}
|
|
1678
|
+
if (loadGlobalConfig() === null) {
|
|
1679
|
+
logger("no global Fabric config found \u2014 minting ~/.fabric (uid + personal store)");
|
|
1680
|
+
await runGlobalInstall({});
|
|
1681
|
+
}
|
|
1706
1682
|
const supports = detectClientSupports(intent.target);
|
|
1707
1683
|
const basePlan = await buildInitExecutionPlan({
|
|
1708
1684
|
target: intent.target,
|
|
@@ -1723,9 +1699,121 @@ async function runInitCommand(args) {
|
|
|
1723
1699
|
console.log(t("cli.install.next-steps"));
|
|
1724
1700
|
console.log("");
|
|
1725
1701
|
console.log(paint.muted("More: docs/surfaces.md explains when to use CLI vs Skill vs MCP."));
|
|
1702
|
+
if (typeof args.url === "string" && args.url.length > 0) {
|
|
1703
|
+
bindRemoteStoreToProject(resolution.target, args.url);
|
|
1704
|
+
} else if (intent.wizardEnabled) {
|
|
1705
|
+
await promptStoreOnboarding(resolution.target);
|
|
1706
|
+
}
|
|
1707
|
+
const unboundStores = unboundAvailableStores(resolution.target);
|
|
1708
|
+
if (unboundStores.length > 0) {
|
|
1709
|
+
console.log("");
|
|
1710
|
+
console.log(
|
|
1711
|
+
t("cli.install.store-bind-nudge", {
|
|
1712
|
+
aliases: unboundStores.map((s) => `'${s.alias}'`).join(", "),
|
|
1713
|
+
first: unboundStores[0].alias
|
|
1714
|
+
})
|
|
1715
|
+
);
|
|
1716
|
+
}
|
|
1717
|
+
if (args["enable-embed"] === true) {
|
|
1718
|
+
enableSemanticSearchAndReport(resolution.target, args["embed-model"]);
|
|
1719
|
+
} else if (intent.wizardEnabled) {
|
|
1720
|
+
await promptSemanticSearch(resolution.target);
|
|
1721
|
+
}
|
|
1726
1722
|
}
|
|
1727
1723
|
return result;
|
|
1728
1724
|
}
|
|
1725
|
+
function enableSemanticSearchAndReport(projectRoot, model) {
|
|
1726
|
+
const enabled = enableSemanticSearch(projectRoot, model === void 0 ? {} : { model });
|
|
1727
|
+
console.log("");
|
|
1728
|
+
if (enabled.alreadyEnabled) {
|
|
1729
|
+
console.log(paint.muted(`\u8BED\u4E49\u641C\u7D22\u5DF2\u662F\u542F\u7528\u72B6\u6001 (embed_model=${enabled.model})\uFF0C\u672A\u6539\u52A8 ${enabled.configPath}\u3002`));
|
|
1730
|
+
return;
|
|
1731
|
+
}
|
|
1732
|
+
for (const line of renderSemanticSearchInstructions(enabled.model)) {
|
|
1733
|
+
console.log(line);
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
async function promptSemanticSearch(projectRoot) {
|
|
1737
|
+
const enable = await confirm({
|
|
1738
|
+
message: "Enable vector semantic search? (downloads an embedding model on first use)",
|
|
1739
|
+
initialValue: false
|
|
1740
|
+
});
|
|
1741
|
+
if (isCancel(enable) || !enable) {
|
|
1742
|
+
return;
|
|
1743
|
+
}
|
|
1744
|
+
enableSemanticSearchAndReport(projectRoot);
|
|
1745
|
+
}
|
|
1746
|
+
function bindRemoteStoreToProject(projectRoot, url, globalRoot = resolveGlobalRoot()) {
|
|
1747
|
+
const already = storeList(globalRoot).find((store) => store.remote === url);
|
|
1748
|
+
const mounted = already ?? mountStoreFromRemote(url, globalRoot);
|
|
1749
|
+
storeBind(projectRoot, { id: mounted.alias, suggested_remote: url });
|
|
1750
|
+
storeSwitchWrite(projectRoot, mounted.alias);
|
|
1751
|
+
regenerateBindingsSnapshot(projectRoot, { now: (/* @__PURE__ */ new Date()).toISOString(), globalRoot });
|
|
1752
|
+
console.log("");
|
|
1753
|
+
console.log(
|
|
1754
|
+
paint.success(
|
|
1755
|
+
`bound store '${mounted.alias}' to this project and set it as the write target.`
|
|
1756
|
+
)
|
|
1757
|
+
);
|
|
1758
|
+
}
|
|
1759
|
+
function bindCreatedStoreToProject(projectRoot, alias, options = {}) {
|
|
1760
|
+
const globalRoot = options.globalRoot ?? resolveGlobalRoot();
|
|
1761
|
+
storeCreate(alias, (/* @__PURE__ */ new Date()).toISOString(), {
|
|
1762
|
+
...options.remote === void 0 ? {} : { remote: options.remote },
|
|
1763
|
+
globalRoot
|
|
1764
|
+
});
|
|
1765
|
+
storeBind(
|
|
1766
|
+
projectRoot,
|
|
1767
|
+
options.remote === void 0 ? { id: alias } : { id: alias, suggested_remote: options.remote }
|
|
1768
|
+
);
|
|
1769
|
+
storeSwitchWrite(projectRoot, alias);
|
|
1770
|
+
regenerateBindingsSnapshot(projectRoot, { now: (/* @__PURE__ */ new Date()).toISOString(), globalRoot });
|
|
1771
|
+
console.log("");
|
|
1772
|
+
console.log(
|
|
1773
|
+
paint.success(
|
|
1774
|
+
`created store '${alias}', bound it to this project, and set it as the write target.`
|
|
1775
|
+
)
|
|
1776
|
+
);
|
|
1777
|
+
}
|
|
1778
|
+
async function promptStoreOnboarding(projectRoot) {
|
|
1779
|
+
const config = loadProjectConfig(projectRoot);
|
|
1780
|
+
if (typeof config?.active_write_store === "string" && config.active_write_store.length > 0) {
|
|
1781
|
+
return;
|
|
1782
|
+
}
|
|
1783
|
+
const choice = await select({
|
|
1784
|
+
message: "Set up a team / shared knowledge store for this project?",
|
|
1785
|
+
initialValue: "skip",
|
|
1786
|
+
options: [
|
|
1787
|
+
{ value: "skip", label: "skip", hint: "personal store only (default)" },
|
|
1788
|
+
{ value: "join", label: "join existing", hint: "clone + bind a shared store from a git remote" },
|
|
1789
|
+
{ value: "create", label: "create new", hint: "start a fresh local store (optionally remote-backed)" }
|
|
1790
|
+
]
|
|
1791
|
+
});
|
|
1792
|
+
if (isCancel(choice) || choice === "skip") {
|
|
1793
|
+
return;
|
|
1794
|
+
}
|
|
1795
|
+
if (choice === "join") {
|
|
1796
|
+
const url = await text({
|
|
1797
|
+
message: "Shared store git remote (url):",
|
|
1798
|
+
placeholder: "git@github.com:org/knowledge.git"
|
|
1799
|
+
});
|
|
1800
|
+
if (isCancel(url) || typeof url !== "string" || url.length === 0) {
|
|
1801
|
+
return;
|
|
1802
|
+
}
|
|
1803
|
+
bindRemoteStoreToProject(projectRoot, url);
|
|
1804
|
+
return;
|
|
1805
|
+
}
|
|
1806
|
+
const alias = await text({ message: "Local alias for the new store:", initialValue: "team" });
|
|
1807
|
+
if (isCancel(alias) || typeof alias !== "string" || alias.length === 0) {
|
|
1808
|
+
return;
|
|
1809
|
+
}
|
|
1810
|
+
const remote = await text({
|
|
1811
|
+
message: "Git remote to back it (optional \u2014 leave blank to skip):",
|
|
1812
|
+
placeholder: "git@github.com:org/knowledge.git"
|
|
1813
|
+
});
|
|
1814
|
+
const remoteStr = !isCancel(remote) && typeof remote === "string" && remote.length > 0 ? remote : void 0;
|
|
1815
|
+
bindCreatedStoreToProject(projectRoot, alias, remoteStr === void 0 ? {} : { remote: remoteStr });
|
|
1816
|
+
}
|
|
1729
1817
|
var FABRIC_GITIGNORE_CONTENT = [
|
|
1730
1818
|
"# Fabric per-dev activity ledgers & caches \u2014 auto-generated, not shared.",
|
|
1731
1819
|
"# Managed by `fabric install`; edit freely (re-install never overwrites this).",
|
|
@@ -1739,14 +1827,14 @@ var FABRIC_GITIGNORE_CONTENT = [
|
|
|
1739
1827
|
""
|
|
1740
1828
|
].join("\n");
|
|
1741
1829
|
function writeDefaultGitignore(fabricDir) {
|
|
1742
|
-
const target =
|
|
1743
|
-
if (
|
|
1830
|
+
const target = join7(fabricDir, ".gitignore");
|
|
1831
|
+
if (existsSync5(target)) return;
|
|
1744
1832
|
mkdirSync2(fabricDir, { recursive: true });
|
|
1745
|
-
|
|
1833
|
+
writeFileSync2(target, FABRIC_GITIGNORE_CONTENT, "utf8");
|
|
1746
1834
|
}
|
|
1747
1835
|
function writeDefaultFabricConfig(fabricDir, targetRoot) {
|
|
1748
|
-
const target =
|
|
1749
|
-
if (
|
|
1836
|
+
const target = join7(fabricDir, "fabric-config.json");
|
|
1837
|
+
if (existsSync5(target)) return;
|
|
1750
1838
|
const detectedLanguage = detectExistingLanguage(targetRoot);
|
|
1751
1839
|
const FABRIC_CONFIG_DEFAULTS = {
|
|
1752
1840
|
// Scan/import language policy. Fixated at init time by probing
|
|
@@ -1808,7 +1896,7 @@ function writeDefaultFabricConfig(fabricDir, targetRoot) {
|
|
|
1808
1896
|
review_stale_pending_days: 14
|
|
1809
1897
|
};
|
|
1810
1898
|
mkdirSync2(fabricDir, { recursive: true });
|
|
1811
|
-
|
|
1899
|
+
writeFileSync2(target, JSON.stringify(FABRIC_CONFIG_DEFAULTS, null, 2) + "\n", "utf8");
|
|
1812
1900
|
log.info(
|
|
1813
1901
|
`Detected and fixated fabric_language = ${detectedLanguage}; edit ${target} to override.`
|
|
1814
1902
|
);
|
|
@@ -1871,7 +1959,6 @@ async function executeInitExecutionPlan(plan) {
|
|
|
1871
1959
|
printInitPlanSummary(plan.target, plan.options, plan.mcpInstallMode, plan.supports);
|
|
1872
1960
|
}
|
|
1873
1961
|
const scaffoldStates = [
|
|
1874
|
-
{ path: plan.scaffold.metaPath, state: plan.scaffold.metaState },
|
|
1875
1962
|
{ path: plan.scaffold.eventsPath, state: plan.scaffold.eventsState },
|
|
1876
1963
|
{ path: plan.scaffold.forensicPath, state: plan.scaffold.forensicState }
|
|
1877
1964
|
];
|
|
@@ -1885,7 +1972,7 @@ async function executeInitExecutionPlan(plan) {
|
|
|
1885
1972
|
finalSupports: plan.supports
|
|
1886
1973
|
};
|
|
1887
1974
|
}
|
|
1888
|
-
if (
|
|
1975
|
+
if (existsSync5(plan.scaffold.fabricDir) && !statSync4(plan.scaffold.fabricDir).isDirectory()) {
|
|
1889
1976
|
throw new Error(
|
|
1890
1977
|
t("cli.install.diff.drift-abort", { path: plan.scaffold.fabricDir })
|
|
1891
1978
|
);
|
|
@@ -1932,26 +2019,16 @@ async function executeInitExecutionPlan(plan) {
|
|
|
1932
2019
|
finalSupports
|
|
1933
2020
|
};
|
|
1934
2021
|
}
|
|
1935
|
-
var KNOWLEDGE_SUBDIRS = ["decisions", "pitfalls", "guidelines", "models", "processes", "pending"];
|
|
1936
|
-
function resolvePersonalFabricRoot() {
|
|
1937
|
-
return process.env.FABRIC_HOME ?? homedir();
|
|
1938
|
-
}
|
|
1939
2022
|
async function buildInitFabricPlan(target, options) {
|
|
1940
2023
|
assertExistingDirectory3(target);
|
|
1941
|
-
const fabricDir =
|
|
1942
|
-
const agentsMdPath =
|
|
1943
|
-
const agentsMdAction =
|
|
1944
|
-
const
|
|
1945
|
-
const
|
|
1946
|
-
const forensicPath = join6(fabricDir, "forensic.json");
|
|
1947
|
-
const eventsPath = join6(fabricDir, "events.jsonl");
|
|
1948
|
-
const metaPath = join6(fabricDir, "agents.meta.json");
|
|
2024
|
+
const fabricDir = join7(target, ".fabric");
|
|
2025
|
+
const agentsMdPath = join7(target, "AGENTS.md");
|
|
2026
|
+
const agentsMdAction = existsSync5(agentsMdPath) ? "preserved" : "created";
|
|
2027
|
+
const forensicPath = join7(fabricDir, "forensic.json");
|
|
2028
|
+
const eventsPath = join7(fabricDir, "events.jsonl");
|
|
1949
2029
|
const replaceFabricDir = shouldReplaceWritableDirectory(fabricDir, options);
|
|
1950
|
-
const knowledgeDirAction = existsSync4(knowledgeDir) ? "overwritten" : "created";
|
|
1951
|
-
const metaClassification = classifyFreshPath(metaPath, "structural");
|
|
1952
2030
|
const eventsClassification = classifyFreshPath(eventsPath, "presence");
|
|
1953
2031
|
const forensicClassification = classifyFreshPath(forensicPath, "always-rewrite");
|
|
1954
|
-
const metaAction = diffStateToWriteAction(metaClassification.state);
|
|
1955
2032
|
const eventsAction = diffStateToWriteAction(eventsClassification.state);
|
|
1956
2033
|
const forensicAction = diffStateToWriteAction(forensicClassification.state);
|
|
1957
2034
|
const showScanProgress = process.stderr.isTTY === true;
|
|
@@ -1964,7 +2041,6 @@ async function buildInitFabricPlan(target, options) {
|
|
|
1964
2041
|
process.stderr.write(`${t("cli.install.scan-complete")}
|
|
1965
2042
|
`);
|
|
1966
2043
|
}
|
|
1967
|
-
const meta = createInitialMeta();
|
|
1968
2044
|
return {
|
|
1969
2045
|
target,
|
|
1970
2046
|
options,
|
|
@@ -1972,18 +2048,11 @@ async function buildInitFabricPlan(target, options) {
|
|
|
1972
2048
|
replaceFabricDir,
|
|
1973
2049
|
agentsMdPath,
|
|
1974
2050
|
agentsMdAction,
|
|
1975
|
-
knowledgeDir,
|
|
1976
|
-
knowledgeDirAction,
|
|
1977
|
-
personalKnowledgeDir,
|
|
1978
|
-
metaPath,
|
|
1979
|
-
metaAction,
|
|
1980
|
-
meta,
|
|
1981
2051
|
eventsPath,
|
|
1982
2052
|
eventsAction,
|
|
1983
2053
|
forensicPath,
|
|
1984
2054
|
forensicAction,
|
|
1985
2055
|
forensicReport,
|
|
1986
|
-
metaState: metaClassification.state,
|
|
1987
2056
|
eventsState: eventsClassification.state,
|
|
1988
2057
|
forensicState: forensicClassification.state
|
|
1989
2058
|
};
|
|
@@ -1995,39 +2064,18 @@ async function executeInitFabricPlan(plan) {
|
|
|
1995
2064
|
mkdirSync2(plan.fabricDir, { recursive: true });
|
|
1996
2065
|
writeDefaultFabricConfig(plan.fabricDir, plan.target);
|
|
1997
2066
|
writeDefaultGitignore(plan.fabricDir);
|
|
1998
|
-
mkdirSync2(plan.knowledgeDir, { recursive: true });
|
|
1999
|
-
for (const sub of KNOWLEDGE_SUBDIRS) {
|
|
2000
|
-
const teamSubDir = join6(plan.knowledgeDir, sub);
|
|
2001
|
-
mkdirSync2(teamSubDir, { recursive: true });
|
|
2002
|
-
const teamGitkeep = join6(teamSubDir, ".gitkeep");
|
|
2003
|
-
if (!existsSync4(teamGitkeep)) {
|
|
2004
|
-
writeFileSync(teamGitkeep, "", "utf8");
|
|
2005
|
-
}
|
|
2006
|
-
}
|
|
2007
|
-
try {
|
|
2008
|
-
mkdirSync2(plan.personalKnowledgeDir, { recursive: true });
|
|
2009
|
-
for (const sub of KNOWLEDGE_SUBDIRS) {
|
|
2010
|
-
mkdirSync2(join6(plan.personalKnowledgeDir, sub), { recursive: true });
|
|
2011
|
-
}
|
|
2012
|
-
} catch {
|
|
2013
|
-
}
|
|
2014
|
-
if (plan.metaState === "missing") {
|
|
2015
|
-
preparePlannedPath(plan.metaPath, plan.metaAction);
|
|
2016
|
-
await atomicWriteJson(plan.metaPath, plan.meta);
|
|
2017
|
-
}
|
|
2018
2067
|
if (plan.eventsState === "missing") {
|
|
2019
2068
|
preparePlannedPath(plan.eventsPath, plan.eventsAction);
|
|
2020
2069
|
mkdirSync2(dirname(plan.eventsPath), { recursive: true });
|
|
2021
|
-
|
|
2070
|
+
writeFileSync2(plan.eventsPath, "", "utf8");
|
|
2022
2071
|
}
|
|
2023
2072
|
preparePlannedPath(plan.forensicPath, plan.forensicAction);
|
|
2024
2073
|
await atomicWriteJson(plan.forensicPath, plan.forensicReport);
|
|
2025
|
-
if (
|
|
2074
|
+
if (existsSync5(plan.eventsPath)) {
|
|
2026
2075
|
const applied = [];
|
|
2027
2076
|
const canonical = [];
|
|
2028
2077
|
const drifted = [];
|
|
2029
2078
|
for (const entry of [
|
|
2030
|
-
{ path: plan.metaPath, state: plan.metaState },
|
|
2031
2079
|
{ path: plan.eventsPath, state: plan.eventsState },
|
|
2032
2080
|
{ path: plan.forensicPath, state: plan.forensicState }
|
|
2033
2081
|
]) {
|
|
@@ -2044,11 +2092,6 @@ async function executeInitFabricPlan(plan) {
|
|
|
2044
2092
|
return {
|
|
2045
2093
|
agentsMdPath: plan.agentsMdPath,
|
|
2046
2094
|
agentsMdAction: plan.agentsMdAction,
|
|
2047
|
-
knowledgeDir: plan.knowledgeDir,
|
|
2048
|
-
knowledgeDirAction: plan.knowledgeDirAction,
|
|
2049
|
-
personalKnowledgeDir: plan.personalKnowledgeDir,
|
|
2050
|
-
metaPath: plan.metaPath,
|
|
2051
|
-
metaAction: plan.metaAction,
|
|
2052
2095
|
eventsPath: plan.eventsPath,
|
|
2053
2096
|
eventsAction: plan.eventsAction,
|
|
2054
2097
|
forensicPath: plan.forensicPath,
|
|
@@ -2098,8 +2141,6 @@ function exhaustiveInitStagePlan(value) {
|
|
|
2098
2141
|
}
|
|
2099
2142
|
function printInitScaffoldResult(created) {
|
|
2100
2143
|
console.log(formatAgentsMdAction(created.agentsMdPath, created.agentsMdAction));
|
|
2101
|
-
console.log(formatInitPathAction(created.knowledgeDir, created.knowledgeDirAction));
|
|
2102
|
-
console.log(formatInitPathAction(created.metaPath, created.metaAction));
|
|
2103
2144
|
console.log(formatInitPathAction(created.eventsPath, created.eventsAction));
|
|
2104
2145
|
console.log(formatInitPathAction(created.forensicPath, created.forensicAction));
|
|
2105
2146
|
}
|
|
@@ -2146,11 +2187,6 @@ function buildPlanOnlyScaffoldResult(plan) {
|
|
|
2146
2187
|
return {
|
|
2147
2188
|
agentsMdPath: plan.agentsMdPath,
|
|
2148
2189
|
agentsMdAction: plan.agentsMdAction,
|
|
2149
|
-
knowledgeDir: plan.knowledgeDir,
|
|
2150
|
-
knowledgeDirAction: plan.knowledgeDirAction,
|
|
2151
|
-
personalKnowledgeDir: plan.personalKnowledgeDir,
|
|
2152
|
-
metaPath: plan.metaPath,
|
|
2153
|
-
metaAction: plan.metaAction,
|
|
2154
2190
|
eventsPath: plan.eventsPath,
|
|
2155
2191
|
eventsAction: plan.eventsAction,
|
|
2156
2192
|
forensicPath: plan.forensicPath,
|
|
@@ -2183,6 +2219,8 @@ async function executeInitStagePlan(plan, stageName) {
|
|
|
2183
2219
|
installResults.push(...await runBestEffort("hook-broad-script", () => installKnowledgeHintBroadHook(plan.target)));
|
|
2184
2220
|
installResults.push(...await runBestEffort("hook-narrow-script", () => installKnowledgeHintNarrowHook(plan.target)));
|
|
2185
2221
|
installResults.push(...await runBestEffort("hook-cite-policy-evict-script", () => installCitePolicyEvictHook(plan.target)));
|
|
2222
|
+
installResults.push(...await runBestEffort("hook-session-end-script", () => installSessionEndMarkerHook(plan.target)));
|
|
2223
|
+
installResults.push(...await runBestEffort("hook-post-tooluse-script", () => installPostTooluseMutationHook(plan.target)));
|
|
2186
2224
|
installResults.push(...await runBestEffort("hook-lib", () => installHookLibs(plan.target)));
|
|
2187
2225
|
installResults.push(await runBestEffortSingle("claude-hook-config", () => mergeClaudeCodeHookConfig(plan.target)));
|
|
2188
2226
|
installResults.push(await runBestEffortSingle("codex-hook-config", () => mergeCodexHookConfig(plan.target)));
|
|
@@ -2238,7 +2276,7 @@ async function executeInitStagePlan(plan, stageName) {
|
|
|
2238
2276
|
}
|
|
2239
2277
|
}
|
|
2240
2278
|
function shouldReplaceWritableDirectory(path, _options) {
|
|
2241
|
-
if (!
|
|
2279
|
+
if (!existsSync5(path)) {
|
|
2242
2280
|
return false;
|
|
2243
2281
|
}
|
|
2244
2282
|
if (statSync4(path).isDirectory()) {
|
|
@@ -2246,8 +2284,8 @@ function shouldReplaceWritableDirectory(path, _options) {
|
|
|
2246
2284
|
}
|
|
2247
2285
|
return false;
|
|
2248
2286
|
}
|
|
2249
|
-
function classifyFreshPath(path,
|
|
2250
|
-
if (!
|
|
2287
|
+
function classifyFreshPath(path, _strategy) {
|
|
2288
|
+
if (!existsSync5(path)) {
|
|
2251
2289
|
return { path, state: "missing" };
|
|
2252
2290
|
}
|
|
2253
2291
|
let stat;
|
|
@@ -2263,30 +2301,7 @@ function classifyFreshPath(path, strategy) {
|
|
|
2263
2301
|
if (!stat.isFile()) {
|
|
2264
2302
|
return { path, state: "user-modified", reason: "expected a file" };
|
|
2265
2303
|
}
|
|
2266
|
-
|
|
2267
|
-
return { path, state: "present-canonical" };
|
|
2268
|
-
}
|
|
2269
|
-
try {
|
|
2270
|
-
const raw = readFileSync3(path, "utf8");
|
|
2271
|
-
const parsed = JSON.parse(raw);
|
|
2272
|
-
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
2273
|
-
return { path, state: "user-modified", reason: "not a JSON object" };
|
|
2274
|
-
}
|
|
2275
|
-
const record = parsed;
|
|
2276
|
-
const hasRevision = typeof record["revision"] === "string";
|
|
2277
|
-
const hasNodes = record["nodes"] !== void 0 && record["nodes"] !== null && typeof record["nodes"] === "object" && !Array.isArray(record["nodes"]);
|
|
2278
|
-
const hasCounters = record["counters"] !== void 0 && record["counters"] !== null && typeof record["counters"] === "object" && !Array.isArray(record["counters"]);
|
|
2279
|
-
if (!hasRevision || !hasNodes || !hasCounters) {
|
|
2280
|
-
return { path, state: "drifted", reason: "missing required AgentsMeta fields" };
|
|
2281
|
-
}
|
|
2282
|
-
return { path, state: "present-canonical" };
|
|
2283
|
-
} catch (error) {
|
|
2284
|
-
return {
|
|
2285
|
-
path,
|
|
2286
|
-
state: "user-modified",
|
|
2287
|
-
reason: error instanceof Error ? error.message : String(error)
|
|
2288
|
-
};
|
|
2289
|
-
}
|
|
2304
|
+
return { path, state: "present-canonical" };
|
|
2290
2305
|
}
|
|
2291
2306
|
function diffStateToWriteAction(_state) {
|
|
2292
2307
|
return "created";
|
|
@@ -2296,7 +2311,7 @@ function formatDiffFileState(state) {
|
|
|
2296
2311
|
}
|
|
2297
2312
|
function preparePlannedPath(path, action) {
|
|
2298
2313
|
mkdirSync2(dirname(path), { recursive: true });
|
|
2299
|
-
if (action === "overwritten" &&
|
|
2314
|
+
if (action === "overwritten" && existsSync5(path)) {
|
|
2300
2315
|
rmSync2(path, { recursive: true, force: true });
|
|
2301
2316
|
}
|
|
2302
2317
|
}
|
|
@@ -2450,19 +2465,19 @@ function normalizeTarget3(targetInput) {
|
|
|
2450
2465
|
return isAbsolute3(targetInput) ? targetInput : resolve3(process.cwd(), targetInput);
|
|
2451
2466
|
}
|
|
2452
2467
|
function assertExistingDirectory3(target) {
|
|
2453
|
-
if (!
|
|
2468
|
+
if (!existsSync5(target) || !statSync4(target).isDirectory()) {
|
|
2454
2469
|
throw new Error(`Target must be an existing directory: ${target}`);
|
|
2455
2470
|
}
|
|
2456
2471
|
}
|
|
2457
2472
|
function detectPackageManager(cwd) {
|
|
2458
2473
|
const workspaceRoot = resolve3(cwd);
|
|
2459
|
-
if (
|
|
2474
|
+
if (existsSync5(join7(workspaceRoot, "pnpm-lock.yaml"))) {
|
|
2460
2475
|
return "pnpm";
|
|
2461
2476
|
}
|
|
2462
|
-
if (
|
|
2477
|
+
if (existsSync5(join7(workspaceRoot, "yarn.lock"))) {
|
|
2463
2478
|
return "yarn";
|
|
2464
2479
|
}
|
|
2465
|
-
if (
|
|
2480
|
+
if (existsSync5(join7(workspaceRoot, "package-lock.json"))) {
|
|
2466
2481
|
return "npm";
|
|
2467
2482
|
}
|
|
2468
2483
|
return "npm";
|
|
@@ -2475,13 +2490,6 @@ function installLocalFabricServer(target, manager) {
|
|
|
2475
2490
|
shell: process.platform === "win32"
|
|
2476
2491
|
});
|
|
2477
2492
|
}
|
|
2478
|
-
function createInitialMeta() {
|
|
2479
|
-
return {
|
|
2480
|
-
revision: "sha256:initial",
|
|
2481
|
-
nodes: {},
|
|
2482
|
-
counters: defaultAgentsMetaCounters()
|
|
2483
|
-
};
|
|
2484
|
-
}
|
|
2485
2493
|
function appendInstallDiffLedgerEvent(eventsPath, payload) {
|
|
2486
2494
|
const event = {
|
|
2487
2495
|
kind: "fabric-event",
|
|
@@ -2573,8 +2581,6 @@ function printInitPlanSummary(target, options, mcpInstallMode, supports) {
|
|
|
2573
2581
|
})
|
|
2574
2582
|
);
|
|
2575
2583
|
console.log(t("cli.install.plan.writes"));
|
|
2576
|
-
console.log(` - ${target}/.fabric/knowledge/{decisions,pitfalls,guidelines,models,processes,pending}/`);
|
|
2577
|
-
console.log(` - ${target}/.fabric/agents.meta.json`);
|
|
2578
2584
|
console.log(` - ${target}/.fabric/events.jsonl`);
|
|
2579
2585
|
console.log(` - ${target}/.fabric/forensic.json`);
|
|
2580
2586
|
console.log(` - ${target}/.fabric/fabric-config.json`);
|
|
@@ -2720,6 +2726,8 @@ function writeStderr(message) {
|
|
|
2720
2726
|
`);
|
|
2721
2727
|
}
|
|
2722
2728
|
export {
|
|
2729
|
+
bindCreatedStoreToProject,
|
|
2730
|
+
bindRemoteStoreToProject,
|
|
2723
2731
|
buildInitExecutionPlan,
|
|
2724
2732
|
buildInitFabricPlan,
|
|
2725
2733
|
createDefaultInitWizardAdapter,
|
|
@@ -2730,8 +2738,6 @@ export {
|
|
|
2730
2738
|
initFabric,
|
|
2731
2739
|
installCommand,
|
|
2732
2740
|
resolveInitExecutionPlanWithWizard,
|
|
2733
|
-
runHooksOnlyRefresh,
|
|
2734
2741
|
runInitCommand,
|
|
2735
|
-
runSkillsOnlyRefresh,
|
|
2736
2742
|
shouldUseInitWizard
|
|
2737
2743
|
};
|