@silbaram/artifact-driven-agent 0.1.9 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. package/README.md +296 -25
  2. package/ai-dev-team/.ada-status.template.json +10 -10
  3. package/ai-dev-team/README.md +39 -44
  4. package/ai-dev-team/artifacts/api.md +2 -212
  5. package/ai-dev-team/artifacts/features/_template/api.md +19 -19
  6. package/ai-dev-team/artifacts/features/_template/review.md +14 -14
  7. package/ai-dev-team/artifacts/features/_template/spec.md +28 -28
  8. package/ai-dev-team/artifacts/features/_template/ui.md +14 -14
  9. package/ai-dev-team/artifacts/improvement-reports/IMP-0000-template.md +57 -57
  10. package/ai-dev-team/artifacts/project.md +2 -193
  11. package/ai-dev-team/artifacts/rfc/RFC-0000-template.md +49 -49
  12. package/core/artifacts/decision.md +72 -72
  13. package/core/artifacts/plan.md +187 -187
  14. package/core/artifacts/project.md +193 -193
  15. package/core/artifacts/sprints/_template/docs/release-notes.md +37 -37
  16. package/core/artifacts/sprints/_template/meta.md +54 -54
  17. package/core/artifacts/sprints/_template/retrospective.md +50 -50
  18. package/core/artifacts/sprints/_template/review-reports/review-template.md +49 -49
  19. package/core/artifacts/sprints/_template/tasks/task-template.md +43 -43
  20. package/core/docs-templates/mkdocs/docs/architecture/overview.md +29 -29
  21. package/core/docs-templates/mkdocs/docs/changelog.md +36 -36
  22. package/core/docs-templates/mkdocs/docs/contributing/contributing.md +60 -60
  23. package/core/docs-templates/mkdocs/docs/getting-started/configuration.md +51 -51
  24. package/core/docs-templates/mkdocs/docs/getting-started/installation.md +41 -41
  25. package/core/docs-templates/mkdocs/docs/getting-started/quick-start.md +56 -56
  26. package/core/docs-templates/mkdocs/docs/guides/api-reference.md +83 -83
  27. package/core/docs-templates/mkdocs/docs/index.md +32 -32
  28. package/core/docs-templates/mkdocs/mkdocs.yml +86 -86
  29. package/core/roles/analyzer.md +265 -265
  30. package/core/roles/developer.md +6 -1
  31. package/core/roles/documenter.md +226 -715
  32. package/core/roles/improver.md +461 -461
  33. package/core/roles/manager.md +574 -544
  34. package/core/roles/planner.md +398 -398
  35. package/core/roles/reviewer.md +294 -294
  36. package/core/rules/document-priority.md +198 -198
  37. package/core/rules/escalation.md +172 -172
  38. package/core/rules/iteration.md +236 -236
  39. package/core/rules/rfc.md +31 -31
  40. package/core/rules/rollback.md +218 -218
  41. package/core/skills/_template/SKILL.md +50 -0
  42. package/core/skills/clean-code/SKILL.md +177 -0
  43. package/core/skills/code-review-checklist/SKILL.md +125 -0
  44. package/core/skills/error-handling/SKILL.md +155 -0
  45. package/core/skills/talk-game-dot-design/SKILL.md +85 -0
  46. package/core/skills/talk-game-dot-design/references/asset-specs.md +57 -0
  47. package/core/skills/talk-game-dot-design/references/review-checklist.md +34 -0
  48. package/core/skills/task-writing/SKILL.md +166 -0
  49. package/core/skills/testing-patterns/SKILL.md +140 -0
  50. package/dist/bin/cli.d.ts +2 -0
  51. package/dist/bin/cli.js +121 -0
  52. package/dist/bin/cli.js.map +1 -0
  53. package/dist/src/commands/config.d.ts +4 -0
  54. package/dist/src/commands/config.js +427 -0
  55. package/dist/src/commands/config.js.map +1 -0
  56. package/dist/src/commands/docs.d.ts +12 -0
  57. package/dist/src/commands/docs.js +453 -0
  58. package/dist/src/commands/docs.js.map +1 -0
  59. package/dist/src/commands/interactive.d.ts +4 -0
  60. package/dist/src/commands/interactive.js +380 -0
  61. package/dist/src/commands/interactive.js.map +1 -0
  62. package/dist/src/commands/logs.d.ts +1 -0
  63. package/dist/src/commands/logs.js +76 -0
  64. package/dist/src/commands/logs.js.map +1 -0
  65. package/dist/src/commands/monitor.d.ts +6 -0
  66. package/dist/src/commands/monitor.js +216 -0
  67. package/dist/src/commands/monitor.js.map +1 -0
  68. package/dist/src/commands/reset.d.ts +4 -0
  69. package/dist/src/commands/reset.js +57 -0
  70. package/dist/src/commands/reset.js.map +1 -0
  71. package/dist/src/commands/run.d.ts +45 -0
  72. package/dist/src/commands/run.js +421 -0
  73. package/dist/src/commands/run.js.map +1 -0
  74. package/dist/src/commands/sessions.d.ts +5 -0
  75. package/dist/src/commands/sessions.js +563 -0
  76. package/dist/src/commands/sessions.js.map +1 -0
  77. package/dist/src/commands/setup.d.ts +1 -0
  78. package/dist/src/commands/setup.js +132 -0
  79. package/dist/src/commands/setup.js.map +1 -0
  80. package/dist/src/commands/skills.d.ts +4 -0
  81. package/dist/src/commands/skills.js +748 -0
  82. package/dist/src/commands/skills.js.map +1 -0
  83. package/dist/src/commands/sprint.d.ts +4 -0
  84. package/dist/src/commands/sprint.js +434 -0
  85. package/dist/src/commands/sprint.js.map +1 -0
  86. package/dist/src/commands/status.d.ts +1 -0
  87. package/dist/src/commands/status.js +97 -0
  88. package/dist/src/commands/status.js.map +1 -0
  89. package/dist/src/commands/upgrade.d.ts +6 -0
  90. package/dist/src/commands/upgrade.js +350 -0
  91. package/dist/src/commands/upgrade.js.map +1 -0
  92. package/dist/src/commands/validate.d.ts +8 -0
  93. package/dist/src/commands/validate.js +260 -0
  94. package/dist/src/commands/validate.js.map +1 -0
  95. package/dist/src/commands/validate.test.d.ts +1 -0
  96. package/dist/src/commands/validate.test.js +74 -0
  97. package/dist/src/commands/validate.test.js.map +1 -0
  98. package/{src/index.js → dist/src/index.d.ts} +1 -3
  99. package/dist/src/index.js +15 -0
  100. package/dist/src/index.js.map +1 -0
  101. package/dist/src/types/common.d.ts +31 -0
  102. package/dist/src/types/common.js +2 -0
  103. package/dist/src/types/common.js.map +1 -0
  104. package/dist/src/types/config.d.ts +46 -0
  105. package/dist/src/types/config.js +2 -0
  106. package/dist/src/types/config.js.map +1 -0
  107. package/dist/src/types/index.d.ts +53 -0
  108. package/dist/src/types/index.js +2 -0
  109. package/dist/src/types/index.js.map +1 -0
  110. package/dist/src/types/session.d.ts +87 -0
  111. package/dist/src/types/session.js +2 -0
  112. package/dist/src/types/session.js.map +1 -0
  113. package/dist/src/types/task.d.ts +32 -0
  114. package/dist/src/types/task.js +2 -0
  115. package/dist/src/types/task.js.map +1 -0
  116. package/dist/src/ui/dashboard.d.ts +9 -0
  117. package/dist/src/ui/dashboard.js +468 -0
  118. package/dist/src/ui/dashboard.js.map +1 -0
  119. package/dist/src/ui/keyHandler.d.ts +47 -0
  120. package/dist/src/ui/keyHandler.js +132 -0
  121. package/dist/src/ui/keyHandler.js.map +1 -0
  122. package/dist/src/ui/quickActions.d.ts +25 -0
  123. package/dist/src/ui/quickActions.js +106 -0
  124. package/dist/src/ui/quickActions.js.map +1 -0
  125. package/dist/src/utils/config.d.ts +33 -0
  126. package/dist/src/utils/config.js +135 -0
  127. package/dist/src/utils/config.js.map +1 -0
  128. package/dist/src/utils/files.d.ts +84 -0
  129. package/dist/src/utils/files.js +202 -0
  130. package/dist/src/utils/files.js.map +1 -0
  131. package/dist/src/utils/promptBuilder.d.ts +39 -0
  132. package/dist/src/utils/promptBuilder.js +726 -0
  133. package/dist/src/utils/promptBuilder.js.map +1 -0
  134. package/dist/src/utils/sessionState.d.ts +83 -0
  135. package/dist/src/utils/sessionState.js +466 -0
  136. package/dist/src/utils/sessionState.js.map +1 -0
  137. package/dist/src/utils/sessionState.process.test.d.ts +1 -0
  138. package/dist/src/utils/sessionState.process.test.js +93 -0
  139. package/dist/src/utils/sessionState.process.test.js.map +1 -0
  140. package/dist/src/utils/sessionState.test.d.ts +1 -0
  141. package/dist/src/utils/sessionState.test.js +150 -0
  142. package/dist/src/utils/sessionState.test.js.map +1 -0
  143. package/dist/src/utils/sprintUtils.d.ts +13 -0
  144. package/dist/src/utils/sprintUtils.js +129 -0
  145. package/dist/src/utils/sprintUtils.js.map +1 -0
  146. package/dist/src/utils/taskParser.d.ts +9 -0
  147. package/dist/src/utils/taskParser.js +122 -0
  148. package/dist/src/utils/taskParser.js.map +1 -0
  149. package/dist/src/utils/taskParser.test.d.ts +1 -0
  150. package/dist/src/utils/taskParser.test.js +69 -0
  151. package/dist/src/utils/taskParser.test.js.map +1 -0
  152. package/package.json +62 -47
  153. package/templates/cli/artifacts/commands.md +262 -262
  154. package/templates/cli/artifacts/output-format.md +298 -298
  155. package/templates/cli/rules/command-change.md +225 -225
  156. package/templates/game/artifacts/assets.md +148 -148
  157. package/templates/game/artifacts/game-systems.md +217 -217
  158. package/templates/game/artifacts/hud.md +199 -199
  159. package/templates/game/rules/system-change.md +184 -184
  160. package/templates/library/artifacts/changelog.md +84 -84
  161. package/templates/library/artifacts/examples.md +157 -157
  162. package/templates/library/artifacts/public-api.md +197 -197
  163. package/templates/library/rules/versioning.md +186 -186
  164. package/templates/web-dev/artifacts/api.md +212 -212
  165. package/templates/web-dev/artifacts/ui.md +104 -104
  166. package/templates/web-dev/rules/api-change.md +198 -198
  167. package/ai-dev-team/.ada-status.json +0 -10
  168. package/ai-dev-team/.ada-version +0 -6
  169. package/ai-dev-team/.current-template +0 -1
  170. package/ai-dev-team/.gitkeep +0 -0
  171. package/ai-dev-team/.sessions/logs/20260124-014551-00f04724.log +0 -5
  172. package/ai-dev-team/.sessions/logs/20260124-014623-cb2b1d44.log +0 -5
  173. package/ai-dev-team/artifacts/.gitkeep +0 -0
  174. package/ai-dev-team/artifacts/decision.md +0 -72
  175. package/ai-dev-team/artifacts/plan.md +0 -187
  176. package/ai-dev-team/artifacts/sprints/_template/docs/release-notes.md +0 -37
  177. package/ai-dev-team/artifacts/sprints/_template/meta.md +0 -54
  178. package/ai-dev-team/artifacts/sprints/_template/retrospective.md +0 -50
  179. package/ai-dev-team/artifacts/sprints/_template/review-reports/review-template.md +0 -49
  180. package/ai-dev-team/artifacts/sprints/_template/tasks/task-template.md +0 -43
  181. package/ai-dev-team/artifacts/ui.md +0 -104
  182. package/ai-dev-team/roles/.gitkeep +0 -0
  183. package/ai-dev-team/roles/analyzer.md +0 -265
  184. package/ai-dev-team/roles/developer.md +0 -222
  185. package/ai-dev-team/roles/documenter.md +0 -715
  186. package/ai-dev-team/roles/improver.md +0 -461
  187. package/ai-dev-team/roles/manager.md +0 -544
  188. package/ai-dev-team/roles/planner.md +0 -398
  189. package/ai-dev-team/roles/reviewer.md +0 -294
  190. package/ai-dev-team/rules/.gitkeep +0 -0
  191. package/ai-dev-team/rules/api-change.md +0 -198
  192. package/ai-dev-team/rules/document-priority.md +0 -199
  193. package/ai-dev-team/rules/escalation.md +0 -172
  194. package/ai-dev-team/rules/iteration.md +0 -236
  195. package/ai-dev-team/rules/rfc.md +0 -31
  196. package/ai-dev-team/rules/rollback.md +0 -218
  197. package/bin/cli.js +0 -127
  198. package/src/commands/config.js +0 -371
  199. package/src/commands/docs.js +0 -502
  200. package/src/commands/interactive.js +0 -392
  201. package/src/commands/logs.js +0 -81
  202. package/src/commands/monitor.js +0 -236
  203. package/src/commands/reset.js +0 -66
  204. package/src/commands/run.js +0 -739
  205. package/src/commands/sessions.js +0 -646
  206. package/src/commands/setup.js +0 -149
  207. package/src/commands/sprint.js +0 -503
  208. package/src/commands/status.js +0 -109
  209. package/src/commands/upgrade.js +0 -416
  210. package/src/commands/validate.js +0 -289
  211. package/src/commands/validate.test.js +0 -84
  212. package/src/ui/dashboard.js +0 -518
  213. package/src/ui/keyHandler.js +0 -147
  214. package/src/ui/quickActions.js +0 -111
  215. package/src/utils/config.js +0 -74
  216. package/src/utils/files.js +0 -201
  217. package/src/utils/sessionState.js +0 -513
  218. package/src/utils/sessionState.process.test.js +0 -101
  219. package/src/utils/sessionState.test.js +0 -183
  220. package/src/utils/sprintUtils.js +0 -134
  221. package/src/utils/taskParser.js +0 -134
  222. package/src/utils/taskParser.test.js +0 -76
@@ -1,739 +0,0 @@
1
- import fs from 'fs-extra';
2
- import path from 'path';
3
- import { spawn } from 'child_process';
4
- import chalk from 'chalk';
5
- import inquirer from 'inquirer';
6
- import {
7
- getWorkspaceDir,
8
- getSessionsDir,
9
- getLogsDir,
10
- getCurrentTemplate,
11
- getAvailableRoles,
12
- generateSessionId,
13
- getTimestamp,
14
- isWorkspaceSetup
15
- } from '../utils/files.js';
16
- import {
17
- registerSession,
18
- unregisterSession,
19
- updateSessionDetails,
20
- getActiveSessions,
21
- getPendingQuestions
22
- } from '../utils/sessionState.js';
23
- import { getToolForRole } from '../utils/config.js';
24
-
25
- /**
26
- * [API] AI 에이전트 세션 실행 (핵심 로직)
27
- * @param {string} role - 실행할 역할 (예: 'developer')
28
- * @param {string} tool - 사용할 도구 (예: 'claude')
29
- * @param {object} options - 추가 옵션
30
- * @returns {Promise<object>} 세션 결과 정보
31
- */
32
- export async function executeAgentSession(role, tool, options = {}) {
33
- const roles = getAvailableRoles();
34
- const tools = ['claude', 'codex', 'gemini', 'copilot'];
35
-
36
- // 역할 검증
37
- if (!roles.includes(role)) {
38
- throw new Error(`알 수 없는 역할입니다: ${role} (사용 가능: ${roles.join(', ')})`);
39
- }
40
-
41
- // 도구 검증
42
- if (!tools.includes(tool)) {
43
- throw new Error(`알 수 없는 도구입니다: ${tool} (사용 가능: ${tools.join(', ')})`);
44
- }
45
-
46
- const workspace = getWorkspaceDir();
47
- const template = getCurrentTemplate();
48
- const sessionId = generateSessionId();
49
- const sessionsDir = getSessionsDir();
50
- const logsDir = getLogsDir();
51
-
52
- // 세션 디렉토리 생성
53
- const sessionDir = path.join(sessionsDir, sessionId);
54
- fs.ensureDirSync(sessionDir);
55
- fs.ensureDirSync(logsDir);
56
-
57
- // 세션 정보 객체
58
- const sessionInfo = {
59
- session_id: sessionId,
60
- role: role,
61
- tool: tool,
62
- template: template,
63
- started_at: getTimestamp(),
64
- status: 'active'
65
- };
66
- const sessionFile = path.join(sessionDir, 'session.json');
67
- fs.writeFileSync(sessionFile, JSON.stringify(sessionInfo, null, 2));
68
-
69
- // 로그 헬퍼
70
- const logFile = path.join(logsDir, `${sessionId}.log`);
71
- const logMessage = (level, msg) => {
72
- const line = `[${getTimestamp()}] [${level}] ${msg}\n`;
73
- fs.appendFileSync(logFile, line);
74
- // 옵션에 따라 콘솔 출력 제어 가능 (현재는 항상 출력)
75
- };
76
-
77
- // 세션 정리 함수 (시그널 핸들러 및 정상 종료에서 공통 사용)
78
- let isCleanedUp = false;
79
- const cleanupSession = (status, reason = null) => {
80
- if (isCleanedUp) return;
81
- isCleanedUp = true;
82
-
83
- sessionInfo.status = status;
84
- sessionInfo.ended_at = getTimestamp();
85
- if (reason) {
86
- sessionInfo.termination_reason = reason;
87
- }
88
-
89
- try {
90
- fs.writeFileSync(sessionFile, JSON.stringify(sessionInfo, null, 2));
91
- logMessage('INFO', `세션 종료 (${status}): ${reason || '정상 종료'}`);
92
- unregisterSession(sessionId);
93
- } catch (err) {
94
- // 정리 중 에러는 무시 (이미 종료 중)
95
- }
96
- };
97
-
98
- // 시그널 핸들러 (Ctrl+C 등 강제 종료 시 세션 정리)
99
- const handleSignal = (signal) => {
100
- logMessage('INFO', `시그널 수신: ${signal}`);
101
- cleanupSession('completed', `사용자 종료 (${signal})`);
102
-
103
- // 핸들러 제거
104
- process.removeListener('SIGINT', handleSignal);
105
- process.removeListener('SIGTERM', handleSignal);
106
-
107
- // exitOnSignal 옵션이 false가 아니면 프로세스 종료 (기본값: true)
108
- if (options.exitOnSignal !== false) {
109
- setTimeout(() => {
110
- process.exit(0);
111
- }, 100);
112
- }
113
- };
114
-
115
- // 시그널 핸들러 등록
116
- process.on('SIGINT', handleSignal);
117
- process.on('SIGTERM', handleSignal);
118
-
119
- try {
120
- logMessage('INFO', `세션 시작: role=${role}, tool=${tool}, template=${template}`);
121
-
122
- // 멀티 세션 등록
123
- registerSession(sessionId, role, tool);
124
- logMessage('INFO', `세션 등록: ${sessionId}`);
125
-
126
- // 역할 파일 로드 (옵션으로 오버라이드 가능)
127
- let systemPrompt;
128
- if (options.systemPromptOverride) {
129
- systemPrompt = options.systemPromptOverride;
130
- logMessage('INFO', '시스템 프롬프트 오버라이드 사용됨');
131
- } else {
132
- const roleFile = path.join(workspace, 'roles', `${role}.md`);
133
- if (!fs.existsSync(roleFile)) {
134
- throw new Error(`역할 파일이 존재하지 않습니다: ${roleFile}`);
135
- }
136
- const roleContent = fs.readFileSync(roleFile, 'utf-8');
137
- systemPrompt = buildSystemPrompt(workspace, role, roleContent);
138
- }
139
-
140
- // 프롬프트 파일 저장
141
- const promptFile = path.join(sessionDir, 'system-prompt.md');
142
- fs.writeFileSync(promptFile, systemPrompt, 'utf-8');
143
- logMessage('INFO', `시스템 프롬프트 저장: ${promptFile}`);
144
-
145
- // 터미널 UI 출력 (Headless 모드가 아닐 때만)
146
- if (!options.headless) {
147
- printSessionBanner(role, tool, sessionId, template);
148
- }
149
-
150
- // AI 도구 프로세스 실행
151
- const handleSpawn = (child) => {
152
- sessionInfo.pid = child.pid;
153
- fs.writeFileSync(sessionFile, JSON.stringify(sessionInfo, null, 2));
154
- updateSessionDetails(sessionId, { pid: child.pid });
155
- };
156
-
157
- const output = await launchTool(tool, systemPrompt, promptFile, logMessage, {
158
- ...options,
159
- onSpawn: handleSpawn
160
- });
161
-
162
- // 시그널 핸들러 제거
163
- process.removeListener('SIGINT', handleSignal);
164
- process.removeListener('SIGTERM', handleSignal);
165
-
166
- // 정상 종료 처리
167
- if (output) {
168
- sessionInfo.output = output;
169
- }
170
- cleanupSession('completed');
171
-
172
- // 캡처된 출력 반환
173
- return { ...sessionInfo, output };
174
-
175
- } catch (error) {
176
- // 시그널 핸들러 제거
177
- process.removeListener('SIGINT', handleSignal);
178
- process.removeListener('SIGTERM', handleSignal);
179
-
180
- // 에러가 사용자 종료(exit code 비정상)인 경우 completed로 처리
181
- const isUserTermination = error.message && (
182
- error.message.includes('exited with code 130') || // SIGINT
183
- error.message.includes('exited with code 143') || // SIGTERM
184
- error.message.includes('exited with code 1') // 일반 종료
185
- );
186
-
187
- if (isUserTermination) {
188
- cleanupSession('completed', '사용자 종료');
189
- return { ...sessionInfo };
190
- }
191
-
192
- // 실제 에러 처리
193
- sessionInfo.error = error.message;
194
- cleanupSession('error', error.message);
195
- logMessage('ERROR', error.message);
196
-
197
- throw error;
198
- }
199
- }
200
-
201
- /**
202
- * [CLI] 실행 명령어 핸들러
203
- * 사용자 입력을 처리하고 executeAgentSession을 호출
204
- */
205
- export async function runCommand(role, tool) {
206
- if (!isWorkspaceSetup()) {
207
- console.log(chalk.red('❌ 먼저 setup을 실행하세요.'));
208
- console.log(chalk.gray(' ada setup'));
209
- process.exit(1);
210
- }
211
-
212
- try {
213
- // 1. 역할 선택 (입력 없으면 질문)
214
- if (!role) {
215
- const roles = getAvailableRoles();
216
- const answer = await inquirer.prompt([
217
- {
218
- type: 'list',
219
- name: 'role',
220
- message: '실행할 역할을 선택하세요:',
221
- choices: roles
222
- }
223
- ]);
224
- role = answer.role;
225
- }
226
-
227
- // 2. 도구 자동 선택 (입력 없으면 설정 파일 참조)
228
- if (!tool) {
229
- tool = getToolForRole(role);
230
- console.log(chalk.gray(`ℹ️ 설정된 기본 도구를 사용합니다: ${tool}`));
231
- }
232
-
233
- // 3. 세션 실행
234
- await executeAgentSession(role, tool);
235
-
236
- } catch (error) {
237
- console.error(chalk.red('\n❌ 실행 중 오류가 발생했습니다:'));
238
- console.error(chalk.white(error.message));
239
- process.exit(1);
240
- }
241
- }
242
-
243
- // 기존 CLI 호환성을 위해 run이라는 이름으로 export
244
- export { runCommand as run };
245
- // 시스템 프롬프트 생성 로직도 외부에서 쓸 수 있게 export
246
- export { buildSystemPrompt };
247
-
248
-
249
- // ============================================================================
250
- // 내부 헬퍼 함수들
251
- // ============================================================================
252
-
253
- function printSessionBanner(role, tool, sessionId, template) {
254
- // 다른 활성 세션 정보
255
- const activeSessions = getActiveSessions().filter(s => s.sessionId !== sessionId);
256
- const pendingQuestions = getPendingQuestions();
257
-
258
- // 터미널 타이틀
259
- const terminalTitle = `ADA: ${role} (${tool})`;
260
- process.stdout.write(`\x1b]0;${terminalTitle}\x07`);
261
-
262
- console.log('');
263
- console.log(chalk.cyan('━'.repeat(60)));
264
- console.log(chalk.cyan.bold('🚀 AI 에이전트 실행'));
265
- console.log(chalk.cyan('━'.repeat(60)));
266
- console.log('');
267
-
268
- const roleEmojis = {
269
- 'analyzer': '🔍',
270
- 'planner': '📋',
271
- 'improver': '🔧',
272
- 'architect': '🏛️',
273
- 'developer': '💻',
274
- 'backend': '⚙️',
275
- 'frontend': '🎨',
276
- 'reviewer': '👀',
277
- 'manager': '👔',
278
- 'library-developer': '📚',
279
- 'game-logic': '🎮',
280
- 'rendering': '🎬',
281
- 'cli-developer': '⌨️'
282
- };
283
-
284
- const roleEmoji = roleEmojis[role] || '🤖';
285
- console.log(chalk.bgCyan.black.bold(` ${roleEmoji} 역할: ${role.toUpperCase()} `));
286
- console.log('');
287
-
288
- console.log(chalk.white(` 세션 ID: ${chalk.yellow(sessionId)}`));
289
- console.log(chalk.white(` 템플릿: ${chalk.green(template)}`));
290
- console.log(chalk.white(` 도구: ${chalk.green(tool)}`));
291
- console.log(chalk.white(` 작업공간: ${chalk.gray('ai-dev-team/')}`));
292
- console.log(chalk.white(` 로그: ${chalk.gray('.sessions/logs/' + sessionId + '.log')}`));
293
- console.log('');
294
-
295
- if (activeSessions.length > 0) {
296
- console.log(chalk.white(` 🔗 활성 세션: ${chalk.yellow(activeSessions.length)}개`));
297
- activeSessions.forEach(s => {
298
- console.log(chalk.gray(` - ${s.role} (${s.tool})`));
299
- });
300
- console.log('');
301
- }
302
-
303
- if (pendingQuestions.length > 0) {
304
- console.log(chalk.yellow(` ⚠️ 대기 질문: ${pendingQuestions.length}개`));
305
- console.log('');
306
- }
307
-
308
- console.log(chalk.cyan('━'.repeat(60)));
309
- console.log('');
310
- }
311
-
312
- function buildSystemPrompt(workspace, role, roleContent) {
313
- const artifactsDir = path.join(workspace, 'artifacts');
314
- const rulesDir = path.join(workspace, 'rules');
315
-
316
- let prompt = `# Role: ${role}\n\n`;
317
- prompt += roleContent;
318
- prompt += '\n\n---\n\n';
319
-
320
- // 1. 규칙 문서 - 역할별 필수 규칙만 포함
321
- const roleRules = {
322
- planner: ['iteration.md', 'escalation.md', 'document-priority.md'],
323
- improver: ['iteration.md', 'escalation.md', 'document-priority.md', 'rfc.md'],
324
- developer: ['iteration.md', 'escalation.md', 'rollback.md', 'document-priority.md', 'rfc.md'],
325
- reviewer: ['iteration.md', 'rollback.md', 'escalation.md', 'document-priority.md'],
326
- documenter: ['escalation.md', 'document-priority.md'],
327
- analyzer: ['escalation.md', 'document-priority.md'],
328
- manager: ['escalation.md', 'document-priority.md', 'rfc.md'] // Manager는 모든 규칙 참고
329
- };
330
-
331
- const requiredRules = roleRules[role] || [];
332
-
333
- prompt += '# 규칙 (Rules)\n\n';
334
- prompt += `이 역할에 적용되는 필수 규칙: ${requiredRules.join(', ')}\n\n`;
335
-
336
- if (fs.existsSync(rulesDir) && requiredRules.length > 0) {
337
- requiredRules.forEach(ruleFile => {
338
- const rulePath = path.join(rulesDir, ruleFile);
339
- if (fs.existsSync(rulePath)) {
340
- try {
341
- const content = fs.readFileSync(rulePath, 'utf-8');
342
- prompt += `## ${ruleFile}\n\n`;
343
- prompt += content;
344
- prompt += '\n\n---\n\n';
345
- } catch (err) {
346
- prompt += `## ${ruleFile} (읽기 실패)\n\n`;
347
- }
348
- } else {
349
- prompt += `## ${ruleFile} (파일 없음)\n\n`;
350
- }
351
- });
352
- } else if (requiredRules.length === 0) {
353
- prompt += '(이 역할에 필수 규칙이 지정되지 않았습니다)\n\n';
354
- }
355
-
356
- // 2. 핵심 산출물 전체 포함 (우선순위 높은 문서)
357
- prompt += '# 핵심 산출물 (Core Artifacts)\n\n';
358
-
359
- const priorityArtifacts = [
360
- 'decision.md', // 최우선 문서
361
- 'project.md', // 기술 기준 (Frozen)
362
- 'plan.md' // 요구사항
363
- ];
364
-
365
- priorityArtifacts.forEach(artifactFile => {
366
- const artifactPath = path.join(artifactsDir, artifactFile);
367
- if (fs.existsSync(artifactPath)) {
368
- try {
369
- const content = fs.readFileSync(artifactPath, 'utf-8');
370
- prompt += `## ${artifactFile}\n\n`;
371
- prompt += content;
372
- prompt += '\n\n---\n\n';
373
- } catch (err) {
374
- prompt += `## ${artifactFile} (읽기 실패)\n\n`;
375
- }
376
- } else {
377
- prompt += `## ${artifactFile} (아직 작성되지 않음)\n\n`;
378
- }
379
- });
380
-
381
- // 2.1 현재 활성 스프린트 포함
382
- prompt += '# 현재 스프린트 정보\n\n';
383
-
384
- const sprintsDir = path.join(artifactsDir, 'sprints');
385
- if (fs.existsSync(sprintsDir)) {
386
- const sprints = fs.readdirSync(sprintsDir, { withFileTypes: true })
387
- .filter(dirent => dirent.isDirectory() && !dirent.name.startsWith('_'))
388
- .map(dirent => dirent.name);
389
-
390
- // 가장 최근 스프린트 찾기 (sprint-N 형식)
391
- const activeSprint = sprints
392
- .filter(name => /^sprint-\d+$/.test(name))
393
- .sort((a, b) => {
394
- const numA = parseInt(a.split('-')[1]);
395
- const numB = parseInt(b.split('-')[1]);
396
- return numB - numA;
397
- })[0];
398
-
399
- if (activeSprint) {
400
- const sprintMetaPath = path.join(sprintsDir, activeSprint, 'meta.md');
401
- if (fs.existsSync(sprintMetaPath)) {
402
- try {
403
- const content = fs.readFileSync(sprintMetaPath, 'utf-8');
404
- prompt += `## 현재 스프린트: ${activeSprint}/meta.md\n\n`;
405
- prompt += content;
406
- prompt += '\n\n---\n\n';
407
- } catch (err) {
408
- prompt += `## ${activeSprint}/meta.md (읽기 실패)\n\n`;
409
- }
410
- }
411
-
412
- // 스프린트의 Task 파일 전체 포함
413
- const sprintTasksDir = path.join(sprintsDir, activeSprint, 'tasks');
414
- if (fs.existsSync(sprintTasksDir)) {
415
- const taskFiles = fs.readdirSync(sprintTasksDir)
416
- .filter(f => f.endsWith('.md') && !f.includes('template'));
417
-
418
- if (taskFiles.length > 0) {
419
- prompt += `## 현재 스프린트 Task 파일들\n\n`;
420
-
421
- // 각 Task 파일 내용 포함
422
- taskFiles.forEach(f => {
423
- const taskPath = path.join(sprintTasksDir, f);
424
- try {
425
- const taskContent = fs.readFileSync(taskPath, 'utf-8');
426
- prompt += `### ${f}\n\n`;
427
- prompt += taskContent;
428
- prompt += '\n\n---\n\n';
429
- } catch (err) {
430
- prompt += `### ${f} (읽기 실패)\n\n`;
431
- }
432
- });
433
- } else {
434
- // Task 파일이 없는 경우
435
- prompt += `## ⚠️ 스프린트에 Task 없음\n\n`;
436
- prompt += `현재 스프린트(${activeSprint})에 할당된 Task가 없습니다.\n\n`;
437
- prompt += '**다음 단계:**\n';
438
- prompt += '1. `ada sprint add task-001 task-002` 명령으로 Task 할당\n';
439
- prompt += '2. Developer 세션 재시작\n\n';
440
- prompt += '---\n\n';
441
- }
442
- } else {
443
- // tasks 디렉토리가 없는 경우
444
- prompt += `## ⚠️ tasks 디렉토리 없음\n\n`;
445
- prompt += `현재 스프린트(${activeSprint})에 tasks 디렉토리가 없습니다.\n\n`;
446
- prompt += '스프린트 구조가 올바르지 않습니다. `ada sprint create` 명령으로 재생성하세요.\n\n';
447
- prompt += '---\n\n';
448
- }
449
- } else {
450
- // 스프린트가 없는 경우
451
- prompt += '## ⚠️ 현재 활성 스프린트 없음\n\n';
452
- prompt += '스프린트가 아직 생성되지 않았습니다.\n\n';
453
- prompt += '**다음 단계:**\n';
454
- prompt += '1. Planner가 plan.md와 backlog/ Task를 작성했는지 확인\n';
455
- prompt += '2. `ada sprint create` 명령으로 스프린트 생성\n';
456
- prompt += '3. `ada sprint add task-001 task-002` 명령으로 Task 할당\n';
457
- prompt += '4. Developer 세션 재시작\n\n';
458
- prompt += '**참고:** Developer는 스프린트가 있어야 작업할 수 있습니다.\n';
459
- prompt += '스프린트 없이는 어떤 Task를 해야 할지 알 수 없습니다.\n\n';
460
- prompt += '---\n\n';
461
- }
462
- } else {
463
- // sprints 디렉토리 자체가 없는 경우
464
- prompt += '## ⚠️ sprints 디렉토리 없음\n\n';
465
- prompt += 'sprints 디렉토리가 존재하지 않습니다.\n\n';
466
- prompt += '**다음 단계:**\n';
467
- prompt += '1. `ada sprint create` 명령으로 첫 스프린트 생성\n';
468
- prompt += '2. Task를 스프린트에 추가\n';
469
- prompt += '3. Developer 세션 재시작\n\n';
470
- prompt += '---\n\n';
471
- }
472
-
473
- // 2.2 Backlog Task 목록
474
- const backlogDir = path.join(artifactsDir, 'backlog');
475
- if (fs.existsSync(backlogDir)) {
476
- const backlogFiles = fs.readdirSync(backlogDir)
477
- .filter(f => f.endsWith('.md') && f.startsWith('task-'));
478
-
479
- if (backlogFiles.length > 0) {
480
- prompt += `## Backlog Task 목록\n\n`;
481
- prompt += `다음 Task 파일들을 필요 시 읽어서 확인하세요:\n`;
482
- backlogFiles.forEach(f => {
483
- prompt += `- backlog/${f}\n`;
484
- });
485
- prompt += '\n---\n\n';
486
- }
487
- }
488
-
489
- // 3. 인터페이스 문서 전체 포함 (api.md, ui.md 등)
490
- prompt += '# 인터페이스 산출물 (Interface Artifacts)\n\n';
491
-
492
- const interfaceArtifacts = ['api.md', 'ui.md', 'public-api.md', 'commands.md', 'output-format.md',
493
- 'game-systems.md', 'assets.md', 'hud.md', 'examples.md', 'changelog.md'];
494
-
495
- let hasInterfaceDoc = false;
496
- interfaceArtifacts.forEach(artifactFile => {
497
- const artifactPath = path.join(artifactsDir, artifactFile);
498
- if (fs.existsSync(artifactPath)) {
499
- hasInterfaceDoc = true;
500
- try {
501
- const content = fs.readFileSync(artifactPath, 'utf-8');
502
- prompt += `## ${artifactFile}\n\n`;
503
- prompt += content;
504
- prompt += '\n\n---\n\n';
505
- } catch (err) {
506
- prompt += `## ${artifactFile} (읽기 실패)\n\n`;
507
- }
508
- }
509
- });
510
-
511
- if (!hasInterfaceDoc) {
512
- prompt += '(인터페이스 문서 없음)\n\n';
513
- }
514
-
515
- // 4. 나머지 산출물은 목록만 (필요 시 AI가 파일 읽기 도구 사용)
516
- prompt += '# 기타 산출물 (목록)\n\n';
517
-
518
- if (fs.existsSync(artifactsDir)) {
519
- const allArtifacts = fs.readdirSync(artifactsDir, { withFileTypes: true });
520
- const otherFiles = allArtifacts
521
- .filter(dirent => dirent.isFile() && dirent.name.endsWith('.md'))
522
- .map(dirent => dirent.name)
523
- .filter(name => !priorityArtifacts.includes(name) && !interfaceArtifacts.includes(name));
524
-
525
- if (otherFiles.length > 0) {
526
- prompt += '다음 산출물들은 필요 시 파일을 읽어서 확인하세요:\n';
527
- otherFiles.forEach(f => {
528
- prompt += `- artifacts/${f}\n`;
529
- });
530
- prompt += '\n';
531
- }
532
-
533
- // features 디렉토리 확인
534
- const featuresDir = path.join(artifactsDir, 'features');
535
- if (fs.existsSync(featuresDir)) {
536
- const features = fs.readdirSync(featuresDir, { withFileTypes: true })
537
- .filter(dirent => dirent.isDirectory() && !dirent.name.startsWith('_'))
538
- .map(dirent => dirent.name);
539
-
540
- if (features.length > 0) {
541
- prompt += '\n**Features 디렉토리:**\n';
542
- features.forEach(feature => {
543
- prompt += `- features/${feature}/\n`;
544
- });
545
- prompt += '\n필요한 Feature 문서는 파일을 직접 읽어서 확인하세요.\n';
546
- }
547
- }
548
-
549
- // RFC 디렉토리 확인
550
- const rfcDir = path.join(artifactsDir, 'rfc');
551
- if (fs.existsSync(rfcDir)) {
552
- const rfcs = fs.readdirSync(rfcDir)
553
- .filter(f => f.endsWith('.md') && !f.includes('template'));
554
-
555
- if (rfcs.length > 0) {
556
- prompt += '\n**RFC 문서:**\n';
557
- rfcs.forEach(rfc => {
558
- prompt += `- rfc/${rfc}\n`;
559
- });
560
- }
561
- }
562
- }
563
-
564
- prompt += '\n---\n\n';
565
- prompt += '# 작업 지침\n\n';
566
- prompt += '- **문서 기준 판단**: 위에 포함된 문서 내용을 기준으로 판단하세요.\n';
567
- prompt += '- **추측 금지**: 문서에 없는 내용은 추측하지 말고 사용자에게 에스컬레이션하세요.\n';
568
- prompt += '- **규칙 준수**: 모든 규칙(Rules)을 반드시 따라야 합니다.\n';
569
- prompt += '- **우선순위**: 문서 간 충돌 시 document-priority.md의 우선순위를 따르세요.\n';
570
- prompt += '- **현재 범위**: 현재 스프린트 meta.md에 정의된 Task만 작업하세요.\n';
571
- prompt += '- **파일 읽기**: 필요한 경우 목록에 표시된 산출물을 파일 읽기 도구로 확인하세요.\n';
572
- prompt += '\n';
573
- prompt += '## 멀티 세션 상태 관리\n\n';
574
- prompt += '여러 터미널에서 동시에 다른 역할이 작업할 수 있습니다.\n';
575
- prompt += '상태 공유를 위해 `ai-dev-team/.ada-status.json` 파일을 사용하세요.\n\n';
576
- prompt += '**주요 작업:**\n';
577
- prompt += '1. **Task 진행 상황 업데이트**: 작업 시작/완료 시 taskProgress 업데이트\n';
578
- prompt += '2. **질문 등록**: 사용자에게 질문이 필요하면 pendingQuestions에 추가\n';
579
- prompt += '3. **알림 전송**: 다른 역할에게 알릴 사항이 있으면 notifications 추가\n';
580
- prompt += '4. **상태 파일**: .ada-status.json을 통해 세션 간 상태 공유\n';
581
-
582
- return prompt;
583
- }
584
-
585
- async function launchTool(tool, systemPrompt, promptFile, logMessage, options = {}) {
586
- // 프롬프트 파일의 상대 경로 (작업 디렉토리 기준)
587
- const relativePromptPath = path.relative(process.cwd(), promptFile);
588
-
589
- // 도구별 설정
590
- const commands = {
591
- claude: {
592
- cmd: 'claude',
593
- args: ['--system-prompt-file', promptFile],
594
- automation: 'perfect'
595
- },
596
- gemini: {
597
- cmd: 'gemini',
598
- args: [],
599
- env: {
600
- GEMINI_SYSTEM_MD: promptFile // 시스템 프롬프트 파일 경로
601
- },
602
- automation: 'perfect'
603
- },
604
- codex: {
605
- cmd: 'codex',
606
- args: [],
607
- automation: 'manual',
608
- instruction: `@${relativePromptPath}`
609
- },
610
- copilot: {
611
- cmd: 'gh',
612
- args: ['copilot'],
613
- automation: 'manual',
614
- instruction: `@${relativePromptPath}`
615
- }
616
- };
617
-
618
- const config = commands[tool];
619
- let { cmd, args } = config;
620
- let usePromptStdin = false;
621
- let promptInput = null;
622
-
623
- if (options.captureOutput) {
624
- // captureOutput 모드일 때 Claude는 --print 옵션 필요
625
- if (tool === 'claude') {
626
- args = ['--print', '-p', '위 시스템 프롬프트의 지시에 따라 JSON으로 응답하세요.', '--system-prompt-file', promptFile];
627
- }
628
-
629
- // codex exec는 프롬프트를 stdin으로 받는다
630
- if (tool === 'codex') {
631
- args = ['exec', '-'];
632
- usePromptStdin = true;
633
- promptInput = systemPrompt;
634
- }
635
-
636
- // gemini는 Stdin으로 프롬프트 전달 (에러 해결: No input provided via stdin)
637
- if (tool === 'gemini') {
638
- const userPrompt = '위 시스템 프롬프트의 지시에 따라 JSON으로 응답하세요.';
639
- args = ['-o', 'text'];
640
- usePromptStdin = true;
641
- promptInput = userPrompt;
642
- }
643
- }
644
-
645
- // 도구 존재 확인 (Windows: where, Unix: which)
646
- const whichCmd = process.platform === 'win32' ? 'where' : 'which';
647
- const which = spawn(whichCmd, [cmd], { shell: true });
648
-
649
- return new Promise((resolve, reject) => {
650
- which.on('close', (code) => {
651
- if (code !== 0) {
652
- console.log(chalk.yellow(`⚠️ ${tool} CLI가 설치되어 있지 않습니다.`));
653
- logMessage('WARN', `${tool} CLI not found, prompt displayed`);
654
- resolve(null);
655
- return;
656
- }
657
-
658
- if (!options.captureOutput) {
659
- console.log('');
660
- if (config.automation === 'perfect') {
661
- console.log(chalk.green('━'.repeat(60)));
662
- console.log(chalk.green.bold('✓ 역할이 자동으로 설정됩니다'));
663
- console.log(chalk.green('━'.repeat(60)));
664
- console.log('');
665
- console.log(chalk.gray(`시스템 프롬프트: ${relativePromptPath}`));
666
- console.log('');
667
- } else if (config.automation === 'manual') {
668
- console.log(chalk.yellow('━'.repeat(60)));
669
- console.log(chalk.yellow.bold('⚠️ 수동 설정이 필요합니다'));
670
- console.log(chalk.yellow('━'.repeat(60)));
671
- console.log('');
672
- console.log('CLI가 실행되면 아래 명령어를 복사해서 입력하세요:');
673
- console.log('');
674
- console.log(chalk.bgWhite.black.bold(` ${config.instruction} `));
675
- console.log('');
676
- }
677
- console.log(chalk.green(`✓ ${tool} 실행 중...`));
678
- console.log('');
679
- }
680
-
681
- logMessage('INFO', `${tool} CLI 실행 (automation: ${config.automation}, captureOutput: ${!!options.captureOutput})`);
682
-
683
- // 환경 변수 병합
684
- const envVars = {
685
- ...process.env,
686
- ADA_SYSTEM_PROMPT: systemPrompt,
687
- ...(config.env || {})
688
- };
689
-
690
- // 캡처 모드에 따라 stdio 설정 변경
691
- const stdioConfig = options.captureOutput
692
- ? (usePromptStdin ? ['pipe', 'pipe', 'pipe'] : ['ignore', 'pipe', 'pipe'])
693
- : 'inherit';
694
-
695
- const child = spawn(cmd, args, {
696
- stdio: stdioConfig,
697
- shell: true,
698
- env: envVars
699
- });
700
-
701
- if (typeof options.onSpawn === 'function') {
702
- options.onSpawn(child);
703
- }
704
-
705
- let capturedOutput = '';
706
- let capturedError = '';
707
-
708
- if (options.captureOutput) {
709
- child.stdout.on('data', (data) => {
710
- capturedOutput += data.toString();
711
- });
712
- child.stderr.on('data', (data) => {
713
- capturedError += data.toString();
714
- });
715
- }
716
-
717
- if (options.captureOutput && usePromptStdin && child.stdin) {
718
- const input = promptInput || systemPrompt;
719
- child.stdin.write(input);
720
- child.stdin.end();
721
- }
722
-
723
- child.on('close', (code) => {
724
- if (code === 0) {
725
- resolve(options.captureOutput ? capturedOutput : null);
726
- } else {
727
- const errorMsg = options.captureOutput
728
- ? `${tool} exited with code ${code}. Stderr: ${capturedError}`
729
- : `${tool} exited with code ${code}`;
730
- reject(new Error(errorMsg));
731
- }
732
- });
733
-
734
- child.on('error', (err) => {
735
- reject(err);
736
- });
737
- });
738
- });
739
- }