ai-sdlc 0.2.0-alpha.6 → 0.2.0-alpha.61
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 +65 -1057
- package/dist/agents/implementation.d.ts +36 -1
- package/dist/agents/implementation.d.ts.map +1 -1
- package/dist/agents/implementation.js +259 -30
- package/dist/agents/implementation.js.map +1 -1
- package/dist/agents/index.d.ts +2 -0
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/index.js +2 -0
- package/dist/agents/index.js.map +1 -1
- package/dist/agents/orchestrator.d.ts +61 -0
- package/dist/agents/orchestrator.d.ts.map +1 -0
- package/dist/agents/orchestrator.js +443 -0
- package/dist/agents/orchestrator.js.map +1 -0
- package/dist/agents/planning.d.ts +1 -1
- package/dist/agents/planning.d.ts.map +1 -1
- package/dist/agents/planning.js +55 -4
- package/dist/agents/planning.js.map +1 -1
- package/dist/agents/refinement.d.ts.map +1 -1
- package/dist/agents/refinement.js +22 -3
- package/dist/agents/refinement.js.map +1 -1
- package/dist/agents/research.d.ts +85 -1
- package/dist/agents/research.d.ts.map +1 -1
- package/dist/agents/research.js +506 -16
- package/dist/agents/research.js.map +1 -1
- package/dist/agents/review.d.ts +116 -2
- package/dist/agents/review.d.ts.map +1 -1
- package/dist/agents/review.js +847 -93
- package/dist/agents/review.js.map +1 -1
- package/dist/agents/rework.d.ts.map +1 -1
- package/dist/agents/rework.js +25 -4
- package/dist/agents/rework.js.map +1 -1
- package/dist/agents/single-task.d.ts +41 -0
- package/dist/agents/single-task.d.ts.map +1 -0
- package/dist/agents/single-task.js +357 -0
- package/dist/agents/single-task.js.map +1 -0
- package/dist/agents/state-assessor.d.ts +3 -3
- package/dist/agents/state-assessor.d.ts.map +1 -1
- package/dist/agents/state-assessor.js +6 -6
- package/dist/agents/state-assessor.js.map +1 -1
- package/dist/agents/test-pattern-detector.d.ts +49 -0
- package/dist/agents/test-pattern-detector.d.ts.map +1 -0
- package/dist/agents/test-pattern-detector.js +273 -0
- package/dist/agents/test-pattern-detector.js.map +1 -0
- package/dist/agents/verification.d.ts +11 -0
- package/dist/agents/verification.d.ts.map +1 -1
- package/dist/agents/verification.js +99 -12
- package/dist/agents/verification.js.map +1 -1
- package/dist/cli/batch-processor.d.ts +64 -0
- package/dist/cli/batch-processor.d.ts.map +1 -0
- package/dist/cli/batch-processor.js +85 -0
- package/dist/cli/batch-processor.js.map +1 -0
- package/dist/cli/batch-validator.d.ts +80 -0
- package/dist/cli/batch-validator.d.ts.map +1 -0
- package/dist/cli/batch-validator.js +121 -0
- package/dist/cli/batch-validator.js.map +1 -0
- package/dist/cli/commands/migrate.js +1 -1
- package/dist/cli/commands/migrate.js.map +1 -1
- package/dist/cli/commands.d.ts +67 -3
- package/dist/cli/commands.d.ts.map +1 -1
- package/dist/cli/commands.js +1765 -198
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli/daemon.d.ts.map +1 -1
- package/dist/cli/daemon.js +25 -3
- package/dist/cli/daemon.js.map +1 -1
- package/dist/cli/runner.d.ts.map +1 -1
- package/dist/cli/runner.js +35 -12
- package/dist/cli/runner.js.map +1 -1
- package/dist/core/auth.d.ts +43 -0
- package/dist/core/auth.d.ts.map +1 -1
- package/dist/core/auth.js +105 -1
- package/dist/core/auth.js.map +1 -1
- package/dist/core/client.d.ts +25 -1
- package/dist/core/client.d.ts.map +1 -1
- package/dist/core/client.js +247 -7
- package/dist/core/client.js.map +1 -1
- package/dist/core/config.d.ts +32 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +146 -3
- package/dist/core/config.js.map +1 -1
- package/dist/core/conflict-detector.d.ts +108 -0
- package/dist/core/conflict-detector.d.ts.map +1 -0
- package/dist/core/conflict-detector.js +413 -0
- package/dist/core/conflict-detector.js.map +1 -0
- package/dist/core/git-utils.d.ts +28 -0
- package/dist/core/git-utils.d.ts.map +1 -0
- package/dist/core/git-utils.js +146 -0
- package/dist/core/git-utils.js.map +1 -0
- package/dist/core/index.d.ts +19 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +19 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/kanban.d.ts +1 -1
- package/dist/core/kanban.d.ts.map +1 -1
- package/dist/core/kanban.js +3 -3
- package/dist/core/kanban.js.map +1 -1
- package/dist/core/llm-utils.d.ts +103 -0
- package/dist/core/llm-utils.d.ts.map +1 -0
- package/dist/core/llm-utils.js +368 -0
- package/dist/core/llm-utils.js.map +1 -0
- package/dist/core/logger.d.ts +92 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +221 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/process-manager.d.ts +15 -0
- package/dist/core/process-manager.d.ts.map +1 -0
- package/dist/core/process-manager.js +132 -0
- package/dist/core/process-manager.js.map +1 -0
- package/dist/core/story-logger.d.ts +102 -0
- package/dist/core/story-logger.d.ts.map +1 -0
- package/dist/core/story-logger.js +265 -0
- package/dist/core/story-logger.js.map +1 -0
- package/dist/core/story.d.ts +113 -20
- package/dist/core/story.d.ts.map +1 -1
- package/dist/core/story.js +328 -40
- package/dist/core/story.js.map +1 -1
- package/dist/core/task-parser.d.ts +59 -0
- package/dist/core/task-parser.d.ts.map +1 -0
- package/dist/core/task-parser.js +235 -0
- package/dist/core/task-parser.js.map +1 -0
- package/dist/core/task-progress.d.ts +92 -0
- package/dist/core/task-progress.d.ts.map +1 -0
- package/dist/core/task-progress.js +280 -0
- package/dist/core/task-progress.js.map +1 -0
- package/dist/core/workflow-state.d.ts +45 -6
- package/dist/core/workflow-state.d.ts.map +1 -1
- package/dist/core/workflow-state.js +201 -12
- package/dist/core/workflow-state.js.map +1 -1
- package/dist/core/worktree.d.ts +186 -0
- package/dist/core/worktree.d.ts.map +1 -0
- package/dist/core/worktree.js +554 -0
- package/dist/core/worktree.js.map +1 -0
- package/dist/index.js +146 -5
- package/dist/index.js.map +1 -1
- package/dist/services/error-classifier.d.ts +119 -0
- package/dist/services/error-classifier.d.ts.map +1 -0
- package/dist/services/error-classifier.js +182 -0
- package/dist/services/error-classifier.js.map +1 -0
- package/dist/types/index.d.ts +381 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -0
- package/dist/types/index.js.map +1 -1
- package/package.json +5 -2
- package/templates/story.md +5 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
/**
|
|
4
|
+
* Log level priority for filtering
|
|
5
|
+
*/
|
|
6
|
+
const LOG_LEVEL_PRIORITY = {
|
|
7
|
+
debug: 0,
|
|
8
|
+
info: 1,
|
|
9
|
+
warn: 2,
|
|
10
|
+
error: 3,
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Rolling file logger for ai-sdlc operations
|
|
14
|
+
*
|
|
15
|
+
* Features:
|
|
16
|
+
* - JSON Lines format (one JSON object per line)
|
|
17
|
+
* - Rolling by size (configurable, default 10MB)
|
|
18
|
+
* - Retains last N files (configurable, default 5)
|
|
19
|
+
* - Location: .ai-sdlc/logs/ai-sdlc-YYYY-MM-DD.log
|
|
20
|
+
*/
|
|
21
|
+
export class Logger {
|
|
22
|
+
projectRoot;
|
|
23
|
+
config;
|
|
24
|
+
logDir;
|
|
25
|
+
currentLogFile = null;
|
|
26
|
+
constructor(projectRoot, config) {
|
|
27
|
+
this.projectRoot = projectRoot;
|
|
28
|
+
this.config = config;
|
|
29
|
+
this.logDir = path.join(projectRoot, '.ai-sdlc', 'logs');
|
|
30
|
+
if (this.config.enabled) {
|
|
31
|
+
this.ensureLogDirectory();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Ensure the log directory exists
|
|
36
|
+
*/
|
|
37
|
+
ensureLogDirectory() {
|
|
38
|
+
if (!fs.existsSync(this.logDir)) {
|
|
39
|
+
fs.mkdirSync(this.logDir, { recursive: true });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Get the current log file path based on today's date
|
|
44
|
+
*/
|
|
45
|
+
getCurrentLogFile() {
|
|
46
|
+
const today = new Date().toISOString().split('T')[0]; // YYYY-MM-DD
|
|
47
|
+
return path.join(this.logDir, `ai-sdlc-${today}.log`);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Check if the current log file needs rotation based on size
|
|
51
|
+
*/
|
|
52
|
+
needsRotation(logFile) {
|
|
53
|
+
if (!fs.existsSync(logFile)) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
const stats = fs.statSync(logFile);
|
|
57
|
+
const maxSizeBytes = this.config.maxFileSizeMb * 1024 * 1024;
|
|
58
|
+
return stats.size >= maxSizeBytes;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Rotate the log file by renaming it with a sequence number
|
|
62
|
+
*/
|
|
63
|
+
rotateLogFile(logFile) {
|
|
64
|
+
const baseName = path.basename(logFile, '.log');
|
|
65
|
+
const dir = path.dirname(logFile);
|
|
66
|
+
// Find the next available sequence number
|
|
67
|
+
let seq = 1;
|
|
68
|
+
while (fs.existsSync(path.join(dir, `${baseName}.${seq}.log`))) {
|
|
69
|
+
seq++;
|
|
70
|
+
}
|
|
71
|
+
// Rename current file
|
|
72
|
+
fs.renameSync(logFile, path.join(dir, `${baseName}.${seq}.log`));
|
|
73
|
+
// Clean up old files
|
|
74
|
+
this.cleanupOldFiles();
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Remove old log files beyond the retention limit
|
|
78
|
+
*/
|
|
79
|
+
cleanupOldFiles() {
|
|
80
|
+
if (!fs.existsSync(this.logDir)) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const files = fs.readdirSync(this.logDir)
|
|
84
|
+
.filter(f => f.startsWith('ai-sdlc-') && f.endsWith('.log'))
|
|
85
|
+
.map(f => ({
|
|
86
|
+
name: f,
|
|
87
|
+
path: path.join(this.logDir, f),
|
|
88
|
+
mtime: fs.statSync(path.join(this.logDir, f)).mtime.getTime(),
|
|
89
|
+
}))
|
|
90
|
+
.sort((a, b) => b.mtime - a.mtime); // Newest first
|
|
91
|
+
// Keep only the configured number of files
|
|
92
|
+
const filesToDelete = files.slice(this.config.maxFiles);
|
|
93
|
+
for (const file of filesToDelete) {
|
|
94
|
+
try {
|
|
95
|
+
fs.unlinkSync(file.path);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
// Ignore deletion errors
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Check if the given level should be logged based on config
|
|
104
|
+
*/
|
|
105
|
+
shouldLog(level) {
|
|
106
|
+
if (!this.config.enabled) {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
return LOG_LEVEL_PRIORITY[level] >= LOG_LEVEL_PRIORITY[this.config.level];
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Write a log entry to the current log file
|
|
113
|
+
*/
|
|
114
|
+
write(entry) {
|
|
115
|
+
if (!this.config.enabled) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const logFile = this.getCurrentLogFile();
|
|
119
|
+
// Check for rotation
|
|
120
|
+
if (this.needsRotation(logFile)) {
|
|
121
|
+
this.rotateLogFile(logFile);
|
|
122
|
+
}
|
|
123
|
+
// Append log entry as JSON line
|
|
124
|
+
const line = JSON.stringify(entry) + '\n';
|
|
125
|
+
fs.appendFileSync(logFile, line, 'utf-8');
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Create a log entry with common fields
|
|
129
|
+
*/
|
|
130
|
+
createEntry(level, category, message, data) {
|
|
131
|
+
const entry = {
|
|
132
|
+
timestamp: new Date().toISOString(),
|
|
133
|
+
level,
|
|
134
|
+
category,
|
|
135
|
+
message,
|
|
136
|
+
};
|
|
137
|
+
if (data !== undefined) {
|
|
138
|
+
entry.data = data;
|
|
139
|
+
}
|
|
140
|
+
return entry;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Log a debug message
|
|
144
|
+
*/
|
|
145
|
+
debug(category, message, data) {
|
|
146
|
+
if (this.shouldLog('debug')) {
|
|
147
|
+
this.write(this.createEntry('debug', category, message, data));
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Log an info message
|
|
152
|
+
*/
|
|
153
|
+
info(category, message, data) {
|
|
154
|
+
if (this.shouldLog('info')) {
|
|
155
|
+
this.write(this.createEntry('info', category, message, data));
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Log a warning message
|
|
160
|
+
*/
|
|
161
|
+
warn(category, message, data) {
|
|
162
|
+
if (this.shouldLog('warn')) {
|
|
163
|
+
this.write(this.createEntry('warn', category, message, data));
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Log an error message
|
|
168
|
+
*/
|
|
169
|
+
error(category, message, data) {
|
|
170
|
+
if (this.shouldLog('error')) {
|
|
171
|
+
this.write(this.createEntry('error', category, message, data));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Get the path to the current log file
|
|
176
|
+
*/
|
|
177
|
+
getLogFilePath() {
|
|
178
|
+
return this.getCurrentLogFile();
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Get all log file paths
|
|
182
|
+
*/
|
|
183
|
+
getLogFiles() {
|
|
184
|
+
if (!fs.existsSync(this.logDir)) {
|
|
185
|
+
return [];
|
|
186
|
+
}
|
|
187
|
+
return fs.readdirSync(this.logDir)
|
|
188
|
+
.filter(f => f.startsWith('ai-sdlc-') && f.endsWith('.log'))
|
|
189
|
+
.map(f => path.join(this.logDir, f))
|
|
190
|
+
.sort((a, b) => {
|
|
191
|
+
const statA = fs.statSync(a);
|
|
192
|
+
const statB = fs.statSync(b);
|
|
193
|
+
return statB.mtime.getTime() - statA.mtime.getTime();
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
// Singleton logger instance
|
|
198
|
+
let globalLogger = null;
|
|
199
|
+
/**
|
|
200
|
+
* Initialize the global logger instance
|
|
201
|
+
*/
|
|
202
|
+
export function initLogger(projectRoot, config) {
|
|
203
|
+
globalLogger = new Logger(projectRoot, config);
|
|
204
|
+
return globalLogger;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Get the global logger instance (or create a disabled one if not initialized)
|
|
208
|
+
*/
|
|
209
|
+
export function getLogger() {
|
|
210
|
+
if (!globalLogger) {
|
|
211
|
+
// Return a disabled logger if not initialized
|
|
212
|
+
globalLogger = new Logger(process.cwd(), {
|
|
213
|
+
enabled: false,
|
|
214
|
+
level: 'info',
|
|
215
|
+
maxFileSizeMb: 10,
|
|
216
|
+
maxFiles: 5,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
return globalLogger;
|
|
220
|
+
}
|
|
221
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/core/logger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ChildProcess } from 'child_process';
|
|
2
|
+
export declare class ProcessManager {
|
|
3
|
+
private static instance;
|
|
4
|
+
private children;
|
|
5
|
+
private isCleaningUp;
|
|
6
|
+
private constructor();
|
|
7
|
+
static getInstance(): ProcessManager;
|
|
8
|
+
static resetInstance(): void;
|
|
9
|
+
registerChild(child: ChildProcess): void;
|
|
10
|
+
getTrackedCount(): number;
|
|
11
|
+
killAll(signal?: NodeJS.Signals): void;
|
|
12
|
+
killAllWithTimeout(gracefulTimeoutMs?: number): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
export declare function setupGlobalCleanupHandlers(): void;
|
|
15
|
+
//# sourceMappingURL=process-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-manager.d.ts","sourceRoot":"","sources":["../../src/core/process-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA+B;IACtD,OAAO,CAAC,QAAQ,CAAgC;IAChD,OAAO,CAAC,YAAY,CAAkB;IAEtC,OAAO;IAEP,MAAM,CAAC,WAAW,IAAI,cAAc;IAOpC,MAAM,CAAC,aAAa,IAAI,IAAI;IAQ5B,aAAa,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI;IAcxC,eAAe,IAAI,MAAM;IAIzB,OAAO,CAAC,MAAM,GAAE,MAAM,CAAC,OAAmB,GAAG,IAAI;IAiB3C,kBAAkB,CAAC,iBAAiB,GAAE,MAAa,GAAG,OAAO,CAAC,IAAI,CAAC;CAwD1E;AAED,wBAAgB,0BAA0B,IAAI,IAAI,CAkCjD"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
export class ProcessManager {
|
|
2
|
+
static instance = null;
|
|
3
|
+
children = new Set();
|
|
4
|
+
isCleaningUp = false;
|
|
5
|
+
constructor() { }
|
|
6
|
+
static getInstance() {
|
|
7
|
+
if (!ProcessManager.instance) {
|
|
8
|
+
ProcessManager.instance = new ProcessManager();
|
|
9
|
+
}
|
|
10
|
+
return ProcessManager.instance;
|
|
11
|
+
}
|
|
12
|
+
static resetInstance() {
|
|
13
|
+
if (ProcessManager.instance) {
|
|
14
|
+
ProcessManager.instance.children.clear();
|
|
15
|
+
ProcessManager.instance.isCleaningUp = false;
|
|
16
|
+
}
|
|
17
|
+
ProcessManager.instance = null;
|
|
18
|
+
}
|
|
19
|
+
registerChild(child) {
|
|
20
|
+
if (!child || !child.pid)
|
|
21
|
+
return;
|
|
22
|
+
this.children.add(child);
|
|
23
|
+
const cleanup = () => {
|
|
24
|
+
this.children.delete(child);
|
|
25
|
+
};
|
|
26
|
+
child.once('exit', cleanup);
|
|
27
|
+
child.once('error', cleanup);
|
|
28
|
+
child.once('close', cleanup);
|
|
29
|
+
}
|
|
30
|
+
getTrackedCount() {
|
|
31
|
+
return this.children.size;
|
|
32
|
+
}
|
|
33
|
+
killAll(signal = 'SIGTERM') {
|
|
34
|
+
if (this.children.size === 0)
|
|
35
|
+
return;
|
|
36
|
+
const childrenToKill = Array.from(this.children);
|
|
37
|
+
for (const child of childrenToKill) {
|
|
38
|
+
if (child.pid && !child.killed) {
|
|
39
|
+
try {
|
|
40
|
+
child.kill(signal);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// Process may have already exited - ignore ESRCH errors
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
this.children.delete(child);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async killAllWithTimeout(gracefulTimeoutMs = 5000) {
|
|
50
|
+
if (this.children.size === 0)
|
|
51
|
+
return;
|
|
52
|
+
if (this.isCleaningUp)
|
|
53
|
+
return;
|
|
54
|
+
this.isCleaningUp = true;
|
|
55
|
+
try {
|
|
56
|
+
const childrenToKill = Array.from(this.children);
|
|
57
|
+
const killPromises = childrenToKill.map((child) => {
|
|
58
|
+
return new Promise((resolve) => {
|
|
59
|
+
if (!child.pid || child.killed) {
|
|
60
|
+
this.children.delete(child);
|
|
61
|
+
resolve();
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
let resolved = false;
|
|
65
|
+
const onExit = () => {
|
|
66
|
+
if (!resolved) {
|
|
67
|
+
resolved = true;
|
|
68
|
+
this.children.delete(child);
|
|
69
|
+
resolve();
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
child.once('exit', onExit);
|
|
73
|
+
child.once('close', onExit);
|
|
74
|
+
try {
|
|
75
|
+
child.kill('SIGTERM');
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// Ignore errors
|
|
79
|
+
}
|
|
80
|
+
setTimeout(() => {
|
|
81
|
+
if (!resolved) {
|
|
82
|
+
try {
|
|
83
|
+
child.kill('SIGKILL');
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// Ignore errors
|
|
87
|
+
}
|
|
88
|
+
resolved = true;
|
|
89
|
+
this.children.delete(child);
|
|
90
|
+
resolve();
|
|
91
|
+
}
|
|
92
|
+
}, gracefulTimeoutMs);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
await Promise.all(killPromises);
|
|
96
|
+
}
|
|
97
|
+
finally {
|
|
98
|
+
this.isCleaningUp = false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
export function setupGlobalCleanupHandlers() {
|
|
103
|
+
const processManager = ProcessManager.getInstance();
|
|
104
|
+
process.on('exit', () => {
|
|
105
|
+
processManager.killAll('SIGKILL');
|
|
106
|
+
});
|
|
107
|
+
process.on('SIGTERM', () => {
|
|
108
|
+
processManager.killAll('SIGTERM');
|
|
109
|
+
setTimeout(() => {
|
|
110
|
+
processManager.killAll('SIGKILL');
|
|
111
|
+
process.exit(0);
|
|
112
|
+
}, 5000);
|
|
113
|
+
});
|
|
114
|
+
process.on('SIGINT', () => {
|
|
115
|
+
processManager.killAll('SIGTERM');
|
|
116
|
+
setTimeout(() => {
|
|
117
|
+
processManager.killAll('SIGKILL');
|
|
118
|
+
process.exit(0);
|
|
119
|
+
}, 5000);
|
|
120
|
+
});
|
|
121
|
+
process.on('uncaughtException', (err) => {
|
|
122
|
+
console.error('Uncaught exception:', err);
|
|
123
|
+
processManager.killAll('SIGKILL');
|
|
124
|
+
process.exit(1);
|
|
125
|
+
});
|
|
126
|
+
process.on('unhandledRejection', (reason) => {
|
|
127
|
+
console.error('Unhandled rejection:', reason);
|
|
128
|
+
processManager.killAll('SIGKILL');
|
|
129
|
+
process.exit(1);
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=process-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-manager.js","sourceRoot":"","sources":["../../src/core/process-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-story logging for concurrent execution
|
|
3
|
+
*
|
|
4
|
+
* Each story execution creates a new timestamped log file with dual output
|
|
5
|
+
* (console + file) for debugging and audit trail.
|
|
6
|
+
*/
|
|
7
|
+
import { LogLevel } from '../types/index.js';
|
|
8
|
+
/**
|
|
9
|
+
* Per-story logger that writes to timestamped files
|
|
10
|
+
*
|
|
11
|
+
* Features:
|
|
12
|
+
* - Dual output: console + timestamped file per execution
|
|
13
|
+
* - Automatic log rotation (keeps last N logs per story)
|
|
14
|
+
* - Crash-safe synchronous writes
|
|
15
|
+
* - Location: stories/{id}/logs/{timestamp}.log
|
|
16
|
+
*/
|
|
17
|
+
export declare class StoryLogger {
|
|
18
|
+
private logStream;
|
|
19
|
+
private storyId;
|
|
20
|
+
private logPath;
|
|
21
|
+
private closed;
|
|
22
|
+
/**
|
|
23
|
+
* Initialize a per-story logger
|
|
24
|
+
*
|
|
25
|
+
* @param storyId - Story ID (sanitized automatically)
|
|
26
|
+
* @param sdlcRoot - Path to .ai-sdlc directory
|
|
27
|
+
* @param maxLogs - Maximum number of log files to retain per story (default: 5)
|
|
28
|
+
*/
|
|
29
|
+
constructor(storyId: string, sdlcRoot: string, maxLogs?: number);
|
|
30
|
+
/**
|
|
31
|
+
* Log a message with specified level
|
|
32
|
+
*
|
|
33
|
+
* Writes to both console and file. Sanitizes message to prevent issues
|
|
34
|
+
* with very long lines or non-printable characters.
|
|
35
|
+
*
|
|
36
|
+
* @param level - Log level (INFO, AGENT, ERROR, WARN, DEBUG)
|
|
37
|
+
* @param message - Message to log
|
|
38
|
+
*/
|
|
39
|
+
log(level: LogLevel, message: string): void;
|
|
40
|
+
/**
|
|
41
|
+
* Close the log stream
|
|
42
|
+
*
|
|
43
|
+
* Should be called when story execution completes or on process exit.
|
|
44
|
+
* Flushes any buffered data and closes the file handle.
|
|
45
|
+
*
|
|
46
|
+
* @returns Promise that resolves when the stream is fully closed
|
|
47
|
+
*/
|
|
48
|
+
close(): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* Get the path to the current log file
|
|
51
|
+
*
|
|
52
|
+
* @returns Absolute path to the current log file
|
|
53
|
+
*/
|
|
54
|
+
getLogPath(): string;
|
|
55
|
+
/**
|
|
56
|
+
* Sanitize log message to prevent issues
|
|
57
|
+
*
|
|
58
|
+
* - Truncates messages longer than 10KB
|
|
59
|
+
* - Strips or escapes non-printable characters (except newlines/tabs)
|
|
60
|
+
* - Preserves ANSI color codes for console output
|
|
61
|
+
*
|
|
62
|
+
* @param message - Raw message to sanitize
|
|
63
|
+
* @returns Sanitized message
|
|
64
|
+
*/
|
|
65
|
+
private sanitizeMessage;
|
|
66
|
+
/**
|
|
67
|
+
* Rotate old log files, keeping only the most recent N logs
|
|
68
|
+
*
|
|
69
|
+
* Sorting is lexicographic (filename-based) since timestamps are in ISO 8601 format.
|
|
70
|
+
* Deletes oldest logs beyond the retention limit.
|
|
71
|
+
*
|
|
72
|
+
* @param logDir - Directory containing log files
|
|
73
|
+
* @param keep - Number of logs to retain
|
|
74
|
+
*/
|
|
75
|
+
private rotateOldLogs;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Get the latest log file path for a story
|
|
79
|
+
*
|
|
80
|
+
* @param sdlcRoot - Path to .ai-sdlc directory
|
|
81
|
+
* @param storyId - Story ID (sanitized automatically)
|
|
82
|
+
* @returns Path to latest log file, or null if no logs exist
|
|
83
|
+
*/
|
|
84
|
+
export declare function getLatestLogPath(sdlcRoot: string, storyId: string): string | null;
|
|
85
|
+
/**
|
|
86
|
+
* Read the last N lines from a log file
|
|
87
|
+
*
|
|
88
|
+
* @param filePath - Path to log file
|
|
89
|
+
* @param lines - Number of lines to read (default: 50)
|
|
90
|
+
* @returns Last N lines as a string
|
|
91
|
+
*/
|
|
92
|
+
export declare function readLastLines(filePath: string, lines?: number): Promise<string>;
|
|
93
|
+
/**
|
|
94
|
+
* Tail a log file (follow mode, like tail -f)
|
|
95
|
+
*
|
|
96
|
+
* Watches the file for changes and outputs new lines as they're written.
|
|
97
|
+
* Press Ctrl+C to exit.
|
|
98
|
+
*
|
|
99
|
+
* @param filePath - Path to log file
|
|
100
|
+
*/
|
|
101
|
+
export declare function tailLog(filePath: string): void;
|
|
102
|
+
//# sourceMappingURL=story-logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"story-logger.d.ts","sourceRoot":"","sources":["../../src/core/story-logger.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAAkB,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAI7D;;;;;;;;GAQG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAkB;IAEhC;;;;;;OAMG;gBACS,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,MAAU;IAwClE;;;;;;;;OAQG;IACH,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAwC3C;;;;;;;OAOG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAatB;;;;OAIG;IACH,UAAU,IAAI,MAAM;IAIpB;;;;;;;;;OASG;IACH,OAAO,CAAC,eAAe;IAgBvB;;;;;;;;OAQG;IACH,OAAO,CAAC,aAAa;CAsBtB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAmBjF;AAED;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAKzF;AAED;;;;;;;GAOG;AACH,wBAAgB,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAiC9C"}
|