ai-ops-cli 0.1.11 → 0.1.14

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,8 +4,8 @@
4
4
  import { Command } from "commander";
5
5
 
6
6
  // src/commands/init.ts
7
- import * as p2 from "@clack/prompts";
8
- import { join as join8 } from "path";
7
+ import * as p3 from "@clack/prompts";
8
+ import { join as join9 } from "path";
9
9
 
10
10
  // src/core/schemas/rule.schema.ts
11
11
  import { z } from "zod";
@@ -42,6 +42,10 @@ var PresetSchema = z2.object({
42
42
 
43
43
  // src/core/schemas/manifest.schema.ts
44
44
  import { z as z3 } from "zod";
45
+ var SettingsConfigSchema = z3.object({
46
+ claude: z3.array(z3.string().min(1)).optional(),
47
+ gemini: z3.array(z3.string().min(1)).optional()
48
+ }).strict();
45
49
  var WorkspaceEntrySchema = z3.object({
46
50
  preset: z3.string().min(1),
47
51
  rules: z3.array(z3.string().min(1))
@@ -58,6 +62,8 @@ var ManifestSchema = z3.object({
58
62
  installed_files: z3.array(z3.string().min(1)).optional(),
59
63
  /** non-managed 파일에 섹션을 append한 경우 추적 (uninstall 시 섹션만 제거) */
60
64
  appended_files: z3.array(z3.string().min(1)).optional(),
65
+ /** init 시 선택된 settings 항목 — update 시 재생성에 사용 */
66
+ settings: SettingsConfigSchema.optional(),
61
67
  /** SSOT 데이터 파일들의 deterministic SHA-256 해시 (6자리 hex). diff/update 판단 기준 */
62
68
  sourceHash: z3.string().regex(/^[a-f0-9]{6}$/, "sourceHash must be 6 lowercase hex chars"),
63
69
  generatedAt: z3.string().datetime({ offset: true })
@@ -67,18 +73,48 @@ var ManifestSchema = z3.object({
67
73
  import { readFileSync, readdirSync } from "fs";
68
74
  import { resolve } from "path";
69
75
  import { parse } from "yaml";
76
+ var PRESET_RULE_BUNDLES = {
77
+ "frontend-web": {
78
+ graphql: ["graphql-core", "graphql-client-web"]
79
+ },
80
+ "frontend-app": {
81
+ graphql: ["graphql-core", "graphql-client-app"]
82
+ },
83
+ "backend-ts": {
84
+ graphql: ["graphql-core", "graphql-server"]
85
+ }
86
+ };
70
87
  var sortRulesByPriority = (rules) => [...rules].sort((a, b) => b.priority - a.priority);
71
- var parseRawPresets = (raw) => Object.entries(raw).map(([id, value]) => PresetSchema.parse({ id, ...value }));
72
- var excludeRules = (rules, excludeIds) => {
73
- const excludeSet = new Set(excludeIds);
74
- return rules.filter((r) => !excludeSet.has(r.id));
88
+ var deduplicateRulesById = (rules) => {
89
+ const seen = /* @__PURE__ */ new Set();
90
+ return rules.filter((rule) => {
91
+ if (seen.has(rule.id)) return false;
92
+ seen.add(rule.id);
93
+ return true;
94
+ });
75
95
  };
96
+ var resolveBundledRuleIds = (presetId, logicalRuleId) => {
97
+ const presetBundles = PRESET_RULE_BUNDLES[presetId];
98
+ if (!presetBundles) return [logicalRuleId];
99
+ return presetBundles[logicalRuleId] ?? [logicalRuleId];
100
+ };
101
+ var resolveRuleById = (ruleId, allRules, context) => {
102
+ const found = allRules.find((rule) => rule.id === ruleId);
103
+ if (!found) {
104
+ const suffix = context ? ` (from ${context})` : "";
105
+ throw new Error(`Rule not found: ${ruleId}${suffix}`);
106
+ }
107
+ return found;
108
+ };
109
+ var parseRawPresets = (raw) => Object.entries(raw).map(([id, value]) => PresetSchema.parse({ id, ...value }));
110
+ var resolvePresetRuleGroups = (preset, allRules) => preset.rules.map((logicalRuleId) => {
111
+ const bundledRuleIds = resolveBundledRuleIds(preset.id, logicalRuleId);
112
+ const rules = bundledRuleIds.map((ruleId) => resolveRuleById(ruleId, allRules, `${preset.id}:${logicalRuleId}`));
113
+ return { id: logicalRuleId, rules };
114
+ });
76
115
  var resolvePresetRules = (preset, allRules) => {
77
- const resolved = preset.rules.map((ruleId) => {
78
- const found = allRules.find((r) => r.id === ruleId);
79
- if (!found) throw new Error(`Rule not found: ${ruleId}`);
80
- return found;
81
- });
116
+ const groups = resolvePresetRuleGroups(preset, allRules);
117
+ const resolved = deduplicateRulesById(groups.flatMap((group) => group.rules));
82
118
  return sortRulesByPriority(resolved);
83
119
  };
84
120
  var loadRuleFile = (filePath) => {
@@ -106,7 +142,10 @@ var CLAUDE_CODE_PATH_GLOBS = {
106
142
  nextjs: ["**/app/**", "next.config.*", "**/middleware.ts"],
107
143
  nestjs: ["**/*.module.ts", "**/*.controller.ts", "**/*.service.ts"],
108
144
  "nestjs-graphql": ["**/*.resolver.ts"],
109
- graphql: ["**/*.graphql", "**/*.gql"],
145
+ "graphql-core": ["**/*.graphql", "**/*.gql"],
146
+ "graphql-client-web": ["**/*.graphql", "**/*.gql", "**/*.tsx", "**/*.ts"],
147
+ "graphql-client-app": ["**/*.graphql", "**/*.gql", "lib/**/*.dart"],
148
+ "graphql-server": ["**/*.graphql", "**/*.gql", "**/*.resolver.ts"],
110
149
  "prisma-postgresql": ["prisma/**", "**/*.prisma"],
111
150
  "shadcn-ui": ["**/components/ui/**"],
112
151
  flutter: ["lib/**/*.dart"],
@@ -198,7 +237,7 @@ var partitionRules = (rules) => {
198
237
  return { global, domain };
199
238
  };
200
239
  var renderFrontmatter = (paths) => {
201
- const lines = paths.map((p6) => ` - "${p6}"`).join("\n");
240
+ const lines = paths.map((p7) => ` - "${p7}"`).join("\n");
202
241
  return `---
203
242
  paths:
204
243
  ${lines}
@@ -268,6 +307,10 @@ var buildManifest = (params) => ManifestSchema.parse({
268
307
  installed_rules: [...params.installedRules],
269
308
  installed_files: params.installedFiles ? [...params.installedFiles] : void 0,
270
309
  appended_files: params.appendedFiles && params.appendedFiles.length > 0 ? [...params.appendedFiles] : void 0,
310
+ settings: params.settings ? {
311
+ claude: params.settings.claude ? [...params.settings.claude] : void 0,
312
+ gemini: params.settings.gemini ? [...params.settings.gemini] : void 0
313
+ } : void 0,
271
314
  sourceHash: params.sourceHash,
272
315
  generatedAt: (/* @__PURE__ */ new Date()).toISOString()
273
316
  });
@@ -351,7 +394,7 @@ var computeDiff = (params) => {
351
394
 
352
395
  // src/core/install-plan.ts
353
396
  import { join as join3 } from "path";
354
- var CODEX_PLAN_SECTION = "\n\n---\n\n## Plan\n\nSave plans to `.codex/plans/<timestamp>-<topic>.md` when creating or updating plans in plan mode.";
397
+ var CODEX_PLAN_SECTION = "\n\n---\n\n## Plan Snapshot\n\nBefore implementation, save the latest `<proposed_plan>` to `.codex/plans/YYYYMMDD_<topic>.md` (`<topic>` = kebab-case title, fallback `task`).\nEnsure `.codex/plans` exists; if the filename exists, append `-v2`, `-v3`, ...\nDo not start any mutating implementation step until this file is written.";
355
398
  var buildInstallPlan = (params) => {
356
399
  const { toolId, renderResult, meta } = params;
357
400
  if (toolId === "claude-code" && renderResult.tool === "claude-code") {
@@ -590,6 +633,75 @@ var installGeminiSettings = (basePath, selectedValues) => {
590
633
  writeFileSync3(settingsPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
591
634
  };
592
635
 
636
+ // src/lib/claude-settings.ts
637
+ import * as p2 from "@clack/prompts";
638
+ import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
639
+ import { join as join8 } from "path";
640
+ var SETTING_GROUPS2 = [
641
+ {
642
+ value: "model",
643
+ label: "Model \u2014 Plan \uBAA8\uB4DC \uBAA8\uB378",
644
+ hint: "model: opusplan \u2014 Plan \uBAA8\uB4DC\uC5D0\uC11C Opus \uBAA8\uB378 \uC0AC\uC6A9",
645
+ patch: { model: "opusplan" }
646
+ },
647
+ {
648
+ value: "plansDirectory",
649
+ label: "Plans Directory \u2014 \uACC4\uD68D \uD30C\uC77C \uC800\uC7A5 \uACBD\uB85C",
650
+ hint: "plansDirectory: ./.claude/plans \u2014 \uACC4\uD68D \uD30C\uC77C\uC744 .claude/plans\uC5D0 \uC800\uC7A5",
651
+ patch: { plansDirectory: "./.claude/plans" }
652
+ }
653
+ ];
654
+ var deepMerge2 = (base, patch) => {
655
+ const result = { ...base };
656
+ for (const [key, value] of Object.entries(patch)) {
657
+ if (value !== null && typeof value === "object" && !Array.isArray(value) && typeof result[key] === "object" && result[key] !== null) {
658
+ result[key] = deepMerge2(result[key], value);
659
+ } else {
660
+ result[key] = value;
661
+ }
662
+ }
663
+ return result;
664
+ };
665
+ var promptClaudeSettings = async () => {
666
+ const wantSettings = await p2.confirm({
667
+ message: "Claude Code \uC124\uC815 \uD30C\uC77C(.claude/settings.local.json)\uC744 \uC124\uCE58\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
668
+ initialValue: true
669
+ });
670
+ if (p2.isCancel(wantSettings) || !wantSettings) return null;
671
+ const selected = await p2.multiselect({
672
+ message: "\uC124\uCE58\uD560 \uC124\uC815 \uD56D\uBAA9\uC744 \uC120\uD0DD\uD558\uC138\uC694 (\uC2A4\uD398\uC774\uC2A4\uB85C \uD1A0\uAE00)",
673
+ options: SETTING_GROUPS2.map((g) => ({
674
+ value: g.value,
675
+ label: g.label,
676
+ hint: g.hint
677
+ })),
678
+ initialValues: SETTING_GROUPS2.map((g) => g.value),
679
+ required: false
680
+ });
681
+ if (p2.isCancel(selected)) return null;
682
+ return selected;
683
+ };
684
+ var installClaudeSettings = (basePath, selectedValues) => {
685
+ if (selectedValues.length === 0) return;
686
+ const settingsDir = join8(basePath, ".claude");
687
+ const settingsPath = join8(settingsDir, "settings.local.json");
688
+ let existing = {};
689
+ if (existsSync4(settingsPath)) {
690
+ try {
691
+ existing = JSON.parse(readFileSync6(settingsPath, "utf-8"));
692
+ } catch {
693
+ }
694
+ }
695
+ let merged = existing;
696
+ for (const val of selectedValues) {
697
+ const group = SETTING_GROUPS2.find((g) => g.value === val);
698
+ if (!group) continue;
699
+ merged = deepMerge2(merged, group.patch);
700
+ }
701
+ mkdirSync4(settingsDir, { recursive: true });
702
+ writeFileSync4(settingsPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
703
+ };
704
+
593
705
  // src/commands/init.ts
594
706
  var TOOL_OPTIONS = [
595
707
  { value: "claude-code", label: "Claude Code" },
@@ -605,7 +717,7 @@ var deduplicateRules = (rules) => {
605
717
  });
606
718
  };
607
719
  var selectPresetAndFineTune = async (workspaceName, presets, allRules) => {
608
- const preset = await p2.select({
720
+ const preset = await p3.select({
609
721
  message: `[${workspaceName}] \uD504\uB9AC\uC14B\uC744 \uC120\uD0DD\uD558\uC138\uC694`,
610
722
  options: presets.map((pr) => ({
611
723
  value: pr,
@@ -613,25 +725,31 @@ var selectPresetAndFineTune = async (workspaceName, presets, allRules) => {
613
725
  hint: pr.description
614
726
  }))
615
727
  });
616
- if (p2.isCancel(preset)) return null;
617
- const presetRules = resolvePresetRules(preset, allRules);
618
- const globalRules = presetRules.filter(isGlobalRule);
619
- const domainRules = presetRules.filter((r) => !isGlobalRule(r));
728
+ if (p3.isCancel(preset)) return null;
729
+ const presetRuleGroups = resolvePresetRuleGroups(preset, allRules);
730
+ const globalGroups = presetRuleGroups.filter((group) => group.rules.every(isGlobalRule));
731
+ const domainGroups = presetRuleGroups.filter((group) => !group.rules.every(isGlobalRule));
732
+ const globalGroupIds = globalGroups.map((group) => group.id);
733
+ const globalRules = globalGroupIds.length > 0 ? resolvePresetRules({ ...preset, rules: globalGroupIds }, allRules) : [];
620
734
  if (globalRules.length > 0) {
621
- p2.note(globalRules.map((r) => ` \u2713 ${r.id}`).join("\n"), `[${workspaceName}] \uAE30\uBCF8 \uADDC\uCE59 (\uC7A0\uAE08)`);
735
+ p3.note(globalRules.map((r) => ` \u2713 ${r.id}`).join("\n"), `[${workspaceName}] \uAE30\uBCF8 \uADDC\uCE59 (\uC7A0\uAE08)`);
622
736
  }
623
- if (domainRules.length === 0) {
624
- return { workspace: workspaceName, preset, finalRules: presetRules };
737
+ if (domainGroups.length === 0) {
738
+ return { workspace: workspaceName, preset, finalRules: resolvePresetRules(preset, allRules) };
625
739
  }
626
- const selectedDomain = await p2.multiselect({
740
+ const selectedDomain = await p3.multiselect({
627
741
  message: `[${workspaceName}] \uB3C4\uBA54\uC778 \uADDC\uCE59 \uC120\uD0DD (\uD574\uC81C\uD558\uC5EC \uC81C\uC678)`,
628
- options: domainRules.map((r) => ({ value: r.id, label: r.id })),
629
- initialValues: domainRules.map((r) => r.id),
742
+ options: domainGroups.map((group) => ({ value: group.id, label: group.id })),
743
+ initialValues: domainGroups.map((group) => group.id),
630
744
  required: false
631
745
  });
632
- if (p2.isCancel(selectedDomain)) return null;
633
- const excludeIds = domainRules.map((r) => r.id).filter((id) => !selectedDomain.includes(id));
634
- return { workspace: workspaceName, preset, finalRules: excludeRules(presetRules, excludeIds) };
746
+ if (p3.isCancel(selectedDomain)) return null;
747
+ const selectedLogicalRuleIds = [...globalGroupIds, ...selectedDomain];
748
+ return {
749
+ workspace: workspaceName,
750
+ preset,
751
+ finalRules: resolvePresetRules({ ...preset, rules: selectedLogicalRuleIds }, allRules)
752
+ };
635
753
  };
636
754
  var installHierarchicalMonorepo = (toolId, mappings, basePath, meta) => {
637
755
  const config = TOOL_OUTPUT_MAP[toolId];
@@ -641,7 +759,7 @@ var installHierarchicalMonorepo = (toolId, mappings, basePath, meta) => {
641
759
  const { global } = partitionRules(allRules);
642
760
  if (global.length > 0) {
643
761
  const rootAction = {
644
- relativePath: join8(config.dir, config.rootFileName),
762
+ relativePath: join9(config.dir, config.rootFileName),
645
763
  content: wrapWithHeader(renderRulesToMarkdown(global), meta)
646
764
  };
647
765
  const r = installFiles(basePath, [rootAction], meta);
@@ -652,7 +770,7 @@ var installHierarchicalMonorepo = (toolId, mappings, basePath, meta) => {
652
770
  const { domain } = partitionRules(mapping.finalRules);
653
771
  if (domain.length === 0) continue;
654
772
  const domainAction = {
655
- relativePath: join8(mapping.workspace, config.domainFileName),
773
+ relativePath: join9(mapping.workspace, config.domainFileName),
656
774
  content: wrapWithHeader(renderRulesToMarkdown(domain), meta)
657
775
  };
658
776
  const r = installFiles(basePath, [domainAction], meta);
@@ -675,22 +793,22 @@ var installClaudeCodeMonorepo = (mappings, basePath, meta) => {
675
793
  var initCommand = async () => {
676
794
  const basePath = resolveBasePath();
677
795
  const rulesDir = resolveRulesDir();
678
- p2.intro("ai-ops init");
679
- const selectedTools = await p2.multiselect({
796
+ p3.intro("ai-ops init");
797
+ const selectedTools = await p3.multiselect({
680
798
  message: "AI \uB3C4\uAD6C\uB97C \uC120\uD0DD\uD558\uC138\uC694",
681
799
  options: TOOL_OPTIONS,
682
800
  required: true
683
801
  });
684
- if (p2.isCancel(selectedTools)) {
685
- p2.cancel("\uCDE8\uC18C\uB428");
802
+ if (p3.isCancel(selectedTools)) {
803
+ p3.cancel("\uCDE8\uC18C\uB428");
686
804
  process.exit(0);
687
805
  }
688
- const isMonorepo = await p2.confirm({
806
+ const isMonorepo = await p3.confirm({
689
807
  message: "\uBAA8\uB178\uB808\uD3EC \uD504\uB85C\uC81D\uD2B8\uC785\uB2C8\uAE4C?",
690
808
  initialValue: false
691
809
  });
692
- if (p2.isCancel(isMonorepo)) {
693
- p2.cancel("\uCDE8\uC18C\uB428");
810
+ if (p3.isCancel(isMonorepo)) {
811
+ p3.cancel("\uCDE8\uC18C\uB428");
694
812
  process.exit(0);
695
813
  }
696
814
  const allRules = loadAllRules(rulesDir);
@@ -700,32 +818,33 @@ var initCommand = async () => {
700
818
  if (!isMonorepo) {
701
819
  const mapping = await selectPresetAndFineTune(".", presets, allRules);
702
820
  if (!mapping) {
703
- p2.cancel("\uCDE8\uC18C\uB428");
821
+ p3.cancel("\uCDE8\uC18C\uB428");
704
822
  process.exit(0);
705
823
  }
706
824
  mappings.push(mapping);
707
825
  } else {
708
826
  const candidates = listWorkspaceCandidates(basePath);
709
- const selectedWorkspaces = await p2.multiselect({
827
+ const selectedWorkspaces = await p3.multiselect({
710
828
  message: "\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4\uB97C \uC120\uD0DD\uD558\uC138\uC694",
711
829
  options: candidates.map((c) => ({ value: c, label: c })),
712
830
  required: true
713
831
  });
714
- if (p2.isCancel(selectedWorkspaces)) {
715
- p2.cancel("\uCDE8\uC18C\uB428");
832
+ if (p3.isCancel(selectedWorkspaces)) {
833
+ p3.cancel("\uCDE8\uC18C\uB428");
716
834
  process.exit(0);
717
835
  }
718
836
  for (const ws of selectedWorkspaces) {
719
837
  const mapping = await selectPresetAndFineTune(ws, presets, allRules);
720
838
  if (!mapping) {
721
- p2.cancel("\uCDE8\uC18C\uB428");
839
+ p3.cancel("\uCDE8\uC18C\uB428");
722
840
  process.exit(0);
723
841
  }
724
842
  mappings.push(mapping);
725
843
  }
726
844
  }
727
845
  const geminiSettingValues = selectedTools.includes("gemini") ? await promptGeminiSettings() : null;
728
- const s = p2.spinner();
846
+ const claudeSettingValues = selectedTools.includes("claude-code") ? await promptClaudeSettings() : null;
847
+ const s = p3.spinner();
729
848
  s.start("\uADDC\uCE59 \uC124\uCE58 \uC911...");
730
849
  const meta = { sourceHash, generatedAt: (/* @__PURE__ */ new Date()).toISOString() };
731
850
  const allInstalledFiles = [];
@@ -753,6 +872,10 @@ var initCommand = async () => {
753
872
  installGeminiSettings(basePath, geminiSettingValues);
754
873
  allInstalledFiles.push(".gemini/settings.json");
755
874
  }
875
+ if (claudeSettingValues && claudeSettingValues.length > 0) {
876
+ installClaudeSettings(basePath, claudeSettingValues);
877
+ allInstalledFiles.push(".claude/settings.local.json");
878
+ }
756
879
  s.stop("\uADDC\uCE59 \uC124\uCE58 \uC644\uB8CC");
757
880
  const allInstalledRuleIds = deduplicateRules(mappings.flatMap((m) => m.finalRules)).map((r) => r.id);
758
881
  const workspacesRecord = isMonorepo ? Object.fromEntries(
@@ -766,27 +889,31 @@ var initCommand = async () => {
766
889
  installedRules: allInstalledRuleIds,
767
890
  installedFiles: allInstalledFiles,
768
891
  appendedFiles: allAppended,
892
+ settings: claudeSettingValues || geminiSettingValues ? {
893
+ claude: claudeSettingValues ? [...claudeSettingValues] : void 0,
894
+ gemini: geminiSettingValues ? [...geminiSettingValues] : void 0
895
+ } : void 0,
769
896
  sourceHash
770
897
  });
771
898
  writeManifest(resolveManifestPath(basePath), manifest);
772
899
  if (allAppended.length > 0) {
773
- p2.log.info(`\uAE30\uC874 \uD30C\uC77C\uC5D0 \uC139\uC158 \uCD94\uAC00\uB428 (\uB0B4\uC6A9 \uBCF4\uC874):
900
+ p3.log.info(`\uAE30\uC874 \uD30C\uC77C\uC5D0 \uC139\uC158 \uCD94\uAC00\uB428 (\uB0B4\uC6A9 \uBCF4\uC874):
774
901
  ${allAppended.map((f) => ` ${f}`).join("\n")}`);
775
902
  }
776
- p2.log.success(`\uC124\uCE58\uB41C \uADDC\uCE59: ${allInstalledRuleIds.length}\uAC1C`);
777
- p2.outro("ai-ops init \uC644\uB8CC");
903
+ p3.log.success(`\uC124\uCE58\uB41C \uADDC\uCE59: ${allInstalledRuleIds.length}\uAC1C`);
904
+ p3.outro("ai-ops init \uC644\uB8CC");
778
905
  };
779
906
 
780
907
  // src/commands/update.ts
781
- import * as p3 from "@clack/prompts";
782
- import { join as join9 } from "path";
908
+ import * as p4 from "@clack/prompts";
909
+ import { join as join10 } from "path";
783
910
  var updateCommand = async (opts) => {
784
911
  const basePath = resolveBasePath();
785
912
  const manifestPath = resolveManifestPath(basePath);
786
- p3.intro("ai-ops update");
913
+ p4.intro("ai-ops update");
787
914
  const manifest = readManifest(manifestPath);
788
915
  if (!manifest) {
789
- p3.log.error("manifest\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 ai-ops init\uC744 \uC2E4\uD589\uD558\uC138\uC694.");
916
+ p4.log.error("manifest\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 ai-ops init\uC744 \uC2E4\uD589\uD558\uC138\uC694.");
790
917
  process.exit(1);
791
918
  }
792
919
  const rulesDir = resolveRulesDir();
@@ -797,11 +924,11 @@ var updateCommand = async (opts) => {
797
924
  currentSourceHash: sourceHash
798
925
  });
799
926
  if (diffResult.status === "up-to-date" && !opts.force) {
800
- p3.log.info("\uBCC0\uACBD \uC0AC\uD56D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
801
- p3.outro("ai-ops update \uC644\uB8CC");
927
+ p4.log.info("\uBCC0\uACBD \uC0AC\uD56D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
928
+ p4.outro("ai-ops update \uC644\uB8CC");
802
929
  return;
803
930
  }
804
- const s = p3.spinner();
931
+ const s = p4.spinner();
805
932
  s.start("\uADDC\uCE59 \uAC31\uC2E0 \uC911...");
806
933
  const allRules = loadAllRules(rulesDir);
807
934
  const meta = { sourceHash, generatedAt: (/* @__PURE__ */ new Date()).toISOString() };
@@ -830,7 +957,7 @@ var updateCommand = async (opts) => {
830
957
  const { global } = partitionRules(allRulesToInstall);
831
958
  if (global.length > 0) {
832
959
  const rootAction = {
833
- relativePath: join9(config.dir, config.rootFileName),
960
+ relativePath: join10(config.dir, config.rootFileName),
834
961
  content: wrapWithHeader(renderRulesToMarkdown(global), meta)
835
962
  };
836
963
  const r = installFiles(basePath, [rootAction], meta);
@@ -843,7 +970,7 @@ var updateCommand = async (opts) => {
843
970
  const { domain } = partitionRules(wsRules);
844
971
  if (domain.length === 0) continue;
845
972
  const domainAction = {
846
- relativePath: join9(ws, config.domainFileName),
973
+ relativePath: join10(ws, config.domainFileName),
847
974
  content: wrapWithHeader(renderRulesToMarkdown(domain), meta)
848
975
  };
849
976
  const r = installFiles(basePath, [domainAction], meta);
@@ -864,6 +991,12 @@ var updateCommand = async (opts) => {
864
991
  allAppended.push(...r.appended);
865
992
  }
866
993
  }
994
+ if (manifest.settings?.claude) {
995
+ installClaudeSettings(basePath, manifest.settings.claude);
996
+ }
997
+ if (manifest.settings?.gemini) {
998
+ installGeminiSettings(basePath, manifest.settings.gemini);
999
+ }
867
1000
  const newManifest = buildManifest({
868
1001
  tools: manifest.tools,
869
1002
  scope: manifest.scope,
@@ -872,21 +1005,25 @@ var updateCommand = async (opts) => {
872
1005
  installedRules: manifest.installed_rules,
873
1006
  installedFiles: allInstalledFiles.length > 0 ? allInstalledFiles : manifest.installed_files,
874
1007
  appendedFiles: allAppended.length > 0 ? allAppended : manifest.appended_files,
1008
+ settings: manifest.settings ? {
1009
+ claude: manifest.settings.claude,
1010
+ gemini: manifest.settings.gemini
1011
+ } : void 0,
875
1012
  sourceHash
876
1013
  });
877
1014
  writeManifest(manifestPath, newManifest);
878
1015
  s.stop("\uADDC\uCE59 \uAC31\uC2E0 \uC644\uB8CC");
879
- p3.outro("ai-ops update \uC644\uB8CC");
1016
+ p4.outro("ai-ops update \uC644\uB8CC");
880
1017
  };
881
1018
 
882
1019
  // src/commands/diff.ts
883
- import * as p4 from "@clack/prompts";
1020
+ import * as p5 from "@clack/prompts";
884
1021
  var diffCommand = async () => {
885
1022
  const basePath = resolveBasePath();
886
- p4.intro("ai-ops diff");
1023
+ p5.intro("ai-ops diff");
887
1024
  const manifest = readManifest(resolveManifestPath(basePath));
888
1025
  if (!manifest) {
889
- p4.log.error("manifest\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 ai-ops init\uC744 \uC2E4\uD589\uD558\uC138\uC694.");
1026
+ p5.log.error("manifest\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 ai-ops init\uC744 \uC2E4\uD589\uD558\uC138\uC694.");
890
1027
  process.exit(1);
891
1028
  }
892
1029
  const sourceHash = computeSourceHash(resolveRulesDir());
@@ -896,27 +1033,27 @@ var diffCommand = async () => {
896
1033
  currentSourceHash: sourceHash
897
1034
  });
898
1035
  if (result.status === "up-to-date") {
899
- p4.log.success("\uBCC0\uACBD \uC0AC\uD56D \uC5C6\uC74C. \uCD5C\uC2E0 \uC0C1\uD0DC\uC785\uB2C8\uB2E4.");
1036
+ p5.log.success("\uBCC0\uACBD \uC0AC\uD56D \uC5C6\uC74C. \uCD5C\uC2E0 \uC0C1\uD0DC\uC785\uB2C8\uB2E4.");
900
1037
  } else {
901
1038
  if (result.sourceChanged) {
902
- p4.log.warn(`\uC18C\uC2A4 \uBCC0\uACBD \uAC10\uC9C0: ${manifest.sourceHash} \u2192 ${sourceHash}`);
1039
+ p5.log.warn(`\uC18C\uC2A4 \uBCC0\uACBD \uAC10\uC9C0: ${manifest.sourceHash} \u2192 ${sourceHash}`);
903
1040
  }
904
1041
  if (result.added.length > 0) {
905
- p4.log.info(`\uCD94\uAC00\uB41C \uADDC\uCE59: ${result.added.join(", ")}`);
1042
+ p5.log.info(`\uCD94\uAC00\uB41C \uADDC\uCE59: ${result.added.join(", ")}`);
906
1043
  }
907
1044
  if (result.removed.length > 0) {
908
- p4.log.info(`\uC81C\uAC70\uB41C \uADDC\uCE59: ${result.removed.join(", ")}`);
1045
+ p5.log.info(`\uC81C\uAC70\uB41C \uADDC\uCE59: ${result.removed.join(", ")}`);
909
1046
  }
910
1047
  }
911
- p4.outro("ai-ops diff \uC644\uB8CC");
1048
+ p5.outro("ai-ops diff \uC644\uB8CC");
912
1049
  };
913
1050
 
914
1051
  // src/commands/uninstall.ts
915
- import * as p5 from "@clack/prompts";
1052
+ import * as p6 from "@clack/prompts";
916
1053
  import { rmSync as rmSync2 } from "fs";
917
1054
 
918
1055
  // src/lib/uninstall.ts
919
- import { existsSync as existsSync4, readFileSync as readFileSync6, rmSync, readdirSync as readdirSync4, writeFileSync as writeFileSync4 } from "fs";
1056
+ import { existsSync as existsSync5, readFileSync as readFileSync7, rmSync, readdirSync as readdirSync4, writeFileSync as writeFileSync5 } from "fs";
920
1057
  import { resolve as resolve6, dirname as dirname4 } from "path";
921
1058
  var removeFiles = (basePath, relativePaths) => {
922
1059
  const deleted = [];
@@ -925,15 +1062,15 @@ var removeFiles = (basePath, relativePaths) => {
925
1062
  const notFound = [];
926
1063
  for (const rel of relativePaths) {
927
1064
  const absPath = resolve6(basePath, rel);
928
- if (!existsSync4(absPath)) {
1065
+ if (!existsSync5(absPath)) {
929
1066
  notFound.push(rel);
930
1067
  continue;
931
1068
  }
932
- const content = readFileSync6(absPath, "utf-8");
1069
+ const content = readFileSync7(absPath, "utf-8");
933
1070
  if (!isManagedFile(content)) {
934
1071
  if (hasAiOpsSection(content)) {
935
1072
  const stripped = stripAiOpsSection(content);
936
- writeFileSync4(absPath, stripped, "utf-8");
1073
+ writeFileSync5(absPath, stripped, "utf-8");
937
1074
  cleaned.push(rel);
938
1075
  } else {
939
1076
  skipped.push(rel);
@@ -949,7 +1086,7 @@ var cleanEmptyDirs = (basePath, dirs) => {
949
1086
  const removed = [];
950
1087
  for (const dir of dirs) {
951
1088
  const absDir = resolve6(basePath, dir);
952
- if (!existsSync4(absDir)) continue;
1089
+ if (!existsSync5(absDir)) continue;
953
1090
  try {
954
1091
  const entries = readdirSync4(absDir);
955
1092
  if (entries.length === 0) {
@@ -976,10 +1113,10 @@ var collectManagedDirs = (relativePaths) => {
976
1113
  var uninstallCommand = async () => {
977
1114
  const basePath = resolveBasePath();
978
1115
  const manifestPath = resolveManifestPath(basePath);
979
- p5.intro("ai-ops uninstall");
1116
+ p6.intro("ai-ops uninstall");
980
1117
  const manifest = readManifest(manifestPath);
981
1118
  if (!manifest) {
982
- p5.log.error("manifest\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 ai-ops init\uC744 \uC2E4\uD589\uD558\uC138\uC694.");
1119
+ p6.log.error("manifest\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 ai-ops init\uC744 \uC2E4\uD589\uD558\uC138\uC694.");
983
1120
  process.exit(1);
984
1121
  }
985
1122
  const targetFiles = [
@@ -987,18 +1124,18 @@ var uninstallCommand = async () => {
987
1124
  ...manifest.appended_files ?? []
988
1125
  ];
989
1126
  if (targetFiles.length === 0) {
990
- p5.log.warn("\uC0AD\uC81C\uD560 \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
991
- p5.outro("ai-ops uninstall \uC644\uB8CC");
1127
+ p6.log.warn("\uC0AD\uC81C\uD560 \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
1128
+ p6.outro("ai-ops uninstall \uC644\uB8CC");
992
1129
  return;
993
1130
  }
994
- p5.log.info(`\uC0AD\uC81C \uB300\uC0C1 \uD30C\uC77C (${targetFiles.length}\uAC1C):
1131
+ p6.log.info(`\uC0AD\uC81C \uB300\uC0C1 \uD30C\uC77C (${targetFiles.length}\uAC1C):
995
1132
  ${targetFiles.map((f) => ` ${f}`).join("\n")}`);
996
- const confirmed = await p5.confirm({
1133
+ const confirmed = await p6.confirm({
997
1134
  message: "\uC704 \uD30C\uC77C\uACFC manifest\uB97C \uBAA8\uB450 \uC0AD\uC81C\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
998
1135
  initialValue: false
999
1136
  });
1000
- if (p5.isCancel(confirmed) || !confirmed) {
1001
- p5.cancel("\uCDE8\uC18C\uB428");
1137
+ if (p6.isCancel(confirmed) || !confirmed) {
1138
+ p6.cancel("\uCDE8\uC18C\uB428");
1002
1139
  process.exit(0);
1003
1140
  }
1004
1141
  const result = removeFiles(basePath, targetFiles);
@@ -1006,31 +1143,31 @@ ${targetFiles.map((f) => ` ${f}`).join("\n")}`);
1006
1143
  const removedDirs = cleanEmptyDirs(basePath, dirs);
1007
1144
  rmSync2(manifestPath, { force: true });
1008
1145
  if (result.deleted.length > 0) {
1009
- p5.log.success(`\uC0AD\uC81C \uC644\uB8CC (${result.deleted.length}\uAC1C):
1146
+ p6.log.success(`\uC0AD\uC81C \uC644\uB8CC (${result.deleted.length}\uAC1C):
1010
1147
  ${result.deleted.map((f) => ` ${f}`).join("\n")}`);
1011
1148
  }
1012
1149
  if (result.cleaned.length > 0) {
1013
- p5.log.success(
1150
+ p6.log.success(
1014
1151
  `\uC139\uC158 \uC81C\uAC70 \uC644\uB8CC (\uC0AC\uC6A9\uC790 \uB0B4\uC6A9 \uBCF4\uC874, ${result.cleaned.length}\uAC1C):
1015
1152
  ${result.cleaned.map((f) => ` ${f}`).join("\n")}`
1016
1153
  );
1017
1154
  }
1018
1155
  if (result.skipped.length > 0) {
1019
- p5.log.warn(
1156
+ p6.log.warn(
1020
1157
  `\uAC74\uB108\uB700 (non-managed \uD30C\uC77C \uBCF4\uD638, ${result.skipped.length}\uAC1C):
1021
1158
  ${result.skipped.map((f) => ` ${f}`).join("\n")}`
1022
1159
  );
1023
1160
  }
1024
1161
  if (result.notFound.length > 0) {
1025
- p5.log.info(`\uC774\uBBF8 \uC5C6\uC74C (${result.notFound.length}\uAC1C):
1162
+ p6.log.info(`\uC774\uBBF8 \uC5C6\uC74C (${result.notFound.length}\uAC1C):
1026
1163
  ${result.notFound.map((f) => ` ${f}`).join("\n")}`);
1027
1164
  }
1028
1165
  if (removedDirs.length > 0) {
1029
- p5.log.info(`\uBE48 \uB514\uB809\uD1A0\uB9AC \uC815\uB9AC (${removedDirs.length}\uAC1C):
1166
+ p6.log.info(`\uBE48 \uB514\uB809\uD1A0\uB9AC \uC815\uB9AC (${removedDirs.length}\uAC1C):
1030
1167
  ${removedDirs.map((d) => ` ${d}`).join("\n")}`);
1031
1168
  }
1032
- p5.log.success(`manifest \uC0AD\uC81C: ${MANIFEST_FILENAME}`);
1033
- p5.outro("ai-ops uninstall \uC644\uB8CC");
1169
+ p6.log.success(`manifest \uC0AD\uC81C: ${MANIFEST_FILENAME}`);
1170
+ p6.outro("ai-ops uninstall \uC644\uB8CC");
1034
1171
  };
1035
1172
 
1036
1173
  // src/bin/index.ts