@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.
@@ -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-UJOFZUYF.js").then((module) => module.default),
15
- doctor: () => import("./doctor-ZIQXN2T2.js").then((module) => module.default),
16
- serve: () => import("./serve-U3TPWDOB.js").then((module) => module.default),
17
- uninstall: () => import("./uninstall-O3PXESM2.js").then((module) => module.default),
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-JJ5NGU7I.js").then((module) => module.default)
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.27",
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-XEGXQOOJ.js";
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.27" : "unknown";
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
- description: "Report S5 onboard-slot coverage for the workspace. Used by the fabric-archive Skill's first-run phase to detect unclaimed project-tone slots.",
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: "Emit machine-readable JSON to stdout instead of the human table.",
188
+ description: t("cli.onboard-coverage.args.json.description"),
182
189
  default: false
183
190
  },
184
191
  target: {
185
192
  type: "string",
186
- description: "Override the project root (defaults to cwd)."
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 host = validateHost(requestedHost, authToken);
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) {
@@ -7,7 +7,7 @@ import {
7
7
  HOOK_SCRIPT_DESTINATIONS,
8
8
  SKILL_DESTINATIONS,
9
9
  fabricAgentsSnapshotPath
10
- } from "./chunk-XEGXQOOJ.js";
10
+ } from "./chunk-PNRWNUFX.js";
11
11
  import {
12
12
  detectClientSupports,
13
13
  resolveClients
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fenglimg/fabric-cli",
3
- "version": "2.0.0-rc.27",
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.27",
24
- "@fenglimg/fabric-shared": "2.0.0-rc.27"
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 narrow entries > 30. The threshold
231
- // was originally aligned with the rc.5 plan-context degenerate-mode cutoff,
232
- // which is now retired (rc.7 T9 see docs/decisions/rc5-a3-superseded.md).
233
- // We keep 30 here as a stable rendering boundary independent of that protocol
234
- // change: it's a UI-density choice, not a wire-shape one.
235
- const TRUNCATION_THRESHOLD = 30;
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
  }