ai-ops-cli 0.1.24 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +107 -45
- package/data/presets.yaml +0 -23
- package/data/rules/communication.yaml +3 -2
- package/data/skills/README.md +157 -0
- package/data/skills/reference-skills/ai-llm-python-runtime/SKILL.md +10 -0
- package/data/skills/reference-skills/ai-llm-python-runtime/references/reference.md +26 -0
- package/data/skills/reference-skills/backend-python-fastapi-runtime/SKILL.md +10 -0
- package/data/skills/reference-skills/backend-python-fastapi-runtime/references/reference.md +43 -0
- package/data/skills/reference-skills/backend-service-standards/SKILL.md +10 -0
- package/data/skills/reference-skills/backend-service-standards/references/reference.md +29 -0
- package/data/skills/reference-skills/backend-ts-nestjs-runtime/SKILL.md +10 -0
- package/data/skills/reference-skills/backend-ts-nestjs-runtime/references/reference.md +43 -0
- package/data/skills/reference-skills/data-pipeline-python-performance/SKILL.md +10 -0
- package/data/skills/reference-skills/data-pipeline-python-performance/references/reference.md +24 -0
- package/data/skills/reference-skills/db-prisma-postgresql/SKILL.md +10 -0
- package/data/skills/reference-skills/db-prisma-postgresql/references/reference.md +25 -0
- package/data/skills/reference-skills/db-sqlalchemy-postgresql/SKILL.md +10 -0
- package/data/skills/reference-skills/db-sqlalchemy-postgresql/references/reference.md +25 -0
- package/data/skills/reference-skills/frontend-app-flutter-runtime/SKILL.md +10 -0
- package/data/skills/reference-skills/frontend-app-flutter-runtime/references/reference.md +50 -0
- package/data/skills/reference-skills/frontend-web-react-next-runtime/SKILL.md +10 -0
- package/data/skills/reference-skills/frontend-web-react-next-runtime/references/reference.md +51 -0
- package/data/skills/reference-skills/frontend-web-shadcn-ui/SKILL.md +10 -0
- package/data/skills/reference-skills/frontend-web-shadcn-ui/references/reference.md +26 -0
- package/data/skills/reference-skills/graphql-client-integration/SKILL.md +10 -0
- package/data/skills/reference-skills/graphql-client-integration/references/reference.md +22 -0
- package/data/skills/reference-skills/graphql-contract/SKILL.md +10 -0
- package/data/skills/reference-skills/graphql-contract/references/reference.md +22 -0
- package/data/skills/reference-skills/graphql-server-runtime/SKILL.md +10 -0
- package/data/skills/reference-skills/graphql-server-runtime/references/reference.md +40 -0
- package/data/skills/reference-skills/python-language/SKILL.md +10 -0
- package/data/skills/reference-skills/python-language/references/reference.md +26 -0
- package/data/skills/reference-skills/typescript-language/SKILL.md +10 -0
- package/data/skills/reference-skills/typescript-language/references/reference.md +23 -0
- package/data/skills/skill-registry.json +148 -0
- package/data/skills/task-skills/skill-load-check/SKILL.md +8 -0
- package/data/skills/task-skills/skill-load-check/scripts/loaded.js +1 -0
- package/dist/bin/index.js +1325 -423
- package/dist/bin/index.js.map +1 -1
- package/package.json +1 -1
- package/data/rules/ai-llm-python.yaml +0 -35
- package/data/rules/data-pipeline-python.yaml +0 -34
- package/data/rules/engineering-standards.yaml +0 -39
- package/data/rules/fastapi.yaml +0 -34
- package/data/rules/flutter.yaml +0 -40
- package/data/rules/graphql-client-app.yaml +0 -29
- package/data/rules/graphql-client-web.yaml +0 -30
- package/data/rules/graphql-core.yaml +0 -31
- package/data/rules/graphql-server.yaml +0 -33
- package/data/rules/libs-backend-python.yaml +0 -35
- package/data/rules/libs-backend-ts.yaml +0 -36
- package/data/rules/libs-frontend-app.yaml +0 -39
- package/data/rules/libs-frontend-web.yaml +0 -39
- package/data/rules/nestjs-graphql.yaml +0 -31
- package/data/rules/nestjs.yaml +0 -26
- package/data/rules/nextjs.yaml +0 -34
- package/data/rules/prisma-postgresql.yaml +0 -30
- package/data/rules/python.yaml +0 -31
- package/data/rules/react-typescript.yaml +0 -11
- package/data/rules/shadcn-ui.yaml +0 -36
- package/data/rules/sqlalchemy.yaml +0 -32
- package/data/rules/typescript.yaml +0 -22
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
|
|
7
|
+
import * as p3 from "@clack/prompts";
|
|
8
8
|
|
|
9
9
|
// src/core/schemas/rule.schema.ts
|
|
10
10
|
import { z } from "zod";
|
|
@@ -28,6 +28,7 @@ var RuleSchema = z.object({
|
|
|
28
28
|
tags: z.array(z.string().min(1)),
|
|
29
29
|
/** 0-100. 높을수록 생성 파일 상단 배치 (U-shaped attention 최적화) */
|
|
30
30
|
priority: z.number().int().min(0).max(100),
|
|
31
|
+
supported_tools: z.array(z.string().min(1)).min(1).default(["claude-code", "codex", "gemini"]),
|
|
31
32
|
content: RuleContentSchema
|
|
32
33
|
}).strict();
|
|
33
34
|
|
|
@@ -39,53 +40,132 @@ var PresetSchema = z2.object({
|
|
|
39
40
|
rules: z2.array(z2.string().min(1)).min(1)
|
|
40
41
|
}).strict();
|
|
41
42
|
|
|
42
|
-
// src/core/schemas/
|
|
43
|
+
// src/core/schemas/skill.schema.ts
|
|
43
44
|
import { z as z3 } from "zod";
|
|
44
|
-
var
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
var SKILL_KIND = {
|
|
46
|
+
REFERENCE: "reference",
|
|
47
|
+
TASK: "task"
|
|
48
|
+
};
|
|
49
|
+
var SKILL_SCOPE = {
|
|
50
|
+
PROJECT: "project",
|
|
51
|
+
USER: "user"
|
|
52
|
+
};
|
|
53
|
+
var SKILL_TOOL = {
|
|
54
|
+
CLAUDE_CODE: "claude-code",
|
|
55
|
+
CODEX: "codex",
|
|
56
|
+
GEMINI: "gemini"
|
|
57
|
+
};
|
|
58
|
+
var SkillKindSchema = z3.union([z3.literal(SKILL_KIND.REFERENCE), z3.literal(SKILL_KIND.TASK)]);
|
|
59
|
+
var SkillScopeSchema = z3.union([z3.literal(SKILL_SCOPE.PROJECT), z3.literal(SKILL_SCOPE.USER)]);
|
|
60
|
+
var SkillToolSchema = z3.union([
|
|
61
|
+
z3.literal(SKILL_TOOL.CLAUDE_CODE),
|
|
62
|
+
z3.literal(SKILL_TOOL.CODEX),
|
|
63
|
+
z3.literal(SKILL_TOOL.GEMINI)
|
|
64
|
+
]);
|
|
65
|
+
var SkillFileSchema = z3.object({
|
|
66
|
+
path: z3.string().min(1),
|
|
67
|
+
content: z3.string()
|
|
68
|
+
}).strict();
|
|
69
|
+
var SkillFrontmatterSchema = z3.object({
|
|
70
|
+
name: z3.string().regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, "name must be kebab-case"),
|
|
71
|
+
description: z3.string().min(1)
|
|
72
|
+
}).passthrough();
|
|
73
|
+
var InstalledSkillSchema = z3.object({
|
|
74
|
+
id: z3.string().regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, "id must be kebab-case"),
|
|
75
|
+
kind: SkillKindSchema,
|
|
76
|
+
tools: z3.array(SkillToolSchema).min(1),
|
|
77
|
+
scope: SkillScopeSchema,
|
|
78
|
+
installed_paths: z3.array(z3.string().min(1)).min(1),
|
|
79
|
+
sourceHash: z3.string().regex(/^[a-f0-9]{6}$/, "sourceHash must be 6 lowercase hex chars")
|
|
80
|
+
}).strip();
|
|
81
|
+
|
|
82
|
+
// src/core/schemas/skill-catalog.schema.ts
|
|
83
|
+
import { z as z4 } from "zod";
|
|
84
|
+
var SkillIdSchema = z4.string().regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, "id must be kebab-case");
|
|
85
|
+
var SkillCatalogPathSchema = z4.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*(?:\/[a-z0-9]+(?:-[a-z0-9]+)*)+$/, "source_path must be relative kebab-case path");
|
|
86
|
+
var SkillCatalogEntrySchema = z4.object({
|
|
87
|
+
id: SkillIdSchema,
|
|
88
|
+
kind: SkillKindSchema,
|
|
89
|
+
supported_tools: z4.array(SkillToolSchema).min(1),
|
|
90
|
+
install_scopes: z4.array(SkillScopeSchema).min(1),
|
|
91
|
+
groups: z4.array(z4.string().min(1)),
|
|
92
|
+
included_in_presets: z4.array(z4.string().min(1)),
|
|
93
|
+
source_path: SkillCatalogPathSchema
|
|
94
|
+
}).strict().superRefine((entry, ctx) => {
|
|
95
|
+
const expectedPrefix = entry.kind === "reference" ? "reference-skills/" : "task-skills/";
|
|
96
|
+
if (!entry.source_path.startsWith(expectedPrefix)) {
|
|
97
|
+
ctx.addIssue({
|
|
98
|
+
code: z4.ZodIssueCode.custom,
|
|
99
|
+
path: ["source_path"],
|
|
100
|
+
message: `source_path must start with ${expectedPrefix}`
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
var SkillCatalogSchema = z4.object({
|
|
105
|
+
skills: z4.array(SkillCatalogEntrySchema)
|
|
106
|
+
}).strict();
|
|
107
|
+
|
|
108
|
+
// src/core/schemas/skill-registry.schema.ts
|
|
109
|
+
import { z as z5 } from "zod";
|
|
110
|
+
var SkillRegistrySchema = z5.object({
|
|
111
|
+
skills: z5.array(InstalledSkillSchema),
|
|
112
|
+
cliVersion: z5.string().optional(),
|
|
113
|
+
generatedAt: z5.string().datetime({ offset: true })
|
|
48
114
|
}).strict();
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
115
|
+
|
|
116
|
+
// src/core/schemas/manifest.schema.ts
|
|
117
|
+
import { z as z6 } from "zod";
|
|
118
|
+
var SettingsConfigSchema = z6.object({
|
|
119
|
+
claude: z6.array(z6.string().min(1)).optional(),
|
|
120
|
+
gemini: z6.array(z6.string().min(1)).optional(),
|
|
121
|
+
prettierignore: z6.boolean().optional()
|
|
52
122
|
}).strict();
|
|
53
|
-
var
|
|
54
|
-
|
|
55
|
-
|
|
123
|
+
var WorkspaceEntrySchema = z6.object({
|
|
124
|
+
preset: z6.string().min(1),
|
|
125
|
+
rules: z6.array(z6.string().min(1))
|
|
126
|
+
}).strict();
|
|
127
|
+
var ManifestSchema = z6.object({
|
|
128
|
+
tools: z6.array(z6.string().min(1)).min(1),
|
|
129
|
+
scope: z6.literal("project"),
|
|
56
130
|
/** 비모노레포 단일 preset */
|
|
57
|
-
preset:
|
|
131
|
+
preset: z6.string().min(1).optional(),
|
|
58
132
|
/** 모노레포: workspace path → { preset, rules } */
|
|
59
|
-
workspaces:
|
|
60
|
-
installed_rules:
|
|
133
|
+
workspaces: z6.record(z6.string(), WorkspaceEntrySchema).optional(),
|
|
134
|
+
installed_rules: z6.array(z6.string().min(1)),
|
|
61
135
|
/** 실제 디스크에 쓰여진 파일 상대 경로 목록 (uninstall용). 기존 manifest 호환성 위해 optional */
|
|
62
|
-
installed_files:
|
|
136
|
+
installed_files: z6.array(z6.string().min(1)).optional(),
|
|
137
|
+
/** skill 설치 루트 디렉토리 목록 */
|
|
138
|
+
installed_skills: z6.array(InstalledSkillSchema).optional(),
|
|
63
139
|
/** non-managed 파일에 섹션을 append한 경우 추적 (uninstall 시 섹션만 제거) */
|
|
64
|
-
appended_files:
|
|
140
|
+
appended_files: z6.array(z6.string().min(1)).optional(),
|
|
65
141
|
/** init 시 선택된 settings 항목 — update 시 재생성에 사용 */
|
|
66
142
|
settings: SettingsConfigSchema.optional(),
|
|
67
143
|
/** init/update 실행 시점의 CLI 패키지 버전 — 버전 변경 감지에 사용 */
|
|
68
|
-
cliVersion:
|
|
144
|
+
cliVersion: z6.string().optional(),
|
|
69
145
|
/** SSOT 데이터 파일들의 deterministic SHA-256 해시 (6자리 hex). diff/update 판단 기준 */
|
|
70
|
-
sourceHash:
|
|
71
|
-
generatedAt:
|
|
146
|
+
sourceHash: z6.string().regex(/^[a-f0-9]{6}$/, "sourceHash must be 6 lowercase hex chars"),
|
|
147
|
+
generatedAt: z6.string().datetime({ offset: true })
|
|
72
148
|
}).strict();
|
|
73
149
|
|
|
74
150
|
// src/core/loader.ts
|
|
75
151
|
import { readFileSync, readdirSync } from "fs";
|
|
76
|
-
import { resolve } from "path";
|
|
152
|
+
import { join, resolve } from "path";
|
|
153
|
+
import { parse as parse2 } from "yaml";
|
|
154
|
+
|
|
155
|
+
// src/core/frontmatter.ts
|
|
77
156
|
import { parse } from "yaml";
|
|
78
|
-
var
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
"frontend-app": {
|
|
83
|
-
graphql: ["graphql-core", "graphql-client-app"]
|
|
84
|
-
},
|
|
85
|
-
"backend-ts": {
|
|
86
|
-
graphql: ["graphql-core", "graphql-server"]
|
|
157
|
+
var parseMarkdownFrontmatter = (content) => {
|
|
158
|
+
const match = content.match(/^---\n([\s\S]*?)\n---\n?/);
|
|
159
|
+
if (!match) {
|
|
160
|
+
throw new Error("Missing YAML frontmatter");
|
|
87
161
|
}
|
|
162
|
+
return {
|
|
163
|
+
frontmatter: parse(match[1]),
|
|
164
|
+
body: content.slice(match[0].length)
|
|
165
|
+
};
|
|
88
166
|
};
|
|
167
|
+
|
|
168
|
+
// src/core/loader.ts
|
|
89
169
|
var sortRulesByPriority = (rules) => [...rules].sort((a, b) => b.priority - a.priority);
|
|
90
170
|
var deduplicateRulesById = (rules) => {
|
|
91
171
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -95,11 +175,6 @@ var deduplicateRulesById = (rules) => {
|
|
|
95
175
|
return true;
|
|
96
176
|
});
|
|
97
177
|
};
|
|
98
|
-
var resolveBundledRuleIds = (presetId, logicalRuleId) => {
|
|
99
|
-
const presetBundles = PRESET_RULE_BUNDLES[presetId];
|
|
100
|
-
if (!presetBundles) return [logicalRuleId];
|
|
101
|
-
return presetBundles[logicalRuleId] ?? [logicalRuleId];
|
|
102
|
-
};
|
|
103
178
|
var resolveRuleById = (ruleId, allRules, context) => {
|
|
104
179
|
const found = allRules.find((rule) => rule.id === ruleId);
|
|
105
180
|
if (!found) {
|
|
@@ -109,33 +184,80 @@ var resolveRuleById = (ruleId, allRules, context) => {
|
|
|
109
184
|
return found;
|
|
110
185
|
};
|
|
111
186
|
var parseRawPresets = (raw) => Object.entries(raw).map(([id, value]) => PresetSchema.parse({ id, ...value }));
|
|
112
|
-
var resolvePresetRuleGroups = (preset, allRules) => preset.rules.map((logicalRuleId) => {
|
|
113
|
-
const bundledRuleIds = resolveBundledRuleIds(preset.id, logicalRuleId);
|
|
114
|
-
const rules = bundledRuleIds.map((ruleId) => resolveRuleById(ruleId, allRules, `${preset.id}:${logicalRuleId}`));
|
|
115
|
-
return { id: logicalRuleId, rules };
|
|
116
|
-
});
|
|
117
187
|
var resolvePresetRules = (preset, allRules) => {
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
188
|
+
const resolved = preset.rules.map((ruleId) => resolveRuleById(ruleId, allRules, preset.id));
|
|
189
|
+
return sortRulesByPriority(deduplicateRulesById(resolved));
|
|
190
|
+
};
|
|
191
|
+
var resolvePresetSkills = (preset, allSkills) => {
|
|
192
|
+
return allSkills.filter((skill) => skill.included_in_presets.includes(preset.id)).sort((a, b) => a.id.localeCompare(b.id));
|
|
121
193
|
};
|
|
122
194
|
var loadRuleFile = (filePath) => {
|
|
123
195
|
const raw = readFileSync(filePath, "utf-8");
|
|
124
|
-
return RuleSchema.parse(
|
|
196
|
+
return RuleSchema.parse(parse2(raw));
|
|
197
|
+
};
|
|
198
|
+
var loadSkillDirectoryFiles = (skillDir) => {
|
|
199
|
+
const files = [];
|
|
200
|
+
const walk = (relativeDir = "") => {
|
|
201
|
+
const absDir = relativeDir.length > 0 ? join(skillDir, relativeDir) : skillDir;
|
|
202
|
+
const entries = readdirSync(absDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
203
|
+
for (const entry of entries) {
|
|
204
|
+
const nextRelativePath = relativeDir.length > 0 ? join(relativeDir, entry.name) : entry.name;
|
|
205
|
+
if (entry.isDirectory()) {
|
|
206
|
+
walk(nextRelativePath);
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
files.push({
|
|
210
|
+
path: nextRelativePath,
|
|
211
|
+
content: readFileSync(join(skillDir, nextRelativePath), "utf-8")
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
walk();
|
|
216
|
+
return files;
|
|
125
217
|
};
|
|
126
218
|
var loadAllRules = (rulesDir) => {
|
|
127
219
|
const files = readdirSync(rulesDir).filter((f) => f.endsWith(".yaml")).sort();
|
|
128
220
|
const rules = files.map((f) => loadRuleFile(resolve(rulesDir, f)));
|
|
129
221
|
return sortRulesByPriority(rules);
|
|
130
222
|
};
|
|
223
|
+
var loadSkillCatalog = (skillsDir) => SkillCatalogSchema.parse(JSON.parse(readFileSync(resolve(skillsDir, "skill-registry.json"), "utf-8")));
|
|
224
|
+
var loadAllSkills = (skillsDir) => {
|
|
225
|
+
const catalog = loadSkillCatalog(skillsDir);
|
|
226
|
+
const entries = [...catalog.skills].sort((a, b) => a.id.localeCompare(b.id));
|
|
227
|
+
return entries.map((entry) => {
|
|
228
|
+
const directory = resolve(skillsDir, entry.source_path);
|
|
229
|
+
const skillMdPath = join(directory, "SKILL.md");
|
|
230
|
+
const rawSkillMd = readFileSync(skillMdPath, "utf-8");
|
|
231
|
+
const { frontmatter } = parseMarkdownFrontmatter(rawSkillMd);
|
|
232
|
+
const parsed = SkillFrontmatterSchema.parse(frontmatter);
|
|
233
|
+
if (parsed.name !== entry.id) {
|
|
234
|
+
throw new Error(`Skill directory and frontmatter name mismatch: ${entry.id} != ${parsed.name}`);
|
|
235
|
+
}
|
|
236
|
+
const files = loadSkillDirectoryFiles(directory);
|
|
237
|
+
if (entry.kind === "reference" && !files.some((file) => file.path === "references/reference.md")) {
|
|
238
|
+
throw new Error(`Reference skill must include references/reference.md: ${parsed.name}`);
|
|
239
|
+
}
|
|
240
|
+
return {
|
|
241
|
+
id: entry.id,
|
|
242
|
+
kind: entry.kind,
|
|
243
|
+
description: parsed.description,
|
|
244
|
+
supported_tools: [...entry.supported_tools],
|
|
245
|
+
install_scopes: [...entry.install_scopes],
|
|
246
|
+
groups: [...entry.groups],
|
|
247
|
+
included_in_presets: [...entry.included_in_presets],
|
|
248
|
+
directory,
|
|
249
|
+
files
|
|
250
|
+
};
|
|
251
|
+
});
|
|
252
|
+
};
|
|
131
253
|
var loadPresets = (presetsPath) => {
|
|
132
254
|
const raw = readFileSync(presetsPath, "utf-8");
|
|
133
|
-
const data =
|
|
255
|
+
const data = parse2(raw);
|
|
134
256
|
return parseRawPresets(data);
|
|
135
257
|
};
|
|
136
258
|
|
|
137
259
|
// src/core/renderer.ts
|
|
138
|
-
import { join } from "path";
|
|
260
|
+
import { join as join2 } from "path";
|
|
139
261
|
|
|
140
262
|
// src/core/tool-output.ts
|
|
141
263
|
var GLOBAL_CATEGORIES = ["persona", "communication", "philosophy", "convention", "standard"];
|
|
@@ -225,7 +347,7 @@ var renderRuleToMarkdown = (rule) => {
|
|
|
225
347
|
}
|
|
226
348
|
return sections.join("\n\n");
|
|
227
349
|
};
|
|
228
|
-
var renderRulesToMarkdown = (rules) => rules.map(renderRuleToMarkdown).join("\n\n---\n\n");
|
|
350
|
+
var renderRulesToMarkdown = (rules) => rules.map((rule) => renderRuleToMarkdown(rule)).join("\n\n---\n\n");
|
|
229
351
|
var isGlobalRule = (rule) => GLOBAL_CATEGORIES.includes(rule.category);
|
|
230
352
|
var partitionRules = (rules) => {
|
|
231
353
|
const global = [];
|
|
@@ -261,14 +383,14 @@ var renderForTool = (toolId, rules, workspaceMappings) => {
|
|
|
261
383
|
const { rulesDir, fileExtension } = config;
|
|
262
384
|
if (!workspaceMappings || workspaceMappings.length === 0) {
|
|
263
385
|
const files = rules.map((rule) => ({
|
|
264
|
-
relativePath:
|
|
386
|
+
relativePath: join2(rulesDir, `${rule.id}${fileExtension}`),
|
|
265
387
|
content: renderClaudeCodeRule(rule)
|
|
266
388
|
}));
|
|
267
389
|
return { tool: "claude-code", files };
|
|
268
390
|
}
|
|
269
391
|
const { global: global2, domain: domain2 } = partitionRules(rules);
|
|
270
392
|
const globalFiles = global2.map((rule) => ({
|
|
271
|
-
relativePath:
|
|
393
|
+
relativePath: join2(rulesDir, `${rule.id}${fileExtension}`),
|
|
272
394
|
content: renderRuleToMarkdown(rule)
|
|
273
395
|
// global은 frontmatter 불필요
|
|
274
396
|
}));
|
|
@@ -277,7 +399,7 @@ var renderForTool = (toolId, rules, workspaceMappings) => {
|
|
|
277
399
|
const wsRules = domain2.filter((r) => ws.ruleIds.includes(r.id));
|
|
278
400
|
if (wsRules.length === 0) continue;
|
|
279
401
|
workspaceFiles.push({
|
|
280
|
-
relativePath:
|
|
402
|
+
relativePath: join2(ws.path, "CLAUDE.md"),
|
|
281
403
|
content: renderRulesToMarkdown(wsRules)
|
|
282
404
|
});
|
|
283
405
|
}
|
|
@@ -295,7 +417,10 @@ var renderForTool = (toolId, rules, workspaceMappings) => {
|
|
|
295
417
|
for (const ws of workspaceMappings) {
|
|
296
418
|
const wsRules = domain.filter((r) => ws.ruleIds.includes(r.id));
|
|
297
419
|
if (wsRules.length === 0) continue;
|
|
298
|
-
domainFiles.push({
|
|
420
|
+
domainFiles.push({
|
|
421
|
+
workspacePath: ws.path,
|
|
422
|
+
content: renderRulesToMarkdown(wsRules)
|
|
423
|
+
});
|
|
299
424
|
}
|
|
300
425
|
if (toolId === "codex") {
|
|
301
426
|
return { tool: "codex", rootContent, domainFiles };
|
|
@@ -303,10 +428,13 @@ var renderForTool = (toolId, rules, workspaceMappings) => {
|
|
|
303
428
|
return { tool: "gemini", rootContent, domainFiles };
|
|
304
429
|
};
|
|
305
430
|
|
|
431
|
+
// src/core/skill-renderer.ts
|
|
432
|
+
import { join as join4 } from "path";
|
|
433
|
+
|
|
306
434
|
// src/core/source-hash.ts
|
|
307
435
|
import { createHash } from "crypto";
|
|
308
|
-
import { readFileSync as readFileSync2, readdirSync as readdirSync2 } from "fs";
|
|
309
|
-
import { dirname, resolve as resolve2 } from "path";
|
|
436
|
+
import { existsSync, readFileSync as readFileSync2, readdirSync as readdirSync2 } from "fs";
|
|
437
|
+
import { dirname, join as join3, resolve as resolve2 } from "path";
|
|
310
438
|
import { fileURLToPath } from "url";
|
|
311
439
|
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
312
440
|
var getCliVersion = () => {
|
|
@@ -319,11 +447,50 @@ var getCliVersion = () => {
|
|
|
319
447
|
}
|
|
320
448
|
};
|
|
321
449
|
var computeHash = (contents) => createHash("sha256").update(contents.join("")).digest("hex").slice(0, 6);
|
|
322
|
-
var
|
|
323
|
-
const files = readdirSync2(
|
|
324
|
-
|
|
325
|
-
return computeHash(contents);
|
|
450
|
+
var loadSortedFileContents = (dirPath) => {
|
|
451
|
+
const files = readdirSync2(dirPath).filter((f) => f.endsWith(".yaml")).sort();
|
|
452
|
+
return files.map((f) => readFileSync2(resolve2(dirPath, f), "utf-8"));
|
|
326
453
|
};
|
|
454
|
+
var loadDirectoryContents = (baseDir) => {
|
|
455
|
+
const contents = [];
|
|
456
|
+
const walk = (relativeDir = "") => {
|
|
457
|
+
const absDir = relativeDir.length > 0 ? join3(baseDir, relativeDir) : baseDir;
|
|
458
|
+
const entries = readdirSync2(absDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
459
|
+
for (const entry of entries) {
|
|
460
|
+
const nextRelativePath = relativeDir.length > 0 ? join3(relativeDir, entry.name) : entry.name;
|
|
461
|
+
if (entry.isDirectory()) {
|
|
462
|
+
walk(nextRelativePath);
|
|
463
|
+
continue;
|
|
464
|
+
}
|
|
465
|
+
contents.push(`${nextRelativePath}:${readFileSync2(join3(baseDir, nextRelativePath), "utf-8")}`);
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
walk();
|
|
469
|
+
return contents;
|
|
470
|
+
};
|
|
471
|
+
var loadSkillTreeContents = (skillsDir) => {
|
|
472
|
+
const contents = [];
|
|
473
|
+
const registryPath = resolve2(skillsDir, "skill-registry.json");
|
|
474
|
+
if (existsSync(registryPath)) {
|
|
475
|
+
contents.push(`skill-registry.json:${readFileSync2(registryPath, "utf-8")}`);
|
|
476
|
+
}
|
|
477
|
+
for (const directoryName of ["reference-skills", "task-skills"]) {
|
|
478
|
+
const directoryPath = resolve2(skillsDir, directoryName);
|
|
479
|
+
if (!existsSync(directoryPath)) {
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
const directoryContents = loadDirectoryContents(directoryPath).map((content) => `${directoryName}/${content}`);
|
|
483
|
+
contents.push(...directoryContents);
|
|
484
|
+
}
|
|
485
|
+
return contents;
|
|
486
|
+
};
|
|
487
|
+
var computeSourceHash = (dataDir) => {
|
|
488
|
+
const ruleContents = loadSortedFileContents(resolve2(dataDir, "rules"));
|
|
489
|
+
const skillContents = loadSkillTreeContents(resolve2(dataDir, "skills"));
|
|
490
|
+
const presetsContent = readFileSync2(resolve2(dataDir, "presets.yaml"), "utf-8");
|
|
491
|
+
return computeHash([...ruleContents, ...skillContents, presetsContent]);
|
|
492
|
+
};
|
|
493
|
+
var computeInstalledSkillHash = (params) => computeHash([params.kind, params.description, ...[...params.tools].sort(), ...[...params.files].sort()]);
|
|
327
494
|
var buildManifest = (params) => ManifestSchema.parse({
|
|
328
495
|
tools: [...params.tools],
|
|
329
496
|
scope: params.scope,
|
|
@@ -331,16 +498,71 @@ var buildManifest = (params) => ManifestSchema.parse({
|
|
|
331
498
|
workspaces: params.workspaces,
|
|
332
499
|
installed_rules: [...params.installedRules],
|
|
333
500
|
installed_files: params.installedFiles ? [...params.installedFiles] : void 0,
|
|
501
|
+
installed_skills: params.installedSkills ? [...params.installedSkills] : void 0,
|
|
334
502
|
appended_files: params.appendedFiles && params.appendedFiles.length > 0 ? [...params.appendedFiles] : void 0,
|
|
335
503
|
settings: params.settings ? {
|
|
336
504
|
claude: params.settings.claude ? [...params.settings.claude] : void 0,
|
|
337
|
-
gemini: params.settings.gemini ? [...params.settings.gemini] : void 0
|
|
505
|
+
gemini: params.settings.gemini ? [...params.settings.gemini] : void 0,
|
|
506
|
+
prettierignore: params.settings.prettierignore
|
|
338
507
|
} : void 0,
|
|
339
508
|
cliVersion: params.cliVersion,
|
|
340
509
|
sourceHash: params.sourceHash,
|
|
341
510
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
342
511
|
});
|
|
343
512
|
|
|
513
|
+
// src/core/skill-renderer.ts
|
|
514
|
+
var AGENT_SKILLS_DIR = ".agents/skills";
|
|
515
|
+
var CLAUDE_SKILLS_DIR = ".claude/skills";
|
|
516
|
+
var buildRootDirs = (skillId, toolIds) => {
|
|
517
|
+
const dirs = [];
|
|
518
|
+
if (toolIds.some((toolId) => toolId === "codex" || toolId === "gemini")) {
|
|
519
|
+
dirs.push(join4(AGENT_SKILLS_DIR, skillId));
|
|
520
|
+
}
|
|
521
|
+
if (toolIds.includes("claude-code")) {
|
|
522
|
+
dirs.push(join4(CLAUDE_SKILLS_DIR, skillId));
|
|
523
|
+
}
|
|
524
|
+
return dirs;
|
|
525
|
+
};
|
|
526
|
+
var normalizeSelectedTools = (skill, requestedTools) => {
|
|
527
|
+
const supportedToolSet = new Set(skill.supported_tools);
|
|
528
|
+
return requestedTools.filter((toolId) => supportedToolSet.has(toolId));
|
|
529
|
+
};
|
|
530
|
+
var buildSkillInstallPlan = (params) => {
|
|
531
|
+
const selectedTools = normalizeSelectedTools(params.skill, params.requestedTools);
|
|
532
|
+
if (selectedTools.length === 0) {
|
|
533
|
+
throw new Error(`Skill ${params.skill.id} does not support the requested tools`);
|
|
534
|
+
}
|
|
535
|
+
const rootDirs = buildRootDirs(params.skill.id, selectedTools);
|
|
536
|
+
const skillHash = computeInstalledSkillHash({
|
|
537
|
+
kind: params.skill.kind,
|
|
538
|
+
description: params.skill.description,
|
|
539
|
+
tools: selectedTools,
|
|
540
|
+
files: params.skill.files.map((file) => `${file.path}:${file.content}`)
|
|
541
|
+
});
|
|
542
|
+
const packages = rootDirs.map((rootDir) => {
|
|
543
|
+
const files = params.skill.files.map((file) => ({
|
|
544
|
+
relativePath: join4(rootDir, file.path),
|
|
545
|
+
content: file.content
|
|
546
|
+
}));
|
|
547
|
+
return {
|
|
548
|
+
skillId: params.skill.id,
|
|
549
|
+
rootDir,
|
|
550
|
+
files
|
|
551
|
+
};
|
|
552
|
+
});
|
|
553
|
+
return {
|
|
554
|
+
packages,
|
|
555
|
+
installedSkill: {
|
|
556
|
+
id: params.skill.id,
|
|
557
|
+
kind: params.skill.kind,
|
|
558
|
+
tools: selectedTools,
|
|
559
|
+
scope: params.scope,
|
|
560
|
+
installed_paths: rootDirs,
|
|
561
|
+
sourceHash: skillHash
|
|
562
|
+
}
|
|
563
|
+
};
|
|
564
|
+
};
|
|
565
|
+
|
|
344
566
|
// src/core/managed-header.ts
|
|
345
567
|
var MANAGED_MARKER = "<!-- managed by ai-ops -->";
|
|
346
568
|
var SECTION_START = "<!-- ai-ops:start -->";
|
|
@@ -374,11 +596,11 @@ var replaceAiOpsSection = (existing, newSection) => {
|
|
|
374
596
|
|
|
375
597
|
// src/core/manifest-io.ts
|
|
376
598
|
import { mkdirSync, readFileSync as readFileSync3, writeFileSync } from "fs";
|
|
377
|
-
import { dirname as dirname2, join as
|
|
599
|
+
import { dirname as dirname2, join as join5 } from "path";
|
|
378
600
|
var MANIFEST_FILENAME = ".ai-ops-manifest.json";
|
|
379
601
|
var parseManifest = (json) => ManifestSchema.parse(JSON.parse(json));
|
|
380
602
|
var serializeManifest = (manifest) => JSON.stringify(manifest, null, 2) + "\n";
|
|
381
|
-
var resolveManifestPath = (basePath) =>
|
|
603
|
+
var resolveManifestPath = (basePath) => join5(basePath, MANIFEST_FILENAME);
|
|
382
604
|
var readManifest = (manifestPath) => {
|
|
383
605
|
let raw;
|
|
384
606
|
try {
|
|
@@ -393,6 +615,144 @@ var writeManifest = (manifestPath, manifest) => {
|
|
|
393
615
|
writeFileSync(manifestPath, serializeManifest(manifest), "utf-8");
|
|
394
616
|
};
|
|
395
617
|
|
|
618
|
+
// src/core/manifest-resolution.ts
|
|
619
|
+
var LEGACY_SKILL_ID_MAP = {
|
|
620
|
+
"engineering-standards-pack": "backend-service-standards"
|
|
621
|
+
};
|
|
622
|
+
var LEGACY_EXTERNALIZED_RULE_SKILL_MAP = {
|
|
623
|
+
"engineering-standards": "backend-service-standards",
|
|
624
|
+
typescript: "typescript-language",
|
|
625
|
+
python: "python-language",
|
|
626
|
+
"react-typescript": "frontend-web-react-next-runtime",
|
|
627
|
+
nextjs: "frontend-web-react-next-runtime",
|
|
628
|
+
"libs-frontend-web": "frontend-web-react-next-runtime",
|
|
629
|
+
"shadcn-ui": "frontend-web-shadcn-ui",
|
|
630
|
+
flutter: "frontend-app-flutter-runtime",
|
|
631
|
+
"libs-frontend-app": "frontend-app-flutter-runtime",
|
|
632
|
+
nestjs: "backend-ts-nestjs-runtime",
|
|
633
|
+
"libs-backend-ts": "backend-ts-nestjs-runtime",
|
|
634
|
+
fastapi: "backend-python-fastapi-runtime",
|
|
635
|
+
"libs-backend-python": "backend-python-fastapi-runtime",
|
|
636
|
+
"graphql-core": "graphql-contract",
|
|
637
|
+
"graphql-client-web": "graphql-client-integration",
|
|
638
|
+
"graphql-client-app": "graphql-client-integration",
|
|
639
|
+
"graphql-server": "graphql-server-runtime",
|
|
640
|
+
"nestjs-graphql": "graphql-server-runtime",
|
|
641
|
+
"prisma-postgresql": "db-prisma-postgresql",
|
|
642
|
+
sqlalchemy: "db-sqlalchemy-postgresql",
|
|
643
|
+
"ai-llm-python": "ai-llm-python-runtime",
|
|
644
|
+
"data-pipeline-python": "data-pipeline-python-performance"
|
|
645
|
+
};
|
|
646
|
+
var resolveCanonicalSkillId = (skillId) => LEGACY_SKILL_ID_MAP[skillId] ?? skillId;
|
|
647
|
+
var resolveRulesFromIds = (ruleIds, allRules) => {
|
|
648
|
+
const ruleMap = new Map(allRules.map((rule) => [rule.id, rule]));
|
|
649
|
+
const seen = /* @__PURE__ */ new Set();
|
|
650
|
+
const resolved = ruleIds.flatMap((ruleId) => {
|
|
651
|
+
const rule = ruleMap.get(ruleId);
|
|
652
|
+
if (!rule || seen.has(rule.id)) {
|
|
653
|
+
return [];
|
|
654
|
+
}
|
|
655
|
+
seen.add(rule.id);
|
|
656
|
+
return [rule];
|
|
657
|
+
});
|
|
658
|
+
return [...resolved].sort((a, b) => b.priority - a.priority);
|
|
659
|
+
};
|
|
660
|
+
var resolvePresetById = (presetId, presets) => {
|
|
661
|
+
if (presetId === void 0) {
|
|
662
|
+
return void 0;
|
|
663
|
+
}
|
|
664
|
+
return presets.find((preset) => preset.id === presetId);
|
|
665
|
+
};
|
|
666
|
+
var resolveManifestRules = (params) => {
|
|
667
|
+
const { manifest, allRules, presets } = params;
|
|
668
|
+
if (manifest.workspaces) {
|
|
669
|
+
const resolvedWorkspaces = Object.fromEntries(
|
|
670
|
+
Object.entries(manifest.workspaces).map(([workspacePath, entry]) => {
|
|
671
|
+
const preset2 = resolvePresetById(entry.preset, presets);
|
|
672
|
+
const rules = preset2 ? resolvePresetRules(preset2, allRules) : resolveRulesFromIds(entry.rules, allRules);
|
|
673
|
+
return [
|
|
674
|
+
workspacePath,
|
|
675
|
+
{
|
|
676
|
+
preset: entry.preset,
|
|
677
|
+
rules: rules.map((rule) => rule.id)
|
|
678
|
+
}
|
|
679
|
+
];
|
|
680
|
+
})
|
|
681
|
+
);
|
|
682
|
+
const installedRules2 = resolveRulesFromIds(
|
|
683
|
+
Object.values(resolvedWorkspaces).flatMap((entry) => entry.rules),
|
|
684
|
+
allRules
|
|
685
|
+
);
|
|
686
|
+
return {
|
|
687
|
+
installedRules: installedRules2,
|
|
688
|
+
workspaces: resolvedWorkspaces
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
const preset = resolvePresetById(manifest.preset, presets);
|
|
692
|
+
const installedRules = preset ? resolvePresetRules(preset, allRules) : resolveRulesFromIds(manifest.installed_rules, allRules);
|
|
693
|
+
return {
|
|
694
|
+
installedRules
|
|
695
|
+
};
|
|
696
|
+
};
|
|
697
|
+
var resolveManifestProjectSkills = (params) => {
|
|
698
|
+
const { manifest, allSkills } = params;
|
|
699
|
+
const skillMap = new Map(allSkills.map((skill) => [skill.id, skill]));
|
|
700
|
+
const targets = [];
|
|
701
|
+
const seen = /* @__PURE__ */ new Set();
|
|
702
|
+
for (const installedSkill of manifest.installed_skills ?? []) {
|
|
703
|
+
const canonicalSkillId = resolveCanonicalSkillId(installedSkill.id);
|
|
704
|
+
const skill = skillMap.get(canonicalSkillId);
|
|
705
|
+
if (!skill) {
|
|
706
|
+
throw new Error(`Skill not found during manifest resolution: ${installedSkill.id}`);
|
|
707
|
+
}
|
|
708
|
+
if (seen.has(skill.id)) {
|
|
709
|
+
continue;
|
|
710
|
+
}
|
|
711
|
+
seen.add(skill.id);
|
|
712
|
+
targets.push({
|
|
713
|
+
skill,
|
|
714
|
+
requestedTools: [...installedSkill.tools]
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
for (const ruleId of manifest.installed_rules) {
|
|
718
|
+
const mappedSkillId = LEGACY_EXTERNALIZED_RULE_SKILL_MAP[ruleId];
|
|
719
|
+
if (!mappedSkillId || seen.has(mappedSkillId)) {
|
|
720
|
+
continue;
|
|
721
|
+
}
|
|
722
|
+
const skill = skillMap.get(mappedSkillId);
|
|
723
|
+
if (!skill) {
|
|
724
|
+
throw new Error(`Skill not found during legacy rule migration: ${mappedSkillId}`);
|
|
725
|
+
}
|
|
726
|
+
seen.add(mappedSkillId);
|
|
727
|
+
targets.push({
|
|
728
|
+
skill,
|
|
729
|
+
requestedTools: [...manifest.tools]
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
return targets;
|
|
733
|
+
};
|
|
734
|
+
|
|
735
|
+
// src/core/skill-registry-io.ts
|
|
736
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
737
|
+
import { dirname as dirname3, join as join6 } from "path";
|
|
738
|
+
var SKILL_REGISTRY_FILENAME = "skills-manifest.json";
|
|
739
|
+
var parseSkillRegistry = (json) => SkillRegistrySchema.parse(JSON.parse(json));
|
|
740
|
+
var serializeSkillRegistry = (registry) => JSON.stringify(registry, null, 2) + "\n";
|
|
741
|
+
var resolveSkillRegistryPath = (userBasePath) => join6(userBasePath, ".ai-ops", SKILL_REGISTRY_FILENAME);
|
|
742
|
+
var readSkillRegistry = (registryPath) => {
|
|
743
|
+
let raw;
|
|
744
|
+
try {
|
|
745
|
+
raw = readFileSync4(registryPath, "utf-8");
|
|
746
|
+
} catch {
|
|
747
|
+
return null;
|
|
748
|
+
}
|
|
749
|
+
return parseSkillRegistry(raw);
|
|
750
|
+
};
|
|
751
|
+
var writeSkillRegistry = (registryPath, registry) => {
|
|
752
|
+
mkdirSync2(dirname3(registryPath), { recursive: true });
|
|
753
|
+
writeFileSync2(registryPath, serializeSkillRegistry(registry), "utf-8");
|
|
754
|
+
};
|
|
755
|
+
|
|
396
756
|
// src/core/diff.ts
|
|
397
757
|
var computeDiff = (params) => {
|
|
398
758
|
const { previous, currentRules, currentSourceHash, currentCliVersion } = params;
|
|
@@ -407,7 +767,7 @@ var computeDiff = (params) => {
|
|
|
407
767
|
};
|
|
408
768
|
|
|
409
769
|
// src/core/install-plan.ts
|
|
410
|
-
import { join as
|
|
770
|
+
import { join as join7 } from "path";
|
|
411
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.";
|
|
412
772
|
var buildInstallPlan = (params) => {
|
|
413
773
|
const { toolId, renderResult, meta } = params;
|
|
@@ -422,12 +782,12 @@ var buildInstallPlan = (params) => {
|
|
|
422
782
|
const actions = [];
|
|
423
783
|
const rootContent = renderResult.rootContent ? renderResult.rootContent + "\n\n---\n\n" + CODEX_PLAN_BODY : CODEX_PLAN_BODY;
|
|
424
784
|
actions.push({
|
|
425
|
-
relativePath:
|
|
785
|
+
relativePath: join7(config.dir, config.rootFileName),
|
|
426
786
|
content: wrapWithSection(rootContent, meta)
|
|
427
787
|
});
|
|
428
788
|
for (const df of renderResult.domainFiles) {
|
|
429
789
|
actions.push({
|
|
430
|
-
relativePath:
|
|
790
|
+
relativePath: join7(df.workspacePath, config.domainFileName),
|
|
431
791
|
content: wrapWithSection(df.content, meta)
|
|
432
792
|
});
|
|
433
793
|
}
|
|
@@ -438,13 +798,13 @@ var buildInstallPlan = (params) => {
|
|
|
438
798
|
const actions = [];
|
|
439
799
|
if (renderResult.rootContent) {
|
|
440
800
|
actions.push({
|
|
441
|
-
relativePath:
|
|
801
|
+
relativePath: join7(config.dir, config.rootFileName),
|
|
442
802
|
content: wrapWithSection(renderResult.rootContent, meta)
|
|
443
803
|
});
|
|
444
804
|
}
|
|
445
805
|
for (const df of renderResult.domainFiles) {
|
|
446
806
|
actions.push({
|
|
447
|
-
relativePath:
|
|
807
|
+
relativePath: join7(df.workspacePath, config.domainFileName),
|
|
448
808
|
content: wrapWithSection(df.content, meta)
|
|
449
809
|
});
|
|
450
810
|
}
|
|
@@ -454,7 +814,7 @@ var buildInstallPlan = (params) => {
|
|
|
454
814
|
};
|
|
455
815
|
|
|
456
816
|
// src/core/uninstall-plan.ts
|
|
457
|
-
import { join as
|
|
817
|
+
import { join as join8 } from "path";
|
|
458
818
|
var inferInstalledFiles = (manifest) => {
|
|
459
819
|
const files = [];
|
|
460
820
|
const isMonorepo = manifest.workspaces !== void 0;
|
|
@@ -462,27 +822,27 @@ var inferInstalledFiles = (manifest) => {
|
|
|
462
822
|
if (toolId === "claude-code") {
|
|
463
823
|
const config = TOOL_OUTPUT_MAP["claude-code"];
|
|
464
824
|
for (const ruleId of manifest.installed_rules) {
|
|
465
|
-
files.push(
|
|
825
|
+
files.push(join8(config.rulesDir, `${ruleId}${config.fileExtension}`));
|
|
466
826
|
}
|
|
467
827
|
} else if (toolId === "codex") {
|
|
468
828
|
const config = TOOL_OUTPUT_MAP["codex"];
|
|
469
829
|
if (!isMonorepo) {
|
|
470
|
-
files.push(
|
|
471
|
-
files.push(
|
|
830
|
+
files.push(join8(config.dir, config.rootFileName));
|
|
831
|
+
files.push(join8(config.dir, config.domainFileName));
|
|
472
832
|
} else {
|
|
473
|
-
files.push(
|
|
833
|
+
files.push(join8(config.dir, config.rootFileName));
|
|
474
834
|
for (const ws of Object.keys(manifest.workspaces ?? {})) {
|
|
475
|
-
files.push(
|
|
835
|
+
files.push(join8(ws, config.domainFileName));
|
|
476
836
|
}
|
|
477
837
|
}
|
|
478
838
|
} else if (toolId === "gemini") {
|
|
479
839
|
const config = TOOL_OUTPUT_MAP["gemini"];
|
|
480
840
|
if (!isMonorepo) {
|
|
481
|
-
files.push(
|
|
841
|
+
files.push(join8(config.dir, config.rootFileName));
|
|
482
842
|
} else {
|
|
483
|
-
files.push(
|
|
843
|
+
files.push(join8(config.dir, config.rootFileName));
|
|
484
844
|
for (const ws of Object.keys(manifest.workspaces ?? {})) {
|
|
485
|
-
files.push(
|
|
845
|
+
files.push(join8(ws, config.domainFileName));
|
|
486
846
|
}
|
|
487
847
|
}
|
|
488
848
|
}
|
|
@@ -491,20 +851,23 @@ var inferInstalledFiles = (manifest) => {
|
|
|
491
851
|
};
|
|
492
852
|
|
|
493
853
|
// src/core/paths.ts
|
|
494
|
-
import { dirname as
|
|
854
|
+
import { dirname as dirname4, resolve as resolve3 } from "path";
|
|
495
855
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
496
|
-
var __dirname2 =
|
|
856
|
+
var __dirname2 = dirname4(fileURLToPath2(import.meta.url));
|
|
497
857
|
var COMPILER_DATA_DIR = resolve3(__dirname2, "..", "..", "data");
|
|
498
858
|
|
|
499
859
|
// src/lib/paths.ts
|
|
500
|
-
import { join as
|
|
501
|
-
var
|
|
502
|
-
var
|
|
860
|
+
import { join as join9 } from "path";
|
|
861
|
+
var resolveCompilerDataDir = () => COMPILER_DATA_DIR;
|
|
862
|
+
var resolveRulesDir = () => join9(COMPILER_DATA_DIR, "rules");
|
|
863
|
+
var resolveSkillsDir = () => join9(COMPILER_DATA_DIR, "skills");
|
|
864
|
+
var resolvePresetsPath = () => join9(COMPILER_DATA_DIR, "presets.yaml");
|
|
503
865
|
var resolveBasePath = () => process.cwd();
|
|
866
|
+
var resolveUserBasePath = () => process.env.AI_OPS_HOME ?? process.env.HOME ?? process.cwd();
|
|
504
867
|
|
|
505
868
|
// src/lib/workspace.ts
|
|
506
|
-
import { existsSync, readdirSync as readdirSync3, statSync } from "fs";
|
|
507
|
-
import { join as
|
|
869
|
+
import { existsSync as existsSync2, readdirSync as readdirSync3, statSync } from "fs";
|
|
870
|
+
import { join as join10, resolve as resolve4 } from "path";
|
|
508
871
|
var EXCLUDE_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", ".turbo", ".cache", "coverage"]);
|
|
509
872
|
var isVisibleDir = (basePath, name) => {
|
|
510
873
|
if (name.startsWith(".") || EXCLUDE_DIRS.has(name)) return false;
|
|
@@ -524,7 +887,7 @@ var PROJECT_MANIFESTS = [
|
|
|
524
887
|
"go.mod"
|
|
525
888
|
// Go
|
|
526
889
|
];
|
|
527
|
-
var isWorkspaceRoot = (dirPath) => PROJECT_MANIFESTS.some((f) =>
|
|
890
|
+
var isWorkspaceRoot = (dirPath) => PROJECT_MANIFESTS.some((f) => existsSync2(join10(dirPath, f)));
|
|
528
891
|
var listWorkspaceCandidates = (basePath) => {
|
|
529
892
|
const topLevel = readdirSync3(basePath).filter((name) => isVisibleDir(basePath, name));
|
|
530
893
|
const candidates = [];
|
|
@@ -537,7 +900,7 @@ var listWorkspaceCandidates = (basePath) => {
|
|
|
537
900
|
const wsChildren = children.filter((name) => isWorkspaceRoot(resolve4(subPath, name)));
|
|
538
901
|
if (wsChildren.length > 0) {
|
|
539
902
|
for (const child of wsChildren) {
|
|
540
|
-
candidates.push(
|
|
903
|
+
candidates.push(join10(dir, child));
|
|
541
904
|
}
|
|
542
905
|
} else {
|
|
543
906
|
candidates.push(dir);
|
|
@@ -548,31 +911,31 @@ var listWorkspaceCandidates = (basePath) => {
|
|
|
548
911
|
};
|
|
549
912
|
|
|
550
913
|
// src/lib/install.ts
|
|
551
|
-
import { existsSync as
|
|
552
|
-
import { dirname as
|
|
914
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
|
|
915
|
+
import { dirname as dirname5, resolve as resolve5 } from "path";
|
|
553
916
|
var installFiles = (basePath, actions, _meta) => {
|
|
554
917
|
const written = [];
|
|
555
918
|
const appended = [];
|
|
556
919
|
const skipped = [];
|
|
557
920
|
for (const action of actions) {
|
|
558
921
|
const absPath = resolve5(basePath, action.relativePath);
|
|
559
|
-
if (!
|
|
560
|
-
|
|
561
|
-
|
|
922
|
+
if (!existsSync3(absPath)) {
|
|
923
|
+
mkdirSync3(dirname5(absPath), { recursive: true });
|
|
924
|
+
writeFileSync3(absPath, action.content + "\n", "utf-8");
|
|
562
925
|
written.push(action.relativePath);
|
|
563
926
|
} else {
|
|
564
|
-
const existing =
|
|
927
|
+
const existing = readFileSync5(absPath, "utf-8");
|
|
565
928
|
if (hasAiOpsSection(existing)) {
|
|
566
929
|
const updated = replaceAiOpsSection(existing, action.content);
|
|
567
|
-
|
|
930
|
+
writeFileSync3(absPath, updated, "utf-8");
|
|
568
931
|
const stripped = stripAiOpsSection(existing);
|
|
569
932
|
(stripped.trim().length > 0 ? appended : written).push(action.relativePath);
|
|
570
933
|
} else if (hasLegacyHeader(existing)) {
|
|
571
|
-
|
|
934
|
+
writeFileSync3(absPath, action.content + "\n", "utf-8");
|
|
572
935
|
written.push(action.relativePath);
|
|
573
936
|
} else {
|
|
574
937
|
const updated = existing.trimEnd() + "\n\n" + action.content + "\n";
|
|
575
|
-
|
|
938
|
+
writeFileSync3(absPath, updated, "utf-8");
|
|
576
939
|
appended.push(action.relativePath);
|
|
577
940
|
}
|
|
578
941
|
}
|
|
@@ -580,10 +943,40 @@ var installFiles = (basePath, actions, _meta) => {
|
|
|
580
943
|
return { written, appended, skipped };
|
|
581
944
|
};
|
|
582
945
|
|
|
583
|
-
// src/lib/
|
|
946
|
+
// src/lib/skill-install.ts
|
|
947
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync4, rmSync, writeFileSync as writeFileSync4 } from "fs";
|
|
948
|
+
import { dirname as dirname6, resolve as resolve6 } from "path";
|
|
949
|
+
var installSkillPackages = (basePath, packages) => {
|
|
950
|
+
const writtenRoots = [];
|
|
951
|
+
for (const skillPackage of packages) {
|
|
952
|
+
const absRoot = resolve6(basePath, skillPackage.rootDir);
|
|
953
|
+
if (existsSync4(absRoot)) {
|
|
954
|
+
rmSync(absRoot, { recursive: true, force: true });
|
|
955
|
+
}
|
|
956
|
+
for (const file of skillPackage.files) {
|
|
957
|
+
const absPath = resolve6(basePath, file.relativePath);
|
|
958
|
+
mkdirSync4(dirname6(absPath), { recursive: true });
|
|
959
|
+
writeFileSync4(absPath, file.content + "\n", "utf-8");
|
|
960
|
+
}
|
|
961
|
+
writtenRoots.push(skillPackage.rootDir);
|
|
962
|
+
}
|
|
963
|
+
return writtenRoots;
|
|
964
|
+
};
|
|
965
|
+
var removeDirectories = (basePath, relativeDirs) => {
|
|
966
|
+
const removed = [];
|
|
967
|
+
for (const relativeDir of relativeDirs) {
|
|
968
|
+
const absPath = resolve6(basePath, relativeDir);
|
|
969
|
+
if (!existsSync4(absPath)) continue;
|
|
970
|
+
rmSync(absPath, { recursive: true, force: true });
|
|
971
|
+
removed.push(relativeDir);
|
|
972
|
+
}
|
|
973
|
+
return removed;
|
|
974
|
+
};
|
|
975
|
+
|
|
976
|
+
// src/lib/tool-settings.ts
|
|
584
977
|
import * as p from "@clack/prompts";
|
|
585
|
-
import { existsSync as
|
|
586
|
-
import { join as
|
|
978
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync5, readFileSync as readFileSync6, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
979
|
+
import { join as join11 } from "path";
|
|
587
980
|
|
|
588
981
|
// src/lib/deep-merge.util.ts
|
|
589
982
|
var deepMerge = (base, patch) => {
|
|
@@ -615,100 +1008,106 @@ var deepRemoveKeys = (base, patch) => {
|
|
|
615
1008
|
return result;
|
|
616
1009
|
};
|
|
617
1010
|
|
|
618
|
-
// src/lib/
|
|
619
|
-
var
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
value: "plan",
|
|
628
|
-
label: "Plan \u2014 \uACC4\uD68D \uD30C\uC77C \uC800\uC7A5 \uBC0F \uBAA8\uB378 \uB77C\uC6B0\uD305",
|
|
629
|
-
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",
|
|
630
|
-
patch: { general: { plan: { directory: ".gemini/plans", modelRouting: true } } }
|
|
631
|
-
},
|
|
632
|
-
{
|
|
633
|
-
value: "sessionRetention",
|
|
634
|
-
label: "Session Retention \u2014 \uC138\uC158 30\uC77C \uBCF4\uC874",
|
|
635
|
-
hint: "general.sessionRetention.maxAge: 30d \u2014 \uC774\uC804 \uB300\uD654 \uCEE8\uD14D\uC2A4\uD2B8\uB97C 30\uC77C\uAC04 \uC720\uC9C0",
|
|
636
|
-
patch: { general: { sessionRetention: { maxAge: "30d" } } }
|
|
637
|
-
},
|
|
638
|
-
{
|
|
639
|
-
value: "experimental",
|
|
640
|
-
label: "Experimental \u2014 JIT \uCEE8\uD14D\uC2A4\uD2B8 + Plan \uAE30\uB2A5",
|
|
641
|
-
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",
|
|
642
|
-
patch: { experimental: { jitContext: true, plan: true } }
|
|
643
|
-
}
|
|
644
|
-
];
|
|
645
|
-
var promptGeminiSettings = async () => {
|
|
646
|
-
const wantSettings = await p.confirm({
|
|
647
|
-
message: "Gemini CLI \uC124\uC815 \uD30C\uC77C(.gemini/settings.json)\uC744 \uC124\uCE58\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
|
|
648
|
-
initialValue: true
|
|
649
|
-
});
|
|
650
|
-
if (p.isCancel(wantSettings) || !wantSettings) return null;
|
|
1011
|
+
// src/lib/prompt-control.ts
|
|
1012
|
+
var PROMPT_CANCELLED = /* @__PURE__ */ Symbol("prompt-cancelled");
|
|
1013
|
+
var isPromptCancelled = (value) => value === PROMPT_CANCELLED;
|
|
1014
|
+
|
|
1015
|
+
// src/lib/tool-settings.ts
|
|
1016
|
+
var promptToolSettings = async (config) => {
|
|
1017
|
+
const want = await p.confirm({ message: config.promptMessage, initialValue: true });
|
|
1018
|
+
if (p.isCancel(want)) return PROMPT_CANCELLED;
|
|
1019
|
+
if (!want) return null;
|
|
651
1020
|
const selected = await p.multiselect({
|
|
652
1021
|
message: "\uC124\uCE58\uD560 \uC124\uC815 \uD56D\uBAA9\uC744 \uC120\uD0DD\uD558\uC138\uC694 (\uC2A4\uD398\uC774\uC2A4\uB85C \uD1A0\uAE00)",
|
|
653
|
-
options:
|
|
654
|
-
|
|
655
|
-
label: g.label,
|
|
656
|
-
hint: g.hint
|
|
657
|
-
})),
|
|
658
|
-
initialValues: SETTING_GROUPS.map((g) => g.value),
|
|
1022
|
+
options: config.groups.map((g) => ({ value: g.value, label: g.label, hint: g.hint })),
|
|
1023
|
+
initialValues: config.groups.map((g) => g.value),
|
|
659
1024
|
required: false
|
|
660
1025
|
});
|
|
661
|
-
if (p.isCancel(selected)) return
|
|
1026
|
+
if (p.isCancel(selected)) return PROMPT_CANCELLED;
|
|
662
1027
|
return selected;
|
|
663
1028
|
};
|
|
664
|
-
var
|
|
1029
|
+
var installToolSettings = (basePath, selectedValues, config) => {
|
|
665
1030
|
if (selectedValues.length === 0) return;
|
|
666
|
-
const settingsDir =
|
|
667
|
-
const settingsPath =
|
|
1031
|
+
const settingsDir = join11(basePath, config.dirName);
|
|
1032
|
+
const settingsPath = join11(settingsDir, config.fileName);
|
|
668
1033
|
let existing = {};
|
|
669
|
-
if (
|
|
1034
|
+
if (existsSync5(settingsPath)) {
|
|
670
1035
|
try {
|
|
671
|
-
existing = JSON.parse(
|
|
1036
|
+
existing = JSON.parse(readFileSync6(settingsPath, "utf-8"));
|
|
672
1037
|
} catch {
|
|
673
1038
|
}
|
|
674
1039
|
}
|
|
675
1040
|
let merged = existing;
|
|
676
1041
|
for (const val of selectedValues) {
|
|
677
|
-
const group =
|
|
1042
|
+
const group = config.groups.find((g) => g.value === val);
|
|
678
1043
|
if (!group) continue;
|
|
679
1044
|
merged = deepMerge(merged, group.patch);
|
|
680
1045
|
}
|
|
681
|
-
|
|
682
|
-
|
|
1046
|
+
mkdirSync5(settingsDir, { recursive: true });
|
|
1047
|
+
writeFileSync5(settingsPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
683
1048
|
};
|
|
684
|
-
var
|
|
685
|
-
const settingsPath =
|
|
686
|
-
if (!
|
|
1049
|
+
var uninstallToolSettings = (basePath, selectedValues, config) => {
|
|
1050
|
+
const settingsPath = join11(basePath, config.dirName, config.fileName);
|
|
1051
|
+
if (!existsSync5(settingsPath)) return "notFound";
|
|
687
1052
|
let existing = {};
|
|
688
1053
|
try {
|
|
689
|
-
existing = JSON.parse(
|
|
1054
|
+
existing = JSON.parse(readFileSync6(settingsPath, "utf-8"));
|
|
690
1055
|
} catch {
|
|
691
|
-
|
|
1056
|
+
rmSync2(settingsPath, { force: true });
|
|
692
1057
|
return "deleted";
|
|
693
1058
|
}
|
|
694
1059
|
let result = existing;
|
|
695
1060
|
for (const val of selectedValues) {
|
|
696
|
-
const group =
|
|
1061
|
+
const group = config.groups.find((g) => g.value === val);
|
|
697
1062
|
if (!group) continue;
|
|
698
1063
|
result = deepRemoveKeys(result, group.patch);
|
|
699
1064
|
}
|
|
700
1065
|
if (Object.keys(result).length === 0) {
|
|
701
|
-
|
|
1066
|
+
rmSync2(settingsPath, { force: true });
|
|
702
1067
|
return "deleted";
|
|
703
1068
|
}
|
|
704
|
-
|
|
1069
|
+
writeFileSync5(settingsPath, JSON.stringify(result, null, 2) + "\n", "utf-8");
|
|
705
1070
|
return "cleaned";
|
|
706
1071
|
};
|
|
707
1072
|
|
|
1073
|
+
// src/lib/gemini-settings.ts
|
|
1074
|
+
var SETTING_GROUPS = [
|
|
1075
|
+
{
|
|
1076
|
+
value: "ui",
|
|
1077
|
+
label: "UI \u2014 \uC904 \uBC88\uD638 \uC228\uAE30\uAE30",
|
|
1078
|
+
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",
|
|
1079
|
+
patch: { ui: { showLineNumbers: false } }
|
|
1080
|
+
},
|
|
1081
|
+
{
|
|
1082
|
+
value: "plan",
|
|
1083
|
+
label: "Plan \u2014 \uACC4\uD68D \uD30C\uC77C \uC800\uC7A5 \uBC0F \uBAA8\uB378 \uB77C\uC6B0\uD305",
|
|
1084
|
+
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",
|
|
1085
|
+
patch: { general: { plan: { directory: ".gemini/plans", modelRouting: true } } }
|
|
1086
|
+
},
|
|
1087
|
+
{
|
|
1088
|
+
value: "sessionRetention",
|
|
1089
|
+
label: "Session Retention \u2014 \uC138\uC158 30\uC77C \uBCF4\uC874",
|
|
1090
|
+
hint: "general.sessionRetention.maxAge: 30d \u2014 \uC774\uC804 \uB300\uD654 \uCEE8\uD14D\uC2A4\uD2B8\uB97C 30\uC77C\uAC04 \uC720\uC9C0",
|
|
1091
|
+
patch: { general: { sessionRetention: { maxAge: "30d" } } }
|
|
1092
|
+
},
|
|
1093
|
+
{
|
|
1094
|
+
value: "experimental",
|
|
1095
|
+
label: "Experimental \u2014 JIT \uCEE8\uD14D\uC2A4\uD2B8 + Plan \uAE30\uB2A5",
|
|
1096
|
+
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",
|
|
1097
|
+
patch: { experimental: { jitContext: true, plan: true } }
|
|
1098
|
+
}
|
|
1099
|
+
];
|
|
1100
|
+
var CONFIG = {
|
|
1101
|
+
dirName: ".gemini",
|
|
1102
|
+
fileName: "settings.json",
|
|
1103
|
+
promptMessage: "Gemini CLI \uC124\uC815 \uD30C\uC77C(.gemini/settings.json)\uC744 \uC124\uCE58\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
|
|
1104
|
+
groups: SETTING_GROUPS
|
|
1105
|
+
};
|
|
1106
|
+
var promptGeminiSettings = () => promptToolSettings(CONFIG);
|
|
1107
|
+
var installGeminiSettings = (basePath, selectedValues) => installToolSettings(basePath, selectedValues, CONFIG);
|
|
1108
|
+
var uninstallGeminiSettings = (basePath, selectedValues) => uninstallToolSettings(basePath, selectedValues, CONFIG);
|
|
1109
|
+
|
|
708
1110
|
// src/lib/claude-settings.ts
|
|
709
|
-
import * as p2 from "@clack/prompts";
|
|
710
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync6, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
711
|
-
import { join as join8 } from "path";
|
|
712
1111
|
var SETTING_GROUPS2 = [
|
|
713
1112
|
{
|
|
714
1113
|
value: "model",
|
|
@@ -723,73 +1122,20 @@ var SETTING_GROUPS2 = [
|
|
|
723
1122
|
patch: { plansDirectory: "./.claude/plans" }
|
|
724
1123
|
}
|
|
725
1124
|
];
|
|
726
|
-
var
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
if (p2.isCancel(wantSettings) || !wantSettings) return null;
|
|
732
|
-
const selected = await p2.multiselect({
|
|
733
|
-
message: "\uC124\uCE58\uD560 \uC124\uC815 \uD56D\uBAA9\uC744 \uC120\uD0DD\uD558\uC138\uC694 (\uC2A4\uD398\uC774\uC2A4\uB85C \uD1A0\uAE00)",
|
|
734
|
-
options: SETTING_GROUPS2.map((g) => ({
|
|
735
|
-
value: g.value,
|
|
736
|
-
label: g.label,
|
|
737
|
-
hint: g.hint
|
|
738
|
-
})),
|
|
739
|
-
initialValues: SETTING_GROUPS2.map((g) => g.value),
|
|
740
|
-
required: false
|
|
741
|
-
});
|
|
742
|
-
if (p2.isCancel(selected)) return null;
|
|
743
|
-
return selected;
|
|
744
|
-
};
|
|
745
|
-
var installClaudeSettings = (basePath, selectedValues) => {
|
|
746
|
-
if (selectedValues.length === 0) return;
|
|
747
|
-
const settingsDir = join8(basePath, ".claude");
|
|
748
|
-
const settingsPath = join8(settingsDir, "settings.local.json");
|
|
749
|
-
let existing = {};
|
|
750
|
-
if (existsSync4(settingsPath)) {
|
|
751
|
-
try {
|
|
752
|
-
existing = JSON.parse(readFileSync6(settingsPath, "utf-8"));
|
|
753
|
-
} catch {
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
let merged = existing;
|
|
757
|
-
for (const val of selectedValues) {
|
|
758
|
-
const group = SETTING_GROUPS2.find((g) => g.value === val);
|
|
759
|
-
if (!group) continue;
|
|
760
|
-
merged = deepMerge(merged, group.patch);
|
|
761
|
-
}
|
|
762
|
-
mkdirSync4(settingsDir, { recursive: true });
|
|
763
|
-
writeFileSync4(settingsPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
764
|
-
};
|
|
765
|
-
var uninstallClaudeSettings = (basePath, selectedValues) => {
|
|
766
|
-
const settingsPath = join8(basePath, ".claude", "settings.local.json");
|
|
767
|
-
if (!existsSync4(settingsPath)) return "notFound";
|
|
768
|
-
let existing = {};
|
|
769
|
-
try {
|
|
770
|
-
existing = JSON.parse(readFileSync6(settingsPath, "utf-8"));
|
|
771
|
-
} catch {
|
|
772
|
-
rmSync2(settingsPath, { force: true });
|
|
773
|
-
return "deleted";
|
|
774
|
-
}
|
|
775
|
-
let result = existing;
|
|
776
|
-
for (const val of selectedValues) {
|
|
777
|
-
const group = SETTING_GROUPS2.find((g) => g.value === val);
|
|
778
|
-
if (!group) continue;
|
|
779
|
-
result = deepRemoveKeys(result, group.patch);
|
|
780
|
-
}
|
|
781
|
-
if (Object.keys(result).length === 0) {
|
|
782
|
-
rmSync2(settingsPath, { force: true });
|
|
783
|
-
return "deleted";
|
|
784
|
-
}
|
|
785
|
-
writeFileSync4(settingsPath, JSON.stringify(result, null, 2) + "\n", "utf-8");
|
|
786
|
-
return "cleaned";
|
|
1125
|
+
var CONFIG2 = {
|
|
1126
|
+
dirName: ".claude",
|
|
1127
|
+
fileName: "settings.local.json",
|
|
1128
|
+
promptMessage: "Claude Code \uC124\uC815 \uD30C\uC77C(.claude/settings.local.json)\uC744 \uC124\uCE58\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
|
|
1129
|
+
groups: SETTING_GROUPS2
|
|
787
1130
|
};
|
|
1131
|
+
var promptClaudeSettings = () => promptToolSettings(CONFIG2);
|
|
1132
|
+
var installClaudeSettings = (basePath, selectedValues) => installToolSettings(basePath, selectedValues, CONFIG2);
|
|
1133
|
+
var uninstallClaudeSettings = (basePath, selectedValues) => uninstallToolSettings(basePath, selectedValues, CONFIG2);
|
|
788
1134
|
|
|
789
1135
|
// src/lib/prettier-ignore.ts
|
|
790
|
-
import * as
|
|
791
|
-
import { existsSync as
|
|
792
|
-
import { join as
|
|
1136
|
+
import * as p2 from "@clack/prompts";
|
|
1137
|
+
import { existsSync as existsSync6, readFileSync as readFileSync7, rmSync as rmSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
1138
|
+
import { join as join12 } from "path";
|
|
793
1139
|
var PRETTIER_IGNORE_CONTENT = `# CLAUDE
|
|
794
1140
|
.claude/rules/
|
|
795
1141
|
**/CLAUDE.md
|
|
@@ -847,31 +1193,31 @@ var stripAiOpsSection2 = (content) => {
|
|
|
847
1193
|
return result.join("\n");
|
|
848
1194
|
};
|
|
849
1195
|
var promptPrettierIgnore = async () => {
|
|
850
|
-
const want = await
|
|
1196
|
+
const want = await p2.confirm({
|
|
851
1197
|
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)",
|
|
852
1198
|
initialValue: false
|
|
853
1199
|
});
|
|
854
|
-
if (
|
|
1200
|
+
if (p2.isCancel(want)) return PROMPT_CANCELLED;
|
|
855
1201
|
return want;
|
|
856
1202
|
};
|
|
857
1203
|
var installPrettierIgnore = (basePath) => {
|
|
858
|
-
const filePath =
|
|
1204
|
+
const filePath = join12(basePath, ".prettierignore");
|
|
859
1205
|
const section = wrapSection(PRETTIER_IGNORE_CONTENT);
|
|
860
|
-
if (!
|
|
861
|
-
|
|
1206
|
+
if (!existsSync6(filePath)) {
|
|
1207
|
+
writeFileSync6(filePath, section + "\n", "utf-8");
|
|
862
1208
|
return;
|
|
863
1209
|
}
|
|
864
1210
|
const existing = readFileSync7(filePath, "utf-8");
|
|
865
1211
|
if (hasAiOpsSection2(existing)) {
|
|
866
|
-
|
|
1212
|
+
writeFileSync6(filePath, replaceSection(existing, PRETTIER_IGNORE_CONTENT), "utf-8");
|
|
867
1213
|
return;
|
|
868
1214
|
}
|
|
869
1215
|
const separator = existing.endsWith("\n") ? "\n" : "\n\n";
|
|
870
|
-
|
|
1216
|
+
writeFileSync6(filePath, existing + separator + section + "\n", "utf-8");
|
|
871
1217
|
};
|
|
872
1218
|
var uninstallPrettierIgnore = (basePath) => {
|
|
873
|
-
const filePath =
|
|
874
|
-
if (!
|
|
1219
|
+
const filePath = join12(basePath, ".prettierignore");
|
|
1220
|
+
if (!existsSync6(filePath)) return "notFound";
|
|
875
1221
|
const existing = readFileSync7(filePath, "utf-8");
|
|
876
1222
|
if (!hasAiOpsSection2(existing)) return "notFound";
|
|
877
1223
|
const stripped = stripAiOpsSection2(existing).trim();
|
|
@@ -879,10 +1225,54 @@ var uninstallPrettierIgnore = (basePath) => {
|
|
|
879
1225
|
rmSync3(filePath, { force: true });
|
|
880
1226
|
return "deleted";
|
|
881
1227
|
}
|
|
882
|
-
|
|
1228
|
+
writeFileSync6(filePath, stripped + "\n", "utf-8");
|
|
883
1229
|
return "cleaned";
|
|
884
1230
|
};
|
|
885
1231
|
|
|
1232
|
+
// src/lib/skill-state.ts
|
|
1233
|
+
var resolveSkillScope = (params) => {
|
|
1234
|
+
if (params.scope !== void 0) {
|
|
1235
|
+
if (params.scope === "user") return "user";
|
|
1236
|
+
if (params.scope === "project") return "project";
|
|
1237
|
+
throw new Error(`Unsupported scope: ${params.scope}`);
|
|
1238
|
+
}
|
|
1239
|
+
if (params.project) return "project";
|
|
1240
|
+
return "user";
|
|
1241
|
+
};
|
|
1242
|
+
var resolveRequestedTools = (params) => {
|
|
1243
|
+
if (params.requested === void 0 || params.requested.length === 0) {
|
|
1244
|
+
return [...params.supported];
|
|
1245
|
+
}
|
|
1246
|
+
const supportedSet = new Set(params.supported);
|
|
1247
|
+
const invalid = params.requested.filter((tool) => !supportedSet.has(tool));
|
|
1248
|
+
if (invalid.length > 0) {
|
|
1249
|
+
throw new Error(`Unsupported tools requested: ${invalid.join(", ")}`);
|
|
1250
|
+
}
|
|
1251
|
+
return [...params.requested];
|
|
1252
|
+
};
|
|
1253
|
+
var TOOL_ORDER = [SKILL_TOOL.CLAUDE_CODE, SKILL_TOOL.CODEX, SKILL_TOOL.GEMINI];
|
|
1254
|
+
var mergeSkillTools = (params) => {
|
|
1255
|
+
const merged = /* @__PURE__ */ new Set([...params.existing ?? [], ...params.requested]);
|
|
1256
|
+
return TOOL_ORDER.filter((tool) => merged.has(tool));
|
|
1257
|
+
};
|
|
1258
|
+
var subtractSkillTools = (params) => {
|
|
1259
|
+
const installed = new Set(params.installed ?? []);
|
|
1260
|
+
return params.requested.filter((tool) => !installed.has(tool));
|
|
1261
|
+
};
|
|
1262
|
+
var upsertInstalledSkill = (installedSkills, nextSkill) => {
|
|
1263
|
+
const nextSkillId = resolveCanonicalSkillId(nextSkill.id);
|
|
1264
|
+
const remaining = installedSkills.filter((skill) => resolveCanonicalSkillId(skill.id) !== nextSkillId);
|
|
1265
|
+
return [...remaining, nextSkill];
|
|
1266
|
+
};
|
|
1267
|
+
var removeInstalledSkill = (installedSkills, skillId) => {
|
|
1268
|
+
const targetSkillId = resolveCanonicalSkillId(skillId);
|
|
1269
|
+
return installedSkills.filter((skill) => resolveCanonicalSkillId(skill.id) !== targetSkillId);
|
|
1270
|
+
};
|
|
1271
|
+
var findInstalledSkill = (installedSkills, skillId) => {
|
|
1272
|
+
const targetSkillId = resolveCanonicalSkillId(skillId);
|
|
1273
|
+
return installedSkills.find((skill) => resolveCanonicalSkillId(skill.id) === targetSkillId);
|
|
1274
|
+
};
|
|
1275
|
+
|
|
886
1276
|
// src/commands/init.ts
|
|
887
1277
|
var TOOL_OPTIONS = [
|
|
888
1278
|
{ value: "claude-code", label: "Claude Code" },
|
|
@@ -891,203 +1281,393 @@ var TOOL_OPTIONS = [
|
|
|
891
1281
|
];
|
|
892
1282
|
var deduplicateRules = (rules) => {
|
|
893
1283
|
const seen = /* @__PURE__ */ new Set();
|
|
894
|
-
return rules.filter((
|
|
895
|
-
if (seen.has(
|
|
896
|
-
seen.add(
|
|
1284
|
+
return rules.filter((rule) => {
|
|
1285
|
+
if (seen.has(rule.id)) return false;
|
|
1286
|
+
seen.add(rule.id);
|
|
897
1287
|
return true;
|
|
898
1288
|
});
|
|
899
1289
|
};
|
|
900
|
-
var
|
|
901
|
-
|
|
1290
|
+
var formatToolList = (toolIds) => toolIds.join(", ");
|
|
1291
|
+
var deduplicateSkillTargets = (targets) => {
|
|
1292
|
+
const merged = /* @__PURE__ */ new Map();
|
|
1293
|
+
for (const target of targets) {
|
|
1294
|
+
const previous = merged.get(target.skill.id);
|
|
1295
|
+
if (!previous) {
|
|
1296
|
+
merged.set(target.skill.id, {
|
|
1297
|
+
skill: target.skill,
|
|
1298
|
+
requestedTools: [...target.requestedTools]
|
|
1299
|
+
});
|
|
1300
|
+
continue;
|
|
1301
|
+
}
|
|
1302
|
+
merged.set(target.skill.id, {
|
|
1303
|
+
skill: target.skill,
|
|
1304
|
+
requestedTools: mergeSkillTools({
|
|
1305
|
+
existing: previous.requestedTools,
|
|
1306
|
+
requested: target.requestedTools
|
|
1307
|
+
})
|
|
1308
|
+
});
|
|
1309
|
+
}
|
|
1310
|
+
return [...merged.values()].sort((a, b) => a.skill.id.localeCompare(b.skill.id));
|
|
1311
|
+
};
|
|
1312
|
+
var resolveSupportedRequestedTools = (skill, selectedTools) => selectedTools.filter((toolId) => skill.supported_tools.includes(toolId));
|
|
1313
|
+
var partitionPresetSkills = (params) => {
|
|
1314
|
+
const globalSkills = [];
|
|
1315
|
+
const installableSkills = [];
|
|
1316
|
+
for (const skill of resolvePresetSkills(params.preset, params.allSkills)) {
|
|
1317
|
+
if (skill.kind !== "reference") {
|
|
1318
|
+
continue;
|
|
1319
|
+
}
|
|
1320
|
+
const supportedRequestedTools = resolveSupportedRequestedTools(skill, params.selectedTools);
|
|
1321
|
+
if (supportedRequestedTools.length === 0) {
|
|
1322
|
+
continue;
|
|
1323
|
+
}
|
|
1324
|
+
const installedGlobalSkill = findInstalledSkill(params.globalInstalledSkills, skill.id);
|
|
1325
|
+
const availableTools = installedGlobalSkill ? supportedRequestedTools.filter((toolId) => installedGlobalSkill.tools.includes(toolId)) : [];
|
|
1326
|
+
const requestedTools = subtractSkillTools({
|
|
1327
|
+
requested: supportedRequestedTools,
|
|
1328
|
+
installed: availableTools
|
|
1329
|
+
});
|
|
1330
|
+
if (requestedTools.length === 0) {
|
|
1331
|
+
globalSkills.push({
|
|
1332
|
+
skill,
|
|
1333
|
+
availableTools
|
|
1334
|
+
});
|
|
1335
|
+
continue;
|
|
1336
|
+
}
|
|
1337
|
+
installableSkills.push({
|
|
1338
|
+
skill,
|
|
1339
|
+
requestedTools,
|
|
1340
|
+
globalTools: availableTools
|
|
1341
|
+
});
|
|
1342
|
+
}
|
|
1343
|
+
return {
|
|
1344
|
+
globalSkills,
|
|
1345
|
+
installableSkills
|
|
1346
|
+
};
|
|
1347
|
+
};
|
|
1348
|
+
var selectPresetAndFineTune = async (workspaceName, presets, allRules, allSkills, selectedTools, globalInstalledSkills) => {
|
|
1349
|
+
const preset = await p3.select({
|
|
902
1350
|
message: `[${workspaceName}] \uD504\uB9AC\uC14B\uC744 \uC120\uD0DD\uD558\uC138\uC694`,
|
|
903
|
-
options: presets.map((
|
|
904
|
-
value:
|
|
905
|
-
label:
|
|
906
|
-
hint:
|
|
1351
|
+
options: presets.map((candidate) => ({
|
|
1352
|
+
value: candidate,
|
|
1353
|
+
label: candidate.id,
|
|
1354
|
+
hint: candidate.description
|
|
907
1355
|
}))
|
|
908
1356
|
});
|
|
909
|
-
if (
|
|
910
|
-
const
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
const
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
1357
|
+
if (p3.isCancel(preset)) return null;
|
|
1358
|
+
const finalRules = resolvePresetRules(preset, allRules);
|
|
1359
|
+
if (finalRules.length > 0) {
|
|
1360
|
+
p3.note(finalRules.map((rule) => ` \u2713 ${rule.id}`).join("\n"), `[${workspaceName}] core rules (\uC7A0\uAE08)`);
|
|
1361
|
+
}
|
|
1362
|
+
const { globalSkills, installableSkills } = partitionPresetSkills({
|
|
1363
|
+
preset,
|
|
1364
|
+
allSkills,
|
|
1365
|
+
selectedTools,
|
|
1366
|
+
globalInstalledSkills
|
|
1367
|
+
});
|
|
1368
|
+
if (globalSkills.length > 0) {
|
|
1369
|
+
const globalLines = globalSkills.map(
|
|
1370
|
+
({ skill, availableTools }) => ` \u2713 ${skill.id} (${formatToolList(availableTools)})`
|
|
1371
|
+
);
|
|
1372
|
+
p3.note(globalLines.join("\n"), `[${workspaceName}] already available globally`);
|
|
1373
|
+
}
|
|
1374
|
+
if (installableSkills.length === 0) {
|
|
1375
|
+
p3.note(" \uC0C8\uB85C \uC124\uCE58\uD560 reference skill\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.", `[${workspaceName}] installable reference skills`);
|
|
1376
|
+
return {
|
|
1377
|
+
workspace: workspaceName,
|
|
1378
|
+
preset,
|
|
1379
|
+
finalRules,
|
|
1380
|
+
finalSkillTargets: []
|
|
1381
|
+
};
|
|
1382
|
+
}
|
|
1383
|
+
const selectedSkillIds = await p3.multiselect({
|
|
1384
|
+
message: `[${workspaceName}] installable reference skills \uC120\uD0DD`,
|
|
1385
|
+
options: installableSkills.map(({ skill, requestedTools, globalTools }) => ({
|
|
1386
|
+
value: skill.id,
|
|
1387
|
+
label: skill.id,
|
|
1388
|
+
hint: globalTools.length > 0 ? `global: ${formatToolList(globalTools)} / install: ${formatToolList(requestedTools)}` : `${skill.description} / install: ${formatToolList(requestedTools)}`
|
|
1389
|
+
})),
|
|
1390
|
+
initialValues: installableSkills.map(({ skill }) => skill.id),
|
|
925
1391
|
required: false
|
|
926
1392
|
});
|
|
927
|
-
if (
|
|
928
|
-
const
|
|
1393
|
+
if (p3.isCancel(selectedSkillIds)) return null;
|
|
1394
|
+
const selectedSkillSet = new Set(selectedSkillIds);
|
|
929
1395
|
return {
|
|
930
1396
|
workspace: workspaceName,
|
|
931
1397
|
preset,
|
|
932
|
-
finalRules
|
|
1398
|
+
finalRules,
|
|
1399
|
+
finalSkillTargets: installableSkills.filter(({ skill }) => selectedSkillSet.has(skill.id)).map(({ skill, requestedTools }) => ({
|
|
1400
|
+
skill,
|
|
1401
|
+
requestedTools
|
|
1402
|
+
}))
|
|
933
1403
|
};
|
|
934
1404
|
};
|
|
1405
|
+
var selectInitSkillScope = async () => {
|
|
1406
|
+
const scope = await p3.select({
|
|
1407
|
+
message: "\uC120\uD0DD\uB41C skills\uB97C \uC5B4\uB514\uC5D0 \uC124\uCE58\uD560\uAE4C\uC694?",
|
|
1408
|
+
options: [
|
|
1409
|
+
{ value: "user", label: "user (global)", hint: "\uAE30\uBCF8\uAC12. \uC5EC\uB7EC \uD504\uB85C\uC81D\uD2B8\uC5D0\uC11C \uC7AC\uC0AC\uC6A9" },
|
|
1410
|
+
{ value: "project", label: "project", hint: "\uD604\uC7AC \uD504\uB85C\uC81D\uD2B8\uC5D0\uB9CC \uC124\uCE58" }
|
|
1411
|
+
]
|
|
1412
|
+
});
|
|
1413
|
+
return p3.isCancel(scope) ? null : scope;
|
|
1414
|
+
};
|
|
935
1415
|
var initCommand = async () => {
|
|
936
1416
|
const basePath = resolveBasePath();
|
|
1417
|
+
const userBasePath = resolveUserBasePath();
|
|
937
1418
|
const rulesDir = resolveRulesDir();
|
|
938
|
-
|
|
939
|
-
const
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
p4.cancel("\uCDE8\uC18C\uB428");
|
|
946
|
-
process.exit(0);
|
|
947
|
-
}
|
|
948
|
-
const isMonorepo = await p4.confirm({
|
|
949
|
-
message: "\uBAA8\uB178\uB808\uD3EC \uD504\uB85C\uC81D\uD2B8\uC785\uB2C8\uAE4C?",
|
|
950
|
-
initialValue: false
|
|
951
|
-
});
|
|
952
|
-
if (p4.isCancel(isMonorepo)) {
|
|
953
|
-
p4.cancel("\uCDE8\uC18C\uB428");
|
|
954
|
-
process.exit(0);
|
|
955
|
-
}
|
|
956
|
-
const allRules = loadAllRules(rulesDir);
|
|
957
|
-
const presets = loadPresets(resolvePresetsPath());
|
|
958
|
-
const sourceHash = computeSourceHash(rulesDir);
|
|
959
|
-
const mappings = [];
|
|
960
|
-
if (!isMonorepo) {
|
|
961
|
-
const mapping = await selectPresetAndFineTune(".", presets, allRules);
|
|
962
|
-
if (!mapping) {
|
|
963
|
-
p4.cancel("\uCDE8\uC18C\uB428");
|
|
964
|
-
process.exit(0);
|
|
1419
|
+
const skillsDir = resolveSkillsDir();
|
|
1420
|
+
const spinner3 = p3.spinner();
|
|
1421
|
+
let spinnerStarted = false;
|
|
1422
|
+
const cancelInit = (params) => {
|
|
1423
|
+
if (spinnerStarted) {
|
|
1424
|
+
spinner3.stop("\uC124\uCE58 \uC911\uB2E8\uB428");
|
|
1425
|
+
spinnerStarted = false;
|
|
965
1426
|
}
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
1427
|
+
p3.cancel(params?.message ?? "\uCDE8\uC18C\uB428");
|
|
1428
|
+
process.exit(params?.exitCode ?? 0);
|
|
1429
|
+
};
|
|
1430
|
+
const handleSigint = () => cancelInit({
|
|
1431
|
+
message: "\uC0AC\uC6A9\uC790 \uC694\uCCAD\uC73C\uB85C init\uC774 \uC911\uB2E8\uB418\uC5C8\uC2B5\uB2C8\uB2E4.",
|
|
1432
|
+
exitCode: 130
|
|
1433
|
+
});
|
|
1434
|
+
process.once("SIGINT", handleSigint);
|
|
1435
|
+
try {
|
|
1436
|
+
p3.intro("ai-ops init");
|
|
1437
|
+
const selectedTools = await p3.multiselect({
|
|
1438
|
+
message: "AI \uB3C4\uAD6C\uB97C \uC120\uD0DD\uD558\uC138\uC694",
|
|
1439
|
+
options: TOOL_OPTIONS,
|
|
972
1440
|
required: true
|
|
973
1441
|
});
|
|
974
|
-
if (
|
|
975
|
-
|
|
976
|
-
process.exit(0);
|
|
1442
|
+
if (p3.isCancel(selectedTools)) {
|
|
1443
|
+
cancelInit();
|
|
977
1444
|
}
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
mappings.push(mapping);
|
|
1445
|
+
const isMonorepo = await p3.confirm({
|
|
1446
|
+
message: "\uBAA8\uB178\uB808\uD3EC \uD504\uB85C\uC81D\uD2B8\uC785\uB2C8\uAE4C?",
|
|
1447
|
+
initialValue: false
|
|
1448
|
+
});
|
|
1449
|
+
if (p3.isCancel(isMonorepo)) {
|
|
1450
|
+
cancelInit();
|
|
985
1451
|
}
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
const renderResult = renderForTool(toolId, allRules2, workspaceMappings);
|
|
1003
|
-
const actions = buildInstallPlan({ toolId, renderResult, meta });
|
|
1004
|
-
const result = installFiles(basePath, actions, meta);
|
|
1005
|
-
allInstalledFiles.push(...result.written);
|
|
1006
|
-
allAppended.push(...result.appended);
|
|
1452
|
+
const allRules = loadAllRules(rulesDir);
|
|
1453
|
+
const allSkills = loadAllSkills(skillsDir);
|
|
1454
|
+
const presets = loadPresets(resolvePresetsPath());
|
|
1455
|
+
const sourceHash = computeSourceHash(resolveCompilerDataDir());
|
|
1456
|
+
const globalInstalledSkills = readSkillRegistry(resolveSkillRegistryPath(userBasePath))?.skills ?? [];
|
|
1457
|
+
const mappings = [];
|
|
1458
|
+
if (!isMonorepo) {
|
|
1459
|
+
const mapping = await selectPresetAndFineTune(
|
|
1460
|
+
".",
|
|
1461
|
+
presets,
|
|
1462
|
+
allRules,
|
|
1463
|
+
allSkills,
|
|
1464
|
+
selectedTools,
|
|
1465
|
+
globalInstalledSkills
|
|
1466
|
+
) ?? cancelInit();
|
|
1467
|
+
mappings.push(mapping);
|
|
1007
1468
|
} else {
|
|
1008
|
-
const
|
|
1009
|
-
const
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1469
|
+
const candidates = listWorkspaceCandidates(basePath);
|
|
1470
|
+
const selectedWorkspaces = await p3.multiselect({
|
|
1471
|
+
message: "\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4\uB97C \uC120\uD0DD\uD558\uC138\uC694",
|
|
1472
|
+
options: candidates.map((candidate) => ({ value: candidate, label: candidate })),
|
|
1473
|
+
required: true
|
|
1474
|
+
});
|
|
1475
|
+
if (p3.isCancel(selectedWorkspaces)) {
|
|
1476
|
+
cancelInit();
|
|
1477
|
+
}
|
|
1478
|
+
for (const workspace of selectedWorkspaces) {
|
|
1479
|
+
const mapping = await selectPresetAndFineTune(
|
|
1480
|
+
workspace,
|
|
1481
|
+
presets,
|
|
1482
|
+
allRules,
|
|
1483
|
+
allSkills,
|
|
1484
|
+
selectedTools,
|
|
1485
|
+
globalInstalledSkills
|
|
1486
|
+
) ?? cancelInit();
|
|
1487
|
+
mappings.push(mapping);
|
|
1488
|
+
}
|
|
1013
1489
|
}
|
|
1490
|
+
const selectedSkillTargets = deduplicateSkillTargets(mappings.flatMap((mapping) => mapping.finalSkillTargets));
|
|
1491
|
+
const skillScope = selectedSkillTargets.length > 0 ? await selectInitSkillScope() : null;
|
|
1492
|
+
if (selectedSkillTargets.length > 0 && skillScope === null) {
|
|
1493
|
+
cancelInit();
|
|
1494
|
+
}
|
|
1495
|
+
const geminiSettingValues = selectedTools.includes("gemini") ? await promptGeminiSettings() : null;
|
|
1496
|
+
const resolvedGeminiSettingValues = isPromptCancelled(geminiSettingValues) ? cancelInit() : geminiSettingValues;
|
|
1497
|
+
const claudeSettingValues = selectedTools.includes("claude-code") ? await promptClaudeSettings() : null;
|
|
1498
|
+
const resolvedClaudeSettingValues = isPromptCancelled(claudeSettingValues) ? cancelInit() : claudeSettingValues;
|
|
1499
|
+
const wantPrettierIgnore = await promptPrettierIgnore();
|
|
1500
|
+
const resolvedWantPrettierIgnore = isPromptCancelled(wantPrettierIgnore) ? cancelInit() : wantPrettierIgnore;
|
|
1501
|
+
spinner3.start("\uADDC\uCE59 \uC124\uCE58 \uC911...");
|
|
1502
|
+
spinnerStarted = true;
|
|
1503
|
+
const meta = { sourceHash, generatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
1504
|
+
const allInstalledFiles = [];
|
|
1505
|
+
const allAppended = [];
|
|
1506
|
+
const selectedRuleIds = deduplicateRules(mappings.flatMap((mapping) => mapping.finalRules)).map((rule) => rule.id);
|
|
1507
|
+
let projectInstalledSkills = [];
|
|
1508
|
+
if (selectedSkillTargets.length > 0 && skillScope !== null) {
|
|
1509
|
+
const skillBasePath = skillScope === "project" ? basePath : userBasePath;
|
|
1510
|
+
const installedSkills = selectedSkillTargets.map(({ skill, requestedTools }) => {
|
|
1511
|
+
const existingUserSkill = skillScope === "user" ? findInstalledSkill(globalInstalledSkills, skill.id) : void 0;
|
|
1512
|
+
const nextRequestedTools = skillScope === "user" ? mergeSkillTools({
|
|
1513
|
+
existing: existingUserSkill?.tools,
|
|
1514
|
+
requested: requestedTools
|
|
1515
|
+
}) : requestedTools;
|
|
1516
|
+
const { packages, installedSkill } = buildSkillInstallPlan({
|
|
1517
|
+
skill,
|
|
1518
|
+
requestedTools: nextRequestedTools,
|
|
1519
|
+
scope: skillScope
|
|
1520
|
+
});
|
|
1521
|
+
installSkillPackages(skillBasePath, packages);
|
|
1522
|
+
return installedSkill;
|
|
1523
|
+
});
|
|
1524
|
+
if (skillScope === "project") {
|
|
1525
|
+
projectInstalledSkills = installedSkills;
|
|
1526
|
+
} else {
|
|
1527
|
+
const registryPath = resolveSkillRegistryPath(skillBasePath);
|
|
1528
|
+
const previous = readSkillRegistry(registryPath);
|
|
1529
|
+
const nextSkills = installedSkills.reduce(
|
|
1530
|
+
(acc, installedSkill) => upsertInstalledSkill(acc, installedSkill),
|
|
1531
|
+
previous?.skills ?? []
|
|
1532
|
+
);
|
|
1533
|
+
writeSkillRegistry(registryPath, {
|
|
1534
|
+
skills: nextSkills,
|
|
1535
|
+
cliVersion: getCliVersion(),
|
|
1536
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1537
|
+
});
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
for (const toolId of selectedTools) {
|
|
1541
|
+
if (isMonorepo) {
|
|
1542
|
+
const allWorkspaceRules = deduplicateRules(mappings.flatMap((mapping) => mapping.finalRules));
|
|
1543
|
+
const workspaceMappings = mappings.map((mapping) => ({
|
|
1544
|
+
path: mapping.workspace,
|
|
1545
|
+
ruleIds: mapping.finalRules.map((rule) => rule.id)
|
|
1546
|
+
}));
|
|
1547
|
+
const renderResult = renderForTool(toolId, allWorkspaceRules, workspaceMappings);
|
|
1548
|
+
const actions = buildInstallPlan({ toolId, renderResult, meta });
|
|
1549
|
+
const result = installFiles(basePath, actions, meta);
|
|
1550
|
+
allInstalledFiles.push(...result.written);
|
|
1551
|
+
allAppended.push(...result.appended);
|
|
1552
|
+
} else {
|
|
1553
|
+
const renderResult = renderForTool(toolId, mappings[0].finalRules);
|
|
1554
|
+
const actions = buildInstallPlan({ toolId, renderResult, meta });
|
|
1555
|
+
const result = installFiles(basePath, actions, meta);
|
|
1556
|
+
allInstalledFiles.push(...result.written);
|
|
1557
|
+
allAppended.push(...result.appended);
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
if (resolvedGeminiSettingValues && resolvedGeminiSettingValues.length > 0) {
|
|
1561
|
+
installGeminiSettings(basePath, resolvedGeminiSettingValues);
|
|
1562
|
+
}
|
|
1563
|
+
if (resolvedClaudeSettingValues && resolvedClaudeSettingValues.length > 0) {
|
|
1564
|
+
installClaudeSettings(basePath, resolvedClaudeSettingValues);
|
|
1565
|
+
}
|
|
1566
|
+
if (resolvedWantPrettierIgnore) {
|
|
1567
|
+
installPrettierIgnore(basePath);
|
|
1568
|
+
}
|
|
1569
|
+
spinner3.stop("\uADDC\uCE59 \uC124\uCE58 \uC644\uB8CC");
|
|
1570
|
+
spinnerStarted = false;
|
|
1571
|
+
const workspacesRecord = isMonorepo ? Object.fromEntries(
|
|
1572
|
+
mappings.map((mapping) => [
|
|
1573
|
+
mapping.workspace,
|
|
1574
|
+
{
|
|
1575
|
+
preset: mapping.preset.id,
|
|
1576
|
+
rules: mapping.finalRules.map((rule) => rule.id)
|
|
1577
|
+
}
|
|
1578
|
+
])
|
|
1579
|
+
) : void 0;
|
|
1580
|
+
const manifest = buildManifest({
|
|
1581
|
+
tools: selectedTools,
|
|
1582
|
+
scope: "project",
|
|
1583
|
+
preset: !isMonorepo ? mappings[0].preset.id : void 0,
|
|
1584
|
+
workspaces: workspacesRecord,
|
|
1585
|
+
installedRules: selectedRuleIds,
|
|
1586
|
+
installedFiles: allInstalledFiles,
|
|
1587
|
+
installedSkills: projectInstalledSkills,
|
|
1588
|
+
appendedFiles: allAppended,
|
|
1589
|
+
settings: resolvedClaudeSettingValues || resolvedGeminiSettingValues || resolvedWantPrettierIgnore ? {
|
|
1590
|
+
claude: resolvedClaudeSettingValues ? [...resolvedClaudeSettingValues] : void 0,
|
|
1591
|
+
gemini: resolvedGeminiSettingValues ? [...resolvedGeminiSettingValues] : void 0,
|
|
1592
|
+
prettierignore: resolvedWantPrettierIgnore || void 0
|
|
1593
|
+
} : void 0,
|
|
1594
|
+
cliVersion: getCliVersion(),
|
|
1595
|
+
sourceHash
|
|
1596
|
+
});
|
|
1597
|
+
writeManifest(resolveManifestPath(basePath), manifest);
|
|
1598
|
+
if (allAppended.length > 0) {
|
|
1599
|
+
p3.log.info(`\uAE30\uC874 \uD30C\uC77C\uC5D0 \uC139\uC158 \uCD94\uAC00\uB428 (\uB0B4\uC6A9 \uBCF4\uC874):
|
|
1600
|
+
${allAppended.map((file) => ` ${file}`).join("\n")}`);
|
|
1601
|
+
}
|
|
1602
|
+
p3.log.success(`\uC124\uCE58\uB41C core rules: ${selectedRuleIds.length}\uAC1C`);
|
|
1603
|
+
p3.log.success(`\uC124\uCE58\uB41C skills: ${selectedSkillTargets.length}\uAC1C${skillScope ? ` (${skillScope})` : ""}`);
|
|
1604
|
+
if (selectedSkillTargets.length > 0 && skillScope === "user") {
|
|
1605
|
+
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.");
|
|
1606
|
+
}
|
|
1607
|
+
p3.outro("ai-ops init \uC644\uB8CC");
|
|
1608
|
+
} finally {
|
|
1609
|
+
process.off("SIGINT", handleSigint);
|
|
1014
1610
|
}
|
|
1015
|
-
if (geminiSettingValues && geminiSettingValues.length > 0) {
|
|
1016
|
-
installGeminiSettings(basePath, geminiSettingValues);
|
|
1017
|
-
}
|
|
1018
|
-
if (claudeSettingValues && claudeSettingValues.length > 0) {
|
|
1019
|
-
installClaudeSettings(basePath, claudeSettingValues);
|
|
1020
|
-
}
|
|
1021
|
-
if (wantPrettierIgnore) {
|
|
1022
|
-
installPrettierIgnore(basePath);
|
|
1023
|
-
}
|
|
1024
|
-
s.stop("\uADDC\uCE59 \uC124\uCE58 \uC644\uB8CC");
|
|
1025
|
-
const allInstalledRuleIds = deduplicateRules(mappings.flatMap((m) => m.finalRules)).map((r) => r.id);
|
|
1026
|
-
const workspacesRecord = isMonorepo ? Object.fromEntries(
|
|
1027
|
-
mappings.map((m) => [m.workspace, { preset: m.preset.id, rules: m.finalRules.map((r) => r.id) }])
|
|
1028
|
-
) : void 0;
|
|
1029
|
-
const manifest = buildManifest({
|
|
1030
|
-
tools: selectedTools,
|
|
1031
|
-
scope: "project",
|
|
1032
|
-
preset: !isMonorepo ? mappings[0].preset.id : void 0,
|
|
1033
|
-
workspaces: workspacesRecord,
|
|
1034
|
-
installedRules: allInstalledRuleIds,
|
|
1035
|
-
installedFiles: allInstalledFiles,
|
|
1036
|
-
appendedFiles: allAppended,
|
|
1037
|
-
settings: claudeSettingValues || geminiSettingValues || wantPrettierIgnore ? {
|
|
1038
|
-
claude: claudeSettingValues ? [...claudeSettingValues] : void 0,
|
|
1039
|
-
gemini: geminiSettingValues ? [...geminiSettingValues] : void 0,
|
|
1040
|
-
prettierignore: wantPrettierIgnore || void 0
|
|
1041
|
-
} : void 0,
|
|
1042
|
-
cliVersion: getCliVersion(),
|
|
1043
|
-
sourceHash
|
|
1044
|
-
});
|
|
1045
|
-
writeManifest(resolveManifestPath(basePath), manifest);
|
|
1046
|
-
if (allAppended.length > 0) {
|
|
1047
|
-
p4.log.info(`\uAE30\uC874 \uD30C\uC77C\uC5D0 \uC139\uC158 \uCD94\uAC00\uB428 (\uB0B4\uC6A9 \uBCF4\uC874):
|
|
1048
|
-
${allAppended.map((f) => ` ${f}`).join("\n")}`);
|
|
1049
|
-
}
|
|
1050
|
-
p4.log.success(`\uC124\uCE58\uB41C \uADDC\uCE59: ${allInstalledRuleIds.length}\uAC1C`);
|
|
1051
|
-
p4.outro("ai-ops init \uC644\uB8CC");
|
|
1052
1611
|
};
|
|
1053
1612
|
|
|
1054
1613
|
// src/commands/update.ts
|
|
1055
|
-
import * as
|
|
1614
|
+
import * as p4 from "@clack/prompts";
|
|
1056
1615
|
var updateCommand = async (opts) => {
|
|
1057
1616
|
const basePath = resolveBasePath();
|
|
1058
1617
|
const manifestPath = resolveManifestPath(basePath);
|
|
1059
|
-
|
|
1618
|
+
p4.intro("ai-ops update");
|
|
1060
1619
|
const manifest = readManifest(manifestPath);
|
|
1061
1620
|
if (!manifest) {
|
|
1062
|
-
|
|
1621
|
+
p4.log.error("manifest\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 ai-ops init\uC744 \uC2E4\uD589\uD558\uC138\uC694.");
|
|
1063
1622
|
process.exit(1);
|
|
1064
1623
|
}
|
|
1065
1624
|
const rulesDir = resolveRulesDir();
|
|
1066
|
-
const
|
|
1625
|
+
const skillsDir = resolveSkillsDir();
|
|
1626
|
+
const presetsPath = resolvePresetsPath();
|
|
1627
|
+
const sourceHash = computeSourceHash(resolveCompilerDataDir());
|
|
1067
1628
|
const cliVersion = getCliVersion();
|
|
1629
|
+
const allRules = loadAllRules(rulesDir);
|
|
1630
|
+
const allSkills = loadAllSkills(skillsDir);
|
|
1631
|
+
const presets = loadPresets(presetsPath);
|
|
1632
|
+
const resolvedRules = resolveManifestRules({
|
|
1633
|
+
manifest,
|
|
1634
|
+
allRules,
|
|
1635
|
+
presets
|
|
1636
|
+
});
|
|
1637
|
+
const resolvedSkills = resolveManifestProjectSkills({
|
|
1638
|
+
manifest,
|
|
1639
|
+
allSkills
|
|
1640
|
+
});
|
|
1068
1641
|
const diffResult = computeDiff({
|
|
1069
1642
|
previous: manifest,
|
|
1070
|
-
currentRules:
|
|
1643
|
+
currentRules: resolvedRules.installedRules.map((rule) => rule.id),
|
|
1071
1644
|
currentSourceHash: sourceHash,
|
|
1072
1645
|
currentCliVersion: cliVersion
|
|
1073
1646
|
});
|
|
1074
1647
|
if (diffResult.status === "up-to-date" && !opts.force) {
|
|
1075
|
-
|
|
1076
|
-
|
|
1648
|
+
p4.log.info("\uBCC0\uACBD \uC0AC\uD56D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
1649
|
+
p4.outro("ai-ops update \uC644\uB8CC");
|
|
1077
1650
|
return;
|
|
1078
1651
|
}
|
|
1079
|
-
const s =
|
|
1652
|
+
const s = p4.spinner();
|
|
1080
1653
|
s.start("\uADDC\uCE59 \uAC31\uC2E0 \uC911...");
|
|
1081
|
-
const allRules = loadAllRules(rulesDir);
|
|
1082
1654
|
const meta = { sourceHash, generatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
1083
1655
|
const allInstalledFiles = [];
|
|
1084
1656
|
const allAppended = [];
|
|
1657
|
+
const installedSkills = resolvedSkills.map(({ skill, requestedTools }) => {
|
|
1658
|
+
const { packages, installedSkill } = buildSkillInstallPlan({
|
|
1659
|
+
skill,
|
|
1660
|
+
requestedTools,
|
|
1661
|
+
scope: "project"
|
|
1662
|
+
});
|
|
1663
|
+
installSkillPackages(basePath, packages);
|
|
1664
|
+
return installedSkill;
|
|
1665
|
+
});
|
|
1085
1666
|
if (manifest.workspaces) {
|
|
1086
|
-
const workspaceEntries = Object.entries(
|
|
1667
|
+
const workspaceEntries = Object.entries(resolvedRules.workspaces ?? {});
|
|
1087
1668
|
for (const toolIdStr of manifest.tools) {
|
|
1088
1669
|
const toolId = toolIdStr;
|
|
1089
|
-
const
|
|
1090
|
-
const rulesToInstall = allRules.filter((r2) => allInstalledRuleSet.has(r2.id));
|
|
1670
|
+
const rulesToInstall = resolvedRules.installedRules;
|
|
1091
1671
|
const workspaceMappings = workspaceEntries.map(([path, entry]) => ({
|
|
1092
1672
|
path,
|
|
1093
1673
|
ruleIds: entry.rules
|
|
@@ -1099,8 +1679,7 @@ var updateCommand = async (opts) => {
|
|
|
1099
1679
|
allAppended.push(...r.appended);
|
|
1100
1680
|
}
|
|
1101
1681
|
} else {
|
|
1102
|
-
const
|
|
1103
|
-
const rulesToInstall = allRules.filter((r) => installedRuleSet.has(r.id));
|
|
1682
|
+
const rulesToInstall = resolvedRules.installedRules;
|
|
1104
1683
|
for (const toolIdStr of manifest.tools) {
|
|
1105
1684
|
const toolId = toolIdStr;
|
|
1106
1685
|
const renderResult = renderForTool(toolId, rulesToInstall);
|
|
@@ -1123,9 +1702,10 @@ var updateCommand = async (opts) => {
|
|
|
1123
1702
|
tools: manifest.tools,
|
|
1124
1703
|
scope: manifest.scope,
|
|
1125
1704
|
preset: manifest.preset,
|
|
1126
|
-
workspaces:
|
|
1127
|
-
installedRules:
|
|
1705
|
+
workspaces: resolvedRules.workspaces,
|
|
1706
|
+
installedRules: resolvedRules.installedRules.map((rule) => rule.id),
|
|
1128
1707
|
installedFiles: allInstalledFiles.length > 0 ? allInstalledFiles : manifest.installed_files,
|
|
1708
|
+
installedSkills,
|
|
1129
1709
|
appendedFiles: allAppended.length > 0 ? allAppended : manifest.appended_files,
|
|
1130
1710
|
settings: manifest.settings ? {
|
|
1131
1711
|
claude: manifest.settings.claude,
|
|
@@ -1137,56 +1717,83 @@ var updateCommand = async (opts) => {
|
|
|
1137
1717
|
});
|
|
1138
1718
|
writeManifest(manifestPath, newManifest);
|
|
1139
1719
|
s.stop("\uADDC\uCE59 \uAC31\uC2E0 \uC644\uB8CC");
|
|
1140
|
-
|
|
1720
|
+
p4.outro("ai-ops update \uC644\uB8CC");
|
|
1141
1721
|
};
|
|
1142
1722
|
|
|
1143
1723
|
// src/commands/diff.ts
|
|
1144
|
-
import * as
|
|
1724
|
+
import * as p5 from "@clack/prompts";
|
|
1145
1725
|
var diffCommand = async () => {
|
|
1146
1726
|
const basePath = resolveBasePath();
|
|
1147
|
-
|
|
1727
|
+
p5.intro("ai-ops diff");
|
|
1148
1728
|
const manifest = readManifest(resolveManifestPath(basePath));
|
|
1149
1729
|
if (!manifest) {
|
|
1150
|
-
|
|
1730
|
+
p5.log.error("manifest\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 ai-ops init\uC744 \uC2E4\uD589\uD558\uC138\uC694.");
|
|
1151
1731
|
process.exit(1);
|
|
1152
1732
|
}
|
|
1153
|
-
const sourceHash = computeSourceHash(
|
|
1733
|
+
const sourceHash = computeSourceHash(resolveCompilerDataDir());
|
|
1734
|
+
const allRules = loadAllRules(resolveRulesDir());
|
|
1735
|
+
const allSkills = loadAllSkills(resolveSkillsDir());
|
|
1736
|
+
const presets = loadPresets(resolvePresetsPath());
|
|
1737
|
+
const resolvedRules = resolveManifestRules({
|
|
1738
|
+
manifest,
|
|
1739
|
+
allRules,
|
|
1740
|
+
presets
|
|
1741
|
+
});
|
|
1742
|
+
const resolvedSkills = resolveManifestProjectSkills({
|
|
1743
|
+
manifest,
|
|
1744
|
+
allSkills
|
|
1745
|
+
});
|
|
1154
1746
|
const result = computeDiff({
|
|
1155
1747
|
previous: manifest,
|
|
1156
|
-
currentRules:
|
|
1748
|
+
currentRules: resolvedRules.installedRules.map((rule) => rule.id),
|
|
1157
1749
|
currentSourceHash: sourceHash
|
|
1158
1750
|
});
|
|
1751
|
+
const skillLines = resolvedSkills.map(({ skill, requestedTools }) => {
|
|
1752
|
+
const { installedSkill: next } = buildSkillInstallPlan({
|
|
1753
|
+
skill,
|
|
1754
|
+
requestedTools,
|
|
1755
|
+
scope: "project"
|
|
1756
|
+
});
|
|
1757
|
+
const previous = (manifest.installed_skills ?? []).find((installedSkill) => installedSkill.id === skill.id);
|
|
1758
|
+
const previousHash = previous?.sourceHash ?? "legacy";
|
|
1759
|
+
const changed = previousHash !== next.sourceHash;
|
|
1760
|
+
return `- ${skill.id}: ${changed ? "changed" : "up-to-date"} (${previousHash} -> ${next.sourceHash})`;
|
|
1761
|
+
});
|
|
1159
1762
|
if (result.status === "up-to-date") {
|
|
1160
|
-
|
|
1763
|
+
p5.log.success("\uBCC0\uACBD \uC0AC\uD56D \uC5C6\uC74C. \uCD5C\uC2E0 \uC0C1\uD0DC\uC785\uB2C8\uB2E4.");
|
|
1161
1764
|
} else {
|
|
1162
1765
|
if (result.sourceChanged) {
|
|
1163
|
-
|
|
1766
|
+
p5.log.warn(`\uC18C\uC2A4 \uBCC0\uACBD \uAC10\uC9C0: ${manifest.sourceHash} \u2192 ${sourceHash}`);
|
|
1164
1767
|
}
|
|
1165
1768
|
if (result.added.length > 0) {
|
|
1166
|
-
|
|
1769
|
+
p5.log.info(`\uCD94\uAC00\uB41C \uADDC\uCE59: ${result.added.join(", ")}`);
|
|
1167
1770
|
}
|
|
1168
1771
|
if (result.removed.length > 0) {
|
|
1169
|
-
|
|
1772
|
+
p5.log.info(`\uC81C\uAC70\uB41C \uADDC\uCE59: ${result.removed.join(", ")}`);
|
|
1170
1773
|
}
|
|
1171
1774
|
}
|
|
1172
|
-
|
|
1775
|
+
if (skillLines.length > 0) {
|
|
1776
|
+
p5.log.info(`project skills:
|
|
1777
|
+
${skillLines.map((line) => ` ${line}`).join("\n")}`);
|
|
1778
|
+
}
|
|
1779
|
+
p5.outro("ai-ops diff \uC644\uB8CC");
|
|
1173
1780
|
};
|
|
1174
1781
|
|
|
1175
1782
|
// src/commands/uninstall.ts
|
|
1176
|
-
import * as
|
|
1783
|
+
import * as p6 from "@clack/prompts";
|
|
1177
1784
|
import { rmSync as rmSync5 } from "fs";
|
|
1178
1785
|
|
|
1179
1786
|
// src/lib/uninstall.ts
|
|
1180
|
-
import { existsSync as
|
|
1181
|
-
import { resolve as
|
|
1787
|
+
import { existsSync as existsSync7, readFileSync as readFileSync8, rmSync as rmSync4, readdirSync as readdirSync4, writeFileSync as writeFileSync7 } from "fs";
|
|
1788
|
+
import { resolve as resolve7, dirname as dirname7 } from "path";
|
|
1182
1789
|
var removeFiles = (basePath, relativePaths) => {
|
|
1183
1790
|
const deleted = [];
|
|
1184
1791
|
const cleaned = [];
|
|
1185
1792
|
const skipped = [];
|
|
1186
1793
|
const notFound = [];
|
|
1187
1794
|
for (const rel of relativePaths) {
|
|
1188
|
-
const absPath =
|
|
1189
|
-
if (!
|
|
1795
|
+
const absPath = resolve7(basePath, rel);
|
|
1796
|
+
if (!existsSync7(absPath)) {
|
|
1190
1797
|
notFound.push(rel);
|
|
1191
1798
|
continue;
|
|
1192
1799
|
}
|
|
@@ -1197,7 +1804,7 @@ var removeFiles = (basePath, relativePaths) => {
|
|
|
1197
1804
|
rmSync4(absPath);
|
|
1198
1805
|
deleted.push(rel);
|
|
1199
1806
|
} else {
|
|
1200
|
-
|
|
1807
|
+
writeFileSync7(absPath, stripped, "utf-8");
|
|
1201
1808
|
cleaned.push(rel);
|
|
1202
1809
|
}
|
|
1203
1810
|
} else if (hasLegacyHeader(content)) {
|
|
@@ -1212,8 +1819,8 @@ var removeFiles = (basePath, relativePaths) => {
|
|
|
1212
1819
|
var cleanEmptyDirs = (basePath, dirs) => {
|
|
1213
1820
|
const removed = [];
|
|
1214
1821
|
for (const dir of dirs) {
|
|
1215
|
-
const absDir =
|
|
1216
|
-
if (!
|
|
1822
|
+
const absDir = resolve7(basePath, dir);
|
|
1823
|
+
if (!existsSync7(absDir)) continue;
|
|
1217
1824
|
try {
|
|
1218
1825
|
const entries = readdirSync4(absDir);
|
|
1219
1826
|
if (entries.length === 0) {
|
|
@@ -1228,7 +1835,7 @@ var cleanEmptyDirs = (basePath, dirs) => {
|
|
|
1228
1835
|
var collectManagedDirs = (relativePaths) => {
|
|
1229
1836
|
const dirs = /* @__PURE__ */ new Set();
|
|
1230
1837
|
for (const rel of relativePaths) {
|
|
1231
|
-
const dir =
|
|
1838
|
+
const dir = dirname7(rel);
|
|
1232
1839
|
if (dir !== ".") {
|
|
1233
1840
|
dirs.add(dir);
|
|
1234
1841
|
}
|
|
@@ -1241,36 +1848,46 @@ var SETTINGS_PATHS = /* @__PURE__ */ new Set([".claude/settings.local.json", ".g
|
|
|
1241
1848
|
var uninstallCommand = async () => {
|
|
1242
1849
|
const basePath = resolveBasePath();
|
|
1243
1850
|
const manifestPath = resolveManifestPath(basePath);
|
|
1244
|
-
|
|
1851
|
+
p6.intro("ai-ops uninstall");
|
|
1245
1852
|
const manifest = readManifest(manifestPath);
|
|
1246
1853
|
if (!manifest) {
|
|
1247
|
-
|
|
1854
|
+
p6.log.error("manifest\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 ai-ops init\uC744 \uC2E4\uD589\uD558\uC138\uC694.");
|
|
1248
1855
|
process.exit(1);
|
|
1249
1856
|
}
|
|
1250
1857
|
const targetFiles = [
|
|
1251
1858
|
...manifest.installed_files ?? inferInstalledFiles(manifest),
|
|
1252
1859
|
...manifest.appended_files ?? []
|
|
1253
1860
|
].filter((f) => !SETTINGS_PATHS.has(f));
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1861
|
+
const targetSkillDirs = (manifest.installed_skills ?? []).flatMap((skill) => skill.installed_paths);
|
|
1862
|
+
if (targetFiles.length === 0 && targetSkillDirs.length === 0) {
|
|
1863
|
+
p6.log.warn("\uC0AD\uC81C\uD560 \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
1864
|
+
p6.outro("ai-ops uninstall \uC644\uB8CC");
|
|
1257
1865
|
return;
|
|
1258
1866
|
}
|
|
1259
|
-
|
|
1867
|
+
if (targetFiles.length > 0) {
|
|
1868
|
+
p6.log.info(`\uC0AD\uC81C \uB300\uC0C1 \uD30C\uC77C (${targetFiles.length}\uAC1C):
|
|
1260
1869
|
${targetFiles.map((f) => ` ${f}`).join("\n")}`);
|
|
1261
|
-
|
|
1870
|
+
}
|
|
1871
|
+
if (targetSkillDirs.length > 0) {
|
|
1872
|
+
p6.log.info(
|
|
1873
|
+
`\uC0AD\uC81C \uB300\uC0C1 skill \uB514\uB809\uD1A0\uB9AC (${targetSkillDirs.length}\uAC1C):
|
|
1874
|
+
${targetSkillDirs.map((f) => ` ${f}`).join("\n")}`
|
|
1875
|
+
);
|
|
1876
|
+
}
|
|
1877
|
+
const confirmed = await p6.confirm({
|
|
1262
1878
|
message: "\uC704 \uD30C\uC77C\uACFC manifest\uB97C \uBAA8\uB450 \uC0AD\uC81C\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
|
|
1263
1879
|
initialValue: false
|
|
1264
1880
|
});
|
|
1265
|
-
if (
|
|
1266
|
-
|
|
1881
|
+
if (p6.isCancel(confirmed) || !confirmed) {
|
|
1882
|
+
p6.cancel("\uCDE8\uC18C\uB428");
|
|
1267
1883
|
process.exit(0);
|
|
1268
1884
|
}
|
|
1269
1885
|
const settingsMessages = [];
|
|
1270
1886
|
if (manifest.settings?.claude) {
|
|
1271
1887
|
const status = uninstallClaudeSettings(basePath, manifest.settings.claude);
|
|
1272
1888
|
if (status === "deleted") settingsMessages.push("\uC0AD\uC81C: .claude/settings.local.json");
|
|
1273
|
-
else if (status === "cleaned")
|
|
1889
|
+
else if (status === "cleaned")
|
|
1890
|
+
settingsMessages.push("ai-ops \uD0A4 \uC81C\uAC70 (\uC0AC\uC6A9\uC790 \uC124\uC815 \uBCF4\uC874): .claude/settings.local.json");
|
|
1274
1891
|
}
|
|
1275
1892
|
if (manifest.settings?.gemini) {
|
|
1276
1893
|
const status = uninstallGeminiSettings(basePath, manifest.settings.gemini);
|
|
@@ -1281,54 +1898,339 @@ ${targetFiles.map((f) => ` ${f}`).join("\n")}`);
|
|
|
1281
1898
|
if (prettierStatus === "deleted") settingsMessages.push("\uC0AD\uC81C: .prettierignore");
|
|
1282
1899
|
else if (prettierStatus === "cleaned") settingsMessages.push("ai-ops \uC139\uC158 \uC81C\uAC70 (\uC0AC\uC6A9\uC790 \uB0B4\uC6A9 \uBCF4\uC874): .prettierignore");
|
|
1283
1900
|
const result = removeFiles(basePath, targetFiles);
|
|
1901
|
+
const removedSkillDirs = removeDirectories(basePath, targetSkillDirs);
|
|
1284
1902
|
const dirs = collectManagedDirs(targetFiles);
|
|
1285
1903
|
const removedDirs = cleanEmptyDirs(basePath, dirs);
|
|
1286
1904
|
rmSync5(manifestPath, { force: true });
|
|
1287
1905
|
if (result.deleted.length > 0) {
|
|
1288
|
-
|
|
1906
|
+
p6.log.success(`\uC0AD\uC81C \uC644\uB8CC (${result.deleted.length}\uAC1C):
|
|
1289
1907
|
${result.deleted.map((f) => ` ${f}`).join("\n")}`);
|
|
1290
1908
|
}
|
|
1291
1909
|
if (result.cleaned.length > 0) {
|
|
1292
|
-
|
|
1910
|
+
p6.log.success(
|
|
1293
1911
|
`\uC139\uC158 \uC81C\uAC70 \uC644\uB8CC (\uC0AC\uC6A9\uC790 \uB0B4\uC6A9 \uBCF4\uC874, ${result.cleaned.length}\uAC1C):
|
|
1294
1912
|
${result.cleaned.map((f) => ` ${f}`).join("\n")}`
|
|
1295
1913
|
);
|
|
1296
1914
|
}
|
|
1297
1915
|
if (result.skipped.length > 0) {
|
|
1298
|
-
|
|
1916
|
+
p6.log.warn(
|
|
1299
1917
|
`\uAC74\uB108\uB700 (non-managed \uD30C\uC77C \uBCF4\uD638, ${result.skipped.length}\uAC1C):
|
|
1300
1918
|
${result.skipped.map((f) => ` ${f}`).join("\n")}`
|
|
1301
1919
|
);
|
|
1302
1920
|
}
|
|
1303
1921
|
if (result.notFound.length > 0) {
|
|
1304
|
-
|
|
1922
|
+
p6.log.info(`\uC774\uBBF8 \uC5C6\uC74C (${result.notFound.length}\uAC1C):
|
|
1305
1923
|
${result.notFound.map((f) => ` ${f}`).join("\n")}`);
|
|
1306
1924
|
}
|
|
1307
1925
|
if (removedDirs.length > 0) {
|
|
1308
|
-
|
|
1926
|
+
p6.log.info(`\uBE48 \uB514\uB809\uD1A0\uB9AC \uC815\uB9AC (${removedDirs.length}\uAC1C):
|
|
1309
1927
|
${removedDirs.map((d) => ` ${d}`).join("\n")}`);
|
|
1310
1928
|
}
|
|
1929
|
+
if (removedSkillDirs.length > 0) {
|
|
1930
|
+
p6.log.success(
|
|
1931
|
+
`skill \uB514\uB809\uD1A0\uB9AC \uC0AD\uC81C (${removedSkillDirs.length}\uAC1C):
|
|
1932
|
+
${removedSkillDirs.map((d) => ` ${d}`).join("\n")}`
|
|
1933
|
+
);
|
|
1934
|
+
}
|
|
1311
1935
|
if (settingsMessages.length > 0) {
|
|
1312
|
-
|
|
1936
|
+
p6.log.success(`\uC124\uC815 \uD30C\uC77C \uCC98\uB9AC:
|
|
1313
1937
|
${settingsMessages.map((m) => ` ${m}`).join("\n")}`);
|
|
1314
1938
|
}
|
|
1315
|
-
|
|
1316
|
-
|
|
1939
|
+
p6.log.success(`manifest \uC0AD\uC81C: ${MANIFEST_FILENAME}`);
|
|
1940
|
+
p6.outro("ai-ops uninstall \uC644\uB8CC");
|
|
1317
1941
|
};
|
|
1318
1942
|
|
|
1319
|
-
// src/
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1943
|
+
// src/commands/skill.ts
|
|
1944
|
+
import * as p7 from "@clack/prompts";
|
|
1945
|
+
import { rmSync as rmSync6 } from "fs";
|
|
1946
|
+
var resolveScopeContext = (opts) => {
|
|
1947
|
+
const scope = resolveSkillScope(opts);
|
|
1948
|
+
return {
|
|
1949
|
+
scope,
|
|
1950
|
+
basePath: scope === "project" ? resolveBasePath() : resolveUserBasePath()
|
|
1951
|
+
};
|
|
1952
|
+
};
|
|
1953
|
+
var loadCompilerInputs = () => {
|
|
1954
|
+
const compilerDataDir = resolveCompilerDataDir();
|
|
1955
|
+
return {
|
|
1956
|
+
allSkills: loadAllSkills(resolveSkillsDir()),
|
|
1957
|
+
sourceHash: computeSourceHash(compilerDataDir),
|
|
1958
|
+
cliVersion: getCliVersion()
|
|
1959
|
+
};
|
|
1960
|
+
};
|
|
1961
|
+
var resolveSkillById = (skills, skillId) => {
|
|
1962
|
+
const canonicalSkillId = resolveCanonicalSkillId(skillId);
|
|
1963
|
+
const skill = skills.find((candidate) => candidate.id === canonicalSkillId);
|
|
1964
|
+
if (!skill) {
|
|
1965
|
+
throw new Error(`Unknown skill: ${skillId}`);
|
|
1325
1966
|
}
|
|
1967
|
+
return skill;
|
|
1326
1968
|
};
|
|
1969
|
+
var assertScopeAllowed = (skill, scope) => {
|
|
1970
|
+
if (!skill.install_scopes.includes(scope)) {
|
|
1971
|
+
throw new Error(`Skill ${skill.id} does not support ${scope} scope`);
|
|
1972
|
+
}
|
|
1973
|
+
};
|
|
1974
|
+
var writeProjectSkillState = (params) => {
|
|
1975
|
+
const manifestPath = resolveManifestPath(params.basePath);
|
|
1976
|
+
const previous = readManifest(manifestPath);
|
|
1977
|
+
const installedSkills = params.removeSkillId ? removeInstalledSkill(previous?.installed_skills ?? [], params.removeSkillId) : params.nextSkill ? upsertInstalledSkill(previous?.installed_skills ?? [], params.nextSkill) : previous?.installed_skills ?? [];
|
|
1978
|
+
const nextTools = params.nextSkill !== void 0 ? [.../* @__PURE__ */ new Set([...previous?.tools ?? [], ...params.nextSkill.tools])] : previous?.tools ?? [];
|
|
1979
|
+
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;
|
|
1980
|
+
if (!hasProjectState) {
|
|
1981
|
+
rmSync6(manifestPath, { force: true });
|
|
1982
|
+
return;
|
|
1983
|
+
}
|
|
1984
|
+
const manifest = buildManifest({
|
|
1985
|
+
tools: nextTools.length > 0 ? nextTools : params.nextSkill?.tools ?? ["codex"],
|
|
1986
|
+
scope: "project",
|
|
1987
|
+
preset: previous?.preset,
|
|
1988
|
+
workspaces: previous?.workspaces,
|
|
1989
|
+
installedRules: previous?.installed_rules ?? [],
|
|
1990
|
+
installedFiles: previous?.installed_files,
|
|
1991
|
+
installedSkills,
|
|
1992
|
+
appendedFiles: previous?.appended_files,
|
|
1993
|
+
settings: previous?.settings,
|
|
1994
|
+
cliVersion: params.cliVersion,
|
|
1995
|
+
sourceHash: params.sourceHash
|
|
1996
|
+
});
|
|
1997
|
+
writeManifest(manifestPath, manifest);
|
|
1998
|
+
};
|
|
1999
|
+
var writeUserSkillState = (params) => {
|
|
2000
|
+
const registryPath = resolveSkillRegistryPath(params.basePath);
|
|
2001
|
+
const previous = readSkillRegistry(registryPath);
|
|
2002
|
+
const skills = params.removeSkillId ? removeInstalledSkill(previous?.skills ?? [], params.removeSkillId) : params.nextSkill ? upsertInstalledSkill(previous?.skills ?? [], params.nextSkill) : previous?.skills ?? [];
|
|
2003
|
+
if (skills.length === 0) {
|
|
2004
|
+
rmSync6(registryPath, { force: true });
|
|
2005
|
+
return;
|
|
2006
|
+
}
|
|
2007
|
+
writeSkillRegistry(registryPath, {
|
|
2008
|
+
skills,
|
|
2009
|
+
cliVersion: params.cliVersion,
|
|
2010
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2011
|
+
});
|
|
2012
|
+
};
|
|
2013
|
+
var readInstalledSkills = (scope, basePath) => {
|
|
2014
|
+
if (scope === "project") {
|
|
2015
|
+
return (readManifest(resolveManifestPath(basePath))?.installed_skills ?? []).map((installedSkill) => ({
|
|
2016
|
+
...installedSkill,
|
|
2017
|
+
id: resolveCanonicalSkillId(installedSkill.id)
|
|
2018
|
+
}));
|
|
2019
|
+
}
|
|
2020
|
+
return (readSkillRegistry(resolveSkillRegistryPath(basePath))?.skills ?? []).map((installedSkill) => ({
|
|
2021
|
+
...installedSkill,
|
|
2022
|
+
id: resolveCanonicalSkillId(installedSkill.id)
|
|
2023
|
+
}));
|
|
2024
|
+
};
|
|
2025
|
+
var installSkill = (params) => {
|
|
2026
|
+
const installedSkills = readInstalledSkills(params.scope, params.basePath);
|
|
2027
|
+
const existingInstalledSkill = findInstalledSkill(installedSkills, params.skill.id);
|
|
2028
|
+
const nextRequestedTools = mergeSkillTools({
|
|
2029
|
+
existing: existingInstalledSkill?.tools,
|
|
2030
|
+
requested: params.requestedTools
|
|
2031
|
+
});
|
|
2032
|
+
const { packages, installedSkill } = buildSkillInstallPlan({
|
|
2033
|
+
skill: params.skill,
|
|
2034
|
+
requestedTools: nextRequestedTools,
|
|
2035
|
+
scope: params.scope
|
|
2036
|
+
});
|
|
2037
|
+
installSkillPackages(params.basePath, packages);
|
|
2038
|
+
if (params.scope === "project") {
|
|
2039
|
+
writeProjectSkillState({
|
|
2040
|
+
basePath: params.basePath,
|
|
2041
|
+
sourceHash: params.sourceHash,
|
|
2042
|
+
cliVersion: params.cliVersion,
|
|
2043
|
+
nextSkill: installedSkill
|
|
2044
|
+
});
|
|
2045
|
+
} else {
|
|
2046
|
+
writeUserSkillState({
|
|
2047
|
+
basePath: params.basePath,
|
|
2048
|
+
cliVersion: params.cliVersion,
|
|
2049
|
+
nextSkill: installedSkill
|
|
2050
|
+
});
|
|
2051
|
+
}
|
|
2052
|
+
return installedSkill;
|
|
2053
|
+
};
|
|
2054
|
+
var skillListCommand = async (opts) => {
|
|
2055
|
+
const { scope, basePath } = resolveScopeContext(opts);
|
|
2056
|
+
const { allSkills } = loadCompilerInputs();
|
|
2057
|
+
const installedSkills = readInstalledSkills(scope, basePath);
|
|
2058
|
+
p7.intro(`ai-ops skill list (${scope})`);
|
|
2059
|
+
const sections = [
|
|
2060
|
+
{ kind: "reference", title: "reference skills" },
|
|
2061
|
+
{ kind: "task", title: "task skills" }
|
|
2062
|
+
].map(({ kind, title }) => {
|
|
2063
|
+
const lines = allSkills.filter((skill) => skill.kind === kind).map((skill) => {
|
|
2064
|
+
const installed = findInstalledSkill(installedSkills, skill.id);
|
|
2065
|
+
const suffix = installed ? `installed for ${installed.tools.join(", ")}` : "not installed";
|
|
2066
|
+
return `- ${skill.id} (${skill.install_scopes.join(", ")}) - ${suffix}`;
|
|
2067
|
+
});
|
|
2068
|
+
if (lines.length === 0) {
|
|
2069
|
+
return null;
|
|
2070
|
+
}
|
|
2071
|
+
return `${title}
|
|
2072
|
+
${lines.join("\n")}`;
|
|
2073
|
+
}).filter((section) => section !== null);
|
|
2074
|
+
p7.log.info(sections.join("\n\n"));
|
|
2075
|
+
p7.outro("ai-ops skill list \uC644\uB8CC");
|
|
2076
|
+
};
|
|
2077
|
+
var skillInstallCommand = async (skillId, opts) => {
|
|
2078
|
+
const { scope, basePath } = resolveScopeContext(opts);
|
|
2079
|
+
const { allSkills, sourceHash, cliVersion } = loadCompilerInputs();
|
|
2080
|
+
const skill = resolveSkillById(allSkills, skillId);
|
|
2081
|
+
assertScopeAllowed(skill, scope);
|
|
2082
|
+
const requestedTools = resolveRequestedTools({ requested: opts.tool, supported: skill.supported_tools });
|
|
2083
|
+
p7.intro(`ai-ops skill install ${skillId}`);
|
|
2084
|
+
const installedSkill = installSkill({
|
|
2085
|
+
skill,
|
|
2086
|
+
requestedTools,
|
|
2087
|
+
scope,
|
|
2088
|
+
basePath,
|
|
2089
|
+
cliVersion,
|
|
2090
|
+
sourceHash
|
|
2091
|
+
});
|
|
2092
|
+
p7.log.success(`\uC124\uCE58 \uC644\uB8CC: ${installedSkill.id} (${installedSkill.installed_paths.join(", ")})`);
|
|
2093
|
+
p7.outro("ai-ops skill install \uC644\uB8CC");
|
|
2094
|
+
};
|
|
2095
|
+
var skillDiffCommand = async (skillId, opts) => {
|
|
2096
|
+
const { scope, basePath } = resolveScopeContext(opts);
|
|
2097
|
+
const { allSkills } = loadCompilerInputs();
|
|
2098
|
+
const installedSkills = readInstalledSkills(scope, basePath);
|
|
2099
|
+
const targets = skillId ? installedSkills.filter((skill) => skill.id === skillId) : installedSkills;
|
|
2100
|
+
p7.intro(`ai-ops skill diff (${scope})`);
|
|
2101
|
+
if (targets.length === 0) {
|
|
2102
|
+
p7.log.warn("\uBE44\uAD50\uD560 \uC124\uCE58\uB41C skill\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
2103
|
+
p7.outro("ai-ops skill diff \uC644\uB8CC");
|
|
2104
|
+
return;
|
|
2105
|
+
}
|
|
2106
|
+
const lines = targets.map((installedSkill) => {
|
|
2107
|
+
const skill = resolveSkillById(allSkills, installedSkill.id);
|
|
2108
|
+
const { installedSkill: next } = buildSkillInstallPlan({
|
|
2109
|
+
skill,
|
|
2110
|
+
requestedTools: installedSkill.tools,
|
|
2111
|
+
scope
|
|
2112
|
+
});
|
|
2113
|
+
const changed = next.sourceHash !== installedSkill.sourceHash;
|
|
2114
|
+
return `- ${installedSkill.id}: ${changed ? "changed" : "up-to-date"} (${installedSkill.sourceHash} -> ${next.sourceHash})`;
|
|
2115
|
+
});
|
|
2116
|
+
p7.log.info(lines.join("\n"));
|
|
2117
|
+
p7.outro("ai-ops skill diff \uC644\uB8CC");
|
|
2118
|
+
};
|
|
2119
|
+
var skillUpdateCommand = async (skillId, opts) => {
|
|
2120
|
+
const { scope, basePath } = resolveScopeContext(opts);
|
|
2121
|
+
const { allSkills, sourceHash, cliVersion } = loadCompilerInputs();
|
|
2122
|
+
const installedSkills = readInstalledSkills(scope, basePath);
|
|
2123
|
+
const targets = skillId ? installedSkills.filter((skill) => skill.id === skillId) : installedSkills;
|
|
2124
|
+
p7.intro(`ai-ops skill update (${scope})`);
|
|
2125
|
+
if (targets.length === 0) {
|
|
2126
|
+
p7.log.warn("\uAC31\uC2E0\uD560 \uC124\uCE58\uB41C skill\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
2127
|
+
p7.outro("ai-ops skill update \uC644\uB8CC");
|
|
2128
|
+
return;
|
|
2129
|
+
}
|
|
2130
|
+
const nextInstalledSkills = targets.map((installedSkill) => {
|
|
2131
|
+
const skill = resolveSkillById(allSkills, installedSkill.id);
|
|
2132
|
+
const { packages, installedSkill: next } = buildSkillInstallPlan({
|
|
2133
|
+
skill,
|
|
2134
|
+
requestedTools: installedSkill.tools,
|
|
2135
|
+
scope
|
|
2136
|
+
});
|
|
2137
|
+
installSkillPackages(basePath, packages);
|
|
2138
|
+
return next;
|
|
2139
|
+
});
|
|
2140
|
+
if (scope === "project") {
|
|
2141
|
+
const manifestPath = resolveManifestPath(basePath);
|
|
2142
|
+
const previous = readManifest(manifestPath);
|
|
2143
|
+
if (!previous) {
|
|
2144
|
+
p7.log.error("project manifest\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
2145
|
+
process.exit(1);
|
|
2146
|
+
}
|
|
2147
|
+
const untouched = (previous.installed_skills ?? []).filter(
|
|
2148
|
+
(installedSkill) => !nextInstalledSkills.some((next) => next.id === installedSkill.id)
|
|
2149
|
+
);
|
|
2150
|
+
writeManifest(
|
|
2151
|
+
manifestPath,
|
|
2152
|
+
buildManifest({
|
|
2153
|
+
tools: previous.tools,
|
|
2154
|
+
scope: previous.scope,
|
|
2155
|
+
preset: previous.preset,
|
|
2156
|
+
workspaces: previous.workspaces,
|
|
2157
|
+
installedRules: previous.installed_rules,
|
|
2158
|
+
installedFiles: previous.installed_files,
|
|
2159
|
+
installedSkills: [...untouched, ...nextInstalledSkills],
|
|
2160
|
+
appendedFiles: previous.appended_files,
|
|
2161
|
+
settings: previous.settings,
|
|
2162
|
+
cliVersion,
|
|
2163
|
+
sourceHash
|
|
2164
|
+
})
|
|
2165
|
+
);
|
|
2166
|
+
} else {
|
|
2167
|
+
const registryPath = resolveSkillRegistryPath(basePath);
|
|
2168
|
+
const previous = readSkillRegistry(registryPath);
|
|
2169
|
+
const untouched = (previous?.skills ?? []).filter(
|
|
2170
|
+
(installedSkill) => !nextInstalledSkills.some((next) => next.id === installedSkill.id)
|
|
2171
|
+
);
|
|
2172
|
+
writeSkillRegistry(registryPath, {
|
|
2173
|
+
skills: [...untouched, ...nextInstalledSkills],
|
|
2174
|
+
cliVersion,
|
|
2175
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2176
|
+
});
|
|
2177
|
+
}
|
|
2178
|
+
p7.log.success(`\uAC31\uC2E0 \uC644\uB8CC: ${nextInstalledSkills.map((skill) => skill.id).join(", ")}`);
|
|
2179
|
+
p7.outro("ai-ops skill update \uC644\uB8CC");
|
|
2180
|
+
};
|
|
2181
|
+
var skillUninstallCommand = async (skillId, opts) => {
|
|
2182
|
+
const { scope, basePath } = resolveScopeContext(opts);
|
|
2183
|
+
const { sourceHash, cliVersion } = loadCompilerInputs();
|
|
2184
|
+
const installedSkills = readInstalledSkills(scope, basePath);
|
|
2185
|
+
const installedSkill = findInstalledSkill(installedSkills, skillId);
|
|
2186
|
+
p7.intro(`ai-ops skill uninstall ${skillId}`);
|
|
2187
|
+
if (!installedSkill) {
|
|
2188
|
+
p7.log.warn("\uC124\uCE58\uB41C skill\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.");
|
|
2189
|
+
p7.outro("ai-ops skill uninstall \uC644\uB8CC");
|
|
2190
|
+
return;
|
|
2191
|
+
}
|
|
2192
|
+
const removed = removeDirectories(basePath, installedSkill.installed_paths);
|
|
2193
|
+
if (scope === "project") {
|
|
2194
|
+
writeProjectSkillState({
|
|
2195
|
+
basePath,
|
|
2196
|
+
sourceHash,
|
|
2197
|
+
cliVersion,
|
|
2198
|
+
removeSkillId: skillId
|
|
2199
|
+
});
|
|
2200
|
+
} else {
|
|
2201
|
+
writeUserSkillState({
|
|
2202
|
+
basePath,
|
|
2203
|
+
cliVersion,
|
|
2204
|
+
removeSkillId: skillId
|
|
2205
|
+
});
|
|
2206
|
+
}
|
|
2207
|
+
p7.log.success(`\uC81C\uAC70 \uC644\uB8CC: ${removed.join(", ")}`);
|
|
2208
|
+
p7.outro("ai-ops skill uninstall \uC644\uB8CC");
|
|
2209
|
+
};
|
|
2210
|
+
|
|
2211
|
+
// src/bin/index.ts
|
|
2212
|
+
var program = new Command();
|
|
1327
2213
|
program.name("ai-ops").description("AI \uC5D0\uC774\uC804\uD2B8 \uADDC\uCE59 \uC2A4\uCE90\uD3F4\uB354").version("0.1.0");
|
|
1328
2214
|
program.command("init").description("AI \uADDC\uCE59 \uCD08\uAE30 \uC124\uCE58").action(() => initCommand());
|
|
1329
2215
|
program.command("update").description("\uAE30\uC874 manifest \uAE30\uBC18 \uADDC\uCE59 \uAC31\uC2E0").option("--force", "\uBCC0\uACBD \uC5C6\uC5B4\uB3C4 \uAC15\uC81C \uC7AC\uC124\uCE58", false).action((opts) => updateCommand(opts));
|
|
1330
2216
|
program.command("diff").description("\uC124\uCE58\uB41C \uADDC\uCE59\uACFC \uCD5C\uC2E0 \uC18C\uC2A4 \uBE44\uAD50").action(() => diffCommand());
|
|
1331
2217
|
program.command("uninstall").description("\uC124\uCE58\uB41C \uADDC\uCE59 \uD30C\uC77C \uBC0F manifest \uC81C\uAC70").action(() => uninstallCommand());
|
|
1332
|
-
|
|
2218
|
+
var skillCommand = program.command("skill").description("\uC5D0\uC774\uC804\uD2B8 skill \uC124\uCE58/\uC870\uD68C/\uAC31\uC2E0");
|
|
2219
|
+
var applySkillScopeOptions = (command) => command.option("-g, --global", "user scope\uC5D0 \uC124\uCE58/\uC870\uD68C").option("--project", "project scope\uC5D0 \uC124\uCE58/\uC870\uD68C").option("--scope <scope>", "explicit scope (user|project)").option("--tool <tool...>", "\uB300\uC0C1 \uB3C4\uAD6C \uC9C0\uC815");
|
|
2220
|
+
applySkillScopeOptions(skillCommand.command("list").description("\uC0AC\uC6A9 \uAC00\uB2A5\uD55C skill \uBAA9\uB85D")).action(
|
|
2221
|
+
(opts) => skillListCommand(opts)
|
|
2222
|
+
);
|
|
2223
|
+
applySkillScopeOptions(skillCommand.command("install <skillId>").description("skill \uC124\uCE58")).action(
|
|
2224
|
+
(skillId, opts) => skillInstallCommand(skillId, opts)
|
|
2225
|
+
);
|
|
2226
|
+
applySkillScopeOptions(skillCommand.command("diff [skillId]").description("skill \uBCC0\uACBD \uBE44\uAD50")).action(
|
|
2227
|
+
(skillId, opts) => skillDiffCommand(skillId, opts)
|
|
2228
|
+
);
|
|
2229
|
+
applySkillScopeOptions(skillCommand.command("update [skillId]").description("skill \uAC31\uC2E0")).action(
|
|
2230
|
+
(skillId, opts) => skillUpdateCommand(skillId, opts)
|
|
2231
|
+
);
|
|
2232
|
+
applySkillScopeOptions(skillCommand.command("uninstall <skillId>").description("skill \uC81C\uAC70")).action(
|
|
2233
|
+
(skillId, opts) => skillUninstallCommand(skillId, opts)
|
|
2234
|
+
);
|
|
1333
2235
|
program.parse();
|
|
1334
2236
|
//# sourceMappingURL=index.js.map
|