ai-ops-cli 0.1.10 → 0.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/data/presets.yaml CHANGED
@@ -11,6 +11,7 @@ frontend-web:
11
11
  - react-typescript
12
12
  - shadcn-ui
13
13
  - nextjs
14
+ - graphql
14
15
  - libs-frontend-web
15
16
 
16
17
  frontend-app:
@@ -23,6 +24,7 @@ frontend-app:
23
24
  - naming-convention
24
25
  - engineering-standards
25
26
  - flutter
27
+ - graphql
26
28
  - libs-frontend-app
27
29
 
28
30
  backend-ts:
@@ -0,0 +1,29 @@
1
+ id: graphql-client-app
2
+ category: api
3
+ tags:
4
+ - graphql
5
+ - frontend
6
+ - flutter
7
+ priority: 46
8
+ content:
9
+ constraints:
10
+ - 'DO NOT call GraphQL endpoints directly from widgets. Route operations through provider/repository layers to avoid rebuild-driven duplicate requests and side effects.'
11
+ - 'DO NOT parse GraphQL response maps in UI code. Convert payloads to typed freezed/json_serializable models in the data layer to prevent runtime cast/null errors.'
12
+ - 'DO NOT treat HTTP 200 as success when GraphQL errors[] exists. Map GraphQL errors to typed domain failures.'
13
+ - 'DO NOT create ad-hoc Dio instances for GraphQL calls. Reuse the shared DI-injected client with auth interceptors.'
14
+ guidelines:
15
+ - 'Use Riverpod providers as the boundary between presentation and GraphQL data access.'
16
+ - 'Generate GraphQL DTO/union models with the same build_runner pipeline used for other app models.'
17
+ - 'Map __typename-based unions to freezed union types to force exhaustive handling.'
18
+ - 'Keep optimistic update and cache invalidation logic in repositories/providers, not in widgets.'
19
+ - 'Log operationName with redacted variables for debugging failed GraphQL operations.'
20
+ decision_table:
21
+ - when: 'A widget needs GraphQL data'
22
+ then: 'Read a Riverpod provider that delegates to repository/use-case'
23
+ avoid: 'Calling Dio/GraphQL requests directly inside build() or event handlers'
24
+ - when: 'A mutation returns data plus business errors'
25
+ then: 'Preserve successful fields and map typed errors into UI state'
26
+ avoid: 'Treating the full response as fatal failure'
27
+ - when: 'New GraphQL documents are added'
28
+ then: 'Run build_runner to regenerate typed models before commit'
29
+ avoid: 'Manual map access against dynamic JSON payloads'
@@ -0,0 +1,30 @@
1
+ id: graphql-client-web
2
+ category: api
3
+ tags:
4
+ - graphql
5
+ - frontend
6
+ - nextjs
7
+ - apollo
8
+ priority: 47
9
+ content:
10
+ constraints:
11
+ - 'DO NOT call /graphql with ad-hoc fetch wrappers in Next.js App Router features. Use the shared Apollo client stack to preserve auth links and normalized cache behavior.'
12
+ - 'DO NOT handwrite GraphQL operation/result types or cast payloads with as assertions. Use generated TypedDocumentNode artifacts.'
13
+ - 'DO NOT treat HTTP 200 as success when GraphQL errors[] exists. Handle partial-success responses explicitly.'
14
+ - 'DO NOT create per-feature ApolloClient instances. Reuse one configured provider to preserve auth/cache/link behavior.'
15
+ guidelines:
16
+ - 'Use @apollo/client with @apollo/experimental-nextjs-app-support and mount ApolloNextAppProvider once at the app root.'
17
+ - 'Use GraphQL Codegen client-preset and import generated graphql(...) documents from feature modules.'
18
+ - 'Set fetchPolicy explicitly per operation (cache-first, network-only, no-cache) based on freshness requirements.'
19
+ - 'Co-locate .graphql documents and fragments with feature code, and keep fragment names feature-prefixed.'
20
+ - 'Normalize GraphQL errors/extensions into a shared UI error model before rendering.'
21
+ decision_table:
22
+ - when: 'GraphQL data fetching is needed in App Router'
23
+ then: 'Use the shared ApolloNextAppProvider + generated documents'
24
+ avoid: 'Ad-hoc raw fetch cache wiring in components'
25
+ - when: 'A mutation can return both data and business errors'
26
+ then: 'Render partial-success data and surface typed errors from the same response'
27
+ avoid: 'Throwing away data whenever errors[] is present'
28
+ - when: 'A new query document is added'
29
+ then: 'Regenerate codegen artifacts before committing'
30
+ avoid: 'Shipping stale generated types/documents'
@@ -0,0 +1,31 @@
1
+ id: graphql-core
2
+ category: api
3
+ tags:
4
+ - graphql
5
+ - api
6
+ - schema-contract
7
+ priority: 48
8
+ content:
9
+ constraints:
10
+ - 'DO NOT rely on implicit nullability. Mark every field intentionally (prefer non-null by default).'
11
+ - 'DO NOT return unbounded lists. Every collection must provide pagination arguments and a stable cursor.'
12
+ - 'DO NOT expose internal implementation details in enum values. Use semantic SCREAMING_SNAKE_CASE names that remain stable across refactors.'
13
+ - 'DO NOT rename/remove published fields or types in the same release. Deprecate first, then remove in the next major.'
14
+ guidelines:
15
+ - 'Follow naming conventions: PascalCase for types/enums, camelCase for fields/arguments, SCREAMING_SNAKE_CASE for enum values.'
16
+ - 'Prefer cursor pagination (first/after) for user-facing lists; use offset only for bounded admin tables.'
17
+ - 'Define typed FilterInput/OrderByInput for filtering and sorting instead of JSON-like free-form arguments.'
18
+ - 'Document scalar contracts (DateTime, UUID, JSON) with accepted formats and timezone policy.'
19
+ decision_table:
20
+ - when: 'A field returns a collection'
21
+ then: 'Use cursor pagination with a stable cursor and deterministic ordering'
22
+ avoid: 'Returning unbounded [Node!]! lists'
23
+ - when: 'A field or type must be renamed/removed'
24
+ then: 'Mark it as @deprecated with a migration reason, keep compatibility for at least one release, remove in next major'
25
+ avoid: 'Immediate rename/removal that breaks existing client operations'
26
+ - when: 'A schema change is high-risk (widely used field, auth/payment domain)'
27
+ then: 'Add pre-release usage checks (operation usage, top callers), publish rollback criteria, and keep a revert path that restores the previous contract'
28
+ avoid: 'Shipping a contract change without usage visibility or rollback plan'
29
+ - when: 'Filtering or sorting is complex'
30
+ then: 'Define typed FilterInput/OrderByInput types'
31
+ avoid: 'Accepting raw JSON blobs or ad-hoc string filters'
@@ -0,0 +1,33 @@
1
+ id: graphql-server
2
+ category: api
3
+ tags:
4
+ - graphql
5
+ - api
6
+ - backend
7
+ priority: 44
8
+ content:
9
+ constraints:
10
+ - 'DO NOT perform mutations in Query resolvers. Reads belong to Query, writes belong to Mutation.'
11
+ - 'DO NOT throw top-level GraphQL errors for expected business failures. Return typed domain errors in payloads.'
12
+ - 'DO NOT issue per-parent DB queries from field resolvers. Use request-scoped batching (DataLoader or equivalent).'
13
+ - 'DO NOT accept multiple scalar mutation arguments. Standardize on one input object (mutation X($input: XInput!)).'
14
+ - 'DO NOT expose unauthenticated subscription/live update endpoints in production.'
15
+ guidelines:
16
+ - 'Keep resolvers thin and delegate business logic to service/use-case layers.'
17
+ - 'Use payload types that carry both success data and typed userErrors for partial success handling.'
18
+ - 'Apply query depth/complexity limits and operation timeout guards to prevent expensive nested queries.'
19
+ - 'Record operationName and requestId in structured logs for mutation/subscription debugging.'
20
+ - 'Document idempotency expectations for side-effecting mutations (payment, provisioning, outbound integrations).'
21
+ decision_table:
22
+ - when: 'A mutation can fail due to business validation'
23
+ then: 'Return payload data + typed userErrors and keep top-level errors for unexpected faults only'
24
+ avoid: 'Throwing generic Error for expected business branches'
25
+ - when: 'A field resolver loads related entities'
26
+ then: 'Batch and cache per-request with DataLoader (or equivalent)'
27
+ avoid: 'Repository call per parent row (N+1)'
28
+ - when: 'A new subscription is introduced'
29
+ then: 'Require auth/tenant checks and choose production-safe PubSub transport'
30
+ avoid: 'Unauthenticated or in-memory-only subscription setup in production'
31
+ - when: 'A mutation triggers irreversible side effects (billing, provisioning, partner API)'
32
+ then: 'Verify pre-conditions first, require idempotency keys, and define compensating/rollback actions before execution'
33
+ avoid: 'Calling external side effects before validation or without replay/rollback strategy'
@@ -15,8 +15,6 @@ content:
15
15
  - 'DO NOT use winston/morgan/console.log for app logs. Use pino via nestjs-pino.'
16
16
  guidelines:
17
17
  - 'Use class-validator + class-transformer DTO validation with ValidationPipe({ whitelist: true }).'
18
- - 'Use @nestjs/graphql + @nestjs/apollo code-first for GraphQL APIs.'
19
- - 'Use GraphQL Codegen for operation/result type generation.'
20
18
  - 'Use jose for JWT sign/verify and JWKS workflows.'
21
19
  - 'Use pino + nestjs-pino as the default structured logger.'
22
20
  - 'Use rxjs operators for NestJS interceptors, guards, and event streams.'
@@ -15,8 +15,6 @@ content:
15
15
  - 'DO NOT install parallel UI kits (MUI, Ant, Chakra) alongside shadcn/ui.'
16
16
  - 'DO NOT use Redux/Recoil/MobX for local UI state. Use zustand for client UI state.'
17
17
  guidelines:
18
- - 'Use @apollo/client with @apollo/experimental-nextjs-app-support for GraphQL in App Router.'
19
- - 'Use GraphQL Codegen client-preset to generate TypedDocumentNode types at build time.'
20
18
  - 'Keep styling on Tailwind + design tokens (CSS variables) and cva variants.'
21
19
  - 'Use next-intl for i18n and next-themes for theme switching.'
22
20
  - 'Render Recharts inside client components only.'
@@ -24,9 +22,6 @@ content:
24
22
  - 'Sanitize user HTML with isomorphic-dompurify before dangerouslySetInnerHTML.'
25
23
  - 'Use Vitest + Testing Library; prefer userEvent over fireEvent.'
26
24
  decision_table:
27
- - when: 'GraphQL data fetching is needed in App Router'
28
- then: 'Use ApolloNextAppProvider with @apollo/client'
29
- avoid: 'Ad-hoc raw fetch cache wiring'
30
25
  - when: 'Date handling is needed'
31
26
  then: 'Use date-fns named imports'
32
27
  avoid: 'moment/dayjs'
package/dist/bin/index.js CHANGED
@@ -67,18 +67,48 @@ var ManifestSchema = z3.object({
67
67
  import { readFileSync, readdirSync } from "fs";
68
68
  import { resolve } from "path";
69
69
  import { parse } from "yaml";
70
+ var PRESET_RULE_BUNDLES = {
71
+ "frontend-web": {
72
+ graphql: ["graphql-core", "graphql-client-web"]
73
+ },
74
+ "frontend-app": {
75
+ graphql: ["graphql-core", "graphql-client-app"]
76
+ },
77
+ "backend-ts": {
78
+ graphql: ["graphql-core", "graphql-server"]
79
+ }
80
+ };
70
81
  var sortRulesByPriority = (rules) => [...rules].sort((a, b) => b.priority - a.priority);
71
- var parseRawPresets = (raw) => Object.entries(raw).map(([id, value]) => PresetSchema.parse({ id, ...value }));
72
- var excludeRules = (rules, excludeIds) => {
73
- const excludeSet = new Set(excludeIds);
74
- return rules.filter((r) => !excludeSet.has(r.id));
82
+ var deduplicateRulesById = (rules) => {
83
+ const seen = /* @__PURE__ */ new Set();
84
+ return rules.filter((rule) => {
85
+ if (seen.has(rule.id)) return false;
86
+ seen.add(rule.id);
87
+ return true;
88
+ });
89
+ };
90
+ var resolveBundledRuleIds = (presetId, logicalRuleId) => {
91
+ const presetBundles = PRESET_RULE_BUNDLES[presetId];
92
+ if (!presetBundles) return [logicalRuleId];
93
+ return presetBundles[logicalRuleId] ?? [logicalRuleId];
94
+ };
95
+ var resolveRuleById = (ruleId, allRules, context) => {
96
+ const found = allRules.find((rule) => rule.id === ruleId);
97
+ if (!found) {
98
+ const suffix = context ? ` (from ${context})` : "";
99
+ throw new Error(`Rule not found: ${ruleId}${suffix}`);
100
+ }
101
+ return found;
75
102
  };
103
+ var parseRawPresets = (raw) => Object.entries(raw).map(([id, value]) => PresetSchema.parse({ id, ...value }));
104
+ var resolvePresetRuleGroups = (preset, allRules) => preset.rules.map((logicalRuleId) => {
105
+ const bundledRuleIds = resolveBundledRuleIds(preset.id, logicalRuleId);
106
+ const rules = bundledRuleIds.map((ruleId) => resolveRuleById(ruleId, allRules, `${preset.id}:${logicalRuleId}`));
107
+ return { id: logicalRuleId, rules };
108
+ });
76
109
  var resolvePresetRules = (preset, allRules) => {
77
- const resolved = preset.rules.map((ruleId) => {
78
- const found = allRules.find((r) => r.id === ruleId);
79
- if (!found) throw new Error(`Rule not found: ${ruleId}`);
80
- return found;
81
- });
110
+ const groups = resolvePresetRuleGroups(preset, allRules);
111
+ const resolved = deduplicateRulesById(groups.flatMap((group) => group.rules));
82
112
  return sortRulesByPriority(resolved);
83
113
  };
84
114
  var loadRuleFile = (filePath) => {
@@ -106,7 +136,10 @@ var CLAUDE_CODE_PATH_GLOBS = {
106
136
  nextjs: ["**/app/**", "next.config.*", "**/middleware.ts"],
107
137
  nestjs: ["**/*.module.ts", "**/*.controller.ts", "**/*.service.ts"],
108
138
  "nestjs-graphql": ["**/*.resolver.ts"],
109
- graphql: ["**/*.graphql", "**/*.gql"],
139
+ "graphql-core": ["**/*.graphql", "**/*.gql"],
140
+ "graphql-client-web": ["**/*.graphql", "**/*.gql", "**/*.tsx", "**/*.ts"],
141
+ "graphql-client-app": ["**/*.graphql", "**/*.gql", "lib/**/*.dart"],
142
+ "graphql-server": ["**/*.graphql", "**/*.gql", "**/*.resolver.ts"],
110
143
  "prisma-postgresql": ["prisma/**", "**/*.prisma"],
111
144
  "shadcn-ui": ["**/components/ui/**"],
112
145
  flutter: ["lib/**/*.dart"],
@@ -614,24 +647,30 @@ var selectPresetAndFineTune = async (workspaceName, presets, allRules) => {
614
647
  }))
615
648
  });
616
649
  if (p2.isCancel(preset)) return null;
617
- const presetRules = resolvePresetRules(preset, allRules);
618
- const globalRules = presetRules.filter(isGlobalRule);
619
- const domainRules = presetRules.filter((r) => !isGlobalRule(r));
650
+ const presetRuleGroups = resolvePresetRuleGroups(preset, allRules);
651
+ const globalGroups = presetRuleGroups.filter((group) => group.rules.every(isGlobalRule));
652
+ const domainGroups = presetRuleGroups.filter((group) => !group.rules.every(isGlobalRule));
653
+ const globalGroupIds = globalGroups.map((group) => group.id);
654
+ const globalRules = globalGroupIds.length > 0 ? resolvePresetRules({ ...preset, rules: globalGroupIds }, allRules) : [];
620
655
  if (globalRules.length > 0) {
621
656
  p2.note(globalRules.map((r) => ` \u2713 ${r.id}`).join("\n"), `[${workspaceName}] \uAE30\uBCF8 \uADDC\uCE59 (\uC7A0\uAE08)`);
622
657
  }
623
- if (domainRules.length === 0) {
624
- return { workspace: workspaceName, preset, finalRules: presetRules };
658
+ if (domainGroups.length === 0) {
659
+ return { workspace: workspaceName, preset, finalRules: resolvePresetRules(preset, allRules) };
625
660
  }
626
661
  const selectedDomain = await p2.multiselect({
627
662
  message: `[${workspaceName}] \uB3C4\uBA54\uC778 \uADDC\uCE59 \uC120\uD0DD (\uD574\uC81C\uD558\uC5EC \uC81C\uC678)`,
628
- options: domainRules.map((r) => ({ value: r.id, label: r.id })),
629
- initialValues: domainRules.map((r) => r.id),
663
+ options: domainGroups.map((group) => ({ value: group.id, label: group.id })),
664
+ initialValues: domainGroups.map((group) => group.id),
630
665
  required: false
631
666
  });
632
667
  if (p2.isCancel(selectedDomain)) return null;
633
- const excludeIds = domainRules.map((r) => r.id).filter((id) => !selectedDomain.includes(id));
634
- return { workspace: workspaceName, preset, finalRules: excludeRules(presetRules, excludeIds) };
668
+ const selectedLogicalRuleIds = [...globalGroupIds, ...selectedDomain];
669
+ return {
670
+ workspace: workspaceName,
671
+ preset,
672
+ finalRules: resolvePresetRules({ ...preset, rules: selectedLogicalRuleIds }, allRules)
673
+ };
635
674
  };
636
675
  var installHierarchicalMonorepo = (toolId, mappings, basePath, meta) => {
637
676
  const config = TOOL_OUTPUT_MAP[toolId];
@@ -814,7 +853,7 @@ var updateCommand = async (opts) => {
814
853
  if (toolId === "claude-code") {
815
854
  const allInstalledRuleSet = new Set(manifest.installed_rules);
816
855
  const rulesToInstall = allRules.filter((r2) => allInstalledRuleSet.has(r2.id));
817
- const workspaceMappings = Object.entries(manifest.workspaces).map(([path, entry]) => ({
856
+ const workspaceMappings = workspaceEntries.map(([path, entry]) => ({
818
857
  path,
819
858
  ruleIds: entry.rules
820
859
  }));
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/bin/index.ts","../../src/commands/init.ts","../../src/core/schemas/rule.schema.ts","../../src/core/schemas/preset.schema.ts","../../src/core/schemas/manifest.schema.ts","../../src/core/loader.ts","../../src/core/renderer.ts","../../src/core/tool-output.ts","../../src/core/source-hash.ts","../../src/core/managed-header.ts","../../src/core/manifest-io.ts","../../src/core/diff.ts","../../src/core/install-plan.ts","../../src/core/uninstall-plan.ts","../../src/core/paths.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 '@/core/index.js';\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 '@/core/index.js';\nimport type { FileAction } from '@/core/index.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\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","/**\n * Rule = SSOT의 최소 지식 단위. 하나의 코딩 컨벤션/아키텍처 규칙을 YAML로 구조화한 것.\n */\nimport { z } from 'zod';\n\nexport const DecisionTableEntrySchema = z\n .object({\n when: z.string().min(1),\n then: z.string().min(1),\n /** 조건부 규칙에서 회피해야 할 패턴 */\n avoid: z.string().min(1).optional(),\n })\n .strict();\n\nexport const RuleContentSchema = z\n .object({\n /** Anti-pattern 규칙 ('하지 마라'). guidelines보다 항상 상단 렌더링 */\n constraints: z.array(z.string().min(1)),\n /** Positive 규칙 ('해라') */\n guidelines: z.array(z.string().min(1)),\n /** 조건부 규칙. when→then→avoid 구조 */\n decision_table: z.array(DecisionTableEntrySchema).optional(),\n })\n .strict();\n\nexport const RuleSchema = z\n .object({\n id: z.string().regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, 'id must be kebab-case'),\n category: z.string().min(1),\n tags: z.array(z.string().min(1)),\n /** 0-100. 높을수록 생성 파일 상단 배치 (U-shaped attention 최적화) */\n priority: z.number().int().min(0).max(100),\n content: RuleContentSchema,\n })\n .strict();\n\nexport type DecisionTableEntry = z.infer<typeof DecisionTableEntrySchema>;\nexport type RuleContent = z.infer<typeof RuleContentSchema>;\nexport type Rule = z.infer<typeof RuleSchema>;\n","import { z } from 'zod';\n\nexport const PresetSchema = z\n .object({\n id: z\n .string()\n .regex(/^[a-z][a-z0-9-]*$/)\n .min(1),\n description: z.string().min(1),\n rules: z.array(z.string().min(1)).min(1),\n })\n .strict();\n\nexport type Preset = z.infer<typeof PresetSchema>;\n","/**\n * Manifest = 설치 추적 메타데이터. CLI가 이전 설치 상태를 기억하기 위한 JSON.\n */\nimport { z } from 'zod';\n\nexport const SCOPES = {\n PROJECT: 'project',\n} as const;\n\n/** 모노레포 워크스페이스별 preset + rules 추적 */\nconst WorkspaceEntrySchema = z\n .object({\n preset: z.string().min(1),\n rules: z.array(z.string().min(1)),\n })\n .strict();\n\nexport type WorkspaceEntry = z.infer<typeof WorkspaceEntrySchema>;\n\nexport const ManifestSchema = z\n .object({\n tools: z.array(z.string().min(1)).min(1),\n scope: z.literal('project'),\n /** 비모노레포 단일 preset */\n preset: z.string().min(1).optional(),\n /** 모노레포: workspace path → { preset, rules } */\n workspaces: z.record(z.string(), WorkspaceEntrySchema).optional(),\n installed_rules: z.array(z.string().min(1)),\n /** 실제 디스크에 쓰여진 파일 상대 경로 목록 (uninstall용). 기존 manifest 호환성 위해 optional */\n installed_files: z.array(z.string().min(1)).optional(),\n /** non-managed 파일에 섹션을 append한 경우 추적 (uninstall 시 섹션만 제거) */\n appended_files: z.array(z.string().min(1)).optional(),\n /** SSOT 데이터 파일들의 deterministic SHA-256 해시 (6자리 hex). diff/update 판단 기준 */\n sourceHash: z.string().regex(/^[a-f0-9]{6}$/, 'sourceHash must be 6 lowercase hex chars'),\n generatedAt: z.string().datetime({ offset: true }),\n })\n .strict();\n\nexport type Manifest = z.infer<typeof ManifestSchema>;\n","import { readFileSync, readdirSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { parse } from 'yaml';\nimport { RuleSchema, PresetSchema } from './schemas/index.js';\nimport type { Rule, Preset } from './schemas/index.js';\n\n// priority 내림차순 정렬 (높을수록 상단 → U-shaped attention)\nexport const sortRulesByPriority = (rules: readonly Rule[]): Rule[] =>\n [...rules].sort((a, b) => b.priority - a.priority);\n\n// presets.yaml의 Record<id, {description, rules}> → Preset[] 변환\nexport const parseRawPresets = (raw: Record<string, { description: string; rules: string[] }>): Preset[] =>\n Object.entries(raw).map(([id, value]) => PresetSchema.parse({ id, ...value }));\n\n// TUI 세부조정: 사용자가 해제한 rule ID 목록을 제외 (순서 유지)\nexport const excludeRules = (rules: readonly Rule[], excludeIds: readonly string[]): Rule[] => {\n const excludeSet = new Set(excludeIds);\n return rules.filter((r) => !excludeSet.has(r.id));\n};\n\n// preset.rules ID 목록으로 allRules에서 필터링 + priority 정렬, 누락 시 throw\nexport const resolvePresetRules = (preset: Preset, allRules: readonly Rule[]): Rule[] => {\n const resolved = preset.rules.map((ruleId) => {\n const found = allRules.find((r) => r.id === ruleId);\n if (!found) throw new Error(`Rule not found: ${ruleId}`);\n return found;\n });\n return sortRulesByPriority(resolved);\n};\n\nexport const loadRuleFile = (filePath: string): Rule => {\n const raw = readFileSync(filePath, 'utf-8');\n return RuleSchema.parse(parse(raw));\n};\n\n// readdirSync + .yaml 필터 + sort\nexport const loadAllRules = (rulesDir: string): Rule[] => {\n const files = readdirSync(rulesDir)\n .filter((f) => f.endsWith('.yaml'))\n .sort();\n return files.map((f) => loadRuleFile(resolve(rulesDir, f)));\n};\n\nexport const loadPresets = (presetsPath: string): Preset[] => {\n const raw = readFileSync(presetsPath, 'utf-8');\n const data = parse(raw) as Record<string, { description: string; rules: string[] }>;\n return parseRawPresets(data);\n};\n","import { join } from 'node:path';\nimport type { Rule, DecisionTableEntry } from './schemas/index.js';\nimport { GLOBAL_CATEGORIES, CLAUDE_CODE_PATH_GLOBS, TOOL_OUTPUT_MAP } from './tool-output.js';\nimport type { ToolId } from './tool-output.js';\n\n// \"react-typescript\" → \"React Typescript\"\nexport const ruleIdToTitle = (id: string): string =>\n id\n .split('-')\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(' ');\n\n// DecisionTableEntry[] → Markdown 테이블 (pipe 문자 &#124; escape)\nexport const renderDecisionTable = (entries: readonly DecisionTableEntry[]): string => {\n const escape = (s: string) => s.replace(/\\|/g, '&#124;');\n const hasAvoid = entries.some((e) => e.avoid !== undefined);\n\n const header = hasAvoid ? '| When | Then | Avoid |\\n|------|------|-------|' : '| When | Then |\\n|------|------|';\n\n const rows = entries.map((e) => {\n const when = escape(e.when);\n const then = escape(e.then);\n if (hasAvoid) {\n const avoid = e.avoid ? escape(e.avoid) : '';\n return `| ${when} | ${then} | ${avoid} |`;\n }\n return `| ${when} | ${then} |`;\n });\n\n return [header, ...rows].join('\\n');\n};\n\n// 단일 Rule → Markdown (빈 섹션 생략)\nexport const renderRuleToMarkdown = (rule: Rule): string => {\n const sections: string[] = [`# ${ruleIdToTitle(rule.id)}`];\n\n if (rule.content.constraints.length > 0) {\n sections.push('## Constraints');\n sections.push(rule.content.constraints.map((c) => `- ${c}`).join('\\n'));\n }\n\n if (rule.content.guidelines.length > 0) {\n sections.push('## Guidelines');\n sections.push(rule.content.guidelines.map((g) => `- ${g}`).join('\\n'));\n }\n\n if (rule.content.decision_table && rule.content.decision_table.length > 0) {\n sections.push('## Decision Table');\n sections.push(renderDecisionTable(rule.content.decision_table));\n }\n\n return sections.join('\\n\\n');\n};\n\n// Rule[] → 단일 Markdown (--- separator, single-file 모드용)\nexport const renderRulesToMarkdown = (rules: readonly Rule[]): string =>\n rules.map(renderRuleToMarkdown).join('\\n\\n---\\n\\n');\n\n// Rule이 global 카테고리에 속하는지 판별\nexport const isGlobalRule = (rule: Rule): boolean => (GLOBAL_CATEGORIES as readonly string[]).includes(rule.category);\n\n// Rule[] → { global, domain } 분리\nexport const partitionRules = (rules: readonly Rule[]): { global: Rule[]; domain: Rule[] } => {\n const global: Rule[] = [];\n const domain: Rule[] = [];\n for (const rule of rules) {\n if (isGlobalRule(rule)) {\n global.push(rule);\n } else {\n domain.push(rule);\n }\n }\n return { global, domain };\n};\n\n// glob 배열 → YAML frontmatter 블록\nexport const renderFrontmatter = (paths: readonly string[]): string => {\n const lines = paths.map((p) => ` - \"${p}\"`).join('\\n');\n return `---\\npaths:\\n${lines}\\n---`;\n};\n\nexport type WorkspaceMapping = {\n path: string;\n ruleIds: readonly string[];\n};\n\n// 단일 Rule → Claude Code용 Markdown\n// domain 룰이면서 glob 매핑이 있으면 paths: frontmatter 추가 (단일 프로젝트 전용)\n// global 룰 또는 매핑 없는 domain 룰 → frontmatter 없음\nexport const renderClaudeCodeRule = (rule: Rule): string => {\n const globs = CLAUDE_CODE_PATH_GLOBS[rule.id];\n if (!isGlobalRule(rule) && globs !== undefined) {\n return `${renderFrontmatter(globs)}\\n\\n${renderRuleToMarkdown(rule)}`;\n }\n return renderRuleToMarkdown(rule);\n};\n\n// 도구별 렌더링 결과 타입 (tagged union)\nexport type ClaudeCodeRenderResult = {\n tool: 'claude-code';\n files: { relativePath: string; content: string }[];\n};\n\nexport type CodexRenderResult = {\n tool: 'codex';\n rootContent: string;\n domainContent: string;\n};\n\nexport type GeminiRenderResult = {\n tool: 'gemini';\n rootContent: string;\n domainContent: string;\n};\n\nexport type ToolRenderResult = ClaudeCodeRenderResult | CodexRenderResult | GeminiRenderResult;\n\n// CLI 진입점: toolId + rules → 도구별 렌더링 결과\nexport const renderForTool = (\n toolId: ToolId,\n rules: readonly Rule[],\n workspaceMappings?: readonly WorkspaceMapping[],\n): ToolRenderResult => {\n const config = TOOL_OUTPUT_MAP[toolId];\n\n if (toolId === 'claude-code') {\n const { rulesDir, fileExtension } = config as (typeof TOOL_OUTPUT_MAP)['claude-code'];\n\n if (!workspaceMappings || workspaceMappings.length === 0) {\n // 단일 프로젝트: domain 룰에 paths: frontmatter (path-scoped)\n const files = rules.map((rule) => ({\n relativePath: join(rulesDir, `${rule.id}${fileExtension}`),\n content: renderClaudeCodeRule(rule),\n }));\n return { tool: 'claude-code', files };\n }\n\n // 모노레포: global → .claude/rules/, domain → {workspace}/CLAUDE.md (진짜 지연 로딩)\n const { global, domain } = partitionRules(rules);\n\n const globalFiles = global.map((rule) => ({\n relativePath: join(rulesDir, `${rule.id}${fileExtension}`),\n content: renderRuleToMarkdown(rule), // global은 frontmatter 불필요\n }));\n\n const workspaceFiles: { relativePath: string; content: string }[] = [];\n for (const ws of workspaceMappings) {\n const wsRules = domain.filter((r) => ws.ruleIds.includes(r.id));\n if (wsRules.length === 0) continue;\n workspaceFiles.push({\n relativePath: join(ws.path, 'CLAUDE.md'),\n content: renderRulesToMarkdown(wsRules),\n });\n }\n\n return { tool: 'claude-code', files: [...globalFiles, ...workspaceFiles] };\n }\n\n const { global, domain } = partitionRules(rules);\n const rootContent = renderRulesToMarkdown(global);\n const domainContent = renderRulesToMarkdown(domain);\n\n if (toolId === 'codex') {\n return { tool: 'codex', rootContent, domainContent };\n }\n\n // gemini\n return { tool: 'gemini', rootContent, domainContent };\n};\n","// Global 성격의 category (항상 로딩, frontmatter 없음)\nexport const GLOBAL_CATEGORIES = ['persona', 'communication', 'philosophy', 'convention', 'standard'] as const;\n\n// Claude Code paths: frontmatter용 Rule ID → glob 매핑\n// src/ 없이 루트를 src처럼 쓰는 React/Next.js 프로젝트 고려\n// 매핑에 없는 domain 룰 → frontmatter 없이 항상 로딩 (안전 fallback)\nexport const CLAUDE_CODE_PATH_GLOBS: Readonly<Record<string, readonly string[]>> = {\n typescript: ['**/*.ts', '**/*.tsx'],\n 'react-typescript': ['**/*.tsx', '**/*.jsx'],\n nextjs: ['**/app/**', 'next.config.*', '**/middleware.ts'],\n nestjs: ['**/*.module.ts', '**/*.controller.ts', '**/*.service.ts'],\n 'nestjs-graphql': ['**/*.resolver.ts'],\n graphql: ['**/*.graphql', '**/*.gql'],\n 'prisma-postgresql': ['prisma/**', '**/*.prisma'],\n 'shadcn-ui': ['**/components/ui/**'],\n flutter: ['lib/**/*.dart'],\n python: ['**/*.py'],\n fastapi: ['**/routers/**', '**/main.py'],\n sqlalchemy: ['**/models/**/*.py', 'alembic/**'],\n 'data-pipeline-python': ['**/pipelines/**', '**/etl/**'],\n 'ai-llm-python': ['**/agents/**', '**/chains/**'],\n 'libs-frontend-web': ['**/*.tsx', '**/*.ts'],\n 'libs-frontend-app': ['lib/**/*.dart'],\n 'libs-backend-ts': ['**/*.ts'],\n 'libs-backend-python': ['**/*.py'],\n};\n\nexport const TOOL_OUTPUT_MAP = {\n 'claude-code': {\n mode: 'multi-file' as const,\n rulesDir: '.claude/rules',\n fileExtension: '.md',\n // single: path-scoped (paths: frontmatter) / monorepo: hierarchical ({workspace}/CLAUDE.md)\n contextStrategy: 'hybrid' as const,\n },\n codex: {\n mode: 'multi-file' as const,\n dir: '',\n rootFileName: 'AGENTS.md', // global 룰\n domainFileName: 'AGENTS.override.md', // domain 룰 (하위 폴더)\n contextStrategy: 'hierarchical' as const, // 루트 + 하위 폴더 JIT\n },\n gemini: {\n mode: 'multi-file' as const,\n dir: '.gemini',\n rootFileName: 'GEMINI.md', // global 룰\n domainFileName: 'GEMINI.md', // domain 룰 (하위 폴더)\n contextStrategy: 'hierarchical' as const, // 루트 + 하위 폴더 JIT\n },\n} as const;\n\nexport type ToolId = keyof typeof TOOL_OUTPUT_MAP;\n","import { createHash } from 'node:crypto';\nimport { readFileSync, readdirSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { ManifestSchema } from './schemas/index.js';\nimport type { Manifest } from './schemas/index.js';\n\n// 문자열 배열 → SHA-256 → 6-hex (caller가 정렬 책임)\nexport const computeHash = (contents: readonly string[]): string =>\n createHash('sha256').update(contents.join('')).digest('hex').slice(0, 6);\n\n// rulesDir 내 YAML 파일들을 alphabetical 정렬 후 해싱\nexport const computeSourceHash = (rulesDir: string): string => {\n const files = readdirSync(rulesDir)\n .filter((f) => f.endsWith('.yaml'))\n .sort();\n const contents = files.map((f) => readFileSync(resolve(rulesDir, f), 'utf-8'));\n return computeHash(contents);\n};\n\n// Manifest Builder (Pure, 단 generatedAt에 현재 시각 사용)\nexport const buildManifest = (params: {\n tools: readonly string[];\n scope: 'project';\n preset?: string;\n workspaces?: Record<string, { preset: string; rules: string[] }>;\n installedRules: readonly string[];\n installedFiles?: readonly string[];\n appendedFiles?: readonly string[];\n sourceHash: string;\n}): Manifest =>\n ManifestSchema.parse({\n tools: [...params.tools],\n scope: params.scope,\n preset: params.preset,\n workspaces: params.workspaces,\n installed_rules: [...params.installedRules],\n installed_files: params.installedFiles ? [...params.installedFiles] : undefined,\n appended_files: params.appendedFiles && params.appendedFiles.length > 0 ? [...params.appendedFiles] : undefined,\n sourceHash: params.sourceHash,\n generatedAt: new Date().toISOString(),\n });\n","const MANAGED_MARKER = '<!-- managed by ai-ops -->';\nconst META_PATTERN = /^<!-- sourceHash: ([a-f0-9]{6}) \\| generatedAt: (.+) -->$/;\n\nconst SECTION_START = '<!-- ai-ops:start -->';\nconst SECTION_END = '<!-- ai-ops:end -->';\n\nexport const wrapWithHeader = (content: string, meta: { sourceHash: string; generatedAt: string }): string => {\n const metaLine = `<!-- sourceHash: ${meta.sourceHash} | generatedAt: ${meta.generatedAt} -->`;\n return `${MANAGED_MARKER}\\n${metaLine}\\n\\n${content}`;\n};\n\nexport const isManagedFile = (content: string): boolean => content.startsWith(MANAGED_MARKER);\n\nexport const parseManagedHeader = (content: string): { sourceHash: string; generatedAt: string } | null => {\n if (!isManagedFile(content)) return null;\n\n const lines = content.split('\\n');\n const metaLine = lines[1] ?? '';\n const match = META_PATTERN.exec(metaLine);\n if (!match) return null;\n\n return { sourceHash: match[1], generatedAt: match[2] };\n};\n\nexport const stripManagedHeader = (content: string): string => {\n if (!isManagedFile(content)) return content;\n\n const lines = content.split('\\n');\n // marker line + meta line + blank line = 3 lines\n const stripped = lines.slice(3).join('\\n');\n return stripped;\n};\n\nexport const wrapWithSection = (content: string, meta: { sourceHash: string; generatedAt: string }): string => {\n const metaLine = `<!-- sourceHash: ${meta.sourceHash} | generatedAt: ${meta.generatedAt} -->`;\n return `${SECTION_START}\\n${metaLine}\\n\\n${content}\\n${SECTION_END}`;\n};\n\nexport const hasAiOpsSection = (content: string): boolean =>\n content.includes(SECTION_START) && content.includes(SECTION_END);\n\nexport const stripAiOpsSection = (content: string): string => {\n const startIdx = content.indexOf(SECTION_START);\n const endIdx = content.indexOf(SECTION_END);\n if (startIdx === -1 || endIdx === -1) return content;\n\n const before = content.slice(0, startIdx).trimEnd();\n const after = content.slice(endIdx + SECTION_END.length).trimStart();\n return before + (after ? '\\n\\n' + after : '') + '\\n';\n};\n\nexport const replaceAiOpsSection = (existing: string, newSection: string): string => {\n const startIdx = existing.indexOf(SECTION_START);\n const endIdx = existing.indexOf(SECTION_END);\n if (startIdx === -1 || endIdx === -1) return existing;\n\n const before = existing.slice(0, startIdx).trimEnd();\n const after = existing.slice(endIdx + SECTION_END.length).trimStart();\n return before + '\\n\\n' + newSection + (after ? '\\n\\n' + after : '') + '\\n';\n};\n","import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { ManifestSchema } from './schemas/index.js';\nimport type { Manifest } from './schemas/index.js';\n\nexport const MANIFEST_FILENAME = '.ai-ops-manifest.json';\n\n// Pure\nexport const parseManifest = (json: string): Manifest => ManifestSchema.parse(JSON.parse(json));\n\nexport const serializeManifest = (manifest: Manifest): string => JSON.stringify(manifest, null, 2) + '\\n';\n\n// I/O\nexport const resolveManifestPath = (basePath: string): string => join(basePath, MANIFEST_FILENAME);\n\nexport const readManifest = (manifestPath: string): Manifest | null => {\n let raw: string;\n try {\n raw = readFileSync(manifestPath, 'utf-8');\n } catch {\n return null;\n }\n return parseManifest(raw);\n};\n\nexport const writeManifest = (manifestPath: string, manifest: Manifest): void => {\n mkdirSync(dirname(manifestPath), { recursive: true });\n writeFileSync(manifestPath, serializeManifest(manifest), 'utf-8');\n};\n","import type { Manifest } from './schemas/index.js';\n\nexport type DiffResult = {\n status: 'up-to-date' | 'changed';\n added: readonly string[];\n removed: readonly string[];\n sourceChanged: boolean;\n};\n\nexport const computeDiff = (params: {\n previous: Manifest;\n currentRules: readonly string[];\n currentSourceHash: string;\n}): DiffResult => {\n const { previous, currentRules, currentSourceHash } = params;\n\n const previousSet = new Set(previous.installed_rules);\n const currentSet = new Set(currentRules);\n\n const added = currentRules.filter((id) => !previousSet.has(id));\n const removed = previous.installed_rules.filter((id) => !currentSet.has(id));\n const sourceChanged = previous.sourceHash !== currentSourceHash;\n\n const status = added.length > 0 || removed.length > 0 || sourceChanged ? 'changed' : 'up-to-date';\n\n return { status, added, removed, sourceChanged };\n};\n","import { join } from 'node:path';\nimport { wrapWithHeader } from './managed-header.js';\nimport { TOOL_OUTPUT_MAP } from './tool-output.js';\nimport type { ToolId } from './tool-output.js';\nimport type { ToolRenderResult } from './renderer.js';\n\n// Codex has no settings.json — plan directory convention must live in AGENTS.md\nconst CODEX_PLAN_SECTION =\n '\\n\\n---\\n\\n## Plan\\n\\nSave plans to `.codex/plans/<timestamp>-<topic>.md` when creating or updating plans in plan mode.';\n\nexport type FileAction = {\n relativePath: string;\n content: string;\n};\n\nexport const buildInstallPlan = (params: {\n toolId: ToolId;\n renderResult: ToolRenderResult;\n meta: { sourceHash: string; generatedAt: string };\n}): readonly FileAction[] => {\n const { toolId, renderResult, meta } = params;\n\n if (toolId === 'claude-code' && renderResult.tool === 'claude-code') {\n return renderResult.files.map(({ relativePath, content }) => ({\n relativePath,\n content: wrapWithHeader(content, meta),\n }));\n }\n\n if (\n (toolId === 'codex' && renderResult.tool === 'codex') ||\n (toolId === 'gemini' && renderResult.tool === 'gemini')\n ) {\n const config = TOOL_OUTPUT_MAP[toolId];\n const actions: FileAction[] = [];\n\n if (renderResult.rootContent) {\n const rootContent = toolId === 'codex' ? renderResult.rootContent + CODEX_PLAN_SECTION : renderResult.rootContent;\n actions.push({\n relativePath: join(config.dir, config.rootFileName),\n content: wrapWithHeader(rootContent, meta),\n });\n }\n\n if (renderResult.domainContent) {\n actions.push({\n relativePath: join(config.dir, config.domainFileName),\n content: wrapWithHeader(renderResult.domainContent, meta),\n });\n }\n\n return actions;\n }\n\n return [];\n};\n","import { join } from 'node:path';\nimport { TOOL_OUTPUT_MAP } from './tool-output.js';\nimport type { Manifest } from './schemas/index.js';\n\n/**\n * manifest에 installed_files가 없는 구버전 manifest를 위한 fallback.\n * manifest의 tools/workspaces/installed_rules 정보를 기반으로\n * 실제 설치됐을 파일 경로를 역산한다.\n */\nexport const inferInstalledFiles = (manifest: Manifest): string[] => {\n const files: string[] = [];\n const isMonorepo = manifest.workspaces !== undefined;\n\n for (const toolId of manifest.tools) {\n if (toolId === 'claude-code') {\n // claude-code: .claude/rules/{ruleId}.md\n const config = TOOL_OUTPUT_MAP['claude-code'];\n for (const ruleId of manifest.installed_rules) {\n files.push(join(config.rulesDir, `${ruleId}${config.fileExtension}`));\n }\n } else if (toolId === 'codex') {\n const config = TOOL_OUTPUT_MAP['codex'];\n if (!isMonorepo) {\n // 비모노: .codex/AGENTS.md + .codex/AGENTS.override.md (domain 있으면)\n files.push(join(config.dir, config.rootFileName));\n files.push(join(config.dir, config.domainFileName));\n } else {\n // 모노: .codex/AGENTS.md (global) + {workspace}/AGENTS.override.md (domain)\n files.push(join(config.dir, config.rootFileName));\n for (const ws of Object.keys(manifest.workspaces ?? {})) {\n files.push(join(ws, config.domainFileName));\n }\n }\n } else if (toolId === 'gemini') {\n const config = TOOL_OUTPUT_MAP['gemini'];\n if (!isMonorepo) {\n // 비모노: .gemini/GEMINI.md\n files.push(join(config.dir, config.rootFileName));\n } else {\n // 모노: .gemini/GEMINI.md (global) + {workspace}/GEMINI.md (domain)\n files.push(join(config.dir, config.rootFileName));\n for (const ws of Object.keys(manifest.workspaces ?? {})) {\n files.push(join(ws, config.domainFileName));\n }\n }\n }\n }\n\n // 중복 제거 (codex 비모노에서 rootFileName === domainFileName인 경우 대비)\n return [...new Set(files)];\n};\n","import { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\n// src/core/* 또는 dist/bin/index.js 기준에서도 공통으로 패키지 루트/data를 가리키도록 계산\n// src/core/paths.ts → ../../data = apps/cli/data\n// dist/bin/index.js (bundle) → ../../data = apps/cli/data\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nexport const COMPILER_DATA_DIR = resolve(__dirname, '..', '..', 'data');\n","import { join } from 'node:path';\nimport { COMPILER_DATA_DIR } from '@/core/index.js';\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 '@/core/index.js';\nimport type { FileAction } from '@/core/index.js';\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 '@/core/index.js';\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 '@/core/index.js';\nimport type { FileAction } from '@/core/index.js';\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 '@/core/index.js';\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 '@/core/index.js';\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 '@/core/index.js';\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","z","z","z","p","global","domain","readFileSync","readdirSync","resolve","readdirSync","readFileSync","resolve","readFileSync","join","join","readFileSync","join","join","join","join","dirname","resolve","join","join","readdirSync","join","resolve","existsSync","mkdirSync","readFileSync","writeFileSync","dirname","resolve","resolve","existsSync","readFileSync","writeFileSync","mkdirSync","dirname","existsSync","mkdirSync","readFileSync","writeFileSync","join","join","r","p","join","r","join","p","p","rmSync","existsSync","readFileSync","readdirSync","writeFileSync","resolve","dirname","resolve","existsSync","readFileSync","writeFileSync","readdirSync","dirname","rmSync"]}
1
+ {"version":3,"sources":["../../src/bin/index.ts","../../src/commands/init.ts","../../src/core/schemas/rule.schema.ts","../../src/core/schemas/preset.schema.ts","../../src/core/schemas/manifest.schema.ts","../../src/core/loader.ts","../../src/core/renderer.ts","../../src/core/tool-output.ts","../../src/core/source-hash.ts","../../src/core/managed-header.ts","../../src/core/manifest-io.ts","../../src/core/diff.ts","../../src/core/install-plan.ts","../../src/core/uninstall-plan.ts","../../src/core/paths.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 '@/core/index.js';\nimport {\n loadAllRules,\n loadPresets,\n resolvePresetRules,\n resolvePresetRuleGroups,\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 '@/core/index.js';\nimport type { FileAction } from '@/core/index.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\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 presetRuleGroups = resolvePresetRuleGroups(preset, allRules);\n const globalGroups = presetRuleGroups.filter((group) => group.rules.every(isGlobalRule));\n const domainGroups = presetRuleGroups.filter((group) => !group.rules.every(isGlobalRule));\n const globalGroupIds = globalGroups.map((group) => group.id);\n const globalRules =\n globalGroupIds.length > 0 ? resolvePresetRules({ ...preset, rules: globalGroupIds }, allRules) : [];\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 (domainGroups.length === 0) {\n return { workspace: workspaceName, preset, finalRules: resolvePresetRules(preset, allRules) };\n }\n\n // Domain rules: 제외 가능\n const selectedDomain = await p.multiselect<string>({\n message: `[${workspaceName}] 도메인 규칙 선택 (해제하여 제외)`,\n options: domainGroups.map((group) => ({ value: group.id, label: group.id })),\n initialValues: domainGroups.map((group) => group.id),\n required: false,\n });\n if (p.isCancel(selectedDomain)) return null;\n\n const selectedLogicalRuleIds = [...globalGroupIds, ...(selectedDomain as string[])];\n return {\n workspace: workspaceName,\n preset,\n finalRules: resolvePresetRules({ ...preset, rules: selectedLogicalRuleIds }, allRules),\n };\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","/**\n * Rule = SSOT의 최소 지식 단위. 하나의 코딩 컨벤션/아키텍처 규칙을 YAML로 구조화한 것.\n */\nimport { z } from 'zod';\n\nexport const DecisionTableEntrySchema = z\n .object({\n when: z.string().min(1),\n then: z.string().min(1),\n /** 조건부 규칙에서 회피해야 할 패턴 */\n avoid: z.string().min(1).optional(),\n })\n .strict();\n\nexport const RuleContentSchema = z\n .object({\n /** Anti-pattern 규칙 ('하지 마라'). guidelines보다 항상 상단 렌더링 */\n constraints: z.array(z.string().min(1)),\n /** Positive 규칙 ('해라') */\n guidelines: z.array(z.string().min(1)),\n /** 조건부 규칙. when→then→avoid 구조 */\n decision_table: z.array(DecisionTableEntrySchema).optional(),\n })\n .strict();\n\nexport const RuleSchema = z\n .object({\n id: z.string().regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, 'id must be kebab-case'),\n category: z.string().min(1),\n tags: z.array(z.string().min(1)),\n /** 0-100. 높을수록 생성 파일 상단 배치 (U-shaped attention 최적화) */\n priority: z.number().int().min(0).max(100),\n content: RuleContentSchema,\n })\n .strict();\n\nexport type DecisionTableEntry = z.infer<typeof DecisionTableEntrySchema>;\nexport type RuleContent = z.infer<typeof RuleContentSchema>;\nexport type Rule = z.infer<typeof RuleSchema>;\n","import { z } from 'zod';\n\nexport const PresetSchema = z\n .object({\n id: z\n .string()\n .regex(/^[a-z][a-z0-9-]*$/)\n .min(1),\n description: z.string().min(1),\n rules: z.array(z.string().min(1)).min(1),\n })\n .strict();\n\nexport type Preset = z.infer<typeof PresetSchema>;\n","/**\n * Manifest = 설치 추적 메타데이터. CLI가 이전 설치 상태를 기억하기 위한 JSON.\n */\nimport { z } from 'zod';\n\nexport const SCOPES = {\n PROJECT: 'project',\n} as const;\n\n/** 모노레포 워크스페이스별 preset + rules 추적 */\nconst WorkspaceEntrySchema = z\n .object({\n preset: z.string().min(1),\n rules: z.array(z.string().min(1)),\n })\n .strict();\n\nexport type WorkspaceEntry = z.infer<typeof WorkspaceEntrySchema>;\n\nexport const ManifestSchema = z\n .object({\n tools: z.array(z.string().min(1)).min(1),\n scope: z.literal('project'),\n /** 비모노레포 단일 preset */\n preset: z.string().min(1).optional(),\n /** 모노레포: workspace path → { preset, rules } */\n workspaces: z.record(z.string(), WorkspaceEntrySchema).optional(),\n installed_rules: z.array(z.string().min(1)),\n /** 실제 디스크에 쓰여진 파일 상대 경로 목록 (uninstall용). 기존 manifest 호환성 위해 optional */\n installed_files: z.array(z.string().min(1)).optional(),\n /** non-managed 파일에 섹션을 append한 경우 추적 (uninstall 시 섹션만 제거) */\n appended_files: z.array(z.string().min(1)).optional(),\n /** SSOT 데이터 파일들의 deterministic SHA-256 해시 (6자리 hex). diff/update 판단 기준 */\n sourceHash: z.string().regex(/^[a-f0-9]{6}$/, 'sourceHash must be 6 lowercase hex chars'),\n generatedAt: z.string().datetime({ offset: true }),\n })\n .strict();\n\nexport type Manifest = z.infer<typeof ManifestSchema>;\n","import { readFileSync, readdirSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { parse } from 'yaml';\nimport { RuleSchema, PresetSchema } from './schemas/index.js';\nimport type { Rule, Preset } from './schemas/index.js';\n\ntype PresetRuleBundles = Readonly<Record<string, Readonly<Record<string, readonly string[]>>>>;\n\nexport const PRESET_RULE_BUNDLES: PresetRuleBundles = {\n 'frontend-web': {\n graphql: ['graphql-core', 'graphql-client-web'],\n },\n 'frontend-app': {\n graphql: ['graphql-core', 'graphql-client-app'],\n },\n 'backend-ts': {\n graphql: ['graphql-core', 'graphql-server'],\n },\n} as const;\n\nexport type PresetRuleGroup = {\n id: string;\n rules: Rule[];\n};\n\n// priority 내림차순 정렬 (높을수록 상단 → U-shaped attention)\nexport const sortRulesByPriority = (rules: readonly Rule[]): Rule[] =>\n [...rules].sort((a, b) => b.priority - a.priority);\n\nconst deduplicateRulesById = (rules: readonly Rule[]): Rule[] => {\n const seen = new Set<string>();\n return rules.filter((rule) => {\n if (seen.has(rule.id)) return false;\n seen.add(rule.id);\n return true;\n });\n};\n\nconst resolveBundledRuleIds = (presetId: string, logicalRuleId: string): readonly string[] => {\n const presetBundles = PRESET_RULE_BUNDLES[presetId];\n if (!presetBundles) return [logicalRuleId];\n return presetBundles[logicalRuleId] ?? [logicalRuleId];\n};\n\nconst resolveRuleById = (ruleId: string, allRules: readonly Rule[], context?: string): Rule => {\n const found = allRules.find((rule) => rule.id === ruleId);\n if (!found) {\n const suffix = context ? ` (from ${context})` : '';\n throw new Error(`Rule not found: ${ruleId}${suffix}`);\n }\n return found;\n};\n\n// presets.yaml의 Record<id, {description, rules}> → Preset[] 변환\nexport const parseRawPresets = (raw: Record<string, { description: string; rules: string[] }>): Preset[] =>\n Object.entries(raw).map(([id, value]) => PresetSchema.parse({ id, ...value }));\n\nexport const resolvePresetRuleGroups = (preset: Preset, allRules: readonly Rule[]): PresetRuleGroup[] =>\n preset.rules.map((logicalRuleId) => {\n const bundledRuleIds = resolveBundledRuleIds(preset.id, logicalRuleId);\n const rules = bundledRuleIds.map((ruleId) => resolveRuleById(ruleId, allRules, `${preset.id}:${logicalRuleId}`));\n return { id: logicalRuleId, rules };\n });\n\n// TUI 세부조정: 사용자가 해제한 rule ID 목록을 제외 (순서 유지)\nexport const excludeRules = (rules: readonly Rule[], excludeIds: readonly string[]): Rule[] => {\n const excludeSet = new Set(excludeIds);\n return rules.filter((r) => !excludeSet.has(r.id));\n};\n\n// preset.rules(논리 ID 포함) 목록을 실제 rule로 확장 + priority 정렬, 누락 시 throw\nexport const resolvePresetRules = (preset: Preset, allRules: readonly Rule[]): Rule[] => {\n const groups = resolvePresetRuleGroups(preset, allRules);\n const resolved = deduplicateRulesById(groups.flatMap((group) => group.rules));\n return sortRulesByPriority(resolved);\n};\n\nexport const loadRuleFile = (filePath: string): Rule => {\n const raw = readFileSync(filePath, 'utf-8');\n return RuleSchema.parse(parse(raw));\n};\n\n// readdirSync + .yaml 필터 + sort\nexport const loadAllRules = (rulesDir: string): Rule[] => {\n const files = readdirSync(rulesDir)\n .filter((f) => f.endsWith('.yaml'))\n .sort();\n return files.map((f) => loadRuleFile(resolve(rulesDir, f)));\n};\n\nexport const loadPresets = (presetsPath: string): Preset[] => {\n const raw = readFileSync(presetsPath, 'utf-8');\n const data = parse(raw) as Record<string, { description: string; rules: string[] }>;\n return parseRawPresets(data);\n};\n","import { join } from 'node:path';\nimport type { Rule, DecisionTableEntry } from './schemas/index.js';\nimport { GLOBAL_CATEGORIES, CLAUDE_CODE_PATH_GLOBS, TOOL_OUTPUT_MAP } from './tool-output.js';\nimport type { ToolId } from './tool-output.js';\n\n// \"react-typescript\" → \"React Typescript\"\nexport const ruleIdToTitle = (id: string): string =>\n id\n .split('-')\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(' ');\n\n// DecisionTableEntry[] → Markdown 테이블 (pipe 문자 &#124; escape)\nexport const renderDecisionTable = (entries: readonly DecisionTableEntry[]): string => {\n const escape = (s: string) => s.replace(/\\|/g, '&#124;');\n const hasAvoid = entries.some((e) => e.avoid !== undefined);\n\n const header = hasAvoid ? '| When | Then | Avoid |\\n|------|------|-------|' : '| When | Then |\\n|------|------|';\n\n const rows = entries.map((e) => {\n const when = escape(e.when);\n const then = escape(e.then);\n if (hasAvoid) {\n const avoid = e.avoid ? escape(e.avoid) : '';\n return `| ${when} | ${then} | ${avoid} |`;\n }\n return `| ${when} | ${then} |`;\n });\n\n return [header, ...rows].join('\\n');\n};\n\n// 단일 Rule → Markdown (빈 섹션 생략)\nexport const renderRuleToMarkdown = (rule: Rule): string => {\n const sections: string[] = [`# ${ruleIdToTitle(rule.id)}`];\n\n if (rule.content.constraints.length > 0) {\n sections.push('## Constraints');\n sections.push(rule.content.constraints.map((c) => `- ${c}`).join('\\n'));\n }\n\n if (rule.content.guidelines.length > 0) {\n sections.push('## Guidelines');\n sections.push(rule.content.guidelines.map((g) => `- ${g}`).join('\\n'));\n }\n\n if (rule.content.decision_table && rule.content.decision_table.length > 0) {\n sections.push('## Decision Table');\n sections.push(renderDecisionTable(rule.content.decision_table));\n }\n\n return sections.join('\\n\\n');\n};\n\n// Rule[] → 단일 Markdown (--- separator, single-file 모드용)\nexport const renderRulesToMarkdown = (rules: readonly Rule[]): string =>\n rules.map(renderRuleToMarkdown).join('\\n\\n---\\n\\n');\n\n// Rule이 global 카테고리에 속하는지 판별\nexport const isGlobalRule = (rule: Rule): boolean => (GLOBAL_CATEGORIES as readonly string[]).includes(rule.category);\n\n// Rule[] → { global, domain } 분리\nexport const partitionRules = (rules: readonly Rule[]): { global: Rule[]; domain: Rule[] } => {\n const global: Rule[] = [];\n const domain: Rule[] = [];\n for (const rule of rules) {\n if (isGlobalRule(rule)) {\n global.push(rule);\n } else {\n domain.push(rule);\n }\n }\n return { global, domain };\n};\n\n// glob 배열 → YAML frontmatter 블록\nexport const renderFrontmatter = (paths: readonly string[]): string => {\n const lines = paths.map((p) => ` - \"${p}\"`).join('\\n');\n return `---\\npaths:\\n${lines}\\n---`;\n};\n\nexport type WorkspaceMapping = {\n path: string;\n ruleIds: readonly string[];\n};\n\n// 단일 Rule → Claude Code용 Markdown\n// domain 룰이면서 glob 매핑이 있으면 paths: frontmatter 추가 (단일 프로젝트 전용)\n// global 룰 또는 매핑 없는 domain 룰 → frontmatter 없음\nexport const renderClaudeCodeRule = (rule: Rule): string => {\n const globs = CLAUDE_CODE_PATH_GLOBS[rule.id];\n if (!isGlobalRule(rule) && globs !== undefined) {\n return `${renderFrontmatter(globs)}\\n\\n${renderRuleToMarkdown(rule)}`;\n }\n return renderRuleToMarkdown(rule);\n};\n\n// 도구별 렌더링 결과 타입 (tagged union)\nexport type ClaudeCodeRenderResult = {\n tool: 'claude-code';\n files: { relativePath: string; content: string }[];\n};\n\nexport type CodexRenderResult = {\n tool: 'codex';\n rootContent: string;\n domainContent: string;\n};\n\nexport type GeminiRenderResult = {\n tool: 'gemini';\n rootContent: string;\n domainContent: string;\n};\n\nexport type ToolRenderResult = ClaudeCodeRenderResult | CodexRenderResult | GeminiRenderResult;\n\n// CLI 진입점: toolId + rules → 도구별 렌더링 결과\nexport const renderForTool = (\n toolId: ToolId,\n rules: readonly Rule[],\n workspaceMappings?: readonly WorkspaceMapping[],\n): ToolRenderResult => {\n const config = TOOL_OUTPUT_MAP[toolId];\n\n if (toolId === 'claude-code') {\n const { rulesDir, fileExtension } = config as (typeof TOOL_OUTPUT_MAP)['claude-code'];\n\n if (!workspaceMappings || workspaceMappings.length === 0) {\n // 단일 프로젝트: domain 룰에 paths: frontmatter (path-scoped)\n const files = rules.map((rule) => ({\n relativePath: join(rulesDir, `${rule.id}${fileExtension}`),\n content: renderClaudeCodeRule(rule),\n }));\n return { tool: 'claude-code', files };\n }\n\n // 모노레포: global → .claude/rules/, domain → {workspace}/CLAUDE.md (진짜 지연 로딩)\n const { global, domain } = partitionRules(rules);\n\n const globalFiles = global.map((rule) => ({\n relativePath: join(rulesDir, `${rule.id}${fileExtension}`),\n content: renderRuleToMarkdown(rule), // global은 frontmatter 불필요\n }));\n\n const workspaceFiles: { relativePath: string; content: string }[] = [];\n for (const ws of workspaceMappings) {\n const wsRules = domain.filter((r) => ws.ruleIds.includes(r.id));\n if (wsRules.length === 0) continue;\n workspaceFiles.push({\n relativePath: join(ws.path, 'CLAUDE.md'),\n content: renderRulesToMarkdown(wsRules),\n });\n }\n\n return { tool: 'claude-code', files: [...globalFiles, ...workspaceFiles] };\n }\n\n const { global, domain } = partitionRules(rules);\n const rootContent = renderRulesToMarkdown(global);\n const domainContent = renderRulesToMarkdown(domain);\n\n if (toolId === 'codex') {\n return { tool: 'codex', rootContent, domainContent };\n }\n\n // gemini\n return { tool: 'gemini', rootContent, domainContent };\n};\n","// Global 성격의 category (항상 로딩, frontmatter 없음)\nexport const GLOBAL_CATEGORIES = ['persona', 'communication', 'philosophy', 'convention', 'standard'] as const;\n\n// Claude Code paths: frontmatter용 Rule ID → glob 매핑\n// src/ 없이 루트를 src처럼 쓰는 React/Next.js 프로젝트 고려\n// 매핑에 없는 domain 룰 → frontmatter 없이 항상 로딩 (안전 fallback)\nexport const CLAUDE_CODE_PATH_GLOBS: Readonly<Record<string, readonly string[]>> = {\n typescript: ['**/*.ts', '**/*.tsx'],\n 'react-typescript': ['**/*.tsx', '**/*.jsx'],\n nextjs: ['**/app/**', 'next.config.*', '**/middleware.ts'],\n nestjs: ['**/*.module.ts', '**/*.controller.ts', '**/*.service.ts'],\n 'nestjs-graphql': ['**/*.resolver.ts'],\n 'graphql-core': ['**/*.graphql', '**/*.gql'],\n 'graphql-client-web': ['**/*.graphql', '**/*.gql', '**/*.tsx', '**/*.ts'],\n 'graphql-client-app': ['**/*.graphql', '**/*.gql', 'lib/**/*.dart'],\n 'graphql-server': ['**/*.graphql', '**/*.gql', '**/*.resolver.ts'],\n 'prisma-postgresql': ['prisma/**', '**/*.prisma'],\n 'shadcn-ui': ['**/components/ui/**'],\n flutter: ['lib/**/*.dart'],\n python: ['**/*.py'],\n fastapi: ['**/routers/**', '**/main.py'],\n sqlalchemy: ['**/models/**/*.py', 'alembic/**'],\n 'data-pipeline-python': ['**/pipelines/**', '**/etl/**'],\n 'ai-llm-python': ['**/agents/**', '**/chains/**'],\n 'libs-frontend-web': ['**/*.tsx', '**/*.ts'],\n 'libs-frontend-app': ['lib/**/*.dart'],\n 'libs-backend-ts': ['**/*.ts'],\n 'libs-backend-python': ['**/*.py'],\n};\n\nexport const TOOL_OUTPUT_MAP = {\n 'claude-code': {\n mode: 'multi-file' as const,\n rulesDir: '.claude/rules',\n fileExtension: '.md',\n // single: path-scoped (paths: frontmatter) / monorepo: hierarchical ({workspace}/CLAUDE.md)\n contextStrategy: 'hybrid' as const,\n },\n codex: {\n mode: 'multi-file' as const,\n dir: '',\n rootFileName: 'AGENTS.md', // global 룰\n domainFileName: 'AGENTS.override.md', // domain 룰 (하위 폴더)\n contextStrategy: 'hierarchical' as const, // 루트 + 하위 폴더 JIT\n },\n gemini: {\n mode: 'multi-file' as const,\n dir: '.gemini',\n rootFileName: 'GEMINI.md', // global 룰\n domainFileName: 'GEMINI.md', // domain 룰 (하위 폴더)\n contextStrategy: 'hierarchical' as const, // 루트 + 하위 폴더 JIT\n },\n} as const;\n\nexport type ToolId = keyof typeof TOOL_OUTPUT_MAP;\n","import { createHash } from 'node:crypto';\nimport { readFileSync, readdirSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { ManifestSchema } from './schemas/index.js';\nimport type { Manifest } from './schemas/index.js';\n\n// 문자열 배열 → SHA-256 → 6-hex (caller가 정렬 책임)\nexport const computeHash = (contents: readonly string[]): string =>\n createHash('sha256').update(contents.join('')).digest('hex').slice(0, 6);\n\n// rulesDir 내 YAML 파일들을 alphabetical 정렬 후 해싱\nexport const computeSourceHash = (rulesDir: string): string => {\n const files = readdirSync(rulesDir)\n .filter((f) => f.endsWith('.yaml'))\n .sort();\n const contents = files.map((f) => readFileSync(resolve(rulesDir, f), 'utf-8'));\n return computeHash(contents);\n};\n\n// Manifest Builder (Pure, 단 generatedAt에 현재 시각 사용)\nexport const buildManifest = (params: {\n tools: readonly string[];\n scope: 'project';\n preset?: string;\n workspaces?: Record<string, { preset: string; rules: string[] }>;\n installedRules: readonly string[];\n installedFiles?: readonly string[];\n appendedFiles?: readonly string[];\n sourceHash: string;\n}): Manifest =>\n ManifestSchema.parse({\n tools: [...params.tools],\n scope: params.scope,\n preset: params.preset,\n workspaces: params.workspaces,\n installed_rules: [...params.installedRules],\n installed_files: params.installedFiles ? [...params.installedFiles] : undefined,\n appended_files: params.appendedFiles && params.appendedFiles.length > 0 ? [...params.appendedFiles] : undefined,\n sourceHash: params.sourceHash,\n generatedAt: new Date().toISOString(),\n });\n","const MANAGED_MARKER = '<!-- managed by ai-ops -->';\nconst META_PATTERN = /^<!-- sourceHash: ([a-f0-9]{6}) \\| generatedAt: (.+) -->$/;\n\nconst SECTION_START = '<!-- ai-ops:start -->';\nconst SECTION_END = '<!-- ai-ops:end -->';\n\nexport const wrapWithHeader = (content: string, meta: { sourceHash: string; generatedAt: string }): string => {\n const metaLine = `<!-- sourceHash: ${meta.sourceHash} | generatedAt: ${meta.generatedAt} -->`;\n return `${MANAGED_MARKER}\\n${metaLine}\\n\\n${content}`;\n};\n\nexport const isManagedFile = (content: string): boolean => content.startsWith(MANAGED_MARKER);\n\nexport const parseManagedHeader = (content: string): { sourceHash: string; generatedAt: string } | null => {\n if (!isManagedFile(content)) return null;\n\n const lines = content.split('\\n');\n const metaLine = lines[1] ?? '';\n const match = META_PATTERN.exec(metaLine);\n if (!match) return null;\n\n return { sourceHash: match[1], generatedAt: match[2] };\n};\n\nexport const stripManagedHeader = (content: string): string => {\n if (!isManagedFile(content)) return content;\n\n const lines = content.split('\\n');\n // marker line + meta line + blank line = 3 lines\n const stripped = lines.slice(3).join('\\n');\n return stripped;\n};\n\nexport const wrapWithSection = (content: string, meta: { sourceHash: string; generatedAt: string }): string => {\n const metaLine = `<!-- sourceHash: ${meta.sourceHash} | generatedAt: ${meta.generatedAt} -->`;\n return `${SECTION_START}\\n${metaLine}\\n\\n${content}\\n${SECTION_END}`;\n};\n\nexport const hasAiOpsSection = (content: string): boolean =>\n content.includes(SECTION_START) && content.includes(SECTION_END);\n\nexport const stripAiOpsSection = (content: string): string => {\n const startIdx = content.indexOf(SECTION_START);\n const endIdx = content.indexOf(SECTION_END);\n if (startIdx === -1 || endIdx === -1) return content;\n\n const before = content.slice(0, startIdx).trimEnd();\n const after = content.slice(endIdx + SECTION_END.length).trimStart();\n return before + (after ? '\\n\\n' + after : '') + '\\n';\n};\n\nexport const replaceAiOpsSection = (existing: string, newSection: string): string => {\n const startIdx = existing.indexOf(SECTION_START);\n const endIdx = existing.indexOf(SECTION_END);\n if (startIdx === -1 || endIdx === -1) return existing;\n\n const before = existing.slice(0, startIdx).trimEnd();\n const after = existing.slice(endIdx + SECTION_END.length).trimStart();\n return before + '\\n\\n' + newSection + (after ? '\\n\\n' + after : '') + '\\n';\n};\n","import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { ManifestSchema } from './schemas/index.js';\nimport type { Manifest } from './schemas/index.js';\n\nexport const MANIFEST_FILENAME = '.ai-ops-manifest.json';\n\n// Pure\nexport const parseManifest = (json: string): Manifest => ManifestSchema.parse(JSON.parse(json));\n\nexport const serializeManifest = (manifest: Manifest): string => JSON.stringify(manifest, null, 2) + '\\n';\n\n// I/O\nexport const resolveManifestPath = (basePath: string): string => join(basePath, MANIFEST_FILENAME);\n\nexport const readManifest = (manifestPath: string): Manifest | null => {\n let raw: string;\n try {\n raw = readFileSync(manifestPath, 'utf-8');\n } catch {\n return null;\n }\n return parseManifest(raw);\n};\n\nexport const writeManifest = (manifestPath: string, manifest: Manifest): void => {\n mkdirSync(dirname(manifestPath), { recursive: true });\n writeFileSync(manifestPath, serializeManifest(manifest), 'utf-8');\n};\n","import type { Manifest } from './schemas/index.js';\n\nexport type DiffResult = {\n status: 'up-to-date' | 'changed';\n added: readonly string[];\n removed: readonly string[];\n sourceChanged: boolean;\n};\n\nexport const computeDiff = (params: {\n previous: Manifest;\n currentRules: readonly string[];\n currentSourceHash: string;\n}): DiffResult => {\n const { previous, currentRules, currentSourceHash } = params;\n\n const previousSet = new Set(previous.installed_rules);\n const currentSet = new Set(currentRules);\n\n const added = currentRules.filter((id) => !previousSet.has(id));\n const removed = previous.installed_rules.filter((id) => !currentSet.has(id));\n const sourceChanged = previous.sourceHash !== currentSourceHash;\n\n const status = added.length > 0 || removed.length > 0 || sourceChanged ? 'changed' : 'up-to-date';\n\n return { status, added, removed, sourceChanged };\n};\n","import { join } from 'node:path';\nimport { wrapWithHeader } from './managed-header.js';\nimport { TOOL_OUTPUT_MAP } from './tool-output.js';\nimport type { ToolId } from './tool-output.js';\nimport type { ToolRenderResult } from './renderer.js';\n\n// Codex has no settings.json — plan directory convention must live in AGENTS.md\nconst CODEX_PLAN_SECTION =\n '\\n\\n---\\n\\n## Plan\\n\\nSave plans to `.codex/plans/<timestamp>-<topic>.md` when creating or updating plans in plan mode.';\n\nexport type FileAction = {\n relativePath: string;\n content: string;\n};\n\nexport const buildInstallPlan = (params: {\n toolId: ToolId;\n renderResult: ToolRenderResult;\n meta: { sourceHash: string; generatedAt: string };\n}): readonly FileAction[] => {\n const { toolId, renderResult, meta } = params;\n\n if (toolId === 'claude-code' && renderResult.tool === 'claude-code') {\n return renderResult.files.map(({ relativePath, content }) => ({\n relativePath,\n content: wrapWithHeader(content, meta),\n }));\n }\n\n if (\n (toolId === 'codex' && renderResult.tool === 'codex') ||\n (toolId === 'gemini' && renderResult.tool === 'gemini')\n ) {\n const config = TOOL_OUTPUT_MAP[toolId];\n const actions: FileAction[] = [];\n\n if (renderResult.rootContent) {\n const rootContent = toolId === 'codex' ? renderResult.rootContent + CODEX_PLAN_SECTION : renderResult.rootContent;\n actions.push({\n relativePath: join(config.dir, config.rootFileName),\n content: wrapWithHeader(rootContent, meta),\n });\n }\n\n if (renderResult.domainContent) {\n actions.push({\n relativePath: join(config.dir, config.domainFileName),\n content: wrapWithHeader(renderResult.domainContent, meta),\n });\n }\n\n return actions;\n }\n\n return [];\n};\n","import { join } from 'node:path';\nimport { TOOL_OUTPUT_MAP } from './tool-output.js';\nimport type { Manifest } from './schemas/index.js';\n\n/**\n * manifest에 installed_files가 없는 구버전 manifest를 위한 fallback.\n * manifest의 tools/workspaces/installed_rules 정보를 기반으로\n * 실제 설치됐을 파일 경로를 역산한다.\n */\nexport const inferInstalledFiles = (manifest: Manifest): string[] => {\n const files: string[] = [];\n const isMonorepo = manifest.workspaces !== undefined;\n\n for (const toolId of manifest.tools) {\n if (toolId === 'claude-code') {\n // claude-code: .claude/rules/{ruleId}.md\n const config = TOOL_OUTPUT_MAP['claude-code'];\n for (const ruleId of manifest.installed_rules) {\n files.push(join(config.rulesDir, `${ruleId}${config.fileExtension}`));\n }\n } else if (toolId === 'codex') {\n const config = TOOL_OUTPUT_MAP['codex'];\n if (!isMonorepo) {\n // 비모노: .codex/AGENTS.md + .codex/AGENTS.override.md (domain 있으면)\n files.push(join(config.dir, config.rootFileName));\n files.push(join(config.dir, config.domainFileName));\n } else {\n // 모노: .codex/AGENTS.md (global) + {workspace}/AGENTS.override.md (domain)\n files.push(join(config.dir, config.rootFileName));\n for (const ws of Object.keys(manifest.workspaces ?? {})) {\n files.push(join(ws, config.domainFileName));\n }\n }\n } else if (toolId === 'gemini') {\n const config = TOOL_OUTPUT_MAP['gemini'];\n if (!isMonorepo) {\n // 비모노: .gemini/GEMINI.md\n files.push(join(config.dir, config.rootFileName));\n } else {\n // 모노: .gemini/GEMINI.md (global) + {workspace}/GEMINI.md (domain)\n files.push(join(config.dir, config.rootFileName));\n for (const ws of Object.keys(manifest.workspaces ?? {})) {\n files.push(join(ws, config.domainFileName));\n }\n }\n }\n }\n\n // 중복 제거 (codex 비모노에서 rootFileName === domainFileName인 경우 대비)\n return [...new Set(files)];\n};\n","import { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\n// src/core/* 또는 dist/bin/index.js 기준에서도 공통으로 패키지 루트/data를 가리키도록 계산\n// src/core/paths.ts → ../../data = apps/cli/data\n// dist/bin/index.js (bundle) → ../../data = apps/cli/data\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nexport const COMPILER_DATA_DIR = resolve(__dirname, '..', '..', 'data');\n","import { join } from 'node:path';\nimport { COMPILER_DATA_DIR } from '@/core/index.js';\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 '@/core/index.js';\nimport type { FileAction } from '@/core/index.js';\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 '@/core/index.js';\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 '@/core/index.js';\nimport type { FileAction } from '@/core/index.js';\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 = workspaceEntries.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 '@/core/index.js';\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 '@/core/index.js';\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 '@/core/index.js';\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","z","z","z","p","global","domain","readFileSync","readdirSync","resolve","readdirSync","readFileSync","resolve","readFileSync","join","join","readFileSync","join","join","join","join","dirname","resolve","join","join","readdirSync","join","resolve","existsSync","mkdirSync","readFileSync","writeFileSync","dirname","resolve","resolve","existsSync","readFileSync","writeFileSync","mkdirSync","dirname","existsSync","mkdirSync","readFileSync","writeFileSync","join","join","r","p","join","r","join","p","p","rmSync","existsSync","readFileSync","readdirSync","writeFileSync","resolve","dirname","resolve","existsSync","readFileSync","writeFileSync","readdirSync","dirname","rmSync"]}
package/package.json CHANGED
@@ -1,8 +1,17 @@
1
1
  {
2
2
  "name": "ai-ops-cli",
3
- "version": "0.1.10",
3
+ "version": "0.1.13",
4
4
  "description": "CLI for ai-ops scaffolding — manage AI tool rules and presets",
5
5
  "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/MincheolC/ai-ops-cli.git",
9
+ "directory": "apps/cli"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/MincheolC/ai-ops-cli/issues"
13
+ },
14
+ "homepage": "https://github.com/MincheolC/ai-ops-cli#readme",
6
15
  "type": "module",
7
16
  "engines": {
8
17
  "node": ">=18"
@@ -1,34 +0,0 @@
1
- id: graphql
2
- category: api
3
- tags:
4
- - graphql
5
- - api
6
- - schema-design
7
- priority: 48
8
- content:
9
- constraints:
10
- - 'DO NOT rely on implicit nullability. Mark every field intentionally (prefer non-null by default).'
11
- - 'DO NOT perform mutations in Query resolvers. Reads belong to Query, writes belong to Mutation.'
12
- - 'DO NOT return a generic Error type. Use operation-specific typed errors (union or payload errors).'
13
- - 'DO NOT return unbounded lists. Every collection must support pagination.'
14
- - 'DO NOT expose internal implementation details in enum values. Use semantic SCREAMING_SNAKE_CASE names.'
15
- guidelines:
16
- - 'Prefer cursor pagination (first/after) for user-facing lists; use offset pagination only for bounded admin tables.'
17
- - 'Use a single input object for every mutation: mutation X($input: XInput!).'
18
- - 'Return mutation payload types that can carry both result data and structured errors.'
19
- - 'Use DataLoader for related entity resolution; field resolvers must not issue per-parent DB queries.'
20
- - 'Follow naming conventions: PascalCase types/enums, camelCase fields/args, SCREAMING_SNAKE_CASE enum values.'
21
- - 'Deprecate fields first with @deprecated(reason: "...") and remove only in the next major version.'
22
- decision_table:
23
- - when: 'A field returns a collection'
24
- then: 'Use cursor pagination with a stable cursor'
25
- avoid: 'Returning unbounded [Node!]! lists'
26
- - when: 'A mutation must report business failure'
27
- then: 'Use both a Payload wrapper type AND typed union errors together — Payload carries partial success data, union enables __typename switching; they are complementary, not alternatives'
28
- avoid: 'Throwing top-level GraphQL errors for expected failures, or using only a bare union without a Payload'
29
- - when: 'A field is removed or renamed'
30
- then: 'Deprecate first, migrate clients, then remove in next major'
31
- avoid: 'Immediate schema removal'
32
- - when: 'Filtering or sorting is complex'
33
- then: 'Define typed FilterInput/OrderByInput types'
34
- avoid: 'Accepting raw JSON or string filters'