@litmers/cursorflow-orchestrator 0.1.40 → 0.2.2
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/CHANGELOG.md +0 -2
- package/README.md +7 -3
- package/commands/cursorflow-init.md +0 -4
- package/dist/cli/logs.js +108 -9
- package/dist/cli/logs.js.map +1 -1
- package/dist/cli/models.js +20 -3
- package/dist/cli/models.js.map +1 -1
- package/dist/cli/monitor.d.ts +7 -10
- package/dist/cli/monitor.js +1088 -1240
- package/dist/cli/monitor.js.map +1 -1
- package/dist/cli/resume.js +21 -1
- package/dist/cli/resume.js.map +1 -1
- package/dist/cli/run.js +28 -9
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/signal.d.ts +6 -1
- package/dist/cli/signal.js +94 -12
- package/dist/cli/signal.js.map +1 -1
- package/dist/cli/tasks.js +3 -46
- package/dist/cli/tasks.js.map +1 -1
- package/dist/core/agent-supervisor.d.ts +23 -0
- package/dist/core/agent-supervisor.js +42 -0
- package/dist/core/agent-supervisor.js.map +1 -0
- package/dist/core/auto-recovery.d.ts +2 -1
- package/dist/core/auto-recovery.js +6 -1
- package/dist/core/auto-recovery.js.map +1 -1
- package/dist/core/failure-policy.d.ts +0 -1
- package/dist/core/failure-policy.js +0 -1
- package/dist/core/failure-policy.js.map +1 -1
- package/dist/core/git-lifecycle-manager.d.ts +284 -0
- package/dist/core/git-lifecycle-manager.js +778 -0
- package/dist/core/git-lifecycle-manager.js.map +1 -0
- package/dist/core/git-pipeline-coordinator.d.ts +21 -0
- package/dist/core/git-pipeline-coordinator.js +205 -0
- package/dist/core/git-pipeline-coordinator.js.map +1 -0
- package/dist/core/intervention.d.ts +176 -0
- package/dist/core/intervention.js +424 -0
- package/dist/core/intervention.js.map +1 -0
- package/dist/core/lane-state-machine.d.ts +423 -0
- package/dist/core/lane-state-machine.js +890 -0
- package/dist/core/lane-state-machine.js.map +1 -0
- package/dist/core/orchestrator.d.ts +4 -1
- package/dist/core/orchestrator.js +29 -62
- package/dist/core/orchestrator.js.map +1 -1
- package/dist/core/runner/agent.d.ts +7 -1
- package/dist/core/runner/agent.js +45 -30
- package/dist/core/runner/agent.js.map +1 -1
- package/dist/core/runner/pipeline.js +283 -123
- package/dist/core/runner/pipeline.js.map +1 -1
- package/dist/core/runner/task.d.ts +4 -5
- package/dist/core/runner/task.js +6 -80
- package/dist/core/runner/task.js.map +1 -1
- package/dist/core/runner.js +8 -2
- package/dist/core/runner.js.map +1 -1
- package/dist/core/stall-detection.d.ts +11 -4
- package/dist/core/stall-detection.js +62 -27
- package/dist/core/stall-detection.js.map +1 -1
- package/dist/hooks/contexts/index.d.ts +104 -0
- package/dist/hooks/contexts/index.js +134 -0
- package/dist/hooks/contexts/index.js.map +1 -0
- package/dist/hooks/data-accessor.d.ts +86 -0
- package/dist/hooks/data-accessor.js +410 -0
- package/dist/hooks/data-accessor.js.map +1 -0
- package/dist/hooks/flow-controller.d.ts +136 -0
- package/dist/hooks/flow-controller.js +351 -0
- package/dist/hooks/flow-controller.js.map +1 -0
- package/dist/hooks/index.d.ts +68 -0
- package/dist/hooks/index.js +105 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/manager.d.ts +129 -0
- package/dist/hooks/manager.js +389 -0
- package/dist/hooks/manager.js.map +1 -0
- package/dist/hooks/types.d.ts +463 -0
- package/dist/hooks/types.js +45 -0
- package/dist/hooks/types.js.map +1 -0
- package/dist/services/logging/buffer.d.ts +2 -2
- package/dist/services/logging/buffer.js +95 -42
- package/dist/services/logging/buffer.js.map +1 -1
- package/dist/services/logging/console.js +6 -1
- package/dist/services/logging/console.js.map +1 -1
- package/dist/services/logging/formatter.d.ts +9 -4
- package/dist/services/logging/formatter.js +64 -18
- package/dist/services/logging/formatter.js.map +1 -1
- package/dist/services/logging/index.d.ts +0 -1
- package/dist/services/logging/index.js +0 -1
- package/dist/services/logging/index.js.map +1 -1
- package/dist/services/logging/paths.d.ts +8 -0
- package/dist/services/logging/paths.js +48 -0
- package/dist/services/logging/paths.js.map +1 -0
- package/dist/services/logging/raw-log.d.ts +6 -0
- package/dist/services/logging/raw-log.js +37 -0
- package/dist/services/logging/raw-log.js.map +1 -0
- package/dist/services/process/index.js +1 -1
- package/dist/services/process/index.js.map +1 -1
- package/dist/types/agent.d.ts +15 -0
- package/dist/types/config.d.ts +22 -1
- package/dist/types/event-categories.d.ts +601 -0
- package/dist/types/event-categories.js +233 -0
- package/dist/types/event-categories.js.map +1 -0
- package/dist/types/events.d.ts +0 -20
- package/dist/types/flow.d.ts +10 -6
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.js +17 -3
- package/dist/types/index.js.map +1 -1
- package/dist/types/lane.d.ts +1 -1
- package/dist/types/logging.d.ts +1 -1
- package/dist/types/task.d.ts +12 -1
- package/dist/ui/log-viewer.d.ts +3 -0
- package/dist/ui/log-viewer.js +3 -0
- package/dist/ui/log-viewer.js.map +1 -1
- package/dist/utils/config.js +10 -1
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/cursor-agent.d.ts +11 -1
- package/dist/utils/cursor-agent.js +63 -16
- package/dist/utils/cursor-agent.js.map +1 -1
- package/dist/utils/enhanced-logger.d.ts +5 -1
- package/dist/utils/enhanced-logger.js +98 -19
- package/dist/utils/enhanced-logger.js.map +1 -1
- package/dist/utils/event-registry.d.ts +222 -0
- package/dist/utils/event-registry.js +463 -0
- package/dist/utils/event-registry.js.map +1 -0
- package/dist/utils/events.d.ts +1 -13
- package/dist/utils/events.js.map +1 -1
- package/dist/utils/flow.d.ts +10 -0
- package/dist/utils/flow.js +75 -0
- package/dist/utils/flow.js.map +1 -1
- package/dist/utils/log-constants.d.ts +1 -0
- package/dist/utils/log-constants.js +2 -1
- package/dist/utils/log-constants.js.map +1 -1
- package/dist/utils/log-formatter.d.ts +2 -1
- package/dist/utils/log-formatter.js +10 -10
- package/dist/utils/log-formatter.js.map +1 -1
- package/dist/utils/logger.d.ts +11 -0
- package/dist/utils/logger.js +82 -3
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/repro-thinking-logs.js +0 -13
- package/dist/utils/repro-thinking-logs.js.map +1 -1
- package/dist/utils/run-service.js +1 -1
- package/dist/utils/run-service.js.map +1 -1
- package/examples/README.md +0 -2
- package/examples/demo-project/README.md +1 -2
- package/package.json +18 -28
- package/scripts/setup-security.sh +0 -1
- package/scripts/test-log-parser.ts +171 -0
- package/scripts/verify-change.sh +272 -0
- package/src/cli/logs.ts +121 -10
- package/src/cli/models.ts +20 -3
- package/src/cli/monitor.ts +1257 -1342
- package/src/cli/resume.ts +27 -1
- package/src/cli/run.ts +29 -11
- package/src/cli/signal.ts +115 -17
- package/src/cli/tasks.ts +2 -59
- package/src/core/agent-supervisor.ts +64 -0
- package/src/core/auto-recovery.ts +7 -1
- package/src/core/failure-policy.ts +0 -1
- package/src/core/git-lifecycle-manager.ts +1011 -0
- package/src/core/git-pipeline-coordinator.ts +221 -0
- package/src/core/intervention.ts +481 -0
- package/src/core/lane-state-machine.ts +1097 -0
- package/src/core/orchestrator.ts +35 -61
- package/src/core/runner/agent.ts +66 -33
- package/src/core/runner/pipeline.ts +318 -138
- package/src/core/runner/task.ts +12 -97
- package/src/core/runner.ts +8 -2
- package/src/core/stall-detection.ts +72 -27
- package/src/hooks/contexts/index.ts +256 -0
- package/src/hooks/data-accessor.ts +488 -0
- package/src/hooks/flow-controller.ts +425 -0
- package/src/hooks/index.ts +154 -0
- package/src/hooks/manager.ts +434 -0
- package/src/hooks/types.ts +544 -0
- package/src/services/logging/buffer.ts +104 -43
- package/src/services/logging/console.ts +7 -1
- package/src/services/logging/formatter.ts +74 -18
- package/src/services/logging/index.ts +0 -2
- package/src/services/logging/paths.ts +14 -0
- package/src/services/logging/raw-log.ts +43 -0
- package/src/services/process/index.ts +1 -1
- package/src/types/agent.ts +15 -0
- package/src/types/config.ts +23 -1
- package/src/types/event-categories.ts +663 -0
- package/src/types/events.ts +0 -25
- package/src/types/flow.ts +10 -6
- package/src/types/index.ts +50 -4
- package/src/types/lane.ts +1 -2
- package/src/types/logging.ts +2 -1
- package/src/types/task.ts +12 -1
- package/src/ui/log-viewer.ts +3 -0
- package/src/utils/config.ts +11 -1
- package/src/utils/cursor-agent.ts +68 -16
- package/src/utils/enhanced-logger.ts +105 -19
- package/src/utils/event-registry.ts +595 -0
- package/src/utils/events.ts +0 -16
- package/src/utils/flow.ts +83 -0
- package/src/utils/log-constants.ts +2 -1
- package/src/utils/log-formatter.ts +10 -11
- package/src/utils/logger.ts +49 -3
- package/src/utils/repro-thinking-logs.ts +0 -15
- package/src/utils/run-service.ts +1 -1
- package/dist/services/logging/file-writer.d.ts +0 -71
- package/dist/services/logging/file-writer.js +0 -516
- package/dist/services/logging/file-writer.js.map +0 -1
- package/dist/types/review.d.ts +0 -17
- package/dist/types/review.js +0 -6
- package/dist/types/review.js.map +0 -1
- package/scripts/ai-security-check.js +0 -233
- package/src/services/logging/file-writer.ts +0 -526
- package/src/types/review.ts +0 -20
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CursorFlow Hook System - Data Accessor Implementation
|
|
3
|
+
*
|
|
4
|
+
* Hook에서 Git, 대화 기록, 로그 등의 데이터에 접근하기 위한 구현체입니다.
|
|
5
|
+
* 모든 메서드는 Lazy Loading으로 필요할 때만 데이터를 로드합니다.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as fs from 'fs';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
import * as git from '../utils/git';
|
|
11
|
+
import { safeJoin } from '../utils/path';
|
|
12
|
+
import {
|
|
13
|
+
HookDataAccessor,
|
|
14
|
+
ChangedFile,
|
|
15
|
+
Commit,
|
|
16
|
+
Message,
|
|
17
|
+
TaskResult,
|
|
18
|
+
TaskDefinition,
|
|
19
|
+
ToolCall,
|
|
20
|
+
ErrorLog,
|
|
21
|
+
DependencyResult,
|
|
22
|
+
} from './types';
|
|
23
|
+
|
|
24
|
+
// ============================================================================
|
|
25
|
+
// Data Accessor Options
|
|
26
|
+
// ============================================================================
|
|
27
|
+
|
|
28
|
+
export interface DataAccessorOptions {
|
|
29
|
+
/** Worktree 디렉토리 */
|
|
30
|
+
worktreeDir: string;
|
|
31
|
+
/** Run 디렉토리 (로그 파일 위치) */
|
|
32
|
+
runDir: string;
|
|
33
|
+
/** 현재 태스크 브랜치 */
|
|
34
|
+
taskBranch: string;
|
|
35
|
+
/** 파이프라인 브랜치 */
|
|
36
|
+
pipelineBranch: string;
|
|
37
|
+
/** Lane 이름 */
|
|
38
|
+
laneName: string;
|
|
39
|
+
/** 현재 태스크 이름 */
|
|
40
|
+
taskName: string;
|
|
41
|
+
/** 완료된 태스크 목록 */
|
|
42
|
+
completedTasks: TaskResult[];
|
|
43
|
+
/** 남은 태스크 목록 */
|
|
44
|
+
pendingTasks: TaskDefinition[];
|
|
45
|
+
/** 의존성 결과 */
|
|
46
|
+
dependencyResults: DependencyResult[];
|
|
47
|
+
/** 태스크 시작 시간 */
|
|
48
|
+
taskStartTime: number;
|
|
49
|
+
/** Lane 시작 시간 */
|
|
50
|
+
laneStartTime: number;
|
|
51
|
+
/** Run Root (lanes 디렉토리의 부모) */
|
|
52
|
+
runRoot?: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ============================================================================
|
|
56
|
+
// Data Accessor Implementation
|
|
57
|
+
// ============================================================================
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* HookDataAccessor 구현체
|
|
61
|
+
*/
|
|
62
|
+
export class HookDataAccessorImpl implements HookDataAccessor {
|
|
63
|
+
private options: DataAccessorOptions;
|
|
64
|
+
|
|
65
|
+
// 캐시
|
|
66
|
+
private cachedChangedFiles: ChangedFile[] | null = null;
|
|
67
|
+
private cachedDiff: string | null = null;
|
|
68
|
+
private cachedMessages: Message[] | null = null;
|
|
69
|
+
private cachedToolCalls: ToolCall[] | null = null;
|
|
70
|
+
private cachedRawOutput: string | null = null;
|
|
71
|
+
|
|
72
|
+
constructor(options: DataAccessorOptions) {
|
|
73
|
+
this.options = options;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ==========================================================================
|
|
77
|
+
// Git Data Access
|
|
78
|
+
// ==========================================================================
|
|
79
|
+
|
|
80
|
+
git = {
|
|
81
|
+
getChangedFiles: async (): Promise<ChangedFile[]> => {
|
|
82
|
+
if (this.cachedChangedFiles) {
|
|
83
|
+
return this.cachedChangedFiles;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
const { worktreeDir, pipelineBranch, taskBranch } = this.options;
|
|
88
|
+
|
|
89
|
+
// Get diff stat between pipeline and task branch
|
|
90
|
+
const diffStat = git.runGit(
|
|
91
|
+
['diff', '--numstat', pipelineBranch, taskBranch],
|
|
92
|
+
{ cwd: worktreeDir, silent: true }
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
const files: ChangedFile[] = [];
|
|
96
|
+
|
|
97
|
+
if (diffStat) {
|
|
98
|
+
const lines = diffStat.trim().split('\n').filter(l => l);
|
|
99
|
+
|
|
100
|
+
for (const line of lines) {
|
|
101
|
+
const [additions, deletions, filePath] = line.split('\t');
|
|
102
|
+
if (!filePath) continue;
|
|
103
|
+
|
|
104
|
+
// Get file status
|
|
105
|
+
let status: ChangedFile['status'] = 'modified';
|
|
106
|
+
try {
|
|
107
|
+
const statusOutput = git.runGit(
|
|
108
|
+
['diff', '--name-status', pipelineBranch, taskBranch, '--', filePath],
|
|
109
|
+
{ cwd: worktreeDir, silent: true }
|
|
110
|
+
);
|
|
111
|
+
if (statusOutput) {
|
|
112
|
+
const statusChar = statusOutput.charAt(0);
|
|
113
|
+
if (statusChar === 'A') status = 'added';
|
|
114
|
+
else if (statusChar === 'D') status = 'deleted';
|
|
115
|
+
else if (statusChar === 'R') status = 'renamed';
|
|
116
|
+
}
|
|
117
|
+
} catch {
|
|
118
|
+
// Ignore status detection errors
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
files.push({
|
|
122
|
+
path: filePath,
|
|
123
|
+
status,
|
|
124
|
+
additions: additions === '-' ? 0 : parseInt(additions, 10) || 0,
|
|
125
|
+
deletions: deletions === '-' ? 0 : parseInt(deletions, 10) || 0,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
this.cachedChangedFiles = files;
|
|
131
|
+
return files;
|
|
132
|
+
} catch (error) {
|
|
133
|
+
console.error('Failed to get changed files:', error);
|
|
134
|
+
return [];
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
getDiff: async (): Promise<string> => {
|
|
139
|
+
if (this.cachedDiff !== null) {
|
|
140
|
+
return this.cachedDiff;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
const { worktreeDir, pipelineBranch, taskBranch } = this.options;
|
|
145
|
+
|
|
146
|
+
const diff = git.runGit(
|
|
147
|
+
['diff', pipelineBranch, taskBranch],
|
|
148
|
+
{ cwd: worktreeDir, silent: true }
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
this.cachedDiff = diff || '';
|
|
152
|
+
return this.cachedDiff;
|
|
153
|
+
} catch (error) {
|
|
154
|
+
console.error('Failed to get diff:', error);
|
|
155
|
+
return '';
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
getRecentCommits: async (count: number = 10): Promise<Commit[]> => {
|
|
160
|
+
try {
|
|
161
|
+
const { worktreeDir } = this.options;
|
|
162
|
+
|
|
163
|
+
// Get commit log with format
|
|
164
|
+
const logOutput = git.runGit(
|
|
165
|
+
['log', `-${count}`, '--pretty=format:%H|%s|%an|%aI', '--name-only'],
|
|
166
|
+
{ cwd: worktreeDir, silent: true }
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
if (!logOutput) return [];
|
|
170
|
+
|
|
171
|
+
const commits: Commit[] = [];
|
|
172
|
+
const entries = logOutput.split('\n\n').filter(e => e.trim());
|
|
173
|
+
|
|
174
|
+
for (const entry of entries) {
|
|
175
|
+
const lines = entry.trim().split('\n');
|
|
176
|
+
if (lines.length === 0) continue;
|
|
177
|
+
|
|
178
|
+
const [firstLine, ...fileLines] = lines;
|
|
179
|
+
const [hash, message, author, date] = firstLine.split('|');
|
|
180
|
+
|
|
181
|
+
if (hash && message && author && date) {
|
|
182
|
+
commits.push({
|
|
183
|
+
hash,
|
|
184
|
+
message,
|
|
185
|
+
author,
|
|
186
|
+
date,
|
|
187
|
+
files: fileLines.filter(f => f.trim()),
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return commits;
|
|
193
|
+
} catch (error) {
|
|
194
|
+
console.error('Failed to get recent commits:', error);
|
|
195
|
+
return [];
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
|
|
199
|
+
getCurrentBranch: (): string => {
|
|
200
|
+
return this.options.taskBranch;
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
getConflictFiles: async (): Promise<string[]> => {
|
|
204
|
+
try {
|
|
205
|
+
const { worktreeDir } = this.options;
|
|
206
|
+
|
|
207
|
+
// Check for merge conflicts
|
|
208
|
+
const statusOutput = git.runGit(
|
|
209
|
+
['status', '--porcelain'],
|
|
210
|
+
{ cwd: worktreeDir, silent: true }
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
if (!statusOutput) return [];
|
|
214
|
+
|
|
215
|
+
const conflictFiles: string[] = [];
|
|
216
|
+
const lines = statusOutput.split('\n');
|
|
217
|
+
|
|
218
|
+
for (const line of lines) {
|
|
219
|
+
// UU = both modified (conflict)
|
|
220
|
+
// AA = both added (conflict)
|
|
221
|
+
if (line.startsWith('UU ') || line.startsWith('AA ')) {
|
|
222
|
+
conflictFiles.push(line.substring(3).trim());
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return conflictFiles;
|
|
227
|
+
} catch (error) {
|
|
228
|
+
console.error('Failed to get conflict files:', error);
|
|
229
|
+
return [];
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
// ==========================================================================
|
|
235
|
+
// Conversation Data Access
|
|
236
|
+
// ==========================================================================
|
|
237
|
+
|
|
238
|
+
conversation = {
|
|
239
|
+
getCurrentTaskMessages: async (): Promise<Message[]> => {
|
|
240
|
+
const allMessages = await this.conversation.getAllMessages();
|
|
241
|
+
return allMessages.filter(m => m.taskName === this.options.taskName);
|
|
242
|
+
},
|
|
243
|
+
|
|
244
|
+
getAllMessages: async (): Promise<Message[]> => {
|
|
245
|
+
if (this.cachedMessages) {
|
|
246
|
+
return this.cachedMessages;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
try {
|
|
250
|
+
const convoPath = safeJoin(this.options.runDir, 'conversation.jsonl');
|
|
251
|
+
|
|
252
|
+
if (!fs.existsSync(convoPath)) {
|
|
253
|
+
return [];
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const content = fs.readFileSync(convoPath, 'utf8');
|
|
257
|
+
const lines = content.split('\n').filter(l => l.trim());
|
|
258
|
+
|
|
259
|
+
const messages: Message[] = [];
|
|
260
|
+
|
|
261
|
+
for (const line of lines) {
|
|
262
|
+
try {
|
|
263
|
+
const entry = JSON.parse(line);
|
|
264
|
+
messages.push({
|
|
265
|
+
role: entry.role || 'user',
|
|
266
|
+
content: entry.content || '',
|
|
267
|
+
timestamp: entry.timestamp || new Date().toISOString(),
|
|
268
|
+
taskName: entry.task || entry.taskName,
|
|
269
|
+
metadata: entry.metadata,
|
|
270
|
+
});
|
|
271
|
+
} catch {
|
|
272
|
+
// Skip invalid JSON lines
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
this.cachedMessages = messages;
|
|
277
|
+
return messages;
|
|
278
|
+
} catch (error) {
|
|
279
|
+
console.error('Failed to get messages:', error);
|
|
280
|
+
return [];
|
|
281
|
+
}
|
|
282
|
+
},
|
|
283
|
+
|
|
284
|
+
getRecentMessages: async (count: number = 10): Promise<Message[]> => {
|
|
285
|
+
const allMessages = await this.conversation.getAllMessages();
|
|
286
|
+
return allMessages.slice(-count);
|
|
287
|
+
},
|
|
288
|
+
|
|
289
|
+
getLastResponse: async (): Promise<string | null> => {
|
|
290
|
+
const allMessages = await this.conversation.getAllMessages();
|
|
291
|
+
|
|
292
|
+
// Find last assistant message
|
|
293
|
+
for (let i = allMessages.length - 1; i >= 0; i--) {
|
|
294
|
+
if (allMessages[i].role === 'assistant') {
|
|
295
|
+
return allMessages[i].content;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return null;
|
|
300
|
+
},
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
// ==========================================================================
|
|
304
|
+
// Tasks Data Access
|
|
305
|
+
// ==========================================================================
|
|
306
|
+
|
|
307
|
+
tasks = {
|
|
308
|
+
getCompletedTasks: (): TaskResult[] => {
|
|
309
|
+
return this.options.completedTasks;
|
|
310
|
+
},
|
|
311
|
+
|
|
312
|
+
getPendingTasks: (): TaskDefinition[] => {
|
|
313
|
+
return this.options.pendingTasks;
|
|
314
|
+
},
|
|
315
|
+
|
|
316
|
+
getTaskResult: (taskName: string): TaskResult | null => {
|
|
317
|
+
return this.options.completedTasks.find(t => t.name === taskName) || null;
|
|
318
|
+
},
|
|
319
|
+
|
|
320
|
+
getDependencyResults: (): DependencyResult[] => {
|
|
321
|
+
return this.options.dependencyResults;
|
|
322
|
+
},
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
// ==========================================================================
|
|
326
|
+
// Logs Data Access
|
|
327
|
+
// ==========================================================================
|
|
328
|
+
|
|
329
|
+
logs = {
|
|
330
|
+
getRawOutput: async (): Promise<string> => {
|
|
331
|
+
if (this.cachedRawOutput !== null) {
|
|
332
|
+
return this.cachedRawOutput;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
try {
|
|
336
|
+
// Try multiple possible log file locations
|
|
337
|
+
const possiblePaths = [
|
|
338
|
+
safeJoin(this.options.runDir, 'terminal-raw.log'),
|
|
339
|
+
safeJoin(this.options.runDir, 'terminal.log'),
|
|
340
|
+
safeJoin(this.options.runDir, 'agent-output.log'),
|
|
341
|
+
];
|
|
342
|
+
|
|
343
|
+
for (const logPath of possiblePaths) {
|
|
344
|
+
if (fs.existsSync(logPath)) {
|
|
345
|
+
const content = fs.readFileSync(logPath, 'utf8');
|
|
346
|
+
this.cachedRawOutput = content;
|
|
347
|
+
return content;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return '';
|
|
352
|
+
} catch (error) {
|
|
353
|
+
console.error('Failed to get raw output:', error);
|
|
354
|
+
return '';
|
|
355
|
+
}
|
|
356
|
+
},
|
|
357
|
+
|
|
358
|
+
getToolCalls: async (): Promise<ToolCall[]> => {
|
|
359
|
+
if (this.cachedToolCalls) {
|
|
360
|
+
return this.cachedToolCalls;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
try {
|
|
364
|
+
const rawOutput = await this.logs.getRawOutput();
|
|
365
|
+
const toolCalls: ToolCall[] = [];
|
|
366
|
+
|
|
367
|
+
// Parse tool calls from JSONL output
|
|
368
|
+
const lines = rawOutput.split('\n');
|
|
369
|
+
|
|
370
|
+
for (const line of lines) {
|
|
371
|
+
if (!line.trim().startsWith('{')) continue;
|
|
372
|
+
|
|
373
|
+
try {
|
|
374
|
+
const parsed = JSON.parse(line);
|
|
375
|
+
|
|
376
|
+
// Check for tool_call type messages
|
|
377
|
+
if (parsed.type === 'tool_call' || parsed.tool_calls) {
|
|
378
|
+
const calls = parsed.tool_calls || [parsed];
|
|
379
|
+
|
|
380
|
+
for (const call of calls) {
|
|
381
|
+
if (call.name || call.function?.name) {
|
|
382
|
+
toolCalls.push({
|
|
383
|
+
name: call.name || call.function?.name,
|
|
384
|
+
parameters: call.parameters || call.function?.arguments || {},
|
|
385
|
+
result: call.result,
|
|
386
|
+
timestamp: parsed.timestamp || new Date().toISOString(),
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
} catch {
|
|
392
|
+
// Skip non-JSON lines
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
this.cachedToolCalls = toolCalls;
|
|
397
|
+
return toolCalls;
|
|
398
|
+
} catch (error) {
|
|
399
|
+
console.error('Failed to get tool calls:', error);
|
|
400
|
+
return [];
|
|
401
|
+
}
|
|
402
|
+
},
|
|
403
|
+
|
|
404
|
+
getErrors: async (): Promise<ErrorLog[]> => {
|
|
405
|
+
try {
|
|
406
|
+
const rawOutput = await this.logs.getRawOutput();
|
|
407
|
+
const errors: ErrorLog[] = [];
|
|
408
|
+
|
|
409
|
+
const lines = rawOutput.split('\n');
|
|
410
|
+
|
|
411
|
+
for (const line of lines) {
|
|
412
|
+
// Check for error patterns
|
|
413
|
+
const lowerLine = line.toLowerCase();
|
|
414
|
+
|
|
415
|
+
if (lowerLine.includes('error') || lowerLine.includes('exception') || lowerLine.includes('failed')) {
|
|
416
|
+
errors.push({
|
|
417
|
+
level: 'error',
|
|
418
|
+
message: line.trim(),
|
|
419
|
+
timestamp: new Date().toISOString(),
|
|
420
|
+
});
|
|
421
|
+
} else if (lowerLine.includes('warning') || lowerLine.includes('warn')) {
|
|
422
|
+
errors.push({
|
|
423
|
+
level: 'warn',
|
|
424
|
+
message: line.trim(),
|
|
425
|
+
timestamp: new Date().toISOString(),
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
return errors;
|
|
431
|
+
} catch (error) {
|
|
432
|
+
console.error('Failed to get errors:', error);
|
|
433
|
+
return [];
|
|
434
|
+
}
|
|
435
|
+
},
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
// ==========================================================================
|
|
439
|
+
// Timing Data Access
|
|
440
|
+
// ==========================================================================
|
|
441
|
+
|
|
442
|
+
get timing() {
|
|
443
|
+
const options = this.options;
|
|
444
|
+
return {
|
|
445
|
+
taskStartTime: options.taskStartTime,
|
|
446
|
+
laneStartTime: options.laneStartTime,
|
|
447
|
+
|
|
448
|
+
getElapsedTime: (): number => {
|
|
449
|
+
return Date.now() - options.taskStartTime;
|
|
450
|
+
},
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// ==========================================================================
|
|
455
|
+
// Cache Management
|
|
456
|
+
// ==========================================================================
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* 캐시 초기화
|
|
460
|
+
*/
|
|
461
|
+
clearCache(): void {
|
|
462
|
+
this.cachedChangedFiles = null;
|
|
463
|
+
this.cachedDiff = null;
|
|
464
|
+
this.cachedMessages = null;
|
|
465
|
+
this.cachedToolCalls = null;
|
|
466
|
+
this.cachedRawOutput = null;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* 옵션 업데이트 (태스크 변경 시)
|
|
471
|
+
*/
|
|
472
|
+
updateOptions(updates: Partial<DataAccessorOptions>): void {
|
|
473
|
+
this.options = { ...this.options, ...updates };
|
|
474
|
+
this.clearCache();
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// ============================================================================
|
|
479
|
+
// Factory Function
|
|
480
|
+
// ============================================================================
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* HookDataAccessor 인스턴스 생성
|
|
484
|
+
*/
|
|
485
|
+
export function createDataAccessor(options: DataAccessorOptions): HookDataAccessor {
|
|
486
|
+
return new HookDataAccessorImpl(options);
|
|
487
|
+
}
|
|
488
|
+
|