ai-ops-cli 0.1.4 → 0.1.5

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 CHANGED
@@ -4,8 +4,8 @@
4
4
  import { Command } from "commander";
5
5
 
6
6
  // src/commands/init.ts
7
- import * as p from "@clack/prompts";
8
- import { join as join3 } from "path";
7
+ import * as p2 from "@clack/prompts";
8
+ import { join as join4 } from "path";
9
9
  import {
10
10
  loadAllRules,
11
11
  loadPresets,
@@ -100,6 +100,87 @@ var installFiles = (basePath, actions) => {
100
100
  return { written, skipped };
101
101
  };
102
102
 
103
+ // src/lib/gemini-settings.ts
104
+ import * as p from "@clack/prompts";
105
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
106
+ import { join as join3 } from "path";
107
+ var SETTING_GROUPS = [
108
+ {
109
+ value: "ui",
110
+ label: "UI \u2014 \uC904 \uBC88\uD638 \uC228\uAE30\uAE30",
111
+ hint: "ui.showLineNumbers: false \u2014 \uCF54\uB4DC \uBCF5\uC0AC \uC2DC \uC904 \uBC88\uD638\uAC00 \uD3EC\uD568\uB418\uC9C0 \uC54A\uB3C4\uB85D \uBE44\uD65C\uC131\uD654",
112
+ patch: { ui: { showLineNumbers: false } }
113
+ },
114
+ {
115
+ value: "plan",
116
+ label: "Plan \u2014 \uACC4\uD68D \uD30C\uC77C \uC800\uC7A5 \uBC0F \uBAA8\uB378 \uB77C\uC6B0\uD305",
117
+ hint: "general.plan.directory: .gemini/plans, modelRouting: true \u2014 AI \uACC4\uD68D\uC744 \uD30C\uC77C\uB85C \uC800\uC7A5\uD558\uACE0 \uD0DC\uC2A4\uD06C\uBCC4 \uCD5C\uC801 \uBAA8\uB378 \uC790\uB3D9 \uC120\uD0DD",
118
+ patch: { general: { plan: { directory: ".gemini/plans", modelRouting: true } } }
119
+ },
120
+ {
121
+ value: "sessionRetention",
122
+ label: "Session Retention \u2014 \uC138\uC158 30\uC77C \uBCF4\uC874",
123
+ hint: "general.sessionRetention.maxAge: 30d \u2014 \uC774\uC804 \uB300\uD654 \uCEE8\uD14D\uC2A4\uD2B8\uB97C 30\uC77C\uAC04 \uC720\uC9C0",
124
+ patch: { general: { sessionRetention: { maxAge: "30d" } } }
125
+ },
126
+ {
127
+ value: "experimental",
128
+ label: "Experimental \u2014 JIT \uCEE8\uD14D\uC2A4\uD2B8 + Plan \uAE30\uB2A5",
129
+ hint: "experimental.jitContext: true, plan: true \u2014 \uC11C\uBE0C\uB514\uB809\uD1A0\uB9AC \uCEE8\uD14D\uC2A4\uD2B8 \uC9C0\uC5F0 \uB85C\uB529 \uBC0F \uACC4\uD68D \uAE30\uB2A5 \uC2E4\uD5D8\uC801 \uD65C\uC131\uD654",
130
+ patch: { experimental: { jitContext: true, plan: true } }
131
+ }
132
+ ];
133
+ var deepMerge = (base, patch) => {
134
+ const result = { ...base };
135
+ for (const [key, value] of Object.entries(patch)) {
136
+ if (value !== null && typeof value === "object" && !Array.isArray(value) && typeof result[key] === "object" && result[key] !== null) {
137
+ result[key] = deepMerge(result[key], value);
138
+ } else {
139
+ result[key] = value;
140
+ }
141
+ }
142
+ return result;
143
+ };
144
+ var promptGeminiSettings = async () => {
145
+ const wantSettings = await p.confirm({
146
+ message: "Gemini CLI \uC124\uC815 \uD30C\uC77C(.gemini/settings.json)\uC744 \uC124\uCE58\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
147
+ initialValue: true
148
+ });
149
+ if (p.isCancel(wantSettings) || !wantSettings) return null;
150
+ const selected = await p.multiselect({
151
+ message: "\uC124\uCE58\uD560 \uC124\uC815 \uD56D\uBAA9\uC744 \uC120\uD0DD\uD558\uC138\uC694 (\uC2A4\uD398\uC774\uC2A4\uB85C \uD1A0\uAE00)",
152
+ options: SETTING_GROUPS.map((g) => ({
153
+ value: g.value,
154
+ label: g.label,
155
+ hint: g.hint
156
+ })),
157
+ initialValues: SETTING_GROUPS.map((g) => g.value),
158
+ required: false
159
+ });
160
+ if (p.isCancel(selected)) return null;
161
+ return selected;
162
+ };
163
+ var installGeminiSettings = (basePath, selectedValues) => {
164
+ if (selectedValues.length === 0) return;
165
+ const settingsDir = join3(basePath, ".gemini");
166
+ const settingsPath = join3(settingsDir, "settings.json");
167
+ let existing = {};
168
+ if (existsSync3(settingsPath)) {
169
+ try {
170
+ existing = JSON.parse(readFileSync2(settingsPath, "utf-8"));
171
+ } catch {
172
+ }
173
+ }
174
+ let merged = existing;
175
+ for (const val of selectedValues) {
176
+ const group = SETTING_GROUPS.find((g) => g.value === val);
177
+ if (!group) continue;
178
+ merged = deepMerge(merged, group.patch);
179
+ }
180
+ mkdirSync2(settingsDir, { recursive: true });
181
+ writeFileSync2(settingsPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
182
+ };
183
+
103
184
  // src/commands/init.ts
104
185
  var TOOL_OPTIONS = [
105
186
  { value: "claude-code", label: "Claude Code" },
@@ -115,7 +196,7 @@ var deduplicateRules = (rules) => {
115
196
  });
116
197
  };
117
198
  var selectPresetAndFineTune = async (workspaceName, presets, allRules) => {
118
- const preset = await p.select({
199
+ const preset = await p2.select({
119
200
  message: `[${workspaceName}] \uD504\uB9AC\uC14B\uC744 \uC120\uD0DD\uD558\uC138\uC694`,
120
201
  options: presets.map((pr) => ({
121
202
  value: pr,
@@ -123,46 +204,48 @@ var selectPresetAndFineTune = async (workspaceName, presets, allRules) => {
123
204
  hint: pr.description
124
205
  }))
125
206
  });
126
- if (p.isCancel(preset)) return null;
207
+ if (p2.isCancel(preset)) return null;
127
208
  const presetRules = resolvePresetRules(preset, allRules);
128
209
  const globalRules = presetRules.filter(isGlobalRule);
129
210
  const domainRules = presetRules.filter((r) => !isGlobalRule(r));
130
211
  if (globalRules.length > 0) {
131
- p.note(globalRules.map((r) => ` \u2713 ${r.id}`).join("\n"), `[${workspaceName}] \uAE30\uBCF8 \uADDC\uCE59 (\uC7A0\uAE08)`);
212
+ p2.note(globalRules.map((r) => ` \u2713 ${r.id}`).join("\n"), `[${workspaceName}] \uAE30\uBCF8 \uADDC\uCE59 (\uC7A0\uAE08)`);
132
213
  }
133
214
  if (domainRules.length === 0) {
134
215
  return { workspace: workspaceName, preset, finalRules: presetRules };
135
216
  }
136
- const selectedDomain = await p.multiselect({
217
+ const selectedDomain = await p2.multiselect({
137
218
  message: `[${workspaceName}] \uB3C4\uBA54\uC778 \uADDC\uCE59 \uC120\uD0DD (\uD574\uC81C\uD558\uC5EC \uC81C\uC678)`,
138
219
  options: domainRules.map((r) => ({ value: r.id, label: r.id })),
139
220
  initialValues: domainRules.map((r) => r.id),
140
221
  required: false
141
222
  });
142
- if (p.isCancel(selectedDomain)) return null;
223
+ if (p2.isCancel(selectedDomain)) return null;
143
224
  const excludeIds = domainRules.map((r) => r.id).filter((id) => !selectedDomain.includes(id));
144
225
  return { workspace: workspaceName, preset, finalRules: excludeRules(presetRules, excludeIds) };
145
226
  };
146
227
  var installHierarchicalMonorepo = (toolId, mappings, basePath, meta) => {
147
228
  const config = TOOL_OUTPUT_MAP[toolId];
229
+ const written = [];
148
230
  const allRules = deduplicateRules(mappings.flatMap((m) => m.finalRules));
149
231
  const { global } = partitionRules(allRules);
150
232
  if (global.length > 0) {
151
233
  const rootAction = {
152
- relativePath: join3(config.dir, config.rootFileName),
234
+ relativePath: join4(config.dir, config.rootFileName),
153
235
  content: wrapWithHeader(renderRulesToMarkdown(global), meta)
154
236
  };
155
- installFiles(basePath, [rootAction]);
237
+ written.push(...installFiles(basePath, [rootAction]).written);
156
238
  }
157
239
  for (const mapping of mappings) {
158
240
  const { domain } = partitionRules(mapping.finalRules);
159
241
  if (domain.length === 0) continue;
160
242
  const domainAction = {
161
- relativePath: join3(mapping.workspace, config.domainFileName),
243
+ relativePath: join4(mapping.workspace, config.domainFileName),
162
244
  content: wrapWithHeader(renderRulesToMarkdown(domain), meta)
163
245
  };
164
- installFiles(basePath, [domainAction]);
246
+ written.push(...installFiles(basePath, [domainAction]).written);
165
247
  }
248
+ return written;
166
249
  };
167
250
  var installClaudeCodeMonorepo = (mappings, basePath, meta) => {
168
251
  const allRules = deduplicateRules(mappings.flatMap((m) => m.finalRules));
@@ -172,27 +255,27 @@ var installClaudeCodeMonorepo = (mappings, basePath, meta) => {
172
255
  }));
173
256
  const renderResult = renderForTool("claude-code", allRules, workspaceMappings);
174
257
  const actions = buildInstallPlan({ toolId: "claude-code", renderResult, meta });
175
- installFiles(basePath, actions);
258
+ return installFiles(basePath, actions).written;
176
259
  };
177
260
  var initCommand = async (opts) => {
178
261
  const basePath = resolveBasePath(opts.scope);
179
262
  const rulesDir = resolveRulesDir();
180
- p.intro("ai-ops init");
181
- const selectedTools = await p.multiselect({
263
+ p2.intro("ai-ops init");
264
+ const selectedTools = await p2.multiselect({
182
265
  message: "AI \uB3C4\uAD6C\uB97C \uC120\uD0DD\uD558\uC138\uC694",
183
266
  options: TOOL_OPTIONS,
184
267
  required: true
185
268
  });
186
- if (p.isCancel(selectedTools)) {
187
- p.cancel("\uCDE8\uC18C\uB428");
269
+ if (p2.isCancel(selectedTools)) {
270
+ p2.cancel("\uCDE8\uC18C\uB428");
188
271
  process.exit(0);
189
272
  }
190
- const isMonorepo = await p.confirm({
273
+ const isMonorepo = await p2.confirm({
191
274
  message: "\uBAA8\uB178\uB808\uD3EC \uD504\uB85C\uC81D\uD2B8\uC785\uB2C8\uAE4C?",
192
275
  initialValue: false
193
276
  });
194
- if (p.isCancel(isMonorepo)) {
195
- p.cancel("\uCDE8\uC18C\uB428");
277
+ if (p2.isCancel(isMonorepo)) {
278
+ p2.cancel("\uCDE8\uC18C\uB428");
196
279
  process.exit(0);
197
280
  }
198
281
  const allRules = loadAllRules(rulesDir);
@@ -202,48 +285,55 @@ var initCommand = async (opts) => {
202
285
  if (!isMonorepo) {
203
286
  const mapping = await selectPresetAndFineTune(".", presets, allRules);
204
287
  if (!mapping) {
205
- p.cancel("\uCDE8\uC18C\uB428");
288
+ p2.cancel("\uCDE8\uC18C\uB428");
206
289
  process.exit(0);
207
290
  }
208
291
  mappings.push(mapping);
209
292
  } else {
210
293
  const candidates = listWorkspaceCandidates(basePath);
211
- const selectedWorkspaces = await p.multiselect({
294
+ const selectedWorkspaces = await p2.multiselect({
212
295
  message: "\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4\uB97C \uC120\uD0DD\uD558\uC138\uC694",
213
296
  options: candidates.map((c) => ({ value: c, label: c })),
214
297
  required: true
215
298
  });
216
- if (p.isCancel(selectedWorkspaces)) {
217
- p.cancel("\uCDE8\uC18C\uB428");
299
+ if (p2.isCancel(selectedWorkspaces)) {
300
+ p2.cancel("\uCDE8\uC18C\uB428");
218
301
  process.exit(0);
219
302
  }
220
303
  for (const ws of selectedWorkspaces) {
221
304
  const mapping = await selectPresetAndFineTune(ws, presets, allRules);
222
305
  if (!mapping) {
223
- p.cancel("\uCDE8\uC18C\uB428");
306
+ p2.cancel("\uCDE8\uC18C\uB428");
224
307
  process.exit(0);
225
308
  }
226
309
  mappings.push(mapping);
227
310
  }
228
311
  }
229
- const s = p.spinner();
312
+ const geminiSettingValues = selectedTools.includes("gemini") ? await promptGeminiSettings() : null;
313
+ const s = p2.spinner();
230
314
  s.start("\uADDC\uCE59 \uC124\uCE58 \uC911...");
231
315
  const meta = { sourceHash, generatedAt: (/* @__PURE__ */ new Date()).toISOString() };
232
316
  const allSkipped = [];
317
+ const allInstalledFiles = [];
233
318
  for (const toolId of selectedTools) {
234
319
  if (isMonorepo) {
235
320
  if (toolId === "claude-code") {
236
- installClaudeCodeMonorepo(mappings, basePath, meta);
321
+ allInstalledFiles.push(...installClaudeCodeMonorepo(mappings, basePath, meta));
237
322
  } else {
238
- installHierarchicalMonorepo(toolId, mappings, basePath, meta);
323
+ allInstalledFiles.push(...installHierarchicalMonorepo(toolId, mappings, basePath, meta));
239
324
  }
240
325
  } else {
241
326
  const renderResult = renderForTool(toolId, mappings[0].finalRules);
242
327
  const actions = buildInstallPlan({ toolId, renderResult, meta });
243
328
  const result = installFiles(basePath, actions);
244
329
  allSkipped.push(...result.skipped);
330
+ allInstalledFiles.push(...result.written);
245
331
  }
246
332
  }
333
+ if (geminiSettingValues && geminiSettingValues.length > 0) {
334
+ installGeminiSettings(basePath, geminiSettingValues);
335
+ allInstalledFiles.push(".gemini/settings.json");
336
+ }
247
337
  s.stop("\uADDC\uCE59 \uC124\uCE58 \uC644\uB8CC");
248
338
  const allInstalledRuleIds = deduplicateRules(mappings.flatMap((m) => m.finalRules)).map((r) => r.id);
249
339
  const workspacesRecord = isMonorepo ? Object.fromEntries(
@@ -255,19 +345,20 @@ var initCommand = async (opts) => {
255
345
  preset: !isMonorepo ? mappings[0].preset.id : void 0,
256
346
  workspaces: workspacesRecord,
257
347
  installedRules: allInstalledRuleIds,
348
+ installedFiles: allInstalledFiles,
258
349
  sourceHash
259
350
  });
260
351
  writeManifest(resolveManifestPath(basePath), manifest);
261
352
  if (allSkipped.length > 0) {
262
- p.log.warn(`\uCDA9\uB3CC(non-managed) \uD30C\uC77C \uAC74\uB108\uB700:
353
+ p2.log.warn(`\uCDA9\uB3CC(non-managed) \uD30C\uC77C \uAC74\uB108\uB700:
263
354
  ${allSkipped.map((f) => ` ${f}`).join("\n")}`);
264
355
  }
265
- p.log.success(`\uC124\uCE58\uB41C \uADDC\uCE59: ${allInstalledRuleIds.length}\uAC1C`);
266
- p.outro("ai-ops init \uC644\uB8CC");
356
+ p2.log.success(`\uC124\uCE58\uB41C \uADDC\uCE59: ${allInstalledRuleIds.length}\uAC1C`);
357
+ p2.outro("ai-ops init \uC644\uB8CC");
267
358
  };
268
359
 
269
360
  // src/commands/update.ts
270
- import * as p2 from "@clack/prompts";
361
+ import * as p3 from "@clack/prompts";
271
362
  import {
272
363
  readManifest,
273
364
  resolveManifestPath as resolveManifestPath2,
@@ -283,14 +374,14 @@ import {
283
374
  wrapWithHeader as wrapWithHeader2,
284
375
  TOOL_OUTPUT_MAP as TOOL_OUTPUT_MAP2
285
376
  } from "ai-ops-compiler";
286
- import { join as join4 } from "path";
377
+ import { join as join5 } from "path";
287
378
  var updateCommand = async (opts) => {
288
379
  const basePath = resolveBasePath(opts.scope);
289
380
  const manifestPath = resolveManifestPath2(basePath);
290
- p2.intro("ai-ops update");
381
+ p3.intro("ai-ops update");
291
382
  const manifest = readManifest(manifestPath);
292
383
  if (!manifest) {
293
- p2.log.error("manifest\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 ai-ops init\uC744 \uC2E4\uD589\uD558\uC138\uC694.");
384
+ p3.log.error("manifest\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 ai-ops init\uC744 \uC2E4\uD589\uD558\uC138\uC694.");
294
385
  process.exit(1);
295
386
  }
296
387
  const rulesDir = resolveRulesDir();
@@ -301,14 +392,15 @@ var updateCommand = async (opts) => {
301
392
  currentSourceHash: sourceHash
302
393
  });
303
394
  if (diffResult.status === "up-to-date" && !opts.force) {
304
- p2.log.info("\uBCC0\uACBD \uC0AC\uD56D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
305
- p2.outro("ai-ops update \uC644\uB8CC");
395
+ p3.log.info("\uBCC0\uACBD \uC0AC\uD56D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
396
+ p3.outro("ai-ops update \uC644\uB8CC");
306
397
  return;
307
398
  }
308
- const s = p2.spinner();
399
+ const s = p3.spinner();
309
400
  s.start("\uADDC\uCE59 \uAC31\uC2E0 \uC911...");
310
401
  const allRules = loadAllRules2(rulesDir);
311
402
  const meta = { sourceHash, generatedAt: (/* @__PURE__ */ new Date()).toISOString() };
403
+ const allInstalledFiles = [];
312
404
  if (manifest.workspaces) {
313
405
  const workspaceEntries = Object.entries(manifest.workspaces);
314
406
  for (const toolIdStr of manifest.tools) {
@@ -322,7 +414,7 @@ var updateCommand = async (opts) => {
322
414
  }));
323
415
  const renderResult = renderForTool2("claude-code", rulesToInstall, workspaceMappings);
324
416
  const actions = buildInstallPlan2({ toolId: "claude-code", renderResult, meta });
325
- installFiles(basePath, actions);
417
+ allInstalledFiles.push(...installFiles(basePath, actions).written);
326
418
  } else {
327
419
  const config = TOOL_OUTPUT_MAP2[toolId];
328
420
  const allInstalledRuleSet = new Set(manifest.installed_rules);
@@ -330,10 +422,10 @@ var updateCommand = async (opts) => {
330
422
  const { global } = partitionRules2(allRulesToInstall);
331
423
  if (global.length > 0) {
332
424
  const rootAction = {
333
- relativePath: join4(config.dir, config.rootFileName),
425
+ relativePath: join5(config.dir, config.rootFileName),
334
426
  content: wrapWithHeader2(renderRulesToMarkdown2(global), meta)
335
427
  };
336
- installFiles(basePath, [rootAction]);
428
+ allInstalledFiles.push(...installFiles(basePath, [rootAction]).written);
337
429
  }
338
430
  for (const [ws, entry] of workspaceEntries) {
339
431
  const wsRuleSet = new Set(entry.rules);
@@ -341,10 +433,10 @@ var updateCommand = async (opts) => {
341
433
  const { domain } = partitionRules2(wsRules);
342
434
  if (domain.length === 0) continue;
343
435
  const domainAction = {
344
- relativePath: join4(ws, config.domainFileName),
436
+ relativePath: join5(ws, config.domainFileName),
345
437
  content: wrapWithHeader2(renderRulesToMarkdown2(domain), meta)
346
438
  };
347
- installFiles(basePath, [domainAction]);
439
+ allInstalledFiles.push(...installFiles(basePath, [domainAction]).written);
348
440
  }
349
441
  }
350
442
  }
@@ -355,7 +447,7 @@ var updateCommand = async (opts) => {
355
447
  const toolId = toolIdStr;
356
448
  const renderResult = renderForTool2(toolId, rulesToInstall);
357
449
  const actions = buildInstallPlan2({ toolId, renderResult, meta });
358
- installFiles(basePath, actions);
450
+ allInstalledFiles.push(...installFiles(basePath, actions).written);
359
451
  }
360
452
  }
361
453
  const newManifest = buildManifest2({
@@ -364,22 +456,23 @@ var updateCommand = async (opts) => {
364
456
  preset: manifest.preset,
365
457
  workspaces: manifest.workspaces,
366
458
  installedRules: manifest.installed_rules,
459
+ installedFiles: allInstalledFiles.length > 0 ? allInstalledFiles : manifest.installed_files,
367
460
  sourceHash
368
461
  });
369
462
  writeManifest2(manifestPath, newManifest);
370
463
  s.stop("\uADDC\uCE59 \uAC31\uC2E0 \uC644\uB8CC");
371
- p2.outro("ai-ops update \uC644\uB8CC");
464
+ p3.outro("ai-ops update \uC644\uB8CC");
372
465
  };
373
466
 
374
467
  // src/commands/diff.ts
375
- import * as p3 from "@clack/prompts";
468
+ import * as p4 from "@clack/prompts";
376
469
  import { readManifest as readManifest2, resolveManifestPath as resolveManifestPath3, computeSourceHash as computeSourceHash3, computeDiff as computeDiff2 } from "ai-ops-compiler";
377
470
  var diffCommand = async (opts) => {
378
471
  const basePath = resolveBasePath(opts.scope);
379
- p3.intro("ai-ops diff");
472
+ p4.intro("ai-ops diff");
380
473
  const manifest = readManifest2(resolveManifestPath3(basePath));
381
474
  if (!manifest) {
382
- p3.log.error("manifest\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 ai-ops init\uC744 \uC2E4\uD589\uD558\uC138\uC694.");
475
+ p4.log.error("manifest\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 ai-ops init\uC744 \uC2E4\uD589\uD558\uC138\uC694.");
383
476
  process.exit(1);
384
477
  }
385
478
  const sourceHash = computeSourceHash3(resolveRulesDir());
@@ -389,19 +482,127 @@ var diffCommand = async (opts) => {
389
482
  currentSourceHash: sourceHash
390
483
  });
391
484
  if (result.status === "up-to-date") {
392
- p3.log.success("\uBCC0\uACBD \uC0AC\uD56D \uC5C6\uC74C. \uCD5C\uC2E0 \uC0C1\uD0DC\uC785\uB2C8\uB2E4.");
485
+ p4.log.success("\uBCC0\uACBD \uC0AC\uD56D \uC5C6\uC74C. \uCD5C\uC2E0 \uC0C1\uD0DC\uC785\uB2C8\uB2E4.");
393
486
  } else {
394
487
  if (result.sourceChanged) {
395
- p3.log.warn(`\uC18C\uC2A4 \uBCC0\uACBD \uAC10\uC9C0: ${manifest.sourceHash} \u2192 ${sourceHash}`);
488
+ p4.log.warn(`\uC18C\uC2A4 \uBCC0\uACBD \uAC10\uC9C0: ${manifest.sourceHash} \u2192 ${sourceHash}`);
396
489
  }
397
490
  if (result.added.length > 0) {
398
- p3.log.info(`\uCD94\uAC00\uB41C \uADDC\uCE59: ${result.added.join(", ")}`);
491
+ p4.log.info(`\uCD94\uAC00\uB41C \uADDC\uCE59: ${result.added.join(", ")}`);
399
492
  }
400
493
  if (result.removed.length > 0) {
401
- p3.log.info(`\uC81C\uAC70\uB41C \uADDC\uCE59: ${result.removed.join(", ")}`);
494
+ p4.log.info(`\uC81C\uAC70\uB41C \uADDC\uCE59: ${result.removed.join(", ")}`);
402
495
  }
403
496
  }
404
- p3.outro("ai-ops diff \uC644\uB8CC");
497
+ p4.outro("ai-ops diff \uC644\uB8CC");
498
+ };
499
+
500
+ // src/commands/uninstall.ts
501
+ import * as p5 from "@clack/prompts";
502
+ import { rmSync as rmSync2 } from "fs";
503
+ import { readManifest as readManifest3, resolveManifestPath as resolveManifestPath4, inferInstalledFiles, MANIFEST_FILENAME } from "ai-ops-compiler";
504
+
505
+ // 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";
509
+ var removeFiles = (basePath, relativePaths) => {
510
+ const deleted = [];
511
+ const skipped = [];
512
+ const notFound = [];
513
+ for (const rel of relativePaths) {
514
+ const absPath = resolve4(basePath, rel);
515
+ if (!existsSync4(absPath)) {
516
+ notFound.push(rel);
517
+ continue;
518
+ }
519
+ const content = readFileSync3(absPath, "utf-8");
520
+ if (!isManagedFile2(content)) {
521
+ skipped.push(rel);
522
+ continue;
523
+ }
524
+ rmSync(absPath);
525
+ deleted.push(rel);
526
+ }
527
+ return { deleted, skipped, notFound };
528
+ };
529
+ var cleanEmptyDirs = (basePath, dirs) => {
530
+ const removed = [];
531
+ for (const dir of dirs) {
532
+ const absDir = resolve4(basePath, dir);
533
+ if (!existsSync4(absDir)) continue;
534
+ try {
535
+ const entries = readdirSync2(absDir);
536
+ if (entries.length === 0) {
537
+ rmSync(absDir, { recursive: true });
538
+ removed.push(dir);
539
+ }
540
+ } catch {
541
+ }
542
+ }
543
+ return removed;
544
+ };
545
+ var collectManagedDirs = (relativePaths) => {
546
+ const dirs = /* @__PURE__ */ new Set();
547
+ for (const rel of relativePaths) {
548
+ const dir = dirname2(rel);
549
+ if (dir !== ".") {
550
+ dirs.add(dir);
551
+ }
552
+ }
553
+ return [...dirs];
554
+ };
555
+
556
+ // src/commands/uninstall.ts
557
+ var uninstallCommand = async (opts) => {
558
+ const basePath = resolveBasePath(opts.scope);
559
+ const manifestPath = resolveManifestPath4(basePath);
560
+ p5.intro("ai-ops uninstall");
561
+ const manifest = readManifest3(manifestPath);
562
+ if (!manifest) {
563
+ p5.log.error("manifest\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 ai-ops init\uC744 \uC2E4\uD589\uD558\uC138\uC694.");
564
+ process.exit(1);
565
+ }
566
+ const targetFiles = manifest.installed_files ?? inferInstalledFiles(manifest);
567
+ if (targetFiles.length === 0) {
568
+ p5.log.warn("\uC0AD\uC81C\uD560 \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
569
+ p5.outro("ai-ops uninstall \uC644\uB8CC");
570
+ return;
571
+ }
572
+ p5.log.info(`\uC0AD\uC81C \uB300\uC0C1 \uD30C\uC77C (${targetFiles.length}\uAC1C):
573
+ ${targetFiles.map((f) => ` ${f}`).join("\n")}`);
574
+ const confirmed = await p5.confirm({
575
+ message: "\uC704 \uD30C\uC77C\uACFC manifest\uB97C \uBAA8\uB450 \uC0AD\uC81C\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
576
+ initialValue: false
577
+ });
578
+ if (p5.isCancel(confirmed) || !confirmed) {
579
+ p5.cancel("\uCDE8\uC18C\uB428");
580
+ process.exit(0);
581
+ }
582
+ const result = removeFiles(basePath, targetFiles);
583
+ const dirs = collectManagedDirs(targetFiles);
584
+ const removedDirs = cleanEmptyDirs(basePath, dirs);
585
+ rmSync2(manifestPath, { force: true });
586
+ if (result.deleted.length > 0) {
587
+ p5.log.success(`\uC0AD\uC81C \uC644\uB8CC (${result.deleted.length}\uAC1C):
588
+ ${result.deleted.map((f) => ` ${f}`).join("\n")}`);
589
+ }
590
+ if (result.skipped.length > 0) {
591
+ p5.log.warn(
592
+ `\uAC74\uB108\uB700 (non-managed \uD30C\uC77C \uBCF4\uD638, ${result.skipped.length}\uAC1C):
593
+ ${result.skipped.map((f) => ` ${f}`).join("\n")}`
594
+ );
595
+ }
596
+ if (result.notFound.length > 0) {
597
+ p5.log.info(`\uC774\uBBF8 \uC5C6\uC74C (${result.notFound.length}\uAC1C):
598
+ ${result.notFound.map((f) => ` ${f}`).join("\n")}`);
599
+ }
600
+ if (removedDirs.length > 0) {
601
+ p5.log.info(`\uBE48 \uB514\uB809\uD1A0\uB9AC \uC815\uB9AC (${removedDirs.length}\uAC1C):
602
+ ${removedDirs.map((d) => ` ${d}`).join("\n")}`);
603
+ }
604
+ p5.log.success(`manifest \uC0AD\uC81C: ${MANIFEST_FILENAME}`);
605
+ p5.outro("ai-ops uninstall \uC644\uB8CC");
405
606
  };
406
607
 
407
608
  // src/bin/index.ts
@@ -410,5 +611,6 @@ program.name("ai-ops").description("AI \uC5D0\uC774\uC804\uD2B8 \uADDC\uCE59 \uC
410
611
  program.command("init").description("AI \uADDC\uCE59 \uCD08\uAE30 \uC124\uCE58").option("--scope <scope>", "project | global", "project").action((opts) => initCommand(opts));
411
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));
412
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));
413
615
  program.parse();
414
616
  //# 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/commands/update.ts","../../src/commands/diff.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 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.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';\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): void => {\n const config = TOOL_OUTPUT_MAP[toolId];\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 installFiles(basePath, [rootAction]);\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 installFiles(basePath, [domainAction]);\n }\n};\n\nconst installClaudeCodeMonorepo = (\n mappings: readonly WorkspacePresetMapping[],\n basePath: string,\n meta: { sourceHash: string; generatedAt: string },\n): void => {\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 installFiles(basePath, actions);\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 // 5. 설치\n const s = p.spinner();\n s.start('규칙 설치 중...');\n\n const meta = { sourceHash, generatedAt: new Date().toISOString() };\n const allSkipped: string[] = [];\n\n for (const toolId of selectedTools as ToolId[]) {\n if (isMonorepo) {\n if (toolId === 'claude-code') {\n installClaudeCodeMonorepo(mappings, basePath, meta);\n } else {\n 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 }\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 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 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\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 installFiles(basePath, actions);\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 installFiles(basePath, [rootAction]);\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 installFiles(basePath, [domainAction]);\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 installFiles(basePath, actions);\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 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"],"mappings":"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","names":["join","join","resolve","existsSync","resolve","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"]}
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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-ops-cli",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "CLI for ai-ops scaffolding — manage AI tool rules and presets",
5
5
  "license": "MIT",
6
6
  "type": "module",