@fenglimg/fabric-cli 1.8.0-rc.2 → 2.0.0-rc.1

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.
@@ -1,125 +1,50 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- createScanReport,
4
- detectFramework
5
- } from "./chunk-NMMUETVK.js";
3
+ detectFramework,
4
+ runInitScan
5
+ } from "./chunk-UHNP7T7W.js";
6
6
  import {
7
7
  createDebugLogger,
8
8
  displayWidth,
9
9
  padEnd,
10
10
  paint,
11
- readFabricConfig,
12
11
  resolveDevMode,
13
12
  t
14
- } from "./chunk-QPCRBQ5Y.js";
13
+ } from "./chunk-5LOYBXWD.js";
15
14
 
16
15
  // src/commands/init.ts
17
- import { createHash, randomUUID } from "crypto";
16
+ import { randomUUID } from "crypto";
17
+ import { homedir as homedir5 } from "os";
18
18
  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";
19
+ import { appendFileSync, existsSync as existsSync8, mkdirSync, rmSync, statSync as statSync3, writeFileSync } from "fs";
20
+ import { dirname as dirname3, isAbsolute as isAbsolute3, join as join6, resolve as resolve7 } from "path";
22
21
  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";
22
+ import { defaultAgentsMetaCounters } from "@fenglimg/fabric-shared";
23
+ import { atomicWriteJson as atomicWriteJson2, atomicWriteText as atomicWriteText2 } from "@fenglimg/fabric-shared/node/atomic-write";
24
+ import { defineCommand as defineCommand3 } from "citty";
25
25
  import { checkLockOrThrow } from "@fenglimg/fabric-server";
26
26
 
27
- // src/bootstrap-guide.ts
28
- import { existsSync, mkdirSync, readFileSync } from "fs";
29
- import { dirname, isAbsolute, join, parse, resolve } from "path";
30
- 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
27
+ // src/commands/config.ts
28
+ import { existsSync as existsSync6 } from "fs";
29
+ import { readFile as readFile3 } from "fs/promises";
106
30
  import { resolve as resolve5 } from "path";
107
- import { defineCommand } from "citty";
31
+ import { fileURLToPath } from "url";
32
+ import { defineCommand as defineCommand2 } from "citty";
108
33
 
109
34
  // src/config/resolver.ts
110
- import { existsSync as existsSync5 } from "fs";
111
- import { join as join5 } from "path";
35
+ import { existsSync as existsSync4 } from "fs";
36
+ import { join as join4 } from "path";
112
37
  import { homedir as homedir4 } from "os";
113
38
 
114
39
  // src/config/claude-code.ts
115
- import { existsSync as existsSync3 } from "fs";
116
- import { join as join3, resolve as resolve3 } from "path";
40
+ import { existsSync as existsSync2 } from "fs";
41
+ import { join as join2, resolve as resolve2 } from "path";
117
42
  import { homedir as homedir2, platform } from "os";
118
43
 
119
44
  // src/config/json.ts
120
- import { existsSync as existsSync2 } from "fs";
45
+ import { existsSync } from "fs";
121
46
  import { mkdir, readFile } from "fs/promises";
122
- import { dirname as dirname2, join as join2, resolve as resolve2 } from "path";
47
+ import { dirname, join, resolve } from "path";
123
48
  import { homedir } from "os";
124
49
  import { atomicWriteJson } from "@fenglimg/fabric-shared/node/atomic-write";
125
50
 
@@ -150,12 +75,12 @@ function expandHome(filePath) {
150
75
  return homedir();
151
76
  }
152
77
  if (filePath.startsWith("~/")) {
153
- return join2(homedir(), filePath.slice(2));
78
+ return join(homedir(), filePath.slice(2));
154
79
  }
155
80
  return filePath;
156
81
  }
157
82
  function normalizeConfigPath(filePath) {
158
- return resolve2(expandHome(filePath));
83
+ return resolve(expandHome(filePath));
159
84
  }
160
85
  async function readJsonConfig(configPath) {
161
86
  try {
@@ -178,7 +103,7 @@ async function readJsonConfig(configPath) {
178
103
  async function writeJsonClientConfig(configPath, serverEntry) {
179
104
  const existing = await readJsonConfig(configPath);
180
105
  const merged = deepMerge(existing, { mcpServers: { fabric: serverEntry } });
181
- await mkdir(dirname2(configPath), { recursive: true });
106
+ await mkdir(dirname(configPath), { recursive: true });
182
107
  await atomicWriteJson(configPath, merged, { indent: 2 });
183
108
  }
184
109
  var JsonClientConfigWriter = class {
@@ -213,12 +138,12 @@ var ClaudeCodeCLIWriter = class extends JsonClientConfigWriter {
213
138
  // or ~/.claude.json for user scope.
214
139
  // Detection still checks ~/.claude to confirm Claude Code is installed.
215
140
  defaultPath(workspaceRoot) {
216
- const globalClaudeDir = join2(homedir(), ".claude");
217
- const projectClaudeDir = join2(workspaceRoot, ".claude");
218
- if (!existsSync2(globalClaudeDir) && !existsSync2(projectClaudeDir)) {
141
+ const globalClaudeDir = join(homedir(), ".claude");
142
+ const projectClaudeDir = join(workspaceRoot, ".claude");
143
+ if (!existsSync(globalClaudeDir) && !existsSync(projectClaudeDir)) {
219
144
  return null;
220
145
  }
221
- return this.scope === "user" ? join2(homedir(), ".claude.json") : join2(workspaceRoot, ".mcp.json");
146
+ return this.scope === "user" ? join(homedir(), ".claude.json") : join(workspaceRoot, ".mcp.json");
222
147
  }
223
148
  };
224
149
  var CursorWriter = class extends JsonClientConfigWriter {
@@ -227,8 +152,8 @@ var CursorWriter = class extends JsonClientConfigWriter {
227
152
  super(configuredPath);
228
153
  }
229
154
  defaultPath(workspaceRoot) {
230
- const cursorDir = join2(workspaceRoot, ".cursor");
231
- return existsSync2(cursorDir) ? join2(cursorDir, "mcp.json") : null;
155
+ const cursorDir = join(workspaceRoot, ".cursor");
156
+ return existsSync(cursorDir) ? join(cursorDir, "mcp.json") : null;
232
157
  }
233
158
  };
234
159
 
@@ -236,12 +161,12 @@ var CursorWriter = class extends JsonClientConfigWriter {
236
161
  function getClaudeDesktopConfigPath() {
237
162
  const os = platform();
238
163
  if (os === "darwin") {
239
- return join3(homedir2(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
164
+ return join2(homedir2(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
240
165
  }
241
166
  if (os === "win32") {
242
- return join3(process.env.APPDATA ?? join3(homedir2(), "AppData", "Roaming"), "Claude", "claude_desktop_config.json");
167
+ return join2(process.env.APPDATA ?? join2(homedir2(), "AppData", "Roaming"), "Claude", "claude_desktop_config.json");
243
168
  }
244
- return join3(homedir2(), ".config", "Claude", "claude_desktop_config.json");
169
+ return join2(homedir2(), ".config", "Claude", "claude_desktop_config.json");
245
170
  }
246
171
  var ClaudeCodeDesktopWriter = class {
247
172
  clientKind = "ClaudeCodeDesktop";
@@ -251,7 +176,7 @@ var ClaudeCodeDesktopWriter = class {
251
176
  }
252
177
  async detect(_workspaceRoot, overridePath) {
253
178
  const configPath = normalizeConfigPath(overridePath ?? this.configuredPath ?? getClaudeDesktopConfigPath());
254
- return existsSync3(configPath) || overridePath !== void 0 || this.configuredPath !== void 0 ? configPath : null;
179
+ return existsSync2(configPath) || overridePath !== void 0 || this.configuredPath !== void 0 ? configPath : null;
255
180
  }
256
181
  async write(serverPath, workspaceRoot, overridePath) {
257
182
  const configPath = await this.detect(workspaceRoot, overridePath);
@@ -266,17 +191,17 @@ var ClaudeCodeDesktopWriter = class {
266
191
  };
267
192
 
268
193
  // src/config/toml.ts
269
- import { existsSync as existsSync4 } from "fs";
194
+ import { existsSync as existsSync3 } from "fs";
270
195
  import { mkdir as mkdir2, readFile as readFile2 } from "fs/promises";
271
- import { dirname as dirname3, join as join4, resolve as resolve4 } from "path";
196
+ import { dirname as dirname2, join as join3, resolve as resolve3 } from "path";
272
197
  import { homedir as homedir3 } from "os";
273
- import { atomicWriteText as atomicWriteText2 } from "@fenglimg/fabric-shared/node/atomic-write";
198
+ import { atomicWriteText } from "@fenglimg/fabric-shared/node/atomic-write";
274
199
  function expandHome2(filePath) {
275
200
  if (filePath === "~") {
276
201
  return homedir3();
277
202
  }
278
203
  if (filePath.startsWith("~/")) {
279
- return join4(homedir3(), filePath.slice(2));
204
+ return join3(homedir3(), filePath.slice(2));
280
205
  }
281
206
  return filePath;
282
207
  }
@@ -342,10 +267,10 @@ var CodexTOMLConfigWriter = class {
342
267
  async detect(_workspaceRoot, overridePath) {
343
268
  const explicitPath = overridePath ?? this.configuredPath;
344
269
  if (explicitPath !== void 0) {
345
- return resolve4(expandHome2(explicitPath));
270
+ return resolve3(expandHome2(explicitPath));
346
271
  }
347
- const codexDir = join4(homedir3(), ".codex");
348
- return existsSync4(codexDir) ? resolve4(join4(codexDir, "config.toml")) : null;
272
+ const codexDir = join3(homedir3(), ".codex");
273
+ return existsSync3(codexDir) ? resolve3(join3(codexDir, "config.toml")) : null;
349
274
  }
350
275
  async write(serverPath, workspaceRoot, overridePath) {
351
276
  const configPath = await this.detect(workspaceRoot, overridePath);
@@ -354,8 +279,8 @@ var CodexTOMLConfigWriter = class {
354
279
  }
355
280
  const rawConfig = await readTomlConfigText(configPath);
356
281
  const nextConfig = upsertCodexServerBlock(rawConfig, "fabric", createServerEntry(serverPath));
357
- await mkdir2(dirname3(configPath), { recursive: true });
358
- await atomicWriteText2(configPath, nextConfig);
282
+ await mkdir2(dirname2(configPath), { recursive: true });
283
+ await atomicWriteText(configPath, nextConfig);
359
284
  }
360
285
  };
361
286
 
@@ -375,25 +300,25 @@ function resolveClients(workspaceRoot, fabricConfig = {}, opts = {}) {
375
300
  const claudeMcpScope = opts.claudeMcpScope ?? "project";
376
301
  addIfDetected(
377
302
  writers,
378
- existsSync5(join5(homedir4(), ".claude")) || existsSync5(join5(workspaceRoot, ".claude")),
303
+ existsSync4(join4(homedir4(), ".claude")) || existsSync4(join4(workspaceRoot, ".claude")),
379
304
  (configuredPath) => new ClaudeCodeCLIWriter(configuredPath, claudeMcpScope),
380
305
  hasExplicitPath(clientPaths, "claudeCodeCLI") ? clientPaths.claudeCodeCLI : void 0
381
306
  );
382
307
  addIfDetected(
383
308
  writers,
384
- existsSync5(getClaudeDesktopConfigPath()),
309
+ existsSync4(getClaudeDesktopConfigPath()),
385
310
  (configuredPath) => new ClaudeCodeDesktopWriter(configuredPath),
386
311
  hasExplicitPath(clientPaths, "claudeCodeDesktop") ? clientPaths.claudeCodeDesktop : void 0
387
312
  );
388
313
  addIfDetected(
389
314
  writers,
390
- existsSync5(join5(workspaceRoot, ".cursor")),
315
+ existsSync4(join4(workspaceRoot, ".cursor")),
391
316
  (configuredPath) => new CursorWriter(configuredPath),
392
317
  hasExplicitPath(clientPaths, "cursor") ? clientPaths.cursor : void 0
393
318
  );
394
319
  addIfDetected(
395
320
  writers,
396
- existsSync5(join5(homedir4(), ".codex")),
321
+ existsSync4(join4(homedir4(), ".codex")),
397
322
  (configuredPath) => new CodexTOMLConfigWriter(configuredPath),
398
323
  hasExplicitPath(clientPaths, "codexCLI") ? clientPaths.codexCLI : void 0
399
324
  );
@@ -401,10 +326,10 @@ function resolveClients(workspaceRoot, fabricConfig = {}, opts = {}) {
401
326
  }
402
327
  function detectClientSupports(workspaceRoot, fabricConfig = {}) {
403
328
  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"));
329
+ const claudeDetected = existsSync4(join4(homedir4(), ".claude")) || existsSync4(join4(workspaceRoot, ".claude"));
330
+ const claudeDesktopDetected = existsSync4(getClaudeDesktopConfigPath());
331
+ const cursorDetected = existsSync4(join4(workspaceRoot, ".cursor"));
332
+ const codexDetected = existsSync4(join4(homedir4(), ".codex"));
408
333
  return [
409
334
  {
410
335
  clientKind: "ClaudeCodeCLI",
@@ -462,166 +387,27 @@ function detectClientSupports(workspaceRoot, fabricConfig = {}) {
462
387
  skill: true
463
388
  },
464
389
  installedCapabilities: {
465
- hook: existsSync5(join5(workspaceRoot, ".codex", "hooks.json")),
466
- skill: existsSync5(join5(workspaceRoot, ".agents", "skills", "fabric-init", "SKILL.md"))
390
+ hook: existsSync4(join4(workspaceRoot, ".codex", "hooks.json")),
391
+ // v2/rc.2: v1 client-side init skill removed; skill-installation probes
392
+ // will return once rc.2/3/4 introduce the v2 skills (fabric-archive,
393
+ // fabric-review, fabric-import). Until then there is nothing to probe.
394
+ skill: false
467
395
  }
468
396
  }
469
397
  ];
470
398
  }
471
399
 
472
- // src/commands/bootstrap.ts
473
- 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
400
  // 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({
401
+ import { existsSync as existsSync5, statSync } from "fs";
402
+ import { isAbsolute, resolve as resolve4 } from "path";
403
+ import { defineCommand } from "citty";
404
+ var hooksCommand = defineCommand({
619
405
  meta: {
620
406
  name: "hooks",
621
407
  description: t("cli.hooks.description")
622
408
  },
623
409
  subCommands: {
624
- install: defineCommand2({
410
+ install: defineCommand({
625
411
  meta: {
626
412
  name: "install",
627
413
  description: t("cli.hooks.install.description")
@@ -634,127 +420,29 @@ var hooksCommand = defineCommand2({
634
420
  }
635
421
  },
636
422
  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
- }
423
+ await installHooks(args.target);
650
424
  }
651
425
  })
652
426
  }
653
427
  });
654
- async function installHooks(target, options = {}) {
655
- const normalizedTarget = normalizeTarget2(target);
428
+ async function installHooks(target, _options = {}) {
429
+ const normalizedTarget = normalizeTarget(target);
656
430
  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
- };
431
+ throw new Error(
432
+ `fab hooks install is not available in v2 (husky pre-commit template removed). Use per-client hooks under .claude/ and .codex/ instead. Target: ${normalizedTarget}`
433
+ );
697
434
  }
698
- function normalizeTarget2(targetInput) {
699
- return isAbsolute2(targetInput) ? targetInput : resolve6(process.cwd(), targetInput);
435
+ function normalizeTarget(targetInput) {
436
+ return isAbsolute(targetInput) ? targetInput : resolve4(process.cwd(), targetInput);
700
437
  }
701
438
  function assertExistingDirectory(target) {
702
- if (!existsSync6(target) || !statSync(target).isDirectory()) {
439
+ if (!existsSync5(target) || !statSync(target).isDirectory()) {
703
440
  throw new Error(t("cli.shared.target-invalid", { target }));
704
441
  }
705
442
  }
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
443
 
756
444
  // src/commands/config.ts
757
- var CLIENT_ALIASES2 = {
445
+ var CLIENT_ALIASES = {
758
446
  claude: "ClaudeCodeCLI",
759
447
  claudecodecli: "ClaudeCodeCLI",
760
448
  "claude-code-cli": "ClaudeCodeCLI",
@@ -767,14 +455,14 @@ var CLIENT_ALIASES2 = {
767
455
  "codex-cli": "CodexCLI",
768
456
  codex: "CodexCLI"
769
457
  };
770
- function parseClientFilter2(value) {
458
+ function parseClientFilter(value) {
771
459
  if (value === void 0 || value.trim().length === 0) {
772
460
  return null;
773
461
  }
774
462
  const clients = /* @__PURE__ */ new Set();
775
463
  for (const rawClient of value.split(",")) {
776
464
  const alias = rawClient.trim().toLowerCase();
777
- const clientKind = CLIENT_ALIASES2[alias];
465
+ const clientKind = CLIENT_ALIASES[alias];
778
466
  if (clientKind === void 0) {
779
467
  throw new Error(t("cli.config.errors.unknown-client", { client: rawClient }));
780
468
  }
@@ -783,8 +471,8 @@ function parseClientFilter2(value) {
783
471
  return clients;
784
472
  }
785
473
  async function loadFabricConfig(workspaceRoot) {
786
- const configPath = resolve7(workspaceRoot, "fabric.config.json");
787
- if (!existsSync7(configPath)) {
474
+ const configPath = resolve5(workspaceRoot, "fabric.config.json");
475
+ if (!existsSync6(configPath)) {
788
476
  return {};
789
477
  }
790
478
  const parsed = JSON.parse(await readFile3(configPath, "utf8"));
@@ -795,21 +483,21 @@ async function loadFabricConfig(workspaceRoot) {
795
483
  }
796
484
  function resolveServerPath(override) {
797
485
  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"));
486
+ if (process.env.FAB_SERVER_PATH) return resolve5(process.env.FAB_SERVER_PATH);
487
+ return fileURLToPath(import.meta.resolve("@fenglimg/fabric-server"));
800
488
  }
801
- function writeStderr2(message) {
489
+ function writeStderr(message) {
802
490
  process.stderr.write(`${message}
803
491
  `);
804
492
  }
805
- var configCmd = defineCommand3({
493
+ var configCmd = defineCommand2({
806
494
  meta: {
807
495
  name: "config",
808
496
  description: t("cli.config.description")
809
497
  },
810
498
  subCommands: {
811
499
  hooks: hooksCommand,
812
- install: defineCommand3({
500
+ install: defineCommand2({
813
501
  meta: {
814
502
  name: "install",
815
503
  description: t("cli.config.install.description")
@@ -826,26 +514,26 @@ var configCmd = defineCommand3({
826
514
  }
827
515
  },
828
516
  async run({ args }) {
829
- const selectedClients = parseClientFilter2(args.clients);
517
+ const selectedClients = parseClientFilter(args.clients);
830
518
  const result = await installMcpClients(process.cwd(), {
831
519
  clients: selectedClients === null ? void 0 : Array.from(selectedClients),
832
520
  dryRun: args["dry-run"]
833
521
  });
834
522
  if (result.details.length === 0) {
835
- writeStderr2(t("cli.config.install.no-configs"));
523
+ writeStderr(t("cli.config.install.no-configs"));
836
524
  return;
837
525
  }
838
526
  for (const detail of result.details) {
839
527
  if (detail.action === "skipped") {
840
- writeStderr2(t("cli.config.install.no-config-path", { client: detail.client }));
528
+ writeStderr(t("cli.config.install.no-config-path", { client: detail.client }));
841
529
  continue;
842
530
  }
843
531
  if (detail.action === "dry-run" && detail.path !== null) {
844
- writeStderr2(t("cli.config.install.dry-run", { client: detail.client, path: detail.path }));
532
+ writeStderr(t("cli.config.install.dry-run", { client: detail.client, path: detail.path }));
845
533
  continue;
846
534
  }
847
535
  if (detail.path !== null) {
848
- writeStderr2(t("cli.config.install.wrote", { client: detail.client, path: detail.path }));
536
+ writeStderr(t("cli.config.install.wrote", { client: detail.client, path: detail.path }));
849
537
  }
850
538
  }
851
539
  }
@@ -853,7 +541,7 @@ var configCmd = defineCommand3({
853
541
  }
854
542
  });
855
543
  async function installMcpClients(target, options = {}) {
856
- const workspaceRoot = resolve7(target);
544
+ const workspaceRoot = resolve5(target);
857
545
  const fabricConfig = await loadFabricConfig(workspaceRoot);
858
546
  const selectedClients = options.clients === void 0 ? null : new Set(options.clients);
859
547
  const serverPath = resolveServerPath(options.localServerPath);
@@ -884,9 +572,9 @@ async function installMcpClients(target, options = {}) {
884
572
 
885
573
  // src/scanner/forensic.ts
886
574
  import { execFileSync } from "child_process";
887
- import { existsSync as existsSync8, readdirSync, readFileSync as readFileSync3, statSync as statSync2 } from "fs";
575
+ import { existsSync as existsSync7, readdirSync, readFileSync, statSync as statSync2 } from "fs";
888
576
  import { createRequire } from "module";
889
- import { basename, extname, isAbsolute as isAbsolute3, join as join7, posix, relative, resolve as resolve8, sep } from "path";
577
+ import { basename, extname, isAbsolute as isAbsolute2, join as join5, posix, relative, resolve as resolve6, sep } from "path";
890
578
  import {
891
579
  forensicReportSchema
892
580
  } from "@fenglimg/fabric-shared";
@@ -972,7 +660,7 @@ var parserInitPromise = null;
972
660
  var languagePromiseByKind = {};
973
661
  var parserBundlePromiseByKind = {};
974
662
  async function buildForensicReport(targetInput) {
975
- const target = normalizeTarget3(targetInput);
663
+ const target = normalizeTarget2(targetInput);
976
664
  const framework = detectFramework(target);
977
665
  const topology = buildTopology(target);
978
666
  const entryPoints = collectEntryPoints(target, topology.files);
@@ -1008,8 +696,8 @@ async function buildForensicReport(targetInput) {
1008
696
  }
1009
697
  return validation.data;
1010
698
  }
1011
- function normalizeTarget3(targetInput) {
1012
- return isAbsolute3(targetInput) ? targetInput : resolve8(process.cwd(), targetInput);
699
+ function normalizeTarget2(targetInput) {
700
+ return isAbsolute2(targetInput) ? targetInput : resolve6(process.cwd(), targetInput);
1013
701
  }
1014
702
  function buildTopology(root) {
1015
703
  assertExistingDirectory2(root);
@@ -1025,7 +713,7 @@ function buildTopology(root) {
1025
713
  continue;
1026
714
  }
1027
715
  for (const entry of readdirSync(current, { withFileTypes: true })) {
1028
- const absolutePath = join7(current, entry.name);
716
+ const absolutePath = join5(current, entry.name);
1029
717
  const relativePath = toPosixPath(relative(root, absolutePath));
1030
718
  if (relativePath.length === 0) {
1031
719
  continue;
@@ -1064,7 +752,7 @@ function buildTopology(root) {
1064
752
  };
1065
753
  }
1066
754
  function assertExistingDirectory2(target) {
1067
- if (!existsSync8(target) || !statSync2(target).isDirectory()) {
755
+ if (!existsSync7(target) || !statSync2(target).isDirectory()) {
1068
756
  throw new Error(`Target must be an existing directory: ${target}`);
1069
757
  }
1070
758
  }
@@ -1113,7 +801,7 @@ function getEntryPointReason(relativePath) {
1113
801
  async function buildCodeSamples(target, entryPoints, frameworkKind, topology, packageDependencies) {
1114
802
  const samples = [];
1115
803
  for (const entryPoint of entryPoints.slice(0, SAMPLE_LIMIT)) {
1116
- const absolutePath = join7(target, ...entryPoint.path.split("/"));
804
+ const absolutePath = join5(target, ...entryPoint.path.split("/"));
1117
805
  const sample = readFirstLines(absolutePath, SAMPLE_LINE_LIMIT);
1118
806
  const patternAnalysis = await inferPatternHint(entryPoint.path, sample.snippet, {
1119
807
  frameworkKind,
@@ -1133,7 +821,7 @@ async function buildCodeSamples(target, entryPoints, frameworkKind, topology, pa
1133
821
  }
1134
822
  function readFirstLines(path, lineLimit) {
1135
823
  try {
1136
- const lines = readFileSync3(path, "utf8").split(/\r?\n/);
824
+ const lines = readFileSync(path, "utf8").split(/\r?\n/);
1137
825
  if (lines.at(-1) === "") {
1138
826
  lines.pop();
1139
827
  }
@@ -1150,12 +838,12 @@ function readFirstLines(path, lineLimit) {
1150
838
  }
1151
839
  }
1152
840
  function readPackageDependencies(target) {
1153
- const packageJsonPath = join7(target, "package.json");
1154
- if (!existsSync8(packageJsonPath)) {
841
+ const packageJsonPath = join5(target, "package.json");
842
+ if (!existsSync7(packageJsonPath)) {
1155
843
  return /* @__PURE__ */ new Map();
1156
844
  }
1157
845
  try {
1158
- const packageJson = JSON.parse(readFileSync3(packageJsonPath, "utf8"));
846
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
1159
847
  return new Map([
1160
848
  ...Object.entries(packageJson.dependencies ?? {}),
1161
849
  ...Object.entries(packageJson.devDependencies ?? {}),
@@ -1491,16 +1179,16 @@ function scoreFrameworkConfidence(input) {
1491
1179
  return input.configCount > 0 || input.packageCount > 0 ? "MEDIUM" : "LOW";
1492
1180
  }
1493
1181
  function readReadmeInfo(target) {
1494
- const readmePath = join7(target, "README.md");
1495
- const hasContributing = existsSync8(join7(target, "CONTRIBUTING.md"));
1496
- if (!existsSync8(readmePath)) {
1182
+ const readmePath = join5(target, "README.md");
1183
+ const hasContributing = existsSync7(join5(target, "CONTRIBUTING.md"));
1184
+ if (!existsSync7(readmePath)) {
1497
1185
  return {
1498
1186
  quality: "missing",
1499
1187
  line_count: 0,
1500
1188
  has_contributing: hasContributing
1501
1189
  };
1502
1190
  }
1503
- const readme = readFileSync3(readmePath, "utf8");
1191
+ const readme = readFileSync(readmePath, "utf8");
1504
1192
  const wordCount = readme.trim().split(/\s+/).filter(Boolean).length;
1505
1193
  return {
1506
1194
  quality: wordCount >= 200 ? "ok" : "stub",
@@ -1733,7 +1421,7 @@ function buildDomainAssertion(codeSamples) {
1733
1421
  namedModules.length >= 2 ? "domain-named-components" : null,
1734
1422
  namedSamples.some((sample) => sample.snippet.includes("start():")) ? "lifecycle-hook" : null
1735
1423
  ]),
1736
- proposedRule: "Preserve domain-specific module names when mirroring structure into .fabric/rules/."
1424
+ proposedRule: "Preserve domain-specific module names when authoring knowledge entries that reference these modules."
1737
1425
  });
1738
1426
  }
1739
1427
  function createAssertion(input) {
@@ -1978,10 +1666,10 @@ function buildSkillRecommendations(frameworkKind, topology, readme) {
1978
1666
  return recommendations;
1979
1667
  }
1980
1668
  function readProjectName(target) {
1981
- const packageJsonPath = join7(target, "package.json");
1982
- if (existsSync8(packageJsonPath)) {
1669
+ const packageJsonPath = join5(target, "package.json");
1670
+ if (existsSync7(packageJsonPath)) {
1983
1671
  try {
1984
- const packageJson = JSON.parse(readFileSync3(packageJsonPath, "utf8"));
1672
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
1985
1673
  if (packageJson.name !== void 0 && packageJson.name.trim().length > 0) {
1986
1674
  return packageJson.name;
1987
1675
  }
@@ -1992,7 +1680,7 @@ function readProjectName(target) {
1992
1680
  return basename(target);
1993
1681
  }
1994
1682
  function getCliVersion() {
1995
- return true ? "1.8.0-rc.2" : "unknown";
1683
+ return true ? "2.0.0-rc.1" : "unknown";
1996
1684
  }
1997
1685
  function sortRecord(record) {
1998
1686
  return Object.fromEntries(Object.entries(record).sort(([left], [right]) => left.localeCompare(right)));
@@ -2002,18 +1690,19 @@ function toPosixPath(path) {
2002
1690
  }
2003
1691
 
2004
1692
  // 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");
1693
+ var AGENTS_MD_DEFAULT_CONTENT = `# Project Knowledge
1694
+
1695
+ This project uses [Fabric](https://github.com/fenglimg/fabric) for cross-client AI knowledge management.
1696
+
1697
+ Knowledge entries live in \`.fabric/knowledge/\` (team) and \`~/.fabric/knowledge/\` (personal).
1698
+ Run \`fabric doctor\` to verify state.
1699
+
1700
+ See \`.fabric/knowledge/\` for project decisions, pitfalls, guidelines, models, and processes.
1701
+ `;
1702
+ var LOCAL_FABRIC_SERVER_PATH = join6("node_modules", "@fenglimg", "fabric-server", "dist", "index.js");
2014
1703
  var FABRIC_SERVER_PACKAGE = "@fenglimg/fabric-server";
2015
1704
  var INIT_WIZARD_GROUP_CANCELLED = /* @__PURE__ */ Symbol("init-wizard-group-cancelled");
2016
- var initCommand = defineCommand4({
1705
+ var initCommand = defineCommand3({
2017
1706
  meta: {
2018
1707
  name: "init",
2019
1708
  description: t("cli.init.description")
@@ -2095,13 +1784,13 @@ async function runInitCommand(args) {
2095
1784
  logger(step);
2096
1785
  }
2097
1786
  if (intent.options.planOnly) {
2098
- writeStderr3(t("cli.init.compat.plan"));
1787
+ writeStderr2(t("cli.init.compat.plan"));
2099
1788
  }
2100
1789
  if (args.interactive === false) {
2101
- writeStderr3(t("cli.init.compat.interactive"));
1790
+ writeStderr2(t("cli.init.compat.interactive"));
2102
1791
  }
2103
1792
  if (args.bootstrap === false || args.mcp === false || args.hooks === false) {
2104
- writeStderr3(t("cli.init.compat.legacy-stage-flags"));
1793
+ writeStderr2(t("cli.init.compat.legacy-stage-flags"));
2105
1794
  }
2106
1795
  const supports = detectClientSupports(intent.target);
2107
1796
  const basePlan = await buildInitExecutionPlan({
@@ -2120,7 +1809,7 @@ async function runInitCommand(args) {
2120
1809
  return executeInitExecutionPlan(plan);
2121
1810
  }
2122
1811
  function resolveInitCliIntent(args, targetInput) {
2123
- const target = normalizeTarget4(targetInput);
1812
+ const target = normalizeTarget3(targetInput);
2124
1813
  const mcpInstallMode = resolveMcpInstallMode(args["mcp-install"]);
2125
1814
  const claudeMcpScope = resolveClaudeMcpScope(args.scope);
2126
1815
  const terminalInteractive = isInteractiveInit();
@@ -2150,7 +1839,7 @@ function resolveClaudeMcpScope(raw) {
2150
1839
  if (raw === "user") {
2151
1840
  return "user";
2152
1841
  }
2153
- writeStderr3(t("cli.init.mcp.scope.invalid", { value: raw }));
1842
+ writeStderr2(t("cli.init.mcp.scope.invalid", { value: raw }));
2154
1843
  return "project";
2155
1844
  }
2156
1845
  async function buildInitExecutionPlan(input) {
@@ -2190,10 +1879,10 @@ async function buildInitExecutionPlan(input) {
2190
1879
  }
2191
1880
  async function executeInitExecutionPlan(plan) {
2192
1881
  if (plan.options.force) {
2193
- writeStderr3(t("cli.init.force.warning", { path: plan.target }));
1882
+ writeStderr2(t("cli.init.force.warning", { path: plan.target }));
2194
1883
  }
2195
1884
  if (plan.options.reapply && !plan.options.planOnly && !plan.interactive) {
2196
- writeStderr3(formatInitModeBanner(plan.options));
1885
+ writeStderr2(formatInitModeBanner(plan.options));
2197
1886
  }
2198
1887
  if (plan.interactive) {
2199
1888
  printInitPlanSummary(plan.target, plan.options, plan.mcpInstallMode, plan.supports);
@@ -2238,98 +1927,77 @@ async function executeInitExecutionPlan(plan) {
2238
1927
  finalSupports
2239
1928
  };
2240
1929
  }
1930
+ var KNOWLEDGE_SUBDIRS = ["decisions", "pitfalls", "guidelines", "models", "processes", "pending"];
1931
+ function resolvePersonalFabricRoot() {
1932
+ return process.env.FABRIC_HOME ?? homedir5();
1933
+ }
2241
1934
  async function buildInitFabricPlan(target, options) {
2242
1935
  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, ".agents", "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");
1936
+ const fabricDir = join6(target, ".fabric");
1937
+ const agentsMdPath = join6(target, "AGENTS.md");
1938
+ const agentsMdAction = existsSync8(agentsMdPath) ? "preserved" : "created";
1939
+ const knowledgeDir = join6(fabricDir, "knowledge");
1940
+ const personalKnowledgeDir = join6(resolvePersonalFabricRoot(), ".fabric", "knowledge");
1941
+ const forensicPath = join6(fabricDir, "forensic.json");
1942
+ const eventsPath = join6(fabricDir, "events.jsonl");
1943
+ const metaPath = join6(fabricDir, "agents.meta.json");
2257
1944
  const replaceFabricDir = shouldReplaceWritableDirectory(fabricDir, options);
2258
- const bootstrapAction = planFreshPath(bootstrapPath, options);
1945
+ const knowledgeDirAction = existsSync8(knowledgeDir) ? "overwritten" : "created";
2259
1946
  const metaAction = planFreshPath(metaPath, options);
2260
- const taxonomyAction = planFreshPath(taxonomyPath, options);
2261
1947
  const eventsAction = planFreshPath(eventsPath, options);
2262
1948
  const forensicAction = planFreshPath(forensicPath, options);
2263
1949
  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);
1950
+ const meta = createInitialMeta();
2268
1951
  return {
2269
1952
  target,
2270
1953
  options,
2271
1954
  fabricDir,
2272
1955
  replaceFabricDir,
2273
- bootstrapPath,
2274
- bootstrapAction,
2275
- bootstrapContent,
1956
+ agentsMdPath,
1957
+ agentsMdAction,
1958
+ knowledgeDir,
1959
+ knowledgeDirAction,
1960
+ personalKnowledgeDir,
2276
1961
  metaPath,
2277
1962
  metaAction,
2278
1963
  meta,
2279
- taxonomyPath,
2280
- taxonomyAction,
2281
- taxonomyContent,
2282
- rulesDir,
2283
1964
  eventsPath,
2284
1965
  eventsAction,
2285
1966
  forensicPath,
2286
1967
  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)
1968
+ forensicReport
2310
1969
  };
2311
1970
  }
2312
1971
  async function executeInitFabricPlan(plan) {
2313
1972
  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
1973
  if (plan.replaceFabricDir) {
2317
1974
  rmSync(plan.fabricDir, { force: true });
2318
1975
  }
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 });
1976
+ mkdirSync(plan.fabricDir, { recursive: true });
1977
+ if (plan.agentsMdAction === "created" && !existsSync8(plan.agentsMdPath)) {
1978
+ await atomicWriteText2(plan.agentsMdPath, AGENTS_MD_DEFAULT_CONTENT);
1979
+ }
1980
+ mkdirSync(plan.knowledgeDir, { recursive: true });
1981
+ for (const sub of KNOWLEDGE_SUBDIRS) {
1982
+ const teamSubDir = join6(plan.knowledgeDir, sub);
1983
+ mkdirSync(teamSubDir, { recursive: true });
1984
+ const teamGitkeep = join6(teamSubDir, ".gitkeep");
1985
+ if (!existsSync8(teamGitkeep)) {
1986
+ writeFileSync(teamGitkeep, "", "utf8");
1987
+ }
1988
+ }
1989
+ try {
1990
+ mkdirSync(plan.personalKnowledgeDir, { recursive: true });
1991
+ for (const sub of KNOWLEDGE_SUBDIRS) {
1992
+ mkdirSync(join6(plan.personalKnowledgeDir, sub), { recursive: true });
1993
+ }
1994
+ } catch {
1995
+ }
1996
+ preparePlannedPath(plan.metaPath, plan.metaAction);
1997
+ await atomicWriteJson2(plan.metaPath, plan.meta);
2330
1998
  if (isReapply) {
2331
- if (!existsSync9(plan.eventsPath)) {
2332
- mkdirSync3(dirname5(plan.eventsPath), { recursive: true });
1999
+ if (!existsSync8(plan.eventsPath)) {
2000
+ mkdirSync(dirname3(plan.eventsPath), { recursive: true });
2333
2001
  writeFileSync(plan.eventsPath, "", "utf8");
2334
2002
  }
2335
2003
  } else {
@@ -2337,46 +2005,33 @@ async function executeInitFabricPlan(plan) {
2337
2005
  writeFileSync(plan.eventsPath, "", "utf8");
2338
2006
  }
2339
2007
  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);
2008
+ await atomicWriteJson2(plan.forensicPath, plan.forensicReport);
2009
+ if (!plan.options?.reapply) {
2010
+ try {
2011
+ await runInitScan(plan.target, { source: "init" });
2012
+ } catch (error) {
2013
+ writeStderr2(
2014
+ `[warn] init-scan failed: ${error instanceof Error ? error.message : String(error)} \u2014 re-run \`fab scan\` to populate baseline knowledge entries.`
2015
+ );
2016
+ }
2017
+ }
2348
2018
  if (isReapply) {
2349
2019
  appendReapplyLedgerEvent(plan.eventsPath, {
2350
- preserved_ledger: true,
2351
- preserved_meta: preserveMeta,
2352
- rules_count: existingRules.length
2020
+ preserved_ledger: true
2353
2021
  });
2354
2022
  }
2355
2023
  return {
2356
- bootstrapPath: plan.bootstrapPath,
2357
- bootstrapAction: plan.bootstrapAction,
2024
+ agentsMdPath: plan.agentsMdPath,
2025
+ agentsMdAction: plan.agentsMdAction,
2026
+ knowledgeDir: plan.knowledgeDir,
2027
+ knowledgeDirAction: plan.knowledgeDirAction,
2028
+ personalKnowledgeDir: plan.personalKnowledgeDir,
2358
2029
  metaPath: plan.metaPath,
2359
2030
  metaAction: plan.metaAction,
2360
- taxonomyPath: plan.taxonomyPath,
2361
- taxonomyAction: plan.taxonomyAction,
2362
2031
  eventsPath: plan.eventsPath,
2363
2032
  eventsAction: plan.eventsAction,
2364
2033
  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
2034
+ forensicAction: plan.forensicAction
2380
2035
  };
2381
2036
  }
2382
2037
  async function initFabric(target, options) {
@@ -2421,22 +2076,11 @@ function exhaustiveInitStagePlan(value) {
2421
2076
  throw new Error(`Unsupported init stage plan: ${JSON.stringify(value)}`);
2422
2077
  }
2423
2078
  function printInitScaffoldResult(created) {
2424
- console.log(formatInitPathAction(created.bootstrapPath, created.bootstrapAction));
2079
+ console.log(formatAgentsMdAction(created.agentsMdPath, created.agentsMdAction));
2080
+ console.log(formatInitPathAction(created.knowledgeDir, created.knowledgeDirAction));
2425
2081
  console.log(formatInitPathAction(created.metaPath, created.metaAction));
2426
- console.log(formatInitPathAction(created.taxonomyPath, created.taxonomyAction));
2427
2082
  console.log(formatInitPathAction(created.eventsPath, created.eventsAction));
2428
2083
  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
2084
  }
2441
2085
  function printInitPostSetup(plan, stageResults, finalSupports) {
2442
2086
  if (shouldPrintHooksNextStep(plan.options, stageResults)) {
@@ -2470,30 +2114,17 @@ function printInitPlanPreview(plan) {
2470
2114
  }
2471
2115
  function buildPlanOnlyScaffoldResult(plan) {
2472
2116
  return {
2473
- bootstrapPath: plan.bootstrapPath,
2474
- bootstrapAction: plan.bootstrapAction,
2117
+ agentsMdPath: plan.agentsMdPath,
2118
+ agentsMdAction: plan.agentsMdAction,
2119
+ knowledgeDir: plan.knowledgeDir,
2120
+ knowledgeDirAction: plan.knowledgeDirAction,
2121
+ personalKnowledgeDir: plan.personalKnowledgeDir,
2475
2122
  metaPath: plan.metaPath,
2476
2123
  metaAction: plan.metaAction,
2477
- taxonomyPath: plan.taxonomyPath,
2478
- taxonomyAction: plan.taxonomyAction,
2479
2124
  eventsPath: plan.eventsPath,
2480
2125
  eventsAction: plan.eventsAction,
2481
2126
  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
2127
+ forensicAction: plan.forensicAction
2497
2128
  };
2498
2129
  }
2499
2130
  async function executeInitStagePlan(plan, stageName) {
@@ -2508,25 +2139,18 @@ async function executeInitStagePlan(plan, stageName) {
2508
2139
  try {
2509
2140
  switch (stage.name) {
2510
2141
  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" };
2515
- }
2516
- console.log(
2517
- formatInitStageResult("bootstrap", "completed", result.installed.length, result.skipped.length)
2518
- );
2519
- return { name: "bootstrap", disposition: "ran" };
2142
+ console.log(formatInitStageResult("bootstrap", "skipped", 0, 0, t("cli.bootstrap.install.no-targets")));
2143
+ return { name: "bootstrap", disposition: "skipped" };
2520
2144
  }
2521
2145
  case "mcp": {
2522
2146
  if (stage.installMode === "local") {
2523
2147
  const manager = stage.packageManager ?? detectPackageManager(plan.target);
2524
- writeStderr3(t("cli.init.mcp.install.local"));
2525
- writeStderr3(t("cli.init.mcp.local.installing", { manager }));
2148
+ writeStderr2(t("cli.init.mcp.install.local"));
2149
+ writeStderr2(t("cli.init.mcp.local.installing", { manager }));
2526
2150
  installLocalFabricServer(plan.target, manager);
2527
- writeStderr3(t("cli.init.mcp.local.installed"));
2151
+ writeStderr2(t("cli.init.mcp.local.installed"));
2528
2152
  } else {
2529
- writeStderr3(t("cli.init.mcp.install.global"));
2153
+ writeStderr2(t("cli.init.mcp.install.global"));
2530
2154
  }
2531
2155
  const result = await installMcpClients(plan.target, {
2532
2156
  force: plan.options.force,
@@ -2549,12 +2173,12 @@ async function executeInitStagePlan(plan, stageName) {
2549
2173
  return exhaustiveInitStagePlan(stage);
2550
2174
  }
2551
2175
  } catch (error) {
2552
- writeStderr3(formatInitStageFailure(stageName, error));
2176
+ writeStderr2(formatInitStageFailure(stageName, error));
2553
2177
  return { name: stageName, disposition: "failed" };
2554
2178
  }
2555
2179
  }
2556
2180
  function shouldReplaceWritableDirectory(path, options) {
2557
- if (!existsSync9(path)) {
2181
+ if (!existsSync8(path)) {
2558
2182
  return false;
2559
2183
  }
2560
2184
  if (statSync3(path).isDirectory()) {
@@ -2566,7 +2190,7 @@ function shouldReplaceWritableDirectory(path, options) {
2566
2190
  return true;
2567
2191
  }
2568
2192
  function planFreshPath(path, options) {
2569
- if (!existsSync9(path)) {
2193
+ if (!existsSync8(path)) {
2570
2194
  return "created";
2571
2195
  }
2572
2196
  if (!options?.force) {
@@ -2575,131 +2199,11 @@ function planFreshPath(path, options) {
2575
2199
  return "overwritten";
2576
2200
  }
2577
2201
  function preparePlannedPath(path, action) {
2578
- mkdirSync3(dirname5(path), { recursive: true });
2579
- if (action === "overwritten" && existsSync9(path)) {
2202
+ mkdirSync(dirname3(path), { recursive: true });
2203
+ if (action === "overwritten" && existsSync8(path)) {
2580
2204
  rmSync(path, { recursive: true, force: true });
2581
2205
  }
2582
2206
  }
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
2207
  function createDefaultInitWizardAdapter() {
2704
2208
  return {
2705
2209
  async run(context) {
@@ -2871,23 +2375,23 @@ function formatInitModeBadge(options) {
2871
2375
  }
2872
2376
  return t("cli.init.mode.badge.default");
2873
2377
  }
2874
- function normalizeTarget4(targetInput) {
2875
- return isAbsolute4(targetInput) ? targetInput : resolve9(process.cwd(), targetInput);
2378
+ function normalizeTarget3(targetInput) {
2379
+ return isAbsolute3(targetInput) ? targetInput : resolve7(process.cwd(), targetInput);
2876
2380
  }
2877
2381
  function assertExistingDirectory3(target) {
2878
- if (!existsSync9(target) || !statSync3(target).isDirectory()) {
2382
+ if (!existsSync8(target) || !statSync3(target).isDirectory()) {
2879
2383
  throw new Error(`Target must be an existing directory: ${target}`);
2880
2384
  }
2881
2385
  }
2882
2386
  function detectPackageManager(cwd) {
2883
- const workspaceRoot = resolve9(cwd);
2884
- if (existsSync9(join8(workspaceRoot, "pnpm-lock.yaml"))) {
2387
+ const workspaceRoot = resolve7(cwd);
2388
+ if (existsSync8(join6(workspaceRoot, "pnpm-lock.yaml"))) {
2885
2389
  return "pnpm";
2886
2390
  }
2887
- if (existsSync9(join8(workspaceRoot, "yarn.lock"))) {
2391
+ if (existsSync8(join6(workspaceRoot, "yarn.lock"))) {
2888
2392
  return "yarn";
2889
2393
  }
2890
- if (existsSync9(join8(workspaceRoot, "package-lock.json"))) {
2394
+ if (existsSync8(join6(workspaceRoot, "package-lock.json"))) {
2891
2395
  return "npm";
2892
2396
  }
2893
2397
  return "npm";
@@ -2896,7 +2400,7 @@ function resolveMcpInstallMode(rawMode) {
2896
2400
  if (rawMode === void 0 || rawMode === "global" || rawMode === "local") {
2897
2401
  return rawMode ?? "global";
2898
2402
  }
2899
- writeStderr3(t("cli.init.mcp.install.invalid", { value: rawMode }));
2403
+ writeStderr2(t("cli.init.mcp.install.invalid", { value: rawMode }));
2900
2404
  return "global";
2901
2405
  }
2902
2406
  function installLocalFabricServer(target, manager) {
@@ -2907,20 +2411,11 @@ function installLocalFabricServer(target, manager) {
2907
2411
  shell: process.platform === "win32"
2908
2412
  });
2909
2413
  }
2910
- function createInitialMeta(agentsHash) {
2414
+ function createInitialMeta() {
2911
2415
  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
- }
2416
+ revision: "sha256:initial",
2417
+ nodes: {},
2418
+ counters: defaultAgentsMetaCounters()
2924
2419
  };
2925
2420
  }
2926
2421
  function appendReapplyLedgerEvent(eventsPath, payload) {
@@ -2930,127 +2425,12 @@ function appendReapplyLedgerEvent(eventsPath, payload) {
2930
2425
  ts: Date.now(),
2931
2426
  schema_version: 1,
2932
2427
  event_type: "reapply_completed",
2933
- preserved_ledger: payload.preserved_ledger,
2934
- preserved_meta: payload.preserved_meta,
2935
- rules_count: payload.rules_count
2428
+ preserved_ledger: payload.preserved_ledger
2936
2429
  };
2937
2430
  const line = `${JSON.stringify(event)}
2938
2431
  `;
2939
2432
  appendFileSync(eventsPath, line, "utf8");
2940
2433
  }
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;
3030
- }
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
- }
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 });
3052
- }
3053
- }
3054
2434
  function formatInitStageHeader(message) {
3055
2435
  return `${nextLabel()} ${paint.muted(message)}`;
3056
2436
  }
@@ -3101,9 +2481,8 @@ function printInitPlanSummary(target, options, mcpInstallMode, supports) {
3101
2481
  })
3102
2482
  );
3103
2483
  console.log(t("cli.init.plan.writes"));
3104
- console.log(` - ${target}/.fabric/bootstrap/README.md`);
2484
+ console.log(` - ${target}/.fabric/knowledge/{decisions,pitfalls,guidelines,models,processes,pending}/`);
3105
2485
  console.log(` - ${target}/.fabric/agents.meta.json`);
3106
- console.log(` - ${target}/.fabric/INITIAL_TAXONOMY.md`);
3107
2486
  console.log(` - ${target}/.fabric/events.jsonl`);
3108
2487
  console.log(` - ${target}/.fabric/forensic.json`);
3109
2488
  }
@@ -3137,18 +2516,6 @@ function printInitCapabilitySummary(supports, stageResults, options) {
3137
2516
  console.log(formatCapabilityTableRow(row, widths));
3138
2517
  }
3139
2518
  }
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
2519
  function toCapabilityRow(support, stageResults, options) {
3153
2520
  const stage = (name) => stageResults.find((entry) => entry.name === name)?.disposition ?? null;
3154
2521
  const bootstrap = support.capabilities.bootstrap ? capabilityStatus(options.skipBootstrap ? "skipped" : stage("bootstrap")) : t("cli.init.capabilities.status.na");
@@ -3209,18 +2576,6 @@ function formatCapabilityDivider(widths) {
3209
2576
  }
3210
2577
  function formatInitReasonMessage(supports) {
3211
2578
  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
2579
  if (detected.some((support) => support.capabilities.skill)) {
3225
2580
  return t("cli.init.reason-message.installable-body");
3226
2581
  }
@@ -3232,11 +2587,11 @@ function yesNoLabel(value) {
3232
2587
  function formatInitPathAction(path, action) {
3233
2588
  return t("cli.init.created-path", { label: labelForInitWriteAction(action), path });
3234
2589
  }
3235
- function formatOptionalInitPathAction(path, action) {
3236
- if (action === "skipped") {
2590
+ function formatAgentsMdAction(path, action) {
2591
+ if (action === "preserved") {
3237
2592
  return t("cli.init.skipped-existing-path", { label: skippedLabel(), path });
3238
2593
  }
3239
- return formatInitPathAction(path, action);
2594
+ return t("cli.init.created-path", { label: createdLabel(), path });
3240
2595
  }
3241
2596
  function labelForInitWriteAction(action) {
3242
2597
  return action === "overwritten" ? overwrittenLabel() : createdLabel();
@@ -3253,9 +2608,6 @@ function nextLabel() {
3253
2608
  function reasonLabel() {
3254
2609
  return paint.human(t("cli.shared.reason"));
3255
2610
  }
3256
- function updatedLabel() {
3257
- return paint.success(t("cli.shared.updated"));
3258
- }
3259
2611
  function overwrittenLabel() {
3260
2612
  return paint.warn(t("cli.init.force.overwritten"));
3261
2613
  }
@@ -3268,13 +2620,10 @@ function skippedStageLabel() {
3268
2620
  function failedStageLabel() {
3269
2621
  return paint.error(t("cli.init.stages.failed"));
3270
2622
  }
3271
- function writeStderr3(message) {
2623
+ function writeStderr2(message) {
3272
2624
  process.stderr.write(`${message}
3273
2625
  `);
3274
2626
  }
3275
- function sha256(content) {
3276
- return `sha256:${createHash("sha256").update(content).digest("hex")}`;
3277
- }
3278
2627
  export {
3279
2628
  buildInitExecutionPlan,
3280
2629
  buildInitFabricPlan,