aiag-cli 2.2.3 → 2.4.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.
- package/README.md +72 -37
- package/dist/api/client.d.ts +9 -1
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +20 -0
- package/dist/api/client.js.map +1 -1
- package/dist/api/endpoints.d.ts +2 -0
- package/dist/api/endpoints.d.ts.map +1 -1
- package/dist/api/endpoints.js +2 -0
- package/dist/api/endpoints.js.map +1 -1
- package/dist/api/types.d.ts +34 -0
- package/dist/api/types.d.ts.map +1 -1
- package/dist/cli.js +60 -9
- package/dist/cli.js.map +1 -1
- package/dist/commands/commit.d.ts.map +1 -1
- package/dist/commands/commit.js +37 -1
- package/dist/commands/commit.js.map +1 -1
- package/dist/commands/complete.d.ts.map +1 -1
- package/dist/commands/complete.js +11 -1
- package/dist/commands/complete.js.map +1 -1
- package/dist/commands/feature.d.ts +11 -0
- package/dist/commands/feature.d.ts.map +1 -0
- package/dist/commands/feature.js +153 -0
- package/dist/commands/feature.js.map +1 -0
- package/dist/commands/init.d.ts +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +29 -78
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/prd.d.ts +12 -0
- package/dist/commands/prd.d.ts.map +1 -0
- package/dist/commands/prd.js +179 -0
- package/dist/commands/prd.js.map +1 -0
- package/dist/commands/session.d.ts +29 -0
- package/dist/commands/session.d.ts.map +1 -1
- package/dist/commands/session.js +270 -120
- package/dist/commands/session.js.map +1 -1
- package/dist/commands/sync.d.ts +10 -0
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +95 -2
- package/dist/commands/sync.js.map +1 -1
- package/dist/commands/work.d.ts.map +1 -1
- package/dist/commands/work.js +13 -1
- package/dist/commands/work.js.map +1 -1
- package/dist/prompts/index.d.ts +2 -0
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +2 -0
- package/dist/prompts/index.js.map +1 -1
- package/dist/prompts/prd.d.ts +28 -0
- package/dist/prompts/prd.d.ts.map +1 -0
- package/dist/prompts/prd.js +105 -0
- package/dist/prompts/prd.js.map +1 -0
- package/dist/skills/index.d.ts +12 -0
- package/dist/skills/index.d.ts.map +1 -0
- package/dist/skills/index.js +12 -0
- package/dist/skills/index.js.map +1 -0
- package/dist/skills/installer.d.ts +38 -0
- package/dist/skills/installer.d.ts.map +1 -0
- package/dist/skills/installer.js +153 -0
- package/dist/skills/installer.js.map +1 -0
- package/dist/skills/loader.d.ts +34 -0
- package/dist/skills/loader.d.ts.map +1 -0
- package/dist/skills/loader.js +134 -0
- package/dist/skills/loader.js.map +1 -0
- package/dist/skills/runner.d.ts +14 -0
- package/dist/skills/runner.d.ts.map +1 -0
- package/dist/skills/runner.js +238 -0
- package/dist/skills/runner.js.map +1 -0
- package/dist/types.d.ts +160 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/prd.d.ts +21 -0
- package/dist/utils/prd.d.ts.map +1 -1
- package/dist/utils/prd.js +69 -0
- package/dist/utils/prd.js.map +1 -1
- package/dist/utils/sessionManager.d.ts +63 -0
- package/dist/utils/sessionManager.d.ts.map +1 -0
- package/dist/utils/sessionManager.js +372 -0
- package/dist/utils/sessionManager.js.map +1 -0
- package/dist/utils/taskmasterConverter.d.ts +72 -0
- package/dist/utils/taskmasterConverter.d.ts.map +1 -0
- package/dist/utils/taskmasterConverter.js +401 -0
- package/dist/utils/taskmasterConverter.js.map +1 -0
- package/dist/utils/taskmasterParser.d.ts +35 -0
- package/dist/utils/taskmasterParser.d.ts.map +1 -0
- package/dist/utils/taskmasterParser.js +259 -0
- package/dist/utils/taskmasterParser.js.map +1 -0
- package/package.json +1 -1
- package/templates/skills/prd-taskmaster/.taskmaster/docs/prd.md +2571 -0
- package/templates/skills/prd-taskmaster/.taskmaster/scripts/execution-state.py +87 -0
- package/templates/skills/prd-taskmaster/.taskmaster/scripts/learn-accuracy.py +113 -0
- package/templates/skills/prd-taskmaster/.taskmaster/scripts/rollback.sh +71 -0
- package/templates/skills/prd-taskmaster/.taskmaster/scripts/security-audit.py +130 -0
- package/templates/skills/prd-taskmaster/.taskmaster/scripts/track-time.py +133 -0
- package/templates/skills/prd-taskmaster/LICENSE +21 -0
- package/templates/skills/prd-taskmaster/README.md +608 -0
- package/templates/skills/prd-taskmaster/SKILL.md +1258 -0
- package/templates/skills/prd-taskmaster/reference/taskmaster-integration-guide.md +645 -0
- package/templates/skills/prd-taskmaster/reference/validation-checklist.md +394 -0
- package/templates/skills/prd-taskmaster/scripts/setup-taskmaster.sh +112 -0
- package/templates/skills/prd-taskmaster/templates/CLAUDE.md.template +635 -0
- package/templates/skills/prd-taskmaster/templates/taskmaster-prd-comprehensive.md +983 -0
- package/templates/skills/prd-taskmaster/templates/taskmaster-prd-minimal.md +103 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* aiag prd 명령어
|
|
3
|
+
*
|
|
4
|
+
* prd-taskmaster 스킬을 사용하여 PRD를 생성합니다.
|
|
5
|
+
* --from 옵션으로 기존 요구사항 문서를 Taskmaster PRD로 변환할 수 있습니다.
|
|
6
|
+
*/
|
|
7
|
+
import { spawn } from 'child_process';
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import chalk from 'chalk';
|
|
11
|
+
import ora from 'ora';
|
|
12
|
+
import { skillExists } from '../skills/loader.js';
|
|
13
|
+
import { installSkillIfNeeded } from '../skills/installer.js';
|
|
14
|
+
import { runPrdSkill } from '../skills/runner.js';
|
|
15
|
+
import { printHeader, printSuccess, printError, printWarning, printInfo } from '../utils/output.js';
|
|
16
|
+
import { getPrdInfo } from '../utils/prd.js';
|
|
17
|
+
/**
|
|
18
|
+
* TaskMaster 설치 확인
|
|
19
|
+
*/
|
|
20
|
+
async function checkTaskMasterInstalled() {
|
|
21
|
+
return new Promise((resolve) => {
|
|
22
|
+
// CLI 버전 확인
|
|
23
|
+
const child = spawn('taskmaster', ['--version'], {
|
|
24
|
+
stdio: 'pipe',
|
|
25
|
+
shell: true,
|
|
26
|
+
});
|
|
27
|
+
child.on('close', (code) => {
|
|
28
|
+
resolve(code === 0);
|
|
29
|
+
});
|
|
30
|
+
child.on('error', () => {
|
|
31
|
+
resolve(false);
|
|
32
|
+
});
|
|
33
|
+
// 타임아웃
|
|
34
|
+
setTimeout(() => {
|
|
35
|
+
child.kill();
|
|
36
|
+
resolve(false);
|
|
37
|
+
}, 5000);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* PRD 생성 명령어
|
|
42
|
+
*/
|
|
43
|
+
export async function prd(options = {}) {
|
|
44
|
+
const baseDir = process.cwd();
|
|
45
|
+
const outputPath = options.output || '.taskmaster/docs/prd.md';
|
|
46
|
+
const template = options.template || 'comprehensive';
|
|
47
|
+
const fromPath = options.from;
|
|
48
|
+
// 모드 결정: 변환 모드 vs 대화형 모드
|
|
49
|
+
const isConversionMode = !!fromPath;
|
|
50
|
+
printHeader(isConversionMode ? 'AIAG PRD Converter' : 'AIAG PRD Generator');
|
|
51
|
+
console.log();
|
|
52
|
+
// 1. --from 옵션: 기존 문서 로딩
|
|
53
|
+
let existingDocContent;
|
|
54
|
+
let existingDocPath;
|
|
55
|
+
if (isConversionMode) {
|
|
56
|
+
const fullFromPath = path.isAbsolute(fromPath) ? fromPath : path.join(baseDir, fromPath);
|
|
57
|
+
if (!fs.existsSync(fullFromPath)) {
|
|
58
|
+
printError(`파일을 찾을 수 없습니다: ${fromPath}`);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
const docInfo = getPrdInfo(fullFromPath);
|
|
62
|
+
printInfo(`기존 문서 로드: ${docInfo.relativePath} (${docInfo.sizeKB} KB)`);
|
|
63
|
+
existingDocContent = fs.readFileSync(fullFromPath, 'utf-8');
|
|
64
|
+
existingDocPath = fromPath;
|
|
65
|
+
if (existingDocContent.length < 100) {
|
|
66
|
+
printWarning('문서 내용이 매우 짧습니다. 충분한 정보가 포함되어 있는지 확인하세요.');
|
|
67
|
+
}
|
|
68
|
+
console.log();
|
|
69
|
+
}
|
|
70
|
+
// 2. TaskMaster 확인 (필수)
|
|
71
|
+
const spinner = ora('TaskMaster 확인 중...').start();
|
|
72
|
+
const taskMasterInstalled = await checkTaskMasterInstalled();
|
|
73
|
+
if (!taskMasterInstalled) {
|
|
74
|
+
spinner.fail('TaskMaster가 설치되어 있지 않습니다.');
|
|
75
|
+
console.log();
|
|
76
|
+
printError('TaskMaster는 필수 의존성입니다.');
|
|
77
|
+
console.log();
|
|
78
|
+
printInfo('설치 방법:');
|
|
79
|
+
console.log(chalk.gray(' npm install -g task-master-ai'));
|
|
80
|
+
console.log(chalk.gray(' 또는 Claude Code MCP 설정에 TaskMaster 추가'));
|
|
81
|
+
console.log();
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
spinner.succeed('TaskMaster 설치 확인됨');
|
|
85
|
+
// 3. prd-taskmaster 스킬 확인 및 설치
|
|
86
|
+
spinner.start('prd-taskmaster 스킬 확인 중...');
|
|
87
|
+
if (!skillExists('prd-taskmaster')) {
|
|
88
|
+
spinner.text = 'prd-taskmaster 스킬 설치 중...';
|
|
89
|
+
const installResult = installSkillIfNeeded('prd-taskmaster');
|
|
90
|
+
if (installResult.status === 'failed') {
|
|
91
|
+
spinner.fail('스킬 설치 실패');
|
|
92
|
+
printError(installResult.error || '알 수 없는 오류');
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
spinner.succeed('prd-taskmaster 스킬 설치됨');
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
spinner.succeed('prd-taskmaster 스킬 확인됨');
|
|
99
|
+
}
|
|
100
|
+
// 4. 기존 PRD 확인
|
|
101
|
+
const fullOutputPath = path.join(baseDir, outputPath);
|
|
102
|
+
if (fs.existsSync(fullOutputPath)) {
|
|
103
|
+
console.log();
|
|
104
|
+
printWarning(`기존 PRD 발견: ${outputPath}`);
|
|
105
|
+
printInfo('새 PRD 생성 시 기존 파일이 백업됩니다.');
|
|
106
|
+
console.log();
|
|
107
|
+
}
|
|
108
|
+
// 5. Dry run 모드
|
|
109
|
+
if (options.dryRun) {
|
|
110
|
+
console.log();
|
|
111
|
+
printInfo('[DRY RUN] PRD 생성 시뮬레이션');
|
|
112
|
+
console.log(chalk.gray(` 모드: ${isConversionMode ? '변환 모드' : '대화형 모드'}`));
|
|
113
|
+
if (isConversionMode) {
|
|
114
|
+
console.log(chalk.gray(` 소스 문서: ${fromPath}`));
|
|
115
|
+
}
|
|
116
|
+
console.log(chalk.gray(` 출력 경로: ${outputPath}`));
|
|
117
|
+
console.log(chalk.gray(` 템플릿: ${template}`));
|
|
118
|
+
console.log(chalk.gray(` 작업 디렉토리: ${baseDir}`));
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
// 6. PRD 생성 실행
|
|
122
|
+
console.log();
|
|
123
|
+
if (isConversionMode) {
|
|
124
|
+
printInfo('PRD 변환을 시작합니다...');
|
|
125
|
+
printInfo('기존 문서를 분석하고, 누락된 정보가 있으면 질문합니다.');
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
printInfo('PRD 생성을 시작합니다...');
|
|
129
|
+
printInfo('prd-taskmaster 스킬의 질문에 답변해주세요.');
|
|
130
|
+
}
|
|
131
|
+
console.log();
|
|
132
|
+
const result = await runPrdSkill({
|
|
133
|
+
workingDirectory: baseDir,
|
|
134
|
+
template,
|
|
135
|
+
verbose: options.verbose,
|
|
136
|
+
existingDocContent,
|
|
137
|
+
existingDocPath,
|
|
138
|
+
onProgress: (message) => {
|
|
139
|
+
if (options.verbose) {
|
|
140
|
+
console.log(chalk.gray(`[진행] ${message}`));
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
console.log();
|
|
145
|
+
// 7. 결과 처리
|
|
146
|
+
if (result.success) {
|
|
147
|
+
printSuccess(isConversionMode ? 'PRD 변환 완료!' : 'PRD 생성 완료!');
|
|
148
|
+
console.log();
|
|
149
|
+
if (result.filesCreated && result.filesCreated.length > 0) {
|
|
150
|
+
printInfo('생성된 파일:');
|
|
151
|
+
for (const file of result.filesCreated) {
|
|
152
|
+
console.log(chalk.gray(` • ${file}`));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (result.numTurns) {
|
|
156
|
+
console.log(chalk.gray(` 턴 수: ${result.numTurns}`));
|
|
157
|
+
}
|
|
158
|
+
if (result.costUsd) {
|
|
159
|
+
console.log(chalk.gray(` 비용: $${result.costUsd.toFixed(4)}`));
|
|
160
|
+
}
|
|
161
|
+
if (result.durationMs) {
|
|
162
|
+
const minutes = Math.floor(result.durationMs / 60000);
|
|
163
|
+
const seconds = Math.floor((result.durationMs % 60000) / 1000);
|
|
164
|
+
console.log(chalk.gray(` 소요 시간: ${minutes}분 ${seconds}초`));
|
|
165
|
+
}
|
|
166
|
+
console.log();
|
|
167
|
+
printInfo('다음 단계:');
|
|
168
|
+
console.log(chalk.cyan(' aiag feature ') + chalk.gray('# PRD에서 Feature 목록 생성'));
|
|
169
|
+
console.log(chalk.cyan(' aiag validate ') + chalk.gray('# Feature 목록 검증'));
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
printError(isConversionMode ? 'PRD 변환 실패' : 'PRD 생성 실패');
|
|
173
|
+
if (result.error) {
|
|
174
|
+
console.log(chalk.red(` ${result.error}`));
|
|
175
|
+
}
|
|
176
|
+
process.exit(1);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=prd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prd.js","sourceRoot":"","sources":["../../src/commands/prd.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 세션 명령어 (재정의)
|
|
3
|
+
*
|
|
4
|
+
* 암시적 세션 관리 도입으로 session start/end는 deprecated.
|
|
5
|
+
* 새로운 역할: 세션 상태 조회 및 유틸리티
|
|
6
|
+
*
|
|
7
|
+
* 사용법:
|
|
8
|
+
* aiag session # 현재 세션 상태 표시
|
|
9
|
+
* aiag session list # 최근 세션 목록 (progress.md에서)
|
|
10
|
+
* aiag session close # 현재 세션 강제 종료
|
|
11
|
+
*
|
|
12
|
+
* Deprecated (하위 호환성):
|
|
13
|
+
* aiag session start # → work 명령 사용 권장
|
|
14
|
+
* aiag session end # → session close 또는 자동 종료
|
|
15
|
+
*/
|
|
16
|
+
interface SessionOptions {
|
|
17
|
+
count?: number;
|
|
18
|
+
force?: boolean;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* 세션 상태 표시 (기본 명령)
|
|
22
|
+
*/
|
|
23
|
+
export declare function session(subcommand?: string, options?: SessionOptions): Promise<void>;
|
|
1
24
|
interface SessionStartOptions {
|
|
2
25
|
offline?: boolean;
|
|
3
26
|
notes?: string;
|
|
4
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* @deprecated Use `aiag work` instead. Sessions start automatically.
|
|
30
|
+
*/
|
|
5
31
|
export declare function sessionStart(featureId?: string, options?: SessionStartOptions): Promise<void>;
|
|
6
32
|
interface SessionEndOptions {
|
|
7
33
|
offline?: boolean;
|
|
8
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* @deprecated Use `aiag session close` instead. Sessions end automatically after 24h inactivity.
|
|
37
|
+
*/
|
|
9
38
|
export declare function sessionEnd(options?: SessionEndOptions): Promise<void>;
|
|
10
39
|
export {};
|
|
11
40
|
//# sourceMappingURL=session.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/commands/session.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/commands/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AA4BH,UAAU,cAAc;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,wBAAsB,OAAO,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CA2C9F;AA2ND,UAAU,mBAAmB;IAC3B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CA+CvG;AAED,UAAU,iBAAiB;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ/E"}
|
package/dist/commands/session.js
CHANGED
|
@@ -1,26 +1,259 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* 세션 명령어 (재정의)
|
|
3
|
+
*
|
|
4
|
+
* 암시적 세션 관리 도입으로 session start/end는 deprecated.
|
|
5
|
+
* 새로운 역할: 세션 상태 조회 및 유틸리티
|
|
6
|
+
*
|
|
7
|
+
* 사용법:
|
|
8
|
+
* aiag session # 현재 세션 상태 표시
|
|
9
|
+
* aiag session list # 최근 세션 목록 (progress.md에서)
|
|
10
|
+
* aiag session close # 현재 세션 강제 종료
|
|
11
|
+
*
|
|
12
|
+
* Deprecated (하위 호환성):
|
|
13
|
+
* aiag session start # → work 명령 사용 권장
|
|
14
|
+
* aiag session end # → session close 또는 자동 종료
|
|
15
|
+
*/
|
|
16
|
+
import { updateLastUpdated, readProgress } from '../utils/progress.js';
|
|
2
17
|
import { getProgress, getNextFeature } from '../utils/featureList.js';
|
|
3
|
-
import { printHeader, printSection, colors, icons, printInfo, printSuccess, printWarning } from '../utils/output.js';
|
|
18
|
+
import { printHeader, printSection, colors, icons, printInfo, printSuccess, printWarning, printError, } from '../utils/output.js';
|
|
4
19
|
import { clearSessionContext, hasInterruptedWork } from '../utils/sessionContext.js';
|
|
5
|
-
import { loadConnection, saveCurrentSession
|
|
20
|
+
import { loadConnection, saveCurrentSession } from '../utils/connection.js';
|
|
6
21
|
import { getApiClient } from '../api/client.js';
|
|
7
|
-
|
|
22
|
+
import { syncProgressToServer } from './sync.js';
|
|
23
|
+
import { loadSessionState, closeSession, getSessionStats, } from '../utils/sessionManager.js';
|
|
24
|
+
/**
|
|
25
|
+
* 세션 상태 표시 (기본 명령)
|
|
26
|
+
*/
|
|
27
|
+
export async function session(subcommand, options = {}) {
|
|
8
28
|
const baseDir = process.cwd();
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
29
|
+
switch (subcommand) {
|
|
30
|
+
case undefined:
|
|
31
|
+
case 'status':
|
|
32
|
+
await showSessionStatus(baseDir);
|
|
33
|
+
break;
|
|
34
|
+
case 'list':
|
|
35
|
+
await listRecentSessions(baseDir, options.count || 5);
|
|
36
|
+
break;
|
|
37
|
+
case 'close':
|
|
38
|
+
await closeCurrentSession(baseDir, options.force);
|
|
39
|
+
break;
|
|
40
|
+
// Deprecated 명령어 (하위 호환성)
|
|
41
|
+
case 'start':
|
|
42
|
+
printWarning('⚠ "session start" is deprecated.');
|
|
43
|
+
console.log(colors.dim(' 세션은 work/auto 명령 실행 시 자동으로 시작됩니다.'));
|
|
44
|
+
console.log(colors.dim(' 작업을 시작하려면: aiag work'));
|
|
45
|
+
console.log('');
|
|
46
|
+
break;
|
|
47
|
+
case 'end':
|
|
48
|
+
printWarning('⚠ "session end" is deprecated.');
|
|
49
|
+
console.log(colors.dim(' 세션은 24시간 비활성 후 자동으로 종료됩니다.'));
|
|
50
|
+
console.log(colors.dim(' 강제 종료가 필요하면: aiag session close'));
|
|
51
|
+
console.log('');
|
|
52
|
+
// 하위 호환성을 위해 close 실행
|
|
53
|
+
await closeCurrentSession(baseDir, options.force);
|
|
54
|
+
break;
|
|
55
|
+
default:
|
|
56
|
+
printError(`Unknown subcommand: ${subcommand}`);
|
|
57
|
+
console.log('');
|
|
58
|
+
console.log('Usage:');
|
|
59
|
+
console.log(' aiag session 현재 세션 상태 표시');
|
|
60
|
+
console.log(' aiag session list 최근 세션 목록');
|
|
61
|
+
console.log(' aiag session close 현재 세션 강제 종료');
|
|
62
|
+
console.log('');
|
|
18
63
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* 현재 세션 상태 표시
|
|
67
|
+
*/
|
|
68
|
+
async function showSessionStatus(baseDir) {
|
|
69
|
+
const stats = getSessionStats(baseDir);
|
|
70
|
+
const progress = getProgress(baseDir);
|
|
71
|
+
printHeader('Session Status');
|
|
72
|
+
console.log('');
|
|
73
|
+
if (!stats.session) {
|
|
74
|
+
console.log(colors.dim('No active session.'));
|
|
75
|
+
console.log('');
|
|
76
|
+
console.log(colors.dim('세션은 다음 명령 실행 시 자동으로 시작됩니다:'));
|
|
77
|
+
console.log(colors.info(' aiag work'));
|
|
78
|
+
console.log(colors.info(' aiag auto'));
|
|
79
|
+
console.log('');
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const session = stats.session;
|
|
83
|
+
const statusIcon = stats.active ? colors.success('●') : colors.warning('○');
|
|
84
|
+
const statusText = stats.active ? 'Active' : 'Stale (will close on next command)';
|
|
85
|
+
// 세션 정보
|
|
86
|
+
printSection('Current Session');
|
|
87
|
+
console.log(` ${colors.bold('ID:')} ${session.id}`);
|
|
88
|
+
console.log(` ${colors.bold('Status:')} ${statusIcon} ${statusText}`);
|
|
89
|
+
console.log(` ${colors.bold('Mode:')} ${session.mode}`);
|
|
90
|
+
console.log(` ${colors.bold('Started:')} ${formatRelativeTime(session.startedAt)}`);
|
|
91
|
+
console.log(` ${colors.bold('Duration:')} ${stats.duration}`);
|
|
92
|
+
console.log('');
|
|
93
|
+
// 작업 통계
|
|
94
|
+
printSection('Activity');
|
|
95
|
+
console.log(` ${colors.bold('Features worked:')} ${session.featuresWorked.length}`);
|
|
96
|
+
console.log(` ${colors.bold('Features completed:')} ${session.featuresCompleted.length}`);
|
|
97
|
+
console.log(` ${colors.bold('Commits:')} ${session.commits.length}`);
|
|
98
|
+
console.log('');
|
|
99
|
+
// 진행률 변화
|
|
100
|
+
const progressDelta = stats.progressDelta || 0;
|
|
101
|
+
const deltaText = progressDelta > 0
|
|
102
|
+
? colors.success(`+${progressDelta}%`)
|
|
103
|
+
: progressDelta < 0
|
|
104
|
+
? colors.error(`${progressDelta}%`)
|
|
105
|
+
: colors.dim('0%');
|
|
106
|
+
printSection('Progress');
|
|
107
|
+
console.log(` ${session.initialProgress}% → ${progress.percentage}% (${deltaText})`);
|
|
108
|
+
console.log('');
|
|
109
|
+
// 작업한 features
|
|
110
|
+
if (session.featuresWorked.length > 0) {
|
|
111
|
+
printSection('Features Worked');
|
|
112
|
+
session.featuresWorked.forEach((id) => {
|
|
113
|
+
const completed = session.featuresCompleted.includes(id);
|
|
114
|
+
const icon = completed ? icons.success : icons.arrow;
|
|
115
|
+
console.log(` ${icon} ${id}`);
|
|
116
|
+
});
|
|
117
|
+
console.log('');
|
|
118
|
+
}
|
|
119
|
+
// 커밋
|
|
120
|
+
if (session.commits.length > 0) {
|
|
121
|
+
printSection('Commits');
|
|
122
|
+
session.commits.slice(-5).forEach((commit) => {
|
|
123
|
+
console.log(` ${colors.dim(commit.hash)} ${commit.message.substring(0, 50)}`);
|
|
124
|
+
});
|
|
125
|
+
if (session.commits.length > 5) {
|
|
126
|
+
console.log(colors.dim(` ... and ${session.commits.length - 5} more`));
|
|
127
|
+
}
|
|
128
|
+
console.log('');
|
|
129
|
+
}
|
|
130
|
+
// 중단된 작업 확인
|
|
131
|
+
if (hasInterruptedWork(baseDir)) {
|
|
132
|
+
printWarning('⚠ There is interrupted work in session_context.md');
|
|
133
|
+
console.log(colors.dim(' Resume with: aiag work'));
|
|
134
|
+
console.log(colors.dim(' Or clear with: aiag session close --force'));
|
|
135
|
+
console.log('');
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* 최근 세션 목록 표시
|
|
140
|
+
*/
|
|
141
|
+
async function listRecentSessions(baseDir, count) {
|
|
142
|
+
const progressContent = readProgress(baseDir);
|
|
143
|
+
if (!progressContent) {
|
|
144
|
+
printError('No progress.md found.');
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
printHeader('Recent Sessions');
|
|
148
|
+
console.log('');
|
|
149
|
+
// progress.md에서 세션 파싱
|
|
150
|
+
const sessionPattern = /## Session: (\S+)\s*\n([\s\S]*?)(?=\n## Session:|\n---\s*$|$)/g;
|
|
151
|
+
const sessions = [];
|
|
152
|
+
let match;
|
|
153
|
+
while ((match = sessionPattern.exec(progressContent)) !== null) {
|
|
154
|
+
sessions.push({
|
|
155
|
+
id: match[1],
|
|
156
|
+
content: match[2],
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
if (sessions.length === 0) {
|
|
160
|
+
console.log(colors.dim('No session history found.'));
|
|
161
|
+
console.log('');
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
// 최근 N개 세션 표시
|
|
165
|
+
const recentSessions = sessions.slice(0, count);
|
|
166
|
+
recentSessions.forEach((session, index) => {
|
|
167
|
+
// 메트릭 파싱
|
|
168
|
+
const durationMatch = session.content.match(/\*\*Duration\*\*:\s*(.+)/);
|
|
169
|
+
const progressMatch = session.content.match(/\*\*Progress\*\*:\s*(.+)/);
|
|
170
|
+
const modeMatch = session.content.match(/\*\*Mode\*\*:\s*(.+)/);
|
|
171
|
+
const completedMatch = session.content.match(/Features Completed \((\d+)\)/);
|
|
172
|
+
const duration = durationMatch ? durationMatch[1] : 'unknown';
|
|
173
|
+
const progressChange = progressMatch ? progressMatch[1] : '-';
|
|
174
|
+
const mode = modeMatch ? modeMatch[1] : '-';
|
|
175
|
+
const completedCount = completedMatch ? completedMatch[1] : '0';
|
|
176
|
+
console.log(`${colors.bold(`${index + 1}. ${session.id}`)}`);
|
|
177
|
+
console.log(` Duration: ${duration} | Mode: ${mode} | Completed: ${completedCount}`);
|
|
178
|
+
console.log(` Progress: ${progressChange}`);
|
|
179
|
+
console.log('');
|
|
180
|
+
});
|
|
181
|
+
if (sessions.length > count) {
|
|
182
|
+
console.log(colors.dim(`... and ${sessions.length - count} more sessions in progress.md`));
|
|
183
|
+
console.log('');
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* 현재 세션 강제 종료
|
|
188
|
+
*
|
|
189
|
+
* 참고: 서버 세션 종료는 sessionManager.ts의 finalizeSession()에서 자동 처리
|
|
190
|
+
*/
|
|
191
|
+
async function closeCurrentSession(baseDir, force) {
|
|
192
|
+
const session = loadSessionState(baseDir);
|
|
193
|
+
if (!session) {
|
|
194
|
+
printInfo('No active session to close.');
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
printHeader('Closing Session');
|
|
198
|
+
console.log('');
|
|
199
|
+
// 세션 종료 (서버 알림 포함)
|
|
200
|
+
closeSession(baseDir, 'manual');
|
|
201
|
+
const serverSynced = session.serverId ? ' (server synced)' : '';
|
|
202
|
+
printSuccess(`Session ${session.id} closed.${serverSynced}`);
|
|
203
|
+
console.log(colors.dim(' Session summary recorded in progress.md'));
|
|
204
|
+
console.log('');
|
|
205
|
+
// Progress 자동 동기화 (연결된 경우)
|
|
206
|
+
const connection = loadConnection(baseDir);
|
|
207
|
+
const client = getApiClient();
|
|
208
|
+
if (connection && client) {
|
|
209
|
+
await syncProgressToServer(client, connection.projectId, undefined, true).catch(() => {
|
|
210
|
+
// 동기화 실패 시 무시 - 다음 sync 명령에서 재시도
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
// 중단된 작업 클리어 (force 옵션)
|
|
214
|
+
if (force && hasInterruptedWork(baseDir)) {
|
|
215
|
+
clearSessionContext(baseDir);
|
|
216
|
+
printSuccess('Cleared interrupted work from session_context.md');
|
|
217
|
+
console.log('');
|
|
218
|
+
}
|
|
219
|
+
updateLastUpdated(baseDir);
|
|
220
|
+
}
|
|
221
|
+
// ============================================================================
|
|
222
|
+
// 유틸리티 함수
|
|
223
|
+
// ============================================================================
|
|
224
|
+
/**
|
|
225
|
+
* 상대 시간 포맷팅
|
|
226
|
+
*/
|
|
227
|
+
function formatRelativeTime(isoString) {
|
|
228
|
+
const date = new Date(isoString);
|
|
229
|
+
const now = new Date();
|
|
230
|
+
const diffMs = now.getTime() - date.getTime();
|
|
231
|
+
const diffSeconds = Math.floor(diffMs / 1000);
|
|
232
|
+
const diffMinutes = Math.floor(diffSeconds / 60);
|
|
233
|
+
const diffHours = Math.floor(diffMinutes / 60);
|
|
234
|
+
const diffDays = Math.floor(diffHours / 24);
|
|
235
|
+
if (diffDays > 0) {
|
|
236
|
+
return `${diffDays} day${diffDays > 1 ? 's' : ''} ago`;
|
|
237
|
+
}
|
|
238
|
+
else if (diffHours > 0) {
|
|
239
|
+
return `${diffHours} hour${diffHours > 1 ? 's' : ''} ago`;
|
|
240
|
+
}
|
|
241
|
+
else if (diffMinutes > 0) {
|
|
242
|
+
return `${diffMinutes} minute${diffMinutes > 1 ? 's' : ''} ago`;
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
return 'just now';
|
|
22
246
|
}
|
|
23
|
-
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* @deprecated Use `aiag work` instead. Sessions start automatically.
|
|
250
|
+
*/
|
|
251
|
+
export async function sessionStart(featureId, options = {}) {
|
|
252
|
+
printWarning('⚠ sessionStart() is deprecated.');
|
|
253
|
+
console.log(colors.dim(' 세션은 work/auto 명령 실행 시 자동으로 시작됩니다.'));
|
|
254
|
+
console.log('');
|
|
255
|
+
const baseDir = process.cwd();
|
|
256
|
+
// 하위 호환성: 서버 세션은 여전히 시작
|
|
24
257
|
const connection = loadConnection(baseDir);
|
|
25
258
|
const client = getApiClient();
|
|
26
259
|
if (connection && client && !options.offline) {
|
|
@@ -29,124 +262,41 @@ export async function sessionStart(featureId, options = {}) {
|
|
|
29
262
|
agentType: 'cli',
|
|
30
263
|
notes: options.notes,
|
|
31
264
|
});
|
|
32
|
-
// 현재 세션 정보 저장
|
|
33
265
|
saveCurrentSession({
|
|
34
266
|
id: sessionInfo.id,
|
|
35
267
|
projectId: sessionInfo.projectId,
|
|
36
268
|
startedAt: sessionInfo.startedAt,
|
|
37
269
|
agentType: 'cli',
|
|
38
270
|
}, baseDir);
|
|
39
|
-
printInfo(colors.dim(
|
|
40
|
-
// 세션 시작 로그 전송
|
|
41
|
-
if (featureId) {
|
|
42
|
-
await client.sendLog(featureId, {
|
|
43
|
-
type: 'started',
|
|
44
|
-
message: `Session started for feature ${featureId}`,
|
|
45
|
-
});
|
|
46
|
-
}
|
|
271
|
+
printInfo(colors.dim(`서버 세션 시작됨 (ID: ${sessionInfo.id.slice(0, 8)}...)`));
|
|
47
272
|
}
|
|
48
|
-
catch
|
|
49
|
-
printWarning('서버 세션 등록 실패
|
|
273
|
+
catch {
|
|
274
|
+
printWarning('서버 세션 등록 실패');
|
|
50
275
|
}
|
|
51
276
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
console.log(` ${
|
|
56
|
-
|
|
57
|
-
console.log(` ${icons.checkbox.unchecked} git log --oneline -10`);
|
|
58
|
-
console.log(` ${icons.checkbox.unchecked} .aiag/init.sh`);
|
|
59
|
-
console.log(` ${icons.checkbox.unchecked} Identify next feature`);
|
|
60
|
-
console.log('');
|
|
61
|
-
printInfo('Session started. Remember: ONE feature at a time!');
|
|
62
|
-
if (connection) {
|
|
63
|
-
console.log(colors.dim(` 연결된 프로젝트: ${connection.projectName}`));
|
|
64
|
-
}
|
|
65
|
-
console.log('');
|
|
66
|
-
}
|
|
67
|
-
export async function sessionEnd(options = {}) {
|
|
68
|
-
const baseDir = process.cwd();
|
|
69
|
-
printHeader('Session End');
|
|
70
|
-
const progress = getProgress();
|
|
71
|
-
const startProgress = progress; // 세션 시작 시점의 진행률 (실제로는 저장해두어야 함)
|
|
72
|
-
// Check if there's interrupted work and clear it
|
|
73
|
-
const hadInterruptedWork = hasInterruptedWork(baseDir);
|
|
74
|
-
if (hadInterruptedWork) {
|
|
75
|
-
clearSessionContext(baseDir);
|
|
76
|
-
console.log('');
|
|
77
|
-
printSuccess('✓ Cleared interrupted work from session context');
|
|
277
|
+
const progress = getProgress(baseDir);
|
|
278
|
+
const nextFeature = featureId ? null : getNextFeature();
|
|
279
|
+
printSection('Current Progress');
|
|
280
|
+
console.log(` ${progress.completed}/${progress.total} features (${progress.percentage}%)`);
|
|
281
|
+
if (nextFeature) {
|
|
78
282
|
console.log('');
|
|
283
|
+
console.log(colors.dim('Next recommended feature:'));
|
|
284
|
+
console.log(` ${icons.arrow} ${nextFeature.id}: ${nextFeature.description}`);
|
|
79
285
|
}
|
|
80
|
-
// Get session summary
|
|
81
|
-
printSection('Session Summary');
|
|
82
|
-
console.log(` Progress: ${progress.completed}/${progress.total} features (${progress.percentage}%)`);
|
|
83
|
-
// 서버에 세션 종료 알림 (연결된 경우)
|
|
84
|
-
const connection = loadConnection(baseDir);
|
|
85
|
-
const client = getApiClient();
|
|
86
|
-
const currentSession = loadCurrentSession(baseDir);
|
|
87
|
-
if (connection && client && currentSession && !options.offline) {
|
|
88
|
-
try {
|
|
89
|
-
const sessionDuration = Math.floor((Date.now() - new Date(currentSession.startedAt).getTime()) / 1000);
|
|
90
|
-
// 세션 요약 생성
|
|
91
|
-
const summary = {
|
|
92
|
-
workDone: [], // 실제로는 progress.md에서 파싱해야 함
|
|
93
|
-
commits: [], // git log에서 가져올 수 있음
|
|
94
|
-
nextSteps: [],
|
|
95
|
-
notes: [],
|
|
96
|
-
duration: sessionDuration,
|
|
97
|
-
};
|
|
98
|
-
await client.endSession(currentSession.id, summary);
|
|
99
|
-
printInfo(colors.dim(`세션 종료됨 (${formatDuration(sessionDuration)})`));
|
|
100
|
-
// 현재 세션 정보 삭제
|
|
101
|
-
clearCurrentSession(baseDir);
|
|
102
|
-
}
|
|
103
|
-
catch (error) {
|
|
104
|
-
printWarning('서버 세션 종료 알림 실패');
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
// Checklist
|
|
108
|
-
printSection('Session End Checklist');
|
|
109
|
-
console.log(` ${icons.checkbox.unchecked} Feature complete OR clean state`);
|
|
110
|
-
console.log(` ${icons.checkbox.unchecked} All tests passing`);
|
|
111
|
-
console.log(` ${icons.checkbox.unchecked} progress.md updated`);
|
|
112
|
-
console.log(` ${icons.checkbox.unchecked} feature_list.json updated`);
|
|
113
|
-
console.log(` ${icons.checkbox.unchecked} All changes committed`);
|
|
114
|
-
console.log(` ${icons.checkbox.unchecked} git status clean`);
|
|
115
|
-
// Update last updated
|
|
116
|
-
updateLastUpdated();
|
|
117
|
-
console.log('');
|
|
118
|
-
printInfo('Remember to update progress.md with your session summary!');
|
|
119
286
|
console.log('');
|
|
120
|
-
console.log(colors.dim('
|
|
121
|
-
console.log(colors.
|
|
122
|
-
console.log(colors.dim(`## Session: ${new Date().toISOString()}`));
|
|
123
|
-
console.log(colors.dim(''));
|
|
124
|
-
console.log(colors.dim('### Work Done'));
|
|
125
|
-
console.log(colors.dim('- [x] Completed task'));
|
|
126
|
-
console.log(colors.dim(''));
|
|
127
|
-
console.log(colors.dim('### Commits'));
|
|
128
|
-
console.log(colors.dim('- `hash` commit message'));
|
|
129
|
-
console.log(colors.dim(''));
|
|
130
|
-
console.log(colors.dim('### Next Session'));
|
|
131
|
-
console.log(colors.dim('- Continue with FEATURE-ID'));
|
|
132
|
-
console.log(colors.dim('---'));
|
|
287
|
+
console.log(colors.dim('To start working:'));
|
|
288
|
+
console.log(colors.info(' aiag work'));
|
|
133
289
|
console.log('');
|
|
134
290
|
}
|
|
135
291
|
/**
|
|
136
|
-
*
|
|
292
|
+
* @deprecated Use `aiag session close` instead. Sessions end automatically after 24h inactivity.
|
|
137
293
|
*/
|
|
138
|
-
function
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
else if (minutes > 0) {
|
|
146
|
-
return `${minutes}분 ${secs}초`;
|
|
147
|
-
}
|
|
148
|
-
else {
|
|
149
|
-
return `${secs}초`;
|
|
150
|
-
}
|
|
294
|
+
export async function sessionEnd(options = {}) {
|
|
295
|
+
printWarning('⚠ sessionEnd() is deprecated.');
|
|
296
|
+
console.log(colors.dim(' 세션은 24시간 비활성 후 자동으로 종료됩니다.'));
|
|
297
|
+
console.log(colors.dim(' 강제 종료: aiag session close'));
|
|
298
|
+
console.log('');
|
|
299
|
+
const baseDir = process.cwd();
|
|
300
|
+
await closeCurrentSession(baseDir, true);
|
|
151
301
|
}
|
|
152
302
|
//# sourceMappingURL=session.js.map
|