@fenglimg/fabric-cli 2.0.0-rc.27 → 2.0.0-rc.29
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-XEGXQOOJ.js → chunk-PNRWNUFX.js} +63 -0
- package/dist/{doctor-ZIQXN2T2.js → doctor-TTDTKOFJ.js} +25 -0
- package/dist/index.js +6 -6
- package/dist/{install-UJOFZUYF.js → install-ODEKSJDS.js} +2 -2
- package/dist/{onboard-coverage-JJ5NGU7I.js → onboard-coverage-6MN3CYHT.js} +10 -3
- package/dist/{serve-U3TPWDOB.js → serve-43JTEM3U.js} +16 -3
- package/dist/{uninstall-O3PXESM2.js → uninstall-VLLJG7JT.js} +1 -1
- package/package.json +3 -3
- package/templates/hooks/fabric-hint.cjs +14 -1
- package/templates/hooks/knowledge-hint-broad.cjs +10 -6
- package/templates/hooks/knowledge-hint-narrow.cjs +13 -1
- package/templates/skills/fabric-archive/SKILL.md +15 -410
- package/templates/skills/fabric-archive/ref/e5-cron-recap.md +58 -0
- package/templates/skills/fabric-archive/ref/i18n-policy.md +80 -0
- package/templates/skills/fabric-archive/ref/phase-0-4-onboard.md +218 -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 +7 -117
- package/templates/skills/fabric-import/ref/i18n-policy.md +73 -0
- package/templates/skills/fabric-import/ref/state-recovery.md +57 -0
- package/templates/skills/fabric-review/SKILL.md +6 -187
- package/templates/skills/fabric-review/ref/i18n-policy.md +105 -0
- package/templates/skills/fabric-review/ref/worked-examples.md +95 -0
- package/templates/hooks/archive-hint.cjs +0 -463
|
@@ -148,6 +148,7 @@ async function installFabricArchiveSkill(projectRoot, _options = {}) {
|
|
|
148
148
|
for (const target of targets) {
|
|
149
149
|
results.push(await copyTextIdempotent("skill", source, target));
|
|
150
150
|
}
|
|
151
|
+
results.push(...await installSkillRefFiles(projectRoot, "fabric-archive"));
|
|
151
152
|
return results;
|
|
152
153
|
}
|
|
153
154
|
async function installFabricReviewSkill(projectRoot, _options = {}) {
|
|
@@ -157,6 +158,7 @@ async function installFabricReviewSkill(projectRoot, _options = {}) {
|
|
|
157
158
|
for (const target of targets) {
|
|
158
159
|
results.push(await copyTextIdempotent("skill-review", source, target));
|
|
159
160
|
}
|
|
161
|
+
results.push(...await installSkillRefFiles(projectRoot, "fabric-review"));
|
|
160
162
|
return results;
|
|
161
163
|
}
|
|
162
164
|
async function installFabricImportSkill(projectRoot, _options = {}) {
|
|
@@ -166,6 +168,67 @@ async function installFabricImportSkill(projectRoot, _options = {}) {
|
|
|
166
168
|
for (const target of targets) {
|
|
167
169
|
results.push(await copyTextIdempotent("skill-import", source, target));
|
|
168
170
|
}
|
|
171
|
+
results.push(...await installSkillRefFiles(projectRoot, "fabric-import"));
|
|
172
|
+
return results;
|
|
173
|
+
}
|
|
174
|
+
async function installSkillRefFiles(projectRoot, skillSlug) {
|
|
175
|
+
let refTemplateDir;
|
|
176
|
+
try {
|
|
177
|
+
refTemplateDir = findTemplatePath(`skills/${skillSlug}/ref`);
|
|
178
|
+
} catch {
|
|
179
|
+
return [
|
|
180
|
+
{
|
|
181
|
+
step: "skill-ref",
|
|
182
|
+
path: `skills/${skillSlug}/ref`,
|
|
183
|
+
status: "skipped",
|
|
184
|
+
message: `no-ref-dir: ${skillSlug}`
|
|
185
|
+
}
|
|
186
|
+
];
|
|
187
|
+
}
|
|
188
|
+
let refFiles;
|
|
189
|
+
try {
|
|
190
|
+
refFiles = readdirSync(refTemplateDir).filter((name) => name.endsWith(".md"));
|
|
191
|
+
} catch {
|
|
192
|
+
return [
|
|
193
|
+
{
|
|
194
|
+
step: "skill-ref",
|
|
195
|
+
path: refTemplateDir,
|
|
196
|
+
status: "skipped",
|
|
197
|
+
message: `no-ref-files: ${skillSlug}`
|
|
198
|
+
}
|
|
199
|
+
];
|
|
200
|
+
}
|
|
201
|
+
if (refFiles.length === 0) {
|
|
202
|
+
return [
|
|
203
|
+
{
|
|
204
|
+
step: "skill-ref",
|
|
205
|
+
path: refTemplateDir,
|
|
206
|
+
status: "skipped",
|
|
207
|
+
message: `no-ref-files: ${skillSlug}`
|
|
208
|
+
}
|
|
209
|
+
];
|
|
210
|
+
}
|
|
211
|
+
const clientPrefixes = [".claude", ".codex"];
|
|
212
|
+
const results = [];
|
|
213
|
+
for (const refFile of refFiles) {
|
|
214
|
+
const sourcePath = join2(refTemplateDir, refFile);
|
|
215
|
+
let source;
|
|
216
|
+
try {
|
|
217
|
+
source = readFileSync2(sourcePath, "utf8");
|
|
218
|
+
} catch (error) {
|
|
219
|
+
results.push({
|
|
220
|
+
step: "skill-ref",
|
|
221
|
+
path: sourcePath,
|
|
222
|
+
status: "error",
|
|
223
|
+
message: error instanceof Error ? error.message : String(error)
|
|
224
|
+
});
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
for (const prefix of clientPrefixes) {
|
|
228
|
+
const target = join2(projectRoot, prefix, "skills", skillSlug, "ref", refFile);
|
|
229
|
+
results.push(await copyTextIdempotent("skill-ref", source, target));
|
|
230
|
+
}
|
|
231
|
+
}
|
|
169
232
|
return results;
|
|
170
233
|
}
|
|
171
234
|
async function installArchiveHintHook(projectRoot, _options = {}) {
|
|
@@ -145,6 +145,15 @@ var doctorCommand = defineCommand({
|
|
|
145
145
|
const citeCoverage = args["cite-coverage"] === true;
|
|
146
146
|
const enrichDesc = args["enrich-descriptions"] === true;
|
|
147
147
|
const archiveHistory = args["archive-history"] === true;
|
|
148
|
+
if (args.since !== void 0) {
|
|
149
|
+
try {
|
|
150
|
+
parseSinceDuration(args.since);
|
|
151
|
+
} catch {
|
|
152
|
+
writeStderr(dt("cli.doctor.errors.invalid-since", { input: args.since }));
|
|
153
|
+
process.exitCode = 1;
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
148
157
|
if (archiveHistory) {
|
|
149
158
|
if (fix || fixKnowledge || citeCoverage || enrichDesc) {
|
|
150
159
|
writeStderr(dt("cli.doctor.errors.archive-history-mutex"));
|
|
@@ -300,6 +309,22 @@ function renderHumanReport(report, dt) {
|
|
|
300
309
|
writeIssueSection(dt("doctor.section.fixable"), report.fixable_errors);
|
|
301
310
|
writeIssueSection(dt("doctor.section.manual"), report.manual_errors);
|
|
302
311
|
writeIssueSection(dt("doctor.section.warnings"), report.warnings);
|
|
312
|
+
renderPayloadLimits(report, dt);
|
|
313
|
+
}
|
|
314
|
+
function renderPayloadLimits(report, dt) {
|
|
315
|
+
const limits = report.summary.payload_limits;
|
|
316
|
+
if (limits === void 0) {
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
writeStdout("");
|
|
320
|
+
writeStdout(dt("doctor.section.payload-limits"));
|
|
321
|
+
writeStdout(
|
|
322
|
+
`- ${dt("doctor.payload-limits.line", {
|
|
323
|
+
warnKb: String(Math.round(limits.warn_bytes / 1024)),
|
|
324
|
+
hardKb: String(Math.round(limits.hard_bytes / 1024)),
|
|
325
|
+
source: limits.source
|
|
326
|
+
})}`
|
|
327
|
+
);
|
|
303
328
|
}
|
|
304
329
|
function renderFixKnowledgeMutations(fixKnowledgeReport, dt) {
|
|
305
330
|
if (fixKnowledgeReport.mutations.length === 0) {
|
package/dist/index.js
CHANGED
|
@@ -11,22 +11,22 @@ 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
|
-
serve: () => import("./serve-
|
|
17
|
-
uninstall: () => import("./uninstall-
|
|
14
|
+
install: () => import("./install-ODEKSJDS.js").then((module) => module.default),
|
|
15
|
+
doctor: () => import("./doctor-TTDTKOFJ.js").then((module) => module.default),
|
|
16
|
+
serve: () => import("./serve-43JTEM3U.js").then((module) => module.default),
|
|
17
|
+
uninstall: () => import("./uninstall-VLLJG7JT.js").then((module) => module.default),
|
|
18
18
|
config: () => import("./config-5CH4EJQ2.js").then((module) => module.default),
|
|
19
19
|
"plan-context-hint": () => import("./plan-context-hint-CXTLNVSV.js").then((module) => module.default),
|
|
20
20
|
// v2.0.0-rc.23 TASK-014 (F8c): S5 onboard-slot coverage. Used by the
|
|
21
21
|
// fabric-archive Skill's first-run phase to detect unclaimed slots.
|
|
22
|
-
"onboard-coverage": () => import("./onboard-coverage-
|
|
22
|
+
"onboard-coverage": () => import("./onboard-coverage-6MN3CYHT.js").then((module) => module.default)
|
|
23
23
|
};
|
|
24
24
|
|
|
25
25
|
// src/index.ts
|
|
26
26
|
var main = defineCommand({
|
|
27
27
|
meta: {
|
|
28
28
|
name: "fabric",
|
|
29
|
-
version: "2.0.0-rc.
|
|
29
|
+
version: "2.0.0-rc.29",
|
|
30
30
|
description: t("cli.main.description")
|
|
31
31
|
},
|
|
32
32
|
subCommands: allCommands
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
writeCodexBootstrapManagedBlock,
|
|
19
19
|
writeCursorBootstrapManagedBlock,
|
|
20
20
|
writeFabricAgentsSnapshot
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-PNRWNUFX.js";
|
|
22
22
|
import {
|
|
23
23
|
detectClientSupports
|
|
24
24
|
} from "./chunk-MF3OTILQ.js";
|
|
@@ -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.29" : "unknown";
|
|
1352
1352
|
}
|
|
1353
1353
|
function sortRecord(record) {
|
|
1354
1354
|
return Object.fromEntries(Object.entries(record).sort(([left], [right]) => left.localeCompare(right)));
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
t
|
|
4
|
+
} from "./chunk-PWLW3B57.js";
|
|
2
5
|
|
|
3
6
|
// src/commands/onboard-coverage.ts
|
|
4
7
|
import { existsSync, readdirSync, readFileSync } from "fs";
|
|
@@ -169,7 +172,11 @@ function renderHumanReadable(report) {
|
|
|
169
172
|
var onboardCoverageCommand = defineCommand({
|
|
170
173
|
meta: {
|
|
171
174
|
name: "onboard-coverage",
|
|
172
|
-
|
|
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 `fab --help` rendered zh-CN, so Chinese-locale
|
|
178
|
+
// users saw an isolated English block under --help.
|
|
179
|
+
description: t("cli.onboard-coverage.description"),
|
|
173
180
|
// Mirrors `plan-context-hint`: hidden from `fab --help` so the top-level
|
|
174
181
|
// banner stays focused on install/doctor/serve/config. The command stays
|
|
175
182
|
// callable directly from Skills via `fab onboard-coverage --json`.
|
|
@@ -178,12 +185,12 @@ var onboardCoverageCommand = defineCommand({
|
|
|
178
185
|
args: {
|
|
179
186
|
json: {
|
|
180
187
|
type: "boolean",
|
|
181
|
-
description: "
|
|
188
|
+
description: t("cli.onboard-coverage.args.json.description"),
|
|
182
189
|
default: false
|
|
183
190
|
},
|
|
184
191
|
target: {
|
|
185
192
|
type: "string",
|
|
186
|
-
description: "
|
|
193
|
+
description: t("cli.onboard-coverage.args.target.description")
|
|
187
194
|
}
|
|
188
195
|
},
|
|
189
196
|
async run({ args }) {
|
|
@@ -41,6 +41,12 @@ var serveCommand = defineCommand({
|
|
|
41
41
|
type: "boolean",
|
|
42
42
|
description: t("cli.serve.args.debug.description"),
|
|
43
43
|
default: false
|
|
44
|
+
},
|
|
45
|
+
// v2.0.0-rc.29 TASK-002 (BUG-K1): default-deny strict-auth.
|
|
46
|
+
"allow-loopback-no-auth": {
|
|
47
|
+
type: "boolean",
|
|
48
|
+
description: t("cli.serve.args.allow-loopback-no-auth.description"),
|
|
49
|
+
default: false
|
|
44
50
|
}
|
|
45
51
|
},
|
|
46
52
|
async run({ args }) {
|
|
@@ -50,7 +56,8 @@ var serveCommand = defineCommand({
|
|
|
50
56
|
const port = parsePort(args.port);
|
|
51
57
|
const requestedHost = parseHost(args.host);
|
|
52
58
|
const authToken = readAuthTokenFromEnv();
|
|
53
|
-
const
|
|
59
|
+
const allowLoopbackNoAuth = args["allow-loopback-no-auth"] === true;
|
|
60
|
+
const host = validateHost(requestedHost, authToken, allowLoopbackNoAuth);
|
|
54
61
|
const projectRoot = resolution.target;
|
|
55
62
|
try {
|
|
56
63
|
acquireLock(projectRoot);
|
|
@@ -73,7 +80,8 @@ var serveCommand = defineCommand({
|
|
|
73
80
|
port,
|
|
74
81
|
projectRoot,
|
|
75
82
|
host,
|
|
76
|
-
authToken
|
|
83
|
+
authToken,
|
|
84
|
+
allowLoopbackNoAuth
|
|
77
85
|
});
|
|
78
86
|
} catch (error) {
|
|
79
87
|
if (isNodeError(error) && error.code === "EADDRINUSE") {
|
|
@@ -105,7 +113,7 @@ function readAuthTokenFromEnv() {
|
|
|
105
113
|
const token = process.env.FABRIC_AUTH_TOKEN;
|
|
106
114
|
return token === void 0 || token.length === 0 ? void 0 : token;
|
|
107
115
|
}
|
|
108
|
-
function validateHost(host, authToken) {
|
|
116
|
+
function validateHost(host, authToken, allowLoopbackNoAuth) {
|
|
109
117
|
if (authToken !== void 0) {
|
|
110
118
|
return host;
|
|
111
119
|
}
|
|
@@ -115,6 +123,11 @@ function validateHost(host, authToken) {
|
|
|
115
123
|
);
|
|
116
124
|
return "127.0.0.1";
|
|
117
125
|
}
|
|
126
|
+
if (!allowLoopbackNoAuth) {
|
|
127
|
+
console.error(
|
|
128
|
+
`${symbol.warn} ${paint.warn(t("cli.serve.warning.loopback-deny-default"))}`
|
|
129
|
+
);
|
|
130
|
+
}
|
|
118
131
|
return host;
|
|
119
132
|
}
|
|
120
133
|
function isLoopbackHost(host) {
|
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.29",
|
|
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.29",
|
|
24
|
+
"@fenglimg/fabric-shared": "2.0.0-rc.29"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@types/node": "^22.15.0",
|
|
@@ -1079,7 +1079,20 @@ function tryReadStdinJson() {
|
|
|
1079
1079
|
const parsed = JSON.parse(buf);
|
|
1080
1080
|
if (parsed === null || typeof parsed !== "object") return null;
|
|
1081
1081
|
return parsed;
|
|
1082
|
-
} catch {
|
|
1082
|
+
} catch (e) {
|
|
1083
|
+
// v2.0.0-rc.29 TASK-008 (BUG-L1): hook used to silent-swallow JSON.parse
|
|
1084
|
+
// errors which masked real client-side payload bugs (e.g. CLI hosts that
|
|
1085
|
+
// stopped emitting Stop-hook JSON envelopes). Log a single best-effort
|
|
1086
|
+
// diagnostic line so operators see WHY the hook went quiet; keep returning
|
|
1087
|
+
// null so downstream behaviour (graceful exit 0, no rule render) is
|
|
1088
|
+
// unchanged.
|
|
1089
|
+
try {
|
|
1090
|
+
const message = (e && typeof e === "object" && "message" in e) ? String(e.message) : String(e);
|
|
1091
|
+
process.stderr.write(`[fabric-hint] malformed input: ${message}\n`);
|
|
1092
|
+
} catch {
|
|
1093
|
+
// stderr write failed (very unusual — sandbox / closed fd). The
|
|
1094
|
+
// hook contract still requires we never throw upward.
|
|
1095
|
+
}
|
|
1083
1096
|
return null;
|
|
1084
1097
|
}
|
|
1085
1098
|
}
|
|
@@ -227,12 +227,16 @@ function shouldRecommendImport(projectRoot) {
|
|
|
227
227
|
// CONSTANTS
|
|
228
228
|
// -----------------------------------------------------------------------------
|
|
229
229
|
|
|
230
|
-
// Per-type truncation triggers when total
|
|
231
|
-
//
|
|
232
|
-
//
|
|
233
|
-
//
|
|
234
|
-
//
|
|
235
|
-
|
|
230
|
+
// Per-type truncation triggers when total broad-scope entries > N.
|
|
231
|
+
// v2.0.0-rc.29 TASK-007 (BUG-F1): lowered from 30 → 12. SessionStart hint
|
|
232
|
+
// should bias toward "is there anything relevant?" rather than "exhaustive
|
|
233
|
+
// index" — at 30, the banner consumed several terminal screens on
|
|
234
|
+
// well-seeded repos and operators reported scroll fatigue. 12 keeps a
|
|
235
|
+
// dense-enough scan (still fits "top hits per type" in 1-2 screenfuls)
|
|
236
|
+
// without prompting the user to mentally truncate themselves. The constant
|
|
237
|
+
// stays a stable rendering boundary; downstream consumers (banner-i18n.cjs,
|
|
238
|
+
// truncation summary lines) consume it as a single source of truth.
|
|
239
|
+
const TRUNCATION_THRESHOLD = 12;
|
|
236
240
|
|
|
237
241
|
// `fabric plan-context-hint` is a thin wrapper over planContext(); on a
|
|
238
242
|
// well-seeded repo it returns in ~100ms. Two-second cap is defensive — any
|
|
@@ -142,7 +142,19 @@ function readPayload(rawStdin) {
|
|
|
142
142
|
return null;
|
|
143
143
|
}
|
|
144
144
|
return parsed;
|
|
145
|
-
} catch {
|
|
145
|
+
} catch (e) {
|
|
146
|
+
// v2.0.0-rc.29 REVIEW (codex LOW-1): apply BUG-L1's malformed-input
|
|
147
|
+
// diagnostic uniformly across hook scripts. fabric-hint.cjs got the stderr
|
|
148
|
+
// trace in TASK-008; without this matching write here, a broken Codex /
|
|
149
|
+
// Cursor host payload silently kills the narrow hint with no operator
|
|
150
|
+
// signal at all. Best-effort: a failed stderr write must not throw upward
|
|
151
|
+
// (hook contract — never crash the host's edit pipeline).
|
|
152
|
+
try {
|
|
153
|
+
const message = (e && typeof e === "object" && "message" in e) ? String(e.message) : String(e);
|
|
154
|
+
process.stderr.write(`[fabric-knowledge-hint-narrow] malformed input: ${message}\n`);
|
|
155
|
+
} catch {
|
|
156
|
+
// stderr write itself failed (sandbox / closed fd) — accept silence.
|
|
157
|
+
}
|
|
146
158
|
return null;
|
|
147
159
|
}
|
|
148
160
|
}
|