@fenglimg/fabric-cli 2.0.0-rc.15 → 2.0.0-rc.22
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-4HC5ZK7H.js +598 -0
- package/dist/chunk-KZ2YITOS.js +225 -0
- package/dist/{chunk-SKSYUHKK.js → chunk-MF3OTILQ.js} +0 -4
- package/dist/{chunk-AXIFEVAS.js → chunk-PSVKSMRO.js} +181 -83
- package/dist/{chunk-OBQU6NHO.js → chunk-ZSESMG6L.js} +0 -6
- package/dist/{config-7YD365I3.js → config-AYP5F72E.js} +2 -2
- package/dist/{doctor-6XHLQJXB.js → doctor-HIX2FFEP.js} +129 -3
- package/dist/index.js +11 -8
- package/dist/{install-JLDCHAXV.js → install-WJZQZM7D.js} +23 -25
- package/dist/{plan-context-hint-73U4FGKO.js → plan-context-hint-RYVSMULL.js} +12 -5
- package/dist/{serve-L3X5UHG2.js → serve-6PPQX7AW.js} +1 -1
- package/dist/{uninstall-DD6FIFCI.js → uninstall-L2HEEOU3.js} +147 -55
- package/package.json +3 -3
- package/templates/hooks/fabric-hint.cjs +350 -21
- package/templates/hooks/knowledge-hint-broad.cjs +79 -14
- package/templates/hooks/knowledge-hint-narrow.cjs +31 -7
- package/templates/hooks/lib/banner-i18n.cjs +282 -0
- package/dist/chunk-AIB54QRT.js +0 -82
- package/dist/chunk-UTF4YBDN.js +0 -366
- package/templates/agents-md/AGENTS.md.template +0 -59
- package/templates/bootstrap/CLAUDE.md +0 -8
- package/templates/bootstrap/codex-AGENTS-header.md +0 -6
- package/templates/bootstrap/cursor-fabric-bootstrap.mdc +0 -10
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
runInitScan
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-PSVKSMRO.js";
|
|
5
5
|
import {
|
|
6
6
|
hasActionHint,
|
|
7
7
|
paint,
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
} from "./chunk-6ICJICVU.js";
|
|
14
14
|
import {
|
|
15
15
|
resolveDevMode
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-ZSESMG6L.js";
|
|
17
17
|
|
|
18
18
|
// src/commands/doctor.ts
|
|
19
19
|
import { confirm, isCancel } from "@clack/prompts";
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
appendEventLedgerEvent,
|
|
23
23
|
checkLockOrThrow,
|
|
24
24
|
runDoctorApplyLint as runDoctorFixKnowledge,
|
|
25
|
+
runDoctorCiteCoverage,
|
|
25
26
|
runDoctorFix,
|
|
26
27
|
runDoctorReport
|
|
27
28
|
} from "@fenglimg/fabric-server";
|
|
@@ -75,6 +76,25 @@ var doctorCommand = defineCommand({
|
|
|
75
76
|
type: "boolean",
|
|
76
77
|
description: t("cli.doctor.args.yes.description"),
|
|
77
78
|
default: false
|
|
79
|
+
},
|
|
80
|
+
// rc.20 TASK-05: cite policy adherence report (read-only). Skips standard
|
|
81
|
+
// inspections entirely — different output surface. Mutually exclusive
|
|
82
|
+
// with --fix / --fix-knowledge (enforced in run()).
|
|
83
|
+
"cite-coverage": {
|
|
84
|
+
type: "boolean",
|
|
85
|
+
description: t("cli.doctor.args.cite-coverage.description"),
|
|
86
|
+
default: false
|
|
87
|
+
},
|
|
88
|
+
since: {
|
|
89
|
+
type: "string",
|
|
90
|
+
description: t("cli.doctor.args.since.description"),
|
|
91
|
+
default: "7d"
|
|
92
|
+
},
|
|
93
|
+
client: {
|
|
94
|
+
type: "string",
|
|
95
|
+
description: t("cli.doctor.args.client.description"),
|
|
96
|
+
default: "all",
|
|
97
|
+
valueHint: "cc|codex|cursor|all"
|
|
78
98
|
}
|
|
79
99
|
},
|
|
80
100
|
async run({ args }) {
|
|
@@ -92,6 +112,34 @@ var doctorCommand = defineCommand({
|
|
|
92
112
|
const fixKnowledge = args["fix-knowledge"] === true;
|
|
93
113
|
const fix = args.fix === true;
|
|
94
114
|
const rescan = args.rescan === true;
|
|
115
|
+
const citeCoverage = args["cite-coverage"] === true;
|
|
116
|
+
if (citeCoverage) {
|
|
117
|
+
if (fix || fixKnowledge) {
|
|
118
|
+
writeStderr(t("cli.doctor.errors.cite-coverage-mutex"));
|
|
119
|
+
process.exitCode = 1;
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
let sinceMs;
|
|
123
|
+
try {
|
|
124
|
+
sinceMs = parseSinceDuration(args.since ?? "7d");
|
|
125
|
+
} catch {
|
|
126
|
+
writeStderr(t("cli.doctor.errors.invalid-since", { input: args.since ?? "7d" }));
|
|
127
|
+
process.exitCode = 1;
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const clientFilter = args.client ?? "all";
|
|
131
|
+
if (!isValidClientFilter(clientFilter)) {
|
|
132
|
+
writeStderr(t("cli.doctor.errors.invalid-client", { input: clientFilter }));
|
|
133
|
+
process.exitCode = 1;
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const report2 = await runDoctorCiteCoverage(resolution.target, {
|
|
137
|
+
since: sinceMs,
|
|
138
|
+
client: clientFilter
|
|
139
|
+
});
|
|
140
|
+
renderCiteCoverageReport(report2, args.json === true);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
95
143
|
if (fixKnowledge && fix) {
|
|
96
144
|
writeStderr(t("cli.doctor.errors.fix-knowledge-fix-mutually-exclusive"));
|
|
97
145
|
process.exitCode = 1;
|
|
@@ -293,7 +341,85 @@ async function resolveFixKnowledgeConsent(options) {
|
|
|
293
341
|
}
|
|
294
342
|
return "proceed";
|
|
295
343
|
}
|
|
344
|
+
var CITE_COVERAGE_CLIENT_FILTERS = /* @__PURE__ */ new Set([
|
|
345
|
+
"cc",
|
|
346
|
+
"codex",
|
|
347
|
+
"cursor",
|
|
348
|
+
"all"
|
|
349
|
+
]);
|
|
350
|
+
function isValidClientFilter(input) {
|
|
351
|
+
return CITE_COVERAGE_CLIENT_FILTERS.has(input);
|
|
352
|
+
}
|
|
353
|
+
function renderCiteCoverageReport(report, jsonMode) {
|
|
354
|
+
if (jsonMode) {
|
|
355
|
+
writeStdout(JSON.stringify(report, null, 2));
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
if (report.status === "skipped") {
|
|
359
|
+
writeStdout(t("doctor.cite.status.skipped"));
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
const lines = [];
|
|
363
|
+
lines.push(t("doctor.section.cite-coverage"));
|
|
364
|
+
lines.push(
|
|
365
|
+
t("doctor.cite.header", {
|
|
366
|
+
since: new Date(report.since_ts).toISOString(),
|
|
367
|
+
marker: new Date(report.marker_ts).toISOString()
|
|
368
|
+
})
|
|
369
|
+
);
|
|
370
|
+
if (report.marker_emitted_now) {
|
|
371
|
+
lines.push(t("doctor.cite.warning.justActivated"));
|
|
372
|
+
}
|
|
373
|
+
lines.push("");
|
|
374
|
+
lines.push(` ${t("doctor.cite.metric.editsTouched")}: ${report.metrics.edits_touched}`);
|
|
375
|
+
lines.push(` ${t("doctor.cite.metric.qualifyingCites")}: ${report.metrics.qualifying_cites}`);
|
|
376
|
+
lines.push(` ${t("doctor.cite.metric.recalledUnverified")}: ${report.metrics.recalled_unverified}`);
|
|
377
|
+
lines.push(` ${t("doctor.cite.metric.expectedButMissed")}: ${report.metrics.expected_but_missed}`);
|
|
378
|
+
lines.push(` ${t("doctor.cite.metric.totalTurns")}: ${report.metrics.total_turns}`);
|
|
379
|
+
if (report.per_client !== void 0 && Object.keys(report.per_client).length > 1) {
|
|
380
|
+
lines.push("");
|
|
381
|
+
lines.push(`### ${t("doctor.cite.section.perClient")}`);
|
|
382
|
+
for (const [client, metrics] of Object.entries(report.per_client)) {
|
|
383
|
+
const summary = Object.entries(metrics).map(([k, v]) => `${k}=${v}`).join(" / ");
|
|
384
|
+
lines.push(` ${client}: ${summary}`);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
if (report.dismissed_reason_histogram !== void 0 && Object.keys(report.dismissed_reason_histogram).length > 0) {
|
|
388
|
+
lines.push("");
|
|
389
|
+
lines.push(`### ${t("doctor.cite.section.dismissedReasons")}`);
|
|
390
|
+
for (const [reason, count] of Object.entries(report.dismissed_reason_histogram)) {
|
|
391
|
+
const label = t(`doctor.cite.dismissed.${reason}`);
|
|
392
|
+
lines.push(` ${label}: ${count}`);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
writeStdout(lines.join("\n"));
|
|
396
|
+
}
|
|
397
|
+
function parseSinceDuration(input) {
|
|
398
|
+
const trimmed = input.trim();
|
|
399
|
+
if (trimmed.length === 0) {
|
|
400
|
+
throw new Error(`invalid --since value: ${input}`);
|
|
401
|
+
}
|
|
402
|
+
const durationMatch = /^(\d+)([dhm])$/.exec(trimmed);
|
|
403
|
+
if (durationMatch !== null) {
|
|
404
|
+
const value = Number.parseInt(durationMatch[1], 10);
|
|
405
|
+
const unit = durationMatch[2];
|
|
406
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
407
|
+
throw new Error(`invalid --since value: ${input}`);
|
|
408
|
+
}
|
|
409
|
+
const unitMs = unit === "d" ? 864e5 : unit === "h" ? 36e5 : 6e4;
|
|
410
|
+
return Date.now() - value * unitMs;
|
|
411
|
+
}
|
|
412
|
+
if (/^\d+$/.test(trimmed)) {
|
|
413
|
+
const value = Number.parseInt(trimmed, 10);
|
|
414
|
+
if (!Number.isFinite(value) || value < 0) {
|
|
415
|
+
throw new Error(`invalid --since value: ${input}`);
|
|
416
|
+
}
|
|
417
|
+
return value;
|
|
418
|
+
}
|
|
419
|
+
throw new Error(`invalid --since value: ${input}`);
|
|
420
|
+
}
|
|
296
421
|
export {
|
|
297
422
|
doctor_default as default,
|
|
298
|
-
doctorCommand
|
|
423
|
+
doctorCommand,
|
|
424
|
+
parseSinceDuration
|
|
299
425
|
};
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
t
|
|
4
|
+
} from "./chunk-6ICJICVU.js";
|
|
2
5
|
|
|
3
6
|
// src/index.ts
|
|
4
7
|
import { realpathSync } from "fs";
|
|
@@ -8,20 +11,20 @@ import { defineCommand, runMain } from "citty";
|
|
|
8
11
|
|
|
9
12
|
// src/commands/index.ts
|
|
10
13
|
var allCommands = {
|
|
11
|
-
install: () => import("./install-
|
|
12
|
-
doctor: () => import("./doctor-
|
|
13
|
-
serve: () => import("./serve-
|
|
14
|
-
uninstall: () => import("./uninstall-
|
|
15
|
-
config: () => import("./config-
|
|
16
|
-
"plan-context-hint": () => import("./plan-context-hint-
|
|
14
|
+
install: () => import("./install-WJZQZM7D.js").then((module) => module.default),
|
|
15
|
+
doctor: () => import("./doctor-HIX2FFEP.js").then((module) => module.default),
|
|
16
|
+
serve: () => import("./serve-6PPQX7AW.js").then((module) => module.default),
|
|
17
|
+
uninstall: () => import("./uninstall-L2HEEOU3.js").then((module) => module.default),
|
|
18
|
+
config: () => import("./config-AYP5F72E.js").then((module) => module.default),
|
|
19
|
+
"plan-context-hint": () => import("./plan-context-hint-RYVSMULL.js").then((module) => module.default)
|
|
17
20
|
};
|
|
18
21
|
|
|
19
22
|
// src/index.ts
|
|
20
23
|
var main = defineCommand({
|
|
21
24
|
meta: {
|
|
22
25
|
name: "fabric",
|
|
23
|
-
version: "2.0.0-rc.
|
|
24
|
-
description:
|
|
26
|
+
version: "2.0.0-rc.22",
|
|
27
|
+
description: t("cli.main.description")
|
|
25
28
|
},
|
|
26
29
|
subCommands: allCommands
|
|
27
30
|
});
|
|
@@ -1,27 +1,31 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
installMcpClients
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-KZ2YITOS.js";
|
|
5
5
|
import {
|
|
6
6
|
detectExistingLanguage,
|
|
7
7
|
runInitScan
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-PSVKSMRO.js";
|
|
9
9
|
import {
|
|
10
|
-
addFabricKnowledgeBaseSection,
|
|
11
10
|
installArchiveHintHook,
|
|
12
11
|
installFabricArchiveSkill,
|
|
13
12
|
installFabricImportSkill,
|
|
14
13
|
installFabricReviewSkill,
|
|
14
|
+
installHookLibs,
|
|
15
15
|
installKnowledgeHintBroadHook,
|
|
16
16
|
installKnowledgeHintNarrowHook,
|
|
17
17
|
mergeClaudeCodeHookConfig,
|
|
18
18
|
mergeCodexHookConfig,
|
|
19
19
|
mergeCursorHookConfig,
|
|
20
|
-
readFabricLanguagePreference
|
|
21
|
-
|
|
20
|
+
readFabricLanguagePreference,
|
|
21
|
+
writeClaudeBootstrapThinShell,
|
|
22
|
+
writeCodexBootstrapManagedBlock,
|
|
23
|
+
writeCursorBootstrapManagedBlock,
|
|
24
|
+
writeFabricAgentsSnapshot
|
|
25
|
+
} from "./chunk-4HC5ZK7H.js";
|
|
22
26
|
import {
|
|
23
27
|
detectClientSupports
|
|
24
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-MF3OTILQ.js";
|
|
25
29
|
import {
|
|
26
30
|
displayWidth,
|
|
27
31
|
hasActionHint,
|
|
@@ -35,7 +39,7 @@ import {
|
|
|
35
39
|
import {
|
|
36
40
|
createDebugLogger,
|
|
37
41
|
resolveDevMode
|
|
38
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-ZSESMG6L.js";
|
|
39
43
|
|
|
40
44
|
// src/commands/install.ts
|
|
41
45
|
import { randomUUID } from "crypto";
|
|
@@ -45,7 +49,7 @@ import { appendFileSync, existsSync as existsSync3, mkdirSync, readFileSync as r
|
|
|
45
49
|
import { dirname, isAbsolute as isAbsolute3, join as join3, resolve as resolve3 } from "path";
|
|
46
50
|
import { cancel, confirm, group, intro, isCancel, log, note, outro, select } from "@clack/prompts";
|
|
47
51
|
import { defaultAgentsMetaCounters } from "@fenglimg/fabric-shared";
|
|
48
|
-
import { atomicWriteJson
|
|
52
|
+
import { atomicWriteJson } from "@fenglimg/fabric-shared/node/atomic-write";
|
|
49
53
|
import { defineCommand } from "citty";
|
|
50
54
|
import { checkLockOrThrow } from "@fenglimg/fabric-server";
|
|
51
55
|
|
|
@@ -62,11 +66,14 @@ async function installHooks(target, _options = {}) {
|
|
|
62
66
|
results.push(...await runStep(() => installArchiveHintHook(normalizedTarget)));
|
|
63
67
|
results.push(...await runStep(() => installKnowledgeHintBroadHook(normalizedTarget)));
|
|
64
68
|
results.push(...await runStep(() => installKnowledgeHintNarrowHook(normalizedTarget)));
|
|
69
|
+
results.push(...await runStep(() => installHookLibs(normalizedTarget)));
|
|
65
70
|
results.push(await runSingleStep("claude-hook-config", () => mergeClaudeCodeHookConfig(normalizedTarget)));
|
|
66
71
|
results.push(await runSingleStep("codex-hook-config", () => mergeCodexHookConfig(normalizedTarget)));
|
|
67
72
|
results.push(await runSingleStep("cursor-hook-config", () => mergeCursorHookConfig(normalizedTarget)));
|
|
68
|
-
|
|
69
|
-
results.push(
|
|
73
|
+
results.push(await runSingleStep("bootstrap-snapshot", () => writeFabricAgentsSnapshot(normalizedTarget)));
|
|
74
|
+
results.push(await runSingleStep("bootstrap-claude", () => writeClaudeBootstrapThinShell(normalizedTarget)));
|
|
75
|
+
results.push(await runSingleStep("bootstrap-codex", () => writeCodexBootstrapManagedBlock(normalizedTarget)));
|
|
76
|
+
results.push(await runSingleStep("bootstrap-cursor", () => writeCursorBootstrapManagedBlock(normalizedTarget)));
|
|
70
77
|
results.push(...validateHookPaths(normalizedTarget));
|
|
71
78
|
return summarizeResults(results);
|
|
72
79
|
}
|
|
@@ -1292,7 +1299,7 @@ function readProjectName(target) {
|
|
|
1292
1299
|
return basename(target);
|
|
1293
1300
|
}
|
|
1294
1301
|
function getCliVersion() {
|
|
1295
|
-
return true ? "2.0.0-rc.
|
|
1302
|
+
return true ? "2.0.0-rc.22" : "unknown";
|
|
1296
1303
|
}
|
|
1297
1304
|
function sortRecord(record) {
|
|
1298
1305
|
return Object.fromEntries(Object.entries(record).sort(([left], [right]) => left.localeCompare(right)));
|
|
@@ -1302,15 +1309,6 @@ function toPosixPath(path) {
|
|
|
1302
1309
|
}
|
|
1303
1310
|
|
|
1304
1311
|
// src/commands/install.ts
|
|
1305
|
-
var AGENTS_MD_DEFAULT_CONTENT = `# Project Knowledge
|
|
1306
|
-
|
|
1307
|
-
This project uses [Fabric](https://github.com/fenglimg/fabric) for cross-client AI knowledge management.
|
|
1308
|
-
|
|
1309
|
-
Knowledge entries live in \`.fabric/knowledge/\` (team) and \`~/.fabric/knowledge/\` (personal).
|
|
1310
|
-
Run \`fabric doctor\` to verify state.
|
|
1311
|
-
|
|
1312
|
-
See \`.fabric/knowledge/\` for project decisions, pitfalls, guidelines, models, and processes.
|
|
1313
|
-
`;
|
|
1314
1312
|
var LOCAL_FABRIC_SERVER_PATH = join3("node_modules", "@fenglimg", "fabric-server", "dist", "index.js");
|
|
1315
1313
|
var FABRIC_SERVER_PACKAGE = "@fenglimg/fabric-server";
|
|
1316
1314
|
var INIT_WIZARD_GROUP_CANCELLED = /* @__PURE__ */ Symbol("init-wizard-group-cancelled");
|
|
@@ -1626,9 +1624,6 @@ async function executeInitFabricPlan(plan) {
|
|
|
1626
1624
|
}
|
|
1627
1625
|
mkdirSync(plan.fabricDir, { recursive: true });
|
|
1628
1626
|
writeDefaultFabricConfig(plan.fabricDir, plan.target);
|
|
1629
|
-
if (plan.agentsMdAction === "created" && !existsSync3(plan.agentsMdPath)) {
|
|
1630
|
-
await atomicWriteText(plan.agentsMdPath, AGENTS_MD_DEFAULT_CONTENT);
|
|
1631
|
-
}
|
|
1632
1627
|
mkdirSync(plan.knowledgeDir, { recursive: true });
|
|
1633
1628
|
for (const sub of KNOWLEDGE_SUBDIRS) {
|
|
1634
1629
|
const teamSubDir = join3(plan.knowledgeDir, sub);
|
|
@@ -1820,11 +1815,14 @@ async function executeInitStagePlan(plan, stageName) {
|
|
|
1820
1815
|
installResults.push(...await runBestEffort("hook-script", () => installArchiveHintHook(plan.target)));
|
|
1821
1816
|
installResults.push(...await runBestEffort("hook-broad-script", () => installKnowledgeHintBroadHook(plan.target)));
|
|
1822
1817
|
installResults.push(...await runBestEffort("hook-narrow-script", () => installKnowledgeHintNarrowHook(plan.target)));
|
|
1818
|
+
installResults.push(...await runBestEffort("hook-lib", () => installHookLibs(plan.target)));
|
|
1823
1819
|
installResults.push(await runBestEffortSingle("claude-hook-config", () => mergeClaudeCodeHookConfig(plan.target)));
|
|
1824
1820
|
installResults.push(await runBestEffortSingle("codex-hook-config", () => mergeCodexHookConfig(plan.target)));
|
|
1825
1821
|
installResults.push(await runBestEffortSingle("cursor-hook-config", () => mergeCursorHookConfig(plan.target)));
|
|
1826
|
-
|
|
1827
|
-
installResults.push(
|
|
1822
|
+
installResults.push(await runBestEffortSingle("bootstrap-snapshot", () => writeFabricAgentsSnapshot(plan.target)));
|
|
1823
|
+
installResults.push(await runBestEffortSingle("bootstrap-claude", () => writeClaudeBootstrapThinShell(plan.target)));
|
|
1824
|
+
installResults.push(await runBestEffortSingle("bootstrap-codex", () => writeCodexBootstrapManagedBlock(plan.target)));
|
|
1825
|
+
installResults.push(await runBestEffortSingle("bootstrap-cursor", () => writeCursorBootstrapManagedBlock(plan.target)));
|
|
1828
1826
|
const installedCount = installResults.filter((r) => r.status === "written").length;
|
|
1829
1827
|
const skippedCount = installResults.filter((r) => r.status === "skipped").length;
|
|
1830
1828
|
const errorCount = installResults.filter((r) => r.status === "error").length;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
resolveDevMode
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-ZSESMG6L.js";
|
|
5
5
|
|
|
6
6
|
// src/commands/plan-context-hint.ts
|
|
7
7
|
import { defineCommand } from "citty";
|
|
@@ -66,19 +66,26 @@ async function runPlanContextHint(opts) {
|
|
|
66
66
|
// scope_glob matches the requested path.
|
|
67
67
|
dedupeByStableId(result.entries.flatMap((entry) => entry.description_index))
|
|
68
68
|
);
|
|
69
|
-
const
|
|
69
|
+
const entries = narrowSource.map((item) => ({
|
|
70
70
|
id: item.stable_id,
|
|
71
71
|
type: item.type ?? item.description.knowledge_type ?? "",
|
|
72
72
|
maturity: item.maturity ?? item.description.maturity ?? "",
|
|
73
73
|
summary: item.description.summary
|
|
74
74
|
}));
|
|
75
|
-
|
|
76
|
-
version:
|
|
75
|
+
const output = {
|
|
76
|
+
version: 2,
|
|
77
77
|
revision_hash: result.revision_hash,
|
|
78
78
|
target_paths: targetPaths,
|
|
79
|
-
|
|
79
|
+
entries,
|
|
80
80
|
broad_count: sharedIndex.length
|
|
81
81
|
};
|
|
82
|
+
if (result.auto_healed === true) {
|
|
83
|
+
output.auto_healed = true;
|
|
84
|
+
if (typeof result.previous_revision_hash === "string") {
|
|
85
|
+
output.previous_revision_hash = result.previous_revision_hash;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return output;
|
|
82
89
|
}
|
|
83
90
|
function parsePathsArg(raw) {
|
|
84
91
|
if (raw === void 0 || raw.length === 0) {
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
FABRIC_HOOK_COMMAND_PATHS,
|
|
4
|
-
FABRIC_SECTION_REGEX,
|
|
5
4
|
HOOK_CONFIG_ARRAY_PATHS,
|
|
6
5
|
HOOK_CONFIG_TARGETS,
|
|
6
|
+
HOOK_LIB_DESTINATIONS,
|
|
7
7
|
HOOK_SCRIPT_DESTINATIONS,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
} from "./chunk-
|
|
8
|
+
SKILL_DESTINATIONS,
|
|
9
|
+
fabricAgentsSnapshotPath
|
|
10
|
+
} from "./chunk-4HC5ZK7H.js";
|
|
11
11
|
import {
|
|
12
12
|
detectClientSupports,
|
|
13
13
|
resolveClients
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-MF3OTILQ.js";
|
|
15
15
|
import {
|
|
16
16
|
hasActionHint,
|
|
17
17
|
paint,
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
import {
|
|
24
24
|
createDebugLogger,
|
|
25
25
|
resolveDevMode
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-ZSESMG6L.js";
|
|
27
27
|
|
|
28
28
|
// src/commands/uninstall.ts
|
|
29
29
|
import { existsSync as existsSync2, statSync } from "fs";
|
|
@@ -39,6 +39,7 @@ import { existsSync } from "fs";
|
|
|
39
39
|
import { readdir, readFile, rm, rmdir } from "fs/promises";
|
|
40
40
|
import { dirname, join } from "path";
|
|
41
41
|
import { atomicWriteJson, atomicWriteText } from "@fenglimg/fabric-shared/node/atomic-write";
|
|
42
|
+
import { BOOTSTRAP_REGEX } from "@fenglimg/fabric-shared/templates/bootstrap-canonical";
|
|
42
43
|
async function uninstallFabricArchiveSkill(projectRoot) {
|
|
43
44
|
return removeSkill("skill", SKILL_DESTINATIONS.fabricArchive, projectRoot);
|
|
44
45
|
}
|
|
@@ -82,6 +83,34 @@ async function removeHookScripts(step, rels, projectRoot) {
|
|
|
82
83
|
}
|
|
83
84
|
return results;
|
|
84
85
|
}
|
|
86
|
+
async function removeHookLibs(projectRoot) {
|
|
87
|
+
const results = [];
|
|
88
|
+
for (const dirRel of HOOK_LIB_DESTINATIONS) {
|
|
89
|
+
const dirAbs = join(projectRoot, dirRel);
|
|
90
|
+
if (!existsSync(dirAbs)) {
|
|
91
|
+
results.push({ step: "hook-lib", path: dirAbs, status: "skipped", message: "absent" });
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
let entries;
|
|
95
|
+
try {
|
|
96
|
+
entries = await readdir(dirAbs);
|
|
97
|
+
} catch (error) {
|
|
98
|
+
results.push({
|
|
99
|
+
step: "hook-lib",
|
|
100
|
+
path: dirAbs,
|
|
101
|
+
status: "error",
|
|
102
|
+
message: error instanceof Error ? error.message : String(error)
|
|
103
|
+
});
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
for (const entry of entries) {
|
|
107
|
+
if (!entry.endsWith(".cjs")) continue;
|
|
108
|
+
results.push(await rmIfExists("hook-lib", join(dirAbs, entry)));
|
|
109
|
+
}
|
|
110
|
+
results.push(await rmDirIfEmpty("hook-lib-dir", dirAbs));
|
|
111
|
+
}
|
|
112
|
+
return results;
|
|
113
|
+
}
|
|
85
114
|
async function unmergeClaudeCodeHookConfig(projectRoot) {
|
|
86
115
|
return unmergeHookConfig({
|
|
87
116
|
step: "claude-hook-config",
|
|
@@ -112,69 +141,131 @@ async function unmergeCursorHookConfig(projectRoot) {
|
|
|
112
141
|
extractCommands: extractFlatCommands
|
|
113
142
|
});
|
|
114
143
|
}
|
|
115
|
-
async function
|
|
144
|
+
async function stripFabricBootstrapBlocks(projectRoot) {
|
|
116
145
|
const results = [];
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
146
|
+
results.push(await stripClaudeBootstrapImports(projectRoot));
|
|
147
|
+
results.push(await stripManagedBlock(projectRoot, "AGENTS.md", { deleteWhenEmpty: false }));
|
|
148
|
+
results.push(
|
|
149
|
+
await stripManagedBlock(projectRoot, join(".cursor", "rules", "fabric-bootstrap.mdc"), {
|
|
150
|
+
deleteWhenEmpty: true
|
|
151
|
+
})
|
|
152
|
+
);
|
|
153
|
+
return results;
|
|
154
|
+
}
|
|
155
|
+
async function stripClaudeBootstrapImports(projectRoot) {
|
|
156
|
+
const step = "bootstrap-claude";
|
|
157
|
+
const target = join(projectRoot, "CLAUDE.md");
|
|
158
|
+
if (!existsSync(target)) {
|
|
159
|
+
return { step, path: target, status: "skipped", message: "absent" };
|
|
160
|
+
}
|
|
161
|
+
let existing;
|
|
162
|
+
try {
|
|
163
|
+
existing = await readFile(target, "utf8");
|
|
164
|
+
} catch (error) {
|
|
165
|
+
return {
|
|
166
|
+
step,
|
|
167
|
+
path: target,
|
|
168
|
+
status: "error",
|
|
169
|
+
message: error instanceof Error ? error.message : String(error)
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
const managedLines = /* @__PURE__ */ new Set(["@.fabric/AGENTS.md", "@.fabric/project-rules.md"]);
|
|
173
|
+
const lines = existing.split(/\r?\n/);
|
|
174
|
+
const filtered = lines.filter((l) => !managedLines.has(l.replace(/\s+$/, "")));
|
|
175
|
+
if (filtered.length === lines.length) {
|
|
176
|
+
return { step, path: target, status: "skipped", message: "no-fabric-section" };
|
|
177
|
+
}
|
|
178
|
+
while (filtered.length > 1 && filtered[filtered.length - 1] === "" && filtered[filtered.length - 2] === "") {
|
|
179
|
+
filtered.pop();
|
|
180
|
+
}
|
|
181
|
+
const next = filtered.join("\n");
|
|
182
|
+
if (next === existing) {
|
|
183
|
+
return { step, path: target, status: "skipped", message: "no-fabric-section" };
|
|
184
|
+
}
|
|
185
|
+
try {
|
|
186
|
+
await atomicWriteText(target, next);
|
|
187
|
+
return { step, path: target, status: "removed" };
|
|
188
|
+
} catch (error) {
|
|
189
|
+
return {
|
|
190
|
+
step,
|
|
191
|
+
path: target,
|
|
192
|
+
status: "error",
|
|
193
|
+
message: error instanceof Error ? error.message : String(error)
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
async function stripManagedBlock(projectRoot, relPath, options) {
|
|
198
|
+
const step = relPath.endsWith(".mdc") ? "bootstrap-cursor" : "bootstrap-codex";
|
|
199
|
+
const target = join(projectRoot, relPath);
|
|
200
|
+
if (!existsSync(target)) {
|
|
201
|
+
return { step, path: target, status: "skipped", message: "absent" };
|
|
202
|
+
}
|
|
203
|
+
let existing;
|
|
204
|
+
try {
|
|
205
|
+
existing = await readFile(target, "utf8");
|
|
206
|
+
} catch (error) {
|
|
207
|
+
return {
|
|
208
|
+
step,
|
|
209
|
+
path: target,
|
|
210
|
+
status: "error",
|
|
211
|
+
message: error instanceof Error ? error.message : String(error)
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
const match = existing.match(BOOTSTRAP_REGEX);
|
|
215
|
+
if (match === null) {
|
|
216
|
+
return { step, path: target, status: "skipped", message: "no-fabric-section" };
|
|
217
|
+
}
|
|
218
|
+
const before = existing.slice(0, match.index ?? 0);
|
|
219
|
+
const after = existing.slice((match.index ?? 0) + match[0].length);
|
|
220
|
+
const filtered = `${before}${after.replace(/^\r?\n/, "")}`;
|
|
221
|
+
if (options.deleteWhenEmpty && isFrontMatterOnly(filtered)) {
|
|
157
222
|
try {
|
|
158
|
-
await
|
|
159
|
-
|
|
223
|
+
await rm(target, { force: true });
|
|
224
|
+
return { step, path: target, status: "removed", message: "front-matter-only" };
|
|
160
225
|
} catch (error) {
|
|
161
|
-
|
|
162
|
-
step
|
|
226
|
+
return {
|
|
227
|
+
step,
|
|
163
228
|
path: target,
|
|
164
229
|
status: "error",
|
|
165
230
|
message: error instanceof Error ? error.message : String(error)
|
|
166
|
-
}
|
|
231
|
+
};
|
|
167
232
|
}
|
|
168
233
|
}
|
|
169
|
-
|
|
234
|
+
try {
|
|
235
|
+
await atomicWriteText(target, filtered);
|
|
236
|
+
return { step, path: target, status: "removed" };
|
|
237
|
+
} catch (error) {
|
|
238
|
+
return {
|
|
239
|
+
step,
|
|
240
|
+
path: target,
|
|
241
|
+
status: "error",
|
|
242
|
+
message: error instanceof Error ? error.message : String(error)
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
function isFrontMatterOnly(content) {
|
|
247
|
+
const trimmed = content.replace(/^\s+/, "");
|
|
248
|
+
const match = trimmed.match(/^---\n[\s\S]*?\n---\s*$/);
|
|
249
|
+
if (match === null) return trimmed.length === 0;
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
async function deleteFabricAgentsSnapshot(projectRoot) {
|
|
253
|
+
const target = fabricAgentsSnapshotPath(projectRoot);
|
|
254
|
+
return rmIfExists("bootstrap-snapshot", target);
|
|
170
255
|
}
|
|
171
256
|
async function uninstallBootstrapStage(projectRoot, _opts = {}) {
|
|
172
257
|
const results = [];
|
|
173
258
|
await runAndCollect(
|
|
174
259
|
results,
|
|
175
|
-
"
|
|
260
|
+
"bootstrap-blocks",
|
|
261
|
+
projectRoot,
|
|
262
|
+
() => stripFabricBootstrapBlocks(projectRoot)
|
|
263
|
+
);
|
|
264
|
+
await runAndCollectOne(
|
|
265
|
+
results,
|
|
266
|
+
"bootstrap-snapshot",
|
|
176
267
|
projectRoot,
|
|
177
|
-
() =>
|
|
268
|
+
() => deleteFabricAgentsSnapshot(projectRoot)
|
|
178
269
|
);
|
|
179
270
|
await runAndCollectOne(
|
|
180
271
|
results,
|
|
@@ -194,6 +285,7 @@ async function uninstallBootstrapStage(projectRoot, _opts = {}) {
|
|
|
194
285
|
projectRoot,
|
|
195
286
|
() => unmergeClaudeCodeHookConfig(projectRoot)
|
|
196
287
|
);
|
|
288
|
+
await runAndCollect(results, "hook-lib", projectRoot, () => removeHookLibs(projectRoot));
|
|
197
289
|
await runAndCollect(
|
|
198
290
|
results,
|
|
199
291
|
"hook-narrow-script",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fenglimg/fabric-cli",
|
|
3
|
-
"version": "2.0.0-rc.
|
|
3
|
+
"version": "2.0.0-rc.22",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"fab": "dist/index.js",
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
"tree-sitter-javascript": "^0.25.0",
|
|
21
21
|
"tree-sitter-typescript": "^0.23.2",
|
|
22
22
|
"web-tree-sitter": "^0.26.8",
|
|
23
|
-
"@fenglimg/fabric-server": "2.0.0-rc.
|
|
24
|
-
"@fenglimg/fabric-shared": "2.0.0-rc.
|
|
23
|
+
"@fenglimg/fabric-server": "2.0.0-rc.22",
|
|
24
|
+
"@fenglimg/fabric-shared": "2.0.0-rc.22"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@types/node": "^22.15.0",
|