ai-ops-cli 0.1.14 → 0.1.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/index.js +349 -159
- package/dist/bin/index.js.map +1 -1
- package/package.json +1 -1
package/dist/bin/index.js
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/commands/init.ts
|
|
7
|
-
import * as
|
|
8
|
-
import { join as
|
|
7
|
+
import * as p4 from "@clack/prompts";
|
|
8
|
+
import { join as join10 } from "path";
|
|
9
9
|
|
|
10
10
|
// src/core/schemas/rule.schema.ts
|
|
11
11
|
import { z } from "zod";
|
|
@@ -44,7 +44,8 @@ var PresetSchema = z2.object({
|
|
|
44
44
|
import { z as z3 } from "zod";
|
|
45
45
|
var SettingsConfigSchema = z3.object({
|
|
46
46
|
claude: z3.array(z3.string().min(1)).optional(),
|
|
47
|
-
gemini: z3.array(z3.string().min(1)).optional()
|
|
47
|
+
gemini: z3.array(z3.string().min(1)).optional(),
|
|
48
|
+
prettierignore: z3.boolean().optional()
|
|
48
49
|
}).strict();
|
|
49
50
|
var WorkspaceEntrySchema = z3.object({
|
|
50
51
|
preset: z3.string().min(1),
|
|
@@ -64,6 +65,8 @@ var ManifestSchema = z3.object({
|
|
|
64
65
|
appended_files: z3.array(z3.string().min(1)).optional(),
|
|
65
66
|
/** init 시 선택된 settings 항목 — update 시 재생성에 사용 */
|
|
66
67
|
settings: SettingsConfigSchema.optional(),
|
|
68
|
+
/** init/update 실행 시점의 CLI 패키지 버전 — 버전 변경 감지에 사용 */
|
|
69
|
+
cliVersion: z3.string().optional(),
|
|
67
70
|
/** SSOT 데이터 파일들의 deterministic SHA-256 해시 (6자리 hex). diff/update 판단 기준 */
|
|
68
71
|
sourceHash: z3.string().regex(/^[a-f0-9]{6}$/, "sourceHash must be 6 lowercase hex chars"),
|
|
69
72
|
generatedAt: z3.string().datetime({ offset: true })
|
|
@@ -237,7 +240,7 @@ var partitionRules = (rules) => {
|
|
|
237
240
|
return { global, domain };
|
|
238
241
|
};
|
|
239
242
|
var renderFrontmatter = (paths) => {
|
|
240
|
-
const lines = paths.map((
|
|
243
|
+
const lines = paths.map((p8) => ` - "${p8}"`).join("\n");
|
|
241
244
|
return `---
|
|
242
245
|
paths:
|
|
243
246
|
${lines}
|
|
@@ -292,7 +295,18 @@ var renderForTool = (toolId, rules, workspaceMappings) => {
|
|
|
292
295
|
// src/core/source-hash.ts
|
|
293
296
|
import { createHash } from "crypto";
|
|
294
297
|
import { readFileSync as readFileSync2, readdirSync as readdirSync2 } from "fs";
|
|
295
|
-
import { resolve as resolve2 } from "path";
|
|
298
|
+
import { dirname, resolve as resolve2 } from "path";
|
|
299
|
+
import { fileURLToPath } from "url";
|
|
300
|
+
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
301
|
+
var getCliVersion = () => {
|
|
302
|
+
try {
|
|
303
|
+
const pkgPath = resolve2(__dirname, "..", "..", "package.json");
|
|
304
|
+
const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
|
|
305
|
+
return pkg.version;
|
|
306
|
+
} catch {
|
|
307
|
+
return "unknown";
|
|
308
|
+
}
|
|
309
|
+
};
|
|
296
310
|
var computeHash = (contents) => createHash("sha256").update(contents.join("")).digest("hex").slice(0, 6);
|
|
297
311
|
var computeSourceHash = (rulesDir) => {
|
|
298
312
|
const files = readdirSync2(rulesDir).filter((f) => f.endsWith(".yaml")).sort();
|
|
@@ -311,6 +325,7 @@ var buildManifest = (params) => ManifestSchema.parse({
|
|
|
311
325
|
claude: params.settings.claude ? [...params.settings.claude] : void 0,
|
|
312
326
|
gemini: params.settings.gemini ? [...params.settings.gemini] : void 0
|
|
313
327
|
} : void 0,
|
|
328
|
+
cliVersion: params.cliVersion,
|
|
314
329
|
sourceHash: params.sourceHash,
|
|
315
330
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
316
331
|
});
|
|
@@ -319,20 +334,7 @@ var buildManifest = (params) => ManifestSchema.parse({
|
|
|
319
334
|
var MANAGED_MARKER = "<!-- managed by ai-ops -->";
|
|
320
335
|
var SECTION_START = "<!-- ai-ops:start -->";
|
|
321
336
|
var SECTION_END = "<!-- ai-ops:end -->";
|
|
322
|
-
var
|
|
323
|
-
const metaLine = `<!-- sourceHash: ${meta.sourceHash} | generatedAt: ${meta.generatedAt} -->`;
|
|
324
|
-
return `${MANAGED_MARKER}
|
|
325
|
-
${metaLine}
|
|
326
|
-
|
|
327
|
-
${content}`;
|
|
328
|
-
};
|
|
329
|
-
var isManagedFile = (content) => content.startsWith(MANAGED_MARKER);
|
|
330
|
-
var stripManagedHeader = (content) => {
|
|
331
|
-
if (!isManagedFile(content)) return content;
|
|
332
|
-
const lines = content.split("\n");
|
|
333
|
-
const stripped = lines.slice(3).join("\n");
|
|
334
|
-
return stripped;
|
|
335
|
-
};
|
|
337
|
+
var hasLegacyHeader = (content) => content.includes(MANAGED_MARKER);
|
|
336
338
|
var wrapWithSection = (content, meta) => {
|
|
337
339
|
const metaLine = `<!-- sourceHash: ${meta.sourceHash} | generatedAt: ${meta.generatedAt} -->`;
|
|
338
340
|
return `${SECTION_START}
|
|
@@ -356,12 +358,12 @@ var replaceAiOpsSection = (existing, newSection) => {
|
|
|
356
358
|
if (startIdx === -1 || endIdx === -1) return existing;
|
|
357
359
|
const before = existing.slice(0, startIdx).trimEnd();
|
|
358
360
|
const after = existing.slice(endIdx + SECTION_END.length).trimStart();
|
|
359
|
-
return before
|
|
361
|
+
return [before, newSection, after].filter(Boolean).join("\n\n") + "\n";
|
|
360
362
|
};
|
|
361
363
|
|
|
362
364
|
// src/core/manifest-io.ts
|
|
363
365
|
import { mkdirSync, readFileSync as readFileSync3, writeFileSync } from "fs";
|
|
364
|
-
import { dirname, join as join2 } from "path";
|
|
366
|
+
import { dirname as dirname2, join as join2 } from "path";
|
|
365
367
|
var MANIFEST_FILENAME = ".ai-ops-manifest.json";
|
|
366
368
|
var parseManifest = (json) => ManifestSchema.parse(JSON.parse(json));
|
|
367
369
|
var serializeManifest = (manifest) => JSON.stringify(manifest, null, 2) + "\n";
|
|
@@ -376,20 +378,21 @@ var readManifest = (manifestPath) => {
|
|
|
376
378
|
return parseManifest(raw);
|
|
377
379
|
};
|
|
378
380
|
var writeManifest = (manifestPath, manifest) => {
|
|
379
|
-
mkdirSync(
|
|
381
|
+
mkdirSync(dirname2(manifestPath), { recursive: true });
|
|
380
382
|
writeFileSync(manifestPath, serializeManifest(manifest), "utf-8");
|
|
381
383
|
};
|
|
382
384
|
|
|
383
385
|
// src/core/diff.ts
|
|
384
386
|
var computeDiff = (params) => {
|
|
385
|
-
const { previous, currentRules, currentSourceHash } = params;
|
|
387
|
+
const { previous, currentRules, currentSourceHash, currentCliVersion } = params;
|
|
386
388
|
const previousSet = new Set(previous.installed_rules);
|
|
387
389
|
const currentSet = new Set(currentRules);
|
|
388
390
|
const added = currentRules.filter((id) => !previousSet.has(id));
|
|
389
391
|
const removed = previous.installed_rules.filter((id) => !currentSet.has(id));
|
|
390
392
|
const sourceChanged = previous.sourceHash !== currentSourceHash;
|
|
391
|
-
const
|
|
392
|
-
|
|
393
|
+
const versionChanged = previous.cliVersion !== void 0 && currentCliVersion !== void 0 && previous.cliVersion !== currentCliVersion;
|
|
394
|
+
const status = added.length > 0 || removed.length > 0 || sourceChanged || versionChanged ? "changed" : "up-to-date";
|
|
395
|
+
return { status, added, removed, sourceChanged, versionChanged };
|
|
393
396
|
};
|
|
394
397
|
|
|
395
398
|
// src/core/install-plan.ts
|
|
@@ -400,7 +403,7 @@ var buildInstallPlan = (params) => {
|
|
|
400
403
|
if (toolId === "claude-code" && renderResult.tool === "claude-code") {
|
|
401
404
|
return renderResult.files.map(({ relativePath, content }) => ({
|
|
402
405
|
relativePath,
|
|
403
|
-
content:
|
|
406
|
+
content: wrapWithSection(content, meta)
|
|
404
407
|
}));
|
|
405
408
|
}
|
|
406
409
|
if (toolId === "codex" && renderResult.tool === "codex" || toolId === "gemini" && renderResult.tool === "gemini") {
|
|
@@ -410,13 +413,13 @@ var buildInstallPlan = (params) => {
|
|
|
410
413
|
const rootContent = toolId === "codex" ? renderResult.rootContent + CODEX_PLAN_SECTION : renderResult.rootContent;
|
|
411
414
|
actions.push({
|
|
412
415
|
relativePath: join3(config.dir, config.rootFileName),
|
|
413
|
-
content:
|
|
416
|
+
content: wrapWithSection(rootContent, meta)
|
|
414
417
|
});
|
|
415
418
|
}
|
|
416
419
|
if (renderResult.domainContent) {
|
|
417
420
|
actions.push({
|
|
418
421
|
relativePath: join3(config.dir, config.domainFileName),
|
|
419
|
-
content:
|
|
422
|
+
content: wrapWithSection(renderResult.domainContent, meta)
|
|
420
423
|
});
|
|
421
424
|
}
|
|
422
425
|
return actions;
|
|
@@ -462,10 +465,10 @@ var inferInstalledFiles = (manifest) => {
|
|
|
462
465
|
};
|
|
463
466
|
|
|
464
467
|
// src/core/paths.ts
|
|
465
|
-
import { dirname as
|
|
466
|
-
import { fileURLToPath } from "url";
|
|
467
|
-
var
|
|
468
|
-
var COMPILER_DATA_DIR = resolve3(
|
|
468
|
+
import { dirname as dirname3, resolve as resolve3 } from "path";
|
|
469
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
470
|
+
var __dirname2 = dirname3(fileURLToPath2(import.meta.url));
|
|
471
|
+
var COMPILER_DATA_DIR = resolve3(__dirname2, "..", "..", "data");
|
|
469
472
|
|
|
470
473
|
// src/lib/paths.ts
|
|
471
474
|
import { join as join5 } from "path";
|
|
@@ -520,33 +523,32 @@ var listWorkspaceCandidates = (basePath) => {
|
|
|
520
523
|
|
|
521
524
|
// src/lib/install.ts
|
|
522
525
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
523
|
-
import { dirname as
|
|
524
|
-
var installFiles = (basePath, actions,
|
|
526
|
+
import { dirname as dirname4, resolve as resolve5 } from "path";
|
|
527
|
+
var installFiles = (basePath, actions, _meta) => {
|
|
525
528
|
const written = [];
|
|
526
529
|
const appended = [];
|
|
527
530
|
const skipped = [];
|
|
528
531
|
for (const action of actions) {
|
|
529
532
|
const absPath = resolve5(basePath, action.relativePath);
|
|
530
|
-
if (existsSync2(absPath)) {
|
|
533
|
+
if (!existsSync2(absPath)) {
|
|
534
|
+
mkdirSync2(dirname4(absPath), { recursive: true });
|
|
535
|
+
writeFileSync2(absPath, action.content + "\n", "utf-8");
|
|
536
|
+
written.push(action.relativePath);
|
|
537
|
+
} else {
|
|
531
538
|
const existing = readFileSync4(absPath, "utf-8");
|
|
532
|
-
if (
|
|
533
|
-
|
|
534
|
-
written.push(action.relativePath);
|
|
535
|
-
} else if (hasAiOpsSection(existing)) {
|
|
536
|
-
const sectionContent = wrapWithSection(stripManagedHeader(action.content), meta);
|
|
537
|
-
const updated = replaceAiOpsSection(existing, sectionContent);
|
|
539
|
+
if (hasAiOpsSection(existing)) {
|
|
540
|
+
const updated = replaceAiOpsSection(existing, action.content);
|
|
538
541
|
writeFileSync2(absPath, updated, "utf-8");
|
|
539
|
-
|
|
542
|
+
const stripped = stripAiOpsSection(existing);
|
|
543
|
+
(stripped.trim().length > 0 ? appended : written).push(action.relativePath);
|
|
544
|
+
} else if (hasLegacyHeader(existing)) {
|
|
545
|
+
writeFileSync2(absPath, action.content + "\n", "utf-8");
|
|
546
|
+
written.push(action.relativePath);
|
|
540
547
|
} else {
|
|
541
|
-
const
|
|
542
|
-
const updated = existing.trimEnd() + "\n\n" + sectionContent + "\n";
|
|
548
|
+
const updated = existing.trimEnd() + "\n\n" + action.content + "\n";
|
|
543
549
|
writeFileSync2(absPath, updated, "utf-8");
|
|
544
550
|
appended.push(action.relativePath);
|
|
545
551
|
}
|
|
546
|
-
} else {
|
|
547
|
-
mkdirSync2(dirname3(absPath), { recursive: true });
|
|
548
|
-
writeFileSync2(absPath, action.content, "utf-8");
|
|
549
|
-
written.push(action.relativePath);
|
|
550
552
|
}
|
|
551
553
|
}
|
|
552
554
|
return { written, appended, skipped };
|
|
@@ -554,8 +556,40 @@ var installFiles = (basePath, actions, meta) => {
|
|
|
554
556
|
|
|
555
557
|
// src/lib/gemini-settings.ts
|
|
556
558
|
import * as p from "@clack/prompts";
|
|
557
|
-
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
|
|
559
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync5, rmSync, writeFileSync as writeFileSync3 } from "fs";
|
|
558
560
|
import { join as join7 } from "path";
|
|
561
|
+
|
|
562
|
+
// src/lib/deep-merge.util.ts
|
|
563
|
+
var deepMerge = (base, patch) => {
|
|
564
|
+
const result = { ...base };
|
|
565
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
566
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value) && typeof result[key] === "object" && result[key] !== null && !Array.isArray(result[key])) {
|
|
567
|
+
result[key] = deepMerge(result[key], value);
|
|
568
|
+
} else {
|
|
569
|
+
result[key] = value;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
return result;
|
|
573
|
+
};
|
|
574
|
+
var deepRemoveKeys = (base, patch) => {
|
|
575
|
+
const result = { ...base };
|
|
576
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
577
|
+
if (!(key in result)) continue;
|
|
578
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value) && typeof result[key] === "object" && result[key] !== null && !Array.isArray(result[key])) {
|
|
579
|
+
const nested = deepRemoveKeys(result[key], value);
|
|
580
|
+
if (Object.keys(nested).length === 0) {
|
|
581
|
+
delete result[key];
|
|
582
|
+
} else {
|
|
583
|
+
result[key] = nested;
|
|
584
|
+
}
|
|
585
|
+
} else {
|
|
586
|
+
delete result[key];
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
return result;
|
|
590
|
+
};
|
|
591
|
+
|
|
592
|
+
// src/lib/gemini-settings.ts
|
|
559
593
|
var SETTING_GROUPS = [
|
|
560
594
|
{
|
|
561
595
|
value: "ui",
|
|
@@ -582,17 +616,6 @@ var SETTING_GROUPS = [
|
|
|
582
616
|
patch: { experimental: { jitContext: true, plan: true } }
|
|
583
617
|
}
|
|
584
618
|
];
|
|
585
|
-
var deepMerge = (base, patch) => {
|
|
586
|
-
const result = { ...base };
|
|
587
|
-
for (const [key, value] of Object.entries(patch)) {
|
|
588
|
-
if (value !== null && typeof value === "object" && !Array.isArray(value) && typeof result[key] === "object" && result[key] !== null) {
|
|
589
|
-
result[key] = deepMerge(result[key], value);
|
|
590
|
-
} else {
|
|
591
|
-
result[key] = value;
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
return result;
|
|
595
|
-
};
|
|
596
619
|
var promptGeminiSettings = async () => {
|
|
597
620
|
const wantSettings = await p.confirm({
|
|
598
621
|
message: "Gemini CLI \uC124\uC815 \uD30C\uC77C(.gemini/settings.json)\uC744 \uC124\uCE58\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
|
|
@@ -632,10 +655,33 @@ var installGeminiSettings = (basePath, selectedValues) => {
|
|
|
632
655
|
mkdirSync3(settingsDir, { recursive: true });
|
|
633
656
|
writeFileSync3(settingsPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
634
657
|
};
|
|
658
|
+
var uninstallGeminiSettings = (basePath, selectedValues) => {
|
|
659
|
+
const settingsPath = join7(basePath, ".gemini", "settings.json");
|
|
660
|
+
if (!existsSync3(settingsPath)) return "notFound";
|
|
661
|
+
let existing = {};
|
|
662
|
+
try {
|
|
663
|
+
existing = JSON.parse(readFileSync5(settingsPath, "utf-8"));
|
|
664
|
+
} catch {
|
|
665
|
+
rmSync(settingsPath, { force: true });
|
|
666
|
+
return "deleted";
|
|
667
|
+
}
|
|
668
|
+
let result = existing;
|
|
669
|
+
for (const val of selectedValues) {
|
|
670
|
+
const group = SETTING_GROUPS.find((g) => g.value === val);
|
|
671
|
+
if (!group) continue;
|
|
672
|
+
result = deepRemoveKeys(result, group.patch);
|
|
673
|
+
}
|
|
674
|
+
if (Object.keys(result).length === 0) {
|
|
675
|
+
rmSync(settingsPath, { force: true });
|
|
676
|
+
return "deleted";
|
|
677
|
+
}
|
|
678
|
+
writeFileSync3(settingsPath, JSON.stringify(result, null, 2) + "\n", "utf-8");
|
|
679
|
+
return "cleaned";
|
|
680
|
+
};
|
|
635
681
|
|
|
636
682
|
// src/lib/claude-settings.ts
|
|
637
683
|
import * as p2 from "@clack/prompts";
|
|
638
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
684
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync6, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
639
685
|
import { join as join8 } from "path";
|
|
640
686
|
var SETTING_GROUPS2 = [
|
|
641
687
|
{
|
|
@@ -651,17 +697,6 @@ var SETTING_GROUPS2 = [
|
|
|
651
697
|
patch: { plansDirectory: "./.claude/plans" }
|
|
652
698
|
}
|
|
653
699
|
];
|
|
654
|
-
var deepMerge2 = (base, patch) => {
|
|
655
|
-
const result = { ...base };
|
|
656
|
-
for (const [key, value] of Object.entries(patch)) {
|
|
657
|
-
if (value !== null && typeof value === "object" && !Array.isArray(value) && typeof result[key] === "object" && result[key] !== null) {
|
|
658
|
-
result[key] = deepMerge2(result[key], value);
|
|
659
|
-
} else {
|
|
660
|
-
result[key] = value;
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
return result;
|
|
664
|
-
};
|
|
665
700
|
var promptClaudeSettings = async () => {
|
|
666
701
|
const wantSettings = await p2.confirm({
|
|
667
702
|
message: "Claude Code \uC124\uC815 \uD30C\uC77C(.claude/settings.local.json)\uC744 \uC124\uCE58\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
|
|
@@ -696,11 +731,131 @@ var installClaudeSettings = (basePath, selectedValues) => {
|
|
|
696
731
|
for (const val of selectedValues) {
|
|
697
732
|
const group = SETTING_GROUPS2.find((g) => g.value === val);
|
|
698
733
|
if (!group) continue;
|
|
699
|
-
merged =
|
|
734
|
+
merged = deepMerge(merged, group.patch);
|
|
700
735
|
}
|
|
701
736
|
mkdirSync4(settingsDir, { recursive: true });
|
|
702
737
|
writeFileSync4(settingsPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
703
738
|
};
|
|
739
|
+
var uninstallClaudeSettings = (basePath, selectedValues) => {
|
|
740
|
+
const settingsPath = join8(basePath, ".claude", "settings.local.json");
|
|
741
|
+
if (!existsSync4(settingsPath)) return "notFound";
|
|
742
|
+
let existing = {};
|
|
743
|
+
try {
|
|
744
|
+
existing = JSON.parse(readFileSync6(settingsPath, "utf-8"));
|
|
745
|
+
} catch {
|
|
746
|
+
rmSync2(settingsPath, { force: true });
|
|
747
|
+
return "deleted";
|
|
748
|
+
}
|
|
749
|
+
let result = existing;
|
|
750
|
+
for (const val of selectedValues) {
|
|
751
|
+
const group = SETTING_GROUPS2.find((g) => g.value === val);
|
|
752
|
+
if (!group) continue;
|
|
753
|
+
result = deepRemoveKeys(result, group.patch);
|
|
754
|
+
}
|
|
755
|
+
if (Object.keys(result).length === 0) {
|
|
756
|
+
rmSync2(settingsPath, { force: true });
|
|
757
|
+
return "deleted";
|
|
758
|
+
}
|
|
759
|
+
writeFileSync4(settingsPath, JSON.stringify(result, null, 2) + "\n", "utf-8");
|
|
760
|
+
return "cleaned";
|
|
761
|
+
};
|
|
762
|
+
|
|
763
|
+
// src/lib/prettier-ignore.ts
|
|
764
|
+
import * as p3 from "@clack/prompts";
|
|
765
|
+
import { existsSync as existsSync5, readFileSync as readFileSync7, rmSync as rmSync3, writeFileSync as writeFileSync5 } from "fs";
|
|
766
|
+
import { join as join9 } from "path";
|
|
767
|
+
var PRETTIER_IGNORE_CONTENT = `# CLAUDE
|
|
768
|
+
.claude/rules/
|
|
769
|
+
**/CLAUDE.md
|
|
770
|
+
|
|
771
|
+
# GEMINI
|
|
772
|
+
**/GEMINI.md
|
|
773
|
+
|
|
774
|
+
# CODEX
|
|
775
|
+
**/AGENTS.md
|
|
776
|
+
**/AGENTS.override.md
|
|
777
|
+
|
|
778
|
+
.ai-ops-manifest.json`;
|
|
779
|
+
var SECTION_START2 = "# ai-ops:start";
|
|
780
|
+
var SECTION_END2 = "# ai-ops:end";
|
|
781
|
+
var wrapSection = (content) => `${SECTION_START2}
|
|
782
|
+
${content}
|
|
783
|
+
${SECTION_END2}`;
|
|
784
|
+
var hasAiOpsSection2 = (content) => content.includes(SECTION_START2) && content.includes(SECTION_END2);
|
|
785
|
+
var replaceSection = (content, newContent) => {
|
|
786
|
+
const lines = content.split("\n");
|
|
787
|
+
const result = [];
|
|
788
|
+
let inside = false;
|
|
789
|
+
let replaced = false;
|
|
790
|
+
for (const line of lines) {
|
|
791
|
+
if (line.trim() === SECTION_START2) {
|
|
792
|
+
inside = true;
|
|
793
|
+
result.push(wrapSection(newContent));
|
|
794
|
+
replaced = true;
|
|
795
|
+
continue;
|
|
796
|
+
}
|
|
797
|
+
if (line.trim() === SECTION_END2) {
|
|
798
|
+
inside = false;
|
|
799
|
+
continue;
|
|
800
|
+
}
|
|
801
|
+
if (!inside) result.push(line);
|
|
802
|
+
}
|
|
803
|
+
if (!replaced) result.push(wrapSection(newContent));
|
|
804
|
+
return result.join("\n");
|
|
805
|
+
};
|
|
806
|
+
var stripAiOpsSection2 = (content) => {
|
|
807
|
+
const lines = content.split("\n");
|
|
808
|
+
const result = [];
|
|
809
|
+
let inside = false;
|
|
810
|
+
for (const line of lines) {
|
|
811
|
+
if (line.trim() === SECTION_START2) {
|
|
812
|
+
inside = true;
|
|
813
|
+
continue;
|
|
814
|
+
}
|
|
815
|
+
if (line.trim() === SECTION_END2) {
|
|
816
|
+
inside = false;
|
|
817
|
+
continue;
|
|
818
|
+
}
|
|
819
|
+
if (!inside) result.push(line);
|
|
820
|
+
}
|
|
821
|
+
return result.join("\n");
|
|
822
|
+
};
|
|
823
|
+
var promptPrettierIgnore = async () => {
|
|
824
|
+
const want = await p3.confirm({
|
|
825
|
+
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)",
|
|
826
|
+
initialValue: false
|
|
827
|
+
});
|
|
828
|
+
if (p3.isCancel(want)) return false;
|
|
829
|
+
return want;
|
|
830
|
+
};
|
|
831
|
+
var installPrettierIgnore = (basePath) => {
|
|
832
|
+
const filePath = join9(basePath, ".prettierignore");
|
|
833
|
+
const section = wrapSection(PRETTIER_IGNORE_CONTENT);
|
|
834
|
+
if (!existsSync5(filePath)) {
|
|
835
|
+
writeFileSync5(filePath, section + "\n", "utf-8");
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
const existing = readFileSync7(filePath, "utf-8");
|
|
839
|
+
if (hasAiOpsSection2(existing)) {
|
|
840
|
+
writeFileSync5(filePath, replaceSection(existing, PRETTIER_IGNORE_CONTENT), "utf-8");
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
const separator = existing.endsWith("\n") ? "\n" : "\n\n";
|
|
844
|
+
writeFileSync5(filePath, existing + separator + section + "\n", "utf-8");
|
|
845
|
+
};
|
|
846
|
+
var uninstallPrettierIgnore = (basePath) => {
|
|
847
|
+
const filePath = join9(basePath, ".prettierignore");
|
|
848
|
+
if (!existsSync5(filePath)) return "notFound";
|
|
849
|
+
const existing = readFileSync7(filePath, "utf-8");
|
|
850
|
+
if (!hasAiOpsSection2(existing)) return "notFound";
|
|
851
|
+
const stripped = stripAiOpsSection2(existing).trim();
|
|
852
|
+
if (stripped.length === 0) {
|
|
853
|
+
rmSync3(filePath, { force: true });
|
|
854
|
+
return "deleted";
|
|
855
|
+
}
|
|
856
|
+
writeFileSync5(filePath, stripped + "\n", "utf-8");
|
|
857
|
+
return "cleaned";
|
|
858
|
+
};
|
|
704
859
|
|
|
705
860
|
// src/commands/init.ts
|
|
706
861
|
var TOOL_OPTIONS = [
|
|
@@ -717,7 +872,7 @@ var deduplicateRules = (rules) => {
|
|
|
717
872
|
});
|
|
718
873
|
};
|
|
719
874
|
var selectPresetAndFineTune = async (workspaceName, presets, allRules) => {
|
|
720
|
-
const preset = await
|
|
875
|
+
const preset = await p4.select({
|
|
721
876
|
message: `[${workspaceName}] \uD504\uB9AC\uC14B\uC744 \uC120\uD0DD\uD558\uC138\uC694`,
|
|
722
877
|
options: presets.map((pr) => ({
|
|
723
878
|
value: pr,
|
|
@@ -725,25 +880,25 @@ var selectPresetAndFineTune = async (workspaceName, presets, allRules) => {
|
|
|
725
880
|
hint: pr.description
|
|
726
881
|
}))
|
|
727
882
|
});
|
|
728
|
-
if (
|
|
883
|
+
if (p4.isCancel(preset)) return null;
|
|
729
884
|
const presetRuleGroups = resolvePresetRuleGroups(preset, allRules);
|
|
730
885
|
const globalGroups = presetRuleGroups.filter((group) => group.rules.every(isGlobalRule));
|
|
731
886
|
const domainGroups = presetRuleGroups.filter((group) => !group.rules.every(isGlobalRule));
|
|
732
887
|
const globalGroupIds = globalGroups.map((group) => group.id);
|
|
733
888
|
const globalRules = globalGroupIds.length > 0 ? resolvePresetRules({ ...preset, rules: globalGroupIds }, allRules) : [];
|
|
734
889
|
if (globalRules.length > 0) {
|
|
735
|
-
|
|
890
|
+
p4.note(globalRules.map((r) => ` \u2713 ${r.id}`).join("\n"), `[${workspaceName}] \uAE30\uBCF8 \uADDC\uCE59 (\uC7A0\uAE08)`);
|
|
736
891
|
}
|
|
737
892
|
if (domainGroups.length === 0) {
|
|
738
893
|
return { workspace: workspaceName, preset, finalRules: resolvePresetRules(preset, allRules) };
|
|
739
894
|
}
|
|
740
|
-
const selectedDomain = await
|
|
895
|
+
const selectedDomain = await p4.multiselect({
|
|
741
896
|
message: `[${workspaceName}] \uB3C4\uBA54\uC778 \uADDC\uCE59 \uC120\uD0DD (\uD574\uC81C\uD558\uC5EC \uC81C\uC678)`,
|
|
742
897
|
options: domainGroups.map((group) => ({ value: group.id, label: group.id })),
|
|
743
898
|
initialValues: domainGroups.map((group) => group.id),
|
|
744
899
|
required: false
|
|
745
900
|
});
|
|
746
|
-
if (
|
|
901
|
+
if (p4.isCancel(selectedDomain)) return null;
|
|
747
902
|
const selectedLogicalRuleIds = [...globalGroupIds, ...selectedDomain];
|
|
748
903
|
return {
|
|
749
904
|
workspace: workspaceName,
|
|
@@ -759,8 +914,8 @@ var installHierarchicalMonorepo = (toolId, mappings, basePath, meta) => {
|
|
|
759
914
|
const { global } = partitionRules(allRules);
|
|
760
915
|
if (global.length > 0) {
|
|
761
916
|
const rootAction = {
|
|
762
|
-
relativePath:
|
|
763
|
-
content:
|
|
917
|
+
relativePath: join10(config.dir, config.rootFileName),
|
|
918
|
+
content: wrapWithSection(renderRulesToMarkdown(global), meta)
|
|
764
919
|
};
|
|
765
920
|
const r = installFiles(basePath, [rootAction], meta);
|
|
766
921
|
written.push(...r.written);
|
|
@@ -770,8 +925,8 @@ var installHierarchicalMonorepo = (toolId, mappings, basePath, meta) => {
|
|
|
770
925
|
const { domain } = partitionRules(mapping.finalRules);
|
|
771
926
|
if (domain.length === 0) continue;
|
|
772
927
|
const domainAction = {
|
|
773
|
-
relativePath:
|
|
774
|
-
content:
|
|
928
|
+
relativePath: join10(mapping.workspace, config.domainFileName),
|
|
929
|
+
content: wrapWithSection(renderRulesToMarkdown(domain), meta)
|
|
775
930
|
};
|
|
776
931
|
const r = installFiles(basePath, [domainAction], meta);
|
|
777
932
|
written.push(...r.written);
|
|
@@ -793,22 +948,22 @@ var installClaudeCodeMonorepo = (mappings, basePath, meta) => {
|
|
|
793
948
|
var initCommand = async () => {
|
|
794
949
|
const basePath = resolveBasePath();
|
|
795
950
|
const rulesDir = resolveRulesDir();
|
|
796
|
-
|
|
797
|
-
const selectedTools = await
|
|
951
|
+
p4.intro("ai-ops init");
|
|
952
|
+
const selectedTools = await p4.multiselect({
|
|
798
953
|
message: "AI \uB3C4\uAD6C\uB97C \uC120\uD0DD\uD558\uC138\uC694",
|
|
799
954
|
options: TOOL_OPTIONS,
|
|
800
955
|
required: true
|
|
801
956
|
});
|
|
802
|
-
if (
|
|
803
|
-
|
|
957
|
+
if (p4.isCancel(selectedTools)) {
|
|
958
|
+
p4.cancel("\uCDE8\uC18C\uB428");
|
|
804
959
|
process.exit(0);
|
|
805
960
|
}
|
|
806
|
-
const isMonorepo = await
|
|
961
|
+
const isMonorepo = await p4.confirm({
|
|
807
962
|
message: "\uBAA8\uB178\uB808\uD3EC \uD504\uB85C\uC81D\uD2B8\uC785\uB2C8\uAE4C?",
|
|
808
963
|
initialValue: false
|
|
809
964
|
});
|
|
810
|
-
if (
|
|
811
|
-
|
|
965
|
+
if (p4.isCancel(isMonorepo)) {
|
|
966
|
+
p4.cancel("\uCDE8\uC18C\uB428");
|
|
812
967
|
process.exit(0);
|
|
813
968
|
}
|
|
814
969
|
const allRules = loadAllRules(rulesDir);
|
|
@@ -818,25 +973,25 @@ var initCommand = async () => {
|
|
|
818
973
|
if (!isMonorepo) {
|
|
819
974
|
const mapping = await selectPresetAndFineTune(".", presets, allRules);
|
|
820
975
|
if (!mapping) {
|
|
821
|
-
|
|
976
|
+
p4.cancel("\uCDE8\uC18C\uB428");
|
|
822
977
|
process.exit(0);
|
|
823
978
|
}
|
|
824
979
|
mappings.push(mapping);
|
|
825
980
|
} else {
|
|
826
981
|
const candidates = listWorkspaceCandidates(basePath);
|
|
827
|
-
const selectedWorkspaces = await
|
|
982
|
+
const selectedWorkspaces = await p4.multiselect({
|
|
828
983
|
message: "\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4\uB97C \uC120\uD0DD\uD558\uC138\uC694",
|
|
829
984
|
options: candidates.map((c) => ({ value: c, label: c })),
|
|
830
985
|
required: true
|
|
831
986
|
});
|
|
832
|
-
if (
|
|
833
|
-
|
|
987
|
+
if (p4.isCancel(selectedWorkspaces)) {
|
|
988
|
+
p4.cancel("\uCDE8\uC18C\uB428");
|
|
834
989
|
process.exit(0);
|
|
835
990
|
}
|
|
836
991
|
for (const ws of selectedWorkspaces) {
|
|
837
992
|
const mapping = await selectPresetAndFineTune(ws, presets, allRules);
|
|
838
993
|
if (!mapping) {
|
|
839
|
-
|
|
994
|
+
p4.cancel("\uCDE8\uC18C\uB428");
|
|
840
995
|
process.exit(0);
|
|
841
996
|
}
|
|
842
997
|
mappings.push(mapping);
|
|
@@ -844,7 +999,8 @@ var initCommand = async () => {
|
|
|
844
999
|
}
|
|
845
1000
|
const geminiSettingValues = selectedTools.includes("gemini") ? await promptGeminiSettings() : null;
|
|
846
1001
|
const claudeSettingValues = selectedTools.includes("claude-code") ? await promptClaudeSettings() : null;
|
|
847
|
-
const
|
|
1002
|
+
const wantPrettierIgnore = await promptPrettierIgnore();
|
|
1003
|
+
const s = p4.spinner();
|
|
848
1004
|
s.start("\uADDC\uCE59 \uC124\uCE58 \uC911...");
|
|
849
1005
|
const meta = { sourceHash, generatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
850
1006
|
const allInstalledFiles = [];
|
|
@@ -870,11 +1026,12 @@ var initCommand = async () => {
|
|
|
870
1026
|
}
|
|
871
1027
|
if (geminiSettingValues && geminiSettingValues.length > 0) {
|
|
872
1028
|
installGeminiSettings(basePath, geminiSettingValues);
|
|
873
|
-
allInstalledFiles.push(".gemini/settings.json");
|
|
874
1029
|
}
|
|
875
1030
|
if (claudeSettingValues && claudeSettingValues.length > 0) {
|
|
876
1031
|
installClaudeSettings(basePath, claudeSettingValues);
|
|
877
|
-
|
|
1032
|
+
}
|
|
1033
|
+
if (wantPrettierIgnore) {
|
|
1034
|
+
installPrettierIgnore(basePath);
|
|
878
1035
|
}
|
|
879
1036
|
s.stop("\uADDC\uCE59 \uC124\uCE58 \uC644\uB8CC");
|
|
880
1037
|
const allInstalledRuleIds = deduplicateRules(mappings.flatMap((m) => m.finalRules)).map((r) => r.id);
|
|
@@ -889,46 +1046,50 @@ var initCommand = async () => {
|
|
|
889
1046
|
installedRules: allInstalledRuleIds,
|
|
890
1047
|
installedFiles: allInstalledFiles,
|
|
891
1048
|
appendedFiles: allAppended,
|
|
892
|
-
settings: claudeSettingValues || geminiSettingValues ? {
|
|
1049
|
+
settings: claudeSettingValues || geminiSettingValues || wantPrettierIgnore ? {
|
|
893
1050
|
claude: claudeSettingValues ? [...claudeSettingValues] : void 0,
|
|
894
|
-
gemini: geminiSettingValues ? [...geminiSettingValues] : void 0
|
|
1051
|
+
gemini: geminiSettingValues ? [...geminiSettingValues] : void 0,
|
|
1052
|
+
prettierignore: wantPrettierIgnore || void 0
|
|
895
1053
|
} : void 0,
|
|
1054
|
+
cliVersion: getCliVersion(),
|
|
896
1055
|
sourceHash
|
|
897
1056
|
});
|
|
898
1057
|
writeManifest(resolveManifestPath(basePath), manifest);
|
|
899
1058
|
if (allAppended.length > 0) {
|
|
900
|
-
|
|
1059
|
+
p4.log.info(`\uAE30\uC874 \uD30C\uC77C\uC5D0 \uC139\uC158 \uCD94\uAC00\uB428 (\uB0B4\uC6A9 \uBCF4\uC874):
|
|
901
1060
|
${allAppended.map((f) => ` ${f}`).join("\n")}`);
|
|
902
1061
|
}
|
|
903
|
-
|
|
904
|
-
|
|
1062
|
+
p4.log.success(`\uC124\uCE58\uB41C \uADDC\uCE59: ${allInstalledRuleIds.length}\uAC1C`);
|
|
1063
|
+
p4.outro("ai-ops init \uC644\uB8CC");
|
|
905
1064
|
};
|
|
906
1065
|
|
|
907
1066
|
// src/commands/update.ts
|
|
908
|
-
import * as
|
|
909
|
-
import { join as
|
|
1067
|
+
import * as p5 from "@clack/prompts";
|
|
1068
|
+
import { join as join11 } from "path";
|
|
910
1069
|
var updateCommand = async (opts) => {
|
|
911
1070
|
const basePath = resolveBasePath();
|
|
912
1071
|
const manifestPath = resolveManifestPath(basePath);
|
|
913
|
-
|
|
1072
|
+
p5.intro("ai-ops update");
|
|
914
1073
|
const manifest = readManifest(manifestPath);
|
|
915
1074
|
if (!manifest) {
|
|
916
|
-
|
|
1075
|
+
p5.log.error("manifest\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 ai-ops init\uC744 \uC2E4\uD589\uD558\uC138\uC694.");
|
|
917
1076
|
process.exit(1);
|
|
918
1077
|
}
|
|
919
1078
|
const rulesDir = resolveRulesDir();
|
|
920
1079
|
const sourceHash = computeSourceHash(rulesDir);
|
|
1080
|
+
const cliVersion = getCliVersion();
|
|
921
1081
|
const diffResult = computeDiff({
|
|
922
1082
|
previous: manifest,
|
|
923
1083
|
currentRules: manifest.installed_rules,
|
|
924
|
-
currentSourceHash: sourceHash
|
|
1084
|
+
currentSourceHash: sourceHash,
|
|
1085
|
+
currentCliVersion: cliVersion
|
|
925
1086
|
});
|
|
926
1087
|
if (diffResult.status === "up-to-date" && !opts.force) {
|
|
927
|
-
|
|
928
|
-
|
|
1088
|
+
p5.log.info("\uBCC0\uACBD \uC0AC\uD56D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
1089
|
+
p5.outro("ai-ops update \uC644\uB8CC");
|
|
929
1090
|
return;
|
|
930
1091
|
}
|
|
931
|
-
const s =
|
|
1092
|
+
const s = p5.spinner();
|
|
932
1093
|
s.start("\uADDC\uCE59 \uAC31\uC2E0 \uC911...");
|
|
933
1094
|
const allRules = loadAllRules(rulesDir);
|
|
934
1095
|
const meta = { sourceHash, generatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
@@ -957,8 +1118,8 @@ var updateCommand = async (opts) => {
|
|
|
957
1118
|
const { global } = partitionRules(allRulesToInstall);
|
|
958
1119
|
if (global.length > 0) {
|
|
959
1120
|
const rootAction = {
|
|
960
|
-
relativePath:
|
|
961
|
-
content:
|
|
1121
|
+
relativePath: join11(config.dir, config.rootFileName),
|
|
1122
|
+
content: wrapWithSection(renderRulesToMarkdown(global), meta)
|
|
962
1123
|
};
|
|
963
1124
|
const r = installFiles(basePath, [rootAction], meta);
|
|
964
1125
|
allInstalledFiles.push(...r.written);
|
|
@@ -970,8 +1131,8 @@ var updateCommand = async (opts) => {
|
|
|
970
1131
|
const { domain } = partitionRules(wsRules);
|
|
971
1132
|
if (domain.length === 0) continue;
|
|
972
1133
|
const domainAction = {
|
|
973
|
-
relativePath:
|
|
974
|
-
content:
|
|
1134
|
+
relativePath: join11(ws, config.domainFileName),
|
|
1135
|
+
content: wrapWithSection(renderRulesToMarkdown(domain), meta)
|
|
975
1136
|
};
|
|
976
1137
|
const r = installFiles(basePath, [domainAction], meta);
|
|
977
1138
|
allInstalledFiles.push(...r.written);
|
|
@@ -997,6 +1158,9 @@ var updateCommand = async (opts) => {
|
|
|
997
1158
|
if (manifest.settings?.gemini) {
|
|
998
1159
|
installGeminiSettings(basePath, manifest.settings.gemini);
|
|
999
1160
|
}
|
|
1161
|
+
if (manifest.settings?.prettierignore) {
|
|
1162
|
+
installPrettierIgnore(basePath);
|
|
1163
|
+
}
|
|
1000
1164
|
const newManifest = buildManifest({
|
|
1001
1165
|
tools: manifest.tools,
|
|
1002
1166
|
scope: manifest.scope,
|
|
@@ -1007,23 +1171,25 @@ var updateCommand = async (opts) => {
|
|
|
1007
1171
|
appendedFiles: allAppended.length > 0 ? allAppended : manifest.appended_files,
|
|
1008
1172
|
settings: manifest.settings ? {
|
|
1009
1173
|
claude: manifest.settings.claude,
|
|
1010
|
-
gemini: manifest.settings.gemini
|
|
1174
|
+
gemini: manifest.settings.gemini,
|
|
1175
|
+
prettierignore: manifest.settings.prettierignore
|
|
1011
1176
|
} : void 0,
|
|
1177
|
+
cliVersion,
|
|
1012
1178
|
sourceHash
|
|
1013
1179
|
});
|
|
1014
1180
|
writeManifest(manifestPath, newManifest);
|
|
1015
1181
|
s.stop("\uADDC\uCE59 \uAC31\uC2E0 \uC644\uB8CC");
|
|
1016
|
-
|
|
1182
|
+
p5.outro("ai-ops update \uC644\uB8CC");
|
|
1017
1183
|
};
|
|
1018
1184
|
|
|
1019
1185
|
// src/commands/diff.ts
|
|
1020
|
-
import * as
|
|
1186
|
+
import * as p6 from "@clack/prompts";
|
|
1021
1187
|
var diffCommand = async () => {
|
|
1022
1188
|
const basePath = resolveBasePath();
|
|
1023
|
-
|
|
1189
|
+
p6.intro("ai-ops diff");
|
|
1024
1190
|
const manifest = readManifest(resolveManifestPath(basePath));
|
|
1025
1191
|
if (!manifest) {
|
|
1026
|
-
|
|
1192
|
+
p6.log.error("manifest\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 ai-ops init\uC744 \uC2E4\uD589\uD558\uC138\uC694.");
|
|
1027
1193
|
process.exit(1);
|
|
1028
1194
|
}
|
|
1029
1195
|
const sourceHash = computeSourceHash(resolveRulesDir());
|
|
@@ -1033,28 +1199,28 @@ var diffCommand = async () => {
|
|
|
1033
1199
|
currentSourceHash: sourceHash
|
|
1034
1200
|
});
|
|
1035
1201
|
if (result.status === "up-to-date") {
|
|
1036
|
-
|
|
1202
|
+
p6.log.success("\uBCC0\uACBD \uC0AC\uD56D \uC5C6\uC74C. \uCD5C\uC2E0 \uC0C1\uD0DC\uC785\uB2C8\uB2E4.");
|
|
1037
1203
|
} else {
|
|
1038
1204
|
if (result.sourceChanged) {
|
|
1039
|
-
|
|
1205
|
+
p6.log.warn(`\uC18C\uC2A4 \uBCC0\uACBD \uAC10\uC9C0: ${manifest.sourceHash} \u2192 ${sourceHash}`);
|
|
1040
1206
|
}
|
|
1041
1207
|
if (result.added.length > 0) {
|
|
1042
|
-
|
|
1208
|
+
p6.log.info(`\uCD94\uAC00\uB41C \uADDC\uCE59: ${result.added.join(", ")}`);
|
|
1043
1209
|
}
|
|
1044
1210
|
if (result.removed.length > 0) {
|
|
1045
|
-
|
|
1211
|
+
p6.log.info(`\uC81C\uAC70\uB41C \uADDC\uCE59: ${result.removed.join(", ")}`);
|
|
1046
1212
|
}
|
|
1047
1213
|
}
|
|
1048
|
-
|
|
1214
|
+
p6.outro("ai-ops diff \uC644\uB8CC");
|
|
1049
1215
|
};
|
|
1050
1216
|
|
|
1051
1217
|
// src/commands/uninstall.ts
|
|
1052
|
-
import * as
|
|
1053
|
-
import { rmSync as
|
|
1218
|
+
import * as p7 from "@clack/prompts";
|
|
1219
|
+
import { rmSync as rmSync5 } from "fs";
|
|
1054
1220
|
|
|
1055
1221
|
// src/lib/uninstall.ts
|
|
1056
|
-
import { existsSync as
|
|
1057
|
-
import { resolve as resolve6, dirname as
|
|
1222
|
+
import { existsSync as existsSync6, readFileSync as readFileSync8, rmSync as rmSync4, readdirSync as readdirSync4, writeFileSync as writeFileSync6 } from "fs";
|
|
1223
|
+
import { resolve as resolve6, dirname as dirname5 } from "path";
|
|
1058
1224
|
var removeFiles = (basePath, relativePaths) => {
|
|
1059
1225
|
const deleted = [];
|
|
1060
1226
|
const cleaned = [];
|
|
@@ -1062,23 +1228,26 @@ var removeFiles = (basePath, relativePaths) => {
|
|
|
1062
1228
|
const notFound = [];
|
|
1063
1229
|
for (const rel of relativePaths) {
|
|
1064
1230
|
const absPath = resolve6(basePath, rel);
|
|
1065
|
-
if (!
|
|
1231
|
+
if (!existsSync6(absPath)) {
|
|
1066
1232
|
notFound.push(rel);
|
|
1067
1233
|
continue;
|
|
1068
1234
|
}
|
|
1069
|
-
const content =
|
|
1070
|
-
if (
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1235
|
+
const content = readFileSync8(absPath, "utf-8");
|
|
1236
|
+
if (hasAiOpsSection(content)) {
|
|
1237
|
+
const stripped = stripAiOpsSection(content);
|
|
1238
|
+
if (stripped.trim().length === 0) {
|
|
1239
|
+
rmSync4(absPath);
|
|
1240
|
+
deleted.push(rel);
|
|
1075
1241
|
} else {
|
|
1076
|
-
|
|
1242
|
+
writeFileSync6(absPath, stripped, "utf-8");
|
|
1243
|
+
cleaned.push(rel);
|
|
1077
1244
|
}
|
|
1078
|
-
|
|
1245
|
+
} else if (hasLegacyHeader(content)) {
|
|
1246
|
+
rmSync4(absPath);
|
|
1247
|
+
deleted.push(rel);
|
|
1248
|
+
} else {
|
|
1249
|
+
skipped.push(rel);
|
|
1079
1250
|
}
|
|
1080
|
-
rmSync(absPath);
|
|
1081
|
-
deleted.push(rel);
|
|
1082
1251
|
}
|
|
1083
1252
|
return { deleted, cleaned, skipped, notFound };
|
|
1084
1253
|
};
|
|
@@ -1086,11 +1255,11 @@ var cleanEmptyDirs = (basePath, dirs) => {
|
|
|
1086
1255
|
const removed = [];
|
|
1087
1256
|
for (const dir of dirs) {
|
|
1088
1257
|
const absDir = resolve6(basePath, dir);
|
|
1089
|
-
if (!
|
|
1258
|
+
if (!existsSync6(absDir)) continue;
|
|
1090
1259
|
try {
|
|
1091
1260
|
const entries = readdirSync4(absDir);
|
|
1092
1261
|
if (entries.length === 0) {
|
|
1093
|
-
|
|
1262
|
+
rmSync4(absDir, { recursive: true });
|
|
1094
1263
|
removed.push(dir);
|
|
1095
1264
|
}
|
|
1096
1265
|
} catch {
|
|
@@ -1101,7 +1270,7 @@ var cleanEmptyDirs = (basePath, dirs) => {
|
|
|
1101
1270
|
var collectManagedDirs = (relativePaths) => {
|
|
1102
1271
|
const dirs = /* @__PURE__ */ new Set();
|
|
1103
1272
|
for (const rel of relativePaths) {
|
|
1104
|
-
const dir =
|
|
1273
|
+
const dir = dirname5(rel);
|
|
1105
1274
|
if (dir !== ".") {
|
|
1106
1275
|
dirs.add(dir);
|
|
1107
1276
|
}
|
|
@@ -1110,64 +1279,85 @@ var collectManagedDirs = (relativePaths) => {
|
|
|
1110
1279
|
};
|
|
1111
1280
|
|
|
1112
1281
|
// src/commands/uninstall.ts
|
|
1282
|
+
var SETTINGS_PATHS = /* @__PURE__ */ new Set([".claude/settings.local.json", ".gemini/settings.json"]);
|
|
1113
1283
|
var uninstallCommand = async () => {
|
|
1114
1284
|
const basePath = resolveBasePath();
|
|
1115
1285
|
const manifestPath = resolveManifestPath(basePath);
|
|
1116
|
-
|
|
1286
|
+
p7.intro("ai-ops uninstall");
|
|
1117
1287
|
const manifest = readManifest(manifestPath);
|
|
1118
1288
|
if (!manifest) {
|
|
1119
|
-
|
|
1289
|
+
p7.log.error("manifest\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 ai-ops init\uC744 \uC2E4\uD589\uD558\uC138\uC694.");
|
|
1120
1290
|
process.exit(1);
|
|
1121
1291
|
}
|
|
1122
1292
|
const targetFiles = [
|
|
1123
1293
|
...manifest.installed_files ?? inferInstalledFiles(manifest),
|
|
1124
1294
|
...manifest.appended_files ?? []
|
|
1125
|
-
];
|
|
1295
|
+
].filter((f) => !SETTINGS_PATHS.has(f));
|
|
1126
1296
|
if (targetFiles.length === 0) {
|
|
1127
|
-
|
|
1128
|
-
|
|
1297
|
+
p7.log.warn("\uC0AD\uC81C\uD560 \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
1298
|
+
p7.outro("ai-ops uninstall \uC644\uB8CC");
|
|
1129
1299
|
return;
|
|
1130
1300
|
}
|
|
1131
|
-
|
|
1301
|
+
p7.log.info(`\uC0AD\uC81C \uB300\uC0C1 \uD30C\uC77C (${targetFiles.length}\uAC1C):
|
|
1132
1302
|
${targetFiles.map((f) => ` ${f}`).join("\n")}`);
|
|
1133
|
-
const confirmed = await
|
|
1303
|
+
const confirmed = await p7.confirm({
|
|
1134
1304
|
message: "\uC704 \uD30C\uC77C\uACFC manifest\uB97C \uBAA8\uB450 \uC0AD\uC81C\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
|
|
1135
1305
|
initialValue: false
|
|
1136
1306
|
});
|
|
1137
|
-
if (
|
|
1138
|
-
|
|
1307
|
+
if (p7.isCancel(confirmed) || !confirmed) {
|
|
1308
|
+
p7.cancel("\uCDE8\uC18C\uB428");
|
|
1139
1309
|
process.exit(0);
|
|
1140
1310
|
}
|
|
1311
|
+
const settingsMessages = [];
|
|
1312
|
+
if (manifest.settings?.claude) {
|
|
1313
|
+
const status = uninstallClaudeSettings(basePath, manifest.settings.claude);
|
|
1314
|
+
if (status === "deleted") settingsMessages.push("\uC0AD\uC81C: .claude/settings.local.json");
|
|
1315
|
+
else if (status === "cleaned") settingsMessages.push("ai-ops \uD0A4 \uC81C\uAC70 (\uC0AC\uC6A9\uC790 \uC124\uC815 \uBCF4\uC874): .claude/settings.local.json");
|
|
1316
|
+
}
|
|
1317
|
+
if (manifest.settings?.gemini) {
|
|
1318
|
+
const status = uninstallGeminiSettings(basePath, manifest.settings.gemini);
|
|
1319
|
+
if (status === "deleted") settingsMessages.push("\uC0AD\uC81C: .gemini/settings.json");
|
|
1320
|
+
else if (status === "cleaned") settingsMessages.push("ai-ops \uD0A4 \uC81C\uAC70 (\uC0AC\uC6A9\uC790 \uC124\uC815 \uBCF4\uC874): .gemini/settings.json");
|
|
1321
|
+
}
|
|
1322
|
+
if (manifest.settings?.prettierignore) {
|
|
1323
|
+
const status = uninstallPrettierIgnore(basePath);
|
|
1324
|
+
if (status === "deleted") settingsMessages.push("\uC0AD\uC81C: .prettierignore");
|
|
1325
|
+
else if (status === "cleaned") settingsMessages.push("ai-ops \uC139\uC158 \uC81C\uAC70 (\uC0AC\uC6A9\uC790 \uB0B4\uC6A9 \uBCF4\uC874): .prettierignore");
|
|
1326
|
+
}
|
|
1141
1327
|
const result = removeFiles(basePath, targetFiles);
|
|
1142
1328
|
const dirs = collectManagedDirs(targetFiles);
|
|
1143
1329
|
const removedDirs = cleanEmptyDirs(basePath, dirs);
|
|
1144
|
-
|
|
1330
|
+
rmSync5(manifestPath, { force: true });
|
|
1145
1331
|
if (result.deleted.length > 0) {
|
|
1146
|
-
|
|
1332
|
+
p7.log.success(`\uC0AD\uC81C \uC644\uB8CC (${result.deleted.length}\uAC1C):
|
|
1147
1333
|
${result.deleted.map((f) => ` ${f}`).join("\n")}`);
|
|
1148
1334
|
}
|
|
1149
1335
|
if (result.cleaned.length > 0) {
|
|
1150
|
-
|
|
1336
|
+
p7.log.success(
|
|
1151
1337
|
`\uC139\uC158 \uC81C\uAC70 \uC644\uB8CC (\uC0AC\uC6A9\uC790 \uB0B4\uC6A9 \uBCF4\uC874, ${result.cleaned.length}\uAC1C):
|
|
1152
1338
|
${result.cleaned.map((f) => ` ${f}`).join("\n")}`
|
|
1153
1339
|
);
|
|
1154
1340
|
}
|
|
1155
1341
|
if (result.skipped.length > 0) {
|
|
1156
|
-
|
|
1342
|
+
p7.log.warn(
|
|
1157
1343
|
`\uAC74\uB108\uB700 (non-managed \uD30C\uC77C \uBCF4\uD638, ${result.skipped.length}\uAC1C):
|
|
1158
1344
|
${result.skipped.map((f) => ` ${f}`).join("\n")}`
|
|
1159
1345
|
);
|
|
1160
1346
|
}
|
|
1161
1347
|
if (result.notFound.length > 0) {
|
|
1162
|
-
|
|
1348
|
+
p7.log.info(`\uC774\uBBF8 \uC5C6\uC74C (${result.notFound.length}\uAC1C):
|
|
1163
1349
|
${result.notFound.map((f) => ` ${f}`).join("\n")}`);
|
|
1164
1350
|
}
|
|
1165
1351
|
if (removedDirs.length > 0) {
|
|
1166
|
-
|
|
1352
|
+
p7.log.info(`\uBE48 \uB514\uB809\uD1A0\uB9AC \uC815\uB9AC (${removedDirs.length}\uAC1C):
|
|
1167
1353
|
${removedDirs.map((d) => ` ${d}`).join("\n")}`);
|
|
1168
1354
|
}
|
|
1169
|
-
|
|
1170
|
-
|
|
1355
|
+
if (settingsMessages.length > 0) {
|
|
1356
|
+
p7.log.success(`\uC124\uC815 \uD30C\uC77C \uCC98\uB9AC:
|
|
1357
|
+
${settingsMessages.map((m) => ` ${m}`).join("\n")}`);
|
|
1358
|
+
}
|
|
1359
|
+
p7.log.success(`manifest \uC0AD\uC81C: ${MANIFEST_FILENAME}`);
|
|
1360
|
+
p7.outro("ai-ops uninstall \uC644\uB8CC");
|
|
1171
1361
|
};
|
|
1172
1362
|
|
|
1173
1363
|
// src/bin/index.ts
|