ai-ops-cli 0.1.9 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/presets.yaml +56 -0
- package/data/rules/ai-llm-python.yaml +35 -0
- package/data/rules/code-philosophy.yaml +30 -0
- package/data/rules/communication.yaml +11 -0
- package/data/rules/data-pipeline-python.yaml +34 -0
- package/data/rules/engineering-standards.yaml +39 -0
- package/data/rules/fastapi.yaml +34 -0
- package/data/rules/flutter.yaml +40 -0
- package/data/rules/graphql.yaml +34 -0
- package/data/rules/libs-backend-python.yaml +35 -0
- package/data/rules/libs-backend-ts.yaml +38 -0
- package/data/rules/libs-frontend-app.yaml +39 -0
- package/data/rules/libs-frontend-web.yaml +44 -0
- package/data/rules/naming-convention.yaml +10 -0
- package/data/rules/nestjs-graphql.yaml +31 -0
- package/data/rules/nestjs.yaml +26 -0
- package/data/rules/nextjs.yaml +34 -0
- package/data/rules/plan-mode.yaml +30 -0
- package/data/rules/prisma-postgresql.yaml +30 -0
- package/data/rules/python.yaml +31 -0
- package/data/rules/react-typescript.yaml +11 -0
- package/data/rules/role-persona.yaml +14 -0
- package/data/rules/shadcn-ui.yaml +36 -0
- package/data/rules/sqlalchemy.yaml +32 -0
- package/data/rules/typescript.yaml +22 -0
- package/dist/bin/index.js +479 -105
- package/dist/bin/index.js.map +1 -1
- package/package.json +6 -3
package/dist/bin/index.js
CHANGED
|
@@ -5,39 +5,438 @@ import { Command } from "commander";
|
|
|
5
5
|
|
|
6
6
|
// src/commands/init.ts
|
|
7
7
|
import * as p2 from "@clack/prompts";
|
|
8
|
+
import { join as join8 } from "path";
|
|
9
|
+
|
|
10
|
+
// src/core/schemas/rule.schema.ts
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
var DecisionTableEntrySchema = z.object({
|
|
13
|
+
when: z.string().min(1),
|
|
14
|
+
then: z.string().min(1),
|
|
15
|
+
/** 조건부 규칙에서 회피해야 할 패턴 */
|
|
16
|
+
avoid: z.string().min(1).optional()
|
|
17
|
+
}).strict();
|
|
18
|
+
var RuleContentSchema = z.object({
|
|
19
|
+
/** Anti-pattern 규칙 ('하지 마라'). guidelines보다 항상 상단 렌더링 */
|
|
20
|
+
constraints: z.array(z.string().min(1)),
|
|
21
|
+
/** Positive 규칙 ('해라') */
|
|
22
|
+
guidelines: z.array(z.string().min(1)),
|
|
23
|
+
/** 조건부 규칙. when→then→avoid 구조 */
|
|
24
|
+
decision_table: z.array(DecisionTableEntrySchema).optional()
|
|
25
|
+
}).strict();
|
|
26
|
+
var RuleSchema = z.object({
|
|
27
|
+
id: z.string().regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, "id must be kebab-case"),
|
|
28
|
+
category: z.string().min(1),
|
|
29
|
+
tags: z.array(z.string().min(1)),
|
|
30
|
+
/** 0-100. 높을수록 생성 파일 상단 배치 (U-shaped attention 최적화) */
|
|
31
|
+
priority: z.number().int().min(0).max(100),
|
|
32
|
+
content: RuleContentSchema
|
|
33
|
+
}).strict();
|
|
34
|
+
|
|
35
|
+
// src/core/schemas/preset.schema.ts
|
|
36
|
+
import { z as z2 } from "zod";
|
|
37
|
+
var PresetSchema = z2.object({
|
|
38
|
+
id: z2.string().regex(/^[a-z][a-z0-9-]*$/).min(1),
|
|
39
|
+
description: z2.string().min(1),
|
|
40
|
+
rules: z2.array(z2.string().min(1)).min(1)
|
|
41
|
+
}).strict();
|
|
42
|
+
|
|
43
|
+
// src/core/schemas/manifest.schema.ts
|
|
44
|
+
import { z as z3 } from "zod";
|
|
45
|
+
var WorkspaceEntrySchema = z3.object({
|
|
46
|
+
preset: z3.string().min(1),
|
|
47
|
+
rules: z3.array(z3.string().min(1))
|
|
48
|
+
}).strict();
|
|
49
|
+
var ManifestSchema = z3.object({
|
|
50
|
+
tools: z3.array(z3.string().min(1)).min(1),
|
|
51
|
+
scope: z3.literal("project"),
|
|
52
|
+
/** 비모노레포 단일 preset */
|
|
53
|
+
preset: z3.string().min(1).optional(),
|
|
54
|
+
/** 모노레포: workspace path → { preset, rules } */
|
|
55
|
+
workspaces: z3.record(z3.string(), WorkspaceEntrySchema).optional(),
|
|
56
|
+
installed_rules: z3.array(z3.string().min(1)),
|
|
57
|
+
/** 실제 디스크에 쓰여진 파일 상대 경로 목록 (uninstall용). 기존 manifest 호환성 위해 optional */
|
|
58
|
+
installed_files: z3.array(z3.string().min(1)).optional(),
|
|
59
|
+
/** non-managed 파일에 섹션을 append한 경우 추적 (uninstall 시 섹션만 제거) */
|
|
60
|
+
appended_files: z3.array(z3.string().min(1)).optional(),
|
|
61
|
+
/** SSOT 데이터 파일들의 deterministic SHA-256 해시 (6자리 hex). diff/update 판단 기준 */
|
|
62
|
+
sourceHash: z3.string().regex(/^[a-f0-9]{6}$/, "sourceHash must be 6 lowercase hex chars"),
|
|
63
|
+
generatedAt: z3.string().datetime({ offset: true })
|
|
64
|
+
}).strict();
|
|
65
|
+
|
|
66
|
+
// src/core/loader.ts
|
|
67
|
+
import { readFileSync, readdirSync } from "fs";
|
|
68
|
+
import { resolve } from "path";
|
|
69
|
+
import { parse } from "yaml";
|
|
70
|
+
var sortRulesByPriority = (rules) => [...rules].sort((a, b) => b.priority - a.priority);
|
|
71
|
+
var parseRawPresets = (raw) => Object.entries(raw).map(([id, value]) => PresetSchema.parse({ id, ...value }));
|
|
72
|
+
var excludeRules = (rules, excludeIds) => {
|
|
73
|
+
const excludeSet = new Set(excludeIds);
|
|
74
|
+
return rules.filter((r) => !excludeSet.has(r.id));
|
|
75
|
+
};
|
|
76
|
+
var resolvePresetRules = (preset, allRules) => {
|
|
77
|
+
const resolved = preset.rules.map((ruleId) => {
|
|
78
|
+
const found = allRules.find((r) => r.id === ruleId);
|
|
79
|
+
if (!found) throw new Error(`Rule not found: ${ruleId}`);
|
|
80
|
+
return found;
|
|
81
|
+
});
|
|
82
|
+
return sortRulesByPriority(resolved);
|
|
83
|
+
};
|
|
84
|
+
var loadRuleFile = (filePath) => {
|
|
85
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
86
|
+
return RuleSchema.parse(parse(raw));
|
|
87
|
+
};
|
|
88
|
+
var loadAllRules = (rulesDir) => {
|
|
89
|
+
const files = readdirSync(rulesDir).filter((f) => f.endsWith(".yaml")).sort();
|
|
90
|
+
return files.map((f) => loadRuleFile(resolve(rulesDir, f)));
|
|
91
|
+
};
|
|
92
|
+
var loadPresets = (presetsPath) => {
|
|
93
|
+
const raw = readFileSync(presetsPath, "utf-8");
|
|
94
|
+
const data = parse(raw);
|
|
95
|
+
return parseRawPresets(data);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// src/core/renderer.ts
|
|
99
|
+
import { join } from "path";
|
|
100
|
+
|
|
101
|
+
// src/core/tool-output.ts
|
|
102
|
+
var GLOBAL_CATEGORIES = ["persona", "communication", "philosophy", "convention", "standard"];
|
|
103
|
+
var CLAUDE_CODE_PATH_GLOBS = {
|
|
104
|
+
typescript: ["**/*.ts", "**/*.tsx"],
|
|
105
|
+
"react-typescript": ["**/*.tsx", "**/*.jsx"],
|
|
106
|
+
nextjs: ["**/app/**", "next.config.*", "**/middleware.ts"],
|
|
107
|
+
nestjs: ["**/*.module.ts", "**/*.controller.ts", "**/*.service.ts"],
|
|
108
|
+
"nestjs-graphql": ["**/*.resolver.ts"],
|
|
109
|
+
graphql: ["**/*.graphql", "**/*.gql"],
|
|
110
|
+
"prisma-postgresql": ["prisma/**", "**/*.prisma"],
|
|
111
|
+
"shadcn-ui": ["**/components/ui/**"],
|
|
112
|
+
flutter: ["lib/**/*.dart"],
|
|
113
|
+
python: ["**/*.py"],
|
|
114
|
+
fastapi: ["**/routers/**", "**/main.py"],
|
|
115
|
+
sqlalchemy: ["**/models/**/*.py", "alembic/**"],
|
|
116
|
+
"data-pipeline-python": ["**/pipelines/**", "**/etl/**"],
|
|
117
|
+
"ai-llm-python": ["**/agents/**", "**/chains/**"],
|
|
118
|
+
"libs-frontend-web": ["**/*.tsx", "**/*.ts"],
|
|
119
|
+
"libs-frontend-app": ["lib/**/*.dart"],
|
|
120
|
+
"libs-backend-ts": ["**/*.ts"],
|
|
121
|
+
"libs-backend-python": ["**/*.py"]
|
|
122
|
+
};
|
|
123
|
+
var TOOL_OUTPUT_MAP = {
|
|
124
|
+
"claude-code": {
|
|
125
|
+
mode: "multi-file",
|
|
126
|
+
rulesDir: ".claude/rules",
|
|
127
|
+
fileExtension: ".md",
|
|
128
|
+
// single: path-scoped (paths: frontmatter) / monorepo: hierarchical ({workspace}/CLAUDE.md)
|
|
129
|
+
contextStrategy: "hybrid"
|
|
130
|
+
},
|
|
131
|
+
codex: {
|
|
132
|
+
mode: "multi-file",
|
|
133
|
+
dir: "",
|
|
134
|
+
rootFileName: "AGENTS.md",
|
|
135
|
+
// global 룰
|
|
136
|
+
domainFileName: "AGENTS.override.md",
|
|
137
|
+
// domain 룰 (하위 폴더)
|
|
138
|
+
contextStrategy: "hierarchical"
|
|
139
|
+
// 루트 + 하위 폴더 JIT
|
|
140
|
+
},
|
|
141
|
+
gemini: {
|
|
142
|
+
mode: "multi-file",
|
|
143
|
+
dir: ".gemini",
|
|
144
|
+
rootFileName: "GEMINI.md",
|
|
145
|
+
// global 룰
|
|
146
|
+
domainFileName: "GEMINI.md",
|
|
147
|
+
// domain 룰 (하위 폴더)
|
|
148
|
+
contextStrategy: "hierarchical"
|
|
149
|
+
// 루트 + 하위 폴더 JIT
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
// src/core/renderer.ts
|
|
154
|
+
var ruleIdToTitle = (id) => id.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
155
|
+
var renderDecisionTable = (entries) => {
|
|
156
|
+
const escape = (s) => s.replace(/\|/g, "|");
|
|
157
|
+
const hasAvoid = entries.some((e) => e.avoid !== void 0);
|
|
158
|
+
const header = hasAvoid ? "| When | Then | Avoid |\n|------|------|-------|" : "| When | Then |\n|------|------|";
|
|
159
|
+
const rows = entries.map((e) => {
|
|
160
|
+
const when = escape(e.when);
|
|
161
|
+
const then = escape(e.then);
|
|
162
|
+
if (hasAvoid) {
|
|
163
|
+
const avoid = e.avoid ? escape(e.avoid) : "";
|
|
164
|
+
return `| ${when} | ${then} | ${avoid} |`;
|
|
165
|
+
}
|
|
166
|
+
return `| ${when} | ${then} |`;
|
|
167
|
+
});
|
|
168
|
+
return [header, ...rows].join("\n");
|
|
169
|
+
};
|
|
170
|
+
var renderRuleToMarkdown = (rule) => {
|
|
171
|
+
const sections = [`# ${ruleIdToTitle(rule.id)}`];
|
|
172
|
+
if (rule.content.constraints.length > 0) {
|
|
173
|
+
sections.push("## Constraints");
|
|
174
|
+
sections.push(rule.content.constraints.map((c) => `- ${c}`).join("\n"));
|
|
175
|
+
}
|
|
176
|
+
if (rule.content.guidelines.length > 0) {
|
|
177
|
+
sections.push("## Guidelines");
|
|
178
|
+
sections.push(rule.content.guidelines.map((g) => `- ${g}`).join("\n"));
|
|
179
|
+
}
|
|
180
|
+
if (rule.content.decision_table && rule.content.decision_table.length > 0) {
|
|
181
|
+
sections.push("## Decision Table");
|
|
182
|
+
sections.push(renderDecisionTable(rule.content.decision_table));
|
|
183
|
+
}
|
|
184
|
+
return sections.join("\n\n");
|
|
185
|
+
};
|
|
186
|
+
var renderRulesToMarkdown = (rules) => rules.map(renderRuleToMarkdown).join("\n\n---\n\n");
|
|
187
|
+
var isGlobalRule = (rule) => GLOBAL_CATEGORIES.includes(rule.category);
|
|
188
|
+
var partitionRules = (rules) => {
|
|
189
|
+
const global = [];
|
|
190
|
+
const domain = [];
|
|
191
|
+
for (const rule of rules) {
|
|
192
|
+
if (isGlobalRule(rule)) {
|
|
193
|
+
global.push(rule);
|
|
194
|
+
} else {
|
|
195
|
+
domain.push(rule);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return { global, domain };
|
|
199
|
+
};
|
|
200
|
+
var renderFrontmatter = (paths) => {
|
|
201
|
+
const lines = paths.map((p6) => ` - "${p6}"`).join("\n");
|
|
202
|
+
return `---
|
|
203
|
+
paths:
|
|
204
|
+
${lines}
|
|
205
|
+
---`;
|
|
206
|
+
};
|
|
207
|
+
var renderClaudeCodeRule = (rule) => {
|
|
208
|
+
const globs = CLAUDE_CODE_PATH_GLOBS[rule.id];
|
|
209
|
+
if (!isGlobalRule(rule) && globs !== void 0) {
|
|
210
|
+
return `${renderFrontmatter(globs)}
|
|
211
|
+
|
|
212
|
+
${renderRuleToMarkdown(rule)}`;
|
|
213
|
+
}
|
|
214
|
+
return renderRuleToMarkdown(rule);
|
|
215
|
+
};
|
|
216
|
+
var renderForTool = (toolId, rules, workspaceMappings) => {
|
|
217
|
+
const config = TOOL_OUTPUT_MAP[toolId];
|
|
218
|
+
if (toolId === "claude-code") {
|
|
219
|
+
const { rulesDir, fileExtension } = config;
|
|
220
|
+
if (!workspaceMappings || workspaceMappings.length === 0) {
|
|
221
|
+
const files = rules.map((rule) => ({
|
|
222
|
+
relativePath: join(rulesDir, `${rule.id}${fileExtension}`),
|
|
223
|
+
content: renderClaudeCodeRule(rule)
|
|
224
|
+
}));
|
|
225
|
+
return { tool: "claude-code", files };
|
|
226
|
+
}
|
|
227
|
+
const { global: global2, domain: domain2 } = partitionRules(rules);
|
|
228
|
+
const globalFiles = global2.map((rule) => ({
|
|
229
|
+
relativePath: join(rulesDir, `${rule.id}${fileExtension}`),
|
|
230
|
+
content: renderRuleToMarkdown(rule)
|
|
231
|
+
// global은 frontmatter 불필요
|
|
232
|
+
}));
|
|
233
|
+
const workspaceFiles = [];
|
|
234
|
+
for (const ws of workspaceMappings) {
|
|
235
|
+
const wsRules = domain2.filter((r) => ws.ruleIds.includes(r.id));
|
|
236
|
+
if (wsRules.length === 0) continue;
|
|
237
|
+
workspaceFiles.push({
|
|
238
|
+
relativePath: join(ws.path, "CLAUDE.md"),
|
|
239
|
+
content: renderRulesToMarkdown(wsRules)
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
return { tool: "claude-code", files: [...globalFiles, ...workspaceFiles] };
|
|
243
|
+
}
|
|
244
|
+
const { global, domain } = partitionRules(rules);
|
|
245
|
+
const rootContent = renderRulesToMarkdown(global);
|
|
246
|
+
const domainContent = renderRulesToMarkdown(domain);
|
|
247
|
+
if (toolId === "codex") {
|
|
248
|
+
return { tool: "codex", rootContent, domainContent };
|
|
249
|
+
}
|
|
250
|
+
return { tool: "gemini", rootContent, domainContent };
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
// src/core/source-hash.ts
|
|
254
|
+
import { createHash } from "crypto";
|
|
255
|
+
import { readFileSync as readFileSync2, readdirSync as readdirSync2 } from "fs";
|
|
256
|
+
import { resolve as resolve2 } from "path";
|
|
257
|
+
var computeHash = (contents) => createHash("sha256").update(contents.join("")).digest("hex").slice(0, 6);
|
|
258
|
+
var computeSourceHash = (rulesDir) => {
|
|
259
|
+
const files = readdirSync2(rulesDir).filter((f) => f.endsWith(".yaml")).sort();
|
|
260
|
+
const contents = files.map((f) => readFileSync2(resolve2(rulesDir, f), "utf-8"));
|
|
261
|
+
return computeHash(contents);
|
|
262
|
+
};
|
|
263
|
+
var buildManifest = (params) => ManifestSchema.parse({
|
|
264
|
+
tools: [...params.tools],
|
|
265
|
+
scope: params.scope,
|
|
266
|
+
preset: params.preset,
|
|
267
|
+
workspaces: params.workspaces,
|
|
268
|
+
installed_rules: [...params.installedRules],
|
|
269
|
+
installed_files: params.installedFiles ? [...params.installedFiles] : void 0,
|
|
270
|
+
appended_files: params.appendedFiles && params.appendedFiles.length > 0 ? [...params.appendedFiles] : void 0,
|
|
271
|
+
sourceHash: params.sourceHash,
|
|
272
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// src/core/managed-header.ts
|
|
276
|
+
var MANAGED_MARKER = "<!-- managed by ai-ops -->";
|
|
277
|
+
var SECTION_START = "<!-- ai-ops:start -->";
|
|
278
|
+
var SECTION_END = "<!-- ai-ops:end -->";
|
|
279
|
+
var wrapWithHeader = (content, meta) => {
|
|
280
|
+
const metaLine = `<!-- sourceHash: ${meta.sourceHash} | generatedAt: ${meta.generatedAt} -->`;
|
|
281
|
+
return `${MANAGED_MARKER}
|
|
282
|
+
${metaLine}
|
|
283
|
+
|
|
284
|
+
${content}`;
|
|
285
|
+
};
|
|
286
|
+
var isManagedFile = (content) => content.startsWith(MANAGED_MARKER);
|
|
287
|
+
var stripManagedHeader = (content) => {
|
|
288
|
+
if (!isManagedFile(content)) return content;
|
|
289
|
+
const lines = content.split("\n");
|
|
290
|
+
const stripped = lines.slice(3).join("\n");
|
|
291
|
+
return stripped;
|
|
292
|
+
};
|
|
293
|
+
var wrapWithSection = (content, meta) => {
|
|
294
|
+
const metaLine = `<!-- sourceHash: ${meta.sourceHash} | generatedAt: ${meta.generatedAt} -->`;
|
|
295
|
+
return `${SECTION_START}
|
|
296
|
+
${metaLine}
|
|
297
|
+
|
|
298
|
+
${content}
|
|
299
|
+
${SECTION_END}`;
|
|
300
|
+
};
|
|
301
|
+
var hasAiOpsSection = (content) => content.includes(SECTION_START) && content.includes(SECTION_END);
|
|
302
|
+
var stripAiOpsSection = (content) => {
|
|
303
|
+
const startIdx = content.indexOf(SECTION_START);
|
|
304
|
+
const endIdx = content.indexOf(SECTION_END);
|
|
305
|
+
if (startIdx === -1 || endIdx === -1) return content;
|
|
306
|
+
const before = content.slice(0, startIdx).trimEnd();
|
|
307
|
+
const after = content.slice(endIdx + SECTION_END.length).trimStart();
|
|
308
|
+
return before + (after ? "\n\n" + after : "") + "\n";
|
|
309
|
+
};
|
|
310
|
+
var replaceAiOpsSection = (existing, newSection) => {
|
|
311
|
+
const startIdx = existing.indexOf(SECTION_START);
|
|
312
|
+
const endIdx = existing.indexOf(SECTION_END);
|
|
313
|
+
if (startIdx === -1 || endIdx === -1) return existing;
|
|
314
|
+
const before = existing.slice(0, startIdx).trimEnd();
|
|
315
|
+
const after = existing.slice(endIdx + SECTION_END.length).trimStart();
|
|
316
|
+
return before + "\n\n" + newSection + (after ? "\n\n" + after : "") + "\n";
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
// src/core/manifest-io.ts
|
|
320
|
+
import { mkdirSync, readFileSync as readFileSync3, writeFileSync } from "fs";
|
|
321
|
+
import { dirname, join as join2 } from "path";
|
|
322
|
+
var MANIFEST_FILENAME = ".ai-ops-manifest.json";
|
|
323
|
+
var parseManifest = (json) => ManifestSchema.parse(JSON.parse(json));
|
|
324
|
+
var serializeManifest = (manifest) => JSON.stringify(manifest, null, 2) + "\n";
|
|
325
|
+
var resolveManifestPath = (basePath) => join2(basePath, MANIFEST_FILENAME);
|
|
326
|
+
var readManifest = (manifestPath) => {
|
|
327
|
+
let raw;
|
|
328
|
+
try {
|
|
329
|
+
raw = readFileSync3(manifestPath, "utf-8");
|
|
330
|
+
} catch {
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
return parseManifest(raw);
|
|
334
|
+
};
|
|
335
|
+
var writeManifest = (manifestPath, manifest) => {
|
|
336
|
+
mkdirSync(dirname(manifestPath), { recursive: true });
|
|
337
|
+
writeFileSync(manifestPath, serializeManifest(manifest), "utf-8");
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
// src/core/diff.ts
|
|
341
|
+
var computeDiff = (params) => {
|
|
342
|
+
const { previous, currentRules, currentSourceHash } = params;
|
|
343
|
+
const previousSet = new Set(previous.installed_rules);
|
|
344
|
+
const currentSet = new Set(currentRules);
|
|
345
|
+
const added = currentRules.filter((id) => !previousSet.has(id));
|
|
346
|
+
const removed = previous.installed_rules.filter((id) => !currentSet.has(id));
|
|
347
|
+
const sourceChanged = previous.sourceHash !== currentSourceHash;
|
|
348
|
+
const status = added.length > 0 || removed.length > 0 || sourceChanged ? "changed" : "up-to-date";
|
|
349
|
+
return { status, added, removed, sourceChanged };
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
// src/core/install-plan.ts
|
|
353
|
+
import { join as join3 } from "path";
|
|
354
|
+
var CODEX_PLAN_SECTION = "\n\n---\n\n## Plan\n\nSave plans to `.codex/plans/<timestamp>-<topic>.md` when creating or updating plans in plan mode.";
|
|
355
|
+
var buildInstallPlan = (params) => {
|
|
356
|
+
const { toolId, renderResult, meta } = params;
|
|
357
|
+
if (toolId === "claude-code" && renderResult.tool === "claude-code") {
|
|
358
|
+
return renderResult.files.map(({ relativePath, content }) => ({
|
|
359
|
+
relativePath,
|
|
360
|
+
content: wrapWithHeader(content, meta)
|
|
361
|
+
}));
|
|
362
|
+
}
|
|
363
|
+
if (toolId === "codex" && renderResult.tool === "codex" || toolId === "gemini" && renderResult.tool === "gemini") {
|
|
364
|
+
const config = TOOL_OUTPUT_MAP[toolId];
|
|
365
|
+
const actions = [];
|
|
366
|
+
if (renderResult.rootContent) {
|
|
367
|
+
const rootContent = toolId === "codex" ? renderResult.rootContent + CODEX_PLAN_SECTION : renderResult.rootContent;
|
|
368
|
+
actions.push({
|
|
369
|
+
relativePath: join3(config.dir, config.rootFileName),
|
|
370
|
+
content: wrapWithHeader(rootContent, meta)
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
if (renderResult.domainContent) {
|
|
374
|
+
actions.push({
|
|
375
|
+
relativePath: join3(config.dir, config.domainFileName),
|
|
376
|
+
content: wrapWithHeader(renderResult.domainContent, meta)
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
return actions;
|
|
380
|
+
}
|
|
381
|
+
return [];
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
// src/core/uninstall-plan.ts
|
|
8
385
|
import { join as join4 } from "path";
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
386
|
+
var inferInstalledFiles = (manifest) => {
|
|
387
|
+
const files = [];
|
|
388
|
+
const isMonorepo = manifest.workspaces !== void 0;
|
|
389
|
+
for (const toolId of manifest.tools) {
|
|
390
|
+
if (toolId === "claude-code") {
|
|
391
|
+
const config = TOOL_OUTPUT_MAP["claude-code"];
|
|
392
|
+
for (const ruleId of manifest.installed_rules) {
|
|
393
|
+
files.push(join4(config.rulesDir, `${ruleId}${config.fileExtension}`));
|
|
394
|
+
}
|
|
395
|
+
} else if (toolId === "codex") {
|
|
396
|
+
const config = TOOL_OUTPUT_MAP["codex"];
|
|
397
|
+
if (!isMonorepo) {
|
|
398
|
+
files.push(join4(config.dir, config.rootFileName));
|
|
399
|
+
files.push(join4(config.dir, config.domainFileName));
|
|
400
|
+
} else {
|
|
401
|
+
files.push(join4(config.dir, config.rootFileName));
|
|
402
|
+
for (const ws of Object.keys(manifest.workspaces ?? {})) {
|
|
403
|
+
files.push(join4(ws, config.domainFileName));
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
} else if (toolId === "gemini") {
|
|
407
|
+
const config = TOOL_OUTPUT_MAP["gemini"];
|
|
408
|
+
if (!isMonorepo) {
|
|
409
|
+
files.push(join4(config.dir, config.rootFileName));
|
|
410
|
+
} else {
|
|
411
|
+
files.push(join4(config.dir, config.rootFileName));
|
|
412
|
+
for (const ws of Object.keys(manifest.workspaces ?? {})) {
|
|
413
|
+
files.push(join4(ws, config.domainFileName));
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return [...new Set(files)];
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
// src/core/paths.ts
|
|
422
|
+
import { dirname as dirname2, resolve as resolve3 } from "path";
|
|
423
|
+
import { fileURLToPath } from "url";
|
|
424
|
+
var __dirname = dirname2(fileURLToPath(import.meta.url));
|
|
425
|
+
var COMPILER_DATA_DIR = resolve3(__dirname, "..", "..", "data");
|
|
26
426
|
|
|
27
427
|
// src/lib/paths.ts
|
|
28
|
-
import { join } from "path";
|
|
29
|
-
|
|
30
|
-
var
|
|
31
|
-
var resolvePresetsPath = () => join(COMPILER_DATA_DIR, "presets.yaml");
|
|
428
|
+
import { join as join5 } from "path";
|
|
429
|
+
var resolveRulesDir = () => join5(COMPILER_DATA_DIR, "rules");
|
|
430
|
+
var resolvePresetsPath = () => join5(COMPILER_DATA_DIR, "presets.yaml");
|
|
32
431
|
var resolveBasePath = () => process.cwd();
|
|
33
432
|
|
|
34
433
|
// src/lib/workspace.ts
|
|
35
|
-
import { existsSync, readdirSync, statSync } from "fs";
|
|
36
|
-
import { join as
|
|
434
|
+
import { existsSync, readdirSync as readdirSync3, statSync } from "fs";
|
|
435
|
+
import { join as join6, resolve as resolve4 } from "path";
|
|
37
436
|
var EXCLUDE_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", ".turbo", ".cache", "coverage"]);
|
|
38
437
|
var isVisibleDir = (basePath, name) => {
|
|
39
438
|
if (name.startsWith(".") || EXCLUDE_DIRS.has(name)) return false;
|
|
40
|
-
return statSync(
|
|
439
|
+
return statSync(resolve4(basePath, name)).isDirectory();
|
|
41
440
|
};
|
|
42
441
|
var PROJECT_MANIFESTS = [
|
|
43
442
|
"package.json",
|
|
@@ -53,20 +452,20 @@ var PROJECT_MANIFESTS = [
|
|
|
53
452
|
"go.mod"
|
|
54
453
|
// Go
|
|
55
454
|
];
|
|
56
|
-
var isWorkspaceRoot = (dirPath) => PROJECT_MANIFESTS.some((f) => existsSync(
|
|
455
|
+
var isWorkspaceRoot = (dirPath) => PROJECT_MANIFESTS.some((f) => existsSync(join6(dirPath, f)));
|
|
57
456
|
var listWorkspaceCandidates = (basePath) => {
|
|
58
|
-
const topLevel =
|
|
457
|
+
const topLevel = readdirSync3(basePath).filter((name) => isVisibleDir(basePath, name));
|
|
59
458
|
const candidates = [];
|
|
60
459
|
for (const dir of topLevel) {
|
|
61
|
-
const subPath =
|
|
460
|
+
const subPath = resolve4(basePath, dir);
|
|
62
461
|
if (isWorkspaceRoot(subPath)) {
|
|
63
462
|
candidates.push(dir);
|
|
64
463
|
} else {
|
|
65
|
-
const children =
|
|
66
|
-
const wsChildren = children.filter((name) => isWorkspaceRoot(
|
|
464
|
+
const children = readdirSync3(subPath).filter((name) => isVisibleDir(subPath, name));
|
|
465
|
+
const wsChildren = children.filter((name) => isWorkspaceRoot(resolve4(subPath, name)));
|
|
67
466
|
if (wsChildren.length > 0) {
|
|
68
467
|
for (const child of wsChildren) {
|
|
69
|
-
candidates.push(
|
|
468
|
+
candidates.push(join6(dir, child));
|
|
70
469
|
}
|
|
71
470
|
} else {
|
|
72
471
|
candidates.push(dir);
|
|
@@ -77,40 +476,33 @@ var listWorkspaceCandidates = (basePath) => {
|
|
|
77
476
|
};
|
|
78
477
|
|
|
79
478
|
// src/lib/install.ts
|
|
80
|
-
import { existsSync as existsSync2, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
81
|
-
import { dirname, resolve as
|
|
82
|
-
import {
|
|
83
|
-
isManagedFile,
|
|
84
|
-
hasAiOpsSection,
|
|
85
|
-
wrapWithSection,
|
|
86
|
-
replaceAiOpsSection,
|
|
87
|
-
stripManagedHeader
|
|
88
|
-
} from "ai-ops-compiler";
|
|
479
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
480
|
+
import { dirname as dirname3, resolve as resolve5 } from "path";
|
|
89
481
|
var installFiles = (basePath, actions, meta) => {
|
|
90
482
|
const written = [];
|
|
91
483
|
const appended = [];
|
|
92
484
|
const skipped = [];
|
|
93
485
|
for (const action of actions) {
|
|
94
|
-
const absPath =
|
|
486
|
+
const absPath = resolve5(basePath, action.relativePath);
|
|
95
487
|
if (existsSync2(absPath)) {
|
|
96
|
-
const existing =
|
|
488
|
+
const existing = readFileSync4(absPath, "utf-8");
|
|
97
489
|
if (isManagedFile(existing)) {
|
|
98
|
-
|
|
490
|
+
writeFileSync2(absPath, action.content, "utf-8");
|
|
99
491
|
written.push(action.relativePath);
|
|
100
492
|
} else if (hasAiOpsSection(existing)) {
|
|
101
493
|
const sectionContent = wrapWithSection(stripManagedHeader(action.content), meta);
|
|
102
494
|
const updated = replaceAiOpsSection(existing, sectionContent);
|
|
103
|
-
|
|
495
|
+
writeFileSync2(absPath, updated, "utf-8");
|
|
104
496
|
appended.push(action.relativePath);
|
|
105
497
|
} else {
|
|
106
498
|
const sectionContent = wrapWithSection(stripManagedHeader(action.content), meta);
|
|
107
499
|
const updated = existing.trimEnd() + "\n\n" + sectionContent + "\n";
|
|
108
|
-
|
|
500
|
+
writeFileSync2(absPath, updated, "utf-8");
|
|
109
501
|
appended.push(action.relativePath);
|
|
110
502
|
}
|
|
111
503
|
} else {
|
|
112
|
-
|
|
113
|
-
|
|
504
|
+
mkdirSync2(dirname3(absPath), { recursive: true });
|
|
505
|
+
writeFileSync2(absPath, action.content, "utf-8");
|
|
114
506
|
written.push(action.relativePath);
|
|
115
507
|
}
|
|
116
508
|
}
|
|
@@ -119,8 +511,8 @@ var installFiles = (basePath, actions, meta) => {
|
|
|
119
511
|
|
|
120
512
|
// src/lib/gemini-settings.ts
|
|
121
513
|
import * as p from "@clack/prompts";
|
|
122
|
-
import { existsSync as existsSync3, mkdirSync as
|
|
123
|
-
import { join as
|
|
514
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
|
|
515
|
+
import { join as join7 } from "path";
|
|
124
516
|
var SETTING_GROUPS = [
|
|
125
517
|
{
|
|
126
518
|
value: "ui",
|
|
@@ -179,12 +571,12 @@ var promptGeminiSettings = async () => {
|
|
|
179
571
|
};
|
|
180
572
|
var installGeminiSettings = (basePath, selectedValues) => {
|
|
181
573
|
if (selectedValues.length === 0) return;
|
|
182
|
-
const settingsDir =
|
|
183
|
-
const settingsPath =
|
|
574
|
+
const settingsDir = join7(basePath, ".gemini");
|
|
575
|
+
const settingsPath = join7(settingsDir, "settings.json");
|
|
184
576
|
let existing = {};
|
|
185
577
|
if (existsSync3(settingsPath)) {
|
|
186
578
|
try {
|
|
187
|
-
existing = JSON.parse(
|
|
579
|
+
existing = JSON.parse(readFileSync5(settingsPath, "utf-8"));
|
|
188
580
|
} catch {
|
|
189
581
|
}
|
|
190
582
|
}
|
|
@@ -194,8 +586,8 @@ var installGeminiSettings = (basePath, selectedValues) => {
|
|
|
194
586
|
if (!group) continue;
|
|
195
587
|
merged = deepMerge(merged, group.patch);
|
|
196
588
|
}
|
|
197
|
-
|
|
198
|
-
|
|
589
|
+
mkdirSync3(settingsDir, { recursive: true });
|
|
590
|
+
writeFileSync3(settingsPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
199
591
|
};
|
|
200
592
|
|
|
201
593
|
// src/commands/init.ts
|
|
@@ -249,7 +641,7 @@ var installHierarchicalMonorepo = (toolId, mappings, basePath, meta) => {
|
|
|
249
641
|
const { global } = partitionRules(allRules);
|
|
250
642
|
if (global.length > 0) {
|
|
251
643
|
const rootAction = {
|
|
252
|
-
relativePath:
|
|
644
|
+
relativePath: join8(config.dir, config.rootFileName),
|
|
253
645
|
content: wrapWithHeader(renderRulesToMarkdown(global), meta)
|
|
254
646
|
};
|
|
255
647
|
const r = installFiles(basePath, [rootAction], meta);
|
|
@@ -260,7 +652,7 @@ var installHierarchicalMonorepo = (toolId, mappings, basePath, meta) => {
|
|
|
260
652
|
const { domain } = partitionRules(mapping.finalRules);
|
|
261
653
|
if (domain.length === 0) continue;
|
|
262
654
|
const domainAction = {
|
|
263
|
-
relativePath:
|
|
655
|
+
relativePath: join8(mapping.workspace, config.domainFileName),
|
|
264
656
|
content: wrapWithHeader(renderRulesToMarkdown(domain), meta)
|
|
265
657
|
};
|
|
266
658
|
const r = installFiles(basePath, [domainAction], meta);
|
|
@@ -387,25 +779,10 @@ ${allAppended.map((f) => ` ${f}`).join("\n")}`);
|
|
|
387
779
|
|
|
388
780
|
// src/commands/update.ts
|
|
389
781
|
import * as p3 from "@clack/prompts";
|
|
390
|
-
import {
|
|
391
|
-
readManifest,
|
|
392
|
-
resolveManifestPath as resolveManifestPath2,
|
|
393
|
-
loadAllRules as loadAllRules2,
|
|
394
|
-
renderForTool as renderForTool2,
|
|
395
|
-
buildInstallPlan as buildInstallPlan2,
|
|
396
|
-
buildManifest as buildManifest2,
|
|
397
|
-
writeManifest as writeManifest2,
|
|
398
|
-
computeSourceHash as computeSourceHash2,
|
|
399
|
-
computeDiff,
|
|
400
|
-
partitionRules as partitionRules2,
|
|
401
|
-
renderRulesToMarkdown as renderRulesToMarkdown2,
|
|
402
|
-
wrapWithHeader as wrapWithHeader2,
|
|
403
|
-
TOOL_OUTPUT_MAP as TOOL_OUTPUT_MAP2
|
|
404
|
-
} from "ai-ops-compiler";
|
|
405
|
-
import { join as join5 } from "path";
|
|
782
|
+
import { join as join9 } from "path";
|
|
406
783
|
var updateCommand = async (opts) => {
|
|
407
784
|
const basePath = resolveBasePath();
|
|
408
|
-
const manifestPath =
|
|
785
|
+
const manifestPath = resolveManifestPath(basePath);
|
|
409
786
|
p3.intro("ai-ops update");
|
|
410
787
|
const manifest = readManifest(manifestPath);
|
|
411
788
|
if (!manifest) {
|
|
@@ -413,7 +790,7 @@ var updateCommand = async (opts) => {
|
|
|
413
790
|
process.exit(1);
|
|
414
791
|
}
|
|
415
792
|
const rulesDir = resolveRulesDir();
|
|
416
|
-
const sourceHash =
|
|
793
|
+
const sourceHash = computeSourceHash(rulesDir);
|
|
417
794
|
const diffResult = computeDiff({
|
|
418
795
|
previous: manifest,
|
|
419
796
|
currentRules: manifest.installed_rules,
|
|
@@ -426,7 +803,7 @@ var updateCommand = async (opts) => {
|
|
|
426
803
|
}
|
|
427
804
|
const s = p3.spinner();
|
|
428
805
|
s.start("\uADDC\uCE59 \uAC31\uC2E0 \uC911...");
|
|
429
|
-
const allRules =
|
|
806
|
+
const allRules = loadAllRules(rulesDir);
|
|
430
807
|
const meta = { sourceHash, generatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
431
808
|
const allInstalledFiles = [];
|
|
432
809
|
const allAppended = [];
|
|
@@ -441,20 +818,20 @@ var updateCommand = async (opts) => {
|
|
|
441
818
|
path,
|
|
442
819
|
ruleIds: entry.rules
|
|
443
820
|
}));
|
|
444
|
-
const renderResult =
|
|
445
|
-
const actions =
|
|
821
|
+
const renderResult = renderForTool("claude-code", rulesToInstall, workspaceMappings);
|
|
822
|
+
const actions = buildInstallPlan({ toolId: "claude-code", renderResult, meta });
|
|
446
823
|
const r = installFiles(basePath, actions, meta);
|
|
447
824
|
allInstalledFiles.push(...r.written);
|
|
448
825
|
allAppended.push(...r.appended);
|
|
449
826
|
} else {
|
|
450
|
-
const config =
|
|
827
|
+
const config = TOOL_OUTPUT_MAP[toolId];
|
|
451
828
|
const allInstalledRuleSet = new Set(manifest.installed_rules);
|
|
452
829
|
const allRulesToInstall = allRules.filter((r) => allInstalledRuleSet.has(r.id));
|
|
453
|
-
const { global } =
|
|
830
|
+
const { global } = partitionRules(allRulesToInstall);
|
|
454
831
|
if (global.length > 0) {
|
|
455
832
|
const rootAction = {
|
|
456
|
-
relativePath:
|
|
457
|
-
content:
|
|
833
|
+
relativePath: join9(config.dir, config.rootFileName),
|
|
834
|
+
content: wrapWithHeader(renderRulesToMarkdown(global), meta)
|
|
458
835
|
};
|
|
459
836
|
const r = installFiles(basePath, [rootAction], meta);
|
|
460
837
|
allInstalledFiles.push(...r.written);
|
|
@@ -463,11 +840,11 @@ var updateCommand = async (opts) => {
|
|
|
463
840
|
for (const [ws, entry] of workspaceEntries) {
|
|
464
841
|
const wsRuleSet = new Set(entry.rules);
|
|
465
842
|
const wsRules = allRules.filter((r2) => wsRuleSet.has(r2.id));
|
|
466
|
-
const { domain } =
|
|
843
|
+
const { domain } = partitionRules(wsRules);
|
|
467
844
|
if (domain.length === 0) continue;
|
|
468
845
|
const domainAction = {
|
|
469
|
-
relativePath:
|
|
470
|
-
content:
|
|
846
|
+
relativePath: join9(ws, config.domainFileName),
|
|
847
|
+
content: wrapWithHeader(renderRulesToMarkdown(domain), meta)
|
|
471
848
|
};
|
|
472
849
|
const r = installFiles(basePath, [domainAction], meta);
|
|
473
850
|
allInstalledFiles.push(...r.written);
|
|
@@ -480,14 +857,14 @@ var updateCommand = async (opts) => {
|
|
|
480
857
|
const rulesToInstall = allRules.filter((r) => installedRuleSet.has(r.id));
|
|
481
858
|
for (const toolIdStr of manifest.tools) {
|
|
482
859
|
const toolId = toolIdStr;
|
|
483
|
-
const renderResult =
|
|
484
|
-
const actions =
|
|
860
|
+
const renderResult = renderForTool(toolId, rulesToInstall);
|
|
861
|
+
const actions = buildInstallPlan({ toolId, renderResult, meta });
|
|
485
862
|
const r = installFiles(basePath, actions, meta);
|
|
486
863
|
allInstalledFiles.push(...r.written);
|
|
487
864
|
allAppended.push(...r.appended);
|
|
488
865
|
}
|
|
489
866
|
}
|
|
490
|
-
const newManifest =
|
|
867
|
+
const newManifest = buildManifest({
|
|
491
868
|
tools: manifest.tools,
|
|
492
869
|
scope: manifest.scope,
|
|
493
870
|
preset: manifest.preset,
|
|
@@ -497,24 +874,23 @@ var updateCommand = async (opts) => {
|
|
|
497
874
|
appendedFiles: allAppended.length > 0 ? allAppended : manifest.appended_files,
|
|
498
875
|
sourceHash
|
|
499
876
|
});
|
|
500
|
-
|
|
877
|
+
writeManifest(manifestPath, newManifest);
|
|
501
878
|
s.stop("\uADDC\uCE59 \uAC31\uC2E0 \uC644\uB8CC");
|
|
502
879
|
p3.outro("ai-ops update \uC644\uB8CC");
|
|
503
880
|
};
|
|
504
881
|
|
|
505
882
|
// src/commands/diff.ts
|
|
506
883
|
import * as p4 from "@clack/prompts";
|
|
507
|
-
import { readManifest as readManifest2, resolveManifestPath as resolveManifestPath3, computeSourceHash as computeSourceHash3, computeDiff as computeDiff2 } from "ai-ops-compiler";
|
|
508
884
|
var diffCommand = async () => {
|
|
509
885
|
const basePath = resolveBasePath();
|
|
510
886
|
p4.intro("ai-ops diff");
|
|
511
|
-
const manifest =
|
|
887
|
+
const manifest = readManifest(resolveManifestPath(basePath));
|
|
512
888
|
if (!manifest) {
|
|
513
889
|
p4.log.error("manifest\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 ai-ops init\uC744 \uC2E4\uD589\uD558\uC138\uC694.");
|
|
514
890
|
process.exit(1);
|
|
515
891
|
}
|
|
516
|
-
const sourceHash =
|
|
517
|
-
const result =
|
|
892
|
+
const sourceHash = computeSourceHash(resolveRulesDir());
|
|
893
|
+
const result = computeDiff({
|
|
518
894
|
previous: manifest,
|
|
519
895
|
currentRules: manifest.installed_rules,
|
|
520
896
|
currentSourceHash: sourceHash
|
|
@@ -538,28 +914,26 @@ var diffCommand = async () => {
|
|
|
538
914
|
// src/commands/uninstall.ts
|
|
539
915
|
import * as p5 from "@clack/prompts";
|
|
540
916
|
import { rmSync as rmSync2 } from "fs";
|
|
541
|
-
import { readManifest as readManifest3, resolveManifestPath as resolveManifestPath4, inferInstalledFiles, MANIFEST_FILENAME } from "ai-ops-compiler";
|
|
542
917
|
|
|
543
918
|
// src/lib/uninstall.ts
|
|
544
|
-
import { existsSync as existsSync4, readFileSync as
|
|
545
|
-
import { resolve as
|
|
546
|
-
import { isManagedFile as isManagedFile2, hasAiOpsSection as hasAiOpsSection2, stripAiOpsSection } from "ai-ops-compiler";
|
|
919
|
+
import { existsSync as existsSync4, readFileSync as readFileSync6, rmSync, readdirSync as readdirSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
920
|
+
import { resolve as resolve6, dirname as dirname4 } from "path";
|
|
547
921
|
var removeFiles = (basePath, relativePaths) => {
|
|
548
922
|
const deleted = [];
|
|
549
923
|
const cleaned = [];
|
|
550
924
|
const skipped = [];
|
|
551
925
|
const notFound = [];
|
|
552
926
|
for (const rel of relativePaths) {
|
|
553
|
-
const absPath =
|
|
927
|
+
const absPath = resolve6(basePath, rel);
|
|
554
928
|
if (!existsSync4(absPath)) {
|
|
555
929
|
notFound.push(rel);
|
|
556
930
|
continue;
|
|
557
931
|
}
|
|
558
|
-
const content =
|
|
559
|
-
if (!
|
|
560
|
-
if (
|
|
932
|
+
const content = readFileSync6(absPath, "utf-8");
|
|
933
|
+
if (!isManagedFile(content)) {
|
|
934
|
+
if (hasAiOpsSection(content)) {
|
|
561
935
|
const stripped = stripAiOpsSection(content);
|
|
562
|
-
|
|
936
|
+
writeFileSync4(absPath, stripped, "utf-8");
|
|
563
937
|
cleaned.push(rel);
|
|
564
938
|
} else {
|
|
565
939
|
skipped.push(rel);
|
|
@@ -574,10 +948,10 @@ var removeFiles = (basePath, relativePaths) => {
|
|
|
574
948
|
var cleanEmptyDirs = (basePath, dirs) => {
|
|
575
949
|
const removed = [];
|
|
576
950
|
for (const dir of dirs) {
|
|
577
|
-
const absDir =
|
|
951
|
+
const absDir = resolve6(basePath, dir);
|
|
578
952
|
if (!existsSync4(absDir)) continue;
|
|
579
953
|
try {
|
|
580
|
-
const entries =
|
|
954
|
+
const entries = readdirSync4(absDir);
|
|
581
955
|
if (entries.length === 0) {
|
|
582
956
|
rmSync(absDir, { recursive: true });
|
|
583
957
|
removed.push(dir);
|
|
@@ -590,7 +964,7 @@ var cleanEmptyDirs = (basePath, dirs) => {
|
|
|
590
964
|
var collectManagedDirs = (relativePaths) => {
|
|
591
965
|
const dirs = /* @__PURE__ */ new Set();
|
|
592
966
|
for (const rel of relativePaths) {
|
|
593
|
-
const dir =
|
|
967
|
+
const dir = dirname4(rel);
|
|
594
968
|
if (dir !== ".") {
|
|
595
969
|
dirs.add(dir);
|
|
596
970
|
}
|
|
@@ -601,9 +975,9 @@ var collectManagedDirs = (relativePaths) => {
|
|
|
601
975
|
// src/commands/uninstall.ts
|
|
602
976
|
var uninstallCommand = async () => {
|
|
603
977
|
const basePath = resolveBasePath();
|
|
604
|
-
const manifestPath =
|
|
978
|
+
const manifestPath = resolveManifestPath(basePath);
|
|
605
979
|
p5.intro("ai-ops uninstall");
|
|
606
|
-
const manifest =
|
|
980
|
+
const manifest = readManifest(manifestPath);
|
|
607
981
|
if (!manifest) {
|
|
608
982
|
p5.log.error("manifest\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 ai-ops init\uC744 \uC2E4\uD589\uD558\uC138\uC694.");
|
|
609
983
|
process.exit(1);
|