@fenglimg/fabric-cli 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +6 -5
- package/dist/chunk-BATF4PEJ.js +361 -0
- package/dist/{chunk-OBQU6NHO.js → chunk-COI5VDFU.js} +0 -18
- package/dist/chunk-D25XJ4BC.js +880 -0
- package/dist/chunk-MF3OTILQ.js +544 -0
- package/dist/chunk-PWLW3B57.js +18 -0
- package/dist/config-XJIPZNUP.js +13 -0
- package/dist/doctor-EJDSEJSS.js +810 -0
- package/dist/index.js +15 -8
- package/dist/{init-BIRSIOXO.js → install-EKWMFLUU.js} +622 -711
- package/dist/metrics-ACEQFPDU.js +122 -0
- package/dist/onboard-coverage-MFCAEBDO.js +220 -0
- package/dist/{plan-context-hint-QMUPAXIB.js → plan-context-hint-FC6P3WFE.js} +34 -28
- package/dist/uninstall-MH7ZIB6M.js +1064 -0
- package/package.json +30 -5
- package/templates/hooks/cite-policy-evict.cjs +231 -0
- package/templates/hooks/configs/README.md +29 -6
- package/templates/hooks/configs/claude-code.json +14 -3
- package/templates/hooks/configs/codex-hooks.json +6 -3
- package/templates/hooks/configs/cursor-hooks.json +8 -10
- package/templates/hooks/fabric-hint.cjs +833 -105
- package/templates/hooks/knowledge-hint-broad.cjs +509 -135
- package/templates/hooks/knowledge-hint-narrow.cjs +791 -26
- package/templates/hooks/lib/banner-i18n.cjs +309 -0
- package/templates/hooks/lib/cite-contract-reminder.cjs +173 -0
- package/templates/hooks/lib/cite-line-parser.cjs +158 -0
- 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/hooks/lib/summary-fallback.cjs +210 -0
- package/templates/skills/fabric-archive/SKILL.md +93 -419
- package/templates/skills/fabric-archive/ref/dry-run-scope.md +16 -0
- package/templates/skills/fabric-archive/ref/e5-cron-recap.md +58 -0
- package/templates/skills/fabric-archive/ref/i18n-policy.md +86 -0
- package/templates/skills/fabric-archive/ref/phase-0-range-resolution.md +156 -0
- package/templates/skills/fabric-archive/ref/phase-1-5-onboard.md +218 -0
- package/templates/skills/fabric-archive/ref/phase-1-cross-session.md +62 -0
- package/templates/skills/fabric-archive/ref/phase-2-5-viability.md +68 -0
- package/templates/skills/fabric-archive/ref/phase-3-5-scope.md +108 -0
- package/templates/skills/fabric-archive/ref/phase-3-classify.md +63 -0
- package/templates/skills/fabric-archive/ref/phase-4-5-emit.md +78 -0
- package/templates/skills/fabric-archive/ref/phase-4-mcp-persist.md +89 -0
- package/templates/skills/fabric-archive/ref/rc-history.md +38 -0
- package/templates/skills/fabric-archive/ref/worked-examples.md +78 -0
- package/templates/skills/fabric-import/SKILL.md +75 -516
- package/templates/skills/fabric-import/ref/checkpoint-state.md +85 -0
- package/templates/skills/fabric-import/ref/i18n-policy.md +79 -0
- package/templates/skills/fabric-import/ref/output-contract.md +61 -0
- package/templates/skills/fabric-import/ref/phase-2-mining.md +213 -0
- package/templates/skills/fabric-import/ref/phase-3-dedup.md +75 -0
- package/templates/skills/fabric-import/ref/state-recovery.md +57 -0
- package/templates/skills/fabric-import/ref/worked-examples.md +127 -0
- package/templates/skills/fabric-review/SKILL.md +86 -284
- package/templates/skills/fabric-review/ref/askuserquestion-policy.md +66 -0
- package/templates/skills/fabric-review/ref/i18n-policy.md +111 -0
- package/templates/skills/fabric-review/ref/modify-flow.md +103 -0
- package/templates/skills/fabric-review/ref/output-contract.md +58 -0
- package/templates/skills/fabric-review/ref/per-mode-flows.md +155 -0
- package/templates/skills/fabric-review/ref/semantic-check.md +26 -0
- package/templates/skills/fabric-review/ref/worked-examples.md +95 -0
- package/templates/skills/lib/shared-policy.md +69 -0
- package/dist/chunk-6ICJICVU.js +0 -10
- package/dist/chunk-74SZWYPH.js +0 -658
- package/dist/chunk-EYIDD2YS.js +0 -1000
- package/dist/doctor-T7JWODKG.js +0 -282
- package/dist/hooks-Y74Y5LQS.js +0 -12
- package/dist/scan-LMK3UCWL.js +0 -22
- package/dist/serve-H554BHLG.js +0 -124
- 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
|
@@ -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
|
+
};
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
t
|
|
4
|
+
} from "./chunk-PWLW3B57.js";
|
|
5
|
+
|
|
6
|
+
// src/commands/onboard-coverage.ts
|
|
7
|
+
import { existsSync, readdirSync, readFileSync } from "fs";
|
|
8
|
+
import { join, resolve } from "path";
|
|
9
|
+
import { defineCommand } from "citty";
|
|
10
|
+
import {
|
|
11
|
+
ONBOARD_SLOT_NAMES,
|
|
12
|
+
ONBOARD_SLOT_TOTAL
|
|
13
|
+
} from "@fenglimg/fabric-shared";
|
|
14
|
+
var KNOWLEDGE_TYPE_DIRS = [
|
|
15
|
+
"decisions",
|
|
16
|
+
"pitfalls",
|
|
17
|
+
"guidelines",
|
|
18
|
+
"models",
|
|
19
|
+
"processes"
|
|
20
|
+
];
|
|
21
|
+
var FABRIC_CONFIG_PATH = [".fabric", "fabric-config.json"];
|
|
22
|
+
function emptyFilled() {
|
|
23
|
+
const filled = {};
|
|
24
|
+
for (const slot of ONBOARD_SLOT_NAMES) {
|
|
25
|
+
filled[slot] = [];
|
|
26
|
+
}
|
|
27
|
+
return filled;
|
|
28
|
+
}
|
|
29
|
+
function readOnboardSlotFrontmatter(filePath) {
|
|
30
|
+
let content;
|
|
31
|
+
try {
|
|
32
|
+
content = readFileSync(filePath, "utf8");
|
|
33
|
+
} catch {
|
|
34
|
+
return void 0;
|
|
35
|
+
}
|
|
36
|
+
const match = /^---\n([\s\S]*?)\n---/u.exec(content);
|
|
37
|
+
if (match === null) return void 0;
|
|
38
|
+
const block = match[1];
|
|
39
|
+
if (block === void 0) return void 0;
|
|
40
|
+
for (const rawLine of block.split(/\r?\n/u)) {
|
|
41
|
+
const line = rawLine.trim();
|
|
42
|
+
const sep = line.indexOf(":");
|
|
43
|
+
if (sep === -1) continue;
|
|
44
|
+
const key = line.slice(0, sep).trim();
|
|
45
|
+
if (key !== "onboard_slot") continue;
|
|
46
|
+
let value = line.slice(sep + 1).trim();
|
|
47
|
+
if (value.startsWith('"') && value.endsWith('"') && value.length >= 2) {
|
|
48
|
+
value = value.slice(1, -1);
|
|
49
|
+
}
|
|
50
|
+
return value;
|
|
51
|
+
}
|
|
52
|
+
return void 0;
|
|
53
|
+
}
|
|
54
|
+
function readStableIdFrontmatter(filePath, fallbackName) {
|
|
55
|
+
let content;
|
|
56
|
+
try {
|
|
57
|
+
content = readFileSync(filePath, "utf8");
|
|
58
|
+
} catch {
|
|
59
|
+
return fallbackName.replace(/\.md$/u, "");
|
|
60
|
+
}
|
|
61
|
+
const match = /^---\n([\s\S]*?)\n---/u.exec(content);
|
|
62
|
+
if (match === null) return fallbackName.replace(/\.md$/u, "");
|
|
63
|
+
const block = match[1];
|
|
64
|
+
if (block === void 0) return fallbackName.replace(/\.md$/u, "");
|
|
65
|
+
for (const rawLine of block.split(/\r?\n/u)) {
|
|
66
|
+
const line = rawLine.trim();
|
|
67
|
+
const sep = line.indexOf(":");
|
|
68
|
+
if (sep === -1) continue;
|
|
69
|
+
if (line.slice(0, sep).trim() !== "id") continue;
|
|
70
|
+
return line.slice(sep + 1).trim();
|
|
71
|
+
}
|
|
72
|
+
return fallbackName.replace(/\.md$/u, "");
|
|
73
|
+
}
|
|
74
|
+
function readOptedOutSlots(projectRoot) {
|
|
75
|
+
const path = join(projectRoot, ...FABRIC_CONFIG_PATH);
|
|
76
|
+
if (!existsSync(path)) return [];
|
|
77
|
+
let raw;
|
|
78
|
+
try {
|
|
79
|
+
raw = readFileSync(path, "utf8");
|
|
80
|
+
} catch {
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
let parsed;
|
|
84
|
+
try {
|
|
85
|
+
parsed = JSON.parse(raw);
|
|
86
|
+
} catch (err) {
|
|
87
|
+
process.stderr.write(
|
|
88
|
+
`onboard-coverage: ignoring malformed fabric-config.json (${err instanceof Error ? err.message : String(err)})
|
|
89
|
+
`
|
|
90
|
+
);
|
|
91
|
+
return [];
|
|
92
|
+
}
|
|
93
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
94
|
+
return [];
|
|
95
|
+
}
|
|
96
|
+
const slots = parsed.onboard_slots_opted_out;
|
|
97
|
+
if (!Array.isArray(slots)) return [];
|
|
98
|
+
return slots.filter((v) => typeof v === "string");
|
|
99
|
+
}
|
|
100
|
+
function runOnboardCoverage(projectRoot) {
|
|
101
|
+
const filled = emptyFilled();
|
|
102
|
+
const knowledgeRoot = join(projectRoot, ".fabric", "knowledge");
|
|
103
|
+
if (existsSync(knowledgeRoot)) {
|
|
104
|
+
for (const typeDir of KNOWLEDGE_TYPE_DIRS) {
|
|
105
|
+
const dir = join(knowledgeRoot, typeDir);
|
|
106
|
+
if (!existsSync(dir)) continue;
|
|
107
|
+
let entries;
|
|
108
|
+
try {
|
|
109
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
110
|
+
} catch {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
for (const entry of entries) {
|
|
114
|
+
if (!entry.isFile()) continue;
|
|
115
|
+
if (!entry.name.endsWith(".md")) continue;
|
|
116
|
+
const filePath = join(dir, entry.name);
|
|
117
|
+
const slot = readOnboardSlotFrontmatter(filePath);
|
|
118
|
+
if (slot === void 0) continue;
|
|
119
|
+
if (!ONBOARD_SLOT_NAMES.includes(slot)) continue;
|
|
120
|
+
const stableId = readStableIdFrontmatter(filePath, entry.name);
|
|
121
|
+
filled[slot].push(stableId);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const optedOut = readOptedOutSlots(projectRoot);
|
|
126
|
+
const missing = ONBOARD_SLOT_NAMES.filter((slot) => {
|
|
127
|
+
if (filled[slot].length > 0) return false;
|
|
128
|
+
if (optedOut.includes(slot)) return false;
|
|
129
|
+
return true;
|
|
130
|
+
});
|
|
131
|
+
for (const slot of ONBOARD_SLOT_NAMES) {
|
|
132
|
+
filled[slot].sort();
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
filled,
|
|
136
|
+
missing,
|
|
137
|
+
opted_out: optedOut,
|
|
138
|
+
total: ONBOARD_SLOT_TOTAL
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
function renderHumanReadable(report) {
|
|
142
|
+
const filledCount = ONBOARD_SLOT_NAMES.filter(
|
|
143
|
+
(slot) => report.filled[slot].length > 0
|
|
144
|
+
).length;
|
|
145
|
+
const optedOutCount = report.opted_out.length;
|
|
146
|
+
const missingCount = report.missing.length;
|
|
147
|
+
const lines = [];
|
|
148
|
+
lines.push(
|
|
149
|
+
`Onboard coverage: ${filledCount}/${report.total} filled, ${optedOutCount} opted-out, ${missingCount} missing`
|
|
150
|
+
);
|
|
151
|
+
lines.push("");
|
|
152
|
+
lines.push(" slot status entries");
|
|
153
|
+
lines.push(" -------------------------- ----------- -------------------------");
|
|
154
|
+
for (const slot of ONBOARD_SLOT_NAMES) {
|
|
155
|
+
const entries = report.filled[slot];
|
|
156
|
+
let status;
|
|
157
|
+
let detail;
|
|
158
|
+
if (entries.length > 0) {
|
|
159
|
+
status = "filled";
|
|
160
|
+
detail = entries.join(", ");
|
|
161
|
+
} else if (report.opted_out.includes(slot)) {
|
|
162
|
+
status = "opted-out";
|
|
163
|
+
detail = "(user-dismissed; run `fabric config onboard-reset` to re-open)";
|
|
164
|
+
} else {
|
|
165
|
+
status = "missing";
|
|
166
|
+
detail = "(run /fabric-archive to onboard)";
|
|
167
|
+
}
|
|
168
|
+
lines.push(` ${slot.padEnd(26)} ${status.padEnd(11)} ${detail}`);
|
|
169
|
+
}
|
|
170
|
+
return lines.join("\n");
|
|
171
|
+
}
|
|
172
|
+
var onboardCoverageCommand = defineCommand({
|
|
173
|
+
meta: {
|
|
174
|
+
name: "onboard-coverage",
|
|
175
|
+
// v2.0.0-rc.29 TASK-008 (BUG-L2): route description strings through t()
|
|
176
|
+
// (mirrors serve.ts pattern). Previously this command was English-only
|
|
177
|
+
// even when the rest of `fabric --help` rendered zh-CN, so Chinese-locale
|
|
178
|
+
// users saw an isolated English block under --help.
|
|
179
|
+
description: t("cli.onboard-coverage.description"),
|
|
180
|
+
// Mirrors `plan-context-hint`: hidden from `fabric --help` so the top-level
|
|
181
|
+
// banner stays focused on install/doctor/serve/config. The command stays
|
|
182
|
+
// callable directly from Skills via `fabric onboard-coverage --json`.
|
|
183
|
+
hidden: true
|
|
184
|
+
},
|
|
185
|
+
args: {
|
|
186
|
+
json: {
|
|
187
|
+
type: "boolean",
|
|
188
|
+
description: t("cli.onboard-coverage.args.json.description"),
|
|
189
|
+
default: false
|
|
190
|
+
},
|
|
191
|
+
target: {
|
|
192
|
+
type: "string",
|
|
193
|
+
description: t("cli.onboard-coverage.args.target.description")
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
async run({ args }) {
|
|
197
|
+
try {
|
|
198
|
+
const projectRoot = resolve(args.target ?? process.cwd());
|
|
199
|
+
const report = runOnboardCoverage(projectRoot);
|
|
200
|
+
if (args.json === true) {
|
|
201
|
+
process.stdout.write(`${JSON.stringify(report)}
|
|
202
|
+
`);
|
|
203
|
+
} else {
|
|
204
|
+
process.stdout.write(`${renderHumanReadable(report)}
|
|
205
|
+
`);
|
|
206
|
+
}
|
|
207
|
+
} catch (error) {
|
|
208
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
209
|
+
process.stderr.write(`onboard-coverage failed: ${message}
|
|
210
|
+
`);
|
|
211
|
+
process.exitCode = 1;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
var onboard_coverage_default = onboardCoverageCommand;
|
|
216
|
+
export {
|
|
217
|
+
onboard_coverage_default as default,
|
|
218
|
+
onboardCoverageCommand,
|
|
219
|
+
runOnboardCoverage
|
|
220
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
resolveDevMode
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-COI5VDFU.js";
|
|
5
5
|
|
|
6
6
|
// src/commands/plan-context-hint.ts
|
|
7
7
|
import { defineCommand } from "citty";
|
|
@@ -10,7 +10,12 @@ var ALL_PATHS_SENTINEL = "**";
|
|
|
10
10
|
var planContextHintCommand = defineCommand({
|
|
11
11
|
meta: {
|
|
12
12
|
name: "plan-context-hint",
|
|
13
|
-
description: "Emit versioned knowledge hint JSON to stdout. Used by rc.6 hooks and the fabric-import skill."
|
|
13
|
+
description: "Emit versioned knowledge hint JSON to stdout. Used by rc.6 hooks and the fabric-import skill.",
|
|
14
|
+
// rc.15 TASK-004 (C8): hidden from `fabric --help` listing. The command stays
|
|
15
|
+
// callable so hook scripts and the fabric-import skill can still invoke
|
|
16
|
+
// it via `fabric plan-context-hint ...`; it just no longer appears in the
|
|
17
|
+
// top-level usage banner alongside install/doctor/serve/uninstall/config.
|
|
18
|
+
hidden: true
|
|
14
19
|
},
|
|
15
20
|
args: {
|
|
16
21
|
paths: {
|
|
@@ -53,27 +58,38 @@ async function runPlanContextHint(opts) {
|
|
|
53
58
|
const result = await planContext(resolution.target, {
|
|
54
59
|
paths: targetPaths
|
|
55
60
|
});
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
// Path mode: union of per-entry description_index across requested
|
|
59
|
-
// paths, deduped by stable_id. This is identical to `shared` for L0/L1
|
|
60
|
-
// entries (always included) and additionally captures L2 entries whose
|
|
61
|
-
// scope_glob matches the requested path.
|
|
62
|
-
dedupeByStableId(result.entries.flatMap((entry) => entry.description_index))
|
|
63
|
-
);
|
|
64
|
-
const narrow = narrowSource.map((item) => ({
|
|
61
|
+
const candidates = result.candidates;
|
|
62
|
+
const entries = candidates.map((item) => ({
|
|
65
63
|
id: item.stable_id,
|
|
66
|
-
type: item.
|
|
67
|
-
maturity: item.
|
|
68
|
-
summary: item.description.summary
|
|
64
|
+
type: item.description.knowledge_type ?? "",
|
|
65
|
+
maturity: item.description.maturity ?? "",
|
|
66
|
+
summary: item.description.summary,
|
|
67
|
+
relevance_scope: item.description.relevance_scope ?? "broad"
|
|
69
68
|
}));
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
let narrow_count = 0;
|
|
70
|
+
let broad_only_count = 0;
|
|
71
|
+
for (const e of entries) {
|
|
72
|
+
if (e.relevance_scope === "narrow") narrow_count += 1;
|
|
73
|
+
else broad_only_count += 1;
|
|
74
|
+
}
|
|
75
|
+
const output = {
|
|
76
|
+
version: 2,
|
|
72
77
|
revision_hash: result.revision_hash,
|
|
73
78
|
target_paths: targetPaths,
|
|
74
|
-
|
|
75
|
-
|
|
79
|
+
entries,
|
|
80
|
+
// Legacy field — preserved for v2 consumers that haven't migrated. Value
|
|
81
|
+
// semantics unchanged from rc.18 (total candidate count).
|
|
82
|
+
broad_count: candidates.length,
|
|
83
|
+
narrow_count,
|
|
84
|
+
broad_only_count
|
|
76
85
|
};
|
|
86
|
+
if (result.auto_healed === true) {
|
|
87
|
+
output.auto_healed = true;
|
|
88
|
+
if (typeof result.previous_revision_hash === "string") {
|
|
89
|
+
output.previous_revision_hash = result.previous_revision_hash;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return output;
|
|
77
93
|
}
|
|
78
94
|
function parsePathsArg(raw) {
|
|
79
95
|
if (raw === void 0 || raw.length === 0) {
|
|
@@ -81,16 +97,6 @@ function parsePathsArg(raw) {
|
|
|
81
97
|
}
|
|
82
98
|
return raw.split(",").map((part) => part.trim()).filter((part) => part.length > 0);
|
|
83
99
|
}
|
|
84
|
-
function dedupeByStableId(items) {
|
|
85
|
-
const seen = /* @__PURE__ */ new Set();
|
|
86
|
-
const result = [];
|
|
87
|
-
for (const item of items) {
|
|
88
|
-
if (seen.has(item.stable_id)) continue;
|
|
89
|
-
seen.add(item.stable_id);
|
|
90
|
-
result.push(item);
|
|
91
|
-
}
|
|
92
|
-
return result;
|
|
93
|
-
}
|
|
94
100
|
export {
|
|
95
101
|
plan_context_hint_default as default,
|
|
96
102
|
planContextHintCommand,
|