@dusky-bluehour/agent-service 0.6.6 → 0.6.8
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 +255 -36
- package/catalog/tool-catalog.ko.json +92 -57
- package/claude-code/README.md +75 -30
- 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/codex/README.md +58 -24
- package/codex/instructions/AGENTS.permissions.generated.md +121 -0
- package/codex/instructions/AGENTS.template.md +18 -3
- package/codex/settings/runtime-policy.json +188 -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/common/settings/security-policy.json +221 -0
- package/common/skills/skill-catalog.json +368 -136
- package/common/workflows/workflow-catalog.json +89 -1238
- package/gemini/README.md +104 -0
- package/gemini/commands/definitions/cmd-dev-be-api.toml +35 -0
- package/gemini/commands/definitions/cmd-dev-fe-hook-separate.toml +35 -0
- package/gemini/commands/definitions/cmd-dev-fe-ui-componentize.toml +35 -0
- package/gemini/commands/definitions/cmd-dev-perf-optimize.toml +35 -0
- package/gemini/commands/definitions/cmd-dev-sequential-autorun.toml +35 -0
- package/gemini/commands/definitions/cmd-doc-handoff.toml +36 -0
- package/gemini/commands/definitions/cmd-improve-techdebt.toml +35 -0
- package/gemini/commands/definitions/cmd-incident-triage.toml +35 -0
- package/gemini/commands/definitions/cmd-ops-ci-cd-gate.toml +35 -0
- package/gemini/commands/definitions/cmd-ops-deploy.toml +35 -0
- package/gemini/commands/definitions/cmd-ops-monitoring.toml +35 -0
- package/gemini/commands/definitions/cmd-plan-arch-decision.toml +35 -0
- package/gemini/commands/definitions/cmd-plan-implementation-bootstrap.toml +35 -0
- package/gemini/commands/definitions/cmd-plan-prd-details.toml +35 -0
- package/gemini/commands/definitions/cmd-plan-prd-master.toml +36 -0
- package/gemini/commands/definitions/cmd-plan-req-lock.toml +36 -0
- package/gemini/commands/definitions/cmd-review-code.toml +35 -0
- package/gemini/commands/definitions/cmd-sec-dependency-audit.toml +35 -0
- package/gemini/commands/definitions/cmd-sec-threat-model.toml +35 -0
- package/gemini/commands/definitions/cmd-test-unit-integration.toml +35 -0
- package/gemini/gemini-extension.json +6 -0
- package/gemini/instructions/GEMINI.template.md +34 -0
- package/gemini/settings/editor-policy.json +193 -0
- package/{antigravity → gemini}/skills/change-safety-review/SKILL.md +8 -6
- package/{antigravity → gemini}/skills/code-review-and-improvement/SKILL.md +8 -3
- package/gemini/skills/frontend-repetition-pack/SKILL.md +44 -0
- package/gemini/skills/incident-response/SKILL.md +44 -0
- package/{antigravity → gemini}/skills/prd-to-production-pipeline/SKILL.md +13 -4
- package/{antigravity → gemini}/skills/release-and-operations/SKILL.md +11 -3
- package/gemini/skills/security-hardening/SKILL.md +43 -0
- package/gemini/skills/service-lifecycle-orchestration/SKILL.md +46 -0
- package/{antigravity → gemini}/workflows/workflow-catalog.json +1 -1
- package/package.json +4 -4
- package/scripts/generate-from-common.mjs +537 -23
- package/scripts/init.mjs +310 -41
- package/scripts/validate.mjs +264 -32
- package/antigravity/README.md +0 -43
- package/antigravity/skills/frontend-repetition-pack/SKILL.md +0 -35
- package/antigravity/skills/incident-response/SKILL.md +0 -35
- package/antigravity/skills/security-hardening/SKILL.md +0 -35
- package/antigravity/skills/service-lifecycle-orchestration/SKILL.md +0 -36
- package/claude-code/workflows/workflow-catalog.json +0 -688
- package/codex/workflows/workflow-catalog.json +0 -450
- /package/{antigravity/agents → common/gemini}/agent-catalog.json +0 -0
- /package/{antigravity/artifacts → common/gemini}/artifact-catalog.json +0 -0
- /package/{common/antigravity → gemini/agents}/agent-catalog.json +0 -0
- /package/{common/antigravity → gemini/artifacts}/artifact-catalog.json +0 -0
- /package/{antigravity → gemini}/commands/command-catalog.json +0 -0
- /package/{antigravity → gemini}/workflows/definitions/WF-FRONTEND-REFACTOR.workflow.yaml +0 -0
- /package/{antigravity → gemini}/workflows/definitions/WF-INCIDENT-RESPONSE.workflow.yaml +0 -0
- /package/{antigravity → gemini}/workflows/definitions/WF-PRD-TO-PRODUCTION.workflow.yaml +0 -0
- /package/{antigravity → gemini}/workflows/definitions/WF-SECURITY-HARDENING.workflow.yaml +0 -0
- /package/{antigravity → gemini}/workflows/definitions/WF-SERVICE-E2E.workflow.yaml +0 -0
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dusky-bluehour/agent-service",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.8",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
7
|
-
"description": "Service operation skills/workflows pack for Claude Code,
|
|
7
|
+
"description": "Service operation skills/workflows pack for Claude Code, Gemini CLI, and Codex",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"bin": {
|
|
10
10
|
"tri-agent-manager": "scripts/init.mjs"
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"files": [
|
|
21
21
|
"common",
|
|
22
22
|
"claude-code",
|
|
23
|
-
"
|
|
23
|
+
"gemini",
|
|
24
24
|
"codex",
|
|
25
25
|
"catalog/tool-catalog.ko.json",
|
|
26
26
|
"scripts/init.mjs",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"workflow",
|
|
35
35
|
"codex",
|
|
36
36
|
"claude-code",
|
|
37
|
-
"
|
|
37
|
+
"gemini-cli",
|
|
38
38
|
"devops",
|
|
39
39
|
"security",
|
|
40
40
|
"code-review"
|
|
@@ -8,7 +8,7 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
8
8
|
const __dirname = path.dirname(__filename);
|
|
9
9
|
const rootDir = path.resolve(__dirname, '..');
|
|
10
10
|
|
|
11
|
-
const DEFAULT_TOOL_IDS = ['claude-code', 'codex', '
|
|
11
|
+
const DEFAULT_TOOL_IDS = ['claude-code', 'codex', 'gemini'];
|
|
12
12
|
const args = new Set(process.argv.slice(2));
|
|
13
13
|
const checkMode = args.has('--check');
|
|
14
14
|
|
|
@@ -36,6 +36,41 @@ function quoteYamlString(value) {
|
|
|
36
36
|
return `"${normalized.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
|
|
37
37
|
}
|
|
38
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
|
+
|
|
39
74
|
function renderCodexOpenAiYaml(skill) {
|
|
40
75
|
const ui = skill.codex_openai ?? {};
|
|
41
76
|
return [
|
|
@@ -124,7 +159,7 @@ function renderYamlList(lines, indent, values) {
|
|
|
124
159
|
}
|
|
125
160
|
}
|
|
126
161
|
|
|
127
|
-
function
|
|
162
|
+
function renderGeminiWorkflowDefinition(workflow) {
|
|
128
163
|
const lines = [];
|
|
129
164
|
lines.push('# generated by scripts/generate-from-common.mjs');
|
|
130
165
|
lines.push('workflow:');
|
|
@@ -153,6 +188,346 @@ function renderAntigravityWorkflowDefinition(workflow) {
|
|
|
153
188
|
return lines.join('\n');
|
|
154
189
|
}
|
|
155
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 renderGeminiCommandToml(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
|
+
const promptLines = [];
|
|
258
|
+
promptLines.push(`${command.id} - ${command.name}`);
|
|
259
|
+
promptLines.push(`Phase: ${command.phase ?? ''} | Owner: ${command.owner_role ?? ''}`);
|
|
260
|
+
promptLines.push('');
|
|
261
|
+
promptLines.push('[Purpose]');
|
|
262
|
+
promptLines.push(String(command.purpose ?? ''));
|
|
263
|
+
promptLines.push('');
|
|
264
|
+
promptLines.push('[Input Contract]');
|
|
265
|
+
promptLines.push('Required:');
|
|
266
|
+
for (const item of (requiredInputs ?? [])) {
|
|
267
|
+
promptLines.push(`- ${item}`);
|
|
268
|
+
}
|
|
269
|
+
promptLines.push('Optional:');
|
|
270
|
+
for (const item of (optionalInputs ?? [])) {
|
|
271
|
+
promptLines.push(`- ${item}`);
|
|
272
|
+
}
|
|
273
|
+
promptLines.push('');
|
|
274
|
+
promptLines.push('[Execution Steps]');
|
|
275
|
+
for (const [index, step] of (executionSteps ?? []).entries()) {
|
|
276
|
+
promptLines.push(`${index + 1}. ${step}`);
|
|
277
|
+
}
|
|
278
|
+
promptLines.push('');
|
|
279
|
+
promptLines.push('[Quality Gates]');
|
|
280
|
+
for (const gate of (qualityGates ?? [])) {
|
|
281
|
+
promptLines.push(`- ${gate}`);
|
|
282
|
+
}
|
|
283
|
+
promptLines.push('');
|
|
284
|
+
promptLines.push('[Output Artifacts]');
|
|
285
|
+
for (const artifact of (artifacts ?? [])) {
|
|
286
|
+
promptLines.push(`- ${artifact}`);
|
|
287
|
+
}
|
|
288
|
+
promptLines.push('');
|
|
289
|
+
promptLines.push('[Handoff]');
|
|
290
|
+
promptLines.push(`- next_roles: ${(Array.isArray(nextRoles) && nextRoles.length > 0 ? nextRoles.join(', ') : '(none)')}`);
|
|
291
|
+
promptLines.push(`- blockers: ${(Array.isArray(blockers) && blockers.length > 0 ? blockers.join(', ') : '(none)')}`);
|
|
292
|
+
promptLines.push('');
|
|
293
|
+
promptLines.push('Additional context: {{args}}');
|
|
294
|
+
|
|
295
|
+
const description = `${command.name} (${command.phase ?? ''} · ${command.owner_role ?? ''})`;
|
|
296
|
+
|
|
297
|
+
return [
|
|
298
|
+
`prompt = """${promptLines.join('\n')}"""`,
|
|
299
|
+
`description = "${description.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`,
|
|
300
|
+
''
|
|
301
|
+
].join('\n');
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function renderCodexCommandSkillMarkdown(command, skillName) {
|
|
305
|
+
const requiredInputs = Array.isArray(command.input_contract?.required)
|
|
306
|
+
? command.input_contract.required.map((item) => `[필수] ${item}`)
|
|
307
|
+
: [];
|
|
308
|
+
const optionalInputs = Array.isArray(command.input_contract?.optional)
|
|
309
|
+
? command.input_contract.optional.map((item) => `[선택] ${item}`)
|
|
310
|
+
: [];
|
|
311
|
+
const executionSteps = Array.isArray(command.execution_contract?.steps)
|
|
312
|
+
? command.execution_contract.steps
|
|
313
|
+
: [];
|
|
314
|
+
const qualityGates = Array.isArray(command.quality_gates) ? command.quality_gates : [];
|
|
315
|
+
const artifacts = Array.isArray(command.output_contract?.artifacts)
|
|
316
|
+
? command.output_contract.artifacts
|
|
317
|
+
: [];
|
|
318
|
+
const nextRoles = Array.isArray(command.handoff?.next_roles) ? command.handoff.next_roles : [];
|
|
319
|
+
const blockers = Array.isArray(command.handoff?.blockers) ? command.handoff.blockers : [];
|
|
320
|
+
|
|
321
|
+
const checklistLines = toBulletLines([...requiredInputs, ...optionalInputs]);
|
|
322
|
+
const procedureLines = toNumberedLines([
|
|
323
|
+
`명령 계약을 확인한다: ${command.id} (${command.name})`,
|
|
324
|
+
...executionSteps,
|
|
325
|
+
'품질 게이트를 검증한다.',
|
|
326
|
+
'산출물과 핸드오프를 보고한다.'
|
|
327
|
+
]);
|
|
328
|
+
const reportLines = toBulletLines([
|
|
329
|
+
`실행 명령: ${command.id}`,
|
|
330
|
+
`입력 검증: 필수 입력 충족 여부 / 누락 항목`,
|
|
331
|
+
`품질 게이트: ${qualityGates.length > 0 ? qualityGates.join(' | ') : '(없음)'}`,
|
|
332
|
+
`산출물: ${artifacts.length > 0 ? artifacts.join(', ') : '(없음)'}`,
|
|
333
|
+
`핸드오프: 다음 역할 ${nextRoles.length > 0 ? nextRoles.join(', ') : '(없음)'}`
|
|
334
|
+
]);
|
|
335
|
+
const stopLines = toBulletLines([
|
|
336
|
+
'필수 입력이 누락된 경우',
|
|
337
|
+
'품질 게이트를 충족하지 못한 경우',
|
|
338
|
+
...blockers
|
|
339
|
+
]);
|
|
340
|
+
const qualityRuleLines = toBulletLines([
|
|
341
|
+
'검증되지 않은 추정은 사실과 분리해서 기록한다.',
|
|
342
|
+
'품질 게이트를 우회하지 않는다.',
|
|
343
|
+
...qualityGates
|
|
344
|
+
]);
|
|
345
|
+
|
|
346
|
+
return [
|
|
347
|
+
'---',
|
|
348
|
+
`name: ${skillName}`,
|
|
349
|
+
`description: ${command.name} 명령 계약을 Codex 스킬 형태로 실행한다.`,
|
|
350
|
+
'---',
|
|
351
|
+
'',
|
|
352
|
+
'# 시작 전 체크리스트',
|
|
353
|
+
'',
|
|
354
|
+
...checklistLines,
|
|
355
|
+
'',
|
|
356
|
+
'# 실행 절차',
|
|
357
|
+
'',
|
|
358
|
+
...procedureLines,
|
|
359
|
+
'',
|
|
360
|
+
'# 결과 보고 형식',
|
|
361
|
+
'',
|
|
362
|
+
...reportLines,
|
|
363
|
+
'',
|
|
364
|
+
'# 중단 조건',
|
|
365
|
+
'',
|
|
366
|
+
...stopLines,
|
|
367
|
+
'',
|
|
368
|
+
'# 품질 규칙',
|
|
369
|
+
'',
|
|
370
|
+
...qualityRuleLines,
|
|
371
|
+
''
|
|
372
|
+
].join('\n');
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function renderCodexCommandOpenAiYaml(command) {
|
|
376
|
+
return [
|
|
377
|
+
'skill:',
|
|
378
|
+
` display_name: ${quoteYamlString(command.name ?? command.id)}`,
|
|
379
|
+
` short_description: ${quoteYamlString(`${command.phase ?? ''} · ${command.owner_role ?? ''}`)}`,
|
|
380
|
+
` default_prompt: ${quoteYamlString(`${command.id} 명령 계약을 준수해 실행해줘.`)}`,
|
|
381
|
+
''
|
|
382
|
+
].join('\n');
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function buildClaudePermissionsFromSecurityPolicy(securityPolicy, includeLocalOverrides = false) {
|
|
386
|
+
const shared = securityPolicy.shared_permissions ?? {};
|
|
387
|
+
const claude = securityPolicy.claude_code ?? {};
|
|
388
|
+
|
|
389
|
+
const allow = [];
|
|
390
|
+
const ask = [];
|
|
391
|
+
const deny = [];
|
|
392
|
+
|
|
393
|
+
if (shared.read_all) {
|
|
394
|
+
allow.push('Read');
|
|
395
|
+
}
|
|
396
|
+
if (shared.edit_all) {
|
|
397
|
+
allow.push('Edit');
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
for (const pattern of shared.allow_bash ?? []) {
|
|
401
|
+
allow.push(toBashRule(pattern));
|
|
402
|
+
}
|
|
403
|
+
for (const pattern of shared.ask_bash ?? []) {
|
|
404
|
+
ask.push(toBashRule(pattern));
|
|
405
|
+
}
|
|
406
|
+
for (const pattern of shared.deny_bash ?? []) {
|
|
407
|
+
deny.push(toBashRule(pattern));
|
|
408
|
+
}
|
|
409
|
+
for (const pattern of shared.deny_read ?? []) {
|
|
410
|
+
deny.push(toPathRule('Read', pattern));
|
|
411
|
+
}
|
|
412
|
+
for (const pattern of shared.deny_edit ?? []) {
|
|
413
|
+
deny.push(toPathRule('Edit', pattern));
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
if (includeLocalOverrides) {
|
|
417
|
+
for (const pattern of claude.local_overrides?.ask_bash ?? []) {
|
|
418
|
+
ask.push(toBashRule(pattern));
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
return {
|
|
423
|
+
defaultMode: claude.default_mode ?? 'acceptEdits',
|
|
424
|
+
disableBypassPermissionsMode: claude.disable_bypass_permissions_mode ?? 'disable',
|
|
425
|
+
additionalDirectories: includeLocalOverrides
|
|
426
|
+
? (claude.local_overrides?.additional_directories ?? [])
|
|
427
|
+
: (claude.additional_directories ?? []),
|
|
428
|
+
allow: [...new Set(allow)],
|
|
429
|
+
ask: [...new Set(ask)],
|
|
430
|
+
deny: [...new Set(deny)]
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function renderClaudeProjectSettingsJson(securityPolicy) {
|
|
435
|
+
return {
|
|
436
|
+
$schema: 'https://json.schemastore.org/claude-code-settings.json',
|
|
437
|
+
permissions: buildClaudePermissionsFromSecurityPolicy(securityPolicy, false),
|
|
438
|
+
enabledPlugins: securityPolicy.claude_code?.enabled_plugins ?? {}
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
function renderClaudeLocalSettingsJson(securityPolicy) {
|
|
443
|
+
const claude = securityPolicy.claude_code ?? {};
|
|
444
|
+
return {
|
|
445
|
+
$schema: 'https://json.schemastore.org/claude-code-settings.json',
|
|
446
|
+
permissions: {
|
|
447
|
+
additionalDirectories: claude.local_overrides?.additional_directories ?? [],
|
|
448
|
+
ask: [...new Set((claude.local_overrides?.ask_bash ?? []).map((pattern) => toBashRule(pattern)))]
|
|
449
|
+
}
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function renderGeminiEditorPolicyJson(securityPolicy) {
|
|
454
|
+
const shared = securityPolicy.shared_permissions ?? {};
|
|
455
|
+
return {
|
|
456
|
+
schema_version: '1.0.0',
|
|
457
|
+
policy_id: securityPolicy.policy_id ?? 'strict-dev',
|
|
458
|
+
description_ko:
|
|
459
|
+
'Gemini CLI에서 수동 반영할 수 있는 프로젝트 보안/권한 정책 템플릿',
|
|
460
|
+
editor: securityPolicy.gemini?.editor_settings ?? {},
|
|
461
|
+
command_policy: {
|
|
462
|
+
allow_shell: shared.allow_bash ?? [],
|
|
463
|
+
ask_shell: shared.ask_bash ?? [],
|
|
464
|
+
deny_shell: shared.deny_bash ?? []
|
|
465
|
+
},
|
|
466
|
+
path_policy: {
|
|
467
|
+
deny_read: shared.deny_read ?? [],
|
|
468
|
+
deny_edit: shared.deny_edit ?? []
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function renderCodexRuntimePolicyJson(securityPolicy) {
|
|
474
|
+
const shared = securityPolicy.shared_permissions ?? {};
|
|
475
|
+
return {
|
|
476
|
+
schema_version: '1.0.0',
|
|
477
|
+
policy_id: securityPolicy.policy_id ?? 'strict-dev',
|
|
478
|
+
description_ko:
|
|
479
|
+
'Codex 프로젝트 규칙(AGENTS.md)과 함께 사용하는 런타임 권한 정책 템플릿',
|
|
480
|
+
runtime: securityPolicy.codex?.runtime_policy ?? {},
|
|
481
|
+
command_policy: {
|
|
482
|
+
allow_shell: shared.allow_bash ?? [],
|
|
483
|
+
ask_shell: shared.ask_bash ?? [],
|
|
484
|
+
deny_shell: shared.deny_bash ?? []
|
|
485
|
+
},
|
|
486
|
+
path_policy: {
|
|
487
|
+
deny_read: shared.deny_read ?? [],
|
|
488
|
+
deny_edit: shared.deny_edit ?? []
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
function renderCodexPermissionsMarkdown(securityPolicy) {
|
|
494
|
+
const shared = securityPolicy.shared_permissions ?? {};
|
|
495
|
+
const runtime = securityPolicy.codex?.runtime_policy ?? {};
|
|
496
|
+
const allowShell = shared.allow_bash ?? [];
|
|
497
|
+
const askShell = shared.ask_bash ?? [];
|
|
498
|
+
const denyShell = shared.deny_bash ?? [];
|
|
499
|
+
|
|
500
|
+
return [
|
|
501
|
+
'# Codex 권한 정책 템플릿',
|
|
502
|
+
'',
|
|
503
|
+
'이 문서는 `common/settings/security-policy.json`에서 자동 생성됩니다.',
|
|
504
|
+
'`AGENTS.md`의 프로젝트 규칙 섹션에 붙여 넣어 팀 기본 정책으로 사용하세요.',
|
|
505
|
+
'',
|
|
506
|
+
'## 런타임 정책',
|
|
507
|
+
'',
|
|
508
|
+
...toBulletLines([
|
|
509
|
+
`sandbox_mode: ${runtime.sandbox_mode ?? 'workspace-write'}`,
|
|
510
|
+
`network_access: ${runtime.network_access ?? 'restricted'}`,
|
|
511
|
+
`git_write_actions: ${runtime.git_write_actions ?? 'ask'}`,
|
|
512
|
+
`destructive_shell: ${runtime.destructive_shell ?? 'deny'}`,
|
|
513
|
+
`global_package_installs: ${runtime.global_package_installs ?? 'deny'}`
|
|
514
|
+
]),
|
|
515
|
+
'',
|
|
516
|
+
'## Shell 허용 규칙',
|
|
517
|
+
'',
|
|
518
|
+
...toBulletLines(allowShell.map((rule) => `\`${rule}\``)),
|
|
519
|
+
'',
|
|
520
|
+
'## Shell 확인(ask) 규칙',
|
|
521
|
+
'',
|
|
522
|
+
...toBulletLines(askShell.map((rule) => `\`${rule}\``)),
|
|
523
|
+
'',
|
|
524
|
+
'## Shell 차단 규칙',
|
|
525
|
+
'',
|
|
526
|
+
...toBulletLines(denyShell.map((rule) => `\`${rule}\``)),
|
|
527
|
+
''
|
|
528
|
+
].join('\n');
|
|
529
|
+
}
|
|
530
|
+
|
|
156
531
|
function buildWorkflowCatalogByTool(commonWorkflowCatalog) {
|
|
157
532
|
const toolIds = Array.isArray(commonWorkflowCatalog.tools)
|
|
158
533
|
? commonWorkflowCatalog.tools
|
|
@@ -268,12 +643,11 @@ async function writeOrCheck(filePath, expected, state) {
|
|
|
268
643
|
state.written += 1;
|
|
269
644
|
}
|
|
270
645
|
|
|
271
|
-
async function
|
|
272
|
-
const dir = path.join(rootDir, 'antigravity', 'workflows', 'definitions');
|
|
646
|
+
async function pruneGeneratedFilesBySuffix(dir, suffix, expectedFileNames, state) {
|
|
273
647
|
await fs.mkdir(dir, { recursive: true });
|
|
274
648
|
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
275
649
|
for (const entry of entries) {
|
|
276
|
-
if (!entry.isFile() || !entry.name.endsWith(
|
|
650
|
+
if (!entry.isFile() || !entry.name.endsWith(suffix)) {
|
|
277
651
|
continue;
|
|
278
652
|
}
|
|
279
653
|
if (expectedFileNames.has(entry.name)) {
|
|
@@ -289,14 +663,39 @@ async function pruneAntigravityWorkflowDefinitions(expectedFileNames, state) {
|
|
|
289
663
|
}
|
|
290
664
|
}
|
|
291
665
|
|
|
666
|
+
async function pruneCodexCommandSkills(expectedDirNames, state) {
|
|
667
|
+
const dir = path.join(rootDir, 'codex', 'skills');
|
|
668
|
+
await fs.mkdir(dir, { recursive: true });
|
|
669
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
670
|
+
|
|
671
|
+
for (const entry of entries) {
|
|
672
|
+
if (!entry.isDirectory() || !/^cmd-[a-z0-9-]+$/.test(entry.name)) {
|
|
673
|
+
continue;
|
|
674
|
+
}
|
|
675
|
+
if (expectedDirNames.has(entry.name)) {
|
|
676
|
+
continue;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
const removePath = path.join(dir, entry.name);
|
|
680
|
+
if (checkMode) {
|
|
681
|
+
state.outOfDate.push(path.relative(rootDir, removePath));
|
|
682
|
+
continue;
|
|
683
|
+
}
|
|
684
|
+
await fs.rm(removePath, { recursive: true, force: true });
|
|
685
|
+
state.deleted += 1;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
292
689
|
async function main() {
|
|
690
|
+
const packageJson = await readJson('package.json');
|
|
293
691
|
const commands = await readJson('common/commands/command-catalog.json');
|
|
294
692
|
const commonWorkflowCatalog = await readJson('common/workflows/workflow-catalog.json');
|
|
295
693
|
const skillCatalog = await readJson('common/skills/skill-catalog.json');
|
|
694
|
+
const securityPolicy = await readJson('common/settings/security-policy.json');
|
|
296
695
|
const claudeSubagents = await readJson('common/claude/subagent-catalog.json');
|
|
297
696
|
const claudeTeams = await readJson('common/claude/team-catalog.json');
|
|
298
|
-
const
|
|
299
|
-
const
|
|
697
|
+
const geminiAgents = await readJson('common/gemini/agent-catalog.json');
|
|
698
|
+
const geminiArtifacts = await readJson('common/gemini/artifact-catalog.json');
|
|
300
699
|
const { toolIds, catalogs: workflowCatalogByTool } = buildWorkflowCatalogByTool(
|
|
301
700
|
commonWorkflowCatalog
|
|
302
701
|
);
|
|
@@ -313,23 +712,138 @@ async function main() {
|
|
|
313
712
|
await writeOrCheck(commandPath, toPrettyJson(commands), state);
|
|
314
713
|
}
|
|
315
714
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
715
|
+
await writeOrCheck(
|
|
716
|
+
path.join(rootDir, 'claude-code', 'settings', 'settings.json'),
|
|
717
|
+
toPrettyJson(renderClaudeProjectSettingsJson(securityPolicy)),
|
|
718
|
+
state
|
|
719
|
+
);
|
|
720
|
+
await writeOrCheck(
|
|
721
|
+
path.join(rootDir, 'claude-code', 'settings', 'settings.local.json'),
|
|
722
|
+
toPrettyJson(renderClaudeLocalSettingsJson(securityPolicy)),
|
|
723
|
+
state
|
|
724
|
+
);
|
|
725
|
+
await writeOrCheck(
|
|
726
|
+
path.join(rootDir, 'gemini', 'settings', 'editor-policy.json'),
|
|
727
|
+
toPrettyJson(renderGeminiEditorPolicyJson(securityPolicy)),
|
|
728
|
+
state
|
|
729
|
+
);
|
|
730
|
+
await writeOrCheck(
|
|
731
|
+
path.join(rootDir, 'codex', 'settings', 'runtime-policy.json'),
|
|
732
|
+
toPrettyJson(renderCodexRuntimePolicyJson(securityPolicy)),
|
|
733
|
+
state
|
|
734
|
+
);
|
|
735
|
+
await writeOrCheck(
|
|
736
|
+
path.join(rootDir, 'codex', 'instructions', 'AGENTS.permissions.generated.md'),
|
|
737
|
+
renderCodexPermissionsMarkdown(securityPolicy),
|
|
738
|
+
state
|
|
739
|
+
);
|
|
740
|
+
await pruneGeneratedFilesBySuffix(
|
|
741
|
+
path.join(rootDir, 'claude-code', 'settings'),
|
|
742
|
+
'.json',
|
|
743
|
+
new Set(['settings.json', 'settings.local.json']),
|
|
744
|
+
state
|
|
745
|
+
);
|
|
746
|
+
await pruneGeneratedFilesBySuffix(
|
|
747
|
+
path.join(rootDir, 'gemini', 'settings'),
|
|
748
|
+
'.json',
|
|
749
|
+
new Set(['editor-policy.json']),
|
|
750
|
+
state
|
|
751
|
+
);
|
|
752
|
+
await pruneGeneratedFilesBySuffix(
|
|
753
|
+
path.join(rootDir, 'codex', 'settings'),
|
|
754
|
+
'.json',
|
|
755
|
+
new Set(['runtime-policy.json']),
|
|
756
|
+
state
|
|
757
|
+
);
|
|
758
|
+
|
|
759
|
+
const claudeCommandFileNames = new Set();
|
|
760
|
+
const geminiCommandFileNames = new Set();
|
|
761
|
+
const codexCommandSkillNames = new Set();
|
|
762
|
+
for (const command of commands.commands ?? []) {
|
|
763
|
+
if (!command?.id) {
|
|
764
|
+
throw new Error('[commands] command id가 비어 있습니다.');
|
|
320
765
|
}
|
|
321
|
-
const
|
|
322
|
-
|
|
766
|
+
const basename = normalizeCommandBasename(command.id);
|
|
767
|
+
claudeCommandFileNames.add(`${basename}.md`);
|
|
768
|
+
geminiCommandFileNames.add(`${basename}.toml`);
|
|
769
|
+
codexCommandSkillNames.add(basename);
|
|
770
|
+
|
|
771
|
+
await writeOrCheck(
|
|
772
|
+
path.join(rootDir, 'claude-code', 'commands', 'native', `${basename}.md`),
|
|
773
|
+
renderClaudeCommandMarkdown(command),
|
|
774
|
+
state
|
|
775
|
+
);
|
|
776
|
+
await writeOrCheck(
|
|
777
|
+
path.join(rootDir, 'gemini', 'commands', 'definitions', `${basename}.toml`),
|
|
778
|
+
renderGeminiCommandToml(command),
|
|
779
|
+
state
|
|
780
|
+
);
|
|
781
|
+
await writeOrCheck(
|
|
782
|
+
path.join(rootDir, 'codex', 'skills', basename, 'SKILL.md'),
|
|
783
|
+
renderCodexCommandSkillMarkdown(command, basename),
|
|
784
|
+
state
|
|
785
|
+
);
|
|
786
|
+
await writeOrCheck(
|
|
787
|
+
path.join(rootDir, 'codex', 'skills', basename, 'agents', 'openai.yaml'),
|
|
788
|
+
renderCodexCommandOpenAiYaml(command),
|
|
789
|
+
state
|
|
790
|
+
);
|
|
323
791
|
}
|
|
792
|
+
await pruneGeneratedFilesBySuffix(
|
|
793
|
+
path.join(rootDir, 'claude-code', 'commands', 'native'),
|
|
794
|
+
'.md',
|
|
795
|
+
claudeCommandFileNames,
|
|
796
|
+
state
|
|
797
|
+
);
|
|
798
|
+
await pruneGeneratedFilesBySuffix(
|
|
799
|
+
path.join(rootDir, 'gemini', 'commands', 'definitions'),
|
|
800
|
+
'.toml',
|
|
801
|
+
geminiCommandFileNames,
|
|
802
|
+
state
|
|
803
|
+
);
|
|
804
|
+
// Remove legacy .md command definitions (migrated to .toml)
|
|
805
|
+
await pruneGeneratedFilesBySuffix(
|
|
806
|
+
path.join(rootDir, 'gemini', 'commands', 'definitions'),
|
|
807
|
+
'.md',
|
|
808
|
+
new Set(),
|
|
809
|
+
state
|
|
810
|
+
);
|
|
811
|
+
await pruneCodexCommandSkills(codexCommandSkillNames, state);
|
|
812
|
+
|
|
813
|
+
await writeOrCheck(
|
|
814
|
+
path.join(rootDir, 'gemini', 'gemini-extension.json'),
|
|
815
|
+
toPrettyJson({
|
|
816
|
+
name: 'tri-agent-manager',
|
|
817
|
+
version: packageJson.version,
|
|
818
|
+
description: 'Service operation skills and workflows pack for Gemini CLI',
|
|
819
|
+
contextFileName: 'GEMINI.md'
|
|
820
|
+
}),
|
|
821
|
+
state
|
|
822
|
+
);
|
|
324
823
|
|
|
325
|
-
|
|
326
|
-
|
|
824
|
+
// Workflows are gemini-only: only generate workflow catalog for gemini
|
|
825
|
+
{
|
|
826
|
+
const geminiCatalog = workflowCatalogByTool?.gemini;
|
|
827
|
+
if (!geminiCatalog) {
|
|
828
|
+
throw new Error('[workflows] gemini workflow catalog 누락');
|
|
829
|
+
}
|
|
830
|
+
const workflowPath = path.join(rootDir, 'gemini', 'workflows', 'workflow-catalog.json');
|
|
831
|
+
await writeOrCheck(workflowPath, toPrettyJson(geminiCatalog), state);
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
const geminiWorkflowNames = new Set();
|
|
835
|
+
for (const workflow of workflowCatalogByTool.gemini?.workflows ?? []) {
|
|
327
836
|
const filename = `${workflow.id}.workflow.yaml`;
|
|
328
|
-
|
|
329
|
-
const definitionPath = path.join(rootDir, '
|
|
330
|
-
await writeOrCheck(definitionPath,
|
|
837
|
+
geminiWorkflowNames.add(filename);
|
|
838
|
+
const definitionPath = path.join(rootDir, 'gemini', 'workflows', 'definitions', filename);
|
|
839
|
+
await writeOrCheck(definitionPath, renderGeminiWorkflowDefinition(workflow), state);
|
|
331
840
|
}
|
|
332
|
-
await
|
|
841
|
+
await pruneGeneratedFilesBySuffix(
|
|
842
|
+
path.join(rootDir, 'gemini', 'workflows', 'definitions'),
|
|
843
|
+
'.workflow.yaml',
|
|
844
|
+
geminiWorkflowNames,
|
|
845
|
+
state
|
|
846
|
+
);
|
|
333
847
|
|
|
334
848
|
for (const skill of skillCatalog.skills ?? []) {
|
|
335
849
|
for (const toolId of toolIds) {
|
|
@@ -353,14 +867,14 @@ async function main() {
|
|
|
353
867
|
);
|
|
354
868
|
|
|
355
869
|
await writeOrCheck(
|
|
356
|
-
path.join(rootDir, '
|
|
357
|
-
toPrettyJson(
|
|
870
|
+
path.join(rootDir, 'gemini', 'agents', 'agent-catalog.json'),
|
|
871
|
+
toPrettyJson(geminiAgents),
|
|
358
872
|
state
|
|
359
873
|
);
|
|
360
874
|
|
|
361
875
|
await writeOrCheck(
|
|
362
|
-
path.join(rootDir, '
|
|
363
|
-
toPrettyJson(
|
|
876
|
+
path.join(rootDir, 'gemini', 'artifacts', 'artifact-catalog.json'),
|
|
877
|
+
toPrettyJson(geminiArtifacts),
|
|
364
878
|
state
|
|
365
879
|
);
|
|
366
880
|
|