flanders 0.0.2 → 0.2.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/lib/ai/AiRunner.d.ts +24 -0
- package/lib/ai/AiRunner.js +100 -0
- package/lib/ai/AiSession.d.ts +32 -0
- package/lib/ai/AiSession.js +143 -0
- package/lib/ai/ClaudeAdapter.d.ts +12 -0
- package/lib/ai/ClaudeAdapter.js +345 -0
- package/lib/ai/CodexAdapter.d.ts +13 -0
- package/lib/ai/CodexAdapter.js +354 -0
- package/lib/ai/ToolAdapter.d.ts +52 -0
- package/lib/ai/ToolAdapter.js +2 -0
- package/lib/cli.js +56 -35
- package/lib/commands/Implement.d.ts +22 -8
- package/lib/commands/Implement.js +296 -171
- package/lib/commands/Install.d.ts +31 -3
- package/lib/commands/Install.js +649 -49
- package/lib/commands/InstallAvailabilityCheck.d.ts +8 -0
- package/lib/commands/InstallAvailabilityCheck.js +45 -0
- package/lib/commands/InstallModelProbe.d.ts +2 -0
- package/lib/commands/InstallModelProbe.js +74 -0
- package/lib/contexts.d.ts +5 -4
- package/lib/index.d.ts +5 -3
- package/lib/{PlanFile.d.ts → plan/PlanFile.d.ts} +8 -1
- package/lib/{PlanFile.js → plan/PlanFile.js} +44 -5
- package/lib/prompts/prompts.d.ts +48 -0
- package/lib/prompts/prompts.js +328 -0
- package/lib/prompts/skills.d.ts +3 -0
- package/lib/prompts/skills.js +463 -0
- package/lib/{Git.d.ts → system/Git.d.ts} +4 -2
- package/lib/{Git.js → system/Git.js} +63 -3
- package/lib/{ScriptRunner.d.ts → system/ScriptRunner.d.ts} +1 -1
- package/lib/system/ShellScriptContext.d.ts +36 -0
- package/lib/system/ShellScriptContext.js +70 -0
- package/lib/{fsUtils.d.ts → system/fsUtils.d.ts} +1 -1
- package/lib/{wait.d.ts → system/wait.d.ts} +1 -1
- package/lib/ui/BottomBlock.d.ts +9 -1
- package/lib/ui/BottomBlock.js +30 -14
- package/lib/ui/PromptHelper.d.ts +12 -0
- package/lib/ui/PromptHelper.js +32 -0
- package/lib/ui/TerminalSizeSource.d.ts +19 -0
- package/lib/ui/TerminalSizeSource.js +77 -0
- package/lib/ui/formatters.d.ts +14 -0
- package/lib/ui/formatters.js +65 -2
- package/lib/workspace/FlandersConfig.d.ts +23 -0
- package/lib/workspace/FlandersConfig.js +90 -0
- package/lib/workspace/SpecDiscovery.d.ts +12 -0
- package/lib/workspace/SpecDiscovery.js +40 -0
- package/lib/{Workspace.d.ts → workspace/Workspace.d.ts} +10 -2
- package/lib/{Workspace.js → workspace/Workspace.js} +52 -6
- package/package.json +3 -2
- package/lib/Claude.d.ts +0 -129
- package/lib/Claude.js +0 -387
- package/lib/ClaudeSession.d.ts +0 -34
- package/lib/ClaudeSession.js +0 -292
- package/lib/prompts.d.ts +0 -18
- package/lib/prompts.js +0 -221
- package/lib/skills.d.ts +0 -3
- package/lib/skills.js +0 -256
- /package/lib/{ScriptRunner.js → system/ScriptRunner.js} +0 -0
- /package/lib/{fsUtils.js → system/fsUtils.js} +0 -0
- /package/lib/{wait.js → system/wait.js} +0 -0
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CodexAdapter = void 0;
|
|
4
|
+
exports.formatCodexCommand = formatCodexCommand;
|
|
5
|
+
const COMMAND_INLINE_MAX = 120;
|
|
6
|
+
const RATE_LIMIT_SUBSTRINGS = [
|
|
7
|
+
"out of credits",
|
|
8
|
+
"refill",
|
|
9
|
+
"usage limit",
|
|
10
|
+
"rate limit",
|
|
11
|
+
"rate-limit",
|
|
12
|
+
"rate_limit",
|
|
13
|
+
"quota",
|
|
14
|
+
"too many requests"
|
|
15
|
+
];
|
|
16
|
+
const RATE_LIMIT_429_RE = /\b429\b/;
|
|
17
|
+
const EIGHT_MINUTES_MS = 8 * 60000;
|
|
18
|
+
const TWELVE_MINUTES_MS = 12 * 60000;
|
|
19
|
+
const FIVE_XX_RE = /\b5\d{2}\b/;
|
|
20
|
+
const STATUS_408_RE = /\b408\b/;
|
|
21
|
+
const STATUS_425_RE = /\b425\b/;
|
|
22
|
+
const TRANSPORT_SUBSTRINGS = [
|
|
23
|
+
"timeout",
|
|
24
|
+
"timed out",
|
|
25
|
+
"connection reset",
|
|
26
|
+
"connection refused",
|
|
27
|
+
"socket hang up",
|
|
28
|
+
"temporarily unavailable",
|
|
29
|
+
"service unavailable",
|
|
30
|
+
"gateway",
|
|
31
|
+
"network",
|
|
32
|
+
"econnreset",
|
|
33
|
+
"econnrefused",
|
|
34
|
+
"enotfound",
|
|
35
|
+
"etimedout",
|
|
36
|
+
"eai_again"
|
|
37
|
+
];
|
|
38
|
+
function formatCodexCommand(command) {
|
|
39
|
+
if (!command)
|
|
40
|
+
return "";
|
|
41
|
+
const firstLine = command.split("\n")[0];
|
|
42
|
+
if (firstLine.length > COMMAND_INLINE_MAX) {
|
|
43
|
+
return firstLine.slice(0, COMMAND_INLINE_MAX - 3) + "...";
|
|
44
|
+
}
|
|
45
|
+
return firstLine;
|
|
46
|
+
}
|
|
47
|
+
function isRateLimitMessage(message) {
|
|
48
|
+
const lower = message.trim().toLowerCase();
|
|
49
|
+
for (const sub of RATE_LIMIT_SUBSTRINGS) {
|
|
50
|
+
if (lower.includes(sub))
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
return RATE_LIMIT_429_RE.test(message.trim());
|
|
54
|
+
}
|
|
55
|
+
function isRetryableHttpStatus(message) {
|
|
56
|
+
const trimmed = message.trim();
|
|
57
|
+
if (FIVE_XX_RE.test(trimmed))
|
|
58
|
+
return true;
|
|
59
|
+
if (STATUS_408_RE.test(trimmed))
|
|
60
|
+
return true;
|
|
61
|
+
if (STATUS_425_RE.test(trimmed))
|
|
62
|
+
return true;
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
function isRetryableTransport(message) {
|
|
66
|
+
const lower = message.trim().toLowerCase();
|
|
67
|
+
for (const sub of TRANSPORT_SUBSTRINGS) {
|
|
68
|
+
if (lower.includes(sub))
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
class CodexAdapter {
|
|
74
|
+
constructor(_contexts) {
|
|
75
|
+
this._contexts = _contexts;
|
|
76
|
+
}
|
|
77
|
+
invoke(args) {
|
|
78
|
+
const iter = new CodexAdapterIterator(args, this._contexts);
|
|
79
|
+
return {
|
|
80
|
+
[Symbol.asyncIterator]() {
|
|
81
|
+
return iter;
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
exports.CodexAdapter = CodexAdapter;
|
|
87
|
+
class CodexAdapterIterator {
|
|
88
|
+
constructor(_args, _contexts) {
|
|
89
|
+
this._args = _args;
|
|
90
|
+
this._contexts = _contexts;
|
|
91
|
+
this._proc = null;
|
|
92
|
+
this._capturedSessionId = null;
|
|
93
|
+
this._queue = [];
|
|
94
|
+
this._done = false;
|
|
95
|
+
this._waitResolve = null;
|
|
96
|
+
this._abortListener = null;
|
|
97
|
+
this._exitPromise = null;
|
|
98
|
+
this._sawTurnCompleted = false;
|
|
99
|
+
this._receivedAnyEvent = false;
|
|
100
|
+
this._fallbackAttempted = false;
|
|
101
|
+
this._usedResumeOrFork = false;
|
|
102
|
+
this._start(false);
|
|
103
|
+
}
|
|
104
|
+
_start(useExecFallback) {
|
|
105
|
+
var _a, _b, _c;
|
|
106
|
+
const isResume = !useExecFallback && !!this._args.resumeSessionId;
|
|
107
|
+
const isFork = !useExecFallback && !!this._args.forkParentSessionId;
|
|
108
|
+
this._usedResumeOrFork = isResume || isFork;
|
|
109
|
+
const argv = this._buildArgv(isResume, isFork);
|
|
110
|
+
const spawnOptions = { stdio: "pipe" };
|
|
111
|
+
const proc = this._contexts.script.spawn("codex", argv, spawnOptions);
|
|
112
|
+
this._proc = proc;
|
|
113
|
+
(_a = proc.stdin) === null || _a === void 0 ? void 0 : _a.write(this._args.prompt);
|
|
114
|
+
(_b = proc.stdin) === null || _b === void 0 ? void 0 : _b.end();
|
|
115
|
+
let exitResolve = null;
|
|
116
|
+
this._exitPromise = new Promise(resolve => { exitResolve = resolve; });
|
|
117
|
+
let buffer = "";
|
|
118
|
+
(_c = proc.stdout) === null || _c === void 0 ? void 0 : _c.on("data", (chunk) => {
|
|
119
|
+
buffer += String(chunk);
|
|
120
|
+
for (;;) {
|
|
121
|
+
const nl = buffer.indexOf("\n");
|
|
122
|
+
if (nl < 0)
|
|
123
|
+
break;
|
|
124
|
+
const line = buffer.slice(0, nl).replace(/\r$/, "");
|
|
125
|
+
buffer = buffer.slice(nl + 1);
|
|
126
|
+
if (line) {
|
|
127
|
+
this._handleLine(line);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
proc.on("error", (e) => {
|
|
132
|
+
if (this._done) {
|
|
133
|
+
exitResolve === null || exitResolve === void 0 ? void 0 : exitResolve();
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
this._done = true;
|
|
137
|
+
const err = e instanceof Error ? e : new Error(String(e));
|
|
138
|
+
if (err.code === "ENOENT") {
|
|
139
|
+
this._queue.push({ type: "error", retryable: false, message: "codex binary not found" });
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
this._queue.push({ type: "error", retryable: false, message: err.message });
|
|
143
|
+
}
|
|
144
|
+
this._wake();
|
|
145
|
+
exitResolve === null || exitResolve === void 0 ? void 0 : exitResolve();
|
|
146
|
+
});
|
|
147
|
+
proc.on("exit", (code, signal) => {
|
|
148
|
+
if (this._done) {
|
|
149
|
+
exitResolve === null || exitResolve === void 0 ? void 0 : exitResolve();
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (this._usedResumeOrFork && !this._receivedAnyEvent && !this._fallbackAttempted) {
|
|
153
|
+
this._fallbackAttempted = true;
|
|
154
|
+
this._queue.push({
|
|
155
|
+
type: "output",
|
|
156
|
+
title: "Continuity lost",
|
|
157
|
+
subtitle: "",
|
|
158
|
+
details: "codex resume/fork unavailable in installed CLI"
|
|
159
|
+
});
|
|
160
|
+
this._cleanup();
|
|
161
|
+
this._sawTurnCompleted = false;
|
|
162
|
+
this._receivedAnyEvent = false;
|
|
163
|
+
this._usedResumeOrFork = false;
|
|
164
|
+
exitResolve === null || exitResolve === void 0 ? void 0 : exitResolve();
|
|
165
|
+
this._start(true);
|
|
166
|
+
this._wake();
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
if (this._sawTurnCompleted) {
|
|
170
|
+
this._queue.push({ type: "done" });
|
|
171
|
+
}
|
|
172
|
+
else if (signal) {
|
|
173
|
+
this._queue.push({
|
|
174
|
+
type: "error",
|
|
175
|
+
retryable: true,
|
|
176
|
+
message: `codex terminated by signal ${signal}`
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
this._queue.push({
|
|
181
|
+
type: "error",
|
|
182
|
+
retryable: true,
|
|
183
|
+
message: `codex exited unexpectedly (code ${code} signal ${signal})`
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
this._done = true;
|
|
187
|
+
this._wake();
|
|
188
|
+
exitResolve === null || exitResolve === void 0 ? void 0 : exitResolve();
|
|
189
|
+
});
|
|
190
|
+
this._abortListener = () => {
|
|
191
|
+
if (this._proc) {
|
|
192
|
+
this._proc.kill("SIGINT");
|
|
193
|
+
}
|
|
194
|
+
this._done = true;
|
|
195
|
+
this._wake();
|
|
196
|
+
};
|
|
197
|
+
if (this._args.abortSignal.aborted) {
|
|
198
|
+
this._abortListener();
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
this._args.abortSignal.addEventListener("abort", this._abortListener, { once: true });
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
_buildArgv(isResume, isFork) {
|
|
205
|
+
const argv = [];
|
|
206
|
+
if (isResume) {
|
|
207
|
+
argv.push("resume", this._args.resumeSessionId);
|
|
208
|
+
}
|
|
209
|
+
else if (isFork) {
|
|
210
|
+
argv.push("fork", this._args.forkParentSessionId);
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
argv.push("exec");
|
|
214
|
+
}
|
|
215
|
+
argv.push("--json");
|
|
216
|
+
argv.push("-c", "approval_policy=never");
|
|
217
|
+
argv.push("-c", "sandbox_mode=danger-full-access");
|
|
218
|
+
if (this._args.model) {
|
|
219
|
+
argv.push("-m", this._args.model);
|
|
220
|
+
}
|
|
221
|
+
if (this._args.effort) {
|
|
222
|
+
argv.push("-c", `model_reasoning_effort=${this._args.effort}`);
|
|
223
|
+
}
|
|
224
|
+
argv.push("-");
|
|
225
|
+
return argv;
|
|
226
|
+
}
|
|
227
|
+
_handleLine(line) {
|
|
228
|
+
var _a, _b, _c;
|
|
229
|
+
if (this._done)
|
|
230
|
+
return;
|
|
231
|
+
let parsed = null;
|
|
232
|
+
try {
|
|
233
|
+
parsed = JSON.parse(line);
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
if (!parsed)
|
|
239
|
+
return;
|
|
240
|
+
this._receivedAnyEvent = true;
|
|
241
|
+
if (parsed.thread_id && parsed.thread_id !== this._capturedSessionId) {
|
|
242
|
+
this._capturedSessionId = parsed.thread_id;
|
|
243
|
+
this._queue.push({ type: "session", id: parsed.thread_id });
|
|
244
|
+
}
|
|
245
|
+
if (parsed.type === "item.completed" && parsed.item) {
|
|
246
|
+
this._handleItemCompleted(parsed.item);
|
|
247
|
+
}
|
|
248
|
+
else if (parsed.type === "turn.completed") {
|
|
249
|
+
this._sawTurnCompleted = true;
|
|
250
|
+
if (parsed.usage && this._args.onUsage) {
|
|
251
|
+
this._args.onUsage({
|
|
252
|
+
inputTokens: (_a = parsed.usage.input_tokens) !== null && _a !== void 0 ? _a : 0,
|
|
253
|
+
outputTokens: (_b = parsed.usage.output_tokens) !== null && _b !== void 0 ? _b : 0
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
else if (parsed.type === "error") {
|
|
258
|
+
this._handleFailure(typeof parsed.message === "string" ? parsed.message : "unknown error");
|
|
259
|
+
}
|
|
260
|
+
else if (parsed.type === "turn.failed") {
|
|
261
|
+
this._handleFailure(typeof ((_c = parsed.error) === null || _c === void 0 ? void 0 : _c.message) === "string" ? parsed.error.message : "unknown error");
|
|
262
|
+
}
|
|
263
|
+
this._wake();
|
|
264
|
+
}
|
|
265
|
+
_handleItemCompleted(item) {
|
|
266
|
+
if (item.type === "agent_message") {
|
|
267
|
+
this._queue.push({
|
|
268
|
+
type: "output",
|
|
269
|
+
title: "Assistant",
|
|
270
|
+
subtitle: "",
|
|
271
|
+
details: typeof item.text === "string" ? item.text : ""
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
else if (item.type === "command_execution") {
|
|
275
|
+
this._queue.push({
|
|
276
|
+
type: "output",
|
|
277
|
+
title: "command",
|
|
278
|
+
subtitle: formatCodexCommand(item.command),
|
|
279
|
+
details: typeof item.aggregated_output === "string" ? item.aggregated_output : ""
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
else if (item.type === "reasoning") {
|
|
283
|
+
this._queue.push({
|
|
284
|
+
type: "output",
|
|
285
|
+
title: "Thinking",
|
|
286
|
+
subtitle: "",
|
|
287
|
+
details: typeof item.text === "string" ? item.text : ""
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
_handleFailure(message) {
|
|
292
|
+
if (isRateLimitMessage(message)) {
|
|
293
|
+
const r = EIGHT_MINUTES_MS + Math.round(this._contexts.random.random() * (TWELVE_MINUTES_MS - EIGHT_MINUTES_MS));
|
|
294
|
+
this._queue.push({
|
|
295
|
+
type: "rate_limit",
|
|
296
|
+
waitUntilMs: this._contexts.time.now() + r
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
else if (isRetryableHttpStatus(message)) {
|
|
300
|
+
this._queue.push({ type: "error", retryable: true, message });
|
|
301
|
+
}
|
|
302
|
+
else if (isRetryableTransport(message)) {
|
|
303
|
+
this._queue.push({ type: "error", retryable: true, message });
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
this._queue.push({ type: "error", retryable: false, message });
|
|
307
|
+
}
|
|
308
|
+
this._done = true;
|
|
309
|
+
}
|
|
310
|
+
_wake() {
|
|
311
|
+
if (this._waitResolve) {
|
|
312
|
+
const resolve = this._waitResolve;
|
|
313
|
+
this._waitResolve = null;
|
|
314
|
+
resolve();
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
_wait() {
|
|
318
|
+
return new Promise(resolve => {
|
|
319
|
+
this._waitResolve = resolve;
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
async next() {
|
|
323
|
+
for (;;) {
|
|
324
|
+
if (this._queue.length > 0) {
|
|
325
|
+
return { value: this._queue.shift(), done: false };
|
|
326
|
+
}
|
|
327
|
+
if (this._done && this._queue.length === 0) {
|
|
328
|
+
this._cleanup();
|
|
329
|
+
if (this._exitPromise) {
|
|
330
|
+
await this._exitPromise;
|
|
331
|
+
}
|
|
332
|
+
return { value: undefined, done: true };
|
|
333
|
+
}
|
|
334
|
+
await this._wait();
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
async return() {
|
|
338
|
+
this._done = true;
|
|
339
|
+
this._cleanup();
|
|
340
|
+
if (this._proc) {
|
|
341
|
+
this._proc.kill("SIGINT");
|
|
342
|
+
if (this._exitPromise) {
|
|
343
|
+
await this._exitPromise;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return { value: undefined, done: true };
|
|
347
|
+
}
|
|
348
|
+
_cleanup() {
|
|
349
|
+
if (this._abortListener) {
|
|
350
|
+
this._args.abortSignal.removeEventListener("abort", this._abortListener);
|
|
351
|
+
this._abortListener = null;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export type ToolEventOutput = Readonly<{
|
|
2
|
+
type: "output";
|
|
3
|
+
title: string;
|
|
4
|
+
subtitle: string;
|
|
5
|
+
details: string;
|
|
6
|
+
}>;
|
|
7
|
+
export type ToolEventSession = Readonly<{
|
|
8
|
+
type: "session";
|
|
9
|
+
id: string;
|
|
10
|
+
}>;
|
|
11
|
+
export type ToolEventError = Readonly<{
|
|
12
|
+
type: "error";
|
|
13
|
+
retryable: boolean;
|
|
14
|
+
message: string;
|
|
15
|
+
}>;
|
|
16
|
+
export type ToolEventRateLimit = Readonly<{
|
|
17
|
+
type: "rate_limit";
|
|
18
|
+
waitUntilMs: number;
|
|
19
|
+
}>;
|
|
20
|
+
export type ToolEventDone = Readonly<{
|
|
21
|
+
type: "done";
|
|
22
|
+
}>;
|
|
23
|
+
export type ToolEvent = ToolEventOutput | ToolEventSession | ToolEventError | ToolEventRateLimit | ToolEventDone;
|
|
24
|
+
export type ToolAdapterUsageCallback = (usage: Readonly<{
|
|
25
|
+
inputTokens: number;
|
|
26
|
+
outputTokens: number;
|
|
27
|
+
}>) => void;
|
|
28
|
+
type ToolAdapterInvokeArgsBase = Readonly<{
|
|
29
|
+
prompt: string;
|
|
30
|
+
model: string;
|
|
31
|
+
effort: string;
|
|
32
|
+
abortSignal: AbortSignal;
|
|
33
|
+
onUsage?: ToolAdapterUsageCallback;
|
|
34
|
+
}>;
|
|
35
|
+
export type ToolAdapterInvokeArgsFresh = ToolAdapterInvokeArgsBase & Readonly<{
|
|
36
|
+
resumeSessionId?: undefined;
|
|
37
|
+
forkParentSessionId?: undefined;
|
|
38
|
+
}>;
|
|
39
|
+
export type ToolAdapterInvokeArgsResume = ToolAdapterInvokeArgsBase & Readonly<{
|
|
40
|
+
resumeSessionId: string;
|
|
41
|
+
forkParentSessionId?: undefined;
|
|
42
|
+
}>;
|
|
43
|
+
export type ToolAdapterInvokeArgsFork = ToolAdapterInvokeArgsBase & Readonly<{
|
|
44
|
+
resumeSessionId?: undefined;
|
|
45
|
+
forkParentSessionId: string;
|
|
46
|
+
}>;
|
|
47
|
+
export type ToolAdapterInvokeArgs = ToolAdapterInvokeArgsFresh | ToolAdapterInvokeArgsResume | ToolAdapterInvokeArgsFork;
|
|
48
|
+
export interface ToolAdapter {
|
|
49
|
+
invoke(args: ToolAdapterInvokeArgs): AsyncIterable<ToolEvent>;
|
|
50
|
+
}
|
|
51
|
+
export type ToolName = "claude" | "codex";
|
|
52
|
+
export {};
|
package/lib/cli.js
CHANGED
|
@@ -41,37 +41,27 @@ const os = __importStar(require("os"));
|
|
|
41
41
|
const path = __importStar(require("path"));
|
|
42
42
|
const readline = __importStar(require("readline"));
|
|
43
43
|
const Flanders_1 = require("./Flanders");
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
write(chunk) {
|
|
66
|
-
child.stdin.write(chunk);
|
|
67
|
-
},
|
|
68
|
-
end() {
|
|
69
|
-
child.stdin.end();
|
|
70
|
-
}
|
|
71
|
-
} : undefined
|
|
72
|
-
};
|
|
73
|
-
return proc;
|
|
74
|
-
}
|
|
44
|
+
const ShellScriptContext_1 = require("./system/ShellScriptContext");
|
|
45
|
+
const TerminalSizeSource_1 = require("./ui/TerminalSizeSource");
|
|
46
|
+
const rawSpawn = (command, args, options) => {
|
|
47
|
+
var _a;
|
|
48
|
+
const child = (0, child_process_1.spawn)(command, [...args], options);
|
|
49
|
+
const raw = {
|
|
50
|
+
pid: (_a = child.pid) !== null && _a !== void 0 ? _a : 0,
|
|
51
|
+
stdout: child.stdout,
|
|
52
|
+
stderr: child.stderr,
|
|
53
|
+
stdin: child.stdin,
|
|
54
|
+
on(event, listener) {
|
|
55
|
+
child.on(event, listener);
|
|
56
|
+
},
|
|
57
|
+
kill(signal) {
|
|
58
|
+
child.kill(signal);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
return raw;
|
|
62
|
+
};
|
|
63
|
+
const killPrimitive = (pid, signal) => {
|
|
64
|
+
process.kill(pid, signal);
|
|
75
65
|
};
|
|
76
66
|
const fsContext = {
|
|
77
67
|
async readFile(p) {
|
|
@@ -81,6 +71,9 @@ const fsContext = {
|
|
|
81
71
|
await fs_1.promises.mkdir(path.dirname(p), { recursive: true });
|
|
82
72
|
await fs_1.promises.writeFile(p, content, "utf8");
|
|
83
73
|
},
|
|
74
|
+
async rename(oldP, newP) {
|
|
75
|
+
await fs_1.promises.rename(oldP, newP);
|
|
76
|
+
},
|
|
84
77
|
async readdir(p) {
|
|
85
78
|
const entries = await fs_1.promises.readdir(p, { withFileTypes: true });
|
|
86
79
|
return entries.map(e => ({
|
|
@@ -132,6 +125,32 @@ const timeContext = {
|
|
|
132
125
|
};
|
|
133
126
|
}
|
|
134
127
|
};
|
|
128
|
+
const randomContext = {
|
|
129
|
+
random() {
|
|
130
|
+
return Math.random();
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
const RESIZE_POLL_MS = 200;
|
|
134
|
+
const readTerminalSize = () => {
|
|
135
|
+
var _a, _b;
|
|
136
|
+
const stream = process.stdout;
|
|
137
|
+
const handle = stream._handle;
|
|
138
|
+
if (!handle || typeof handle.getWindowSize !== "function") {
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
const size = [0, 0];
|
|
142
|
+
const err = handle.getWindowSize(size);
|
|
143
|
+
const cols = (_a = size[0]) !== null && _a !== void 0 ? _a : 0;
|
|
144
|
+
const rows = (_b = size[1]) !== null && _b !== void 0 ? _b : 0;
|
|
145
|
+
if (err || cols <= 0) {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
return { columns: cols, rows };
|
|
149
|
+
};
|
|
150
|
+
const terminalSize = new TerminalSizeSource_1.TerminalSizeSource(readTerminalSize, listener => {
|
|
151
|
+
process.stdout.on("resize", listener);
|
|
152
|
+
return () => { process.stdout.off("resize", listener); };
|
|
153
|
+
}, timeContext, RESIZE_POLL_MS);
|
|
135
154
|
const outputContext = {
|
|
136
155
|
write(text) {
|
|
137
156
|
process.stdout.write(text);
|
|
@@ -140,14 +159,13 @@ const outputContext = {
|
|
|
140
159
|
process.stderr.write(text);
|
|
141
160
|
},
|
|
142
161
|
columns() {
|
|
143
|
-
return
|
|
162
|
+
return terminalSize.columns();
|
|
144
163
|
},
|
|
145
164
|
rows() {
|
|
146
|
-
return
|
|
165
|
+
return terminalSize.rows();
|
|
147
166
|
},
|
|
148
167
|
onResize(listener) {
|
|
149
|
-
|
|
150
|
-
return () => { process.stdout.off("resize", listener); };
|
|
168
|
+
return terminalSize.onResize(listener);
|
|
151
169
|
}
|
|
152
170
|
};
|
|
153
171
|
const platformContext = {
|
|
@@ -161,6 +179,7 @@ const platformContext = {
|
|
|
161
179
|
return os.homedir();
|
|
162
180
|
}
|
|
163
181
|
};
|
|
182
|
+
const spawnContext = new ShellScriptContext_1.ShellScriptContext(rawSpawn, killPrimitive, platformContext);
|
|
164
183
|
const ask = (() => {
|
|
165
184
|
let rl = null;
|
|
166
185
|
const ensure = () => {
|
|
@@ -297,6 +316,7 @@ const flanders = new Flanders_1.Flanders(process.argv.slice(2), { projectRoot: p
|
|
|
297
316
|
script: spawnContext,
|
|
298
317
|
fs: fsContext,
|
|
299
318
|
time: timeContext,
|
|
319
|
+
random: randomContext,
|
|
300
320
|
platform: platformContext,
|
|
301
321
|
ask: ask.context,
|
|
302
322
|
output: outputContext
|
|
@@ -311,6 +331,7 @@ const end = async () => {
|
|
|
311
331
|
outputContext.write("Shutting down...\n");
|
|
312
332
|
try {
|
|
313
333
|
await flanders.dispose();
|
|
334
|
+
terminalSize.dispose();
|
|
314
335
|
}
|
|
315
336
|
catch (e) {
|
|
316
337
|
outputContext.writeError(`${e instanceof Error ? (_a = e.stack) !== null && _a !== void 0 ? _a : e.message : String(e)}\n`);
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { FsContext, OutputContext, RandomContext, ScriptContext, TimeContext } from "../contexts";
|
|
2
|
+
import type { FlandersConfig } from "../workspace/FlandersConfig";
|
|
2
3
|
import type { Activity } from "../ui/BottomBlock";
|
|
3
|
-
import { PlatformContext } from "../Workspace";
|
|
4
|
+
import { PlatformContext } from "../workspace/Workspace";
|
|
4
5
|
export type { Activity };
|
|
5
6
|
export type ImplementContexts = Readonly<{
|
|
6
|
-
claude:
|
|
7
|
+
claude: ScriptContext;
|
|
7
8
|
script: ScriptContext;
|
|
8
9
|
fs: FsContext;
|
|
9
10
|
time: TimeContext;
|
|
11
|
+
random: RandomContext;
|
|
10
12
|
platform: PlatformContext;
|
|
11
|
-
ask: AskContext;
|
|
12
13
|
output: OutputContext;
|
|
13
14
|
}>;
|
|
14
15
|
export type ImplementOptions = Readonly<{
|
|
@@ -18,8 +19,10 @@ export declare class Implement {
|
|
|
18
19
|
private _options;
|
|
19
20
|
private _contexts;
|
|
20
21
|
private _disposed;
|
|
22
|
+
private _config;
|
|
21
23
|
private _contractList;
|
|
22
24
|
private _ruleList;
|
|
25
|
+
private _behaviorRuleList;
|
|
23
26
|
private _workspace;
|
|
24
27
|
private _block;
|
|
25
28
|
private _buffered;
|
|
@@ -27,6 +30,8 @@ export declare class Implement {
|
|
|
27
30
|
private _currentPrepSessionId;
|
|
28
31
|
private _activeSession;
|
|
29
32
|
private _activeScript;
|
|
33
|
+
private _activeReviewerSessions;
|
|
34
|
+
private _reviewerStates;
|
|
30
35
|
private _currentIndexLabel;
|
|
31
36
|
private _currentIteration;
|
|
32
37
|
private _currentTask;
|
|
@@ -34,27 +39,36 @@ export declare class Implement {
|
|
|
34
39
|
private _taskRateLimitMs;
|
|
35
40
|
private _taskRateLimitStartedAt;
|
|
36
41
|
private _taskTokens;
|
|
42
|
+
private _restingFooterKind;
|
|
37
43
|
private _runPromise;
|
|
44
|
+
get config(): FlandersConfig | null;
|
|
38
45
|
constructor(rawArgs: readonly string[], _options: ImplementOptions, _contexts: ImplementContexts);
|
|
39
46
|
result(): Promise<number>;
|
|
40
47
|
private _run;
|
|
41
48
|
private _selectPlan;
|
|
42
|
-
private _askOutput;
|
|
43
|
-
private _promptPlanChoice;
|
|
44
49
|
private _detectBuildAndTest;
|
|
45
50
|
private _setActivity;
|
|
46
51
|
private _activeSeconds;
|
|
47
52
|
private _persistMetrics;
|
|
48
53
|
private _updateMetrics;
|
|
54
|
+
private _reviewerMatchesWorker;
|
|
55
|
+
private _prepActive;
|
|
56
|
+
private _gitOutputContext;
|
|
49
57
|
private _runTask;
|
|
50
58
|
private _prepStage;
|
|
51
59
|
private _workerStage;
|
|
60
|
+
private _appendLinkedContent;
|
|
52
61
|
private _buildStage;
|
|
53
62
|
private _testStage;
|
|
63
|
+
private _setReviewerState;
|
|
64
|
+
private _renderReviewingFooter;
|
|
54
65
|
private _reviewerStage;
|
|
55
|
-
private
|
|
66
|
+
private _runOneReviewerToVerdict;
|
|
56
67
|
private _formatPathList;
|
|
57
|
-
private
|
|
68
|
+
private _getAdapter;
|
|
69
|
+
private _defaultRunAiCallbacks;
|
|
70
|
+
private _runAi;
|
|
71
|
+
private _runAiWith;
|
|
58
72
|
private _runScript;
|
|
59
73
|
private _writeLog;
|
|
60
74
|
private _writeErrorLog;
|