@fenglimg/fabric-cli 1.3.1 → 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.
@@ -2,16 +2,16 @@
2
2
  import {
3
3
  buildFabricBootstrapGuide,
4
4
  installBootstrap
5
- } from "./chunk-TKTWHAKV.js";
5
+ } from "./chunk-XQYY2U2C.js";
6
6
  import {
7
7
  detectFramework
8
8
  } from "./chunk-AZRKMFRY.js";
9
9
  import {
10
10
  installMcpClients
11
- } from "./chunk-XFSQM3LJ.js";
11
+ } from "./chunk-BVTMVW5M.js";
12
12
  import {
13
13
  detectClientSupports
14
- } from "./chunk-VOQKQ6W2.js";
14
+ } from "./chunk-BEKSXO5N.js";
15
15
  import {
16
16
  installHooks
17
17
  } from "./chunk-YDZJRLHL.js";
@@ -34,6 +34,7 @@ import * as childProcess from "child_process";
34
34
  import { chmodSync, copyFileSync, existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, renameSync, rmSync, statSync as statSync2, writeFileSync } from "fs";
35
35
  import { dirname, isAbsolute as isAbsolute2, join as join2, parse, resolve as resolve2 } from "path";
36
36
  import { fileURLToPath } from "url";
37
+ import { cancel, confirm, group, intro, isCancel, log, note, outro, select } from "@clack/prompts";
37
38
  import { defineCommand } from "citty";
38
39
 
39
40
  // src/scanner/forensic.ts
@@ -856,7 +857,7 @@ function readProjectName(target) {
856
857
  return basename(target);
857
858
  }
858
859
  function getCliVersion() {
859
- return true ? "1.3.1" : "unknown";
860
+ return true ? "1.4.0" : "unknown";
860
861
  }
861
862
  function sortRecord(record) {
862
863
  return Object.fromEntries(Object.entries(record).sort(([left], [right]) => left.localeCompare(right)));
@@ -869,8 +870,14 @@ function toPosixPath(path) {
869
870
  var CLAUDE_INIT_SKILL_TEMPLATE = "templates/claude-skills/agents-md-init/SKILL.md";
870
871
  var CLAUDE_INIT_REMINDER_HOOK_TEMPLATE = "templates/claude-hooks/agents-md-init-reminder.cjs";
871
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";
872
878
  var LOCAL_FABRIC_SERVER_PATH = join2("node_modules", "@fenglimg", "fabric-server", "dist", "index.js");
873
879
  var FABRIC_SERVER_PACKAGE = "@fenglimg/fabric-server";
880
+ var INIT_WIZARD_GROUP_CANCELLED = /* @__PURE__ */ Symbol("init-wizard-group-cancelled");
874
881
  var initCommand = defineCommand({
875
882
  meta: {
876
883
  name: "init",
@@ -891,6 +898,21 @@ var initCommand = defineCommand({
891
898
  description: t("cli.init.args.force.description"),
892
899
  default: false
893
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
+ },
894
916
  bootstrap: {
895
917
  type: "boolean",
896
918
  default: true,
@@ -918,178 +940,727 @@ var initCommand = defineCommand({
918
940
  }
919
941
  },
920
942
  async run({ args }) {
921
- const logger = createDebugLogger(args.debug);
922
- const resolution = resolveDevMode(args.target, process.cwd());
923
- const target = normalizeTarget2(resolution.target);
924
- const mcpInstallMode = resolveMcpInstallMode(args["mcp-install"]);
925
- const options = {
926
- force: args.force,
927
- skipBootstrap: args.bootstrap === false ? true : args.skipBootstrap,
928
- skipMcp: args.mcp === false ? true : args.skipMcp,
929
- skipHooks: args.hooks === false ? true : args.skipHooks
930
- };
931
- logger(`init target source: ${resolution.source}`);
932
- for (const step of resolution.chain) {
933
- logger(step);
934
- }
935
- const supports = detectClientSupports(target);
936
- const interactive = args.interactive !== false && isInteractiveInit();
937
- if (options.force) {
938
- writeStderr(t("cli.init.force.warning", { path: target }));
939
- }
940
- if (interactive) {
941
- printInitPlanSummary(target, options, mcpInstallMode, supports);
942
- }
943
- const created = initFabric(target, options);
944
- console.log(formatInitPathAction(created.bootstrapPath, created.bootstrapAction));
945
- console.log(formatInitPathAction(created.metaPath, created.metaAction));
946
- console.log(formatInitPathAction(created.humanLockPath, created.humanLockAction));
947
- console.log(formatInitPathAction(created.forensicPath, created.forensicAction));
948
- writeStderr(
949
- formatOptionalInitPathAction(created.claudeSkillPath, created.claudeSkillAction)
950
- );
951
- writeStderr(
952
- formatOptionalInitPathAction(created.claudeHookPath, created.claudeHookAction)
953
- );
954
- writeStderr(formatClaudeSettingsAction(created.claudeSettingsPath, created.claudeSettingsAction));
955
- const stageResults = [];
956
- if (options.skipBootstrap) {
957
- stageResults.push({ name: "bootstrap", disposition: "skipped" });
958
- } else {
959
- console.log(formatInitStageHeader(t("cli.init.stages.bootstrap")));
960
- try {
961
- const result = await installBootstrap(target, { force: options.force });
962
- if (result.details.length === 0) {
963
- console.log(formatInitStageResult("bootstrap", "skipped", 0, 0, t("cli.bootstrap.install.no-targets")));
964
- stageResults.push({ name: "bootstrap", disposition: "skipped" });
965
- } else {
966
- console.log(
967
- formatInitStageResult("bootstrap", "completed", result.installed.length, result.skipped.length)
968
- );
969
- stageResults.push({ name: "bootstrap", disposition: "ran" });
970
- }
971
- } catch (error) {
972
- writeStderr(formatInitStageFailure("bootstrap", error));
973
- stageResults.push({ name: "bootstrap", disposition: "failed" });
974
- }
975
- }
976
- if (options.skipMcp) {
977
- stageResults.push({ name: "mcp", disposition: "skipped" });
978
- } else {
979
- console.log(formatInitStageHeader(t("cli.init.stages.mcp")));
980
- try {
981
- let localServerPath;
982
- if (mcpInstallMode === "local") {
983
- const manager = detectPackageManager(target);
984
- writeStderr(t("cli.init.mcp.install.local"));
985
- writeStderr(t("cli.init.mcp.local.installing", { manager }));
986
- installLocalFabricServer(target, manager);
987
- writeStderr(t("cli.init.mcp.local.installed"));
988
- localServerPath = LOCAL_FABRIC_SERVER_PATH;
989
- } else {
990
- writeStderr(t("cli.init.mcp.install.global"));
991
- }
992
- const result = await installMcpClients(target, {
993
- force: options.force,
994
- localServerPath
995
- });
996
- if (result.details.length === 0) {
997
- console.log(formatInitStageResult("mcp", "skipped", 0, 0, t("cli.config.install.no-configs")));
998
- stageResults.push({ name: "mcp", disposition: "skipped" });
999
- } else {
1000
- console.log(formatInitStageResult("mcp", "completed", result.installed.length, result.skipped.length));
1001
- stageResults.push({ name: "mcp", disposition: "ran" });
1002
- }
1003
- } catch (error) {
1004
- writeStderr(formatInitStageFailure("mcp", error));
1005
- stageResults.push({ name: "mcp", disposition: "failed" });
1006
- }
1007
- }
1008
- if (options.skipHooks) {
1009
- stageResults.push({ name: "hooks", disposition: "skipped" });
1010
- } else {
1011
- console.log(formatInitStageHeader(t("cli.init.stages.hooks")));
1012
- try {
1013
- const result = await installHooks(target, { force: options.force });
1014
- console.log(formatInitStageResult("hooks", "completed", result.installed.length, result.skipped.length));
1015
- stageResults.push({ name: "hooks", disposition: "ran" });
1016
- } catch (error) {
1017
- writeStderr(formatInitStageFailure("hooks", error));
1018
- stageResults.push({ name: "hooks", disposition: "failed" });
1019
- }
1020
- }
1021
- if (shouldPrintHooksNextStep(options, stageResults)) {
1022
- console.log(
1023
- t("cli.init.next-step", {
1024
- label: nextLabel(),
1025
- message: paint.muted(t("cli.init.next-step.message"))
1026
- })
1027
- );
1028
- }
1029
- console.log(
1030
- t("cli.init.reason-message", {
1031
- label: reasonLabel(),
1032
- message: paint.muted(formatInitReasonMessage(supports))
1033
- })
1034
- );
1035
- printInitStageSummary(stageResults);
1036
- printInitCapabilitySummary(supports, stageResults, options);
943
+ await runInitCommand(args);
1037
944
  }
1038
945
  });
1039
946
  var init_default = initCommand;
1040
- function initFabric(target, options) {
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
1050
+ };
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);
1074
+ }
1075
+ }
1076
+ return {
1077
+ plan,
1078
+ created: created ?? unreachableInitScaffold(),
1079
+ stageResults,
1080
+ finalSupports
1081
+ };
1082
+ }
1083
+ function buildInitFabricPlan(target, options) {
1041
1084
  assertExistingDirectory2(target);
1042
1085
  const fabricDir = join2(target, ".fabric");
1043
1086
  const bootstrapPath = join2(fabricDir, "bootstrap", "README.md");
1044
1087
  const forensicPath = join2(fabricDir, "forensic.json");
1045
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");
1046
1093
  const claudeHookPath = join2(target, ".claude", "hooks", "agents-md-init-reminder.cjs");
1047
1094
  const claudeSettingsPath = join2(target, ".claude", "settings.json");
1048
1095
  const metaPath = join2(fabricDir, "agents.meta.json");
1049
1096
  const humanLockPath = join2(fabricDir, "human-lock.json");
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);
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);
1055
1102
  const forensicReport = buildForensicReport(target);
1056
1103
  const humanLockTemplate = readFileSync2(findTemplatePath("templates/fabric/human-lock.json"), "utf8");
1057
1104
  const bootstrapContent = buildFabricBootstrapGuide(target);
1058
1105
  const bootstrapHash = sha256(bootstrapContent);
1059
1106
  const meta = createInitialMeta(bootstrapHash);
1060
- mkdirSync(fabricDir, { recursive: true });
1061
- mkdirSync(dirname(bootstrapPath), { recursive: true });
1062
- writeNewFile(bootstrapPath, bootstrapContent, options);
1063
- writeNewFile(metaPath, `${JSON.stringify(meta, null, 2)}
1064
- `, options);
1065
- writeNewFile(humanLockPath, humanLockTemplate.endsWith("\n") ? humanLockTemplate : `${humanLockTemplate}
1066
- `, options);
1067
- writeNewFile(forensicPath, `${JSON.stringify(forensicReport, null, 2)}
1068
- `, options);
1069
- const claudeSkillAction = copyTemplateIfMissing(findTemplatePath(CLAUDE_INIT_SKILL_TEMPLATE), claudeSkillPath, options);
1070
- const claudeHookAction = copyExecutableTemplateIfMissing(
1071
- findTemplatePath(CLAUDE_INIT_REMINDER_HOOK_TEMPLATE),
1072
- claudeHookPath,
1073
- options
1074
- );
1075
- const claudeSettingsAction = mergeClaudeStopHook(claudeSettingsPath, options);
1076
1107
  return {
1108
+ target,
1109
+ options,
1110
+ fabricDir,
1111
+ replaceFabricDir,
1077
1112
  bootstrapPath,
1078
1113
  bootstrapAction,
1114
+ bootstrapContent,
1079
1115
  metaPath,
1080
1116
  metaAction,
1117
+ meta,
1081
1118
  humanLockPath,
1082
1119
  humanLockAction,
1120
+ humanLockContent: humanLockTemplate.endsWith("\n") ? humanLockTemplate : `${humanLockTemplate}
1121
+ `,
1083
1122
  forensicPath,
1084
1123
  forensicAction,
1085
- claudeSkillPath,
1086
- claudeSkillAction,
1087
- claudeHookPath,
1088
- claudeHookAction,
1089
- claudeSettingsPath,
1090
- claudeSettingsAction
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
+ })
1260
+ );
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 });
1322
+ if (result.details.length === 0) {
1323
+ console.log(formatInitStageResult("bootstrap", "skipped", 0, 0, t("cli.bootstrap.install.no-targets")));
1324
+ return { name: "bootstrap", disposition: "skipped" };
1325
+ }
1326
+ console.log(
1327
+ formatInitStageResult("bootstrap", "completed", result.installed.length, result.skipped.length)
1328
+ );
1329
+ return { name: "bootstrap", disposition: "ran" };
1330
+ }
1331
+ case "mcp": {
1332
+ if (stage.installMode === "local") {
1333
+ const manager = stage.packageManager ?? detectPackageManager(plan.target);
1334
+ writeStderr(t("cli.init.mcp.install.local"));
1335
+ writeStderr(t("cli.init.mcp.local.installing", { manager }));
1336
+ installLocalFabricServer(plan.target, manager);
1337
+ writeStderr(t("cli.init.mcp.local.installed"));
1338
+ } else {
1339
+ writeStderr(t("cli.init.mcp.install.global"));
1340
+ }
1341
+ const result = await installMcpClients(plan.target, {
1342
+ force: plan.options.force,
1343
+ localServerPath: stage.localServerPath
1344
+ });
1345
+ if (result.details.length === 0) {
1346
+ console.log(formatInitStageResult("mcp", "skipped", 0, 0, t("cli.config.install.no-configs")));
1347
+ return { name: "mcp", disposition: "skipped" };
1348
+ }
1349
+ console.log(formatInitStageResult("mcp", "completed", result.installed.length, result.skipped.length));
1350
+ return { name: "mcp", disposition: "ran" };
1351
+ }
1352
+ case "hooks": {
1353
+ const result = await installHooks(plan.target, { force: plan.options.force });
1354
+ console.log(formatInitStageResult("hooks", "completed", result.installed.length, result.skipped.length));
1355
+ return { name: "hooks", disposition: "ran" };
1356
+ }
1357
+ default:
1358
+ return exhaustiveInitStagePlan(stage);
1359
+ }
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
+ ]
1429
+ }
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;
1443
+ }
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
+ };
1499
+ return {
1500
+ path: settingsPath,
1501
+ action: hasExistingFabricHook && options?.force ? "overwritten" : action,
1502
+ value: nextSettings
1503
+ };
1504
+ }
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
+ }
1091
1601
  };
1092
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");
1663
+ }
1093
1664
  function normalizeTarget2(targetInput) {
1094
1665
  return isAbsolute2(targetInput) ? targetInput : resolve2(process.cwd(), targetInput);
1095
1666
  }
@@ -1168,102 +1739,6 @@ function templateCandidatesFrom(start, relativePath) {
1168
1739
  }
1169
1740
  return candidates.reverse();
1170
1741
  }
1171
- function prepareFreshPath(path, options) {
1172
- if (!existsSync2(path)) {
1173
- return "created";
1174
- }
1175
- if (!options?.force) {
1176
- throw new Error(t("cli.init.errors.abort-existing", { path }));
1177
- }
1178
- rmSync(path, { recursive: true, force: true });
1179
- return "overwritten";
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
- }
1190
- function writeNewFile(path, content, options) {
1191
- const existed = existsSync2(path);
1192
- if (existed && !options?.force) {
1193
- throw new Error(t("cli.init.errors.abort-existing", { path }));
1194
- }
1195
- writeFileSync(path, content, "utf8");
1196
- return existed ? "overwritten" : "created";
1197
- }
1198
- function copyTemplateIfMissing(templatePath, targetPath, options) {
1199
- mkdirSync(dirname(targetPath), { recursive: true });
1200
- const existed = existsSync2(targetPath);
1201
- if (existed && !options?.force) {
1202
- return "skipped";
1203
- }
1204
- copyFileSync(templatePath, targetPath);
1205
- return existed ? "overwritten" : "created";
1206
- }
1207
- function copyExecutableTemplateIfMissing(templatePath, targetPath, options) {
1208
- const action = copyTemplateIfMissing(templatePath, targetPath, options);
1209
- if (action !== "skipped") {
1210
- chmodSync(targetPath, 493);
1211
- }
1212
- return action;
1213
- }
1214
- function mergeClaudeStopHook(settingsPath, options) {
1215
- mkdirSync(dirname(settingsPath), { recursive: true });
1216
- let settings;
1217
- let action = "updated";
1218
- if (!existsSync2(settingsPath)) {
1219
- settings = {};
1220
- action = "created";
1221
- } else {
1222
- try {
1223
- const parsed = JSON.parse(readFileSync2(settingsPath, "utf8"));
1224
- if (!isRecord(parsed)) {
1225
- writeStderr(t("cli.init.claude-settings.invalid-object", { label: skippedLabel(), path: settingsPath }));
1226
- return "skipped-invalid";
1227
- }
1228
- settings = parsed;
1229
- } catch (error) {
1230
- const reason = error instanceof Error ? error.message : "unknown parse error";
1231
- writeStderr(t("cli.init.claude-settings.invalid-json", { label: skippedLabel(), path: settingsPath, reason }));
1232
- return "skipped-invalid";
1233
- }
1234
- }
1235
- if (settings.hooks !== void 0 && !isRecord(settings.hooks)) {
1236
- writeStderr(t("cli.init.claude-settings.invalid-hooks", { label: skippedLabel(), path: settingsPath }));
1237
- return "skipped-invalid";
1238
- }
1239
- const hooks = settings.hooks ?? {};
1240
- const stopHooksValue = hooks.Stop;
1241
- if (stopHooksValue !== void 0 && !Array.isArray(stopHooksValue)) {
1242
- writeStderr(t("cli.init.claude-settings.invalid-stop-array", { label: skippedLabel(), path: settingsPath }));
1243
- return "skipped-invalid";
1244
- }
1245
- const stopHooks = Array.isArray(stopHooksValue) ? stopHooksValue : [];
1246
- const hasExistingFabricHook = hasClaudeInitReminderHook(stopHooks);
1247
- if (hasExistingFabricHook && !options?.force) {
1248
- return "skipped";
1249
- }
1250
- const nextStopHooks = hasExistingFabricHook && options?.force ? removeClaudeInitReminderHook(stopHooks) : [...stopHooks];
1251
- nextStopHooks.push({
1252
- matcher: "*",
1253
- hooks: [
1254
- {
1255
- type: "command",
1256
- command: CLAUDE_INIT_REMINDER_COMMAND
1257
- }
1258
- ]
1259
- });
1260
- settings.hooks = {
1261
- ...hooks,
1262
- Stop: nextStopHooks
1263
- };
1264
- writeJsonAtomically(settingsPath, settings);
1265
- return hasExistingFabricHook && options?.force ? "overwritten" : action;
1266
- }
1267
1742
  function hasClaudeInitReminderHook(stopHooks) {
1268
1743
  return stopHooks.some((entry) => isClaudeInitReminderStopEntry(entry));
1269
1744
  }
@@ -1306,10 +1781,10 @@ function formatClaudeSettingsAction(settingsPath, action) {
1306
1781
  function formatInitStageHeader(message) {
1307
1782
  return `${nextLabel()} ${paint.muted(message)}`;
1308
1783
  }
1309
- function formatInitStageResult(stage, status, installedCount, skippedCount, note) {
1784
+ function formatInitStageResult(stage, status, installedCount, skippedCount, note2) {
1310
1785
  const label = status === "completed" ? completedStageLabel() : skippedStageLabel();
1311
1786
  const counts = `installed=${installedCount} skipped=${skippedCount}`;
1312
- const suffix = note ? ` ${paint.muted(`(${note})`)}` : "";
1787
+ const suffix = note2 ? ` ${paint.muted(`(${note2})`)}` : "";
1313
1788
  return `${label} ${stage}: ${counts}${suffix}`;
1314
1789
  }
1315
1790
  function formatInitStageFailure(stage, error) {
@@ -1332,10 +1807,11 @@ function shouldPrintHooksNextStep(options, stageResults) {
1332
1807
  return Boolean(options.skipHooks) || stageResults.some((stage) => stage.name === "hooks" && stage.disposition === "failed");
1333
1808
  }
1334
1809
  function isInteractiveInit() {
1335
- return Boolean(process.stdout.isTTY) && Boolean(process.stderr.isTTY);
1810
+ return Boolean(process.stdin.isTTY) && Boolean(process.stdout.isTTY) && Boolean(process.stderr.isTTY);
1336
1811
  }
1337
1812
  function printInitPlanSummary(target, options, mcpInstallMode, supports) {
1338
1813
  console.log(t("cli.init.plan.title"));
1814
+ console.log(formatInitModeBanner(options));
1339
1815
  console.log(t("cli.init.plan.target", { target }));
1340
1816
  console.log(
1341
1817
  t("cli.init.plan.actions", {
@@ -1387,21 +1863,42 @@ function printInitCapabilitySummary(supports, stageResults, options) {
1387
1863
  console.log(formatCapabilityTableRow(row, widths));
1388
1864
  }
1389
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
+ }
1390
1878
  function toCapabilityRow(support, stageResults, options) {
1391
1879
  const stage = (name) => stageResults.find((entry) => entry.name === name)?.disposition ?? null;
1392
1880
  const bootstrap = support.capabilities.bootstrap ? capabilityStatus(options.skipBootstrap ? "skipped" : stage("bootstrap")) : t("cli.init.capabilities.status.na");
1393
1881
  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");
1882
+ const hook = capabilityInstallStatus(support, "hook");
1883
+ const skill = capabilityInstallStatus(support, "skill");
1396
1884
  return {
1397
1885
  client: support.label,
1398
1886
  bootstrap,
1399
1887
  mcp,
1400
1888
  hook,
1401
1889
  skill,
1402
- followUp: support.capabilities.skill ? t("cli.init.capabilities.follow-up.ready") : t("cli.init.capabilities.follow-up.manual")
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")
1403
1891
  };
1404
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
+ }
1405
1902
  function capabilityStatus(disposition) {
1406
1903
  switch (disposition) {
1407
1904
  case "ran":
@@ -1437,8 +1934,21 @@ function formatCapabilityDivider(widths) {
1437
1934
  ].join(" ");
1438
1935
  }
1439
1936
  function formatInitReasonMessage(supports) {
1440
- if (supports.some((support) => support.detected && support.capabilities.skill)) {
1441
- return t("cli.init.reason-message.body");
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");
1442
1952
  }
1443
1953
  return t("cli.init.reason-message.manual-body");
1444
1954
  }
@@ -1492,8 +2002,15 @@ function sha256(content) {
1492
2002
  return `sha256:${createHash("sha256").update(content).digest("hex")}`;
1493
2003
  }
1494
2004
  export {
2005
+ buildInitExecutionPlan,
2006
+ buildInitFabricPlan,
2007
+ createDefaultInitWizardAdapter,
1495
2008
  init_default as default,
1496
2009
  detectPackageManager,
2010
+ executeInitExecutionPlan,
2011
+ executeInitFabricPlan,
1497
2012
  initCommand,
1498
- initFabric
2013
+ initFabric,
2014
+ resolveInitExecutionPlanWithWizard,
2015
+ shouldUseInitWizard
1499
2016
  };