@silbaram/artifact-driven-agent 0.1.0

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 (68) hide show
  1. package/README.md +456 -0
  2. package/ai-dev-team/.gitkeep +0 -0
  3. package/ai-dev-team/README.md +44 -0
  4. package/ai-dev-team/artifacts/.gitkeep +0 -0
  5. package/ai-dev-team/artifacts/features/_template/api.md +19 -0
  6. package/ai-dev-team/artifacts/features/_template/qa.md +16 -0
  7. package/ai-dev-team/artifacts/features/_template/review.md +14 -0
  8. package/ai-dev-team/artifacts/features/_template/spec.md +28 -0
  9. package/ai-dev-team/artifacts/features/_template/ui.md +14 -0
  10. package/ai-dev-team/artifacts/rfc/RFC-0000-template.md +49 -0
  11. package/ai-dev-team/roles/.gitkeep +0 -0
  12. package/ai-dev-team/rules/.gitkeep +0 -0
  13. package/bin/cli.js +75 -0
  14. package/core/artifacts/architecture-options.md +85 -0
  15. package/core/artifacts/backlog.md +177 -0
  16. package/core/artifacts/current-sprint.md +125 -0
  17. package/core/artifacts/decision.md +72 -0
  18. package/core/artifacts/plan.md +187 -0
  19. package/core/artifacts/project.md +191 -0
  20. package/core/artifacts/qa-report.md +104 -0
  21. package/core/artifacts/review-report.md +103 -0
  22. package/core/roles/architect.md +236 -0
  23. package/core/roles/developer.md +203 -0
  24. package/core/roles/manager.md +300 -0
  25. package/core/roles/planner.md +231 -0
  26. package/core/roles/qa.md +262 -0
  27. package/core/roles/reviewer.md +280 -0
  28. package/core/rules/document-priority.md +196 -0
  29. package/core/rules/escalation.md +171 -0
  30. package/core/rules/iteration.md +236 -0
  31. package/core/rules/rfc.md +31 -0
  32. package/core/rules/rollback.md +218 -0
  33. package/docs/feature-structure.md +36 -0
  34. package/examples/todo-app/README.md +23 -0
  35. package/examples/todo-app/artifacts/backlog.md +23 -0
  36. package/examples/todo-app/artifacts/plan.md +23 -0
  37. package/examples/todo-app/artifacts/project.md +23 -0
  38. package/package.json +49 -0
  39. package/src/commands/interactive.js +101 -0
  40. package/src/commands/logs.js +81 -0
  41. package/src/commands/reset.js +66 -0
  42. package/src/commands/run.js +202 -0
  43. package/src/commands/sessions.js +70 -0
  44. package/src/commands/setup.js +128 -0
  45. package/src/commands/status.js +76 -0
  46. package/src/commands/validate.js +219 -0
  47. package/src/index.js +12 -0
  48. package/src/utils/files.js +134 -0
  49. package/templates/cli/artifacts/commands.md +262 -0
  50. package/templates/cli/artifacts/output-format.md +298 -0
  51. package/templates/cli/roles/cli-developer.md +239 -0
  52. package/templates/cli/rules/command-change.md +225 -0
  53. package/templates/game/artifacts/assets.md +148 -0
  54. package/templates/game/artifacts/game-systems.md +217 -0
  55. package/templates/game/artifacts/hud.md +199 -0
  56. package/templates/game/roles/game-logic.md +193 -0
  57. package/templates/game/roles/rendering.md +137 -0
  58. package/templates/game/rules/system-change.md +184 -0
  59. package/templates/library/artifacts/changelog.md +84 -0
  60. package/templates/library/artifacts/examples.md +157 -0
  61. package/templates/library/artifacts/public-api.md +197 -0
  62. package/templates/library/roles/library-developer.md +180 -0
  63. package/templates/library/rules/versioning.md +186 -0
  64. package/templates/web-dev/artifacts/api.md +212 -0
  65. package/templates/web-dev/artifacts/ui.md +104 -0
  66. package/templates/web-dev/roles/backend.md +134 -0
  67. package/templates/web-dev/roles/frontend.md +161 -0
  68. package/templates/web-dev/rules/api-change.md +198 -0
@@ -0,0 +1,23 @@
1
+ # Backlog
2
+
3
+ > DONE: 5개
4
+
5
+ ## ~~TASK-001: 데이터 모델~~ ✅
6
+ - 상태: DONE
7
+ - 크기: S
8
+
9
+ ## ~~TASK-002: 추가 기능~~ ✅
10
+ - 상태: DONE
11
+ - 크기: M
12
+
13
+ ## ~~TASK-003: 삭제 기능~~ ✅
14
+ - 상태: DONE
15
+ - 크기: S
16
+
17
+ ## ~~TASK-004: 완료 토글~~ ✅
18
+ - 상태: DONE
19
+ - 크기: S
20
+
21
+ ## ~~TASK-005: 필터~~ ✅
22
+ - 상태: DONE
23
+ - 크기: M
@@ -0,0 +1,23 @@
1
+ # Todo App 기획서
2
+
3
+ > 상태: Confirmed | 버전: v1.0
4
+
5
+ ## ✅ 체크리스트
6
+
7
+ - [x] 서비스 개요 작성
8
+ - [x] 기능 목록 정의
9
+ - [x] 비기능 요구사항 정의
10
+
11
+ ## 서비스 개요
12
+
13
+ - **무엇**: 웹 기반 Todo 리스트
14
+ - **누가**: 개인 사용자
15
+ - **왜**: 간단한 할 일 관리
16
+
17
+ ## 기능 목록
18
+
19
+ | ID | 기능명 | 우선순위 |
20
+ |----|--------|:--------:|
21
+ | F001 | Task CRUD | P0 |
22
+ | F002 | 완료 표시 | P0 |
23
+ | F003 | 필터링 | P1 |
@@ -0,0 +1,23 @@
1
+ # Project Standards (Frozen)
2
+
3
+ > 🔒 Frozen 상태
4
+
5
+ ## ✅ 체크리스트
6
+
7
+ - [x] 기술 스택 결정
8
+ - [x] 프로젝트 구조 정의
9
+
10
+ ## 프로젝트 규모
11
+
12
+ | 항목 | 값 |
13
+ |------|-----|
14
+ | 규모 | S |
15
+ | 복잡도 | 낮음 |
16
+
17
+ ## 기술 스택
18
+
19
+ | 영역 | 선택 | 버전 |
20
+ |------|------|------|
21
+ | 언어 | JavaScript | ES2022 |
22
+ | 번들러 | Vite | 5.0.10 |
23
+ | 테스트 | Vitest | 1.1.0 |
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@silbaram/artifact-driven-agent",
3
+ "version": "0.1.0",
4
+ "description": "CLI 기반 멀티 AI 에이전트 개발 프레임워크 - AI가 규칙을 어기지 못하게 하는 문서 기반 개발",
5
+ "type": "module",
6
+ "bin": {
7
+ "ada": "./bin/cli.js"
8
+ },
9
+ "files": [
10
+ "bin",
11
+ "src",
12
+ "core",
13
+ "templates",
14
+ "docs",
15
+ "examples",
16
+ "ai-dev-team"
17
+ ],
18
+ "scripts": {
19
+ "test": "node --test src/**/*.test.js",
20
+ "lint": "echo 'No linter configured'"
21
+ },
22
+ "keywords": [
23
+ "ai",
24
+ "agent",
25
+ "cli",
26
+ "development",
27
+ "framework",
28
+ "artifact",
29
+ "multi-agent",
30
+ "claude",
31
+ "codex",
32
+ "automation"
33
+ ],
34
+ "author": "silbaram <silbaram79@gmail.com>",
35
+ "license": "MIT",
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "https://github.com/silbaram/artifact-driven-agent"
39
+ },
40
+ "dependencies": {
41
+ "chalk": "^5.3.0",
42
+ "commander": "^12.1.0",
43
+ "fs-extra": "^11.2.0",
44
+ "inquirer": "^9.2.15"
45
+ },
46
+ "engines": {
47
+ "node": ">=18.0.0"
48
+ }
49
+ }
@@ -0,0 +1,101 @@
1
+ import chalk from 'chalk';
2
+ import inquirer from 'inquirer';
3
+ import { setup } from './setup.js';
4
+ import { run } from './run.js';
5
+ import {
6
+ isWorkspaceSetup,
7
+ getAvailableRoles,
8
+ getAvailableTools,
9
+ getAvailableTemplates
10
+ } from '../utils/files.js';
11
+
12
+ export async function interactive() {
13
+ console.log('');
14
+ console.log(chalk.cyan('━'.repeat(50)));
15
+ console.log(chalk.cyan.bold('🤖 Artifact-Driven AI Agent'));
16
+ console.log(chalk.cyan('━'.repeat(50)));
17
+ console.log('');
18
+
19
+ // 세팅되지 않은 경우
20
+ if (!isWorkspaceSetup()) {
21
+ console.log(chalk.yellow('⚠️ 프로젝트가 세팅되지 않았습니다.'));
22
+ console.log('');
23
+
24
+ const answer = await inquirer.prompt([
25
+ {
26
+ type: 'confirm',
27
+ name: 'doSetup',
28
+ message: '지금 세팅하시겠습니까?',
29
+ default: true
30
+ }
31
+ ]);
32
+
33
+ if (answer.doSetup) {
34
+ await setup();
35
+ } else {
36
+ console.log(chalk.gray('나중에 `ada setup`으로 세팅할 수 있습니다.'));
37
+ return;
38
+ }
39
+ }
40
+
41
+ // 역할 선택
42
+ const roles = getAvailableRoles();
43
+ const tools = getAvailableTools();
44
+
45
+ if (roles.length === 0) {
46
+ console.log(chalk.red('❌ 사용 가능한 역할이 없습니다.'));
47
+ console.log(chalk.gray('`ada setup`으로 다시 세팅하세요.'));
48
+ return;
49
+ }
50
+
51
+ const answers = await inquirer.prompt([
52
+ {
53
+ type: 'list',
54
+ name: 'role',
55
+ message: '역할을 선택하세요:',
56
+ choices: roles.map(r => ({
57
+ name: getRoleDescription(r),
58
+ value: r
59
+ }))
60
+ },
61
+ {
62
+ type: 'list',
63
+ name: 'tool',
64
+ message: 'AI 도구를 선택하세요:',
65
+ choices: tools.map(t => ({
66
+ name: getToolDescription(t),
67
+ value: t
68
+ }))
69
+ }
70
+ ]);
71
+
72
+ await run(answers.role, answers.tool);
73
+ }
74
+
75
+ function getRoleDescription(role) {
76
+ const descriptions = {
77
+ planner: 'planner - 요구사항 수집, Task 분해',
78
+ architect: 'architect - 규모 예측, 기술 스택 결정',
79
+ developer: 'developer - 코드 구현 (범용)',
80
+ reviewer: 'reviewer - 코드 리뷰',
81
+ qa: 'qa - 수용 조건 검증',
82
+ manager: 'manager - 스프린트 관리, 승인',
83
+ backend: 'backend - API 설계, 서버 구현',
84
+ frontend: 'frontend - UI 구현, API 연동',
85
+ 'library-developer': 'library-dev - 공개 API 설계, 버전 관리',
86
+ 'game-logic': 'game-logic - 게임 시스템 설계',
87
+ rendering: 'rendering - 화면/이펙트 구현',
88
+ 'cli-developer': 'cli-dev - 명령어 설계, 출력 형식'
89
+ };
90
+ return descriptions[role] || role;
91
+ }
92
+
93
+ function getToolDescription(tool) {
94
+ const descriptions = {
95
+ claude: 'Claude - Anthropic Claude CLI',
96
+ codex: 'Codex - OpenAI Codex CLI',
97
+ gemini: 'Gemini - Google Gemini CLI',
98
+ copilot: 'Copilot - GitHub Copilot CLI'
99
+ };
100
+ return descriptions[tool] || tool;
101
+ }
@@ -0,0 +1,81 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import chalk from 'chalk';
4
+ import { getLogsDir, getSessionsDir, isWorkspaceSetup } from '../utils/files.js';
5
+
6
+ export async function logs(sessionId) {
7
+ if (!isWorkspaceSetup()) {
8
+ console.log(chalk.red('❌ 먼저 setup을 실행하세요.'));
9
+ process.exit(1);
10
+ }
11
+
12
+ const logsDir = getLogsDir();
13
+ const sessionsDir = getSessionsDir();
14
+
15
+ // 세션 ID가 없으면 가장 최근 세션 찾기
16
+ if (!sessionId) {
17
+ if (fs.existsSync(sessionsDir)) {
18
+ const sessions = fs.readdirSync(sessionsDir)
19
+ .filter(f => fs.statSync(path.join(sessionsDir, f)).isDirectory())
20
+ .sort()
21
+ .reverse();
22
+
23
+ if (sessions.length > 0) {
24
+ sessionId = sessions[0];
25
+ }
26
+ }
27
+ }
28
+
29
+ if (!sessionId) {
30
+ console.log(chalk.yellow('⚠️ 세션 기록이 없습니다.'));
31
+ return;
32
+ }
33
+
34
+ const logFile = path.join(logsDir, `${sessionId}.log`);
35
+
36
+ console.log('');
37
+ console.log(chalk.cyan('━'.repeat(60)));
38
+ console.log(chalk.cyan.bold(`📄 로그: ${sessionId}`));
39
+ console.log(chalk.cyan('━'.repeat(60)));
40
+ console.log('');
41
+
42
+ if (!fs.existsSync(logFile)) {
43
+ console.log(chalk.gray(' 로그 파일 없음'));
44
+ console.log('');
45
+
46
+ // 세션 정보라도 표시
47
+ const sessionFile = path.join(sessionsDir, sessionId, 'session.json');
48
+ if (fs.existsSync(sessionFile)) {
49
+ try {
50
+ const session = JSON.parse(fs.readFileSync(sessionFile, 'utf-8'));
51
+ console.log(chalk.white.bold(' 세션 정보:'));
52
+ console.log(chalk.gray(` 역할: ${session.role || '-'}`));
53
+ console.log(chalk.gray(` 도구: ${session.tool || '-'}`));
54
+ console.log(chalk.gray(` 템플릿: ${session.template || '-'}`));
55
+ console.log(chalk.gray(` 시작: ${session.started_at || '-'}`));
56
+ console.log(chalk.gray(` 상태: ${session.status || '-'}`));
57
+ console.log('');
58
+ } catch (e) {
59
+ // ignore
60
+ }
61
+ }
62
+ return;
63
+ }
64
+
65
+ const content = fs.readFileSync(logFile, 'utf-8');
66
+ const lines = content.split('\n');
67
+
68
+ for (const line of lines) {
69
+ if (line.includes('[ERROR]')) {
70
+ console.log(chalk.red(line));
71
+ } else if (line.includes('[WARN]')) {
72
+ console.log(chalk.yellow(line));
73
+ } else if (line.includes('[INFO]')) {
74
+ console.log(chalk.gray(line));
75
+ } else {
76
+ console.log(line);
77
+ }
78
+ }
79
+
80
+ console.log('');
81
+ }
@@ -0,0 +1,66 @@
1
+ import fs from 'fs-extra';
2
+ import chalk from 'chalk';
3
+ import inquirer from 'inquirer';
4
+ import { getWorkspaceDir, isWorkspaceSetup } from '../utils/files.js';
5
+
6
+ export async function reset(options = {}) {
7
+ const workspace = getWorkspaceDir();
8
+
9
+ if (!isWorkspaceSetup()) {
10
+ console.log(chalk.yellow('⚠️ 이미 초기화 상태입니다.'));
11
+ return;
12
+ }
13
+
14
+ // 확인
15
+ if (!options.force) {
16
+ const answer = await inquirer.prompt([
17
+ {
18
+ type: 'confirm',
19
+ name: 'confirm',
20
+ message: chalk.red('ai-dev-team 디렉토리를 초기화하시겠습니까? (모든 작업 내용이 삭제됩니다)'),
21
+ default: false
22
+ }
23
+ ]);
24
+
25
+ if (!answer.confirm) {
26
+ console.log(chalk.gray('취소되었습니다.'));
27
+ return;
28
+ }
29
+ }
30
+
31
+ console.log('');
32
+ console.log(chalk.yellow('🗑️ 초기화 중...'));
33
+
34
+ // roles, artifacts, rules 내용 삭제 (.gitkeep 유지)
35
+ const dirs = ['roles', 'artifacts', 'rules'];
36
+
37
+ for (const dir of dirs) {
38
+ const dirPath = `${workspace}/${dir}`;
39
+ if (fs.existsSync(dirPath)) {
40
+ const files = fs.readdirSync(dirPath);
41
+ for (const file of files) {
42
+ if (file !== '.gitkeep') {
43
+ fs.removeSync(`${dirPath}/${file}`);
44
+ }
45
+ }
46
+ }
47
+ }
48
+
49
+ // .current-template 삭제
50
+ const templateFile = `${workspace}/.current-template`;
51
+ if (fs.existsSync(templateFile)) {
52
+ fs.removeSync(templateFile);
53
+ }
54
+
55
+ // .sessions 삭제
56
+ const sessionsDir = `${workspace}/.sessions`;
57
+ if (fs.existsSync(sessionsDir)) {
58
+ fs.removeSync(sessionsDir);
59
+ }
60
+
61
+ console.log(chalk.green('✅ 초기화 완료'));
62
+ console.log('');
63
+ console.log(chalk.gray('다시 세팅하려면:'));
64
+ console.log(chalk.white(' ada setup'));
65
+ console.log('');
66
+ }
@@ -0,0 +1,202 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import { spawn } from 'child_process';
4
+ import chalk from 'chalk';
5
+ import {
6
+ getWorkspaceDir,
7
+ getSessionsDir,
8
+ getLogsDir,
9
+ getCurrentTemplate,
10
+ getAvailableRoles,
11
+ generateSessionId,
12
+ getTimestamp,
13
+ isWorkspaceSetup
14
+ } from '../utils/files.js';
15
+
16
+ export async function run(role, tool) {
17
+ if (!isWorkspaceSetup()) {
18
+ console.log(chalk.red('❌ 먼저 setup을 실행하세요.'));
19
+ console.log(chalk.gray(' ada setup'));
20
+ process.exit(1);
21
+ }
22
+
23
+ const roles = getAvailableRoles();
24
+ const tools = ['claude', 'codex', 'gemini', 'copilot'];
25
+
26
+ // 역할 검증
27
+ if (!roles.includes(role)) {
28
+ console.log(chalk.red(`❌ 알 수 없는 역할: ${role}`));
29
+ console.log(chalk.gray(`사용 가능: ${roles.join(', ')}`));
30
+ process.exit(1);
31
+ }
32
+
33
+ // 도구 검증
34
+ if (!tools.includes(tool)) {
35
+ console.log(chalk.red(`❌ 알 수 없는 도구: ${tool}`));
36
+ console.log(chalk.gray(`사용 가능: ${tools.join(', ')}`));
37
+ process.exit(1);
38
+ }
39
+
40
+ const workspace = getWorkspaceDir();
41
+ const template = getCurrentTemplate();
42
+ const sessionId = generateSessionId();
43
+ const sessionsDir = getSessionsDir();
44
+ const logsDir = getLogsDir();
45
+
46
+ // 세션 디렉토리 생성
47
+ const sessionDir = path.join(sessionsDir, sessionId);
48
+ fs.ensureDirSync(sessionDir);
49
+ fs.ensureDirSync(logsDir);
50
+
51
+ // 세션 정보 저장
52
+ const sessionInfo = {
53
+ session_id: sessionId,
54
+ role: role,
55
+ tool: tool,
56
+ template: template,
57
+ started_at: getTimestamp(),
58
+ status: 'active'
59
+ };
60
+ fs.writeFileSync(path.join(sessionDir, 'session.json'), JSON.stringify(sessionInfo, null, 2));
61
+
62
+ // 로그 파일 초기화
63
+ const logFile = path.join(logsDir, `${sessionId}.log`);
64
+ const logMessage = (level, msg) => {
65
+ const line = `[${getTimestamp()}] [${level}] ${msg}\n`;
66
+ fs.appendFileSync(logFile, line);
67
+ };
68
+
69
+ logMessage('INFO', `세션 시작: role=${role}, tool=${tool}, template=${template}`);
70
+
71
+ // 역할 파일 경로
72
+ const roleFile = path.join(workspace, 'roles', `${role}.md`);
73
+ const roleContent = fs.readFileSync(roleFile, 'utf-8');
74
+
75
+ // 시스템 프롬프트 생성
76
+ const systemPrompt = buildSystemPrompt(workspace, role, roleContent);
77
+
78
+ console.log('');
79
+ console.log(chalk.cyan('━'.repeat(60)));
80
+ console.log(chalk.cyan.bold('🚀 AI 에이전트 실행'));
81
+ console.log(chalk.cyan('━'.repeat(60)));
82
+ console.log('');
83
+ console.log(chalk.white(` 세션 ID: ${chalk.yellow(sessionId)}`));
84
+ console.log(chalk.white(` 템플릿: ${chalk.green(template)}`));
85
+ console.log(chalk.white(` 역할: ${chalk.green(role)}`));
86
+ console.log(chalk.white(` 도구: ${chalk.green(tool)}`));
87
+ console.log(chalk.white(` 작업공간: ${chalk.gray('ai-dev-team/')}`));
88
+ console.log(chalk.white(` 로그: ${chalk.gray(`logs/${sessionId}.log`)}`));
89
+ console.log('');
90
+ console.log(chalk.cyan('━'.repeat(60)));
91
+ console.log('');
92
+
93
+ // 도구별 실행
94
+ try {
95
+ await launchTool(tool, systemPrompt, logMessage);
96
+
97
+ // 세션 완료 처리
98
+ sessionInfo.status = 'completed';
99
+ sessionInfo.ended_at = getTimestamp();
100
+ fs.writeFileSync(path.join(sessionDir, 'session.json'), JSON.stringify(sessionInfo, null, 2));
101
+ logMessage('INFO', '세션 종료');
102
+ } catch (error) {
103
+ sessionInfo.status = 'error';
104
+ sessionInfo.error = error.message;
105
+ fs.writeFileSync(path.join(sessionDir, 'session.json'), JSON.stringify(sessionInfo, null, 2));
106
+ logMessage('ERROR', error.message);
107
+ throw error;
108
+ }
109
+ }
110
+
111
+ function buildSystemPrompt(workspace, role, roleContent) {
112
+ const artifactsDir = path.join(workspace, 'artifacts');
113
+ const rulesDir = path.join(workspace, 'rules');
114
+
115
+ let prompt = `# Role: ${role}\n\n`;
116
+ prompt += roleContent;
117
+ prompt += '\n\n---\n\n';
118
+ prompt += '# 참조 문서\n\n';
119
+
120
+ // 산출물 목록
121
+ if (fs.existsSync(artifactsDir)) {
122
+ const artifacts = fs.readdirSync(artifactsDir).filter(f => f.endsWith('.md'));
123
+ prompt += '## 산출물 (artifacts/)\n';
124
+ artifacts.forEach(a => {
125
+ prompt += `- ${a}\n`;
126
+ });
127
+ prompt += '\n';
128
+ }
129
+
130
+ // 규칙 목록
131
+ if (fs.existsSync(rulesDir)) {
132
+ const rules = fs.readdirSync(rulesDir).filter(f => f.endsWith('.md'));
133
+ prompt += '## 규칙 (rules/)\n';
134
+ rules.forEach(r => {
135
+ prompt += `- ${r}\n`;
136
+ });
137
+ prompt += '\n';
138
+ }
139
+
140
+ prompt += '---\n\n';
141
+ prompt += '위 역할과 규칙에 따라 작업을 수행하세요.\n';
142
+ prompt += '문서를 기준으로 판단하고, 문서에 없는 내용은 추측하지 마세요.\n';
143
+
144
+ return prompt;
145
+ }
146
+
147
+ async function launchTool(tool, systemPrompt, logMessage) {
148
+ const commands = {
149
+ claude: { cmd: 'claude', args: [] },
150
+ codex: { cmd: 'codex', args: [] },
151
+ gemini: { cmd: 'gemini', args: [] },
152
+ copilot: { cmd: 'gh', args: ['copilot', 'suggest'] }
153
+ };
154
+
155
+ const { cmd, args } = commands[tool];
156
+
157
+ // 도구 존재 확인
158
+ const which = spawn('which', [cmd], { shell: true });
159
+
160
+ return new Promise((resolve, reject) => {
161
+ which.on('close', (code) => {
162
+ if (code !== 0) {
163
+ console.log(chalk.yellow(`⚠️ ${tool} CLI가 설치되어 있지 않습니다.`));
164
+ console.log('');
165
+ console.log(chalk.white('시스템 프롬프트를 출력합니다:'));
166
+ console.log(chalk.gray('─'.repeat(60)));
167
+ console.log(systemPrompt);
168
+ console.log(chalk.gray('─'.repeat(60)));
169
+ console.log('');
170
+ console.log(chalk.gray('위 내용을 복사하여 AI 도구에 붙여넣으세요.'));
171
+ logMessage('WARN', `${tool} CLI not found, prompt displayed`);
172
+ resolve();
173
+ return;
174
+ }
175
+
176
+ // CLI 실행
177
+ console.log(chalk.green(`✓ ${tool} 실행 중...`));
178
+ logMessage('INFO', `${tool} CLI 실행`);
179
+
180
+ const child = spawn(cmd, args, {
181
+ stdio: 'inherit',
182
+ shell: true,
183
+ env: {
184
+ ...process.env,
185
+ ADA_SYSTEM_PROMPT: systemPrompt
186
+ }
187
+ });
188
+
189
+ child.on('close', (code) => {
190
+ if (code === 0) {
191
+ resolve();
192
+ } else {
193
+ reject(new Error(`${tool} exited with code ${code}`));
194
+ }
195
+ });
196
+
197
+ child.on('error', (err) => {
198
+ reject(err);
199
+ });
200
+ });
201
+ });
202
+ }
@@ -0,0 +1,70 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import chalk from 'chalk';
4
+ import { getSessionsDir, isWorkspaceSetup } from '../utils/files.js';
5
+
6
+ export async function sessions() {
7
+ if (!isWorkspaceSetup()) {
8
+ console.log(chalk.red('❌ 먼저 setup을 실행하세요.'));
9
+ process.exit(1);
10
+ }
11
+
12
+ const sessionsDir = getSessionsDir();
13
+
14
+ console.log('');
15
+ console.log(chalk.cyan('━'.repeat(60)));
16
+ console.log(chalk.cyan.bold('📋 세션 목록'));
17
+ console.log(chalk.cyan('━'.repeat(60)));
18
+ console.log('');
19
+
20
+ if (!fs.existsSync(sessionsDir)) {
21
+ console.log(chalk.gray(' 세션 기록 없음'));
22
+ console.log('');
23
+ return;
24
+ }
25
+
26
+ const sessionDirs = fs.readdirSync(sessionsDir)
27
+ .filter(f => fs.statSync(path.join(sessionsDir, f)).isDirectory())
28
+ .sort()
29
+ .reverse();
30
+
31
+ if (sessionDirs.length === 0) {
32
+ console.log(chalk.gray(' 세션 기록 없음'));
33
+ console.log('');
34
+ return;
35
+ }
36
+
37
+ // 헤더
38
+ console.log(chalk.gray(' 세션 ID 역할 도구 상태'));
39
+ console.log(chalk.gray(' ' + '─'.repeat(56)));
40
+
41
+ for (const sessionId of sessionDirs.slice(0, 20)) {
42
+ const sessionFile = path.join(sessionsDir, sessionId, 'session.json');
43
+
44
+ if (fs.existsSync(sessionFile)) {
45
+ try {
46
+ const session = JSON.parse(fs.readFileSync(sessionFile, 'utf-8'));
47
+ const role = (session.role || '-').padEnd(10);
48
+ const tool = (session.tool || '-').padEnd(8);
49
+ const status = session.status || 'unknown';
50
+
51
+ const statusColor = status === 'completed' ? chalk.green :
52
+ status === 'active' ? chalk.yellow :
53
+ chalk.gray;
54
+
55
+ console.log(` ${sessionId} ${role} ${tool} ${statusColor(status)}`);
56
+ } catch (e) {
57
+ console.log(` ${sessionId} ${chalk.gray('(읽기 실패)')}`);
58
+ }
59
+ } else {
60
+ console.log(` ${sessionId} ${chalk.gray('(정보 없음)')}`);
61
+ }
62
+ }
63
+
64
+ console.log('');
65
+
66
+ if (sessionDirs.length > 20) {
67
+ console.log(chalk.gray(` ... 그 외 ${sessionDirs.length - 20}개 세션`));
68
+ console.log('');
69
+ }
70
+ }