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/data/presets.yaml +2 -0
- package/data/rules/graphql-client-app.yaml +29 -0
- package/data/rules/graphql-client-web.yaml +30 -0
- package/data/rules/graphql-core.yaml +31 -0
- package/data/rules/graphql-server.yaml +33 -0
- package/data/rules/libs-backend-ts.yaml +0 -2
- package/data/rules/libs-frontend-web.yaml +0 -5
- package/dist/bin/index.js +222 -85
- package/dist/bin/index.js.map +1 -1
- package/package.json +1 -1
- package/data/rules/graphql.yaml +0 -34
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
|
|
8
|
-
import { join as
|
|
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
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
|
78
|
-
|
|
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((
|
|
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\
|
|
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
|
|
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 (
|
|
617
|
-
const
|
|
618
|
-
const
|
|
619
|
-
const
|
|
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
|
-
|
|
735
|
+
p3.note(globalRules.map((r) => ` \u2713 ${r.id}`).join("\n"), `[${workspaceName}] \uAE30\uBCF8 \uADDC\uCE59 (\uC7A0\uAE08)`);
|
|
622
736
|
}
|
|
623
|
-
if (
|
|
624
|
-
return { workspace: workspaceName, preset, finalRules:
|
|
737
|
+
if (domainGroups.length === 0) {
|
|
738
|
+
return { workspace: workspaceName, preset, finalRules: resolvePresetRules(preset, allRules) };
|
|
625
739
|
}
|
|
626
|
-
const selectedDomain = await
|
|
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:
|
|
629
|
-
initialValues:
|
|
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 (
|
|
633
|
-
const
|
|
634
|
-
return {
|
|
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:
|
|
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:
|
|
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
|
-
|
|
679
|
-
const selectedTools = await
|
|
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 (
|
|
685
|
-
|
|
802
|
+
if (p3.isCancel(selectedTools)) {
|
|
803
|
+
p3.cancel("\uCDE8\uC18C\uB428");
|
|
686
804
|
process.exit(0);
|
|
687
805
|
}
|
|
688
|
-
const isMonorepo = await
|
|
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 (
|
|
693
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
715
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
777
|
-
|
|
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
|
|
782
|
-
import { join as
|
|
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
|
-
|
|
913
|
+
p4.intro("ai-ops update");
|
|
787
914
|
const manifest = readManifest(manifestPath);
|
|
788
915
|
if (!manifest) {
|
|
789
|
-
|
|
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
|
-
|
|
801
|
-
|
|
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 =
|
|
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:
|
|
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:
|
|
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
|
-
|
|
1016
|
+
p4.outro("ai-ops update \uC644\uB8CC");
|
|
880
1017
|
};
|
|
881
1018
|
|
|
882
1019
|
// src/commands/diff.ts
|
|
883
|
-
import * as
|
|
1020
|
+
import * as p5 from "@clack/prompts";
|
|
884
1021
|
var diffCommand = async () => {
|
|
885
1022
|
const basePath = resolveBasePath();
|
|
886
|
-
|
|
1023
|
+
p5.intro("ai-ops diff");
|
|
887
1024
|
const manifest = readManifest(resolveManifestPath(basePath));
|
|
888
1025
|
if (!manifest) {
|
|
889
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1039
|
+
p5.log.warn(`\uC18C\uC2A4 \uBCC0\uACBD \uAC10\uC9C0: ${manifest.sourceHash} \u2192 ${sourceHash}`);
|
|
903
1040
|
}
|
|
904
1041
|
if (result.added.length > 0) {
|
|
905
|
-
|
|
1042
|
+
p5.log.info(`\uCD94\uAC00\uB41C \uADDC\uCE59: ${result.added.join(", ")}`);
|
|
906
1043
|
}
|
|
907
1044
|
if (result.removed.length > 0) {
|
|
908
|
-
|
|
1045
|
+
p5.log.info(`\uC81C\uAC70\uB41C \uADDC\uCE59: ${result.removed.join(", ")}`);
|
|
909
1046
|
}
|
|
910
1047
|
}
|
|
911
|
-
|
|
1048
|
+
p5.outro("ai-ops diff \uC644\uB8CC");
|
|
912
1049
|
};
|
|
913
1050
|
|
|
914
1051
|
// src/commands/uninstall.ts
|
|
915
|
-
import * as
|
|
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
|
|
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 (!
|
|
1065
|
+
if (!existsSync5(absPath)) {
|
|
929
1066
|
notFound.push(rel);
|
|
930
1067
|
continue;
|
|
931
1068
|
}
|
|
932
|
-
const content =
|
|
1069
|
+
const content = readFileSync7(absPath, "utf-8");
|
|
933
1070
|
if (!isManagedFile(content)) {
|
|
934
1071
|
if (hasAiOpsSection(content)) {
|
|
935
1072
|
const stripped = stripAiOpsSection(content);
|
|
936
|
-
|
|
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 (!
|
|
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
|
-
|
|
1116
|
+
p6.intro("ai-ops uninstall");
|
|
980
1117
|
const manifest = readManifest(manifestPath);
|
|
981
1118
|
if (!manifest) {
|
|
982
|
-
|
|
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
|
-
|
|
991
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
1001
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1033
|
-
|
|
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
|