@dusky-bluehour/agent-service 0.6.5 → 0.6.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -25
- package/antigravity/README.md +14 -1
- package/antigravity/agents/agent-catalog.json +5 -5
- package/antigravity/commands/definitions/cmd-dev-be-api.md +43 -0
- package/antigravity/commands/definitions/cmd-dev-fe-hook-separate.md +43 -0
- package/antigravity/commands/definitions/cmd-dev-fe-ui-componentize.md +43 -0
- package/antigravity/commands/definitions/cmd-dev-perf-optimize.md +43 -0
- package/antigravity/commands/definitions/cmd-dev-sequential-autorun.md +43 -0
- package/antigravity/commands/definitions/cmd-doc-handoff.md +44 -0
- package/antigravity/commands/definitions/cmd-improve-techdebt.md +43 -0
- package/antigravity/commands/definitions/cmd-incident-triage.md +43 -0
- package/antigravity/commands/definitions/cmd-ops-ci-cd-gate.md +43 -0
- package/antigravity/commands/definitions/cmd-ops-deploy.md +43 -0
- package/antigravity/commands/definitions/cmd-ops-monitoring.md +43 -0
- package/antigravity/commands/definitions/cmd-plan-arch-decision.md +43 -0
- package/antigravity/commands/definitions/cmd-plan-implementation-bootstrap.md +43 -0
- package/antigravity/commands/definitions/cmd-plan-prd-details.md +43 -0
- package/antigravity/commands/definitions/cmd-plan-prd-master.md +44 -0
- package/antigravity/commands/definitions/cmd-plan-req-lock.md +44 -0
- package/antigravity/commands/definitions/cmd-review-code.md +43 -0
- package/antigravity/commands/definitions/cmd-sec-dependency-audit.md +43 -0
- package/antigravity/commands/definitions/cmd-sec-threat-model.md +43 -0
- package/antigravity/commands/definitions/cmd-test-unit-integration.md +43 -0
- package/antigravity/instructions/WORKSPACE-RULES.template.md +34 -0
- package/antigravity/settings/editor-policy.json +193 -0
- package/antigravity/skills/change-safety-review/SKILL.md +40 -0
- package/antigravity/skills/code-review-and-improvement/SKILL.md +20 -0
- package/antigravity/skills/frontend-repetition-pack/SKILL.md +20 -0
- package/antigravity/skills/incident-response/SKILL.md +20 -0
- package/antigravity/skills/prd-to-production-pipeline/SKILL.md +21 -1
- package/antigravity/skills/release-and-operations/SKILL.md +20 -0
- package/antigravity/skills/security-hardening/SKILL.md +21 -1
- package/antigravity/skills/service-lifecycle-orchestration/SKILL.md +21 -1
- package/antigravity/workflows/definitions/WF-FRONTEND-REFACTOR.workflow.yaml +38 -0
- package/antigravity/workflows/definitions/WF-INCIDENT-RESPONSE.workflow.yaml +41 -0
- package/antigravity/workflows/definitions/WF-PRD-TO-PRODUCTION.workflow.yaml +76 -0
- package/antigravity/workflows/definitions/WF-SECURITY-HARDENING.workflow.yaml +40 -0
- package/antigravity/workflows/definitions/WF-SERVICE-E2E.workflow.yaml +67 -0
- package/antigravity/workflows/workflow-catalog.json +5 -5
- package/catalog/tool-catalog.ko.json +78 -17
- package/claude-code/README.md +31 -1
- package/claude-code/agent-teams/team-catalog.json +7 -7
- package/claude-code/commands/native/cmd-dev-be-api.md +51 -0
- package/claude-code/commands/native/cmd-dev-fe-hook-separate.md +51 -0
- package/claude-code/commands/native/cmd-dev-fe-ui-componentize.md +51 -0
- package/claude-code/commands/native/cmd-dev-perf-optimize.md +51 -0
- package/claude-code/commands/native/cmd-dev-sequential-autorun.md +51 -0
- package/claude-code/commands/native/cmd-doc-handoff.md +52 -0
- package/claude-code/commands/native/cmd-improve-techdebt.md +51 -0
- package/claude-code/commands/native/cmd-incident-triage.md +51 -0
- package/claude-code/commands/native/cmd-ops-ci-cd-gate.md +51 -0
- package/claude-code/commands/native/cmd-ops-deploy.md +51 -0
- package/claude-code/commands/native/cmd-ops-monitoring.md +51 -0
- package/claude-code/commands/native/cmd-plan-arch-decision.md +51 -0
- package/claude-code/commands/native/cmd-plan-implementation-bootstrap.md +51 -0
- package/claude-code/commands/native/cmd-plan-prd-details.md +51 -0
- package/claude-code/commands/native/cmd-plan-prd-master.md +52 -0
- package/claude-code/commands/native/cmd-plan-req-lock.md +52 -0
- package/claude-code/commands/native/cmd-review-code.md +51 -0
- package/claude-code/commands/native/cmd-sec-dependency-audit.md +51 -0
- package/claude-code/commands/native/cmd-sec-threat-model.md +51 -0
- package/claude-code/commands/native/cmd-test-unit-integration.md +51 -0
- package/claude-code/instructions/CLAUDE.template.md +42 -0
- package/claude-code/settings/settings.json +183 -0
- package/claude-code/settings/settings.local.json +10 -0
- package/claude-code/skills/change-safety-review/SKILL.md +40 -0
- package/claude-code/skills/code-review-and-improvement/SKILL.md +21 -1
- package/claude-code/skills/frontend-repetition-pack/SKILL.md +21 -1
- package/claude-code/skills/incident-response/SKILL.md +21 -1
- package/claude-code/skills/prd-to-production-pipeline/SKILL.md +21 -1
- package/claude-code/skills/release-and-operations/SKILL.md +21 -1
- package/claude-code/skills/security-hardening/SKILL.md +21 -1
- package/claude-code/skills/service-lifecycle-orchestration/SKILL.md +21 -1
- package/claude-code/workflows/workflow-catalog.json +8 -8
- package/codex/README.md +18 -3
- package/codex/automations/automation-recipes.toml +4 -4
- package/codex/instructions/AGENTS.permissions.generated.md +121 -0
- package/codex/instructions/AGENTS.template.md +24 -8
- package/codex/settings/runtime-policy.json +188 -0
- package/codex/skills/change-safety-review/SKILL.md +40 -0
- package/codex/skills/change-safety-review/agents/openai.yaml +4 -0
- package/codex/skills/cmd-dev-be-api/SKILL.md +43 -0
- package/codex/skills/cmd-dev-be-api/agents/openai.yaml +4 -0
- package/codex/skills/cmd-dev-fe-hook-separate/SKILL.md +43 -0
- package/codex/skills/cmd-dev-fe-hook-separate/agents/openai.yaml +4 -0
- package/codex/skills/cmd-dev-fe-ui-componentize/SKILL.md +43 -0
- package/codex/skills/cmd-dev-fe-ui-componentize/agents/openai.yaml +4 -0
- package/codex/skills/cmd-dev-perf-optimize/SKILL.md +43 -0
- package/codex/skills/cmd-dev-perf-optimize/agents/openai.yaml +4 -0
- package/codex/skills/cmd-dev-sequential-autorun/SKILL.md +43 -0
- package/codex/skills/cmd-dev-sequential-autorun/agents/openai.yaml +4 -0
- package/codex/skills/cmd-doc-handoff/SKILL.md +43 -0
- package/codex/skills/cmd-doc-handoff/agents/openai.yaml +4 -0
- package/codex/skills/cmd-improve-techdebt/SKILL.md +43 -0
- package/codex/skills/cmd-improve-techdebt/agents/openai.yaml +4 -0
- package/codex/skills/cmd-incident-triage/SKILL.md +43 -0
- package/codex/skills/cmd-incident-triage/agents/openai.yaml +4 -0
- package/codex/skills/cmd-ops-ci-cd-gate/SKILL.md +43 -0
- package/codex/skills/cmd-ops-ci-cd-gate/agents/openai.yaml +4 -0
- package/codex/skills/cmd-ops-deploy/SKILL.md +43 -0
- package/codex/skills/cmd-ops-deploy/agents/openai.yaml +4 -0
- package/codex/skills/cmd-ops-monitoring/SKILL.md +43 -0
- package/codex/skills/cmd-ops-monitoring/agents/openai.yaml +4 -0
- package/codex/skills/cmd-plan-arch-decision/SKILL.md +43 -0
- package/codex/skills/cmd-plan-arch-decision/agents/openai.yaml +4 -0
- package/codex/skills/cmd-plan-implementation-bootstrap/SKILL.md +43 -0
- package/codex/skills/cmd-plan-implementation-bootstrap/agents/openai.yaml +4 -0
- package/codex/skills/cmd-plan-prd-details/SKILL.md +43 -0
- package/codex/skills/cmd-plan-prd-details/agents/openai.yaml +4 -0
- package/codex/skills/cmd-plan-prd-master/SKILL.md +44 -0
- package/codex/skills/cmd-plan-prd-master/agents/openai.yaml +4 -0
- package/codex/skills/cmd-plan-req-lock/SKILL.md +44 -0
- package/codex/skills/cmd-plan-req-lock/agents/openai.yaml +4 -0
- package/codex/skills/cmd-review-code/SKILL.md +43 -0
- package/codex/skills/cmd-review-code/agents/openai.yaml +4 -0
- package/codex/skills/cmd-sec-dependency-audit/SKILL.md +43 -0
- package/codex/skills/cmd-sec-dependency-audit/agents/openai.yaml +4 -0
- package/codex/skills/cmd-sec-threat-model/SKILL.md +43 -0
- package/codex/skills/cmd-sec-threat-model/agents/openai.yaml +4 -0
- package/codex/skills/cmd-test-unit-integration/SKILL.md +43 -0
- package/codex/skills/cmd-test-unit-integration/agents/openai.yaml +4 -0
- package/codex/skills/code-review-and-improvement/SKILL.md +21 -1
- package/codex/skills/frontend-repetition-pack/SKILL.md +20 -0
- package/codex/skills/incident-response/SKILL.md +21 -1
- package/codex/skills/prd-to-production-pipeline/SKILL.md +21 -1
- package/codex/skills/release-and-operations/SKILL.md +20 -0
- package/codex/skills/security-hardening/SKILL.md +21 -1
- package/codex/skills/service-lifecycle-orchestration/SKILL.md +21 -1
- package/codex/workflows/workflow-catalog.json +6 -6
- package/common/antigravity/agent-catalog.json +72 -0
- package/common/antigravity/artifact-catalog.json +184 -0
- package/common/claude/subagent-catalog.json +419 -0
- package/common/claude/team-catalog.json +69 -0
- package/common/commands/command-catalog.json +942 -0
- package/common/settings/security-policy.json +221 -0
- package/common/skills/skill-catalog.json +566 -0
- package/common/workflows/workflow-catalog.json +1550 -0
- package/package.json +6 -2
- package/scripts/generate-from-common.mjs +872 -0
- package/scripts/init.mjs +295 -36
- package/scripts/validate.mjs +451 -10
|
@@ -0,0 +1,872 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from 'node:fs/promises';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = path.dirname(__filename);
|
|
9
|
+
const rootDir = path.resolve(__dirname, '..');
|
|
10
|
+
|
|
11
|
+
const DEFAULT_TOOL_IDS = ['claude-code', 'codex', 'antigravity'];
|
|
12
|
+
const args = new Set(process.argv.slice(2));
|
|
13
|
+
const checkMode = args.has('--check');
|
|
14
|
+
|
|
15
|
+
function usageAndExit() {
|
|
16
|
+
console.log('Usage: node scripts/generate-from-common.mjs [--check]');
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (args.size > 1 || (args.size === 1 && !args.has('--check'))) {
|
|
21
|
+
usageAndExit();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function readJson(relativePath) {
|
|
25
|
+
const fullPath = path.join(rootDir, relativePath);
|
|
26
|
+
const raw = await fs.readFile(fullPath, 'utf8');
|
|
27
|
+
return JSON.parse(raw);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function toPrettyJson(value) {
|
|
31
|
+
return `${JSON.stringify(value, null, 2)}\n`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function quoteYamlString(value) {
|
|
35
|
+
const normalized = String(value ?? '');
|
|
36
|
+
return `"${normalized.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function normalizeCommandBasename(commandId) {
|
|
40
|
+
const normalized = String(commandId ?? '')
|
|
41
|
+
.trim()
|
|
42
|
+
.toLowerCase()
|
|
43
|
+
.replace(/[^a-z0-9-]+/g, '-')
|
|
44
|
+
.replace(/^-+|-+$/g, '');
|
|
45
|
+
|
|
46
|
+
if (!normalized) {
|
|
47
|
+
throw new Error(`[commands] command id가 비어 있습니다: ${commandId}`);
|
|
48
|
+
}
|
|
49
|
+
return normalized;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function toBulletLines(values) {
|
|
53
|
+
if (!Array.isArray(values) || values.length === 0) {
|
|
54
|
+
return ['- (없음)'];
|
|
55
|
+
}
|
|
56
|
+
return values.map((value) => `- ${value}`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function toNumberedLines(values) {
|
|
60
|
+
if (!Array.isArray(values) || values.length === 0) {
|
|
61
|
+
return ['1. (없음)'];
|
|
62
|
+
}
|
|
63
|
+
return values.map((value, index) => `${index + 1}. ${value}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function toBashRule(pattern) {
|
|
67
|
+
return `Bash(${pattern})`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function toPathRule(kind, pattern) {
|
|
71
|
+
return `${kind}(${pattern})`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function renderCodexOpenAiYaml(skill) {
|
|
75
|
+
const ui = skill.codex_openai ?? {};
|
|
76
|
+
return [
|
|
77
|
+
'skill:',
|
|
78
|
+
` display_name: ${quoteYamlString(ui.display_name ?? '')}`,
|
|
79
|
+
` short_description: ${quoteYamlString(ui.short_description ?? '')}`,
|
|
80
|
+
` default_prompt: ${quoteYamlString(ui.default_prompt ?? '')}`,
|
|
81
|
+
''
|
|
82
|
+
].join('\n');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function renderSkillMarkdown(skill, toolId) {
|
|
86
|
+
const name = skill.names?.[toolId] ?? skill.id;
|
|
87
|
+
const description = skill.descriptions?.[toolId];
|
|
88
|
+
const procedure = skill.procedures?.[toolId];
|
|
89
|
+
const qualityRules = skill.quality_rules?.[toolId];
|
|
90
|
+
const inputChecklist = skill.input_checklist;
|
|
91
|
+
const reportFormat = skill.report_format;
|
|
92
|
+
const stopConditions = skill.stop_conditions;
|
|
93
|
+
|
|
94
|
+
if (typeof description !== 'string' || !Array.isArray(procedure) || !Array.isArray(qualityRules)) {
|
|
95
|
+
throw new Error(`[skills] ${skill.id}(${toolId}) 정의가 불완전합니다.`);
|
|
96
|
+
}
|
|
97
|
+
if (!Array.isArray(inputChecklist) || !Array.isArray(reportFormat) || !Array.isArray(stopConditions)) {
|
|
98
|
+
throw new Error(`[skills] ${skill.id} 공통 체크리스트/리포트/중단조건 정의가 누락되었습니다.`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const procedureLines = procedure.map((step, index) => `${index + 1}. ${step}`);
|
|
102
|
+
const checklistLines = inputChecklist.map((item) => `- ${item}`);
|
|
103
|
+
const reportLines = reportFormat.map((item) => `- ${item}`);
|
|
104
|
+
const stopLines = stopConditions.map((item) => `- ${item}`);
|
|
105
|
+
const qualityRuleLines = qualityRules.map((rule) => `- ${rule}`);
|
|
106
|
+
|
|
107
|
+
return [
|
|
108
|
+
'---',
|
|
109
|
+
`name: ${name}`,
|
|
110
|
+
`description: ${description}`,
|
|
111
|
+
'---',
|
|
112
|
+
'',
|
|
113
|
+
'# 시작 전 체크리스트',
|
|
114
|
+
'',
|
|
115
|
+
...checklistLines,
|
|
116
|
+
'',
|
|
117
|
+
'# 실행 절차',
|
|
118
|
+
'',
|
|
119
|
+
...procedureLines,
|
|
120
|
+
'',
|
|
121
|
+
'# 결과 보고 형식',
|
|
122
|
+
'',
|
|
123
|
+
...reportLines,
|
|
124
|
+
'',
|
|
125
|
+
'# 중단 조건',
|
|
126
|
+
'',
|
|
127
|
+
...stopLines,
|
|
128
|
+
'',
|
|
129
|
+
'# 품질 규칙',
|
|
130
|
+
'',
|
|
131
|
+
...qualityRuleLines,
|
|
132
|
+
''
|
|
133
|
+
].join('\n');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function renderClaudeSubagentMarkdown(subagent) {
|
|
137
|
+
const tools = Array.isArray(subagent.tools) ? subagent.tools.join(', ') : '';
|
|
138
|
+
const bodyLines = Array.isArray(subagent.body_lines) ? subagent.body_lines : [];
|
|
139
|
+
return [
|
|
140
|
+
'---',
|
|
141
|
+
`name: ${subagent.name ?? subagent.id}`,
|
|
142
|
+
`description: ${subagent.description ?? ''}`,
|
|
143
|
+
`tools: ${tools}`,
|
|
144
|
+
`model: ${subagent.model ?? 'sonnet'}`,
|
|
145
|
+
'---',
|
|
146
|
+
'',
|
|
147
|
+
...bodyLines,
|
|
148
|
+
''
|
|
149
|
+
].join('\n');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function renderYamlList(lines, indent, values) {
|
|
153
|
+
if (!Array.isArray(values) || values.length === 0) {
|
|
154
|
+
lines.push(`${indent}[]`);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
for (const value of values) {
|
|
158
|
+
lines.push(`${indent}- ${quoteYamlString(value)}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function renderAntigravityWorkflowDefinition(workflow) {
|
|
163
|
+
const lines = [];
|
|
164
|
+
lines.push('# generated by scripts/generate-from-common.mjs');
|
|
165
|
+
lines.push('workflow:');
|
|
166
|
+
lines.push(` id: ${quoteYamlString(workflow.id)}`);
|
|
167
|
+
lines.push(` name: ${quoteYamlString(workflow.name)}`);
|
|
168
|
+
lines.push(` summary: ${quoteYamlString(workflow.summary ?? '')}`);
|
|
169
|
+
lines.push(` orchestrator: ${quoteYamlString(workflow.orchestrator ?? '')}`);
|
|
170
|
+
lines.push(` when_to_use: ${quoteYamlString(workflow.when_to_use ?? '')}`);
|
|
171
|
+
lines.push(' stages:');
|
|
172
|
+
|
|
173
|
+
for (const stage of workflow.stages ?? []) {
|
|
174
|
+
lines.push(` - order: ${Number(stage.order ?? 0)}`);
|
|
175
|
+
lines.push(` stage_id: ${quoteYamlString(stage.stage_id ?? '')}`);
|
|
176
|
+
lines.push(` agent: ${quoteYamlString(stage.agent ?? '')}`);
|
|
177
|
+
lines.push(' commands:');
|
|
178
|
+
renderYamlList(lines, ' ', stage.commands);
|
|
179
|
+
lines.push(` input_artifact: ${quoteYamlString(stage.input_artifact ?? '')}`);
|
|
180
|
+
lines.push(` output_artifact: ${quoteYamlString(stage.output_artifact ?? '')}`);
|
|
181
|
+
lines.push(' exit_criteria:');
|
|
182
|
+
renderYamlList(lines, ' ', stage.exit_criteria);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
lines.push(' final_outputs:');
|
|
186
|
+
renderYamlList(lines, ' ', workflow.final_outputs);
|
|
187
|
+
lines.push('');
|
|
188
|
+
return lines.join('\n');
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function renderClaudeCommandMarkdown(command) {
|
|
192
|
+
const requiredInputs = command.input_contract?.required;
|
|
193
|
+
const optionalInputs = command.input_contract?.optional;
|
|
194
|
+
const executionSteps = command.execution_contract?.steps;
|
|
195
|
+
const qualityGates = command.quality_gates;
|
|
196
|
+
const artifacts = command.output_contract?.artifacts;
|
|
197
|
+
const nextRoles = command.handoff?.next_roles;
|
|
198
|
+
const blockers = command.handoff?.blockers;
|
|
199
|
+
|
|
200
|
+
return [
|
|
201
|
+
'---',
|
|
202
|
+
`description: ${quoteYamlString(`${command.name} (${command.id})`)}`,
|
|
203
|
+
'argument-hint: "[추가 컨텍스트]"',
|
|
204
|
+
'---',
|
|
205
|
+
'',
|
|
206
|
+
`이 명령은 \`${command.id}\` 계약을 그대로 적용합니다.`,
|
|
207
|
+
`추가 컨텍스트가 있으면 반영하세요: $ARGUMENTS`,
|
|
208
|
+
'',
|
|
209
|
+
'## 목적',
|
|
210
|
+
'',
|
|
211
|
+
String(command.purpose ?? ''),
|
|
212
|
+
'',
|
|
213
|
+
'## 입력 계약',
|
|
214
|
+
'',
|
|
215
|
+
'### 필수 입력',
|
|
216
|
+
...toBulletLines(requiredInputs),
|
|
217
|
+
'',
|
|
218
|
+
'### 선택 입력',
|
|
219
|
+
...toBulletLines(optionalInputs),
|
|
220
|
+
'',
|
|
221
|
+
'## 실행 절차',
|
|
222
|
+
'',
|
|
223
|
+
...toNumberedLines(executionSteps),
|
|
224
|
+
'',
|
|
225
|
+
'## 품질 게이트',
|
|
226
|
+
'',
|
|
227
|
+
...toBulletLines(qualityGates),
|
|
228
|
+
'',
|
|
229
|
+
'## 산출물',
|
|
230
|
+
'',
|
|
231
|
+
...toBulletLines(artifacts),
|
|
232
|
+
'',
|
|
233
|
+
'## 핸드오프',
|
|
234
|
+
'',
|
|
235
|
+
`- 다음 역할: ${(Array.isArray(nextRoles) && nextRoles.length > 0 ? nextRoles.join(', ') : '(없음)')}`,
|
|
236
|
+
`- 차단 요인: ${(Array.isArray(blockers) && blockers.length > 0 ? blockers.join(', ') : '(없음)')}`,
|
|
237
|
+
'',
|
|
238
|
+
'## 결과 응답 형식',
|
|
239
|
+
'',
|
|
240
|
+
'- 실행 요약(무엇을 왜 수행했는지)',
|
|
241
|
+
'- 입력 검증(충족/누락 항목)',
|
|
242
|
+
'- 품질 게이트 통과 여부',
|
|
243
|
+
'- 산출물 및 다음 핸드오프',
|
|
244
|
+
''
|
|
245
|
+
].join('\n');
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function renderAntigravityCommandDefinition(command) {
|
|
249
|
+
const requiredInputs = command.input_contract?.required;
|
|
250
|
+
const optionalInputs = command.input_contract?.optional;
|
|
251
|
+
const executionSteps = command.execution_contract?.steps;
|
|
252
|
+
const qualityGates = command.quality_gates;
|
|
253
|
+
const artifacts = command.output_contract?.artifacts;
|
|
254
|
+
const nextRoles = command.handoff?.next_roles;
|
|
255
|
+
const blockers = command.handoff?.blockers;
|
|
256
|
+
|
|
257
|
+
return [
|
|
258
|
+
'# generated by scripts/generate-from-common.mjs',
|
|
259
|
+
'',
|
|
260
|
+
`# ${command.id} - ${command.name}`,
|
|
261
|
+
'',
|
|
262
|
+
`- phase: ${command.phase ?? ''}`,
|
|
263
|
+
`- owner_role: ${command.owner_role ?? ''}`,
|
|
264
|
+
'',
|
|
265
|
+
'## Purpose',
|
|
266
|
+
'',
|
|
267
|
+
String(command.purpose ?? ''),
|
|
268
|
+
'',
|
|
269
|
+
'## Input Contract',
|
|
270
|
+
'',
|
|
271
|
+
'### Required',
|
|
272
|
+
...toBulletLines(requiredInputs),
|
|
273
|
+
'',
|
|
274
|
+
'### Optional',
|
|
275
|
+
...toBulletLines(optionalInputs),
|
|
276
|
+
'',
|
|
277
|
+
'## Execution Contract',
|
|
278
|
+
'',
|
|
279
|
+
...toNumberedLines(executionSteps),
|
|
280
|
+
'',
|
|
281
|
+
'## Quality Gates',
|
|
282
|
+
'',
|
|
283
|
+
...toBulletLines(qualityGates),
|
|
284
|
+
'',
|
|
285
|
+
'## Output Contract',
|
|
286
|
+
'',
|
|
287
|
+
...toBulletLines(artifacts),
|
|
288
|
+
'',
|
|
289
|
+
'## Handoff',
|
|
290
|
+
'',
|
|
291
|
+
`- next_roles: ${(Array.isArray(nextRoles) && nextRoles.length > 0 ? nextRoles.join(', ') : '(none)')}`,
|
|
292
|
+
`- blockers: ${(Array.isArray(blockers) && blockers.length > 0 ? blockers.join(', ') : '(none)')}`,
|
|
293
|
+
''
|
|
294
|
+
].join('\n');
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function renderCodexCommandSkillMarkdown(command, skillName) {
|
|
298
|
+
const requiredInputs = Array.isArray(command.input_contract?.required)
|
|
299
|
+
? command.input_contract.required.map((item) => `[필수] ${item}`)
|
|
300
|
+
: [];
|
|
301
|
+
const optionalInputs = Array.isArray(command.input_contract?.optional)
|
|
302
|
+
? command.input_contract.optional.map((item) => `[선택] ${item}`)
|
|
303
|
+
: [];
|
|
304
|
+
const executionSteps = Array.isArray(command.execution_contract?.steps)
|
|
305
|
+
? command.execution_contract.steps
|
|
306
|
+
: [];
|
|
307
|
+
const qualityGates = Array.isArray(command.quality_gates) ? command.quality_gates : [];
|
|
308
|
+
const artifacts = Array.isArray(command.output_contract?.artifacts)
|
|
309
|
+
? command.output_contract.artifacts
|
|
310
|
+
: [];
|
|
311
|
+
const nextRoles = Array.isArray(command.handoff?.next_roles) ? command.handoff.next_roles : [];
|
|
312
|
+
const blockers = Array.isArray(command.handoff?.blockers) ? command.handoff.blockers : [];
|
|
313
|
+
|
|
314
|
+
const checklistLines = toBulletLines([...requiredInputs, ...optionalInputs]);
|
|
315
|
+
const procedureLines = toNumberedLines([
|
|
316
|
+
`명령 계약을 확인한다: ${command.id} (${command.name})`,
|
|
317
|
+
...executionSteps,
|
|
318
|
+
'품질 게이트를 검증한다.',
|
|
319
|
+
'산출물과 핸드오프를 보고한다.'
|
|
320
|
+
]);
|
|
321
|
+
const reportLines = toBulletLines([
|
|
322
|
+
`실행 명령: ${command.id}`,
|
|
323
|
+
`입력 검증: 필수 입력 충족 여부 / 누락 항목`,
|
|
324
|
+
`품질 게이트: ${qualityGates.length > 0 ? qualityGates.join(' | ') : '(없음)'}`,
|
|
325
|
+
`산출물: ${artifacts.length > 0 ? artifacts.join(', ') : '(없음)'}`,
|
|
326
|
+
`핸드오프: 다음 역할 ${nextRoles.length > 0 ? nextRoles.join(', ') : '(없음)'}`
|
|
327
|
+
]);
|
|
328
|
+
const stopLines = toBulletLines([
|
|
329
|
+
'필수 입력이 누락된 경우',
|
|
330
|
+
'품질 게이트를 충족하지 못한 경우',
|
|
331
|
+
...blockers
|
|
332
|
+
]);
|
|
333
|
+
const qualityRuleLines = toBulletLines([
|
|
334
|
+
'검증되지 않은 추정은 사실과 분리해서 기록한다.',
|
|
335
|
+
'품질 게이트를 우회하지 않는다.',
|
|
336
|
+
...qualityGates
|
|
337
|
+
]);
|
|
338
|
+
|
|
339
|
+
return [
|
|
340
|
+
'---',
|
|
341
|
+
`name: ${skillName}`,
|
|
342
|
+
`description: ${command.name} 명령 계약을 Codex 스킬 형태로 실행한다.`,
|
|
343
|
+
'---',
|
|
344
|
+
'',
|
|
345
|
+
'# 시작 전 체크리스트',
|
|
346
|
+
'',
|
|
347
|
+
...checklistLines,
|
|
348
|
+
'',
|
|
349
|
+
'# 실행 절차',
|
|
350
|
+
'',
|
|
351
|
+
...procedureLines,
|
|
352
|
+
'',
|
|
353
|
+
'# 결과 보고 형식',
|
|
354
|
+
'',
|
|
355
|
+
...reportLines,
|
|
356
|
+
'',
|
|
357
|
+
'# 중단 조건',
|
|
358
|
+
'',
|
|
359
|
+
...stopLines,
|
|
360
|
+
'',
|
|
361
|
+
'# 품질 규칙',
|
|
362
|
+
'',
|
|
363
|
+
...qualityRuleLines,
|
|
364
|
+
''
|
|
365
|
+
].join('\n');
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function renderCodexCommandOpenAiYaml(command) {
|
|
369
|
+
return [
|
|
370
|
+
'skill:',
|
|
371
|
+
` display_name: ${quoteYamlString(command.name ?? command.id)}`,
|
|
372
|
+
` short_description: ${quoteYamlString(`${command.phase ?? ''} · ${command.owner_role ?? ''}`)}`,
|
|
373
|
+
` default_prompt: ${quoteYamlString(`${command.id} 명령 계약을 준수해 실행해줘.`)}`,
|
|
374
|
+
''
|
|
375
|
+
].join('\n');
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function buildClaudePermissionsFromSecurityPolicy(securityPolicy, includeLocalOverrides = false) {
|
|
379
|
+
const shared = securityPolicy.shared_permissions ?? {};
|
|
380
|
+
const claude = securityPolicy.claude_code ?? {};
|
|
381
|
+
|
|
382
|
+
const allow = [];
|
|
383
|
+
const ask = [];
|
|
384
|
+
const deny = [];
|
|
385
|
+
|
|
386
|
+
if (shared.read_all) {
|
|
387
|
+
allow.push('Read');
|
|
388
|
+
}
|
|
389
|
+
if (shared.edit_all) {
|
|
390
|
+
allow.push('Edit');
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
for (const pattern of shared.allow_bash ?? []) {
|
|
394
|
+
allow.push(toBashRule(pattern));
|
|
395
|
+
}
|
|
396
|
+
for (const pattern of shared.ask_bash ?? []) {
|
|
397
|
+
ask.push(toBashRule(pattern));
|
|
398
|
+
}
|
|
399
|
+
for (const pattern of shared.deny_bash ?? []) {
|
|
400
|
+
deny.push(toBashRule(pattern));
|
|
401
|
+
}
|
|
402
|
+
for (const pattern of shared.deny_read ?? []) {
|
|
403
|
+
deny.push(toPathRule('Read', pattern));
|
|
404
|
+
}
|
|
405
|
+
for (const pattern of shared.deny_edit ?? []) {
|
|
406
|
+
deny.push(toPathRule('Edit', pattern));
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (includeLocalOverrides) {
|
|
410
|
+
for (const pattern of claude.local_overrides?.ask_bash ?? []) {
|
|
411
|
+
ask.push(toBashRule(pattern));
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
return {
|
|
416
|
+
defaultMode: claude.default_mode ?? 'acceptEdits',
|
|
417
|
+
disableBypassPermissionsMode: claude.disable_bypass_permissions_mode ?? 'disable',
|
|
418
|
+
additionalDirectories: includeLocalOverrides
|
|
419
|
+
? (claude.local_overrides?.additional_directories ?? [])
|
|
420
|
+
: (claude.additional_directories ?? []),
|
|
421
|
+
allow: [...new Set(allow)],
|
|
422
|
+
ask: [...new Set(ask)],
|
|
423
|
+
deny: [...new Set(deny)]
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
function renderClaudeProjectSettingsJson(securityPolicy) {
|
|
428
|
+
return {
|
|
429
|
+
$schema: 'https://json.schemastore.org/claude-code-settings.json',
|
|
430
|
+
permissions: buildClaudePermissionsFromSecurityPolicy(securityPolicy, false),
|
|
431
|
+
enabledPlugins: securityPolicy.claude_code?.enabled_plugins ?? {}
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
function renderClaudeLocalSettingsJson(securityPolicy) {
|
|
436
|
+
const claude = securityPolicy.claude_code ?? {};
|
|
437
|
+
return {
|
|
438
|
+
$schema: 'https://json.schemastore.org/claude-code-settings.json',
|
|
439
|
+
permissions: {
|
|
440
|
+
additionalDirectories: claude.local_overrides?.additional_directories ?? [],
|
|
441
|
+
ask: [...new Set((claude.local_overrides?.ask_bash ?? []).map((pattern) => toBashRule(pattern)))]
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
function renderAntigravityEditorPolicyJson(securityPolicy) {
|
|
447
|
+
const shared = securityPolicy.shared_permissions ?? {};
|
|
448
|
+
return {
|
|
449
|
+
schema_version: '1.0.0',
|
|
450
|
+
policy_id: securityPolicy.policy_id ?? 'strict-dev',
|
|
451
|
+
description_ko:
|
|
452
|
+
'Antigravity Editor에서 수동 반영할 수 있는 프로젝트 보안/권한 정책 템플릿',
|
|
453
|
+
editor: securityPolicy.antigravity?.editor_settings ?? {},
|
|
454
|
+
command_policy: {
|
|
455
|
+
allow_shell: shared.allow_bash ?? [],
|
|
456
|
+
ask_shell: shared.ask_bash ?? [],
|
|
457
|
+
deny_shell: shared.deny_bash ?? []
|
|
458
|
+
},
|
|
459
|
+
path_policy: {
|
|
460
|
+
deny_read: shared.deny_read ?? [],
|
|
461
|
+
deny_edit: shared.deny_edit ?? []
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
function renderCodexRuntimePolicyJson(securityPolicy) {
|
|
467
|
+
const shared = securityPolicy.shared_permissions ?? {};
|
|
468
|
+
return {
|
|
469
|
+
schema_version: '1.0.0',
|
|
470
|
+
policy_id: securityPolicy.policy_id ?? 'strict-dev',
|
|
471
|
+
description_ko:
|
|
472
|
+
'Codex 프로젝트 규칙(AGENTS.md)과 함께 사용하는 런타임 권한 정책 템플릿',
|
|
473
|
+
runtime: securityPolicy.codex?.runtime_policy ?? {},
|
|
474
|
+
command_policy: {
|
|
475
|
+
allow_shell: shared.allow_bash ?? [],
|
|
476
|
+
ask_shell: shared.ask_bash ?? [],
|
|
477
|
+
deny_shell: shared.deny_bash ?? []
|
|
478
|
+
},
|
|
479
|
+
path_policy: {
|
|
480
|
+
deny_read: shared.deny_read ?? [],
|
|
481
|
+
deny_edit: shared.deny_edit ?? []
|
|
482
|
+
}
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
function renderCodexPermissionsMarkdown(securityPolicy) {
|
|
487
|
+
const shared = securityPolicy.shared_permissions ?? {};
|
|
488
|
+
const runtime = securityPolicy.codex?.runtime_policy ?? {};
|
|
489
|
+
const allowShell = shared.allow_bash ?? [];
|
|
490
|
+
const askShell = shared.ask_bash ?? [];
|
|
491
|
+
const denyShell = shared.deny_bash ?? [];
|
|
492
|
+
|
|
493
|
+
return [
|
|
494
|
+
'# Codex 권한 정책 템플릿',
|
|
495
|
+
'',
|
|
496
|
+
'이 문서는 `common/settings/security-policy.json`에서 자동 생성됩니다.',
|
|
497
|
+
'`AGENTS.md`의 프로젝트 규칙 섹션에 붙여 넣어 팀 기본 정책으로 사용하세요.',
|
|
498
|
+
'',
|
|
499
|
+
'## 런타임 정책',
|
|
500
|
+
'',
|
|
501
|
+
...toBulletLines([
|
|
502
|
+
`sandbox_mode: ${runtime.sandbox_mode ?? 'workspace-write'}`,
|
|
503
|
+
`network_access: ${runtime.network_access ?? 'restricted'}`,
|
|
504
|
+
`git_write_actions: ${runtime.git_write_actions ?? 'ask'}`,
|
|
505
|
+
`destructive_shell: ${runtime.destructive_shell ?? 'deny'}`,
|
|
506
|
+
`global_package_installs: ${runtime.global_package_installs ?? 'deny'}`
|
|
507
|
+
]),
|
|
508
|
+
'',
|
|
509
|
+
'## Shell 허용 규칙',
|
|
510
|
+
'',
|
|
511
|
+
...toBulletLines(allowShell.map((rule) => `\`${rule}\``)),
|
|
512
|
+
'',
|
|
513
|
+
'## Shell 확인(ask) 규칙',
|
|
514
|
+
'',
|
|
515
|
+
...toBulletLines(askShell.map((rule) => `\`${rule}\``)),
|
|
516
|
+
'',
|
|
517
|
+
'## Shell 차단 규칙',
|
|
518
|
+
'',
|
|
519
|
+
...toBulletLines(denyShell.map((rule) => `\`${rule}\``)),
|
|
520
|
+
''
|
|
521
|
+
].join('\n');
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
function buildWorkflowCatalogByTool(commonWorkflowCatalog) {
|
|
525
|
+
const toolIds = Array.isArray(commonWorkflowCatalog.tools)
|
|
526
|
+
? commonWorkflowCatalog.tools
|
|
527
|
+
: DEFAULT_TOOL_IDS;
|
|
528
|
+
const validToolSet = new Set(toolIds);
|
|
529
|
+
|
|
530
|
+
const workflowById = new Map();
|
|
531
|
+
for (const workflow of commonWorkflowCatalog.workflows ?? []) {
|
|
532
|
+
if (!workflow?.id || typeof workflow.id !== 'string') {
|
|
533
|
+
throw new Error('[workflows] workflow id가 비어 있습니다.');
|
|
534
|
+
}
|
|
535
|
+
if (workflowById.has(workflow.id)) {
|
|
536
|
+
throw new Error(`[workflows] workflow id 중복: ${workflow.id}`);
|
|
537
|
+
}
|
|
538
|
+
workflowById.set(workflow.id, workflow);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
const catalogs = {};
|
|
542
|
+
for (const toolId of toolIds) {
|
|
543
|
+
const workflowPolicy = commonWorkflowCatalog.workflow_policy?.[toolId] ?? {};
|
|
544
|
+
const order = Array.isArray(commonWorkflowCatalog.workflow_order?.[toolId])
|
|
545
|
+
? commonWorkflowCatalog.workflow_order[toolId]
|
|
546
|
+
: [];
|
|
547
|
+
|
|
548
|
+
const orderedIds = [];
|
|
549
|
+
const seen = new Set();
|
|
550
|
+
for (const workflowId of order) {
|
|
551
|
+
if (!workflowById.has(workflowId)) {
|
|
552
|
+
throw new Error(`[workflows] ${toolId} workflow_order가 없는 workflow를 참조합니다: ${workflowId}`);
|
|
553
|
+
}
|
|
554
|
+
const profile = workflowById.get(workflowId).profiles?.[toolId];
|
|
555
|
+
if (!profile) {
|
|
556
|
+
throw new Error(`[workflows] ${toolId} profile 누락: ${workflowId}`);
|
|
557
|
+
}
|
|
558
|
+
if (seen.has(workflowId)) {
|
|
559
|
+
throw new Error(`[workflows] ${toolId} workflow_order 중복: ${workflowId}`);
|
|
560
|
+
}
|
|
561
|
+
seen.add(workflowId);
|
|
562
|
+
orderedIds.push(workflowId);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
for (const workflow of commonWorkflowCatalog.workflows ?? []) {
|
|
566
|
+
const profile = workflow.profiles?.[toolId];
|
|
567
|
+
if (!profile) continue;
|
|
568
|
+
if (seen.has(workflow.id)) continue;
|
|
569
|
+
seen.add(workflow.id);
|
|
570
|
+
orderedIds.push(workflow.id);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
const workflows = orderedIds.map((workflowId) => {
|
|
574
|
+
const workflow = workflowById.get(workflowId);
|
|
575
|
+
const profile = workflow.profiles?.[toolId];
|
|
576
|
+
if (!profile) {
|
|
577
|
+
throw new Error(`[workflows] ${toolId} profile 누락: ${workflowId}`);
|
|
578
|
+
}
|
|
579
|
+
return {
|
|
580
|
+
id: workflowId,
|
|
581
|
+
...profile
|
|
582
|
+
};
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
catalogs[toolId] = {
|
|
586
|
+
schema_version: '1.0.0',
|
|
587
|
+
workflow_policy: workflowPolicy,
|
|
588
|
+
workflows
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
for (const workflow of commonWorkflowCatalog.workflows ?? []) {
|
|
593
|
+
const profileTools = Object.keys(workflow.profiles ?? {});
|
|
594
|
+
for (const toolId of profileTools) {
|
|
595
|
+
if (!validToolSet.has(toolId)) {
|
|
596
|
+
throw new Error(`[workflows] workflow(${workflow.id})에 알 수 없는 tool profile이 있습니다: ${toolId}`);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
return { toolIds, catalogs };
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
async function ensureDir(filePath) {
|
|
605
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
async function readIfExists(filePath) {
|
|
609
|
+
try {
|
|
610
|
+
return await fs.readFile(filePath, 'utf8');
|
|
611
|
+
} catch (error) {
|
|
612
|
+
if (error.code === 'ENOENT') {
|
|
613
|
+
return null;
|
|
614
|
+
}
|
|
615
|
+
throw error;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
async function writeOrCheck(filePath, expected, state) {
|
|
620
|
+
const current = await readIfExists(filePath);
|
|
621
|
+
|
|
622
|
+
if (checkMode) {
|
|
623
|
+
if (current !== expected) {
|
|
624
|
+
state.outOfDate.push(path.relative(rootDir, filePath));
|
|
625
|
+
}
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
if (current === expected) {
|
|
630
|
+
state.unchanged += 1;
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
await ensureDir(filePath);
|
|
635
|
+
await fs.writeFile(filePath, expected, 'utf8');
|
|
636
|
+
state.written += 1;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
async function pruneGeneratedFilesBySuffix(dir, suffix, expectedFileNames, state) {
|
|
640
|
+
await fs.mkdir(dir, { recursive: true });
|
|
641
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
642
|
+
for (const entry of entries) {
|
|
643
|
+
if (!entry.isFile() || !entry.name.endsWith(suffix)) {
|
|
644
|
+
continue;
|
|
645
|
+
}
|
|
646
|
+
if (expectedFileNames.has(entry.name)) {
|
|
647
|
+
continue;
|
|
648
|
+
}
|
|
649
|
+
const removePath = path.join(dir, entry.name);
|
|
650
|
+
if (checkMode) {
|
|
651
|
+
state.outOfDate.push(path.relative(rootDir, removePath));
|
|
652
|
+
continue;
|
|
653
|
+
}
|
|
654
|
+
await fs.unlink(removePath);
|
|
655
|
+
state.deleted += 1;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
async function pruneCodexCommandSkills(expectedDirNames, state) {
|
|
660
|
+
const dir = path.join(rootDir, 'codex', 'skills');
|
|
661
|
+
await fs.mkdir(dir, { recursive: true });
|
|
662
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
663
|
+
|
|
664
|
+
for (const entry of entries) {
|
|
665
|
+
if (!entry.isDirectory() || !/^cmd-[a-z0-9-]+$/.test(entry.name)) {
|
|
666
|
+
continue;
|
|
667
|
+
}
|
|
668
|
+
if (expectedDirNames.has(entry.name)) {
|
|
669
|
+
continue;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
const removePath = path.join(dir, entry.name);
|
|
673
|
+
if (checkMode) {
|
|
674
|
+
state.outOfDate.push(path.relative(rootDir, removePath));
|
|
675
|
+
continue;
|
|
676
|
+
}
|
|
677
|
+
await fs.rm(removePath, { recursive: true, force: true });
|
|
678
|
+
state.deleted += 1;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
async function main() {
|
|
683
|
+
const commands = await readJson('common/commands/command-catalog.json');
|
|
684
|
+
const commonWorkflowCatalog = await readJson('common/workflows/workflow-catalog.json');
|
|
685
|
+
const skillCatalog = await readJson('common/skills/skill-catalog.json');
|
|
686
|
+
const securityPolicy = await readJson('common/settings/security-policy.json');
|
|
687
|
+
const claudeSubagents = await readJson('common/claude/subagent-catalog.json');
|
|
688
|
+
const claudeTeams = await readJson('common/claude/team-catalog.json');
|
|
689
|
+
const antigravityAgents = await readJson('common/antigravity/agent-catalog.json');
|
|
690
|
+
const antigravityArtifacts = await readJson('common/antigravity/artifact-catalog.json');
|
|
691
|
+
const { toolIds, catalogs: workflowCatalogByTool } = buildWorkflowCatalogByTool(
|
|
692
|
+
commonWorkflowCatalog
|
|
693
|
+
);
|
|
694
|
+
|
|
695
|
+
const state = {
|
|
696
|
+
written: 0,
|
|
697
|
+
unchanged: 0,
|
|
698
|
+
deleted: 0,
|
|
699
|
+
outOfDate: []
|
|
700
|
+
};
|
|
701
|
+
|
|
702
|
+
for (const toolId of toolIds) {
|
|
703
|
+
const commandPath = path.join(rootDir, toolId, 'commands', 'command-catalog.json');
|
|
704
|
+
await writeOrCheck(commandPath, toPrettyJson(commands), state);
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
await writeOrCheck(
|
|
708
|
+
path.join(rootDir, 'claude-code', 'settings', 'settings.json'),
|
|
709
|
+
toPrettyJson(renderClaudeProjectSettingsJson(securityPolicy)),
|
|
710
|
+
state
|
|
711
|
+
);
|
|
712
|
+
await writeOrCheck(
|
|
713
|
+
path.join(rootDir, 'claude-code', 'settings', 'settings.local.json'),
|
|
714
|
+
toPrettyJson(renderClaudeLocalSettingsJson(securityPolicy)),
|
|
715
|
+
state
|
|
716
|
+
);
|
|
717
|
+
await writeOrCheck(
|
|
718
|
+
path.join(rootDir, 'antigravity', 'settings', 'editor-policy.json'),
|
|
719
|
+
toPrettyJson(renderAntigravityEditorPolicyJson(securityPolicy)),
|
|
720
|
+
state
|
|
721
|
+
);
|
|
722
|
+
await writeOrCheck(
|
|
723
|
+
path.join(rootDir, 'codex', 'settings', 'runtime-policy.json'),
|
|
724
|
+
toPrettyJson(renderCodexRuntimePolicyJson(securityPolicy)),
|
|
725
|
+
state
|
|
726
|
+
);
|
|
727
|
+
await writeOrCheck(
|
|
728
|
+
path.join(rootDir, 'codex', 'instructions', 'AGENTS.permissions.generated.md'),
|
|
729
|
+
renderCodexPermissionsMarkdown(securityPolicy),
|
|
730
|
+
state
|
|
731
|
+
);
|
|
732
|
+
await pruneGeneratedFilesBySuffix(
|
|
733
|
+
path.join(rootDir, 'claude-code', 'settings'),
|
|
734
|
+
'.json',
|
|
735
|
+
new Set(['settings.json', 'settings.local.json']),
|
|
736
|
+
state
|
|
737
|
+
);
|
|
738
|
+
await pruneGeneratedFilesBySuffix(
|
|
739
|
+
path.join(rootDir, 'antigravity', 'settings'),
|
|
740
|
+
'.json',
|
|
741
|
+
new Set(['editor-policy.json']),
|
|
742
|
+
state
|
|
743
|
+
);
|
|
744
|
+
await pruneGeneratedFilesBySuffix(
|
|
745
|
+
path.join(rootDir, 'codex', 'settings'),
|
|
746
|
+
'.json',
|
|
747
|
+
new Set(['runtime-policy.json']),
|
|
748
|
+
state
|
|
749
|
+
);
|
|
750
|
+
|
|
751
|
+
const commandFileNames = new Set();
|
|
752
|
+
const codexCommandSkillNames = new Set();
|
|
753
|
+
for (const command of commands.commands ?? []) {
|
|
754
|
+
if (!command?.id) {
|
|
755
|
+
throw new Error('[commands] command id가 비어 있습니다.');
|
|
756
|
+
}
|
|
757
|
+
const basename = normalizeCommandBasename(command.id);
|
|
758
|
+
commandFileNames.add(`${basename}.md`);
|
|
759
|
+
codexCommandSkillNames.add(basename);
|
|
760
|
+
|
|
761
|
+
await writeOrCheck(
|
|
762
|
+
path.join(rootDir, 'claude-code', 'commands', 'native', `${basename}.md`),
|
|
763
|
+
renderClaudeCommandMarkdown(command),
|
|
764
|
+
state
|
|
765
|
+
);
|
|
766
|
+
await writeOrCheck(
|
|
767
|
+
path.join(rootDir, 'antigravity', 'commands', 'definitions', `${basename}.md`),
|
|
768
|
+
renderAntigravityCommandDefinition(command),
|
|
769
|
+
state
|
|
770
|
+
);
|
|
771
|
+
await writeOrCheck(
|
|
772
|
+
path.join(rootDir, 'codex', 'skills', basename, 'SKILL.md'),
|
|
773
|
+
renderCodexCommandSkillMarkdown(command, basename),
|
|
774
|
+
state
|
|
775
|
+
);
|
|
776
|
+
await writeOrCheck(
|
|
777
|
+
path.join(rootDir, 'codex', 'skills', basename, 'agents', 'openai.yaml'),
|
|
778
|
+
renderCodexCommandOpenAiYaml(command),
|
|
779
|
+
state
|
|
780
|
+
);
|
|
781
|
+
}
|
|
782
|
+
await pruneGeneratedFilesBySuffix(
|
|
783
|
+
path.join(rootDir, 'claude-code', 'commands', 'native'),
|
|
784
|
+
'.md',
|
|
785
|
+
commandFileNames,
|
|
786
|
+
state
|
|
787
|
+
);
|
|
788
|
+
await pruneGeneratedFilesBySuffix(
|
|
789
|
+
path.join(rootDir, 'antigravity', 'commands', 'definitions'),
|
|
790
|
+
'.md',
|
|
791
|
+
commandFileNames,
|
|
792
|
+
state
|
|
793
|
+
);
|
|
794
|
+
await pruneCodexCommandSkills(codexCommandSkillNames, state);
|
|
795
|
+
|
|
796
|
+
for (const toolId of toolIds) {
|
|
797
|
+
const catalog = workflowCatalogByTool?.[toolId];
|
|
798
|
+
if (!catalog) {
|
|
799
|
+
throw new Error(`[workflows] tool 누락: ${toolId}`);
|
|
800
|
+
}
|
|
801
|
+
const workflowPath = path.join(rootDir, toolId, 'workflows', 'workflow-catalog.json');
|
|
802
|
+
await writeOrCheck(workflowPath, toPrettyJson(catalog), state);
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
const antigravityWorkflowNames = new Set();
|
|
806
|
+
for (const workflow of workflowCatalogByTool.antigravity?.workflows ?? []) {
|
|
807
|
+
const filename = `${workflow.id}.workflow.yaml`;
|
|
808
|
+
antigravityWorkflowNames.add(filename);
|
|
809
|
+
const definitionPath = path.join(rootDir, 'antigravity', 'workflows', 'definitions', filename);
|
|
810
|
+
await writeOrCheck(definitionPath, renderAntigravityWorkflowDefinition(workflow), state);
|
|
811
|
+
}
|
|
812
|
+
await pruneGeneratedFilesBySuffix(
|
|
813
|
+
path.join(rootDir, 'antigravity', 'workflows', 'definitions'),
|
|
814
|
+
'.workflow.yaml',
|
|
815
|
+
antigravityWorkflowNames,
|
|
816
|
+
state
|
|
817
|
+
);
|
|
818
|
+
|
|
819
|
+
for (const skill of skillCatalog.skills ?? []) {
|
|
820
|
+
for (const toolId of toolIds) {
|
|
821
|
+
const skillPath = path.join(rootDir, toolId, 'skills', skill.id, 'SKILL.md');
|
|
822
|
+
await writeOrCheck(skillPath, renderSkillMarkdown(skill, toolId), state);
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
const codexYamlPath = path.join(rootDir, 'codex', 'skills', skill.id, 'agents', 'openai.yaml');
|
|
826
|
+
await writeOrCheck(codexYamlPath, renderCodexOpenAiYaml(skill), state);
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
for (const subagent of claudeSubagents.subagents ?? []) {
|
|
830
|
+
const subagentPath = path.join(rootDir, 'claude-code', 'subagents', `${subagent.id}.md`);
|
|
831
|
+
await writeOrCheck(subagentPath, renderClaudeSubagentMarkdown(subagent), state);
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
await writeOrCheck(
|
|
835
|
+
path.join(rootDir, 'claude-code', 'agent-teams', 'team-catalog.json'),
|
|
836
|
+
toPrettyJson(claudeTeams),
|
|
837
|
+
state
|
|
838
|
+
);
|
|
839
|
+
|
|
840
|
+
await writeOrCheck(
|
|
841
|
+
path.join(rootDir, 'antigravity', 'agents', 'agent-catalog.json'),
|
|
842
|
+
toPrettyJson(antigravityAgents),
|
|
843
|
+
state
|
|
844
|
+
);
|
|
845
|
+
|
|
846
|
+
await writeOrCheck(
|
|
847
|
+
path.join(rootDir, 'antigravity', 'artifacts', 'artifact-catalog.json'),
|
|
848
|
+
toPrettyJson(antigravityArtifacts),
|
|
849
|
+
state
|
|
850
|
+
);
|
|
851
|
+
|
|
852
|
+
if (checkMode) {
|
|
853
|
+
if (state.outOfDate.length > 0) {
|
|
854
|
+
console.error('Generated files are out of date:');
|
|
855
|
+
for (const relPath of [...new Set(state.outOfDate)].sort()) {
|
|
856
|
+
console.error(`- ${relPath}`);
|
|
857
|
+
}
|
|
858
|
+
process.exit(1);
|
|
859
|
+
}
|
|
860
|
+
console.log('All generated files are up to date.');
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
console.log(
|
|
865
|
+
`Generation complete. written=${state.written}, unchanged=${state.unchanged}, deleted=${state.deleted}`
|
|
866
|
+
);
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
main().catch((error) => {
|
|
870
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
871
|
+
process.exit(1);
|
|
872
|
+
});
|