flanders 0.1.0 → 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.
Files changed (60) hide show
  1. package/lib/ai/AiRunner.d.ts +24 -0
  2. package/lib/ai/AiRunner.js +100 -0
  3. package/lib/ai/AiSession.d.ts +32 -0
  4. package/lib/ai/AiSession.js +143 -0
  5. package/lib/ai/ClaudeAdapter.d.ts +12 -0
  6. package/lib/ai/ClaudeAdapter.js +345 -0
  7. package/lib/ai/CodexAdapter.d.ts +13 -0
  8. package/lib/ai/CodexAdapter.js +354 -0
  9. package/lib/ai/ToolAdapter.d.ts +52 -0
  10. package/lib/ai/ToolAdapter.js +2 -0
  11. package/lib/cli.js +56 -35
  12. package/lib/commands/Implement.d.ts +22 -8
  13. package/lib/commands/Implement.js +296 -171
  14. package/lib/commands/Install.d.ts +31 -3
  15. package/lib/commands/Install.js +649 -49
  16. package/lib/commands/InstallAvailabilityCheck.d.ts +8 -0
  17. package/lib/commands/InstallAvailabilityCheck.js +45 -0
  18. package/lib/commands/InstallModelProbe.d.ts +2 -0
  19. package/lib/commands/InstallModelProbe.js +74 -0
  20. package/lib/contexts.d.ts +5 -4
  21. package/lib/index.d.ts +5 -3
  22. package/lib/{PlanFile.d.ts → plan/PlanFile.d.ts} +8 -1
  23. package/lib/{PlanFile.js → plan/PlanFile.js} +44 -5
  24. package/lib/prompts/prompts.d.ts +48 -0
  25. package/lib/prompts/prompts.js +328 -0
  26. package/lib/prompts/skills.d.ts +3 -0
  27. package/lib/prompts/skills.js +463 -0
  28. package/lib/{Git.d.ts → system/Git.d.ts} +4 -2
  29. package/lib/{Git.js → system/Git.js} +63 -3
  30. package/lib/{ScriptRunner.d.ts → system/ScriptRunner.d.ts} +1 -1
  31. package/lib/system/ShellScriptContext.d.ts +36 -0
  32. package/lib/system/ShellScriptContext.js +70 -0
  33. package/lib/{fsUtils.d.ts → system/fsUtils.d.ts} +1 -1
  34. package/lib/{wait.d.ts → system/wait.d.ts} +1 -1
  35. package/lib/ui/BottomBlock.d.ts +9 -1
  36. package/lib/ui/BottomBlock.js +30 -14
  37. package/lib/ui/PromptHelper.d.ts +12 -0
  38. package/lib/ui/PromptHelper.js +32 -0
  39. package/lib/ui/TerminalSizeSource.d.ts +19 -0
  40. package/lib/ui/TerminalSizeSource.js +77 -0
  41. package/lib/ui/formatters.d.ts +14 -0
  42. package/lib/ui/formatters.js +65 -2
  43. package/lib/workspace/FlandersConfig.d.ts +23 -0
  44. package/lib/workspace/FlandersConfig.js +90 -0
  45. package/lib/workspace/SpecDiscovery.d.ts +12 -0
  46. package/lib/workspace/SpecDiscovery.js +40 -0
  47. package/lib/{Workspace.d.ts → workspace/Workspace.d.ts} +10 -2
  48. package/lib/{Workspace.js → workspace/Workspace.js} +52 -6
  49. package/package.json +3 -2
  50. package/lib/Claude.d.ts +0 -129
  51. package/lib/Claude.js +0 -387
  52. package/lib/ClaudeSession.d.ts +0 -34
  53. package/lib/ClaudeSession.js +0 -292
  54. package/lib/prompts.d.ts +0 -18
  55. package/lib/prompts.js +0 -221
  56. package/lib/skills.d.ts +0 -3
  57. package/lib/skills.js +0 -267
  58. /package/lib/{ScriptRunner.js → system/ScriptRunner.js} +0 -0
  59. /package/lib/{fsUtils.js → system/fsUtils.js} +0 -0
  60. /package/lib/{wait.js → system/wait.js} +0 -0
package/lib/Claude.js DELETED
@@ -1,387 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Claude = exports.NonRetryableError = void 0;
4
- const wait_1 = require("./wait");
5
- const RATE_LIMIT_CHUNK_MS = 60 * 60 * 1000;
6
- const INITIAL_TRANSIENT_WAIT_MS = 1000;
7
- const TRANSIENT_WAIT_CAP_MS = 60000;
8
- class RateLimitError extends Error {
9
- constructor(retryAfterMs, inputTokens = 0, outputTokens = 0) {
10
- super(`rate_limit: retry after ${retryAfterMs}ms`);
11
- this.retryAfterMs = retryAfterMs;
12
- this.inputTokens = inputTokens;
13
- this.outputTokens = outputTokens;
14
- }
15
- }
16
- class RetryableTransientError extends Error {
17
- constructor(inputTokens = 0, outputTokens = 0) {
18
- super("retryable transient error");
19
- this.inputTokens = inputTokens;
20
- this.outputTokens = outputTokens;
21
- }
22
- }
23
- class NonRetryableError extends Error {
24
- constructor(message) {
25
- super(message);
26
- }
27
- }
28
- exports.NonRetryableError = NonRetryableError;
29
- class Claude {
30
- constructor(options, _context, _time) {
31
- var _a, _b;
32
- this.options = options;
33
- this._context = _context;
34
- this._time = _time;
35
- this._disposed = false;
36
- this._process = null;
37
- this._waitAbort = null;
38
- this._killTimer = null;
39
- this._transientAttempt = 0;
40
- this._sessionId = (_a = options.initialSessionId) !== null && _a !== void 0 ? _a : null;
41
- this._forkFromSessionId = (_b = options.forkFromSessionId) !== null && _b !== void 0 ? _b : null;
42
- this._runPromise = this._run();
43
- this._runPromise.catch(() => { });
44
- }
45
- result() {
46
- return this._runPromise;
47
- }
48
- sendUserMessage(content) {
49
- this._writeStdin({ type: "user", message: { role: "user", content } });
50
- }
51
- sendControlResponse(requestId, response) {
52
- this._writeStdin({
53
- type: "control_response",
54
- response: {
55
- subtype: "success",
56
- request_id: requestId,
57
- response
58
- }
59
- });
60
- }
61
- endSession() {
62
- var _a, _b;
63
- if (this._disposed) {
64
- return;
65
- }
66
- (_b = (_a = this._process) === null || _a === void 0 ? void 0 : _a.stdin) === null || _b === void 0 ? void 0 : _b.end();
67
- }
68
- _writeStdin(payload) {
69
- var _a;
70
- if (this._disposed) {
71
- return;
72
- }
73
- const stdin = (_a = this._process) === null || _a === void 0 ? void 0 : _a.stdin;
74
- if (!stdin) {
75
- return;
76
- }
77
- stdin.write(JSON.stringify(payload) + "\n");
78
- }
79
- async _run() {
80
- var _a, _b, _c, _d;
81
- if (this._sessionId != null && this._forkFromSessionId != null) {
82
- throw new Error("Cannot specify both forkFromSessionId and initialSessionId");
83
- }
84
- let totalInputTokens = 0;
85
- let totalOutputTokens = 0;
86
- for (;;) {
87
- if (this._disposed) {
88
- throw new Error("Claude disposed");
89
- }
90
- try {
91
- const result = await this._runOnce();
92
- totalInputTokens += result.inputTokens;
93
- totalOutputTokens += result.outputTokens;
94
- this._transientAttempt = 0;
95
- return { ...result, inputTokens: totalInputTokens, outputTokens: totalOutputTokens };
96
- }
97
- catch (e) {
98
- if (e instanceof RateLimitError) {
99
- totalInputTokens += e.inputTokens;
100
- totalOutputTokens += e.outputTokens;
101
- if (this._disposed) {
102
- throw new Error("Claude disposed");
103
- }
104
- (_b = (_a = this.options).onLongWaitStart) === null || _b === void 0 ? void 0 : _b.call(_a, "rate-limit", this._time.now() + e.retryAfterMs);
105
- try {
106
- await this._scheduleWait(e.retryAfterMs, RATE_LIMIT_CHUNK_MS);
107
- }
108
- finally {
109
- (_d = (_c = this.options).onLongWaitEnd) === null || _d === void 0 ? void 0 : _d.call(_c);
110
- }
111
- if (this._disposed) {
112
- throw new Error("Claude disposed");
113
- }
114
- continue;
115
- }
116
- if (e instanceof RetryableTransientError) {
117
- totalInputTokens += e.inputTokens;
118
- totalOutputTokens += e.outputTokens;
119
- this._transientAttempt++;
120
- const waitMs = Math.min(TRANSIENT_WAIT_CAP_MS, INITIAL_TRANSIENT_WAIT_MS * 2 ** (this._transientAttempt - 1));
121
- await this._scheduleWait(waitMs, waitMs);
122
- if (this._disposed) {
123
- throw new Error("Claude disposed");
124
- }
125
- continue;
126
- }
127
- throw e;
128
- }
129
- }
130
- }
131
- _runOnce() {
132
- return new Promise((resolve, reject) => {
133
- var _a;
134
- if (this._disposed) {
135
- return reject(new Error("Claude disposed"));
136
- }
137
- const args = [];
138
- if (this._sessionId) {
139
- args.push("--resume", this._sessionId);
140
- }
141
- else if (this._forkFromSessionId) {
142
- args.push("--resume", this._forkFromSessionId, "--fork-session");
143
- this._forkFromSessionId = null;
144
- }
145
- args.push("--model=claude-opus-4-6", "--effort=xhigh", "--input-format=stream-json", "--output-format=stream-json", "--include-partial-messages", "--permission-prompt-tool=stdio", "--verbose", "-p");
146
- const spawnOptions = {
147
- stdio: "pipe",
148
- ...(this.options.cwd ? { cwd: this.options.cwd } : null)
149
- };
150
- const proc = this._context.spawn("claude", args, spawnOptions);
151
- this._process = proc;
152
- const initialMessage = {
153
- type: "user",
154
- message: { role: "user", content: this.options.prompt }
155
- };
156
- (_a = proc.stdin) === null || _a === void 0 ? void 0 : _a.write(JSON.stringify(initialMessage) + "\n");
157
- const collected = {
158
- text: "",
159
- session: null,
160
- rateLimit: null,
161
- isRetryableError: false,
162
- nonRetryableReason: null,
163
- inputTokens: 0,
164
- outputTokens: 0
165
- };
166
- this._attachStreamParser(proc, collected);
167
- let settled = false;
168
- const settle = (outcome) => {
169
- if (settled) {
170
- return;
171
- }
172
- settled = true;
173
- this._process = null;
174
- if (outcome.ok) {
175
- resolve(outcome.value);
176
- }
177
- else {
178
- reject(outcome.error);
179
- }
180
- };
181
- proc.on("error", e => {
182
- settle({ ok: false, error: e instanceof Error ? e : new Error(String(e)) });
183
- });
184
- proc.on("exit", code => {
185
- if (collected.session) {
186
- this._sessionId = collected.session;
187
- }
188
- if (this._disposed) {
189
- settle({ ok: false, error: new Error("Claude disposed") });
190
- return;
191
- }
192
- if (collected.rateLimit) {
193
- settle({ ok: false, error: new RateLimitError(collected.rateLimit.retryAfterMs, collected.inputTokens, collected.outputTokens) });
194
- return;
195
- }
196
- if (collected.isRetryableError) {
197
- settle({ ok: false, error: new RetryableTransientError(collected.inputTokens, collected.outputTokens) });
198
- return;
199
- }
200
- if (collected.nonRetryableReason) {
201
- settle({ ok: false, error: new NonRetryableError(collected.nonRetryableReason) });
202
- return;
203
- }
204
- if (code === 0 || code === null) {
205
- settle({ ok: true, value: { text: collected.text, sessionId: collected.session, inputTokens: collected.inputTokens, outputTokens: collected.outputTokens } });
206
- }
207
- else {
208
- settle({ ok: false, error: new NonRetryableError(`Claude process exited with code ${code}`) });
209
- }
210
- });
211
- });
212
- }
213
- _attachStreamParser(proc, collected) {
214
- var _a, _b;
215
- let buffer = "";
216
- const handleLine = (line) => {
217
- var _a, _b, _c, _d, _e, _f, _g, _h;
218
- if (!line) {
219
- return;
220
- }
221
- let parsed = null;
222
- try {
223
- parsed = JSON.parse(line);
224
- }
225
- catch {
226
- return;
227
- }
228
- if (!parsed) {
229
- return;
230
- }
231
- if (parsed.session_id && !collected.session) {
232
- collected.session = parsed.session_id;
233
- }
234
- if (parsed.type === "rate_limit_event" && ((_a = parsed.rate_limit_info) === null || _a === void 0 ? void 0 : _a.status) === "rejected") {
235
- const info = parsed.rate_limit_info;
236
- const target = info.isUsingOverage && typeof info.overageResetsAt === "number"
237
- ? info.overageResetsAt
238
- : info.resetsAt;
239
- if (typeof target === "number") {
240
- const retryAfterMs = Math.max(0, target * 1000 - this._time.now());
241
- collected.rateLimit = new RateLimitError(retryAfterMs);
242
- }
243
- }
244
- else if (parsed.type === "result") {
245
- if (typeof parsed.result === "string") {
246
- collected.text = parsed.result;
247
- }
248
- const u = parsed.usage;
249
- if (u) {
250
- collected.inputTokens = ((_b = u.input_tokens) !== null && _b !== void 0 ? _b : 0) + ((_c = u.cache_creation_input_tokens) !== null && _c !== void 0 ? _c : 0) + ((_d = u.cache_read_input_tokens) !== null && _d !== void 0 ? _d : 0);
251
- collected.outputTokens = (_e = u.output_tokens) !== null && _e !== void 0 ? _e : 0;
252
- }
253
- if (parsed.is_error) {
254
- const subtype = parsed.subtype;
255
- const status = parsed.api_error_status;
256
- if (subtype === "error_max_turns" || subtype === "error_max_budget_usd" || subtype === "error_max_structured_output_retries") {
257
- collected.nonRetryableReason = subtype;
258
- }
259
- else if ((typeof status === "number" && (status >= 500 || status === 408 || status === 425)) ||
260
- status === null ||
261
- subtype === "error_during_execution") {
262
- collected.isRetryableError = true;
263
- }
264
- else {
265
- collected.nonRetryableReason = typeof status === "number" ? `HTTP ${status}` : "unknown error";
266
- }
267
- }
268
- }
269
- else if (parsed.type === "assistant" && ((_f = parsed.message) === null || _f === void 0 ? void 0 : _f.content)) {
270
- let text = "";
271
- for (const block of parsed.message.content) {
272
- if (block.type === "text" && typeof block.text === "string") {
273
- text += block.text;
274
- }
275
- }
276
- if (text) {
277
- collected.text = text;
278
- }
279
- }
280
- else if (parsed.type === "control_request" || parsed.type === "sdk_control_request") {
281
- this._handleControlRequest(parsed);
282
- }
283
- try {
284
- (_h = (_g = this.options).onEvent) === null || _h === void 0 ? void 0 : _h.call(_g, parsed);
285
- }
286
- catch (e) {
287
- if (this.options.onStderr) {
288
- this.options.onStderr(`onEvent handler threw: ${String(e)}\n`);
289
- }
290
- }
291
- };
292
- (_a = proc.stdout) === null || _a === void 0 ? void 0 : _a.on("data", chunk => {
293
- buffer += String(chunk);
294
- for (;;) {
295
- const nl = buffer.indexOf("\n");
296
- if (nl < 0) {
297
- break;
298
- }
299
- const line = buffer.slice(0, nl).replace(/\r$/, "");
300
- buffer = buffer.slice(nl + 1);
301
- handleLine(line);
302
- }
303
- });
304
- (_b = proc.stderr) === null || _b === void 0 ? void 0 : _b.on("data", chunk => {
305
- if (this.options.onStderr) {
306
- this.options.onStderr(String(chunk));
307
- }
308
- });
309
- }
310
- _scheduleWait(totalMs, chunkMs) {
311
- const abort = new AbortController();
312
- this._waitAbort = abort;
313
- return (0, wait_1.wait)(totalMs, chunkMs, this._time, abort.signal).then(() => {
314
- if (this._waitAbort === abort) {
315
- this._waitAbort = null;
316
- }
317
- });
318
- }
319
- _handleControlRequest(parsed) {
320
- var _a, _b;
321
- const req = parsed.request;
322
- if (!req) {
323
- return;
324
- }
325
- if (req.subtype !== "can_use_tool" && req.subtype !== "permission") {
326
- return;
327
- }
328
- const requestId = (_a = parsed.request_id) !== null && _a !== void 0 ? _a : req.request_id;
329
- if (typeof requestId !== "string" || requestId.length === 0) {
330
- return;
331
- }
332
- const toolName = req.tool_name;
333
- if (typeof toolName !== "string") {
334
- return;
335
- }
336
- const toolInput = (_b = req.input) !== null && _b !== void 0 ? _b : req.tool_input;
337
- const handler = this.options.onPermissionRequest;
338
- const responsePromise = handler
339
- ? handler({ request_id: requestId, tool_name: toolName, tool_input: toolInput })
340
- : Promise.resolve({
341
- behavior: "allow",
342
- updatedInput: typeof toolInput === "object" && toolInput !== null ? toolInput : {}
343
- });
344
- responsePromise.then(response => {
345
- this.sendControlResponse(requestId, response);
346
- }, err => {
347
- this.sendControlResponse(requestId, {
348
- behavior: "deny",
349
- message: err instanceof Error ? err.message : String(err)
350
- });
351
- });
352
- }
353
- async dispose() {
354
- var _a, _b, _c;
355
- if (this._disposed) {
356
- try {
357
- await this._runPromise;
358
- }
359
- catch {
360
- }
361
- return;
362
- }
363
- this._disposed = true;
364
- (_a = this._waitAbort) === null || _a === void 0 ? void 0 : _a.abort();
365
- const proc = this._process;
366
- if (proc) {
367
- const exitWaiter = new Promise(resolve => {
368
- proc.on("exit", () => resolve());
369
- });
370
- (_b = proc.stdin) === null || _b === void 0 ? void 0 : _b.end();
371
- proc.kill("SIGINT");
372
- this._killTimer = this._time.setTimeout(() => {
373
- this._killTimer = null;
374
- proc.kill("SIGTERM");
375
- }, 5000);
376
- await exitWaiter;
377
- (_c = this._killTimer) === null || _c === void 0 ? void 0 : _c.cancel();
378
- this._killTimer = null;
379
- }
380
- try {
381
- await this._runPromise;
382
- }
383
- catch {
384
- }
385
- }
386
- }
387
- exports.Claude = Claude;
@@ -1,34 +0,0 @@
1
- import { ClaudeResult } from "./Claude";
2
- import type { AskContext, ClaudeContext, OutputContext, TimeContext } from "./contexts";
3
- export type ClaudeSessionOptions = Readonly<{
4
- prompt: string;
5
- cwd?: string;
6
- initialSessionId?: string | null;
7
- forkFromSessionId?: string | null;
8
- onLongWaitStart?(kind: "rate-limit", endTimeMs: number): void;
9
- onLongWaitEnd?(): void;
10
- }>;
11
- export type ClaudeSessionContexts = Readonly<{
12
- claude: ClaudeContext;
13
- time: TimeContext;
14
- output: OutputContext;
15
- ask: AskContext;
16
- }>;
17
- export declare class ClaudeSession {
18
- private _options;
19
- private _contexts;
20
- private _disposed;
21
- private _claude;
22
- private _printedAnyOutputThisRun;
23
- private _seenTextThisMessage;
24
- private _textOpen;
25
- constructor(_options: ClaudeSessionOptions, _contexts: ClaudeSessionContexts);
26
- run(): Promise<ClaudeResult>;
27
- dispose(): Promise<void>;
28
- private _resetState;
29
- private _onPermissionRequest;
30
- private _handleAskUserQuestion;
31
- private _onEvent;
32
- private _emitText;
33
- private _closeOpenTextLine;
34
- }