@fenglimg/fabric-cli 2.0.0-rc.11 → 2.0.0-rc.15

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.
@@ -0,0 +1,366 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ deepMerge
4
+ } from "./chunk-SKSYUHKK.js";
5
+
6
+ // src/install/skills-and-hooks.ts
7
+ import { chmodSync, existsSync, readFileSync } from "fs";
8
+ import { mkdir, readFile } from "fs/promises";
9
+ import { dirname, join, parse, resolve } from "path";
10
+ import { fileURLToPath } from "url";
11
+ import { atomicWriteJson, atomicWriteText } from "@fenglimg/fabric-shared/node/atomic-write";
12
+ var SKILL_TEMPLATE_REL = "skills/fabric-archive/SKILL.md";
13
+ var SKILL_REVIEW_TEMPLATE_REL = "skills/fabric-review/SKILL.md";
14
+ var SKILL_IMPORT_TEMPLATE_REL = "skills/fabric-import/SKILL.md";
15
+ var HOOK_SCRIPT_TEMPLATE_REL = "hooks/fabric-hint.cjs";
16
+ var HOOK_BROAD_SCRIPT_TEMPLATE_REL = "hooks/knowledge-hint-broad.cjs";
17
+ var HOOK_NARROW_SCRIPT_TEMPLATE_REL = "hooks/knowledge-hint-narrow.cjs";
18
+ var CLAUDE_HOOK_CONFIG_TEMPLATE_REL = "hooks/configs/claude-code.json";
19
+ var CODEX_HOOK_CONFIG_TEMPLATE_REL = "hooks/configs/codex-hooks.json";
20
+ var CURSOR_HOOK_CONFIG_TEMPLATE_REL = "hooks/configs/cursor-hooks.json";
21
+ var SKILL_DESTINATIONS = {
22
+ fabricArchive: [
23
+ ".claude/skills/fabric-archive/SKILL.md",
24
+ ".codex/skills/fabric-archive/SKILL.md"
25
+ ],
26
+ fabricReview: [
27
+ ".claude/skills/fabric-review/SKILL.md",
28
+ ".codex/skills/fabric-review/SKILL.md"
29
+ ],
30
+ fabricImport: [
31
+ ".claude/skills/fabric-import/SKILL.md",
32
+ ".codex/skills/fabric-import/SKILL.md"
33
+ ]
34
+ };
35
+ var HOOK_SCRIPT_DESTINATIONS = {
36
+ fabricHint: [
37
+ ".claude/hooks/fabric-hint.cjs",
38
+ ".codex/hooks/fabric-hint.cjs",
39
+ ".cursor/hooks/fabric-hint.cjs"
40
+ ],
41
+ knowledgeHintBroad: [
42
+ ".claude/hooks/knowledge-hint-broad.cjs",
43
+ ".codex/hooks/knowledge-hint-broad.cjs",
44
+ ".cursor/hooks/knowledge-hint-broad.cjs"
45
+ ],
46
+ knowledgeHintNarrow: [
47
+ ".claude/hooks/knowledge-hint-narrow.cjs",
48
+ ".codex/hooks/knowledge-hint-narrow.cjs",
49
+ ".cursor/hooks/knowledge-hint-narrow.cjs"
50
+ ]
51
+ };
52
+ var HOOK_CONFIG_TARGETS = {
53
+ claudeCode: ".claude/settings.json",
54
+ codex: ".codex/hooks.json",
55
+ cursor: ".cursor/hooks.json"
56
+ };
57
+ var HOOK_CONFIG_ARRAY_PATHS = {
58
+ claudeCode: ["hooks.Stop", "hooks.SessionStart", "hooks.PreToolUse"],
59
+ codex: ["events.Stop", "events.SessionStart", "events.PreToolUse"],
60
+ cursor: ["hooks.stop", "hooks.sessionStart", "hooks.preToolUse"]
61
+ };
62
+ var FABRIC_HOOK_COMMAND_PATHS = {
63
+ claudeCode: {
64
+ fabricHint: ".claude/hooks/fabric-hint.cjs",
65
+ knowledgeHintBroad: ".claude/hooks/knowledge-hint-broad.cjs",
66
+ knowledgeHintNarrow: ".claude/hooks/knowledge-hint-narrow.cjs"
67
+ },
68
+ codex: {
69
+ fabricHint: ".codex/hooks/fabric-hint.cjs",
70
+ knowledgeHintBroad: ".codex/hooks/knowledge-hint-broad.cjs",
71
+ knowledgeHintNarrow: ".codex/hooks/knowledge-hint-narrow.cjs"
72
+ },
73
+ cursor: {
74
+ fabricHint: ".cursor/hooks/fabric-hint.cjs",
75
+ knowledgeHintBroad: ".cursor/hooks/knowledge-hint-broad.cjs",
76
+ knowledgeHintNarrow: ".cursor/hooks/knowledge-hint-narrow.cjs"
77
+ }
78
+ };
79
+ var SECTION_TARGETS = ["CLAUDE.md", "AGENTS.md", join(".cursor", "rules")];
80
+ var FABRIC_SECTION_BEGIN_MARKER = "<!-- fabric:knowledge-base:begin -->";
81
+ var FABRIC_SECTION_END_MARKER = "<!-- fabric:knowledge-base:end -->";
82
+ var FABRIC_SECTION_REGEX = /(?:\r?\n){0,2}<!-- fabric:knowledge-base:begin -->[\s\S]*?<!-- fabric:knowledge-base:end -->/;
83
+ function readFabricLanguagePreference(projectRoot) {
84
+ const configPath = join(projectRoot, ".fabric", "fabric-config.json");
85
+ if (!existsSync(configPath)) {
86
+ return "match-existing";
87
+ }
88
+ try {
89
+ const raw = readFileSync(configPath, "utf8");
90
+ const parsed = JSON.parse(raw);
91
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
92
+ return "match-existing";
93
+ }
94
+ const value = parsed["fabric_language"];
95
+ return typeof value === "string" && value.length > 0 ? value : "match-existing";
96
+ } catch {
97
+ return "match-existing";
98
+ }
99
+ }
100
+ function buildFabricKnowledgeBaseSection(fabricLanguage) {
101
+ return `${FABRIC_SECTION_BEGIN_MARKER}
102
+
103
+ ## Fabric Knowledge Base
104
+
105
+ This project uses Fabric for persistent project knowledge under \`.fabric/knowledge/\`.
106
+
107
+ - **Discovery**: SessionStart lists available entries (broad menu); editing files may surface narrow hints
108
+ - **Usage**: call \`fab_get_knowledge_sections\` to fetch full content of any entry by id
109
+ - **Write flows**: see fabric-archive (record), fabric-review (validate), fabric-import (backfill) Skills
110
+ - **Language**: rendered per \`fabric_language\` in \`.fabric/fabric-config.json\` (current: \`${fabricLanguage}\`)
111
+
112
+ ${FABRIC_SECTION_END_MARKER}`;
113
+ }
114
+ async function installFabricArchiveSkill(projectRoot, _options = {}) {
115
+ const source = await readTemplate(SKILL_TEMPLATE_REL);
116
+ const targets = SKILL_DESTINATIONS.fabricArchive.map((rel) => join(projectRoot, rel));
117
+ const results = [];
118
+ for (const target of targets) {
119
+ results.push(await copyTextIdempotent("skill", source, target));
120
+ }
121
+ return results;
122
+ }
123
+ async function installFabricReviewSkill(projectRoot, _options = {}) {
124
+ const source = await readTemplate(SKILL_REVIEW_TEMPLATE_REL);
125
+ const targets = SKILL_DESTINATIONS.fabricReview.map((rel) => join(projectRoot, rel));
126
+ const results = [];
127
+ for (const target of targets) {
128
+ results.push(await copyTextIdempotent("skill-review", source, target));
129
+ }
130
+ return results;
131
+ }
132
+ async function installFabricImportSkill(projectRoot, _options = {}) {
133
+ const source = await readTemplate(SKILL_IMPORT_TEMPLATE_REL);
134
+ const targets = SKILL_DESTINATIONS.fabricImport.map((rel) => join(projectRoot, rel));
135
+ const results = [];
136
+ for (const target of targets) {
137
+ results.push(await copyTextIdempotent("skill-import", source, target));
138
+ }
139
+ return results;
140
+ }
141
+ async function installArchiveHintHook(projectRoot, _options = {}) {
142
+ const source = await readTemplate(HOOK_SCRIPT_TEMPLATE_REL);
143
+ const targets = HOOK_SCRIPT_DESTINATIONS.fabricHint.map((rel) => join(projectRoot, rel));
144
+ const results = [];
145
+ for (const target of targets) {
146
+ const result = await copyTextIdempotent("hook-script", source, target);
147
+ if (result.status === "written" && process.platform !== "win32") {
148
+ try {
149
+ chmodSync(target, 493);
150
+ } catch {
151
+ }
152
+ }
153
+ results.push(result);
154
+ }
155
+ return results;
156
+ }
157
+ async function installKnowledgeHintBroadHook(projectRoot, _options = {}) {
158
+ const source = await readTemplate(HOOK_BROAD_SCRIPT_TEMPLATE_REL);
159
+ const targets = HOOK_SCRIPT_DESTINATIONS.knowledgeHintBroad.map((rel) => join(projectRoot, rel));
160
+ const results = [];
161
+ for (const target of targets) {
162
+ const result = await copyTextIdempotent("hook-broad-script", source, target);
163
+ if (result.status === "written" && process.platform !== "win32") {
164
+ try {
165
+ chmodSync(target, 493);
166
+ } catch {
167
+ }
168
+ }
169
+ results.push(result);
170
+ }
171
+ return results;
172
+ }
173
+ async function installKnowledgeHintNarrowHook(projectRoot, _options = {}) {
174
+ const source = await readTemplate(HOOK_NARROW_SCRIPT_TEMPLATE_REL);
175
+ const targets = HOOK_SCRIPT_DESTINATIONS.knowledgeHintNarrow.map((rel) => join(projectRoot, rel));
176
+ const results = [];
177
+ for (const target of targets) {
178
+ const result = await copyTextIdempotent("hook-narrow-script", source, target);
179
+ if (result.status === "written" && process.platform !== "win32") {
180
+ try {
181
+ chmodSync(target, 493);
182
+ } catch {
183
+ }
184
+ }
185
+ results.push(result);
186
+ }
187
+ return results;
188
+ }
189
+ async function mergeClaudeCodeHookConfig(projectRoot, _options = {}) {
190
+ const fragment = await readJsonTemplate(CLAUDE_HOOK_CONFIG_TEMPLATE_REL);
191
+ const targetPath = join(projectRoot, HOOK_CONFIG_TARGETS.claudeCode);
192
+ return mergeJsonIdempotent(
193
+ "claude-hook-config",
194
+ targetPath,
195
+ fragment,
196
+ [...HOOK_CONFIG_ARRAY_PATHS.claudeCode]
197
+ );
198
+ }
199
+ async function mergeCodexHookConfig(projectRoot, _options = {}) {
200
+ const fragment = await readJsonTemplate(CODEX_HOOK_CONFIG_TEMPLATE_REL);
201
+ const targetPath = join(projectRoot, HOOK_CONFIG_TARGETS.codex);
202
+ return mergeJsonIdempotent(
203
+ "codex-hook-config",
204
+ targetPath,
205
+ fragment,
206
+ [...HOOK_CONFIG_ARRAY_PATHS.codex]
207
+ );
208
+ }
209
+ async function mergeCursorHookConfig(projectRoot, _options = {}) {
210
+ const fragment = await readJsonTemplate(CURSOR_HOOK_CONFIG_TEMPLATE_REL);
211
+ const targetPath = join(projectRoot, HOOK_CONFIG_TARGETS.cursor);
212
+ return mergeJsonIdempotent(
213
+ "cursor-hook-config",
214
+ targetPath,
215
+ fragment,
216
+ [...HOOK_CONFIG_ARRAY_PATHS.cursor]
217
+ );
218
+ }
219
+ async function addFabricKnowledgeBaseSection(projectRoot, fabricLanguage, _options = {}) {
220
+ const sectionBody = buildFabricKnowledgeBaseSection(fabricLanguage);
221
+ const results = [];
222
+ for (const rel of SECTION_TARGETS) {
223
+ const target = join(projectRoot, rel);
224
+ if (!existsSync(target)) {
225
+ results.push({ step: "section", path: target, status: "skipped", message: "absent" });
226
+ continue;
227
+ }
228
+ let existing;
229
+ try {
230
+ existing = await readFile(target, "utf8");
231
+ } catch (error) {
232
+ results.push({
233
+ step: "section",
234
+ path: target,
235
+ status: "error",
236
+ message: error instanceof Error ? error.message : String(error)
237
+ });
238
+ continue;
239
+ }
240
+ let next;
241
+ const match = existing.match(FABRIC_SECTION_REGEX);
242
+ if (match !== null) {
243
+ const before = existing.slice(0, match.index ?? 0);
244
+ const after = existing.slice((match.index ?? 0) + match[0].length);
245
+ const stripped = `${before}${after.replace(/^\r?\n/, "")}`;
246
+ const trailingNewline = stripped.length === 0 || stripped.endsWith("\n") ? "" : "\n";
247
+ next = `${stripped}${trailingNewline}
248
+ ${sectionBody}
249
+ `;
250
+ } else {
251
+ const trailingNewline = existing.length === 0 || existing.endsWith("\n") ? "" : "\n";
252
+ next = `${existing}${trailingNewline}
253
+ ${sectionBody}
254
+ `;
255
+ }
256
+ if (next === existing) {
257
+ results.push({ step: "section", path: target, status: "skipped", message: "up-to-date" });
258
+ continue;
259
+ }
260
+ try {
261
+ await atomicWriteText(target, next);
262
+ results.push({ step: "section", path: target, status: "written" });
263
+ } catch (error) {
264
+ results.push({
265
+ step: "section",
266
+ path: target,
267
+ status: "error",
268
+ message: error instanceof Error ? error.message : String(error)
269
+ });
270
+ }
271
+ }
272
+ return results;
273
+ }
274
+ async function copyTextIdempotent(step, source, target) {
275
+ if (existsSync(target)) {
276
+ try {
277
+ const existing = readFileSync(target, "utf8");
278
+ if (existing === source) {
279
+ return { step, path: target, status: "skipped", message: "up-to-date" };
280
+ }
281
+ } catch {
282
+ }
283
+ }
284
+ await mkdir(dirname(target), { recursive: true });
285
+ await atomicWriteText(target, source);
286
+ return { step, path: target, status: "written" };
287
+ }
288
+ async function mergeJsonIdempotent(step, target, fragment, arrayAppendPaths) {
289
+ const existing = await readJsonObjectOrEmpty(target);
290
+ const merged = deepMerge(existing, fragment, { arrayAppendPaths });
291
+ if (jsonEqual(existing, merged)) {
292
+ return { step, path: target, status: "skipped", message: "up-to-date" };
293
+ }
294
+ await mkdir(dirname(target), { recursive: true });
295
+ await atomicWriteJson(target, merged, { indent: 2 });
296
+ return { step, path: target, status: "written" };
297
+ }
298
+ async function readJsonObjectOrEmpty(path) {
299
+ try {
300
+ const raw = await readFile(path, "utf8");
301
+ if (raw.trim().length === 0) {
302
+ return {};
303
+ }
304
+ const parsed = JSON.parse(raw);
305
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
306
+ return {};
307
+ }
308
+ return parsed;
309
+ } catch (error) {
310
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") {
311
+ return {};
312
+ }
313
+ throw error;
314
+ }
315
+ }
316
+ function jsonEqual(a, b) {
317
+ return JSON.stringify(a) === JSON.stringify(b);
318
+ }
319
+ async function readTemplate(relativePath) {
320
+ const path = findTemplatePath(relativePath);
321
+ return readFile(path, "utf8");
322
+ }
323
+ async function readJsonTemplate(relativePath) {
324
+ const raw = await readTemplate(relativePath);
325
+ const parsed = JSON.parse(raw);
326
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
327
+ throw new Error(`Template at ${relativePath} is not a JSON object`);
328
+ }
329
+ return parsed;
330
+ }
331
+ function findTemplatePath(relativePath) {
332
+ const startDir = dirname(fileURLToPath(import.meta.url));
333
+ let current = resolve(startDir);
334
+ while (true) {
335
+ const candidate = join(current, "templates", relativePath);
336
+ if (existsSync(candidate)) {
337
+ return candidate;
338
+ }
339
+ const parent = dirname(current);
340
+ if (parent === current || parse(current).root === current) {
341
+ throw new Error(`Template not found: templates/${relativePath} (searched up from ${startDir})`);
342
+ }
343
+ current = parent;
344
+ }
345
+ }
346
+
347
+ export {
348
+ SKILL_DESTINATIONS,
349
+ HOOK_SCRIPT_DESTINATIONS,
350
+ HOOK_CONFIG_TARGETS,
351
+ HOOK_CONFIG_ARRAY_PATHS,
352
+ FABRIC_HOOK_COMMAND_PATHS,
353
+ SECTION_TARGETS,
354
+ FABRIC_SECTION_REGEX,
355
+ readFabricLanguagePreference,
356
+ installFabricArchiveSkill,
357
+ installFabricReviewSkill,
358
+ installFabricImportSkill,
359
+ installArchiveHintHook,
360
+ installKnowledgeHintBroadHook,
361
+ installKnowledgeHintNarrowHook,
362
+ mergeClaudeCodeHookConfig,
363
+ mergeCodexHookConfig,
364
+ mergeCursorHookConfig,
365
+ addFabricKnowledgeBaseSection
366
+ };
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ configCmd,
4
+ config_default,
5
+ installMcpClients
6
+ } from "./chunk-AIB54QRT.js";
7
+ import "./chunk-SKSYUHKK.js";
8
+ import "./chunk-6ICJICVU.js";
9
+ export {
10
+ configCmd,
11
+ config_default as default,
12
+ installMcpClients
13
+ };
@@ -1,14 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ runInitScan
4
+ } from "./chunk-AXIFEVAS.js";
5
+ import {
6
+ hasActionHint,
3
7
  paint,
8
+ renderFabricError,
4
9
  symbol
5
- } from "./chunk-WWNXR34K.js";
6
- import {
7
- resolveDevMode
8
- } from "./chunk-OBQU6NHO.js";
10
+ } from "./chunk-G2CIOLD4.js";
9
11
  import {
10
12
  t
11
13
  } from "./chunk-6ICJICVU.js";
14
+ import {
15
+ resolveDevMode
16
+ } from "./chunk-OBQU6NHO.js";
12
17
 
13
18
  // src/commands/doctor.ts
14
19
  import { confirm, isCancel } from "@clack/prompts";
@@ -16,11 +21,11 @@ import { defineCommand } from "citty";
16
21
  import {
17
22
  appendEventLedgerEvent,
18
23
  checkLockOrThrow,
19
- runDoctorApplyLint,
24
+ runDoctorApplyLint as runDoctorFixKnowledge,
20
25
  runDoctorFix,
21
26
  runDoctorReport
22
27
  } from "@fenglimg/fabric-server";
23
- var APPLY_LINT_CODE_LABELS = {
28
+ var FIX_KNOWLEDGE_CODE_LABELS = {
24
29
  knowledge_orphan_demote_required: "demote (maturity)",
25
30
  knowledge_stale_archive_required: "archive (git mv)",
26
31
  knowledge_pending_auto_archive: "archive (git mv, pending)",
@@ -43,28 +48,28 @@ var doctorCommand = defineCommand({
43
48
  description: t("cli.doctor.args.fix.description"),
44
49
  default: false
45
50
  },
46
- json: {
51
+ "fix-knowledge": {
47
52
  type: "boolean",
48
- description: t("cli.doctor.args.json.description"),
53
+ description: t("cli.doctor.args.fix-knowledge.description"),
49
54
  default: false
50
55
  },
51
- strict: {
56
+ json: {
52
57
  type: "boolean",
53
- description: t("cli.doctor.args.strict.description"),
58
+ description: t("cli.doctor.args.json.description"),
54
59
  default: false
55
60
  },
56
- force: {
61
+ rescan: {
57
62
  type: "boolean",
58
- description: t("cli.doctor.args.force.description"),
63
+ description: t("cli.doctor.args.rescan.description"),
59
64
  default: false
60
65
  },
61
- "apply-lint": {
66
+ strict: {
62
67
  type: "boolean",
63
- description: t("cli.doctor.args.apply-lint.description"),
68
+ description: t("cli.doctor.args.strict.description"),
64
69
  default: false
65
70
  },
66
71
  // rc.7 T11: skip the safety confirm before mutations. Required for any
67
- // non-tty invocation that wants to run --apply-lint without setting
72
+ // non-tty invocation that wants to run --fix-knowledge without setting
68
73
  // FABRIC_NONINTERACTIVE=1 in the environment.
69
74
  yes: {
70
75
  type: "boolean",
@@ -75,26 +80,38 @@ var doctorCommand = defineCommand({
75
80
  async run({ args }) {
76
81
  const workspaceRoot = process.cwd();
77
82
  const resolution = resolveDevMode(args.target, workspaceRoot);
78
- checkLockOrThrow(resolution.target, { force: args.force });
79
- const applyLint = args["apply-lint"] === true;
83
+ try {
84
+ checkLockOrThrow(resolution.target);
85
+ } catch (err) {
86
+ if (hasActionHint(err)) {
87
+ renderFabricError(err);
88
+ process.exit(1);
89
+ }
90
+ throw err;
91
+ }
92
+ const fixKnowledge = args["fix-knowledge"] === true;
80
93
  const fix = args.fix === true;
81
- if (applyLint && fix) {
82
- writeStderr(t("cli.doctor.errors.apply-lint-fix-mutually-exclusive"));
94
+ const rescan = args.rescan === true;
95
+ if (fixKnowledge && fix) {
96
+ writeStderr(t("cli.doctor.errors.fix-knowledge-fix-mutually-exclusive"));
83
97
  process.exitCode = 1;
84
98
  return;
85
99
  }
86
- let applyLintReport = null;
100
+ if (rescan) {
101
+ await runInitScan(resolution.target, { source: "doctor-rescan" });
102
+ }
103
+ let fixKnowledgeReport = null;
87
104
  let fixReport = null;
88
105
  let report;
89
- if (applyLint) {
106
+ if (fixKnowledge) {
90
107
  const preReport = await runDoctorReport(resolution.target);
91
- const plan = computeApplyLintPlan(preReport);
108
+ const plan = computeFixKnowledgePlan(preReport);
92
109
  const yesFlag = args.yes === true;
93
110
  const envBypass = process.env.FABRIC_NONINTERACTIVE === "1";
94
111
  if (plan.totalCount === 0) {
95
112
  } else {
96
- renderApplyLintPlan(plan);
97
- const decision = await resolveApplyLintConsent({
113
+ renderFixKnowledgePlan(plan);
114
+ const decision = await resolveFixKnowledgeConsent({
98
115
  yesFlag,
99
116
  envBypass,
100
117
  plan
@@ -104,8 +121,8 @@ var doctorCommand = defineCommand({
104
121
  return;
105
122
  }
106
123
  }
107
- applyLintReport = await runDoctorApplyLint(resolution.target);
108
- report = applyLintReport.report;
124
+ fixKnowledgeReport = await runDoctorFixKnowledge(resolution.target);
125
+ report = fixKnowledgeReport.report;
109
126
  } else if (fix) {
110
127
  fixReport = await runDoctorFix(resolution.target);
111
128
  report = fixReport.report;
@@ -113,30 +130,30 @@ var doctorCommand = defineCommand({
113
130
  report = await runDoctorReport(resolution.target);
114
131
  }
115
132
  if (args.json === true) {
116
- writeStdout(JSON.stringify(applyLintReport ?? fixReport ?? report, null, 2));
133
+ writeStdout(JSON.stringify(fixKnowledgeReport ?? fixReport ?? report, null, 2));
117
134
  } else {
118
- if (applyLintReport !== null) {
119
- writeStdout(applyLintReport.message);
120
- if (applyLintReport.aborted && applyLintReport.abort_reason !== void 0) {
121
- writeStderr(applyLintReport.abort_reason);
135
+ if (fixKnowledgeReport !== null) {
136
+ writeStdout(fixKnowledgeReport.message);
137
+ if (fixKnowledgeReport.aborted && fixKnowledgeReport.abort_reason !== void 0) {
138
+ writeStderr(fixKnowledgeReport.abort_reason);
122
139
  }
123
- renderApplyLintMutations(applyLintReport);
140
+ renderFixKnowledgeMutations(fixKnowledgeReport);
124
141
  } else if (fixReport !== null) {
125
142
  writeStdout(fixReport.message);
126
143
  }
127
144
  renderHumanReport(report);
128
145
  }
129
146
  await emitDoctorRunEventBestEffort(resolution.target, {
130
- mode: applyLint ? "apply-lint" : "lint",
147
+ mode: fixKnowledge ? "fix-knowledge" : "lint",
131
148
  issues: report.fixable_errors.length + report.manual_errors.length + report.warnings.length,
132
- mutations: applyLintReport !== null ? applyLintReport.mutations.filter((m) => m.applied).length : void 0
149
+ mutations: fixKnowledgeReport !== null ? fixKnowledgeReport.mutations.filter((m) => m.applied).length : void 0
133
150
  });
134
- if (applyLintReport !== null) {
135
- if (applyLintReport.aborted) {
151
+ if (fixKnowledgeReport !== null) {
152
+ if (fixKnowledgeReport.aborted) {
136
153
  process.exitCode = 1;
137
154
  return;
138
155
  }
139
- if (applyLintReport.mutations.some((m) => !m.applied)) {
156
+ if (fixKnowledgeReport.mutations.some((m) => !m.applied)) {
140
157
  process.exitCode = 1;
141
158
  return;
142
159
  }
@@ -156,13 +173,13 @@ function renderHumanReport(report) {
156
173
  writeIssueSection(t("doctor.section.manual"), report.manual_errors);
157
174
  writeIssueSection(t("doctor.section.warnings"), report.warnings);
158
175
  }
159
- function renderApplyLintMutations(applyLintReport) {
160
- if (applyLintReport.mutations.length === 0) {
176
+ function renderFixKnowledgeMutations(fixKnowledgeReport) {
177
+ if (fixKnowledgeReport.mutations.length === 0) {
161
178
  return;
162
179
  }
163
180
  writeStdout("");
164
- writeStdout(t("doctor.section.apply-lint-mutations"));
165
- for (const mutation of applyLintReport.mutations) {
181
+ writeStdout(t("doctor.section.fix-knowledge-mutations"));
182
+ for (const mutation of fixKnowledgeReport.mutations) {
166
183
  const marker = mutation.applied ? symbol.ok : symbol.error;
167
184
  const errSuffix = mutation.applied || mutation.error === void 0 ? "" : ` (${mutation.error})`;
168
185
  writeStdout(`${marker} ${mutation.kind}: ${mutation.path} [${mutation.detail}]${errSuffix}`);
@@ -207,28 +224,28 @@ function writeStderr(message) {
207
224
  process.stderr.write(`${message}
208
225
  `);
209
226
  }
210
- function computeApplyLintPlan(report) {
227
+ function computeFixKnowledgePlan(report) {
211
228
  const buckets = {};
212
229
  const sources = [
213
230
  ...report.fixable_errors,
214
231
  ...report.warnings
215
232
  ];
216
233
  for (const issue of sources) {
217
- if (APPLY_LINT_CODE_LABELS[issue.code] === void 0) continue;
234
+ if (FIX_KNOWLEDGE_CODE_LABELS[issue.code] === void 0) continue;
218
235
  if (!Array.isArray(buckets[issue.code])) {
219
236
  buckets[issue.code] = [];
220
237
  }
221
238
  buckets[issue.code].push(issue);
222
239
  }
223
240
  const codes = Object.keys(buckets).sort(
224
- (a, b) => APPLY_LINT_CODE_LABELS[a].localeCompare(APPLY_LINT_CODE_LABELS[b])
241
+ (a, b) => FIX_KNOWLEDGE_CODE_LABELS[a].localeCompare(FIX_KNOWLEDGE_CODE_LABELS[b])
225
242
  );
226
243
  const perCodeLines = [];
227
244
  let totalCount = 0;
228
245
  for (const code of codes) {
229
246
  const items = buckets[code];
230
247
  totalCount += items.length;
231
- perCodeLines.push(` - ${APPLY_LINT_CODE_LABELS[code]}: ${items.length}`);
248
+ perCodeLines.push(` - ${FIX_KNOWLEDGE_CODE_LABELS[code]}: ${items.length}`);
232
249
  }
233
250
  const previewLines = [];
234
251
  const flattened = codes.flatMap((c) => buckets[c]);
@@ -241,9 +258,9 @@ function computeApplyLintPlan(report) {
241
258
  }
242
259
  return { totalCount, perCodeLines, previewLines };
243
260
  }
244
- function renderApplyLintPlan(plan) {
261
+ function renderFixKnowledgePlan(plan) {
245
262
  writeStdout("");
246
- writeStdout(`${paint.warn("apply-lint mutation plan")} (${plan.totalCount} total)`);
263
+ writeStdout(`${paint.warn("fix-knowledge mutation plan")} (${plan.totalCount} total)`);
247
264
  for (const line of plan.perCodeLines) {
248
265
  writeStdout(line);
249
266
  }
@@ -255,13 +272,13 @@ function renderApplyLintPlan(plan) {
255
272
  }
256
273
  }
257
274
  }
258
- async function resolveApplyLintConsent(options) {
275
+ async function resolveFixKnowledgeConsent(options) {
259
276
  if (options.yesFlag || options.envBypass) {
260
277
  return "proceed";
261
278
  }
262
279
  if (process.stdin.isTTY !== true) {
263
280
  writeStderr(
264
- "doctor --apply-lint: stdin is not a TTY and neither --yes nor FABRIC_NONINTERACTIVE=1 is set. Refusing to mutate."
281
+ "doctor --fix-knowledge: stdin is not a TTY and neither --yes nor FABRIC_NONINTERACTIVE=1 is set. Refusing to mutate."
265
282
  );
266
283
  return "abort";
267
284
  }
@@ -271,7 +288,7 @@ async function resolveApplyLintConsent(options) {
271
288
  initialValue: false
272
289
  });
273
290
  if (isCancel(answer) || answer !== true) {
274
- writeStderr("doctor --apply-lint: aborted by user.");
291
+ writeStderr("doctor --fix-knowledge: aborted by user.");
275
292
  return "abort";
276
293
  }
277
294
  return "proceed";
package/dist/index.js CHANGED
@@ -8,21 +8,20 @@ import { defineCommand, runMain } from "citty";
8
8
 
9
9
  // src/commands/index.ts
10
10
  var allCommands = {
11
- init: () => import("./init-C56PWHID.js").then((module) => module.default),
12
- scan: () => import("./scan-66EKMNAY.js").then((module) => module.default),
13
- serve: () => import("./serve-NGLXHDYC.js").then((module) => module.default),
14
- uninstall: () => import("./uninstall-DBAR2JBS.js").then((module) => module.default),
15
- doctor: () => import("./doctor-RILCO5OG.js").then((module) => module.default),
16
- hooks: () => import("./hooks-NX32PPEN.js").then((module) => module.default),
17
- "plan-context-hint": () => import("./plan-context-hint-QMUPAXIB.js").then((module) => module.default)
11
+ install: () => import("./install-JLDCHAXV.js").then((module) => module.default),
12
+ doctor: () => import("./doctor-6XHLQJXB.js").then((module) => module.default),
13
+ serve: () => import("./serve-L3X5UHG2.js").then((module) => module.default),
14
+ uninstall: () => import("./uninstall-DD6FIFCI.js").then((module) => module.default),
15
+ config: () => import("./config-7YD365I3.js").then((module) => module.default),
16
+ "plan-context-hint": () => import("./plan-context-hint-73U4FGKO.js").then((module) => module.default)
18
17
  };
19
18
 
20
19
  // src/index.ts
21
20
  var main = defineCommand({
22
21
  meta: {
23
22
  name: "fabric",
24
- version: "2.0.0-rc.11",
25
- description: 'Initialize and manage Fabric projects. Use "fabric init" for one-shot setup.'
23
+ version: "2.0.0-rc.15",
24
+ description: 'Initialize and manage Fabric projects. Use "fabric install" for one-shot setup.'
26
25
  },
27
26
  subCommands: allCommands
28
27
  });