@fenglimg/fabric-cli 1.2.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +24 -22
  2. package/dist/{bootstrap-IUL4SAAK.js → bootstrap-3PUKUYTY.js} +4 -2
  3. package/dist/{chunk-N4DCTOXW.js → chunk-AZRKMFRY.js} +6 -6
  4. package/dist/{chunk-F2BXHPM5.js → chunk-N7EZORJZ.js} +9 -1
  5. package/dist/{chunk-6UUPKSDE.js → chunk-Q4LOVXML.js} +13 -8
  6. package/dist/{chunk-RUQCZA2Q.js → chunk-TKTWHAKV.js} +92 -115
  7. package/dist/{chunk-VMYPJPKV.js → chunk-VOQKQ6W2.js} +153 -21
  8. package/dist/{chunk-MDI7523D.js → chunk-XFSQM3LJ.js} +5 -1
  9. package/dist/{config-3JBB77TX.js → config-GINBGANU.js} +3 -2
  10. package/dist/index.js +11 -22
  11. package/dist/{init-3FPLOABB.js → init-T3LGMGAO.js} +170 -54
  12. package/dist/{ledger-append-XZ5SX4O5.js → ledger-append-DULKJ6Q2.js} +1 -1
  13. package/dist/pre-commit-IK6SJOPT.js +97 -0
  14. package/dist/{scan-WKDSKEBB.js → scan-43R3IBLR.js} +2 -2
  15. package/dist/{sync-meta-THZSEM7Y.js → sync-meta-LKVSO6TS.js} +1 -1
  16. package/dist/update-AN3FYF2O.js +116 -0
  17. package/package.json +4 -3
  18. package/templates/agents-md/AGENTS.md.template +14 -13
  19. package/templates/agents-md/variants/cocos.md +20 -20
  20. package/templates/agents-md/variants/next.md +20 -20
  21. package/templates/agents-md/variants/vite.md +20 -20
  22. package/templates/bootstrap/CLAUDE.md +3 -5
  23. package/templates/bootstrap/GEMINI.md +3 -5
  24. package/templates/bootstrap/codex-AGENTS-header.md +3 -5
  25. package/templates/bootstrap/cursor-fabric-bootstrap.mdc +11 -11
  26. package/templates/bootstrap/roo-fabric.md +6 -6
  27. package/templates/bootstrap/windsurf-fabric.md +6 -6
  28. package/templates/claude-hooks/agents-md-init-reminder.cjs +18 -18
  29. package/templates/claude-skills/agents-md-init/SKILL.md +86 -86
  30. package/templates/fabric/human-lock.json +12 -12
  31. package/templates/husky/pre-commit +24 -24
  32. package/dist/pre-commit-CJ7EDKJK.js +0 -59
  33. package/dist/{doctor-5KJGOV2P.js → doctor-QTSG2RWF.js} +3 -3
  34. package/dist/{serve-MMN4GYLM.js → serve-4J2CQY25.js} +4 -4
package/dist/index.js CHANGED
@@ -8,28 +8,17 @@ import { defineCommand, runMain } from "citty";
8
8
 
9
9
  // src/commands/index.ts
10
10
  var allCommands = {
11
- init: () => import("./init-3FPLOABB.js").then((module) => module.default),
12
- scan: () => import("./scan-WKDSKEBB.js").then((module) => module.default),
13
- serve: () => import("./serve-MMN4GYLM.js").then((module) => module.default),
14
- doctor: () => import("./doctor-5KJGOV2P.js").then((module) => module.default),
15
- "sync-meta": () => import("./sync-meta-THZSEM7Y.js").then((module) => module.default),
11
+ init: () => import("./init-T3LGMGAO.js").then((module) => module.default),
12
+ update: () => import("./update-AN3FYF2O.js").then((module) => module.default),
13
+ scan: () => import("./scan-43R3IBLR.js").then((module) => module.default),
14
+ serve: () => import("./serve-4J2CQY25.js").then((module) => module.default),
15
+ doctor: () => import("./doctor-QTSG2RWF.js").then((module) => module.default),
16
+ "sync-meta": () => import("./sync-meta-LKVSO6TS.js").then((module) => module.default),
16
17
  "human-lint": () => import("./human-lint-YSFOZHZ7.js").then((module) => module.default),
17
- "ledger-append": () => import("./ledger-append-XZ5SX4O5.js").then((module) => module.default),
18
- "pre-commit": () => import("./pre-commit-CJ7EDKJK.js").then((module) => module.default),
19
- bootstrap: () => import("./bootstrap-IUL4SAAK.js").then((module) => ({
20
- ...module.default,
21
- meta: {
22
- ...module.default.meta,
23
- hidden: true
24
- }
25
- })),
26
- config: () => import("./config-3JBB77TX.js").then((module) => ({
27
- ...module.configCmd,
28
- meta: {
29
- ...module.configCmd.meta,
30
- hidden: true
31
- }
32
- })),
18
+ "ledger-append": () => import("./ledger-append-DULKJ6Q2.js").then((module) => module.default),
19
+ "pre-commit": () => import("./pre-commit-IK6SJOPT.js").then((module) => module.default),
20
+ bootstrap: () => import("./bootstrap-3PUKUYTY.js").then((module) => module.default),
21
+ config: () => import("./config-GINBGANU.js").then((module) => module.configCmd),
33
22
  hooks: () => import("./hooks-ZSWVH2JD.js").then((module) => ({
34
23
  ...module.default,
35
24
  meta: {
@@ -43,7 +32,7 @@ var allCommands = {
43
32
  var main = defineCommand({
44
33
  meta: {
45
34
  name: "fabric",
46
- version: "1.2.0",
35
+ version: "1.3.1",
47
36
  description: 'Initialize and manage Fabric projects. Use "fabric init" for one-shot setup.'
48
37
  },
49
38
  subCommands: allCommands
@@ -1,25 +1,29 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ buildFabricBootstrapGuide,
3
4
  installBootstrap
4
- } from "./chunk-RUQCZA2Q.js";
5
+ } from "./chunk-TKTWHAKV.js";
6
+ import {
7
+ detectFramework
8
+ } from "./chunk-AZRKMFRY.js";
5
9
  import {
6
10
  installMcpClients
7
- } from "./chunk-MDI7523D.js";
8
- import "./chunk-VMYPJPKV.js";
11
+ } from "./chunk-XFSQM3LJ.js";
12
+ import {
13
+ detectClientSupports
14
+ } from "./chunk-VOQKQ6W2.js";
9
15
  import {
10
16
  installHooks
11
17
  } from "./chunk-YDZJRLHL.js";
12
- import {
13
- createScanReport,
14
- detectFramework
15
- } from "./chunk-N4DCTOXW.js";
16
- import {
17
- paint
18
- } from "./chunk-WWNXR34K.js";
19
18
  import {
20
19
  createDebugLogger,
21
20
  resolveDevMode
22
21
  } from "./chunk-AEOYCVBG.js";
22
+ import {
23
+ displayWidth,
24
+ padEnd,
25
+ paint
26
+ } from "./chunk-WWNXR34K.js";
23
27
  import {
24
28
  t
25
29
  } from "./chunk-6ICJICVU.js";
@@ -852,7 +856,7 @@ function readProjectName(target) {
852
856
  return basename(target);
853
857
  }
854
858
  function getCliVersion() {
855
- return true ? "1.2.0" : "unknown";
859
+ return true ? "1.3.1" : "unknown";
856
860
  }
857
861
  function sortRecord(record) {
858
862
  return Object.fromEntries(Object.entries(record).sort(([left], [right]) => left.localeCompare(right)));
@@ -862,11 +866,6 @@ function toPosixPath(path) {
862
866
  }
863
867
 
864
868
  // src/commands/init.ts
865
- var AGENTS_TEMPLATE_BY_FRAMEWORK = {
866
- "cocos-creator": "templates/agents-md/variants/cocos.md",
867
- vite: "templates/agents-md/variants/vite.md",
868
- next: "templates/agents-md/variants/next.md"
869
- };
870
869
  var CLAUDE_INIT_SKILL_TEMPLATE = "templates/claude-skills/agents-md-init/SKILL.md";
871
870
  var CLAUDE_INIT_REMINDER_HOOK_TEMPLATE = "templates/claude-hooks/agents-md-init-reminder.cjs";
872
871
  var CLAUDE_INIT_REMINDER_COMMAND = ".claude/hooks/agents-md-init-reminder.cjs";
@@ -907,6 +906,11 @@ var initCommand = defineCommand({
907
906
  default: true,
908
907
  negativeDescription: t("cli.init.args.no-hooks.description")
909
908
  },
909
+ interactive: {
910
+ type: "boolean",
911
+ description: t("cli.init.args.interactive.description"),
912
+ default: true
913
+ },
910
914
  "mcp-install": {
911
915
  type: "string",
912
916
  default: "global",
@@ -928,11 +932,16 @@ var initCommand = defineCommand({
928
932
  for (const step of resolution.chain) {
929
933
  logger(step);
930
934
  }
935
+ const supports = detectClientSupports(target);
936
+ const interactive = args.interactive !== false && isInteractiveInit();
931
937
  if (options.force) {
932
938
  writeStderr(t("cli.init.force.warning", { path: target }));
933
939
  }
940
+ if (interactive) {
941
+ printInitPlanSummary(target, options, mcpInstallMode, supports);
942
+ }
934
943
  const created = initFabric(target, options);
935
- console.log(formatInitPathAction(created.agentsPath, created.agentsAction));
944
+ console.log(formatInitPathAction(created.bootstrapPath, created.bootstrapAction));
936
945
  console.log(formatInitPathAction(created.metaPath, created.metaAction));
937
946
  console.log(formatInitPathAction(created.humanLockPath, created.humanLockAction));
938
947
  console.log(formatInitPathAction(created.forensicPath, created.forensicAction));
@@ -1020,37 +1029,37 @@ var initCommand = defineCommand({
1020
1029
  console.log(
1021
1030
  t("cli.init.reason-message", {
1022
1031
  label: reasonLabel(),
1023
- message: paint.muted(t("cli.init.reason-message.body"))
1032
+ message: paint.muted(formatInitReasonMessage(supports))
1024
1033
  })
1025
1034
  );
1026
1035
  printInitStageSummary(stageResults);
1036
+ printInitCapabilitySummary(supports, stageResults, options);
1027
1037
  }
1028
1038
  });
1029
1039
  var init_default = initCommand;
1030
1040
  function initFabric(target, options) {
1031
1041
  assertExistingDirectory2(target);
1032
- const agentsPath = join2(target, "AGENTS.md");
1033
1042
  const fabricDir = join2(target, ".fabric");
1043
+ const bootstrapPath = join2(fabricDir, "bootstrap", "README.md");
1034
1044
  const forensicPath = join2(fabricDir, "forensic.json");
1035
1045
  const claudeSkillPath = join2(target, ".claude", "skills", "agents-md-init", "SKILL.md");
1036
1046
  const claudeHookPath = join2(target, ".claude", "hooks", "agents-md-init-reminder.cjs");
1037
1047
  const claudeSettingsPath = join2(target, ".claude", "settings.json");
1038
- const forensicGuardAction = prepareFreshPath(forensicPath, options);
1039
- const agentsAction = prepareFreshPath(agentsPath, options);
1040
- const fabricDirAction = prepareFreshPath(fabricDir, options);
1041
- const forensicAction = forensicGuardAction === "overwritten" || fabricDirAction === "overwritten" ? "overwritten" : "created";
1042
- const scanReport = createScanReport(target);
1043
- const forensicReport = buildForensicReport(target);
1044
- const template = readFileSync2(findAgentsTemplatePath(scanReport.framework.kind), "utf8");
1045
- const humanLockTemplate = readFileSync2(findTemplatePath("templates/fabric/human-lock.json"), "utf8");
1046
- const packageName = readPackageName(target) ?? parse(target).base;
1047
- const agentsContent = template.replaceAll("{ projectName }", packageName).replaceAll("{ frameworkKind }", scanReport.framework.kind);
1048
- const agentsHash = sha256(agentsContent);
1049
- const meta = createInitialMeta(agentsHash);
1050
1048
  const metaPath = join2(fabricDir, "agents.meta.json");
1051
1049
  const humanLockPath = join2(fabricDir, "human-lock.json");
1052
- mkdirSync(fabricDir, { recursive: false });
1053
- writeNewFile(agentsPath, agentsContent, options);
1050
+ prepareWritableDirectory(fabricDir, options);
1051
+ const bootstrapAction = prepareFreshPath(bootstrapPath, options);
1052
+ const metaAction = prepareFreshPath(metaPath, options);
1053
+ const humanLockAction = prepareFreshPath(humanLockPath, options);
1054
+ const forensicAction = prepareFreshPath(forensicPath, options);
1055
+ const forensicReport = buildForensicReport(target);
1056
+ const humanLockTemplate = readFileSync2(findTemplatePath("templates/fabric/human-lock.json"), "utf8");
1057
+ const bootstrapContent = buildFabricBootstrapGuide(target);
1058
+ const bootstrapHash = sha256(bootstrapContent);
1059
+ const meta = createInitialMeta(bootstrapHash);
1060
+ mkdirSync(fabricDir, { recursive: true });
1061
+ mkdirSync(dirname(bootstrapPath), { recursive: true });
1062
+ writeNewFile(bootstrapPath, bootstrapContent, options);
1054
1063
  writeNewFile(metaPath, `${JSON.stringify(meta, null, 2)}
1055
1064
  `, options);
1056
1065
  writeNewFile(humanLockPath, humanLockTemplate.endsWith("\n") ? humanLockTemplate : `${humanLockTemplate}
@@ -1065,12 +1074,12 @@ function initFabric(target, options) {
1065
1074
  );
1066
1075
  const claudeSettingsAction = mergeClaudeStopHook(claudeSettingsPath, options);
1067
1076
  return {
1068
- agentsPath,
1069
- agentsAction,
1077
+ bootstrapPath,
1078
+ bootstrapAction,
1070
1079
  metaPath,
1071
- metaAction: fabricDirAction,
1080
+ metaAction,
1072
1081
  humanLockPath,
1073
- humanLockAction: fabricDirAction,
1082
+ humanLockAction,
1074
1083
  forensicPath,
1075
1084
  forensicAction,
1076
1085
  claudeSkillPath,
@@ -1081,10 +1090,6 @@ function initFabric(target, options) {
1081
1090
  claudeSettingsAction
1082
1091
  };
1083
1092
  }
1084
- function findAgentsTemplatePath(frameworkKind) {
1085
- const relativePath = AGENTS_TEMPLATE_BY_FRAMEWORK[frameworkKind] ?? "templates/agents-md/AGENTS.md.template";
1086
- return findTemplatePath(relativePath);
1087
- }
1088
1093
  function normalizeTarget2(targetInput) {
1089
1094
  return isAbsolute2(targetInput) ? targetInput : resolve2(process.cwd(), targetInput);
1090
1095
  }
@@ -1126,7 +1131,7 @@ function createInitialMeta(agentsHash) {
1126
1131
  revision: sha256(agentsHash),
1127
1132
  nodes: {
1128
1133
  L0: {
1129
- file: "AGENTS.md",
1134
+ file: ".fabric/bootstrap/README.md",
1130
1135
  scope_glob: "**",
1131
1136
  deps: [],
1132
1137
  priority: "high",
@@ -1137,18 +1142,6 @@ function createInitialMeta(agentsHash) {
1137
1142
  }
1138
1143
  };
1139
1144
  }
1140
- function readPackageName(target) {
1141
- const packageJsonPath = join2(target, "package.json");
1142
- if (!existsSync2(packageJsonPath)) {
1143
- return void 0;
1144
- }
1145
- try {
1146
- const packageJson = JSON.parse(readFileSync2(packageJsonPath, "utf8"));
1147
- return packageJson.name;
1148
- } catch {
1149
- return void 0;
1150
- }
1151
- }
1152
1145
  function findTemplatePath(relativePath) {
1153
1146
  const currentModuleDir = dirname(fileURLToPath(import.meta.url));
1154
1147
  const candidates = [
@@ -1185,6 +1178,15 @@ function prepareFreshPath(path, options) {
1185
1178
  rmSync(path, { recursive: true, force: true });
1186
1179
  return "overwritten";
1187
1180
  }
1181
+ function prepareWritableDirectory(path, options) {
1182
+ if (!existsSync2(path) || statSync2(path).isDirectory()) {
1183
+ return;
1184
+ }
1185
+ if (!options?.force) {
1186
+ throw new Error(t("cli.init.errors.abort-existing", { path }));
1187
+ }
1188
+ rmSync(path, { force: true });
1189
+ }
1188
1190
  function writeNewFile(path, content, options) {
1189
1191
  const existed = existsSync2(path);
1190
1192
  if (existed && !options?.force) {
@@ -1329,6 +1331,120 @@ function collectInitStageNames(stageResults, disposition) {
1329
1331
  function shouldPrintHooksNextStep(options, stageResults) {
1330
1332
  return Boolean(options.skipHooks) || stageResults.some((stage) => stage.name === "hooks" && stage.disposition === "failed");
1331
1333
  }
1334
+ function isInteractiveInit() {
1335
+ return Boolean(process.stdout.isTTY) && Boolean(process.stderr.isTTY);
1336
+ }
1337
+ function printInitPlanSummary(target, options, mcpInstallMode, supports) {
1338
+ console.log(t("cli.init.plan.title"));
1339
+ console.log(t("cli.init.plan.target", { target }));
1340
+ console.log(
1341
+ t("cli.init.plan.actions", {
1342
+ bootstrap: yesNoLabel(!options.skipBootstrap),
1343
+ mcp: yesNoLabel(!options.skipMcp),
1344
+ hooks: yesNoLabel(!options.skipHooks),
1345
+ mcpInstall: mcpInstallMode
1346
+ })
1347
+ );
1348
+ const detected = supports.filter((support) => support.detected);
1349
+ console.log(
1350
+ t("cli.init.plan.detected", {
1351
+ clients: detected.length > 0 ? detected.map((support) => support.label).join(", ") : t("cli.shared.none")
1352
+ })
1353
+ );
1354
+ console.log(t("cli.init.plan.writes"));
1355
+ console.log(` - ${target}/.fabric/bootstrap/README.md`);
1356
+ console.log(` - ${target}/.fabric/agents.meta.json`);
1357
+ console.log(` - ${target}/.fabric/human-lock.json`);
1358
+ console.log(` - ${target}/.fabric/forensic.json`);
1359
+ }
1360
+ function printInitCapabilitySummary(supports, stageResults, options) {
1361
+ const detected = supports.filter((support) => support.detected);
1362
+ if (detected.length === 0) {
1363
+ console.log(t("cli.init.capabilities.none"));
1364
+ return;
1365
+ }
1366
+ console.log(t("cli.init.capabilities.title"));
1367
+ const rows = detected.map((support) => toCapabilityRow(support, stageResults, options));
1368
+ const headers = {
1369
+ client: t("cli.init.capabilities.header.client"),
1370
+ bootstrap: t("cli.init.capabilities.header.bootstrap"),
1371
+ mcp: t("cli.init.capabilities.header.mcp"),
1372
+ hook: t("cli.init.capabilities.header.hook"),
1373
+ skill: t("cli.init.capabilities.header.skill"),
1374
+ followUp: t("cli.init.capabilities.header.follow-up")
1375
+ };
1376
+ const widths = {
1377
+ client: Math.max(displayWidth(headers.client), ...rows.map((row) => displayWidth(row.client))),
1378
+ bootstrap: Math.max(displayWidth(headers.bootstrap), ...rows.map((row) => displayWidth(row.bootstrap))),
1379
+ mcp: Math.max(displayWidth(headers.mcp), ...rows.map((row) => displayWidth(row.mcp))),
1380
+ hook: Math.max(displayWidth(headers.hook), ...rows.map((row) => displayWidth(row.hook))),
1381
+ skill: Math.max(displayWidth(headers.skill), ...rows.map((row) => displayWidth(row.skill))),
1382
+ followUp: Math.max(displayWidth(headers.followUp), ...rows.map((row) => displayWidth(row.followUp)))
1383
+ };
1384
+ console.log(formatCapabilityTableRow(headers, widths));
1385
+ console.log(formatCapabilityDivider(widths));
1386
+ for (const row of rows) {
1387
+ console.log(formatCapabilityTableRow(row, widths));
1388
+ }
1389
+ }
1390
+ function toCapabilityRow(support, stageResults, options) {
1391
+ const stage = (name) => stageResults.find((entry) => entry.name === name)?.disposition ?? null;
1392
+ const bootstrap = support.capabilities.bootstrap ? capabilityStatus(options.skipBootstrap ? "skipped" : stage("bootstrap")) : t("cli.init.capabilities.status.na");
1393
+ const mcp = support.capabilities.mcp ? capabilityStatus(options.skipMcp ? "skipped" : stage("mcp")) : t("cli.init.capabilities.status.na");
1394
+ const hook = support.capabilities.hook ? capabilityStatus("ran") : t("cli.init.capabilities.status.na");
1395
+ const skill = support.capabilities.skill ? t("cli.init.capabilities.status.installed") : t("cli.init.capabilities.status.manual");
1396
+ return {
1397
+ client: support.label,
1398
+ bootstrap,
1399
+ mcp,
1400
+ hook,
1401
+ skill,
1402
+ followUp: support.capabilities.skill ? t("cli.init.capabilities.follow-up.ready") : t("cli.init.capabilities.follow-up.manual")
1403
+ };
1404
+ }
1405
+ function capabilityStatus(disposition) {
1406
+ switch (disposition) {
1407
+ case "ran":
1408
+ return t("cli.init.capabilities.status.ready");
1409
+ case "skipped":
1410
+ return t("cli.init.capabilities.status.skipped");
1411
+ case "failed":
1412
+ return t("cli.init.capabilities.status.failed");
1413
+ case null:
1414
+ return t("cli.init.capabilities.status.na");
1415
+ default:
1416
+ return t("cli.init.capabilities.status.ready");
1417
+ }
1418
+ }
1419
+ function formatCapabilityTableRow(row, widths) {
1420
+ return [
1421
+ padEnd(row.client, widths.client),
1422
+ padEnd(row.bootstrap, widths.bootstrap),
1423
+ padEnd(row.mcp, widths.mcp),
1424
+ padEnd(row.hook, widths.hook),
1425
+ padEnd(row.skill, widths.skill),
1426
+ padEnd(row.followUp, widths.followUp)
1427
+ ].join(" ");
1428
+ }
1429
+ function formatCapabilityDivider(widths) {
1430
+ return [
1431
+ "".padEnd(widths.client, "-"),
1432
+ "".padEnd(widths.bootstrap, "-"),
1433
+ "".padEnd(widths.mcp, "-"),
1434
+ "".padEnd(widths.hook, "-"),
1435
+ "".padEnd(widths.skill, "-"),
1436
+ "".padEnd(widths.followUp, "-")
1437
+ ].join(" ");
1438
+ }
1439
+ function formatInitReasonMessage(supports) {
1440
+ if (supports.some((support) => support.detected && support.capabilities.skill)) {
1441
+ return t("cli.init.reason-message.body");
1442
+ }
1443
+ return t("cli.init.reason-message.manual-body");
1444
+ }
1445
+ function yesNoLabel(value) {
1446
+ return value ? t("cli.shared.yes") : t("cli.shared.no");
1447
+ }
1332
1448
  function formatInitPathAction(path, action) {
1333
1449
  return t("cli.init.created-path", { label: labelForInitWriteAction(action), path });
1334
1450
  }
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  ledgerAppendCommand,
4
4
  ledger_append_default
5
- } from "./chunk-F2BXHPM5.js";
5
+ } from "./chunk-N7EZORJZ.js";
6
6
  import "./chunk-6ICJICVU.js";
7
7
  export {
8
8
  ledger_append_default as default,
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ sync_meta_default
4
+ } from "./chunk-Q4LOVXML.js";
5
+ import {
6
+ human_lint_default
7
+ } from "./chunk-L43IGJ6X.js";
8
+ import {
9
+ ledger_append_default
10
+ } from "./chunk-N7EZORJZ.js";
11
+ import {
12
+ resolveDevModeTarget
13
+ } from "./chunk-AEOYCVBG.js";
14
+ import "./chunk-WWNXR34K.js";
15
+ import {
16
+ t
17
+ } from "./chunk-6ICJICVU.js";
18
+
19
+ // src/commands/pre-commit.ts
20
+ import { execSync } from "child_process";
21
+ import { readFileSync } from "fs";
22
+ import { join } from "path";
23
+ import process from "process";
24
+ import { agentsMetaSchema } from "@fenglimg/fabric-shared";
25
+ import { defineCommand } from "citty";
26
+ import { minimatch } from "minimatch";
27
+ async function runOrFail(name, cmd, args) {
28
+ try {
29
+ await cmd.run?.({ args });
30
+ } catch (err) {
31
+ process.stderr.write(
32
+ `${t("cli.pre-commit.run-failed", { name, message: err.message })}
33
+ `
34
+ );
35
+ process.exit(1);
36
+ }
37
+ }
38
+ function getStagedFiles(target) {
39
+ try {
40
+ const output = execSync("git diff --cached --name-only --no-renames", {
41
+ cwd: target,
42
+ encoding: "utf8",
43
+ stdio: ["ignore", "pipe", "pipe"]
44
+ });
45
+ return output.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
46
+ } catch {
47
+ return [];
48
+ }
49
+ }
50
+ function tryReadAgentsMeta(target) {
51
+ const metaPath = join(target, ".fabric", "agents.meta.json");
52
+ try {
53
+ const raw = readFileSync(metaPath, "utf8");
54
+ return agentsMetaSchema.parse(JSON.parse(raw));
55
+ } catch {
56
+ return null;
57
+ }
58
+ }
59
+ function matchesFabricScope(stagedFiles, meta) {
60
+ const scopeGlobs = Object.values(meta.nodes).filter((node) => node.file !== ".fabric/bootstrap/README.md" && node.file !== "AGENTS.md").map((node) => node.scope_glob);
61
+ return stagedFiles.some(
62
+ (file) => file === ".fabric/bootstrap/README.md" || file === "AGENTS.md" || file === ".fabric/agents.meta.json" || file === ".fabric/human-lock.json" || file === ".intent-ledger.jsonl" || scopeGlobs.some((pattern) => minimatch(file, pattern, { dot: true }))
63
+ );
64
+ }
65
+ var pre_commit_default = defineCommand({
66
+ meta: {
67
+ name: "pre-commit",
68
+ description: t("cli.pre-commit.description")
69
+ },
70
+ args: {
71
+ target: {
72
+ type: "string",
73
+ description: t("cli.pre-commit.args.target.description")
74
+ }
75
+ },
76
+ async run({ args }) {
77
+ const target = resolveDevModeTarget(args.target);
78
+ const stagedFiles = getStagedFiles(target);
79
+ const meta = tryReadAgentsMeta(target);
80
+ if (stagedFiles.length > 0 && meta !== null && !matchesFabricScope(stagedFiles, meta)) {
81
+ process.stderr.write("No fabric-managed files staged, skipping checks\n");
82
+ return;
83
+ }
84
+ await runOrFail("sync-meta --check-only", sync_meta_default, {
85
+ target,
86
+ "check-only": true
87
+ });
88
+ await runOrFail("human-lint", human_lint_default, { target });
89
+ await runOrFail("ledger-append --staged", ledger_append_default, {
90
+ target,
91
+ staged: true
92
+ });
93
+ }
94
+ });
95
+ export {
96
+ pre_commit_default as default
97
+ };
@@ -3,9 +3,9 @@ import {
3
3
  createScanReport,
4
4
  scanCommand,
5
5
  scan_default
6
- } from "./chunk-N4DCTOXW.js";
7
- import "./chunk-WWNXR34K.js";
6
+ } from "./chunk-AZRKMFRY.js";
8
7
  import "./chunk-AEOYCVBG.js";
8
+ import "./chunk-WWNXR34K.js";
9
9
  import "./chunk-6ICJICVU.js";
10
10
  export {
11
11
  createScanReport,
@@ -5,7 +5,7 @@ import {
5
5
  deriveTopologyType,
6
6
  syncMetaCommand,
7
7
  sync_meta_default
8
- } from "./chunk-6UUPKSDE.js";
8
+ } from "./chunk-Q4LOVXML.js";
9
9
  import "./chunk-6ICJICVU.js";
10
10
  export {
11
11
  computeAgentsMeta,
@@ -0,0 +1,116 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ installMcpClients
4
+ } from "./chunk-XFSQM3LJ.js";
5
+ import "./chunk-VOQKQ6W2.js";
6
+ import {
7
+ installHooks
8
+ } from "./chunk-YDZJRLHL.js";
9
+ import {
10
+ resolveDevModeTarget
11
+ } from "./chunk-AEOYCVBG.js";
12
+ import {
13
+ paint
14
+ } from "./chunk-WWNXR34K.js";
15
+ import {
16
+ t
17
+ } from "./chunk-6ICJICVU.js";
18
+
19
+ // src/commands/update.ts
20
+ import { defineCommand } from "citty";
21
+ function writeStderr(message) {
22
+ process.stderr.write(`${message}
23
+ `);
24
+ }
25
+ function completedLabel() {
26
+ return paint.success(t("cli.init.stages.completed"));
27
+ }
28
+ function skippedLabel() {
29
+ return paint.muted(t("cli.init.stages.skipped"));
30
+ }
31
+ function failedLabel() {
32
+ return paint.error(t("cli.init.stages.failed"));
33
+ }
34
+ function formatStageResult(stage, status, installedCount, skippedCount, note) {
35
+ const label = status === "completed" ? completedLabel() : skippedLabel();
36
+ const counts = `installed=${installedCount} skipped=${skippedCount}`;
37
+ const suffix = note ? ` ${paint.muted(`(${note})`)}` : "";
38
+ return `${label} ${stage}: ${counts}${suffix}`;
39
+ }
40
+ function formatStageFailure(stage, error) {
41
+ const message = error instanceof Error ? error.message : String(error);
42
+ return `${failedLabel()} ${stage}: ${message}`;
43
+ }
44
+ function printStageSummary(stageResults) {
45
+ const ran = stageResults.filter((s) => s.disposition === "ran").map((s) => s.name);
46
+ const skipped = stageResults.filter((s) => s.disposition === "skipped").map((s) => s.name);
47
+ const failed = stageResults.filter((s) => s.disposition === "failed").map((s) => s.name);
48
+ console.log(`${paint.success(t("cli.init.stages.summary.ran"))}: ${ran.length > 0 ? ran.join(", ") : t("cli.shared.none")}`);
49
+ console.log(`${paint.muted(t("cli.init.stages.summary.skipped"))}: ${skipped.length > 0 ? skipped.join(", ") : t("cli.shared.none")}`);
50
+ console.log(`${paint.error(t("cli.init.stages.summary.failed"))}: ${failed.length > 0 ? failed.join(", ") : t("cli.shared.none")}`);
51
+ }
52
+ var updateCommand = defineCommand({
53
+ meta: {
54
+ name: "update",
55
+ description: t("cli.update.description")
56
+ },
57
+ args: {
58
+ target: {
59
+ type: "string",
60
+ description: t("cli.update.args.target.description")
61
+ },
62
+ mcp: {
63
+ type: "boolean",
64
+ default: true,
65
+ negativeDescription: t("cli.update.args.no-mcp.description")
66
+ },
67
+ hooks: {
68
+ type: "boolean",
69
+ default: true,
70
+ negativeDescription: t("cli.update.args.no-hooks.description")
71
+ }
72
+ },
73
+ async run({ args }) {
74
+ const target = resolveDevModeTarget(args.target);
75
+ const skipMcp = args.mcp === false;
76
+ const skipHooks = args.hooks === false;
77
+ const stageResults = [];
78
+ if (skipMcp) {
79
+ stageResults.push({ name: "mcp", disposition: "skipped" });
80
+ } else {
81
+ console.log(`${paint.ai(t("cli.shared.next"))} ${paint.muted(t("cli.init.stages.mcp"))}`);
82
+ try {
83
+ const result = await installMcpClients(target);
84
+ if (result.details.length === 0) {
85
+ console.log(formatStageResult("mcp", "skipped", 0, 0, t("cli.config.install.no-configs")));
86
+ stageResults.push({ name: "mcp", disposition: "skipped" });
87
+ } else {
88
+ console.log(formatStageResult("mcp", "completed", result.installed.length, result.skipped.length));
89
+ stageResults.push({ name: "mcp", disposition: "ran" });
90
+ }
91
+ } catch (error) {
92
+ writeStderr(formatStageFailure("mcp", error));
93
+ stageResults.push({ name: "mcp", disposition: "failed" });
94
+ }
95
+ }
96
+ if (skipHooks) {
97
+ stageResults.push({ name: "hooks", disposition: "skipped" });
98
+ } else {
99
+ console.log(`${paint.ai(t("cli.shared.next"))} ${paint.muted(t("cli.init.stages.hooks"))}`);
100
+ try {
101
+ const result = await installHooks(target);
102
+ console.log(formatStageResult("hooks", "completed", result.installed.length, result.skipped.length));
103
+ stageResults.push({ name: "hooks", disposition: "ran" });
104
+ } catch (error) {
105
+ writeStderr(formatStageFailure("hooks", error));
106
+ stageResults.push({ name: "hooks", disposition: "failed" });
107
+ }
108
+ }
109
+ printStageSummary(stageResults);
110
+ }
111
+ });
112
+ var update_default = updateCommand;
113
+ export {
114
+ update_default as default,
115
+ updateCommand
116
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fenglimg/fabric-cli",
3
- "version": "1.2.0",
3
+ "version": "1.3.1",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "fab": "dist/index.js",
@@ -15,10 +15,11 @@
15
15
  "dependencies": {
16
16
  "@iarna/toml": "^2.2.5",
17
17
  "citty": "^0.2.2",
18
+ "minimatch": "^10.0.1",
18
19
  "picocolors": "^1.1.1",
19
20
  "string-width": "^7.2.0",
20
- "@fenglimg/fabric-shared": "1.2.0",
21
- "@fenglimg/fabric-server": "1.2.0"
21
+ "@fenglimg/fabric-shared": "1.3.1",
22
+ "@fenglimg/fabric-server": "1.3.1"
22
23
  },
23
24
  "devDependencies": {
24
25
  "@types/iarna__toml": "^2.0.5",