ai-ops-cli 0.1.6 → 0.1.9

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 CHANGED
@@ -2,6 +2,56 @@
2
2
 
3
3
  CLI for managing AI tool rules and presets across projects.
4
4
 
5
+ ## Why this exists
6
+
7
+ `ai-ops-cli` was created to reduce configuration drift across AI coding tools in a team.
8
+
9
+ - Different tools (Claude Code, Codex, Gemini CLI) require different file locations and prompt layouts.
10
+ - Tool conventions evolve over time, so manually maintained setup files become inconsistent quickly.
11
+ - Teams need a single, repeatable way to install and maintain AI rule scaffolding.
12
+
13
+ This project uses a centralized rule source (SSOT) and scaffolds tool-native files into the current project.
14
+ For the full product background and architecture intent, see [`docs/plan.md`](../../docs/plan.md).
15
+
16
+ ### What this library provides
17
+
18
+ - Interactive installation flow for supported AI tools (`ai-ops init`)
19
+ - Managed updates based on installed manifest (`ai-ops update`)
20
+ - Drift detection against current source hash (`ai-ops diff`)
21
+ - Safe cleanup of installed managed files + manifest (`ai-ops uninstall`)
22
+ - Project-local installation and management
23
+
24
+ ### What this library does not provide
25
+
26
+ - A hosted backend or remote state service
27
+ - Rule authoring workflow inside the CLI itself
28
+ - IDE-specific plugin management
29
+
30
+ ## Supported AI tools and installation model
31
+
32
+ `ai-ops-cli` currently supports:
33
+
34
+ - Claude Code (`claude-code`)
35
+ - Codex (`codex`)
36
+ - Gemini CLI (`gemini`)
37
+
38
+ ### Tool-specific installation layout
39
+
40
+ | Tool | Single project | Monorepo | Why this layout (JIT rationale) |
41
+ | ----------- | ------------------------------------- | ----------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
42
+ | Claude Code | `.claude/rules/<rule-id>.md` per rule | Shared rules in `.claude/rules/*.md`, domain rules in `<workspace>/CLAUDE.md` | Keeps always-on rules stable while loading domain rules only for matching paths/workspaces. |
43
+ | Codex | `AGENTS.md` + `AGENTS.override.md` | Root `AGENTS.md` + `<workspace>/AGENTS.override.md` | Uses root baseline + local override so only relevant workspace context is applied at execution time. |
44
+ | Gemini CLI | `.gemini/GEMINI.md` | Root `.gemini/GEMINI.md` + `<workspace>/GEMINI.md` | Splits shared defaults and workspace-local context to reduce irrelevant prompt context. |
45
+
46
+ Gemini CLI can also install optional runtime settings to `.gemini/settings.json`.
47
+
48
+ ### Installation behavior details
49
+
50
+ - Rules are split into shared and domain categories and rendered per tool with tool-native file shapes.
51
+ - Existing managed files are replaced safely using ai-ops metadata headers.
52
+ - Existing non-managed files are preserved and receive an `ai-ops` managed section block instead of full overwrite.
53
+ - `update`, `diff`, and `uninstall` operate from the manifest to keep changes deterministic and idempotent.
54
+
5
55
  ## Install
6
56
 
7
57
  ```bash
@@ -19,6 +69,9 @@ ai-ops diff
19
69
 
20
70
  # Apply updates
21
71
  ai-ops update
72
+
73
+ # Remove installed managed files and manifest
74
+ ai-ops uninstall
22
75
  ```
23
76
 
24
77
  ## Options
@@ -30,9 +83,9 @@ Commands:
30
83
  init Initialize AI tool rules for a project
31
84
  update Update installed rules
32
85
  diff Show diff between installed and current rules
86
+ uninstall Remove installed rules and manifest
33
87
 
34
88
  Options:
35
- --scope <scope> Target scope: project (default) or global
36
89
  --force Force update even when no changes detected
37
90
  -V, --version Output the version number
38
91
  -h, --help Display help
package/dist/bin/index.js CHANGED
@@ -25,20 +25,19 @@ import {
25
25
  } from "ai-ops-compiler";
26
26
 
27
27
  // src/lib/paths.ts
28
- import { join, resolve } from "path";
29
- import { homedir } from "os";
28
+ import { join } from "path";
30
29
  import { COMPILER_DATA_DIR } from "ai-ops-compiler";
31
30
  var resolveRulesDir = () => join(COMPILER_DATA_DIR, "rules");
32
31
  var resolvePresetsPath = () => join(COMPILER_DATA_DIR, "presets.yaml");
33
- var resolveBasePath = (scope) => scope === "global" ? resolve(homedir(), ".ai-ops") : process.cwd();
32
+ var resolveBasePath = () => process.cwd();
34
33
 
35
34
  // src/lib/workspace.ts
36
35
  import { existsSync, readdirSync, statSync } from "fs";
37
- import { join as join2, resolve as resolve2 } from "path";
36
+ import { join as join2, resolve } from "path";
38
37
  var EXCLUDE_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", ".turbo", ".cache", "coverage"]);
39
38
  var isVisibleDir = (basePath, name) => {
40
39
  if (name.startsWith(".") || EXCLUDE_DIRS.has(name)) return false;
41
- return statSync(resolve2(basePath, name)).isDirectory();
40
+ return statSync(resolve(basePath, name)).isDirectory();
42
41
  };
43
42
  var PROJECT_MANIFESTS = [
44
43
  "package.json",
@@ -59,12 +58,12 @@ var listWorkspaceCandidates = (basePath) => {
59
58
  const topLevel = readdirSync(basePath).filter((name) => isVisibleDir(basePath, name));
60
59
  const candidates = [];
61
60
  for (const dir of topLevel) {
62
- const subPath = resolve2(basePath, dir);
61
+ const subPath = resolve(basePath, dir);
63
62
  if (isWorkspaceRoot(subPath)) {
64
63
  candidates.push(dir);
65
64
  } else {
66
65
  const children = readdirSync(subPath).filter((name) => isVisibleDir(subPath, name));
67
- const wsChildren = children.filter((name) => isWorkspaceRoot(resolve2(subPath, name)));
66
+ const wsChildren = children.filter((name) => isWorkspaceRoot(resolve(subPath, name)));
68
67
  if (wsChildren.length > 0) {
69
68
  for (const child of wsChildren) {
70
69
  candidates.push(join2(dir, child));
@@ -79,25 +78,43 @@ var listWorkspaceCandidates = (basePath) => {
79
78
 
80
79
  // src/lib/install.ts
81
80
  import { existsSync as existsSync2, mkdirSync, readFileSync, writeFileSync } from "fs";
82
- import { dirname, resolve as resolve3 } from "path";
83
- import { isManagedFile } from "ai-ops-compiler";
84
- var installFiles = (basePath, actions) => {
81
+ import { dirname, resolve as resolve2 } from "path";
82
+ import {
83
+ isManagedFile,
84
+ hasAiOpsSection,
85
+ wrapWithSection,
86
+ replaceAiOpsSection,
87
+ stripManagedHeader
88
+ } from "ai-ops-compiler";
89
+ var installFiles = (basePath, actions, meta) => {
85
90
  const written = [];
91
+ const appended = [];
86
92
  const skipped = [];
87
93
  for (const action of actions) {
88
- const absPath = resolve3(basePath, action.relativePath);
94
+ const absPath = resolve2(basePath, action.relativePath);
89
95
  if (existsSync2(absPath)) {
90
96
  const existing = readFileSync(absPath, "utf-8");
91
- if (!isManagedFile(existing)) {
92
- skipped.push(action.relativePath);
93
- continue;
97
+ if (isManagedFile(existing)) {
98
+ writeFileSync(absPath, action.content, "utf-8");
99
+ written.push(action.relativePath);
100
+ } else if (hasAiOpsSection(existing)) {
101
+ const sectionContent = wrapWithSection(stripManagedHeader(action.content), meta);
102
+ const updated = replaceAiOpsSection(existing, sectionContent);
103
+ writeFileSync(absPath, updated, "utf-8");
104
+ appended.push(action.relativePath);
105
+ } else {
106
+ const sectionContent = wrapWithSection(stripManagedHeader(action.content), meta);
107
+ const updated = existing.trimEnd() + "\n\n" + sectionContent + "\n";
108
+ writeFileSync(absPath, updated, "utf-8");
109
+ appended.push(action.relativePath);
94
110
  }
111
+ } else {
112
+ mkdirSync(dirname(absPath), { recursive: true });
113
+ writeFileSync(absPath, action.content, "utf-8");
114
+ written.push(action.relativePath);
95
115
  }
96
- mkdirSync(dirname(absPath), { recursive: true });
97
- writeFileSync(absPath, action.content, "utf-8");
98
- written.push(action.relativePath);
99
116
  }
100
- return { written, skipped };
117
+ return { written, appended, skipped };
101
118
  };
102
119
 
103
120
  // src/lib/gemini-settings.ts
@@ -227,6 +244,7 @@ var selectPresetAndFineTune = async (workspaceName, presets, allRules) => {
227
244
  var installHierarchicalMonorepo = (toolId, mappings, basePath, meta) => {
228
245
  const config = TOOL_OUTPUT_MAP[toolId];
229
246
  const written = [];
247
+ const appended = [];
230
248
  const allRules = deduplicateRules(mappings.flatMap((m) => m.finalRules));
231
249
  const { global } = partitionRules(allRules);
232
250
  if (global.length > 0) {
@@ -234,7 +252,9 @@ var installHierarchicalMonorepo = (toolId, mappings, basePath, meta) => {
234
252
  relativePath: join4(config.dir, config.rootFileName),
235
253
  content: wrapWithHeader(renderRulesToMarkdown(global), meta)
236
254
  };
237
- written.push(...installFiles(basePath, [rootAction]).written);
255
+ const r = installFiles(basePath, [rootAction], meta);
256
+ written.push(...r.written);
257
+ appended.push(...r.appended);
238
258
  }
239
259
  for (const mapping of mappings) {
240
260
  const { domain } = partitionRules(mapping.finalRules);
@@ -243,22 +263,25 @@ var installHierarchicalMonorepo = (toolId, mappings, basePath, meta) => {
243
263
  relativePath: join4(mapping.workspace, config.domainFileName),
244
264
  content: wrapWithHeader(renderRulesToMarkdown(domain), meta)
245
265
  };
246
- written.push(...installFiles(basePath, [domainAction]).written);
266
+ const r = installFiles(basePath, [domainAction], meta);
267
+ written.push(...r.written);
268
+ appended.push(...r.appended);
247
269
  }
248
- return written;
270
+ return { written, appended };
249
271
  };
250
272
  var installClaudeCodeMonorepo = (mappings, basePath, meta) => {
251
273
  const allRules = deduplicateRules(mappings.flatMap((m) => m.finalRules));
252
274
  const workspaceMappings = mappings.map((m) => ({
253
275
  path: m.workspace,
254
- ruleIds: m.finalRules.map((r) => r.id)
276
+ ruleIds: m.finalRules.map((r2) => r2.id)
255
277
  }));
256
278
  const renderResult = renderForTool("claude-code", allRules, workspaceMappings);
257
279
  const actions = buildInstallPlan({ toolId: "claude-code", renderResult, meta });
258
- return installFiles(basePath, actions).written;
280
+ const r = installFiles(basePath, actions, meta);
281
+ return { written: r.written, appended: r.appended };
259
282
  };
260
- var initCommand = async (opts) => {
261
- const basePath = resolveBasePath(opts.scope);
283
+ var initCommand = async () => {
284
+ const basePath = resolveBasePath();
262
285
  const rulesDir = resolveRulesDir();
263
286
  p2.intro("ai-ops init");
264
287
  const selectedTools = await p2.multiselect({
@@ -313,21 +336,25 @@ var initCommand = async (opts) => {
313
336
  const s = p2.spinner();
314
337
  s.start("\uADDC\uCE59 \uC124\uCE58 \uC911...");
315
338
  const meta = { sourceHash, generatedAt: (/* @__PURE__ */ new Date()).toISOString() };
316
- const allSkipped = [];
317
339
  const allInstalledFiles = [];
340
+ const allAppended = [];
318
341
  for (const toolId of selectedTools) {
319
342
  if (isMonorepo) {
320
343
  if (toolId === "claude-code") {
321
- allInstalledFiles.push(...installClaudeCodeMonorepo(mappings, basePath, meta));
344
+ const stats = installClaudeCodeMonorepo(mappings, basePath, meta);
345
+ allInstalledFiles.push(...stats.written);
346
+ allAppended.push(...stats.appended);
322
347
  } else {
323
- allInstalledFiles.push(...installHierarchicalMonorepo(toolId, mappings, basePath, meta));
348
+ const stats = installHierarchicalMonorepo(toolId, mappings, basePath, meta);
349
+ allInstalledFiles.push(...stats.written);
350
+ allAppended.push(...stats.appended);
324
351
  }
325
352
  } else {
326
353
  const renderResult = renderForTool(toolId, mappings[0].finalRules);
327
354
  const actions = buildInstallPlan({ toolId, renderResult, meta });
328
- const result = installFiles(basePath, actions);
329
- allSkipped.push(...result.skipped);
355
+ const result = installFiles(basePath, actions, meta);
330
356
  allInstalledFiles.push(...result.written);
357
+ allAppended.push(...result.appended);
331
358
  }
332
359
  }
333
360
  if (geminiSettingValues && geminiSettingValues.length > 0) {
@@ -341,17 +368,18 @@ var initCommand = async (opts) => {
341
368
  ) : void 0;
342
369
  const manifest = buildManifest({
343
370
  tools: selectedTools,
344
- scope: opts.scope,
371
+ scope: "project",
345
372
  preset: !isMonorepo ? mappings[0].preset.id : void 0,
346
373
  workspaces: workspacesRecord,
347
374
  installedRules: allInstalledRuleIds,
348
375
  installedFiles: allInstalledFiles,
376
+ appendedFiles: allAppended,
349
377
  sourceHash
350
378
  });
351
379
  writeManifest(resolveManifestPath(basePath), manifest);
352
- if (allSkipped.length > 0) {
353
- p2.log.warn(`\uCDA9\uB3CC(non-managed) \uD30C\uC77C \uAC74\uB108\uB700:
354
- ${allSkipped.map((f) => ` ${f}`).join("\n")}`);
380
+ if (allAppended.length > 0) {
381
+ p2.log.info(`\uAE30\uC874 \uD30C\uC77C\uC5D0 \uC139\uC158 \uCD94\uAC00\uB428 (\uB0B4\uC6A9 \uBCF4\uC874):
382
+ ${allAppended.map((f) => ` ${f}`).join("\n")}`);
355
383
  }
356
384
  p2.log.success(`\uC124\uCE58\uB41C \uADDC\uCE59: ${allInstalledRuleIds.length}\uAC1C`);
357
385
  p2.outro("ai-ops init \uC644\uB8CC");
@@ -376,7 +404,7 @@ import {
376
404
  } from "ai-ops-compiler";
377
405
  import { join as join5 } from "path";
378
406
  var updateCommand = async (opts) => {
379
- const basePath = resolveBasePath(opts.scope);
407
+ const basePath = resolveBasePath();
380
408
  const manifestPath = resolveManifestPath2(basePath);
381
409
  p3.intro("ai-ops update");
382
410
  const manifest = readManifest(manifestPath);
@@ -401,20 +429,23 @@ var updateCommand = async (opts) => {
401
429
  const allRules = loadAllRules2(rulesDir);
402
430
  const meta = { sourceHash, generatedAt: (/* @__PURE__ */ new Date()).toISOString() };
403
431
  const allInstalledFiles = [];
432
+ const allAppended = [];
404
433
  if (manifest.workspaces) {
405
434
  const workspaceEntries = Object.entries(manifest.workspaces);
406
435
  for (const toolIdStr of manifest.tools) {
407
436
  const toolId = toolIdStr;
408
437
  if (toolId === "claude-code") {
409
438
  const allInstalledRuleSet = new Set(manifest.installed_rules);
410
- const rulesToInstall = allRules.filter((r) => allInstalledRuleSet.has(r.id));
439
+ const rulesToInstall = allRules.filter((r2) => allInstalledRuleSet.has(r2.id));
411
440
  const workspaceMappings = Object.entries(manifest.workspaces).map(([path, entry]) => ({
412
441
  path,
413
442
  ruleIds: entry.rules
414
443
  }));
415
444
  const renderResult = renderForTool2("claude-code", rulesToInstall, workspaceMappings);
416
445
  const actions = buildInstallPlan2({ toolId: "claude-code", renderResult, meta });
417
- allInstalledFiles.push(...installFiles(basePath, actions).written);
446
+ const r = installFiles(basePath, actions, meta);
447
+ allInstalledFiles.push(...r.written);
448
+ allAppended.push(...r.appended);
418
449
  } else {
419
450
  const config = TOOL_OUTPUT_MAP2[toolId];
420
451
  const allInstalledRuleSet = new Set(manifest.installed_rules);
@@ -425,18 +456,22 @@ var updateCommand = async (opts) => {
425
456
  relativePath: join5(config.dir, config.rootFileName),
426
457
  content: wrapWithHeader2(renderRulesToMarkdown2(global), meta)
427
458
  };
428
- allInstalledFiles.push(...installFiles(basePath, [rootAction]).written);
459
+ const r = installFiles(basePath, [rootAction], meta);
460
+ allInstalledFiles.push(...r.written);
461
+ allAppended.push(...r.appended);
429
462
  }
430
463
  for (const [ws, entry] of workspaceEntries) {
431
464
  const wsRuleSet = new Set(entry.rules);
432
- const wsRules = allRules.filter((r) => wsRuleSet.has(r.id));
465
+ const wsRules = allRules.filter((r2) => wsRuleSet.has(r2.id));
433
466
  const { domain } = partitionRules2(wsRules);
434
467
  if (domain.length === 0) continue;
435
468
  const domainAction = {
436
469
  relativePath: join5(ws, config.domainFileName),
437
470
  content: wrapWithHeader2(renderRulesToMarkdown2(domain), meta)
438
471
  };
439
- allInstalledFiles.push(...installFiles(basePath, [domainAction]).written);
472
+ const r = installFiles(basePath, [domainAction], meta);
473
+ allInstalledFiles.push(...r.written);
474
+ allAppended.push(...r.appended);
440
475
  }
441
476
  }
442
477
  }
@@ -447,7 +482,9 @@ var updateCommand = async (opts) => {
447
482
  const toolId = toolIdStr;
448
483
  const renderResult = renderForTool2(toolId, rulesToInstall);
449
484
  const actions = buildInstallPlan2({ toolId, renderResult, meta });
450
- allInstalledFiles.push(...installFiles(basePath, actions).written);
485
+ const r = installFiles(basePath, actions, meta);
486
+ allInstalledFiles.push(...r.written);
487
+ allAppended.push(...r.appended);
451
488
  }
452
489
  }
453
490
  const newManifest = buildManifest2({
@@ -457,6 +494,7 @@ var updateCommand = async (opts) => {
457
494
  workspaces: manifest.workspaces,
458
495
  installedRules: manifest.installed_rules,
459
496
  installedFiles: allInstalledFiles.length > 0 ? allInstalledFiles : manifest.installed_files,
497
+ appendedFiles: allAppended.length > 0 ? allAppended : manifest.appended_files,
460
498
  sourceHash
461
499
  });
462
500
  writeManifest2(manifestPath, newManifest);
@@ -467,8 +505,8 @@ var updateCommand = async (opts) => {
467
505
  // src/commands/diff.ts
468
506
  import * as p4 from "@clack/prompts";
469
507
  import { readManifest as readManifest2, resolveManifestPath as resolveManifestPath3, computeSourceHash as computeSourceHash3, computeDiff as computeDiff2 } from "ai-ops-compiler";
470
- var diffCommand = async (opts) => {
471
- const basePath = resolveBasePath(opts.scope);
508
+ var diffCommand = async () => {
509
+ const basePath = resolveBasePath();
472
510
  p4.intro("ai-ops diff");
473
511
  const manifest = readManifest2(resolveManifestPath3(basePath));
474
512
  if (!manifest) {
@@ -503,33 +541,40 @@ import { rmSync as rmSync2 } from "fs";
503
541
  import { readManifest as readManifest3, resolveManifestPath as resolveManifestPath4, inferInstalledFiles, MANIFEST_FILENAME } from "ai-ops-compiler";
504
542
 
505
543
  // src/lib/uninstall.ts
506
- import { existsSync as existsSync4, readFileSync as readFileSync3, rmSync, readdirSync as readdirSync2 } from "fs";
507
- import { resolve as resolve4, dirname as dirname2 } from "path";
508
- import { isManagedFile as isManagedFile2 } from "ai-ops-compiler";
544
+ import { existsSync as existsSync4, readFileSync as readFileSync3, rmSync, readdirSync as readdirSync2, writeFileSync as writeFileSync3 } from "fs";
545
+ import { resolve as resolve3, dirname as dirname2 } from "path";
546
+ import { isManagedFile as isManagedFile2, hasAiOpsSection as hasAiOpsSection2, stripAiOpsSection } from "ai-ops-compiler";
509
547
  var removeFiles = (basePath, relativePaths) => {
510
548
  const deleted = [];
549
+ const cleaned = [];
511
550
  const skipped = [];
512
551
  const notFound = [];
513
552
  for (const rel of relativePaths) {
514
- const absPath = resolve4(basePath, rel);
553
+ const absPath = resolve3(basePath, rel);
515
554
  if (!existsSync4(absPath)) {
516
555
  notFound.push(rel);
517
556
  continue;
518
557
  }
519
558
  const content = readFileSync3(absPath, "utf-8");
520
559
  if (!isManagedFile2(content)) {
521
- skipped.push(rel);
560
+ if (hasAiOpsSection2(content)) {
561
+ const stripped = stripAiOpsSection(content);
562
+ writeFileSync3(absPath, stripped, "utf-8");
563
+ cleaned.push(rel);
564
+ } else {
565
+ skipped.push(rel);
566
+ }
522
567
  continue;
523
568
  }
524
569
  rmSync(absPath);
525
570
  deleted.push(rel);
526
571
  }
527
- return { deleted, skipped, notFound };
572
+ return { deleted, cleaned, skipped, notFound };
528
573
  };
529
574
  var cleanEmptyDirs = (basePath, dirs) => {
530
575
  const removed = [];
531
576
  for (const dir of dirs) {
532
- const absDir = resolve4(basePath, dir);
577
+ const absDir = resolve3(basePath, dir);
533
578
  if (!existsSync4(absDir)) continue;
534
579
  try {
535
580
  const entries = readdirSync2(absDir);
@@ -554,8 +599,8 @@ var collectManagedDirs = (relativePaths) => {
554
599
  };
555
600
 
556
601
  // src/commands/uninstall.ts
557
- var uninstallCommand = async (opts) => {
558
- const basePath = resolveBasePath(opts.scope);
602
+ var uninstallCommand = async () => {
603
+ const basePath = resolveBasePath();
559
604
  const manifestPath = resolveManifestPath4(basePath);
560
605
  p5.intro("ai-ops uninstall");
561
606
  const manifest = readManifest3(manifestPath);
@@ -563,7 +608,10 @@ var uninstallCommand = async (opts) => {
563
608
  p5.log.error("manifest\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 ai-ops init\uC744 \uC2E4\uD589\uD558\uC138\uC694.");
564
609
  process.exit(1);
565
610
  }
566
- const targetFiles = manifest.installed_files ?? inferInstalledFiles(manifest);
611
+ const targetFiles = [
612
+ ...manifest.installed_files ?? inferInstalledFiles(manifest),
613
+ ...manifest.appended_files ?? []
614
+ ];
567
615
  if (targetFiles.length === 0) {
568
616
  p5.log.warn("\uC0AD\uC81C\uD560 \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
569
617
  p5.outro("ai-ops uninstall \uC644\uB8CC");
@@ -587,6 +635,12 @@ ${targetFiles.map((f) => ` ${f}`).join("\n")}`);
587
635
  p5.log.success(`\uC0AD\uC81C \uC644\uB8CC (${result.deleted.length}\uAC1C):
588
636
  ${result.deleted.map((f) => ` ${f}`).join("\n")}`);
589
637
  }
638
+ if (result.cleaned.length > 0) {
639
+ p5.log.success(
640
+ `\uC139\uC158 \uC81C\uAC70 \uC644\uB8CC (\uC0AC\uC6A9\uC790 \uB0B4\uC6A9 \uBCF4\uC874, ${result.cleaned.length}\uAC1C):
641
+ ${result.cleaned.map((f) => ` ${f}`).join("\n")}`
642
+ );
643
+ }
590
644
  if (result.skipped.length > 0) {
591
645
  p5.log.warn(
592
646
  `\uAC74\uB108\uB700 (non-managed \uD30C\uC77C \uBCF4\uD638, ${result.skipped.length}\uAC1C):
@@ -607,10 +661,17 @@ ${removedDirs.map((d) => ` ${d}`).join("\n")}`);
607
661
 
608
662
  // src/bin/index.ts
609
663
  var program = new Command();
664
+ var ensureNoDeprecatedScopeFlag = (argv) => {
665
+ if (argv.some((arg) => arg === "--scope" || arg.startsWith("--scope="))) {
666
+ console.error("`--scope` is no longer supported. ai-ops is now project-only.");
667
+ process.exit(1);
668
+ }
669
+ };
610
670
  program.name("ai-ops").description("AI \uC5D0\uC774\uC804\uD2B8 \uADDC\uCE59 \uC2A4\uCE90\uD3F4\uB354").version("0.1.0");
611
- program.command("init").description("AI \uADDC\uCE59 \uCD08\uAE30 \uC124\uCE58").option("--scope <scope>", "project | global", "project").action((opts) => initCommand(opts));
612
- program.command("update").description("\uAE30\uC874 manifest \uAE30\uBC18 \uADDC\uCE59 \uAC31\uC2E0").option("--scope <scope>", "project | global", "project").option("--force", "\uBCC0\uACBD \uC5C6\uC5B4\uB3C4 \uAC15\uC81C \uC7AC\uC124\uCE58", false).action((opts) => updateCommand(opts));
613
- program.command("diff").description("\uC124\uCE58\uB41C \uADDC\uCE59\uACFC \uCD5C\uC2E0 \uC18C\uC2A4 \uBE44\uAD50").option("--scope <scope>", "project | global", "project").action((opts) => diffCommand(opts));
614
- program.command("uninstall").description("\uC124\uCE58\uB41C \uADDC\uCE59 \uD30C\uC77C \uBC0F manifest \uC81C\uAC70").option("--scope <scope>", "project | global", "project").action((opts) => uninstallCommand(opts));
671
+ program.command("init").description("AI \uADDC\uCE59 \uCD08\uAE30 \uC124\uCE58").action(() => initCommand());
672
+ 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));
673
+ program.command("diff").description("\uC124\uCE58\uB41C \uADDC\uCE59\uACFC \uCD5C\uC2E0 \uC18C\uC2A4 \uBE44\uAD50").action(() => diffCommand());
674
+ program.command("uninstall").description("\uC124\uCE58\uB41C \uADDC\uCE59 \uD30C\uC77C \uBC0F manifest \uC81C\uAC70").action(() => uninstallCommand());
675
+ ensureNoDeprecatedScopeFlag(process.argv);
615
676
  program.parse();
616
677
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/bin/index.ts","../../src/commands/init.ts","../../src/lib/paths.ts","../../src/lib/workspace.ts","../../src/lib/install.ts","../../src/lib/gemini-settings.ts","../../src/commands/update.ts","../../src/commands/diff.ts","../../src/commands/uninstall.ts","../../src/lib/uninstall.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { initCommand } from '../commands/init.js';\nimport { updateCommand } from '../commands/update.js';\nimport { diffCommand } from '../commands/diff.js';\nimport { uninstallCommand } from '../commands/uninstall.js';\nimport type { Scope } from '../lib/paths.js';\n\nconst program = new Command();\n\nprogram.name('ai-ops').description('AI 에이전트 규칙 스캐폴더').version('0.1.0');\n\nprogram\n .command('init')\n .description('AI 규칙 초기 설치')\n .option('--scope <scope>', 'project | global', 'project')\n .action((opts: { scope: Scope }) => initCommand(opts));\n\nprogram\n .command('update')\n .description('기존 manifest 기반 규칙 갱신')\n .option('--scope <scope>', 'project | global', 'project')\n .option('--force', '변경 없어도 강제 재설치', false)\n .action((opts: { scope: Scope; force: boolean }) => updateCommand(opts));\n\nprogram\n .command('diff')\n .description('설치된 규칙과 최신 소스 비교')\n .option('--scope <scope>', 'project | global', 'project')\n .action((opts: { scope: Scope }) => diffCommand(opts));\n\nprogram\n .command('uninstall')\n .description('설치된 규칙 파일 및 manifest 제거')\n .option('--scope <scope>', 'project | global', 'project')\n .action((opts: { scope: Scope }) => uninstallCommand(opts));\n\nprogram.parse();\n","import * as p from '@clack/prompts';\nimport { join } from 'node:path';\nimport type { Rule, Preset, ToolId, WorkspaceMapping } from 'ai-ops-compiler';\nimport {\n loadAllRules,\n loadPresets,\n resolvePresetRules,\n excludeRules,\n isGlobalRule,\n partitionRules,\n renderForTool,\n renderRulesToMarkdown,\n buildInstallPlan,\n buildManifest,\n computeSourceHash,\n resolveManifestPath,\n writeManifest,\n wrapWithHeader,\n TOOL_OUTPUT_MAP,\n} from 'ai-ops-compiler';\nimport type { FileAction } from 'ai-ops-compiler';\nimport type { Scope } from '../lib/paths.js';\nimport { resolveBasePath, resolveRulesDir, resolvePresetsPath } from '../lib/paths.js';\nimport { listWorkspaceCandidates } from '../lib/workspace.js';\nimport { installFiles } from '../lib/install.js';\nimport { promptGeminiSettings, installGeminiSettings } from '../lib/gemini-settings.js';\n\ntype WorkspacePresetMapping = {\n workspace: string;\n preset: Preset;\n finalRules: Rule[];\n};\n\nconst TOOL_OPTIONS = [\n { value: 'claude-code' as ToolId, label: 'Claude Code' },\n { value: 'codex' as ToolId, label: 'Codex' },\n { value: 'gemini' as ToolId, label: 'Gemini CLI' },\n];\n\nconst deduplicateRules = (rules: readonly Rule[]): Rule[] => {\n const seen = new Set<string>();\n return rules.filter((r) => {\n if (seen.has(r.id)) return false;\n seen.add(r.id);\n return true;\n });\n};\n\nconst selectPresetAndFineTune = async (\n workspaceName: string,\n presets: readonly Preset[],\n allRules: readonly Rule[],\n): Promise<WorkspacePresetMapping | null> => {\n const preset = await p.select<Preset>({\n message: `[${workspaceName}] 프리셋을 선택하세요`,\n options: presets.map((pr) => ({\n value: pr,\n label: pr.id,\n hint: pr.description,\n })),\n });\n if (p.isCancel(preset)) return null;\n\n const presetRules = resolvePresetRules(preset, allRules);\n const globalRules = presetRules.filter(isGlobalRule);\n const domainRules = presetRules.filter((r) => !isGlobalRule(r));\n\n // Global rules: locked (항상 포함)\n if (globalRules.length > 0) {\n p.note(globalRules.map((r) => ` ✓ ${r.id}`).join('\\n'), `[${workspaceName}] 기본 규칙 (잠금)`);\n }\n\n if (domainRules.length === 0) {\n return { workspace: workspaceName, preset, finalRules: presetRules };\n }\n\n // Domain rules: 제외 가능\n const selectedDomain = await p.multiselect<string>({\n message: `[${workspaceName}] 도메인 규칙 선택 (해제하여 제외)`,\n options: domainRules.map((r) => ({ value: r.id, label: r.id })),\n initialValues: domainRules.map((r) => r.id),\n required: false,\n });\n if (p.isCancel(selectedDomain)) return null;\n\n const excludeIds = domainRules.map((r) => r.id).filter((id) => !(selectedDomain as string[]).includes(id));\n\n return { workspace: workspaceName, preset, finalRules: excludeRules(presetRules, excludeIds) };\n};\n\nconst installHierarchicalMonorepo = (\n toolId: 'codex' | 'gemini',\n mappings: readonly WorkspacePresetMapping[],\n basePath: string,\n meta: { sourceHash: string; generatedAt: string },\n): string[] => {\n const config = TOOL_OUTPUT_MAP[toolId];\n const written: string[] = [];\n\n const allRules = deduplicateRules(mappings.flatMap((m) => m.finalRules));\n const { global } = partitionRules(allRules);\n\n if (global.length > 0) {\n const rootAction: FileAction = {\n relativePath: join(config.dir, config.rootFileName),\n content: wrapWithHeader(renderRulesToMarkdown(global), meta),\n };\n written.push(...installFiles(basePath, [rootAction]).written);\n }\n\n for (const mapping of mappings) {\n const { domain } = partitionRules(mapping.finalRules);\n if (domain.length === 0) continue;\n\n const domainAction: FileAction = {\n relativePath: join(mapping.workspace, config.domainFileName),\n content: wrapWithHeader(renderRulesToMarkdown(domain), meta),\n };\n written.push(...installFiles(basePath, [domainAction]).written);\n }\n\n return written;\n};\n\nconst installClaudeCodeMonorepo = (\n mappings: readonly WorkspacePresetMapping[],\n basePath: string,\n meta: { sourceHash: string; generatedAt: string },\n): string[] => {\n const allRules = deduplicateRules(mappings.flatMap((m) => m.finalRules));\n const workspaceMappings: WorkspaceMapping[] = mappings.map((m) => ({\n path: m.workspace,\n ruleIds: m.finalRules.map((r) => r.id),\n }));\n const renderResult = renderForTool('claude-code', allRules, workspaceMappings);\n const actions = buildInstallPlan({ toolId: 'claude-code', renderResult, meta });\n return installFiles(basePath, actions).written;\n};\n\nexport const initCommand = async (opts: { scope: Scope }): Promise<void> => {\n const basePath = resolveBasePath(opts.scope);\n const rulesDir = resolveRulesDir();\n\n p.intro('ai-ops init');\n\n // 1. AI 도구 다중 선택\n const selectedTools = await p.multiselect<ToolId>({\n message: 'AI 도구를 선택하세요',\n options: TOOL_OPTIONS,\n required: true,\n });\n if (p.isCancel(selectedTools)) {\n p.cancel('취소됨');\n process.exit(0);\n }\n\n // 2. 모노레포 여부\n const isMonorepo = await p.confirm({\n message: '모노레포 프로젝트입니까?',\n initialValue: false,\n });\n if (p.isCancel(isMonorepo)) {\n p.cancel('취소됨');\n process.exit(0);\n }\n\n // 3. 데이터 로드\n const allRules = loadAllRules(rulesDir);\n const presets = loadPresets(resolvePresetsPath());\n const sourceHash = computeSourceHash(rulesDir);\n\n // 4. 워크스페이스별 preset 선택 + fine-tune\n const mappings: WorkspacePresetMapping[] = [];\n\n if (!isMonorepo) {\n const mapping = await selectPresetAndFineTune('.', presets, allRules);\n if (!mapping) {\n p.cancel('취소됨');\n process.exit(0);\n }\n mappings.push(mapping);\n } else {\n const candidates = listWorkspaceCandidates(basePath);\n const selectedWorkspaces = await p.multiselect<string>({\n message: '워크스페이스를 선택하세요',\n options: candidates.map((c) => ({ value: c, label: c })),\n required: true,\n });\n if (p.isCancel(selectedWorkspaces)) {\n p.cancel('취소됨');\n process.exit(0);\n }\n\n for (const ws of selectedWorkspaces as string[]) {\n const mapping = await selectPresetAndFineTune(ws, presets, allRules);\n if (!mapping) {\n p.cancel('취소됨');\n process.exit(0);\n }\n mappings.push(mapping);\n }\n }\n\n // 4.5. Gemini 설정 (gemini 선택 시)\n const geminiSettingValues: readonly string[] | null = (selectedTools as ToolId[]).includes('gemini')\n ? await promptGeminiSettings()\n : null;\n\n // 5. 설치\n const s = p.spinner();\n s.start('규칙 설치 중...');\n\n const meta = { sourceHash, generatedAt: new Date().toISOString() };\n const allSkipped: string[] = [];\n const allInstalledFiles: string[] = [];\n\n for (const toolId of selectedTools as ToolId[]) {\n if (isMonorepo) {\n if (toolId === 'claude-code') {\n allInstalledFiles.push(...installClaudeCodeMonorepo(mappings, basePath, meta));\n } else {\n allInstalledFiles.push(...installHierarchicalMonorepo(toolId, mappings, basePath, meta));\n }\n } else {\n const renderResult = renderForTool(toolId, mappings[0].finalRules);\n const actions = buildInstallPlan({ toolId, renderResult, meta });\n const result = installFiles(basePath, actions);\n allSkipped.push(...result.skipped);\n allInstalledFiles.push(...result.written);\n }\n }\n\n if (geminiSettingValues && geminiSettingValues.length > 0) {\n installGeminiSettings(basePath, geminiSettingValues);\n allInstalledFiles.push('.gemini/settings.json');\n }\n\n s.stop('규칙 설치 완료');\n\n // 6. Manifest 저장\n const allInstalledRuleIds = deduplicateRules(mappings.flatMap((m) => m.finalRules)).map((r) => r.id);\n\n const workspacesRecord = isMonorepo\n ? Object.fromEntries(\n mappings.map((m) => [m.workspace, { preset: m.preset.id, rules: m.finalRules.map((r) => r.id) }]),\n )\n : undefined;\n\n const manifest = buildManifest({\n tools: selectedTools as string[],\n scope: opts.scope,\n preset: !isMonorepo ? mappings[0].preset.id : undefined,\n workspaces: workspacesRecord,\n installedRules: allInstalledRuleIds,\n installedFiles: allInstalledFiles,\n sourceHash,\n });\n writeManifest(resolveManifestPath(basePath), manifest);\n\n // 7. 결과 요약\n if (allSkipped.length > 0) {\n p.log.warn(`충돌(non-managed) 파일 건너뜀:\\n${allSkipped.map((f) => ` ${f}`).join('\\n')}`);\n }\n p.log.success(`설치된 규칙: ${allInstalledRuleIds.length}개`);\n p.outro('ai-ops init 완료');\n};\n","import { join, resolve } from 'node:path';\nimport { homedir } from 'node:os';\nimport { COMPILER_DATA_DIR } from 'ai-ops-compiler';\n\nexport type Scope = 'project' | 'global';\n\nexport const resolveCompilerDataDir = (): string => COMPILER_DATA_DIR;\n\nexport const resolveRulesDir = (): string => join(COMPILER_DATA_DIR, 'rules');\n\nexport const resolvePresetsPath = (): string => join(COMPILER_DATA_DIR, 'presets.yaml');\n\n// scope에 따른 설치 기준 디렉토리\nexport const resolveBasePath = (scope: Scope): string =>\n scope === 'global' ? resolve(homedir(), '.ai-ops') : process.cwd();\n","import { existsSync, readdirSync, statSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\n\nconst EXCLUDE_DIRS = new Set(['node_modules', '.git', 'dist', 'build', '.next', '.turbo', '.cache', 'coverage']);\n\nconst isVisibleDir = (basePath: string, name: string): boolean => {\n if (name.startsWith('.') || EXCLUDE_DIRS.has(name)) return false;\n return statSync(resolve(basePath, name)).isDirectory();\n};\n\n// Project manifest files that indicate a workspace root\nconst PROJECT_MANIFESTS = [\n 'package.json', // Node.js / JS / TS\n 'pubspec.yaml', // Flutter / Dart\n 'pyproject.toml', // Python (modern)\n 'setup.py', // Python (legacy)\n 'Cargo.toml', // Rust\n 'go.mod', // Go\n];\n\nconst isWorkspaceRoot = (dirPath: string): boolean => PROJECT_MANIFESTS.some((f) => existsSync(join(dirPath, f)));\n\n// 프로젝트 매니페스트 파일 존재 여부로 워크스페이스 판별:\n// 1. top-level dir에 매니페스트 → 그 자체가 워크스페이스 (e.g. backend-ts, mobile, web)\n// 2. top-level dir에 매니페스트 없고 자식에 있음 → 자식을 후보로 (e.g. apps/web, packages/ui)\n// 3. 매니페스트 없는 경우 → 1-depth 그대로\nexport const listWorkspaceCandidates = (basePath: string): string[] => {\n const topLevel = readdirSync(basePath).filter((name) => isVisibleDir(basePath, name));\n\n const candidates: string[] = [];\n for (const dir of topLevel) {\n const subPath = resolve(basePath, dir);\n if (isWorkspaceRoot(subPath)) {\n candidates.push(dir);\n } else {\n const children = readdirSync(subPath).filter((name) => isVisibleDir(subPath, name));\n const wsChildren = children.filter((name) => isWorkspaceRoot(resolve(subPath, name)));\n if (wsChildren.length > 0) {\n for (const child of wsChildren) {\n candidates.push(join(dir, child));\n }\n } else {\n candidates.push(dir);\n }\n }\n }\n\n return candidates.sort();\n};\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { isManagedFile } from 'ai-ops-compiler';\nimport type { FileAction } from 'ai-ops-compiler';\n\nexport type InstallResult = {\n written: string[];\n skipped: string[]; // 기존 파일이 managed가 아닌 경우 (사용자 파일 보호)\n};\n\nexport const installFiles = (basePath: string, actions: readonly FileAction[]): InstallResult => {\n const written: string[] = [];\n const skipped: string[] = [];\n\n for (const action of actions) {\n const absPath = resolve(basePath, action.relativePath);\n\n if (existsSync(absPath)) {\n const existing = readFileSync(absPath, 'utf-8');\n if (!isManagedFile(existing)) {\n skipped.push(action.relativePath);\n continue;\n }\n }\n\n mkdirSync(dirname(absPath), { recursive: true });\n writeFileSync(absPath, action.content, 'utf-8');\n written.push(action.relativePath);\n }\n\n return { written, skipped };\n};\n","import * as p from '@clack/prompts';\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\ntype GeminiSettings = {\n ui?: { showLineNumbers?: boolean };\n general?: {\n plan?: { directory?: string; modelRouting?: boolean };\n sessionRetention?: { maxAge?: string };\n };\n experimental?: { jitContext?: boolean; plan?: boolean };\n};\n\ntype SettingGroup = {\n value: string;\n label: string;\n hint: string;\n patch: GeminiSettings;\n};\n\nconst SETTING_GROUPS: readonly SettingGroup[] = [\n {\n value: 'ui',\n label: 'UI — 줄 번호 숨기기',\n hint: 'ui.showLineNumbers: false — 코드 복사 시 줄 번호가 포함되지 않도록 비활성화',\n patch: { ui: { showLineNumbers: false } },\n },\n {\n value: 'plan',\n label: 'Plan — 계획 파일 저장 및 모델 라우팅',\n hint: 'general.plan.directory: .gemini/plans, modelRouting: true — AI 계획을 파일로 저장하고 태스크별 최적 모델 자동 선택',\n patch: { general: { plan: { directory: '.gemini/plans', modelRouting: true } } },\n },\n {\n value: 'sessionRetention',\n label: 'Session Retention — 세션 30일 보존',\n hint: 'general.sessionRetention.maxAge: 30d — 이전 대화 컨텍스트를 30일간 유지',\n patch: { general: { sessionRetention: { maxAge: '30d' } } },\n },\n {\n value: 'experimental',\n label: 'Experimental — JIT 컨텍스트 + Plan 기능',\n hint: 'experimental.jitContext: true, plan: true — 서브디렉토리 컨텍스트 지연 로딩 및 계획 기능 실험적 활성화',\n patch: { experimental: { jitContext: true, plan: true } },\n },\n];\n\nconst deepMerge = (base: Record<string, unknown>, patch: Record<string, unknown>): Record<string, unknown> => {\n const result = { ...base };\n for (const [key, value] of Object.entries(patch)) {\n if (\n value !== null &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n typeof result[key] === 'object' &&\n result[key] !== null\n ) {\n result[key] = deepMerge(result[key] as Record<string, unknown>, value as Record<string, unknown>);\n } else {\n result[key] = value;\n }\n }\n return result;\n};\n\n// null → 건너뜀 (취소 또는 \"No\"), string[] → 선택된 항목\nexport const promptGeminiSettings = async (): Promise<readonly string[] | null> => {\n const wantSettings = await p.confirm({\n message: 'Gemini CLI 설정 파일(.gemini/settings.json)을 설치하시겠습니까?',\n initialValue: true,\n });\n if (p.isCancel(wantSettings) || !wantSettings) return null;\n\n const selected = await p.multiselect<string>({\n message: '설치할 설정 항목을 선택하세요 (스페이스로 토글)',\n options: SETTING_GROUPS.map((g) => ({\n value: g.value,\n label: g.label,\n hint: g.hint,\n })),\n initialValues: SETTING_GROUPS.map((g) => g.value),\n required: false,\n });\n if (p.isCancel(selected)) return null;\n return selected as string[];\n};\n\nexport const installGeminiSettings = (basePath: string, selectedValues: readonly string[]): void => {\n if (selectedValues.length === 0) return;\n\n const settingsDir = join(basePath, '.gemini');\n const settingsPath = join(settingsDir, 'settings.json');\n\n let existing: GeminiSettings = {};\n if (existsSync(settingsPath)) {\n try {\n existing = JSON.parse(readFileSync(settingsPath, 'utf-8')) as GeminiSettings;\n } catch {\n // parse 실패 시 덮어쓰기\n }\n }\n\n let merged: GeminiSettings = existing;\n for (const val of selectedValues) {\n const group = SETTING_GROUPS.find((g) => g.value === val);\n if (!group) continue;\n merged = deepMerge(merged as Record<string, unknown>, group.patch as Record<string, unknown>) as GeminiSettings;\n }\n\n mkdirSync(settingsDir, { recursive: true });\n writeFileSync(settingsPath, JSON.stringify(merged, null, 2) + '\\n', 'utf-8');\n};\n","import * as p from '@clack/prompts';\nimport type { ToolId } from 'ai-ops-compiler';\nimport {\n readManifest,\n resolveManifestPath,\n loadAllRules,\n renderForTool,\n buildInstallPlan,\n buildManifest,\n writeManifest,\n computeSourceHash,\n computeDiff,\n partitionRules,\n renderRulesToMarkdown,\n wrapWithHeader,\n TOOL_OUTPUT_MAP,\n} from 'ai-ops-compiler';\nimport type { FileAction } from 'ai-ops-compiler';\nimport { join } from 'node:path';\nimport type { Scope } from '../lib/paths.js';\nimport { resolveBasePath, resolveRulesDir } from '../lib/paths.js';\nimport { installFiles } from '../lib/install.js';\n\nexport const updateCommand = async (opts: { scope: Scope; force: boolean }): Promise<void> => {\n const basePath = resolveBasePath(opts.scope);\n const manifestPath = resolveManifestPath(basePath);\n\n p.intro('ai-ops update');\n\n const manifest = readManifest(manifestPath);\n if (!manifest) {\n p.log.error('manifest가 없습니다. 먼저 ai-ops init을 실행하세요.');\n process.exit(1);\n }\n\n const rulesDir = resolveRulesDir();\n const sourceHash = computeSourceHash(rulesDir);\n\n const diffResult = computeDiff({\n previous: manifest,\n currentRules: manifest.installed_rules,\n currentSourceHash: sourceHash,\n });\n\n if (diffResult.status === 'up-to-date' && !opts.force) {\n p.log.info('변경 사항이 없습니다.');\n p.outro('ai-ops update 완료');\n return;\n }\n\n const s = p.spinner();\n s.start('규칙 갱신 중...');\n\n const allRules = loadAllRules(rulesDir);\n const meta = { sourceHash, generatedAt: new Date().toISOString() };\n const allInstalledFiles: string[] = [];\n\n if (manifest.workspaces) {\n // 모노레포: workspaces 기반 재설치\n const workspaceEntries = Object.entries(manifest.workspaces);\n\n for (const toolIdStr of manifest.tools) {\n const toolId = toolIdStr as ToolId;\n\n if (toolId === 'claude-code') {\n const allInstalledRuleSet = new Set(manifest.installed_rules);\n const rulesToInstall = allRules.filter((r) => allInstalledRuleSet.has(r.id));\n const workspaceMappings = Object.entries(manifest.workspaces!).map(([path, entry]) => ({\n path,\n ruleIds: entry.rules,\n }));\n const renderResult = renderForTool('claude-code', rulesToInstall, workspaceMappings);\n const actions = buildInstallPlan({ toolId: 'claude-code', renderResult, meta });\n allInstalledFiles.push(...installFiles(basePath, actions).written);\n } else {\n // codex/gemini: global → 루트, domain → 워크스페이스별\n const config = TOOL_OUTPUT_MAP[toolId];\n\n const allInstalledRuleSet = new Set(manifest.installed_rules);\n const allRulesToInstall = allRules.filter((r) => allInstalledRuleSet.has(r.id));\n const { global } = partitionRules(allRulesToInstall);\n\n if (global.length > 0) {\n const rootAction: FileAction = {\n relativePath: join(config.dir, config.rootFileName),\n content: wrapWithHeader(renderRulesToMarkdown(global), meta),\n };\n allInstalledFiles.push(...installFiles(basePath, [rootAction]).written);\n }\n\n for (const [ws, entry] of workspaceEntries) {\n const wsRuleSet = new Set(entry.rules);\n const wsRules = allRules.filter((r) => wsRuleSet.has(r.id));\n const { domain } = partitionRules(wsRules);\n if (domain.length === 0) continue;\n\n const domainAction: FileAction = {\n relativePath: join(ws, config.domainFileName),\n content: wrapWithHeader(renderRulesToMarkdown(domain), meta),\n };\n allInstalledFiles.push(...installFiles(basePath, [domainAction]).written);\n }\n }\n }\n } else {\n // 단일 프로젝트: installed_rules 기반 재설치\n const installedRuleSet = new Set(manifest.installed_rules);\n const rulesToInstall = allRules.filter((r) => installedRuleSet.has(r.id));\n\n for (const toolIdStr of manifest.tools) {\n const toolId = toolIdStr as ToolId;\n const renderResult = renderForTool(toolId, rulesToInstall);\n const actions = buildInstallPlan({ toolId, renderResult, meta });\n allInstalledFiles.push(...installFiles(basePath, actions).written);\n }\n }\n\n const newManifest = buildManifest({\n tools: manifest.tools,\n scope: manifest.scope,\n preset: manifest.preset,\n workspaces: manifest.workspaces,\n installedRules: manifest.installed_rules,\n installedFiles: allInstalledFiles.length > 0 ? allInstalledFiles : manifest.installed_files,\n sourceHash,\n });\n writeManifest(manifestPath, newManifest);\n\n s.stop('규칙 갱신 완료');\n p.outro('ai-ops update 완료');\n};\n","import * as p from '@clack/prompts';\nimport { readManifest, resolveManifestPath, computeSourceHash, computeDiff } from 'ai-ops-compiler';\nimport type { Scope } from '../lib/paths.js';\nimport { resolveBasePath, resolveRulesDir } from '../lib/paths.js';\n\nexport const diffCommand = async (opts: { scope: Scope }): Promise<void> => {\n const basePath = resolveBasePath(opts.scope);\n\n p.intro('ai-ops diff');\n\n const manifest = readManifest(resolveManifestPath(basePath));\n if (!manifest) {\n p.log.error('manifest가 없습니다. 먼저 ai-ops init을 실행하세요.');\n process.exit(1);\n }\n\n const sourceHash = computeSourceHash(resolveRulesDir());\n\n const result = computeDiff({\n previous: manifest,\n currentRules: manifest.installed_rules,\n currentSourceHash: sourceHash,\n });\n\n if (result.status === 'up-to-date') {\n p.log.success('변경 사항 없음. 최신 상태입니다.');\n } else {\n if (result.sourceChanged) {\n p.log.warn(`소스 변경 감지: ${manifest.sourceHash} → ${sourceHash}`);\n }\n if (result.added.length > 0) {\n p.log.info(`추가된 규칙: ${result.added.join(', ')}`);\n }\n if (result.removed.length > 0) {\n p.log.info(`제거된 규칙: ${result.removed.join(', ')}`);\n }\n }\n\n p.outro('ai-ops diff 완료');\n};\n","import * as p from '@clack/prompts';\nimport { rmSync } from 'node:fs';\nimport { readManifest, resolveManifestPath, inferInstalledFiles, MANIFEST_FILENAME } from 'ai-ops-compiler';\nimport type { Scope } from '../lib/paths.js';\nimport { resolveBasePath } from '../lib/paths.js';\nimport { removeFiles, cleanEmptyDirs, collectManagedDirs } from '../lib/uninstall.js';\n\nexport const uninstallCommand = async (opts: { scope: Scope }): Promise<void> => {\n const basePath = resolveBasePath(opts.scope);\n const manifestPath = resolveManifestPath(basePath);\n\n p.intro('ai-ops uninstall');\n\n // 1. manifest 읽기\n const manifest = readManifest(manifestPath);\n if (!manifest) {\n p.log.error('manifest가 없습니다. 먼저 ai-ops init을 실행하세요.');\n process.exit(1);\n }\n\n // 2. 삭제 대상 결정\n const targetFiles = manifest.installed_files ?? inferInstalledFiles(manifest);\n\n if (targetFiles.length === 0) {\n p.log.warn('삭제할 파일이 없습니다.');\n p.outro('ai-ops uninstall 완료');\n return;\n }\n\n // 3. 삭제 대상 목록 출력\n p.log.info(`삭제 대상 파일 (${targetFiles.length}개):\\n${targetFiles.map((f) => ` ${f}`).join('\\n')}`);\n\n // 4. confirm\n const confirmed = await p.confirm({\n message: '위 파일과 manifest를 모두 삭제하시겠습니까?',\n initialValue: false,\n });\n if (p.isCancel(confirmed) || !confirmed) {\n p.cancel('취소됨');\n process.exit(0);\n }\n\n // 5. 파일 삭제\n const result = removeFiles(basePath, targetFiles);\n\n // 6. 빈 디렉토리 정리\n const dirs = collectManagedDirs(targetFiles);\n const removedDirs = cleanEmptyDirs(basePath, dirs);\n\n // 7. manifest 삭제\n rmSync(manifestPath, { force: true });\n\n // 8. 결과 요약\n if (result.deleted.length > 0) {\n p.log.success(`삭제 완료 (${result.deleted.length}개):\\n${result.deleted.map((f) => ` ${f}`).join('\\n')}`);\n }\n if (result.skipped.length > 0) {\n p.log.warn(\n `건너뜀 (non-managed 파일 보호, ${result.skipped.length}개):\\n${result.skipped.map((f) => ` ${f}`).join('\\n')}`,\n );\n }\n if (result.notFound.length > 0) {\n p.log.info(`이미 없음 (${result.notFound.length}개):\\n${result.notFound.map((f) => ` ${f}`).join('\\n')}`);\n }\n if (removedDirs.length > 0) {\n p.log.info(`빈 디렉토리 정리 (${removedDirs.length}개):\\n${removedDirs.map((d) => ` ${d}`).join('\\n')}`);\n }\n\n p.log.success(`manifest 삭제: ${MANIFEST_FILENAME}`);\n p.outro('ai-ops uninstall 완료');\n};\n","import { existsSync, readFileSync, rmSync, readdirSync } from 'node:fs';\nimport { resolve, dirname } from 'node:path';\nimport { isManagedFile } from 'ai-ops-compiler';\n\nexport type UninstallResult = {\n deleted: string[];\n skipped: string[]; // non-managed 파일 (사용자 파일 보호)\n notFound: string[]; // 이미 삭제됨\n};\n\nexport const removeFiles = (basePath: string, relativePaths: readonly string[]): UninstallResult => {\n const deleted: string[] = [];\n const skipped: string[] = [];\n const notFound: string[] = [];\n\n for (const rel of relativePaths) {\n const absPath = resolve(basePath, rel);\n\n if (!existsSync(absPath)) {\n notFound.push(rel);\n continue;\n }\n\n const content = readFileSync(absPath, 'utf-8');\n if (!isManagedFile(content)) {\n skipped.push(rel);\n continue;\n }\n\n rmSync(absPath);\n deleted.push(rel);\n }\n\n return { deleted, skipped, notFound };\n};\n\n/** 대상 디렉토리가 비어 있으면 삭제하고, 삭제한 경로 배열 반환 */\nexport const cleanEmptyDirs = (basePath: string, dirs: readonly string[]): string[] => {\n const removed: string[] = [];\n\n for (const dir of dirs) {\n const absDir = resolve(basePath, dir);\n if (!existsSync(absDir)) continue;\n\n try {\n const entries = readdirSync(absDir);\n if (entries.length === 0) {\n rmSync(absDir, { recursive: true });\n removed.push(dir);\n }\n } catch {\n // 삭제 실패는 무시\n }\n }\n\n return removed;\n};\n\n/** manifest의 installed_files에서 정리 대상 디렉토리 목록 추출 */\nexport const collectManagedDirs = (relativePaths: readonly string[]): string[] => {\n const dirs = new Set<string>();\n for (const rel of relativePaths) {\n const dir = dirname(rel);\n if (dir !== '.') {\n dirs.add(dir);\n }\n }\n return [...dirs];\n};\n"],"mappings":"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","names":["p","join","join","resolve","existsSync","resolve","existsSync","mkdirSync","readFileSync","writeFileSync","join","join","p","resolveManifestPath","loadAllRules","renderForTool","buildInstallPlan","buildManifest","writeManifest","computeSourceHash","partitionRules","renderRulesToMarkdown","wrapWithHeader","TOOL_OUTPUT_MAP","join","resolveManifestPath","computeSourceHash","loadAllRules","renderForTool","buildInstallPlan","TOOL_OUTPUT_MAP","partitionRules","join","wrapWithHeader","renderRulesToMarkdown","buildManifest","writeManifest","p","readManifest","resolveManifestPath","computeSourceHash","computeDiff","readManifest","resolveManifestPath","computeSourceHash","computeDiff","p","rmSync","readManifest","resolveManifestPath","existsSync","readFileSync","readdirSync","resolve","dirname","isManagedFile","resolveManifestPath","readManifest","rmSync"]}
1
+ {"version":3,"sources":["../../src/bin/index.ts","../../src/commands/init.ts","../../src/lib/paths.ts","../../src/lib/workspace.ts","../../src/lib/install.ts","../../src/lib/gemini-settings.ts","../../src/commands/update.ts","../../src/commands/diff.ts","../../src/commands/uninstall.ts","../../src/lib/uninstall.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { initCommand } from '../commands/init.js';\nimport { updateCommand } from '../commands/update.js';\nimport { diffCommand } from '../commands/diff.js';\nimport { uninstallCommand } from '../commands/uninstall.js';\n\nconst program = new Command();\n\nconst ensureNoDeprecatedScopeFlag = (argv: readonly string[]): void => {\n if (argv.some((arg) => arg === '--scope' || arg.startsWith('--scope='))) {\n console.error('`--scope` is no longer supported. ai-ops is now project-only.');\n process.exit(1);\n }\n};\n\nprogram.name('ai-ops').description('AI 에이전트 규칙 스캐폴더').version('0.1.0');\n\nprogram\n .command('init')\n .description('AI 규칙 초기 설치')\n .action(() => initCommand());\n\nprogram\n .command('update')\n .description('기존 manifest 기반 규칙 갱신')\n .option('--force', '변경 없어도 강제 재설치', false)\n .action((opts: { force: boolean }) => updateCommand(opts));\n\nprogram\n .command('diff')\n .description('설치된 규칙과 최신 소스 비교')\n .action(() => diffCommand());\n\nprogram\n .command('uninstall')\n .description('설치된 규칙 파일 및 manifest 제거')\n .action(() => uninstallCommand());\n\nensureNoDeprecatedScopeFlag(process.argv);\nprogram.parse();\n","import * as p from '@clack/prompts';\nimport { join } from 'node:path';\nimport type { Rule, Preset, ToolId, WorkspaceMapping } from 'ai-ops-compiler';\nimport {\n loadAllRules,\n loadPresets,\n resolvePresetRules,\n excludeRules,\n isGlobalRule,\n partitionRules,\n renderForTool,\n renderRulesToMarkdown,\n buildInstallPlan,\n buildManifest,\n computeSourceHash,\n resolveManifestPath,\n writeManifest,\n wrapWithHeader,\n TOOL_OUTPUT_MAP,\n} from 'ai-ops-compiler';\nimport type { FileAction } from 'ai-ops-compiler';\nimport { resolveBasePath, resolveRulesDir, resolvePresetsPath } from '../lib/paths.js';\nimport { listWorkspaceCandidates } from '../lib/workspace.js';\nimport { installFiles } from '../lib/install.js';\nimport { promptGeminiSettings, installGeminiSettings } from '../lib/gemini-settings.js';\n\ntype WorkspacePresetMapping = {\n workspace: string;\n preset: Preset;\n finalRules: Rule[];\n};\n\ntype InstallStats = { written: string[]; appended: string[] };\n\nconst TOOL_OPTIONS = [\n { value: 'claude-code' as ToolId, label: 'Claude Code' },\n { value: 'codex' as ToolId, label: 'Codex' },\n { value: 'gemini' as ToolId, label: 'Gemini CLI' },\n];\n\nconst deduplicateRules = (rules: readonly Rule[]): Rule[] => {\n const seen = new Set<string>();\n return rules.filter((r) => {\n if (seen.has(r.id)) return false;\n seen.add(r.id);\n return true;\n });\n};\n\nconst selectPresetAndFineTune = async (\n workspaceName: string,\n presets: readonly Preset[],\n allRules: readonly Rule[],\n): Promise<WorkspacePresetMapping | null> => {\n const preset = await p.select<Preset>({\n message: `[${workspaceName}] 프리셋을 선택하세요`,\n options: presets.map((pr) => ({\n value: pr,\n label: pr.id,\n hint: pr.description,\n })),\n });\n if (p.isCancel(preset)) return null;\n\n const presetRules = resolvePresetRules(preset, allRules);\n const globalRules = presetRules.filter(isGlobalRule);\n const domainRules = presetRules.filter((r) => !isGlobalRule(r));\n\n // Global rules: locked (항상 포함)\n if (globalRules.length > 0) {\n p.note(globalRules.map((r) => ` ✓ ${r.id}`).join('\\n'), `[${workspaceName}] 기본 규칙 (잠금)`);\n }\n\n if (domainRules.length === 0) {\n return { workspace: workspaceName, preset, finalRules: presetRules };\n }\n\n // Domain rules: 제외 가능\n const selectedDomain = await p.multiselect<string>({\n message: `[${workspaceName}] 도메인 규칙 선택 (해제하여 제외)`,\n options: domainRules.map((r) => ({ value: r.id, label: r.id })),\n initialValues: domainRules.map((r) => r.id),\n required: false,\n });\n if (p.isCancel(selectedDomain)) return null;\n\n const excludeIds = domainRules.map((r) => r.id).filter((id) => !(selectedDomain as string[]).includes(id));\n\n return { workspace: workspaceName, preset, finalRules: excludeRules(presetRules, excludeIds) };\n};\n\nconst installHierarchicalMonorepo = (\n toolId: 'codex' | 'gemini',\n mappings: readonly WorkspacePresetMapping[],\n basePath: string,\n meta: { sourceHash: string; generatedAt: string },\n): InstallStats => {\n const config = TOOL_OUTPUT_MAP[toolId];\n const written: string[] = [];\n const appended: string[] = [];\n\n const allRules = deduplicateRules(mappings.flatMap((m) => m.finalRules));\n const { global } = partitionRules(allRules);\n\n if (global.length > 0) {\n const rootAction: FileAction = {\n relativePath: join(config.dir, config.rootFileName),\n content: wrapWithHeader(renderRulesToMarkdown(global), meta),\n };\n const r = installFiles(basePath, [rootAction], meta);\n written.push(...r.written);\n appended.push(...r.appended);\n }\n\n for (const mapping of mappings) {\n const { domain } = partitionRules(mapping.finalRules);\n if (domain.length === 0) continue;\n\n const domainAction: FileAction = {\n relativePath: join(mapping.workspace, config.domainFileName),\n content: wrapWithHeader(renderRulesToMarkdown(domain), meta),\n };\n const r = installFiles(basePath, [domainAction], meta);\n written.push(...r.written);\n appended.push(...r.appended);\n }\n\n return { written, appended };\n};\n\nconst installClaudeCodeMonorepo = (\n mappings: readonly WorkspacePresetMapping[],\n basePath: string,\n meta: { sourceHash: string; generatedAt: string },\n): InstallStats => {\n const allRules = deduplicateRules(mappings.flatMap((m) => m.finalRules));\n const workspaceMappings: WorkspaceMapping[] = mappings.map((m) => ({\n path: m.workspace,\n ruleIds: m.finalRules.map((r) => r.id),\n }));\n const renderResult = renderForTool('claude-code', allRules, workspaceMappings);\n const actions = buildInstallPlan({ toolId: 'claude-code', renderResult, meta });\n const r = installFiles(basePath, actions, meta);\n return { written: r.written, appended: r.appended };\n};\n\nexport const initCommand = async (): Promise<void> => {\n const basePath = resolveBasePath();\n const rulesDir = resolveRulesDir();\n\n p.intro('ai-ops init');\n\n // 1. AI 도구 다중 선택\n const selectedTools = await p.multiselect<ToolId>({\n message: 'AI 도구를 선택하세요',\n options: TOOL_OPTIONS,\n required: true,\n });\n if (p.isCancel(selectedTools)) {\n p.cancel('취소됨');\n process.exit(0);\n }\n\n // 2. 모노레포 여부\n const isMonorepo = await p.confirm({\n message: '모노레포 프로젝트입니까?',\n initialValue: false,\n });\n if (p.isCancel(isMonorepo)) {\n p.cancel('취소됨');\n process.exit(0);\n }\n\n // 3. 데이터 로드\n const allRules = loadAllRules(rulesDir);\n const presets = loadPresets(resolvePresetsPath());\n const sourceHash = computeSourceHash(rulesDir);\n\n // 4. 워크스페이스별 preset 선택 + fine-tune\n const mappings: WorkspacePresetMapping[] = [];\n\n if (!isMonorepo) {\n const mapping = await selectPresetAndFineTune('.', presets, allRules);\n if (!mapping) {\n p.cancel('취소됨');\n process.exit(0);\n }\n mappings.push(mapping);\n } else {\n const candidates = listWorkspaceCandidates(basePath);\n const selectedWorkspaces = await p.multiselect<string>({\n message: '워크스페이스를 선택하세요',\n options: candidates.map((c) => ({ value: c, label: c })),\n required: true,\n });\n if (p.isCancel(selectedWorkspaces)) {\n p.cancel('취소됨');\n process.exit(0);\n }\n\n for (const ws of selectedWorkspaces as string[]) {\n const mapping = await selectPresetAndFineTune(ws, presets, allRules);\n if (!mapping) {\n p.cancel('취소됨');\n process.exit(0);\n }\n mappings.push(mapping);\n }\n }\n\n // 4.5. Gemini 설정 (gemini 선택 시)\n const geminiSettingValues: readonly string[] | null = (selectedTools as ToolId[]).includes('gemini')\n ? await promptGeminiSettings()\n : null;\n\n // 5. 설치\n const s = p.spinner();\n s.start('규칙 설치 중...');\n\n const meta = { sourceHash, generatedAt: new Date().toISOString() };\n const allInstalledFiles: string[] = [];\n const allAppended: string[] = [];\n\n for (const toolId of selectedTools as ToolId[]) {\n if (isMonorepo) {\n if (toolId === 'claude-code') {\n const stats = installClaudeCodeMonorepo(mappings, basePath, meta);\n allInstalledFiles.push(...stats.written);\n allAppended.push(...stats.appended);\n } else {\n const stats = installHierarchicalMonorepo(toolId, mappings, basePath, meta);\n allInstalledFiles.push(...stats.written);\n allAppended.push(...stats.appended);\n }\n } else {\n const renderResult = renderForTool(toolId, mappings[0].finalRules);\n const actions = buildInstallPlan({ toolId, renderResult, meta });\n const result = installFiles(basePath, actions, meta);\n allInstalledFiles.push(...result.written);\n allAppended.push(...result.appended);\n }\n }\n\n if (geminiSettingValues && geminiSettingValues.length > 0) {\n installGeminiSettings(basePath, geminiSettingValues);\n allInstalledFiles.push('.gemini/settings.json');\n }\n\n s.stop('규칙 설치 완료');\n\n // 6. Manifest 저장\n const allInstalledRuleIds = deduplicateRules(mappings.flatMap((m) => m.finalRules)).map((r) => r.id);\n\n const workspacesRecord = isMonorepo\n ? Object.fromEntries(\n mappings.map((m) => [m.workspace, { preset: m.preset.id, rules: m.finalRules.map((r) => r.id) }]),\n )\n : undefined;\n\n const manifest = buildManifest({\n tools: selectedTools as string[],\n scope: 'project',\n preset: !isMonorepo ? mappings[0].preset.id : undefined,\n workspaces: workspacesRecord,\n installedRules: allInstalledRuleIds,\n installedFiles: allInstalledFiles,\n appendedFiles: allAppended,\n sourceHash,\n });\n writeManifest(resolveManifestPath(basePath), manifest);\n\n // 7. 결과 요약\n if (allAppended.length > 0) {\n p.log.info(`기존 파일에 섹션 추가됨 (내용 보존):\\n${allAppended.map((f) => ` ${f}`).join('\\n')}`);\n }\n p.log.success(`설치된 규칙: ${allInstalledRuleIds.length}개`);\n p.outro('ai-ops init 완료');\n};\n","import { join } from 'node:path';\nimport { COMPILER_DATA_DIR } from 'ai-ops-compiler';\n\nexport const resolveCompilerDataDir = (): string => COMPILER_DATA_DIR;\n\nexport const resolveRulesDir = (): string => join(COMPILER_DATA_DIR, 'rules');\n\nexport const resolvePresetsPath = (): string => join(COMPILER_DATA_DIR, 'presets.yaml');\n\n// project-only 설치 기준 디렉토리\nexport const resolveBasePath = (): string => process.cwd();\n","import { existsSync, readdirSync, statSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\n\nconst EXCLUDE_DIRS = new Set(['node_modules', '.git', 'dist', 'build', '.next', '.turbo', '.cache', 'coverage']);\n\nconst isVisibleDir = (basePath: string, name: string): boolean => {\n if (name.startsWith('.') || EXCLUDE_DIRS.has(name)) return false;\n return statSync(resolve(basePath, name)).isDirectory();\n};\n\n// Project manifest files that indicate a workspace root\nconst PROJECT_MANIFESTS = [\n 'package.json', // Node.js / JS / TS\n 'pubspec.yaml', // Flutter / Dart\n 'pyproject.toml', // Python (modern)\n 'setup.py', // Python (legacy)\n 'Cargo.toml', // Rust\n 'go.mod', // Go\n];\n\nconst isWorkspaceRoot = (dirPath: string): boolean => PROJECT_MANIFESTS.some((f) => existsSync(join(dirPath, f)));\n\n// 프로젝트 매니페스트 파일 존재 여부로 워크스페이스 판별:\n// 1. top-level dir에 매니페스트 → 그 자체가 워크스페이스 (e.g. backend-ts, mobile, web)\n// 2. top-level dir에 매니페스트 없고 자식에 있음 → 자식을 후보로 (e.g. apps/web, packages/ui)\n// 3. 매니페스트 없는 경우 → 1-depth 그대로\nexport const listWorkspaceCandidates = (basePath: string): string[] => {\n const topLevel = readdirSync(basePath).filter((name) => isVisibleDir(basePath, name));\n\n const candidates: string[] = [];\n for (const dir of topLevel) {\n const subPath = resolve(basePath, dir);\n if (isWorkspaceRoot(subPath)) {\n candidates.push(dir);\n } else {\n const children = readdirSync(subPath).filter((name) => isVisibleDir(subPath, name));\n const wsChildren = children.filter((name) => isWorkspaceRoot(resolve(subPath, name)));\n if (wsChildren.length > 0) {\n for (const child of wsChildren) {\n candidates.push(join(dir, child));\n }\n } else {\n candidates.push(dir);\n }\n }\n }\n\n return candidates.sort();\n};\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport {\n isManagedFile,\n hasAiOpsSection,\n wrapWithSection,\n replaceAiOpsSection,\n stripManagedHeader,\n} from 'ai-ops-compiler';\nimport type { FileAction } from 'ai-ops-compiler';\n\nexport type InstallResult = {\n written: string[];\n appended: string[]; // 기존 non-managed 파일에 섹션 추가됨\n skipped: string[]; // 더 이상 발생하지 않음 (하위 호환용)\n};\n\nexport const installFiles = (\n basePath: string,\n actions: readonly FileAction[],\n meta: { sourceHash: string; generatedAt: string },\n): InstallResult => {\n const written: string[] = [];\n const appended: string[] = [];\n const skipped: string[] = [];\n\n for (const action of actions) {\n const absPath = resolve(basePath, action.relativePath);\n\n if (existsSync(absPath)) {\n const existing = readFileSync(absPath, 'utf-8');\n\n if (isManagedFile(existing)) {\n writeFileSync(absPath, action.content, 'utf-8');\n written.push(action.relativePath);\n } else if (hasAiOpsSection(existing)) {\n // 이전에 append된 파일 → 섹션만 교체\n const sectionContent = wrapWithSection(stripManagedHeader(action.content), meta);\n const updated = replaceAiOpsSection(existing, sectionContent);\n writeFileSync(absPath, updated, 'utf-8');\n appended.push(action.relativePath);\n } else {\n // non-managed, 섹션 없음 → 최초 append\n const sectionContent = wrapWithSection(stripManagedHeader(action.content), meta);\n const updated = existing.trimEnd() + '\\n\\n' + sectionContent + '\\n';\n writeFileSync(absPath, updated, 'utf-8');\n appended.push(action.relativePath);\n }\n } else {\n mkdirSync(dirname(absPath), { recursive: true });\n writeFileSync(absPath, action.content, 'utf-8');\n written.push(action.relativePath);\n }\n }\n\n return { written, appended, skipped };\n};\n","import * as p from '@clack/prompts';\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\ntype GeminiSettings = {\n ui?: { showLineNumbers?: boolean };\n general?: {\n plan?: { directory?: string; modelRouting?: boolean };\n sessionRetention?: { maxAge?: string };\n };\n experimental?: { jitContext?: boolean; plan?: boolean };\n};\n\ntype SettingGroup = {\n value: string;\n label: string;\n hint: string;\n patch: GeminiSettings;\n};\n\nconst SETTING_GROUPS: readonly SettingGroup[] = [\n {\n value: 'ui',\n label: 'UI — 줄 번호 숨기기',\n hint: 'ui.showLineNumbers: false — 코드 복사 시 줄 번호가 포함되지 않도록 비활성화',\n patch: { ui: { showLineNumbers: false } },\n },\n {\n value: 'plan',\n label: 'Plan — 계획 파일 저장 및 모델 라우팅',\n hint: 'general.plan.directory: .gemini/plans, modelRouting: true — AI 계획을 파일로 저장하고 태스크별 최적 모델 자동 선택',\n patch: { general: { plan: { directory: '.gemini/plans', modelRouting: true } } },\n },\n {\n value: 'sessionRetention',\n label: 'Session Retention — 세션 30일 보존',\n hint: 'general.sessionRetention.maxAge: 30d — 이전 대화 컨텍스트를 30일간 유지',\n patch: { general: { sessionRetention: { maxAge: '30d' } } },\n },\n {\n value: 'experimental',\n label: 'Experimental — JIT 컨텍스트 + Plan 기능',\n hint: 'experimental.jitContext: true, plan: true — 서브디렉토리 컨텍스트 지연 로딩 및 계획 기능 실험적 활성화',\n patch: { experimental: { jitContext: true, plan: true } },\n },\n];\n\nconst deepMerge = (base: Record<string, unknown>, patch: Record<string, unknown>): Record<string, unknown> => {\n const result = { ...base };\n for (const [key, value] of Object.entries(patch)) {\n if (\n value !== null &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n typeof result[key] === 'object' &&\n result[key] !== null\n ) {\n result[key] = deepMerge(result[key] as Record<string, unknown>, value as Record<string, unknown>);\n } else {\n result[key] = value;\n }\n }\n return result;\n};\n\n// null → 건너뜀 (취소 또는 \"No\"), string[] → 선택된 항목\nexport const promptGeminiSettings = async (): Promise<readonly string[] | null> => {\n const wantSettings = await p.confirm({\n message: 'Gemini CLI 설정 파일(.gemini/settings.json)을 설치하시겠습니까?',\n initialValue: true,\n });\n if (p.isCancel(wantSettings) || !wantSettings) return null;\n\n const selected = await p.multiselect<string>({\n message: '설치할 설정 항목을 선택하세요 (스페이스로 토글)',\n options: SETTING_GROUPS.map((g) => ({\n value: g.value,\n label: g.label,\n hint: g.hint,\n })),\n initialValues: SETTING_GROUPS.map((g) => g.value),\n required: false,\n });\n if (p.isCancel(selected)) return null;\n return selected as string[];\n};\n\nexport const installGeminiSettings = (basePath: string, selectedValues: readonly string[]): void => {\n if (selectedValues.length === 0) return;\n\n const settingsDir = join(basePath, '.gemini');\n const settingsPath = join(settingsDir, 'settings.json');\n\n let existing: GeminiSettings = {};\n if (existsSync(settingsPath)) {\n try {\n existing = JSON.parse(readFileSync(settingsPath, 'utf-8')) as GeminiSettings;\n } catch {\n // parse 실패 시 덮어쓰기\n }\n }\n\n let merged: GeminiSettings = existing;\n for (const val of selectedValues) {\n const group = SETTING_GROUPS.find((g) => g.value === val);\n if (!group) continue;\n merged = deepMerge(merged as Record<string, unknown>, group.patch as Record<string, unknown>) as GeminiSettings;\n }\n\n mkdirSync(settingsDir, { recursive: true });\n writeFileSync(settingsPath, JSON.stringify(merged, null, 2) + '\\n', 'utf-8');\n};\n","import * as p from '@clack/prompts';\nimport type { ToolId } from 'ai-ops-compiler';\nimport {\n readManifest,\n resolveManifestPath,\n loadAllRules,\n renderForTool,\n buildInstallPlan,\n buildManifest,\n writeManifest,\n computeSourceHash,\n computeDiff,\n partitionRules,\n renderRulesToMarkdown,\n wrapWithHeader,\n TOOL_OUTPUT_MAP,\n} from 'ai-ops-compiler';\nimport type { FileAction } from 'ai-ops-compiler';\nimport { join } from 'node:path';\nimport { resolveBasePath, resolveRulesDir } from '../lib/paths.js';\nimport { installFiles } from '../lib/install.js';\n\nexport const updateCommand = async (opts: { force: boolean }): Promise<void> => {\n const basePath = resolveBasePath();\n const manifestPath = resolveManifestPath(basePath);\n\n p.intro('ai-ops update');\n\n const manifest = readManifest(manifestPath);\n if (!manifest) {\n p.log.error('manifest가 없습니다. 먼저 ai-ops init을 실행하세요.');\n process.exit(1);\n }\n\n const rulesDir = resolveRulesDir();\n const sourceHash = computeSourceHash(rulesDir);\n\n const diffResult = computeDiff({\n previous: manifest,\n currentRules: manifest.installed_rules,\n currentSourceHash: sourceHash,\n });\n\n if (diffResult.status === 'up-to-date' && !opts.force) {\n p.log.info('변경 사항이 없습니다.');\n p.outro('ai-ops update 완료');\n return;\n }\n\n const s = p.spinner();\n s.start('규칙 갱신 중...');\n\n const allRules = loadAllRules(rulesDir);\n const meta = { sourceHash, generatedAt: new Date().toISOString() };\n const allInstalledFiles: string[] = [];\n const allAppended: string[] = [];\n\n if (manifest.workspaces) {\n // 모노레포: workspaces 기반 재설치\n const workspaceEntries = Object.entries(manifest.workspaces);\n\n for (const toolIdStr of manifest.tools) {\n const toolId = toolIdStr as ToolId;\n\n if (toolId === 'claude-code') {\n const allInstalledRuleSet = new Set(manifest.installed_rules);\n const rulesToInstall = allRules.filter((r) => allInstalledRuleSet.has(r.id));\n const workspaceMappings = Object.entries(manifest.workspaces!).map(([path, entry]) => ({\n path,\n ruleIds: entry.rules,\n }));\n const renderResult = renderForTool('claude-code', rulesToInstall, workspaceMappings);\n const actions = buildInstallPlan({ toolId: 'claude-code', renderResult, meta });\n const r = installFiles(basePath, actions, meta);\n allInstalledFiles.push(...r.written);\n allAppended.push(...r.appended);\n } else {\n // codex/gemini: global → 루트, domain → 워크스페이스별\n const config = TOOL_OUTPUT_MAP[toolId];\n\n const allInstalledRuleSet = new Set(manifest.installed_rules);\n const allRulesToInstall = allRules.filter((r) => allInstalledRuleSet.has(r.id));\n const { global } = partitionRules(allRulesToInstall);\n\n if (global.length > 0) {\n const rootAction: FileAction = {\n relativePath: join(config.dir, config.rootFileName),\n content: wrapWithHeader(renderRulesToMarkdown(global), meta),\n };\n const r = installFiles(basePath, [rootAction], meta);\n allInstalledFiles.push(...r.written);\n allAppended.push(...r.appended);\n }\n\n for (const [ws, entry] of workspaceEntries) {\n const wsRuleSet = new Set(entry.rules);\n const wsRules = allRules.filter((r) => wsRuleSet.has(r.id));\n const { domain } = partitionRules(wsRules);\n if (domain.length === 0) continue;\n\n const domainAction: FileAction = {\n relativePath: join(ws, config.domainFileName),\n content: wrapWithHeader(renderRulesToMarkdown(domain), meta),\n };\n const r = installFiles(basePath, [domainAction], meta);\n allInstalledFiles.push(...r.written);\n allAppended.push(...r.appended);\n }\n }\n }\n } else {\n // 단일 프로젝트: installed_rules 기반 재설치\n const installedRuleSet = new Set(manifest.installed_rules);\n const rulesToInstall = allRules.filter((r) => installedRuleSet.has(r.id));\n\n for (const toolIdStr of manifest.tools) {\n const toolId = toolIdStr as ToolId;\n const renderResult = renderForTool(toolId, rulesToInstall);\n const actions = buildInstallPlan({ toolId, renderResult, meta });\n const r = installFiles(basePath, actions, meta);\n allInstalledFiles.push(...r.written);\n allAppended.push(...r.appended);\n }\n }\n\n const newManifest = buildManifest({\n tools: manifest.tools,\n scope: manifest.scope,\n preset: manifest.preset,\n workspaces: manifest.workspaces,\n installedRules: manifest.installed_rules,\n installedFiles: allInstalledFiles.length > 0 ? allInstalledFiles : manifest.installed_files,\n appendedFiles: allAppended.length > 0 ? allAppended : manifest.appended_files,\n sourceHash,\n });\n writeManifest(manifestPath, newManifest);\n\n s.stop('규칙 갱신 완료');\n p.outro('ai-ops update 완료');\n};\n","import * as p from '@clack/prompts';\nimport { readManifest, resolveManifestPath, computeSourceHash, computeDiff } from 'ai-ops-compiler';\nimport { resolveBasePath, resolveRulesDir } from '../lib/paths.js';\n\nexport const diffCommand = async (): Promise<void> => {\n const basePath = resolveBasePath();\n\n p.intro('ai-ops diff');\n\n const manifest = readManifest(resolveManifestPath(basePath));\n if (!manifest) {\n p.log.error('manifest가 없습니다. 먼저 ai-ops init을 실행하세요.');\n process.exit(1);\n }\n\n const sourceHash = computeSourceHash(resolveRulesDir());\n\n const result = computeDiff({\n previous: manifest,\n currentRules: manifest.installed_rules,\n currentSourceHash: sourceHash,\n });\n\n if (result.status === 'up-to-date') {\n p.log.success('변경 사항 없음. 최신 상태입니다.');\n } else {\n if (result.sourceChanged) {\n p.log.warn(`소스 변경 감지: ${manifest.sourceHash} → ${sourceHash}`);\n }\n if (result.added.length > 0) {\n p.log.info(`추가된 규칙: ${result.added.join(', ')}`);\n }\n if (result.removed.length > 0) {\n p.log.info(`제거된 규칙: ${result.removed.join(', ')}`);\n }\n }\n\n p.outro('ai-ops diff 완료');\n};\n","import * as p from '@clack/prompts';\nimport { rmSync } from 'node:fs';\nimport { readManifest, resolveManifestPath, inferInstalledFiles, MANIFEST_FILENAME } from 'ai-ops-compiler';\nimport { resolveBasePath } from '../lib/paths.js';\nimport { removeFiles, cleanEmptyDirs, collectManagedDirs } from '../lib/uninstall.js';\n\nexport const uninstallCommand = async (): Promise<void> => {\n const basePath = resolveBasePath();\n const manifestPath = resolveManifestPath(basePath);\n\n p.intro('ai-ops uninstall');\n\n // 1. manifest 읽기\n const manifest = readManifest(manifestPath);\n if (!manifest) {\n p.log.error('manifest가 없습니다. 먼저 ai-ops init을 실행하세요.');\n process.exit(1);\n }\n\n // 2. 삭제 대상 결정 (managed 파일 + append된 파일)\n const targetFiles = [\n ...(manifest.installed_files ?? inferInstalledFiles(manifest)),\n ...(manifest.appended_files ?? []),\n ];\n\n if (targetFiles.length === 0) {\n p.log.warn('삭제할 파일이 없습니다.');\n p.outro('ai-ops uninstall 완료');\n return;\n }\n\n // 3. 삭제 대상 목록 출력\n p.log.info(`삭제 대상 파일 (${targetFiles.length}개):\\n${targetFiles.map((f) => ` ${f}`).join('\\n')}`);\n\n // 4. confirm\n const confirmed = await p.confirm({\n message: '위 파일과 manifest를 모두 삭제하시겠습니까?',\n initialValue: false,\n });\n if (p.isCancel(confirmed) || !confirmed) {\n p.cancel('취소됨');\n process.exit(0);\n }\n\n // 5. 파일 삭제\n const result = removeFiles(basePath, targetFiles);\n\n // 6. 빈 디렉토리 정리\n const dirs = collectManagedDirs(targetFiles);\n const removedDirs = cleanEmptyDirs(basePath, dirs);\n\n // 7. manifest 삭제\n rmSync(manifestPath, { force: true });\n\n // 8. 결과 요약\n if (result.deleted.length > 0) {\n p.log.success(`삭제 완료 (${result.deleted.length}개):\\n${result.deleted.map((f) => ` ${f}`).join('\\n')}`);\n }\n if (result.cleaned.length > 0) {\n p.log.success(\n `섹션 제거 완료 (사용자 내용 보존, ${result.cleaned.length}개):\\n${result.cleaned.map((f) => ` ${f}`).join('\\n')}`,\n );\n }\n if (result.skipped.length > 0) {\n p.log.warn(\n `건너뜀 (non-managed 파일 보호, ${result.skipped.length}개):\\n${result.skipped.map((f) => ` ${f}`).join('\\n')}`,\n );\n }\n if (result.notFound.length > 0) {\n p.log.info(`이미 없음 (${result.notFound.length}개):\\n${result.notFound.map((f) => ` ${f}`).join('\\n')}`);\n }\n if (removedDirs.length > 0) {\n p.log.info(`빈 디렉토리 정리 (${removedDirs.length}개):\\n${removedDirs.map((d) => ` ${d}`).join('\\n')}`);\n }\n\n p.log.success(`manifest 삭제: ${MANIFEST_FILENAME}`);\n p.outro('ai-ops uninstall 완료');\n};\n","import { existsSync, readFileSync, rmSync, readdirSync, writeFileSync } from 'node:fs';\nimport { resolve, dirname } from 'node:path';\nimport { isManagedFile, hasAiOpsSection, stripAiOpsSection } from 'ai-ops-compiler';\n\nexport type UninstallResult = {\n deleted: string[];\n cleaned: string[]; // 섹션만 제거된 파일 (append 되었던 파일)\n skipped: string[]; // non-managed 파일 (사용자 파일 보호)\n notFound: string[]; // 이미 삭제됨\n};\n\nexport const removeFiles = (basePath: string, relativePaths: readonly string[]): UninstallResult => {\n const deleted: string[] = [];\n const cleaned: string[] = [];\n const skipped: string[] = [];\n const notFound: string[] = [];\n\n for (const rel of relativePaths) {\n const absPath = resolve(basePath, rel);\n\n if (!existsSync(absPath)) {\n notFound.push(rel);\n continue;\n }\n\n const content = readFileSync(absPath, 'utf-8');\n\n if (!isManagedFile(content)) {\n if (hasAiOpsSection(content)) {\n // append된 파일 → 섹션만 제거, 사용자 콘텐츠 보존\n const stripped = stripAiOpsSection(content);\n writeFileSync(absPath, stripped, 'utf-8');\n cleaned.push(rel);\n } else {\n skipped.push(rel);\n }\n continue;\n }\n\n rmSync(absPath);\n deleted.push(rel);\n }\n\n return { deleted, cleaned, skipped, notFound };\n};\n\n/** 대상 디렉토리가 비어 있으면 삭제하고, 삭제한 경로 배열 반환 */\nexport const cleanEmptyDirs = (basePath: string, dirs: readonly string[]): string[] => {\n const removed: string[] = [];\n\n for (const dir of dirs) {\n const absDir = resolve(basePath, dir);\n if (!existsSync(absDir)) continue;\n\n try {\n const entries = readdirSync(absDir);\n if (entries.length === 0) {\n rmSync(absDir, { recursive: true });\n removed.push(dir);\n }\n } catch {\n // 삭제 실패는 무시\n }\n }\n\n return removed;\n};\n\n/** manifest의 installed_files에서 정리 대상 디렉토리 목록 추출 */\nexport const collectManagedDirs = (relativePaths: readonly string[]): string[] => {\n const dirs = new Set<string>();\n for (const rel of relativePaths) {\n const dir = dirname(rel);\n if (dir !== '.') {\n dirs.add(dir);\n }\n }\n return [...dirs];\n};\n"],"mappings":"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","names":["p","join","join","existsSync","resolve","existsSync","mkdirSync","readFileSync","writeFileSync","join","join","r","p","resolveManifestPath","loadAllRules","renderForTool","buildInstallPlan","buildManifest","writeManifest","computeSourceHash","partitionRules","renderRulesToMarkdown","wrapWithHeader","TOOL_OUTPUT_MAP","join","resolveManifestPath","computeSourceHash","loadAllRules","r","renderForTool","buildInstallPlan","TOOL_OUTPUT_MAP","partitionRules","join","wrapWithHeader","renderRulesToMarkdown","buildManifest","writeManifest","p","readManifest","resolveManifestPath","computeSourceHash","computeDiff","readManifest","resolveManifestPath","computeSourceHash","computeDiff","p","rmSync","readManifest","resolveManifestPath","existsSync","readFileSync","readdirSync","writeFileSync","resolve","dirname","isManagedFile","hasAiOpsSection","resolveManifestPath","readManifest","rmSync"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-ops-cli",
3
- "version": "0.1.6",
3
+ "version": "0.1.9",
4
4
  "description": "CLI for ai-ops scaffolding — manage AI tool rules and presets",
5
5
  "license": "MIT",
6
6
  "type": "module",