@fenglimg/fabric-cli 1.5.2 → 1.7.0

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 (42) hide show
  1. package/README.md +8 -14
  2. package/dist/{chunk-QSAEGVKE.js → chunk-NMMUETVK.js} +4 -8
  3. package/dist/{chunk-AEOYCVBG.js → chunk-QPCRBQ5Y.js} +52 -5
  4. package/dist/doctor-XP4OQOTX.js +92 -0
  5. package/dist/index.js +5 -20
  6. package/dist/{init-FDTUWEZX.js → init-WMB3WLXM.js} +1153 -144
  7. package/dist/{scan-QH76LC7Z.js → scan-NNBNGIZG.js} +2 -4
  8. package/dist/{serve-4J2CQY25.js → serve-7AXYKBIT.js} +3 -7
  9. package/package.json +3 -3
  10. package/templates/agents-md/AGENTS.md.template +7 -7
  11. package/templates/agents-md/variants/cocos.md +7 -7
  12. package/templates/agents-md/variants/next.md +7 -7
  13. package/templates/agents-md/variants/vite.md +7 -7
  14. package/templates/bootstrap/CLAUDE.md +3 -1
  15. package/templates/bootstrap/GEMINI.md +3 -1
  16. package/templates/bootstrap/codex-AGENTS-header.md +3 -1
  17. package/templates/bootstrap/cursor-fabric-bootstrap.mdc +5 -6
  18. package/templates/bootstrap/roo-fabric.md +5 -6
  19. package/templates/bootstrap/windsurf-fabric.md +5 -6
  20. package/templates/claude-skills/agents-md-init/SKILL.md +1 -1
  21. package/templates/codex-skills/fabric-init/SKILL.md +1 -1
  22. package/templates/husky/pre-commit +9 -24
  23. package/dist/approve-YT4DEABS.js +0 -138
  24. package/dist/bootstrap-VGL3AR26.js +0 -16
  25. package/dist/chunk-2YW5CJ32.js +0 -147
  26. package/dist/chunk-6ICJICVU.js +0 -10
  27. package/dist/chunk-BEKSXO5N.js +0 -442
  28. package/dist/chunk-BVTMVW5M.js +0 -159
  29. package/dist/chunk-KOAEIH72.js +0 -270
  30. package/dist/chunk-L43IGJ6X.js +0 -106
  31. package/dist/chunk-T2WJF5I3.js +0 -254
  32. package/dist/chunk-WWNXR34K.js +0 -49
  33. package/dist/chunk-YDZJRLHL.js +0 -155
  34. package/dist/config-EC5L2QNI.js +0 -16
  35. package/dist/doctor-4BPYHV7V.js +0 -134
  36. package/dist/hooks-ZSWVH2JD.js +0 -12
  37. package/dist/human-lint-YSFOZHZ7.js +0 -13
  38. package/dist/ledger-append-3MDNR3GU.js +0 -10
  39. package/dist/pre-commit-53ENJDRZ.js +0 -98
  40. package/dist/sync-meta-IZR2WLIL.js +0 -16
  41. package/dist/update-M5M5PYKE.js +0 -116
  42. package/templates/fabric/human-lock.json +0 -12
@@ -1,47 +1,988 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- buildFabricBootstrapGuide,
4
- installBootstrap
5
- } from "./chunk-T2WJF5I3.js";
6
- import {
3
+ createScanReport,
7
4
  detectFramework
8
- } from "./chunk-QSAEGVKE.js";
5
+ } from "./chunk-NMMUETVK.js";
9
6
  import {
10
7
  createDebugLogger,
11
- resolveDevMode
12
- } from "./chunk-AEOYCVBG.js";
13
- import {
14
8
  displayWidth,
15
9
  padEnd,
16
- paint
17
- } from "./chunk-WWNXR34K.js";
18
- import {
19
- installMcpClients
20
- } from "./chunk-BVTMVW5M.js";
21
- import {
22
- detectClientSupports
23
- } from "./chunk-BEKSXO5N.js";
24
- import {
25
- installHooks
26
- } from "./chunk-YDZJRLHL.js";
27
- import {
10
+ paint,
11
+ readFabricConfig,
12
+ resolveDevMode,
28
13
  t
29
- } from "./chunk-6ICJICVU.js";
14
+ } from "./chunk-QPCRBQ5Y.js";
30
15
 
31
16
  // src/commands/init.ts
32
17
  import { createHash } from "crypto";
33
18
  import * as childProcess from "child_process";
34
- import { chmodSync, copyFileSync, existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, renameSync, rmSync, statSync as statSync2, writeFileSync } from "fs";
35
- import { dirname, isAbsolute as isAbsolute2, join as join2, parse, resolve as resolve2 } from "path";
36
- import { fileURLToPath } from "url";
19
+ import { chmodSync as chmodSync2, copyFileSync, existsSync as existsSync9, mkdirSync as mkdirSync3, readFileSync as readFileSync4, renameSync, rmSync, statSync as statSync3, writeFileSync as writeFileSync3 } 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";
37
22
  import { cancel, confirm, group, intro, isCancel, log, note, outro, select } from "@clack/prompts";
23
+ import { defineCommand as defineCommand4 } from "citty";
24
+
25
+ // src/bootstrap-guide.ts
26
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
27
+ import { dirname, isAbsolute, join, parse, resolve } from "path";
28
+ import { fileURLToPath } from "url";
29
+ var AGENTS_TEMPLATE_BY_FRAMEWORK = {
30
+ "cocos-creator": "templates/agents-md/variants/cocos.md",
31
+ vite: "templates/agents-md/variants/vite.md",
32
+ next: "templates/agents-md/variants/next.md"
33
+ };
34
+ var FABRIC_GUIDE_PATH = ".fabric/bootstrap/README.md";
35
+ async function buildFabricBootstrapGuide(target) {
36
+ const workspaceRoot = normalizeTarget(target);
37
+ const scanReport = await createScanReport(workspaceRoot);
38
+ const template = readFileSync(findBootstrapTemplatePath(scanReport.framework.kind), "utf8");
39
+ const packageName = readPackageName(workspaceRoot) ?? parse(workspaceRoot).base;
40
+ return ensureTrailingNewline(
41
+ template.replaceAll("{ projectName }", packageName).replaceAll("{ frameworkKind }", scanReport.framework.kind)
42
+ );
43
+ }
44
+ async function ensureFabricBootstrapGuide(workspaceRoot, force) {
45
+ const guidePath = resolve(workspaceRoot, FABRIC_GUIDE_PATH);
46
+ if (existsSync(guidePath) && !force) {
47
+ return;
48
+ }
49
+ mkdirSync(dirname(guidePath), { recursive: true });
50
+ writeFileSync(guidePath, await buildFabricBootstrapGuide(workspaceRoot), "utf8");
51
+ }
52
+ function findBootstrapTemplatePath(frameworkKind) {
53
+ const relativePath = AGENTS_TEMPLATE_BY_FRAMEWORK[frameworkKind] ?? "templates/agents-md/AGENTS.md.template";
54
+ return findTemplatePath(relativePath);
55
+ }
56
+ function readPackageName(target) {
57
+ const packageJsonPath = join(target, "package.json");
58
+ if (!existsSync(packageJsonPath)) {
59
+ return void 0;
60
+ }
61
+ try {
62
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
63
+ return packageJson.name;
64
+ } catch {
65
+ return void 0;
66
+ }
67
+ }
68
+ function findTemplatePath(relativePath) {
69
+ const currentModuleDir = dirname(fileURLToPath(import.meta.url));
70
+ const candidates = [
71
+ ...templateCandidatesFrom(process.cwd(), relativePath),
72
+ ...templateCandidatesFrom(currentModuleDir, relativePath)
73
+ ];
74
+ for (const candidate of candidates) {
75
+ if (existsSync(candidate)) {
76
+ return candidate;
77
+ }
78
+ }
79
+ throw new Error(t("cli.shared.template-not-found", { path: relativePath }));
80
+ }
81
+ function templateCandidatesFrom(start, relativePath) {
82
+ const candidates = [];
83
+ let current = resolve(start);
84
+ while (true) {
85
+ candidates.push(join(current, ...relativePath.split("/")));
86
+ const parent = dirname(current);
87
+ if (parent === current || parse(current).root === current) {
88
+ break;
89
+ }
90
+ current = parent;
91
+ }
92
+ return candidates.reverse();
93
+ }
94
+ function ensureTrailingNewline(content) {
95
+ return content.endsWith("\n") ? content : `${content}
96
+ `;
97
+ }
98
+ function normalizeTarget(targetInput) {
99
+ return isAbsolute(targetInput) ? targetInput : resolve(process.cwd(), targetInput);
100
+ }
101
+
102
+ // src/commands/bootstrap.ts
103
+ import { resolve as resolve5 } from "path";
38
104
  import { defineCommand } from "citty";
39
105
 
106
+ // src/config/resolver.ts
107
+ import { existsSync as existsSync5 } from "fs";
108
+ import { join as join5 } from "path";
109
+ import { homedir as homedir4 } from "os";
110
+
111
+ // src/config/claude-code.ts
112
+ import { existsSync as existsSync3 } from "fs";
113
+ import { join as join3, resolve as resolve3 } from "path";
114
+ import { homedir as homedir2, platform } from "os";
115
+
116
+ // src/config/json.ts
117
+ import { existsSync as existsSync2 } from "fs";
118
+ import { mkdir, readFile, writeFile } from "fs/promises";
119
+ import { dirname as dirname2, join as join2, resolve as resolve2 } from "path";
120
+ import { homedir } from "os";
121
+
122
+ // src/config/writer.ts
123
+ function createServerEntry(serverPath) {
124
+ return {
125
+ command: process.execPath,
126
+ args: [serverPath]
127
+ };
128
+ }
129
+
130
+ // src/config/json.ts
131
+ function expandHome(filePath) {
132
+ if (filePath === "~") {
133
+ return homedir();
134
+ }
135
+ if (filePath.startsWith("~/")) {
136
+ return join2(homedir(), filePath.slice(2));
137
+ }
138
+ return filePath;
139
+ }
140
+ function normalizeConfigPath(filePath) {
141
+ return resolve2(expandHome(filePath));
142
+ }
143
+ async function readJsonConfig(configPath) {
144
+ try {
145
+ const raw = await readFile(configPath, "utf8");
146
+ if (raw.trim().length === 0) {
147
+ return {};
148
+ }
149
+ const parsed = JSON.parse(raw);
150
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
151
+ throw new Error(`Expected JSON object in ${configPath}`);
152
+ }
153
+ return parsed;
154
+ } catch (error) {
155
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") {
156
+ return {};
157
+ }
158
+ throw error;
159
+ }
160
+ }
161
+ async function writeJsonClientConfig(configPath, serverEntry) {
162
+ const config = await readJsonConfig(configPath);
163
+ const existingServers = config.mcpServers;
164
+ config.mcpServers = existingServers !== null && typeof existingServers === "object" && !Array.isArray(existingServers) ? { ...existingServers, fabric: serverEntry } : { fabric: serverEntry };
165
+ await mkdir(dirname2(configPath), { recursive: true });
166
+ await writeFile(configPath, `${JSON.stringify(config, null, 2)}
167
+ `, "utf8");
168
+ }
169
+ var JsonClientConfigWriter = class {
170
+ configuredPath;
171
+ constructor(configuredPath) {
172
+ this.configuredPath = configuredPath;
173
+ }
174
+ async detect(workspaceRoot, overridePath) {
175
+ const explicitPath = overridePath ?? this.configuredPath;
176
+ if (explicitPath !== void 0) {
177
+ return normalizeConfigPath(explicitPath);
178
+ }
179
+ const configPath = this.defaultPath(workspaceRoot);
180
+ return configPath === null ? null : normalizeConfigPath(configPath);
181
+ }
182
+ async write(serverPath, workspaceRoot, overridePath) {
183
+ const configPath = await this.detect(workspaceRoot, overridePath);
184
+ if (configPath === null) {
185
+ return;
186
+ }
187
+ await writeJsonClientConfig(configPath, createServerEntry(serverPath));
188
+ }
189
+ };
190
+ var ClaudeCodeCLIWriter = class extends JsonClientConfigWriter {
191
+ clientKind = "ClaudeCodeCLI";
192
+ constructor(configuredPath) {
193
+ super(configuredPath);
194
+ }
195
+ // Writes to project-level .claude/settings.json so MCP is scoped to the project.
196
+ // Detection in resolver still checks ~/ to confirm Claude Code is installed.
197
+ defaultPath(workspaceRoot) {
198
+ const globalClaudeDir = join2(homedir(), ".claude");
199
+ const projectClaudeDir = join2(workspaceRoot, ".claude");
200
+ if (!existsSync2(globalClaudeDir) && !existsSync2(projectClaudeDir)) {
201
+ return null;
202
+ }
203
+ return join2(projectClaudeDir, "settings.json");
204
+ }
205
+ };
206
+ var CursorWriter = class extends JsonClientConfigWriter {
207
+ clientKind = "Cursor";
208
+ constructor(configuredPath) {
209
+ super(configuredPath);
210
+ }
211
+ defaultPath(workspaceRoot) {
212
+ const cursorDir = join2(workspaceRoot, ".cursor");
213
+ return existsSync2(cursorDir) ? join2(cursorDir, "mcp.json") : null;
214
+ }
215
+ };
216
+ var WindsurfWriter = class extends JsonClientConfigWriter {
217
+ clientKind = "Windsurf";
218
+ constructor(configuredPath) {
219
+ super(configuredPath);
220
+ }
221
+ defaultPath(workspaceRoot) {
222
+ const windsurfDir = join2(workspaceRoot, ".windsurf");
223
+ return existsSync2(windsurfDir) ? join2(windsurfDir, "mcp.json") : null;
224
+ }
225
+ };
226
+ var RooCodeWriter = class extends JsonClientConfigWriter {
227
+ clientKind = "RooCode";
228
+ constructor(configuredPath) {
229
+ super(configuredPath);
230
+ }
231
+ defaultPath(workspaceRoot) {
232
+ const rooDir = join2(workspaceRoot, ".roo");
233
+ return existsSync2(rooDir) ? join2(rooDir, "mcp.json") : null;
234
+ }
235
+ };
236
+ var GeminiCLIWriter = class extends JsonClientConfigWriter {
237
+ clientKind = "GeminiCLI";
238
+ constructor(configuredPath) {
239
+ super(configuredPath);
240
+ }
241
+ defaultPath(workspaceRoot) {
242
+ const geminiDir = join2(homedir(), ".gemini");
243
+ return existsSync2(geminiDir) || existsSync2(join2(workspaceRoot, "GEMINI.md")) ? join2(geminiDir, "settings.json") : null;
244
+ }
245
+ };
246
+
247
+ // src/config/claude-code.ts
248
+ function getClaudeDesktopConfigPath() {
249
+ const os = platform();
250
+ if (os === "darwin") {
251
+ return join3(homedir2(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
252
+ }
253
+ if (os === "win32") {
254
+ return join3(process.env.APPDATA ?? join3(homedir2(), "AppData", "Roaming"), "Claude", "claude_desktop_config.json");
255
+ }
256
+ return join3(homedir2(), ".config", "Claude", "claude_desktop_config.json");
257
+ }
258
+ var ClaudeCodeDesktopWriter = class {
259
+ clientKind = "ClaudeCodeDesktop";
260
+ configuredPath;
261
+ constructor(configuredPath) {
262
+ this.configuredPath = configuredPath;
263
+ }
264
+ async detect(_workspaceRoot, overridePath) {
265
+ const configPath = normalizeConfigPath(overridePath ?? this.configuredPath ?? getClaudeDesktopConfigPath());
266
+ return existsSync3(configPath) || overridePath !== void 0 || this.configuredPath !== void 0 ? configPath : null;
267
+ }
268
+ async write(serverPath, workspaceRoot, overridePath) {
269
+ const configPath = await this.detect(workspaceRoot, overridePath);
270
+ if (configPath === null) {
271
+ return;
272
+ }
273
+ await writeJsonClientConfig(configPath, {
274
+ command: process.execPath,
275
+ args: [serverPath]
276
+ });
277
+ }
278
+ };
279
+
280
+ // src/config/toml.ts
281
+ import { existsSync as existsSync4 } from "fs";
282
+ import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
283
+ import { dirname as dirname3, join as join4, resolve as resolve4 } from "path";
284
+ import { homedir as homedir3 } from "os";
285
+ function expandHome2(filePath) {
286
+ if (filePath === "~") {
287
+ return homedir3();
288
+ }
289
+ if (filePath.startsWith("~/")) {
290
+ return join4(homedir3(), filePath.slice(2));
291
+ }
292
+ return filePath;
293
+ }
294
+ function escapeTomlString(value) {
295
+ return JSON.stringify(value);
296
+ }
297
+ function serializeTomlStringArray(values) {
298
+ return `[${values.map((value) => escapeTomlString(value)).join(", ")}]`;
299
+ }
300
+ function serializeTomlInlineTable(values) {
301
+ const entries = Object.entries(values).sort(([left], [right]) => left.localeCompare(right)).map(([key, value]) => `${key} = ${escapeTomlString(value)}`);
302
+ return `{ ${entries.join(", ")} }`;
303
+ }
304
+ function serializeCodexServerBlock(serverName, serverEntry) {
305
+ const lines = [
306
+ `[mcp_servers.${serverName}]`,
307
+ `command = ${escapeTomlString(serverEntry.command)}`,
308
+ `args = ${serializeTomlStringArray(serverEntry.args)}`
309
+ ];
310
+ if (serverEntry.env !== void 0 && Object.keys(serverEntry.env).length > 0) {
311
+ lines.push(`env = ${serializeTomlInlineTable(serverEntry.env)}`);
312
+ }
313
+ return `${lines.join("\n")}
314
+ `;
315
+ }
316
+ function trimTrailingBlankLines(value) {
317
+ return value.replace(/\s+$/u, "");
318
+ }
319
+ function upsertCodexServerBlock(rawConfig, serverName, serverEntry) {
320
+ const block = serializeCodexServerBlock(serverName, serverEntry);
321
+ const normalized = rawConfig.replace(/\r\n/g, "\n");
322
+ const legacyPattern = new RegExp(String.raw`\n?\[mcp\.servers\.${serverName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\]\n[\s\S]*?(?=\n\[[^\n]+\]\n|$)`, "g");
323
+ const currentPattern = new RegExp(
324
+ String.raw`\n?\[mcp_servers\.${serverName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\]\n[\s\S]*?(?=\n\[[^\n]+\]\n|$)`,
325
+ "g"
326
+ );
327
+ const withoutLegacy = normalized.replace(legacyPattern, "");
328
+ const withoutExisting = withoutLegacy.replace(currentPattern, "");
329
+ const trimmed = trimTrailingBlankLines(withoutExisting);
330
+ if (trimmed.length === 0) {
331
+ return block;
332
+ }
333
+ return `${trimmed}
334
+
335
+ ${block}`;
336
+ }
337
+ async function readTomlConfigText(configPath) {
338
+ try {
339
+ return await readFile2(configPath, "utf8");
340
+ } catch (error) {
341
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") {
342
+ return "";
343
+ }
344
+ throw error;
345
+ }
346
+ }
347
+ var CodexTOMLConfigWriter = class {
348
+ clientKind = "CodexCLI";
349
+ configuredPath;
350
+ constructor(configuredPath) {
351
+ this.configuredPath = configuredPath;
352
+ }
353
+ async detect(_workspaceRoot, overridePath) {
354
+ const explicitPath = overridePath ?? this.configuredPath;
355
+ if (explicitPath !== void 0) {
356
+ return resolve4(expandHome2(explicitPath));
357
+ }
358
+ const codexDir = join4(homedir3(), ".codex");
359
+ return existsSync4(codexDir) ? resolve4(join4(codexDir, "config.toml")) : null;
360
+ }
361
+ async write(serverPath, workspaceRoot, overridePath) {
362
+ const configPath = await this.detect(workspaceRoot, overridePath);
363
+ if (configPath === null) {
364
+ return;
365
+ }
366
+ const rawConfig = await readTomlConfigText(configPath);
367
+ const nextConfig = upsertCodexServerBlock(rawConfig, "fabric", createServerEntry(serverPath));
368
+ await mkdir2(dirname3(configPath), { recursive: true });
369
+ await writeFile2(configPath, nextConfig, "utf8");
370
+ }
371
+ };
372
+
373
+ // src/config/resolver.ts
374
+ import { clientPathsSchema, fabricConfigSchema } from "@fenglimg/fabric-shared";
375
+ function hasExplicitPath(clientPaths, key) {
376
+ return typeof clientPaths?.[key] === "string" && clientPaths[key].trim().length > 0;
377
+ }
378
+ function addIfDetected(writers, detected, createWriter, configuredPath) {
379
+ if (configuredPath !== void 0 || detected) {
380
+ writers.push(createWriter(configuredPath));
381
+ }
382
+ }
383
+ function resolveClients(workspaceRoot, fabricConfig = {}) {
384
+ const clientPaths = fabricConfig.clientPaths;
385
+ const writers = [];
386
+ addIfDetected(
387
+ writers,
388
+ existsSync5(join5(homedir4(), ".claude")) || existsSync5(join5(workspaceRoot, ".claude")),
389
+ (configuredPath) => new ClaudeCodeCLIWriter(configuredPath),
390
+ hasExplicitPath(clientPaths, "claudeCodeCLI") ? clientPaths.claudeCodeCLI : void 0
391
+ );
392
+ addIfDetected(
393
+ writers,
394
+ existsSync5(getClaudeDesktopConfigPath()),
395
+ (configuredPath) => new ClaudeCodeDesktopWriter(configuredPath),
396
+ hasExplicitPath(clientPaths, "claudeCodeDesktop") ? clientPaths.claudeCodeDesktop : void 0
397
+ );
398
+ addIfDetected(
399
+ writers,
400
+ existsSync5(join5(workspaceRoot, ".cursor")),
401
+ (configuredPath) => new CursorWriter(configuredPath),
402
+ hasExplicitPath(clientPaths, "cursor") ? clientPaths.cursor : void 0
403
+ );
404
+ addIfDetected(
405
+ writers,
406
+ existsSync5(join5(workspaceRoot, ".windsurf")),
407
+ (configuredPath) => new WindsurfWriter(configuredPath),
408
+ hasExplicitPath(clientPaths, "windsurf") ? clientPaths.windsurf : void 0
409
+ );
410
+ addIfDetected(
411
+ writers,
412
+ existsSync5(join5(workspaceRoot, ".roo")),
413
+ (configuredPath) => new RooCodeWriter(configuredPath),
414
+ hasExplicitPath(clientPaths, "rooCode") ? clientPaths.rooCode : void 0
415
+ );
416
+ addIfDetected(
417
+ writers,
418
+ existsSync5(join5(homedir4(), ".gemini")) || existsSync5(join5(workspaceRoot, "GEMINI.md")),
419
+ (configuredPath) => new GeminiCLIWriter(configuredPath),
420
+ hasExplicitPath(clientPaths, "geminiCLI") ? clientPaths.geminiCLI : void 0
421
+ );
422
+ addIfDetected(
423
+ writers,
424
+ existsSync5(join5(homedir4(), ".codex")),
425
+ (configuredPath) => new CodexTOMLConfigWriter(configuredPath),
426
+ hasExplicitPath(clientPaths, "codexCLI") ? clientPaths.codexCLI : void 0
427
+ );
428
+ return writers;
429
+ }
430
+ function detectClientSupports(workspaceRoot, fabricConfig = {}) {
431
+ const clientPaths = fabricConfig.clientPaths;
432
+ const claudeDetected = existsSync5(join5(homedir4(), ".claude")) || existsSync5(join5(workspaceRoot, ".claude"));
433
+ const claudeDesktopDetected = existsSync5(getClaudeDesktopConfigPath());
434
+ const cursorDetected = existsSync5(join5(workspaceRoot, ".cursor"));
435
+ const windsurfDetected = existsSync5(join5(workspaceRoot, ".windsurf"));
436
+ const rooDetected = existsSync5(join5(workspaceRoot, ".roo"));
437
+ const geminiDetected = existsSync5(join5(homedir4(), ".gemini")) || existsSync5(join5(workspaceRoot, "GEMINI.md"));
438
+ const codexDetected = existsSync5(join5(homedir4(), ".codex"));
439
+ return [
440
+ {
441
+ clientKind: "ClaudeCodeCLI",
442
+ label: "Claude Code CLI",
443
+ detected: claudeDetected || hasExplicitPath(clientPaths, "claudeCodeCLI"),
444
+ bootstrapTargetPath: ".fabric/bootstrap/README.md",
445
+ configPath: "project .claude/settings.json",
446
+ capabilities: {
447
+ bootstrap: true,
448
+ mcp: true,
449
+ hook: true,
450
+ skill: true
451
+ },
452
+ installedCapabilities: {
453
+ hook: true,
454
+ skill: true
455
+ }
456
+ },
457
+ {
458
+ clientKind: "ClaudeCodeDesktop",
459
+ label: "Claude Code Desktop",
460
+ detected: claudeDesktopDetected || hasExplicitPath(clientPaths, "claudeCodeDesktop"),
461
+ bootstrapTargetPath: ".fabric/bootstrap/README.md",
462
+ configPath: "desktop Claude config",
463
+ capabilities: {
464
+ bootstrap: true,
465
+ mcp: true,
466
+ hook: false,
467
+ skill: false
468
+ }
469
+ },
470
+ {
471
+ clientKind: "Cursor",
472
+ label: "Cursor",
473
+ detected: cursorDetected || hasExplicitPath(clientPaths, "cursor"),
474
+ bootstrapTargetPath: ".fabric/bootstrap/README.md",
475
+ configPath: ".cursor/mcp.json",
476
+ capabilities: {
477
+ bootstrap: true,
478
+ mcp: true,
479
+ hook: false,
480
+ skill: false
481
+ }
482
+ },
483
+ {
484
+ clientKind: "Windsurf",
485
+ label: "Windsurf",
486
+ detected: windsurfDetected || hasExplicitPath(clientPaths, "windsurf"),
487
+ bootstrapTargetPath: ".fabric/bootstrap/README.md",
488
+ configPath: ".windsurf/mcp.json",
489
+ capabilities: {
490
+ bootstrap: true,
491
+ mcp: true,
492
+ hook: false,
493
+ skill: false
494
+ }
495
+ },
496
+ {
497
+ clientKind: "RooCode",
498
+ label: "Roo Code",
499
+ detected: rooDetected || hasExplicitPath(clientPaths, "rooCode"),
500
+ bootstrapTargetPath: ".fabric/bootstrap/README.md",
501
+ configPath: ".roo/mcp.json",
502
+ capabilities: {
503
+ bootstrap: true,
504
+ mcp: true,
505
+ hook: false,
506
+ skill: false
507
+ }
508
+ },
509
+ {
510
+ clientKind: "GeminiCLI",
511
+ label: "Gemini CLI",
512
+ detected: geminiDetected || hasExplicitPath(clientPaths, "geminiCLI"),
513
+ bootstrapTargetPath: ".fabric/bootstrap/README.md",
514
+ configPath: "~/.gemini/settings.json",
515
+ capabilities: {
516
+ bootstrap: true,
517
+ mcp: true,
518
+ hook: false,
519
+ skill: false
520
+ }
521
+ },
522
+ {
523
+ clientKind: "CodexCLI",
524
+ label: "Codex CLI",
525
+ detected: codexDetected || hasExplicitPath(clientPaths, "codexCLI"),
526
+ bootstrapTargetPath: ".fabric/bootstrap/README.md",
527
+ configPath: "~/.codex/config.toml",
528
+ capabilities: {
529
+ bootstrap: true,
530
+ mcp: true,
531
+ hook: true,
532
+ skill: true
533
+ },
534
+ installedCapabilities: {
535
+ hook: existsSync5(join5(workspaceRoot, ".codex", "hooks.json")),
536
+ skill: existsSync5(join5(workspaceRoot, ".agents", "skills", "fabric-init", "SKILL.md"))
537
+ }
538
+ }
539
+ ];
540
+ }
541
+
542
+ // src/commands/bootstrap.ts
543
+ var CLIENT_ALIASES = {
544
+ claude: "claude",
545
+ "claude-code": "claude",
546
+ claudecode: "claude",
547
+ claudecli: "claude",
548
+ claudecodecli: "claude",
549
+ claudedesktop: "claude",
550
+ claudecodedesktop: "claude",
551
+ cursor: "cursor",
552
+ windsurf: "windsurf",
553
+ roo: "roo",
554
+ "roo-code": "roo",
555
+ roocode: "roo",
556
+ gemini: "gemini",
557
+ "gemini-cli": "gemini",
558
+ geminicli: "gemini",
559
+ codex: "codex",
560
+ "codex-cli": "codex",
561
+ codexcli: "codex"
562
+ };
563
+ var bootstrapCommand = defineCommand({
564
+ meta: {
565
+ name: "bootstrap",
566
+ description: t("cli.bootstrap.description")
567
+ },
568
+ subCommands: {
569
+ install: defineCommand({
570
+ meta: {
571
+ name: "install",
572
+ description: t("cli.bootstrap.install.description")
573
+ },
574
+ args: {
575
+ clients: {
576
+ type: "string",
577
+ description: t("cli.bootstrap.install.args.clients.description")
578
+ }
579
+ },
580
+ async run({ args }) {
581
+ const workspaceRoot = process.cwd();
582
+ const selectedClients = parseClientFilter(args.clients);
583
+ const result = await installBootstrap(workspaceRoot, {
584
+ clients: selectedClients === null ? void 0 : Array.from(selectedClients, mapBootstrapClientToClientKind)
585
+ });
586
+ if (result.details.length === 0) {
587
+ process.stderr.write(
588
+ `${t("cli.bootstrap.install.no-targets")}
589
+ `
590
+ );
591
+ return;
592
+ }
593
+ for (const detail of result.details) {
594
+ if (detail.action === "skipped") {
595
+ process.stderr.write(`${t("cli.bootstrap.install.skipped-header", { path: detail.path })}
596
+ `);
597
+ continue;
598
+ }
599
+ if (detail.action === "prepended") {
600
+ process.stderr.write(`${t("cli.bootstrap.install.prepended", { path: detail.path })}
601
+ `);
602
+ continue;
603
+ }
604
+ process.stderr.write(`${t("cli.bootstrap.install.installed", { path: detail.path })}
605
+ `);
606
+ }
607
+ }
608
+ })
609
+ }
610
+ });
611
+ async function installBootstrap(target, options = {}) {
612
+ const workspaceRoot = resolve5(target);
613
+ const fabricConfig = readFabricConfig(workspaceRoot);
614
+ const targets = resolveBootstrapTargets(workspaceRoot, fabricConfig, options.clients);
615
+ const installed = [];
616
+ const skipped = [];
617
+ const details = [];
618
+ await ensureFabricBootstrapGuide(workspaceRoot, options.force);
619
+ for (const bootstrapTarget of targets) {
620
+ details.push({
621
+ client: bootstrapTarget.client,
622
+ path: resolve5(workspaceRoot, FABRIC_GUIDE_PATH),
623
+ action: "skipped"
624
+ });
625
+ skipped.push(bootstrapTarget.client);
626
+ }
627
+ return { installed, skipped, details };
628
+ }
629
+ function parseClientFilter(value) {
630
+ if (value === void 0 || value.trim().length === 0) {
631
+ return null;
632
+ }
633
+ const clients = /* @__PURE__ */ new Set();
634
+ for (const rawClient of value.split(",")) {
635
+ const alias = rawClient.trim().toLowerCase();
636
+ const client = CLIENT_ALIASES[alias];
637
+ if (client === void 0) {
638
+ throw new Error(t("cli.bootstrap.errors.unknown-client", { client: rawClient }));
639
+ }
640
+ clients.add(client);
641
+ }
642
+ return clients;
643
+ }
644
+ function resolveBootstrapTargets(workspaceRoot, fabricConfig, selectedClients) {
645
+ const targets = [];
646
+ const seenClients = /* @__PURE__ */ new Set();
647
+ const clientKinds = selectedClients ?? resolveClients(workspaceRoot, fabricConfig).map((writer) => writer.clientKind);
648
+ for (const clientKind of clientKinds) {
649
+ const bootstrapClient = mapClientKind(clientKind);
650
+ if (bootstrapClient === null || seenClients.has(bootstrapClient)) {
651
+ continue;
652
+ }
653
+ seenClients.add(bootstrapClient);
654
+ targets.push({ client: clientKind, bootstrapClient });
655
+ }
656
+ return targets;
657
+ }
658
+ function mapClientKind(clientKind) {
659
+ switch (clientKind) {
660
+ case "ClaudeCodeCLI":
661
+ case "ClaudeCodeDesktop":
662
+ return "claude";
663
+ case "Cursor":
664
+ return "cursor";
665
+ case "Windsurf":
666
+ return "windsurf";
667
+ case "RooCode":
668
+ return "roo";
669
+ case "GeminiCLI":
670
+ return "gemini";
671
+ case "CodexCLI":
672
+ return "codex";
673
+ default:
674
+ return null;
675
+ }
676
+ }
677
+ function mapBootstrapClientToClientKind(client) {
678
+ switch (client) {
679
+ case "claude":
680
+ return "ClaudeCodeCLI";
681
+ case "cursor":
682
+ return "Cursor";
683
+ case "windsurf":
684
+ return "Windsurf";
685
+ case "roo":
686
+ return "RooCode";
687
+ case "gemini":
688
+ return "GeminiCLI";
689
+ case "codex":
690
+ return "CodexCLI";
691
+ }
692
+ }
693
+
694
+ // src/commands/config.ts
695
+ import { existsSync as existsSync7 } from "fs";
696
+ import { readFile as readFile3 } from "fs/promises";
697
+ import { resolve as resolve7 } from "path";
698
+ import { fileURLToPath as fileURLToPath3 } from "url";
699
+ import { defineCommand as defineCommand3 } from "citty";
700
+
701
+ // src/commands/hooks.ts
702
+ import { chmodSync, existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync2, statSync, writeFileSync as writeFileSync2 } from "fs";
703
+ import { dirname as dirname4, isAbsolute as isAbsolute2, join as join6, parse as parse2, resolve as resolve6 } from "path";
704
+ import { fileURLToPath as fileURLToPath2 } from "url";
705
+ import { defineCommand as defineCommand2 } from "citty";
706
+ var hooksCommand = defineCommand2({
707
+ meta: {
708
+ name: "hooks",
709
+ description: t("cli.hooks.description")
710
+ },
711
+ subCommands: {
712
+ install: defineCommand2({
713
+ meta: {
714
+ name: "install",
715
+ description: t("cli.hooks.install.description")
716
+ },
717
+ args: {
718
+ target: {
719
+ type: "string",
720
+ description: t("cli.hooks.install.args.target.description"),
721
+ default: process.cwd()
722
+ }
723
+ },
724
+ async run({ args }) {
725
+ const result = await installHooks(args.target);
726
+ if (result.hookAction === "skipped") {
727
+ writeStderr(t("cli.hooks.install.hook-skipped", { path: result.hookPath }));
728
+ } else if (result.hookAction === "appended") {
729
+ writeStderr(t("cli.hooks.install.hook-appended", { path: result.hookPath }));
730
+ } else {
731
+ writeStderr(t("cli.hooks.install.hook-created", { path: result.hookPath }));
732
+ }
733
+ if (result.prepareAction === "left") {
734
+ writeStderr(t("cli.hooks.install.prepare-left", { path: result.packageJsonPath }));
735
+ } else {
736
+ writeStderr(t("cli.hooks.install.prepare-added", { path: result.packageJsonPath }));
737
+ }
738
+ }
739
+ })
740
+ }
741
+ });
742
+ async function installHooks(target, options = {}) {
743
+ const normalizedTarget = normalizeTarget2(target);
744
+ assertExistingDirectory(normalizedTarget);
745
+ const huskyDir = join6(normalizedTarget, ".husky");
746
+ const hookPath = join6(huskyDir, "pre-commit");
747
+ const packageJsonPath = join6(normalizedTarget, "package.json");
748
+ if (!existsSync6(packageJsonPath)) {
749
+ throw new Error(t("cli.hooks.errors.package-json-required", { path: packageJsonPath }));
750
+ }
751
+ mkdirSync2(huskyDir, { recursive: true });
752
+ const templateContent = readFileSync2(findTemplatePath2("templates/husky/pre-commit"), "utf8");
753
+ const hookAction = installHookFile(hookPath, templateContent, options.force);
754
+ chmodSync(hookPath, 493);
755
+ const packageJson = JSON.parse(readFileSync2(packageJsonPath, "utf8"));
756
+ const scripts = packageJson.scripts && typeof packageJson.scripts === "object" && !Array.isArray(packageJson.scripts) ? packageJson.scripts : {};
757
+ const hadPrepare = typeof scripts.prepare === "string" && scripts.prepare.trim().length > 0;
758
+ let prepareAction = "left";
759
+ if (!hadPrepare) {
760
+ scripts.prepare = "husky install";
761
+ packageJson.scripts = scripts;
762
+ writeFileSync2(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}
763
+ `, "utf8");
764
+ prepareAction = "added";
765
+ }
766
+ const installed = [];
767
+ const skipped = [];
768
+ if (hookAction === "skipped") {
769
+ skipped.push(hookPath);
770
+ } else {
771
+ installed.push(hookPath);
772
+ }
773
+ if (prepareAction === "left") {
774
+ skipped.push(packageJsonPath);
775
+ } else {
776
+ installed.push(packageJsonPath);
777
+ }
778
+ return {
779
+ installed,
780
+ skipped,
781
+ hookPath,
782
+ packageJsonPath,
783
+ hookAction,
784
+ prepareAction
785
+ };
786
+ }
787
+ function normalizeTarget2(targetInput) {
788
+ return isAbsolute2(targetInput) ? targetInput : resolve6(process.cwd(), targetInput);
789
+ }
790
+ function assertExistingDirectory(target) {
791
+ if (!existsSync6(target) || !statSync(target).isDirectory()) {
792
+ throw new Error(t("cli.shared.target-invalid", { target }));
793
+ }
794
+ }
795
+ function installHookFile(hookPath, templateContent, force) {
796
+ if (existsSync6(hookPath)) {
797
+ if (force) {
798
+ writeFileSync2(hookPath, templateContent, "utf8");
799
+ return "overwritten";
800
+ }
801
+ const existing = readFileSync2(hookPath, "utf8");
802
+ if (existing.includes("FAB_BIN=")) {
803
+ return "skipped";
804
+ }
805
+ const fabricBlock = templateContent.replace(/^#!\/bin\/sh\n/, "");
806
+ const separator = existing.endsWith("\n") ? "\n" : "\n\n";
807
+ writeFileSync2(hookPath, `${existing}${separator}# --- Fabric ---
808
+ ${fabricBlock}`, "utf8");
809
+ return "appended";
810
+ }
811
+ writeFileSync2(hookPath, templateContent, "utf8");
812
+ return "created";
813
+ }
814
+ function findTemplatePath2(relativePath) {
815
+ const currentModuleDir = dirname4(fileURLToPath2(import.meta.url));
816
+ const candidates = [
817
+ ...templateCandidatesFrom2(process.cwd(), relativePath),
818
+ ...templateCandidatesFrom2(currentModuleDir, relativePath)
819
+ ];
820
+ for (const candidate of candidates) {
821
+ if (existsSync6(candidate)) {
822
+ return candidate;
823
+ }
824
+ }
825
+ throw new Error(t("cli.shared.template-not-found", { path: relativePath }));
826
+ }
827
+ function templateCandidatesFrom2(start, relativePath) {
828
+ const candidates = [];
829
+ let current = resolve6(start);
830
+ while (true) {
831
+ candidates.push(join6(current, ...relativePath.split("/")));
832
+ const parent = dirname4(current);
833
+ if (parent === current || parse2(current).root === current) {
834
+ break;
835
+ }
836
+ current = parent;
837
+ }
838
+ return candidates.reverse();
839
+ }
840
+ function writeStderr(message) {
841
+ process.stderr.write(`${message}
842
+ `);
843
+ }
844
+
845
+ // src/commands/config.ts
846
+ var CLIENT_ALIASES2 = {
847
+ claude: "ClaudeCodeCLI",
848
+ claudecodecli: "ClaudeCodeCLI",
849
+ "claude-code-cli": "ClaudeCodeCLI",
850
+ claudecli: "ClaudeCodeCLI",
851
+ claudecodedesktop: "ClaudeCodeDesktop",
852
+ "claude-code-desktop": "ClaudeCodeDesktop",
853
+ claudedesktop: "ClaudeCodeDesktop",
854
+ cursor: "Cursor",
855
+ windsurf: "Windsurf",
856
+ roocode: "RooCode",
857
+ "roo-code": "RooCode",
858
+ roo: "RooCode",
859
+ geminicli: "GeminiCLI",
860
+ "gemini-cli": "GeminiCLI",
861
+ gemini: "GeminiCLI",
862
+ codexcli: "CodexCLI",
863
+ "codex-cli": "CodexCLI",
864
+ codex: "CodexCLI"
865
+ };
866
+ function parseClientFilter2(value) {
867
+ if (value === void 0 || value.trim().length === 0) {
868
+ return null;
869
+ }
870
+ const clients = /* @__PURE__ */ new Set();
871
+ for (const rawClient of value.split(",")) {
872
+ const alias = rawClient.trim().toLowerCase();
873
+ const clientKind = CLIENT_ALIASES2[alias];
874
+ if (clientKind === void 0) {
875
+ throw new Error(t("cli.config.errors.unknown-client", { client: rawClient }));
876
+ }
877
+ clients.add(clientKind);
878
+ }
879
+ return clients;
880
+ }
881
+ async function loadFabricConfig(workspaceRoot) {
882
+ const configPath = resolve7(workspaceRoot, "fabric.config.json");
883
+ if (!existsSync7(configPath)) {
884
+ return {};
885
+ }
886
+ const parsed = JSON.parse(await readFile3(configPath, "utf8"));
887
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
888
+ throw new Error(t("cli.config.errors.expected-object", { path: configPath }));
889
+ }
890
+ return parsed;
891
+ }
892
+ function resolveServerPath(override) {
893
+ if (override) return override;
894
+ if (process.env.FAB_SERVER_PATH) return resolve7(process.env.FAB_SERVER_PATH);
895
+ return fileURLToPath3(import.meta.resolve("@fenglimg/fabric-server"));
896
+ }
897
+ function writeStderr2(message) {
898
+ process.stderr.write(`${message}
899
+ `);
900
+ }
901
+ var configCmd = defineCommand3({
902
+ meta: {
903
+ name: "config",
904
+ description: t("cli.config.description")
905
+ },
906
+ subCommands: {
907
+ hooks: hooksCommand,
908
+ install: defineCommand3({
909
+ meta: {
910
+ name: "install",
911
+ description: t("cli.config.install.description")
912
+ },
913
+ args: {
914
+ clients: {
915
+ type: "string",
916
+ description: t("cli.config.install.args.clients.description")
917
+ },
918
+ "dry-run": {
919
+ type: "boolean",
920
+ description: t("cli.config.install.args.dry-run.description"),
921
+ default: false
922
+ }
923
+ },
924
+ async run({ args }) {
925
+ const selectedClients = parseClientFilter2(args.clients);
926
+ const result = await installMcpClients(process.cwd(), {
927
+ clients: selectedClients === null ? void 0 : Array.from(selectedClients),
928
+ dryRun: args["dry-run"]
929
+ });
930
+ if (result.details.length === 0) {
931
+ writeStderr2(t("cli.config.install.no-configs"));
932
+ return;
933
+ }
934
+ for (const detail of result.details) {
935
+ if (detail.action === "skipped") {
936
+ writeStderr2(t("cli.config.install.no-config-path", { client: detail.client }));
937
+ continue;
938
+ }
939
+ if (detail.action === "dry-run" && detail.path !== null) {
940
+ writeStderr2(t("cli.config.install.dry-run", { client: detail.client, path: detail.path }));
941
+ continue;
942
+ }
943
+ if (detail.path !== null) {
944
+ writeStderr2(t("cli.config.install.wrote", { client: detail.client, path: detail.path }));
945
+ }
946
+ }
947
+ }
948
+ })
949
+ }
950
+ });
951
+ async function installMcpClients(target, options = {}) {
952
+ const workspaceRoot = resolve7(target);
953
+ const fabricConfig = await loadFabricConfig(workspaceRoot);
954
+ const selectedClients = options.clients === void 0 ? null : new Set(options.clients);
955
+ const serverPath = resolveServerPath(options.localServerPath);
956
+ const writers = resolveClients(workspaceRoot, fabricConfig).filter(
957
+ (writer) => selectedClients === null ? true : selectedClients.has(writer.clientKind)
958
+ );
959
+ const installed = [];
960
+ const skipped = [];
961
+ const details = [];
962
+ for (const writer of writers) {
963
+ const configPath = await writer.detect(workspaceRoot);
964
+ if (configPath === null) {
965
+ skipped.push(writer.clientKind);
966
+ details.push({ client: writer.clientKind, path: null, action: "skipped" });
967
+ continue;
968
+ }
969
+ if (options.dryRun) {
970
+ skipped.push(writer.clientKind);
971
+ details.push({ client: writer.clientKind, path: configPath, action: "dry-run" });
972
+ continue;
973
+ }
974
+ await writer.write(serverPath, workspaceRoot);
975
+ installed.push(writer.clientKind);
976
+ details.push({ client: writer.clientKind, path: configPath, action: "wrote" });
977
+ }
978
+ return { installed, skipped, details };
979
+ }
980
+
40
981
  // src/scanner/forensic.ts
41
982
  import { execFileSync } from "child_process";
42
- import { existsSync, readdirSync, readFileSync, statSync } from "fs";
983
+ import { existsSync as existsSync8, readdirSync, readFileSync as readFileSync3, statSync as statSync2 } from "fs";
43
984
  import { createRequire } from "module";
44
- import { basename, extname, isAbsolute, join, posix, relative, resolve, sep } from "path";
985
+ import { basename, extname, isAbsolute as isAbsolute3, join as join7, posix, relative, resolve as resolve8, sep } from "path";
45
986
  import {
46
987
  forensicReportSchema
47
988
  } from "@fenglimg/fabric-shared";
@@ -127,7 +1068,7 @@ var parserInitPromise = null;
127
1068
  var languagePromiseByKind = {};
128
1069
  var parserBundlePromiseByKind = {};
129
1070
  async function buildForensicReport(targetInput) {
130
- const target = normalizeTarget(targetInput);
1071
+ const target = normalizeTarget3(targetInput);
131
1072
  const framework = detectFramework(target);
132
1073
  const topology = buildTopology(target);
133
1074
  const entryPoints = collectEntryPoints(target, topology.files);
@@ -163,11 +1104,11 @@ async function buildForensicReport(targetInput) {
163
1104
  }
164
1105
  return validation.data;
165
1106
  }
166
- function normalizeTarget(targetInput) {
167
- return isAbsolute(targetInput) ? targetInput : resolve(process.cwd(), targetInput);
1107
+ function normalizeTarget3(targetInput) {
1108
+ return isAbsolute3(targetInput) ? targetInput : resolve8(process.cwd(), targetInput);
168
1109
  }
169
1110
  function buildTopology(root) {
170
- assertExistingDirectory(root);
1111
+ assertExistingDirectory2(root);
171
1112
  const byExt = {};
172
1113
  const keyDirs = /* @__PURE__ */ new Set();
173
1114
  const files = [];
@@ -180,7 +1121,7 @@ function buildTopology(root) {
180
1121
  continue;
181
1122
  }
182
1123
  for (const entry of readdirSync(current, { withFileTypes: true })) {
183
- const absolutePath = join(current, entry.name);
1124
+ const absolutePath = join7(current, entry.name);
184
1125
  const relativePath = toPosixPath(relative(root, absolutePath));
185
1126
  if (relativePath.length === 0) {
186
1127
  continue;
@@ -200,7 +1141,7 @@ function buildTopology(root) {
200
1141
  if (!entry.isFile()) {
201
1142
  continue;
202
1143
  }
203
- const stats = statSync(absolutePath);
1144
+ const stats = statSync2(absolutePath);
204
1145
  const extension = extname(entry.name) || "[none]";
205
1146
  byExt[extension] = (byExt[extension] ?? 0) + 1;
206
1147
  totalFiles += 1;
@@ -218,8 +1159,8 @@ function buildTopology(root) {
218
1159
  files: files.sort((left, right) => left.relativePath.localeCompare(right.relativePath))
219
1160
  };
220
1161
  }
221
- function assertExistingDirectory(target) {
222
- if (!existsSync(target) || !statSync(target).isDirectory()) {
1162
+ function assertExistingDirectory2(target) {
1163
+ if (!existsSync8(target) || !statSync2(target).isDirectory()) {
223
1164
  throw new Error(`Target must be an existing directory: ${target}`);
224
1165
  }
225
1166
  }
@@ -268,7 +1209,7 @@ function getEntryPointReason(relativePath) {
268
1209
  async function buildCodeSamples(target, entryPoints, frameworkKind, topology, packageDependencies) {
269
1210
  const samples = [];
270
1211
  for (const entryPoint of entryPoints.slice(0, SAMPLE_LIMIT)) {
271
- const absolutePath = join(target, ...entryPoint.path.split("/"));
1212
+ const absolutePath = join7(target, ...entryPoint.path.split("/"));
272
1213
  const sample = readFirstLines(absolutePath, SAMPLE_LINE_LIMIT);
273
1214
  const patternAnalysis = await inferPatternHint(entryPoint.path, sample.snippet, {
274
1215
  frameworkKind,
@@ -288,7 +1229,7 @@ async function buildCodeSamples(target, entryPoints, frameworkKind, topology, pa
288
1229
  }
289
1230
  function readFirstLines(path, lineLimit) {
290
1231
  try {
291
- const lines = readFileSync(path, "utf8").split(/\r?\n/);
1232
+ const lines = readFileSync3(path, "utf8").split(/\r?\n/);
292
1233
  if (lines.at(-1) === "") {
293
1234
  lines.pop();
294
1235
  }
@@ -305,12 +1246,12 @@ function readFirstLines(path, lineLimit) {
305
1246
  }
306
1247
  }
307
1248
  function readPackageDependencies(target) {
308
- const packageJsonPath = join(target, "package.json");
309
- if (!existsSync(packageJsonPath)) {
1249
+ const packageJsonPath = join7(target, "package.json");
1250
+ if (!existsSync8(packageJsonPath)) {
310
1251
  return /* @__PURE__ */ new Map();
311
1252
  }
312
1253
  try {
313
- const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
1254
+ const packageJson = JSON.parse(readFileSync3(packageJsonPath, "utf8"));
314
1255
  return new Map([
315
1256
  ...Object.entries(packageJson.dependencies ?? {}),
316
1257
  ...Object.entries(packageJson.devDependencies ?? {}),
@@ -646,16 +1587,16 @@ function scoreFrameworkConfidence(input) {
646
1587
  return input.configCount > 0 || input.packageCount > 0 ? "MEDIUM" : "LOW";
647
1588
  }
648
1589
  function readReadmeInfo(target) {
649
- const readmePath = join(target, "README.md");
650
- const hasContributing = existsSync(join(target, "CONTRIBUTING.md"));
651
- if (!existsSync(readmePath)) {
1590
+ const readmePath = join7(target, "README.md");
1591
+ const hasContributing = existsSync8(join7(target, "CONTRIBUTING.md"));
1592
+ if (!existsSync8(readmePath)) {
652
1593
  return {
653
1594
  quality: "missing",
654
1595
  line_count: 0,
655
1596
  has_contributing: hasContributing
656
1597
  };
657
1598
  }
658
- const readme = readFileSync(readmePath, "utf8");
1599
+ const readme = readFileSync3(readmePath, "utf8");
659
1600
  const wordCount = readme.trim().split(/\s+/).filter(Boolean).length;
660
1601
  return {
661
1602
  quality: wordCount >= 200 ? "ok" : "stub",
@@ -888,7 +1829,7 @@ function buildDomainAssertion(codeSamples) {
888
1829
  namedModules.length >= 2 ? "domain-named-components" : null,
889
1830
  namedSamples.some((sample) => sample.snippet.includes("start():")) ? "lifecycle-hook" : null
890
1831
  ]),
891
- proposedRule: "Preserve domain-specific module names when mirroring structure into AGENTS.md or .fabric/agents/."
1832
+ proposedRule: "Preserve domain-specific module names when mirroring structure into .fabric/rules/."
892
1833
  });
893
1834
  }
894
1835
  function createAssertion(input) {
@@ -1133,10 +2074,10 @@ function buildSkillRecommendations(frameworkKind, topology, readme) {
1133
2074
  return recommendations;
1134
2075
  }
1135
2076
  function readProjectName(target) {
1136
- const packageJsonPath = join(target, "package.json");
1137
- if (existsSync(packageJsonPath)) {
2077
+ const packageJsonPath = join7(target, "package.json");
2078
+ if (existsSync8(packageJsonPath)) {
1138
2079
  try {
1139
- const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
2080
+ const packageJson = JSON.parse(readFileSync3(packageJsonPath, "utf8"));
1140
2081
  if (packageJson.name !== void 0 && packageJson.name.trim().length > 0) {
1141
2082
  return packageJson.name;
1142
2083
  }
@@ -1147,7 +2088,7 @@ function readProjectName(target) {
1147
2088
  return basename(target);
1148
2089
  }
1149
2090
  function getCliVersion() {
1150
- return true ? "1.5.2" : "unknown";
2091
+ return true ? "1.7.0" : "unknown";
1151
2092
  }
1152
2093
  function sortRecord(record) {
1153
2094
  return Object.fromEntries(Object.entries(record).sort(([left], [right]) => left.localeCompare(right)));
@@ -1165,10 +2106,10 @@ var CODEX_SESSION_START_HOOK_TEMPLATE = "templates/codex-hooks/fabric-session-st
1165
2106
  var CODEX_STOP_HOOK_TEMPLATE = "templates/codex-hooks/fabric-stop-reminder.cjs";
1166
2107
  var CODEX_SESSION_START_COMMAND = ".codex/hooks/fabric-session-start.cjs";
1167
2108
  var CODEX_STOP_COMMAND = ".codex/hooks/fabric-stop-reminder.cjs";
1168
- var LOCAL_FABRIC_SERVER_PATH = join2("node_modules", "@fenglimg", "fabric-server", "dist", "index.js");
2109
+ var LOCAL_FABRIC_SERVER_PATH = join8("node_modules", "@fenglimg", "fabric-server", "dist", "index.js");
1169
2110
  var FABRIC_SERVER_PACKAGE = "@fenglimg/fabric-server";
1170
2111
  var INIT_WIZARD_GROUP_CANCELLED = /* @__PURE__ */ Symbol("init-wizard-group-cancelled");
1171
- var initCommand = defineCommand({
2112
+ var initCommand = defineCommand4({
1172
2113
  meta: {
1173
2114
  name: "init",
1174
2115
  description: t("cli.init.description")
@@ -1243,13 +2184,13 @@ async function runInitCommand(args) {
1243
2184
  logger(step);
1244
2185
  }
1245
2186
  if (intent.options.planOnly) {
1246
- writeStderr(t("cli.init.compat.plan"));
2187
+ writeStderr3(t("cli.init.compat.plan"));
1247
2188
  }
1248
2189
  if (args.interactive === false) {
1249
- writeStderr(t("cli.init.compat.interactive"));
2190
+ writeStderr3(t("cli.init.compat.interactive"));
1250
2191
  }
1251
2192
  if (args.bootstrap === false || args.mcp === false || args.hooks === false) {
1252
- writeStderr(t("cli.init.compat.legacy-stage-flags"));
2193
+ writeStderr3(t("cli.init.compat.legacy-stage-flags"));
1253
2194
  }
1254
2195
  const supports = detectClientSupports(intent.target);
1255
2196
  const basePlan = await buildInitExecutionPlan({
@@ -1261,13 +2202,13 @@ async function runInitCommand(args) {
1261
2202
  });
1262
2203
  const plan = intent.wizardEnabled ? await resolveInitExecutionPlanWithWizard(basePlan, args, createDefaultInitWizardAdapter()) : basePlan;
1263
2204
  if (plan === null) {
1264
- writeStderr(t("cli.init.wizard.cancelled"));
2205
+ writeStderr3(t("cli.init.wizard.cancelled"));
1265
2206
  throw new Error(t("cli.init.wizard.cancelled"));
1266
2207
  }
1267
2208
  return executeInitExecutionPlan(plan);
1268
2209
  }
1269
2210
  function resolveInitCliIntent(args, targetInput) {
1270
- const target = normalizeTarget2(targetInput);
2211
+ const target = normalizeTarget4(targetInput);
1271
2212
  const mcpInstallMode = resolveMcpInstallMode(args["mcp-install"]);
1272
2213
  const terminalInteractive = isInteractiveInit();
1273
2214
  const planOnly = args.plan === true;
@@ -1322,10 +2263,10 @@ async function buildInitExecutionPlan(input) {
1322
2263
  }
1323
2264
  async function executeInitExecutionPlan(plan) {
1324
2265
  if (plan.options.force) {
1325
- writeStderr(t("cli.init.force.warning", { path: plan.target }));
2266
+ writeStderr3(t("cli.init.force.warning", { path: plan.target }));
1326
2267
  }
1327
2268
  if (plan.options.reapply && !plan.options.planOnly && !plan.interactive) {
1328
- writeStderr(formatInitModeBanner(plan.options));
2269
+ writeStderr3(formatInitModeBanner(plan.options));
1329
2270
  }
1330
2271
  if (plan.interactive) {
1331
2272
  printInitPlanSummary(plan.target, plan.options, plan.mcpInstallMode, plan.supports);
@@ -1371,27 +2312,30 @@ async function executeInitExecutionPlan(plan) {
1371
2312
  };
1372
2313
  }
1373
2314
  async function buildInitFabricPlan(target, options) {
1374
- assertExistingDirectory2(target);
1375
- const fabricDir = join2(target, ".fabric");
1376
- const bootstrapPath = join2(fabricDir, "bootstrap", "README.md");
1377
- const forensicPath = join2(fabricDir, "forensic.json");
1378
- const claudeSkillPath = join2(target, ".claude", "skills", "agents-md-init", "SKILL.md");
1379
- const codexSkillPath = join2(target, ".agents", "skills", "fabric-init", "SKILL.md");
1380
- const codexSessionStartHookPath = join2(target, ".codex", "hooks", "fabric-session-start.cjs");
1381
- const codexStopHookPath = join2(target, ".codex", "hooks", "fabric-stop-reminder.cjs");
1382
- const codexHooksConfigPath = join2(target, ".codex", "hooks.json");
1383
- const claudeHookPath = join2(target, ".claude", "hooks", "agents-md-init-reminder.cjs");
1384
- const claudeSettingsPath = join2(target, ".claude", "settings.json");
1385
- const metaPath = join2(fabricDir, "agents.meta.json");
1386
- const humanLockPath = join2(fabricDir, "human-lock.json");
2315
+ assertExistingDirectory3(target);
2316
+ const fabricDir = join8(target, ".fabric");
2317
+ const bootstrapPath = join8(fabricDir, "bootstrap", "README.md");
2318
+ const forensicPath = join8(fabricDir, "forensic.json");
2319
+ const taxonomyPath = join8(fabricDir, "INITIAL_TAXONOMY.md");
2320
+ const rulesDir = join8(fabricDir, "rules");
2321
+ const eventsPath = join8(fabricDir, "events.jsonl");
2322
+ const claudeSkillPath = join8(target, ".claude", "skills", "agents-md-init", "SKILL.md");
2323
+ const codexSkillPath = join8(target, ".agents", "skills", "fabric-init", "SKILL.md");
2324
+ const codexSessionStartHookPath = join8(target, ".codex", "hooks", "fabric-session-start.cjs");
2325
+ const codexStopHookPath = join8(target, ".codex", "hooks", "fabric-stop-reminder.cjs");
2326
+ const codexHooksConfigPath = join8(target, ".codex", "hooks.json");
2327
+ const claudeHookPath = join8(target, ".claude", "hooks", "agents-md-init-reminder.cjs");
2328
+ const claudeSettingsPath = join8(target, ".claude", "settings.json");
2329
+ const metaPath = join8(fabricDir, "agents.meta.json");
1387
2330
  const replaceFabricDir = shouldReplaceWritableDirectory(fabricDir, options);
1388
2331
  const bootstrapAction = planFreshPath(bootstrapPath, options);
1389
2332
  const metaAction = planFreshPath(metaPath, options);
1390
- const humanLockAction = planFreshPath(humanLockPath, options);
2333
+ const taxonomyAction = planFreshPath(taxonomyPath, options);
2334
+ const eventsAction = planFreshPath(eventsPath, options);
1391
2335
  const forensicAction = planFreshPath(forensicPath, options);
1392
2336
  const forensicReport = await buildForensicReport(target);
1393
- const humanLockTemplate = readFileSync2(findTemplatePath("templates/fabric/human-lock.json"), "utf8");
1394
2337
  const bootstrapContent = await buildFabricBootstrapGuide(target);
2338
+ const taxonomyContent = buildInitialTaxonomyMarkdown(forensicReport);
1395
2339
  const bootstrapHash = sha256(bootstrapContent);
1396
2340
  const meta = createInitialMeta(bootstrapHash);
1397
2341
  return {
@@ -1405,31 +2349,33 @@ async function buildInitFabricPlan(target, options) {
1405
2349
  metaPath,
1406
2350
  metaAction,
1407
2351
  meta,
1408
- humanLockPath,
1409
- humanLockAction,
1410
- humanLockContent: humanLockTemplate.endsWith("\n") ? humanLockTemplate : `${humanLockTemplate}
1411
- `,
2352
+ taxonomyPath,
2353
+ taxonomyAction,
2354
+ taxonomyContent,
2355
+ rulesDir,
2356
+ eventsPath,
2357
+ eventsAction,
1412
2358
  forensicPath,
1413
2359
  forensicAction,
1414
2360
  forensicReport,
1415
- claudeSkill: buildOptionalTemplateWritePlan(claudeSkillPath, findTemplatePath(CLAUDE_INIT_SKILL_TEMPLATE), options),
1416
- codexSkill: buildOptionalTemplateWritePlan(codexSkillPath, findTemplatePath(CODEX_INIT_SKILL_TEMPLATE), options),
2361
+ claudeSkill: buildOptionalTemplateWritePlan(claudeSkillPath, findTemplatePath3(CLAUDE_INIT_SKILL_TEMPLATE), options),
2362
+ codexSkill: buildOptionalTemplateWritePlan(codexSkillPath, findTemplatePath3(CODEX_INIT_SKILL_TEMPLATE), options),
1417
2363
  codexSessionStartHook: buildOptionalTemplateWritePlan(
1418
2364
  codexSessionStartHookPath,
1419
- findTemplatePath(CODEX_SESSION_START_HOOK_TEMPLATE),
2365
+ findTemplatePath3(CODEX_SESSION_START_HOOK_TEMPLATE),
1420
2366
  options,
1421
2367
  true
1422
2368
  ),
1423
2369
  codexStopHook: buildOptionalTemplateWritePlan(
1424
2370
  codexStopHookPath,
1425
- findTemplatePath(CODEX_STOP_HOOK_TEMPLATE),
2371
+ findTemplatePath3(CODEX_STOP_HOOK_TEMPLATE),
1426
2372
  options,
1427
2373
  true
1428
2374
  ),
1429
2375
  codexHooksConfig: buildCodexHooksConfigPlan(codexHooksConfigPath, options),
1430
2376
  claudeHook: buildOptionalTemplateWritePlan(
1431
2377
  claudeHookPath,
1432
- findTemplatePath(CLAUDE_INIT_REMINDER_HOOK_TEMPLATE),
2378
+ findTemplatePath3(CLAUDE_INIT_REMINDER_HOOK_TEMPLATE),
1433
2379
  options,
1434
2380
  true
1435
2381
  ),
@@ -1440,17 +2386,20 @@ function executeInitFabricPlan(plan) {
1440
2386
  if (plan.replaceFabricDir) {
1441
2387
  rmSync(plan.fabricDir, { force: true });
1442
2388
  }
1443
- mkdirSync(plan.fabricDir, { recursive: true });
1444
- mkdirSync(dirname(plan.bootstrapPath), { recursive: true });
2389
+ mkdirSync3(plan.fabricDir, { recursive: true });
2390
+ mkdirSync3(dirname5(plan.bootstrapPath), { recursive: true });
1445
2391
  preparePlannedPath(plan.bootstrapPath, plan.bootstrapAction);
1446
- writeFileSync(plan.bootstrapPath, plan.bootstrapContent, "utf8");
2392
+ writeFileSync3(plan.bootstrapPath, plan.bootstrapContent, "utf8");
1447
2393
  preparePlannedPath(plan.metaPath, plan.metaAction);
1448
- writeFileSync(plan.metaPath, `${JSON.stringify(plan.meta, null, 2)}
2394
+ writeFileSync3(plan.metaPath, `${JSON.stringify(plan.meta, null, 2)}
1449
2395
  `, "utf8");
1450
- preparePlannedPath(plan.humanLockPath, plan.humanLockAction);
1451
- writeFileSync(plan.humanLockPath, plan.humanLockContent, "utf8");
2396
+ preparePlannedPath(plan.taxonomyPath, plan.taxonomyAction);
2397
+ writeFileSync3(plan.taxonomyPath, ensureTrailingNewline2(plan.taxonomyContent), "utf8");
2398
+ mkdirSync3(plan.rulesDir, { recursive: true });
2399
+ preparePlannedPath(plan.eventsPath, plan.eventsAction);
2400
+ writeFileSync3(plan.eventsPath, "", "utf8");
1452
2401
  preparePlannedPath(plan.forensicPath, plan.forensicAction);
1453
- writeFileSync(plan.forensicPath, `${JSON.stringify(plan.forensicReport, null, 2)}
2402
+ writeFileSync3(plan.forensicPath, `${JSON.stringify(plan.forensicReport, null, 2)}
1454
2403
  `, "utf8");
1455
2404
  applyOptionalTemplateWritePlan(plan.claudeSkill);
1456
2405
  applyOptionalTemplateWritePlan(plan.codexSkill);
@@ -1464,8 +2413,10 @@ function executeInitFabricPlan(plan) {
1464
2413
  bootstrapAction: plan.bootstrapAction,
1465
2414
  metaPath: plan.metaPath,
1466
2415
  metaAction: plan.metaAction,
1467
- humanLockPath: plan.humanLockPath,
1468
- humanLockAction: plan.humanLockAction,
2416
+ taxonomyPath: plan.taxonomyPath,
2417
+ taxonomyAction: plan.taxonomyAction,
2418
+ eventsPath: plan.eventsPath,
2419
+ eventsAction: plan.eventsAction,
1469
2420
  forensicPath: plan.forensicPath,
1470
2421
  forensicAction: plan.forensicAction,
1471
2422
  claudeSkillPath: plan.claudeSkill.path,
@@ -1526,19 +2477,20 @@ function exhaustiveInitStagePlan(value) {
1526
2477
  function printInitScaffoldResult(created) {
1527
2478
  console.log(formatInitPathAction(created.bootstrapPath, created.bootstrapAction));
1528
2479
  console.log(formatInitPathAction(created.metaPath, created.metaAction));
1529
- console.log(formatInitPathAction(created.humanLockPath, created.humanLockAction));
2480
+ console.log(formatInitPathAction(created.taxonomyPath, created.taxonomyAction));
2481
+ console.log(formatInitPathAction(created.eventsPath, created.eventsAction));
1530
2482
  console.log(formatInitPathAction(created.forensicPath, created.forensicAction));
1531
- writeStderr(formatOptionalInitPathAction(created.claudeSkillPath, created.claudeSkillAction));
1532
- writeStderr(formatOptionalInitPathAction(created.codexSkillPath, created.codexSkillAction));
1533
- writeStderr(
2483
+ writeStderr3(formatOptionalInitPathAction(created.claudeSkillPath, created.claudeSkillAction));
2484
+ writeStderr3(formatOptionalInitPathAction(created.codexSkillPath, created.codexSkillAction));
2485
+ writeStderr3(
1534
2486
  formatOptionalInitPathAction(created.codexSessionStartHookPath, created.codexSessionStartHookAction)
1535
2487
  );
1536
- writeStderr(
2488
+ writeStderr3(
1537
2489
  formatOptionalInitPathAction(created.codexStopHookPath, created.codexStopHookAction)
1538
2490
  );
1539
- writeStderr(formatCodexHooksAction(created.codexHooksConfigPath, created.codexHooksConfigAction));
1540
- writeStderr(formatOptionalInitPathAction(created.claudeHookPath, created.claudeHookAction));
1541
- writeStderr(formatClaudeSettingsAction(created.claudeSettingsPath, created.claudeSettingsAction));
2491
+ writeStderr3(formatCodexHooksAction(created.codexHooksConfigPath, created.codexHooksConfigAction));
2492
+ writeStderr3(formatOptionalInitPathAction(created.claudeHookPath, created.claudeHookAction));
2493
+ writeStderr3(formatClaudeSettingsAction(created.claudeSettingsPath, created.claudeSettingsAction));
1542
2494
  }
1543
2495
  function printInitPostSetup(plan, stageResults, finalSupports) {
1544
2496
  if (shouldPrintHooksNextStep(plan.options, stageResults)) {
@@ -1576,8 +2528,10 @@ function buildPlanOnlyScaffoldResult(plan) {
1576
2528
  bootstrapAction: plan.bootstrapAction,
1577
2529
  metaPath: plan.metaPath,
1578
2530
  metaAction: plan.metaAction,
1579
- humanLockPath: plan.humanLockPath,
1580
- humanLockAction: plan.humanLockAction,
2531
+ taxonomyPath: plan.taxonomyPath,
2532
+ taxonomyAction: plan.taxonomyAction,
2533
+ eventsPath: plan.eventsPath,
2534
+ eventsAction: plan.eventsAction,
1581
2535
  forensicPath: plan.forensicPath,
1582
2536
  forensicAction: plan.forensicAction,
1583
2537
  claudeSkillPath: plan.claudeSkill.path,
@@ -1621,12 +2575,12 @@ async function executeInitStagePlan(plan, stageName) {
1621
2575
  case "mcp": {
1622
2576
  if (stage.installMode === "local") {
1623
2577
  const manager = stage.packageManager ?? detectPackageManager(plan.target);
1624
- writeStderr(t("cli.init.mcp.install.local"));
1625
- writeStderr(t("cli.init.mcp.local.installing", { manager }));
2578
+ writeStderr3(t("cli.init.mcp.install.local"));
2579
+ writeStderr3(t("cli.init.mcp.local.installing", { manager }));
1626
2580
  installLocalFabricServer(plan.target, manager);
1627
- writeStderr(t("cli.init.mcp.local.installed"));
2581
+ writeStderr3(t("cli.init.mcp.local.installed"));
1628
2582
  } else {
1629
- writeStderr(t("cli.init.mcp.install.global"));
2583
+ writeStderr3(t("cli.init.mcp.install.global"));
1630
2584
  }
1631
2585
  const result = await installMcpClients(plan.target, {
1632
2586
  force: plan.options.force,
@@ -1648,15 +2602,15 @@ async function executeInitStagePlan(plan, stageName) {
1648
2602
  return exhaustiveInitStagePlan(stage);
1649
2603
  }
1650
2604
  } catch (error) {
1651
- writeStderr(formatInitStageFailure(stageName, error));
2605
+ writeStderr3(formatInitStageFailure(stageName, error));
1652
2606
  return { name: stageName, disposition: "failed" };
1653
2607
  }
1654
2608
  }
1655
2609
  function shouldReplaceWritableDirectory(path, options) {
1656
- if (!existsSync2(path)) {
2610
+ if (!existsSync9(path)) {
1657
2611
  return false;
1658
2612
  }
1659
- if (statSync2(path).isDirectory()) {
2613
+ if (statSync3(path).isDirectory()) {
1660
2614
  return false;
1661
2615
  }
1662
2616
  if (!options?.force) {
@@ -1665,7 +2619,7 @@ function shouldReplaceWritableDirectory(path, options) {
1665
2619
  return true;
1666
2620
  }
1667
2621
  function planFreshPath(path, options) {
1668
- if (!existsSync2(path)) {
2622
+ if (!existsSync9(path)) {
1669
2623
  return "created";
1670
2624
  }
1671
2625
  if (!options?.force) {
@@ -1674,13 +2628,13 @@ function planFreshPath(path, options) {
1674
2628
  return "overwritten";
1675
2629
  }
1676
2630
  function preparePlannedPath(path, action) {
1677
- mkdirSync(dirname(path), { recursive: true });
1678
- if (action === "overwritten" && existsSync2(path)) {
2631
+ mkdirSync3(dirname5(path), { recursive: true });
2632
+ if (action === "overwritten" && existsSync9(path)) {
1679
2633
  rmSync(path, { recursive: true, force: true });
1680
2634
  }
1681
2635
  }
1682
2636
  function buildOptionalTemplateWritePlan(path, templatePath, options, executable = false) {
1683
- const existed = existsSync2(path);
2637
+ const existed = existsSync9(path);
1684
2638
  if (existed && !options?.force) {
1685
2639
  return { path, action: "skipped", templatePath, executable };
1686
2640
  }
@@ -1695,10 +2649,10 @@ function applyOptionalTemplateWritePlan(plan) {
1695
2649
  if (plan.action === "skipped") {
1696
2650
  return;
1697
2651
  }
1698
- mkdirSync(dirname(plan.path), { recursive: true });
2652
+ mkdirSync3(dirname5(plan.path), { recursive: true });
1699
2653
  copyFileSync(plan.templatePath, plan.path);
1700
2654
  if (plan.executable) {
1701
- chmodSync(plan.path, 493);
2655
+ chmodSync2(plan.path, 493);
1702
2656
  }
1703
2657
  }
1704
2658
  function buildCodexHooksConfigValue() {
@@ -1720,7 +2674,7 @@ function buildCodexHooksConfigValue() {
1720
2674
  };
1721
2675
  }
1722
2676
  function buildCodexHooksConfigPlan(configPath, options) {
1723
- const action = !existsSync2(configPath) ? "created" : options?.force ? "overwritten" : "skipped";
2677
+ const action = !existsSync9(configPath) ? "created" : options?.force ? "overwritten" : "skipped";
1724
2678
  return {
1725
2679
  path: configPath,
1726
2680
  action,
@@ -1731,37 +2685,37 @@ function applyJsonWritePlan(plan) {
1731
2685
  if (plan.action === "skipped") {
1732
2686
  return;
1733
2687
  }
1734
- mkdirSync(dirname(plan.path), { recursive: true });
2688
+ mkdirSync3(dirname5(plan.path), { recursive: true });
1735
2689
  writeJsonAtomically(plan.path, plan.value);
1736
2690
  }
1737
2691
  function buildClaudeSettingsWritePlan(settingsPath, options) {
1738
2692
  let settings;
1739
2693
  let action = "updated";
1740
- if (!existsSync2(settingsPath)) {
2694
+ if (!existsSync9(settingsPath)) {
1741
2695
  settings = {};
1742
2696
  action = "created";
1743
2697
  } else {
1744
2698
  try {
1745
- const parsed = JSON.parse(readFileSync2(settingsPath, "utf8"));
2699
+ const parsed = JSON.parse(readFileSync4(settingsPath, "utf8"));
1746
2700
  if (!isRecord(parsed)) {
1747
- writeStderr(t("cli.init.claude-settings.invalid-object", { label: skippedLabel(), path: settingsPath }));
2701
+ writeStderr3(t("cli.init.claude-settings.invalid-object", { label: skippedLabel(), path: settingsPath }));
1748
2702
  return { path: settingsPath, action: "skipped-invalid", value: null };
1749
2703
  }
1750
2704
  settings = parsed;
1751
2705
  } catch (error) {
1752
2706
  const reason = error instanceof Error ? error.message : "unknown parse error";
1753
- writeStderr(t("cli.init.claude-settings.invalid-json", { label: skippedLabel(), path: settingsPath, reason }));
2707
+ writeStderr3(t("cli.init.claude-settings.invalid-json", { label: skippedLabel(), path: settingsPath, reason }));
1754
2708
  return { path: settingsPath, action: "skipped-invalid", value: null };
1755
2709
  }
1756
2710
  }
1757
2711
  if (settings.hooks !== void 0 && !isRecord(settings.hooks)) {
1758
- writeStderr(t("cli.init.claude-settings.invalid-hooks", { label: skippedLabel(), path: settingsPath }));
2712
+ writeStderr3(t("cli.init.claude-settings.invalid-hooks", { label: skippedLabel(), path: settingsPath }));
1759
2713
  return { path: settingsPath, action: "skipped-invalid", value: null };
1760
2714
  }
1761
2715
  const hooks = settings.hooks ?? {};
1762
2716
  const stopHooksValue = hooks.Stop;
1763
2717
  if (stopHooksValue !== void 0 && !Array.isArray(stopHooksValue)) {
1764
- writeStderr(t("cli.init.claude-settings.invalid-stop-array", { label: skippedLabel(), path: settingsPath }));
2718
+ writeStderr3(t("cli.init.claude-settings.invalid-stop-array", { label: skippedLabel(), path: settingsPath }));
1765
2719
  return { path: settingsPath, action: "skipped-invalid", value: null };
1766
2720
  }
1767
2721
  const stopHooks = Array.isArray(stopHooksValue) ? stopHooksValue : [];
@@ -1796,7 +2750,7 @@ function applyClaudeSettingsWritePlan(plan) {
1796
2750
  if (plan.value === null) {
1797
2751
  return;
1798
2752
  }
1799
- mkdirSync(dirname(plan.path), { recursive: true });
2753
+ mkdirSync3(dirname5(plan.path), { recursive: true });
1800
2754
  writeJsonAtomically(plan.path, plan.value);
1801
2755
  }
1802
2756
  function createDefaultInitWizardAdapter() {
@@ -1951,23 +2905,23 @@ function formatInitModeBadge(options) {
1951
2905
  }
1952
2906
  return t("cli.init.mode.badge.default");
1953
2907
  }
1954
- function normalizeTarget2(targetInput) {
1955
- return isAbsolute2(targetInput) ? targetInput : resolve2(process.cwd(), targetInput);
2908
+ function normalizeTarget4(targetInput) {
2909
+ return isAbsolute4(targetInput) ? targetInput : resolve9(process.cwd(), targetInput);
1956
2910
  }
1957
- function assertExistingDirectory2(target) {
1958
- if (!existsSync2(target) || !statSync2(target).isDirectory()) {
2911
+ function assertExistingDirectory3(target) {
2912
+ if (!existsSync9(target) || !statSync3(target).isDirectory()) {
1959
2913
  throw new Error(`Target must be an existing directory: ${target}`);
1960
2914
  }
1961
2915
  }
1962
2916
  function detectPackageManager(cwd) {
1963
- const workspaceRoot = resolve2(cwd);
1964
- if (existsSync2(join2(workspaceRoot, "pnpm-lock.yaml"))) {
2917
+ const workspaceRoot = resolve9(cwd);
2918
+ if (existsSync9(join8(workspaceRoot, "pnpm-lock.yaml"))) {
1965
2919
  return "pnpm";
1966
2920
  }
1967
- if (existsSync2(join2(workspaceRoot, "yarn.lock"))) {
2921
+ if (existsSync9(join8(workspaceRoot, "yarn.lock"))) {
1968
2922
  return "yarn";
1969
2923
  }
1970
- if (existsSync2(join2(workspaceRoot, "package-lock.json"))) {
2924
+ if (existsSync9(join8(workspaceRoot, "package-lock.json"))) {
1971
2925
  return "npm";
1972
2926
  }
1973
2927
  return "npm";
@@ -1976,7 +2930,7 @@ function resolveMcpInstallMode(rawMode) {
1976
2930
  if (rawMode === void 0 || rawMode === "global" || rawMode === "local") {
1977
2931
  return rawMode ?? "global";
1978
2932
  }
1979
- writeStderr(t("cli.init.mcp.install.invalid", { value: rawMode }));
2933
+ writeStderr3(t("cli.init.mcp.install.invalid", { value: rawMode }));
1980
2934
  return "global";
1981
2935
  }
1982
2936
  function installLocalFabricServer(target, manager) {
@@ -2003,26 +2957,80 @@ function createInitialMeta(agentsHash) {
2003
2957
  }
2004
2958
  };
2005
2959
  }
2006
- function findTemplatePath(relativePath) {
2007
- const currentModuleDir = dirname(fileURLToPath(import.meta.url));
2960
+ function buildInitialTaxonomyMarkdown(forensicReport) {
2961
+ const frameworkInfo = forensicReport.framework;
2962
+ const framework = [frameworkInfo?.kind ?? "unknown", frameworkInfo?.subkind ?? ""].filter((value) => value.trim() !== "").join(" / ") || "unknown";
2963
+ const keyDirs = forensicReport.topology?.key_dirs?.slice(0, 8) ?? [];
2964
+ const candidateFiles = forensicReport.candidate_files?.slice(0, 8) ?? [];
2965
+ const generatedAt = forensicReport.generated_at ?? (/* @__PURE__ */ new Date()).toISOString();
2966
+ return `# Fabric Initial Taxonomy
2967
+
2968
+ **Date**: ${generatedAt}
2969
+ **Base Architecture**: L0/L1/L2 Tiered System
2970
+ **Detected Framework**: ${framework}
2971
+
2972
+ ## Origin Logic
2973
+
2974
+ - **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
2975
+ - **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
2976
+ - **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
2977
+
2978
+ ## Initial L1 Buckets
2979
+
2980
+ ${formatInitialL1Buckets(keyDirs)}
2981
+
2982
+ ## L2 Candidate Signals
2983
+
2984
+ ${formatInitialL2Signals(candidateFiles)}
2985
+
2986
+ ## Evolution Guide
2987
+
2988
+ - \u6D89\u53CA\u5168\u4ED3\u534F\u4F5C\u7A33\u5B9A\u6027\u7684\u89C4\u5219\u8FDB\u5165 L0\u3002
2989
+ - \u6D89\u53CA\u6280\u672F\u9886\u57DF\u3001\u6846\u67B6\u6A21\u5757\u6216\u529F\u80FD\u6A21\u5757\u7684\u89C4\u5219\u8FDB\u5165 L1\u3002
2990
+ - \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
2991
+ - \u51B2\u7A81\u65F6\u6267\u884C\u89E3\u91CA\u56FA\u5B9A\u4E3A L2 > L1 > L0\uFF1B\u540C\u5C42\u5185\u624D\u4F7F\u7528 priority \u6392\u5E8F\u3002
2992
+ `;
2993
+ }
2994
+ function formatInitialL1Buckets(keyDirs) {
2995
+ if (keyDirs.length === 0) {
2996
+ 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";
2997
+ }
2998
+ return keyDirs.map((dir) => `- **L1-${sanitizeTaxonomyLabel(dir)}**: \u6302\u8F7D\u4F9D\u636E\u2014\u2014forensic topology detected \`${dir}\`.`).join("\n");
2999
+ }
3000
+ function formatInitialL2Signals(candidateFiles) {
3001
+ if (candidateFiles.length === 0) {
3002
+ return "- \u6682\u672A\u8BC6\u522B\u660E\u786E L2 \u5019\u9009\u6587\u4EF6\u3002";
3003
+ }
3004
+ return candidateFiles.map((entry) => `- \`${entry.path}\`: ${entry.family} \u2014 ${entry.rationale}`).join("\n");
3005
+ }
3006
+ function sanitizeTaxonomyLabel(value) {
3007
+ const sanitized = value.replaceAll("\\", "/").split("/").filter(Boolean).join("-").replace(/[^A-Za-z0-9_-]+/gu, "-").replace(/^-+|-+$/gu, "");
3008
+ return sanitized === "" ? "General" : sanitized;
3009
+ }
3010
+ function ensureTrailingNewline2(value) {
3011
+ return value.endsWith("\n") ? value : `${value}
3012
+ `;
3013
+ }
3014
+ function findTemplatePath3(relativePath) {
3015
+ const currentModuleDir = dirname5(fileURLToPath4(import.meta.url));
2008
3016
  const candidates = [
2009
- ...templateCandidatesFrom(process.cwd(), relativePath),
2010
- ...templateCandidatesFrom(currentModuleDir, relativePath)
3017
+ ...templateCandidatesFrom3(process.cwd(), relativePath),
3018
+ ...templateCandidatesFrom3(currentModuleDir, relativePath)
2011
3019
  ];
2012
3020
  for (const candidate of candidates) {
2013
- if (existsSync2(candidate)) {
3021
+ if (existsSync9(candidate)) {
2014
3022
  return candidate;
2015
3023
  }
2016
3024
  }
2017
3025
  throw new Error(t("cli.shared.template-not-found", { path: relativePath }));
2018
3026
  }
2019
- function templateCandidatesFrom(start, relativePath) {
3027
+ function templateCandidatesFrom3(start, relativePath) {
2020
3028
  const candidates = [];
2021
- let current = resolve2(start);
3029
+ let current = resolve9(start);
2022
3030
  while (true) {
2023
- candidates.push(join2(current, ...relativePath.split("/")));
2024
- const parent = dirname(current);
2025
- if (parent === current || parse(current).root === current) {
3031
+ candidates.push(join8(current, ...relativePath.split("/")));
3032
+ const parent = dirname5(current);
3033
+ if (parent === current || parse3(current).root === current) {
2026
3034
  break;
2027
3035
  }
2028
3036
  current = parent;
@@ -2045,7 +3053,7 @@ function isClaudeInitReminderStopEntry(entry) {
2045
3053
  }
2046
3054
  function writeJsonAtomically(path, value) {
2047
3055
  const tempPath = `${path}.${process.pid}.tmp`;
2048
- writeFileSync(tempPath, `${JSON.stringify(value, null, 2)}
3056
+ writeFileSync3(tempPath, `${JSON.stringify(value, null, 2)}
2049
3057
  `, "utf8");
2050
3058
  renameSync(tempPath, path);
2051
3059
  }
@@ -2120,7 +3128,8 @@ function printInitPlanSummary(target, options, mcpInstallMode, supports) {
2120
3128
  console.log(t("cli.init.plan.writes"));
2121
3129
  console.log(` - ${target}/.fabric/bootstrap/README.md`);
2122
3130
  console.log(` - ${target}/.fabric/agents.meta.json`);
2123
- console.log(` - ${target}/.fabric/human-lock.json`);
3131
+ console.log(` - ${target}/.fabric/INITIAL_TAXONOMY.md`);
3132
+ console.log(` - ${target}/.fabric/events.jsonl`);
2124
3133
  console.log(` - ${target}/.fabric/forensic.json`);
2125
3134
  }
2126
3135
  function printInitCapabilitySummary(supports, stageResults, options) {
@@ -2284,7 +3293,7 @@ function skippedStageLabel() {
2284
3293
  function failedStageLabel() {
2285
3294
  return paint.error(t("cli.init.stages.failed"));
2286
3295
  }
2287
- function writeStderr(message) {
3296
+ function writeStderr3(message) {
2288
3297
  process.stderr.write(`${message}
2289
3298
  `);
2290
3299
  }