ai-ops-cli 0.2.0 → 0.2.2

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/dist/bin/index.js CHANGED
@@ -4,7 +4,7 @@
4
4
  import { Command } from "commander";
5
5
 
6
6
  // src/commands/init.ts
7
- import * as p4 from "@clack/prompts";
7
+ import * as p3 from "@clack/prompts";
8
8
 
9
9
  // src/core/schemas/rule.schema.ts
10
10
  import { z } from "zod";
@@ -768,7 +768,7 @@ var computeDiff = (params) => {
768
768
 
769
769
  // src/core/install-plan.ts
770
770
  import { join as join7 } from "path";
771
- var CODEX_PLAN_BODY = "## Plan Snapshot (Plan mode only)\n\n- This rule applies only when `collaboration_mode=Plan`.\n- Before implementation (file edits/creates, installs, commits), save the latest plan content to `.codex/plans/YYYYMMDD_<topic>.md`.\n- In `Default` mode, do not automatically create or update plan files.";
771
+ var CODEX_PLAN_BODY = "## Plan Snapshot (Plan mode only)\n\n- This rule applies only when Codex is running in `collaboration_mode=Plan`.\n- Before implementation (file edits/creates, installs, commits) and before leaving Plan mode, save the latest plan content to `.codex/plans/YYYYMMDD_<topic>.md`.\n- In `Default` mode, do not automatically create or update plan files.";
772
772
  var buildInstallPlan = (params) => {
773
773
  const { toolId, renderResult, meta } = params;
774
774
  if (toolId === "claude-code" && renderResult.tool === "claude-code") {
@@ -813,6 +813,43 @@ var buildInstallPlan = (params) => {
813
813
  return [];
814
814
  };
815
815
 
816
+ // src/data/spec-readme.ts
817
+ var SPEC_README_TEMPLATE = `# Specs
818
+
819
+ \uC774 \uB514\uB809\uD1A0\uB9AC\uB294 AI \uD611\uC5C5 \uAE30\uBC18 spec \uD30C\uC774\uD504\uB77C\uC778\uC744 \uAD00\uB9AC\uD569\uB2C8\uB2E4.
820
+
821
+ ## \uB514\uB809\uD1A0\uB9AC \uAD6C\uC870
822
+
823
+ \`\`\`
824
+ specs/
825
+ \u251C\u2500\u2500 baseline/ # \uAE30\uC900 spec \uBB38\uC11C (\uCD08\uAE30 \uC694\uAD6C\uC0AC\uD56D, \uD655\uC815\uB41C \uC2A4\uD399)
826
+ \u2514\u2500\u2500 delta/ # \uBCC0\uACBD spec \uBB38\uC11C (\uAE30\uC900 \uB300\uBE44 \uCD94\uAC00/\uC218\uC815 \uC0AC\uD56D)
827
+ \`\`\`
828
+
829
+ ## \uC0AC\uC6A9 \uBC29\uBC95
830
+
831
+ ### baseline
832
+
833
+ \uD504\uB85C\uC81D\uD2B8\uC758 \uCD08\uAE30 \uB610\uB294 \uD655\uC815\uB41C \uC2A4\uD399 \uBB38\uC11C\uB97C \`baseline/\` \uB514\uB809\uD1A0\uB9AC\uC5D0 \uC800\uC7A5\uD569\uB2C8\uB2E4.
834
+
835
+ - \uD30C\uC77C\uBA85: \`<feature-name>.md\` (kebab-case)
836
+ - \uB0B4\uC6A9: \uC694\uAD6C\uC0AC\uD56D, \uB3C4\uBA54\uC778 \uC6A9\uC5B4, \uC81C\uC57D \uC870\uAC74 \uB4F1
837
+
838
+ ### delta
839
+
840
+ \uAE30\uC900 \uC2A4\uD399 \uB300\uBE44 \uBCC0\uACBD\uB418\uB294 \uB0B4\uC6A9\uC744 \`delta/\` \uB514\uB809\uD1A0\uB9AC\uC5D0 \uC800\uC7A5\uD569\uB2C8\uB2E4.
841
+
842
+ - \uD30C\uC77C\uBA85: \`<YYYYMMDD>-<feature-name>.md\`
843
+ - \uB0B4\uC6A9: \uBCC0\uACBD \uC774\uC720, \uBCC0\uACBD \uC804/\uD6C4 \uBE44\uAD50, \uC601\uD5A5 \uBC94\uC704 \uB4F1
844
+ `;
845
+
846
+ // src/core/spec-plan.ts
847
+ var buildSpecInitPlan = () => [
848
+ { relativePath: "specs/README.md", content: SPEC_README_TEMPLATE },
849
+ { relativePath: "specs/baseline/.gitkeep", content: "" },
850
+ { relativePath: "specs/delta/.gitkeep", content: "" }
851
+ ];
852
+
816
853
  // src/core/uninstall-plan.ts
817
854
  import { join as join8 } from "path";
818
855
  var inferInstalledFiles = (manifest) => {
@@ -973,7 +1010,7 @@ var removeDirectories = (basePath, relativeDirs) => {
973
1010
  return removed;
974
1011
  };
975
1012
 
976
- // src/lib/gemini-settings.ts
1013
+ // src/lib/tool-settings.ts
977
1014
  import * as p from "@clack/prompts";
978
1015
  import { existsSync as existsSync5, mkdirSync as mkdirSync5, readFileSync as readFileSync6, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "fs";
979
1016
  import { join as join11 } from "path";
@@ -1012,57 +1049,24 @@ var deepRemoveKeys = (base, patch) => {
1012
1049
  var PROMPT_CANCELLED = /* @__PURE__ */ Symbol("prompt-cancelled");
1013
1050
  var isPromptCancelled = (value) => value === PROMPT_CANCELLED;
1014
1051
 
1015
- // src/lib/gemini-settings.ts
1016
- var SETTING_GROUPS = [
1017
- {
1018
- value: "ui",
1019
- label: "UI \u2014 \uC904 \uBC88\uD638 \uC228\uAE30\uAE30",
1020
- hint: "ui.showLineNumbers: false \u2014 \uCF54\uB4DC \uBCF5\uC0AC \uC2DC \uC904 \uBC88\uD638\uAC00 \uD3EC\uD568\uB418\uC9C0 \uC54A\uB3C4\uB85D \uBE44\uD65C\uC131\uD654",
1021
- patch: { ui: { showLineNumbers: false } }
1022
- },
1023
- {
1024
- value: "plan",
1025
- label: "Plan \u2014 \uACC4\uD68D \uD30C\uC77C \uC800\uC7A5 \uBC0F \uBAA8\uB378 \uB77C\uC6B0\uD305",
1026
- hint: "general.plan.directory: .gemini/plans, modelRouting: true \u2014 AI \uACC4\uD68D\uC744 \uD30C\uC77C\uB85C \uC800\uC7A5\uD558\uACE0 \uD0DC\uC2A4\uD06C\uBCC4 \uCD5C\uC801 \uBAA8\uB378 \uC790\uB3D9 \uC120\uD0DD",
1027
- patch: { general: { plan: { directory: ".gemini/plans", modelRouting: true } } }
1028
- },
1029
- {
1030
- value: "sessionRetention",
1031
- label: "Session Retention \u2014 \uC138\uC158 30\uC77C \uBCF4\uC874",
1032
- hint: "general.sessionRetention.maxAge: 30d \u2014 \uC774\uC804 \uB300\uD654 \uCEE8\uD14D\uC2A4\uD2B8\uB97C 30\uC77C\uAC04 \uC720\uC9C0",
1033
- patch: { general: { sessionRetention: { maxAge: "30d" } } }
1034
- },
1035
- {
1036
- value: "experimental",
1037
- label: "Experimental \u2014 JIT \uCEE8\uD14D\uC2A4\uD2B8 + Plan \uAE30\uB2A5",
1038
- hint: "experimental.jitContext: true, plan: true \u2014 \uC11C\uBE0C\uB514\uB809\uD1A0\uB9AC \uCEE8\uD14D\uC2A4\uD2B8 \uC9C0\uC5F0 \uB85C\uB529 \uBC0F \uACC4\uD68D \uAE30\uB2A5 \uC2E4\uD5D8\uC801 \uD65C\uC131\uD654",
1039
- patch: { experimental: { jitContext: true, plan: true } }
1040
- }
1041
- ];
1042
- var promptGeminiSettings = async () => {
1043
- const wantSettings = await p.confirm({
1044
- message: "Gemini CLI \uC124\uC815 \uD30C\uC77C(.gemini/settings.json)\uC744 \uC124\uCE58\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
1045
- initialValue: true
1046
- });
1047
- if (p.isCancel(wantSettings)) return PROMPT_CANCELLED;
1048
- if (!wantSettings) return null;
1052
+ // src/lib/tool-settings.ts
1053
+ var promptToolSettings = async (config) => {
1054
+ const want = await p.confirm({ message: config.promptMessage, initialValue: true });
1055
+ if (p.isCancel(want)) return PROMPT_CANCELLED;
1056
+ if (!want) return null;
1049
1057
  const selected = await p.multiselect({
1050
1058
  message: "\uC124\uCE58\uD560 \uC124\uC815 \uD56D\uBAA9\uC744 \uC120\uD0DD\uD558\uC138\uC694 (\uC2A4\uD398\uC774\uC2A4\uB85C \uD1A0\uAE00)",
1051
- options: SETTING_GROUPS.map((g) => ({
1052
- value: g.value,
1053
- label: g.label,
1054
- hint: g.hint
1055
- })),
1056
- initialValues: SETTING_GROUPS.map((g) => g.value),
1059
+ options: config.groups.map((g) => ({ value: g.value, label: g.label, hint: g.hint })),
1060
+ initialValues: config.groups.map((g) => g.value),
1057
1061
  required: false
1058
1062
  });
1059
1063
  if (p.isCancel(selected)) return PROMPT_CANCELLED;
1060
1064
  return selected;
1061
1065
  };
1062
- var installGeminiSettings = (basePath, selectedValues) => {
1066
+ var installToolSettings = (basePath, selectedValues, config) => {
1063
1067
  if (selectedValues.length === 0) return;
1064
- const settingsDir = join11(basePath, ".gemini");
1065
- const settingsPath = join11(settingsDir, "settings.json");
1068
+ const settingsDir = join11(basePath, config.dirName);
1069
+ const settingsPath = join11(settingsDir, config.fileName);
1066
1070
  let existing = {};
1067
1071
  if (existsSync5(settingsPath)) {
1068
1072
  try {
@@ -1072,15 +1076,15 @@ var installGeminiSettings = (basePath, selectedValues) => {
1072
1076
  }
1073
1077
  let merged = existing;
1074
1078
  for (const val of selectedValues) {
1075
- const group = SETTING_GROUPS.find((g) => g.value === val);
1079
+ const group = config.groups.find((g) => g.value === val);
1076
1080
  if (!group) continue;
1077
1081
  merged = deepMerge(merged, group.patch);
1078
1082
  }
1079
1083
  mkdirSync5(settingsDir, { recursive: true });
1080
1084
  writeFileSync5(settingsPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
1081
1085
  };
1082
- var uninstallGeminiSettings = (basePath, selectedValues) => {
1083
- const settingsPath = join11(basePath, ".gemini", "settings.json");
1086
+ var uninstallToolSettings = (basePath, selectedValues, config) => {
1087
+ const settingsPath = join11(basePath, config.dirName, config.fileName);
1084
1088
  if (!existsSync5(settingsPath)) return "notFound";
1085
1089
  let existing = {};
1086
1090
  try {
@@ -1091,12 +1095,9 @@ var uninstallGeminiSettings = (basePath, selectedValues) => {
1091
1095
  }
1092
1096
  let result = existing;
1093
1097
  for (const val of selectedValues) {
1094
- const group = SETTING_GROUPS.find((g) => g.value === val);
1098
+ const group = config.groups.find((g) => g.value === val);
1095
1099
  if (!group) continue;
1096
- result = deepRemoveKeys(
1097
- result,
1098
- group.patch
1099
- );
1100
+ result = deepRemoveKeys(result, group.patch);
1100
1101
  }
1101
1102
  if (Object.keys(result).length === 0) {
1102
1103
  rmSync2(settingsPath, { force: true });
@@ -1106,10 +1107,44 @@ var uninstallGeminiSettings = (basePath, selectedValues) => {
1106
1107
  return "cleaned";
1107
1108
  };
1108
1109
 
1110
+ // src/lib/gemini-settings.ts
1111
+ var SETTING_GROUPS = [
1112
+ {
1113
+ value: "ui",
1114
+ label: "UI \u2014 \uC904 \uBC88\uD638 \uC228\uAE30\uAE30",
1115
+ hint: "ui.showLineNumbers: false \u2014 \uCF54\uB4DC \uBCF5\uC0AC \uC2DC \uC904 \uBC88\uD638\uAC00 \uD3EC\uD568\uB418\uC9C0 \uC54A\uB3C4\uB85D \uBE44\uD65C\uC131\uD654",
1116
+ patch: { ui: { showLineNumbers: false } }
1117
+ },
1118
+ {
1119
+ value: "plan",
1120
+ label: "Plan \u2014 \uACC4\uD68D \uD30C\uC77C \uC800\uC7A5 \uBC0F \uBAA8\uB378 \uB77C\uC6B0\uD305",
1121
+ hint: "general.plan.directory: .gemini/plans, modelRouting: true \u2014 AI \uACC4\uD68D\uC744 \uD30C\uC77C\uB85C \uC800\uC7A5\uD558\uACE0 \uD0DC\uC2A4\uD06C\uBCC4 \uCD5C\uC801 \uBAA8\uB378 \uC790\uB3D9 \uC120\uD0DD",
1122
+ patch: { general: { plan: { directory: ".gemini/plans", modelRouting: true } } }
1123
+ },
1124
+ {
1125
+ value: "sessionRetention",
1126
+ label: "Session Retention \u2014 \uC138\uC158 30\uC77C \uBCF4\uC874",
1127
+ hint: "general.sessionRetention.maxAge: 30d \u2014 \uC774\uC804 \uB300\uD654 \uCEE8\uD14D\uC2A4\uD2B8\uB97C 30\uC77C\uAC04 \uC720\uC9C0",
1128
+ patch: { general: { sessionRetention: { maxAge: "30d" } } }
1129
+ },
1130
+ {
1131
+ value: "experimental",
1132
+ label: "Experimental \u2014 JIT \uCEE8\uD14D\uC2A4\uD2B8 + Plan \uAE30\uB2A5",
1133
+ hint: "experimental.jitContext: true, plan: true \u2014 \uC11C\uBE0C\uB514\uB809\uD1A0\uB9AC \uCEE8\uD14D\uC2A4\uD2B8 \uC9C0\uC5F0 \uB85C\uB529 \uBC0F \uACC4\uD68D \uAE30\uB2A5 \uC2E4\uD5D8\uC801 \uD65C\uC131\uD654",
1134
+ patch: { experimental: { jitContext: true, plan: true } }
1135
+ }
1136
+ ];
1137
+ var CONFIG = {
1138
+ dirName: ".gemini",
1139
+ fileName: "settings.json",
1140
+ promptMessage: "Gemini CLI \uC124\uC815 \uD30C\uC77C(.gemini/settings.json)\uC744 \uC124\uCE58\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
1141
+ groups: SETTING_GROUPS
1142
+ };
1143
+ var promptGeminiSettings = () => promptToolSettings(CONFIG);
1144
+ var installGeminiSettings = (basePath, selectedValues) => installToolSettings(basePath, selectedValues, CONFIG);
1145
+ var uninstallGeminiSettings = (basePath, selectedValues) => uninstallToolSettings(basePath, selectedValues, CONFIG);
1146
+
1109
1147
  // src/lib/claude-settings.ts
1110
- import * as p2 from "@clack/prompts";
1111
- import { existsSync as existsSync6, mkdirSync as mkdirSync6, readFileSync as readFileSync7, rmSync as rmSync3, writeFileSync as writeFileSync6 } from "fs";
1112
- import { join as join12 } from "path";
1113
1148
  var SETTING_GROUPS2 = [
1114
1149
  {
1115
1150
  value: "model",
@@ -1124,77 +1159,20 @@ var SETTING_GROUPS2 = [
1124
1159
  patch: { plansDirectory: "./.claude/plans" }
1125
1160
  }
1126
1161
  ];
1127
- var promptClaudeSettings = async () => {
1128
- const wantSettings = await p2.confirm({
1129
- message: "Claude Code \uC124\uC815 \uD30C\uC77C(.claude/settings.local.json)\uC744 \uC124\uCE58\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
1130
- initialValue: true
1131
- });
1132
- if (p2.isCancel(wantSettings)) return PROMPT_CANCELLED;
1133
- if (!wantSettings) return null;
1134
- const selected = await p2.multiselect({
1135
- message: "\uC124\uCE58\uD560 \uC124\uC815 \uD56D\uBAA9\uC744 \uC120\uD0DD\uD558\uC138\uC694 (\uC2A4\uD398\uC774\uC2A4\uB85C \uD1A0\uAE00)",
1136
- options: SETTING_GROUPS2.map((g) => ({
1137
- value: g.value,
1138
- label: g.label,
1139
- hint: g.hint
1140
- })),
1141
- initialValues: SETTING_GROUPS2.map((g) => g.value),
1142
- required: false
1143
- });
1144
- if (p2.isCancel(selected)) return PROMPT_CANCELLED;
1145
- return selected;
1146
- };
1147
- var installClaudeSettings = (basePath, selectedValues) => {
1148
- if (selectedValues.length === 0) return;
1149
- const settingsDir = join12(basePath, ".claude");
1150
- const settingsPath = join12(settingsDir, "settings.local.json");
1151
- let existing = {};
1152
- if (existsSync6(settingsPath)) {
1153
- try {
1154
- existing = JSON.parse(readFileSync7(settingsPath, "utf-8"));
1155
- } catch {
1156
- }
1157
- }
1158
- let merged = existing;
1159
- for (const val of selectedValues) {
1160
- const group = SETTING_GROUPS2.find((g) => g.value === val);
1161
- if (!group) continue;
1162
- merged = deepMerge(merged, group.patch);
1163
- }
1164
- mkdirSync6(settingsDir, { recursive: true });
1165
- writeFileSync6(settingsPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
1166
- };
1167
- var uninstallClaudeSettings = (basePath, selectedValues) => {
1168
- const settingsPath = join12(basePath, ".claude", "settings.local.json");
1169
- if (!existsSync6(settingsPath)) return "notFound";
1170
- let existing = {};
1171
- try {
1172
- existing = JSON.parse(readFileSync7(settingsPath, "utf-8"));
1173
- } catch {
1174
- rmSync3(settingsPath, { force: true });
1175
- return "deleted";
1176
- }
1177
- let result = existing;
1178
- for (const val of selectedValues) {
1179
- const group = SETTING_GROUPS2.find((g) => g.value === val);
1180
- if (!group) continue;
1181
- result = deepRemoveKeys(
1182
- result,
1183
- group.patch
1184
- );
1185
- }
1186
- if (Object.keys(result).length === 0) {
1187
- rmSync3(settingsPath, { force: true });
1188
- return "deleted";
1189
- }
1190
- writeFileSync6(settingsPath, JSON.stringify(result, null, 2) + "\n", "utf-8");
1191
- return "cleaned";
1192
- };
1162
+ var CONFIG2 = {
1163
+ dirName: ".claude",
1164
+ fileName: "settings.local.json",
1165
+ promptMessage: "Claude Code \uC124\uC815 \uD30C\uC77C(.claude/settings.local.json)\uC744 \uC124\uCE58\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
1166
+ groups: SETTING_GROUPS2
1167
+ };
1168
+ var promptClaudeSettings = () => promptToolSettings(CONFIG2);
1169
+ var installClaudeSettings = (basePath, selectedValues) => installToolSettings(basePath, selectedValues, CONFIG2);
1170
+ var uninstallClaudeSettings = (basePath, selectedValues) => uninstallToolSettings(basePath, selectedValues, CONFIG2);
1193
1171
 
1194
1172
  // src/lib/prettier-ignore.ts
1195
- import * as p3 from "@clack/prompts";
1196
- import { existsSync as existsSync7, readFileSync as readFileSync8, rmSync as rmSync4, writeFileSync as writeFileSync7 } from "fs";
1197
- import { join as join13 } from "path";
1173
+ import * as p2 from "@clack/prompts";
1174
+ import { existsSync as existsSync6, readFileSync as readFileSync7, rmSync as rmSync3, writeFileSync as writeFileSync6 } from "fs";
1175
+ import { join as join12 } from "path";
1198
1176
  var PRETTIER_IGNORE_CONTENT = `# CLAUDE
1199
1177
  .claude/rules/
1200
1178
  **/CLAUDE.md
@@ -1252,39 +1230,39 @@ var stripAiOpsSection2 = (content) => {
1252
1230
  return result.join("\n");
1253
1231
  };
1254
1232
  var promptPrettierIgnore = async () => {
1255
- const want = await p3.confirm({
1233
+ const want = await p2.confirm({
1256
1234
  message: ".prettierignore\uB97C \uC124\uCE58\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C? (VSCode Prettier \uC790\uB3D9 \uD3EC\uB9F7\uC73C\uB85C\uBD80\uD130 AI \uADDC\uCE59 \uD30C\uC77C \uBCF4\uD638)",
1257
1235
  initialValue: false
1258
1236
  });
1259
- if (p3.isCancel(want)) return PROMPT_CANCELLED;
1237
+ if (p2.isCancel(want)) return PROMPT_CANCELLED;
1260
1238
  return want;
1261
1239
  };
1262
1240
  var installPrettierIgnore = (basePath) => {
1263
- const filePath = join13(basePath, ".prettierignore");
1241
+ const filePath = join12(basePath, ".prettierignore");
1264
1242
  const section = wrapSection(PRETTIER_IGNORE_CONTENT);
1265
- if (!existsSync7(filePath)) {
1266
- writeFileSync7(filePath, section + "\n", "utf-8");
1243
+ if (!existsSync6(filePath)) {
1244
+ writeFileSync6(filePath, section + "\n", "utf-8");
1267
1245
  return;
1268
1246
  }
1269
- const existing = readFileSync8(filePath, "utf-8");
1247
+ const existing = readFileSync7(filePath, "utf-8");
1270
1248
  if (hasAiOpsSection2(existing)) {
1271
- writeFileSync7(filePath, replaceSection(existing, PRETTIER_IGNORE_CONTENT), "utf-8");
1249
+ writeFileSync6(filePath, replaceSection(existing, PRETTIER_IGNORE_CONTENT), "utf-8");
1272
1250
  return;
1273
1251
  }
1274
1252
  const separator = existing.endsWith("\n") ? "\n" : "\n\n";
1275
- writeFileSync7(filePath, existing + separator + section + "\n", "utf-8");
1253
+ writeFileSync6(filePath, existing + separator + section + "\n", "utf-8");
1276
1254
  };
1277
1255
  var uninstallPrettierIgnore = (basePath) => {
1278
- const filePath = join13(basePath, ".prettierignore");
1279
- if (!existsSync7(filePath)) return "notFound";
1280
- const existing = readFileSync8(filePath, "utf-8");
1256
+ const filePath = join12(basePath, ".prettierignore");
1257
+ if (!existsSync6(filePath)) return "notFound";
1258
+ const existing = readFileSync7(filePath, "utf-8");
1281
1259
  if (!hasAiOpsSection2(existing)) return "notFound";
1282
1260
  const stripped = stripAiOpsSection2(existing).trim();
1283
1261
  if (stripped.length === 0) {
1284
- rmSync4(filePath, { force: true });
1262
+ rmSync3(filePath, { force: true });
1285
1263
  return "deleted";
1286
1264
  }
1287
- writeFileSync7(filePath, stripped + "\n", "utf-8");
1265
+ writeFileSync6(filePath, stripped + "\n", "utf-8");
1288
1266
  return "cleaned";
1289
1267
  };
1290
1268
 
@@ -1405,7 +1383,7 @@ var partitionPresetSkills = (params) => {
1405
1383
  };
1406
1384
  };
1407
1385
  var selectPresetAndFineTune = async (workspaceName, presets, allRules, allSkills, selectedTools, globalInstalledSkills) => {
1408
- const preset = await p4.select({
1386
+ const preset = await p3.select({
1409
1387
  message: `[${workspaceName}] \uD504\uB9AC\uC14B\uC744 \uC120\uD0DD\uD558\uC138\uC694`,
1410
1388
  options: presets.map((candidate) => ({
1411
1389
  value: candidate,
@@ -1413,10 +1391,10 @@ var selectPresetAndFineTune = async (workspaceName, presets, allRules, allSkills
1413
1391
  hint: candidate.description
1414
1392
  }))
1415
1393
  });
1416
- if (p4.isCancel(preset)) return null;
1394
+ if (p3.isCancel(preset)) return null;
1417
1395
  const finalRules = resolvePresetRules(preset, allRules);
1418
1396
  if (finalRules.length > 0) {
1419
- p4.note(finalRules.map((rule) => ` \u2713 ${rule.id}`).join("\n"), `[${workspaceName}] core rules (\uC7A0\uAE08)`);
1397
+ p3.note(finalRules.map((rule) => ` \u2713 ${rule.id}`).join("\n"), `[${workspaceName}] core rules (\uC7A0\uAE08)`);
1420
1398
  }
1421
1399
  const { globalSkills, installableSkills } = partitionPresetSkills({
1422
1400
  preset,
@@ -1428,10 +1406,10 @@ var selectPresetAndFineTune = async (workspaceName, presets, allRules, allSkills
1428
1406
  const globalLines = globalSkills.map(
1429
1407
  ({ skill, availableTools }) => ` \u2713 ${skill.id} (${formatToolList(availableTools)})`
1430
1408
  );
1431
- p4.note(globalLines.join("\n"), `[${workspaceName}] already available globally`);
1409
+ p3.note(globalLines.join("\n"), `[${workspaceName}] already available globally`);
1432
1410
  }
1433
1411
  if (installableSkills.length === 0) {
1434
- p4.note(" \uC0C8\uB85C \uC124\uCE58\uD560 reference skill\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.", `[${workspaceName}] installable reference skills`);
1412
+ p3.note(" \uC0C8\uB85C \uC124\uCE58\uD560 reference skill\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.", `[${workspaceName}] installable reference skills`);
1435
1413
  return {
1436
1414
  workspace: workspaceName,
1437
1415
  preset,
@@ -1439,7 +1417,7 @@ var selectPresetAndFineTune = async (workspaceName, presets, allRules, allSkills
1439
1417
  finalSkillTargets: []
1440
1418
  };
1441
1419
  }
1442
- const selectedSkillIds = await p4.multiselect({
1420
+ const selectedSkillIds = await p3.multiselect({
1443
1421
  message: `[${workspaceName}] installable reference skills \uC120\uD0DD`,
1444
1422
  options: installableSkills.map(({ skill, requestedTools, globalTools }) => ({
1445
1423
  value: skill.id,
@@ -1449,7 +1427,7 @@ var selectPresetAndFineTune = async (workspaceName, presets, allRules, allSkills
1449
1427
  initialValues: installableSkills.map(({ skill }) => skill.id),
1450
1428
  required: false
1451
1429
  });
1452
- if (p4.isCancel(selectedSkillIds)) return null;
1430
+ if (p3.isCancel(selectedSkillIds)) return null;
1453
1431
  const selectedSkillSet = new Set(selectedSkillIds);
1454
1432
  return {
1455
1433
  workspace: workspaceName,
@@ -1462,28 +1440,28 @@ var selectPresetAndFineTune = async (workspaceName, presets, allRules, allSkills
1462
1440
  };
1463
1441
  };
1464
1442
  var selectInitSkillScope = async () => {
1465
- const scope = await p4.select({
1443
+ const scope = await p3.select({
1466
1444
  message: "\uC120\uD0DD\uB41C skills\uB97C \uC5B4\uB514\uC5D0 \uC124\uCE58\uD560\uAE4C\uC694?",
1467
1445
  options: [
1468
1446
  { value: "user", label: "user (global)", hint: "\uAE30\uBCF8\uAC12. \uC5EC\uB7EC \uD504\uB85C\uC81D\uD2B8\uC5D0\uC11C \uC7AC\uC0AC\uC6A9" },
1469
1447
  { value: "project", label: "project", hint: "\uD604\uC7AC \uD504\uB85C\uC81D\uD2B8\uC5D0\uB9CC \uC124\uCE58" }
1470
1448
  ]
1471
1449
  });
1472
- return p4.isCancel(scope) ? null : scope;
1450
+ return p3.isCancel(scope) ? null : scope;
1473
1451
  };
1474
1452
  var initCommand = async () => {
1475
1453
  const basePath = resolveBasePath();
1476
1454
  const userBasePath = resolveUserBasePath();
1477
1455
  const rulesDir = resolveRulesDir();
1478
1456
  const skillsDir = resolveSkillsDir();
1479
- const spinner3 = p4.spinner();
1457
+ const spinner3 = p3.spinner();
1480
1458
  let spinnerStarted = false;
1481
1459
  const cancelInit = (params) => {
1482
1460
  if (spinnerStarted) {
1483
1461
  spinner3.stop("\uC124\uCE58 \uC911\uB2E8\uB428");
1484
1462
  spinnerStarted = false;
1485
1463
  }
1486
- p4.cancel(params?.message ?? "\uCDE8\uC18C\uB428");
1464
+ p3.cancel(params?.message ?? "\uCDE8\uC18C\uB428");
1487
1465
  process.exit(params?.exitCode ?? 0);
1488
1466
  };
1489
1467
  const handleSigint = () => cancelInit({
@@ -1492,20 +1470,20 @@ var initCommand = async () => {
1492
1470
  });
1493
1471
  process.once("SIGINT", handleSigint);
1494
1472
  try {
1495
- p4.intro("ai-ops init");
1496
- const selectedTools = await p4.multiselect({
1473
+ p3.intro("ai-ops init");
1474
+ const selectedTools = await p3.multiselect({
1497
1475
  message: "AI \uB3C4\uAD6C\uB97C \uC120\uD0DD\uD558\uC138\uC694",
1498
1476
  options: TOOL_OPTIONS,
1499
1477
  required: true
1500
1478
  });
1501
- if (p4.isCancel(selectedTools)) {
1479
+ if (p3.isCancel(selectedTools)) {
1502
1480
  cancelInit();
1503
1481
  }
1504
- const isMonorepo = await p4.confirm({
1482
+ const isMonorepo = await p3.confirm({
1505
1483
  message: "\uBAA8\uB178\uB808\uD3EC \uD504\uB85C\uC81D\uD2B8\uC785\uB2C8\uAE4C?",
1506
1484
  initialValue: false
1507
1485
  });
1508
- if (p4.isCancel(isMonorepo)) {
1486
+ if (p3.isCancel(isMonorepo)) {
1509
1487
  cancelInit();
1510
1488
  }
1511
1489
  const allRules = loadAllRules(rulesDir);
@@ -1526,12 +1504,12 @@ var initCommand = async () => {
1526
1504
  mappings.push(mapping);
1527
1505
  } else {
1528
1506
  const candidates = listWorkspaceCandidates(basePath);
1529
- const selectedWorkspaces = await p4.multiselect({
1507
+ const selectedWorkspaces = await p3.multiselect({
1530
1508
  message: "\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4\uB97C \uC120\uD0DD\uD558\uC138\uC694",
1531
1509
  options: candidates.map((candidate) => ({ value: candidate, label: candidate })),
1532
1510
  required: true
1533
1511
  });
1534
- if (p4.isCancel(selectedWorkspaces)) {
1512
+ if (p3.isCancel(selectedWorkspaces)) {
1535
1513
  cancelInit();
1536
1514
  }
1537
1515
  for (const workspace of selectedWorkspaces) {
@@ -1655,29 +1633,29 @@ var initCommand = async () => {
1655
1633
  });
1656
1634
  writeManifest(resolveManifestPath(basePath), manifest);
1657
1635
  if (allAppended.length > 0) {
1658
- p4.log.info(`\uAE30\uC874 \uD30C\uC77C\uC5D0 \uC139\uC158 \uCD94\uAC00\uB428 (\uB0B4\uC6A9 \uBCF4\uC874):
1636
+ p3.log.info(`\uAE30\uC874 \uD30C\uC77C\uC5D0 \uC139\uC158 \uCD94\uAC00\uB428 (\uB0B4\uC6A9 \uBCF4\uC874):
1659
1637
  ${allAppended.map((file) => ` ${file}`).join("\n")}`);
1660
1638
  }
1661
- p4.log.success(`\uC124\uCE58\uB41C core rules: ${selectedRuleIds.length}\uAC1C`);
1662
- p4.log.success(`\uC124\uCE58\uB41C skills: ${selectedSkillTargets.length}\uAC1C${skillScope ? ` (${skillScope})` : ""}`);
1639
+ p3.log.success(`\uC124\uCE58\uB41C core rules: ${selectedRuleIds.length}\uAC1C`);
1640
+ p3.log.success(`\uC124\uCE58\uB41C skills: ${selectedSkillTargets.length}\uAC1C${skillScope ? ` (${skillScope})` : ""}`);
1663
1641
  if (selectedSkillTargets.length > 0 && skillScope === "user") {
1664
- p4.log.info("global skill\uC740 ai-ops uninstall \uB300\uC0C1\uC774 \uC544\uB2D9\uB2C8\uB2E4. ai-ops skill uninstall\uC73C\uB85C \uC81C\uAC70\uD558\uC138\uC694.");
1642
+ p3.log.info("global skill\uC740 ai-ops uninstall \uB300\uC0C1\uC774 \uC544\uB2D9\uB2C8\uB2E4. ai-ops skill uninstall\uC73C\uB85C \uC81C\uAC70\uD558\uC138\uC694.");
1665
1643
  }
1666
- p4.outro("ai-ops init \uC644\uB8CC");
1644
+ p3.outro("ai-ops init \uC644\uB8CC");
1667
1645
  } finally {
1668
1646
  process.off("SIGINT", handleSigint);
1669
1647
  }
1670
1648
  };
1671
1649
 
1672
1650
  // src/commands/update.ts
1673
- import * as p5 from "@clack/prompts";
1651
+ import * as p4 from "@clack/prompts";
1674
1652
  var updateCommand = async (opts) => {
1675
1653
  const basePath = resolveBasePath();
1676
1654
  const manifestPath = resolveManifestPath(basePath);
1677
- p5.intro("ai-ops update");
1655
+ p4.intro("ai-ops update");
1678
1656
  const manifest = readManifest(manifestPath);
1679
1657
  if (!manifest) {
1680
- p5.log.error("manifest\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 ai-ops init\uC744 \uC2E4\uD589\uD558\uC138\uC694.");
1658
+ p4.log.error("manifest\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 ai-ops init\uC744 \uC2E4\uD589\uD558\uC138\uC694.");
1681
1659
  process.exit(1);
1682
1660
  }
1683
1661
  const rulesDir = resolveRulesDir();
@@ -1704,11 +1682,11 @@ var updateCommand = async (opts) => {
1704
1682
  currentCliVersion: cliVersion
1705
1683
  });
1706
1684
  if (diffResult.status === "up-to-date" && !opts.force) {
1707
- p5.log.info("\uBCC0\uACBD \uC0AC\uD56D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
1708
- p5.outro("ai-ops update \uC644\uB8CC");
1685
+ p4.log.info("\uBCC0\uACBD \uC0AC\uD56D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
1686
+ p4.outro("ai-ops update \uC644\uB8CC");
1709
1687
  return;
1710
1688
  }
1711
- const s = p5.spinner();
1689
+ const s = p4.spinner();
1712
1690
  s.start("\uADDC\uCE59 \uAC31\uC2E0 \uC911...");
1713
1691
  const meta = { sourceHash, generatedAt: (/* @__PURE__ */ new Date()).toISOString() };
1714
1692
  const allInstalledFiles = [];
@@ -1776,17 +1754,17 @@ var updateCommand = async (opts) => {
1776
1754
  });
1777
1755
  writeManifest(manifestPath, newManifest);
1778
1756
  s.stop("\uADDC\uCE59 \uAC31\uC2E0 \uC644\uB8CC");
1779
- p5.outro("ai-ops update \uC644\uB8CC");
1757
+ p4.outro("ai-ops update \uC644\uB8CC");
1780
1758
  };
1781
1759
 
1782
1760
  // src/commands/diff.ts
1783
- import * as p6 from "@clack/prompts";
1761
+ import * as p5 from "@clack/prompts";
1784
1762
  var diffCommand = async () => {
1785
1763
  const basePath = resolveBasePath();
1786
- p6.intro("ai-ops diff");
1764
+ p5.intro("ai-ops diff");
1787
1765
  const manifest = readManifest(resolveManifestPath(basePath));
1788
1766
  if (!manifest) {
1789
- p6.log.error("manifest\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 ai-ops init\uC744 \uC2E4\uD589\uD558\uC138\uC694.");
1767
+ p5.log.error("manifest\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 ai-ops init\uC744 \uC2E4\uD589\uD558\uC138\uC694.");
1790
1768
  process.exit(1);
1791
1769
  }
1792
1770
  const sourceHash = computeSourceHash(resolveCompilerDataDir());
@@ -1819,31 +1797,31 @@ var diffCommand = async () => {
1819
1797
  return `- ${skill.id}: ${changed ? "changed" : "up-to-date"} (${previousHash} -> ${next.sourceHash})`;
1820
1798
  });
1821
1799
  if (result.status === "up-to-date") {
1822
- p6.log.success("\uBCC0\uACBD \uC0AC\uD56D \uC5C6\uC74C. \uCD5C\uC2E0 \uC0C1\uD0DC\uC785\uB2C8\uB2E4.");
1800
+ p5.log.success("\uBCC0\uACBD \uC0AC\uD56D \uC5C6\uC74C. \uCD5C\uC2E0 \uC0C1\uD0DC\uC785\uB2C8\uB2E4.");
1823
1801
  } else {
1824
1802
  if (result.sourceChanged) {
1825
- p6.log.warn(`\uC18C\uC2A4 \uBCC0\uACBD \uAC10\uC9C0: ${manifest.sourceHash} \u2192 ${sourceHash}`);
1803
+ p5.log.warn(`\uC18C\uC2A4 \uBCC0\uACBD \uAC10\uC9C0: ${manifest.sourceHash} \u2192 ${sourceHash}`);
1826
1804
  }
1827
1805
  if (result.added.length > 0) {
1828
- p6.log.info(`\uCD94\uAC00\uB41C \uADDC\uCE59: ${result.added.join(", ")}`);
1806
+ p5.log.info(`\uCD94\uAC00\uB41C \uADDC\uCE59: ${result.added.join(", ")}`);
1829
1807
  }
1830
1808
  if (result.removed.length > 0) {
1831
- p6.log.info(`\uC81C\uAC70\uB41C \uADDC\uCE59: ${result.removed.join(", ")}`);
1809
+ p5.log.info(`\uC81C\uAC70\uB41C \uADDC\uCE59: ${result.removed.join(", ")}`);
1832
1810
  }
1833
1811
  }
1834
1812
  if (skillLines.length > 0) {
1835
- p6.log.info(`project skills:
1813
+ p5.log.info(`project skills:
1836
1814
  ${skillLines.map((line) => ` ${line}`).join("\n")}`);
1837
1815
  }
1838
- p6.outro("ai-ops diff \uC644\uB8CC");
1816
+ p5.outro("ai-ops diff \uC644\uB8CC");
1839
1817
  };
1840
1818
 
1841
1819
  // src/commands/uninstall.ts
1842
- import * as p7 from "@clack/prompts";
1843
- import { rmSync as rmSync6 } from "fs";
1820
+ import * as p6 from "@clack/prompts";
1821
+ import { rmSync as rmSync5 } from "fs";
1844
1822
 
1845
1823
  // src/lib/uninstall.ts
1846
- import { existsSync as existsSync8, readFileSync as readFileSync9, rmSync as rmSync5, readdirSync as readdirSync4, writeFileSync as writeFileSync8 } from "fs";
1824
+ import { existsSync as existsSync7, readFileSync as readFileSync8, rmSync as rmSync4, readdirSync as readdirSync4, writeFileSync as writeFileSync7 } from "fs";
1847
1825
  import { resolve as resolve7, dirname as dirname7 } from "path";
1848
1826
  var removeFiles = (basePath, relativePaths) => {
1849
1827
  const deleted = [];
@@ -1852,22 +1830,22 @@ var removeFiles = (basePath, relativePaths) => {
1852
1830
  const notFound = [];
1853
1831
  for (const rel of relativePaths) {
1854
1832
  const absPath = resolve7(basePath, rel);
1855
- if (!existsSync8(absPath)) {
1833
+ if (!existsSync7(absPath)) {
1856
1834
  notFound.push(rel);
1857
1835
  continue;
1858
1836
  }
1859
- const content = readFileSync9(absPath, "utf-8");
1837
+ const content = readFileSync8(absPath, "utf-8");
1860
1838
  if (hasAiOpsSection(content)) {
1861
1839
  const stripped = stripAiOpsSection(content);
1862
1840
  if (stripped.trim().length === 0) {
1863
- rmSync5(absPath);
1841
+ rmSync4(absPath);
1864
1842
  deleted.push(rel);
1865
1843
  } else {
1866
- writeFileSync8(absPath, stripped, "utf-8");
1844
+ writeFileSync7(absPath, stripped, "utf-8");
1867
1845
  cleaned.push(rel);
1868
1846
  }
1869
1847
  } else if (hasLegacyHeader(content)) {
1870
- rmSync5(absPath);
1848
+ rmSync4(absPath);
1871
1849
  deleted.push(rel);
1872
1850
  } else {
1873
1851
  skipped.push(rel);
@@ -1879,11 +1857,11 @@ var cleanEmptyDirs = (basePath, dirs) => {
1879
1857
  const removed = [];
1880
1858
  for (const dir of dirs) {
1881
1859
  const absDir = resolve7(basePath, dir);
1882
- if (!existsSync8(absDir)) continue;
1860
+ if (!existsSync7(absDir)) continue;
1883
1861
  try {
1884
1862
  const entries = readdirSync4(absDir);
1885
1863
  if (entries.length === 0) {
1886
- rmSync5(absDir, { recursive: true });
1864
+ rmSync4(absDir, { recursive: true });
1887
1865
  removed.push(dir);
1888
1866
  }
1889
1867
  } catch {
@@ -1907,10 +1885,10 @@ var SETTINGS_PATHS = /* @__PURE__ */ new Set([".claude/settings.local.json", ".g
1907
1885
  var uninstallCommand = async () => {
1908
1886
  const basePath = resolveBasePath();
1909
1887
  const manifestPath = resolveManifestPath(basePath);
1910
- p7.intro("ai-ops uninstall");
1888
+ p6.intro("ai-ops uninstall");
1911
1889
  const manifest = readManifest(manifestPath);
1912
1890
  if (!manifest) {
1913
- p7.log.error("manifest\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 ai-ops init\uC744 \uC2E4\uD589\uD558\uC138\uC694.");
1891
+ p6.log.error("manifest\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 ai-ops init\uC744 \uC2E4\uD589\uD558\uC138\uC694.");
1914
1892
  process.exit(1);
1915
1893
  }
1916
1894
  const targetFiles = [
@@ -1919,31 +1897,34 @@ var uninstallCommand = async () => {
1919
1897
  ].filter((f) => !SETTINGS_PATHS.has(f));
1920
1898
  const targetSkillDirs = (manifest.installed_skills ?? []).flatMap((skill) => skill.installed_paths);
1921
1899
  if (targetFiles.length === 0 && targetSkillDirs.length === 0) {
1922
- p7.log.warn("\uC0AD\uC81C\uD560 \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
1923
- p7.outro("ai-ops uninstall \uC644\uB8CC");
1900
+ p6.log.warn("\uC0AD\uC81C\uD560 \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
1901
+ p6.outro("ai-ops uninstall \uC644\uB8CC");
1924
1902
  return;
1925
1903
  }
1926
1904
  if (targetFiles.length > 0) {
1927
- p7.log.info(`\uC0AD\uC81C \uB300\uC0C1 \uD30C\uC77C (${targetFiles.length}\uAC1C):
1905
+ p6.log.info(`\uC0AD\uC81C \uB300\uC0C1 \uD30C\uC77C (${targetFiles.length}\uAC1C):
1928
1906
  ${targetFiles.map((f) => ` ${f}`).join("\n")}`);
1929
1907
  }
1930
1908
  if (targetSkillDirs.length > 0) {
1931
- p7.log.info(`\uC0AD\uC81C \uB300\uC0C1 skill \uB514\uB809\uD1A0\uB9AC (${targetSkillDirs.length}\uAC1C):
1932
- ${targetSkillDirs.map((f) => ` ${f}`).join("\n")}`);
1909
+ p6.log.info(
1910
+ `\uC0AD\uC81C \uB300\uC0C1 skill \uB514\uB809\uD1A0\uB9AC (${targetSkillDirs.length}\uAC1C):
1911
+ ${targetSkillDirs.map((f) => ` ${f}`).join("\n")}`
1912
+ );
1933
1913
  }
1934
- const confirmed = await p7.confirm({
1914
+ const confirmed = await p6.confirm({
1935
1915
  message: "\uC704 \uD30C\uC77C\uACFC manifest\uB97C \uBAA8\uB450 \uC0AD\uC81C\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
1936
1916
  initialValue: false
1937
1917
  });
1938
- if (p7.isCancel(confirmed) || !confirmed) {
1939
- p7.cancel("\uCDE8\uC18C\uB428");
1918
+ if (p6.isCancel(confirmed) || !confirmed) {
1919
+ p6.cancel("\uCDE8\uC18C\uB428");
1940
1920
  process.exit(0);
1941
1921
  }
1942
1922
  const settingsMessages = [];
1943
1923
  if (manifest.settings?.claude) {
1944
1924
  const status = uninstallClaudeSettings(basePath, manifest.settings.claude);
1945
1925
  if (status === "deleted") settingsMessages.push("\uC0AD\uC81C: .claude/settings.local.json");
1946
- else if (status === "cleaned") settingsMessages.push("ai-ops \uD0A4 \uC81C\uAC70 (\uC0AC\uC6A9\uC790 \uC124\uC815 \uBCF4\uC874): .claude/settings.local.json");
1926
+ else if (status === "cleaned")
1927
+ settingsMessages.push("ai-ops \uD0A4 \uC81C\uAC70 (\uC0AC\uC6A9\uC790 \uC124\uC815 \uBCF4\uC874): .claude/settings.local.json");
1947
1928
  }
1948
1929
  if (manifest.settings?.gemini) {
1949
1930
  const status = uninstallGeminiSettings(basePath, manifest.settings.gemini);
@@ -1957,46 +1938,48 @@ ${targetSkillDirs.map((f) => ` ${f}`).join("\n")}`);
1957
1938
  const removedSkillDirs = removeDirectories(basePath, targetSkillDirs);
1958
1939
  const dirs = collectManagedDirs(targetFiles);
1959
1940
  const removedDirs = cleanEmptyDirs(basePath, dirs);
1960
- rmSync6(manifestPath, { force: true });
1941
+ rmSync5(manifestPath, { force: true });
1961
1942
  if (result.deleted.length > 0) {
1962
- p7.log.success(`\uC0AD\uC81C \uC644\uB8CC (${result.deleted.length}\uAC1C):
1943
+ p6.log.success(`\uC0AD\uC81C \uC644\uB8CC (${result.deleted.length}\uAC1C):
1963
1944
  ${result.deleted.map((f) => ` ${f}`).join("\n")}`);
1964
1945
  }
1965
1946
  if (result.cleaned.length > 0) {
1966
- p7.log.success(
1947
+ p6.log.success(
1967
1948
  `\uC139\uC158 \uC81C\uAC70 \uC644\uB8CC (\uC0AC\uC6A9\uC790 \uB0B4\uC6A9 \uBCF4\uC874, ${result.cleaned.length}\uAC1C):
1968
1949
  ${result.cleaned.map((f) => ` ${f}`).join("\n")}`
1969
1950
  );
1970
1951
  }
1971
1952
  if (result.skipped.length > 0) {
1972
- p7.log.warn(
1953
+ p6.log.warn(
1973
1954
  `\uAC74\uB108\uB700 (non-managed \uD30C\uC77C \uBCF4\uD638, ${result.skipped.length}\uAC1C):
1974
1955
  ${result.skipped.map((f) => ` ${f}`).join("\n")}`
1975
1956
  );
1976
1957
  }
1977
1958
  if (result.notFound.length > 0) {
1978
- p7.log.info(`\uC774\uBBF8 \uC5C6\uC74C (${result.notFound.length}\uAC1C):
1959
+ p6.log.info(`\uC774\uBBF8 \uC5C6\uC74C (${result.notFound.length}\uAC1C):
1979
1960
  ${result.notFound.map((f) => ` ${f}`).join("\n")}`);
1980
1961
  }
1981
1962
  if (removedDirs.length > 0) {
1982
- p7.log.info(`\uBE48 \uB514\uB809\uD1A0\uB9AC \uC815\uB9AC (${removedDirs.length}\uAC1C):
1963
+ p6.log.info(`\uBE48 \uB514\uB809\uD1A0\uB9AC \uC815\uB9AC (${removedDirs.length}\uAC1C):
1983
1964
  ${removedDirs.map((d) => ` ${d}`).join("\n")}`);
1984
1965
  }
1985
1966
  if (removedSkillDirs.length > 0) {
1986
- p7.log.success(`skill \uB514\uB809\uD1A0\uB9AC \uC0AD\uC81C (${removedSkillDirs.length}\uAC1C):
1987
- ${removedSkillDirs.map((d) => ` ${d}`).join("\n")}`);
1967
+ p6.log.success(
1968
+ `skill \uB514\uB809\uD1A0\uB9AC \uC0AD\uC81C (${removedSkillDirs.length}\uAC1C):
1969
+ ${removedSkillDirs.map((d) => ` ${d}`).join("\n")}`
1970
+ );
1988
1971
  }
1989
1972
  if (settingsMessages.length > 0) {
1990
- p7.log.success(`\uC124\uC815 \uD30C\uC77C \uCC98\uB9AC:
1973
+ p6.log.success(`\uC124\uC815 \uD30C\uC77C \uCC98\uB9AC:
1991
1974
  ${settingsMessages.map((m) => ` ${m}`).join("\n")}`);
1992
1975
  }
1993
- p7.log.success(`manifest \uC0AD\uC81C: ${MANIFEST_FILENAME}`);
1994
- p7.outro("ai-ops uninstall \uC644\uB8CC");
1976
+ p6.log.success(`manifest \uC0AD\uC81C: ${MANIFEST_FILENAME}`);
1977
+ p6.outro("ai-ops uninstall \uC644\uB8CC");
1995
1978
  };
1996
1979
 
1997
1980
  // src/commands/skill.ts
1998
- import * as p8 from "@clack/prompts";
1999
- import { rmSync as rmSync7 } from "fs";
1981
+ import * as p7 from "@clack/prompts";
1982
+ import { rmSync as rmSync6 } from "fs";
2000
1983
  var resolveScopeContext = (opts) => {
2001
1984
  const scope = resolveSkillScope(opts);
2002
1985
  return {
@@ -2032,7 +2015,7 @@ var writeProjectSkillState = (params) => {
2032
2015
  const nextTools = params.nextSkill !== void 0 ? [.../* @__PURE__ */ new Set([...previous?.tools ?? [], ...params.nextSkill.tools])] : previous?.tools ?? [];
2033
2016
  const hasProjectState = (previous?.installed_rules.length ?? 0) > 0 || (previous?.installed_files?.length ?? 0) > 0 || (previous?.appended_files?.length ?? 0) > 0 || installedSkills.length > 0 || previous?.settings !== void 0;
2034
2017
  if (!hasProjectState) {
2035
- rmSync7(manifestPath, { force: true });
2018
+ rmSync6(manifestPath, { force: true });
2036
2019
  return;
2037
2020
  }
2038
2021
  const manifest = buildManifest({
@@ -2055,7 +2038,7 @@ var writeUserSkillState = (params) => {
2055
2038
  const previous = readSkillRegistry(registryPath);
2056
2039
  const skills = params.removeSkillId ? removeInstalledSkill(previous?.skills ?? [], params.removeSkillId) : params.nextSkill ? upsertInstalledSkill(previous?.skills ?? [], params.nextSkill) : previous?.skills ?? [];
2057
2040
  if (skills.length === 0) {
2058
- rmSync7(registryPath, { force: true });
2041
+ rmSync6(registryPath, { force: true });
2059
2042
  return;
2060
2043
  }
2061
2044
  writeSkillRegistry(registryPath, {
@@ -2109,7 +2092,7 @@ var skillListCommand = async (opts) => {
2109
2092
  const { scope, basePath } = resolveScopeContext(opts);
2110
2093
  const { allSkills } = loadCompilerInputs();
2111
2094
  const installedSkills = readInstalledSkills(scope, basePath);
2112
- p8.intro(`ai-ops skill list (${scope})`);
2095
+ p7.intro(`ai-ops skill list (${scope})`);
2113
2096
  const sections = [
2114
2097
  { kind: "reference", title: "reference skills" },
2115
2098
  { kind: "task", title: "task skills" }
@@ -2125,8 +2108,8 @@ var skillListCommand = async (opts) => {
2125
2108
  return `${title}
2126
2109
  ${lines.join("\n")}`;
2127
2110
  }).filter((section) => section !== null);
2128
- p8.log.info(sections.join("\n\n"));
2129
- p8.outro("ai-ops skill list \uC644\uB8CC");
2111
+ p7.log.info(sections.join("\n\n"));
2112
+ p7.outro("ai-ops skill list \uC644\uB8CC");
2130
2113
  };
2131
2114
  var skillInstallCommand = async (skillId, opts) => {
2132
2115
  const { scope, basePath } = resolveScopeContext(opts);
@@ -2134,7 +2117,7 @@ var skillInstallCommand = async (skillId, opts) => {
2134
2117
  const skill = resolveSkillById(allSkills, skillId);
2135
2118
  assertScopeAllowed(skill, scope);
2136
2119
  const requestedTools = resolveRequestedTools({ requested: opts.tool, supported: skill.supported_tools });
2137
- p8.intro(`ai-ops skill install ${skillId}`);
2120
+ p7.intro(`ai-ops skill install ${skillId}`);
2138
2121
  const installedSkill = installSkill({
2139
2122
  skill,
2140
2123
  requestedTools,
@@ -2143,18 +2126,18 @@ var skillInstallCommand = async (skillId, opts) => {
2143
2126
  cliVersion,
2144
2127
  sourceHash
2145
2128
  });
2146
- p8.log.success(`\uC124\uCE58 \uC644\uB8CC: ${installedSkill.id} (${installedSkill.installed_paths.join(", ")})`);
2147
- p8.outro("ai-ops skill install \uC644\uB8CC");
2129
+ p7.log.success(`\uC124\uCE58 \uC644\uB8CC: ${installedSkill.id} (${installedSkill.installed_paths.join(", ")})`);
2130
+ p7.outro("ai-ops skill install \uC644\uB8CC");
2148
2131
  };
2149
2132
  var skillDiffCommand = async (skillId, opts) => {
2150
2133
  const { scope, basePath } = resolveScopeContext(opts);
2151
2134
  const { allSkills } = loadCompilerInputs();
2152
2135
  const installedSkills = readInstalledSkills(scope, basePath);
2153
2136
  const targets = skillId ? installedSkills.filter((skill) => skill.id === skillId) : installedSkills;
2154
- p8.intro(`ai-ops skill diff (${scope})`);
2137
+ p7.intro(`ai-ops skill diff (${scope})`);
2155
2138
  if (targets.length === 0) {
2156
- p8.log.warn("\uBE44\uAD50\uD560 \uC124\uCE58\uB41C skill\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
2157
- p8.outro("ai-ops skill diff \uC644\uB8CC");
2139
+ p7.log.warn("\uBE44\uAD50\uD560 \uC124\uCE58\uB41C skill\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
2140
+ p7.outro("ai-ops skill diff \uC644\uB8CC");
2158
2141
  return;
2159
2142
  }
2160
2143
  const lines = targets.map((installedSkill) => {
@@ -2167,18 +2150,18 @@ var skillDiffCommand = async (skillId, opts) => {
2167
2150
  const changed = next.sourceHash !== installedSkill.sourceHash;
2168
2151
  return `- ${installedSkill.id}: ${changed ? "changed" : "up-to-date"} (${installedSkill.sourceHash} -> ${next.sourceHash})`;
2169
2152
  });
2170
- p8.log.info(lines.join("\n"));
2171
- p8.outro("ai-ops skill diff \uC644\uB8CC");
2153
+ p7.log.info(lines.join("\n"));
2154
+ p7.outro("ai-ops skill diff \uC644\uB8CC");
2172
2155
  };
2173
2156
  var skillUpdateCommand = async (skillId, opts) => {
2174
2157
  const { scope, basePath } = resolveScopeContext(opts);
2175
2158
  const { allSkills, sourceHash, cliVersion } = loadCompilerInputs();
2176
2159
  const installedSkills = readInstalledSkills(scope, basePath);
2177
2160
  const targets = skillId ? installedSkills.filter((skill) => skill.id === skillId) : installedSkills;
2178
- p8.intro(`ai-ops skill update (${scope})`);
2161
+ p7.intro(`ai-ops skill update (${scope})`);
2179
2162
  if (targets.length === 0) {
2180
- p8.log.warn("\uAC31\uC2E0\uD560 \uC124\uCE58\uB41C skill\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
2181
- p8.outro("ai-ops skill update \uC644\uB8CC");
2163
+ p7.log.warn("\uAC31\uC2E0\uD560 \uC124\uCE58\uB41C skill\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
2164
+ p7.outro("ai-ops skill update \uC644\uB8CC");
2182
2165
  return;
2183
2166
  }
2184
2167
  const nextInstalledSkills = targets.map((installedSkill) => {
@@ -2195,7 +2178,7 @@ var skillUpdateCommand = async (skillId, opts) => {
2195
2178
  const manifestPath = resolveManifestPath(basePath);
2196
2179
  const previous = readManifest(manifestPath);
2197
2180
  if (!previous) {
2198
- p8.log.error("project manifest\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.");
2181
+ p7.log.error("project manifest\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.");
2199
2182
  process.exit(1);
2200
2183
  }
2201
2184
  const untouched = (previous.installed_skills ?? []).filter(
@@ -2229,18 +2212,18 @@ var skillUpdateCommand = async (skillId, opts) => {
2229
2212
  generatedAt: (/* @__PURE__ */ new Date()).toISOString()
2230
2213
  });
2231
2214
  }
2232
- p8.log.success(`\uAC31\uC2E0 \uC644\uB8CC: ${nextInstalledSkills.map((skill) => skill.id).join(", ")}`);
2233
- p8.outro("ai-ops skill update \uC644\uB8CC");
2215
+ p7.log.success(`\uAC31\uC2E0 \uC644\uB8CC: ${nextInstalledSkills.map((skill) => skill.id).join(", ")}`);
2216
+ p7.outro("ai-ops skill update \uC644\uB8CC");
2234
2217
  };
2235
2218
  var skillUninstallCommand = async (skillId, opts) => {
2236
2219
  const { scope, basePath } = resolveScopeContext(opts);
2237
2220
  const { sourceHash, cliVersion } = loadCompilerInputs();
2238
2221
  const installedSkills = readInstalledSkills(scope, basePath);
2239
2222
  const installedSkill = findInstalledSkill(installedSkills, skillId);
2240
- p8.intro(`ai-ops skill uninstall ${skillId}`);
2223
+ p7.intro(`ai-ops skill uninstall ${skillId}`);
2241
2224
  if (!installedSkill) {
2242
- p8.log.warn("\uC124\uCE58\uB41C skill\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.");
2243
- p8.outro("ai-ops skill uninstall \uC644\uB8CC");
2225
+ p7.log.warn("\uC124\uCE58\uB41C skill\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.");
2226
+ p7.outro("ai-ops skill uninstall \uC644\uB8CC");
2244
2227
  return;
2245
2228
  }
2246
2229
  const removed = removeDirectories(basePath, installedSkill.installed_paths);
@@ -2258,8 +2241,29 @@ var skillUninstallCommand = async (skillId, opts) => {
2258
2241
  removeSkillId: skillId
2259
2242
  });
2260
2243
  }
2261
- p8.log.success(`\uC81C\uAC70 \uC644\uB8CC: ${removed.join(", ")}`);
2262
- p8.outro("ai-ops skill uninstall \uC644\uB8CC");
2244
+ p7.log.success(`\uC81C\uAC70 \uC644\uB8CC: ${removed.join(", ")}`);
2245
+ p7.outro("ai-ops skill uninstall \uC644\uB8CC");
2246
+ };
2247
+
2248
+ // src/commands/spec.ts
2249
+ import { existsSync as existsSync8, mkdirSync as mkdirSync6, writeFileSync as writeFileSync8 } from "fs";
2250
+ import { join as join13, dirname as dirname8 } from "path";
2251
+ import * as p8 from "@clack/prompts";
2252
+ var specInitCommand = async (opts) => {
2253
+ p8.intro("ai-ops spec init");
2254
+ const specsDir = join13(process.cwd(), "specs");
2255
+ if (existsSync8(specsDir) && !opts.force) {
2256
+ p8.log.error("specs/ \uB514\uB809\uD1A0\uB9AC\uAC00 \uC774\uBBF8 \uC874\uC7AC\uD569\uB2C8\uB2E4. \uB36E\uC5B4\uC4F0\uB824\uBA74 --force \uC635\uC158\uC744 \uC0AC\uC6A9\uD558\uC138\uC694.");
2257
+ process.exit(1);
2258
+ }
2259
+ const actions = buildSpecInitPlan();
2260
+ for (const action of actions) {
2261
+ const dest = join13(process.cwd(), action.relativePath);
2262
+ mkdirSync6(dirname8(dest), { recursive: true });
2263
+ writeFileSync8(dest, action.content, "utf-8");
2264
+ p8.log.success(`\uC0DD\uC131: ${action.relativePath}`);
2265
+ }
2266
+ p8.outro("ai-ops spec init \uC644\uB8CC");
2263
2267
  };
2264
2268
 
2265
2269
  // src/bin/index.ts
@@ -2286,5 +2290,7 @@ applySkillScopeOptions(skillCommand.command("update [skillId]").description("ski
2286
2290
  applySkillScopeOptions(skillCommand.command("uninstall <skillId>").description("skill \uC81C\uAC70")).action(
2287
2291
  (skillId, opts) => skillUninstallCommand(skillId, opts)
2288
2292
  );
2293
+ var specCommand = program.command("spec").description("spec \uD30C\uC774\uD504\uB77C\uC778 \uAD00\uB9AC");
2294
+ specCommand.command("init").description("specs/ \uB514\uB809\uD1A0\uB9AC \uAD6C\uC870 \uCD08\uAE30\uD654").option("--force", "\uC774\uBBF8 \uC874\uC7AC\uD574\uB3C4 \uAC15\uC81C \uC7AC\uC0DD\uC131", false).action((opts) => specInitCommand(opts));
2289
2295
  program.parse();
2290
2296
  //# sourceMappingURL=index.js.map