@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.
Files changed (141) hide show
  1. package/README.md +36 -25
  2. package/antigravity/README.md +14 -1
  3. package/antigravity/agents/agent-catalog.json +5 -5
  4. package/antigravity/commands/definitions/cmd-dev-be-api.md +43 -0
  5. package/antigravity/commands/definitions/cmd-dev-fe-hook-separate.md +43 -0
  6. package/antigravity/commands/definitions/cmd-dev-fe-ui-componentize.md +43 -0
  7. package/antigravity/commands/definitions/cmd-dev-perf-optimize.md +43 -0
  8. package/antigravity/commands/definitions/cmd-dev-sequential-autorun.md +43 -0
  9. package/antigravity/commands/definitions/cmd-doc-handoff.md +44 -0
  10. package/antigravity/commands/definitions/cmd-improve-techdebt.md +43 -0
  11. package/antigravity/commands/definitions/cmd-incident-triage.md +43 -0
  12. package/antigravity/commands/definitions/cmd-ops-ci-cd-gate.md +43 -0
  13. package/antigravity/commands/definitions/cmd-ops-deploy.md +43 -0
  14. package/antigravity/commands/definitions/cmd-ops-monitoring.md +43 -0
  15. package/antigravity/commands/definitions/cmd-plan-arch-decision.md +43 -0
  16. package/antigravity/commands/definitions/cmd-plan-implementation-bootstrap.md +43 -0
  17. package/antigravity/commands/definitions/cmd-plan-prd-details.md +43 -0
  18. package/antigravity/commands/definitions/cmd-plan-prd-master.md +44 -0
  19. package/antigravity/commands/definitions/cmd-plan-req-lock.md +44 -0
  20. package/antigravity/commands/definitions/cmd-review-code.md +43 -0
  21. package/antigravity/commands/definitions/cmd-sec-dependency-audit.md +43 -0
  22. package/antigravity/commands/definitions/cmd-sec-threat-model.md +43 -0
  23. package/antigravity/commands/definitions/cmd-test-unit-integration.md +43 -0
  24. package/antigravity/instructions/WORKSPACE-RULES.template.md +34 -0
  25. package/antigravity/settings/editor-policy.json +193 -0
  26. package/antigravity/skills/change-safety-review/SKILL.md +40 -0
  27. package/antigravity/skills/code-review-and-improvement/SKILL.md +20 -0
  28. package/antigravity/skills/frontend-repetition-pack/SKILL.md +20 -0
  29. package/antigravity/skills/incident-response/SKILL.md +20 -0
  30. package/antigravity/skills/prd-to-production-pipeline/SKILL.md +21 -1
  31. package/antigravity/skills/release-and-operations/SKILL.md +20 -0
  32. package/antigravity/skills/security-hardening/SKILL.md +21 -1
  33. package/antigravity/skills/service-lifecycle-orchestration/SKILL.md +21 -1
  34. package/antigravity/workflows/definitions/WF-FRONTEND-REFACTOR.workflow.yaml +38 -0
  35. package/antigravity/workflows/definitions/WF-INCIDENT-RESPONSE.workflow.yaml +41 -0
  36. package/antigravity/workflows/definitions/WF-PRD-TO-PRODUCTION.workflow.yaml +76 -0
  37. package/antigravity/workflows/definitions/WF-SECURITY-HARDENING.workflow.yaml +40 -0
  38. package/antigravity/workflows/definitions/WF-SERVICE-E2E.workflow.yaml +67 -0
  39. package/antigravity/workflows/workflow-catalog.json +5 -5
  40. package/catalog/tool-catalog.ko.json +78 -17
  41. package/claude-code/README.md +31 -1
  42. package/claude-code/agent-teams/team-catalog.json +7 -7
  43. package/claude-code/commands/native/cmd-dev-be-api.md +51 -0
  44. package/claude-code/commands/native/cmd-dev-fe-hook-separate.md +51 -0
  45. package/claude-code/commands/native/cmd-dev-fe-ui-componentize.md +51 -0
  46. package/claude-code/commands/native/cmd-dev-perf-optimize.md +51 -0
  47. package/claude-code/commands/native/cmd-dev-sequential-autorun.md +51 -0
  48. package/claude-code/commands/native/cmd-doc-handoff.md +52 -0
  49. package/claude-code/commands/native/cmd-improve-techdebt.md +51 -0
  50. package/claude-code/commands/native/cmd-incident-triage.md +51 -0
  51. package/claude-code/commands/native/cmd-ops-ci-cd-gate.md +51 -0
  52. package/claude-code/commands/native/cmd-ops-deploy.md +51 -0
  53. package/claude-code/commands/native/cmd-ops-monitoring.md +51 -0
  54. package/claude-code/commands/native/cmd-plan-arch-decision.md +51 -0
  55. package/claude-code/commands/native/cmd-plan-implementation-bootstrap.md +51 -0
  56. package/claude-code/commands/native/cmd-plan-prd-details.md +51 -0
  57. package/claude-code/commands/native/cmd-plan-prd-master.md +52 -0
  58. package/claude-code/commands/native/cmd-plan-req-lock.md +52 -0
  59. package/claude-code/commands/native/cmd-review-code.md +51 -0
  60. package/claude-code/commands/native/cmd-sec-dependency-audit.md +51 -0
  61. package/claude-code/commands/native/cmd-sec-threat-model.md +51 -0
  62. package/claude-code/commands/native/cmd-test-unit-integration.md +51 -0
  63. package/claude-code/instructions/CLAUDE.template.md +42 -0
  64. package/claude-code/settings/settings.json +183 -0
  65. package/claude-code/settings/settings.local.json +10 -0
  66. package/claude-code/skills/change-safety-review/SKILL.md +40 -0
  67. package/claude-code/skills/code-review-and-improvement/SKILL.md +21 -1
  68. package/claude-code/skills/frontend-repetition-pack/SKILL.md +21 -1
  69. package/claude-code/skills/incident-response/SKILL.md +21 -1
  70. package/claude-code/skills/prd-to-production-pipeline/SKILL.md +21 -1
  71. package/claude-code/skills/release-and-operations/SKILL.md +21 -1
  72. package/claude-code/skills/security-hardening/SKILL.md +21 -1
  73. package/claude-code/skills/service-lifecycle-orchestration/SKILL.md +21 -1
  74. package/claude-code/workflows/workflow-catalog.json +8 -8
  75. package/codex/README.md +18 -3
  76. package/codex/automations/automation-recipes.toml +4 -4
  77. package/codex/instructions/AGENTS.permissions.generated.md +121 -0
  78. package/codex/instructions/AGENTS.template.md +24 -8
  79. package/codex/settings/runtime-policy.json +188 -0
  80. package/codex/skills/change-safety-review/SKILL.md +40 -0
  81. package/codex/skills/change-safety-review/agents/openai.yaml +4 -0
  82. package/codex/skills/cmd-dev-be-api/SKILL.md +43 -0
  83. package/codex/skills/cmd-dev-be-api/agents/openai.yaml +4 -0
  84. package/codex/skills/cmd-dev-fe-hook-separate/SKILL.md +43 -0
  85. package/codex/skills/cmd-dev-fe-hook-separate/agents/openai.yaml +4 -0
  86. package/codex/skills/cmd-dev-fe-ui-componentize/SKILL.md +43 -0
  87. package/codex/skills/cmd-dev-fe-ui-componentize/agents/openai.yaml +4 -0
  88. package/codex/skills/cmd-dev-perf-optimize/SKILL.md +43 -0
  89. package/codex/skills/cmd-dev-perf-optimize/agents/openai.yaml +4 -0
  90. package/codex/skills/cmd-dev-sequential-autorun/SKILL.md +43 -0
  91. package/codex/skills/cmd-dev-sequential-autorun/agents/openai.yaml +4 -0
  92. package/codex/skills/cmd-doc-handoff/SKILL.md +43 -0
  93. package/codex/skills/cmd-doc-handoff/agents/openai.yaml +4 -0
  94. package/codex/skills/cmd-improve-techdebt/SKILL.md +43 -0
  95. package/codex/skills/cmd-improve-techdebt/agents/openai.yaml +4 -0
  96. package/codex/skills/cmd-incident-triage/SKILL.md +43 -0
  97. package/codex/skills/cmd-incident-triage/agents/openai.yaml +4 -0
  98. package/codex/skills/cmd-ops-ci-cd-gate/SKILL.md +43 -0
  99. package/codex/skills/cmd-ops-ci-cd-gate/agents/openai.yaml +4 -0
  100. package/codex/skills/cmd-ops-deploy/SKILL.md +43 -0
  101. package/codex/skills/cmd-ops-deploy/agents/openai.yaml +4 -0
  102. package/codex/skills/cmd-ops-monitoring/SKILL.md +43 -0
  103. package/codex/skills/cmd-ops-monitoring/agents/openai.yaml +4 -0
  104. package/codex/skills/cmd-plan-arch-decision/SKILL.md +43 -0
  105. package/codex/skills/cmd-plan-arch-decision/agents/openai.yaml +4 -0
  106. package/codex/skills/cmd-plan-implementation-bootstrap/SKILL.md +43 -0
  107. package/codex/skills/cmd-plan-implementation-bootstrap/agents/openai.yaml +4 -0
  108. package/codex/skills/cmd-plan-prd-details/SKILL.md +43 -0
  109. package/codex/skills/cmd-plan-prd-details/agents/openai.yaml +4 -0
  110. package/codex/skills/cmd-plan-prd-master/SKILL.md +44 -0
  111. package/codex/skills/cmd-plan-prd-master/agents/openai.yaml +4 -0
  112. package/codex/skills/cmd-plan-req-lock/SKILL.md +44 -0
  113. package/codex/skills/cmd-plan-req-lock/agents/openai.yaml +4 -0
  114. package/codex/skills/cmd-review-code/SKILL.md +43 -0
  115. package/codex/skills/cmd-review-code/agents/openai.yaml +4 -0
  116. package/codex/skills/cmd-sec-dependency-audit/SKILL.md +43 -0
  117. package/codex/skills/cmd-sec-dependency-audit/agents/openai.yaml +4 -0
  118. package/codex/skills/cmd-sec-threat-model/SKILL.md +43 -0
  119. package/codex/skills/cmd-sec-threat-model/agents/openai.yaml +4 -0
  120. package/codex/skills/cmd-test-unit-integration/SKILL.md +43 -0
  121. package/codex/skills/cmd-test-unit-integration/agents/openai.yaml +4 -0
  122. package/codex/skills/code-review-and-improvement/SKILL.md +21 -1
  123. package/codex/skills/frontend-repetition-pack/SKILL.md +20 -0
  124. package/codex/skills/incident-response/SKILL.md +21 -1
  125. package/codex/skills/prd-to-production-pipeline/SKILL.md +21 -1
  126. package/codex/skills/release-and-operations/SKILL.md +20 -0
  127. package/codex/skills/security-hardening/SKILL.md +21 -1
  128. package/codex/skills/service-lifecycle-orchestration/SKILL.md +21 -1
  129. package/codex/workflows/workflow-catalog.json +6 -6
  130. package/common/antigravity/agent-catalog.json +72 -0
  131. package/common/antigravity/artifact-catalog.json +184 -0
  132. package/common/claude/subagent-catalog.json +419 -0
  133. package/common/claude/team-catalog.json +69 -0
  134. package/common/commands/command-catalog.json +942 -0
  135. package/common/settings/security-policy.json +221 -0
  136. package/common/skills/skill-catalog.json +566 -0
  137. package/common/workflows/workflow-catalog.json +1550 -0
  138. package/package.json +6 -2
  139. package/scripts/generate-from-common.mjs +872 -0
  140. package/scripts/init.mjs +295 -36
  141. package/scripts/validate.mjs +451 -10
@@ -51,6 +51,18 @@ function parseFrontmatter(content) {
51
51
  return result;
52
52
  }
53
53
 
54
+ function normalizeCommandBasename(commandId) {
55
+ const normalized = String(commandId ?? '')
56
+ .trim()
57
+ .toLowerCase()
58
+ .replace(/[^a-z0-9-]+/g, '-')
59
+ .replace(/^-+|-+$/g, '');
60
+ if (!normalized) {
61
+ throw new Error(`invalid command id: ${commandId}`);
62
+ }
63
+ return normalized;
64
+ }
65
+
54
66
  async function validateCatalog() {
55
67
  const catalogPath = path.join(rootDir, 'catalog', 'tool-catalog.ko.json');
56
68
  if (!(await exists(catalogPath))) {
@@ -188,6 +200,214 @@ async function validateCatalog() {
188
200
  }
189
201
  }
190
202
 
203
+ async function validateCommonSources() {
204
+ const requiredJsonFiles = [
205
+ 'common/commands/command-catalog.json',
206
+ 'common/workflows/workflow-catalog.json',
207
+ 'common/skills/skill-catalog.json',
208
+ 'common/settings/security-policy.json',
209
+ 'common/claude/subagent-catalog.json',
210
+ 'common/claude/team-catalog.json',
211
+ 'common/antigravity/agent-catalog.json',
212
+ 'common/antigravity/artifact-catalog.json'
213
+ ];
214
+
215
+ const commonData = {};
216
+ for (const relPath of requiredJsonFiles) {
217
+ const fullPath = path.join(rootDir, relPath);
218
+ if (!(await exists(fullPath))) {
219
+ fail(`[common] 파일 누락: ${relPath}`);
220
+ continue;
221
+ }
222
+ try {
223
+ commonData[relPath] = await readJson(fullPath);
224
+ } catch (error) {
225
+ fail(`[common] JSON 파싱 실패: ${relPath} (${error.message})`);
226
+ }
227
+ }
228
+
229
+ const workflowCatalog = commonData['common/workflows/workflow-catalog.json'];
230
+ if (workflowCatalog) {
231
+ if (!Array.isArray(workflowCatalog.tools) || workflowCatalog.tools.length === 0) {
232
+ fail('[common/workflows] tools 배열 누락 또는 비어 있음');
233
+ }
234
+
235
+ const toolSet = new Set(Array.isArray(workflowCatalog.tools) ? workflowCatalog.tools : []);
236
+ for (const toolId of toolDirs) {
237
+ if (!toolSet.has(toolId)) {
238
+ fail(`[common/workflows] tools에 필수 tool 누락: ${toolId}`);
239
+ }
240
+ }
241
+
242
+ if (!workflowCatalog.workflow_policy || typeof workflowCatalog.workflow_policy !== 'object') {
243
+ fail('[common/workflows] workflow_policy 누락');
244
+ } else {
245
+ for (const toolId of toolSet) {
246
+ if (!(toolId in workflowCatalog.workflow_policy)) {
247
+ fail(`[common/workflows] workflow_policy 누락: ${toolId}`);
248
+ }
249
+ }
250
+ }
251
+
252
+ if (!Array.isArray(workflowCatalog.workflows) || workflowCatalog.workflows.length === 0) {
253
+ fail('[common/workflows] workflows 배열 누락 또는 비어 있음');
254
+ } else {
255
+ const workflowIds = new Set();
256
+ const workflowMap = new Map();
257
+ for (const workflow of workflowCatalog.workflows) {
258
+ if (!workflow?.id) {
259
+ fail('[common/workflows] workflow id 누락');
260
+ continue;
261
+ }
262
+ if (workflowIds.has(workflow.id)) {
263
+ fail(`[common/workflows] 중복 workflow id: ${workflow.id}`);
264
+ }
265
+ workflowIds.add(workflow.id);
266
+ workflowMap.set(workflow.id, workflow);
267
+
268
+ if (!workflow.profiles || typeof workflow.profiles !== 'object') {
269
+ fail(`[common/workflows] profiles 누락: ${workflow.id}`);
270
+ continue;
271
+ }
272
+
273
+ const profileTools = Object.keys(workflow.profiles);
274
+ if (profileTools.length === 0) {
275
+ fail(`[common/workflows] profile 비어 있음: ${workflow.id}`);
276
+ }
277
+
278
+ for (const toolId of profileTools) {
279
+ if (!toolSet.has(toolId)) {
280
+ fail(`[common/workflows] 알 수 없는 tool profile: ${workflow.id}/${toolId}`);
281
+ continue;
282
+ }
283
+ const profile = workflow.profiles[toolId];
284
+ if (!profile || typeof profile !== 'object') {
285
+ fail(`[common/workflows] profile 형식 오류: ${workflow.id}/${toolId}`);
286
+ continue;
287
+ }
288
+ if (!profile.name || !profile.summary || !profile.when_to_use) {
289
+ fail(`[common/workflows] profile 필수 필드 누락: ${workflow.id}/${toolId}`);
290
+ }
291
+ if (!Array.isArray(profile.stages) || profile.stages.length === 0) {
292
+ fail(`[common/workflows] profile stages 누락: ${workflow.id}/${toolId}`);
293
+ }
294
+ }
295
+ }
296
+
297
+ if (!workflowCatalog.workflow_order || typeof workflowCatalog.workflow_order !== 'object') {
298
+ fail('[common/workflows] workflow_order 누락');
299
+ } else {
300
+ for (const toolId of toolSet) {
301
+ const order = workflowCatalog.workflow_order[toolId];
302
+ if (!Array.isArray(order)) {
303
+ fail(`[common/workflows] workflow_order 형식 오류: ${toolId}`);
304
+ continue;
305
+ }
306
+ const seen = new Set();
307
+ for (const workflowId of order) {
308
+ if (!workflowIds.has(workflowId)) {
309
+ fail(`[common/workflows] workflow_order가 없는 id 참조: ${toolId}/${workflowId}`);
310
+ continue;
311
+ }
312
+ const workflow = workflowMap.get(workflowId);
313
+ if (!workflow?.profiles?.[toolId]) {
314
+ fail(`[common/workflows] workflow_order와 profile 불일치: ${toolId}/${workflowId}`);
315
+ }
316
+ if (seen.has(workflowId)) {
317
+ fail(`[common/workflows] workflow_order 중복: ${toolId}/${workflowId}`);
318
+ }
319
+ seen.add(workflowId);
320
+ }
321
+ }
322
+ }
323
+ }
324
+ }
325
+
326
+ const skillCatalog = commonData['common/skills/skill-catalog.json'];
327
+ if (skillCatalog) {
328
+ if (!Array.isArray(skillCatalog.skills) || skillCatalog.skills.length === 0) {
329
+ fail('[common/skills] skills 배열 누락 또는 비어 있음');
330
+ } else {
331
+ const toolSet = new Set(Array.isArray(skillCatalog.tools) ? skillCatalog.tools : []);
332
+ for (const toolId of toolDirs) {
333
+ if (!toolSet.has(toolId)) {
334
+ fail(`[common/skills] tools 누락: ${toolId}`);
335
+ }
336
+ }
337
+
338
+ const skillIds = new Set();
339
+ for (const skill of skillCatalog.skills) {
340
+ if (!skill?.id) {
341
+ fail('[common/skills] skill id 누락');
342
+ continue;
343
+ }
344
+ if (skillIds.has(skill.id)) {
345
+ fail(`[common/skills] 중복 skill id: ${skill.id}`);
346
+ }
347
+ skillIds.add(skill.id);
348
+
349
+ for (const toolId of toolSet) {
350
+ if (!skill.names?.[toolId] || !skill.descriptions?.[toolId]) {
351
+ fail(`[common/skills] 이름/설명 누락: ${skill.id}/${toolId}`);
352
+ }
353
+ if (!Array.isArray(skill.procedures?.[toolId]) || skill.procedures[toolId].length === 0) {
354
+ fail(`[common/skills] 실행 절차 누락: ${skill.id}/${toolId}`);
355
+ }
356
+ if (
357
+ !Array.isArray(skill.quality_rules?.[toolId]) ||
358
+ skill.quality_rules[toolId].length === 0
359
+ ) {
360
+ fail(`[common/skills] 품질 규칙 누락: ${skill.id}/${toolId}`);
361
+ }
362
+ }
363
+
364
+ if (!Array.isArray(skill.input_checklist) || skill.input_checklist.length === 0) {
365
+ fail(`[common/skills] 시작 전 체크리스트 누락: ${skill.id}`);
366
+ }
367
+ if (!Array.isArray(skill.report_format) || skill.report_format.length === 0) {
368
+ fail(`[common/skills] 결과 보고 형식 누락: ${skill.id}`);
369
+ }
370
+ if (!Array.isArray(skill.stop_conditions) || skill.stop_conditions.length === 0) {
371
+ fail(`[common/skills] 중단 조건 누락: ${skill.id}`);
372
+ }
373
+
374
+ if (!skill.codex_openai) {
375
+ fail(`[common/skills] codex_openai 누락: ${skill.id}`);
376
+ } else {
377
+ const ui = skill.codex_openai;
378
+ if (!ui.display_name || !ui.short_description || !ui.default_prompt) {
379
+ fail(`[common/skills] codex_openai 필수 필드 누락: ${skill.id}`);
380
+ }
381
+ }
382
+ }
383
+ }
384
+ }
385
+
386
+ const securityPolicy = commonData['common/settings/security-policy.json'];
387
+ if (securityPolicy) {
388
+ if (!securityPolicy.policy_id || !securityPolicy.shared_permissions) {
389
+ fail('[common/settings] policy_id/shared_permissions 누락');
390
+ }
391
+ const shared = securityPolicy.shared_permissions ?? {};
392
+ for (const field of [
393
+ 'allow_bash',
394
+ 'ask_bash',
395
+ 'deny_bash',
396
+ 'deny_read',
397
+ 'deny_edit'
398
+ ]) {
399
+ if (!Array.isArray(shared[field])) {
400
+ fail(`[common/settings] 배열 누락: ${field}`);
401
+ }
402
+ }
403
+
404
+ const claude = securityPolicy.claude_code ?? {};
405
+ if (!claude.default_mode || !claude.disable_bypass_permissions_mode) {
406
+ fail('[common/settings] claude_code default_mode/disable_bypass_permissions_mode 누락');
407
+ }
408
+ }
409
+ }
410
+
191
411
  async function validatePackageJson() {
192
412
  const packagePath = path.join(rootDir, 'package.json');
193
413
  if (!(await exists(packagePath))) {
@@ -203,7 +423,14 @@ async function validatePackageJson() {
203
423
  return;
204
424
  }
205
425
 
206
- const requiredScripts = ['validate', 'pack:dry-run', 'prepublish:check', 'prepublishOnly'];
426
+ const requiredScripts = [
427
+ 'generate',
428
+ 'generate:check',
429
+ 'validate',
430
+ 'pack:dry-run',
431
+ 'prepublish:check',
432
+ 'prepublishOnly'
433
+ ];
207
434
  for (const scriptName of requiredScripts) {
208
435
  if (!pkg.scripts || !(scriptName in pkg.scripts)) {
209
436
  fail(`[package] scripts 누락: ${scriptName}`);
@@ -215,11 +442,13 @@ async function validatePackageJson() {
215
442
  }
216
443
 
217
444
  const requiredFiles = [
445
+ 'common',
218
446
  'claude-code',
219
447
  'antigravity',
220
448
  'codex',
221
449
  'catalog/tool-catalog.ko.json',
222
450
  'scripts/init.mjs',
451
+ 'scripts/generate-from-common.mjs',
223
452
  'scripts/validate.mjs'
224
453
  ];
225
454
  for (const fileEntry of requiredFiles) {
@@ -282,10 +511,29 @@ async function validateSkillDirectory(toolName) {
282
511
  fail(`[${toolName}] frontmatter name/description 누락: ${skillFile}`);
283
512
  }
284
513
 
514
+ for (const heading of [
515
+ '# 시작 전 체크리스트',
516
+ '# 실행 절차',
517
+ '# 결과 보고 형식',
518
+ '# 중단 조건',
519
+ '# 품질 규칙'
520
+ ]) {
521
+ if (!content.includes(heading)) {
522
+ fail(`[${toolName}] SKILL 섹션 누락: ${skillName}/${heading}`);
523
+ }
524
+ }
525
+
285
526
  if (toolName === 'codex') {
286
527
  const openaiYaml = path.join(skillsPath, skillName, 'agents', 'openai.yaml');
287
528
  if (!(await exists(openaiYaml))) {
288
529
  fail(`[codex] openai.yaml 누락: ${skillName}`);
530
+ } else {
531
+ const raw = await fs.readFile(openaiYaml, 'utf8');
532
+ for (const key of ['display_name', 'short_description', 'default_prompt']) {
533
+ if (!new RegExp(`\\b${key}:\\s+\".+\"`).test(raw)) {
534
+ fail(`[codex] openai.yaml 필수 필드 누락: ${skillName}/${key}`);
535
+ }
536
+ }
289
537
  }
290
538
  }
291
539
  }
@@ -295,7 +543,7 @@ async function validateCommandCatalog(toolName) {
295
543
  const filePath = path.join(rootDir, toolName, 'commands', 'command-catalog.json');
296
544
  if (!(await exists(filePath))) {
297
545
  fail(`[${toolName}] command-catalog.json 누락`);
298
- return { commandIds: new Set() };
546
+ return { commandIds: new Set(), commandFileBases: new Set() };
299
547
  }
300
548
 
301
549
  let data;
@@ -303,12 +551,12 @@ async function validateCommandCatalog(toolName) {
303
551
  data = await readJson(filePath);
304
552
  } catch (error) {
305
553
  fail(`[${toolName}] command-catalog.json 파싱 실패: ${error.message}`);
306
- return { commandIds: new Set() };
554
+ return { commandIds: new Set(), commandFileBases: new Set() };
307
555
  }
308
556
 
309
557
  if (!Array.isArray(data.commands) || data.commands.length === 0) {
310
558
  fail(`[${toolName}] commands 배열 누락 또는 비어 있음`);
311
- return { commandIds: new Set() };
559
+ return { commandIds: new Set(), commandFileBases: new Set() };
312
560
  }
313
561
 
314
562
  const requiredFields = [
@@ -325,6 +573,7 @@ async function validateCommandCatalog(toolName) {
325
573
  ];
326
574
 
327
575
  const commandIds = new Set();
576
+ const commandFileBases = new Set();
328
577
  for (const cmd of data.commands) {
329
578
  for (const field of requiredFields) {
330
579
  if (!(field in cmd)) {
@@ -341,6 +590,11 @@ async function validateCommandCatalog(toolName) {
341
590
  fail(`[${toolName}] 중복 명령 ID: ${cmd.id}`);
342
591
  }
343
592
  commandIds.add(cmd.id);
593
+ try {
594
+ commandFileBases.add(normalizeCommandBasename(cmd.id));
595
+ } catch (error) {
596
+ fail(`[${toolName}] 명령 ID 형식 오류: ${cmd.id} (${error.message})`);
597
+ }
344
598
  }
345
599
 
346
600
  const repeatedIds = [
@@ -355,7 +609,7 @@ async function validateCommandCatalog(toolName) {
355
609
  }
356
610
  }
357
611
 
358
- return { commandIds };
612
+ return { commandIds, commandFileBases };
359
613
  }
360
614
 
361
615
  async function validateWorkflowCatalog(toolName, commandIds) {
@@ -409,7 +663,7 @@ async function validateWorkflowCatalog(toolName, commandIds) {
409
663
  }
410
664
  }
411
665
 
412
- async function validateClaudeExtras() {
666
+ async function validateClaudeExtras(commandFileBases) {
413
667
  const subagentsPath = path.join(rootDir, 'claude-code', 'subagents');
414
668
  if (!(await exists(subagentsPath))) {
415
669
  fail('[claude-code] subagents 디렉터리 누락');
@@ -434,9 +688,37 @@ async function validateClaudeExtras() {
434
688
  if (!(await exists(teamCatalog))) {
435
689
  fail('[claude-code] team-catalog.json 누락');
436
690
  }
691
+
692
+ const commandDir = path.join(rootDir, 'claude-code', 'commands', 'native');
693
+ if (!(await exists(commandDir))) {
694
+ fail('[claude-code] commands/native 디렉터리 누락');
695
+ return;
696
+ }
697
+
698
+ const commandFiles = new Set((await fs.readdir(commandDir)).filter((file) => file.endsWith('.md')));
699
+ for (const base of commandFileBases) {
700
+ const expected = `${base}.md`;
701
+ if (!commandFiles.has(expected)) {
702
+ fail(`[claude-code] native command 누락: commands/native/${expected}`);
703
+ }
704
+ }
437
705
  }
438
706
 
439
- async function validateAntigravityExtras() {
707
+ async function validateCodexExtras(commandFileBases) {
708
+ const codexSkillDir = path.join(rootDir, 'codex', 'skills');
709
+ for (const base of commandFileBases) {
710
+ const skillFile = path.join(codexSkillDir, base, 'SKILL.md');
711
+ const openAiYaml = path.join(codexSkillDir, base, 'agents', 'openai.yaml');
712
+ if (!(await exists(skillFile))) {
713
+ fail(`[codex] 명령 스킬 누락: skills/${base}/SKILL.md`);
714
+ }
715
+ if (!(await exists(openAiYaml))) {
716
+ fail(`[codex] 명령 스킬 openai.yaml 누락: skills/${base}/agents/openai.yaml`);
717
+ }
718
+ }
719
+ }
720
+
721
+ async function validateAntigravityExtras(commandFileBases) {
440
722
  const agentCatalog = path.join(rootDir, 'antigravity', 'agents', 'agent-catalog.json');
441
723
  const artifactCatalog = path.join(rootDir, 'antigravity', 'artifacts', 'artifact-catalog.json');
442
724
 
@@ -452,6 +734,52 @@ async function validateAntigravityExtras() {
452
734
  fail(`[antigravity] JSON 파싱 실패: ${path.relative(rootDir, f)} (${error.message})`);
453
735
  }
454
736
  }
737
+
738
+ const workflowCatalogPath = path.join(rootDir, 'antigravity', 'workflows', 'workflow-catalog.json');
739
+ const workflowDefsDir = path.join(rootDir, 'antigravity', 'workflows', 'definitions');
740
+
741
+ if (!(await exists(workflowDefsDir))) {
742
+ fail('[antigravity] workflow definitions 디렉터리 누락');
743
+ return;
744
+ }
745
+
746
+ let workflowCatalog;
747
+ try {
748
+ workflowCatalog = await readJson(workflowCatalogPath);
749
+ } catch (error) {
750
+ fail(`[antigravity] workflow-catalog.json 파싱 실패: ${error.message}`);
751
+ return;
752
+ }
753
+
754
+ const workflowIds = new Set(
755
+ Array.isArray(workflowCatalog.workflows) ? workflowCatalog.workflows.map((wf) => wf.id) : []
756
+ );
757
+ const definitionFiles = (await fs.readdir(workflowDefsDir))
758
+ .filter((file) => file.endsWith('.workflow.yaml'))
759
+ .sort();
760
+
761
+ for (const workflowId of workflowIds) {
762
+ const expected = `${workflowId}.workflow.yaml`;
763
+ if (!definitionFiles.includes(expected)) {
764
+ fail(`[antigravity] workflow definition 누락: ${expected}`);
765
+ }
766
+ }
767
+
768
+ const commandDefinitionsDir = path.join(rootDir, 'antigravity', 'commands', 'definitions');
769
+ if (!(await exists(commandDefinitionsDir))) {
770
+ fail('[antigravity] commands/definitions 디렉터리 누락');
771
+ return;
772
+ }
773
+
774
+ const commandDefinitionFiles = new Set(
775
+ (await fs.readdir(commandDefinitionsDir)).filter((file) => file.endsWith('.md'))
776
+ );
777
+ for (const base of commandFileBases) {
778
+ const expected = `${base}.md`;
779
+ if (!commandDefinitionFiles.has(expected)) {
780
+ fail(`[antigravity] command definition 누락: commands/definitions/${expected}`);
781
+ }
782
+ }
455
783
  }
456
784
 
457
785
  async function validateReadmes() {
@@ -463,6 +791,89 @@ async function validateReadmes() {
463
791
  }
464
792
  }
465
793
 
794
+ async function validateSettingsOutputs() {
795
+ const settingsFiles = [
796
+ 'claude-code/settings/settings.json',
797
+ 'claude-code/settings/settings.local.json',
798
+ 'antigravity/settings/editor-policy.json',
799
+ 'codex/settings/runtime-policy.json'
800
+ ];
801
+
802
+ for (const relPath of settingsFiles) {
803
+ const fullPath = path.join(rootDir, relPath);
804
+ if (!(await exists(fullPath))) {
805
+ fail(`[settings] 파일 누락: ${relPath}`);
806
+ continue;
807
+ }
808
+ try {
809
+ await readJson(fullPath);
810
+ } catch (error) {
811
+ fail(`[settings] JSON 파싱 실패: ${relPath} (${error.message})`);
812
+ }
813
+ }
814
+
815
+ const claudeProjectPath = path.join(
816
+ rootDir,
817
+ 'claude-code',
818
+ 'settings',
819
+ 'settings.json'
820
+ );
821
+ if (await exists(claudeProjectPath)) {
822
+ try {
823
+ const claude = await readJson(claudeProjectPath);
824
+ const permissions = claude.permissions ?? {};
825
+ const validModes = new Set([
826
+ 'default',
827
+ 'acceptEdits',
828
+ 'plan',
829
+ 'dontAsk',
830
+ 'bypassPermissions'
831
+ ]);
832
+ if (!validModes.has(permissions.defaultMode)) {
833
+ fail(`[settings] Claude defaultMode 값 오류: ${permissions.defaultMode}`);
834
+ }
835
+ for (const key of ['allow', 'ask', 'deny']) {
836
+ if (!Array.isArray(permissions[key])) {
837
+ fail(`[settings] Claude permissions.${key} 누락`);
838
+ }
839
+ }
840
+ } catch (error) {
841
+ fail(`[settings] Claude 설정 검증 실패: ${error.message}`);
842
+ }
843
+ }
844
+
845
+ const codexAgentsPermissionPath = path.join(
846
+ rootDir,
847
+ 'codex',
848
+ 'instructions',
849
+ 'AGENTS.permissions.generated.md'
850
+ );
851
+ if (!(await exists(codexAgentsPermissionPath))) {
852
+ fail('[settings] Codex AGENTS.permissions.generated.md 누락');
853
+ }
854
+ }
855
+
856
+ async function validateProjectRuleTemplates() {
857
+ const requiredFiles = [
858
+ 'claude-code/instructions/CLAUDE.template.md',
859
+ 'codex/instructions/AGENTS.template.md',
860
+ 'antigravity/instructions/WORKSPACE-RULES.template.md'
861
+ ];
862
+
863
+ for (const relPath of requiredFiles) {
864
+ const fullPath = path.join(rootDir, relPath);
865
+ if (!(await exists(fullPath))) {
866
+ fail(`[project-rules] 템플릿 누락: ${relPath}`);
867
+ continue;
868
+ }
869
+
870
+ const content = await fs.readFile(fullPath, 'utf8');
871
+ if (!content.trim()) {
872
+ fail(`[project-rules] 템플릿 비어 있음: ${relPath}`);
873
+ }
874
+ }
875
+ }
876
+
466
877
  async function runCliSmokeTests() {
467
878
  const nodeBin = process.execPath;
468
879
  const cliPath = path.join(rootDir, 'scripts', 'init.mjs');
@@ -561,6 +972,30 @@ async function runCliSmokeTests() {
561
972
  fail(`[cli] install --install-root dry-run 실행 실패: ${error.message}`);
562
973
  }
563
974
 
975
+ try {
976
+ await execFileAsync(
977
+ nodeBin,
978
+ [
979
+ cliPath,
980
+ 'install',
981
+ '--tool',
982
+ 'antigravity',
983
+ '--components',
984
+ 'skills',
985
+ '--project-rules',
986
+ 'if-instructions',
987
+ '--target',
988
+ '/tmp/tri-agent-manager-validate',
989
+ '--dry-run',
990
+ '--yes',
991
+ '--non-interactive'
992
+ ],
993
+ { cwd: rootDir }
994
+ );
995
+ } catch (error) {
996
+ fail(`[cli] install --project-rules dry-run 실행 실패: ${error.message}`);
997
+ }
998
+
564
999
  try {
565
1000
  await execFileAsync(
566
1001
  nodeBin,
@@ -626,17 +1061,23 @@ async function runCliSmokeTests() {
626
1061
 
627
1062
  async function main() {
628
1063
  await validatePackageJson();
1064
+ await validateCommonSources();
629
1065
  await validateCatalog();
630
1066
  await validateReadmes();
1067
+ await validateSettingsOutputs();
1068
+ await validateProjectRuleTemplates();
631
1069
 
1070
+ const commandFileBaseByTool = {};
632
1071
  for (const toolName of toolDirs) {
633
1072
  await validateSkillDirectory(toolName);
634
- const { commandIds } = await validateCommandCatalog(toolName);
1073
+ const { commandIds, commandFileBases } = await validateCommandCatalog(toolName);
1074
+ commandFileBaseByTool[toolName] = commandFileBases;
635
1075
  await validateWorkflowCatalog(toolName, commandIds);
636
1076
  }
637
1077
 
638
- await validateClaudeExtras();
639
- await validateAntigravityExtras();
1078
+ await validateClaudeExtras(commandFileBaseByTool['claude-code'] ?? new Set());
1079
+ await validateCodexExtras(commandFileBaseByTool.codex ?? new Set());
1080
+ await validateAntigravityExtras(commandFileBaseByTool.antigravity ?? new Set());
640
1081
  await runCliSmokeTests();
641
1082
 
642
1083
  if (errors.length > 0) {