@fenglimg/fabric-cli 1.8.0-rc.3 → 2.0.0-rc.10

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 (49) hide show
  1. package/README.md +6 -6
  2. package/dist/chunk-6ICJICVU.js +10 -0
  3. package/dist/chunk-AW3G7ZH5.js +576 -0
  4. package/dist/chunk-HQLEHH4O.js +321 -0
  5. package/dist/chunk-MT3R57VG.js +1000 -0
  6. package/dist/{chunk-QPCRBQ5Y.js → chunk-OBQU6NHO.js} +1 -52
  7. package/dist/chunk-WPTA74BY.js +184 -0
  8. package/dist/chunk-WWNXR34K.js +49 -0
  9. package/dist/doctor-RILCO5OG.js +282 -0
  10. package/dist/hooks-NX32PPEN.js +13 -0
  11. package/dist/index.js +8 -5
  12. package/dist/{init-7EYGUJNJ.js → init-SAVH4SKE.js} +281 -1235
  13. package/dist/plan-context-hint-QMUPAXIB.js +98 -0
  14. package/dist/scan-ELSNCSKS.js +22 -0
  15. package/dist/{serve-466QXQ5Q.js → serve-NGLXHDYC.js} +8 -4
  16. package/dist/uninstall-DBAR2JBS.js +1082 -0
  17. package/package.json +3 -3
  18. package/templates/agents-md/AGENTS.md.template +55 -17
  19. package/templates/bootstrap/CLAUDE.md +1 -1
  20. package/templates/bootstrap/codex-AGENTS-header.md +1 -1
  21. package/templates/bootstrap/cursor-fabric-bootstrap.mdc +1 -1
  22. package/templates/hooks/configs/README.md +73 -0
  23. package/templates/hooks/configs/claude-code.json +37 -0
  24. package/templates/hooks/configs/codex-hooks.json +20 -0
  25. package/templates/hooks/configs/cursor-hooks.json +20 -0
  26. package/templates/hooks/fabric-hint.cjs +1337 -0
  27. package/templates/hooks/knowledge-hint-broad.cjs +612 -0
  28. package/templates/hooks/knowledge-hint-narrow.cjs +826 -0
  29. package/templates/hooks/lib/session-digest-writer.cjs +172 -0
  30. package/templates/skills/fabric-archive/SKILL.md +486 -0
  31. package/templates/skills/fabric-import/SKILL.md +560 -0
  32. package/templates/skills/fabric-review/SKILL.md +382 -0
  33. package/dist/chunk-NMMUETVK.js +0 -216
  34. package/dist/doctor-F52XWWZC.js +0 -98
  35. package/dist/scan-NNBNGIZG.js +0 -12
  36. package/templates/agents-md/variants/cocos.md +0 -20
  37. package/templates/agents-md/variants/next.md +0 -20
  38. package/templates/agents-md/variants/vite.md +0 -20
  39. package/templates/bootstrap/GEMINI.md +0 -8
  40. package/templates/bootstrap/roo-fabric.md +0 -5
  41. package/templates/bootstrap/windsurf-fabric.md +0 -5
  42. package/templates/claude-hooks/fabric-init-reminder.cjs +0 -18
  43. package/templates/claude-skills/fabric-init/SKILL.md +0 -163
  44. package/templates/codex-hooks/fabric-session-start.cjs +0 -19
  45. package/templates/codex-hooks/fabric-stop-reminder.cjs +0 -18
  46. package/templates/codex-skills/fabric-init/SKILL.md +0 -162
  47. package/templates/husky/pre-commit +0 -9
  48. package/templates/skill-source/fabric-init/SOURCE.md +0 -157
  49. package/templates/skill-source/fabric-init/clients.json +0 -17
@@ -1,760 +1,60 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- createScanReport,
4
- detectFramework
5
- } from "./chunk-NMMUETVK.js";
3
+ hooksCommand,
4
+ installHooks
5
+ } from "./chunk-WPTA74BY.js";
6
+ import {
7
+ detectFramework,
8
+ runInitScan
9
+ } from "./chunk-MT3R57VG.js";
10
+ import {
11
+ detectClientSupports,
12
+ resolveClients
13
+ } from "./chunk-HQLEHH4O.js";
14
+ import {
15
+ addArchiveSkillPointer,
16
+ installArchiveHintHook,
17
+ installFabricArchiveSkill,
18
+ installFabricImportSkill,
19
+ installFabricReviewSkill,
20
+ installKnowledgeHintBroadHook,
21
+ installKnowledgeHintNarrowHook,
22
+ mergeClaudeCodeHookConfig,
23
+ mergeCodexHookConfig,
24
+ mergeCursorHookConfig
25
+ } from "./chunk-AW3G7ZH5.js";
6
26
  import {
7
- createDebugLogger,
8
27
  displayWidth,
9
28
  padEnd,
10
- paint,
11
- readFabricConfig,
12
- resolveDevMode,
29
+ paint
30
+ } from "./chunk-WWNXR34K.js";
31
+ import {
32
+ createDebugLogger,
33
+ resolveDevMode
34
+ } from "./chunk-OBQU6NHO.js";
35
+ import {
13
36
  t
14
- } from "./chunk-QPCRBQ5Y.js";
37
+ } from "./chunk-6ICJICVU.js";
15
38
 
16
39
  // src/commands/init.ts
17
- import { createHash, randomUUID } from "crypto";
40
+ import { randomUUID } from "crypto";
41
+ import { homedir } from "os";
18
42
  import * as childProcess from "child_process";
19
- import { appendFileSync, chmodSync as chmodSync2, copyFileSync, existsSync as existsSync9, mkdirSync as mkdirSync3, readdirSync as readdirSync2, readFileSync as readFileSync4, rmSync, statSync as statSync3, writeFileSync } from "fs";
20
- import { dirname as dirname5, isAbsolute as isAbsolute4, join as join8, parse as parse3, resolve as resolve9 } from "path";
21
- import { fileURLToPath as fileURLToPath4 } from "url";
43
+ import { appendFileSync, existsSync as existsSync3, mkdirSync, rmSync, statSync as statSync2, writeFileSync } from "fs";
44
+ import { dirname, isAbsolute as isAbsolute2, join as join2, resolve as resolve3 } from "path";
22
45
  import { cancel, confirm, group, intro, isCancel, log, note, outro, select } from "@clack/prompts";
23
- import { atomicWriteJson as atomicWriteJson3, atomicWriteText as atomicWriteText4 } from "@fenglimg/fabric-shared/node/atomic-write";
24
- import { defineCommand as defineCommand4 } from "citty";
46
+ import { defaultAgentsMetaCounters } from "@fenglimg/fabric-shared";
47
+ import { atomicWriteJson, atomicWriteText } from "@fenglimg/fabric-shared/node/atomic-write";
48
+ import { defineCommand as defineCommand2 } from "citty";
25
49
  import { checkLockOrThrow } from "@fenglimg/fabric-server";
26
50
 
27
- // src/bootstrap-guide.ts
28
- import { existsSync, mkdirSync, readFileSync } from "fs";
29
- import { dirname, isAbsolute, join, parse, resolve } from "path";
51
+ // src/commands/config.ts
52
+ import { existsSync } from "fs";
53
+ import { readFile } from "fs/promises";
54
+ import { resolve } from "path";
30
55
  import { fileURLToPath } from "url";
31
- import { atomicWriteText } from "@fenglimg/fabric-shared/node/atomic-write";
32
- var AGENTS_TEMPLATE_BY_FRAMEWORK = {
33
- "cocos-creator": "templates/agents-md/variants/cocos.md",
34
- vite: "templates/agents-md/variants/vite.md",
35
- next: "templates/agents-md/variants/next.md"
36
- };
37
- var FABRIC_GUIDE_PATH = ".fabric/bootstrap/README.md";
38
- async function buildFabricBootstrapGuide(target) {
39
- const workspaceRoot = normalizeTarget(target);
40
- const scanReport = await createScanReport(workspaceRoot);
41
- const template = readFileSync(findBootstrapTemplatePath(scanReport.framework.kind), "utf8");
42
- const packageName = readPackageName(workspaceRoot) ?? parse(workspaceRoot).base;
43
- return ensureTrailingNewline(
44
- template.replaceAll("{ projectName }", packageName).replaceAll("{ frameworkKind }", scanReport.framework.kind)
45
- );
46
- }
47
- async function ensureFabricBootstrapGuide(workspaceRoot, force) {
48
- const guidePath = resolve(workspaceRoot, FABRIC_GUIDE_PATH);
49
- if (existsSync(guidePath) && !force) {
50
- return;
51
- }
52
- mkdirSync(dirname(guidePath), { recursive: true });
53
- await atomicWriteText(guidePath, await buildFabricBootstrapGuide(workspaceRoot));
54
- }
55
- function findBootstrapTemplatePath(frameworkKind) {
56
- const relativePath = AGENTS_TEMPLATE_BY_FRAMEWORK[frameworkKind] ?? "templates/agents-md/AGENTS.md.template";
57
- return findTemplatePath(relativePath);
58
- }
59
- function readPackageName(target) {
60
- const packageJsonPath = join(target, "package.json");
61
- if (!existsSync(packageJsonPath)) {
62
- return void 0;
63
- }
64
- try {
65
- const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
66
- return packageJson.name;
67
- } catch {
68
- return void 0;
69
- }
70
- }
71
- function findTemplatePath(relativePath) {
72
- const currentModuleDir = dirname(fileURLToPath(import.meta.url));
73
- const candidates = [
74
- ...templateCandidatesFrom(process.cwd(), relativePath),
75
- ...templateCandidatesFrom(currentModuleDir, relativePath)
76
- ];
77
- for (const candidate of candidates) {
78
- if (existsSync(candidate)) {
79
- return candidate;
80
- }
81
- }
82
- throw new Error(t("cli.shared.template-not-found", { path: relativePath }));
83
- }
84
- function templateCandidatesFrom(start, relativePath) {
85
- const candidates = [];
86
- let current = resolve(start);
87
- while (true) {
88
- candidates.push(join(current, ...relativePath.split("/")));
89
- const parent = dirname(current);
90
- if (parent === current || parse(current).root === current) {
91
- break;
92
- }
93
- current = parent;
94
- }
95
- return candidates.reverse();
96
- }
97
- function ensureTrailingNewline(content) {
98
- return content.endsWith("\n") ? content : `${content}
99
- `;
100
- }
101
- function normalizeTarget(targetInput) {
102
- return isAbsolute(targetInput) ? targetInput : resolve(process.cwd(), targetInput);
103
- }
104
-
105
- // src/commands/bootstrap.ts
106
- import { resolve as resolve5 } from "path";
107
56
  import { defineCommand } from "citty";
108
-
109
- // src/config/resolver.ts
110
- import { existsSync as existsSync5 } from "fs";
111
- import { join as join5 } from "path";
112
- import { homedir as homedir4 } from "os";
113
-
114
- // src/config/claude-code.ts
115
- import { existsSync as existsSync3 } from "fs";
116
- import { join as join3, resolve as resolve3 } from "path";
117
- import { homedir as homedir2, platform } from "os";
118
-
119
- // src/config/json.ts
120
- import { existsSync as existsSync2 } from "fs";
121
- import { mkdir, readFile } from "fs/promises";
122
- import { dirname as dirname2, join as join2, resolve as resolve2 } from "path";
123
- import { homedir } from "os";
124
- import { atomicWriteJson } from "@fenglimg/fabric-shared/node/atomic-write";
125
-
126
- // src/config/writer.ts
127
- function createServerEntry(serverPath) {
128
- return {
129
- command: process.execPath,
130
- args: [serverPath]
131
- };
132
- }
133
-
134
- // src/config/json.ts
135
- function deepMerge(target, source) {
136
- if (target === null || typeof target !== "object" || Array.isArray(target) || source === null || typeof source !== "object" || Array.isArray(source)) {
137
- return source;
138
- }
139
- const out = { ...target };
140
- for (const key of Object.keys(source)) {
141
- out[key] = deepMerge(
142
- target[key],
143
- source[key]
144
- );
145
- }
146
- return out;
147
- }
148
- function expandHome(filePath) {
149
- if (filePath === "~") {
150
- return homedir();
151
- }
152
- if (filePath.startsWith("~/")) {
153
- return join2(homedir(), filePath.slice(2));
154
- }
155
- return filePath;
156
- }
157
- function normalizeConfigPath(filePath) {
158
- return resolve2(expandHome(filePath));
159
- }
160
- async function readJsonConfig(configPath) {
161
- try {
162
- const raw = await readFile(configPath, "utf8");
163
- if (raw.trim().length === 0) {
164
- return {};
165
- }
166
- const parsed = JSON.parse(raw);
167
- if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
168
- throw new Error(`Expected JSON object in ${configPath}`);
169
- }
170
- return parsed;
171
- } catch (error) {
172
- if (error instanceof Error && "code" in error && error.code === "ENOENT") {
173
- return {};
174
- }
175
- throw error;
176
- }
177
- }
178
- async function writeJsonClientConfig(configPath, serverEntry) {
179
- const existing = await readJsonConfig(configPath);
180
- const merged = deepMerge(existing, { mcpServers: { fabric: serverEntry } });
181
- await mkdir(dirname2(configPath), { recursive: true });
182
- await atomicWriteJson(configPath, merged, { indent: 2 });
183
- }
184
- var JsonClientConfigWriter = class {
185
- configuredPath;
186
- constructor(configuredPath) {
187
- this.configuredPath = configuredPath;
188
- }
189
- async detect(workspaceRoot, overridePath) {
190
- const explicitPath = overridePath ?? this.configuredPath;
191
- if (explicitPath !== void 0) {
192
- return normalizeConfigPath(explicitPath);
193
- }
194
- const configPath = this.defaultPath(workspaceRoot);
195
- return configPath === null ? null : normalizeConfigPath(configPath);
196
- }
197
- async write(serverPath, workspaceRoot, overridePath) {
198
- const configPath = await this.detect(workspaceRoot, overridePath);
199
- if (configPath === null) {
200
- return;
201
- }
202
- await writeJsonClientConfig(configPath, createServerEntry(serverPath));
203
- }
204
- };
205
- var ClaudeCodeCLIWriter = class extends JsonClientConfigWriter {
206
- clientKind = "ClaudeCodeCLI";
207
- scope;
208
- constructor(configuredPath, scope = "project") {
209
- super(configuredPath);
210
- this.scope = scope;
211
- }
212
- // Writes to project-level .mcp.json (per Claude Code MCP spec) by default,
213
- // or ~/.claude.json for user scope.
214
- // Detection still checks ~/.claude to confirm Claude Code is installed.
215
- defaultPath(workspaceRoot) {
216
- const globalClaudeDir = join2(homedir(), ".claude");
217
- const projectClaudeDir = join2(workspaceRoot, ".claude");
218
- if (!existsSync2(globalClaudeDir) && !existsSync2(projectClaudeDir)) {
219
- return null;
220
- }
221
- return this.scope === "user" ? join2(homedir(), ".claude.json") : join2(workspaceRoot, ".mcp.json");
222
- }
223
- };
224
- var CursorWriter = class extends JsonClientConfigWriter {
225
- clientKind = "Cursor";
226
- constructor(configuredPath) {
227
- super(configuredPath);
228
- }
229
- defaultPath(workspaceRoot) {
230
- const cursorDir = join2(workspaceRoot, ".cursor");
231
- return existsSync2(cursorDir) ? join2(cursorDir, "mcp.json") : null;
232
- }
233
- };
234
-
235
- // src/config/claude-code.ts
236
- function getClaudeDesktopConfigPath() {
237
- const os = platform();
238
- if (os === "darwin") {
239
- return join3(homedir2(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
240
- }
241
- if (os === "win32") {
242
- return join3(process.env.APPDATA ?? join3(homedir2(), "AppData", "Roaming"), "Claude", "claude_desktop_config.json");
243
- }
244
- return join3(homedir2(), ".config", "Claude", "claude_desktop_config.json");
245
- }
246
- var ClaudeCodeDesktopWriter = class {
247
- clientKind = "ClaudeCodeDesktop";
248
- configuredPath;
249
- constructor(configuredPath) {
250
- this.configuredPath = configuredPath;
251
- }
252
- async detect(_workspaceRoot, overridePath) {
253
- const configPath = normalizeConfigPath(overridePath ?? this.configuredPath ?? getClaudeDesktopConfigPath());
254
- return existsSync3(configPath) || overridePath !== void 0 || this.configuredPath !== void 0 ? configPath : null;
255
- }
256
- async write(serverPath, workspaceRoot, overridePath) {
257
- const configPath = await this.detect(workspaceRoot, overridePath);
258
- if (configPath === null) {
259
- return;
260
- }
261
- await writeJsonClientConfig(configPath, {
262
- command: process.execPath,
263
- args: [serverPath]
264
- });
265
- }
266
- };
267
-
268
- // src/config/toml.ts
269
- import { existsSync as existsSync4 } from "fs";
270
- import { mkdir as mkdir2, readFile as readFile2 } from "fs/promises";
271
- import { dirname as dirname3, join as join4, resolve as resolve4 } from "path";
272
- import { homedir as homedir3 } from "os";
273
- import { atomicWriteText as atomicWriteText2 } from "@fenglimg/fabric-shared/node/atomic-write";
274
- function expandHome2(filePath) {
275
- if (filePath === "~") {
276
- return homedir3();
277
- }
278
- if (filePath.startsWith("~/")) {
279
- return join4(homedir3(), filePath.slice(2));
280
- }
281
- return filePath;
282
- }
283
- function escapeTomlString(value) {
284
- return JSON.stringify(value);
285
- }
286
- function serializeTomlStringArray(values) {
287
- return `[${values.map((value) => escapeTomlString(value)).join(", ")}]`;
288
- }
289
- function serializeTomlInlineTable(values) {
290
- const entries = Object.entries(values).sort(([left], [right]) => left.localeCompare(right)).map(([key, value]) => `${key} = ${escapeTomlString(value)}`);
291
- return `{ ${entries.join(", ")} }`;
292
- }
293
- function serializeCodexServerBlock(serverName, serverEntry) {
294
- const lines = [
295
- `[mcp_servers.${serverName}]`,
296
- `command = ${escapeTomlString(serverEntry.command)}`,
297
- `args = ${serializeTomlStringArray(serverEntry.args)}`
298
- ];
299
- if (serverEntry.env !== void 0 && Object.keys(serverEntry.env).length > 0) {
300
- lines.push(`env = ${serializeTomlInlineTable(serverEntry.env)}`);
301
- }
302
- return `${lines.join("\n")}
303
- `;
304
- }
305
- function trimTrailingBlankLines(value) {
306
- return value.replace(/\s+$/u, "");
307
- }
308
- function upsertCodexServerBlock(rawConfig, serverName, serverEntry) {
309
- const block = serializeCodexServerBlock(serverName, serverEntry);
310
- const normalized = rawConfig.replace(/\r\n/g, "\n");
311
- const legacyPattern = new RegExp(String.raw`\n?\[mcp\.servers\.${serverName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\]\n[\s\S]*?(?=\n\[[^\n]+\]\n|$)`, "g");
312
- const currentPattern = new RegExp(
313
- String.raw`\n?\[mcp_servers\.${serverName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\]\n[\s\S]*?(?=\n\[[^\n]+\]\n|$)`,
314
- "g"
315
- );
316
- const withoutLegacy = normalized.replace(legacyPattern, "");
317
- const withoutExisting = withoutLegacy.replace(currentPattern, "");
318
- const trimmed = trimTrailingBlankLines(withoutExisting);
319
- if (trimmed.length === 0) {
320
- return block;
321
- }
322
- return `${trimmed}
323
-
324
- ${block}`;
325
- }
326
- async function readTomlConfigText(configPath) {
327
- try {
328
- return await readFile2(configPath, "utf8");
329
- } catch (error) {
330
- if (error instanceof Error && "code" in error && error.code === "ENOENT") {
331
- return "";
332
- }
333
- throw error;
334
- }
335
- }
336
- var CodexTOMLConfigWriter = class {
337
- clientKind = "CodexCLI";
338
- configuredPath;
339
- constructor(configuredPath) {
340
- this.configuredPath = configuredPath;
341
- }
342
- async detect(_workspaceRoot, overridePath) {
343
- const explicitPath = overridePath ?? this.configuredPath;
344
- if (explicitPath !== void 0) {
345
- return resolve4(expandHome2(explicitPath));
346
- }
347
- const codexDir = join4(homedir3(), ".codex");
348
- return existsSync4(codexDir) ? resolve4(join4(codexDir, "config.toml")) : null;
349
- }
350
- async write(serverPath, workspaceRoot, overridePath) {
351
- const configPath = await this.detect(workspaceRoot, overridePath);
352
- if (configPath === null) {
353
- return;
354
- }
355
- const rawConfig = await readTomlConfigText(configPath);
356
- const nextConfig = upsertCodexServerBlock(rawConfig, "fabric", createServerEntry(serverPath));
357
- await mkdir2(dirname3(configPath), { recursive: true });
358
- await atomicWriteText2(configPath, nextConfig);
359
- }
360
- };
361
-
362
- // src/config/resolver.ts
363
- import { clientPathsSchema, fabricConfigSchema } from "@fenglimg/fabric-shared";
364
- function hasExplicitPath(clientPaths, key) {
365
- return typeof clientPaths?.[key] === "string" && clientPaths[key].trim().length > 0;
366
- }
367
- function addIfDetected(writers, detected, createWriter, configuredPath) {
368
- if (configuredPath !== void 0 || detected) {
369
- writers.push(createWriter(configuredPath));
370
- }
371
- }
372
- function resolveClients(workspaceRoot, fabricConfig = {}, opts = {}) {
373
- const clientPaths = fabricConfig.clientPaths;
374
- const writers = [];
375
- const claudeMcpScope = opts.claudeMcpScope ?? "project";
376
- addIfDetected(
377
- writers,
378
- existsSync5(join5(homedir4(), ".claude")) || existsSync5(join5(workspaceRoot, ".claude")),
379
- (configuredPath) => new ClaudeCodeCLIWriter(configuredPath, claudeMcpScope),
380
- hasExplicitPath(clientPaths, "claudeCodeCLI") ? clientPaths.claudeCodeCLI : void 0
381
- );
382
- addIfDetected(
383
- writers,
384
- existsSync5(getClaudeDesktopConfigPath()),
385
- (configuredPath) => new ClaudeCodeDesktopWriter(configuredPath),
386
- hasExplicitPath(clientPaths, "claudeCodeDesktop") ? clientPaths.claudeCodeDesktop : void 0
387
- );
388
- addIfDetected(
389
- writers,
390
- existsSync5(join5(workspaceRoot, ".cursor")),
391
- (configuredPath) => new CursorWriter(configuredPath),
392
- hasExplicitPath(clientPaths, "cursor") ? clientPaths.cursor : void 0
393
- );
394
- addIfDetected(
395
- writers,
396
- existsSync5(join5(homedir4(), ".codex")),
397
- (configuredPath) => new CodexTOMLConfigWriter(configuredPath),
398
- hasExplicitPath(clientPaths, "codexCLI") ? clientPaths.codexCLI : void 0
399
- );
400
- return writers;
401
- }
402
- function detectClientSupports(workspaceRoot, fabricConfig = {}) {
403
- const clientPaths = fabricConfig.clientPaths;
404
- const claudeDetected = existsSync5(join5(homedir4(), ".claude")) || existsSync5(join5(workspaceRoot, ".claude"));
405
- const claudeDesktopDetected = existsSync5(getClaudeDesktopConfigPath());
406
- const cursorDetected = existsSync5(join5(workspaceRoot, ".cursor"));
407
- const codexDetected = existsSync5(join5(homedir4(), ".codex"));
408
- return [
409
- {
410
- clientKind: "ClaudeCodeCLI",
411
- label: "Claude Code CLI",
412
- detected: claudeDetected || hasExplicitPath(clientPaths, "claudeCodeCLI"),
413
- bootstrapTargetPath: ".fabric/bootstrap/README.md",
414
- configPath: "project .claude/settings.json",
415
- capabilities: {
416
- bootstrap: true,
417
- mcp: true,
418
- hook: true,
419
- skill: true
420
- },
421
- installedCapabilities: {
422
- hook: true,
423
- skill: true
424
- }
425
- },
426
- {
427
- clientKind: "ClaudeCodeDesktop",
428
- label: "Claude Code Desktop",
429
- detected: claudeDesktopDetected || hasExplicitPath(clientPaths, "claudeCodeDesktop"),
430
- bootstrapTargetPath: ".fabric/bootstrap/README.md",
431
- configPath: "desktop Claude config",
432
- capabilities: {
433
- bootstrap: true,
434
- mcp: true,
435
- hook: false,
436
- skill: false
437
- }
438
- },
439
- {
440
- clientKind: "Cursor",
441
- label: "Cursor",
442
- detected: cursorDetected || hasExplicitPath(clientPaths, "cursor"),
443
- bootstrapTargetPath: ".fabric/bootstrap/README.md",
444
- configPath: ".cursor/mcp.json",
445
- capabilities: {
446
- bootstrap: true,
447
- mcp: true,
448
- hook: false,
449
- skill: false
450
- }
451
- },
452
- {
453
- clientKind: "CodexCLI",
454
- label: "Codex CLI",
455
- detected: codexDetected || hasExplicitPath(clientPaths, "codexCLI"),
456
- bootstrapTargetPath: ".fabric/bootstrap/README.md",
457
- configPath: "~/.codex/config.toml",
458
- capabilities: {
459
- bootstrap: true,
460
- mcp: true,
461
- hook: true,
462
- skill: true
463
- },
464
- installedCapabilities: {
465
- hook: existsSync5(join5(workspaceRoot, ".codex", "hooks.json")),
466
- skill: existsSync5(join5(workspaceRoot, ".codex", "skills", "fabric-init", "SKILL.md"))
467
- }
468
- }
469
- ];
470
- }
471
-
472
- // src/commands/bootstrap.ts
473
57
  var CLIENT_ALIASES = {
474
- claude: "claude",
475
- "claude-code": "claude",
476
- claudecode: "claude",
477
- claudecli: "claude",
478
- claudecodecli: "claude",
479
- claudedesktop: "claude",
480
- claudecodedesktop: "claude",
481
- cursor: "cursor",
482
- codex: "codex",
483
- "codex-cli": "codex",
484
- codexcli: "codex"
485
- };
486
- var bootstrapCommand = defineCommand({
487
- meta: {
488
- name: "bootstrap",
489
- description: t("cli.bootstrap.description")
490
- },
491
- subCommands: {
492
- install: defineCommand({
493
- meta: {
494
- name: "install",
495
- description: t("cli.bootstrap.install.description")
496
- },
497
- args: {
498
- clients: {
499
- type: "string",
500
- description: t("cli.bootstrap.install.args.clients.description")
501
- }
502
- },
503
- async run({ args }) {
504
- const workspaceRoot = process.cwd();
505
- const selectedClients = parseClientFilter(args.clients);
506
- const result = await installBootstrap(workspaceRoot, {
507
- clients: selectedClients === null ? void 0 : Array.from(selectedClients, mapBootstrapClientToClientKind)
508
- });
509
- if (result.details.length === 0) {
510
- process.stderr.write(
511
- `${t("cli.bootstrap.install.no-targets")}
512
- `
513
- );
514
- return;
515
- }
516
- for (const detail of result.details) {
517
- if (detail.action === "skipped") {
518
- process.stderr.write(`${t("cli.bootstrap.install.skipped-header", { path: detail.path })}
519
- `);
520
- continue;
521
- }
522
- if (detail.action === "prepended") {
523
- process.stderr.write(`${t("cli.bootstrap.install.prepended", { path: detail.path })}
524
- `);
525
- continue;
526
- }
527
- process.stderr.write(`${t("cli.bootstrap.install.installed", { path: detail.path })}
528
- `);
529
- }
530
- }
531
- })
532
- }
533
- });
534
- async function installBootstrap(target, options = {}) {
535
- const workspaceRoot = resolve5(target);
536
- const fabricConfig = readFabricConfig(workspaceRoot);
537
- const targets = resolveBootstrapTargets(workspaceRoot, fabricConfig, options.clients);
538
- const installed = [];
539
- const skipped = [];
540
- const details = [];
541
- await ensureFabricBootstrapGuide(workspaceRoot, options.force);
542
- for (const bootstrapTarget of targets) {
543
- details.push({
544
- client: bootstrapTarget.client,
545
- path: resolve5(workspaceRoot, FABRIC_GUIDE_PATH),
546
- action: "skipped"
547
- });
548
- skipped.push(bootstrapTarget.client);
549
- }
550
- return { installed, skipped, details };
551
- }
552
- function parseClientFilter(value) {
553
- if (value === void 0 || value.trim().length === 0) {
554
- return null;
555
- }
556
- const clients = /* @__PURE__ */ new Set();
557
- for (const rawClient of value.split(",")) {
558
- const alias = rawClient.trim().toLowerCase();
559
- const client = CLIENT_ALIASES[alias];
560
- if (client === void 0) {
561
- throw new Error(t("cli.bootstrap.errors.unknown-client", { client: rawClient }));
562
- }
563
- clients.add(client);
564
- }
565
- return clients;
566
- }
567
- function resolveBootstrapTargets(workspaceRoot, fabricConfig, selectedClients) {
568
- const targets = [];
569
- const seenClients = /* @__PURE__ */ new Set();
570
- const clientKinds = selectedClients ?? resolveClients(workspaceRoot, fabricConfig).map((writer) => writer.clientKind);
571
- for (const clientKind of clientKinds) {
572
- const bootstrapClient = mapClientKind(clientKind);
573
- if (bootstrapClient === null || seenClients.has(bootstrapClient)) {
574
- continue;
575
- }
576
- seenClients.add(bootstrapClient);
577
- targets.push({ client: clientKind, bootstrapClient });
578
- }
579
- return targets;
580
- }
581
- function mapClientKind(clientKind) {
582
- switch (clientKind) {
583
- case "ClaudeCodeCLI":
584
- case "ClaudeCodeDesktop":
585
- return "claude";
586
- case "Cursor":
587
- return "cursor";
588
- case "CodexCLI":
589
- return "codex";
590
- default:
591
- return null;
592
- }
593
- }
594
- function mapBootstrapClientToClientKind(client) {
595
- switch (client) {
596
- case "claude":
597
- return "ClaudeCodeCLI";
598
- case "cursor":
599
- return "Cursor";
600
- case "codex":
601
- return "CodexCLI";
602
- }
603
- }
604
-
605
- // src/commands/config.ts
606
- import { existsSync as existsSync7 } from "fs";
607
- import { readFile as readFile3 } from "fs/promises";
608
- import { resolve as resolve7 } from "path";
609
- import { fileURLToPath as fileURLToPath3 } from "url";
610
- import { defineCommand as defineCommand3 } from "citty";
611
-
612
- // src/commands/hooks.ts
613
- import { chmodSync, existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync2, statSync } from "fs";
614
- import { dirname as dirname4, isAbsolute as isAbsolute2, join as join6, parse as parse2, resolve as resolve6 } from "path";
615
- import { fileURLToPath as fileURLToPath2 } from "url";
616
- import { defineCommand as defineCommand2 } from "citty";
617
- import { atomicWriteJson as atomicWriteJson2, atomicWriteText as atomicWriteText3 } from "@fenglimg/fabric-shared/node/atomic-write";
618
- var hooksCommand = defineCommand2({
619
- meta: {
620
- name: "hooks",
621
- description: t("cli.hooks.description")
622
- },
623
- subCommands: {
624
- install: defineCommand2({
625
- meta: {
626
- name: "install",
627
- description: t("cli.hooks.install.description")
628
- },
629
- args: {
630
- target: {
631
- type: "string",
632
- description: t("cli.hooks.install.args.target.description"),
633
- default: process.cwd()
634
- }
635
- },
636
- async run({ args }) {
637
- const result = await installHooks(args.target);
638
- if (result.hookAction === "skipped") {
639
- writeStderr(t("cli.hooks.install.hook-skipped", { path: result.hookPath }));
640
- } else if (result.hookAction === "appended") {
641
- writeStderr(t("cli.hooks.install.hook-appended", { path: result.hookPath }));
642
- } else {
643
- writeStderr(t("cli.hooks.install.hook-created", { path: result.hookPath }));
644
- }
645
- if (result.prepareAction === "left") {
646
- writeStderr(t("cli.hooks.install.prepare-left", { path: result.packageJsonPath }));
647
- } else {
648
- writeStderr(t("cli.hooks.install.prepare-added", { path: result.packageJsonPath }));
649
- }
650
- }
651
- })
652
- }
653
- });
654
- async function installHooks(target, options = {}) {
655
- const normalizedTarget = normalizeTarget2(target);
656
- assertExistingDirectory(normalizedTarget);
657
- const huskyDir = join6(normalizedTarget, ".husky");
658
- const hookPath = join6(huskyDir, "pre-commit");
659
- const packageJsonPath = join6(normalizedTarget, "package.json");
660
- if (!existsSync6(packageJsonPath)) {
661
- throw new Error(t("cli.hooks.errors.package-json-required", { path: packageJsonPath }));
662
- }
663
- mkdirSync2(huskyDir, { recursive: true });
664
- const templateContent = readFileSync2(findTemplatePath2("templates/husky/pre-commit"), "utf8");
665
- const hookAction = await installHookFile(hookPath, templateContent, options.force);
666
- chmodSync(hookPath, 493);
667
- const packageJson = JSON.parse(readFileSync2(packageJsonPath, "utf8"));
668
- const scripts = packageJson.scripts && typeof packageJson.scripts === "object" && !Array.isArray(packageJson.scripts) ? packageJson.scripts : {};
669
- const hadPrepare = typeof scripts.prepare === "string" && scripts.prepare.trim().length > 0;
670
- let prepareAction = "left";
671
- if (!hadPrepare) {
672
- scripts.prepare = "husky install";
673
- packageJson.scripts = scripts;
674
- await atomicWriteJson2(packageJsonPath, packageJson);
675
- prepareAction = "added";
676
- }
677
- const installed = [];
678
- const skipped = [];
679
- if (hookAction === "skipped") {
680
- skipped.push(hookPath);
681
- } else {
682
- installed.push(hookPath);
683
- }
684
- if (prepareAction === "left") {
685
- skipped.push(packageJsonPath);
686
- } else {
687
- installed.push(packageJsonPath);
688
- }
689
- return {
690
- installed,
691
- skipped,
692
- hookPath,
693
- packageJsonPath,
694
- hookAction,
695
- prepareAction
696
- };
697
- }
698
- function normalizeTarget2(targetInput) {
699
- return isAbsolute2(targetInput) ? targetInput : resolve6(process.cwd(), targetInput);
700
- }
701
- function assertExistingDirectory(target) {
702
- if (!existsSync6(target) || !statSync(target).isDirectory()) {
703
- throw new Error(t("cli.shared.target-invalid", { target }));
704
- }
705
- }
706
- async function installHookFile(hookPath, templateContent, force) {
707
- if (existsSync6(hookPath)) {
708
- if (force) {
709
- await atomicWriteText3(hookPath, templateContent);
710
- return "overwritten";
711
- }
712
- const existing = readFileSync2(hookPath, "utf8");
713
- if (existing.includes("FAB_BIN=")) {
714
- return "skipped";
715
- }
716
- const fabricBlock = templateContent.replace(/^#!\/bin\/sh\n/, "");
717
- const separator = existing.endsWith("\n") ? "\n" : "\n\n";
718
- await atomicWriteText3(hookPath, `${existing}${separator}# --- Fabric ---
719
- ${fabricBlock}`);
720
- return "appended";
721
- }
722
- await atomicWriteText3(hookPath, templateContent);
723
- return "created";
724
- }
725
- function findTemplatePath2(relativePath) {
726
- const currentModuleDir = dirname4(fileURLToPath2(import.meta.url));
727
- const candidates = [
728
- ...templateCandidatesFrom2(process.cwd(), relativePath),
729
- ...templateCandidatesFrom2(currentModuleDir, relativePath)
730
- ];
731
- for (const candidate of candidates) {
732
- if (existsSync6(candidate)) {
733
- return candidate;
734
- }
735
- }
736
- throw new Error(t("cli.shared.template-not-found", { path: relativePath }));
737
- }
738
- function templateCandidatesFrom2(start, relativePath) {
739
- const candidates = [];
740
- let current = resolve6(start);
741
- while (true) {
742
- candidates.push(join6(current, ...relativePath.split("/")));
743
- const parent = dirname4(current);
744
- if (parent === current || parse2(current).root === current) {
745
- break;
746
- }
747
- current = parent;
748
- }
749
- return candidates.reverse();
750
- }
751
- function writeStderr(message) {
752
- process.stderr.write(`${message}
753
- `);
754
- }
755
-
756
- // src/commands/config.ts
757
- var CLIENT_ALIASES2 = {
758
58
  claude: "ClaudeCodeCLI",
759
59
  claudecodecli: "ClaudeCodeCLI",
760
60
  "claude-code-cli": "ClaudeCodeCLI",
@@ -767,14 +67,14 @@ var CLIENT_ALIASES2 = {
767
67
  "codex-cli": "CodexCLI",
768
68
  codex: "CodexCLI"
769
69
  };
770
- function parseClientFilter2(value) {
70
+ function parseClientFilter(value) {
771
71
  if (value === void 0 || value.trim().length === 0) {
772
72
  return null;
773
73
  }
774
74
  const clients = /* @__PURE__ */ new Set();
775
75
  for (const rawClient of value.split(",")) {
776
76
  const alias = rawClient.trim().toLowerCase();
777
- const clientKind = CLIENT_ALIASES2[alias];
77
+ const clientKind = CLIENT_ALIASES[alias];
778
78
  if (clientKind === void 0) {
779
79
  throw new Error(t("cli.config.errors.unknown-client", { client: rawClient }));
780
80
  }
@@ -783,11 +83,11 @@ function parseClientFilter2(value) {
783
83
  return clients;
784
84
  }
785
85
  async function loadFabricConfig(workspaceRoot) {
786
- const configPath = resolve7(workspaceRoot, "fabric.config.json");
787
- if (!existsSync7(configPath)) {
86
+ const configPath = resolve(workspaceRoot, "fabric.config.json");
87
+ if (!existsSync(configPath)) {
788
88
  return {};
789
89
  }
790
- const parsed = JSON.parse(await readFile3(configPath, "utf8"));
90
+ const parsed = JSON.parse(await readFile(configPath, "utf8"));
791
91
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
792
92
  throw new Error(t("cli.config.errors.expected-object", { path: configPath }));
793
93
  }
@@ -795,21 +95,21 @@ async function loadFabricConfig(workspaceRoot) {
795
95
  }
796
96
  function resolveServerPath(override) {
797
97
  if (override) return override;
798
- if (process.env.FAB_SERVER_PATH) return resolve7(process.env.FAB_SERVER_PATH);
799
- return fileURLToPath3(import.meta.resolve("@fenglimg/fabric-server"));
98
+ if (process.env.FAB_SERVER_PATH) return resolve(process.env.FAB_SERVER_PATH);
99
+ return fileURLToPath(import.meta.resolve("@fenglimg/fabric-server"));
800
100
  }
801
- function writeStderr2(message) {
101
+ function writeStderr(message) {
802
102
  process.stderr.write(`${message}
803
103
  `);
804
104
  }
805
- var configCmd = defineCommand3({
105
+ var configCmd = defineCommand({
806
106
  meta: {
807
107
  name: "config",
808
108
  description: t("cli.config.description")
809
109
  },
810
110
  subCommands: {
811
111
  hooks: hooksCommand,
812
- install: defineCommand3({
112
+ install: defineCommand({
813
113
  meta: {
814
114
  name: "install",
815
115
  description: t("cli.config.install.description")
@@ -826,26 +126,26 @@ var configCmd = defineCommand3({
826
126
  }
827
127
  },
828
128
  async run({ args }) {
829
- const selectedClients = parseClientFilter2(args.clients);
129
+ const selectedClients = parseClientFilter(args.clients);
830
130
  const result = await installMcpClients(process.cwd(), {
831
131
  clients: selectedClients === null ? void 0 : Array.from(selectedClients),
832
132
  dryRun: args["dry-run"]
833
133
  });
834
134
  if (result.details.length === 0) {
835
- writeStderr2(t("cli.config.install.no-configs"));
135
+ writeStderr(t("cli.config.install.no-configs"));
836
136
  return;
837
137
  }
838
138
  for (const detail of result.details) {
839
139
  if (detail.action === "skipped") {
840
- writeStderr2(t("cli.config.install.no-config-path", { client: detail.client }));
140
+ writeStderr(t("cli.config.install.no-config-path", { client: detail.client }));
841
141
  continue;
842
142
  }
843
143
  if (detail.action === "dry-run" && detail.path !== null) {
844
- writeStderr2(t("cli.config.install.dry-run", { client: detail.client, path: detail.path }));
144
+ writeStderr(t("cli.config.install.dry-run", { client: detail.client, path: detail.path }));
845
145
  continue;
846
146
  }
847
147
  if (detail.path !== null) {
848
- writeStderr2(t("cli.config.install.wrote", { client: detail.client, path: detail.path }));
148
+ writeStderr(t("cli.config.install.wrote", { client: detail.client, path: detail.path }));
849
149
  }
850
150
  }
851
151
  }
@@ -853,7 +153,7 @@ var configCmd = defineCommand3({
853
153
  }
854
154
  });
855
155
  async function installMcpClients(target, options = {}) {
856
- const workspaceRoot = resolve7(target);
156
+ const workspaceRoot = resolve(target);
857
157
  const fabricConfig = await loadFabricConfig(workspaceRoot);
858
158
  const selectedClients = options.clients === void 0 ? null : new Set(options.clients);
859
159
  const serverPath = resolveServerPath(options.localServerPath);
@@ -884,9 +184,9 @@ async function installMcpClients(target, options = {}) {
884
184
 
885
185
  // src/scanner/forensic.ts
886
186
  import { execFileSync } from "child_process";
887
- import { existsSync as existsSync8, readdirSync, readFileSync as readFileSync3, statSync as statSync2 } from "fs";
187
+ import { existsSync as existsSync2, readdirSync, readFileSync, statSync } from "fs";
888
188
  import { createRequire } from "module";
889
- import { basename, extname, isAbsolute as isAbsolute3, join as join7, posix, relative, resolve as resolve8, sep } from "path";
189
+ import { basename, extname, isAbsolute, join, posix, relative, resolve as resolve2, sep } from "path";
890
190
  import {
891
191
  forensicReportSchema
892
192
  } from "@fenglimg/fabric-shared";
@@ -972,7 +272,7 @@ var parserInitPromise = null;
972
272
  var languagePromiseByKind = {};
973
273
  var parserBundlePromiseByKind = {};
974
274
  async function buildForensicReport(targetInput) {
975
- const target = normalizeTarget3(targetInput);
275
+ const target = normalizeTarget(targetInput);
976
276
  const framework = detectFramework(target);
977
277
  const topology = buildTopology(target);
978
278
  const entryPoints = collectEntryPoints(target, topology.files);
@@ -1008,11 +308,11 @@ async function buildForensicReport(targetInput) {
1008
308
  }
1009
309
  return validation.data;
1010
310
  }
1011
- function normalizeTarget3(targetInput) {
1012
- return isAbsolute3(targetInput) ? targetInput : resolve8(process.cwd(), targetInput);
311
+ function normalizeTarget(targetInput) {
312
+ return isAbsolute(targetInput) ? targetInput : resolve2(process.cwd(), targetInput);
1013
313
  }
1014
314
  function buildTopology(root) {
1015
- assertExistingDirectory2(root);
315
+ assertExistingDirectory(root);
1016
316
  const byExt = {};
1017
317
  const keyDirs = /* @__PURE__ */ new Set();
1018
318
  const files = [];
@@ -1025,7 +325,7 @@ function buildTopology(root) {
1025
325
  continue;
1026
326
  }
1027
327
  for (const entry of readdirSync(current, { withFileTypes: true })) {
1028
- const absolutePath = join7(current, entry.name);
328
+ const absolutePath = join(current, entry.name);
1029
329
  const relativePath = toPosixPath(relative(root, absolutePath));
1030
330
  if (relativePath.length === 0) {
1031
331
  continue;
@@ -1045,7 +345,7 @@ function buildTopology(root) {
1045
345
  if (!entry.isFile()) {
1046
346
  continue;
1047
347
  }
1048
- const stats = statSync2(absolutePath);
348
+ const stats = statSync(absolutePath);
1049
349
  const extension = extname(entry.name) || "[none]";
1050
350
  byExt[extension] = (byExt[extension] ?? 0) + 1;
1051
351
  totalFiles += 1;
@@ -1063,8 +363,8 @@ function buildTopology(root) {
1063
363
  files: files.sort((left, right) => left.relativePath.localeCompare(right.relativePath))
1064
364
  };
1065
365
  }
1066
- function assertExistingDirectory2(target) {
1067
- if (!existsSync8(target) || !statSync2(target).isDirectory()) {
366
+ function assertExistingDirectory(target) {
367
+ if (!existsSync2(target) || !statSync(target).isDirectory()) {
1068
368
  throw new Error(`Target must be an existing directory: ${target}`);
1069
369
  }
1070
370
  }
@@ -1113,7 +413,7 @@ function getEntryPointReason(relativePath) {
1113
413
  async function buildCodeSamples(target, entryPoints, frameworkKind, topology, packageDependencies) {
1114
414
  const samples = [];
1115
415
  for (const entryPoint of entryPoints.slice(0, SAMPLE_LIMIT)) {
1116
- const absolutePath = join7(target, ...entryPoint.path.split("/"));
416
+ const absolutePath = join(target, ...entryPoint.path.split("/"));
1117
417
  const sample = readFirstLines(absolutePath, SAMPLE_LINE_LIMIT);
1118
418
  const patternAnalysis = await inferPatternHint(entryPoint.path, sample.snippet, {
1119
419
  frameworkKind,
@@ -1133,7 +433,7 @@ async function buildCodeSamples(target, entryPoints, frameworkKind, topology, pa
1133
433
  }
1134
434
  function readFirstLines(path, lineLimit) {
1135
435
  try {
1136
- const lines = readFileSync3(path, "utf8").split(/\r?\n/);
436
+ const lines = readFileSync(path, "utf8").split(/\r?\n/);
1137
437
  if (lines.at(-1) === "") {
1138
438
  lines.pop();
1139
439
  }
@@ -1150,12 +450,12 @@ function readFirstLines(path, lineLimit) {
1150
450
  }
1151
451
  }
1152
452
  function readPackageDependencies(target) {
1153
- const packageJsonPath = join7(target, "package.json");
1154
- if (!existsSync8(packageJsonPath)) {
453
+ const packageJsonPath = join(target, "package.json");
454
+ if (!existsSync2(packageJsonPath)) {
1155
455
  return /* @__PURE__ */ new Map();
1156
456
  }
1157
457
  try {
1158
- const packageJson = JSON.parse(readFileSync3(packageJsonPath, "utf8"));
458
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
1159
459
  return new Map([
1160
460
  ...Object.entries(packageJson.dependencies ?? {}),
1161
461
  ...Object.entries(packageJson.devDependencies ?? {}),
@@ -1491,16 +791,16 @@ function scoreFrameworkConfidence(input) {
1491
791
  return input.configCount > 0 || input.packageCount > 0 ? "MEDIUM" : "LOW";
1492
792
  }
1493
793
  function readReadmeInfo(target) {
1494
- const readmePath = join7(target, "README.md");
1495
- const hasContributing = existsSync8(join7(target, "CONTRIBUTING.md"));
1496
- if (!existsSync8(readmePath)) {
794
+ const readmePath = join(target, "README.md");
795
+ const hasContributing = existsSync2(join(target, "CONTRIBUTING.md"));
796
+ if (!existsSync2(readmePath)) {
1497
797
  return {
1498
798
  quality: "missing",
1499
799
  line_count: 0,
1500
800
  has_contributing: hasContributing
1501
801
  };
1502
802
  }
1503
- const readme = readFileSync3(readmePath, "utf8");
803
+ const readme = readFileSync(readmePath, "utf8");
1504
804
  const wordCount = readme.trim().split(/\s+/).filter(Boolean).length;
1505
805
  return {
1506
806
  quality: wordCount >= 200 ? "ok" : "stub",
@@ -1733,7 +1033,7 @@ function buildDomainAssertion(codeSamples) {
1733
1033
  namedModules.length >= 2 ? "domain-named-components" : null,
1734
1034
  namedSamples.some((sample) => sample.snippet.includes("start():")) ? "lifecycle-hook" : null
1735
1035
  ]),
1736
- proposedRule: "Preserve domain-specific module names when mirroring structure into .fabric/rules/."
1036
+ proposedRule: "Preserve domain-specific module names when authoring knowledge entries that reference these modules."
1737
1037
  });
1738
1038
  }
1739
1039
  function createAssertion(input) {
@@ -1978,10 +1278,10 @@ function buildSkillRecommendations(frameworkKind, topology, readme) {
1978
1278
  return recommendations;
1979
1279
  }
1980
1280
  function readProjectName(target) {
1981
- const packageJsonPath = join7(target, "package.json");
1982
- if (existsSync8(packageJsonPath)) {
1281
+ const packageJsonPath = join(target, "package.json");
1282
+ if (existsSync2(packageJsonPath)) {
1983
1283
  try {
1984
- const packageJson = JSON.parse(readFileSync3(packageJsonPath, "utf8"));
1284
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
1985
1285
  if (packageJson.name !== void 0 && packageJson.name.trim().length > 0) {
1986
1286
  return packageJson.name;
1987
1287
  }
@@ -1992,7 +1292,7 @@ function readProjectName(target) {
1992
1292
  return basename(target);
1993
1293
  }
1994
1294
  function getCliVersion() {
1995
- return true ? "1.8.0-rc.3" : "unknown";
1295
+ return true ? "2.0.0-rc.10" : "unknown";
1996
1296
  }
1997
1297
  function sortRecord(record) {
1998
1298
  return Object.fromEntries(Object.entries(record).sort(([left], [right]) => left.localeCompare(right)));
@@ -2002,18 +1302,19 @@ function toPosixPath(path) {
2002
1302
  }
2003
1303
 
2004
1304
  // src/commands/init.ts
2005
- var CLAUDE_INIT_SKILL_TEMPLATE = "templates/claude-skills/fabric-init/SKILL.md";
2006
- var CLAUDE_INIT_REMINDER_HOOK_TEMPLATE = "templates/claude-hooks/fabric-init-reminder.cjs";
2007
- var CLAUDE_INIT_REMINDER_COMMAND = ".claude/hooks/fabric-init-reminder.cjs";
2008
- var CODEX_INIT_SKILL_TEMPLATE = "templates/codex-skills/fabric-init/SKILL.md";
2009
- var CODEX_SESSION_START_HOOK_TEMPLATE = "templates/codex-hooks/fabric-session-start.cjs";
2010
- var CODEX_STOP_HOOK_TEMPLATE = "templates/codex-hooks/fabric-stop-reminder.cjs";
2011
- var CODEX_SESSION_START_COMMAND = ".codex/hooks/fabric-session-start.cjs";
2012
- var CODEX_STOP_COMMAND = ".codex/hooks/fabric-stop-reminder.cjs";
2013
- var LOCAL_FABRIC_SERVER_PATH = join8("node_modules", "@fenglimg", "fabric-server", "dist", "index.js");
1305
+ var AGENTS_MD_DEFAULT_CONTENT = `# Project Knowledge
1306
+
1307
+ This project uses [Fabric](https://github.com/fenglimg/fabric) for cross-client AI knowledge management.
1308
+
1309
+ Knowledge entries live in \`.fabric/knowledge/\` (team) and \`~/.fabric/knowledge/\` (personal).
1310
+ Run \`fabric doctor\` to verify state.
1311
+
1312
+ See \`.fabric/knowledge/\` for project decisions, pitfalls, guidelines, models, and processes.
1313
+ `;
1314
+ var LOCAL_FABRIC_SERVER_PATH = join2("node_modules", "@fenglimg", "fabric-server", "dist", "index.js");
2014
1315
  var FABRIC_SERVER_PACKAGE = "@fenglimg/fabric-server";
2015
1316
  var INIT_WIZARD_GROUP_CANCELLED = /* @__PURE__ */ Symbol("init-wizard-group-cancelled");
2016
- var initCommand = defineCommand4({
1317
+ var initCommand = defineCommand2({
2017
1318
  meta: {
2018
1319
  name: "init",
2019
1320
  description: t("cli.init.description")
@@ -2095,13 +1396,13 @@ async function runInitCommand(args) {
2095
1396
  logger(step);
2096
1397
  }
2097
1398
  if (intent.options.planOnly) {
2098
- writeStderr3(t("cli.init.compat.plan"));
1399
+ writeStderr2(t("cli.init.compat.plan"));
2099
1400
  }
2100
1401
  if (args.interactive === false) {
2101
- writeStderr3(t("cli.init.compat.interactive"));
1402
+ writeStderr2(t("cli.init.compat.interactive"));
2102
1403
  }
2103
1404
  if (args.bootstrap === false || args.mcp === false || args.hooks === false) {
2104
- writeStderr3(t("cli.init.compat.legacy-stage-flags"));
1405
+ writeStderr2(t("cli.init.compat.legacy-stage-flags"));
2105
1406
  }
2106
1407
  const supports = detectClientSupports(intent.target);
2107
1408
  const basePlan = await buildInitExecutionPlan({
@@ -2117,10 +1418,47 @@ async function runInitCommand(args) {
2117
1418
  process.exitCode = 130;
2118
1419
  return;
2119
1420
  }
2120
- return executeInitExecutionPlan(plan);
1421
+ const result = await executeInitExecutionPlan(plan);
1422
+ if (!intent.options.planOnly) {
1423
+ console.log(paint.muted("More: docs/surfaces.md explains when to use CLI vs Skill vs MCP."));
1424
+ }
1425
+ return result;
1426
+ }
1427
+ function writeDefaultFabricConfig(fabricDir) {
1428
+ const target = join2(fabricDir, "fabric-config.json");
1429
+ if (existsSync3(target)) return;
1430
+ const FABRIC_CONFIG_DEFAULTS = {
1431
+ // Scan/import language policy. `match-existing` lets init resolve the
1432
+ // effective language from project content; explicit `zh-CN` / `en`
1433
+ // lock the policy. See packages/shared/src/schemas/fabric-config.ts.
1434
+ knowledge_language: "match-existing",
1435
+ // fabric-hint Stop hook Signal A (archive): time-branch threshold, hours
1436
+ // since last knowledge_proposed event.
1437
+ archive_hint_hours: 24,
1438
+ // fabric-hint Stop hook cooldown after ANY signal fires, in hours.
1439
+ archive_hint_cooldown_hours: 12,
1440
+ // fabric-hint Stop hook Signal B (review): pending-count cutoff.
1441
+ review_hint_pending_count: 10,
1442
+ // fabric-hint Stop hook Signal B (review): pending-age cutoff in days.
1443
+ review_hint_pending_age_days: 7,
1444
+ // fabric-hint Stop hook Signal D (maintenance): days since last doctor.
1445
+ maintenance_hint_days: 14,
1446
+ // fabric-hint Stop hook Signal D (maintenance): cooldown between
1447
+ // reminders, in days.
1448
+ maintenance_hint_cooldown_days: 7,
1449
+ // fabric-hint Stop hook Signal A (archive): edit-count branch threshold;
1450
+ // PreToolUse fires recorded in .fabric/.cache/edit-counter since the
1451
+ // last knowledge_proposed event.
1452
+ archive_edit_threshold: 20,
1453
+ // fabric-hint Stop hook Signal C (import) + doctor lint #22: canonical
1454
+ // knowledge node count below this value flags an underseeded workspace.
1455
+ underseed_node_threshold: 10
1456
+ };
1457
+ mkdirSync(fabricDir, { recursive: true });
1458
+ writeFileSync(target, JSON.stringify(FABRIC_CONFIG_DEFAULTS, null, 2) + "\n", "utf8");
2121
1459
  }
2122
1460
  function resolveInitCliIntent(args, targetInput) {
2123
- const target = normalizeTarget4(targetInput);
1461
+ const target = normalizeTarget2(targetInput);
2124
1462
  const mcpInstallMode = resolveMcpInstallMode(args["mcp-install"]);
2125
1463
  const claudeMcpScope = resolveClaudeMcpScope(args.scope);
2126
1464
  const terminalInteractive = isInteractiveInit();
@@ -2150,7 +1488,7 @@ function resolveClaudeMcpScope(raw) {
2150
1488
  if (raw === "user") {
2151
1489
  return "user";
2152
1490
  }
2153
- writeStderr3(t("cli.init.mcp.scope.invalid", { value: raw }));
1491
+ writeStderr2(t("cli.init.mcp.scope.invalid", { value: raw }));
2154
1492
  return "project";
2155
1493
  }
2156
1494
  async function buildInitExecutionPlan(input) {
@@ -2190,10 +1528,10 @@ async function buildInitExecutionPlan(input) {
2190
1528
  }
2191
1529
  async function executeInitExecutionPlan(plan) {
2192
1530
  if (plan.options.force) {
2193
- writeStderr3(t("cli.init.force.warning", { path: plan.target }));
1531
+ writeStderr2(t("cli.init.force.warning", { path: plan.target }));
2194
1532
  }
2195
1533
  if (plan.options.reapply && !plan.options.planOnly && !plan.interactive) {
2196
- writeStderr3(formatInitModeBanner(plan.options));
1534
+ writeStderr2(formatInitModeBanner(plan.options));
2197
1535
  }
2198
1536
  if (plan.interactive) {
2199
1537
  printInitPlanSummary(plan.target, plan.options, plan.mcpInstallMode, plan.supports);
@@ -2238,98 +1576,78 @@ async function executeInitExecutionPlan(plan) {
2238
1576
  finalSupports
2239
1577
  };
2240
1578
  }
1579
+ var KNOWLEDGE_SUBDIRS = ["decisions", "pitfalls", "guidelines", "models", "processes", "pending"];
1580
+ function resolvePersonalFabricRoot() {
1581
+ return process.env.FABRIC_HOME ?? homedir();
1582
+ }
2241
1583
  async function buildInitFabricPlan(target, options) {
2242
- assertExistingDirectory3(target);
2243
- const fabricDir = join8(target, ".fabric");
2244
- const bootstrapPath = join8(fabricDir, "bootstrap", "README.md");
2245
- const forensicPath = join8(fabricDir, "forensic.json");
2246
- const taxonomyPath = join8(fabricDir, "INITIAL_TAXONOMY.md");
2247
- const rulesDir = join8(fabricDir, "rules");
2248
- const eventsPath = join8(fabricDir, "events.jsonl");
2249
- const claudeSkillPath = join8(target, ".claude", "skills", "fabric-init", "SKILL.md");
2250
- const codexSkillPath = join8(target, ".codex", "skills", "fabric-init", "SKILL.md");
2251
- const codexSessionStartHookPath = join8(target, ".codex", "hooks", "fabric-session-start.cjs");
2252
- const codexStopHookPath = join8(target, ".codex", "hooks", "fabric-stop-reminder.cjs");
2253
- const codexHooksConfigPath = join8(target, ".codex", "hooks.json");
2254
- const claudeHookPath = join8(target, ".claude", "hooks", "fabric-init-reminder.cjs");
2255
- const claudeSettingsPath = join8(target, ".claude", "settings.json");
2256
- const metaPath = join8(fabricDir, "agents.meta.json");
1584
+ assertExistingDirectory2(target);
1585
+ const fabricDir = join2(target, ".fabric");
1586
+ const agentsMdPath = join2(target, "AGENTS.md");
1587
+ const agentsMdAction = existsSync3(agentsMdPath) ? "preserved" : "created";
1588
+ const knowledgeDir = join2(fabricDir, "knowledge");
1589
+ const personalKnowledgeDir = join2(resolvePersonalFabricRoot(), ".fabric", "knowledge");
1590
+ const forensicPath = join2(fabricDir, "forensic.json");
1591
+ const eventsPath = join2(fabricDir, "events.jsonl");
1592
+ const metaPath = join2(fabricDir, "agents.meta.json");
2257
1593
  const replaceFabricDir = shouldReplaceWritableDirectory(fabricDir, options);
2258
- const bootstrapAction = planFreshPath(bootstrapPath, options);
1594
+ const knowledgeDirAction = existsSync3(knowledgeDir) ? "overwritten" : "created";
2259
1595
  const metaAction = planFreshPath(metaPath, options);
2260
- const taxonomyAction = planFreshPath(taxonomyPath, options);
2261
1596
  const eventsAction = planFreshPath(eventsPath, options);
2262
1597
  const forensicAction = planFreshPath(forensicPath, options);
2263
1598
  const forensicReport = await buildForensicReport(target);
2264
- const bootstrapContent = await buildFabricBootstrapGuide(target);
2265
- const taxonomyContent = buildInitialTaxonomyMarkdown(forensicReport);
2266
- const bootstrapHash = sha256(bootstrapContent);
2267
- const meta = createInitialMeta(bootstrapHash);
1599
+ const meta = createInitialMeta();
2268
1600
  return {
2269
1601
  target,
2270
1602
  options,
2271
1603
  fabricDir,
2272
1604
  replaceFabricDir,
2273
- bootstrapPath,
2274
- bootstrapAction,
2275
- bootstrapContent,
1605
+ agentsMdPath,
1606
+ agentsMdAction,
1607
+ knowledgeDir,
1608
+ knowledgeDirAction,
1609
+ personalKnowledgeDir,
2276
1610
  metaPath,
2277
1611
  metaAction,
2278
1612
  meta,
2279
- taxonomyPath,
2280
- taxonomyAction,
2281
- taxonomyContent,
2282
- rulesDir,
2283
1613
  eventsPath,
2284
1614
  eventsAction,
2285
1615
  forensicPath,
2286
1616
  forensicAction,
2287
- forensicReport,
2288
- claudeSkill: buildOptionalTemplateWritePlan(claudeSkillPath, findTemplatePath3(CLAUDE_INIT_SKILL_TEMPLATE), options),
2289
- codexSkill: buildOptionalTemplateWritePlan(codexSkillPath, findTemplatePath3(CODEX_INIT_SKILL_TEMPLATE), options),
2290
- codexSessionStartHook: buildOptionalTemplateWritePlan(
2291
- codexSessionStartHookPath,
2292
- findTemplatePath3(CODEX_SESSION_START_HOOK_TEMPLATE),
2293
- options,
2294
- true
2295
- ),
2296
- codexStopHook: buildOptionalTemplateWritePlan(
2297
- codexStopHookPath,
2298
- findTemplatePath3(CODEX_STOP_HOOK_TEMPLATE),
2299
- options,
2300
- true
2301
- ),
2302
- codexHooksConfig: buildCodexHooksConfigPlan(codexHooksConfigPath, options),
2303
- claudeHook: buildOptionalTemplateWritePlan(
2304
- claudeHookPath,
2305
- findTemplatePath3(CLAUDE_INIT_REMINDER_HOOK_TEMPLATE),
2306
- options,
2307
- true
2308
- ),
2309
- claudeSettings: buildClaudeSettingsWritePlan(claudeSettingsPath, options)
1617
+ forensicReport
2310
1618
  };
2311
1619
  }
2312
1620
  async function executeInitFabricPlan(plan) {
2313
1621
  const isReapply = plan.options?.reapply === true;
2314
- const existingRules = isReapply && existsSync9(plan.rulesDir) ? readdirSync2(plan.rulesDir).filter((f) => f.endsWith(".md")) : [];
2315
- const preserveMeta = isReapply && existingRules.length > 0;
2316
1622
  if (plan.replaceFabricDir) {
2317
1623
  rmSync(plan.fabricDir, { force: true });
2318
1624
  }
2319
- mkdirSync3(plan.fabricDir, { recursive: true });
2320
- mkdirSync3(dirname5(plan.bootstrapPath), { recursive: true });
2321
- preparePlannedPath(plan.bootstrapPath, plan.bootstrapAction);
2322
- await atomicWriteText4(plan.bootstrapPath, plan.bootstrapContent);
2323
- if (!preserveMeta) {
2324
- preparePlannedPath(plan.metaPath, plan.metaAction);
2325
- await atomicWriteJson3(plan.metaPath, plan.meta);
2326
- }
2327
- preparePlannedPath(plan.taxonomyPath, plan.taxonomyAction);
2328
- await atomicWriteText4(plan.taxonomyPath, ensureTrailingNewline2(plan.taxonomyContent));
2329
- mkdirSync3(plan.rulesDir, { recursive: true });
1625
+ mkdirSync(plan.fabricDir, { recursive: true });
1626
+ writeDefaultFabricConfig(plan.fabricDir);
1627
+ if (plan.agentsMdAction === "created" && !existsSync3(plan.agentsMdPath)) {
1628
+ await atomicWriteText(plan.agentsMdPath, AGENTS_MD_DEFAULT_CONTENT);
1629
+ }
1630
+ mkdirSync(plan.knowledgeDir, { recursive: true });
1631
+ for (const sub of KNOWLEDGE_SUBDIRS) {
1632
+ const teamSubDir = join2(plan.knowledgeDir, sub);
1633
+ mkdirSync(teamSubDir, { recursive: true });
1634
+ const teamGitkeep = join2(teamSubDir, ".gitkeep");
1635
+ if (!existsSync3(teamGitkeep)) {
1636
+ writeFileSync(teamGitkeep, "", "utf8");
1637
+ }
1638
+ }
1639
+ try {
1640
+ mkdirSync(plan.personalKnowledgeDir, { recursive: true });
1641
+ for (const sub of KNOWLEDGE_SUBDIRS) {
1642
+ mkdirSync(join2(plan.personalKnowledgeDir, sub), { recursive: true });
1643
+ }
1644
+ } catch {
1645
+ }
1646
+ preparePlannedPath(plan.metaPath, plan.metaAction);
1647
+ await atomicWriteJson(plan.metaPath, plan.meta);
2330
1648
  if (isReapply) {
2331
- if (!existsSync9(plan.eventsPath)) {
2332
- mkdirSync3(dirname5(plan.eventsPath), { recursive: true });
1649
+ if (!existsSync3(plan.eventsPath)) {
1650
+ mkdirSync(dirname(plan.eventsPath), { recursive: true });
2333
1651
  writeFileSync(plan.eventsPath, "", "utf8");
2334
1652
  }
2335
1653
  } else {
@@ -2337,46 +1655,33 @@ async function executeInitFabricPlan(plan) {
2337
1655
  writeFileSync(plan.eventsPath, "", "utf8");
2338
1656
  }
2339
1657
  preparePlannedPath(plan.forensicPath, plan.forensicAction);
2340
- await atomicWriteJson3(plan.forensicPath, plan.forensicReport);
2341
- applyOptionalTemplateWritePlan(plan.claudeSkill);
2342
- applyOptionalTemplateWritePlan(plan.codexSkill);
2343
- applyOptionalTemplateWritePlan(plan.codexSessionStartHook);
2344
- applyOptionalTemplateWritePlan(plan.codexStopHook);
2345
- await applyJsonWritePlan(plan.codexHooksConfig);
2346
- applyOptionalTemplateWritePlan(plan.claudeHook);
2347
- await applyClaudeSettingsWritePlan(plan.claudeSettings);
1658
+ await atomicWriteJson(plan.forensicPath, plan.forensicReport);
1659
+ if (!plan.options?.reapply) {
1660
+ try {
1661
+ await runInitScan(plan.target, { source: "init" });
1662
+ } catch (error) {
1663
+ writeStderr2(
1664
+ `[warn] init-scan failed: ${error instanceof Error ? error.message : String(error)} \u2014 re-run \`fab scan\` to populate baseline knowledge entries.`
1665
+ );
1666
+ }
1667
+ }
2348
1668
  if (isReapply) {
2349
1669
  appendReapplyLedgerEvent(plan.eventsPath, {
2350
- preserved_ledger: true,
2351
- preserved_meta: preserveMeta,
2352
- rules_count: existingRules.length
1670
+ preserved_ledger: true
2353
1671
  });
2354
1672
  }
2355
1673
  return {
2356
- bootstrapPath: plan.bootstrapPath,
2357
- bootstrapAction: plan.bootstrapAction,
1674
+ agentsMdPath: plan.agentsMdPath,
1675
+ agentsMdAction: plan.agentsMdAction,
1676
+ knowledgeDir: plan.knowledgeDir,
1677
+ knowledgeDirAction: plan.knowledgeDirAction,
1678
+ personalKnowledgeDir: plan.personalKnowledgeDir,
2358
1679
  metaPath: plan.metaPath,
2359
1680
  metaAction: plan.metaAction,
2360
- taxonomyPath: plan.taxonomyPath,
2361
- taxonomyAction: plan.taxonomyAction,
2362
1681
  eventsPath: plan.eventsPath,
2363
1682
  eventsAction: plan.eventsAction,
2364
1683
  forensicPath: plan.forensicPath,
2365
- forensicAction: plan.forensicAction,
2366
- claudeSkillPath: plan.claudeSkill.path,
2367
- claudeSkillAction: plan.claudeSkill.action,
2368
- codexSkillPath: plan.codexSkill.path,
2369
- codexSkillAction: plan.codexSkill.action,
2370
- codexSessionStartHookPath: plan.codexSessionStartHook.path,
2371
- codexSessionStartHookAction: plan.codexSessionStartHook.action,
2372
- codexStopHookPath: plan.codexStopHook.path,
2373
- codexStopHookAction: plan.codexStopHook.action,
2374
- codexHooksConfigPath: plan.codexHooksConfig.path,
2375
- codexHooksConfigAction: plan.codexHooksConfig.action,
2376
- claudeHookPath: plan.claudeHook.path,
2377
- claudeHookAction: plan.claudeHook.action,
2378
- claudeSettingsPath: plan.claudeSettings.path,
2379
- claudeSettingsAction: plan.claudeSettings.action
1684
+ forensicAction: plan.forensicAction
2380
1685
  };
2381
1686
  }
2382
1687
  async function initFabric(target, options) {
@@ -2421,22 +1726,11 @@ function exhaustiveInitStagePlan(value) {
2421
1726
  throw new Error(`Unsupported init stage plan: ${JSON.stringify(value)}`);
2422
1727
  }
2423
1728
  function printInitScaffoldResult(created) {
2424
- console.log(formatInitPathAction(created.bootstrapPath, created.bootstrapAction));
1729
+ console.log(formatAgentsMdAction(created.agentsMdPath, created.agentsMdAction));
1730
+ console.log(formatInitPathAction(created.knowledgeDir, created.knowledgeDirAction));
2425
1731
  console.log(formatInitPathAction(created.metaPath, created.metaAction));
2426
- console.log(formatInitPathAction(created.taxonomyPath, created.taxonomyAction));
2427
1732
  console.log(formatInitPathAction(created.eventsPath, created.eventsAction));
2428
1733
  console.log(formatInitPathAction(created.forensicPath, created.forensicAction));
2429
- writeStderr3(formatOptionalInitPathAction(created.claudeSkillPath, created.claudeSkillAction));
2430
- writeStderr3(formatOptionalInitPathAction(created.codexSkillPath, created.codexSkillAction));
2431
- writeStderr3(
2432
- formatOptionalInitPathAction(created.codexSessionStartHookPath, created.codexSessionStartHookAction)
2433
- );
2434
- writeStderr3(
2435
- formatOptionalInitPathAction(created.codexStopHookPath, created.codexStopHookAction)
2436
- );
2437
- writeStderr3(formatCodexHooksAction(created.codexHooksConfigPath, created.codexHooksConfigAction));
2438
- writeStderr3(formatOptionalInitPathAction(created.claudeHookPath, created.claudeHookAction));
2439
- writeStderr3(formatClaudeSettingsAction(created.claudeSettingsPath, created.claudeSettingsAction));
2440
1734
  }
2441
1735
  function printInitPostSetup(plan, stageResults, finalSupports) {
2442
1736
  if (shouldPrintHooksNextStep(plan.options, stageResults)) {
@@ -2470,30 +1764,17 @@ function printInitPlanPreview(plan) {
2470
1764
  }
2471
1765
  function buildPlanOnlyScaffoldResult(plan) {
2472
1766
  return {
2473
- bootstrapPath: plan.bootstrapPath,
2474
- bootstrapAction: plan.bootstrapAction,
1767
+ agentsMdPath: plan.agentsMdPath,
1768
+ agentsMdAction: plan.agentsMdAction,
1769
+ knowledgeDir: plan.knowledgeDir,
1770
+ knowledgeDirAction: plan.knowledgeDirAction,
1771
+ personalKnowledgeDir: plan.personalKnowledgeDir,
2475
1772
  metaPath: plan.metaPath,
2476
1773
  metaAction: plan.metaAction,
2477
- taxonomyPath: plan.taxonomyPath,
2478
- taxonomyAction: plan.taxonomyAction,
2479
1774
  eventsPath: plan.eventsPath,
2480
1775
  eventsAction: plan.eventsAction,
2481
1776
  forensicPath: plan.forensicPath,
2482
- forensicAction: plan.forensicAction,
2483
- claudeSkillPath: plan.claudeSkill.path,
2484
- claudeSkillAction: plan.claudeSkill.action,
2485
- codexSkillPath: plan.codexSkill.path,
2486
- codexSkillAction: plan.codexSkill.action,
2487
- codexSessionStartHookPath: plan.codexSessionStartHook.path,
2488
- codexSessionStartHookAction: plan.codexSessionStartHook.action,
2489
- codexStopHookPath: plan.codexStopHook.path,
2490
- codexStopHookAction: plan.codexStopHook.action,
2491
- codexHooksConfigPath: plan.codexHooksConfig.path,
2492
- codexHooksConfigAction: plan.codexHooksConfig.action,
2493
- claudeHookPath: plan.claudeHook.path,
2494
- claudeHookAction: plan.claudeHook.action,
2495
- claudeSettingsPath: plan.claudeSettings.path,
2496
- claudeSettingsAction: plan.claudeSettings.action
1777
+ forensicAction: plan.forensicAction
2497
1778
  };
2498
1779
  }
2499
1780
  async function executeInitStagePlan(plan, stageName) {
@@ -2508,25 +1789,38 @@ async function executeInitStagePlan(plan, stageName) {
2508
1789
  try {
2509
1790
  switch (stage.name) {
2510
1791
  case "bootstrap": {
2511
- const result = await installBootstrap(plan.target, { force: plan.options.force });
2512
- if (result.details.length === 0) {
2513
- console.log(formatInitStageResult("bootstrap", "skipped", 0, 0, t("cli.bootstrap.install.no-targets")));
2514
- return { name: "bootstrap", disposition: "skipped" };
1792
+ const installResults = [];
1793
+ installResults.push(...await runBestEffort("skill-install", () => installFabricArchiveSkill(plan.target)));
1794
+ installResults.push(...await runBestEffort("skill-review-install", () => installFabricReviewSkill(plan.target)));
1795
+ installResults.push(...await runBestEffort("skill-import-install", () => installFabricImportSkill(plan.target)));
1796
+ installResults.push(...await runBestEffort("hook-script", () => installArchiveHintHook(plan.target)));
1797
+ installResults.push(...await runBestEffort("hook-broad-script", () => installKnowledgeHintBroadHook(plan.target)));
1798
+ installResults.push(...await runBestEffort("hook-narrow-script", () => installKnowledgeHintNarrowHook(plan.target)));
1799
+ installResults.push(await runBestEffortSingle("claude-hook-config", () => mergeClaudeCodeHookConfig(plan.target)));
1800
+ installResults.push(await runBestEffortSingle("codex-hook-config", () => mergeCodexHookConfig(plan.target)));
1801
+ installResults.push(await runBestEffortSingle("cursor-hook-config", () => mergeCursorHookConfig(plan.target)));
1802
+ installResults.push(...await runBestEffort("pointer", () => addArchiveSkillPointer(plan.target)));
1803
+ const installedCount = installResults.filter((r) => r.status === "written").length;
1804
+ const skippedCount = installResults.filter((r) => r.status === "skipped").length;
1805
+ const errorCount = installResults.filter((r) => r.status === "error").length;
1806
+ for (const result of installResults) {
1807
+ if (result.status === "error") {
1808
+ writeStderr2(`bootstrap ${result.step} ${result.path}: ${result.message ?? "unknown error"}`);
1809
+ }
2515
1810
  }
2516
- console.log(
2517
- formatInitStageResult("bootstrap", "completed", result.installed.length, result.skipped.length)
2518
- );
1811
+ const note2 = errorCount > 0 ? `errors=${errorCount}` : void 0;
1812
+ console.log(formatInitStageResult("bootstrap", "completed", installedCount, skippedCount, note2));
2519
1813
  return { name: "bootstrap", disposition: "ran" };
2520
1814
  }
2521
1815
  case "mcp": {
2522
1816
  if (stage.installMode === "local") {
2523
1817
  const manager = stage.packageManager ?? detectPackageManager(plan.target);
2524
- writeStderr3(t("cli.init.mcp.install.local"));
2525
- writeStderr3(t("cli.init.mcp.local.installing", { manager }));
1818
+ writeStderr2(t("cli.init.mcp.install.local"));
1819
+ writeStderr2(t("cli.init.mcp.local.installing", { manager }));
2526
1820
  installLocalFabricServer(plan.target, manager);
2527
- writeStderr3(t("cli.init.mcp.local.installed"));
1821
+ writeStderr2(t("cli.init.mcp.local.installed"));
2528
1822
  } else {
2529
- writeStderr3(t("cli.init.mcp.install.global"));
1823
+ writeStderr2(t("cli.init.mcp.install.global"));
2530
1824
  }
2531
1825
  const result = await installMcpClients(plan.target, {
2532
1826
  force: plan.options.force,
@@ -2549,15 +1843,15 @@ async function executeInitStagePlan(plan, stageName) {
2549
1843
  return exhaustiveInitStagePlan(stage);
2550
1844
  }
2551
1845
  } catch (error) {
2552
- writeStderr3(formatInitStageFailure(stageName, error));
1846
+ writeStderr2(formatInitStageFailure(stageName, error));
2553
1847
  return { name: stageName, disposition: "failed" };
2554
1848
  }
2555
1849
  }
2556
1850
  function shouldReplaceWritableDirectory(path, options) {
2557
- if (!existsSync9(path)) {
1851
+ if (!existsSync3(path)) {
2558
1852
  return false;
2559
1853
  }
2560
- if (statSync3(path).isDirectory()) {
1854
+ if (statSync2(path).isDirectory()) {
2561
1855
  return false;
2562
1856
  }
2563
1857
  if (!options?.force) {
@@ -2566,7 +1860,7 @@ function shouldReplaceWritableDirectory(path, options) {
2566
1860
  return true;
2567
1861
  }
2568
1862
  function planFreshPath(path, options) {
2569
- if (!existsSync9(path)) {
1863
+ if (!existsSync3(path)) {
2570
1864
  return "created";
2571
1865
  }
2572
1866
  if (!options?.force) {
@@ -2575,131 +1869,11 @@ function planFreshPath(path, options) {
2575
1869
  return "overwritten";
2576
1870
  }
2577
1871
  function preparePlannedPath(path, action) {
2578
- mkdirSync3(dirname5(path), { recursive: true });
2579
- if (action === "overwritten" && existsSync9(path)) {
1872
+ mkdirSync(dirname(path), { recursive: true });
1873
+ if (action === "overwritten" && existsSync3(path)) {
2580
1874
  rmSync(path, { recursive: true, force: true });
2581
1875
  }
2582
1876
  }
2583
- function buildOptionalTemplateWritePlan(path, templatePath, options, executable = false) {
2584
- const existed = existsSync9(path);
2585
- if (existed && !options?.force) {
2586
- return { path, action: "skipped", templatePath, executable };
2587
- }
2588
- return {
2589
- path,
2590
- action: existed ? "overwritten" : "created",
2591
- templatePath,
2592
- executable
2593
- };
2594
- }
2595
- function applyOptionalTemplateWritePlan(plan) {
2596
- if (plan.action === "skipped") {
2597
- return;
2598
- }
2599
- mkdirSync3(dirname5(plan.path), { recursive: true });
2600
- copyFileSync(plan.templatePath, plan.path);
2601
- if (plan.executable) {
2602
- chmodSync2(plan.path, 493);
2603
- }
2604
- }
2605
- function buildCodexHooksConfigValue() {
2606
- return {
2607
- hooks: {
2608
- SessionStart: [
2609
- {
2610
- matcher: "*",
2611
- hooks: [{ type: "command", command: CODEX_SESSION_START_COMMAND }]
2612
- }
2613
- ],
2614
- Stop: [
2615
- {
2616
- matcher: "*",
2617
- hooks: [{ type: "command", command: CODEX_STOP_COMMAND }]
2618
- }
2619
- ]
2620
- }
2621
- };
2622
- }
2623
- function buildCodexHooksConfigPlan(configPath, options) {
2624
- const action = !existsSync9(configPath) ? "created" : options?.force ? "overwritten" : "skipped";
2625
- return {
2626
- path: configPath,
2627
- action,
2628
- value: buildCodexHooksConfigValue()
2629
- };
2630
- }
2631
- async function applyJsonWritePlan(plan) {
2632
- if (plan.action === "skipped") {
2633
- return;
2634
- }
2635
- mkdirSync3(dirname5(plan.path), { recursive: true });
2636
- await atomicWriteJson3(plan.path, plan.value);
2637
- }
2638
- function buildClaudeSettingsWritePlan(settingsPath, options) {
2639
- let settings;
2640
- let action = "updated";
2641
- if (!existsSync9(settingsPath)) {
2642
- settings = {};
2643
- action = "created";
2644
- } else {
2645
- try {
2646
- const parsed = JSON.parse(readFileSync4(settingsPath, "utf8"));
2647
- if (!isRecord(parsed)) {
2648
- writeStderr3(t("cli.init.claude-settings.invalid-object", { label: skippedLabel(), path: settingsPath }));
2649
- return { path: settingsPath, action: "skipped-invalid", value: null };
2650
- }
2651
- settings = parsed;
2652
- } catch (error) {
2653
- const reason = error instanceof Error ? error.message : "unknown parse error";
2654
- writeStderr3(t("cli.init.claude-settings.invalid-json", { label: skippedLabel(), path: settingsPath, reason }));
2655
- return { path: settingsPath, action: "skipped-invalid", value: null };
2656
- }
2657
- }
2658
- if (settings.hooks !== void 0 && !isRecord(settings.hooks)) {
2659
- writeStderr3(t("cli.init.claude-settings.invalid-hooks", { label: skippedLabel(), path: settingsPath }));
2660
- return { path: settingsPath, action: "skipped-invalid", value: null };
2661
- }
2662
- const hooks = settings.hooks ?? {};
2663
- const stopHooksValue = hooks.Stop;
2664
- if (stopHooksValue !== void 0 && !Array.isArray(stopHooksValue)) {
2665
- writeStderr3(t("cli.init.claude-settings.invalid-stop-array", { label: skippedLabel(), path: settingsPath }));
2666
- return { path: settingsPath, action: "skipped-invalid", value: null };
2667
- }
2668
- const stopHooks = Array.isArray(stopHooksValue) ? stopHooksValue : [];
2669
- const hasExistingFabricHook = hasClaudeInitReminderHook(stopHooks);
2670
- if (hasExistingFabricHook && !options?.force) {
2671
- return { path: settingsPath, action: "skipped", value: null };
2672
- }
2673
- const nextStopHooks = hasExistingFabricHook && options?.force ? removeClaudeInitReminderHook(stopHooks) : [...stopHooks];
2674
- nextStopHooks.push({
2675
- matcher: "*",
2676
- hooks: [
2677
- {
2678
- type: "command",
2679
- command: CLAUDE_INIT_REMINDER_COMMAND
2680
- }
2681
- ]
2682
- });
2683
- const nextSettings = {
2684
- ...settings,
2685
- hooks: {
2686
- ...hooks,
2687
- Stop: nextStopHooks
2688
- }
2689
- };
2690
- return {
2691
- path: settingsPath,
2692
- action: hasExistingFabricHook && options?.force ? "overwritten" : action,
2693
- value: nextSettings
2694
- };
2695
- }
2696
- async function applyClaudeSettingsWritePlan(plan) {
2697
- if (plan.value === null) {
2698
- return;
2699
- }
2700
- mkdirSync3(dirname5(plan.path), { recursive: true });
2701
- await atomicWriteJson3(plan.path, plan.value);
2702
- }
2703
1877
  function createDefaultInitWizardAdapter() {
2704
1878
  return {
2705
1879
  async run(context) {
@@ -2871,23 +2045,23 @@ function formatInitModeBadge(options) {
2871
2045
  }
2872
2046
  return t("cli.init.mode.badge.default");
2873
2047
  }
2874
- function normalizeTarget4(targetInput) {
2875
- return isAbsolute4(targetInput) ? targetInput : resolve9(process.cwd(), targetInput);
2048
+ function normalizeTarget2(targetInput) {
2049
+ return isAbsolute2(targetInput) ? targetInput : resolve3(process.cwd(), targetInput);
2876
2050
  }
2877
- function assertExistingDirectory3(target) {
2878
- if (!existsSync9(target) || !statSync3(target).isDirectory()) {
2051
+ function assertExistingDirectory2(target) {
2052
+ if (!existsSync3(target) || !statSync2(target).isDirectory()) {
2879
2053
  throw new Error(`Target must be an existing directory: ${target}`);
2880
2054
  }
2881
2055
  }
2882
2056
  function detectPackageManager(cwd) {
2883
- const workspaceRoot = resolve9(cwd);
2884
- if (existsSync9(join8(workspaceRoot, "pnpm-lock.yaml"))) {
2057
+ const workspaceRoot = resolve3(cwd);
2058
+ if (existsSync3(join2(workspaceRoot, "pnpm-lock.yaml"))) {
2885
2059
  return "pnpm";
2886
2060
  }
2887
- if (existsSync9(join8(workspaceRoot, "yarn.lock"))) {
2061
+ if (existsSync3(join2(workspaceRoot, "yarn.lock"))) {
2888
2062
  return "yarn";
2889
2063
  }
2890
- if (existsSync9(join8(workspaceRoot, "package-lock.json"))) {
2064
+ if (existsSync3(join2(workspaceRoot, "package-lock.json"))) {
2891
2065
  return "npm";
2892
2066
  }
2893
2067
  return "npm";
@@ -2896,7 +2070,7 @@ function resolveMcpInstallMode(rawMode) {
2896
2070
  if (rawMode === void 0 || rawMode === "global" || rawMode === "local") {
2897
2071
  return rawMode ?? "global";
2898
2072
  }
2899
- writeStderr3(t("cli.init.mcp.install.invalid", { value: rawMode }));
2073
+ writeStderr2(t("cli.init.mcp.install.invalid", { value: rawMode }));
2900
2074
  return "global";
2901
2075
  }
2902
2076
  function installLocalFabricServer(target, manager) {
@@ -2907,20 +2081,11 @@ function installLocalFabricServer(target, manager) {
2907
2081
  shell: process.platform === "win32"
2908
2082
  });
2909
2083
  }
2910
- function createInitialMeta(agentsHash) {
2084
+ function createInitialMeta() {
2911
2085
  return {
2912
- revision: sha256(agentsHash),
2913
- nodes: {
2914
- L0: {
2915
- file: ".fabric/bootstrap/README.md",
2916
- scope_glob: "**",
2917
- deps: [],
2918
- priority: "high",
2919
- layer: "L0",
2920
- topology_type: "mirror",
2921
- hash: agentsHash
2922
- }
2923
- }
2086
+ revision: "sha256:initial",
2087
+ nodes: {},
2088
+ counters: defaultAgentsMetaCounters()
2924
2089
  };
2925
2090
  }
2926
2091
  function appendReapplyLedgerEvent(eventsPath, payload) {
@@ -2930,125 +2095,36 @@ function appendReapplyLedgerEvent(eventsPath, payload) {
2930
2095
  ts: Date.now(),
2931
2096
  schema_version: 1,
2932
2097
  event_type: "reapply_completed",
2933
- preserved_ledger: payload.preserved_ledger,
2934
- preserved_meta: payload.preserved_meta,
2935
- rules_count: payload.rules_count
2098
+ preserved_ledger: payload.preserved_ledger
2936
2099
  };
2937
2100
  const line = `${JSON.stringify(event)}
2938
2101
  `;
2939
2102
  appendFileSync(eventsPath, line, "utf8");
2940
2103
  }
2941
- function buildInitialTaxonomyMarkdown(forensicReport) {
2942
- const frameworkInfo = forensicReport.framework;
2943
- const framework = [frameworkInfo?.kind ?? "unknown", frameworkInfo?.subkind ?? ""].filter((value) => value.trim() !== "").join(" / ") || "unknown";
2944
- const keyDirs = forensicReport.topology?.key_dirs?.slice(0, 8) ?? [];
2945
- const candidateFiles = forensicReport.candidate_files?.slice(0, 8) ?? [];
2946
- const generatedAt = forensicReport.generated_at ?? (/* @__PURE__ */ new Date()).toISOString();
2947
- return `# Fabric Initial Taxonomy
2948
-
2949
- **Date**: ${generatedAt}
2950
- **Base Architecture**: L0/L1/L2 Tiered System
2951
- **Detected Framework**: ${framework}
2952
-
2953
- ## Origin Logic
2954
-
2955
- - **L0 \u5224\u5B9A**: \u5168\u5C40\u534F\u4F5C\u7A33\u5B9A\u6027\u89C4\u5219\u3002\u5178\u578B\u6765\u6E90\u5305\u62EC\u4ED3\u5E93\u6839\u914D\u7F6E\u3001package metadata\u3001Fabric \u5185\u90E8\u534F\u8BAE\u548C\u4E0D\u53EF\u968F\u5C40\u90E8\u4E1A\u52A1\u6F02\u79FB\u7684\u7EA6\u675F\u3002
2956
- - **L1 \u5224\u5B9A**: \u9886\u57DF/\u6A21\u5757\u7EA7\u89C4\u5219\u3002\u4F9D\u636E\u6280\u672F\u6808\u3001\u76EE\u5F55\u804C\u8D23\u3001\u6846\u67B6\u7279\u5F81\u548C\u529F\u80FD\u6A21\u5757\u5212\u5206\uFF0C\u800C\u4E0D\u662F\u8DEF\u5F84\u6DF1\u5EA6\u3002
2957
- - **L2 \u5224\u5B9A**: \u5177\u4F53\u811A\u672C\u3001\u8D44\u6E90\u6216\u5C40\u90E8\u4E1A\u52A1\u72B6\u6001\u89C4\u5219\u3002\u7528\u4E8E\u627F\u8F7D\u7279\u5B9A\u6587\u4EF6\u3001\u8D44\u6E90\u3001\u5386\u53F2\u8865\u4E01\u548C\u5C40\u90E8\u5904\u7406\u7EC6\u5219\u3002
2958
-
2959
- ## Initial L1 Buckets
2960
-
2961
- ${formatInitialL1Buckets(keyDirs)}
2962
-
2963
- ## L2 Candidate Signals
2964
-
2965
- ${formatInitialL2Signals(candidateFiles)}
2966
-
2967
- ## Evolution Guide
2968
-
2969
- - \u6D89\u53CA\u5168\u4ED3\u534F\u4F5C\u7A33\u5B9A\u6027\u7684\u89C4\u5219\u8FDB\u5165 L0\u3002
2970
- - \u6D89\u53CA\u6280\u672F\u9886\u57DF\u3001\u6846\u67B6\u6A21\u5757\u6216\u529F\u80FD\u6A21\u5757\u7684\u89C4\u5219\u8FDB\u5165 L1\u3002
2971
- - \u6D89\u53CA\u5177\u4F53\u6587\u4EF6\u3001\u5177\u4F53\u8D44\u6E90\u6216\u5C40\u90E8\u4E1A\u52A1\u72B6\u6001\u7684\u89C4\u5219\u8FDB\u5165 L2\u3002
2972
- - \u51B2\u7A81\u65F6\u6267\u884C\u89E3\u91CA\u56FA\u5B9A\u4E3A L2 > L1 > L0\uFF1B\u540C\u5C42\u5185\u624D\u4F7F\u7528 priority \u6392\u5E8F\u3002
2973
- `;
2974
- }
2975
- function formatInitialL1Buckets(keyDirs) {
2976
- if (keyDirs.length === 0) {
2977
- return "- **L1-General**: \u521D\u59CB\u5316\u65F6\u672A\u68C0\u6D4B\u5230\u7A33\u5B9A\u76EE\u5F55\u8F74\u7EBF\uFF0C\u540E\u7EED\u4F9D\u636E\u6280\u672F\u6808\u548C\u6A21\u5757\u804C\u8D23\u6F14\u8FDB\u3002";
2978
- }
2979
- return keyDirs.map((dir) => `- **L1-${sanitizeTaxonomyLabel(dir)}**: \u6302\u8F7D\u4F9D\u636E\u2014\u2014forensic topology detected \`${dir}\`.`).join("\n");
2980
- }
2981
- function formatInitialL2Signals(candidateFiles) {
2982
- if (candidateFiles.length === 0) {
2983
- return "- \u6682\u672A\u8BC6\u522B\u660E\u786E L2 \u5019\u9009\u6587\u4EF6\u3002";
2984
- }
2985
- return candidateFiles.map((entry) => `- \`${entry.path}\`: ${entry.family} \u2014 ${entry.rationale}`).join("\n");
2986
- }
2987
- function sanitizeTaxonomyLabel(value) {
2988
- const sanitized = value.replaceAll("\\", "/").split("/").filter(Boolean).join("-").replace(/[^A-Za-z0-9_-]+/gu, "-").replace(/^-+|-+$/gu, "");
2989
- return sanitized === "" ? "General" : sanitized;
2990
- }
2991
- function ensureTrailingNewline2(value) {
2992
- return value.endsWith("\n") ? value : `${value}
2993
- `;
2994
- }
2995
- function findTemplatePath3(relativePath) {
2996
- const currentModuleDir = dirname5(fileURLToPath4(import.meta.url));
2997
- const candidates = [
2998
- ...templateCandidatesFrom3(process.cwd(), relativePath),
2999
- ...templateCandidatesFrom3(currentModuleDir, relativePath)
3000
- ];
3001
- for (const candidate of candidates) {
3002
- if (existsSync9(candidate)) {
3003
- return candidate;
3004
- }
3005
- }
3006
- throw new Error(t("cli.shared.template-not-found", { path: relativePath }));
3007
- }
3008
- function templateCandidatesFrom3(start, relativePath) {
3009
- const candidates = [];
3010
- let current = resolve9(start);
3011
- while (true) {
3012
- candidates.push(join8(current, ...relativePath.split("/")));
3013
- const parent = dirname5(current);
3014
- if (parent === current || parse3(current).root === current) {
3015
- break;
3016
- }
3017
- current = parent;
3018
- }
3019
- return candidates.reverse();
3020
- }
3021
- function hasClaudeInitReminderHook(stopHooks) {
3022
- return stopHooks.some((entry) => isClaudeInitReminderStopEntry(entry));
3023
- }
3024
- function removeClaudeInitReminderHook(stopHooks) {
3025
- return stopHooks.filter((entry) => !isClaudeInitReminderStopEntry(entry));
3026
- }
3027
- function isClaudeInitReminderStopEntry(entry) {
3028
- if (!isRecord(entry) || !Array.isArray(entry.hooks)) {
3029
- return false;
2104
+ async function runBestEffort(step, fn) {
2105
+ try {
2106
+ return await fn();
2107
+ } catch (error) {
2108
+ return [
2109
+ {
2110
+ step,
2111
+ path: "",
2112
+ status: "error",
2113
+ message: error instanceof Error ? error.message : String(error)
2114
+ }
2115
+ ];
3030
2116
  }
3031
- return entry.hooks.some(
3032
- (hook) => isRecord(hook) && hook.type === "command" && typeof hook.command === "string" && (hook.command.includes("fabric-init-reminder.cjs") || hook.command.includes("agents-md-init-reminder.cjs"))
3033
- );
3034
2117
  }
3035
- function isRecord(value) {
3036
- return typeof value === "object" && value !== null && !Array.isArray(value);
3037
- }
3038
- function formatClaudeSettingsAction(settingsPath, action) {
3039
- switch (action) {
3040
- case "created":
3041
- return t("cli.init.claude-settings.created", { label: createdLabel(), path: settingsPath });
3042
- case "updated":
3043
- return t("cli.init.claude-settings.updated", { label: updatedLabel(), path: settingsPath });
3044
- case "overwritten":
3045
- return t("cli.init.claude-settings.updated", { label: overwrittenLabel(), path: settingsPath });
3046
- case "skipped":
3047
- return t("cli.init.claude-settings.skipped", { label: skippedLabel(), path: settingsPath });
3048
- case "skipped-invalid":
3049
- return t("cli.init.claude-settings.skipped-invalid", { label: skippedLabel(), path: settingsPath });
3050
- default:
3051
- return t("cli.init.claude-settings.updated", { label: updatedLabel(), path: settingsPath });
2118
+ async function runBestEffortSingle(step, fn) {
2119
+ try {
2120
+ return await fn();
2121
+ } catch (error) {
2122
+ return {
2123
+ step,
2124
+ path: "",
2125
+ status: "error",
2126
+ message: error instanceof Error ? error.message : String(error)
2127
+ };
3052
2128
  }
3053
2129
  }
3054
2130
  function formatInitStageHeader(message) {
@@ -3101,11 +2177,11 @@ function printInitPlanSummary(target, options, mcpInstallMode, supports) {
3101
2177
  })
3102
2178
  );
3103
2179
  console.log(t("cli.init.plan.writes"));
3104
- console.log(` - ${target}/.fabric/bootstrap/README.md`);
2180
+ console.log(` - ${target}/.fabric/knowledge/{decisions,pitfalls,guidelines,models,processes,pending}/`);
3105
2181
  console.log(` - ${target}/.fabric/agents.meta.json`);
3106
- console.log(` - ${target}/.fabric/INITIAL_TAXONOMY.md`);
3107
2182
  console.log(` - ${target}/.fabric/events.jsonl`);
3108
2183
  console.log(` - ${target}/.fabric/forensic.json`);
2184
+ console.log(` - ${target}/.fabric/fabric-config.json`);
3109
2185
  }
3110
2186
  function printInitCapabilitySummary(supports, stageResults, options) {
3111
2187
  const detected = supports.filter((support) => support.detected);
@@ -3137,18 +2213,6 @@ function printInitCapabilitySummary(supports, stageResults, options) {
3137
2213
  console.log(formatCapabilityTableRow(row, widths));
3138
2214
  }
3139
2215
  }
3140
- function formatCodexHooksAction(configPath, action) {
3141
- switch (action) {
3142
- case "created":
3143
- return t("cli.init.codex-hooks.created", { label: createdLabel(), path: configPath });
3144
- case "overwritten":
3145
- return t("cli.init.codex-hooks.updated", { label: overwrittenLabel(), path: configPath });
3146
- case "skipped":
3147
- return t("cli.init.codex-hooks.skipped", { label: skippedLabel(), path: configPath });
3148
- default:
3149
- return t("cli.init.codex-hooks.updated", { label: updatedLabel(), path: configPath });
3150
- }
3151
- }
3152
2216
  function toCapabilityRow(support, stageResults, options) {
3153
2217
  const stage = (name) => stageResults.find((entry) => entry.name === name)?.disposition ?? null;
3154
2218
  const bootstrap = support.capabilities.bootstrap ? capabilityStatus(options.skipBootstrap ? "skipped" : stage("bootstrap")) : t("cli.init.capabilities.status.na");
@@ -3209,18 +2273,6 @@ function formatCapabilityDivider(widths) {
3209
2273
  }
3210
2274
  function formatInitReasonMessage(supports) {
3211
2275
  const detected = supports.filter((support) => support.detected);
3212
- const installedSkillClients = detected.filter((support) => hasInstalledCapability(support, "skill"));
3213
- const hasClaudeSkill = installedSkillClients.some((support) => support.clientKind === "ClaudeCodeCLI");
3214
- const hasCodexSkill = installedSkillClients.some((support) => support.clientKind === "CodexCLI");
3215
- if (hasClaudeSkill && hasCodexSkill) {
3216
- return t("cli.init.reason-message.multi-body");
3217
- }
3218
- if (hasClaudeSkill) {
3219
- return t("cli.init.reason-message.claude-body");
3220
- }
3221
- if (hasCodexSkill) {
3222
- return t("cli.init.reason-message.codex-body");
3223
- }
3224
2276
  if (detected.some((support) => support.capabilities.skill)) {
3225
2277
  return t("cli.init.reason-message.installable-body");
3226
2278
  }
@@ -3232,11 +2284,11 @@ function yesNoLabel(value) {
3232
2284
  function formatInitPathAction(path, action) {
3233
2285
  return t("cli.init.created-path", { label: labelForInitWriteAction(action), path });
3234
2286
  }
3235
- function formatOptionalInitPathAction(path, action) {
3236
- if (action === "skipped") {
2287
+ function formatAgentsMdAction(path, action) {
2288
+ if (action === "preserved") {
3237
2289
  return t("cli.init.skipped-existing-path", { label: skippedLabel(), path });
3238
2290
  }
3239
- return formatInitPathAction(path, action);
2291
+ return t("cli.init.created-path", { label: createdLabel(), path });
3240
2292
  }
3241
2293
  function labelForInitWriteAction(action) {
3242
2294
  return action === "overwritten" ? overwrittenLabel() : createdLabel();
@@ -3253,9 +2305,6 @@ function nextLabel() {
3253
2305
  function reasonLabel() {
3254
2306
  return paint.human(t("cli.shared.reason"));
3255
2307
  }
3256
- function updatedLabel() {
3257
- return paint.success(t("cli.shared.updated"));
3258
- }
3259
2308
  function overwrittenLabel() {
3260
2309
  return paint.warn(t("cli.init.force.overwritten"));
3261
2310
  }
@@ -3268,13 +2317,10 @@ function skippedStageLabel() {
3268
2317
  function failedStageLabel() {
3269
2318
  return paint.error(t("cli.init.stages.failed"));
3270
2319
  }
3271
- function writeStderr3(message) {
2320
+ function writeStderr2(message) {
3272
2321
  process.stderr.write(`${message}
3273
2322
  `);
3274
2323
  }
3275
- function sha256(content) {
3276
- return `sha256:${createHash("sha256").update(content).digest("hex")}`;
3277
- }
3278
2324
  export {
3279
2325
  buildInitExecutionPlan,
3280
2326
  buildInitFabricPlan,