@fenglimg/fabric-cli 1.8.0-rc.3 → 2.0.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 (46) hide show
  1. package/README.md +6 -6
  2. package/dist/chunk-6ICJICVU.js +10 -0
  3. package/dist/chunk-74SZWYPH.js +658 -0
  4. package/dist/chunk-EYIDD2YS.js +1000 -0
  5. package/dist/{chunk-QPCRBQ5Y.js → chunk-OBQU6NHO.js} +1 -52
  6. package/dist/chunk-WWNXR34K.js +49 -0
  7. package/dist/doctor-T7JWODKG.js +282 -0
  8. package/dist/hooks-Y74Y5LQS.js +12 -0
  9. package/dist/index.js +7 -5
  10. package/dist/{init-7EYGUJNJ.js → init-BIRSIOXO.js} +312 -1022
  11. package/dist/plan-context-hint-QMUPAXIB.js +98 -0
  12. package/dist/scan-LMK3UCWL.js +22 -0
  13. package/dist/{serve-466QXQ5Q.js → serve-H554BHLG.js} +8 -4
  14. package/package.json +3 -3
  15. package/templates/agents-md/AGENTS.md.template +55 -17
  16. package/templates/bootstrap/CLAUDE.md +1 -1
  17. package/templates/bootstrap/codex-AGENTS-header.md +1 -1
  18. package/templates/bootstrap/cursor-fabric-bootstrap.mdc +1 -1
  19. package/templates/hooks/configs/README.md +73 -0
  20. package/templates/hooks/configs/claude-code.json +37 -0
  21. package/templates/hooks/configs/codex-hooks.json +20 -0
  22. package/templates/hooks/configs/cursor-hooks.json +20 -0
  23. package/templates/hooks/fabric-hint.cjs +1307 -0
  24. package/templates/hooks/knowledge-hint-broad.cjs +464 -0
  25. package/templates/hooks/knowledge-hint-narrow.cjs +826 -0
  26. package/templates/hooks/lib/session-digest-writer.cjs +172 -0
  27. package/templates/skills/fabric-archive/SKILL.md +486 -0
  28. package/templates/skills/fabric-import/SKILL.md +588 -0
  29. package/templates/skills/fabric-review/SKILL.md +382 -0
  30. package/dist/chunk-NMMUETVK.js +0 -216
  31. package/dist/doctor-F52XWWZC.js +0 -98
  32. package/dist/scan-NNBNGIZG.js +0 -12
  33. package/templates/agents-md/variants/cocos.md +0 -20
  34. package/templates/agents-md/variants/next.md +0 -20
  35. package/templates/agents-md/variants/vite.md +0 -20
  36. package/templates/bootstrap/GEMINI.md +0 -8
  37. package/templates/bootstrap/roo-fabric.md +0 -5
  38. package/templates/bootstrap/windsurf-fabric.md +0 -5
  39. package/templates/claude-hooks/fabric-init-reminder.cjs +0 -18
  40. package/templates/claude-skills/fabric-init/SKILL.md +0 -163
  41. package/templates/codex-hooks/fabric-session-start.cjs +0 -19
  42. package/templates/codex-hooks/fabric-stop-reminder.cjs +0 -18
  43. package/templates/codex-skills/fabric-init/SKILL.md +0 -162
  44. package/templates/husky/pre-commit +0 -9
  45. package/templates/skill-source/fabric-init/SOURCE.md +0 -157
  46. package/templates/skill-source/fabric-init/clients.json +0 -17
@@ -1,247 +1,77 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- createScanReport,
4
- detectFramework
5
- } from "./chunk-NMMUETVK.js";
3
+ ClaudeCodeCLIWriter,
4
+ CursorWriter,
5
+ addArchiveSkillPointer,
6
+ createServerEntry,
7
+ hooksCommand,
8
+ installArchiveHintHook,
9
+ installFabricArchiveSkill,
10
+ installFabricImportSkill,
11
+ installFabricReviewSkill,
12
+ installHooks,
13
+ installKnowledgeHintBroadHook,
14
+ installKnowledgeHintNarrowHook,
15
+ mergeClaudeCodeHookConfig,
16
+ mergeCodexHookConfig,
17
+ mergeCursorHookConfig,
18
+ normalizeConfigPath,
19
+ writeJsonClientConfig
20
+ } from "./chunk-74SZWYPH.js";
21
+ import {
22
+ detectFramework,
23
+ runInitScan
24
+ } from "./chunk-EYIDD2YS.js";
6
25
  import {
7
- createDebugLogger,
8
26
  displayWidth,
9
27
  padEnd,
10
- paint,
11
- readFabricConfig,
12
- resolveDevMode,
28
+ paint
29
+ } from "./chunk-WWNXR34K.js";
30
+ import {
13
31
  t
14
- } from "./chunk-QPCRBQ5Y.js";
32
+ } from "./chunk-6ICJICVU.js";
33
+ import {
34
+ createDebugLogger,
35
+ resolveDevMode
36
+ } from "./chunk-OBQU6NHO.js";
15
37
 
16
38
  // src/commands/init.ts
17
- import { createHash, randomUUID } from "crypto";
39
+ import { randomUUID } from "crypto";
40
+ import { homedir as homedir4 } from "os";
18
41
  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";
42
+ import { appendFileSync, existsSync as existsSync6, mkdirSync, rmSync, statSync as statSync2, writeFileSync } from "fs";
43
+ import { dirname as dirname2, isAbsolute as isAbsolute2, join as join5, resolve as resolve5 } from "path";
22
44
  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";
45
+ import { defaultAgentsMetaCounters } from "@fenglimg/fabric-shared";
46
+ import { atomicWriteJson, atomicWriteText as atomicWriteText2 } from "@fenglimg/fabric-shared/node/atomic-write";
47
+ import { defineCommand as defineCommand2 } from "citty";
25
48
  import { checkLockOrThrow } from "@fenglimg/fabric-server";
26
49
 
27
- // src/bootstrap-guide.ts
28
- import { existsSync, mkdirSync, readFileSync } from "fs";
29
- import { dirname, isAbsolute, join, parse, resolve } from "path";
50
+ // src/commands/config.ts
51
+ import { existsSync as existsSync4 } from "fs";
52
+ import { readFile as readFile2 } from "fs/promises";
53
+ import { resolve as resolve3 } from "path";
30
54
  import { fileURLToPath } from "url";
31
- import { atomicWriteText } from "@fenglimg/fabric-shared/node/atomic-write";
32
- var AGENTS_TEMPLATE_BY_FRAMEWORK = {
33
- "cocos-creator": "templates/agents-md/variants/cocos.md",
34
- vite: "templates/agents-md/variants/vite.md",
35
- next: "templates/agents-md/variants/next.md"
36
- };
37
- var FABRIC_GUIDE_PATH = ".fabric/bootstrap/README.md";
38
- async function buildFabricBootstrapGuide(target) {
39
- const workspaceRoot = normalizeTarget(target);
40
- const scanReport = await createScanReport(workspaceRoot);
41
- const template = readFileSync(findBootstrapTemplatePath(scanReport.framework.kind), "utf8");
42
- const packageName = readPackageName(workspaceRoot) ?? parse(workspaceRoot).base;
43
- return ensureTrailingNewline(
44
- template.replaceAll("{ projectName }", packageName).replaceAll("{ frameworkKind }", scanReport.framework.kind)
45
- );
46
- }
47
- async function ensureFabricBootstrapGuide(workspaceRoot, force) {
48
- const guidePath = resolve(workspaceRoot, FABRIC_GUIDE_PATH);
49
- if (existsSync(guidePath) && !force) {
50
- return;
51
- }
52
- mkdirSync(dirname(guidePath), { recursive: true });
53
- await atomicWriteText(guidePath, await buildFabricBootstrapGuide(workspaceRoot));
54
- }
55
- function findBootstrapTemplatePath(frameworkKind) {
56
- const relativePath = AGENTS_TEMPLATE_BY_FRAMEWORK[frameworkKind] ?? "templates/agents-md/AGENTS.md.template";
57
- return findTemplatePath(relativePath);
58
- }
59
- function readPackageName(target) {
60
- const packageJsonPath = join(target, "package.json");
61
- if (!existsSync(packageJsonPath)) {
62
- return void 0;
63
- }
64
- try {
65
- const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
66
- return packageJson.name;
67
- } catch {
68
- return void 0;
69
- }
70
- }
71
- function findTemplatePath(relativePath) {
72
- const currentModuleDir = dirname(fileURLToPath(import.meta.url));
73
- const candidates = [
74
- ...templateCandidatesFrom(process.cwd(), relativePath),
75
- ...templateCandidatesFrom(currentModuleDir, relativePath)
76
- ];
77
- for (const candidate of candidates) {
78
- if (existsSync(candidate)) {
79
- return candidate;
80
- }
81
- }
82
- throw new Error(t("cli.shared.template-not-found", { path: relativePath }));
83
- }
84
- function templateCandidatesFrom(start, relativePath) {
85
- const candidates = [];
86
- let current = resolve(start);
87
- while (true) {
88
- candidates.push(join(current, ...relativePath.split("/")));
89
- const parent = dirname(current);
90
- if (parent === current || parse(current).root === current) {
91
- break;
92
- }
93
- current = parent;
94
- }
95
- return candidates.reverse();
96
- }
97
- function ensureTrailingNewline(content) {
98
- return content.endsWith("\n") ? content : `${content}
99
- `;
100
- }
101
- function normalizeTarget(targetInput) {
102
- return isAbsolute(targetInput) ? targetInput : resolve(process.cwd(), targetInput);
103
- }
104
-
105
- // src/commands/bootstrap.ts
106
- import { resolve as resolve5 } from "path";
107
55
  import { defineCommand } from "citty";
108
56
 
109
57
  // src/config/resolver.ts
110
- import { existsSync as existsSync5 } from "fs";
111
- import { join as join5 } from "path";
112
- import { homedir as homedir4 } from "os";
113
-
114
- // src/config/claude-code.ts
115
58
  import { existsSync as existsSync3 } from "fs";
116
- import { join as join3, resolve as resolve3 } from "path";
117
- import { homedir as homedir2, platform } from "os";
118
-
119
- // src/config/json.ts
120
- import { existsSync as existsSync2 } from "fs";
121
- import { mkdir, readFile } from "fs/promises";
122
- import { dirname as dirname2, join as join2, resolve as resolve2 } from "path";
123
- import { homedir } from "os";
124
- import { atomicWriteJson } from "@fenglimg/fabric-shared/node/atomic-write";
125
-
126
- // src/config/writer.ts
127
- function createServerEntry(serverPath) {
128
- return {
129
- command: process.execPath,
130
- args: [serverPath]
131
- };
132
- }
133
-
134
- // src/config/json.ts
135
- function deepMerge(target, source) {
136
- if (target === null || typeof target !== "object" || Array.isArray(target) || source === null || typeof source !== "object" || Array.isArray(source)) {
137
- return source;
138
- }
139
- const out = { ...target };
140
- for (const key of Object.keys(source)) {
141
- out[key] = deepMerge(
142
- target[key],
143
- source[key]
144
- );
145
- }
146
- return out;
147
- }
148
- function expandHome(filePath) {
149
- if (filePath === "~") {
150
- return homedir();
151
- }
152
- if (filePath.startsWith("~/")) {
153
- return join2(homedir(), filePath.slice(2));
154
- }
155
- return filePath;
156
- }
157
- function normalizeConfigPath(filePath) {
158
- return resolve2(expandHome(filePath));
159
- }
160
- async function readJsonConfig(configPath) {
161
- try {
162
- const raw = await readFile(configPath, "utf8");
163
- if (raw.trim().length === 0) {
164
- return {};
165
- }
166
- const parsed = JSON.parse(raw);
167
- if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
168
- throw new Error(`Expected JSON object in ${configPath}`);
169
- }
170
- return parsed;
171
- } catch (error) {
172
- if (error instanceof Error && "code" in error && error.code === "ENOENT") {
173
- return {};
174
- }
175
- throw error;
176
- }
177
- }
178
- async function writeJsonClientConfig(configPath, serverEntry) {
179
- const existing = await readJsonConfig(configPath);
180
- const merged = deepMerge(existing, { mcpServers: { fabric: serverEntry } });
181
- await mkdir(dirname2(configPath), { recursive: true });
182
- await atomicWriteJson(configPath, merged, { indent: 2 });
183
- }
184
- var JsonClientConfigWriter = class {
185
- configuredPath;
186
- constructor(configuredPath) {
187
- this.configuredPath = configuredPath;
188
- }
189
- async detect(workspaceRoot, overridePath) {
190
- const explicitPath = overridePath ?? this.configuredPath;
191
- if (explicitPath !== void 0) {
192
- return normalizeConfigPath(explicitPath);
193
- }
194
- const configPath = this.defaultPath(workspaceRoot);
195
- return configPath === null ? null : normalizeConfigPath(configPath);
196
- }
197
- async write(serverPath, workspaceRoot, overridePath) {
198
- const configPath = await this.detect(workspaceRoot, overridePath);
199
- if (configPath === null) {
200
- return;
201
- }
202
- await writeJsonClientConfig(configPath, createServerEntry(serverPath));
203
- }
204
- };
205
- var ClaudeCodeCLIWriter = class extends JsonClientConfigWriter {
206
- clientKind = "ClaudeCodeCLI";
207
- scope;
208
- constructor(configuredPath, scope = "project") {
209
- super(configuredPath);
210
- this.scope = scope;
211
- }
212
- // Writes to project-level .mcp.json (per Claude Code MCP spec) by default,
213
- // or ~/.claude.json for user scope.
214
- // Detection still checks ~/.claude to confirm Claude Code is installed.
215
- defaultPath(workspaceRoot) {
216
- const globalClaudeDir = join2(homedir(), ".claude");
217
- const projectClaudeDir = join2(workspaceRoot, ".claude");
218
- if (!existsSync2(globalClaudeDir) && !existsSync2(projectClaudeDir)) {
219
- return null;
220
- }
221
- return this.scope === "user" ? join2(homedir(), ".claude.json") : join2(workspaceRoot, ".mcp.json");
222
- }
223
- };
224
- var CursorWriter = class extends JsonClientConfigWriter {
225
- clientKind = "Cursor";
226
- constructor(configuredPath) {
227
- super(configuredPath);
228
- }
229
- defaultPath(workspaceRoot) {
230
- const cursorDir = join2(workspaceRoot, ".cursor");
231
- return existsSync2(cursorDir) ? join2(cursorDir, "mcp.json") : null;
232
- }
233
- };
59
+ import { join as join3 } from "path";
60
+ import { homedir as homedir3 } from "os";
234
61
 
235
62
  // src/config/claude-code.ts
63
+ import { existsSync } from "fs";
64
+ import { join, resolve } from "path";
65
+ import { homedir, platform } from "os";
236
66
  function getClaudeDesktopConfigPath() {
237
67
  const os = platform();
238
68
  if (os === "darwin") {
239
- return join3(homedir2(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
69
+ return join(homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
240
70
  }
241
71
  if (os === "win32") {
242
- return join3(process.env.APPDATA ?? join3(homedir2(), "AppData", "Roaming"), "Claude", "claude_desktop_config.json");
72
+ return join(process.env.APPDATA ?? join(homedir(), "AppData", "Roaming"), "Claude", "claude_desktop_config.json");
243
73
  }
244
- return join3(homedir2(), ".config", "Claude", "claude_desktop_config.json");
74
+ return join(homedir(), ".config", "Claude", "claude_desktop_config.json");
245
75
  }
246
76
  var ClaudeCodeDesktopWriter = class {
247
77
  clientKind = "ClaudeCodeDesktop";
@@ -251,7 +81,7 @@ var ClaudeCodeDesktopWriter = class {
251
81
  }
252
82
  async detect(_workspaceRoot, overridePath) {
253
83
  const configPath = normalizeConfigPath(overridePath ?? this.configuredPath ?? getClaudeDesktopConfigPath());
254
- return existsSync3(configPath) || overridePath !== void 0 || this.configuredPath !== void 0 ? configPath : null;
84
+ return existsSync(configPath) || overridePath !== void 0 || this.configuredPath !== void 0 ? configPath : null;
255
85
  }
256
86
  async write(serverPath, workspaceRoot, overridePath) {
257
87
  const configPath = await this.detect(workspaceRoot, overridePath);
@@ -266,17 +96,17 @@ var ClaudeCodeDesktopWriter = class {
266
96
  };
267
97
 
268
98
  // src/config/toml.ts
269
- import { existsSync as existsSync4 } from "fs";
270
- import { mkdir as mkdir2, readFile as readFile2 } from "fs/promises";
271
- import { dirname as dirname3, join as join4, resolve as resolve4 } from "path";
272
- import { homedir as homedir3 } from "os";
273
- import { atomicWriteText as atomicWriteText2 } from "@fenglimg/fabric-shared/node/atomic-write";
274
- function expandHome2(filePath) {
99
+ import { existsSync as existsSync2 } from "fs";
100
+ import { mkdir, readFile } from "fs/promises";
101
+ import { dirname, join as join2, resolve as resolve2 } from "path";
102
+ import { homedir as homedir2 } from "os";
103
+ import { atomicWriteText } from "@fenglimg/fabric-shared/node/atomic-write";
104
+ function expandHome(filePath) {
275
105
  if (filePath === "~") {
276
- return homedir3();
106
+ return homedir2();
277
107
  }
278
108
  if (filePath.startsWith("~/")) {
279
- return join4(homedir3(), filePath.slice(2));
109
+ return join2(homedir2(), filePath.slice(2));
280
110
  }
281
111
  return filePath;
282
112
  }
@@ -325,7 +155,7 @@ ${block}`;
325
155
  }
326
156
  async function readTomlConfigText(configPath) {
327
157
  try {
328
- return await readFile2(configPath, "utf8");
158
+ return await readFile(configPath, "utf8");
329
159
  } catch (error) {
330
160
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
331
161
  return "";
@@ -342,10 +172,10 @@ var CodexTOMLConfigWriter = class {
342
172
  async detect(_workspaceRoot, overridePath) {
343
173
  const explicitPath = overridePath ?? this.configuredPath;
344
174
  if (explicitPath !== void 0) {
345
- return resolve4(expandHome2(explicitPath));
175
+ return resolve2(expandHome(explicitPath));
346
176
  }
347
- const codexDir = join4(homedir3(), ".codex");
348
- return existsSync4(codexDir) ? resolve4(join4(codexDir, "config.toml")) : null;
177
+ const codexDir = join2(homedir2(), ".codex");
178
+ return existsSync2(codexDir) ? resolve2(join2(codexDir, "config.toml")) : null;
349
179
  }
350
180
  async write(serverPath, workspaceRoot, overridePath) {
351
181
  const configPath = await this.detect(workspaceRoot, overridePath);
@@ -354,8 +184,8 @@ var CodexTOMLConfigWriter = class {
354
184
  }
355
185
  const rawConfig = await readTomlConfigText(configPath);
356
186
  const nextConfig = upsertCodexServerBlock(rawConfig, "fabric", createServerEntry(serverPath));
357
- await mkdir2(dirname3(configPath), { recursive: true });
358
- await atomicWriteText2(configPath, nextConfig);
187
+ await mkdir(dirname(configPath), { recursive: true });
188
+ await atomicWriteText(configPath, nextConfig);
359
189
  }
360
190
  };
361
191
 
@@ -375,25 +205,25 @@ function resolveClients(workspaceRoot, fabricConfig = {}, opts = {}) {
375
205
  const claudeMcpScope = opts.claudeMcpScope ?? "project";
376
206
  addIfDetected(
377
207
  writers,
378
- existsSync5(join5(homedir4(), ".claude")) || existsSync5(join5(workspaceRoot, ".claude")),
208
+ existsSync3(join3(homedir3(), ".claude")) || existsSync3(join3(workspaceRoot, ".claude")),
379
209
  (configuredPath) => new ClaudeCodeCLIWriter(configuredPath, claudeMcpScope),
380
210
  hasExplicitPath(clientPaths, "claudeCodeCLI") ? clientPaths.claudeCodeCLI : void 0
381
211
  );
382
212
  addIfDetected(
383
213
  writers,
384
- existsSync5(getClaudeDesktopConfigPath()),
214
+ existsSync3(getClaudeDesktopConfigPath()),
385
215
  (configuredPath) => new ClaudeCodeDesktopWriter(configuredPath),
386
216
  hasExplicitPath(clientPaths, "claudeCodeDesktop") ? clientPaths.claudeCodeDesktop : void 0
387
217
  );
388
218
  addIfDetected(
389
219
  writers,
390
- existsSync5(join5(workspaceRoot, ".cursor")),
220
+ existsSync3(join3(workspaceRoot, ".cursor")),
391
221
  (configuredPath) => new CursorWriter(configuredPath),
392
222
  hasExplicitPath(clientPaths, "cursor") ? clientPaths.cursor : void 0
393
223
  );
394
224
  addIfDetected(
395
225
  writers,
396
- existsSync5(join5(homedir4(), ".codex")),
226
+ existsSync3(join3(homedir3(), ".codex")),
397
227
  (configuredPath) => new CodexTOMLConfigWriter(configuredPath),
398
228
  hasExplicitPath(clientPaths, "codexCLI") ? clientPaths.codexCLI : void 0
399
229
  );
@@ -401,10 +231,10 @@ function resolveClients(workspaceRoot, fabricConfig = {}, opts = {}) {
401
231
  }
402
232
  function detectClientSupports(workspaceRoot, fabricConfig = {}) {
403
233
  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"));
234
+ const claudeDetected = existsSync3(join3(homedir3(), ".claude")) || existsSync3(join3(workspaceRoot, ".claude"));
235
+ const claudeDesktopDetected = existsSync3(getClaudeDesktopConfigPath());
236
+ const cursorDetected = existsSync3(join3(workspaceRoot, ".cursor"));
237
+ const codexDetected = existsSync3(join3(homedir3(), ".codex"));
408
238
  return [
409
239
  {
410
240
  clientKind: "ClaudeCodeCLI",
@@ -462,299 +292,18 @@ function detectClientSupports(workspaceRoot, fabricConfig = {}) {
462
292
  skill: true
463
293
  },
464
294
  installedCapabilities: {
465
- hook: existsSync5(join5(workspaceRoot, ".codex", "hooks.json")),
466
- skill: existsSync5(join5(workspaceRoot, ".codex", "skills", "fabric-init", "SKILL.md"))
467
- }
468
- }
469
- ];
470
- }
471
-
472
- // src/commands/bootstrap.ts
473
- var CLIENT_ALIASES = {
474
- claude: "claude",
475
- "claude-code": "claude",
476
- claudecode: "claude",
477
- claudecli: "claude",
478
- claudecodecli: "claude",
479
- claudedesktop: "claude",
480
- claudecodedesktop: "claude",
481
- cursor: "cursor",
482
- codex: "codex",
483
- "codex-cli": "codex",
484
- codexcli: "codex"
485
- };
486
- var bootstrapCommand = defineCommand({
487
- meta: {
488
- name: "bootstrap",
489
- description: t("cli.bootstrap.description")
490
- },
491
- subCommands: {
492
- install: defineCommand({
493
- meta: {
494
- name: "install",
495
- description: t("cli.bootstrap.install.description")
496
- },
497
- args: {
498
- clients: {
499
- type: "string",
500
- description: t("cli.bootstrap.install.args.clients.description")
501
- }
502
- },
503
- async run({ args }) {
504
- const workspaceRoot = process.cwd();
505
- const selectedClients = parseClientFilter(args.clients);
506
- const result = await installBootstrap(workspaceRoot, {
507
- clients: selectedClients === null ? void 0 : Array.from(selectedClients, mapBootstrapClientToClientKind)
508
- });
509
- if (result.details.length === 0) {
510
- process.stderr.write(
511
- `${t("cli.bootstrap.install.no-targets")}
512
- `
513
- );
514
- return;
515
- }
516
- for (const detail of result.details) {
517
- if (detail.action === "skipped") {
518
- process.stderr.write(`${t("cli.bootstrap.install.skipped-header", { path: detail.path })}
519
- `);
520
- continue;
521
- }
522
- if (detail.action === "prepended") {
523
- process.stderr.write(`${t("cli.bootstrap.install.prepended", { path: detail.path })}
524
- `);
525
- continue;
526
- }
527
- process.stderr.write(`${t("cli.bootstrap.install.installed", { path: detail.path })}
528
- `);
529
- }
530
- }
531
- })
532
- }
533
- });
534
- async function installBootstrap(target, options = {}) {
535
- const workspaceRoot = resolve5(target);
536
- const fabricConfig = readFabricConfig(workspaceRoot);
537
- const targets = resolveBootstrapTargets(workspaceRoot, fabricConfig, options.clients);
538
- const installed = [];
539
- const skipped = [];
540
- const details = [];
541
- await ensureFabricBootstrapGuide(workspaceRoot, options.force);
542
- for (const bootstrapTarget of targets) {
543
- details.push({
544
- client: bootstrapTarget.client,
545
- path: resolve5(workspaceRoot, FABRIC_GUIDE_PATH),
546
- action: "skipped"
547
- });
548
- skipped.push(bootstrapTarget.client);
549
- }
550
- return { installed, skipped, details };
551
- }
552
- function parseClientFilter(value) {
553
- if (value === void 0 || value.trim().length === 0) {
554
- return null;
555
- }
556
- const clients = /* @__PURE__ */ new Set();
557
- for (const rawClient of value.split(",")) {
558
- const alias = rawClient.trim().toLowerCase();
559
- const client = CLIENT_ALIASES[alias];
560
- if (client === void 0) {
561
- throw new Error(t("cli.bootstrap.errors.unknown-client", { client: rawClient }));
562
- }
563
- clients.add(client);
564
- }
565
- return clients;
566
- }
567
- function resolveBootstrapTargets(workspaceRoot, fabricConfig, selectedClients) {
568
- const targets = [];
569
- const seenClients = /* @__PURE__ */ new Set();
570
- const clientKinds = selectedClients ?? resolveClients(workspaceRoot, fabricConfig).map((writer) => writer.clientKind);
571
- for (const clientKind of clientKinds) {
572
- const bootstrapClient = mapClientKind(clientKind);
573
- if (bootstrapClient === null || seenClients.has(bootstrapClient)) {
574
- continue;
575
- }
576
- seenClients.add(bootstrapClient);
577
- targets.push({ client: clientKind, bootstrapClient });
578
- }
579
- return targets;
580
- }
581
- function mapClientKind(clientKind) {
582
- switch (clientKind) {
583
- case "ClaudeCodeCLI":
584
- case "ClaudeCodeDesktop":
585
- return "claude";
586
- case "Cursor":
587
- return "cursor";
588
- case "CodexCLI":
589
- return "codex";
590
- default:
591
- return null;
592
- }
593
- }
594
- function mapBootstrapClientToClientKind(client) {
595
- switch (client) {
596
- case "claude":
597
- return "ClaudeCodeCLI";
598
- case "cursor":
599
- return "Cursor";
600
- case "codex":
601
- return "CodexCLI";
602
- }
603
- }
604
-
605
- // src/commands/config.ts
606
- import { existsSync as existsSync7 } from "fs";
607
- import { readFile as readFile3 } from "fs/promises";
608
- import { resolve as resolve7 } from "path";
609
- import { fileURLToPath as fileURLToPath3 } from "url";
610
- import { defineCommand as defineCommand3 } from "citty";
611
-
612
- // src/commands/hooks.ts
613
- import { chmodSync, existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync2, statSync } from "fs";
614
- import { dirname as dirname4, isAbsolute as isAbsolute2, join as join6, parse as parse2, resolve as resolve6 } from "path";
615
- import { fileURLToPath as fileURLToPath2 } from "url";
616
- import { defineCommand as defineCommand2 } from "citty";
617
- import { atomicWriteJson as atomicWriteJson2, atomicWriteText as atomicWriteText3 } from "@fenglimg/fabric-shared/node/atomic-write";
618
- var hooksCommand = defineCommand2({
619
- meta: {
620
- name: "hooks",
621
- description: t("cli.hooks.description")
622
- },
623
- subCommands: {
624
- install: defineCommand2({
625
- meta: {
626
- name: "install",
627
- description: t("cli.hooks.install.description")
628
- },
629
- args: {
630
- target: {
631
- type: "string",
632
- description: t("cli.hooks.install.args.target.description"),
633
- default: process.cwd()
634
- }
635
- },
636
- async run({ args }) {
637
- const result = await installHooks(args.target);
638
- if (result.hookAction === "skipped") {
639
- writeStderr(t("cli.hooks.install.hook-skipped", { path: result.hookPath }));
640
- } else if (result.hookAction === "appended") {
641
- writeStderr(t("cli.hooks.install.hook-appended", { path: result.hookPath }));
642
- } else {
643
- writeStderr(t("cli.hooks.install.hook-created", { path: result.hookPath }));
644
- }
645
- if (result.prepareAction === "left") {
646
- writeStderr(t("cli.hooks.install.prepare-left", { path: result.packageJsonPath }));
647
- } else {
648
- writeStderr(t("cli.hooks.install.prepare-added", { path: result.packageJsonPath }));
649
- }
295
+ hook: existsSync3(join3(workspaceRoot, ".codex", "hooks.json")),
296
+ // v2/rc.2: v1 client-side init skill removed; skill-installation probes
297
+ // will return once rc.2/3/4 introduce the v2 skills (fabric-archive,
298
+ // fabric-review, fabric-import). Until then there is nothing to probe.
299
+ skill: false
650
300
  }
651
- })
652
- }
653
- });
654
- async function installHooks(target, options = {}) {
655
- const normalizedTarget = normalizeTarget2(target);
656
- assertExistingDirectory(normalizedTarget);
657
- const huskyDir = join6(normalizedTarget, ".husky");
658
- const hookPath = join6(huskyDir, "pre-commit");
659
- const packageJsonPath = join6(normalizedTarget, "package.json");
660
- if (!existsSync6(packageJsonPath)) {
661
- throw new Error(t("cli.hooks.errors.package-json-required", { path: packageJsonPath }));
662
- }
663
- mkdirSync2(huskyDir, { recursive: true });
664
- const templateContent = readFileSync2(findTemplatePath2("templates/husky/pre-commit"), "utf8");
665
- const hookAction = await installHookFile(hookPath, templateContent, options.force);
666
- chmodSync(hookPath, 493);
667
- const packageJson = JSON.parse(readFileSync2(packageJsonPath, "utf8"));
668
- const scripts = packageJson.scripts && typeof packageJson.scripts === "object" && !Array.isArray(packageJson.scripts) ? packageJson.scripts : {};
669
- const hadPrepare = typeof scripts.prepare === "string" && scripts.prepare.trim().length > 0;
670
- let prepareAction = "left";
671
- if (!hadPrepare) {
672
- scripts.prepare = "husky install";
673
- packageJson.scripts = scripts;
674
- await atomicWriteJson2(packageJsonPath, packageJson);
675
- prepareAction = "added";
676
- }
677
- const installed = [];
678
- const skipped = [];
679
- if (hookAction === "skipped") {
680
- skipped.push(hookPath);
681
- } else {
682
- installed.push(hookPath);
683
- }
684
- if (prepareAction === "left") {
685
- skipped.push(packageJsonPath);
686
- } else {
687
- installed.push(packageJsonPath);
688
- }
689
- return {
690
- installed,
691
- skipped,
692
- hookPath,
693
- packageJsonPath,
694
- hookAction,
695
- prepareAction
696
- };
697
- }
698
- function normalizeTarget2(targetInput) {
699
- return isAbsolute2(targetInput) ? targetInput : resolve6(process.cwd(), targetInput);
700
- }
701
- function assertExistingDirectory(target) {
702
- if (!existsSync6(target) || !statSync(target).isDirectory()) {
703
- throw new Error(t("cli.shared.target-invalid", { target }));
704
- }
705
- }
706
- async function installHookFile(hookPath, templateContent, force) {
707
- if (existsSync6(hookPath)) {
708
- if (force) {
709
- await atomicWriteText3(hookPath, templateContent);
710
- return "overwritten";
711
301
  }
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
302
  ];
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
303
  }
755
304
 
756
305
  // src/commands/config.ts
757
- var CLIENT_ALIASES2 = {
306
+ var CLIENT_ALIASES = {
758
307
  claude: "ClaudeCodeCLI",
759
308
  claudecodecli: "ClaudeCodeCLI",
760
309
  "claude-code-cli": "ClaudeCodeCLI",
@@ -767,14 +316,14 @@ var CLIENT_ALIASES2 = {
767
316
  "codex-cli": "CodexCLI",
768
317
  codex: "CodexCLI"
769
318
  };
770
- function parseClientFilter2(value) {
319
+ function parseClientFilter(value) {
771
320
  if (value === void 0 || value.trim().length === 0) {
772
321
  return null;
773
322
  }
774
323
  const clients = /* @__PURE__ */ new Set();
775
324
  for (const rawClient of value.split(",")) {
776
325
  const alias = rawClient.trim().toLowerCase();
777
- const clientKind = CLIENT_ALIASES2[alias];
326
+ const clientKind = CLIENT_ALIASES[alias];
778
327
  if (clientKind === void 0) {
779
328
  throw new Error(t("cli.config.errors.unknown-client", { client: rawClient }));
780
329
  }
@@ -783,11 +332,11 @@ function parseClientFilter2(value) {
783
332
  return clients;
784
333
  }
785
334
  async function loadFabricConfig(workspaceRoot) {
786
- const configPath = resolve7(workspaceRoot, "fabric.config.json");
787
- if (!existsSync7(configPath)) {
335
+ const configPath = resolve3(workspaceRoot, "fabric.config.json");
336
+ if (!existsSync4(configPath)) {
788
337
  return {};
789
338
  }
790
- const parsed = JSON.parse(await readFile3(configPath, "utf8"));
339
+ const parsed = JSON.parse(await readFile2(configPath, "utf8"));
791
340
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
792
341
  throw new Error(t("cli.config.errors.expected-object", { path: configPath }));
793
342
  }
@@ -795,21 +344,21 @@ async function loadFabricConfig(workspaceRoot) {
795
344
  }
796
345
  function resolveServerPath(override) {
797
346
  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"));
347
+ if (process.env.FAB_SERVER_PATH) return resolve3(process.env.FAB_SERVER_PATH);
348
+ return fileURLToPath(import.meta.resolve("@fenglimg/fabric-server"));
800
349
  }
801
- function writeStderr2(message) {
350
+ function writeStderr(message) {
802
351
  process.stderr.write(`${message}
803
352
  `);
804
353
  }
805
- var configCmd = defineCommand3({
354
+ var configCmd = defineCommand({
806
355
  meta: {
807
356
  name: "config",
808
357
  description: t("cli.config.description")
809
358
  },
810
359
  subCommands: {
811
360
  hooks: hooksCommand,
812
- install: defineCommand3({
361
+ install: defineCommand({
813
362
  meta: {
814
363
  name: "install",
815
364
  description: t("cli.config.install.description")
@@ -826,26 +375,26 @@ var configCmd = defineCommand3({
826
375
  }
827
376
  },
828
377
  async run({ args }) {
829
- const selectedClients = parseClientFilter2(args.clients);
378
+ const selectedClients = parseClientFilter(args.clients);
830
379
  const result = await installMcpClients(process.cwd(), {
831
380
  clients: selectedClients === null ? void 0 : Array.from(selectedClients),
832
381
  dryRun: args["dry-run"]
833
382
  });
834
383
  if (result.details.length === 0) {
835
- writeStderr2(t("cli.config.install.no-configs"));
384
+ writeStderr(t("cli.config.install.no-configs"));
836
385
  return;
837
386
  }
838
387
  for (const detail of result.details) {
839
388
  if (detail.action === "skipped") {
840
- writeStderr2(t("cli.config.install.no-config-path", { client: detail.client }));
389
+ writeStderr(t("cli.config.install.no-config-path", { client: detail.client }));
841
390
  continue;
842
391
  }
843
392
  if (detail.action === "dry-run" && detail.path !== null) {
844
- writeStderr2(t("cli.config.install.dry-run", { client: detail.client, path: detail.path }));
393
+ writeStderr(t("cli.config.install.dry-run", { client: detail.client, path: detail.path }));
845
394
  continue;
846
395
  }
847
396
  if (detail.path !== null) {
848
- writeStderr2(t("cli.config.install.wrote", { client: detail.client, path: detail.path }));
397
+ writeStderr(t("cli.config.install.wrote", { client: detail.client, path: detail.path }));
849
398
  }
850
399
  }
851
400
  }
@@ -853,7 +402,7 @@ var configCmd = defineCommand3({
853
402
  }
854
403
  });
855
404
  async function installMcpClients(target, options = {}) {
856
- const workspaceRoot = resolve7(target);
405
+ const workspaceRoot = resolve3(target);
857
406
  const fabricConfig = await loadFabricConfig(workspaceRoot);
858
407
  const selectedClients = options.clients === void 0 ? null : new Set(options.clients);
859
408
  const serverPath = resolveServerPath(options.localServerPath);
@@ -884,9 +433,9 @@ async function installMcpClients(target, options = {}) {
884
433
 
885
434
  // src/scanner/forensic.ts
886
435
  import { execFileSync } from "child_process";
887
- import { existsSync as existsSync8, readdirSync, readFileSync as readFileSync3, statSync as statSync2 } from "fs";
436
+ import { existsSync as existsSync5, readdirSync, readFileSync, statSync } from "fs";
888
437
  import { createRequire } from "module";
889
- import { basename, extname, isAbsolute as isAbsolute3, join as join7, posix, relative, resolve as resolve8, sep } from "path";
438
+ import { basename, extname, isAbsolute, join as join4, posix, relative, resolve as resolve4, sep } from "path";
890
439
  import {
891
440
  forensicReportSchema
892
441
  } from "@fenglimg/fabric-shared";
@@ -972,7 +521,7 @@ var parserInitPromise = null;
972
521
  var languagePromiseByKind = {};
973
522
  var parserBundlePromiseByKind = {};
974
523
  async function buildForensicReport(targetInput) {
975
- const target = normalizeTarget3(targetInput);
524
+ const target = normalizeTarget(targetInput);
976
525
  const framework = detectFramework(target);
977
526
  const topology = buildTopology(target);
978
527
  const entryPoints = collectEntryPoints(target, topology.files);
@@ -1008,11 +557,11 @@ async function buildForensicReport(targetInput) {
1008
557
  }
1009
558
  return validation.data;
1010
559
  }
1011
- function normalizeTarget3(targetInput) {
1012
- return isAbsolute3(targetInput) ? targetInput : resolve8(process.cwd(), targetInput);
560
+ function normalizeTarget(targetInput) {
561
+ return isAbsolute(targetInput) ? targetInput : resolve4(process.cwd(), targetInput);
1013
562
  }
1014
563
  function buildTopology(root) {
1015
- assertExistingDirectory2(root);
564
+ assertExistingDirectory(root);
1016
565
  const byExt = {};
1017
566
  const keyDirs = /* @__PURE__ */ new Set();
1018
567
  const files = [];
@@ -1025,7 +574,7 @@ function buildTopology(root) {
1025
574
  continue;
1026
575
  }
1027
576
  for (const entry of readdirSync(current, { withFileTypes: true })) {
1028
- const absolutePath = join7(current, entry.name);
577
+ const absolutePath = join4(current, entry.name);
1029
578
  const relativePath = toPosixPath(relative(root, absolutePath));
1030
579
  if (relativePath.length === 0) {
1031
580
  continue;
@@ -1045,7 +594,7 @@ function buildTopology(root) {
1045
594
  if (!entry.isFile()) {
1046
595
  continue;
1047
596
  }
1048
- const stats = statSync2(absolutePath);
597
+ const stats = statSync(absolutePath);
1049
598
  const extension = extname(entry.name) || "[none]";
1050
599
  byExt[extension] = (byExt[extension] ?? 0) + 1;
1051
600
  totalFiles += 1;
@@ -1063,8 +612,8 @@ function buildTopology(root) {
1063
612
  files: files.sort((left, right) => left.relativePath.localeCompare(right.relativePath))
1064
613
  };
1065
614
  }
1066
- function assertExistingDirectory2(target) {
1067
- if (!existsSync8(target) || !statSync2(target).isDirectory()) {
615
+ function assertExistingDirectory(target) {
616
+ if (!existsSync5(target) || !statSync(target).isDirectory()) {
1068
617
  throw new Error(`Target must be an existing directory: ${target}`);
1069
618
  }
1070
619
  }
@@ -1113,7 +662,7 @@ function getEntryPointReason(relativePath) {
1113
662
  async function buildCodeSamples(target, entryPoints, frameworkKind, topology, packageDependencies) {
1114
663
  const samples = [];
1115
664
  for (const entryPoint of entryPoints.slice(0, SAMPLE_LIMIT)) {
1116
- const absolutePath = join7(target, ...entryPoint.path.split("/"));
665
+ const absolutePath = join4(target, ...entryPoint.path.split("/"));
1117
666
  const sample = readFirstLines(absolutePath, SAMPLE_LINE_LIMIT);
1118
667
  const patternAnalysis = await inferPatternHint(entryPoint.path, sample.snippet, {
1119
668
  frameworkKind,
@@ -1133,7 +682,7 @@ async function buildCodeSamples(target, entryPoints, frameworkKind, topology, pa
1133
682
  }
1134
683
  function readFirstLines(path, lineLimit) {
1135
684
  try {
1136
- const lines = readFileSync3(path, "utf8").split(/\r?\n/);
685
+ const lines = readFileSync(path, "utf8").split(/\r?\n/);
1137
686
  if (lines.at(-1) === "") {
1138
687
  lines.pop();
1139
688
  }
@@ -1150,12 +699,12 @@ function readFirstLines(path, lineLimit) {
1150
699
  }
1151
700
  }
1152
701
  function readPackageDependencies(target) {
1153
- const packageJsonPath = join7(target, "package.json");
1154
- if (!existsSync8(packageJsonPath)) {
702
+ const packageJsonPath = join4(target, "package.json");
703
+ if (!existsSync5(packageJsonPath)) {
1155
704
  return /* @__PURE__ */ new Map();
1156
705
  }
1157
706
  try {
1158
- const packageJson = JSON.parse(readFileSync3(packageJsonPath, "utf8"));
707
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
1159
708
  return new Map([
1160
709
  ...Object.entries(packageJson.dependencies ?? {}),
1161
710
  ...Object.entries(packageJson.devDependencies ?? {}),
@@ -1491,16 +1040,16 @@ function scoreFrameworkConfidence(input) {
1491
1040
  return input.configCount > 0 || input.packageCount > 0 ? "MEDIUM" : "LOW";
1492
1041
  }
1493
1042
  function readReadmeInfo(target) {
1494
- const readmePath = join7(target, "README.md");
1495
- const hasContributing = existsSync8(join7(target, "CONTRIBUTING.md"));
1496
- if (!existsSync8(readmePath)) {
1043
+ const readmePath = join4(target, "README.md");
1044
+ const hasContributing = existsSync5(join4(target, "CONTRIBUTING.md"));
1045
+ if (!existsSync5(readmePath)) {
1497
1046
  return {
1498
1047
  quality: "missing",
1499
1048
  line_count: 0,
1500
1049
  has_contributing: hasContributing
1501
1050
  };
1502
1051
  }
1503
- const readme = readFileSync3(readmePath, "utf8");
1052
+ const readme = readFileSync(readmePath, "utf8");
1504
1053
  const wordCount = readme.trim().split(/\s+/).filter(Boolean).length;
1505
1054
  return {
1506
1055
  quality: wordCount >= 200 ? "ok" : "stub",
@@ -1733,7 +1282,7 @@ function buildDomainAssertion(codeSamples) {
1733
1282
  namedModules.length >= 2 ? "domain-named-components" : null,
1734
1283
  namedSamples.some((sample) => sample.snippet.includes("start():")) ? "lifecycle-hook" : null
1735
1284
  ]),
1736
- proposedRule: "Preserve domain-specific module names when mirroring structure into .fabric/rules/."
1285
+ proposedRule: "Preserve domain-specific module names when authoring knowledge entries that reference these modules."
1737
1286
  });
1738
1287
  }
1739
1288
  function createAssertion(input) {
@@ -1978,10 +1527,10 @@ function buildSkillRecommendations(frameworkKind, topology, readme) {
1978
1527
  return recommendations;
1979
1528
  }
1980
1529
  function readProjectName(target) {
1981
- const packageJsonPath = join7(target, "package.json");
1982
- if (existsSync8(packageJsonPath)) {
1530
+ const packageJsonPath = join4(target, "package.json");
1531
+ if (existsSync5(packageJsonPath)) {
1983
1532
  try {
1984
- const packageJson = JSON.parse(readFileSync3(packageJsonPath, "utf8"));
1533
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
1985
1534
  if (packageJson.name !== void 0 && packageJson.name.trim().length > 0) {
1986
1535
  return packageJson.name;
1987
1536
  }
@@ -1992,7 +1541,7 @@ function readProjectName(target) {
1992
1541
  return basename(target);
1993
1542
  }
1994
1543
  function getCliVersion() {
1995
- return true ? "1.8.0-rc.3" : "unknown";
1544
+ return true ? "2.0.0" : "unknown";
1996
1545
  }
1997
1546
  function sortRecord(record) {
1998
1547
  return Object.fromEntries(Object.entries(record).sort(([left], [right]) => left.localeCompare(right)));
@@ -2002,18 +1551,19 @@ function toPosixPath(path) {
2002
1551
  }
2003
1552
 
2004
1553
  // 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");
1554
+ var AGENTS_MD_DEFAULT_CONTENT = `# Project Knowledge
1555
+
1556
+ This project uses [Fabric](https://github.com/fenglimg/fabric) for cross-client AI knowledge management.
1557
+
1558
+ Knowledge entries live in \`.fabric/knowledge/\` (team) and \`~/.fabric/knowledge/\` (personal).
1559
+ Run \`fabric doctor\` to verify state.
1560
+
1561
+ See \`.fabric/knowledge/\` for project decisions, pitfalls, guidelines, models, and processes.
1562
+ `;
1563
+ var LOCAL_FABRIC_SERVER_PATH = join5("node_modules", "@fenglimg", "fabric-server", "dist", "index.js");
2014
1564
  var FABRIC_SERVER_PACKAGE = "@fenglimg/fabric-server";
2015
1565
  var INIT_WIZARD_GROUP_CANCELLED = /* @__PURE__ */ Symbol("init-wizard-group-cancelled");
2016
- var initCommand = defineCommand4({
1566
+ var initCommand = defineCommand2({
2017
1567
  meta: {
2018
1568
  name: "init",
2019
1569
  description: t("cli.init.description")
@@ -2095,13 +1645,13 @@ async function runInitCommand(args) {
2095
1645
  logger(step);
2096
1646
  }
2097
1647
  if (intent.options.planOnly) {
2098
- writeStderr3(t("cli.init.compat.plan"));
1648
+ writeStderr2(t("cli.init.compat.plan"));
2099
1649
  }
2100
1650
  if (args.interactive === false) {
2101
- writeStderr3(t("cli.init.compat.interactive"));
1651
+ writeStderr2(t("cli.init.compat.interactive"));
2102
1652
  }
2103
1653
  if (args.bootstrap === false || args.mcp === false || args.hooks === false) {
2104
- writeStderr3(t("cli.init.compat.legacy-stage-flags"));
1654
+ writeStderr2(t("cli.init.compat.legacy-stage-flags"));
2105
1655
  }
2106
1656
  const supports = detectClientSupports(intent.target);
2107
1657
  const basePlan = await buildInitExecutionPlan({
@@ -2117,10 +1667,44 @@ async function runInitCommand(args) {
2117
1667
  process.exitCode = 130;
2118
1668
  return;
2119
1669
  }
2120
- return executeInitExecutionPlan(plan);
1670
+ const result = await executeInitExecutionPlan(plan);
1671
+ try {
1672
+ await maybeWriteImportSentinel({
1673
+ target: intent.target,
1674
+ planOnly: intent.options.planOnly === true,
1675
+ wizardEnabled: intent.wizardEnabled,
1676
+ terminalInteractive: intent.interactiveSummary
1677
+ });
1678
+ } catch {
1679
+ }
1680
+ if (!intent.options.planOnly) {
1681
+ console.log(paint.muted("More: docs/surfaces.md explains when to use CLI vs Skill vs MCP."));
1682
+ }
1683
+ return result;
1684
+ }
1685
+ async function maybeWriteImportSentinel(opts) {
1686
+ if (opts.planOnly) return;
1687
+ if (process.env.FABRIC_NONINTERACTIVE === "1") return;
1688
+ if (!opts.wizardEnabled || !opts.terminalInteractive) return;
1689
+ if (!Boolean(process.stdin.isTTY)) return;
1690
+ const answer = await confirm({
1691
+ message: "\u4E0B\u6B21\u5F00 AI \u65F6\u8BA9\u6211\u4ECE git log \u62BD\u66F4\u591A\u77E5\u8BC6\u5417?",
1692
+ initialValue: true
1693
+ });
1694
+ if (isCancel(answer) || answer !== true) return;
1695
+ const fabricDir = join5(opts.target, ".fabric");
1696
+ const sentinelPath = join5(fabricDir, ".import-requested");
1697
+ try {
1698
+ if (!existsSync6(fabricDir)) {
1699
+ mkdirSync(fabricDir, { recursive: true });
1700
+ }
1701
+ writeFileSync(sentinelPath, "", "utf8");
1702
+ log.success("\u4E0B\u6B21\u5F00 AI \u4F1A\u770B\u5230\u63D0\u793A");
1703
+ } catch {
1704
+ }
2121
1705
  }
2122
1706
  function resolveInitCliIntent(args, targetInput) {
2123
- const target = normalizeTarget4(targetInput);
1707
+ const target = normalizeTarget2(targetInput);
2124
1708
  const mcpInstallMode = resolveMcpInstallMode(args["mcp-install"]);
2125
1709
  const claudeMcpScope = resolveClaudeMcpScope(args.scope);
2126
1710
  const terminalInteractive = isInteractiveInit();
@@ -2150,7 +1734,7 @@ function resolveClaudeMcpScope(raw) {
2150
1734
  if (raw === "user") {
2151
1735
  return "user";
2152
1736
  }
2153
- writeStderr3(t("cli.init.mcp.scope.invalid", { value: raw }));
1737
+ writeStderr2(t("cli.init.mcp.scope.invalid", { value: raw }));
2154
1738
  return "project";
2155
1739
  }
2156
1740
  async function buildInitExecutionPlan(input) {
@@ -2190,10 +1774,10 @@ async function buildInitExecutionPlan(input) {
2190
1774
  }
2191
1775
  async function executeInitExecutionPlan(plan) {
2192
1776
  if (plan.options.force) {
2193
- writeStderr3(t("cli.init.force.warning", { path: plan.target }));
1777
+ writeStderr2(t("cli.init.force.warning", { path: plan.target }));
2194
1778
  }
2195
1779
  if (plan.options.reapply && !plan.options.planOnly && !plan.interactive) {
2196
- writeStderr3(formatInitModeBanner(plan.options));
1780
+ writeStderr2(formatInitModeBanner(plan.options));
2197
1781
  }
2198
1782
  if (plan.interactive) {
2199
1783
  printInitPlanSummary(plan.target, plan.options, plan.mcpInstallMode, plan.supports);
@@ -2238,98 +1822,77 @@ async function executeInitExecutionPlan(plan) {
2238
1822
  finalSupports
2239
1823
  };
2240
1824
  }
1825
+ var KNOWLEDGE_SUBDIRS = ["decisions", "pitfalls", "guidelines", "models", "processes", "pending"];
1826
+ function resolvePersonalFabricRoot() {
1827
+ return process.env.FABRIC_HOME ?? homedir4();
1828
+ }
2241
1829
  async function buildInitFabricPlan(target, options) {
2242
- assertExistingDirectory3(target);
2243
- const fabricDir = join8(target, ".fabric");
2244
- const bootstrapPath = join8(fabricDir, "bootstrap", "README.md");
2245
- const forensicPath = join8(fabricDir, "forensic.json");
2246
- const taxonomyPath = join8(fabricDir, "INITIAL_TAXONOMY.md");
2247
- const rulesDir = join8(fabricDir, "rules");
2248
- const eventsPath = join8(fabricDir, "events.jsonl");
2249
- const claudeSkillPath = join8(target, ".claude", "skills", "fabric-init", "SKILL.md");
2250
- const codexSkillPath = join8(target, ".codex", "skills", "fabric-init", "SKILL.md");
2251
- const codexSessionStartHookPath = join8(target, ".codex", "hooks", "fabric-session-start.cjs");
2252
- const codexStopHookPath = join8(target, ".codex", "hooks", "fabric-stop-reminder.cjs");
2253
- const codexHooksConfigPath = join8(target, ".codex", "hooks.json");
2254
- const claudeHookPath = join8(target, ".claude", "hooks", "fabric-init-reminder.cjs");
2255
- const claudeSettingsPath = join8(target, ".claude", "settings.json");
2256
- const metaPath = join8(fabricDir, "agents.meta.json");
1830
+ assertExistingDirectory2(target);
1831
+ const fabricDir = join5(target, ".fabric");
1832
+ const agentsMdPath = join5(target, "AGENTS.md");
1833
+ const agentsMdAction = existsSync6(agentsMdPath) ? "preserved" : "created";
1834
+ const knowledgeDir = join5(fabricDir, "knowledge");
1835
+ const personalKnowledgeDir = join5(resolvePersonalFabricRoot(), ".fabric", "knowledge");
1836
+ const forensicPath = join5(fabricDir, "forensic.json");
1837
+ const eventsPath = join5(fabricDir, "events.jsonl");
1838
+ const metaPath = join5(fabricDir, "agents.meta.json");
2257
1839
  const replaceFabricDir = shouldReplaceWritableDirectory(fabricDir, options);
2258
- const bootstrapAction = planFreshPath(bootstrapPath, options);
1840
+ const knowledgeDirAction = existsSync6(knowledgeDir) ? "overwritten" : "created";
2259
1841
  const metaAction = planFreshPath(metaPath, options);
2260
- const taxonomyAction = planFreshPath(taxonomyPath, options);
2261
1842
  const eventsAction = planFreshPath(eventsPath, options);
2262
1843
  const forensicAction = planFreshPath(forensicPath, options);
2263
1844
  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);
1845
+ const meta = createInitialMeta();
2268
1846
  return {
2269
1847
  target,
2270
1848
  options,
2271
1849
  fabricDir,
2272
1850
  replaceFabricDir,
2273
- bootstrapPath,
2274
- bootstrapAction,
2275
- bootstrapContent,
1851
+ agentsMdPath,
1852
+ agentsMdAction,
1853
+ knowledgeDir,
1854
+ knowledgeDirAction,
1855
+ personalKnowledgeDir,
2276
1856
  metaPath,
2277
1857
  metaAction,
2278
1858
  meta,
2279
- taxonomyPath,
2280
- taxonomyAction,
2281
- taxonomyContent,
2282
- rulesDir,
2283
1859
  eventsPath,
2284
1860
  eventsAction,
2285
1861
  forensicPath,
2286
1862
  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)
1863
+ forensicReport
2310
1864
  };
2311
1865
  }
2312
1866
  async function executeInitFabricPlan(plan) {
2313
1867
  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
1868
  if (plan.replaceFabricDir) {
2317
1869
  rmSync(plan.fabricDir, { force: true });
2318
1870
  }
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 });
1871
+ mkdirSync(plan.fabricDir, { recursive: true });
1872
+ if (plan.agentsMdAction === "created" && !existsSync6(plan.agentsMdPath)) {
1873
+ await atomicWriteText2(plan.agentsMdPath, AGENTS_MD_DEFAULT_CONTENT);
1874
+ }
1875
+ mkdirSync(plan.knowledgeDir, { recursive: true });
1876
+ for (const sub of KNOWLEDGE_SUBDIRS) {
1877
+ const teamSubDir = join5(plan.knowledgeDir, sub);
1878
+ mkdirSync(teamSubDir, { recursive: true });
1879
+ const teamGitkeep = join5(teamSubDir, ".gitkeep");
1880
+ if (!existsSync6(teamGitkeep)) {
1881
+ writeFileSync(teamGitkeep, "", "utf8");
1882
+ }
1883
+ }
1884
+ try {
1885
+ mkdirSync(plan.personalKnowledgeDir, { recursive: true });
1886
+ for (const sub of KNOWLEDGE_SUBDIRS) {
1887
+ mkdirSync(join5(plan.personalKnowledgeDir, sub), { recursive: true });
1888
+ }
1889
+ } catch {
1890
+ }
1891
+ preparePlannedPath(plan.metaPath, plan.metaAction);
1892
+ await atomicWriteJson(plan.metaPath, plan.meta);
2330
1893
  if (isReapply) {
2331
- if (!existsSync9(plan.eventsPath)) {
2332
- mkdirSync3(dirname5(plan.eventsPath), { recursive: true });
1894
+ if (!existsSync6(plan.eventsPath)) {
1895
+ mkdirSync(dirname2(plan.eventsPath), { recursive: true });
2333
1896
  writeFileSync(plan.eventsPath, "", "utf8");
2334
1897
  }
2335
1898
  } else {
@@ -2337,46 +1900,33 @@ async function executeInitFabricPlan(plan) {
2337
1900
  writeFileSync(plan.eventsPath, "", "utf8");
2338
1901
  }
2339
1902
  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);
1903
+ await atomicWriteJson(plan.forensicPath, plan.forensicReport);
1904
+ if (!plan.options?.reapply) {
1905
+ try {
1906
+ await runInitScan(plan.target, { source: "init" });
1907
+ } catch (error) {
1908
+ writeStderr2(
1909
+ `[warn] init-scan failed: ${error instanceof Error ? error.message : String(error)} \u2014 re-run \`fab scan\` to populate baseline knowledge entries.`
1910
+ );
1911
+ }
1912
+ }
2348
1913
  if (isReapply) {
2349
1914
  appendReapplyLedgerEvent(plan.eventsPath, {
2350
- preserved_ledger: true,
2351
- preserved_meta: preserveMeta,
2352
- rules_count: existingRules.length
1915
+ preserved_ledger: true
2353
1916
  });
2354
1917
  }
2355
1918
  return {
2356
- bootstrapPath: plan.bootstrapPath,
2357
- bootstrapAction: plan.bootstrapAction,
1919
+ agentsMdPath: plan.agentsMdPath,
1920
+ agentsMdAction: plan.agentsMdAction,
1921
+ knowledgeDir: plan.knowledgeDir,
1922
+ knowledgeDirAction: plan.knowledgeDirAction,
1923
+ personalKnowledgeDir: plan.personalKnowledgeDir,
2358
1924
  metaPath: plan.metaPath,
2359
1925
  metaAction: plan.metaAction,
2360
- taxonomyPath: plan.taxonomyPath,
2361
- taxonomyAction: plan.taxonomyAction,
2362
1926
  eventsPath: plan.eventsPath,
2363
1927
  eventsAction: plan.eventsAction,
2364
1928
  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
1929
+ forensicAction: plan.forensicAction
2380
1930
  };
2381
1931
  }
2382
1932
  async function initFabric(target, options) {
@@ -2421,22 +1971,11 @@ function exhaustiveInitStagePlan(value) {
2421
1971
  throw new Error(`Unsupported init stage plan: ${JSON.stringify(value)}`);
2422
1972
  }
2423
1973
  function printInitScaffoldResult(created) {
2424
- console.log(formatInitPathAction(created.bootstrapPath, created.bootstrapAction));
1974
+ console.log(formatAgentsMdAction(created.agentsMdPath, created.agentsMdAction));
1975
+ console.log(formatInitPathAction(created.knowledgeDir, created.knowledgeDirAction));
2425
1976
  console.log(formatInitPathAction(created.metaPath, created.metaAction));
2426
- console.log(formatInitPathAction(created.taxonomyPath, created.taxonomyAction));
2427
1977
  console.log(formatInitPathAction(created.eventsPath, created.eventsAction));
2428
1978
  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
1979
  }
2441
1980
  function printInitPostSetup(plan, stageResults, finalSupports) {
2442
1981
  if (shouldPrintHooksNextStep(plan.options, stageResults)) {
@@ -2470,30 +2009,17 @@ function printInitPlanPreview(plan) {
2470
2009
  }
2471
2010
  function buildPlanOnlyScaffoldResult(plan) {
2472
2011
  return {
2473
- bootstrapPath: plan.bootstrapPath,
2474
- bootstrapAction: plan.bootstrapAction,
2012
+ agentsMdPath: plan.agentsMdPath,
2013
+ agentsMdAction: plan.agentsMdAction,
2014
+ knowledgeDir: plan.knowledgeDir,
2015
+ knowledgeDirAction: plan.knowledgeDirAction,
2016
+ personalKnowledgeDir: plan.personalKnowledgeDir,
2475
2017
  metaPath: plan.metaPath,
2476
2018
  metaAction: plan.metaAction,
2477
- taxonomyPath: plan.taxonomyPath,
2478
- taxonomyAction: plan.taxonomyAction,
2479
2019
  eventsPath: plan.eventsPath,
2480
2020
  eventsAction: plan.eventsAction,
2481
2021
  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
2022
+ forensicAction: plan.forensicAction
2497
2023
  };
2498
2024
  }
2499
2025
  async function executeInitStagePlan(plan, stageName) {
@@ -2508,25 +2034,38 @@ async function executeInitStagePlan(plan, stageName) {
2508
2034
  try {
2509
2035
  switch (stage.name) {
2510
2036
  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" };
2037
+ const installResults = [];
2038
+ installResults.push(...await runBestEffort("skill-install", () => installFabricArchiveSkill(plan.target)));
2039
+ installResults.push(...await runBestEffort("skill-review-install", () => installFabricReviewSkill(plan.target)));
2040
+ installResults.push(...await runBestEffort("skill-import-install", () => installFabricImportSkill(plan.target)));
2041
+ installResults.push(...await runBestEffort("hook-script", () => installArchiveHintHook(plan.target)));
2042
+ installResults.push(...await runBestEffort("hook-broad-script", () => installKnowledgeHintBroadHook(plan.target)));
2043
+ installResults.push(...await runBestEffort("hook-narrow-script", () => installKnowledgeHintNarrowHook(plan.target)));
2044
+ installResults.push(await runBestEffortSingle("claude-hook-config", () => mergeClaudeCodeHookConfig(plan.target)));
2045
+ installResults.push(await runBestEffortSingle("codex-hook-config", () => mergeCodexHookConfig(plan.target)));
2046
+ installResults.push(await runBestEffortSingle("cursor-hook-config", () => mergeCursorHookConfig(plan.target)));
2047
+ installResults.push(...await runBestEffort("pointer", () => addArchiveSkillPointer(plan.target)));
2048
+ const installedCount = installResults.filter((r) => r.status === "written").length;
2049
+ const skippedCount = installResults.filter((r) => r.status === "skipped").length;
2050
+ const errorCount = installResults.filter((r) => r.status === "error").length;
2051
+ for (const result of installResults) {
2052
+ if (result.status === "error") {
2053
+ writeStderr2(`bootstrap ${result.step} ${result.path}: ${result.message ?? "unknown error"}`);
2054
+ }
2515
2055
  }
2516
- console.log(
2517
- formatInitStageResult("bootstrap", "completed", result.installed.length, result.skipped.length)
2518
- );
2056
+ const note2 = errorCount > 0 ? `errors=${errorCount}` : void 0;
2057
+ console.log(formatInitStageResult("bootstrap", "completed", installedCount, skippedCount, note2));
2519
2058
  return { name: "bootstrap", disposition: "ran" };
2520
2059
  }
2521
2060
  case "mcp": {
2522
2061
  if (stage.installMode === "local") {
2523
2062
  const manager = stage.packageManager ?? detectPackageManager(plan.target);
2524
- writeStderr3(t("cli.init.mcp.install.local"));
2525
- writeStderr3(t("cli.init.mcp.local.installing", { manager }));
2063
+ writeStderr2(t("cli.init.mcp.install.local"));
2064
+ writeStderr2(t("cli.init.mcp.local.installing", { manager }));
2526
2065
  installLocalFabricServer(plan.target, manager);
2527
- writeStderr3(t("cli.init.mcp.local.installed"));
2066
+ writeStderr2(t("cli.init.mcp.local.installed"));
2528
2067
  } else {
2529
- writeStderr3(t("cli.init.mcp.install.global"));
2068
+ writeStderr2(t("cli.init.mcp.install.global"));
2530
2069
  }
2531
2070
  const result = await installMcpClients(plan.target, {
2532
2071
  force: plan.options.force,
@@ -2549,15 +2088,15 @@ async function executeInitStagePlan(plan, stageName) {
2549
2088
  return exhaustiveInitStagePlan(stage);
2550
2089
  }
2551
2090
  } catch (error) {
2552
- writeStderr3(formatInitStageFailure(stageName, error));
2091
+ writeStderr2(formatInitStageFailure(stageName, error));
2553
2092
  return { name: stageName, disposition: "failed" };
2554
2093
  }
2555
2094
  }
2556
2095
  function shouldReplaceWritableDirectory(path, options) {
2557
- if (!existsSync9(path)) {
2096
+ if (!existsSync6(path)) {
2558
2097
  return false;
2559
2098
  }
2560
- if (statSync3(path).isDirectory()) {
2099
+ if (statSync2(path).isDirectory()) {
2561
2100
  return false;
2562
2101
  }
2563
2102
  if (!options?.force) {
@@ -2566,7 +2105,7 @@ function shouldReplaceWritableDirectory(path, options) {
2566
2105
  return true;
2567
2106
  }
2568
2107
  function planFreshPath(path, options) {
2569
- if (!existsSync9(path)) {
2108
+ if (!existsSync6(path)) {
2570
2109
  return "created";
2571
2110
  }
2572
2111
  if (!options?.force) {
@@ -2575,131 +2114,11 @@ function planFreshPath(path, options) {
2575
2114
  return "overwritten";
2576
2115
  }
2577
2116
  function preparePlannedPath(path, action) {
2578
- mkdirSync3(dirname5(path), { recursive: true });
2579
- if (action === "overwritten" && existsSync9(path)) {
2117
+ mkdirSync(dirname2(path), { recursive: true });
2118
+ if (action === "overwritten" && existsSync6(path)) {
2580
2119
  rmSync(path, { recursive: true, force: true });
2581
2120
  }
2582
2121
  }
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
2122
  function createDefaultInitWizardAdapter() {
2704
2123
  return {
2705
2124
  async run(context) {
@@ -2871,23 +2290,23 @@ function formatInitModeBadge(options) {
2871
2290
  }
2872
2291
  return t("cli.init.mode.badge.default");
2873
2292
  }
2874
- function normalizeTarget4(targetInput) {
2875
- return isAbsolute4(targetInput) ? targetInput : resolve9(process.cwd(), targetInput);
2293
+ function normalizeTarget2(targetInput) {
2294
+ return isAbsolute2(targetInput) ? targetInput : resolve5(process.cwd(), targetInput);
2876
2295
  }
2877
- function assertExistingDirectory3(target) {
2878
- if (!existsSync9(target) || !statSync3(target).isDirectory()) {
2296
+ function assertExistingDirectory2(target) {
2297
+ if (!existsSync6(target) || !statSync2(target).isDirectory()) {
2879
2298
  throw new Error(`Target must be an existing directory: ${target}`);
2880
2299
  }
2881
2300
  }
2882
2301
  function detectPackageManager(cwd) {
2883
- const workspaceRoot = resolve9(cwd);
2884
- if (existsSync9(join8(workspaceRoot, "pnpm-lock.yaml"))) {
2302
+ const workspaceRoot = resolve5(cwd);
2303
+ if (existsSync6(join5(workspaceRoot, "pnpm-lock.yaml"))) {
2885
2304
  return "pnpm";
2886
2305
  }
2887
- if (existsSync9(join8(workspaceRoot, "yarn.lock"))) {
2306
+ if (existsSync6(join5(workspaceRoot, "yarn.lock"))) {
2888
2307
  return "yarn";
2889
2308
  }
2890
- if (existsSync9(join8(workspaceRoot, "package-lock.json"))) {
2309
+ if (existsSync6(join5(workspaceRoot, "package-lock.json"))) {
2891
2310
  return "npm";
2892
2311
  }
2893
2312
  return "npm";
@@ -2896,7 +2315,7 @@ function resolveMcpInstallMode(rawMode) {
2896
2315
  if (rawMode === void 0 || rawMode === "global" || rawMode === "local") {
2897
2316
  return rawMode ?? "global";
2898
2317
  }
2899
- writeStderr3(t("cli.init.mcp.install.invalid", { value: rawMode }));
2318
+ writeStderr2(t("cli.init.mcp.install.invalid", { value: rawMode }));
2900
2319
  return "global";
2901
2320
  }
2902
2321
  function installLocalFabricServer(target, manager) {
@@ -2907,20 +2326,11 @@ function installLocalFabricServer(target, manager) {
2907
2326
  shell: process.platform === "win32"
2908
2327
  });
2909
2328
  }
2910
- function createInitialMeta(agentsHash) {
2329
+ function createInitialMeta() {
2911
2330
  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
- }
2331
+ revision: "sha256:initial",
2332
+ nodes: {},
2333
+ counters: defaultAgentsMetaCounters()
2924
2334
  };
2925
2335
  }
2926
2336
  function appendReapplyLedgerEvent(eventsPath, payload) {
@@ -2930,125 +2340,36 @@ function appendReapplyLedgerEvent(eventsPath, payload) {
2930
2340
  ts: Date.now(),
2931
2341
  schema_version: 1,
2932
2342
  event_type: "reapply_completed",
2933
- preserved_ledger: payload.preserved_ledger,
2934
- preserved_meta: payload.preserved_meta,
2935
- rules_count: payload.rules_count
2343
+ preserved_ledger: payload.preserved_ledger
2936
2344
  };
2937
2345
  const line = `${JSON.stringify(event)}
2938
2346
  `;
2939
2347
  appendFileSync(eventsPath, line, "utf8");
2940
2348
  }
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;
2349
+ async function runBestEffort(step, fn) {
2350
+ try {
2351
+ return await fn();
2352
+ } catch (error) {
2353
+ return [
2354
+ {
2355
+ step,
2356
+ path: "",
2357
+ status: "error",
2358
+ message: error instanceof Error ? error.message : String(error)
2359
+ }
2360
+ ];
3030
2361
  }
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
2362
  }
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 });
2363
+ async function runBestEffortSingle(step, fn) {
2364
+ try {
2365
+ return await fn();
2366
+ } catch (error) {
2367
+ return {
2368
+ step,
2369
+ path: "",
2370
+ status: "error",
2371
+ message: error instanceof Error ? error.message : String(error)
2372
+ };
3052
2373
  }
3053
2374
  }
3054
2375
  function formatInitStageHeader(message) {
@@ -3101,9 +2422,8 @@ function printInitPlanSummary(target, options, mcpInstallMode, supports) {
3101
2422
  })
3102
2423
  );
3103
2424
  console.log(t("cli.init.plan.writes"));
3104
- console.log(` - ${target}/.fabric/bootstrap/README.md`);
2425
+ console.log(` - ${target}/.fabric/knowledge/{decisions,pitfalls,guidelines,models,processes,pending}/`);
3105
2426
  console.log(` - ${target}/.fabric/agents.meta.json`);
3106
- console.log(` - ${target}/.fabric/INITIAL_TAXONOMY.md`);
3107
2427
  console.log(` - ${target}/.fabric/events.jsonl`);
3108
2428
  console.log(` - ${target}/.fabric/forensic.json`);
3109
2429
  }
@@ -3137,18 +2457,6 @@ function printInitCapabilitySummary(supports, stageResults, options) {
3137
2457
  console.log(formatCapabilityTableRow(row, widths));
3138
2458
  }
3139
2459
  }
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
2460
  function toCapabilityRow(support, stageResults, options) {
3153
2461
  const stage = (name) => stageResults.find((entry) => entry.name === name)?.disposition ?? null;
3154
2462
  const bootstrap = support.capabilities.bootstrap ? capabilityStatus(options.skipBootstrap ? "skipped" : stage("bootstrap")) : t("cli.init.capabilities.status.na");
@@ -3209,18 +2517,6 @@ function formatCapabilityDivider(widths) {
3209
2517
  }
3210
2518
  function formatInitReasonMessage(supports) {
3211
2519
  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
2520
  if (detected.some((support) => support.capabilities.skill)) {
3225
2521
  return t("cli.init.reason-message.installable-body");
3226
2522
  }
@@ -3232,11 +2528,11 @@ function yesNoLabel(value) {
3232
2528
  function formatInitPathAction(path, action) {
3233
2529
  return t("cli.init.created-path", { label: labelForInitWriteAction(action), path });
3234
2530
  }
3235
- function formatOptionalInitPathAction(path, action) {
3236
- if (action === "skipped") {
2531
+ function formatAgentsMdAction(path, action) {
2532
+ if (action === "preserved") {
3237
2533
  return t("cli.init.skipped-existing-path", { label: skippedLabel(), path });
3238
2534
  }
3239
- return formatInitPathAction(path, action);
2535
+ return t("cli.init.created-path", { label: createdLabel(), path });
3240
2536
  }
3241
2537
  function labelForInitWriteAction(action) {
3242
2538
  return action === "overwritten" ? overwrittenLabel() : createdLabel();
@@ -3253,9 +2549,6 @@ function nextLabel() {
3253
2549
  function reasonLabel() {
3254
2550
  return paint.human(t("cli.shared.reason"));
3255
2551
  }
3256
- function updatedLabel() {
3257
- return paint.success(t("cli.shared.updated"));
3258
- }
3259
2552
  function overwrittenLabel() {
3260
2553
  return paint.warn(t("cli.init.force.overwritten"));
3261
2554
  }
@@ -3268,13 +2561,10 @@ function skippedStageLabel() {
3268
2561
  function failedStageLabel() {
3269
2562
  return paint.error(t("cli.init.stages.failed"));
3270
2563
  }
3271
- function writeStderr3(message) {
2564
+ function writeStderr2(message) {
3272
2565
  process.stderr.write(`${message}
3273
2566
  `);
3274
2567
  }
3275
- function sha256(content) {
3276
- return `sha256:${createHash("sha256").update(content).digest("hex")}`;
3277
- }
3278
2568
  export {
3279
2569
  buildInitExecutionPlan,
3280
2570
  buildInitFabricPlan,