ai-ops-compiler 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +3 -3
- package/dist/index.js +39 -29
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -229,7 +229,7 @@ declare const TOOL_OUTPUT_MAP: {
|
|
|
229
229
|
readonly mode: "multi-file";
|
|
230
230
|
readonly rulesDir: ".claude/rules";
|
|
231
231
|
readonly fileExtension: ".md";
|
|
232
|
-
readonly contextStrategy: "
|
|
232
|
+
readonly contextStrategy: "hybrid";
|
|
233
233
|
};
|
|
234
234
|
readonly codex: {
|
|
235
235
|
readonly mode: "multi-file";
|
|
@@ -262,11 +262,11 @@ type WorkspaceMapping = {
|
|
|
262
262
|
path: string;
|
|
263
263
|
ruleIds: readonly string[];
|
|
264
264
|
};
|
|
265
|
-
declare const renderClaudeCodeRule: (rule: Rule
|
|
265
|
+
declare const renderClaudeCodeRule: (rule: Rule) => string;
|
|
266
266
|
type ClaudeCodeRenderResult = {
|
|
267
267
|
tool: 'claude-code';
|
|
268
268
|
files: {
|
|
269
|
-
|
|
269
|
+
relativePath: string;
|
|
270
270
|
content: string;
|
|
271
271
|
}[];
|
|
272
272
|
};
|
package/dist/index.js
CHANGED
|
@@ -86,6 +86,9 @@ var loadPresets = (presetsPath) => {
|
|
|
86
86
|
return parseRawPresets(data);
|
|
87
87
|
};
|
|
88
88
|
|
|
89
|
+
// src/renderer.ts
|
|
90
|
+
import { join } from "path";
|
|
91
|
+
|
|
89
92
|
// src/tool-output.ts
|
|
90
93
|
var GLOBAL_CATEGORIES = ["persona", "communication", "philosophy", "convention", "standard"];
|
|
91
94
|
var CLAUDE_CODE_PATH_GLOBS = {
|
|
@@ -113,8 +116,8 @@ var TOOL_OUTPUT_MAP = {
|
|
|
113
116
|
mode: "multi-file",
|
|
114
117
|
rulesDir: ".claude/rules",
|
|
115
118
|
fileExtension: ".md",
|
|
116
|
-
|
|
117
|
-
|
|
119
|
+
// single: path-scoped (paths: frontmatter) / monorepo: hierarchical ({workspace}/CLAUDE.md)
|
|
120
|
+
contextStrategy: "hybrid"
|
|
118
121
|
},
|
|
119
122
|
codex: {
|
|
120
123
|
mode: "multi-file",
|
|
@@ -192,15 +195,8 @@ paths:
|
|
|
192
195
|
${lines}
|
|
193
196
|
---`;
|
|
194
197
|
};
|
|
195
|
-
var
|
|
196
|
-
const
|
|
197
|
-
if (!baseGlobs) return void 0;
|
|
198
|
-
const relevant = workspaceMappings.filter((ws) => ws.ruleIds.includes(rule.id));
|
|
199
|
-
if (relevant.length === 0) return baseGlobs;
|
|
200
|
-
return relevant.flatMap((ws) => baseGlobs.map((g) => `${ws.path}/${g}`));
|
|
201
|
-
};
|
|
202
|
-
var renderClaudeCodeRule = (rule, scopedGlobs) => {
|
|
203
|
-
const globs = scopedGlobs ?? CLAUDE_CODE_PATH_GLOBS[rule.id];
|
|
198
|
+
var renderClaudeCodeRule = (rule) => {
|
|
199
|
+
const globs = CLAUDE_CODE_PATH_GLOBS[rule.id];
|
|
204
200
|
if (!isGlobalRule(rule) && globs !== void 0) {
|
|
205
201
|
return `${renderFrontmatter(globs)}
|
|
206
202
|
|
|
@@ -209,19 +205,34 @@ ${renderRuleToMarkdown(rule)}`;
|
|
|
209
205
|
return renderRuleToMarkdown(rule);
|
|
210
206
|
};
|
|
211
207
|
var renderForTool = (toolId, rules, workspaceMappings) => {
|
|
212
|
-
const { global, domain } = partitionRules(rules);
|
|
213
208
|
const config = TOOL_OUTPUT_MAP[toolId];
|
|
214
209
|
if (toolId === "claude-code") {
|
|
215
|
-
const { fileExtension } = config;
|
|
216
|
-
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
|
|
210
|
+
const { rulesDir, fileExtension } = config;
|
|
211
|
+
if (!workspaceMappings || workspaceMappings.length === 0) {
|
|
212
|
+
const files = rules.map((rule) => ({
|
|
213
|
+
relativePath: join(rulesDir, `${rule.id}${fileExtension}`),
|
|
214
|
+
content: renderClaudeCodeRule(rule)
|
|
215
|
+
}));
|
|
216
|
+
return { tool: "claude-code", files };
|
|
217
|
+
}
|
|
218
|
+
const { global: global2, domain: domain2 } = partitionRules(rules);
|
|
219
|
+
const globalFiles = global2.map((rule) => ({
|
|
220
|
+
relativePath: join(rulesDir, `${rule.id}${fileExtension}`),
|
|
221
|
+
content: renderRuleToMarkdown(rule)
|
|
222
|
+
// global은 frontmatter 불필요
|
|
223
|
+
}));
|
|
224
|
+
const workspaceFiles = [];
|
|
225
|
+
for (const ws of workspaceMappings) {
|
|
226
|
+
const wsRules = domain2.filter((r) => ws.ruleIds.includes(r.id));
|
|
227
|
+
if (wsRules.length === 0) continue;
|
|
228
|
+
workspaceFiles.push({
|
|
229
|
+
relativePath: join(ws.path, "CLAUDE.md"),
|
|
230
|
+
content: renderRulesToMarkdown(wsRules)
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
return { tool: "claude-code", files: [...globalFiles, ...workspaceFiles] };
|
|
224
234
|
}
|
|
235
|
+
const { global, domain } = partitionRules(rules);
|
|
225
236
|
const rootContent = renderRulesToMarkdown(global);
|
|
226
237
|
const domainContent = renderRulesToMarkdown(domain);
|
|
227
238
|
if (toolId === "codex") {
|
|
@@ -278,11 +289,11 @@ var stripManagedHeader = (content) => {
|
|
|
278
289
|
|
|
279
290
|
// src/manifest-io.ts
|
|
280
291
|
import { mkdirSync, readFileSync as readFileSync3, writeFileSync } from "fs";
|
|
281
|
-
import { dirname, join } from "path";
|
|
292
|
+
import { dirname, join as join2 } from "path";
|
|
282
293
|
var MANIFEST_FILENAME = ".ai-ops-manifest.json";
|
|
283
294
|
var parseManifest = (json) => ManifestSchema.parse(JSON.parse(json));
|
|
284
295
|
var serializeManifest = (manifest) => JSON.stringify(manifest, null, 2) + "\n";
|
|
285
|
-
var resolveManifestPath = (basePath) =>
|
|
296
|
+
var resolveManifestPath = (basePath) => join2(basePath, MANIFEST_FILENAME);
|
|
286
297
|
var readManifest = (manifestPath) => {
|
|
287
298
|
let raw;
|
|
288
299
|
try {
|
|
@@ -310,13 +321,12 @@ var computeDiff = (params) => {
|
|
|
310
321
|
};
|
|
311
322
|
|
|
312
323
|
// src/install-plan.ts
|
|
313
|
-
import { join as
|
|
324
|
+
import { join as join3 } from "path";
|
|
314
325
|
var buildInstallPlan = (params) => {
|
|
315
326
|
const { toolId, renderResult, meta } = params;
|
|
316
327
|
if (toolId === "claude-code" && renderResult.tool === "claude-code") {
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
relativePath: join2(rulesDir, fileName),
|
|
328
|
+
return renderResult.files.map(({ relativePath, content }) => ({
|
|
329
|
+
relativePath,
|
|
320
330
|
content: wrapWithHeader(content, meta)
|
|
321
331
|
}));
|
|
322
332
|
}
|
|
@@ -325,13 +335,13 @@ var buildInstallPlan = (params) => {
|
|
|
325
335
|
const actions = [];
|
|
326
336
|
if (renderResult.rootContent) {
|
|
327
337
|
actions.push({
|
|
328
|
-
relativePath:
|
|
338
|
+
relativePath: join3(config.dir, config.rootFileName),
|
|
329
339
|
content: wrapWithHeader(renderResult.rootContent, meta)
|
|
330
340
|
});
|
|
331
341
|
}
|
|
332
342
|
if (renderResult.domainContent) {
|
|
333
343
|
actions.push({
|
|
334
|
-
relativePath:
|
|
344
|
+
relativePath: join3(config.dir, config.domainFileName),
|
|
335
345
|
content: wrapWithHeader(renderResult.domainContent, meta)
|
|
336
346
|
});
|
|
337
347
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/schemas/rule.schema.ts","../src/schemas/preset.schema.ts","../src/schemas/manifest.schema.ts","../src/loader.ts","../src/tool-output.ts","../src/renderer.ts","../src/source-hash.ts","../src/managed-header.ts","../src/manifest-io.ts","../src/diff.ts","../src/install-plan.ts","../src/paths.ts"],"sourcesContent":["/**\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 GLOBAL: 'global',\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.enum(['project', 'global']),\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 /** 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","// 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 contextStrategy: 'path-scoped' as const, // paths: frontmatter\n },\n codex: {\n mode: 'multi-file' as const,\n dir: '.codex',\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 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 문자 | escape)\nexport const renderDecisionTable = (entries: readonly DecisionTableEntry[]): string => {\n const escape = (s: string) => s.replace(/\\|/g, '|');\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\n// 모노레포에서 rule이 포함된 워크스페이스별 scoped glob 생성\n// e.g. typescript rule + ['backend-ts', 'web'] → ['backend-ts/**/*.ts', 'web/**/*.ts', ...]\nexport type WorkspaceMapping = {\n path: string;\n ruleIds: readonly string[];\n};\n\nconst buildScopedGlobs = (rule: Rule, workspaceMappings: readonly WorkspaceMapping[]): readonly string[] | undefined => {\n const baseGlobs = CLAUDE_CODE_PATH_GLOBS[rule.id];\n if (!baseGlobs) return undefined;\n\n const relevant = workspaceMappings.filter((ws) => ws.ruleIds.includes(rule.id));\n if (relevant.length === 0) return baseGlobs;\n\n return relevant.flatMap((ws) => baseGlobs.map((g) => `${ws.path}/${g}`));\n};\n\n// 단일 Rule → Claude Code용 Markdown\n// domain 룰이면서 glob 매핑이 있으면 paths: frontmatter 추가\n// global 룰 또는 매핑 없는 domain 룰 → frontmatter 없음 (안전 fallback)\nexport const renderClaudeCodeRule = (rule: Rule, scopedGlobs?: readonly string[]): string => {\n const globs = scopedGlobs ?? 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: { fileName: 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 → 도구별 렌더링 결과\n// workspaceMappings 전달 시 claude-code frontmatter에 workspace-prefixed glob 생성\nexport const renderForTool = (\n toolId: ToolId,\n rules: readonly Rule[],\n workspaceMappings?: readonly WorkspaceMapping[],\n): ToolRenderResult => {\n const { global, domain } = partitionRules(rules);\n const config = TOOL_OUTPUT_MAP[toolId];\n\n if (toolId === 'claude-code') {\n const { fileExtension } = config as (typeof TOOL_OUTPUT_MAP)['claude-code'];\n const files = rules.map((rule) => {\n const scopedGlobs = workspaceMappings ? buildScopedGlobs(rule, workspaceMappings) : undefined;\n return {\n fileName: `${rule.id}${fileExtension}`,\n content: renderClaudeCodeRule(rule, scopedGlobs),\n };\n });\n return { tool: 'claude-code', files };\n }\n\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","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' | 'global';\n preset?: string;\n workspaces?: Record<string, { preset: string; rules: string[] }>;\n installedRules: 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 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\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","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\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 const { rulesDir } = TOOL_OUTPUT_MAP['claude-code'];\n return renderResult.files.map(({ fileName, content }) => ({\n relativePath: join(rulesDir, fileName),\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 actions.push({\n relativePath: join(config.dir, config.rootFileName),\n content: wrapWithHeader(renderResult.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 { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\n// dist/index.js 위치 기준 → 상위(패키지 루트) → data/\n// src/에서 실행 시: src/../data = data/ ✅\n// dist/에서 실행 시: dist/../data = data/ ✅\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nexport const COMPILER_DATA_DIR = resolve(__dirname, '..', 'data');\n"],"mappings":"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","names":["z","z","readFileSync","readdirSync","resolve","readdirSync","readFileSync","resolve","readFileSync","readFileSync","join","join","dirname","resolve"]}
|
|
1
|
+
{"version":3,"sources":["../src/schemas/rule.schema.ts","../src/schemas/preset.schema.ts","../src/schemas/manifest.schema.ts","../src/loader.ts","../src/renderer.ts","../src/tool-output.ts","../src/source-hash.ts","../src/managed-header.ts","../src/manifest-io.ts","../src/diff.ts","../src/install-plan.ts","../src/paths.ts"],"sourcesContent":["/**\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 GLOBAL: 'global',\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.enum(['project', 'global']),\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 /** 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 문자 | escape)\nexport const renderDecisionTable = (entries: readonly DecisionTableEntry[]): string => {\n const escape = (s: string) => s.replace(/\\|/g, '|');\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: '.codex',\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' | 'global';\n preset?: string;\n workspaces?: Record<string, { preset: string; rules: string[] }>;\n installedRules: 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 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\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","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\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 actions.push({\n relativePath: join(config.dir, config.rootFileName),\n content: wrapWithHeader(renderResult.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 { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\n// dist/index.js 위치 기준 → 상위(패키지 루트) → data/\n// src/에서 실행 시: src/../data = data/ ✅\n// dist/에서 실행 시: dist/../data = data/ ✅\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nexport const COMPILER_DATA_DIR = resolve(__dirname, '..', 'data');\n"],"mappings":"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","names":["z","z","global","domain","readFileSync","readdirSync","resolve","readdirSync","readFileSync","resolve","readFileSync","join","join","readFileSync","join","join","dirname","resolve"]}
|