ai-ops-cli 0.1.24 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +107 -45
- package/data/presets.yaml +0 -23
- 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 +1263 -307
- 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
|
@@ -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)
|
|
48
106
|
}).strict();
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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 })
|
|
52
114
|
}).strict();
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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()
|
|
122
|
+
}).strict();
|
|
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 = [];
|
|
@@ -240,7 +362,7 @@ var partitionRules = (rules) => {
|
|
|
240
362
|
return { global, domain };
|
|
241
363
|
};
|
|
242
364
|
var renderFrontmatter = (paths) => {
|
|
243
|
-
const lines = paths.map((
|
|
365
|
+
const lines = paths.map((p9) => ` - "${p9}"`).join("\n");
|
|
244
366
|
return `---
|
|
245
367
|
paths:
|
|
246
368
|
${lines}
|
|
@@ -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
|
|
|
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
|
+
|
|
583
976
|
// src/lib/gemini-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,6 +1008,10 @@ var deepRemoveKeys = (base, patch) => {
|
|
|
615
1008
|
return result;
|
|
616
1009
|
};
|
|
617
1010
|
|
|
1011
|
+
// src/lib/prompt-control.ts
|
|
1012
|
+
var PROMPT_CANCELLED = /* @__PURE__ */ Symbol("prompt-cancelled");
|
|
1013
|
+
var isPromptCancelled = (value) => value === PROMPT_CANCELLED;
|
|
1014
|
+
|
|
618
1015
|
// src/lib/gemini-settings.ts
|
|
619
1016
|
var SETTING_GROUPS = [
|
|
620
1017
|
{
|
|
@@ -647,7 +1044,8 @@ var promptGeminiSettings = async () => {
|
|
|
647
1044
|
message: "Gemini CLI \uC124\uC815 \uD30C\uC77C(.gemini/settings.json)\uC744 \uC124\uCE58\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
|
|
648
1045
|
initialValue: true
|
|
649
1046
|
});
|
|
650
|
-
if (p.isCancel(wantSettings)
|
|
1047
|
+
if (p.isCancel(wantSettings)) return PROMPT_CANCELLED;
|
|
1048
|
+
if (!wantSettings) return null;
|
|
651
1049
|
const selected = await p.multiselect({
|
|
652
1050
|
message: "\uC124\uCE58\uD560 \uC124\uC815 \uD56D\uBAA9\uC744 \uC120\uD0DD\uD558\uC138\uC694 (\uC2A4\uD398\uC774\uC2A4\uB85C \uD1A0\uAE00)",
|
|
653
1051
|
options: SETTING_GROUPS.map((g) => ({
|
|
@@ -658,17 +1056,17 @@ var promptGeminiSettings = async () => {
|
|
|
658
1056
|
initialValues: SETTING_GROUPS.map((g) => g.value),
|
|
659
1057
|
required: false
|
|
660
1058
|
});
|
|
661
|
-
if (p.isCancel(selected)) return
|
|
1059
|
+
if (p.isCancel(selected)) return PROMPT_CANCELLED;
|
|
662
1060
|
return selected;
|
|
663
1061
|
};
|
|
664
1062
|
var installGeminiSettings = (basePath, selectedValues) => {
|
|
665
1063
|
if (selectedValues.length === 0) return;
|
|
666
|
-
const settingsDir =
|
|
667
|
-
const settingsPath =
|
|
1064
|
+
const settingsDir = join11(basePath, ".gemini");
|
|
1065
|
+
const settingsPath = join11(settingsDir, "settings.json");
|
|
668
1066
|
let existing = {};
|
|
669
|
-
if (
|
|
1067
|
+
if (existsSync5(settingsPath)) {
|
|
670
1068
|
try {
|
|
671
|
-
existing = JSON.parse(
|
|
1069
|
+
existing = JSON.parse(readFileSync6(settingsPath, "utf-8"));
|
|
672
1070
|
} catch {
|
|
673
1071
|
}
|
|
674
1072
|
}
|
|
@@ -678,37 +1076,40 @@ var installGeminiSettings = (basePath, selectedValues) => {
|
|
|
678
1076
|
if (!group) continue;
|
|
679
1077
|
merged = deepMerge(merged, group.patch);
|
|
680
1078
|
}
|
|
681
|
-
|
|
682
|
-
|
|
1079
|
+
mkdirSync5(settingsDir, { recursive: true });
|
|
1080
|
+
writeFileSync5(settingsPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
683
1081
|
};
|
|
684
1082
|
var uninstallGeminiSettings = (basePath, selectedValues) => {
|
|
685
|
-
const settingsPath =
|
|
686
|
-
if (!
|
|
1083
|
+
const settingsPath = join11(basePath, ".gemini", "settings.json");
|
|
1084
|
+
if (!existsSync5(settingsPath)) return "notFound";
|
|
687
1085
|
let existing = {};
|
|
688
1086
|
try {
|
|
689
|
-
existing = JSON.parse(
|
|
1087
|
+
existing = JSON.parse(readFileSync6(settingsPath, "utf-8"));
|
|
690
1088
|
} catch {
|
|
691
|
-
|
|
1089
|
+
rmSync2(settingsPath, { force: true });
|
|
692
1090
|
return "deleted";
|
|
693
1091
|
}
|
|
694
1092
|
let result = existing;
|
|
695
1093
|
for (const val of selectedValues) {
|
|
696
1094
|
const group = SETTING_GROUPS.find((g) => g.value === val);
|
|
697
1095
|
if (!group) continue;
|
|
698
|
-
result = deepRemoveKeys(
|
|
1096
|
+
result = deepRemoveKeys(
|
|
1097
|
+
result,
|
|
1098
|
+
group.patch
|
|
1099
|
+
);
|
|
699
1100
|
}
|
|
700
1101
|
if (Object.keys(result).length === 0) {
|
|
701
|
-
|
|
1102
|
+
rmSync2(settingsPath, { force: true });
|
|
702
1103
|
return "deleted";
|
|
703
1104
|
}
|
|
704
|
-
|
|
1105
|
+
writeFileSync5(settingsPath, JSON.stringify(result, null, 2) + "\n", "utf-8");
|
|
705
1106
|
return "cleaned";
|
|
706
1107
|
};
|
|
707
1108
|
|
|
708
1109
|
// src/lib/claude-settings.ts
|
|
709
1110
|
import * as p2 from "@clack/prompts";
|
|
710
|
-
import { existsSync as
|
|
711
|
-
import { join as
|
|
1111
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync6, readFileSync as readFileSync7, rmSync as rmSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
1112
|
+
import { join as join12 } from "path";
|
|
712
1113
|
var SETTING_GROUPS2 = [
|
|
713
1114
|
{
|
|
714
1115
|
value: "model",
|
|
@@ -728,7 +1129,8 @@ var promptClaudeSettings = async () => {
|
|
|
728
1129
|
message: "Claude Code \uC124\uC815 \uD30C\uC77C(.claude/settings.local.json)\uC744 \uC124\uCE58\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
|
|
729
1130
|
initialValue: true
|
|
730
1131
|
});
|
|
731
|
-
if (p2.isCancel(wantSettings)
|
|
1132
|
+
if (p2.isCancel(wantSettings)) return PROMPT_CANCELLED;
|
|
1133
|
+
if (!wantSettings) return null;
|
|
732
1134
|
const selected = await p2.multiselect({
|
|
733
1135
|
message: "\uC124\uCE58\uD560 \uC124\uC815 \uD56D\uBAA9\uC744 \uC120\uD0DD\uD558\uC138\uC694 (\uC2A4\uD398\uC774\uC2A4\uB85C \uD1A0\uAE00)",
|
|
734
1136
|
options: SETTING_GROUPS2.map((g) => ({
|
|
@@ -739,17 +1141,17 @@ var promptClaudeSettings = async () => {
|
|
|
739
1141
|
initialValues: SETTING_GROUPS2.map((g) => g.value),
|
|
740
1142
|
required: false
|
|
741
1143
|
});
|
|
742
|
-
if (p2.isCancel(selected)) return
|
|
1144
|
+
if (p2.isCancel(selected)) return PROMPT_CANCELLED;
|
|
743
1145
|
return selected;
|
|
744
1146
|
};
|
|
745
1147
|
var installClaudeSettings = (basePath, selectedValues) => {
|
|
746
1148
|
if (selectedValues.length === 0) return;
|
|
747
|
-
const settingsDir =
|
|
748
|
-
const settingsPath =
|
|
1149
|
+
const settingsDir = join12(basePath, ".claude");
|
|
1150
|
+
const settingsPath = join12(settingsDir, "settings.local.json");
|
|
749
1151
|
let existing = {};
|
|
750
|
-
if (
|
|
1152
|
+
if (existsSync6(settingsPath)) {
|
|
751
1153
|
try {
|
|
752
|
-
existing = JSON.parse(
|
|
1154
|
+
existing = JSON.parse(readFileSync7(settingsPath, "utf-8"));
|
|
753
1155
|
} catch {
|
|
754
1156
|
}
|
|
755
1157
|
}
|
|
@@ -759,37 +1161,40 @@ var installClaudeSettings = (basePath, selectedValues) => {
|
|
|
759
1161
|
if (!group) continue;
|
|
760
1162
|
merged = deepMerge(merged, group.patch);
|
|
761
1163
|
}
|
|
762
|
-
|
|
763
|
-
|
|
1164
|
+
mkdirSync6(settingsDir, { recursive: true });
|
|
1165
|
+
writeFileSync6(settingsPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
764
1166
|
};
|
|
765
1167
|
var uninstallClaudeSettings = (basePath, selectedValues) => {
|
|
766
|
-
const settingsPath =
|
|
767
|
-
if (!
|
|
1168
|
+
const settingsPath = join12(basePath, ".claude", "settings.local.json");
|
|
1169
|
+
if (!existsSync6(settingsPath)) return "notFound";
|
|
768
1170
|
let existing = {};
|
|
769
1171
|
try {
|
|
770
|
-
existing = JSON.parse(
|
|
1172
|
+
existing = JSON.parse(readFileSync7(settingsPath, "utf-8"));
|
|
771
1173
|
} catch {
|
|
772
|
-
|
|
1174
|
+
rmSync3(settingsPath, { force: true });
|
|
773
1175
|
return "deleted";
|
|
774
1176
|
}
|
|
775
1177
|
let result = existing;
|
|
776
1178
|
for (const val of selectedValues) {
|
|
777
1179
|
const group = SETTING_GROUPS2.find((g) => g.value === val);
|
|
778
1180
|
if (!group) continue;
|
|
779
|
-
result = deepRemoveKeys(
|
|
1181
|
+
result = deepRemoveKeys(
|
|
1182
|
+
result,
|
|
1183
|
+
group.patch
|
|
1184
|
+
);
|
|
780
1185
|
}
|
|
781
1186
|
if (Object.keys(result).length === 0) {
|
|
782
|
-
|
|
1187
|
+
rmSync3(settingsPath, { force: true });
|
|
783
1188
|
return "deleted";
|
|
784
1189
|
}
|
|
785
|
-
|
|
1190
|
+
writeFileSync6(settingsPath, JSON.stringify(result, null, 2) + "\n", "utf-8");
|
|
786
1191
|
return "cleaned";
|
|
787
1192
|
};
|
|
788
1193
|
|
|
789
1194
|
// src/lib/prettier-ignore.ts
|
|
790
1195
|
import * as p3 from "@clack/prompts";
|
|
791
|
-
import { existsSync as
|
|
792
|
-
import { join as
|
|
1196
|
+
import { existsSync as existsSync7, readFileSync as readFileSync8, rmSync as rmSync4, writeFileSync as writeFileSync7 } from "fs";
|
|
1197
|
+
import { join as join13 } from "path";
|
|
793
1198
|
var PRETTIER_IGNORE_CONTENT = `# CLAUDE
|
|
794
1199
|
.claude/rules/
|
|
795
1200
|
**/CLAUDE.md
|
|
@@ -851,38 +1256,82 @@ var promptPrettierIgnore = async () => {
|
|
|
851
1256
|
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
1257
|
initialValue: false
|
|
853
1258
|
});
|
|
854
|
-
if (p3.isCancel(want)) return
|
|
1259
|
+
if (p3.isCancel(want)) return PROMPT_CANCELLED;
|
|
855
1260
|
return want;
|
|
856
1261
|
};
|
|
857
1262
|
var installPrettierIgnore = (basePath) => {
|
|
858
|
-
const filePath =
|
|
1263
|
+
const filePath = join13(basePath, ".prettierignore");
|
|
859
1264
|
const section = wrapSection(PRETTIER_IGNORE_CONTENT);
|
|
860
|
-
if (!
|
|
861
|
-
|
|
1265
|
+
if (!existsSync7(filePath)) {
|
|
1266
|
+
writeFileSync7(filePath, section + "\n", "utf-8");
|
|
862
1267
|
return;
|
|
863
1268
|
}
|
|
864
|
-
const existing =
|
|
1269
|
+
const existing = readFileSync8(filePath, "utf-8");
|
|
865
1270
|
if (hasAiOpsSection2(existing)) {
|
|
866
|
-
|
|
1271
|
+
writeFileSync7(filePath, replaceSection(existing, PRETTIER_IGNORE_CONTENT), "utf-8");
|
|
867
1272
|
return;
|
|
868
1273
|
}
|
|
869
1274
|
const separator = existing.endsWith("\n") ? "\n" : "\n\n";
|
|
870
|
-
|
|
1275
|
+
writeFileSync7(filePath, existing + separator + section + "\n", "utf-8");
|
|
871
1276
|
};
|
|
872
1277
|
var uninstallPrettierIgnore = (basePath) => {
|
|
873
|
-
const filePath =
|
|
874
|
-
if (!
|
|
875
|
-
const existing =
|
|
1278
|
+
const filePath = join13(basePath, ".prettierignore");
|
|
1279
|
+
if (!existsSync7(filePath)) return "notFound";
|
|
1280
|
+
const existing = readFileSync8(filePath, "utf-8");
|
|
876
1281
|
if (!hasAiOpsSection2(existing)) return "notFound";
|
|
877
1282
|
const stripped = stripAiOpsSection2(existing).trim();
|
|
878
1283
|
if (stripped.length === 0) {
|
|
879
|
-
|
|
1284
|
+
rmSync4(filePath, { force: true });
|
|
880
1285
|
return "deleted";
|
|
881
1286
|
}
|
|
882
|
-
|
|
1287
|
+
writeFileSync7(filePath, stripped + "\n", "utf-8");
|
|
883
1288
|
return "cleaned";
|
|
884
1289
|
};
|
|
885
1290
|
|
|
1291
|
+
// src/lib/skill-state.ts
|
|
1292
|
+
var resolveSkillScope = (params) => {
|
|
1293
|
+
if (params.scope !== void 0) {
|
|
1294
|
+
if (params.scope === "user") return "user";
|
|
1295
|
+
if (params.scope === "project") return "project";
|
|
1296
|
+
throw new Error(`Unsupported scope: ${params.scope}`);
|
|
1297
|
+
}
|
|
1298
|
+
if (params.project) return "project";
|
|
1299
|
+
return "user";
|
|
1300
|
+
};
|
|
1301
|
+
var resolveRequestedTools = (params) => {
|
|
1302
|
+
if (params.requested === void 0 || params.requested.length === 0) {
|
|
1303
|
+
return [...params.supported];
|
|
1304
|
+
}
|
|
1305
|
+
const supportedSet = new Set(params.supported);
|
|
1306
|
+
const invalid = params.requested.filter((tool) => !supportedSet.has(tool));
|
|
1307
|
+
if (invalid.length > 0) {
|
|
1308
|
+
throw new Error(`Unsupported tools requested: ${invalid.join(", ")}`);
|
|
1309
|
+
}
|
|
1310
|
+
return [...params.requested];
|
|
1311
|
+
};
|
|
1312
|
+
var TOOL_ORDER = [SKILL_TOOL.CLAUDE_CODE, SKILL_TOOL.CODEX, SKILL_TOOL.GEMINI];
|
|
1313
|
+
var mergeSkillTools = (params) => {
|
|
1314
|
+
const merged = /* @__PURE__ */ new Set([...params.existing ?? [], ...params.requested]);
|
|
1315
|
+
return TOOL_ORDER.filter((tool) => merged.has(tool));
|
|
1316
|
+
};
|
|
1317
|
+
var subtractSkillTools = (params) => {
|
|
1318
|
+
const installed = new Set(params.installed ?? []);
|
|
1319
|
+
return params.requested.filter((tool) => !installed.has(tool));
|
|
1320
|
+
};
|
|
1321
|
+
var upsertInstalledSkill = (installedSkills, nextSkill) => {
|
|
1322
|
+
const nextSkillId = resolveCanonicalSkillId(nextSkill.id);
|
|
1323
|
+
const remaining = installedSkills.filter((skill) => resolveCanonicalSkillId(skill.id) !== nextSkillId);
|
|
1324
|
+
return [...remaining, nextSkill];
|
|
1325
|
+
};
|
|
1326
|
+
var removeInstalledSkill = (installedSkills, skillId) => {
|
|
1327
|
+
const targetSkillId = resolveCanonicalSkillId(skillId);
|
|
1328
|
+
return installedSkills.filter((skill) => resolveCanonicalSkillId(skill.id) !== targetSkillId);
|
|
1329
|
+
};
|
|
1330
|
+
var findInstalledSkill = (installedSkills, skillId) => {
|
|
1331
|
+
const targetSkillId = resolveCanonicalSkillId(skillId);
|
|
1332
|
+
return installedSkills.find((skill) => resolveCanonicalSkillId(skill.id) === targetSkillId);
|
|
1333
|
+
};
|
|
1334
|
+
|
|
886
1335
|
// src/commands/init.ts
|
|
887
1336
|
var TOOL_OPTIONS = [
|
|
888
1337
|
{ value: "claude-code", label: "Claude Code" },
|
|
@@ -891,164 +1340,333 @@ var TOOL_OPTIONS = [
|
|
|
891
1340
|
];
|
|
892
1341
|
var deduplicateRules = (rules) => {
|
|
893
1342
|
const seen = /* @__PURE__ */ new Set();
|
|
894
|
-
return rules.filter((
|
|
895
|
-
if (seen.has(
|
|
896
|
-
seen.add(
|
|
1343
|
+
return rules.filter((rule) => {
|
|
1344
|
+
if (seen.has(rule.id)) return false;
|
|
1345
|
+
seen.add(rule.id);
|
|
897
1346
|
return true;
|
|
898
1347
|
});
|
|
899
1348
|
};
|
|
900
|
-
var
|
|
1349
|
+
var formatToolList = (toolIds) => toolIds.join(", ");
|
|
1350
|
+
var deduplicateSkillTargets = (targets) => {
|
|
1351
|
+
const merged = /* @__PURE__ */ new Map();
|
|
1352
|
+
for (const target of targets) {
|
|
1353
|
+
const previous = merged.get(target.skill.id);
|
|
1354
|
+
if (!previous) {
|
|
1355
|
+
merged.set(target.skill.id, {
|
|
1356
|
+
skill: target.skill,
|
|
1357
|
+
requestedTools: [...target.requestedTools]
|
|
1358
|
+
});
|
|
1359
|
+
continue;
|
|
1360
|
+
}
|
|
1361
|
+
merged.set(target.skill.id, {
|
|
1362
|
+
skill: target.skill,
|
|
1363
|
+
requestedTools: mergeSkillTools({
|
|
1364
|
+
existing: previous.requestedTools,
|
|
1365
|
+
requested: target.requestedTools
|
|
1366
|
+
})
|
|
1367
|
+
});
|
|
1368
|
+
}
|
|
1369
|
+
return [...merged.values()].sort((a, b) => a.skill.id.localeCompare(b.skill.id));
|
|
1370
|
+
};
|
|
1371
|
+
var resolveSupportedRequestedTools = (skill, selectedTools) => selectedTools.filter((toolId) => skill.supported_tools.includes(toolId));
|
|
1372
|
+
var partitionPresetSkills = (params) => {
|
|
1373
|
+
const globalSkills = [];
|
|
1374
|
+
const installableSkills = [];
|
|
1375
|
+
for (const skill of resolvePresetSkills(params.preset, params.allSkills)) {
|
|
1376
|
+
if (skill.kind !== "reference") {
|
|
1377
|
+
continue;
|
|
1378
|
+
}
|
|
1379
|
+
const supportedRequestedTools = resolveSupportedRequestedTools(skill, params.selectedTools);
|
|
1380
|
+
if (supportedRequestedTools.length === 0) {
|
|
1381
|
+
continue;
|
|
1382
|
+
}
|
|
1383
|
+
const installedGlobalSkill = findInstalledSkill(params.globalInstalledSkills, skill.id);
|
|
1384
|
+
const availableTools = installedGlobalSkill ? supportedRequestedTools.filter((toolId) => installedGlobalSkill.tools.includes(toolId)) : [];
|
|
1385
|
+
const requestedTools = subtractSkillTools({
|
|
1386
|
+
requested: supportedRequestedTools,
|
|
1387
|
+
installed: availableTools
|
|
1388
|
+
});
|
|
1389
|
+
if (requestedTools.length === 0) {
|
|
1390
|
+
globalSkills.push({
|
|
1391
|
+
skill,
|
|
1392
|
+
availableTools
|
|
1393
|
+
});
|
|
1394
|
+
continue;
|
|
1395
|
+
}
|
|
1396
|
+
installableSkills.push({
|
|
1397
|
+
skill,
|
|
1398
|
+
requestedTools,
|
|
1399
|
+
globalTools: availableTools
|
|
1400
|
+
});
|
|
1401
|
+
}
|
|
1402
|
+
return {
|
|
1403
|
+
globalSkills,
|
|
1404
|
+
installableSkills
|
|
1405
|
+
};
|
|
1406
|
+
};
|
|
1407
|
+
var selectPresetAndFineTune = async (workspaceName, presets, allRules, allSkills, selectedTools, globalInstalledSkills) => {
|
|
901
1408
|
const preset = await p4.select({
|
|
902
1409
|
message: `[${workspaceName}] \uD504\uB9AC\uC14B\uC744 \uC120\uD0DD\uD558\uC138\uC694`,
|
|
903
|
-
options: presets.map((
|
|
904
|
-
value:
|
|
905
|
-
label:
|
|
906
|
-
hint:
|
|
1410
|
+
options: presets.map((candidate) => ({
|
|
1411
|
+
value: candidate,
|
|
1412
|
+
label: candidate.id,
|
|
1413
|
+
hint: candidate.description
|
|
907
1414
|
}))
|
|
908
1415
|
});
|
|
909
1416
|
if (p4.isCancel(preset)) return null;
|
|
910
|
-
const
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
const
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
1417
|
+
const finalRules = resolvePresetRules(preset, allRules);
|
|
1418
|
+
if (finalRules.length > 0) {
|
|
1419
|
+
p4.note(finalRules.map((rule) => ` \u2713 ${rule.id}`).join("\n"), `[${workspaceName}] core rules (\uC7A0\uAE08)`);
|
|
1420
|
+
}
|
|
1421
|
+
const { globalSkills, installableSkills } = partitionPresetSkills({
|
|
1422
|
+
preset,
|
|
1423
|
+
allSkills,
|
|
1424
|
+
selectedTools,
|
|
1425
|
+
globalInstalledSkills
|
|
1426
|
+
});
|
|
1427
|
+
if (globalSkills.length > 0) {
|
|
1428
|
+
const globalLines = globalSkills.map(
|
|
1429
|
+
({ skill, availableTools }) => ` \u2713 ${skill.id} (${formatToolList(availableTools)})`
|
|
1430
|
+
);
|
|
1431
|
+
p4.note(globalLines.join("\n"), `[${workspaceName}] already available globally`);
|
|
1432
|
+
}
|
|
1433
|
+
if (installableSkills.length === 0) {
|
|
1434
|
+
p4.note(" \uC0C8\uB85C \uC124\uCE58\uD560 reference skill\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.", `[${workspaceName}] installable reference skills`);
|
|
1435
|
+
return {
|
|
1436
|
+
workspace: workspaceName,
|
|
1437
|
+
preset,
|
|
1438
|
+
finalRules,
|
|
1439
|
+
finalSkillTargets: []
|
|
1440
|
+
};
|
|
1441
|
+
}
|
|
1442
|
+
const selectedSkillIds = await p4.multiselect({
|
|
1443
|
+
message: `[${workspaceName}] installable reference skills \uC120\uD0DD`,
|
|
1444
|
+
options: installableSkills.map(({ skill, requestedTools, globalTools }) => ({
|
|
1445
|
+
value: skill.id,
|
|
1446
|
+
label: skill.id,
|
|
1447
|
+
hint: globalTools.length > 0 ? `global: ${formatToolList(globalTools)} / install: ${formatToolList(requestedTools)}` : `${skill.description} / install: ${formatToolList(requestedTools)}`
|
|
1448
|
+
})),
|
|
1449
|
+
initialValues: installableSkills.map(({ skill }) => skill.id),
|
|
925
1450
|
required: false
|
|
926
1451
|
});
|
|
927
|
-
if (p4.isCancel(
|
|
928
|
-
const
|
|
1452
|
+
if (p4.isCancel(selectedSkillIds)) return null;
|
|
1453
|
+
const selectedSkillSet = new Set(selectedSkillIds);
|
|
929
1454
|
return {
|
|
930
1455
|
workspace: workspaceName,
|
|
931
1456
|
preset,
|
|
932
|
-
finalRules
|
|
1457
|
+
finalRules,
|
|
1458
|
+
finalSkillTargets: installableSkills.filter(({ skill }) => selectedSkillSet.has(skill.id)).map(({ skill, requestedTools }) => ({
|
|
1459
|
+
skill,
|
|
1460
|
+
requestedTools
|
|
1461
|
+
}))
|
|
933
1462
|
};
|
|
934
1463
|
};
|
|
1464
|
+
var selectInitSkillScope = async () => {
|
|
1465
|
+
const scope = await p4.select({
|
|
1466
|
+
message: "\uC120\uD0DD\uB41C skills\uB97C \uC5B4\uB514\uC5D0 \uC124\uCE58\uD560\uAE4C\uC694?",
|
|
1467
|
+
options: [
|
|
1468
|
+
{ value: "user", label: "user (global)", hint: "\uAE30\uBCF8\uAC12. \uC5EC\uB7EC \uD504\uB85C\uC81D\uD2B8\uC5D0\uC11C \uC7AC\uC0AC\uC6A9" },
|
|
1469
|
+
{ value: "project", label: "project", hint: "\uD604\uC7AC \uD504\uB85C\uC81D\uD2B8\uC5D0\uB9CC \uC124\uCE58" }
|
|
1470
|
+
]
|
|
1471
|
+
});
|
|
1472
|
+
return p4.isCancel(scope) ? null : scope;
|
|
1473
|
+
};
|
|
935
1474
|
var initCommand = async () => {
|
|
936
1475
|
const basePath = resolveBasePath();
|
|
1476
|
+
const userBasePath = resolveUserBasePath();
|
|
937
1477
|
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);
|
|
1478
|
+
const skillsDir = resolveSkillsDir();
|
|
1479
|
+
const spinner3 = p4.spinner();
|
|
1480
|
+
let spinnerStarted = false;
|
|
1481
|
+
const cancelInit = (params) => {
|
|
1482
|
+
if (spinnerStarted) {
|
|
1483
|
+
spinner3.stop("\uC124\uCE58 \uC911\uB2E8\uB428");
|
|
1484
|
+
spinnerStarted = false;
|
|
965
1485
|
}
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
1486
|
+
p4.cancel(params?.message ?? "\uCDE8\uC18C\uB428");
|
|
1487
|
+
process.exit(params?.exitCode ?? 0);
|
|
1488
|
+
};
|
|
1489
|
+
const handleSigint = () => cancelInit({
|
|
1490
|
+
message: "\uC0AC\uC6A9\uC790 \uC694\uCCAD\uC73C\uB85C init\uC774 \uC911\uB2E8\uB418\uC5C8\uC2B5\uB2C8\uB2E4.",
|
|
1491
|
+
exitCode: 130
|
|
1492
|
+
});
|
|
1493
|
+
process.once("SIGINT", handleSigint);
|
|
1494
|
+
try {
|
|
1495
|
+
p4.intro("ai-ops init");
|
|
1496
|
+
const selectedTools = await p4.multiselect({
|
|
1497
|
+
message: "AI \uB3C4\uAD6C\uB97C \uC120\uD0DD\uD558\uC138\uC694",
|
|
1498
|
+
options: TOOL_OPTIONS,
|
|
972
1499
|
required: true
|
|
973
1500
|
});
|
|
974
|
-
if (p4.isCancel(
|
|
975
|
-
|
|
976
|
-
process.exit(0);
|
|
1501
|
+
if (p4.isCancel(selectedTools)) {
|
|
1502
|
+
cancelInit();
|
|
977
1503
|
}
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
mappings.push(mapping);
|
|
1504
|
+
const isMonorepo = await p4.confirm({
|
|
1505
|
+
message: "\uBAA8\uB178\uB808\uD3EC \uD504\uB85C\uC81D\uD2B8\uC785\uB2C8\uAE4C?",
|
|
1506
|
+
initialValue: false
|
|
1507
|
+
});
|
|
1508
|
+
if (p4.isCancel(isMonorepo)) {
|
|
1509
|
+
cancelInit();
|
|
985
1510
|
}
|
|
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);
|
|
1511
|
+
const allRules = loadAllRules(rulesDir);
|
|
1512
|
+
const allSkills = loadAllSkills(skillsDir);
|
|
1513
|
+
const presets = loadPresets(resolvePresetsPath());
|
|
1514
|
+
const sourceHash = computeSourceHash(resolveCompilerDataDir());
|
|
1515
|
+
const globalInstalledSkills = readSkillRegistry(resolveSkillRegistryPath(userBasePath))?.skills ?? [];
|
|
1516
|
+
const mappings = [];
|
|
1517
|
+
if (!isMonorepo) {
|
|
1518
|
+
const mapping = await selectPresetAndFineTune(
|
|
1519
|
+
".",
|
|
1520
|
+
presets,
|
|
1521
|
+
allRules,
|
|
1522
|
+
allSkills,
|
|
1523
|
+
selectedTools,
|
|
1524
|
+
globalInstalledSkills
|
|
1525
|
+
) ?? cancelInit();
|
|
1526
|
+
mappings.push(mapping);
|
|
1007
1527
|
} else {
|
|
1008
|
-
const
|
|
1009
|
-
const
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1528
|
+
const candidates = listWorkspaceCandidates(basePath);
|
|
1529
|
+
const selectedWorkspaces = await p4.multiselect({
|
|
1530
|
+
message: "\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4\uB97C \uC120\uD0DD\uD558\uC138\uC694",
|
|
1531
|
+
options: candidates.map((candidate) => ({ value: candidate, label: candidate })),
|
|
1532
|
+
required: true
|
|
1533
|
+
});
|
|
1534
|
+
if (p4.isCancel(selectedWorkspaces)) {
|
|
1535
|
+
cancelInit();
|
|
1536
|
+
}
|
|
1537
|
+
for (const workspace of selectedWorkspaces) {
|
|
1538
|
+
const mapping = await selectPresetAndFineTune(
|
|
1539
|
+
workspace,
|
|
1540
|
+
presets,
|
|
1541
|
+
allRules,
|
|
1542
|
+
allSkills,
|
|
1543
|
+
selectedTools,
|
|
1544
|
+
globalInstalledSkills
|
|
1545
|
+
) ?? cancelInit();
|
|
1546
|
+
mappings.push(mapping);
|
|
1547
|
+
}
|
|
1013
1548
|
}
|
|
1549
|
+
const selectedSkillTargets = deduplicateSkillTargets(mappings.flatMap((mapping) => mapping.finalSkillTargets));
|
|
1550
|
+
const skillScope = selectedSkillTargets.length > 0 ? await selectInitSkillScope() : null;
|
|
1551
|
+
if (selectedSkillTargets.length > 0 && skillScope === null) {
|
|
1552
|
+
cancelInit();
|
|
1553
|
+
}
|
|
1554
|
+
const geminiSettingValues = selectedTools.includes("gemini") ? await promptGeminiSettings() : null;
|
|
1555
|
+
const resolvedGeminiSettingValues = isPromptCancelled(geminiSettingValues) ? cancelInit() : geminiSettingValues;
|
|
1556
|
+
const claudeSettingValues = selectedTools.includes("claude-code") ? await promptClaudeSettings() : null;
|
|
1557
|
+
const resolvedClaudeSettingValues = isPromptCancelled(claudeSettingValues) ? cancelInit() : claudeSettingValues;
|
|
1558
|
+
const wantPrettierIgnore = await promptPrettierIgnore();
|
|
1559
|
+
const resolvedWantPrettierIgnore = isPromptCancelled(wantPrettierIgnore) ? cancelInit() : wantPrettierIgnore;
|
|
1560
|
+
spinner3.start("\uADDC\uCE59 \uC124\uCE58 \uC911...");
|
|
1561
|
+
spinnerStarted = true;
|
|
1562
|
+
const meta = { sourceHash, generatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
1563
|
+
const allInstalledFiles = [];
|
|
1564
|
+
const allAppended = [];
|
|
1565
|
+
const selectedRuleIds = deduplicateRules(mappings.flatMap((mapping) => mapping.finalRules)).map((rule) => rule.id);
|
|
1566
|
+
let projectInstalledSkills = [];
|
|
1567
|
+
if (selectedSkillTargets.length > 0 && skillScope !== null) {
|
|
1568
|
+
const skillBasePath = skillScope === "project" ? basePath : userBasePath;
|
|
1569
|
+
const installedSkills = selectedSkillTargets.map(({ skill, requestedTools }) => {
|
|
1570
|
+
const existingUserSkill = skillScope === "user" ? findInstalledSkill(globalInstalledSkills, skill.id) : void 0;
|
|
1571
|
+
const nextRequestedTools = skillScope === "user" ? mergeSkillTools({
|
|
1572
|
+
existing: existingUserSkill?.tools,
|
|
1573
|
+
requested: requestedTools
|
|
1574
|
+
}) : requestedTools;
|
|
1575
|
+
const { packages, installedSkill } = buildSkillInstallPlan({
|
|
1576
|
+
skill,
|
|
1577
|
+
requestedTools: nextRequestedTools,
|
|
1578
|
+
scope: skillScope
|
|
1579
|
+
});
|
|
1580
|
+
installSkillPackages(skillBasePath, packages);
|
|
1581
|
+
return installedSkill;
|
|
1582
|
+
});
|
|
1583
|
+
if (skillScope === "project") {
|
|
1584
|
+
projectInstalledSkills = installedSkills;
|
|
1585
|
+
} else {
|
|
1586
|
+
const registryPath = resolveSkillRegistryPath(skillBasePath);
|
|
1587
|
+
const previous = readSkillRegistry(registryPath);
|
|
1588
|
+
const nextSkills = installedSkills.reduce(
|
|
1589
|
+
(acc, installedSkill) => upsertInstalledSkill(acc, installedSkill),
|
|
1590
|
+
previous?.skills ?? []
|
|
1591
|
+
);
|
|
1592
|
+
writeSkillRegistry(registryPath, {
|
|
1593
|
+
skills: nextSkills,
|
|
1594
|
+
cliVersion: getCliVersion(),
|
|
1595
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1596
|
+
});
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
for (const toolId of selectedTools) {
|
|
1600
|
+
if (isMonorepo) {
|
|
1601
|
+
const allWorkspaceRules = deduplicateRules(mappings.flatMap((mapping) => mapping.finalRules));
|
|
1602
|
+
const workspaceMappings = mappings.map((mapping) => ({
|
|
1603
|
+
path: mapping.workspace,
|
|
1604
|
+
ruleIds: mapping.finalRules.map((rule) => rule.id)
|
|
1605
|
+
}));
|
|
1606
|
+
const renderResult = renderForTool(toolId, allWorkspaceRules, workspaceMappings);
|
|
1607
|
+
const actions = buildInstallPlan({ toolId, renderResult, meta });
|
|
1608
|
+
const result = installFiles(basePath, actions, meta);
|
|
1609
|
+
allInstalledFiles.push(...result.written);
|
|
1610
|
+
allAppended.push(...result.appended);
|
|
1611
|
+
} else {
|
|
1612
|
+
const renderResult = renderForTool(toolId, mappings[0].finalRules);
|
|
1613
|
+
const actions = buildInstallPlan({ toolId, renderResult, meta });
|
|
1614
|
+
const result = installFiles(basePath, actions, meta);
|
|
1615
|
+
allInstalledFiles.push(...result.written);
|
|
1616
|
+
allAppended.push(...result.appended);
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
if (resolvedGeminiSettingValues && resolvedGeminiSettingValues.length > 0) {
|
|
1620
|
+
installGeminiSettings(basePath, resolvedGeminiSettingValues);
|
|
1621
|
+
}
|
|
1622
|
+
if (resolvedClaudeSettingValues && resolvedClaudeSettingValues.length > 0) {
|
|
1623
|
+
installClaudeSettings(basePath, resolvedClaudeSettingValues);
|
|
1624
|
+
}
|
|
1625
|
+
if (resolvedWantPrettierIgnore) {
|
|
1626
|
+
installPrettierIgnore(basePath);
|
|
1627
|
+
}
|
|
1628
|
+
spinner3.stop("\uADDC\uCE59 \uC124\uCE58 \uC644\uB8CC");
|
|
1629
|
+
spinnerStarted = false;
|
|
1630
|
+
const workspacesRecord = isMonorepo ? Object.fromEntries(
|
|
1631
|
+
mappings.map((mapping) => [
|
|
1632
|
+
mapping.workspace,
|
|
1633
|
+
{
|
|
1634
|
+
preset: mapping.preset.id,
|
|
1635
|
+
rules: mapping.finalRules.map((rule) => rule.id)
|
|
1636
|
+
}
|
|
1637
|
+
])
|
|
1638
|
+
) : void 0;
|
|
1639
|
+
const manifest = buildManifest({
|
|
1640
|
+
tools: selectedTools,
|
|
1641
|
+
scope: "project",
|
|
1642
|
+
preset: !isMonorepo ? mappings[0].preset.id : void 0,
|
|
1643
|
+
workspaces: workspacesRecord,
|
|
1644
|
+
installedRules: selectedRuleIds,
|
|
1645
|
+
installedFiles: allInstalledFiles,
|
|
1646
|
+
installedSkills: projectInstalledSkills,
|
|
1647
|
+
appendedFiles: allAppended,
|
|
1648
|
+
settings: resolvedClaudeSettingValues || resolvedGeminiSettingValues || resolvedWantPrettierIgnore ? {
|
|
1649
|
+
claude: resolvedClaudeSettingValues ? [...resolvedClaudeSettingValues] : void 0,
|
|
1650
|
+
gemini: resolvedGeminiSettingValues ? [...resolvedGeminiSettingValues] : void 0,
|
|
1651
|
+
prettierignore: resolvedWantPrettierIgnore || void 0
|
|
1652
|
+
} : void 0,
|
|
1653
|
+
cliVersion: getCliVersion(),
|
|
1654
|
+
sourceHash
|
|
1655
|
+
});
|
|
1656
|
+
writeManifest(resolveManifestPath(basePath), manifest);
|
|
1657
|
+
if (allAppended.length > 0) {
|
|
1658
|
+
p4.log.info(`\uAE30\uC874 \uD30C\uC77C\uC5D0 \uC139\uC158 \uCD94\uAC00\uB428 (\uB0B4\uC6A9 \uBCF4\uC874):
|
|
1659
|
+
${allAppended.map((file) => ` ${file}`).join("\n")}`);
|
|
1660
|
+
}
|
|
1661
|
+
p4.log.success(`\uC124\uCE58\uB41C core rules: ${selectedRuleIds.length}\uAC1C`);
|
|
1662
|
+
p4.log.success(`\uC124\uCE58\uB41C skills: ${selectedSkillTargets.length}\uAC1C${skillScope ? ` (${skillScope})` : ""}`);
|
|
1663
|
+
if (selectedSkillTargets.length > 0 && skillScope === "user") {
|
|
1664
|
+
p4.log.info("global skill\uC740 ai-ops uninstall \uB300\uC0C1\uC774 \uC544\uB2D9\uB2C8\uB2E4. ai-ops skill uninstall\uC73C\uB85C \uC81C\uAC70\uD558\uC138\uC694.");
|
|
1665
|
+
}
|
|
1666
|
+
p4.outro("ai-ops init \uC644\uB8CC");
|
|
1667
|
+
} finally {
|
|
1668
|
+
process.off("SIGINT", handleSigint);
|
|
1014
1669
|
}
|
|
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
1670
|
};
|
|
1053
1671
|
|
|
1054
1672
|
// src/commands/update.ts
|
|
@@ -1063,11 +1681,25 @@ var updateCommand = async (opts) => {
|
|
|
1063
1681
|
process.exit(1);
|
|
1064
1682
|
}
|
|
1065
1683
|
const rulesDir = resolveRulesDir();
|
|
1066
|
-
const
|
|
1684
|
+
const skillsDir = resolveSkillsDir();
|
|
1685
|
+
const presetsPath = resolvePresetsPath();
|
|
1686
|
+
const sourceHash = computeSourceHash(resolveCompilerDataDir());
|
|
1067
1687
|
const cliVersion = getCliVersion();
|
|
1688
|
+
const allRules = loadAllRules(rulesDir);
|
|
1689
|
+
const allSkills = loadAllSkills(skillsDir);
|
|
1690
|
+
const presets = loadPresets(presetsPath);
|
|
1691
|
+
const resolvedRules = resolveManifestRules({
|
|
1692
|
+
manifest,
|
|
1693
|
+
allRules,
|
|
1694
|
+
presets
|
|
1695
|
+
});
|
|
1696
|
+
const resolvedSkills = resolveManifestProjectSkills({
|
|
1697
|
+
manifest,
|
|
1698
|
+
allSkills
|
|
1699
|
+
});
|
|
1068
1700
|
const diffResult = computeDiff({
|
|
1069
1701
|
previous: manifest,
|
|
1070
|
-
currentRules:
|
|
1702
|
+
currentRules: resolvedRules.installedRules.map((rule) => rule.id),
|
|
1071
1703
|
currentSourceHash: sourceHash,
|
|
1072
1704
|
currentCliVersion: cliVersion
|
|
1073
1705
|
});
|
|
@@ -1078,16 +1710,23 @@ var updateCommand = async (opts) => {
|
|
|
1078
1710
|
}
|
|
1079
1711
|
const s = p5.spinner();
|
|
1080
1712
|
s.start("\uADDC\uCE59 \uAC31\uC2E0 \uC911...");
|
|
1081
|
-
const allRules = loadAllRules(rulesDir);
|
|
1082
1713
|
const meta = { sourceHash, generatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
1083
1714
|
const allInstalledFiles = [];
|
|
1084
1715
|
const allAppended = [];
|
|
1716
|
+
const installedSkills = resolvedSkills.map(({ skill, requestedTools }) => {
|
|
1717
|
+
const { packages, installedSkill } = buildSkillInstallPlan({
|
|
1718
|
+
skill,
|
|
1719
|
+
requestedTools,
|
|
1720
|
+
scope: "project"
|
|
1721
|
+
});
|
|
1722
|
+
installSkillPackages(basePath, packages);
|
|
1723
|
+
return installedSkill;
|
|
1724
|
+
});
|
|
1085
1725
|
if (manifest.workspaces) {
|
|
1086
|
-
const workspaceEntries = Object.entries(
|
|
1726
|
+
const workspaceEntries = Object.entries(resolvedRules.workspaces ?? {});
|
|
1087
1727
|
for (const toolIdStr of manifest.tools) {
|
|
1088
1728
|
const toolId = toolIdStr;
|
|
1089
|
-
const
|
|
1090
|
-
const rulesToInstall = allRules.filter((r2) => allInstalledRuleSet.has(r2.id));
|
|
1729
|
+
const rulesToInstall = resolvedRules.installedRules;
|
|
1091
1730
|
const workspaceMappings = workspaceEntries.map(([path, entry]) => ({
|
|
1092
1731
|
path,
|
|
1093
1732
|
ruleIds: entry.rules
|
|
@@ -1099,8 +1738,7 @@ var updateCommand = async (opts) => {
|
|
|
1099
1738
|
allAppended.push(...r.appended);
|
|
1100
1739
|
}
|
|
1101
1740
|
} else {
|
|
1102
|
-
const
|
|
1103
|
-
const rulesToInstall = allRules.filter((r) => installedRuleSet.has(r.id));
|
|
1741
|
+
const rulesToInstall = resolvedRules.installedRules;
|
|
1104
1742
|
for (const toolIdStr of manifest.tools) {
|
|
1105
1743
|
const toolId = toolIdStr;
|
|
1106
1744
|
const renderResult = renderForTool(toolId, rulesToInstall);
|
|
@@ -1123,9 +1761,10 @@ var updateCommand = async (opts) => {
|
|
|
1123
1761
|
tools: manifest.tools,
|
|
1124
1762
|
scope: manifest.scope,
|
|
1125
1763
|
preset: manifest.preset,
|
|
1126
|
-
workspaces:
|
|
1127
|
-
installedRules:
|
|
1764
|
+
workspaces: resolvedRules.workspaces,
|
|
1765
|
+
installedRules: resolvedRules.installedRules.map((rule) => rule.id),
|
|
1128
1766
|
installedFiles: allInstalledFiles.length > 0 ? allInstalledFiles : manifest.installed_files,
|
|
1767
|
+
installedSkills,
|
|
1129
1768
|
appendedFiles: allAppended.length > 0 ? allAppended : manifest.appended_files,
|
|
1130
1769
|
settings: manifest.settings ? {
|
|
1131
1770
|
claude: manifest.settings.claude,
|
|
@@ -1150,12 +1789,35 @@ var diffCommand = async () => {
|
|
|
1150
1789
|
p6.log.error("manifest\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 ai-ops init\uC744 \uC2E4\uD589\uD558\uC138\uC694.");
|
|
1151
1790
|
process.exit(1);
|
|
1152
1791
|
}
|
|
1153
|
-
const sourceHash = computeSourceHash(
|
|
1792
|
+
const sourceHash = computeSourceHash(resolveCompilerDataDir());
|
|
1793
|
+
const allRules = loadAllRules(resolveRulesDir());
|
|
1794
|
+
const allSkills = loadAllSkills(resolveSkillsDir());
|
|
1795
|
+
const presets = loadPresets(resolvePresetsPath());
|
|
1796
|
+
const resolvedRules = resolveManifestRules({
|
|
1797
|
+
manifest,
|
|
1798
|
+
allRules,
|
|
1799
|
+
presets
|
|
1800
|
+
});
|
|
1801
|
+
const resolvedSkills = resolveManifestProjectSkills({
|
|
1802
|
+
manifest,
|
|
1803
|
+
allSkills
|
|
1804
|
+
});
|
|
1154
1805
|
const result = computeDiff({
|
|
1155
1806
|
previous: manifest,
|
|
1156
|
-
currentRules:
|
|
1807
|
+
currentRules: resolvedRules.installedRules.map((rule) => rule.id),
|
|
1157
1808
|
currentSourceHash: sourceHash
|
|
1158
1809
|
});
|
|
1810
|
+
const skillLines = resolvedSkills.map(({ skill, requestedTools }) => {
|
|
1811
|
+
const { installedSkill: next } = buildSkillInstallPlan({
|
|
1812
|
+
skill,
|
|
1813
|
+
requestedTools,
|
|
1814
|
+
scope: "project"
|
|
1815
|
+
});
|
|
1816
|
+
const previous = (manifest.installed_skills ?? []).find((installedSkill) => installedSkill.id === skill.id);
|
|
1817
|
+
const previousHash = previous?.sourceHash ?? "legacy";
|
|
1818
|
+
const changed = previousHash !== next.sourceHash;
|
|
1819
|
+
return `- ${skill.id}: ${changed ? "changed" : "up-to-date"} (${previousHash} -> ${next.sourceHash})`;
|
|
1820
|
+
});
|
|
1159
1821
|
if (result.status === "up-to-date") {
|
|
1160
1822
|
p6.log.success("\uBCC0\uACBD \uC0AC\uD56D \uC5C6\uC74C. \uCD5C\uC2E0 \uC0C1\uD0DC\uC785\uB2C8\uB2E4.");
|
|
1161
1823
|
} else {
|
|
@@ -1169,39 +1831,43 @@ var diffCommand = async () => {
|
|
|
1169
1831
|
p6.log.info(`\uC81C\uAC70\uB41C \uADDC\uCE59: ${result.removed.join(", ")}`);
|
|
1170
1832
|
}
|
|
1171
1833
|
}
|
|
1834
|
+
if (skillLines.length > 0) {
|
|
1835
|
+
p6.log.info(`project skills:
|
|
1836
|
+
${skillLines.map((line) => ` ${line}`).join("\n")}`);
|
|
1837
|
+
}
|
|
1172
1838
|
p6.outro("ai-ops diff \uC644\uB8CC");
|
|
1173
1839
|
};
|
|
1174
1840
|
|
|
1175
1841
|
// src/commands/uninstall.ts
|
|
1176
1842
|
import * as p7 from "@clack/prompts";
|
|
1177
|
-
import { rmSync as
|
|
1843
|
+
import { rmSync as rmSync6 } from "fs";
|
|
1178
1844
|
|
|
1179
1845
|
// src/lib/uninstall.ts
|
|
1180
|
-
import { existsSync as
|
|
1181
|
-
import { resolve as
|
|
1846
|
+
import { existsSync as existsSync8, readFileSync as readFileSync9, rmSync as rmSync5, readdirSync as readdirSync4, writeFileSync as writeFileSync8 } from "fs";
|
|
1847
|
+
import { resolve as resolve7, dirname as dirname7 } from "path";
|
|
1182
1848
|
var removeFiles = (basePath, relativePaths) => {
|
|
1183
1849
|
const deleted = [];
|
|
1184
1850
|
const cleaned = [];
|
|
1185
1851
|
const skipped = [];
|
|
1186
1852
|
const notFound = [];
|
|
1187
1853
|
for (const rel of relativePaths) {
|
|
1188
|
-
const absPath =
|
|
1189
|
-
if (!
|
|
1854
|
+
const absPath = resolve7(basePath, rel);
|
|
1855
|
+
if (!existsSync8(absPath)) {
|
|
1190
1856
|
notFound.push(rel);
|
|
1191
1857
|
continue;
|
|
1192
1858
|
}
|
|
1193
|
-
const content =
|
|
1859
|
+
const content = readFileSync9(absPath, "utf-8");
|
|
1194
1860
|
if (hasAiOpsSection(content)) {
|
|
1195
1861
|
const stripped = stripAiOpsSection(content);
|
|
1196
1862
|
if (stripped.trim().length === 0) {
|
|
1197
|
-
|
|
1863
|
+
rmSync5(absPath);
|
|
1198
1864
|
deleted.push(rel);
|
|
1199
1865
|
} else {
|
|
1200
|
-
|
|
1866
|
+
writeFileSync8(absPath, stripped, "utf-8");
|
|
1201
1867
|
cleaned.push(rel);
|
|
1202
1868
|
}
|
|
1203
1869
|
} else if (hasLegacyHeader(content)) {
|
|
1204
|
-
|
|
1870
|
+
rmSync5(absPath);
|
|
1205
1871
|
deleted.push(rel);
|
|
1206
1872
|
} else {
|
|
1207
1873
|
skipped.push(rel);
|
|
@@ -1212,12 +1878,12 @@ var removeFiles = (basePath, relativePaths) => {
|
|
|
1212
1878
|
var cleanEmptyDirs = (basePath, dirs) => {
|
|
1213
1879
|
const removed = [];
|
|
1214
1880
|
for (const dir of dirs) {
|
|
1215
|
-
const absDir =
|
|
1216
|
-
if (!
|
|
1881
|
+
const absDir = resolve7(basePath, dir);
|
|
1882
|
+
if (!existsSync8(absDir)) continue;
|
|
1217
1883
|
try {
|
|
1218
1884
|
const entries = readdirSync4(absDir);
|
|
1219
1885
|
if (entries.length === 0) {
|
|
1220
|
-
|
|
1886
|
+
rmSync5(absDir, { recursive: true });
|
|
1221
1887
|
removed.push(dir);
|
|
1222
1888
|
}
|
|
1223
1889
|
} catch {
|
|
@@ -1228,7 +1894,7 @@ var cleanEmptyDirs = (basePath, dirs) => {
|
|
|
1228
1894
|
var collectManagedDirs = (relativePaths) => {
|
|
1229
1895
|
const dirs = /* @__PURE__ */ new Set();
|
|
1230
1896
|
for (const rel of relativePaths) {
|
|
1231
|
-
const dir =
|
|
1897
|
+
const dir = dirname7(rel);
|
|
1232
1898
|
if (dir !== ".") {
|
|
1233
1899
|
dirs.add(dir);
|
|
1234
1900
|
}
|
|
@@ -1251,13 +1917,20 @@ var uninstallCommand = async () => {
|
|
|
1251
1917
|
...manifest.installed_files ?? inferInstalledFiles(manifest),
|
|
1252
1918
|
...manifest.appended_files ?? []
|
|
1253
1919
|
].filter((f) => !SETTINGS_PATHS.has(f));
|
|
1254
|
-
|
|
1920
|
+
const targetSkillDirs = (manifest.installed_skills ?? []).flatMap((skill) => skill.installed_paths);
|
|
1921
|
+
if (targetFiles.length === 0 && targetSkillDirs.length === 0) {
|
|
1255
1922
|
p7.log.warn("\uC0AD\uC81C\uD560 \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
1256
1923
|
p7.outro("ai-ops uninstall \uC644\uB8CC");
|
|
1257
1924
|
return;
|
|
1258
1925
|
}
|
|
1259
|
-
|
|
1926
|
+
if (targetFiles.length > 0) {
|
|
1927
|
+
p7.log.info(`\uC0AD\uC81C \uB300\uC0C1 \uD30C\uC77C (${targetFiles.length}\uAC1C):
|
|
1260
1928
|
${targetFiles.map((f) => ` ${f}`).join("\n")}`);
|
|
1929
|
+
}
|
|
1930
|
+
if (targetSkillDirs.length > 0) {
|
|
1931
|
+
p7.log.info(`\uC0AD\uC81C \uB300\uC0C1 skill \uB514\uB809\uD1A0\uB9AC (${targetSkillDirs.length}\uAC1C):
|
|
1932
|
+
${targetSkillDirs.map((f) => ` ${f}`).join("\n")}`);
|
|
1933
|
+
}
|
|
1261
1934
|
const confirmed = await p7.confirm({
|
|
1262
1935
|
message: "\uC704 \uD30C\uC77C\uACFC manifest\uB97C \uBAA8\uB450 \uC0AD\uC81C\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
|
|
1263
1936
|
initialValue: false
|
|
@@ -1281,9 +1954,10 @@ ${targetFiles.map((f) => ` ${f}`).join("\n")}`);
|
|
|
1281
1954
|
if (prettierStatus === "deleted") settingsMessages.push("\uC0AD\uC81C: .prettierignore");
|
|
1282
1955
|
else if (prettierStatus === "cleaned") settingsMessages.push("ai-ops \uC139\uC158 \uC81C\uAC70 (\uC0AC\uC6A9\uC790 \uB0B4\uC6A9 \uBCF4\uC874): .prettierignore");
|
|
1283
1956
|
const result = removeFiles(basePath, targetFiles);
|
|
1957
|
+
const removedSkillDirs = removeDirectories(basePath, targetSkillDirs);
|
|
1284
1958
|
const dirs = collectManagedDirs(targetFiles);
|
|
1285
1959
|
const removedDirs = cleanEmptyDirs(basePath, dirs);
|
|
1286
|
-
|
|
1960
|
+
rmSync6(manifestPath, { force: true });
|
|
1287
1961
|
if (result.deleted.length > 0) {
|
|
1288
1962
|
p7.log.success(`\uC0AD\uC81C \uC644\uB8CC (${result.deleted.length}\uAC1C):
|
|
1289
1963
|
${result.deleted.map((f) => ` ${f}`).join("\n")}`);
|
|
@@ -1307,6 +1981,10 @@ ${result.notFound.map((f) => ` ${f}`).join("\n")}`);
|
|
|
1307
1981
|
if (removedDirs.length > 0) {
|
|
1308
1982
|
p7.log.info(`\uBE48 \uB514\uB809\uD1A0\uB9AC \uC815\uB9AC (${removedDirs.length}\uAC1C):
|
|
1309
1983
|
${removedDirs.map((d) => ` ${d}`).join("\n")}`);
|
|
1984
|
+
}
|
|
1985
|
+
if (removedSkillDirs.length > 0) {
|
|
1986
|
+
p7.log.success(`skill \uB514\uB809\uD1A0\uB9AC \uC0AD\uC81C (${removedSkillDirs.length}\uAC1C):
|
|
1987
|
+
${removedSkillDirs.map((d) => ` ${d}`).join("\n")}`);
|
|
1310
1988
|
}
|
|
1311
1989
|
if (settingsMessages.length > 0) {
|
|
1312
1990
|
p7.log.success(`\uC124\uC815 \uD30C\uC77C \uCC98\uB9AC:
|
|
@@ -1316,19 +1994,297 @@ ${settingsMessages.map((m) => ` ${m}`).join("\n")}`);
|
|
|
1316
1994
|
p7.outro("ai-ops uninstall \uC644\uB8CC");
|
|
1317
1995
|
};
|
|
1318
1996
|
|
|
1319
|
-
// src/
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1997
|
+
// src/commands/skill.ts
|
|
1998
|
+
import * as p8 from "@clack/prompts";
|
|
1999
|
+
import { rmSync as rmSync7 } from "fs";
|
|
2000
|
+
var resolveScopeContext = (opts) => {
|
|
2001
|
+
const scope = resolveSkillScope(opts);
|
|
2002
|
+
return {
|
|
2003
|
+
scope,
|
|
2004
|
+
basePath: scope === "project" ? resolveBasePath() : resolveUserBasePath()
|
|
2005
|
+
};
|
|
2006
|
+
};
|
|
2007
|
+
var loadCompilerInputs = () => {
|
|
2008
|
+
const compilerDataDir = resolveCompilerDataDir();
|
|
2009
|
+
return {
|
|
2010
|
+
allSkills: loadAllSkills(resolveSkillsDir()),
|
|
2011
|
+
sourceHash: computeSourceHash(compilerDataDir),
|
|
2012
|
+
cliVersion: getCliVersion()
|
|
2013
|
+
};
|
|
2014
|
+
};
|
|
2015
|
+
var resolveSkillById = (skills, skillId) => {
|
|
2016
|
+
const canonicalSkillId = resolveCanonicalSkillId(skillId);
|
|
2017
|
+
const skill = skills.find((candidate) => candidate.id === canonicalSkillId);
|
|
2018
|
+
if (!skill) {
|
|
2019
|
+
throw new Error(`Unknown skill: ${skillId}`);
|
|
2020
|
+
}
|
|
2021
|
+
return skill;
|
|
2022
|
+
};
|
|
2023
|
+
var assertScopeAllowed = (skill, scope) => {
|
|
2024
|
+
if (!skill.install_scopes.includes(scope)) {
|
|
2025
|
+
throw new Error(`Skill ${skill.id} does not support ${scope} scope`);
|
|
1325
2026
|
}
|
|
1326
2027
|
};
|
|
2028
|
+
var writeProjectSkillState = (params) => {
|
|
2029
|
+
const manifestPath = resolveManifestPath(params.basePath);
|
|
2030
|
+
const previous = readManifest(manifestPath);
|
|
2031
|
+
const installedSkills = params.removeSkillId ? removeInstalledSkill(previous?.installed_skills ?? [], params.removeSkillId) : params.nextSkill ? upsertInstalledSkill(previous?.installed_skills ?? [], params.nextSkill) : previous?.installed_skills ?? [];
|
|
2032
|
+
const nextTools = params.nextSkill !== void 0 ? [.../* @__PURE__ */ new Set([...previous?.tools ?? [], ...params.nextSkill.tools])] : previous?.tools ?? [];
|
|
2033
|
+
const hasProjectState = (previous?.installed_rules.length ?? 0) > 0 || (previous?.installed_files?.length ?? 0) > 0 || (previous?.appended_files?.length ?? 0) > 0 || installedSkills.length > 0 || previous?.settings !== void 0;
|
|
2034
|
+
if (!hasProjectState) {
|
|
2035
|
+
rmSync7(manifestPath, { force: true });
|
|
2036
|
+
return;
|
|
2037
|
+
}
|
|
2038
|
+
const manifest = buildManifest({
|
|
2039
|
+
tools: nextTools.length > 0 ? nextTools : params.nextSkill?.tools ?? ["codex"],
|
|
2040
|
+
scope: "project",
|
|
2041
|
+
preset: previous?.preset,
|
|
2042
|
+
workspaces: previous?.workspaces,
|
|
2043
|
+
installedRules: previous?.installed_rules ?? [],
|
|
2044
|
+
installedFiles: previous?.installed_files,
|
|
2045
|
+
installedSkills,
|
|
2046
|
+
appendedFiles: previous?.appended_files,
|
|
2047
|
+
settings: previous?.settings,
|
|
2048
|
+
cliVersion: params.cliVersion,
|
|
2049
|
+
sourceHash: params.sourceHash
|
|
2050
|
+
});
|
|
2051
|
+
writeManifest(manifestPath, manifest);
|
|
2052
|
+
};
|
|
2053
|
+
var writeUserSkillState = (params) => {
|
|
2054
|
+
const registryPath = resolveSkillRegistryPath(params.basePath);
|
|
2055
|
+
const previous = readSkillRegistry(registryPath);
|
|
2056
|
+
const skills = params.removeSkillId ? removeInstalledSkill(previous?.skills ?? [], params.removeSkillId) : params.nextSkill ? upsertInstalledSkill(previous?.skills ?? [], params.nextSkill) : previous?.skills ?? [];
|
|
2057
|
+
if (skills.length === 0) {
|
|
2058
|
+
rmSync7(registryPath, { force: true });
|
|
2059
|
+
return;
|
|
2060
|
+
}
|
|
2061
|
+
writeSkillRegistry(registryPath, {
|
|
2062
|
+
skills,
|
|
2063
|
+
cliVersion: params.cliVersion,
|
|
2064
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2065
|
+
});
|
|
2066
|
+
};
|
|
2067
|
+
var readInstalledSkills = (scope, basePath) => {
|
|
2068
|
+
if (scope === "project") {
|
|
2069
|
+
return (readManifest(resolveManifestPath(basePath))?.installed_skills ?? []).map((installedSkill) => ({
|
|
2070
|
+
...installedSkill,
|
|
2071
|
+
id: resolveCanonicalSkillId(installedSkill.id)
|
|
2072
|
+
}));
|
|
2073
|
+
}
|
|
2074
|
+
return (readSkillRegistry(resolveSkillRegistryPath(basePath))?.skills ?? []).map((installedSkill) => ({
|
|
2075
|
+
...installedSkill,
|
|
2076
|
+
id: resolveCanonicalSkillId(installedSkill.id)
|
|
2077
|
+
}));
|
|
2078
|
+
};
|
|
2079
|
+
var installSkill = (params) => {
|
|
2080
|
+
const installedSkills = readInstalledSkills(params.scope, params.basePath);
|
|
2081
|
+
const existingInstalledSkill = findInstalledSkill(installedSkills, params.skill.id);
|
|
2082
|
+
const nextRequestedTools = mergeSkillTools({
|
|
2083
|
+
existing: existingInstalledSkill?.tools,
|
|
2084
|
+
requested: params.requestedTools
|
|
2085
|
+
});
|
|
2086
|
+
const { packages, installedSkill } = buildSkillInstallPlan({
|
|
2087
|
+
skill: params.skill,
|
|
2088
|
+
requestedTools: nextRequestedTools,
|
|
2089
|
+
scope: params.scope
|
|
2090
|
+
});
|
|
2091
|
+
installSkillPackages(params.basePath, packages);
|
|
2092
|
+
if (params.scope === "project") {
|
|
2093
|
+
writeProjectSkillState({
|
|
2094
|
+
basePath: params.basePath,
|
|
2095
|
+
sourceHash: params.sourceHash,
|
|
2096
|
+
cliVersion: params.cliVersion,
|
|
2097
|
+
nextSkill: installedSkill
|
|
2098
|
+
});
|
|
2099
|
+
} else {
|
|
2100
|
+
writeUserSkillState({
|
|
2101
|
+
basePath: params.basePath,
|
|
2102
|
+
cliVersion: params.cliVersion,
|
|
2103
|
+
nextSkill: installedSkill
|
|
2104
|
+
});
|
|
2105
|
+
}
|
|
2106
|
+
return installedSkill;
|
|
2107
|
+
};
|
|
2108
|
+
var skillListCommand = async (opts) => {
|
|
2109
|
+
const { scope, basePath } = resolveScopeContext(opts);
|
|
2110
|
+
const { allSkills } = loadCompilerInputs();
|
|
2111
|
+
const installedSkills = readInstalledSkills(scope, basePath);
|
|
2112
|
+
p8.intro(`ai-ops skill list (${scope})`);
|
|
2113
|
+
const sections = [
|
|
2114
|
+
{ kind: "reference", title: "reference skills" },
|
|
2115
|
+
{ kind: "task", title: "task skills" }
|
|
2116
|
+
].map(({ kind, title }) => {
|
|
2117
|
+
const lines = allSkills.filter((skill) => skill.kind === kind).map((skill) => {
|
|
2118
|
+
const installed = findInstalledSkill(installedSkills, skill.id);
|
|
2119
|
+
const suffix = installed ? `installed for ${installed.tools.join(", ")}` : "not installed";
|
|
2120
|
+
return `- ${skill.id} (${skill.install_scopes.join(", ")}) - ${suffix}`;
|
|
2121
|
+
});
|
|
2122
|
+
if (lines.length === 0) {
|
|
2123
|
+
return null;
|
|
2124
|
+
}
|
|
2125
|
+
return `${title}
|
|
2126
|
+
${lines.join("\n")}`;
|
|
2127
|
+
}).filter((section) => section !== null);
|
|
2128
|
+
p8.log.info(sections.join("\n\n"));
|
|
2129
|
+
p8.outro("ai-ops skill list \uC644\uB8CC");
|
|
2130
|
+
};
|
|
2131
|
+
var skillInstallCommand = async (skillId, opts) => {
|
|
2132
|
+
const { scope, basePath } = resolveScopeContext(opts);
|
|
2133
|
+
const { allSkills, sourceHash, cliVersion } = loadCompilerInputs();
|
|
2134
|
+
const skill = resolveSkillById(allSkills, skillId);
|
|
2135
|
+
assertScopeAllowed(skill, scope);
|
|
2136
|
+
const requestedTools = resolveRequestedTools({ requested: opts.tool, supported: skill.supported_tools });
|
|
2137
|
+
p8.intro(`ai-ops skill install ${skillId}`);
|
|
2138
|
+
const installedSkill = installSkill({
|
|
2139
|
+
skill,
|
|
2140
|
+
requestedTools,
|
|
2141
|
+
scope,
|
|
2142
|
+
basePath,
|
|
2143
|
+
cliVersion,
|
|
2144
|
+
sourceHash
|
|
2145
|
+
});
|
|
2146
|
+
p8.log.success(`\uC124\uCE58 \uC644\uB8CC: ${installedSkill.id} (${installedSkill.installed_paths.join(", ")})`);
|
|
2147
|
+
p8.outro("ai-ops skill install \uC644\uB8CC");
|
|
2148
|
+
};
|
|
2149
|
+
var skillDiffCommand = async (skillId, opts) => {
|
|
2150
|
+
const { scope, basePath } = resolveScopeContext(opts);
|
|
2151
|
+
const { allSkills } = loadCompilerInputs();
|
|
2152
|
+
const installedSkills = readInstalledSkills(scope, basePath);
|
|
2153
|
+
const targets = skillId ? installedSkills.filter((skill) => skill.id === skillId) : installedSkills;
|
|
2154
|
+
p8.intro(`ai-ops skill diff (${scope})`);
|
|
2155
|
+
if (targets.length === 0) {
|
|
2156
|
+
p8.log.warn("\uBE44\uAD50\uD560 \uC124\uCE58\uB41C skill\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
2157
|
+
p8.outro("ai-ops skill diff \uC644\uB8CC");
|
|
2158
|
+
return;
|
|
2159
|
+
}
|
|
2160
|
+
const lines = targets.map((installedSkill) => {
|
|
2161
|
+
const skill = resolveSkillById(allSkills, installedSkill.id);
|
|
2162
|
+
const { installedSkill: next } = buildSkillInstallPlan({
|
|
2163
|
+
skill,
|
|
2164
|
+
requestedTools: installedSkill.tools,
|
|
2165
|
+
scope
|
|
2166
|
+
});
|
|
2167
|
+
const changed = next.sourceHash !== installedSkill.sourceHash;
|
|
2168
|
+
return `- ${installedSkill.id}: ${changed ? "changed" : "up-to-date"} (${installedSkill.sourceHash} -> ${next.sourceHash})`;
|
|
2169
|
+
});
|
|
2170
|
+
p8.log.info(lines.join("\n"));
|
|
2171
|
+
p8.outro("ai-ops skill diff \uC644\uB8CC");
|
|
2172
|
+
};
|
|
2173
|
+
var skillUpdateCommand = async (skillId, opts) => {
|
|
2174
|
+
const { scope, basePath } = resolveScopeContext(opts);
|
|
2175
|
+
const { allSkills, sourceHash, cliVersion } = loadCompilerInputs();
|
|
2176
|
+
const installedSkills = readInstalledSkills(scope, basePath);
|
|
2177
|
+
const targets = skillId ? installedSkills.filter((skill) => skill.id === skillId) : installedSkills;
|
|
2178
|
+
p8.intro(`ai-ops skill update (${scope})`);
|
|
2179
|
+
if (targets.length === 0) {
|
|
2180
|
+
p8.log.warn("\uAC31\uC2E0\uD560 \uC124\uCE58\uB41C skill\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
2181
|
+
p8.outro("ai-ops skill update \uC644\uB8CC");
|
|
2182
|
+
return;
|
|
2183
|
+
}
|
|
2184
|
+
const nextInstalledSkills = targets.map((installedSkill) => {
|
|
2185
|
+
const skill = resolveSkillById(allSkills, installedSkill.id);
|
|
2186
|
+
const { packages, installedSkill: next } = buildSkillInstallPlan({
|
|
2187
|
+
skill,
|
|
2188
|
+
requestedTools: installedSkill.tools,
|
|
2189
|
+
scope
|
|
2190
|
+
});
|
|
2191
|
+
installSkillPackages(basePath, packages);
|
|
2192
|
+
return next;
|
|
2193
|
+
});
|
|
2194
|
+
if (scope === "project") {
|
|
2195
|
+
const manifestPath = resolveManifestPath(basePath);
|
|
2196
|
+
const previous = readManifest(manifestPath);
|
|
2197
|
+
if (!previous) {
|
|
2198
|
+
p8.log.error("project manifest\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
2199
|
+
process.exit(1);
|
|
2200
|
+
}
|
|
2201
|
+
const untouched = (previous.installed_skills ?? []).filter(
|
|
2202
|
+
(installedSkill) => !nextInstalledSkills.some((next) => next.id === installedSkill.id)
|
|
2203
|
+
);
|
|
2204
|
+
writeManifest(
|
|
2205
|
+
manifestPath,
|
|
2206
|
+
buildManifest({
|
|
2207
|
+
tools: previous.tools,
|
|
2208
|
+
scope: previous.scope,
|
|
2209
|
+
preset: previous.preset,
|
|
2210
|
+
workspaces: previous.workspaces,
|
|
2211
|
+
installedRules: previous.installed_rules,
|
|
2212
|
+
installedFiles: previous.installed_files,
|
|
2213
|
+
installedSkills: [...untouched, ...nextInstalledSkills],
|
|
2214
|
+
appendedFiles: previous.appended_files,
|
|
2215
|
+
settings: previous.settings,
|
|
2216
|
+
cliVersion,
|
|
2217
|
+
sourceHash
|
|
2218
|
+
})
|
|
2219
|
+
);
|
|
2220
|
+
} else {
|
|
2221
|
+
const registryPath = resolveSkillRegistryPath(basePath);
|
|
2222
|
+
const previous = readSkillRegistry(registryPath);
|
|
2223
|
+
const untouched = (previous?.skills ?? []).filter(
|
|
2224
|
+
(installedSkill) => !nextInstalledSkills.some((next) => next.id === installedSkill.id)
|
|
2225
|
+
);
|
|
2226
|
+
writeSkillRegistry(registryPath, {
|
|
2227
|
+
skills: [...untouched, ...nextInstalledSkills],
|
|
2228
|
+
cliVersion,
|
|
2229
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2230
|
+
});
|
|
2231
|
+
}
|
|
2232
|
+
p8.log.success(`\uAC31\uC2E0 \uC644\uB8CC: ${nextInstalledSkills.map((skill) => skill.id).join(", ")}`);
|
|
2233
|
+
p8.outro("ai-ops skill update \uC644\uB8CC");
|
|
2234
|
+
};
|
|
2235
|
+
var skillUninstallCommand = async (skillId, opts) => {
|
|
2236
|
+
const { scope, basePath } = resolveScopeContext(opts);
|
|
2237
|
+
const { sourceHash, cliVersion } = loadCompilerInputs();
|
|
2238
|
+
const installedSkills = readInstalledSkills(scope, basePath);
|
|
2239
|
+
const installedSkill = findInstalledSkill(installedSkills, skillId);
|
|
2240
|
+
p8.intro(`ai-ops skill uninstall ${skillId}`);
|
|
2241
|
+
if (!installedSkill) {
|
|
2242
|
+
p8.log.warn("\uC124\uCE58\uB41C skill\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.");
|
|
2243
|
+
p8.outro("ai-ops skill uninstall \uC644\uB8CC");
|
|
2244
|
+
return;
|
|
2245
|
+
}
|
|
2246
|
+
const removed = removeDirectories(basePath, installedSkill.installed_paths);
|
|
2247
|
+
if (scope === "project") {
|
|
2248
|
+
writeProjectSkillState({
|
|
2249
|
+
basePath,
|
|
2250
|
+
sourceHash,
|
|
2251
|
+
cliVersion,
|
|
2252
|
+
removeSkillId: skillId
|
|
2253
|
+
});
|
|
2254
|
+
} else {
|
|
2255
|
+
writeUserSkillState({
|
|
2256
|
+
basePath,
|
|
2257
|
+
cliVersion,
|
|
2258
|
+
removeSkillId: skillId
|
|
2259
|
+
});
|
|
2260
|
+
}
|
|
2261
|
+
p8.log.success(`\uC81C\uAC70 \uC644\uB8CC: ${removed.join(", ")}`);
|
|
2262
|
+
p8.outro("ai-ops skill uninstall \uC644\uB8CC");
|
|
2263
|
+
};
|
|
2264
|
+
|
|
2265
|
+
// src/bin/index.ts
|
|
2266
|
+
var program = new Command();
|
|
1327
2267
|
program.name("ai-ops").description("AI \uC5D0\uC774\uC804\uD2B8 \uADDC\uCE59 \uC2A4\uCE90\uD3F4\uB354").version("0.1.0");
|
|
1328
2268
|
program.command("init").description("AI \uADDC\uCE59 \uCD08\uAE30 \uC124\uCE58").action(() => initCommand());
|
|
1329
2269
|
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
2270
|
program.command("diff").description("\uC124\uCE58\uB41C \uADDC\uCE59\uACFC \uCD5C\uC2E0 \uC18C\uC2A4 \uBE44\uAD50").action(() => diffCommand());
|
|
1331
2271
|
program.command("uninstall").description("\uC124\uCE58\uB41C \uADDC\uCE59 \uD30C\uC77C \uBC0F manifest \uC81C\uAC70").action(() => uninstallCommand());
|
|
1332
|
-
|
|
2272
|
+
var skillCommand = program.command("skill").description("\uC5D0\uC774\uC804\uD2B8 skill \uC124\uCE58/\uC870\uD68C/\uAC31\uC2E0");
|
|
2273
|
+
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");
|
|
2274
|
+
applySkillScopeOptions(skillCommand.command("list").description("\uC0AC\uC6A9 \uAC00\uB2A5\uD55C skill \uBAA9\uB85D")).action(
|
|
2275
|
+
(opts) => skillListCommand(opts)
|
|
2276
|
+
);
|
|
2277
|
+
applySkillScopeOptions(skillCommand.command("install <skillId>").description("skill \uC124\uCE58")).action(
|
|
2278
|
+
(skillId, opts) => skillInstallCommand(skillId, opts)
|
|
2279
|
+
);
|
|
2280
|
+
applySkillScopeOptions(skillCommand.command("diff [skillId]").description("skill \uBCC0\uACBD \uBE44\uAD50")).action(
|
|
2281
|
+
(skillId, opts) => skillDiffCommand(skillId, opts)
|
|
2282
|
+
);
|
|
2283
|
+
applySkillScopeOptions(skillCommand.command("update [skillId]").description("skill \uAC31\uC2E0")).action(
|
|
2284
|
+
(skillId, opts) => skillUpdateCommand(skillId, opts)
|
|
2285
|
+
);
|
|
2286
|
+
applySkillScopeOptions(skillCommand.command("uninstall <skillId>").description("skill \uC81C\uAC70")).action(
|
|
2287
|
+
(skillId, opts) => skillUninstallCommand(skillId, opts)
|
|
2288
|
+
);
|
|
1333
2289
|
program.parse();
|
|
1334
2290
|
//# sourceMappingURL=index.js.map
|