@fenglimg/fabric-cli 2.0.0-rc.35 → 2.0.0-rc.37
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/LICENSE +21 -0
- package/dist/{chunk-XVS4F3P6.js → chunk-D25XJ4BC.js} +49 -5
- package/dist/{chunk-G2CIOLD4.js → chunk-WWNXR34K.js} +1 -16
- package/dist/{doctor-2FCRAWDZ.js → doctor-764NFF3X.js} +112 -16
- package/dist/index.js +7 -6
- package/dist/{install-HOTE5BPA.js → install-U7MGIJ2L.js} +50 -22
- package/dist/metrics-ACEQFPDU.js +122 -0
- package/dist/{uninstall-BIJ5GLEU.js → uninstall-MH7ZIB6M.js} +6 -18
- package/package.json +30 -4
- package/templates/hooks/cite-policy-evict.cjs +80 -91
- package/templates/hooks/configs/README.md +19 -0
- package/templates/hooks/configs/codex-hooks.json +3 -0
- package/templates/hooks/configs/cursor-hooks.json +2 -1
- package/templates/hooks/fabric-hint.cjs +146 -8
- package/templates/hooks/knowledge-hint-broad.cjs +65 -104
- package/templates/hooks/knowledge-hint-narrow.cjs +122 -5
- package/templates/hooks/lib/cite-line-parser.cjs +7 -1
- package/templates/hooks/lib/client-adapter.cjs +106 -0
- package/templates/hooks/lib/config-cache.cjs +107 -0
- package/templates/hooks/lib/state-store.cjs +84 -0
- package/templates/skills/fabric-archive/SKILL.md +29 -7
- package/templates/skills/fabric-archive/ref/dry-run-scope.md +1 -1
- package/templates/skills/fabric-archive/ref/i18n-policy.md +6 -0
- package/templates/skills/fabric-archive/ref/phase-1-5-onboard.md +1 -1
- package/templates/skills/fabric-archive/ref/phase-1-cross-session.md +2 -0
- package/templates/skills/fabric-archive/ref/phase-2-5-viability.md +25 -11
- package/templates/skills/fabric-archive/ref/phase-3-5-scope.md +43 -15
- package/templates/skills/fabric-import/SKILL.md +75 -163
- package/templates/skills/fabric-import/ref/i18n-policy.md +6 -0
- package/templates/skills/fabric-import/ref/phase-2-mining.md +2 -2
- package/templates/skills/fabric-review/SKILL.md +31 -25
- package/templates/skills/fabric-review/ref/i18n-policy.md +6 -0
- package/templates/skills/fabric-review/ref/modify-flow.md +9 -1
- package/templates/skills/fabric-review/ref/per-mode-flows.md +1 -1
- package/templates/skills/lib/shared-policy.md +69 -0
- package/dist/serve-43JTEM3U.js +0 -142
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 wangzhichao (fenglimg)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -96,11 +96,19 @@ var HOOK_SCRIPT_DESTINATIONS = {
|
|
|
96
96
|
".codex/hooks/knowledge-hint-narrow.cjs",
|
|
97
97
|
".cursor/hooks/knowledge-hint-narrow.cjs"
|
|
98
98
|
],
|
|
99
|
-
// v2.0.0-rc.34 TASK-06: Claude Code
|
|
100
|
-
//
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
|
|
99
|
+
// v2.0.0-rc.34 TASK-06: Claude Code — UserPromptSubmit cite-policy long-
|
|
100
|
+
// session evict sidecar.
|
|
101
|
+
// v2.0.0-rc.37 NEW-21: extended to Codex / Cursor SessionStart slots.
|
|
102
|
+
// Those clients don't have an equivalent per-prompt event, so cite-policy-
|
|
103
|
+
// evict.cjs runs in "SessionStart mode" (one-shot stderr emit per session
|
|
104
|
+
// boot, no turn-counter). Cadence is lower than Claude Code's per-prompt
|
|
105
|
+
// window but strictly better than 0 (rc.32 baseline measured Codex/Cursor
|
|
106
|
+
// at 3.1% cite coverage when no cite-reminder surface existed).
|
|
107
|
+
citePolicyEvict: [
|
|
108
|
+
".claude/hooks/cite-policy-evict.cjs",
|
|
109
|
+
".codex/hooks/cite-policy-evict.cjs",
|
|
110
|
+
".cursor/hooks/cite-policy-evict.cjs"
|
|
111
|
+
]
|
|
104
112
|
};
|
|
105
113
|
var HOOK_LIB_DESTINATIONS = [
|
|
106
114
|
".claude/hooks/lib",
|
|
@@ -314,6 +322,41 @@ async function installSkillRefFiles(projectRoot, skillSlug) {
|
|
|
314
322
|
}
|
|
315
323
|
return results;
|
|
316
324
|
}
|
|
325
|
+
async function installSharedSkillLib(projectRoot, _options = {}) {
|
|
326
|
+
let libTemplateDir;
|
|
327
|
+
try {
|
|
328
|
+
libTemplateDir = findTemplatePath("skills/lib");
|
|
329
|
+
} catch {
|
|
330
|
+
return [{ step: "skill-lib", path: "skills/lib", status: "skipped", message: "no-lib-dir" }];
|
|
331
|
+
}
|
|
332
|
+
let libFiles;
|
|
333
|
+
try {
|
|
334
|
+
libFiles = readdirSync(libTemplateDir).filter((name) => name.endsWith(".md"));
|
|
335
|
+
} catch {
|
|
336
|
+
return [{ step: "skill-lib", path: libTemplateDir, status: "skipped", message: "no-lib-files" }];
|
|
337
|
+
}
|
|
338
|
+
const clientPrefixes = [".claude", ".codex"];
|
|
339
|
+
const results = [];
|
|
340
|
+
for (const libFile of libFiles) {
|
|
341
|
+
let source;
|
|
342
|
+
try {
|
|
343
|
+
source = readFileSync2(join2(libTemplateDir, libFile), "utf8");
|
|
344
|
+
} catch (error) {
|
|
345
|
+
results.push({
|
|
346
|
+
step: "skill-lib",
|
|
347
|
+
path: join2(libTemplateDir, libFile),
|
|
348
|
+
status: "error",
|
|
349
|
+
message: error instanceof Error ? error.message : String(error)
|
|
350
|
+
});
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
for (const prefix of clientPrefixes) {
|
|
354
|
+
const target = join2(projectRoot, prefix, "skills", "lib", libFile);
|
|
355
|
+
results.push(await copyTextIdempotent("skill-lib", source, target));
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return results;
|
|
359
|
+
}
|
|
317
360
|
async function installArchiveHintHook(projectRoot, _options = {}) {
|
|
318
361
|
const source = await readTemplate(HOOK_SCRIPT_TEMPLATE_REL);
|
|
319
362
|
const targets = HOOK_SCRIPT_DESTINATIONS.fabricHint.map((rel) => join2(projectRoot, rel));
|
|
@@ -822,6 +865,7 @@ export {
|
|
|
822
865
|
installFabricReviewSkill,
|
|
823
866
|
installFabricImportSkill,
|
|
824
867
|
cleanupDeprecatedSkills,
|
|
868
|
+
installSharedSkillLib,
|
|
825
869
|
installArchiveHintHook,
|
|
826
870
|
installKnowledgeHintBroadHook,
|
|
827
871
|
installKnowledgeHintNarrowHook,
|
|
@@ -41,24 +41,9 @@ function padEnd(value, width, char = " ") {
|
|
|
41
41
|
return result;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
// src/lib/error-render.ts
|
|
45
|
-
function hasActionHint(err) {
|
|
46
|
-
if (err === null || typeof err !== "object") return false;
|
|
47
|
-
const candidate = err;
|
|
48
|
-
return typeof candidate.message === "string" && candidate.message.length > 0 && typeof candidate.actionHint === "string" && candidate.actionHint.length > 0;
|
|
49
|
-
}
|
|
50
|
-
function renderFabricError(err, stream = process.stderr) {
|
|
51
|
-
stream.write(`${err.message}
|
|
52
|
-
`);
|
|
53
|
-
stream.write(` -> ${err.actionHint}
|
|
54
|
-
`);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
44
|
export {
|
|
58
45
|
paint,
|
|
59
46
|
symbol,
|
|
60
47
|
displayWidth,
|
|
61
|
-
padEnd
|
|
62
|
-
hasActionHint,
|
|
63
|
-
renderFabricError
|
|
48
|
+
padEnd
|
|
64
49
|
};
|
|
@@ -1,29 +1,27 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
hasActionHint,
|
|
4
3
|
paint,
|
|
5
|
-
renderFabricError,
|
|
6
4
|
symbol
|
|
7
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-WWNXR34K.js";
|
|
6
|
+
import {
|
|
7
|
+
resolveDevMode
|
|
8
|
+
} from "./chunk-COI5VDFU.js";
|
|
8
9
|
import {
|
|
9
10
|
getDoctorTranslator,
|
|
10
11
|
t
|
|
11
12
|
} from "./chunk-PWLW3B57.js";
|
|
12
|
-
import {
|
|
13
|
-
resolveDevMode
|
|
14
|
-
} from "./chunk-COI5VDFU.js";
|
|
15
13
|
|
|
16
14
|
// src/commands/doctor.ts
|
|
17
15
|
import { confirm, isCancel } from "@clack/prompts";
|
|
18
16
|
import { defineCommand } from "citty";
|
|
19
17
|
import {
|
|
20
18
|
appendEventLedgerEvent,
|
|
21
|
-
checkLockOrThrow,
|
|
22
19
|
enrichDescriptions,
|
|
23
20
|
runDoctorApplyLint as runDoctorFixKnowledge,
|
|
24
21
|
runDoctorArchiveHistory,
|
|
25
22
|
runDoctorCiteCoverage,
|
|
26
23
|
runDoctorFix,
|
|
24
|
+
runDoctorHistoryAll,
|
|
27
25
|
runDoctorReport
|
|
28
26
|
} from "@fenglimg/fabric-server";
|
|
29
27
|
var FIX_KNOWLEDGE_CODE_LABELS = {
|
|
@@ -135,21 +133,22 @@ var doctorCommand = defineCommand({
|
|
|
135
133
|
type: "boolean",
|
|
136
134
|
description: t("cli.doctor.args.archive-history.description"),
|
|
137
135
|
default: false
|
|
136
|
+
},
|
|
137
|
+
// rc.37 NEW-33: unified history view across archive / fix / all surfaces.
|
|
138
|
+
// Mode = `archive | fix | all` (the `archive` mode delegates to the
|
|
139
|
+
// existing runDoctorArchiveHistory; `fix` aggregates doctor_run events;
|
|
140
|
+
// `all` rolls up both into a per-day count table). Read-only; mutex
|
|
141
|
+
// with the mutation arms.
|
|
142
|
+
history: {
|
|
143
|
+
type: "string",
|
|
144
|
+
description: t("cli.doctor.args.history.description"),
|
|
145
|
+
valueHint: "archive|fix|all"
|
|
138
146
|
}
|
|
139
147
|
},
|
|
140
148
|
async run({ args }) {
|
|
141
149
|
const workspaceRoot = process.cwd();
|
|
142
150
|
const resolution = resolveDevMode(args.target, workspaceRoot);
|
|
143
151
|
const dt = getDoctorTranslator(resolution.target);
|
|
144
|
-
try {
|
|
145
|
-
checkLockOrThrow(resolution.target);
|
|
146
|
-
} catch (err) {
|
|
147
|
-
if (hasActionHint(err)) {
|
|
148
|
-
renderFabricError(err);
|
|
149
|
-
process.exit(1);
|
|
150
|
-
}
|
|
151
|
-
throw err;
|
|
152
|
-
}
|
|
153
152
|
const fixKnowledge = args["fix-knowledge"] === true;
|
|
154
153
|
const fix = args.fix === true;
|
|
155
154
|
const citeCoverage = args["cite-coverage"] === true;
|
|
@@ -164,6 +163,44 @@ var doctorCommand = defineCommand({
|
|
|
164
163
|
return;
|
|
165
164
|
}
|
|
166
165
|
}
|
|
166
|
+
const historyMode = args.history;
|
|
167
|
+
if (typeof historyMode === "string" && historyMode.length > 0) {
|
|
168
|
+
if (fix || fixKnowledge || citeCoverage || enrichDesc || archiveHistory) {
|
|
169
|
+
writeStderr(dt("cli.doctor.errors.history-mutex"));
|
|
170
|
+
process.exitCode = 1;
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
if (historyMode !== "archive" && historyMode !== "fix" && historyMode !== "all") {
|
|
174
|
+
writeStderr(dt("cli.doctor.errors.invalid-history-mode", { input: historyMode }));
|
|
175
|
+
process.exitCode = 1;
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
const sinceInput = args.since ?? "7d";
|
|
179
|
+
let sinceMs;
|
|
180
|
+
try {
|
|
181
|
+
sinceMs = parseSinceDuration(sinceInput);
|
|
182
|
+
} catch {
|
|
183
|
+
writeStderr(dt("cli.doctor.errors.invalid-since", { input: sinceInput }));
|
|
184
|
+
process.exitCode = 1;
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
if (historyMode === "archive") {
|
|
188
|
+
const report3 = await runDoctorArchiveHistory(resolution.target, { since: sinceMs });
|
|
189
|
+
if (args.json === true) {
|
|
190
|
+
writeStdout(JSON.stringify(report3, null, 2));
|
|
191
|
+
} else {
|
|
192
|
+
renderArchiveHistoryReport(report3, sinceInput, dt);
|
|
193
|
+
}
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
const report2 = await runDoctorHistoryAll(resolution.target, { since: sinceMs });
|
|
197
|
+
if (args.json === true) {
|
|
198
|
+
writeStdout(JSON.stringify(report2, null, 2));
|
|
199
|
+
} else {
|
|
200
|
+
renderHistoryAllReport(report2, sinceInput, historyMode, dt);
|
|
201
|
+
}
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
167
204
|
if (archiveHistory) {
|
|
168
205
|
if (fix || fixKnowledge || citeCoverage || enrichDesc) {
|
|
169
206
|
writeStderr(dt("cli.doctor.errors.archive-history-mutex"));
|
|
@@ -319,6 +356,7 @@ var doctorCommand = defineCommand({
|
|
|
319
356
|
var doctor_default = doctorCommand;
|
|
320
357
|
function renderHumanReport(report, dt, verbose) {
|
|
321
358
|
writeStdout(`${renderStatus(report.status)} ${paint.ai("fabric doctor")} ${paint.human(report.summary.target)}`);
|
|
359
|
+
renderTldrHeader(report);
|
|
322
360
|
for (const check of report.checks) {
|
|
323
361
|
writeStdout(`${renderStatus(check.status)} ${check.name}: ${check.message}`);
|
|
324
362
|
}
|
|
@@ -372,6 +410,31 @@ function writeIssueSection(title, issues, options) {
|
|
|
372
410
|
}
|
|
373
411
|
}
|
|
374
412
|
}
|
|
413
|
+
function renderTldrHeader(report) {
|
|
414
|
+
const ranked = [];
|
|
415
|
+
for (const issue of report.fixable_errors) {
|
|
416
|
+
ranked.push({ severity: "fixable", code: issue.code, message: issue.message });
|
|
417
|
+
}
|
|
418
|
+
for (const issue of report.manual_errors) {
|
|
419
|
+
ranked.push({ severity: "manual", code: issue.code, message: issue.message });
|
|
420
|
+
}
|
|
421
|
+
for (const issue of report.warnings) {
|
|
422
|
+
ranked.push({ severity: "warn", code: issue.code, message: issue.message });
|
|
423
|
+
}
|
|
424
|
+
if (ranked.length === 0) {
|
|
425
|
+
writeStdout(`${symbol.ok} TL;DR: all 48 checks green \u2014 nothing to fix.`);
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
const top3 = ranked.slice(0, 3);
|
|
429
|
+
writeStdout(
|
|
430
|
+
`TL;DR (top ${top3.length} of ${ranked.length}, severity order: fixable\u2192manual\u2192warn):`
|
|
431
|
+
);
|
|
432
|
+
for (const item of top3) {
|
|
433
|
+
const marker = item.severity === "fixable" ? symbol.error : item.severity === "manual" ? symbol.error : symbol.warn;
|
|
434
|
+
const truncated = item.message.length > 140 ? `${item.message.slice(0, 137)}...` : item.message;
|
|
435
|
+
writeStdout(` ${marker} ${item.code}: ${truncated}`);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
375
438
|
function renderStatus(status) {
|
|
376
439
|
if (status === "ok") {
|
|
377
440
|
return symbol.ok;
|
|
@@ -692,6 +755,39 @@ function renderArchiveHistoryReport(report, sinceLabel, dt) {
|
|
|
692
755
|
}
|
|
693
756
|
writeStdout(lines.join("\n"));
|
|
694
757
|
}
|
|
758
|
+
function renderHistoryAllReport(report, sinceLabel, mode, dt) {
|
|
759
|
+
if (report.rows.length === 0) {
|
|
760
|
+
writeStdout(dt("doctor.history.empty", { sinceLabel, mode }));
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
const lines = [];
|
|
764
|
+
lines.push(
|
|
765
|
+
dt("doctor.history.header", {
|
|
766
|
+
sinceLabel,
|
|
767
|
+
mode,
|
|
768
|
+
days: String(report.rows.length)
|
|
769
|
+
})
|
|
770
|
+
);
|
|
771
|
+
lines.push("");
|
|
772
|
+
if (mode === "fix") {
|
|
773
|
+
lines.push("| date | lint | fix | issues | mutations |");
|
|
774
|
+
lines.push("| ---------- | ---- | --- | ------ | --------- |");
|
|
775
|
+
for (const row of report.rows) {
|
|
776
|
+
lines.push(
|
|
777
|
+
`| ${row.date} | ${row.doctor_runs_lint} | ${row.doctor_runs_fix} | ${row.doctor_total_issues} | ${row.doctor_total_mutations} |`
|
|
778
|
+
);
|
|
779
|
+
}
|
|
780
|
+
} else {
|
|
781
|
+
lines.push("| date | lint | fix | issues | mutations | archive | proposed |");
|
|
782
|
+
lines.push("| ---------- | ---- | --- | ------ | --------- | ------- | -------- |");
|
|
783
|
+
for (const row of report.rows) {
|
|
784
|
+
lines.push(
|
|
785
|
+
`| ${row.date} | ${row.doctor_runs_lint} | ${row.doctor_runs_fix} | ${row.doctor_total_issues} | ${row.doctor_total_mutations} | ${row.archive_attempts} | ${row.archive_proposed} |`
|
|
786
|
+
);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
writeStdout(lines.join("\n"));
|
|
790
|
+
}
|
|
695
791
|
function formatTimestampForTable(iso) {
|
|
696
792
|
const d = new Date(iso);
|
|
697
793
|
if (Number.isNaN(d.getTime())) return iso;
|
package/dist/index.js
CHANGED
|
@@ -11,22 +11,23 @@ import { defineCommand, runMain } from "citty";
|
|
|
11
11
|
|
|
12
12
|
// src/commands/index.ts
|
|
13
13
|
var allCommands = {
|
|
14
|
-
install: () => import("./install-
|
|
15
|
-
doctor: () => import("./doctor-
|
|
16
|
-
|
|
17
|
-
uninstall: () => import("./uninstall-BIJ5GLEU.js").then((module) => module.default),
|
|
14
|
+
install: () => import("./install-U7MGIJ2L.js").then((module) => module.default),
|
|
15
|
+
doctor: () => import("./doctor-764NFF3X.js").then((module) => module.default),
|
|
16
|
+
uninstall: () => import("./uninstall-MH7ZIB6M.js").then((module) => module.default),
|
|
18
17
|
config: () => import("./config-XJIPZNUP.js").then((module) => module.default),
|
|
19
18
|
"plan-context-hint": () => import("./plan-context-hint-UQLRKGBZ.js").then((module) => module.default),
|
|
20
19
|
// v2.0.0-rc.23 TASK-014 (F8c): S5 onboard-slot coverage. Used by the
|
|
21
20
|
// fabric-archive Skill's first-run phase to detect unclaimed slots.
|
|
22
|
-
"onboard-coverage": () => import("./onboard-coverage-MFCAEBDO.js").then((module) => module.default)
|
|
21
|
+
"onboard-coverage": () => import("./onboard-coverage-MFCAEBDO.js").then((module) => module.default),
|
|
22
|
+
// v2.0.0-rc.37 NEW-34: text dashboard over .fabric/metrics.jsonl.
|
|
23
|
+
metrics: () => import("./metrics-ACEQFPDU.js").then((module) => module.default)
|
|
23
24
|
};
|
|
24
25
|
|
|
25
26
|
// src/index.ts
|
|
26
27
|
var main = defineCommand({
|
|
27
28
|
meta: {
|
|
28
29
|
name: "fabric",
|
|
29
|
-
version: "2.0.0-rc.
|
|
30
|
+
version: "2.0.0-rc.37",
|
|
30
31
|
description: t("cli.main.description")
|
|
31
32
|
},
|
|
32
33
|
subCommands: allCommands
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
installHookLibs,
|
|
13
13
|
installKnowledgeHintBroadHook,
|
|
14
14
|
installKnowledgeHintNarrowHook,
|
|
15
|
+
installSharedSkillLib,
|
|
15
16
|
mergeClaudeCodeHookConfig,
|
|
16
17
|
mergeCodexHookConfig,
|
|
17
18
|
mergeCursorHookConfig,
|
|
@@ -20,24 +21,22 @@ import {
|
|
|
20
21
|
writeCodexBootstrapManagedBlock,
|
|
21
22
|
writeCursorBootstrapManagedBlock,
|
|
22
23
|
writeFabricAgentsSnapshot
|
|
23
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-D25XJ4BC.js";
|
|
24
25
|
import {
|
|
25
26
|
detectClientSupports
|
|
26
27
|
} from "./chunk-MF3OTILQ.js";
|
|
27
28
|
import {
|
|
28
29
|
displayWidth,
|
|
29
|
-
hasActionHint,
|
|
30
30
|
padEnd,
|
|
31
|
-
paint
|
|
32
|
-
|
|
33
|
-
} from "./chunk-G2CIOLD4.js";
|
|
34
|
-
import {
|
|
35
|
-
t
|
|
36
|
-
} from "./chunk-PWLW3B57.js";
|
|
31
|
+
paint
|
|
32
|
+
} from "./chunk-WWNXR34K.js";
|
|
37
33
|
import {
|
|
38
34
|
createDebugLogger,
|
|
39
35
|
resolveDevMode
|
|
40
36
|
} from "./chunk-COI5VDFU.js";
|
|
37
|
+
import {
|
|
38
|
+
t
|
|
39
|
+
} from "./chunk-PWLW3B57.js";
|
|
41
40
|
|
|
42
41
|
// src/commands/install.ts
|
|
43
42
|
import { randomUUID } from "crypto";
|
|
@@ -49,7 +48,6 @@ import { cancel, confirm, group, intro, isCancel, log, note, outro, select } fro
|
|
|
49
48
|
import { defaultAgentsMetaCounters } from "@fenglimg/fabric-shared";
|
|
50
49
|
import { atomicWriteJson } from "@fenglimg/fabric-shared/node/atomic-write";
|
|
51
50
|
import { defineCommand } from "citty";
|
|
52
|
-
import { checkLockOrThrow } from "@fenglimg/fabric-server";
|
|
53
51
|
|
|
54
52
|
// src/install/hooks-orchestrator.ts
|
|
55
53
|
import { existsSync, statSync } from "fs";
|
|
@@ -61,6 +59,7 @@ async function installHooks(target, _options = {}) {
|
|
|
61
59
|
results.push(...await runStep(() => installFabricArchiveSkill(normalizedTarget)));
|
|
62
60
|
results.push(...await runStep(() => installFabricReviewSkill(normalizedTarget)));
|
|
63
61
|
results.push(...await runStep(() => installFabricImportSkill(normalizedTarget)));
|
|
62
|
+
results.push(...await runStep(() => installSharedSkillLib(normalizedTarget)));
|
|
64
63
|
results.push(...await runStep(() => installArchiveHintHook(normalizedTarget)));
|
|
65
64
|
results.push(...await runStep(() => installKnowledgeHintBroadHook(normalizedTarget)));
|
|
66
65
|
results.push(...await runStep(() => installKnowledgeHintNarrowHook(normalizedTarget)));
|
|
@@ -1351,7 +1350,7 @@ function readProjectName(target) {
|
|
|
1351
1350
|
return basename(target);
|
|
1352
1351
|
}
|
|
1353
1352
|
function getCliVersion() {
|
|
1354
|
-
return true ? "2.0.0-rc.
|
|
1353
|
+
return true ? "2.0.0-rc.37" : "unknown";
|
|
1355
1354
|
}
|
|
1356
1355
|
function sortRecord(record) {
|
|
1357
1356
|
return Object.fromEntries(Object.entries(record).sort(([left], [right]) => left.localeCompare(right)));
|
|
@@ -1393,6 +1392,11 @@ var installCommand = defineCommand({
|
|
|
1393
1392
|
type: "boolean",
|
|
1394
1393
|
description: t("cli.install.args.force-skills-only.description"),
|
|
1395
1394
|
default: false
|
|
1395
|
+
},
|
|
1396
|
+
"force-hooks-only": {
|
|
1397
|
+
type: "boolean",
|
|
1398
|
+
description: t("cli.install.args.force-hooks-only.description"),
|
|
1399
|
+
default: false
|
|
1396
1400
|
}
|
|
1397
1401
|
},
|
|
1398
1402
|
async run({ args }) {
|
|
@@ -1443,6 +1447,35 @@ ${hint}
|
|
|
1443
1447
|
process.exitCode = 1;
|
|
1444
1448
|
}
|
|
1445
1449
|
}
|
|
1450
|
+
async function runHooksOnlyRefresh(targetInput) {
|
|
1451
|
+
const target = normalizeTarget3(targetInput);
|
|
1452
|
+
const metaPath = join4(target, ".fabric", "agents.meta.json");
|
|
1453
|
+
if (!existsSync4(metaPath)) {
|
|
1454
|
+
const message = t("cli.install.force-hooks-only.uninitialised.message");
|
|
1455
|
+
const hint = t("cli.install.force-hooks-only.uninitialised.hint");
|
|
1456
|
+
process.stderr.write(`${message}
|
|
1457
|
+
${hint}
|
|
1458
|
+
`);
|
|
1459
|
+
process.exitCode = 1;
|
|
1460
|
+
return;
|
|
1461
|
+
}
|
|
1462
|
+
console.log(formatInitStageHeader(t("cli.install.force-hooks-only.banner")));
|
|
1463
|
+
const result = await installHooks(target);
|
|
1464
|
+
console.log(
|
|
1465
|
+
t("cli.install.force-hooks-only.summary", {
|
|
1466
|
+
written: String(result.installed.length),
|
|
1467
|
+
skipped: String(result.skipped.length),
|
|
1468
|
+
errors: String(result.errors.length)
|
|
1469
|
+
})
|
|
1470
|
+
);
|
|
1471
|
+
if (result.errors.length > 0) {
|
|
1472
|
+
for (const err of result.errors) {
|
|
1473
|
+
process.stderr.write(` ${err}
|
|
1474
|
+
`);
|
|
1475
|
+
}
|
|
1476
|
+
process.exitCode = 1;
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1446
1479
|
async function runInitCommand(args) {
|
|
1447
1480
|
const logger = createDebugLogger(args.debug);
|
|
1448
1481
|
const resolution = resolveDevMode(args.target, process.cwd());
|
|
@@ -1450,19 +1483,11 @@ async function runInitCommand(args) {
|
|
|
1450
1483
|
await runSkillsOnlyRefresh(resolution.target);
|
|
1451
1484
|
return;
|
|
1452
1485
|
}
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
try {
|
|
1457
|
-
checkLockOrThrow(intent.target);
|
|
1458
|
-
} catch (err) {
|
|
1459
|
-
if (hasActionHint(err)) {
|
|
1460
|
-
renderFabricError(err);
|
|
1461
|
-
process.exit(1);
|
|
1462
|
-
}
|
|
1463
|
-
throw err;
|
|
1464
|
-
}
|
|
1486
|
+
if (args["force-hooks-only"] === true) {
|
|
1487
|
+
await runHooksOnlyRefresh(resolution.target);
|
|
1488
|
+
return;
|
|
1465
1489
|
}
|
|
1490
|
+
const intent = resolveInitCliIntent(args, resolution.target);
|
|
1466
1491
|
logger(`init target source: ${resolution.source}`);
|
|
1467
1492
|
for (const step of resolution.chain) {
|
|
1468
1493
|
logger(step);
|
|
@@ -2335,6 +2360,8 @@ function printInitCapabilitySummary(supports, stageResults, options) {
|
|
|
2335
2360
|
for (const row of rows) {
|
|
2336
2361
|
console.log(formatCapabilityTableRow(row, widths));
|
|
2337
2362
|
}
|
|
2363
|
+
console.log("");
|
|
2364
|
+
console.log(t("cli.install.restart-banner"));
|
|
2338
2365
|
}
|
|
2339
2366
|
function toCapabilityRow(support, stageResults, options) {
|
|
2340
2367
|
const stage = (name) => stageResults.find((entry) => entry.name === name)?.disposition ?? null;
|
|
@@ -2455,6 +2482,7 @@ export {
|
|
|
2455
2482
|
initFabric,
|
|
2456
2483
|
installCommand,
|
|
2457
2484
|
resolveInitExecutionPlanWithWizard,
|
|
2485
|
+
runHooksOnlyRefresh,
|
|
2458
2486
|
runInitCommand,
|
|
2459
2487
|
runSkillsOnlyRefresh,
|
|
2460
2488
|
shouldUseInitWizard
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/commands/metrics.ts
|
|
4
|
+
import { resolve } from "path";
|
|
5
|
+
import { defineCommand } from "citty";
|
|
6
|
+
import { readMetrics } from "@fenglimg/fabric-server";
|
|
7
|
+
function parseSinceArg(raw) {
|
|
8
|
+
if (raw === void 0 || raw.length === 0) return 0;
|
|
9
|
+
const match = /^(\d+)([smhd]?)$/u.exec(raw);
|
|
10
|
+
if (match === null) {
|
|
11
|
+
throw new Error(`--since: invalid duration "${raw}" (expected e.g. 24h, 7d, 30m)`);
|
|
12
|
+
}
|
|
13
|
+
const n = Number.parseInt(match[1], 10);
|
|
14
|
+
const unit = match[2] ?? "s";
|
|
15
|
+
const multipliers = { s: 1e3, m: 6e4, h: 36e5, d: 864e5 };
|
|
16
|
+
return n * (multipliers[unit] ?? 1e3);
|
|
17
|
+
}
|
|
18
|
+
function aggregate(rows, sinceMs, now) {
|
|
19
|
+
const cutoff = sinceMs > 0 ? now.getTime() - sinceMs : 0;
|
|
20
|
+
const filtered = rows.filter((r) => {
|
|
21
|
+
if (cutoff === 0) return true;
|
|
22
|
+
const ts = Date.parse(r.timestamp);
|
|
23
|
+
return Number.isFinite(ts) && ts >= cutoff;
|
|
24
|
+
});
|
|
25
|
+
const totals = {};
|
|
26
|
+
const perEntryConsumed = {};
|
|
27
|
+
for (const row of filtered) {
|
|
28
|
+
for (const [name, count] of Object.entries(row.counters)) {
|
|
29
|
+
if (name.startsWith("knowledge_consumed:")) {
|
|
30
|
+
const id = name.slice("knowledge_consumed:".length);
|
|
31
|
+
perEntryConsumed[id] = (perEntryConsumed[id] ?? 0) + count;
|
|
32
|
+
totals["knowledge_consumed"] = (totals["knowledge_consumed"] ?? 0) + count;
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
totals[name] = (totals[name] ?? 0) + count;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
windowDescription: sinceMs > 0 ? formatDuration(sinceMs) : "all-time",
|
|
40
|
+
rowCount: filtered.length,
|
|
41
|
+
totals,
|
|
42
|
+
perEntryConsumed,
|
|
43
|
+
rangeStart: filtered[0]?.timestamp ?? null,
|
|
44
|
+
rangeEnd: filtered[filtered.length - 1]?.timestamp ?? null
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function formatDuration(ms) {
|
|
48
|
+
if (ms >= 864e5) return `${Math.round(ms / 864e5)}d`;
|
|
49
|
+
if (ms >= 36e5) return `${Math.round(ms / 36e5)}h`;
|
|
50
|
+
if (ms >= 6e4) return `${Math.round(ms / 6e4)}m`;
|
|
51
|
+
return `${Math.round(ms / 1e3)}s`;
|
|
52
|
+
}
|
|
53
|
+
function renderText(agg) {
|
|
54
|
+
const lines = [];
|
|
55
|
+
lines.push(`Fabric metrics \u2014 window: ${agg.windowDescription}`);
|
|
56
|
+
if (agg.rangeStart && agg.rangeEnd) {
|
|
57
|
+
lines.push(` rows: ${agg.rowCount} (${agg.rangeStart} \u2192 ${agg.rangeEnd})`);
|
|
58
|
+
} else {
|
|
59
|
+
lines.push(` rows: ${agg.rowCount}`);
|
|
60
|
+
}
|
|
61
|
+
lines.push("");
|
|
62
|
+
if (Object.keys(agg.totals).length === 0) {
|
|
63
|
+
lines.push(" (no counter activity in window \u2014 server may be idle or just started)");
|
|
64
|
+
return lines.join("\n");
|
|
65
|
+
}
|
|
66
|
+
lines.push(" counter total");
|
|
67
|
+
lines.push(" ------------------------------------ ----------");
|
|
68
|
+
const sorted = Object.entries(agg.totals).sort((a, b) => b[1] - a[1]);
|
|
69
|
+
for (const [name, count] of sorted) {
|
|
70
|
+
lines.push(` ${name.padEnd(36)} ${String(count).padStart(10)}`);
|
|
71
|
+
}
|
|
72
|
+
const perEntrySorted = Object.entries(agg.perEntryConsumed).sort((a, b) => b[1] - a[1]).slice(0, 10);
|
|
73
|
+
if (perEntrySorted.length > 0) {
|
|
74
|
+
lines.push("");
|
|
75
|
+
lines.push(" Top per-entry consumed (knowledge_consumed:<id>)");
|
|
76
|
+
lines.push(" ------------------------------------ ----------");
|
|
77
|
+
for (const [id, count] of perEntrySorted) {
|
|
78
|
+
lines.push(` ${id.padEnd(36)} ${String(count).padStart(10)}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return lines.join("\n");
|
|
82
|
+
}
|
|
83
|
+
var metricsCommand = defineCommand({
|
|
84
|
+
meta: {
|
|
85
|
+
name: "metrics",
|
|
86
|
+
description: "Print a text dashboard of Fabric counter activity from .fabric/metrics.jsonl",
|
|
87
|
+
hidden: true
|
|
88
|
+
},
|
|
89
|
+
args: {
|
|
90
|
+
json: {
|
|
91
|
+
type: "boolean",
|
|
92
|
+
description: "Emit machine-readable JSON instead of the text table.",
|
|
93
|
+
default: false
|
|
94
|
+
},
|
|
95
|
+
target: {
|
|
96
|
+
type: "string",
|
|
97
|
+
description: "Project root (defaults to the current working directory)."
|
|
98
|
+
},
|
|
99
|
+
since: {
|
|
100
|
+
type: "string",
|
|
101
|
+
description: "Limit to rows within a recent window. Examples: 24h, 7d, 30m, 90s. Omit for all-time."
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
async run({ args }) {
|
|
105
|
+
const projectRoot = resolve(args.target ?? process.cwd());
|
|
106
|
+
const sinceMs = parseSinceArg(args.since);
|
|
107
|
+
const rows = await readMetrics(projectRoot);
|
|
108
|
+
const aggregated = aggregate(rows, sinceMs, /* @__PURE__ */ new Date());
|
|
109
|
+
if (args.json === true) {
|
|
110
|
+
process.stdout.write(`${JSON.stringify(aggregated)}
|
|
111
|
+
`);
|
|
112
|
+
} else {
|
|
113
|
+
process.stdout.write(`${renderText(aggregated)}
|
|
114
|
+
`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
var metrics_default = metricsCommand;
|
|
119
|
+
export {
|
|
120
|
+
metrics_default as default,
|
|
121
|
+
metricsCommand
|
|
122
|
+
};
|
|
@@ -7,23 +7,21 @@ import {
|
|
|
7
7
|
HOOK_SCRIPT_DESTINATIONS,
|
|
8
8
|
SKILL_DESTINATIONS,
|
|
9
9
|
fabricAgentsSnapshotPath
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-D25XJ4BC.js";
|
|
11
11
|
import {
|
|
12
12
|
detectClientSupports,
|
|
13
13
|
resolveClients
|
|
14
14
|
} from "./chunk-MF3OTILQ.js";
|
|
15
15
|
import {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
renderFabricError
|
|
19
|
-
} from "./chunk-G2CIOLD4.js";
|
|
20
|
-
import {
|
|
21
|
-
t
|
|
22
|
-
} from "./chunk-PWLW3B57.js";
|
|
16
|
+
paint
|
|
17
|
+
} from "./chunk-WWNXR34K.js";
|
|
23
18
|
import {
|
|
24
19
|
createDebugLogger,
|
|
25
20
|
resolveDevMode
|
|
26
21
|
} from "./chunk-COI5VDFU.js";
|
|
22
|
+
import {
|
|
23
|
+
t
|
|
24
|
+
} from "./chunk-PWLW3B57.js";
|
|
27
25
|
|
|
28
26
|
// src/commands/uninstall.ts
|
|
29
27
|
import { existsSync as existsSync2, statSync } from "fs";
|
|
@@ -32,7 +30,6 @@ import { homedir } from "os";
|
|
|
32
30
|
import { isAbsolute, join as join2, relative, resolve, sep } from "path";
|
|
33
31
|
import { cancel, confirm, group, intro, isCancel, log, note, outro } from "@clack/prompts";
|
|
34
32
|
import { defineCommand } from "citty";
|
|
35
|
-
import { checkLockOrThrow } from "@fenglimg/fabric-server";
|
|
36
33
|
|
|
37
34
|
// src/install/uninstall-skills-and-hooks.ts
|
|
38
35
|
import { existsSync } from "fs";
|
|
@@ -570,15 +567,6 @@ async function runUninstallCommand(args) {
|
|
|
570
567
|
for (const step of resolution.chain) {
|
|
571
568
|
logger(step);
|
|
572
569
|
}
|
|
573
|
-
try {
|
|
574
|
-
checkLockOrThrow(intent.target);
|
|
575
|
-
} catch (err) {
|
|
576
|
-
if (hasActionHint(err)) {
|
|
577
|
-
renderFabricError(err);
|
|
578
|
-
process.exit(1);
|
|
579
|
-
}
|
|
580
|
-
throw err;
|
|
581
|
-
}
|
|
582
570
|
const supports = detectClientSupports(intent.target);
|
|
583
571
|
const basePlan = await buildUninstallExecutionPlan(intent.target, {
|
|
584
572
|
...intent.options
|