@fenglimg/fabric-cli 2.0.0-rc.23 → 2.0.0-rc.25
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/{doctor-R2E2XO6A.js → doctor-DXKPYPRC.js} +173 -1
- package/dist/index.js +3 -3
- package/dist/{install-TDZYZV54.js → install-S2J76N2B.js} +1 -1
- package/package.json +3 -3
- package/templates/hooks/archive-hint.cjs +463 -0
- package/templates/hooks/fabric-hint.cjs +153 -98
- package/templates/hooks/lib/cite-contract-reminder.cjs +173 -0
- package/templates/hooks/lib/cite-line-parser.cjs +118 -0
- package/templates/skills/fabric-archive/SKILL.md +535 -4
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
checkLockOrThrow,
|
|
21
21
|
enrichDescriptions,
|
|
22
22
|
runDoctorApplyLint as runDoctorFixKnowledge,
|
|
23
|
+
runDoctorArchiveHistory,
|
|
23
24
|
runDoctorCiteCoverage,
|
|
24
25
|
runDoctorFix,
|
|
25
26
|
runDoctorReport
|
|
@@ -89,6 +90,15 @@ var doctorCommand = defineCommand({
|
|
|
89
90
|
default: "all",
|
|
90
91
|
valueHint: "cc|codex|cursor|all"
|
|
91
92
|
},
|
|
93
|
+
// v2.0.0-rc.24 TASK-10: --layer filter for the cite contract audit. Pairs
|
|
94
|
+
// with --cite-coverage. Validated against {'team','personal','all'} at
|
|
95
|
+
// command entry; rejects 'both' (rc.20 plan-context vocabulary) explicitly.
|
|
96
|
+
layer: {
|
|
97
|
+
type: "string",
|
|
98
|
+
description: t("cli.doctor.args.layer.description"),
|
|
99
|
+
default: "all",
|
|
100
|
+
valueHint: "team|personal|all"
|
|
101
|
+
},
|
|
92
102
|
// rc.23 TASK-007 (a-C2): description-grade back-fill flag set. Read-side
|
|
93
103
|
// by default; `--auto` flips the writer arm on. Mutually exclusive with
|
|
94
104
|
// --fix / --fix-knowledge / --cite-coverage (different mutation surfaces).
|
|
@@ -106,6 +116,14 @@ var doctorCommand = defineCommand({
|
|
|
106
116
|
type: "boolean",
|
|
107
117
|
description: t("cli.doctor.args.dry-run.description"),
|
|
108
118
|
default: false
|
|
119
|
+
},
|
|
120
|
+
// v2.0.0-rc.25 TASK-10: --archive-history flag (parallel to rc.20
|
|
121
|
+
// --cite-coverage). Read-only; reads session_archive_attempted events
|
|
122
|
+
// and renders a per-session table. Pairs with the shared `--since` flag.
|
|
123
|
+
"archive-history": {
|
|
124
|
+
type: "boolean",
|
|
125
|
+
description: t("cli.doctor.args.archive-history.description"),
|
|
126
|
+
default: false
|
|
109
127
|
}
|
|
110
128
|
},
|
|
111
129
|
async run({ args }) {
|
|
@@ -124,6 +142,32 @@ var doctorCommand = defineCommand({
|
|
|
124
142
|
const fix = args.fix === true;
|
|
125
143
|
const citeCoverage = args["cite-coverage"] === true;
|
|
126
144
|
const enrichDesc = args["enrich-descriptions"] === true;
|
|
145
|
+
const archiveHistory = args["archive-history"] === true;
|
|
146
|
+
if (archiveHistory) {
|
|
147
|
+
if (fix || fixKnowledge || citeCoverage || enrichDesc) {
|
|
148
|
+
writeStderr(t("cli.doctor.errors.archive-history-mutex"));
|
|
149
|
+
process.exitCode = 1;
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
const sinceInput = args.since ?? "7d";
|
|
153
|
+
let sinceMs;
|
|
154
|
+
try {
|
|
155
|
+
sinceMs = parseSinceDuration(sinceInput);
|
|
156
|
+
} catch {
|
|
157
|
+
writeStderr(t("cli.doctor.errors.invalid-since", { input: sinceInput }));
|
|
158
|
+
process.exitCode = 1;
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const report2 = await runDoctorArchiveHistory(resolution.target, {
|
|
162
|
+
since: sinceMs
|
|
163
|
+
});
|
|
164
|
+
if (args.json === true) {
|
|
165
|
+
writeStdout(JSON.stringify(report2, null, 2));
|
|
166
|
+
} else {
|
|
167
|
+
renderArchiveHistoryReport(report2, sinceInput);
|
|
168
|
+
}
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
127
171
|
if (enrichDesc) {
|
|
128
172
|
if (fix || fixKnowledge || citeCoverage) {
|
|
129
173
|
writeStderr(t("cli.doctor.errors.enrich-descriptions-mutex"));
|
|
@@ -163,9 +207,16 @@ var doctorCommand = defineCommand({
|
|
|
163
207
|
process.exitCode = 1;
|
|
164
208
|
return;
|
|
165
209
|
}
|
|
210
|
+
const layerFilter = args.layer ?? "all";
|
|
211
|
+
if (!isValidLayerFilter(layerFilter)) {
|
|
212
|
+
writeStderr(t("cli.doctor.errors.invalid-layer", { input: layerFilter }));
|
|
213
|
+
process.exitCode = 1;
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
166
216
|
const report2 = await runDoctorCiteCoverage(resolution.target, {
|
|
167
217
|
since: sinceMs,
|
|
168
|
-
client: clientFilter
|
|
218
|
+
client: clientFilter,
|
|
219
|
+
layer: layerFilter
|
|
169
220
|
});
|
|
170
221
|
renderCiteCoverageReport(report2, args.json === true);
|
|
171
222
|
return;
|
|
@@ -377,6 +428,14 @@ var CITE_COVERAGE_CLIENT_FILTERS = /* @__PURE__ */ new Set([
|
|
|
377
428
|
function isValidClientFilter(input) {
|
|
378
429
|
return CITE_COVERAGE_CLIENT_FILTERS.has(input);
|
|
379
430
|
}
|
|
431
|
+
var CITE_COVERAGE_LAYER_FILTERS = /* @__PURE__ */ new Set([
|
|
432
|
+
"team",
|
|
433
|
+
"personal",
|
|
434
|
+
"all"
|
|
435
|
+
]);
|
|
436
|
+
function isValidLayerFilter(input) {
|
|
437
|
+
return CITE_COVERAGE_LAYER_FILTERS.has(input);
|
|
438
|
+
}
|
|
380
439
|
function renderCiteCoverageReport(report, jsonMode) {
|
|
381
440
|
if (jsonMode) {
|
|
382
441
|
writeStdout(JSON.stringify(report, null, 2));
|
|
@@ -427,8 +486,83 @@ function renderCiteCoverageReport(report, jsonMode) {
|
|
|
427
486
|
lines.push(` ${label}: ${count}`);
|
|
428
487
|
}
|
|
429
488
|
}
|
|
489
|
+
appendContractSection(lines, report);
|
|
430
490
|
writeStdout(lines.join("\n"));
|
|
431
491
|
}
|
|
492
|
+
function appendContractSection(lines, report) {
|
|
493
|
+
const status = report.contract_metrics_status;
|
|
494
|
+
if (status === void 0) {
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
const metrics = report.contract_metrics;
|
|
498
|
+
const perLayerType = report.per_layer_type;
|
|
499
|
+
const allCountsZero = metrics === void 0 || metrics.decisions_cited === 0 && metrics.pitfalls_cited === 0 && metrics.contract_with === 0 && metrics.contract_missing === 0 && metrics.hard_violated === 0 && metrics.cite_id_unresolved === 0 && Object.keys(metrics.skip_count).length === 0;
|
|
500
|
+
if (status === "awaiting_marker" && allCountsZero) {
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
lines.push("");
|
|
504
|
+
lines.push(`### ${t("cite-coverage.contract.header")}`);
|
|
505
|
+
if (status === "skipped:bootstrap_drift") {
|
|
506
|
+
lines.push(` ${t("cite-coverage.contract.status.skipped_bootstrap_drift")}`);
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
const statusKey = status === "ok" ? "cite-coverage.contract.status.ok" : "cite-coverage.contract.status.awaiting_marker";
|
|
510
|
+
lines.push(` status: ${t(statusKey)}`);
|
|
511
|
+
if (typeof report.contract_marker_ts === "number" && report.contract_marker_ts > 0) {
|
|
512
|
+
lines.push(` since: ${new Date(report.contract_marker_ts).toISOString()}`);
|
|
513
|
+
}
|
|
514
|
+
if (report.layer_filter !== void 0) {
|
|
515
|
+
lines.push(` layer filter: ${report.layer_filter}`);
|
|
516
|
+
}
|
|
517
|
+
if (metrics !== void 0) {
|
|
518
|
+
lines.push(` ${t("cite-coverage.contract.decisions_cited")}: ${metrics.decisions_cited}`);
|
|
519
|
+
lines.push(` ${t("cite-coverage.contract.pitfalls_cited")}: ${metrics.pitfalls_cited}`);
|
|
520
|
+
lines.push(` ${t("cite-coverage.contract.with")}: ${metrics.contract_with}`);
|
|
521
|
+
lines.push(` ${t("cite-coverage.contract.missing")}: ${metrics.contract_missing}`);
|
|
522
|
+
if (metrics.hard_violated > 0) {
|
|
523
|
+
const layerSuffix = report.layer_filter === "personal" ? t("cite-coverage.layer.personal_fyi") : t("cite-coverage.layer.team_review");
|
|
524
|
+
lines.push(
|
|
525
|
+
` ${t("cite-coverage.contract.hard_violated")} ${layerSuffix}: ${metrics.hard_violated}`
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
if (perLayerType !== void 0) {
|
|
530
|
+
const teamKeys = Object.keys(perLayerType.team).filter(
|
|
531
|
+
(k) => perLayerType.team[k] > 0
|
|
532
|
+
);
|
|
533
|
+
const personalKeys = Object.keys(perLayerType.personal).filter(
|
|
534
|
+
(k) => perLayerType.personal[k] > 0
|
|
535
|
+
);
|
|
536
|
+
if (teamKeys.length > 0 || personalKeys.length > 0) {
|
|
537
|
+
lines.push("");
|
|
538
|
+
lines.push(`#### ${t("cite-coverage.layer.team")} \xD7 ${t("cite-coverage.layer.personal")}`);
|
|
539
|
+
for (const key of teamKeys) {
|
|
540
|
+
const label = t(`cite-coverage.contract.type.${key}`);
|
|
541
|
+
lines.push(` ${t("cite-coverage.layer.team")} \u2014 ${label}: ${perLayerType.team[key]}`);
|
|
542
|
+
}
|
|
543
|
+
for (const key of personalKeys) {
|
|
544
|
+
const label = t(`cite-coverage.contract.type.${key}`);
|
|
545
|
+
lines.push(
|
|
546
|
+
` ${t("cite-coverage.layer.personal")} \u2014 ${label}: ${perLayerType.personal[key]}`
|
|
547
|
+
);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
if (metrics !== void 0 && Object.keys(metrics.skip_count).length > 0) {
|
|
552
|
+
lines.push("");
|
|
553
|
+
lines.push(`#### ${t("cite-coverage.contract.skip_count")}`);
|
|
554
|
+
for (const [reason, count] of Object.entries(metrics.skip_count)) {
|
|
555
|
+
const label = t(`cite-coverage.skip.${reason}`);
|
|
556
|
+
lines.push(` ${label}: ${count}`);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
if (metrics !== void 0 && metrics.cite_id_unresolved > 0) {
|
|
560
|
+
lines.push("");
|
|
561
|
+
lines.push(
|
|
562
|
+
`${symbol.warn} ${t("cite-coverage.contract.cite_id_unresolved")}: ${metrics.cite_id_unresolved}`
|
|
563
|
+
);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
432
566
|
function renderEnrichDescriptionsReport(report) {
|
|
433
567
|
const header = `${symbol.ok} ${paint.ai("fab doctor --enrich-descriptions")} mode=${report.mode}${report.dryRun ? " (dry-run)" : ""} scanned=${report.scanned} modified=${report.modified} skipped=${report.skipped}`;
|
|
434
568
|
writeStdout(header);
|
|
@@ -477,6 +611,44 @@ function parseSinceDuration(input) {
|
|
|
477
611
|
}
|
|
478
612
|
throw new Error(`invalid --since value: ${input}`);
|
|
479
613
|
}
|
|
614
|
+
function renderArchiveHistoryReport(report, sinceLabel) {
|
|
615
|
+
if (report.entries.length === 0) {
|
|
616
|
+
writeStdout(t("doctor.archive-history.empty", { sinceLabel }));
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
const lines = [];
|
|
620
|
+
lines.push(
|
|
621
|
+
t("doctor.archive-history.header", {
|
|
622
|
+
sinceLabel,
|
|
623
|
+
count: String(report.total),
|
|
624
|
+
plural: report.total === 1 ? "" : "s"
|
|
625
|
+
})
|
|
626
|
+
);
|
|
627
|
+
lines.push("");
|
|
628
|
+
lines.push(
|
|
629
|
+
`| ${t("doctor.archive-history.table.session")} | ${t(
|
|
630
|
+
"doctor.archive-history.table.lastAttempt"
|
|
631
|
+
)} | ${t("doctor.archive-history.table.outcome")} | ${t(
|
|
632
|
+
"doctor.archive-history.table.candidates"
|
|
633
|
+
)} | ${t("doctor.archive-history.table.coveredGap")} |`
|
|
634
|
+
);
|
|
635
|
+
lines.push("| ------- | ---------------- | -------- | ---------- | ----------- |");
|
|
636
|
+
for (const entry of report.entries) {
|
|
637
|
+
const lastAttempt = formatTimestampForTable(entry.last_attempted_at);
|
|
638
|
+
lines.push(
|
|
639
|
+
`| ${entry.session_id_short} | ${lastAttempt} | ${entry.outcome} | ${entry.candidates_proposed} | ${entry.age_since_covered_hours}h |`
|
|
640
|
+
);
|
|
641
|
+
}
|
|
642
|
+
writeStdout(lines.join("\n"));
|
|
643
|
+
}
|
|
644
|
+
function formatTimestampForTable(iso) {
|
|
645
|
+
const d = new Date(iso);
|
|
646
|
+
if (Number.isNaN(d.getTime())) return iso;
|
|
647
|
+
const pad = (n) => n < 10 ? `0${n}` : `${n}`;
|
|
648
|
+
return `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())} ${pad(
|
|
649
|
+
d.getUTCHours()
|
|
650
|
+
)}:${pad(d.getUTCMinutes())}`;
|
|
651
|
+
}
|
|
480
652
|
export {
|
|
481
653
|
doctor_default as default,
|
|
482
654
|
doctorCommand,
|
package/dist/index.js
CHANGED
|
@@ -11,8 +11,8 @@ 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-
|
|
14
|
+
install: () => import("./install-S2J76N2B.js").then((module) => module.default),
|
|
15
|
+
doctor: () => import("./doctor-DXKPYPRC.js").then((module) => module.default),
|
|
16
16
|
serve: () => import("./serve-NPCI342P.js").then((module) => module.default),
|
|
17
17
|
uninstall: () => import("./uninstall-MQM6NUFM.js").then((module) => module.default),
|
|
18
18
|
config: () => import("./config-XGUUAYX6.js").then((module) => module.default),
|
|
@@ -26,7 +26,7 @@ var allCommands = {
|
|
|
26
26
|
var main = defineCommand({
|
|
27
27
|
meta: {
|
|
28
28
|
name: "fabric",
|
|
29
|
-
version: "2.0.0-rc.
|
|
29
|
+
version: "2.0.0-rc.25",
|
|
30
30
|
description: t("cli.main.description")
|
|
31
31
|
},
|
|
32
32
|
subCommands: allCommands
|
|
@@ -1348,7 +1348,7 @@ function readProjectName(target) {
|
|
|
1348
1348
|
return basename(target);
|
|
1349
1349
|
}
|
|
1350
1350
|
function getCliVersion() {
|
|
1351
|
-
return true ? "2.0.0-rc.
|
|
1351
|
+
return true ? "2.0.0-rc.25" : "unknown";
|
|
1352
1352
|
}
|
|
1353
1353
|
function sortRecord(record) {
|
|
1354
1354
|
return Object.fromEntries(Object.entries(record).sort(([left], [right]) => left.localeCompare(right)));
|
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.25",
|
|
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.25",
|
|
24
|
+
"@fenglimg/fabric-shared": "2.0.0-rc.25"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@types/node": "^22.15.0",
|