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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,17 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- hooksCommand,
4
- installHooks
5
- } from "./chunk-Q72D24BG.js";
3
+ installMcpClients
4
+ } from "./chunk-AIB54QRT.js";
6
5
  import {
7
6
  detectExistingLanguage,
8
- detectFramework,
9
7
  runInitScan
10
- } from "./chunk-FDRLV5PL.js";
11
- import {
12
- detectClientSupports,
13
- resolveClients
14
- } from "./chunk-OHWQNSLH.js";
8
+ } from "./chunk-AXIFEVAS.js";
15
9
  import {
16
10
  addFabricKnowledgeBaseSection,
17
11
  installArchiveHintHook,
@@ -24,174 +18,178 @@ import {
24
18
  mergeCodexHookConfig,
25
19
  mergeCursorHookConfig,
26
20
  readFabricLanguagePreference
27
- } from "./chunk-X7QPY5KH.js";
21
+ } from "./chunk-UTF4YBDN.js";
22
+ import {
23
+ detectClientSupports
24
+ } from "./chunk-SKSYUHKK.js";
28
25
  import {
29
26
  displayWidth,
27
+ hasActionHint,
30
28
  padEnd,
31
- paint
32
- } from "./chunk-WWNXR34K.js";
29
+ paint,
30
+ renderFabricError
31
+ } from "./chunk-G2CIOLD4.js";
32
+ import {
33
+ t
34
+ } from "./chunk-6ICJICVU.js";
33
35
  import {
34
36
  createDebugLogger,
35
37
  resolveDevMode
36
38
  } from "./chunk-OBQU6NHO.js";
37
- import {
38
- t
39
- } from "./chunk-6ICJICVU.js";
40
39
 
41
40
  // src/commands/install.ts
42
41
  import { randomUUID } from "crypto";
43
42
  import { homedir } from "os";
44
43
  import * as childProcess from "child_process";
45
- import { appendFileSync, existsSync as existsSync3, mkdirSync, rmSync, statSync as statSync2, writeFileSync } from "fs";
46
- import { dirname, isAbsolute as isAbsolute2, join as join2, resolve as resolve3 } from "path";
44
+ import { appendFileSync, existsSync as existsSync3, mkdirSync, readFileSync as readFileSync2, rmSync, statSync as statSync3, writeFileSync } from "fs";
45
+ import { dirname, isAbsolute as isAbsolute3, join as join3, resolve as resolve3 } from "path";
47
46
  import { cancel, confirm, group, intro, isCancel, log, note, outro, select } from "@clack/prompts";
48
47
  import { defaultAgentsMetaCounters } from "@fenglimg/fabric-shared";
49
48
  import { atomicWriteJson, atomicWriteText } from "@fenglimg/fabric-shared/node/atomic-write";
50
- import { defineCommand as defineCommand2 } from "citty";
49
+ import { defineCommand } from "citty";
51
50
  import { checkLockOrThrow } from "@fenglimg/fabric-server";
52
51
 
53
- // src/commands/config.ts
54
- import { existsSync } from "fs";
55
- import { readFile } from "fs/promises";
56
- import { resolve } from "path";
57
- import { fileURLToPath } from "url";
58
- import { defineCommand } from "citty";
59
- var CLIENT_ALIASES = {
60
- claude: "ClaudeCodeCLI",
61
- claudecodecli: "ClaudeCodeCLI",
62
- "claude-code-cli": "ClaudeCodeCLI",
63
- claudecli: "ClaudeCodeCLI",
64
- claudecodedesktop: "ClaudeCodeDesktop",
65
- "claude-code-desktop": "ClaudeCodeDesktop",
66
- claudedesktop: "ClaudeCodeDesktop",
67
- cursor: "Cursor",
68
- codexcli: "CodexCLI",
69
- "codex-cli": "CodexCLI",
70
- codex: "CodexCLI"
71
- };
72
- function parseClientFilter(value) {
73
- if (value === void 0 || value.trim().length === 0) {
74
- return null;
75
- }
76
- const clients = /* @__PURE__ */ new Set();
77
- for (const rawClient of value.split(",")) {
78
- const alias = rawClient.trim().toLowerCase();
79
- const clientKind = CLIENT_ALIASES[alias];
80
- if (clientKind === void 0) {
81
- throw new Error(t("cli.config.errors.unknown-client", { client: rawClient }));
52
+ // src/install/hooks-orchestrator.ts
53
+ import { existsSync, statSync } from "fs";
54
+ import { isAbsolute, join, resolve } from "path";
55
+ async function installHooks(target, _options = {}) {
56
+ const normalizedTarget = normalizeTarget(target);
57
+ assertExistingDirectory(normalizedTarget);
58
+ const results = [];
59
+ results.push(...await runStep(() => installFabricArchiveSkill(normalizedTarget)));
60
+ results.push(...await runStep(() => installFabricReviewSkill(normalizedTarget)));
61
+ results.push(...await runStep(() => installFabricImportSkill(normalizedTarget)));
62
+ results.push(...await runStep(() => installArchiveHintHook(normalizedTarget)));
63
+ results.push(...await runStep(() => installKnowledgeHintBroadHook(normalizedTarget)));
64
+ results.push(...await runStep(() => installKnowledgeHintNarrowHook(normalizedTarget)));
65
+ results.push(await runSingleStep("claude-hook-config", () => mergeClaudeCodeHookConfig(normalizedTarget)));
66
+ results.push(await runSingleStep("codex-hook-config", () => mergeCodexHookConfig(normalizedTarget)));
67
+ results.push(await runSingleStep("cursor-hook-config", () => mergeCursorHookConfig(normalizedTarget)));
68
+ const fabricLanguage = readFabricLanguagePreference(normalizedTarget);
69
+ results.push(...await runStep(() => addFabricKnowledgeBaseSection(normalizedTarget, fabricLanguage)));
70
+ results.push(...validateHookPaths(normalizedTarget));
71
+ return summarizeResults(results);
72
+ }
73
+ function validateHookPaths(projectRoot) {
74
+ const scripts = [
75
+ { stepSuffix: "", hookFile: "fabric-hint.cjs" },
76
+ { stepSuffix: "-broad", hookFile: "knowledge-hint-broad.cjs" },
77
+ { stepSuffix: "-narrow", hookFile: "knowledge-hint-narrow.cjs" }
78
+ ];
79
+ const clients = [
80
+ {
81
+ client: "claude",
82
+ configRel: join(".claude", "settings.json"),
83
+ hookDir: join(".claude", "hooks")
84
+ },
85
+ {
86
+ client: "codex",
87
+ configRel: join(".codex", "hooks.json"),
88
+ hookDir: join(".codex", "hooks")
89
+ },
90
+ {
91
+ client: "cursor",
92
+ configRel: join(".cursor", "hooks.json"),
93
+ hookDir: join(".cursor", "hooks")
94
+ }
95
+ ];
96
+ const results = [];
97
+ for (const { client, configRel, hookDir } of clients) {
98
+ const configPath = resolve(projectRoot, configRel);
99
+ if (!existsSync(configPath)) {
100
+ results.push({
101
+ step: `hook-validate-${client}`,
102
+ path: configPath,
103
+ status: "skipped",
104
+ message: "missing-config"
105
+ });
106
+ continue;
107
+ }
108
+ for (const { stepSuffix, hookFile } of scripts) {
109
+ const expectedHookPath = resolve(projectRoot, hookDir, hookFile);
110
+ const expectedHookRel = join(hookDir, hookFile);
111
+ const step = `hook-validate-${client}${stepSuffix}`;
112
+ if (!existsSync(expectedHookPath)) {
113
+ results.push({
114
+ step,
115
+ path: expectedHookPath,
116
+ status: "error",
117
+ message: `hook script missing: ${expectedHookRel}`
118
+ });
119
+ continue;
120
+ }
121
+ results.push({ step, path: expectedHookPath, status: "skipped", message: "ok" });
82
122
  }
83
- clients.add(clientKind);
84
123
  }
85
- return clients;
124
+ return results;
86
125
  }
87
- async function loadFabricConfig(workspaceRoot) {
88
- const configPath = resolve(workspaceRoot, "fabric.config.json");
89
- if (!existsSync(configPath)) {
90
- return {};
91
- }
92
- const parsed = JSON.parse(await readFile(configPath, "utf8"));
93
- if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
94
- throw new Error(t("cli.config.errors.expected-object", { path: configPath }));
126
+ async function runStep(fn) {
127
+ try {
128
+ return await fn();
129
+ } catch (error) {
130
+ return [
131
+ {
132
+ step: "hook-install",
133
+ path: "",
134
+ status: "error",
135
+ message: error instanceof Error ? error.message : String(error)
136
+ }
137
+ ];
95
138
  }
96
- return parsed;
97
- }
98
- function resolveServerPath(override) {
99
- if (override) return override;
100
- if (process.env.FAB_SERVER_PATH) return resolve(process.env.FAB_SERVER_PATH);
101
- return fileURLToPath(import.meta.resolve("@fenglimg/fabric-server"));
102
- }
103
- function writeStderr(message) {
104
- process.stderr.write(`${message}
105
- `);
106
139
  }
107
- var configCmd = defineCommand({
108
- meta: {
109
- name: "config",
110
- description: t("cli.config.description")
111
- },
112
- subCommands: {
113
- hooks: hooksCommand,
114
- install: defineCommand({
115
- meta: {
116
- name: "install",
117
- description: t("cli.config.install.description")
118
- },
119
- args: {
120
- clients: {
121
- type: "string",
122
- description: t("cli.config.install.args.clients.description")
123
- },
124
- "dry-run": {
125
- type: "boolean",
126
- description: t("cli.config.install.args.dry-run.description"),
127
- default: false
128
- }
129
- },
130
- async run({ args }) {
131
- const selectedClients = parseClientFilter(args.clients);
132
- const result = await installMcpClients(process.cwd(), {
133
- clients: selectedClients === null ? void 0 : Array.from(selectedClients),
134
- dryRun: args["dry-run"]
135
- });
136
- if (result.details.length === 0) {
137
- writeStderr(t("cli.config.install.no-configs"));
138
- return;
139
- }
140
- for (const detail of result.details) {
141
- if (detail.action === "skipped") {
142
- writeStderr(t("cli.config.install.no-config-path", { client: detail.client }));
143
- continue;
144
- }
145
- if (detail.action === "dry-run" && detail.path !== null) {
146
- writeStderr(t("cli.config.install.dry-run", { client: detail.client, path: detail.path }));
147
- continue;
148
- }
149
- if (detail.path !== null) {
150
- writeStderr(t("cli.config.install.wrote", { client: detail.client, path: detail.path }));
151
- }
152
- }
153
- }
154
- })
140
+ async function runSingleStep(step, fn) {
141
+ try {
142
+ return await fn();
143
+ } catch (error) {
144
+ return {
145
+ step,
146
+ path: "",
147
+ status: "error",
148
+ message: error instanceof Error ? error.message : String(error)
149
+ };
155
150
  }
156
- });
157
- async function installMcpClients(target, options = {}) {
158
- const workspaceRoot = resolve(target);
159
- const fabricConfig = await loadFabricConfig(workspaceRoot);
160
- const selectedClients = options.clients === void 0 ? null : new Set(options.clients);
161
- const serverPath = resolveServerPath(options.localServerPath);
162
- const writers = resolveClients(workspaceRoot, fabricConfig, { claudeMcpScope: options.claudeMcpScope }).filter(
163
- (writer) => selectedClients === null ? true : selectedClients.has(writer.clientKind)
164
- );
151
+ }
152
+ function summarizeResults(results) {
165
153
  const installed = [];
166
154
  const skipped = [];
167
- const details = [];
168
- for (const writer of writers) {
169
- const configPath = await writer.detect(workspaceRoot);
170
- if (configPath === null) {
171
- skipped.push(writer.clientKind);
172
- details.push({ client: writer.clientKind, path: null, action: "skipped" });
173
- continue;
174
- }
175
- if (options.dryRun) {
176
- skipped.push(writer.clientKind);
177
- details.push({ client: writer.clientKind, path: configPath, action: "dry-run" });
178
- continue;
155
+ const errors = [];
156
+ for (const r of results) {
157
+ switch (r.status) {
158
+ case "written":
159
+ installed.push(r.path);
160
+ break;
161
+ case "skipped":
162
+ skipped.push(r.path);
163
+ break;
164
+ case "error":
165
+ errors.push(`${r.step} ${r.path}: ${r.message ?? "unknown error"}`);
166
+ break;
179
167
  }
180
- await writer.write(serverPath, workspaceRoot);
181
- installed.push(writer.clientKind);
182
- details.push({ client: writer.clientKind, path: configPath, action: "wrote" });
183
168
  }
184
- return { installed, skipped, details };
169
+ return { installed, skipped, errors };
170
+ }
171
+ function normalizeTarget(targetInput) {
172
+ return isAbsolute(targetInput) ? targetInput : resolve(process.cwd(), targetInput);
173
+ }
174
+ function assertExistingDirectory(target) {
175
+ if (!existsSync(target) || !statSync(target).isDirectory()) {
176
+ throw new Error(t("cli.shared.target-invalid", { target }));
177
+ }
185
178
  }
186
179
 
187
180
  // src/scanner/forensic.ts
188
181
  import { execFileSync } from "child_process";
189
- import { existsSync as existsSync2, readdirSync, readFileSync, statSync } from "fs";
182
+ import { existsSync as existsSync2, readdirSync, readFileSync, statSync as statSync2 } from "fs";
190
183
  import { createRequire } from "module";
191
- import { basename, extname, isAbsolute, join, posix, relative, resolve as resolve2, sep } from "path";
184
+ import { basename, extname, isAbsolute as isAbsolute2, join as join2, posix, relative, resolve as resolve2, sep } from "path";
192
185
  import {
193
186
  forensicReportSchema
194
187
  } from "@fenglimg/fabric-shared";
188
+
189
+ // src/scanner/detector.ts
190
+ import { detectFramework } from "@fenglimg/fabric-shared/node";
191
+
192
+ // src/scanner/forensic.ts
195
193
  var require2 = createRequire(import.meta.url);
196
194
  var IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([
197
195
  ".fabric",
@@ -274,7 +272,7 @@ var parserInitPromise = null;
274
272
  var languagePromiseByKind = {};
275
273
  var parserBundlePromiseByKind = {};
276
274
  async function buildForensicReport(targetInput) {
277
- const target = normalizeTarget(targetInput);
275
+ const target = normalizeTarget2(targetInput);
278
276
  const framework = detectFramework(target);
279
277
  const topology = buildTopology(target);
280
278
  const entryPoints = collectEntryPoints(target, topology.files);
@@ -310,11 +308,11 @@ async function buildForensicReport(targetInput) {
310
308
  }
311
309
  return validation.data;
312
310
  }
313
- function normalizeTarget(targetInput) {
314
- return isAbsolute(targetInput) ? targetInput : resolve2(process.cwd(), targetInput);
311
+ function normalizeTarget2(targetInput) {
312
+ return isAbsolute2(targetInput) ? targetInput : resolve2(process.cwd(), targetInput);
315
313
  }
316
314
  function buildTopology(root) {
317
- assertExistingDirectory(root);
315
+ assertExistingDirectory2(root);
318
316
  const byExt = {};
319
317
  const keyDirs = /* @__PURE__ */ new Set();
320
318
  const files = [];
@@ -327,7 +325,7 @@ function buildTopology(root) {
327
325
  continue;
328
326
  }
329
327
  for (const entry of readdirSync(current, { withFileTypes: true })) {
330
- const absolutePath = join(current, entry.name);
328
+ const absolutePath = join2(current, entry.name);
331
329
  const relativePath = toPosixPath(relative(root, absolutePath));
332
330
  if (relativePath.length === 0) {
333
331
  continue;
@@ -347,7 +345,7 @@ function buildTopology(root) {
347
345
  if (!entry.isFile()) {
348
346
  continue;
349
347
  }
350
- const stats = statSync(absolutePath);
348
+ const stats = statSync2(absolutePath);
351
349
  const extension = extname(entry.name) || "[none]";
352
350
  byExt[extension] = (byExt[extension] ?? 0) + 1;
353
351
  totalFiles += 1;
@@ -365,8 +363,8 @@ function buildTopology(root) {
365
363
  files: files.sort((left, right) => left.relativePath.localeCompare(right.relativePath))
366
364
  };
367
365
  }
368
- function assertExistingDirectory(target) {
369
- if (!existsSync2(target) || !statSync(target).isDirectory()) {
366
+ function assertExistingDirectory2(target) {
367
+ if (!existsSync2(target) || !statSync2(target).isDirectory()) {
370
368
  throw new Error(`Target must be an existing directory: ${target}`);
371
369
  }
372
370
  }
@@ -415,7 +413,7 @@ function getEntryPointReason(relativePath) {
415
413
  async function buildCodeSamples(target, entryPoints, frameworkKind, topology, packageDependencies) {
416
414
  const samples = [];
417
415
  for (const entryPoint of entryPoints.slice(0, SAMPLE_LIMIT)) {
418
- const absolutePath = join(target, ...entryPoint.path.split("/"));
416
+ const absolutePath = join2(target, ...entryPoint.path.split("/"));
419
417
  const sample = readFirstLines(absolutePath, SAMPLE_LINE_LIMIT);
420
418
  const patternAnalysis = await inferPatternHint(entryPoint.path, sample.snippet, {
421
419
  frameworkKind,
@@ -452,7 +450,7 @@ function readFirstLines(path, lineLimit) {
452
450
  }
453
451
  }
454
452
  function readPackageDependencies(target) {
455
- const packageJsonPath = join(target, "package.json");
453
+ const packageJsonPath = join2(target, "package.json");
456
454
  if (!existsSync2(packageJsonPath)) {
457
455
  return /* @__PURE__ */ new Map();
458
456
  }
@@ -793,8 +791,8 @@ function scoreFrameworkConfidence(input) {
793
791
  return input.configCount > 0 || input.packageCount > 0 ? "MEDIUM" : "LOW";
794
792
  }
795
793
  function readReadmeInfo(target) {
796
- const readmePath = join(target, "README.md");
797
- const hasContributing = existsSync2(join(target, "CONTRIBUTING.md"));
794
+ const readmePath = join2(target, "README.md");
795
+ const hasContributing = existsSync2(join2(target, "CONTRIBUTING.md"));
798
796
  if (!existsSync2(readmePath)) {
799
797
  return {
800
798
  quality: "missing",
@@ -1280,7 +1278,7 @@ function buildSkillRecommendations(frameworkKind, topology, readme) {
1280
1278
  return recommendations;
1281
1279
  }
1282
1280
  function readProjectName(target) {
1283
- const packageJsonPath = join(target, "package.json");
1281
+ const packageJsonPath = join2(target, "package.json");
1284
1282
  if (existsSync2(packageJsonPath)) {
1285
1283
  try {
1286
1284
  const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
@@ -1294,7 +1292,7 @@ function readProjectName(target) {
1294
1292
  return basename(target);
1295
1293
  }
1296
1294
  function getCliVersion() {
1297
- return true ? "2.0.0-rc.13" : "unknown";
1295
+ return true ? "2.0.0-rc.15" : "unknown";
1298
1296
  }
1299
1297
  function sortRecord(record) {
1300
1298
  return Object.fromEntries(Object.entries(record).sort(([left], [right]) => left.localeCompare(right)));
@@ -1313,72 +1311,33 @@ Run \`fabric doctor\` to verify state.
1313
1311
 
1314
1312
  See \`.fabric/knowledge/\` for project decisions, pitfalls, guidelines, models, and processes.
1315
1313
  `;
1316
- var LOCAL_FABRIC_SERVER_PATH = join2("node_modules", "@fenglimg", "fabric-server", "dist", "index.js");
1314
+ var LOCAL_FABRIC_SERVER_PATH = join3("node_modules", "@fenglimg", "fabric-server", "dist", "index.js");
1317
1315
  var FABRIC_SERVER_PACKAGE = "@fenglimg/fabric-server";
1318
1316
  var INIT_WIZARD_GROUP_CANCELLED = /* @__PURE__ */ Symbol("init-wizard-group-cancelled");
1319
- var installCommand = defineCommand2({
1317
+ var installCommand = defineCommand({
1320
1318
  meta: {
1321
1319
  name: "install",
1322
1320
  description: t("cli.install.description")
1323
1321
  },
1324
1322
  args: {
1325
- target: {
1326
- type: "string",
1327
- description: t("cli.install.args.target.description")
1328
- },
1329
1323
  debug: {
1330
1324
  type: "boolean",
1331
1325
  description: t("cli.install.args.debug.description"),
1332
1326
  default: false
1333
1327
  },
1334
- force: {
1328
+ "dry-run": {
1335
1329
  type: "boolean",
1336
- description: t("cli.install.args.force.description"),
1330
+ description: t("cli.install.args.dry-run.description"),
1337
1331
  default: false
1338
1332
  },
1333
+ target: {
1334
+ type: "string",
1335
+ description: t("cli.install.args.target.description")
1336
+ },
1339
1337
  yes: {
1340
1338
  type: "boolean",
1341
1339
  description: t("cli.install.args.yes.description"),
1342
1340
  default: false
1343
- },
1344
- plan: {
1345
- type: "boolean",
1346
- description: t("cli.install.args.plan.description"),
1347
- default: false
1348
- },
1349
- reapply: {
1350
- type: "boolean",
1351
- description: t("cli.install.args.reapply.description"),
1352
- default: false
1353
- },
1354
- bootstrap: {
1355
- type: "boolean",
1356
- default: true,
1357
- negativeDescription: t("cli.install.args.no-bootstrap.description")
1358
- },
1359
- mcp: {
1360
- type: "boolean",
1361
- default: true,
1362
- negativeDescription: t("cli.install.args.no-mcp.description")
1363
- },
1364
- hooks: {
1365
- type: "boolean",
1366
- default: true,
1367
- negativeDescription: t("cli.install.args.no-hooks.description")
1368
- },
1369
- interactive: {
1370
- type: "boolean",
1371
- description: t("cli.install.args.interactive.description"),
1372
- default: true
1373
- },
1374
- "mcp-install": {
1375
- type: "string",
1376
- default: "global",
1377
- description: t("cli.install.mcp.install.prompt")
1378
- },
1379
- scope: {
1380
- type: "string",
1381
- description: t("cli.install.mcp.scope.description")
1382
1341
  }
1383
1342
  },
1384
1343
  async run({ args }) {
@@ -1390,22 +1349,22 @@ async function runInitCommand(args) {
1390
1349
  const logger = createDebugLogger(args.debug);
1391
1350
  const resolution = resolveDevMode(args.target, process.cwd());
1392
1351
  const intent = resolveInitCliIntent(args, resolution.target);
1393
- if (args.reapply === true) {
1394
- checkLockOrThrow(intent.target, { force: args.force });
1352
+ const fabricInitialized = existsSync3(join3(intent.target, ".fabric", "events.jsonl"));
1353
+ if (fabricInitialized) {
1354
+ try {
1355
+ checkLockOrThrow(intent.target);
1356
+ } catch (err) {
1357
+ if (hasActionHint(err)) {
1358
+ renderFabricError(err);
1359
+ process.exit(1);
1360
+ }
1361
+ throw err;
1362
+ }
1395
1363
  }
1396
1364
  logger(`init target source: ${resolution.source}`);
1397
1365
  for (const step of resolution.chain) {
1398
1366
  logger(step);
1399
1367
  }
1400
- if (intent.options.planOnly) {
1401
- writeStderr2(t("cli.install.compat.plan"));
1402
- }
1403
- if (args.interactive === false) {
1404
- writeStderr2(t("cli.install.compat.interactive"));
1405
- }
1406
- if (args.bootstrap === false || args.mcp === false || args.hooks === false) {
1407
- writeStderr2(t("cli.install.compat.legacy-stage-flags"));
1408
- }
1409
1368
  const supports = detectClientSupports(intent.target);
1410
1369
  const basePlan = await buildInitExecutionPlan({
1411
1370
  target: intent.target,
@@ -1415,7 +1374,7 @@ async function runInitCommand(args) {
1415
1374
  interactive: intent.interactiveSummary && !intent.wizardEnabled,
1416
1375
  supports
1417
1376
  });
1418
- const plan = intent.wizardEnabled ? await resolveInitExecutionPlanWithWizard(basePlan, args, createDefaultInitWizardAdapter()) : basePlan;
1377
+ const plan = intent.wizardEnabled ? await resolveInitExecutionPlanWithWizard(basePlan, createDefaultInitWizardAdapter()) : basePlan;
1419
1378
  if (plan === null) {
1420
1379
  process.exitCode = 130;
1421
1380
  return;
@@ -1427,7 +1386,7 @@ async function runInitCommand(args) {
1427
1386
  return result;
1428
1387
  }
1429
1388
  function writeDefaultFabricConfig(fabricDir, targetRoot) {
1430
- const target = join2(fabricDir, "fabric-config.json");
1389
+ const target = join3(fabricDir, "fabric-config.json");
1431
1390
  if (existsSync3(target)) return;
1432
1391
  const detectedLanguage = detectExistingLanguage(targetRoot);
1433
1392
  const FABRIC_CONFIG_DEFAULTS = {
@@ -1496,39 +1455,23 @@ function writeDefaultFabricConfig(fabricDir, targetRoot) {
1496
1455
  );
1497
1456
  }
1498
1457
  function resolveInitCliIntent(args, targetInput) {
1499
- const target = normalizeTarget2(targetInput);
1500
- const mcpInstallMode = resolveMcpInstallMode(args["mcp-install"]);
1501
- const claudeMcpScope = resolveClaudeMcpScope(args.scope);
1458
+ const target = normalizeTarget3(targetInput);
1459
+ const mcpInstallMode = "global";
1460
+ const claudeMcpScope = "project";
1502
1461
  const terminalInteractive = isInteractiveInit();
1503
- const planOnly = args.plan === true;
1504
- const reapply = args.reapply === true;
1462
+ const planOnly = args["dry-run"] === true;
1505
1463
  const options = {
1506
- force: reapply ? true : args.force,
1507
- skipBootstrap: args.bootstrap === false ? true : args.skipBootstrap,
1508
- skipMcp: args.mcp === false ? true : args.skipMcp,
1509
- skipHooks: args.hooks === false ? true : args.skipHooks,
1510
- planOnly,
1511
- reapply
1464
+ planOnly
1512
1465
  };
1513
1466
  return {
1514
1467
  target,
1515
1468
  options,
1516
1469
  mcpInstallMode,
1517
1470
  claudeMcpScope,
1518
- interactiveSummary: args.interactive !== false && terminalInteractive,
1471
+ interactiveSummary: terminalInteractive,
1519
1472
  wizardEnabled: shouldUseInitWizard(args, terminalInteractive) && !planOnly
1520
1473
  };
1521
1474
  }
1522
- function resolveClaudeMcpScope(raw) {
1523
- if (raw === void 0 || raw === "project") {
1524
- return "project";
1525
- }
1526
- if (raw === "user") {
1527
- return "user";
1528
- }
1529
- writeStderr2(t("cli.install.mcp.scope.invalid", { value: raw }));
1530
- return "project";
1531
- }
1532
1475
  async function buildInitExecutionPlan(input) {
1533
1476
  const options = input.options ?? {};
1534
1477
  const scaffold = await buildInitFabricPlan(input.target, options);
@@ -1565,17 +1508,17 @@ async function buildInitExecutionPlan(input) {
1565
1508
  };
1566
1509
  }
1567
1510
  async function executeInitExecutionPlan(plan) {
1568
- if (plan.options.force) {
1569
- writeStderr2(t("cli.install.force.warning", { path: plan.target }));
1570
- }
1571
- if (plan.options.reapply && !plan.options.planOnly && !plan.interactive) {
1572
- writeStderr2(formatInitModeBanner(plan.options));
1573
- }
1574
1511
  if (plan.interactive) {
1575
1512
  printInitPlanSummary(plan.target, plan.options, plan.mcpInstallMode, plan.supports);
1576
1513
  }
1514
+ const scaffoldStates = [
1515
+ { path: plan.scaffold.metaPath, state: plan.scaffold.metaState },
1516
+ { path: plan.scaffold.eventsPath, state: plan.scaffold.eventsState },
1517
+ { path: plan.scaffold.forensicPath, state: plan.scaffold.forensicState }
1518
+ ];
1577
1519
  if (plan.options.planOnly) {
1578
1520
  printInitPlanPreview(plan);
1521
+ printInitDiffStateTable(scaffoldStates);
1579
1522
  return {
1580
1523
  plan,
1581
1524
  created: buildPlanOnlyScaffoldResult(plan.scaffold),
@@ -1583,6 +1526,17 @@ async function executeInitExecutionPlan(plan) {
1583
1526
  finalSupports: plan.supports
1584
1527
  };
1585
1528
  }
1529
+ if (existsSync3(plan.scaffold.fabricDir) && !statSync3(plan.scaffold.fabricDir).isDirectory()) {
1530
+ throw new Error(
1531
+ t("cli.install.diff.drift-abort", { path: plan.scaffold.fabricDir })
1532
+ );
1533
+ }
1534
+ const drifted = scaffoldStates.find(
1535
+ (entry) => entry.state === "drifted" || entry.state === "user-modified"
1536
+ );
1537
+ if (drifted !== void 0) {
1538
+ throw new Error(t("cli.install.diff.drift-abort", { path: drifted.path }));
1539
+ }
1586
1540
  let created = null;
1587
1541
  const stageResults = [];
1588
1542
  let finalSupports = plan.supports;
@@ -1607,6 +1561,11 @@ async function executeInitExecutionPlan(plan) {
1607
1561
  exhaustiveInitExecutionStep(step);
1608
1562
  }
1609
1563
  }
1564
+ if (scaffoldStates.every((entry) => entry.state === "present-canonical")) {
1565
+ console.log(
1566
+ t("cli.install.diff.canonical", { count: String(scaffoldStates.length) })
1567
+ );
1568
+ }
1610
1569
  return {
1611
1570
  plan,
1612
1571
  created: created ?? unreachableInitScaffold(),
@@ -1619,20 +1578,23 @@ function resolvePersonalFabricRoot() {
1619
1578
  return process.env.FABRIC_HOME ?? homedir();
1620
1579
  }
1621
1580
  async function buildInitFabricPlan(target, options) {
1622
- assertExistingDirectory2(target);
1623
- const fabricDir = join2(target, ".fabric");
1624
- const agentsMdPath = join2(target, "AGENTS.md");
1581
+ assertExistingDirectory3(target);
1582
+ const fabricDir = join3(target, ".fabric");
1583
+ const agentsMdPath = join3(target, "AGENTS.md");
1625
1584
  const agentsMdAction = existsSync3(agentsMdPath) ? "preserved" : "created";
1626
- const knowledgeDir = join2(fabricDir, "knowledge");
1627
- const personalKnowledgeDir = join2(resolvePersonalFabricRoot(), ".fabric", "knowledge");
1628
- const forensicPath = join2(fabricDir, "forensic.json");
1629
- const eventsPath = join2(fabricDir, "events.jsonl");
1630
- const metaPath = join2(fabricDir, "agents.meta.json");
1585
+ const knowledgeDir = join3(fabricDir, "knowledge");
1586
+ const personalKnowledgeDir = join3(resolvePersonalFabricRoot(), ".fabric", "knowledge");
1587
+ const forensicPath = join3(fabricDir, "forensic.json");
1588
+ const eventsPath = join3(fabricDir, "events.jsonl");
1589
+ const metaPath = join3(fabricDir, "agents.meta.json");
1631
1590
  const replaceFabricDir = shouldReplaceWritableDirectory(fabricDir, options);
1632
1591
  const knowledgeDirAction = existsSync3(knowledgeDir) ? "overwritten" : "created";
1633
- const metaAction = planFreshPath(metaPath, options);
1634
- const eventsAction = planFreshPath(eventsPath, options);
1635
- const forensicAction = planFreshPath(forensicPath, options);
1592
+ const metaClassification = classifyFreshPath(metaPath, "structural");
1593
+ const eventsClassification = classifyFreshPath(eventsPath, "presence");
1594
+ const forensicClassification = classifyFreshPath(forensicPath, "always-rewrite");
1595
+ const metaAction = diffStateToWriteAction(metaClassification.state);
1596
+ const eventsAction = diffStateToWriteAction(eventsClassification.state);
1597
+ const forensicAction = diffStateToWriteAction(forensicClassification.state);
1636
1598
  const forensicReport = await buildForensicReport(target);
1637
1599
  const meta = createInitialMeta();
1638
1600
  return {
@@ -1652,11 +1614,13 @@ async function buildInitFabricPlan(target, options) {
1652
1614
  eventsAction,
1653
1615
  forensicPath,
1654
1616
  forensicAction,
1655
- forensicReport
1617
+ forensicReport,
1618
+ metaState: metaClassification.state,
1619
+ eventsState: eventsClassification.state,
1620
+ forensicState: forensicClassification.state
1656
1621
  };
1657
1622
  }
1658
1623
  async function executeInitFabricPlan(plan) {
1659
- const isReapply = plan.options?.reapply === true;
1660
1624
  if (plan.replaceFabricDir) {
1661
1625
  rmSync(plan.fabricDir, { force: true });
1662
1626
  }
@@ -1667,9 +1631,9 @@ async function executeInitFabricPlan(plan) {
1667
1631
  }
1668
1632
  mkdirSync(plan.knowledgeDir, { recursive: true });
1669
1633
  for (const sub of KNOWLEDGE_SUBDIRS) {
1670
- const teamSubDir = join2(plan.knowledgeDir, sub);
1634
+ const teamSubDir = join3(plan.knowledgeDir, sub);
1671
1635
  mkdirSync(teamSubDir, { recursive: true });
1672
- const teamGitkeep = join2(teamSubDir, ".gitkeep");
1636
+ const teamGitkeep = join3(teamSubDir, ".gitkeep");
1673
1637
  if (!existsSync3(teamGitkeep)) {
1674
1638
  writeFileSync(teamGitkeep, "", "utf8");
1675
1639
  }
@@ -1677,36 +1641,49 @@ async function executeInitFabricPlan(plan) {
1677
1641
  try {
1678
1642
  mkdirSync(plan.personalKnowledgeDir, { recursive: true });
1679
1643
  for (const sub of KNOWLEDGE_SUBDIRS) {
1680
- mkdirSync(join2(plan.personalKnowledgeDir, sub), { recursive: true });
1644
+ mkdirSync(join3(plan.personalKnowledgeDir, sub), { recursive: true });
1681
1645
  }
1682
1646
  } catch {
1683
1647
  }
1684
- preparePlannedPath(plan.metaPath, plan.metaAction);
1685
- await atomicWriteJson(plan.metaPath, plan.meta);
1686
- if (isReapply) {
1687
- if (!existsSync3(plan.eventsPath)) {
1688
- mkdirSync(dirname(plan.eventsPath), { recursive: true });
1689
- writeFileSync(plan.eventsPath, "", "utf8");
1690
- }
1691
- } else {
1648
+ if (plan.metaState === "missing") {
1649
+ preparePlannedPath(plan.metaPath, plan.metaAction);
1650
+ await atomicWriteJson(plan.metaPath, plan.meta);
1651
+ }
1652
+ if (plan.eventsState === "missing") {
1692
1653
  preparePlannedPath(plan.eventsPath, plan.eventsAction);
1654
+ mkdirSync(dirname(plan.eventsPath), { recursive: true });
1693
1655
  writeFileSync(plan.eventsPath, "", "utf8");
1694
1656
  }
1695
1657
  preparePlannedPath(plan.forensicPath, plan.forensicAction);
1696
1658
  await atomicWriteJson(plan.forensicPath, plan.forensicReport);
1697
- if (!plan.options?.reapply) {
1659
+ const wasCanonicalReRun = plan.metaState === "present-canonical" && plan.eventsState === "present-canonical";
1660
+ if (!wasCanonicalReRun) {
1698
1661
  try {
1699
1662
  await runInitScan(plan.target, { source: "init" });
1700
1663
  } catch (error) {
1701
- writeStderr2(
1664
+ writeStderr(
1702
1665
  `[warn] init-scan failed: ${error instanceof Error ? error.message : String(error)} \u2014 re-run \`fab scan\` to populate baseline knowledge entries.`
1703
1666
  );
1704
1667
  }
1705
1668
  }
1706
- if (isReapply) {
1707
- appendReapplyLedgerEvent(plan.eventsPath, {
1708
- preserved_ledger: true
1709
- });
1669
+ if (existsSync3(plan.eventsPath)) {
1670
+ const applied = [];
1671
+ const canonical = [];
1672
+ const drifted = [];
1673
+ for (const entry of [
1674
+ { path: plan.metaPath, state: plan.metaState },
1675
+ { path: plan.eventsPath, state: plan.eventsState },
1676
+ { path: plan.forensicPath, state: plan.forensicState }
1677
+ ]) {
1678
+ if (entry.state === "missing") {
1679
+ applied.push(entry.path);
1680
+ } else if (entry.state === "present-canonical") {
1681
+ canonical.push(entry.path);
1682
+ } else {
1683
+ drifted.push(entry.path);
1684
+ }
1685
+ }
1686
+ appendInstallDiffLedgerEvent(plan.eventsPath, { applied, canonical, drifted });
1710
1687
  }
1711
1688
  return {
1712
1689
  agentsMdPath: plan.agentsMdPath,
@@ -1726,16 +1703,16 @@ async function initFabric(target, options) {
1726
1703
  return await executeInitFabricPlan(await buildInitFabricPlan(target, options));
1727
1704
  }
1728
1705
  function shouldUseInitWizard(args, terminalInteractive = isInteractiveInit()) {
1729
- return terminalInteractive && args.interactive !== false && args.yes !== true;
1706
+ return terminalInteractive && args.yes !== true;
1730
1707
  }
1731
- async function resolveInitExecutionPlanWithWizard(basePlan, args, wizardAdapter) {
1708
+ async function resolveInitExecutionPlanWithWizard(basePlan, wizardAdapter) {
1732
1709
  const selection = await wizardAdapter.run({
1733
1710
  target: basePlan.target,
1734
1711
  options: basePlan.options,
1735
1712
  supports: basePlan.supports,
1736
1713
  mcpInstallMode: basePlan.mcpInstallMode,
1737
1714
  claudeMcpScope: basePlan.claudeMcpScope,
1738
- lockedStages: collectLockedWizardStages(args)
1715
+ lockedStages: []
1739
1716
  });
1740
1717
  if (selection === null) {
1741
1718
  return null;
@@ -1792,12 +1769,17 @@ function printInitPostSetup(plan, stageResults, finalSupports) {
1792
1769
  paint.muted(t("cli.install.language_preference_hint", { value: fabricLanguage }))
1793
1770
  );
1794
1771
  }
1772
+ function printInitDiffStateTable(entries) {
1773
+ for (const entry of entries) {
1774
+ console.log(` ${formatDiffFileState(entry.state)} ${entry.path}`);
1775
+ }
1776
+ }
1795
1777
  function printInitPlanPreview(plan) {
1796
1778
  console.log(t("cli.install.plan.preview-title"));
1797
1779
  printInitPlanSummary(plan.target, plan.options, plan.mcpInstallMode, plan.supports);
1798
1780
  console.log(
1799
1781
  t("cli.install.plan.preview-result", {
1800
- mode: plan.options.reapply ? t("cli.install.mode.reapply") : t("cli.install.mode.default"),
1782
+ mode: t("cli.install.mode.default"),
1801
1783
  bootstrap: yesNoLabel(!plan.options.skipBootstrap),
1802
1784
  mcp: yesNoLabel(!plan.options.skipMcp),
1803
1785
  hooks: yesNoLabel(!plan.options.skipHooks)
@@ -1848,7 +1830,7 @@ async function executeInitStagePlan(plan, stageName) {
1848
1830
  const errorCount = installResults.filter((r) => r.status === "error").length;
1849
1831
  for (const result of installResults) {
1850
1832
  if (result.status === "error") {
1851
- writeStderr2(`bootstrap ${result.step} ${result.path}: ${result.message ?? "unknown error"}`);
1833
+ writeStderr(`bootstrap ${result.step} ${result.path}: ${result.message ?? "unknown error"}`);
1852
1834
  }
1853
1835
  }
1854
1836
  const note2 = errorCount > 0 ? `errors=${errorCount}` : void 0;
@@ -1858,15 +1840,14 @@ async function executeInitStagePlan(plan, stageName) {
1858
1840
  case "mcp": {
1859
1841
  if (stage.installMode === "local") {
1860
1842
  const manager = stage.packageManager ?? detectPackageManager(plan.target);
1861
- writeStderr2(t("cli.install.mcp.install.local"));
1862
- writeStderr2(t("cli.install.mcp.local.installing", { manager }));
1843
+ writeStderr(t("cli.install.mcp.install.local"));
1844
+ writeStderr(t("cli.install.mcp.local.installing", { manager }));
1863
1845
  installLocalFabricServer(plan.target, manager);
1864
- writeStderr2(t("cli.install.mcp.local.installed"));
1846
+ writeStderr(t("cli.install.mcp.local.installed"));
1865
1847
  } else {
1866
- writeStderr2(t("cli.install.mcp.install.global"));
1848
+ writeStderr(t("cli.install.mcp.install.global"));
1867
1849
  }
1868
1850
  const result = await installMcpClients(plan.target, {
1869
- force: plan.options.force,
1870
1851
  localServerPath: stage.localServerPath,
1871
1852
  claudeMcpScope: stage.claudeMcpScope
1872
1853
  });
@@ -1878,7 +1859,7 @@ async function executeInitStagePlan(plan, stageName) {
1878
1859
  return { name: "mcp", disposition: "ran" };
1879
1860
  }
1880
1861
  case "hooks": {
1881
- const result = await installHooks(plan.target, { force: plan.options.force });
1862
+ const result = await installHooks(plan.target);
1882
1863
  console.log(formatInitStageResult("hooks", "completed", result.installed.length, result.skipped.length));
1883
1864
  return { name: "hooks", disposition: "ran" };
1884
1865
  }
@@ -1886,30 +1867,66 @@ async function executeInitStagePlan(plan, stageName) {
1886
1867
  return exhaustiveInitStagePlan(stage);
1887
1868
  }
1888
1869
  } catch (error) {
1889
- writeStderr2(formatInitStageFailure(stageName, error));
1870
+ writeStderr(formatInitStageFailure(stageName, error));
1890
1871
  return { name: stageName, disposition: "failed" };
1891
1872
  }
1892
1873
  }
1893
- function shouldReplaceWritableDirectory(path, options) {
1874
+ function shouldReplaceWritableDirectory(path, _options) {
1894
1875
  if (!existsSync3(path)) {
1895
1876
  return false;
1896
1877
  }
1897
- if (statSync2(path).isDirectory()) {
1878
+ if (statSync3(path).isDirectory()) {
1898
1879
  return false;
1899
1880
  }
1900
- if (!options?.force) {
1901
- throw new Error(t("cli.install.errors.abort-existing", { path }));
1902
- }
1903
- return true;
1881
+ return false;
1904
1882
  }
1905
- function planFreshPath(path, options) {
1883
+ function classifyFreshPath(path, strategy) {
1906
1884
  if (!existsSync3(path)) {
1907
- return "created";
1885
+ return { path, state: "missing" };
1886
+ }
1887
+ let stat;
1888
+ try {
1889
+ stat = statSync3(path);
1890
+ } catch (error) {
1891
+ return {
1892
+ path,
1893
+ state: "user-modified",
1894
+ reason: error instanceof Error ? error.message : String(error)
1895
+ };
1896
+ }
1897
+ if (!stat.isFile()) {
1898
+ return { path, state: "user-modified", reason: "expected a file" };
1899
+ }
1900
+ if (strategy === "presence" || strategy === "always-rewrite") {
1901
+ return { path, state: "present-canonical" };
1908
1902
  }
1909
- if (!options?.force) {
1910
- throw new Error(t("cli.install.errors.abort-existing", { path }));
1903
+ try {
1904
+ const raw = readFileSync2(path, "utf8");
1905
+ const parsed = JSON.parse(raw);
1906
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
1907
+ return { path, state: "user-modified", reason: "not a JSON object" };
1908
+ }
1909
+ const record = parsed;
1910
+ const hasRevision = typeof record["revision"] === "string";
1911
+ const hasNodes = record["nodes"] !== void 0 && record["nodes"] !== null && typeof record["nodes"] === "object" && !Array.isArray(record["nodes"]);
1912
+ const hasCounters = record["counters"] !== void 0 && record["counters"] !== null && typeof record["counters"] === "object" && !Array.isArray(record["counters"]);
1913
+ if (!hasRevision || !hasNodes || !hasCounters) {
1914
+ return { path, state: "drifted", reason: "missing required AgentsMeta fields" };
1915
+ }
1916
+ return { path, state: "present-canonical" };
1917
+ } catch (error) {
1918
+ return {
1919
+ path,
1920
+ state: "user-modified",
1921
+ reason: error instanceof Error ? error.message : String(error)
1922
+ };
1911
1923
  }
1912
- return "overwritten";
1924
+ }
1925
+ function diffStateToWriteAction(_state) {
1926
+ return "created";
1927
+ }
1928
+ function formatDiffFileState(state) {
1929
+ return t(`cli.install.diff.state.${state}`);
1913
1930
  }
1914
1931
  function preparePlannedPath(path, action) {
1915
1932
  mkdirSync(dirname(path), { recursive: true });
@@ -2048,74 +2065,42 @@ async function selectClaudeMcpScopeInGroup(options) {
2048
2065
  }
2049
2066
  return result;
2050
2067
  }
2051
- function collectLockedWizardStages(args) {
2052
- const lockedStages = [];
2053
- if (args.bootstrap === false) {
2054
- lockedStages.push("bootstrap");
2055
- }
2056
- if (args.mcp === false) {
2057
- lockedStages.push("mcp");
2058
- }
2059
- if (args.hooks === false) {
2060
- lockedStages.push("hooks");
2061
- }
2062
- return lockedStages;
2063
- }
2064
2068
  function formatPromptDefault(value) {
2065
2069
  return value ? "Y/n" : "y/N";
2066
2070
  }
2067
2071
  function formatInitModeBanner(options) {
2068
- if (options.planOnly && options.reapply) {
2069
- return t("cli.install.plan.mode-banner.plan-reapply");
2070
- }
2071
2072
  if (options.planOnly) {
2072
2073
  return t("cli.install.plan.mode-banner.plan");
2073
2074
  }
2074
- if (options.reapply) {
2075
- return t("cli.install.plan.mode-banner.reapply");
2076
- }
2077
2075
  return t("cli.install.plan.mode-banner.default");
2078
2076
  }
2079
2077
  function formatInitModeBadge(options) {
2080
- if (options.planOnly && options.reapply) {
2081
- return t("cli.install.mode.badge.plan-reapply");
2082
- }
2083
2078
  if (options.planOnly) {
2084
2079
  return t("cli.install.mode.badge.plan");
2085
2080
  }
2086
- if (options.reapply) {
2087
- return t("cli.install.mode.badge.reapply");
2088
- }
2089
2081
  return t("cli.install.mode.badge.default");
2090
2082
  }
2091
- function normalizeTarget2(targetInput) {
2092
- return isAbsolute2(targetInput) ? targetInput : resolve3(process.cwd(), targetInput);
2083
+ function normalizeTarget3(targetInput) {
2084
+ return isAbsolute3(targetInput) ? targetInput : resolve3(process.cwd(), targetInput);
2093
2085
  }
2094
- function assertExistingDirectory2(target) {
2095
- if (!existsSync3(target) || !statSync2(target).isDirectory()) {
2086
+ function assertExistingDirectory3(target) {
2087
+ if (!existsSync3(target) || !statSync3(target).isDirectory()) {
2096
2088
  throw new Error(`Target must be an existing directory: ${target}`);
2097
2089
  }
2098
2090
  }
2099
2091
  function detectPackageManager(cwd) {
2100
2092
  const workspaceRoot = resolve3(cwd);
2101
- if (existsSync3(join2(workspaceRoot, "pnpm-lock.yaml"))) {
2093
+ if (existsSync3(join3(workspaceRoot, "pnpm-lock.yaml"))) {
2102
2094
  return "pnpm";
2103
2095
  }
2104
- if (existsSync3(join2(workspaceRoot, "yarn.lock"))) {
2096
+ if (existsSync3(join3(workspaceRoot, "yarn.lock"))) {
2105
2097
  return "yarn";
2106
2098
  }
2107
- if (existsSync3(join2(workspaceRoot, "package-lock.json"))) {
2099
+ if (existsSync3(join3(workspaceRoot, "package-lock.json"))) {
2108
2100
  return "npm";
2109
2101
  }
2110
2102
  return "npm";
2111
2103
  }
2112
- function resolveMcpInstallMode(rawMode) {
2113
- if (rawMode === void 0 || rawMode === "global" || rawMode === "local") {
2114
- return rawMode ?? "global";
2115
- }
2116
- writeStderr2(t("cli.install.mcp.install.invalid", { value: rawMode }));
2117
- return "global";
2118
- }
2119
2104
  function installLocalFabricServer(target, manager) {
2120
2105
  const installArgs = manager === "npm" ? ["install", "-D", FABRIC_SERVER_PACKAGE] : ["add", "-D", FABRIC_SERVER_PACKAGE];
2121
2106
  childProcess.execFileSync(manager, installArgs, {
@@ -2131,14 +2116,16 @@ function createInitialMeta() {
2131
2116
  counters: defaultAgentsMetaCounters()
2132
2117
  };
2133
2118
  }
2134
- function appendReapplyLedgerEvent(eventsPath, payload) {
2119
+ function appendInstallDiffLedgerEvent(eventsPath, payload) {
2135
2120
  const event = {
2136
2121
  kind: "fabric-event",
2137
2122
  id: `event:${randomUUID()}`,
2138
2123
  ts: Date.now(),
2139
2124
  schema_version: 1,
2140
- event_type: "reapply_completed",
2141
- preserved_ledger: payload.preserved_ledger
2125
+ event_type: "install_diff_applied",
2126
+ applied: payload.applied,
2127
+ canonical: payload.canonical,
2128
+ drifted: payload.drifted
2142
2129
  };
2143
2130
  const line = `${JSON.stringify(event)}
2144
2131
  `;
@@ -2349,7 +2336,7 @@ function reasonLabel() {
2349
2336
  return paint.human(t("cli.shared.reason"));
2350
2337
  }
2351
2338
  function overwrittenLabel() {
2352
- return paint.warn(t("cli.install.force.overwritten"));
2339
+ return paint.warn(t("cli.install.label.overwritten"));
2353
2340
  }
2354
2341
  function completedStageLabel() {
2355
2342
  return paint.success(t("cli.install.stages.completed"));
@@ -2360,7 +2347,7 @@ function skippedStageLabel() {
2360
2347
  function failedStageLabel() {
2361
2348
  return paint.error(t("cli.install.stages.failed"));
2362
2349
  }
2363
- function writeStderr2(message) {
2350
+ function writeStderr(message) {
2364
2351
  process.stderr.write(`${message}
2365
2352
  `);
2366
2353
  }