@fenglimg/fabric-cli 1.3.0 → 1.4.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 (36) hide show
  1. package/README.md +24 -22
  2. package/dist/{bootstrap-IUL4SAAK.js → bootstrap-B6RCVJZJ.js} +4 -2
  3. package/dist/{chunk-N4DCTOXW.js → chunk-AZRKMFRY.js} +6 -6
  4. package/dist/{chunk-VMYPJPKV.js → chunk-BEKSXO5N.js} +161 -21
  5. package/dist/{chunk-TO5RUB4R.js → chunk-BVTMVW5M.js} +1 -1
  6. package/dist/{chunk-F2BXHPM5.js → chunk-N7EZORJZ.js} +9 -1
  7. package/dist/{chunk-6UUPKSDE.js → chunk-Q4LOVXML.js} +13 -8
  8. package/dist/{chunk-RUQCZA2Q.js → chunk-XQYY2U2C.js} +92 -115
  9. package/dist/{config-MKWKDE32.js → config-EC5L2QNI.js} +2 -2
  10. package/dist/index.js +11 -11
  11. package/dist/{init-BZ73IUHH.js → init-QC2MLFHR.js} +894 -261
  12. package/dist/{ledger-append-XZ5SX4O5.js → ledger-append-DULKJ6Q2.js} +1 -1
  13. package/dist/{pre-commit-AK55G73F.js → pre-commit-IK6SJOPT.js} +5 -5
  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-JZPUJ36D.js → update-FY2WKWPB.js} +5 -5
  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/codex-hooks/fabric-session-start.cjs +19 -0
  31. package/templates/codex-hooks/fabric-stop-reminder.cjs +18 -0
  32. package/templates/codex-skills/fabric-init/SKILL.md +27 -0
  33. package/templates/fabric/human-lock.json +12 -12
  34. package/templates/husky/pre-commit +24 -24
  35. package/dist/{doctor-5KJGOV2P.js → doctor-QTSG2RWF.js} +3 -3
  36. package/dist/{serve-MMN4GYLM.js → serve-4J2CQY25.js} +4 -4
@@ -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-XQYY2U2C.js";
5
6
  import {
6
- createScanReport,
7
7
  detectFramework
8
- } from "./chunk-N4DCTOXW.js";
8
+ } from "./chunk-AZRKMFRY.js";
9
9
  import {
10
10
  installMcpClients
11
- } from "./chunk-TO5RUB4R.js";
12
- import "./chunk-VMYPJPKV.js";
11
+ } from "./chunk-BVTMVW5M.js";
12
+ import {
13
+ detectClientSupports
14
+ } from "./chunk-BEKSXO5N.js";
13
15
  import {
14
16
  installHooks
15
17
  } from "./chunk-YDZJRLHL.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";
@@ -30,6 +34,7 @@ import * as childProcess from "child_process";
30
34
  import { chmodSync, copyFileSync, existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, renameSync, rmSync, statSync as statSync2, writeFileSync } from "fs";
31
35
  import { dirname, isAbsolute as isAbsolute2, join as join2, parse, resolve as resolve2 } from "path";
32
36
  import { fileURLToPath } from "url";
37
+ import { cancel, confirm, group, intro, isCancel, log, note, outro, select } from "@clack/prompts";
33
38
  import { defineCommand } from "citty";
34
39
 
35
40
  // src/scanner/forensic.ts
@@ -852,7 +857,7 @@ function readProjectName(target) {
852
857
  return basename(target);
853
858
  }
854
859
  function getCliVersion() {
855
- return true ? "1.3.0" : "unknown";
860
+ return true ? "1.4.0" : "unknown";
856
861
  }
857
862
  function sortRecord(record) {
858
863
  return Object.fromEntries(Object.entries(record).sort(([left], [right]) => left.localeCompare(right)));
@@ -862,16 +867,17 @@ function toPosixPath(path) {
862
867
  }
863
868
 
864
869
  // 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
870
  var CLAUDE_INIT_SKILL_TEMPLATE = "templates/claude-skills/agents-md-init/SKILL.md";
871
871
  var CLAUDE_INIT_REMINDER_HOOK_TEMPLATE = "templates/claude-hooks/agents-md-init-reminder.cjs";
872
872
  var CLAUDE_INIT_REMINDER_COMMAND = ".claude/hooks/agents-md-init-reminder.cjs";
873
+ var CODEX_INIT_SKILL_TEMPLATE = "templates/codex-skills/fabric-init/SKILL.md";
874
+ var CODEX_SESSION_START_HOOK_TEMPLATE = "templates/codex-hooks/fabric-session-start.cjs";
875
+ var CODEX_STOP_HOOK_TEMPLATE = "templates/codex-hooks/fabric-stop-reminder.cjs";
876
+ var CODEX_SESSION_START_COMMAND = ".codex/hooks/fabric-session-start.cjs";
877
+ var CODEX_STOP_COMMAND = ".codex/hooks/fabric-stop-reminder.cjs";
873
878
  var LOCAL_FABRIC_SERVER_PATH = join2("node_modules", "@fenglimg", "fabric-server", "dist", "index.js");
874
879
  var FABRIC_SERVER_PACKAGE = "@fenglimg/fabric-server";
880
+ var INIT_WIZARD_GROUP_CANCELLED = /* @__PURE__ */ Symbol("init-wizard-group-cancelled");
875
881
  var initCommand = defineCommand({
876
882
  meta: {
877
883
  name: "init",
@@ -892,6 +898,21 @@ var initCommand = defineCommand({
892
898
  description: t("cli.init.args.force.description"),
893
899
  default: false
894
900
  },
901
+ yes: {
902
+ type: "boolean",
903
+ description: t("cli.init.args.yes.description"),
904
+ default: false
905
+ },
906
+ plan: {
907
+ type: "boolean",
908
+ description: t("cli.init.args.plan.description"),
909
+ default: false
910
+ },
911
+ reapply: {
912
+ type: "boolean",
913
+ description: t("cli.init.args.reapply.description"),
914
+ default: false
915
+ },
895
916
  bootstrap: {
896
917
  type: "boolean",
897
918
  default: true,
@@ -907,6 +928,11 @@ var initCommand = defineCommand({
907
928
  default: true,
908
929
  negativeDescription: t("cli.init.args.no-hooks.description")
909
930
  },
931
+ interactive: {
932
+ type: "boolean",
933
+ description: t("cli.init.args.interactive.description"),
934
+ default: true
935
+ },
910
936
  "mcp-install": {
911
937
  type: "string",
912
938
  default: "global",
@@ -914,176 +940,726 @@ var initCommand = defineCommand({
914
940
  }
915
941
  },
916
942
  async run({ args }) {
917
- const logger = createDebugLogger(args.debug);
918
- const resolution = resolveDevMode(args.target, process.cwd());
919
- const target = normalizeTarget2(resolution.target);
920
- const mcpInstallMode = resolveMcpInstallMode(args["mcp-install"]);
921
- const options = {
922
- force: args.force,
923
- skipBootstrap: args.bootstrap === false ? true : args.skipBootstrap,
924
- skipMcp: args.mcp === false ? true : args.skipMcp,
925
- skipHooks: args.hooks === false ? true : args.skipHooks
943
+ await runInitCommand(args);
944
+ }
945
+ });
946
+ var init_default = initCommand;
947
+ async function runInitCommand(args) {
948
+ const logger = createDebugLogger(args.debug);
949
+ const resolution = resolveDevMode(args.target, process.cwd());
950
+ const intent = resolveInitCliIntent(args, resolution.target);
951
+ logger(`init target source: ${resolution.source}`);
952
+ for (const step of resolution.chain) {
953
+ logger(step);
954
+ }
955
+ if (intent.options.planOnly) {
956
+ writeStderr(t("cli.init.compat.plan"));
957
+ }
958
+ if (args.interactive === false) {
959
+ writeStderr(t("cli.init.compat.interactive"));
960
+ }
961
+ if (args.bootstrap === false || args.mcp === false || args.hooks === false) {
962
+ writeStderr(t("cli.init.compat.legacy-stage-flags"));
963
+ }
964
+ const supports = detectClientSupports(intent.target);
965
+ const basePlan = buildInitExecutionPlan({
966
+ target: intent.target,
967
+ options: intent.options,
968
+ mcpInstallMode: intent.mcpInstallMode,
969
+ interactive: intent.interactiveSummary && !intent.wizardEnabled,
970
+ supports
971
+ });
972
+ const plan = intent.wizardEnabled ? await resolveInitExecutionPlanWithWizard(basePlan, args, createDefaultInitWizardAdapter()) : basePlan;
973
+ if (plan === null) {
974
+ writeStderr(t("cli.init.wizard.cancelled"));
975
+ throw new Error(t("cli.init.wizard.cancelled"));
976
+ }
977
+ return executeInitExecutionPlan(plan);
978
+ }
979
+ function resolveInitCliIntent(args, targetInput) {
980
+ const target = normalizeTarget2(targetInput);
981
+ const mcpInstallMode = resolveMcpInstallMode(args["mcp-install"]);
982
+ const terminalInteractive = isInteractiveInit();
983
+ const planOnly = args.plan === true;
984
+ const reapply = args.reapply === true;
985
+ const options = {
986
+ force: reapply ? true : args.force,
987
+ skipBootstrap: args.bootstrap === false ? true : args.skipBootstrap,
988
+ skipMcp: args.mcp === false ? true : args.skipMcp,
989
+ skipHooks: args.hooks === false ? true : args.skipHooks,
990
+ planOnly,
991
+ reapply
992
+ };
993
+ return {
994
+ target,
995
+ options,
996
+ mcpInstallMode,
997
+ interactiveSummary: args.interactive !== false && terminalInteractive,
998
+ wizardEnabled: shouldUseInitWizard(args, terminalInteractive) && !planOnly
999
+ };
1000
+ }
1001
+ function buildInitExecutionPlan(input) {
1002
+ const options = input.options ?? {};
1003
+ const scaffold = buildInitFabricPlan(input.target, options);
1004
+ const supports = input.supports ?? detectClientSupports(input.target);
1005
+ const mcpInstallMode = input.mcpInstallMode ?? "global";
1006
+ const stages = [
1007
+ { name: "bootstrap", skipped: Boolean(options.skipBootstrap) },
1008
+ {
1009
+ name: "mcp",
1010
+ skipped: Boolean(options.skipMcp),
1011
+ installMode: mcpInstallMode,
1012
+ localServerPath: mcpInstallMode === "local" ? LOCAL_FABRIC_SERVER_PATH : void 0,
1013
+ packageManager: mcpInstallMode === "local" ? detectPackageManager(input.target) : void 0
1014
+ },
1015
+ { name: "hooks", skipped: Boolean(options.skipHooks) }
1016
+ ];
1017
+ return {
1018
+ target: input.target,
1019
+ options,
1020
+ mcpInstallMode,
1021
+ interactive: input.interactive ?? false,
1022
+ supports,
1023
+ scaffold,
1024
+ stages,
1025
+ steps: [
1026
+ { name: "preflight" },
1027
+ { name: "scaffold" },
1028
+ ...stages.map((stage) => ({ name: stage.name, skipped: stage.skipped })),
1029
+ { name: "post-setup" }
1030
+ ]
1031
+ };
1032
+ }
1033
+ async function executeInitExecutionPlan(plan) {
1034
+ if (plan.options.force) {
1035
+ writeStderr(t("cli.init.force.warning", { path: plan.target }));
1036
+ }
1037
+ if (plan.options.reapply && !plan.options.planOnly && !plan.interactive) {
1038
+ writeStderr(formatInitModeBanner(plan.options));
1039
+ }
1040
+ if (plan.interactive) {
1041
+ printInitPlanSummary(plan.target, plan.options, plan.mcpInstallMode, plan.supports);
1042
+ }
1043
+ if (plan.options.planOnly) {
1044
+ printInitPlanPreview(plan);
1045
+ return {
1046
+ plan,
1047
+ created: buildPlanOnlyScaffoldResult(plan.scaffold),
1048
+ stageResults: plan.stages.map((stage) => ({ name: stage.name, disposition: "skipped" })),
1049
+ finalSupports: plan.supports
926
1050
  };
927
- logger(`init target source: ${resolution.source}`);
928
- for (const step of resolution.chain) {
929
- logger(step);
930
- }
931
- if (options.force) {
932
- writeStderr(t("cli.init.force.warning", { path: target }));
1051
+ }
1052
+ let created = null;
1053
+ const stageResults = [];
1054
+ let finalSupports = plan.supports;
1055
+ for (const step of plan.steps) {
1056
+ switch (step.name) {
1057
+ case "preflight":
1058
+ break;
1059
+ case "scaffold":
1060
+ created = executeInitFabricPlan(plan.scaffold);
1061
+ printInitScaffoldResult(created);
1062
+ break;
1063
+ case "bootstrap":
1064
+ case "mcp":
1065
+ case "hooks":
1066
+ stageResults.push(await executeInitStagePlan(plan, step.name));
1067
+ break;
1068
+ case "post-setup":
1069
+ finalSupports = detectClientSupports(plan.target);
1070
+ printInitPostSetup(plan, stageResults, finalSupports);
1071
+ break;
1072
+ default:
1073
+ exhaustiveInitExecutionStep(step);
933
1074
  }
934
- const created = initFabric(target, options);
935
- console.log(formatInitPathAction(created.agentsPath, created.agentsAction));
936
- console.log(formatInitPathAction(created.metaPath, created.metaAction));
937
- console.log(formatInitPathAction(created.humanLockPath, created.humanLockAction));
938
- console.log(formatInitPathAction(created.forensicPath, created.forensicAction));
939
- writeStderr(
940
- formatOptionalInitPathAction(created.claudeSkillPath, created.claudeSkillAction)
941
- );
942
- writeStderr(
943
- formatOptionalInitPathAction(created.claudeHookPath, created.claudeHookAction)
1075
+ }
1076
+ return {
1077
+ plan,
1078
+ created: created ?? unreachableInitScaffold(),
1079
+ stageResults,
1080
+ finalSupports
1081
+ };
1082
+ }
1083
+ function buildInitFabricPlan(target, options) {
1084
+ assertExistingDirectory2(target);
1085
+ const fabricDir = join2(target, ".fabric");
1086
+ const bootstrapPath = join2(fabricDir, "bootstrap", "README.md");
1087
+ const forensicPath = join2(fabricDir, "forensic.json");
1088
+ const claudeSkillPath = join2(target, ".claude", "skills", "agents-md-init", "SKILL.md");
1089
+ const codexSkillPath = join2(target, ".agents", "skills", "fabric-init", "SKILL.md");
1090
+ const codexSessionStartHookPath = join2(target, ".codex", "hooks", "fabric-session-start.cjs");
1091
+ const codexStopHookPath = join2(target, ".codex", "hooks", "fabric-stop-reminder.cjs");
1092
+ const codexHooksConfigPath = join2(target, ".codex", "hooks.json");
1093
+ const claudeHookPath = join2(target, ".claude", "hooks", "agents-md-init-reminder.cjs");
1094
+ const claudeSettingsPath = join2(target, ".claude", "settings.json");
1095
+ const metaPath = join2(fabricDir, "agents.meta.json");
1096
+ const humanLockPath = join2(fabricDir, "human-lock.json");
1097
+ const replaceFabricDir = shouldReplaceWritableDirectory(fabricDir, options);
1098
+ const bootstrapAction = planFreshPath(bootstrapPath, options);
1099
+ const metaAction = planFreshPath(metaPath, options);
1100
+ const humanLockAction = planFreshPath(humanLockPath, options);
1101
+ const forensicAction = planFreshPath(forensicPath, options);
1102
+ const forensicReport = buildForensicReport(target);
1103
+ const humanLockTemplate = readFileSync2(findTemplatePath("templates/fabric/human-lock.json"), "utf8");
1104
+ const bootstrapContent = buildFabricBootstrapGuide(target);
1105
+ const bootstrapHash = sha256(bootstrapContent);
1106
+ const meta = createInitialMeta(bootstrapHash);
1107
+ return {
1108
+ target,
1109
+ options,
1110
+ fabricDir,
1111
+ replaceFabricDir,
1112
+ bootstrapPath,
1113
+ bootstrapAction,
1114
+ bootstrapContent,
1115
+ metaPath,
1116
+ metaAction,
1117
+ meta,
1118
+ humanLockPath,
1119
+ humanLockAction,
1120
+ humanLockContent: humanLockTemplate.endsWith("\n") ? humanLockTemplate : `${humanLockTemplate}
1121
+ `,
1122
+ forensicPath,
1123
+ forensicAction,
1124
+ forensicReport,
1125
+ claudeSkill: buildOptionalTemplateWritePlan(claudeSkillPath, findTemplatePath(CLAUDE_INIT_SKILL_TEMPLATE), options),
1126
+ codexSkill: buildOptionalTemplateWritePlan(codexSkillPath, findTemplatePath(CODEX_INIT_SKILL_TEMPLATE), options),
1127
+ codexSessionStartHook: buildOptionalTemplateWritePlan(
1128
+ codexSessionStartHookPath,
1129
+ findTemplatePath(CODEX_SESSION_START_HOOK_TEMPLATE),
1130
+ options,
1131
+ true
1132
+ ),
1133
+ codexStopHook: buildOptionalTemplateWritePlan(
1134
+ codexStopHookPath,
1135
+ findTemplatePath(CODEX_STOP_HOOK_TEMPLATE),
1136
+ options,
1137
+ true
1138
+ ),
1139
+ codexHooksConfig: buildCodexHooksConfigPlan(codexHooksConfigPath, options),
1140
+ claudeHook: buildOptionalTemplateWritePlan(
1141
+ claudeHookPath,
1142
+ findTemplatePath(CLAUDE_INIT_REMINDER_HOOK_TEMPLATE),
1143
+ options,
1144
+ true
1145
+ ),
1146
+ claudeSettings: buildClaudeSettingsWritePlan(claudeSettingsPath, options)
1147
+ };
1148
+ }
1149
+ function executeInitFabricPlan(plan) {
1150
+ if (plan.replaceFabricDir) {
1151
+ rmSync(plan.fabricDir, { force: true });
1152
+ }
1153
+ mkdirSync(plan.fabricDir, { recursive: true });
1154
+ mkdirSync(dirname(plan.bootstrapPath), { recursive: true });
1155
+ preparePlannedPath(plan.bootstrapPath, plan.bootstrapAction);
1156
+ writeFileSync(plan.bootstrapPath, plan.bootstrapContent, "utf8");
1157
+ preparePlannedPath(plan.metaPath, plan.metaAction);
1158
+ writeFileSync(plan.metaPath, `${JSON.stringify(plan.meta, null, 2)}
1159
+ `, "utf8");
1160
+ preparePlannedPath(plan.humanLockPath, plan.humanLockAction);
1161
+ writeFileSync(plan.humanLockPath, plan.humanLockContent, "utf8");
1162
+ preparePlannedPath(plan.forensicPath, plan.forensicAction);
1163
+ writeFileSync(plan.forensicPath, `${JSON.stringify(plan.forensicReport, null, 2)}
1164
+ `, "utf8");
1165
+ applyOptionalTemplateWritePlan(plan.claudeSkill);
1166
+ applyOptionalTemplateWritePlan(plan.codexSkill);
1167
+ applyOptionalTemplateWritePlan(plan.codexSessionStartHook);
1168
+ applyOptionalTemplateWritePlan(plan.codexStopHook);
1169
+ applyJsonWritePlan(plan.codexHooksConfig);
1170
+ applyOptionalTemplateWritePlan(plan.claudeHook);
1171
+ applyClaudeSettingsWritePlan(plan.claudeSettings);
1172
+ return {
1173
+ bootstrapPath: plan.bootstrapPath,
1174
+ bootstrapAction: plan.bootstrapAction,
1175
+ metaPath: plan.metaPath,
1176
+ metaAction: plan.metaAction,
1177
+ humanLockPath: plan.humanLockPath,
1178
+ humanLockAction: plan.humanLockAction,
1179
+ forensicPath: plan.forensicPath,
1180
+ forensicAction: plan.forensicAction,
1181
+ claudeSkillPath: plan.claudeSkill.path,
1182
+ claudeSkillAction: plan.claudeSkill.action,
1183
+ codexSkillPath: plan.codexSkill.path,
1184
+ codexSkillAction: plan.codexSkill.action,
1185
+ codexSessionStartHookPath: plan.codexSessionStartHook.path,
1186
+ codexSessionStartHookAction: plan.codexSessionStartHook.action,
1187
+ codexStopHookPath: plan.codexStopHook.path,
1188
+ codexStopHookAction: plan.codexStopHook.action,
1189
+ codexHooksConfigPath: plan.codexHooksConfig.path,
1190
+ codexHooksConfigAction: plan.codexHooksConfig.action,
1191
+ claudeHookPath: plan.claudeHook.path,
1192
+ claudeHookAction: plan.claudeHook.action,
1193
+ claudeSettingsPath: plan.claudeSettings.path,
1194
+ claudeSettingsAction: plan.claudeSettings.action
1195
+ };
1196
+ }
1197
+ function initFabric(target, options) {
1198
+ return executeInitFabricPlan(buildInitFabricPlan(target, options));
1199
+ }
1200
+ function shouldUseInitWizard(args, terminalInteractive = isInteractiveInit()) {
1201
+ return terminalInteractive && args.interactive !== false && args.yes !== true;
1202
+ }
1203
+ async function resolveInitExecutionPlanWithWizard(basePlan, args, wizardAdapter) {
1204
+ const selection = await wizardAdapter.run({
1205
+ target: basePlan.target,
1206
+ options: basePlan.options,
1207
+ supports: basePlan.supports,
1208
+ mcpInstallMode: basePlan.mcpInstallMode,
1209
+ lockedStages: collectLockedWizardStages(args)
1210
+ });
1211
+ if (selection === null) {
1212
+ return null;
1213
+ }
1214
+ return buildInitExecutionPlan({
1215
+ target: basePlan.target,
1216
+ options: {
1217
+ ...basePlan.options,
1218
+ skipBootstrap: !selection.bootstrap,
1219
+ skipMcp: !selection.mcp,
1220
+ skipHooks: !selection.hooks
1221
+ },
1222
+ mcpInstallMode: selection.mcp ? selection.mcpInstallMode : basePlan.mcpInstallMode,
1223
+ interactive: false,
1224
+ supports: basePlan.supports
1225
+ });
1226
+ }
1227
+ function unreachableInitScaffold() {
1228
+ throw new Error("Init scaffold step did not execute");
1229
+ }
1230
+ function exhaustiveInitExecutionStep(value) {
1231
+ throw new Error(`Unsupported init execution step: ${JSON.stringify(value)}`);
1232
+ }
1233
+ function exhaustiveInitStagePlan(value) {
1234
+ throw new Error(`Unsupported init stage plan: ${JSON.stringify(value)}`);
1235
+ }
1236
+ function printInitScaffoldResult(created) {
1237
+ console.log(formatInitPathAction(created.bootstrapPath, created.bootstrapAction));
1238
+ console.log(formatInitPathAction(created.metaPath, created.metaAction));
1239
+ console.log(formatInitPathAction(created.humanLockPath, created.humanLockAction));
1240
+ console.log(formatInitPathAction(created.forensicPath, created.forensicAction));
1241
+ writeStderr(formatOptionalInitPathAction(created.claudeSkillPath, created.claudeSkillAction));
1242
+ writeStderr(formatOptionalInitPathAction(created.codexSkillPath, created.codexSkillAction));
1243
+ writeStderr(
1244
+ formatOptionalInitPathAction(created.codexSessionStartHookPath, created.codexSessionStartHookAction)
1245
+ );
1246
+ writeStderr(
1247
+ formatOptionalInitPathAction(created.codexStopHookPath, created.codexStopHookAction)
1248
+ );
1249
+ writeStderr(formatCodexHooksAction(created.codexHooksConfigPath, created.codexHooksConfigAction));
1250
+ writeStderr(formatOptionalInitPathAction(created.claudeHookPath, created.claudeHookAction));
1251
+ writeStderr(formatClaudeSettingsAction(created.claudeSettingsPath, created.claudeSettingsAction));
1252
+ }
1253
+ function printInitPostSetup(plan, stageResults, finalSupports) {
1254
+ if (shouldPrintHooksNextStep(plan.options, stageResults)) {
1255
+ console.log(
1256
+ t("cli.init.next-step", {
1257
+ label: nextLabel(),
1258
+ message: paint.muted(t("cli.init.next-step.message"))
1259
+ })
944
1260
  );
945
- writeStderr(formatClaudeSettingsAction(created.claudeSettingsPath, created.claudeSettingsAction));
946
- const stageResults = [];
947
- if (options.skipBootstrap) {
948
- stageResults.push({ name: "bootstrap", disposition: "skipped" });
949
- } else {
950
- console.log(formatInitStageHeader(t("cli.init.stages.bootstrap")));
951
- try {
952
- const result = await installBootstrap(target, { force: options.force });
1261
+ }
1262
+ console.log(
1263
+ t("cli.init.reason-message", {
1264
+ label: reasonLabel(),
1265
+ message: paint.muted(formatInitReasonMessage(finalSupports))
1266
+ })
1267
+ );
1268
+ printInitStageSummary(stageResults);
1269
+ printInitCapabilitySummary(finalSupports, stageResults, plan.options);
1270
+ }
1271
+ function printInitPlanPreview(plan) {
1272
+ console.log(t("cli.init.plan.preview-title"));
1273
+ printInitPlanSummary(plan.target, plan.options, plan.mcpInstallMode, plan.supports);
1274
+ console.log(
1275
+ t("cli.init.plan.preview-result", {
1276
+ mode: plan.options.reapply ? t("cli.init.mode.reapply") : t("cli.init.mode.default"),
1277
+ bootstrap: yesNoLabel(!plan.options.skipBootstrap),
1278
+ mcp: yesNoLabel(!plan.options.skipMcp),
1279
+ hooks: yesNoLabel(!plan.options.skipHooks)
1280
+ })
1281
+ );
1282
+ }
1283
+ function buildPlanOnlyScaffoldResult(plan) {
1284
+ return {
1285
+ bootstrapPath: plan.bootstrapPath,
1286
+ bootstrapAction: plan.bootstrapAction,
1287
+ metaPath: plan.metaPath,
1288
+ metaAction: plan.metaAction,
1289
+ humanLockPath: plan.humanLockPath,
1290
+ humanLockAction: plan.humanLockAction,
1291
+ forensicPath: plan.forensicPath,
1292
+ forensicAction: plan.forensicAction,
1293
+ claudeSkillPath: plan.claudeSkill.path,
1294
+ claudeSkillAction: plan.claudeSkill.action,
1295
+ codexSkillPath: plan.codexSkill.path,
1296
+ codexSkillAction: plan.codexSkill.action,
1297
+ codexSessionStartHookPath: plan.codexSessionStartHook.path,
1298
+ codexSessionStartHookAction: plan.codexSessionStartHook.action,
1299
+ codexStopHookPath: plan.codexStopHook.path,
1300
+ codexStopHookAction: plan.codexStopHook.action,
1301
+ codexHooksConfigPath: plan.codexHooksConfig.path,
1302
+ codexHooksConfigAction: plan.codexHooksConfig.action,
1303
+ claudeHookPath: plan.claudeHook.path,
1304
+ claudeHookAction: plan.claudeHook.action,
1305
+ claudeSettingsPath: plan.claudeSettings.path,
1306
+ claudeSettingsAction: plan.claudeSettings.action
1307
+ };
1308
+ }
1309
+ async function executeInitStagePlan(plan, stageName) {
1310
+ const stage = plan.stages.find((entry) => entry.name === stageName);
1311
+ if (stage === void 0) {
1312
+ throw new Error(`Missing init stage plan: ${stageName}`);
1313
+ }
1314
+ if (stage.skipped) {
1315
+ return { name: stageName, disposition: "skipped" };
1316
+ }
1317
+ console.log(formatInitStageHeader(t(`cli.init.stages.${stageName}`)));
1318
+ try {
1319
+ switch (stage.name) {
1320
+ case "bootstrap": {
1321
+ const result = await installBootstrap(plan.target, { force: plan.options.force });
953
1322
  if (result.details.length === 0) {
954
1323
  console.log(formatInitStageResult("bootstrap", "skipped", 0, 0, t("cli.bootstrap.install.no-targets")));
955
- stageResults.push({ name: "bootstrap", disposition: "skipped" });
956
- } else {
957
- console.log(
958
- formatInitStageResult("bootstrap", "completed", result.installed.length, result.skipped.length)
959
- );
960
- stageResults.push({ name: "bootstrap", disposition: "ran" });
1324
+ return { name: "bootstrap", disposition: "skipped" };
961
1325
  }
962
- } catch (error) {
963
- writeStderr(formatInitStageFailure("bootstrap", error));
964
- stageResults.push({ name: "bootstrap", disposition: "failed" });
1326
+ console.log(
1327
+ formatInitStageResult("bootstrap", "completed", result.installed.length, result.skipped.length)
1328
+ );
1329
+ return { name: "bootstrap", disposition: "ran" };
965
1330
  }
966
- }
967
- if (options.skipMcp) {
968
- stageResults.push({ name: "mcp", disposition: "skipped" });
969
- } else {
970
- console.log(formatInitStageHeader(t("cli.init.stages.mcp")));
971
- try {
972
- let localServerPath;
973
- if (mcpInstallMode === "local") {
974
- const manager = detectPackageManager(target);
1331
+ case "mcp": {
1332
+ if (stage.installMode === "local") {
1333
+ const manager = stage.packageManager ?? detectPackageManager(plan.target);
975
1334
  writeStderr(t("cli.init.mcp.install.local"));
976
1335
  writeStderr(t("cli.init.mcp.local.installing", { manager }));
977
- installLocalFabricServer(target, manager);
1336
+ installLocalFabricServer(plan.target, manager);
978
1337
  writeStderr(t("cli.init.mcp.local.installed"));
979
- localServerPath = LOCAL_FABRIC_SERVER_PATH;
980
1338
  } else {
981
1339
  writeStderr(t("cli.init.mcp.install.global"));
982
1340
  }
983
- const result = await installMcpClients(target, {
984
- force: options.force,
985
- localServerPath
1341
+ const result = await installMcpClients(plan.target, {
1342
+ force: plan.options.force,
1343
+ localServerPath: stage.localServerPath
986
1344
  });
987
1345
  if (result.details.length === 0) {
988
1346
  console.log(formatInitStageResult("mcp", "skipped", 0, 0, t("cli.config.install.no-configs")));
989
- stageResults.push({ name: "mcp", disposition: "skipped" });
990
- } else {
991
- console.log(formatInitStageResult("mcp", "completed", result.installed.length, result.skipped.length));
992
- stageResults.push({ name: "mcp", disposition: "ran" });
1347
+ return { name: "mcp", disposition: "skipped" };
993
1348
  }
994
- } catch (error) {
995
- writeStderr(formatInitStageFailure("mcp", error));
996
- stageResults.push({ name: "mcp", disposition: "failed" });
1349
+ console.log(formatInitStageResult("mcp", "completed", result.installed.length, result.skipped.length));
1350
+ return { name: "mcp", disposition: "ran" };
997
1351
  }
998
- }
999
- if (options.skipHooks) {
1000
- stageResults.push({ name: "hooks", disposition: "skipped" });
1001
- } else {
1002
- console.log(formatInitStageHeader(t("cli.init.stages.hooks")));
1003
- try {
1004
- const result = await installHooks(target, { force: options.force });
1352
+ case "hooks": {
1353
+ const result = await installHooks(plan.target, { force: plan.options.force });
1005
1354
  console.log(formatInitStageResult("hooks", "completed", result.installed.length, result.skipped.length));
1006
- stageResults.push({ name: "hooks", disposition: "ran" });
1007
- } catch (error) {
1008
- writeStderr(formatInitStageFailure("hooks", error));
1009
- stageResults.push({ name: "hooks", disposition: "failed" });
1355
+ return { name: "hooks", disposition: "ran" };
1010
1356
  }
1357
+ default:
1358
+ return exhaustiveInitStagePlan(stage);
1011
1359
  }
1012
- if (shouldPrintHooksNextStep(options, stageResults)) {
1013
- console.log(
1014
- t("cli.init.next-step", {
1015
- label: nextLabel(),
1016
- message: paint.muted(t("cli.init.next-step.message"))
1017
- })
1018
- );
1360
+ } catch (error) {
1361
+ writeStderr(formatInitStageFailure(stageName, error));
1362
+ return { name: stageName, disposition: "failed" };
1363
+ }
1364
+ }
1365
+ function shouldReplaceWritableDirectory(path, options) {
1366
+ if (!existsSync2(path)) {
1367
+ return false;
1368
+ }
1369
+ if (statSync2(path).isDirectory()) {
1370
+ return false;
1371
+ }
1372
+ if (!options?.force) {
1373
+ throw new Error(t("cli.init.errors.abort-existing", { path }));
1374
+ }
1375
+ return true;
1376
+ }
1377
+ function planFreshPath(path, options) {
1378
+ if (!existsSync2(path)) {
1379
+ return "created";
1380
+ }
1381
+ if (!options?.force) {
1382
+ throw new Error(t("cli.init.errors.abort-existing", { path }));
1383
+ }
1384
+ return "overwritten";
1385
+ }
1386
+ function preparePlannedPath(path, action) {
1387
+ mkdirSync(dirname(path), { recursive: true });
1388
+ if (action === "overwritten" && existsSync2(path)) {
1389
+ rmSync(path, { recursive: true, force: true });
1390
+ }
1391
+ }
1392
+ function buildOptionalTemplateWritePlan(path, templatePath, options, executable = false) {
1393
+ const existed = existsSync2(path);
1394
+ if (existed && !options?.force) {
1395
+ return { path, action: "skipped", templatePath, executable };
1396
+ }
1397
+ return {
1398
+ path,
1399
+ action: existed ? "overwritten" : "created",
1400
+ templatePath,
1401
+ executable
1402
+ };
1403
+ }
1404
+ function applyOptionalTemplateWritePlan(plan) {
1405
+ if (plan.action === "skipped") {
1406
+ return;
1407
+ }
1408
+ mkdirSync(dirname(plan.path), { recursive: true });
1409
+ copyFileSync(plan.templatePath, plan.path);
1410
+ if (plan.executable) {
1411
+ chmodSync(plan.path, 493);
1412
+ }
1413
+ }
1414
+ function buildCodexHooksConfigValue() {
1415
+ return {
1416
+ hooks: {
1417
+ SessionStart: [
1418
+ {
1419
+ matcher: "*",
1420
+ hooks: [{ type: "command", command: CODEX_SESSION_START_COMMAND }]
1421
+ }
1422
+ ],
1423
+ Stop: [
1424
+ {
1425
+ matcher: "*",
1426
+ hooks: [{ type: "command", command: CODEX_STOP_COMMAND }]
1427
+ }
1428
+ ]
1019
1429
  }
1020
- console.log(
1021
- t("cli.init.reason-message", {
1022
- label: reasonLabel(),
1023
- message: paint.muted(t("cli.init.reason-message.body"))
1024
- })
1025
- );
1026
- printInitStageSummary(stageResults);
1430
+ };
1431
+ }
1432
+ function buildCodexHooksConfigPlan(configPath, options) {
1433
+ const action = !existsSync2(configPath) ? "created" : options?.force ? "overwritten" : "skipped";
1434
+ return {
1435
+ path: configPath,
1436
+ action,
1437
+ value: buildCodexHooksConfigValue()
1438
+ };
1439
+ }
1440
+ function applyJsonWritePlan(plan) {
1441
+ if (plan.action === "skipped") {
1442
+ return;
1027
1443
  }
1028
- });
1029
- var init_default = initCommand;
1030
- function initFabric(target, options) {
1031
- assertExistingDirectory2(target);
1032
- const agentsPath = join2(target, "AGENTS.md");
1033
- const fabricDir = join2(target, ".fabric");
1034
- const forensicPath = join2(fabricDir, "forensic.json");
1035
- const claudeSkillPath = join2(target, ".claude", "skills", "agents-md-init", "SKILL.md");
1036
- const claudeHookPath = join2(target, ".claude", "hooks", "agents-md-init-reminder.cjs");
1037
- 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
- const metaPath = join2(fabricDir, "agents.meta.json");
1051
- const humanLockPath = join2(fabricDir, "human-lock.json");
1052
- mkdirSync(fabricDir, { recursive: false });
1053
- writeNewFile(agentsPath, agentsContent, options);
1054
- writeNewFile(metaPath, `${JSON.stringify(meta, null, 2)}
1055
- `, options);
1056
- writeNewFile(humanLockPath, humanLockTemplate.endsWith("\n") ? humanLockTemplate : `${humanLockTemplate}
1057
- `, options);
1058
- writeNewFile(forensicPath, `${JSON.stringify(forensicReport, null, 2)}
1059
- `, options);
1060
- const claudeSkillAction = copyTemplateIfMissing(findTemplatePath(CLAUDE_INIT_SKILL_TEMPLATE), claudeSkillPath, options);
1061
- const claudeHookAction = copyExecutableTemplateIfMissing(
1062
- findTemplatePath(CLAUDE_INIT_REMINDER_HOOK_TEMPLATE),
1063
- claudeHookPath,
1064
- options
1065
- );
1066
- const claudeSettingsAction = mergeClaudeStopHook(claudeSettingsPath, options);
1444
+ mkdirSync(dirname(plan.path), { recursive: true });
1445
+ writeJsonAtomically(plan.path, plan.value);
1446
+ }
1447
+ function buildClaudeSettingsWritePlan(settingsPath, options) {
1448
+ let settings;
1449
+ let action = "updated";
1450
+ if (!existsSync2(settingsPath)) {
1451
+ settings = {};
1452
+ action = "created";
1453
+ } else {
1454
+ try {
1455
+ const parsed = JSON.parse(readFileSync2(settingsPath, "utf8"));
1456
+ if (!isRecord(parsed)) {
1457
+ writeStderr(t("cli.init.claude-settings.invalid-object", { label: skippedLabel(), path: settingsPath }));
1458
+ return { path: settingsPath, action: "skipped-invalid", value: null };
1459
+ }
1460
+ settings = parsed;
1461
+ } catch (error) {
1462
+ const reason = error instanceof Error ? error.message : "unknown parse error";
1463
+ writeStderr(t("cli.init.claude-settings.invalid-json", { label: skippedLabel(), path: settingsPath, reason }));
1464
+ return { path: settingsPath, action: "skipped-invalid", value: null };
1465
+ }
1466
+ }
1467
+ if (settings.hooks !== void 0 && !isRecord(settings.hooks)) {
1468
+ writeStderr(t("cli.init.claude-settings.invalid-hooks", { label: skippedLabel(), path: settingsPath }));
1469
+ return { path: settingsPath, action: "skipped-invalid", value: null };
1470
+ }
1471
+ const hooks = settings.hooks ?? {};
1472
+ const stopHooksValue = hooks.Stop;
1473
+ if (stopHooksValue !== void 0 && !Array.isArray(stopHooksValue)) {
1474
+ writeStderr(t("cli.init.claude-settings.invalid-stop-array", { label: skippedLabel(), path: settingsPath }));
1475
+ return { path: settingsPath, action: "skipped-invalid", value: null };
1476
+ }
1477
+ const stopHooks = Array.isArray(stopHooksValue) ? stopHooksValue : [];
1478
+ const hasExistingFabricHook = hasClaudeInitReminderHook(stopHooks);
1479
+ if (hasExistingFabricHook && !options?.force) {
1480
+ return { path: settingsPath, action: "skipped", value: null };
1481
+ }
1482
+ const nextStopHooks = hasExistingFabricHook && options?.force ? removeClaudeInitReminderHook(stopHooks) : [...stopHooks];
1483
+ nextStopHooks.push({
1484
+ matcher: "*",
1485
+ hooks: [
1486
+ {
1487
+ type: "command",
1488
+ command: CLAUDE_INIT_REMINDER_COMMAND
1489
+ }
1490
+ ]
1491
+ });
1492
+ const nextSettings = {
1493
+ ...settings,
1494
+ hooks: {
1495
+ ...hooks,
1496
+ Stop: nextStopHooks
1497
+ }
1498
+ };
1067
1499
  return {
1068
- agentsPath,
1069
- agentsAction,
1070
- metaPath,
1071
- metaAction: fabricDirAction,
1072
- humanLockPath,
1073
- humanLockAction: fabricDirAction,
1074
- forensicPath,
1075
- forensicAction,
1076
- claudeSkillPath,
1077
- claudeSkillAction,
1078
- claudeHookPath,
1079
- claudeHookAction,
1080
- claudeSettingsPath,
1081
- claudeSettingsAction
1500
+ path: settingsPath,
1501
+ action: hasExistingFabricHook && options?.force ? "overwritten" : action,
1502
+ value: nextSettings
1082
1503
  };
1083
1504
  }
1084
- function findAgentsTemplatePath(frameworkKind) {
1085
- const relativePath = AGENTS_TEMPLATE_BY_FRAMEWORK[frameworkKind] ?? "templates/agents-md/AGENTS.md.template";
1086
- return findTemplatePath(relativePath);
1505
+ function applyClaudeSettingsWritePlan(plan) {
1506
+ if (plan.value === null) {
1507
+ return;
1508
+ }
1509
+ mkdirSync(dirname(plan.path), { recursive: true });
1510
+ writeJsonAtomically(plan.path, plan.value);
1511
+ }
1512
+ function createDefaultInitWizardAdapter() {
1513
+ return {
1514
+ async run(context) {
1515
+ intro(t("cli.init.wizard.intro"));
1516
+ note(
1517
+ t("cli.init.wizard.overview.body", {
1518
+ target: context.target,
1519
+ mode: formatInitModeBadge(context.options)
1520
+ }),
1521
+ t("cli.init.wizard.overview.title")
1522
+ );
1523
+ printInitPlanSummary(context.target, context.options, context.mcpInstallMode, context.supports);
1524
+ log.step(t("cli.init.wizard.step.target"));
1525
+ const continueWithTarget = await confirm({
1526
+ message: t("cli.init.wizard.target.confirm", { target: context.target }),
1527
+ initialValue: true
1528
+ });
1529
+ if (isCancel(continueWithTarget) || !continueWithTarget) {
1530
+ emitInitWizardCancellation();
1531
+ return null;
1532
+ }
1533
+ log.step(t("cli.init.wizard.step.plan"));
1534
+ let groupedSelection;
1535
+ try {
1536
+ groupedSelection = await group(
1537
+ {
1538
+ bootstrap: async () => context.lockedStages.includes("bootstrap") ? false : confirmInGroup({
1539
+ message: t("cli.init.wizard.stage.bootstrap", {
1540
+ defaultValue: formatPromptDefault(!context.options.skipBootstrap)
1541
+ }),
1542
+ initialValue: !context.options.skipBootstrap
1543
+ }),
1544
+ mcp: async () => context.lockedStages.includes("mcp") ? false : confirmInGroup({
1545
+ message: t("cli.init.wizard.stage.mcp", {
1546
+ defaultValue: formatPromptDefault(!context.options.skipMcp)
1547
+ }),
1548
+ initialValue: !context.options.skipMcp
1549
+ }),
1550
+ mcpInstallMode: async ({ results }) => results.mcp ? selectMcpInstallModeInGroup({
1551
+ message: t("cli.init.wizard.mcp-install", { defaultValue: context.mcpInstallMode }),
1552
+ initialValue: context.mcpInstallMode,
1553
+ options: [
1554
+ { value: "global", label: "global", hint: t("cli.init.mcp.install.global") },
1555
+ { value: "local", label: "local", hint: t("cli.init.mcp.install.local") }
1556
+ ]
1557
+ }) : context.mcpInstallMode,
1558
+ hooks: async () => context.lockedStages.includes("hooks") ? false : confirmInGroup({
1559
+ message: t("cli.init.wizard.stage.hooks", {
1560
+ defaultValue: formatPromptDefault(!context.options.skipHooks)
1561
+ }),
1562
+ initialValue: !context.options.skipHooks
1563
+ })
1564
+ },
1565
+ {
1566
+ onCancel() {
1567
+ throw INIT_WIZARD_GROUP_CANCELLED;
1568
+ }
1569
+ }
1570
+ );
1571
+ } catch (error) {
1572
+ if (error === INIT_WIZARD_GROUP_CANCELLED) {
1573
+ emitInitWizardCancellation();
1574
+ return null;
1575
+ }
1576
+ throw error;
1577
+ }
1578
+ if (groupedSelection === null) {
1579
+ emitInitWizardCancellation();
1580
+ return null;
1581
+ }
1582
+ const previewOptions = {
1583
+ ...context.options,
1584
+ skipBootstrap: !groupedSelection.bootstrap,
1585
+ skipMcp: !groupedSelection.mcp,
1586
+ skipHooks: !groupedSelection.hooks
1587
+ };
1588
+ log.step(t("cli.init.wizard.step.review"));
1589
+ printInitPlanSummary(context.target, previewOptions, groupedSelection.mcpInstallMode, context.supports);
1590
+ const confirmed = await confirm({
1591
+ message: t("cli.init.wizard.execute.confirm"),
1592
+ initialValue: true
1593
+ });
1594
+ if (isCancel(confirmed) || !confirmed) {
1595
+ emitInitWizardCancellation();
1596
+ return null;
1597
+ }
1598
+ outro(t("cli.init.wizard.outro"));
1599
+ return groupedSelection;
1600
+ }
1601
+ };
1602
+ }
1603
+ function emitInitWizardCancellation() {
1604
+ cancel(t("cli.init.wizard.cancelled"));
1605
+ }
1606
+ async function confirmInGroup(options) {
1607
+ const result = await confirm(options);
1608
+ if (isCancel(result)) {
1609
+ throw INIT_WIZARD_GROUP_CANCELLED;
1610
+ }
1611
+ return result;
1612
+ }
1613
+ async function selectMcpInstallModeInGroup(options) {
1614
+ const result = await select({
1615
+ message: options.message,
1616
+ initialValue: options.initialValue,
1617
+ options: options.options
1618
+ });
1619
+ if (isCancel(result)) {
1620
+ throw INIT_WIZARD_GROUP_CANCELLED;
1621
+ }
1622
+ return result;
1623
+ }
1624
+ function collectLockedWizardStages(args) {
1625
+ const lockedStages = [];
1626
+ if (args.bootstrap === false) {
1627
+ lockedStages.push("bootstrap");
1628
+ }
1629
+ if (args.mcp === false) {
1630
+ lockedStages.push("mcp");
1631
+ }
1632
+ if (args.hooks === false) {
1633
+ lockedStages.push("hooks");
1634
+ }
1635
+ return lockedStages;
1636
+ }
1637
+ function formatPromptDefault(value) {
1638
+ return value ? "Y/n" : "y/N";
1639
+ }
1640
+ function formatInitModeBanner(options) {
1641
+ if (options.planOnly && options.reapply) {
1642
+ return t("cli.init.plan.mode-banner.plan-reapply");
1643
+ }
1644
+ if (options.planOnly) {
1645
+ return t("cli.init.plan.mode-banner.plan");
1646
+ }
1647
+ if (options.reapply) {
1648
+ return t("cli.init.plan.mode-banner.reapply");
1649
+ }
1650
+ return t("cli.init.plan.mode-banner.default");
1651
+ }
1652
+ function formatInitModeBadge(options) {
1653
+ if (options.planOnly && options.reapply) {
1654
+ return t("cli.init.mode.badge.plan-reapply");
1655
+ }
1656
+ if (options.planOnly) {
1657
+ return t("cli.init.mode.badge.plan");
1658
+ }
1659
+ if (options.reapply) {
1660
+ return t("cli.init.mode.badge.reapply");
1661
+ }
1662
+ return t("cli.init.mode.badge.default");
1087
1663
  }
1088
1664
  function normalizeTarget2(targetInput) {
1089
1665
  return isAbsolute2(targetInput) ? targetInput : resolve2(process.cwd(), targetInput);
@@ -1126,7 +1702,7 @@ function createInitialMeta(agentsHash) {
1126
1702
  revision: sha256(agentsHash),
1127
1703
  nodes: {
1128
1704
  L0: {
1129
- file: "AGENTS.md",
1705
+ file: ".fabric/bootstrap/README.md",
1130
1706
  scope_glob: "**",
1131
1707
  deps: [],
1132
1708
  priority: "high",
@@ -1137,18 +1713,6 @@ function createInitialMeta(agentsHash) {
1137
1713
  }
1138
1714
  };
1139
1715
  }
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
1716
  function findTemplatePath(relativePath) {
1153
1717
  const currentModuleDir = dirname(fileURLToPath(import.meta.url));
1154
1718
  const candidates = [
@@ -1175,93 +1739,6 @@ function templateCandidatesFrom(start, relativePath) {
1175
1739
  }
1176
1740
  return candidates.reverse();
1177
1741
  }
1178
- function prepareFreshPath(path, options) {
1179
- if (!existsSync2(path)) {
1180
- return "created";
1181
- }
1182
- if (!options?.force) {
1183
- throw new Error(t("cli.init.errors.abort-existing", { path }));
1184
- }
1185
- rmSync(path, { recursive: true, force: true });
1186
- return "overwritten";
1187
- }
1188
- function writeNewFile(path, content, options) {
1189
- const existed = existsSync2(path);
1190
- if (existed && !options?.force) {
1191
- throw new Error(t("cli.init.errors.abort-existing", { path }));
1192
- }
1193
- writeFileSync(path, content, "utf8");
1194
- return existed ? "overwritten" : "created";
1195
- }
1196
- function copyTemplateIfMissing(templatePath, targetPath, options) {
1197
- mkdirSync(dirname(targetPath), { recursive: true });
1198
- const existed = existsSync2(targetPath);
1199
- if (existed && !options?.force) {
1200
- return "skipped";
1201
- }
1202
- copyFileSync(templatePath, targetPath);
1203
- return existed ? "overwritten" : "created";
1204
- }
1205
- function copyExecutableTemplateIfMissing(templatePath, targetPath, options) {
1206
- const action = copyTemplateIfMissing(templatePath, targetPath, options);
1207
- if (action !== "skipped") {
1208
- chmodSync(targetPath, 493);
1209
- }
1210
- return action;
1211
- }
1212
- function mergeClaudeStopHook(settingsPath, options) {
1213
- mkdirSync(dirname(settingsPath), { recursive: true });
1214
- let settings;
1215
- let action = "updated";
1216
- if (!existsSync2(settingsPath)) {
1217
- settings = {};
1218
- action = "created";
1219
- } else {
1220
- try {
1221
- const parsed = JSON.parse(readFileSync2(settingsPath, "utf8"));
1222
- if (!isRecord(parsed)) {
1223
- writeStderr(t("cli.init.claude-settings.invalid-object", { label: skippedLabel(), path: settingsPath }));
1224
- return "skipped-invalid";
1225
- }
1226
- settings = parsed;
1227
- } catch (error) {
1228
- const reason = error instanceof Error ? error.message : "unknown parse error";
1229
- writeStderr(t("cli.init.claude-settings.invalid-json", { label: skippedLabel(), path: settingsPath, reason }));
1230
- return "skipped-invalid";
1231
- }
1232
- }
1233
- if (settings.hooks !== void 0 && !isRecord(settings.hooks)) {
1234
- writeStderr(t("cli.init.claude-settings.invalid-hooks", { label: skippedLabel(), path: settingsPath }));
1235
- return "skipped-invalid";
1236
- }
1237
- const hooks = settings.hooks ?? {};
1238
- const stopHooksValue = hooks.Stop;
1239
- if (stopHooksValue !== void 0 && !Array.isArray(stopHooksValue)) {
1240
- writeStderr(t("cli.init.claude-settings.invalid-stop-array", { label: skippedLabel(), path: settingsPath }));
1241
- return "skipped-invalid";
1242
- }
1243
- const stopHooks = Array.isArray(stopHooksValue) ? stopHooksValue : [];
1244
- const hasExistingFabricHook = hasClaudeInitReminderHook(stopHooks);
1245
- if (hasExistingFabricHook && !options?.force) {
1246
- return "skipped";
1247
- }
1248
- const nextStopHooks = hasExistingFabricHook && options?.force ? removeClaudeInitReminderHook(stopHooks) : [...stopHooks];
1249
- nextStopHooks.push({
1250
- matcher: "*",
1251
- hooks: [
1252
- {
1253
- type: "command",
1254
- command: CLAUDE_INIT_REMINDER_COMMAND
1255
- }
1256
- ]
1257
- });
1258
- settings.hooks = {
1259
- ...hooks,
1260
- Stop: nextStopHooks
1261
- };
1262
- writeJsonAtomically(settingsPath, settings);
1263
- return hasExistingFabricHook && options?.force ? "overwritten" : action;
1264
- }
1265
1742
  function hasClaudeInitReminderHook(stopHooks) {
1266
1743
  return stopHooks.some((entry) => isClaudeInitReminderStopEntry(entry));
1267
1744
  }
@@ -1304,10 +1781,10 @@ function formatClaudeSettingsAction(settingsPath, action) {
1304
1781
  function formatInitStageHeader(message) {
1305
1782
  return `${nextLabel()} ${paint.muted(message)}`;
1306
1783
  }
1307
- function formatInitStageResult(stage, status, installedCount, skippedCount, note) {
1784
+ function formatInitStageResult(stage, status, installedCount, skippedCount, note2) {
1308
1785
  const label = status === "completed" ? completedStageLabel() : skippedStageLabel();
1309
1786
  const counts = `installed=${installedCount} skipped=${skippedCount}`;
1310
- const suffix = note ? ` ${paint.muted(`(${note})`)}` : "";
1787
+ const suffix = note2 ? ` ${paint.muted(`(${note2})`)}` : "";
1311
1788
  return `${label} ${stage}: ${counts}${suffix}`;
1312
1789
  }
1313
1790
  function formatInitStageFailure(stage, error) {
@@ -1329,6 +1806,155 @@ function collectInitStageNames(stageResults, disposition) {
1329
1806
  function shouldPrintHooksNextStep(options, stageResults) {
1330
1807
  return Boolean(options.skipHooks) || stageResults.some((stage) => stage.name === "hooks" && stage.disposition === "failed");
1331
1808
  }
1809
+ function isInteractiveInit() {
1810
+ return Boolean(process.stdin.isTTY) && Boolean(process.stdout.isTTY) && Boolean(process.stderr.isTTY);
1811
+ }
1812
+ function printInitPlanSummary(target, options, mcpInstallMode, supports) {
1813
+ console.log(t("cli.init.plan.title"));
1814
+ console.log(formatInitModeBanner(options));
1815
+ console.log(t("cli.init.plan.target", { target }));
1816
+ console.log(
1817
+ t("cli.init.plan.actions", {
1818
+ bootstrap: yesNoLabel(!options.skipBootstrap),
1819
+ mcp: yesNoLabel(!options.skipMcp),
1820
+ hooks: yesNoLabel(!options.skipHooks),
1821
+ mcpInstall: mcpInstallMode
1822
+ })
1823
+ );
1824
+ const detected = supports.filter((support) => support.detected);
1825
+ console.log(
1826
+ t("cli.init.plan.detected", {
1827
+ clients: detected.length > 0 ? detected.map((support) => support.label).join(", ") : t("cli.shared.none")
1828
+ })
1829
+ );
1830
+ console.log(t("cli.init.plan.writes"));
1831
+ console.log(` - ${target}/.fabric/bootstrap/README.md`);
1832
+ console.log(` - ${target}/.fabric/agents.meta.json`);
1833
+ console.log(` - ${target}/.fabric/human-lock.json`);
1834
+ console.log(` - ${target}/.fabric/forensic.json`);
1835
+ }
1836
+ function printInitCapabilitySummary(supports, stageResults, options) {
1837
+ const detected = supports.filter((support) => support.detected);
1838
+ if (detected.length === 0) {
1839
+ console.log(t("cli.init.capabilities.none"));
1840
+ return;
1841
+ }
1842
+ console.log(t("cli.init.capabilities.title"));
1843
+ const rows = detected.map((support) => toCapabilityRow(support, stageResults, options));
1844
+ const headers = {
1845
+ client: t("cli.init.capabilities.header.client"),
1846
+ bootstrap: t("cli.init.capabilities.header.bootstrap"),
1847
+ mcp: t("cli.init.capabilities.header.mcp"),
1848
+ hook: t("cli.init.capabilities.header.hook"),
1849
+ skill: t("cli.init.capabilities.header.skill"),
1850
+ followUp: t("cli.init.capabilities.header.follow-up")
1851
+ };
1852
+ const widths = {
1853
+ client: Math.max(displayWidth(headers.client), ...rows.map((row) => displayWidth(row.client))),
1854
+ bootstrap: Math.max(displayWidth(headers.bootstrap), ...rows.map((row) => displayWidth(row.bootstrap))),
1855
+ mcp: Math.max(displayWidth(headers.mcp), ...rows.map((row) => displayWidth(row.mcp))),
1856
+ hook: Math.max(displayWidth(headers.hook), ...rows.map((row) => displayWidth(row.hook))),
1857
+ skill: Math.max(displayWidth(headers.skill), ...rows.map((row) => displayWidth(row.skill))),
1858
+ followUp: Math.max(displayWidth(headers.followUp), ...rows.map((row) => displayWidth(row.followUp)))
1859
+ };
1860
+ console.log(formatCapabilityTableRow(headers, widths));
1861
+ console.log(formatCapabilityDivider(widths));
1862
+ for (const row of rows) {
1863
+ console.log(formatCapabilityTableRow(row, widths));
1864
+ }
1865
+ }
1866
+ function formatCodexHooksAction(configPath, action) {
1867
+ switch (action) {
1868
+ case "created":
1869
+ return t("cli.init.codex-hooks.created", { label: createdLabel(), path: configPath });
1870
+ case "overwritten":
1871
+ return t("cli.init.codex-hooks.updated", { label: overwrittenLabel(), path: configPath });
1872
+ case "skipped":
1873
+ return t("cli.init.codex-hooks.skipped", { label: skippedLabel(), path: configPath });
1874
+ default:
1875
+ return t("cli.init.codex-hooks.updated", { label: updatedLabel(), path: configPath });
1876
+ }
1877
+ }
1878
+ function toCapabilityRow(support, stageResults, options) {
1879
+ const stage = (name) => stageResults.find((entry) => entry.name === name)?.disposition ?? null;
1880
+ const bootstrap = support.capabilities.bootstrap ? capabilityStatus(options.skipBootstrap ? "skipped" : stage("bootstrap")) : t("cli.init.capabilities.status.na");
1881
+ const mcp = support.capabilities.mcp ? capabilityStatus(options.skipMcp ? "skipped" : stage("mcp")) : t("cli.init.capabilities.status.na");
1882
+ const hook = capabilityInstallStatus(support, "hook");
1883
+ const skill = capabilityInstallStatus(support, "skill");
1884
+ return {
1885
+ client: support.label,
1886
+ bootstrap,
1887
+ mcp,
1888
+ hook,
1889
+ skill,
1890
+ followUp: hasInstalledCapability(support, "skill") ? t("cli.init.capabilities.follow-up.ready") : support.capabilities.skill ? t("cli.init.capabilities.follow-up.install") : t("cli.init.capabilities.follow-up.manual")
1891
+ };
1892
+ }
1893
+ function capabilityInstallStatus(support, capability) {
1894
+ if (!support.capabilities[capability]) {
1895
+ return t("cli.init.capabilities.status.na");
1896
+ }
1897
+ return hasInstalledCapability(support, capability) ? t("cli.init.capabilities.status.installed") : t("cli.init.capabilities.status.supported");
1898
+ }
1899
+ function hasInstalledCapability(support, capability) {
1900
+ return support.installedCapabilities?.[capability] === true;
1901
+ }
1902
+ function capabilityStatus(disposition) {
1903
+ switch (disposition) {
1904
+ case "ran":
1905
+ return t("cli.init.capabilities.status.ready");
1906
+ case "skipped":
1907
+ return t("cli.init.capabilities.status.skipped");
1908
+ case "failed":
1909
+ return t("cli.init.capabilities.status.failed");
1910
+ case null:
1911
+ return t("cli.init.capabilities.status.na");
1912
+ default:
1913
+ return t("cli.init.capabilities.status.ready");
1914
+ }
1915
+ }
1916
+ function formatCapabilityTableRow(row, widths) {
1917
+ return [
1918
+ padEnd(row.client, widths.client),
1919
+ padEnd(row.bootstrap, widths.bootstrap),
1920
+ padEnd(row.mcp, widths.mcp),
1921
+ padEnd(row.hook, widths.hook),
1922
+ padEnd(row.skill, widths.skill),
1923
+ padEnd(row.followUp, widths.followUp)
1924
+ ].join(" ");
1925
+ }
1926
+ function formatCapabilityDivider(widths) {
1927
+ return [
1928
+ "".padEnd(widths.client, "-"),
1929
+ "".padEnd(widths.bootstrap, "-"),
1930
+ "".padEnd(widths.mcp, "-"),
1931
+ "".padEnd(widths.hook, "-"),
1932
+ "".padEnd(widths.skill, "-"),
1933
+ "".padEnd(widths.followUp, "-")
1934
+ ].join(" ");
1935
+ }
1936
+ function formatInitReasonMessage(supports) {
1937
+ const detected = supports.filter((support) => support.detected);
1938
+ const installedSkillClients = detected.filter((support) => hasInstalledCapability(support, "skill"));
1939
+ const hasClaudeSkill = installedSkillClients.some((support) => support.clientKind === "ClaudeCodeCLI");
1940
+ const hasCodexSkill = installedSkillClients.some((support) => support.clientKind === "CodexCLI");
1941
+ if (hasClaudeSkill && hasCodexSkill) {
1942
+ return t("cli.init.reason-message.multi-body");
1943
+ }
1944
+ if (hasClaudeSkill) {
1945
+ return t("cli.init.reason-message.claude-body");
1946
+ }
1947
+ if (hasCodexSkill) {
1948
+ return t("cli.init.reason-message.codex-body");
1949
+ }
1950
+ if (detected.some((support) => support.capabilities.skill)) {
1951
+ return t("cli.init.reason-message.installable-body");
1952
+ }
1953
+ return t("cli.init.reason-message.manual-body");
1954
+ }
1955
+ function yesNoLabel(value) {
1956
+ return value ? t("cli.shared.yes") : t("cli.shared.no");
1957
+ }
1332
1958
  function formatInitPathAction(path, action) {
1333
1959
  return t("cli.init.created-path", { label: labelForInitWriteAction(action), path });
1334
1960
  }
@@ -1376,8 +2002,15 @@ function sha256(content) {
1376
2002
  return `sha256:${createHash("sha256").update(content).digest("hex")}`;
1377
2003
  }
1378
2004
  export {
2005
+ buildInitExecutionPlan,
2006
+ buildInitFabricPlan,
2007
+ createDefaultInitWizardAdapter,
1379
2008
  init_default as default,
1380
2009
  detectPackageManager,
2010
+ executeInitExecutionPlan,
2011
+ executeInitFabricPlan,
1381
2012
  initCommand,
1382
- initFabric
2013
+ initFabric,
2014
+ resolveInitExecutionPlanWithWizard,
2015
+ shouldUseInitWizard
1383
2016
  };