@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.
- package/README.md +24 -22
- package/dist/{bootstrap-IUL4SAAK.js → bootstrap-B6RCVJZJ.js} +4 -2
- package/dist/{chunk-N4DCTOXW.js → chunk-AZRKMFRY.js} +6 -6
- package/dist/{chunk-VMYPJPKV.js → chunk-BEKSXO5N.js} +161 -21
- package/dist/{chunk-TO5RUB4R.js → chunk-BVTMVW5M.js} +1 -1
- package/dist/{chunk-F2BXHPM5.js → chunk-N7EZORJZ.js} +9 -1
- package/dist/{chunk-6UUPKSDE.js → chunk-Q4LOVXML.js} +13 -8
- package/dist/{chunk-RUQCZA2Q.js → chunk-XQYY2U2C.js} +92 -115
- package/dist/{config-MKWKDE32.js → config-EC5L2QNI.js} +2 -2
- package/dist/index.js +11 -11
- package/dist/{init-BZ73IUHH.js → init-QC2MLFHR.js} +894 -261
- package/dist/{ledger-append-XZ5SX4O5.js → ledger-append-DULKJ6Q2.js} +1 -1
- package/dist/{pre-commit-AK55G73F.js → pre-commit-IK6SJOPT.js} +5 -5
- package/dist/{scan-WKDSKEBB.js → scan-43R3IBLR.js} +2 -2
- package/dist/{sync-meta-THZSEM7Y.js → sync-meta-LKVSO6TS.js} +1 -1
- package/dist/{update-JZPUJ36D.js → update-FY2WKWPB.js} +5 -5
- package/package.json +4 -3
- package/templates/agents-md/AGENTS.md.template +14 -13
- package/templates/agents-md/variants/cocos.md +20 -20
- package/templates/agents-md/variants/next.md +20 -20
- package/templates/agents-md/variants/vite.md +20 -20
- package/templates/bootstrap/CLAUDE.md +3 -5
- package/templates/bootstrap/GEMINI.md +3 -5
- package/templates/bootstrap/codex-AGENTS-header.md +3 -5
- package/templates/bootstrap/cursor-fabric-bootstrap.mdc +11 -11
- package/templates/bootstrap/roo-fabric.md +6 -6
- package/templates/bootstrap/windsurf-fabric.md +6 -6
- package/templates/claude-hooks/agents-md-init-reminder.cjs +18 -18
- package/templates/claude-skills/agents-md-init/SKILL.md +86 -86
- package/templates/codex-hooks/fabric-session-start.cjs +19 -0
- package/templates/codex-hooks/fabric-stop-reminder.cjs +18 -0
- package/templates/codex-skills/fabric-init/SKILL.md +27 -0
- package/templates/fabric/human-lock.json +12 -12
- package/templates/husky/pre-commit +24 -24
- package/dist/{doctor-5KJGOV2P.js → doctor-QTSG2RWF.js} +3 -3
- 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-
|
|
5
|
+
} from "./chunk-XQYY2U2C.js";
|
|
5
6
|
import {
|
|
6
|
-
createScanReport,
|
|
7
7
|
detectFramework
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-AZRKMFRY.js";
|
|
9
9
|
import {
|
|
10
10
|
installMcpClients
|
|
11
|
-
} from "./chunk-
|
|
12
|
-
import
|
|
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.
|
|
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
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
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
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
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
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
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
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
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
|
-
|
|
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
|
-
|
|
963
|
-
|
|
964
|
-
|
|
1326
|
+
console.log(
|
|
1327
|
+
formatInitStageResult("bootstrap", "completed", result.installed.length, result.skipped.length)
|
|
1328
|
+
);
|
|
1329
|
+
return { name: "bootstrap", disposition: "ran" };
|
|
965
1330
|
}
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
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
|
-
|
|
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
|
-
|
|
995
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
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
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
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
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
const
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
const
|
|
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
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
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
|
|
1085
|
-
|
|
1086
|
-
|
|
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: "
|
|
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,
|
|
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 =
|
|
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
|
};
|