@fenglimg/fabric-cli 2.0.0-rc.33 → 2.0.0-rc.35

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.
Files changed (30) hide show
  1. package/README.md +1 -1
  2. package/dist/{chunk-SRX7WZUG.js → chunk-BATF4PEJ.js} +2 -2
  3. package/dist/{chunk-PNRWNUFX.js → chunk-XVS4F3P6.js} +105 -4
  4. package/dist/{config-5CH4EJQ2.js → config-XJIPZNUP.js} +1 -1
  5. package/dist/{doctor-E26YO67D.js → doctor-2FCRAWDZ.js} +23 -8
  6. package/dist/index.js +7 -7
  7. package/dist/{install-YSFVNY3T.js → install-HOTE5BPA.js} +61 -4
  8. package/dist/{onboard-coverage-6MN3CYHT.js → onboard-coverage-MFCAEBDO.js} +4 -4
  9. package/dist/{plan-context-hint-CXTLNVSV.js → plan-context-hint-UQLRKGBZ.js} +2 -2
  10. package/dist/{uninstall-VLLJG7JT.js → uninstall-BIJ5GLEU.js} +1 -1
  11. package/package.json +3 -4
  12. package/templates/hooks/cite-policy-evict.cjs +242 -0
  13. package/templates/hooks/configs/claude-code.json +11 -0
  14. package/templates/hooks/fabric-hint.cjs +11 -1
  15. package/templates/hooks/knowledge-hint-broad.cjs +34 -6
  16. package/templates/hooks/knowledge-hint-narrow.cjs +106 -1
  17. package/templates/hooks/lib/summary-fallback.cjs +210 -0
  18. package/templates/skills/fabric-archive/SKILL.md +38 -255
  19. package/templates/skills/fabric-archive/ref/dry-run-scope.md +16 -0
  20. package/templates/skills/fabric-archive/ref/e5-cron-recap.md +1 -1
  21. package/templates/skills/fabric-archive/ref/i18n-policy.md +1 -1
  22. package/templates/skills/fabric-archive/ref/phase-1-5-onboard.md +10 -10
  23. package/templates/skills/fabric-import/ref/i18n-policy.md +1 -1
  24. package/templates/skills/fabric-review/SKILL.md +55 -413
  25. package/templates/skills/fabric-review/ref/askuserquestion-policy.md +66 -0
  26. package/templates/skills/fabric-review/ref/i18n-policy.md +1 -1
  27. package/templates/skills/fabric-review/ref/modify-flow.md +95 -0
  28. package/templates/skills/fabric-review/ref/output-contract.md +58 -0
  29. package/templates/skills/fabric-review/ref/per-mode-flows.md +155 -0
  30. package/templates/skills/fabric-review/ref/semantic-check.md +26 -0
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @fenglimg/fabric-cli
2
2
 
3
- `fabric` 是 Fabric 的主命令,`fab` 是永久别名,两者等价。
3
+ `fabric` 是 Fabric CLI 主命令。
4
4
 
5
5
  ## 快速开始
6
6
 
@@ -107,7 +107,7 @@ var dismissSlotCmd = defineCommand({
107
107
  const next = [...optedOut, slot];
108
108
  const merged = { ...config, onboard_slots_opted_out: next };
109
109
  await atomicWriteJson(configPath, merged);
110
- console.log(`Dismissed onboard slot "${slot}". Run \`fab config onboard-reset ${slot}\` to re-open.`);
110
+ console.log(`Dismissed onboard slot "${slot}". Run \`fabric config onboard-reset ${slot}\` to re-open.`);
111
111
  } catch (err) {
112
112
  const message = err instanceof Error ? err.message : String(err);
113
113
  console.error(`dismiss-slot failed: ${message}`);
@@ -153,7 +153,7 @@ var onboardResetCmd = defineCommand({
153
153
  const next = optedOut.filter((s) => s !== slot);
154
154
  const merged = { ...config, onboard_slots_opted_out: next };
155
155
  await atomicWriteJson(configPath, merged);
156
- console.log(`Reset onboard slot "${slot}"; it will appear in \`fab onboard-coverage\` as missing again.`);
156
+ console.log(`Reset onboard slot "${slot}"; it will appear in \`fabric onboard-coverage\` as missing again.`);
157
157
  } catch (err) {
158
158
  const message = err instanceof Error ? err.message : String(err);
159
159
  console.error(`onboard-reset failed: ${message}`);
@@ -57,6 +57,7 @@ var SKILL_IMPORT_TEMPLATE_REL = "skills/fabric-import/SKILL.md";
57
57
  var HOOK_SCRIPT_TEMPLATE_REL = "hooks/fabric-hint.cjs";
58
58
  var HOOK_BROAD_SCRIPT_TEMPLATE_REL = "hooks/knowledge-hint-broad.cjs";
59
59
  var HOOK_NARROW_SCRIPT_TEMPLATE_REL = "hooks/knowledge-hint-narrow.cjs";
60
+ var HOOK_CITE_EVICT_SCRIPT_TEMPLATE_REL = "hooks/cite-policy-evict.cjs";
60
61
  var HOOK_LIB_TEMPLATE_DIR_REL = "hooks/lib";
61
62
  var CLAUDE_HOOK_CONFIG_TEMPLATE_REL = "hooks/configs/claude-code.json";
62
63
  var CODEX_HOOK_CONFIG_TEMPLATE_REL = "hooks/configs/codex-hooks.json";
@@ -75,6 +76,10 @@ var SKILL_DESTINATIONS = {
75
76
  ".codex/skills/fabric-import/SKILL.md"
76
77
  ]
77
78
  };
79
+ var DEPRECATED_SKILL_DIRS = [
80
+ ".claude/skills/fabric-init",
81
+ ".codex/skills/fabric-init"
82
+ ];
78
83
  var HOOK_SCRIPT_DESTINATIONS = {
79
84
  fabricHint: [
80
85
  ".claude/hooks/fabric-hint.cjs",
@@ -90,7 +95,12 @@ var HOOK_SCRIPT_DESTINATIONS = {
90
95
  ".claude/hooks/knowledge-hint-narrow.cjs",
91
96
  ".codex/hooks/knowledge-hint-narrow.cjs",
92
97
  ".cursor/hooks/knowledge-hint-narrow.cjs"
93
- ]
98
+ ],
99
+ // v2.0.0-rc.34 TASK-06: Claude Code only — UserPromptSubmit cite-policy
100
+ // long-session evict sidecar. Codex / Cursor don't have an equivalent
101
+ // event registration; cite-coverage telemetry there relies on the existing
102
+ // Stop / SessionStart hooks (knowledge-hint-broad rc.33 W2 channel).
103
+ citePolicyEvict: [".claude/hooks/cite-policy-evict.cjs"]
94
104
  };
95
105
  var HOOK_LIB_DESTINATIONS = [
96
106
  ".claude/hooks/lib",
@@ -141,36 +151,109 @@ function readFabricLanguagePreference(projectRoot) {
141
151
  return "match-existing";
142
152
  }
143
153
  }
154
+ var SKILL_TOKEN_ERROR_TOKENS = 1e4;
155
+ var STALE_INSTALL_RATIO = 1.5;
156
+ function estimateSkillTokens(text) {
157
+ return Math.ceil(text.length / 3);
158
+ }
159
+ function validateSkillCanonicalSize(source, slug) {
160
+ const tokens = estimateSkillTokens(source);
161
+ if (tokens > SKILL_TOKEN_ERROR_TOKENS) {
162
+ throw new Error(
163
+ `Skill '${slug}' canonical SKILL.md estimates ${tokens} tok (>${SKILL_TOKEN_ERROR_TOKENS} ERROR threshold). Install aborted \u2014 this is a Fabric release bug, not a user-recoverable state. Re-split SKILL.md via progressive disclosure (see fabric-archive/phases/* as canonical example) and rebuild.`
164
+ );
165
+ }
166
+ }
167
+ function inspectStaleInstall(target, source) {
168
+ if (!existsSync2(target)) return null;
169
+ let existing;
170
+ try {
171
+ existing = readFileSync2(target, "utf8");
172
+ } catch {
173
+ return null;
174
+ }
175
+ const existingTok = estimateSkillTokens(existing);
176
+ const sourceTok = estimateSkillTokens(source);
177
+ if (existingTok > sourceTok * STALE_INSTALL_RATIO) {
178
+ return `stale-replaced (${existingTok} tok \u2192 ${sourceTok} tok canonical)`;
179
+ }
180
+ return null;
181
+ }
144
182
  async function installFabricArchiveSkill(projectRoot, _options = {}) {
145
183
  const source = await readTemplate(SKILL_TEMPLATE_REL);
184
+ validateSkillCanonicalSize(source, "fabric-archive");
146
185
  const targets = SKILL_DESTINATIONS.fabricArchive.map((rel) => join2(projectRoot, rel));
147
186
  const results = [];
148
187
  for (const target of targets) {
149
- results.push(await copyTextIdempotent("skill", source, target));
188
+ const staleMsg = inspectStaleInstall(target, source);
189
+ const result = await copyTextIdempotent("skill", source, target);
190
+ if (staleMsg && result.status === "written") {
191
+ result.message = result.message ? `${staleMsg}; ${result.message}` : staleMsg;
192
+ }
193
+ results.push(result);
150
194
  }
151
195
  results.push(...await installSkillRefFiles(projectRoot, "fabric-archive"));
152
196
  return results;
153
197
  }
154
198
  async function installFabricReviewSkill(projectRoot, _options = {}) {
155
199
  const source = await readTemplate(SKILL_REVIEW_TEMPLATE_REL);
200
+ validateSkillCanonicalSize(source, "fabric-review");
156
201
  const targets = SKILL_DESTINATIONS.fabricReview.map((rel) => join2(projectRoot, rel));
157
202
  const results = [];
158
203
  for (const target of targets) {
159
- results.push(await copyTextIdempotent("skill-review", source, target));
204
+ const staleMsg = inspectStaleInstall(target, source);
205
+ const result = await copyTextIdempotent("skill-review", source, target);
206
+ if (staleMsg && result.status === "written") {
207
+ result.message = result.message ? `${staleMsg}; ${result.message}` : staleMsg;
208
+ }
209
+ results.push(result);
160
210
  }
161
211
  results.push(...await installSkillRefFiles(projectRoot, "fabric-review"));
162
212
  return results;
163
213
  }
164
214
  async function installFabricImportSkill(projectRoot, _options = {}) {
165
215
  const source = await readTemplate(SKILL_IMPORT_TEMPLATE_REL);
216
+ validateSkillCanonicalSize(source, "fabric-import");
166
217
  const targets = SKILL_DESTINATIONS.fabricImport.map((rel) => join2(projectRoot, rel));
167
218
  const results = [];
168
219
  for (const target of targets) {
169
- results.push(await copyTextIdempotent("skill-import", source, target));
220
+ const staleMsg = inspectStaleInstall(target, source);
221
+ const result = await copyTextIdempotent("skill-import", source, target);
222
+ if (staleMsg && result.status === "written") {
223
+ result.message = result.message ? `${staleMsg}; ${result.message}` : staleMsg;
224
+ }
225
+ results.push(result);
170
226
  }
171
227
  results.push(...await installSkillRefFiles(projectRoot, "fabric-import"));
172
228
  return results;
173
229
  }
230
+ async function cleanupDeprecatedSkills(projectRoot) {
231
+ const results = [];
232
+ for (const rel of DEPRECATED_SKILL_DIRS) {
233
+ const target = join2(projectRoot, rel);
234
+ if (!existsSync2(target)) {
235
+ results.push({ step: "skill-deprecated-cleanup", path: target, status: "skipped", message: "absent" });
236
+ continue;
237
+ }
238
+ try {
239
+ await rm(target, { recursive: true, force: true });
240
+ results.push({
241
+ step: "skill-deprecated-cleanup",
242
+ path: target,
243
+ status: "written",
244
+ message: "removed-deprecated"
245
+ });
246
+ } catch (error) {
247
+ results.push({
248
+ step: "skill-deprecated-cleanup",
249
+ path: target,
250
+ status: "error",
251
+ message: error instanceof Error ? error.message : String(error)
252
+ });
253
+ }
254
+ }
255
+ return results;
256
+ }
174
257
  async function installSkillRefFiles(projectRoot, skillSlug) {
175
258
  let refTemplateDir;
176
259
  try {
@@ -279,6 +362,22 @@ async function installKnowledgeHintNarrowHook(projectRoot, _options = {}) {
279
362
  }
280
363
  return results;
281
364
  }
365
+ async function installCitePolicyEvictHook(projectRoot, _options = {}) {
366
+ const source = await readTemplate(HOOK_CITE_EVICT_SCRIPT_TEMPLATE_REL);
367
+ const targets = HOOK_SCRIPT_DESTINATIONS.citePolicyEvict.map((rel) => join2(projectRoot, rel));
368
+ const results = [];
369
+ for (const target of targets) {
370
+ const result = await copyTextIdempotent("hook-cite-evict-script", source, target);
371
+ if (result.status === "written" && process.platform !== "win32") {
372
+ try {
373
+ chmodSync(target, 493);
374
+ } catch {
375
+ }
376
+ }
377
+ results.push(result);
378
+ }
379
+ return results;
380
+ }
282
381
  async function installHookLibs(projectRoot, _options = {}) {
283
382
  const libTemplateDir = findTemplatePath(HOOK_LIB_TEMPLATE_DIR_REL);
284
383
  let libFiles;
@@ -722,9 +821,11 @@ export {
722
821
  installFabricArchiveSkill,
723
822
  installFabricReviewSkill,
724
823
  installFabricImportSkill,
824
+ cleanupDeprecatedSkills,
725
825
  installArchiveHintHook,
726
826
  installKnowledgeHintBroadHook,
727
827
  installKnowledgeHintNarrowHook,
828
+ installCitePolicyEvictHook,
728
829
  installHookLibs,
729
830
  mergeClaudeCodeHookConfig,
730
831
  mergeCodexHookConfig,
@@ -3,7 +3,7 @@ import {
3
3
  configCmd,
4
4
  config_default,
5
5
  installMcpClients
6
- } from "./chunk-SRX7WZUG.js";
6
+ } from "./chunk-BATF4PEJ.js";
7
7
  import "./chunk-MF3OTILQ.js";
8
8
  import "./chunk-PWLW3B57.js";
9
9
  export {
@@ -72,6 +72,16 @@ var doctorCommand = defineCommand({
72
72
  description: t("cli.doctor.args.yes.description"),
73
73
  default: false
74
74
  },
75
+ // rc.35 TASK-12 (P0-11): expose maintainer-audience actionHints. By
76
+ // default the renderer folds remediation strings that target Fabric
77
+ // contributors (edit `packages/cli/templates/...`, interpret G1-G5
78
+ // cite-goodhart codes, etc.) since npm end users have no actionable
79
+ // lever for them. --verbose shows them.
80
+ verbose: {
81
+ type: "boolean",
82
+ description: t("cli.doctor.args.verbose.description"),
83
+ default: false
84
+ },
75
85
  // rc.20 TASK-05: cite policy adherence report (read-only). Skips standard
76
86
  // inspections entirely — different output surface. Mutually exclusive
77
87
  // with --fix / --fix-knowledge (enforced in run()).
@@ -284,7 +294,7 @@ var doctorCommand = defineCommand({
284
294
  } else if (fix && args["dry-run"] === true) {
285
295
  writeStdout(dt("cli.doctor.fix-dry-run-banner"));
286
296
  }
287
- renderHumanReport(report, dt);
297
+ renderHumanReport(report, dt, args.verbose === true);
288
298
  }
289
299
  await emitDoctorRunEventBestEffort(resolution.target, {
290
300
  mode: fixKnowledge ? "fix-knowledge" : "lint",
@@ -307,14 +317,15 @@ var doctorCommand = defineCommand({
307
317
  }
308
318
  });
309
319
  var doctor_default = doctorCommand;
310
- function renderHumanReport(report, dt) {
320
+ function renderHumanReport(report, dt, verbose) {
311
321
  writeStdout(`${renderStatus(report.status)} ${paint.ai("fabric doctor")} ${paint.human(report.summary.target)}`);
312
322
  for (const check of report.checks) {
313
323
  writeStdout(`${renderStatus(check.status)} ${check.name}: ${check.message}`);
314
324
  }
315
- writeIssueSection(dt("doctor.section.fixable"), report.fixable_errors);
316
- writeIssueSection(dt("doctor.section.manual"), report.manual_errors);
317
- writeIssueSection(dt("doctor.section.warnings"), report.warnings);
325
+ const opts = { verbose, dt };
326
+ writeIssueSection(dt("doctor.section.fixable"), report.fixable_errors, opts);
327
+ writeIssueSection(dt("doctor.section.manual"), report.manual_errors, opts);
328
+ writeIssueSection(dt("doctor.section.warnings"), report.warnings, opts);
318
329
  renderPayloadLimits(report, dt);
319
330
  }
320
331
  function renderPayloadLimits(report, dt) {
@@ -344,7 +355,7 @@ function renderFixKnowledgeMutations(fixKnowledgeReport, dt) {
344
355
  writeStdout(`${marker} ${mutation.kind}: ${mutation.path} [${mutation.detail}]${errSuffix}`);
345
356
  }
346
357
  }
347
- function writeIssueSection(title, issues) {
358
+ function writeIssueSection(title, issues, options) {
348
359
  if (issues.length === 0) {
349
360
  return;
350
361
  }
@@ -353,7 +364,11 @@ function writeIssueSection(title, issues) {
353
364
  for (const issue of issues) {
354
365
  writeStdout(`- ${issue.code}: ${issue.message}`);
355
366
  if (issue.actionHint !== void 0 && issue.actionHint.length > 0) {
356
- writeStdout(` \u2192 ${issue.actionHint}`);
367
+ if (issue.audience === "maintainer" && !options.verbose) {
368
+ writeStdout(` \u2192 ${options.dt("doctor.maintainer-hint-folded")}`);
369
+ } else {
370
+ writeStdout(` \u2192 ${issue.actionHint}`);
371
+ }
357
372
  }
358
373
  }
359
374
  }
@@ -600,7 +615,7 @@ function appendContractSection(lines, report, dt) {
600
615
  }
601
616
  }
602
617
  function renderEnrichDescriptionsReport(report, dt) {
603
- 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}`;
618
+ const header = `${symbol.ok} ${paint.ai("fabric doctor --enrich-descriptions")} mode=${report.mode}${report.dryRun ? " (dry-run)" : ""} scanned=${report.scanned} modified=${report.modified} skipped=${report.skipped}`;
604
619
  writeStdout(header);
605
620
  if (report.candidates.length === 0) {
606
621
  writeStdout(dt("doctor.enrich.allComplete"));
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-YSFVNY3T.js").then((module) => module.default),
15
- doctor: () => import("./doctor-E26YO67D.js").then((module) => module.default),
14
+ install: () => import("./install-HOTE5BPA.js").then((module) => module.default),
15
+ doctor: () => import("./doctor-2FCRAWDZ.js").then((module) => module.default),
16
16
  serve: () => import("./serve-43JTEM3U.js").then((module) => module.default),
17
- uninstall: () => import("./uninstall-VLLJG7JT.js").then((module) => module.default),
18
- config: () => import("./config-5CH4EJQ2.js").then((module) => module.default),
19
- "plan-context-hint": () => import("./plan-context-hint-CXTLNVSV.js").then((module) => module.default),
17
+ uninstall: () => import("./uninstall-BIJ5GLEU.js").then((module) => module.default),
18
+ config: () => import("./config-XJIPZNUP.js").then((module) => module.default),
19
+ "plan-context-hint": () => import("./plan-context-hint-UQLRKGBZ.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-6MN3CYHT.js").then((module) => module.default)
22
+ "onboard-coverage": () => import("./onboard-coverage-MFCAEBDO.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.33",
29
+ version: "2.0.0-rc.35",
30
30
  description: t("cli.main.description")
31
31
  },
32
32
  subCommands: allCommands
@@ -1,9 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  installMcpClients
4
- } from "./chunk-SRX7WZUG.js";
4
+ } from "./chunk-BATF4PEJ.js";
5
5
  import {
6
+ cleanupDeprecatedSkills,
6
7
  installArchiveHintHook,
8
+ installCitePolicyEvictHook,
7
9
  installFabricArchiveSkill,
8
10
  installFabricImportSkill,
9
11
  installFabricReviewSkill,
@@ -18,7 +20,7 @@ import {
18
20
  writeCodexBootstrapManagedBlock,
19
21
  writeCursorBootstrapManagedBlock,
20
22
  writeFabricAgentsSnapshot
21
- } from "./chunk-PNRWNUFX.js";
23
+ } from "./chunk-XVS4F3P6.js";
22
24
  import {
23
25
  detectClientSupports
24
26
  } from "./chunk-MF3OTILQ.js";
@@ -62,6 +64,7 @@ async function installHooks(target, _options = {}) {
62
64
  results.push(...await runStep(() => installArchiveHintHook(normalizedTarget)));
63
65
  results.push(...await runStep(() => installKnowledgeHintBroadHook(normalizedTarget)));
64
66
  results.push(...await runStep(() => installKnowledgeHintNarrowHook(normalizedTarget)));
67
+ results.push(...await runStep(() => installCitePolicyEvictHook(normalizedTarget)));
65
68
  results.push(...await runStep(() => installHookLibs(normalizedTarget)));
66
69
  results.push(await runSingleStep("claude-hook-config", () => mergeClaudeCodeHookConfig(normalizedTarget)));
67
70
  results.push(await runSingleStep("codex-hook-config", () => mergeCodexHookConfig(normalizedTarget)));
@@ -340,7 +343,7 @@ async function buildForensicReport(targetInput) {
340
343
  const report = {
341
344
  version: "1.0",
342
345
  generated_at: (/* @__PURE__ */ new Date()).toISOString(),
343
- generated_by: `fab-cli@${getCliVersion()}`,
346
+ generated_by: `fabric-cli@${getCliVersion()}`,
344
347
  target,
345
348
  project_name: readProjectName(target),
346
349
  framework,
@@ -1348,7 +1351,7 @@ function readProjectName(target) {
1348
1351
  return basename(target);
1349
1352
  }
1350
1353
  function getCliVersion() {
1351
- return true ? "2.0.0-rc.33" : "unknown";
1354
+ return true ? "2.0.0-rc.35" : "unknown";
1352
1355
  }
1353
1356
  function sortRecord(record) {
1354
1357
  return Object.fromEntries(Object.entries(record).sort(([left], [right]) => left.localeCompare(right)));
@@ -1385,6 +1388,11 @@ var installCommand = defineCommand({
1385
1388
  type: "boolean",
1386
1389
  description: t("cli.install.args.yes.description"),
1387
1390
  default: false
1391
+ },
1392
+ "force-skills-only": {
1393
+ type: "boolean",
1394
+ description: t("cli.install.args.force-skills-only.description"),
1395
+ default: false
1388
1396
  }
1389
1397
  },
1390
1398
  async run({ args }) {
@@ -1392,9 +1400,56 @@ var installCommand = defineCommand({
1392
1400
  }
1393
1401
  });
1394
1402
  var install_default = installCommand;
1403
+ async function runSkillsOnlyRefresh(targetInput) {
1404
+ const target = normalizeTarget3(targetInput);
1405
+ const metaPath = join4(target, ".fabric", "agents.meta.json");
1406
+ if (!existsSync4(metaPath)) {
1407
+ const message = t("cli.install.force-skills-only.uninitialised.message");
1408
+ const hint = t("cli.install.force-skills-only.uninitialised.hint");
1409
+ process.stderr.write(`${message}
1410
+ ${hint}
1411
+ `);
1412
+ process.exitCode = 1;
1413
+ return;
1414
+ }
1415
+ console.log(formatInitStageHeader(t("cli.install.force-skills-only.banner")));
1416
+ const results = [];
1417
+ results.push(...await cleanupDeprecatedSkills(target));
1418
+ results.push(...await installFabricArchiveSkill(target));
1419
+ results.push(...await installFabricReviewSkill(target));
1420
+ results.push(...await installFabricImportSkill(target));
1421
+ let written = 0;
1422
+ let skipped = 0;
1423
+ let errors = 0;
1424
+ for (const r of results) {
1425
+ if (r.status === "written") written += 1;
1426
+ else if (r.status === "skipped") skipped += 1;
1427
+ else if (r.status === "error") errors += 1;
1428
+ }
1429
+ console.log(
1430
+ t("cli.install.force-skills-only.summary", {
1431
+ written: String(written),
1432
+ skipped: String(skipped),
1433
+ errors: String(errors)
1434
+ })
1435
+ );
1436
+ if (errors > 0) {
1437
+ for (const r of results) {
1438
+ if (r.status === "error") {
1439
+ process.stderr.write(` ${r.step} ${r.path}: ${r.message ?? "error"}
1440
+ `);
1441
+ }
1442
+ }
1443
+ process.exitCode = 1;
1444
+ }
1445
+ }
1395
1446
  async function runInitCommand(args) {
1396
1447
  const logger = createDebugLogger(args.debug);
1397
1448
  const resolution = resolveDevMode(args.target, process.cwd());
1449
+ if (args["force-skills-only"] === true) {
1450
+ await runSkillsOnlyRefresh(resolution.target);
1451
+ return;
1452
+ }
1398
1453
  const intent = resolveInitCliIntent(args, resolution.target);
1399
1454
  const fabricInitialized = existsSync4(join4(intent.target, ".fabric", "events.jsonl"));
1400
1455
  if (fabricInitialized) {
@@ -1848,6 +1903,7 @@ async function executeInitStagePlan(plan, stageName) {
1848
1903
  switch (stage.name) {
1849
1904
  case "bootstrap": {
1850
1905
  const installResults = [];
1906
+ installResults.push(...await runBestEffort("skill-deprecated-cleanup", () => cleanupDeprecatedSkills(plan.target)));
1851
1907
  installResults.push(...await runBestEffort("skill-install", () => installFabricArchiveSkill(plan.target)));
1852
1908
  installResults.push(...await runBestEffort("skill-review-install", () => installFabricReviewSkill(plan.target)));
1853
1909
  installResults.push(...await runBestEffort("skill-import-install", () => installFabricImportSkill(plan.target)));
@@ -2400,5 +2456,6 @@ export {
2400
2456
  installCommand,
2401
2457
  resolveInitExecutionPlanWithWizard,
2402
2458
  runInitCommand,
2459
+ runSkillsOnlyRefresh,
2403
2460
  shouldUseInitWizard
2404
2461
  };
@@ -160,7 +160,7 @@ function renderHumanReadable(report) {
160
160
  detail = entries.join(", ");
161
161
  } else if (report.opted_out.includes(slot)) {
162
162
  status = "opted-out";
163
- detail = "(user-dismissed; run `fab config onboard-reset` to re-open)";
163
+ detail = "(user-dismissed; run `fabric config onboard-reset` to re-open)";
164
164
  } else {
165
165
  status = "missing";
166
166
  detail = "(run /fabric-archive to onboard)";
@@ -174,12 +174,12 @@ var onboardCoverageCommand = defineCommand({
174
174
  name: "onboard-coverage",
175
175
  // v2.0.0-rc.29 TASK-008 (BUG-L2): route description strings through t()
176
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
177
+ // even when the rest of `fabric --help` rendered zh-CN, so Chinese-locale
178
178
  // users saw an isolated English block under --help.
179
179
  description: t("cli.onboard-coverage.description"),
180
- // Mirrors `plan-context-hint`: hidden from `fab --help` so the top-level
180
+ // Mirrors `plan-context-hint`: hidden from `fabric --help` so the top-level
181
181
  // banner stays focused on install/doctor/serve/config. The command stays
182
- // callable directly from Skills via `fab onboard-coverage --json`.
182
+ // callable directly from Skills via `fabric onboard-coverage --json`.
183
183
  hidden: true
184
184
  },
185
185
  args: {
@@ -11,9 +11,9 @@ var planContextHintCommand = defineCommand({
11
11
  meta: {
12
12
  name: "plan-context-hint",
13
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 `fab --help` listing. The command stays
14
+ // rc.15 TASK-004 (C8): hidden from `fabric --help` listing. The command stays
15
15
  // callable so hook scripts and the fabric-import skill can still invoke
16
- // it via `fab plan-context-hint ...`; it just no longer appears in the
16
+ // it via `fabric plan-context-hint ...`; it just no longer appears in the
17
17
  // top-level usage banner alongside install/doctor/serve/uninstall/config.
18
18
  hidden: true
19
19
  },
@@ -7,7 +7,7 @@ import {
7
7
  HOOK_SCRIPT_DESTINATIONS,
8
8
  SKILL_DESTINATIONS,
9
9
  fabricAgentsSnapshotPath
10
- } from "./chunk-PNRWNUFX.js";
10
+ } from "./chunk-XVS4F3P6.js";
11
11
  import {
12
12
  detectClientSupports,
13
13
  resolveClients
package/package.json CHANGED
@@ -1,9 +1,8 @@
1
1
  {
2
2
  "name": "@fenglimg/fabric-cli",
3
- "version": "2.0.0-rc.33",
3
+ "version": "2.0.0-rc.35",
4
4
  "type": "module",
5
5
  "bin": {
6
- "fab": "dist/index.js",
7
6
  "fabric": "dist/index.js"
8
7
  },
9
8
  "main": "./dist/index.js",
@@ -20,8 +19,8 @@
20
19
  "tree-sitter-javascript": "^0.25.0",
21
20
  "tree-sitter-typescript": "^0.23.2",
22
21
  "web-tree-sitter": "^0.26.8",
23
- "@fenglimg/fabric-server": "2.0.0-rc.33",
24
- "@fenglimg/fabric-shared": "2.0.0-rc.33"
22
+ "@fenglimg/fabric-server": "2.0.0-rc.35",
23
+ "@fenglimg/fabric-shared": "2.0.0-rc.35"
25
24
  },
26
25
  "devDependencies": {
27
26
  "@types/node": "^22.15.0",