flintmaker 0.1.0 → 0.2.1
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 +54 -6
- package/dist/chunks/chunk-2DDAUP6P.js +23 -0
- package/dist/chunks/chunk-2XK6FXMP.js +72 -0
- package/dist/chunks/chunk-5PRYZOW7.js +1 -0
- package/dist/chunks/chunk-EUPBID7K.js +1 -0
- package/dist/chunks/chunk-HEEZNIZR.js +9 -0
- package/dist/chunks/chunk-K3ZZ2VJH.js +6 -0
- package/dist/chunks/highlight-QJIBHZTN.js +7 -0
- package/dist/chunks/plain-EQXWFHTX.js +5 -0
- package/dist/chunks/render-Q3KDKHPR.js +1 -0
- package/dist/chunks/repl-LUJ6BCT7.js +27 -0
- package/dist/chunks/update-YGHTGBDV.js +17 -0
- package/dist/flint.js +53 -997
- package/package.json +9 -5
package/dist/flint.js
CHANGED
|
@@ -1,1000 +1,56 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
var
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
var
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
while (~index);
|
|
40
|
-
return result + string.substring(cursor);
|
|
41
|
-
}, createColors = (enabled = isColorSupported) => {
|
|
42
|
-
let f = enabled ? formatter : () => String;
|
|
43
|
-
return {
|
|
44
|
-
isColorSupported: enabled,
|
|
45
|
-
reset: f("\x1B[0m", "\x1B[0m"),
|
|
46
|
-
bold: f("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"),
|
|
47
|
-
dim: f("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"),
|
|
48
|
-
italic: f("\x1B[3m", "\x1B[23m"),
|
|
49
|
-
underline: f("\x1B[4m", "\x1B[24m"),
|
|
50
|
-
inverse: f("\x1B[7m", "\x1B[27m"),
|
|
51
|
-
hidden: f("\x1B[8m", "\x1B[28m"),
|
|
52
|
-
strikethrough: f("\x1B[9m", "\x1B[29m"),
|
|
53
|
-
black: f("\x1B[30m", "\x1B[39m"),
|
|
54
|
-
red: f("\x1B[31m", "\x1B[39m"),
|
|
55
|
-
green: f("\x1B[32m", "\x1B[39m"),
|
|
56
|
-
yellow: f("\x1B[33m", "\x1B[39m"),
|
|
57
|
-
blue: f("\x1B[34m", "\x1B[39m"),
|
|
58
|
-
magenta: f("\x1B[35m", "\x1B[39m"),
|
|
59
|
-
cyan: f("\x1B[36m", "\x1B[39m"),
|
|
60
|
-
white: f("\x1B[37m", "\x1B[39m"),
|
|
61
|
-
gray: f("\x1B[90m", "\x1B[39m"),
|
|
62
|
-
bgBlack: f("\x1B[40m", "\x1B[49m"),
|
|
63
|
-
bgRed: f("\x1B[41m", "\x1B[49m"),
|
|
64
|
-
bgGreen: f("\x1B[42m", "\x1B[49m"),
|
|
65
|
-
bgYellow: f("\x1B[43m", "\x1B[49m"),
|
|
66
|
-
bgBlue: f("\x1B[44m", "\x1B[49m"),
|
|
67
|
-
bgMagenta: f("\x1B[45m", "\x1B[49m"),
|
|
68
|
-
bgCyan: f("\x1B[46m", "\x1B[49m"),
|
|
69
|
-
bgWhite: f("\x1B[47m", "\x1B[49m"),
|
|
70
|
-
blackBright: f("\x1B[90m", "\x1B[39m"),
|
|
71
|
-
redBright: f("\x1B[91m", "\x1B[39m"),
|
|
72
|
-
greenBright: f("\x1B[92m", "\x1B[39m"),
|
|
73
|
-
yellowBright: f("\x1B[93m", "\x1B[39m"),
|
|
74
|
-
blueBright: f("\x1B[94m", "\x1B[39m"),
|
|
75
|
-
magentaBright: f("\x1B[95m", "\x1B[39m"),
|
|
76
|
-
cyanBright: f("\x1B[96m", "\x1B[39m"),
|
|
77
|
-
whiteBright: f("\x1B[97m", "\x1B[39m"),
|
|
78
|
-
bgBlackBright: f("\x1B[100m", "\x1B[49m"),
|
|
79
|
-
bgRedBright: f("\x1B[101m", "\x1B[49m"),
|
|
80
|
-
bgGreenBright: f("\x1B[102m", "\x1B[49m"),
|
|
81
|
-
bgYellowBright: f("\x1B[103m", "\x1B[49m"),
|
|
82
|
-
bgBlueBright: f("\x1B[104m", "\x1B[49m"),
|
|
83
|
-
bgMagentaBright: f("\x1B[105m", "\x1B[49m"),
|
|
84
|
-
bgCyanBright: f("\x1B[106m", "\x1B[49m"),
|
|
85
|
-
bgWhiteBright: f("\x1B[107m", "\x1B[49m")
|
|
86
|
-
};
|
|
87
|
-
};
|
|
88
|
-
module.exports = createColors();
|
|
89
|
-
module.exports.createColors = createColors;
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
// src/index.ts
|
|
94
|
-
import { spawn as spawn2 } from "node:child_process";
|
|
95
|
-
import { createInterface } from "node:readline/promises";
|
|
96
|
-
import { homedir as homedir2 } from "node:os";
|
|
97
|
-
|
|
98
|
-
// src/agent.ts
|
|
99
|
-
var ContextTooLarge = class extends Error {
|
|
100
|
-
}, DETAIL = {
|
|
101
|
-
list_files: (a) => String(a.path ?? "."),
|
|
102
|
-
read_file: (a) => `${a.path}${a.start_line ? `:${a.start_line}-${a.end_line ?? ""}` : ""}`,
|
|
103
|
-
search: (a) => `/${a.pattern}/ ${a.path ?? ""}`.trim(),
|
|
104
|
-
write_file: (a) => String(a.path),
|
|
105
|
-
edit_file: (a) => String(a.path),
|
|
106
|
-
run_command: (a) => String(a.command)
|
|
107
|
-
};
|
|
108
|
-
async function execute(ws, name, args, ui) {
|
|
109
|
-
switch (name) {
|
|
110
|
-
case "list_files":
|
|
111
|
-
return ws.listFiles(typeof args.path == "string" ? args.path : ".");
|
|
112
|
-
case "read_file":
|
|
113
|
-
return ws.readFile(String(args.path), num(args.start_line), num(args.end_line));
|
|
114
|
-
case "search":
|
|
115
|
-
return ws.search(String(args.pattern ?? ""), typeof args.path == "string" ? args.path : ".");
|
|
116
|
-
case "write_file":
|
|
117
|
-
case "edit_file": {
|
|
118
|
-
let change = await ws.plan(name, args);
|
|
119
|
-
return await ui.approveChange(change) ? (await change.apply(), `${change.created ? "Created" : "Updated"} ${change.path}.`) : "The user declined this change. Ask what they want instead, or try a different approach.";
|
|
120
|
-
}
|
|
121
|
-
case "run_command": {
|
|
122
|
-
let command = String(args.command ?? "").trim();
|
|
123
|
-
if (!command) throw new Error("command is required");
|
|
124
|
-
return await ui.approveCommand(command) ? ws.runCommand(command) : "The user declined to run this command.";
|
|
125
|
-
}
|
|
126
|
-
default:
|
|
127
|
-
throw new Error(`unknown tool ${name}`);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
var num = (v) => typeof v == "number" && Number.isFinite(v) ? v : void 0;
|
|
131
|
-
function compact(messages) {
|
|
132
|
-
let out = structuredClone(messages), tools = out.map((m, i) => ({ m, i })).filter((x) => x.m.role === "tool" && (x.m.content?.length ?? 0) > 400);
|
|
133
|
-
for (let { m } of tools.slice(0, Math.max(1, Math.ceil(tools.length / 2))))
|
|
134
|
-
m.role === "tool" && (m.content = `${m.content.slice(0, 300)}
|
|
135
|
-
[output trimmed to save context; read again if needed]`);
|
|
136
|
-
return tools.length === 0 && out.length > 4 && out.splice(1, 2), out;
|
|
137
|
-
}
|
|
138
|
-
async function runAgent(ws, start, step, ui, opts = {}) {
|
|
139
|
-
let messages = [...start];
|
|
140
|
-
for (let i = 0; i < (opts.maxSteps ?? 25); i++) {
|
|
141
|
-
let result = null;
|
|
142
|
-
for (let attempt = 0; attempt < 4 && !result; attempt++)
|
|
143
|
-
try {
|
|
144
|
-
result = await step(messages, ui.onDelta);
|
|
145
|
-
} catch (e) {
|
|
146
|
-
if (!(e instanceof ContextTooLarge)) throw e;
|
|
147
|
-
messages = compact(messages);
|
|
148
|
-
}
|
|
149
|
-
if (!result) throw new Error("This conversation is too long. Start a new one with /clear.");
|
|
150
|
-
if (result.toolCalls.length === 0)
|
|
151
|
-
return messages.push({ role: "assistant", content: result.text }), messages;
|
|
152
|
-
messages.push({
|
|
153
|
-
role: "assistant",
|
|
154
|
-
content: result.text || null,
|
|
155
|
-
tool_calls: result.toolCalls.map((c) => ({ id: c.id, type: "function", function: { name: c.name, arguments: c.arguments } }))
|
|
156
|
-
});
|
|
157
|
-
for (let call of result.toolCalls) {
|
|
158
|
-
let args = {};
|
|
159
|
-
try {
|
|
160
|
-
args = JSON.parse(call.arguments || "{}");
|
|
161
|
-
} catch {
|
|
162
|
-
}
|
|
163
|
-
ui.onToolStart(call.name, (DETAIL[call.name] ?? (() => ""))(args));
|
|
164
|
-
let content;
|
|
165
|
-
try {
|
|
166
|
-
content = await execute(ws, call.name, args, ui), ui.onToolResult(call.name, !0);
|
|
167
|
-
} catch (e) {
|
|
168
|
-
content = `Error: ${e.message}`, ui.onToolResult(call.name, !1, e.message);
|
|
169
|
-
}
|
|
170
|
-
messages.push({ role: "tool", tool_call_id: call.id, content });
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
return messages.push({ role: "assistant", content: "I stopped after many steps. Tell me how to continue." }), messages;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// src/sse.ts
|
|
177
|
-
function createSSEParser() {
|
|
178
|
-
let buffer = "";
|
|
179
|
-
return (chunk) => {
|
|
180
|
-
buffer += chunk;
|
|
181
|
-
let blocks = buffer.split(`
|
|
182
|
-
|
|
183
|
-
`);
|
|
184
|
-
return buffer = blocks.pop() ?? "", blocks.filter(Boolean).map((block) => {
|
|
185
|
-
let event = "message", data = [];
|
|
186
|
-
for (let line of block.split(`
|
|
187
|
-
`))
|
|
188
|
-
line.startsWith("event:") ? event = line.slice(6).trim() : line.startsWith("data:") && data.push(line.slice(5).replace(/^ /, ""));
|
|
189
|
-
return { event, data: data.join(`
|
|
190
|
-
`) };
|
|
191
|
-
});
|
|
192
|
-
};
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// src/api.ts
|
|
196
|
-
var FlintError = class extends Error {
|
|
197
|
-
constructor(message, reason = "error", status = 0) {
|
|
198
|
-
super(message);
|
|
199
|
-
this.reason = reason;
|
|
200
|
-
this.status = status;
|
|
201
|
-
}
|
|
202
|
-
reason;
|
|
203
|
-
status;
|
|
204
|
-
}, when = (iso) => iso ? new Date(iso).toLocaleString(void 0, { weekday: "short", hour: "2-digit", minute: "2-digit" }) : "soon";
|
|
205
|
-
async function failure(res, base) {
|
|
206
|
-
let body = await res.json().catch(() => ({})), ref = body.requestId ? ` (ref ${body.requestId})` : "";
|
|
207
|
-
switch (body.reason) {
|
|
208
|
-
case "signed_out":
|
|
209
|
-
return new FlintError("You are signed out. Run: flint login", "signed_out", res.status);
|
|
210
|
-
case "limit":
|
|
211
|
-
return new FlintError(`Out of credits until ${when(body.resetAt)}. Get more at ${base}/pricing`, "limit", res.status);
|
|
212
|
-
case "busy":
|
|
213
|
-
return new FlintError("FlintMaker is very busy right now. Please try again later.", "busy", res.status);
|
|
214
|
-
case "upgrade_required":
|
|
215
|
-
return new FlintError(`The smart model needs a paid plan: ${base}/pricing`, "upgrade_required", res.status);
|
|
216
|
-
case "in_progress":
|
|
217
|
-
return new FlintError("Another flint session is still working. Wait for it to finish.", "in_progress", res.status);
|
|
218
|
-
default:
|
|
219
|
-
return new FlintError(`Request failed (${res.status} ${body.reason ?? ""})${ref}`, body.reason ?? "error", res.status);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
var Client = class {
|
|
223
|
-
constructor(base, token) {
|
|
224
|
-
this.base = base;
|
|
225
|
-
this.token = token;
|
|
226
|
-
}
|
|
227
|
-
base;
|
|
228
|
-
token;
|
|
229
|
-
headers() {
|
|
230
|
-
return { "content-type": "application/json", "user-agent": "flint-cli", ...this.token ? { authorization: `Bearer ${this.token}` } : {} };
|
|
231
|
-
}
|
|
232
|
-
async json(path, init = {}) {
|
|
233
|
-
let res = await fetch(`${this.base}/api${path}`, { ...init, headers: { ...this.headers(), ...init.headers } });
|
|
234
|
-
if (!res.ok) throw await failure(res, this.base);
|
|
235
|
-
return await res.json();
|
|
236
|
-
}
|
|
237
|
-
me() {
|
|
238
|
-
return this.json("/me");
|
|
239
|
-
}
|
|
240
|
-
/** Device login: show the code, wait until the user approves in the browser. */
|
|
241
|
-
async deviceLogin(show) {
|
|
242
|
-
let start = await this.json("/auth/device", {
|
|
243
|
-
method: "POST",
|
|
244
|
-
body: "{}"
|
|
245
|
-
});
|
|
246
|
-
show(start.userCode, start.verifyUrl);
|
|
247
|
-
let deadline = Date.now() + start.expiresIn * 1e3;
|
|
248
|
-
for (; Date.now() < deadline; ) {
|
|
249
|
-
await new Promise((r) => setTimeout(r, start.interval * 1e3));
|
|
250
|
-
let res = await fetch(`${this.base}/api/auth/device/token`, { method: "POST", headers: this.headers(), body: JSON.stringify({ deviceCode: start.deviceCode }) });
|
|
251
|
-
if (res.status !== 428) {
|
|
252
|
-
if (res.ok) return await res.json();
|
|
253
|
-
throw new FlintError("The login code expired. Run flint login again.", "expired", res.status);
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
throw new FlintError("The login code expired. Run flint login again.", "expired");
|
|
257
|
-
}
|
|
258
|
-
/** A StepFn bound to this account: one /api/agent call, streamed. */
|
|
259
|
-
stepper(model, signal, onCredits) {
|
|
260
|
-
return async (messages, onDelta) => {
|
|
261
|
-
let res = await fetch(`${this.base}/api/agent`, {
|
|
262
|
-
method: "POST",
|
|
263
|
-
headers: this.headers(),
|
|
264
|
-
body: JSON.stringify({ messages, model: model() }),
|
|
265
|
-
signal: signal()
|
|
266
|
-
});
|
|
267
|
-
if (res.status === 413) throw new ContextTooLarge();
|
|
268
|
-
if (!res.ok || !res.body) throw await failure(res, this.base);
|
|
269
|
-
let parse = createSSEParser(), decoder = new TextDecoder(), text = "", toolCalls = [];
|
|
270
|
-
for await (let chunk of res.body)
|
|
271
|
-
for (let ev of parse(decoder.decode(chunk, { stream: !0 }))) {
|
|
272
|
-
let data = ev.data ? JSON.parse(ev.data) : {};
|
|
273
|
-
if (ev.event === "delta")
|
|
274
|
-
text += data.text, onDelta(data.text);
|
|
275
|
-
else if (ev.event === "tool_call") toolCalls.push(data);
|
|
276
|
-
else if (ev.event === "done") onCredits(data.credits, data.usage);
|
|
277
|
-
else if (ev.event === "error") throw new FlintError(`The model hit a problem; no credits were used.${data.requestId ? ` (ref ${data.requestId})` : ""}`, "model");
|
|
278
|
-
}
|
|
279
|
-
return { text, toolCalls };
|
|
280
|
-
};
|
|
281
|
-
}
|
|
282
|
-
};
|
|
283
|
-
|
|
284
|
-
// src/config.ts
|
|
285
|
-
import { chmodSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
286
|
-
import { homedir } from "node:os";
|
|
287
|
-
import { join } from "node:path";
|
|
288
|
-
var DEFAULT_API = "https://flintmaker.com", configDir = () => join(process.env.XDG_CONFIG_HOME || join(homedir(), ".config"), "flintmaker"), authPath = (dir = configDir()) => join(dir, "auth.json");
|
|
289
|
-
function loadAuth(dir = configDir()) {
|
|
290
|
-
try {
|
|
291
|
-
let a = JSON.parse(readFileSync(authPath(dir), "utf8"));
|
|
292
|
-
return typeof a?.token == "string" ? a : null;
|
|
293
|
-
} catch {
|
|
294
|
-
return null;
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
function saveAuth(a, dir = configDir()) {
|
|
298
|
-
mkdirSync(dir, { recursive: !0, mode: 448 }), writeFileSync(authPath(dir), JSON.stringify(a, null, 2), { mode: 384 }), chmodSync(authPath(dir), 384);
|
|
299
|
-
}
|
|
300
|
-
function clearAuth(dir = configDir()) {
|
|
301
|
-
existsSync(authPath(dir)) && rmSync(authPath(dir));
|
|
302
|
-
}
|
|
303
|
-
function apiBase(auth) {
|
|
304
|
-
return (process.env.FLINT_API || auth?.api || DEFAULT_API).replace(/\/$/, "");
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// src/tools.ts
|
|
308
|
-
import { spawn, execFileSync } from "node:child_process";
|
|
309
|
-
import { existsSync as existsSync2, lstatSync, realpathSync } from "node:fs";
|
|
310
|
-
import { mkdir, readdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
311
|
-
import { basename, dirname, isAbsolute, join as join2, relative, resolve, sep } from "node:path";
|
|
312
|
-
|
|
313
|
-
// node_modules/diff/libesm/diff/base.js
|
|
314
|
-
var Diff = class {
|
|
315
|
-
diff(oldStr, newStr, options = {}) {
|
|
316
|
-
let callback;
|
|
317
|
-
typeof options == "function" ? (callback = options, options = {}) : "callback" in options && (callback = options.callback);
|
|
318
|
-
let oldString = this.castInput(oldStr, options), newString = this.castInput(newStr, options), oldTokens = this.removeEmpty(this.tokenize(oldString, options)), newTokens = this.removeEmpty(this.tokenize(newString, options));
|
|
319
|
-
return this.diffWithOptionsObj(oldTokens, newTokens, options, callback);
|
|
320
|
-
}
|
|
321
|
-
diffWithOptionsObj(oldTokens, newTokens, options, callback) {
|
|
322
|
-
var _a;
|
|
323
|
-
let done = (value) => {
|
|
324
|
-
if (value = this.postProcess(value, options), callback) {
|
|
325
|
-
setTimeout(function() {
|
|
326
|
-
callback(value);
|
|
327
|
-
}, 0);
|
|
328
|
-
return;
|
|
329
|
-
} else
|
|
330
|
-
return value;
|
|
331
|
-
}, newLen = newTokens.length, oldLen = oldTokens.length, editLength = 1, maxEditLength = newLen + oldLen;
|
|
332
|
-
options.maxEditLength != null && (maxEditLength = Math.min(maxEditLength, options.maxEditLength));
|
|
333
|
-
let maxExecutionTime = (_a = options.timeout) !== null && _a !== void 0 ? _a : 1 / 0, abortAfterTimestamp = Date.now() + maxExecutionTime, bestPath = [{ oldPos: -1, lastComponent: void 0 }], newPos = this.extractCommon(bestPath[0], newTokens, oldTokens, 0, options);
|
|
334
|
-
if (bestPath[0].oldPos + 1 >= oldLen && newPos + 1 >= newLen)
|
|
335
|
-
return done(this.buildValues(bestPath[0].lastComponent, newTokens, oldTokens));
|
|
336
|
-
let minDiagonalToConsider = -1 / 0, maxDiagonalToConsider = 1 / 0, execEditLength = () => {
|
|
337
|
-
for (let diagonalPath = Math.max(minDiagonalToConsider, -editLength); diagonalPath <= Math.min(maxDiagonalToConsider, editLength); diagonalPath += 2) {
|
|
338
|
-
let basePath, removePath = bestPath[diagonalPath - 1], addPath = bestPath[diagonalPath + 1];
|
|
339
|
-
removePath && (bestPath[diagonalPath - 1] = void 0);
|
|
340
|
-
let canAdd = !1;
|
|
341
|
-
if (addPath) {
|
|
342
|
-
let addPathNewPos = addPath.oldPos - diagonalPath;
|
|
343
|
-
canAdd = addPath && 0 <= addPathNewPos && addPathNewPos < newLen;
|
|
344
|
-
}
|
|
345
|
-
let canRemove = removePath && removePath.oldPos + 1 < oldLen;
|
|
346
|
-
if (!canAdd && !canRemove) {
|
|
347
|
-
bestPath[diagonalPath] = void 0;
|
|
348
|
-
continue;
|
|
349
|
-
}
|
|
350
|
-
if (!canRemove || canAdd && removePath.oldPos < addPath.oldPos ? basePath = this.addToPath(addPath, !0, !1, 0, options) : basePath = this.addToPath(removePath, !1, !0, 1, options), newPos = this.extractCommon(basePath, newTokens, oldTokens, diagonalPath, options), basePath.oldPos + 1 >= oldLen && newPos + 1 >= newLen)
|
|
351
|
-
return done(this.buildValues(basePath.lastComponent, newTokens, oldTokens)) || !0;
|
|
352
|
-
bestPath[diagonalPath] = basePath, basePath.oldPos + 1 >= oldLen && (maxDiagonalToConsider = Math.min(maxDiagonalToConsider, diagonalPath - 1)), newPos + 1 >= newLen && (minDiagonalToConsider = Math.max(minDiagonalToConsider, diagonalPath + 1));
|
|
353
|
-
}
|
|
354
|
-
editLength++;
|
|
355
|
-
};
|
|
356
|
-
if (callback)
|
|
357
|
-
(function exec() {
|
|
358
|
-
setTimeout(function() {
|
|
359
|
-
if (editLength > maxEditLength || Date.now() > abortAfterTimestamp)
|
|
360
|
-
return callback(void 0);
|
|
361
|
-
execEditLength() || exec();
|
|
362
|
-
}, 0);
|
|
363
|
-
})();
|
|
364
|
-
else
|
|
365
|
-
for (; editLength <= maxEditLength && Date.now() <= abortAfterTimestamp; ) {
|
|
366
|
-
let ret = execEditLength();
|
|
367
|
-
if (ret)
|
|
368
|
-
return ret;
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
addToPath(path, added, removed, oldPosInc, options) {
|
|
372
|
-
let last = path.lastComponent;
|
|
373
|
-
return last && !options.oneChangePerToken && last.added === added && last.removed === removed ? {
|
|
374
|
-
oldPos: path.oldPos + oldPosInc,
|
|
375
|
-
lastComponent: { count: last.count + 1, added, removed, previousComponent: last.previousComponent }
|
|
376
|
-
} : {
|
|
377
|
-
oldPos: path.oldPos + oldPosInc,
|
|
378
|
-
lastComponent: { count: 1, added, removed, previousComponent: last }
|
|
379
|
-
};
|
|
380
|
-
}
|
|
381
|
-
extractCommon(basePath, newTokens, oldTokens, diagonalPath, options) {
|
|
382
|
-
let newLen = newTokens.length, oldLen = oldTokens.length, oldPos = basePath.oldPos, newPos = oldPos - diagonalPath, commonCount = 0;
|
|
383
|
-
for (; newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(oldTokens[oldPos + 1], newTokens[newPos + 1], options); )
|
|
384
|
-
newPos++, oldPos++, commonCount++, options.oneChangePerToken && (basePath.lastComponent = { count: 1, previousComponent: basePath.lastComponent, added: !1, removed: !1 });
|
|
385
|
-
return commonCount && !options.oneChangePerToken && (basePath.lastComponent = { count: commonCount, previousComponent: basePath.lastComponent, added: !1, removed: !1 }), basePath.oldPos = oldPos, newPos;
|
|
386
|
-
}
|
|
387
|
-
equals(left, right, options) {
|
|
388
|
-
return options.comparator ? options.comparator(left, right) : left === right || !!options.ignoreCase && left.toLowerCase() === right.toLowerCase();
|
|
389
|
-
}
|
|
390
|
-
removeEmpty(array) {
|
|
391
|
-
let ret = [];
|
|
392
|
-
for (let i = 0; i < array.length; i++)
|
|
393
|
-
array[i] && ret.push(array[i]);
|
|
394
|
-
return ret;
|
|
395
|
-
}
|
|
396
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
397
|
-
castInput(value, options) {
|
|
398
|
-
return value;
|
|
399
|
-
}
|
|
400
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
401
|
-
tokenize(value, options) {
|
|
402
|
-
return Array.from(value);
|
|
403
|
-
}
|
|
404
|
-
join(chars) {
|
|
405
|
-
return chars.join("");
|
|
406
|
-
}
|
|
407
|
-
postProcess(changeObjects, options) {
|
|
408
|
-
return changeObjects;
|
|
409
|
-
}
|
|
410
|
-
get useLongestToken() {
|
|
411
|
-
return !1;
|
|
412
|
-
}
|
|
413
|
-
buildValues(lastComponent, newTokens, oldTokens) {
|
|
414
|
-
let components = [], nextComponent;
|
|
415
|
-
for (; lastComponent; )
|
|
416
|
-
components.push(lastComponent), nextComponent = lastComponent.previousComponent, delete lastComponent.previousComponent, lastComponent = nextComponent;
|
|
417
|
-
components.reverse();
|
|
418
|
-
let componentLen = components.length, componentPos = 0, newPos = 0, oldPos = 0;
|
|
419
|
-
for (; componentPos < componentLen; componentPos++) {
|
|
420
|
-
let component = components[componentPos];
|
|
421
|
-
if (component.removed)
|
|
422
|
-
component.value = this.join(oldTokens.slice(oldPos, oldPos + component.count)), oldPos += component.count;
|
|
423
|
-
else {
|
|
424
|
-
if (!component.added && this.useLongestToken) {
|
|
425
|
-
let value = newTokens.slice(newPos, newPos + component.count);
|
|
426
|
-
value = value.map(function(value2, i) {
|
|
427
|
-
let oldValue = oldTokens[oldPos + i];
|
|
428
|
-
return oldValue.length > value2.length ? oldValue : value2;
|
|
429
|
-
}), component.value = this.join(value);
|
|
430
|
-
} else
|
|
431
|
-
component.value = this.join(newTokens.slice(newPos, newPos + component.count));
|
|
432
|
-
newPos += component.count, component.added || (oldPos += component.count);
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
return components;
|
|
436
|
-
}
|
|
437
|
-
};
|
|
438
|
-
|
|
439
|
-
// node_modules/diff/libesm/diff/line.js
|
|
440
|
-
var LineDiff = class extends Diff {
|
|
441
|
-
constructor() {
|
|
442
|
-
super(...arguments), this.tokenize = tokenize;
|
|
443
|
-
}
|
|
444
|
-
equals(left, right, options) {
|
|
445
|
-
return options.ignoreWhitespace ? ((!options.newlineIsToken || !left.includes(`
|
|
446
|
-
`)) && (left = left.trim()), (!options.newlineIsToken || !right.includes(`
|
|
447
|
-
`)) && (right = right.trim())) : options.ignoreNewlineAtEof && !options.newlineIsToken && (left.endsWith(`
|
|
448
|
-
`) && (left = left.slice(0, -1)), right.endsWith(`
|
|
449
|
-
`) && (right = right.slice(0, -1))), super.equals(left, right, options);
|
|
450
|
-
}
|
|
451
|
-
}, lineDiff = new LineDiff();
|
|
452
|
-
function diffLines(oldStr, newStr, options) {
|
|
453
|
-
return lineDiff.diff(oldStr, newStr, options);
|
|
454
|
-
}
|
|
455
|
-
function tokenize(value, options) {
|
|
456
|
-
options.stripTrailingCr && (value = value.replace(/\r\n/g, `
|
|
457
|
-
`));
|
|
458
|
-
let retLines = [], linesAndNewlines = value.split(/(\n|\r\n)/);
|
|
459
|
-
linesAndNewlines[linesAndNewlines.length - 1] || linesAndNewlines.pop();
|
|
460
|
-
for (let i = 0; i < linesAndNewlines.length; i++) {
|
|
461
|
-
let line = linesAndNewlines[i];
|
|
462
|
-
i % 2 && !options.newlineIsToken ? retLines[retLines.length - 1] += line : retLines.push(line);
|
|
463
|
-
}
|
|
464
|
-
return retLines;
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
// node_modules/diff/libesm/patch/create.js
|
|
468
|
-
function needsQuoting(s) {
|
|
469
|
-
for (let i = 0; i < s.length; i++)
|
|
470
|
-
if (s[i] < " " || s[i] > "~" || s[i] === '"' || s[i] === "\\")
|
|
471
|
-
return !0;
|
|
472
|
-
return !1;
|
|
473
|
-
}
|
|
474
|
-
function quoteFileNameIfNeeded(s) {
|
|
475
|
-
if (!needsQuoting(s))
|
|
476
|
-
return s;
|
|
477
|
-
let result = '"', bytes = new TextEncoder().encode(s), i = 0;
|
|
478
|
-
for (; i < bytes.length; ) {
|
|
479
|
-
let b = bytes[i];
|
|
480
|
-
b === 7 ? result += "\\a" : b === 8 ? result += "\\b" : b === 9 ? result += "\\t" : b === 10 ? result += "\\n" : b === 11 ? result += "\\v" : b === 12 ? result += "\\f" : b === 13 ? result += "\\r" : b === 34 ? result += '\\"' : b === 92 ? result += "\\\\" : b >= 32 && b <= 126 ? result += String.fromCharCode(b) : result += "\\" + b.toString(8).padStart(3, "0"), i++;
|
|
481
|
-
}
|
|
482
|
-
return result += '"', result;
|
|
483
|
-
}
|
|
484
|
-
var INCLUDE_HEADERS = {
|
|
485
|
-
includeIndex: !0,
|
|
486
|
-
includeUnderline: !0,
|
|
487
|
-
includeFileHeaders: !0
|
|
488
|
-
};
|
|
489
|
-
function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
|
|
490
|
-
let optionsObj;
|
|
491
|
-
options ? typeof options == "function" ? optionsObj = { callback: options } : optionsObj = options : optionsObj = {}, typeof optionsObj.context > "u" && (optionsObj.context = 4);
|
|
492
|
-
let context = optionsObj.context;
|
|
493
|
-
if (optionsObj.newlineIsToken)
|
|
494
|
-
throw new Error("newlineIsToken may not be used with patch-generation functions, only with diffing functions");
|
|
495
|
-
if (optionsObj.callback) {
|
|
496
|
-
let { callback } = optionsObj;
|
|
497
|
-
diffLines(oldStr, newStr, Object.assign(Object.assign({}, optionsObj), { callback: (diff) => {
|
|
498
|
-
let patch = diffLinesResultToPatch(diff);
|
|
499
|
-
callback(patch);
|
|
500
|
-
} }));
|
|
501
|
-
} else
|
|
502
|
-
return diffLinesResultToPatch(diffLines(oldStr, newStr, optionsObj));
|
|
503
|
-
function diffLinesResultToPatch(diff) {
|
|
504
|
-
if (!diff)
|
|
505
|
-
return;
|
|
506
|
-
diff.push({ value: "", lines: [] });
|
|
507
|
-
function contextLines(lines) {
|
|
508
|
-
return lines.map(function(entry) {
|
|
509
|
-
return " " + entry;
|
|
510
|
-
});
|
|
511
|
-
}
|
|
512
|
-
let hunks = [], oldRangeStart = 0, newRangeStart = 0, curRange = [], oldLine = 1, newLine = 1;
|
|
513
|
-
for (let i = 0; i < diff.length; i++) {
|
|
514
|
-
let current = diff[i], lines = current.lines || splitLines(current.value);
|
|
515
|
-
if (current.lines = lines, current.added || current.removed) {
|
|
516
|
-
if (!oldRangeStart) {
|
|
517
|
-
let prev = diff[i - 1];
|
|
518
|
-
oldRangeStart = oldLine, newRangeStart = newLine, prev && (curRange = context > 0 ? contextLines(prev.lines.slice(-context)) : [], oldRangeStart -= curRange.length, newRangeStart -= curRange.length);
|
|
519
|
-
}
|
|
520
|
-
for (let line of lines)
|
|
521
|
-
curRange.push((current.added ? "+" : "-") + line);
|
|
522
|
-
current.added ? newLine += lines.length : oldLine += lines.length;
|
|
523
|
-
} else {
|
|
524
|
-
if (oldRangeStart)
|
|
525
|
-
if (lines.length <= context * 2 && i < diff.length - 2)
|
|
526
|
-
for (let line of contextLines(lines))
|
|
527
|
-
curRange.push(line);
|
|
528
|
-
else {
|
|
529
|
-
let contextSize = Math.min(lines.length, context);
|
|
530
|
-
for (let line of contextLines(lines.slice(0, contextSize)))
|
|
531
|
-
curRange.push(line);
|
|
532
|
-
let hunk = {
|
|
533
|
-
oldStart: oldRangeStart,
|
|
534
|
-
oldLines: oldLine - oldRangeStart + contextSize,
|
|
535
|
-
newStart: newRangeStart,
|
|
536
|
-
newLines: newLine - newRangeStart + contextSize,
|
|
537
|
-
lines: curRange
|
|
538
|
-
};
|
|
539
|
-
hunks.push(hunk), oldRangeStart = 0, newRangeStart = 0, curRange = [];
|
|
540
|
-
}
|
|
541
|
-
oldLine += lines.length, newLine += lines.length;
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
for (let hunk of hunks)
|
|
545
|
-
for (let i = 0; i < hunk.lines.length; i++)
|
|
546
|
-
hunk.lines[i].endsWith(`
|
|
547
|
-
`) ? hunk.lines[i] = hunk.lines[i].slice(0, -1) : (hunk.lines.splice(i + 1, 0, "\"), i++);
|
|
548
|
-
return {
|
|
549
|
-
oldFileName,
|
|
550
|
-
newFileName,
|
|
551
|
-
oldHeader,
|
|
552
|
-
newHeader,
|
|
553
|
-
hunks
|
|
554
|
-
};
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
function formatPatch(patch, headerOptions) {
|
|
558
|
-
var _a, _b, _c, _d, _e, _f;
|
|
559
|
-
if (headerOptions || (headerOptions = INCLUDE_HEADERS), Array.isArray(patch)) {
|
|
560
|
-
if (patch.length > 1 && !headerOptions.includeFileHeaders && !patch.every((p) => p.isGit))
|
|
561
|
-
throw new Error("Cannot omit file headers on a multi-file patch. (The result would be unparseable; how would a tool trying to apply the patch know which changes are to which file?)");
|
|
562
|
-
return patch.map((p) => formatPatch(p, headerOptions)).join(`
|
|
563
|
-
`);
|
|
564
|
-
}
|
|
565
|
-
let ret = [];
|
|
566
|
-
if (patch.isGit) {
|
|
567
|
-
if (headerOptions = INCLUDE_HEADERS, !patch.oldFileName)
|
|
568
|
-
throw new Error("oldFileName must be specified for Git patches");
|
|
569
|
-
if (!patch.newFileName)
|
|
570
|
-
throw new Error("newFileName must be specified for Git patches");
|
|
571
|
-
let gitOldName = patch.oldFileName, gitNewName = patch.newFileName;
|
|
572
|
-
patch.isCreate && gitOldName === "/dev/null" ? gitOldName = gitNewName.replace(/^b\//, "a/") : patch.isDelete && gitNewName === "/dev/null" && (gitNewName = gitOldName.replace(/^a\//, "b/")), ret.push("diff --git " + quoteFileNameIfNeeded(gitOldName) + " " + quoteFileNameIfNeeded(gitNewName)), patch.isDelete && ret.push("deleted file mode " + ((_a = patch.oldMode) !== null && _a !== void 0 ? _a : "100644")), patch.isCreate && ret.push("new file mode " + ((_b = patch.newMode) !== null && _b !== void 0 ? _b : "100644")), patch.oldMode && patch.newMode && !patch.isDelete && !patch.isCreate && (ret.push("old mode " + patch.oldMode), ret.push("new mode " + patch.newMode)), patch.isRename && (ret.push("rename from " + quoteFileNameIfNeeded(((_c = patch.oldFileName) !== null && _c !== void 0 ? _c : "").replace(/^a\//, ""))), ret.push("rename to " + quoteFileNameIfNeeded(((_d = patch.newFileName) !== null && _d !== void 0 ? _d : "").replace(/^b\//, "")))), patch.isCopy && (ret.push("copy from " + quoteFileNameIfNeeded(((_e = patch.oldFileName) !== null && _e !== void 0 ? _e : "").replace(/^a\//, ""))), ret.push("copy to " + quoteFileNameIfNeeded(((_f = patch.newFileName) !== null && _f !== void 0 ? _f : "").replace(/^b\//, ""))));
|
|
573
|
-
} else
|
|
574
|
-
headerOptions.includeIndex && patch.oldFileName == patch.newFileName && patch.oldFileName !== void 0 && ret.push("Index: " + patch.oldFileName), headerOptions.includeUnderline && ret.push("===================================================================");
|
|
575
|
-
let hasHunks = patch.hunks.length > 0;
|
|
576
|
-
headerOptions.includeFileHeaders && patch.oldFileName !== void 0 && patch.newFileName !== void 0 && (!patch.isGit || hasHunks) && (ret.push("--- " + quoteFileNameIfNeeded(patch.oldFileName) + (patch.oldHeader ? " " + patch.oldHeader : "")), ret.push("+++ " + quoteFileNameIfNeeded(patch.newFileName) + (patch.newHeader ? " " + patch.newHeader : "")));
|
|
577
|
-
for (let i = 0; i < patch.hunks.length; i++) {
|
|
578
|
-
let hunk = patch.hunks[i], oldStart = hunk.oldLines === 0 ? hunk.oldStart - 1 : hunk.oldStart, newStart = hunk.newLines === 0 ? hunk.newStart - 1 : hunk.newStart;
|
|
579
|
-
ret.push("@@ -" + oldStart + "," + hunk.oldLines + " +" + newStart + "," + hunk.newLines + " @@");
|
|
580
|
-
for (let line of hunk.lines)
|
|
581
|
-
ret.push(line);
|
|
582
|
-
}
|
|
583
|
-
return ret.join(`
|
|
584
|
-
`) + `
|
|
585
|
-
`;
|
|
586
|
-
}
|
|
587
|
-
function createTwoFilesPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
|
|
588
|
-
if (typeof options == "function" && (options = { callback: options }), options?.callback) {
|
|
589
|
-
let { callback } = options;
|
|
590
|
-
structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, Object.assign(Object.assign({}, options), { callback: (patchObj) => {
|
|
591
|
-
callback(patchObj ? formatPatch(patchObj, options.headerOptions) : void 0);
|
|
592
|
-
} }));
|
|
593
|
-
} else {
|
|
594
|
-
let patchObj = structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options);
|
|
595
|
-
return patchObj ? formatPatch(patchObj, options?.headerOptions) : void 0;
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
function splitLines(text) {
|
|
599
|
-
let hasTrailingNl = text.endsWith(`
|
|
600
|
-
`), result = text.split(`
|
|
601
|
-
`).map((line) => line + `
|
|
602
|
-
`);
|
|
603
|
-
return hasTrailingNl ? result.pop() : result.push(result.pop().slice(0, -1)), result;
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
// src/tools.ts
|
|
607
|
-
var IGNORED_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", ".nuxt", ".svelte-kit", "coverage", ".venv", "venv", "__pycache__", ".turbo", ".cache", "Pods", "DerivedData"]), SECRET = /^(\.env(\..+)?|\.envrc|\.dev\.vars|\.netrc|\.git-credentials|\.npmrc|\.pypirc|.*\.(pem|key|p12|pfx|keystore|jks|tfvars|tfstate)|id_(rsa|dsa|ecdsa|ed25519)(\.pub)?|credentials(\.json)?|.*service[-_]?account.*\.json|.*-credentials\.json)$/i, SECRET_OK = /^\.env\.(example|sample|template)$/i, MAX_READ = 1e5, MAX_LIST = 400, MAX_OUTPUT = 12e3, Workspace = class {
|
|
608
|
-
root;
|
|
609
|
-
constructor(root) {
|
|
610
|
-
this.root = realpathSync(root);
|
|
611
|
-
}
|
|
612
|
-
/** Resolve a project-relative path, refusing anything outside the root (incl. via symlinks) and secret files. */
|
|
613
|
-
resolve(path) {
|
|
614
|
-
if (typeof path != "string" || path.trim() === "") throw new Error("path is required");
|
|
615
|
-
let p = path.trim();
|
|
616
|
-
if (isAbsolute(p)) throw new Error(`${p} is outside the project`);
|
|
617
|
-
let abs = resolve(this.root, p);
|
|
618
|
-
if (abs !== this.root && !abs.startsWith(this.root + sep)) throw new Error(`${p} is outside the project`);
|
|
619
|
-
let cur = this.root;
|
|
620
|
-
for (let seg of relative(this.root, abs).split(sep).filter(Boolean)) {
|
|
621
|
-
cur = join2(cur, seg);
|
|
622
|
-
let info;
|
|
623
|
-
try {
|
|
624
|
-
info = lstatSync(cur);
|
|
625
|
-
} catch {
|
|
626
|
-
break;
|
|
627
|
-
}
|
|
628
|
-
if (info.isSymbolicLink()) {
|
|
629
|
-
let real;
|
|
630
|
-
try {
|
|
631
|
-
real = realpathSync(cur);
|
|
632
|
-
} catch {
|
|
633
|
-
throw new Error(`${p} goes through a broken symlink; refusing`);
|
|
634
|
-
}
|
|
635
|
-
if (real !== this.root && !real.startsWith(this.root + sep)) throw new Error(`${p} is outside the project`);
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
let name = basename(abs);
|
|
639
|
-
if (SECRET.test(name) && !SECRET_OK.test(name)) throw new Error(`${p} looks like a secret file; flint does not read or change it`);
|
|
640
|
-
return abs;
|
|
641
|
-
}
|
|
642
|
-
rel(abs) {
|
|
643
|
-
return relative(this.root, abs) || ".";
|
|
644
|
-
}
|
|
645
|
-
async listFiles(path = ".") {
|
|
646
|
-
let base = this.resolve(path), files;
|
|
647
|
-
try {
|
|
648
|
-
files = execFileSync("git", ["ls-files", "--cached", "--others", "--exclude-standard", "--", this.rel(base)], {
|
|
649
|
-
cwd: this.root,
|
|
650
|
-
encoding: "utf8",
|
|
651
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
652
|
-
maxBuffer: 16 * 1024 * 1024
|
|
653
|
-
}).split(`
|
|
654
|
-
`).filter(Boolean);
|
|
655
|
-
} catch {
|
|
656
|
-
files = [], await this.walk(base, files);
|
|
657
|
-
}
|
|
658
|
-
return files.sort(), files.slice(0, MAX_LIST).join(`
|
|
659
|
-
`) + (files.length > MAX_LIST ? `
|
|
660
|
-
\u2026 ${files.length - MAX_LIST} more files` : "") || "(empty)";
|
|
661
|
-
}
|
|
662
|
-
async walk(dir, out) {
|
|
663
|
-
if (!(out.length > 5e3))
|
|
664
|
-
for (let entry of await readdir(dir, { withFileTypes: !0 }))
|
|
665
|
-
entry.isDirectory() ? IGNORED_DIRS.has(entry.name) || await this.walk(join2(dir, entry.name), out) : entry.isFile() && out.push(this.rel(join2(dir, entry.name)));
|
|
666
|
-
}
|
|
667
|
-
async readFile(path, start, end) {
|
|
668
|
-
let abs = this.resolve(path), info = await stat(abs);
|
|
669
|
-
if (!info.isFile()) throw new Error(`${path} is not a file`);
|
|
670
|
-
let buf = await readFile(abs);
|
|
671
|
-
if (buf.subarray(0, 8e3).includes(0)) return `[binary file, ${info.size} bytes]`;
|
|
672
|
-
let lines = buf.toString("utf8").split(`
|
|
673
|
-
`);
|
|
674
|
-
lines.at(-1) === "" && lines.pop();
|
|
675
|
-
let from = Math.max(1, start ?? 1), to = Math.min(lines.length, end ?? lines.length), out = "";
|
|
676
|
-
for (let i = from; i <= to; i++) {
|
|
677
|
-
let line = `${i} ${lines[i - 1]}`;
|
|
678
|
-
if (out.length + line.length > MAX_READ) {
|
|
679
|
-
out += `
|
|
680
|
-
[truncated at line ${i - 1} of ${lines.length}; read more with start_line]`;
|
|
681
|
-
break;
|
|
682
|
-
}
|
|
683
|
-
out += (out ? `
|
|
684
|
-
` : "") + line;
|
|
685
|
-
}
|
|
686
|
-
return out;
|
|
687
|
-
}
|
|
688
|
-
async search(pattern, path = ".") {
|
|
689
|
-
let re;
|
|
690
|
-
try {
|
|
691
|
-
re = new RegExp(pattern);
|
|
692
|
-
} catch {
|
|
693
|
-
throw new Error("invalid regular expression");
|
|
694
|
-
}
|
|
695
|
-
let files = (await this.listFiles(path)).split(`
|
|
696
|
-
`).filter((f) => f && !f.startsWith("\u2026 ")), hits = [];
|
|
697
|
-
for (let f of files) {
|
|
698
|
-
if (hits.length >= 200) break;
|
|
699
|
-
let text;
|
|
700
|
-
try {
|
|
701
|
-
let abs = this.resolve(f);
|
|
702
|
-
if ((await stat(abs)).size > 4e5) continue;
|
|
703
|
-
let buf = await readFile(abs);
|
|
704
|
-
if (buf.subarray(0, 8e3).includes(0)) continue;
|
|
705
|
-
text = buf.toString("utf8");
|
|
706
|
-
} catch {
|
|
707
|
-
continue;
|
|
708
|
-
}
|
|
709
|
-
text.split(`
|
|
710
|
-
`).forEach((line, i) => {
|
|
711
|
-
hits.length < 200 && re.test(line) && hits.push(`${f}:${i + 1}:${line.slice(0, 300)}`);
|
|
712
|
-
});
|
|
713
|
-
}
|
|
714
|
-
return hits.join(`
|
|
715
|
-
`) || "No matches.";
|
|
716
|
-
}
|
|
717
|
-
/** Prepare a file change and its diff. Nothing is written until apply(). */
|
|
718
|
-
async plan(tool, args) {
|
|
719
|
-
let abs = this.resolve(args.path), path = this.rel(abs), before = existsSync2(abs) ? await readFile(abs, "utf8") : null, after;
|
|
720
|
-
if (tool === "write_file") {
|
|
721
|
-
if (typeof args.content != "string") throw new Error("content is required");
|
|
722
|
-
after = args.content;
|
|
723
|
-
} else {
|
|
724
|
-
if (before === null) throw new Error(`${path} does not exist`);
|
|
725
|
-
let oldText = args.old_text, newText = args.new_text;
|
|
726
|
-
if (typeof oldText != "string" || oldText === "" || typeof newText != "string") throw new Error("old_text and new_text are required");
|
|
727
|
-
let count = before.split(oldText).length - 1;
|
|
728
|
-
if (count === 0) throw new Error(`old_text not found in ${path}`);
|
|
729
|
-
if (count > 1) throw new Error(`old_text matches ${count} times in ${path}; include more context`);
|
|
730
|
-
after = before.replace(oldText, () => newText);
|
|
731
|
-
}
|
|
732
|
-
let diff = createTwoFilesPatch(`a/${path}`, `b/${path}`, before ?? "", after, "", "", { context: 3 });
|
|
733
|
-
return {
|
|
734
|
-
path,
|
|
735
|
-
diff,
|
|
736
|
-
created: before === null,
|
|
737
|
-
apply: async () => {
|
|
738
|
-
await mkdir(dirname(abs), { recursive: !0 }), await writeFile(abs, after);
|
|
739
|
-
}
|
|
740
|
-
};
|
|
741
|
-
}
|
|
742
|
-
runCommand(command, timeoutMs = 12e4) {
|
|
743
|
-
return new Promise((resolvePromise) => {
|
|
744
|
-
let child = spawn("/bin/sh", ["-c", command], { cwd: this.root, detached: !0, env: { ...process.env, FORCE_COLOR: "0", CI: "1" } }), head = "", tail = "", dropped = 0, take = (chunk) => {
|
|
745
|
-
let s = chunk.toString("utf8");
|
|
746
|
-
if (head.length < 4e3) {
|
|
747
|
-
let room = 4e3 - head.length;
|
|
748
|
-
head += s.slice(0, room), tail += s.slice(room);
|
|
749
|
-
} else tail += s;
|
|
750
|
-
tail.length > 8e3 && (dropped += tail.length - 8e3, tail = tail.slice(-8e3));
|
|
751
|
-
};
|
|
752
|
-
child.stdout.on("data", take), child.stderr.on("data", take);
|
|
753
|
-
let timedOut = !1, timer = setTimeout(() => {
|
|
754
|
-
timedOut = !0;
|
|
755
|
-
try {
|
|
756
|
-
process.kill(-child.pid, "SIGKILL");
|
|
757
|
-
} catch {
|
|
758
|
-
child.kill("SIGKILL");
|
|
759
|
-
}
|
|
760
|
-
}, timeoutMs);
|
|
761
|
-
child.on("close", (code) => {
|
|
762
|
-
clearTimeout(timer);
|
|
763
|
-
let body = head + (dropped ? `
|
|
764
|
-
[\u2026 ${dropped} characters omitted \u2026]
|
|
765
|
-
` : "") + tail, status = timedOut ? `[timed out after ${Math.round(timeoutMs / 1e3)}s]` : `[exit code ${code}]`;
|
|
766
|
-
resolvePromise(`${body.slice(0, MAX_OUTPUT)}
|
|
767
|
-
${status}`.trim());
|
|
768
|
-
}), child.on("error", (err) => {
|
|
769
|
-
clearTimeout(timer), resolvePromise(`[could not run command: ${err.message}]`);
|
|
770
|
-
});
|
|
771
|
-
});
|
|
772
|
-
}
|
|
773
|
-
};
|
|
774
|
-
|
|
775
|
-
// src/ui.ts
|
|
776
|
-
var import_picocolors = __toESM(require_picocolors(), 1);
|
|
777
|
-
function sanitize(text) {
|
|
778
|
-
return text.replace(/\x1b\][^\x07\x1b]*(\x07|\x1b\\)?/g, "").replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, "").replace(/\x1b[@-Z\\-_]/g, "").replace(/[\x00-\x08\x0b-\x1f\x7f-\x9f]/g, "");
|
|
779
|
-
}
|
|
780
|
-
var truecolor = /truecolor|24bit/i.test(process.env.COLORTERM ?? "") && import_picocolors.default.isColorSupported, FIRE = [
|
|
781
|
-
[123, 47, 247],
|
|
782
|
-
[232, 51, 107],
|
|
783
|
-
[255, 122, 26],
|
|
784
|
-
[255, 194, 74]
|
|
785
|
-
];
|
|
786
|
-
function fire(text) {
|
|
787
|
-
if (!truecolor) return import_picocolors.default.bold(import_picocolors.default.yellow(text));
|
|
788
|
-
let n = Math.max(1, text.length - 1);
|
|
789
|
-
return [...text].map((ch, i) => {
|
|
790
|
-
let t = i / n * (FIRE.length - 1), a = FIRE[Math.floor(t)], b = FIRE[Math.min(FIRE.length - 1, Math.floor(t) + 1)], f = t - Math.floor(t), [r, g, bl] = a.map((v, k) => Math.round(v + (b[k] - v) * f));
|
|
791
|
-
return `\x1B[1;38;2;${r};${g};${bl}m${ch}\x1B[0m`;
|
|
792
|
-
}).join("");
|
|
793
|
-
}
|
|
794
|
-
var ember = (s) => truecolor ? `\x1B[38;2;255;122;26m${s}\x1B[0m` : import_picocolors.default.yellow(s), crimson = (s) => truecolor ? `\x1B[38;2;232;51;107m${s}\x1B[0m` : import_picocolors.default.magenta(s), dim = import_picocolors.default.dim, bold = import_picocolors.default.bold;
|
|
795
|
-
function banner(version, lines) {
|
|
796
|
-
console.log(), console.log(` ${ember("\u2726")} ${fire("flintmaker")} ${dim(`v${version}`)}`);
|
|
797
|
-
for (let l of lines) console.log(` ${dim(l)}`);
|
|
798
|
-
console.log();
|
|
799
|
-
}
|
|
800
|
-
var MarkdownStream = class {
|
|
801
|
-
buf = "";
|
|
802
|
-
inCode = !1;
|
|
803
|
-
started = !1;
|
|
804
|
-
write(raw) {
|
|
805
|
-
let text = sanitize(raw);
|
|
806
|
-
this.started || (process.stdout.write(`
|
|
807
|
-
`), this.started = !0), this.buf += text;
|
|
808
|
-
let nl;
|
|
809
|
-
for (; (nl = this.buf.indexOf(`
|
|
810
|
-
`)) >= 0; )
|
|
811
|
-
this.line(this.buf.slice(0, nl)), this.buf = this.buf.slice(nl + 1);
|
|
812
|
-
}
|
|
813
|
-
end() {
|
|
814
|
-
this.buf && this.line(this.buf), this.buf = "", this.started && process.stdout.write(`
|
|
815
|
-
`), this.started = !1, this.inCode = !1;
|
|
816
|
-
}
|
|
817
|
-
line(l) {
|
|
818
|
-
if (/^\s*```/.test(l)) {
|
|
819
|
-
this.inCode = !this.inCode, process.stdout.write(dim(this.inCode ? ` \u250C\u2500 ${l.replace(/^\s*```/, "") || "code"}` : " \u2514\u2500") + `
|
|
820
|
-
`);
|
|
821
|
-
return;
|
|
822
|
-
}
|
|
823
|
-
if (this.inCode) return void process.stdout.write(` ${dim("\u2502")} ${import_picocolors.default.cyan(l)}
|
|
824
|
-
`);
|
|
825
|
-
let out = l.replace(/`([^`]+)`/g, (_, c) => import_picocolors.default.cyan(c)).replace(/\*\*([^*]+)\*\*/g, (_, b) => bold(b));
|
|
826
|
-
/^#{1,3} /.test(l) ? out = bold(ember(l.replace(/^#{1,3} /, ""))) : /^\s*[-*] /.test(l) && (out = out.replace(/^(\s*)[-*] /, (_, s) => `${s}${ember("\u2022")} `)), process.stdout.write(` ${out}
|
|
827
|
-
`);
|
|
828
|
-
}
|
|
829
|
-
}, Spinner = class {
|
|
830
|
-
timer;
|
|
831
|
-
i = 0;
|
|
832
|
-
start(label = "thinking") {
|
|
833
|
-
if (!process.stdout.isTTY || this.timer) return;
|
|
834
|
-
let frames = ["\u2726", "\u2727", "\u22C6", "\u2727"];
|
|
835
|
-
this.timer = setInterval(() => {
|
|
836
|
-
process.stdout.write(`\r ${ember(frames[this.i++ % frames.length])} ${dim(label + "\u2026")} `);
|
|
837
|
-
}, 120);
|
|
838
|
-
}
|
|
839
|
-
stop() {
|
|
840
|
-
this.timer && (clearInterval(this.timer), this.timer = void 0, process.stdout.write("\r\x1B[2K"));
|
|
841
|
-
}
|
|
842
|
-
};
|
|
843
|
-
function printDiff(diff) {
|
|
844
|
-
let lines = sanitize(diff).split(`
|
|
845
|
-
`).slice(2);
|
|
846
|
-
for (let l of lines)
|
|
847
|
-
l.startsWith("===") || l.startsWith("Index:") || (l.startsWith("+++") || l.startsWith("---") ? console.log(dim(` ${l}`)) : l.startsWith("+") ? console.log(import_picocolors.default.green(` ${l}`)) : l.startsWith("-") ? console.log(import_picocolors.default.red(` ${l}`)) : l.startsWith("@@") ? console.log(import_picocolors.default.cyan(` ${l}`)) : console.log(dim(` ${l}`)));
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
// src/index.ts
|
|
851
|
-
var VERSION = "0.1.0", HELP = `
|
|
852
|
-
${bold("flint")} \u2014 the FlintMaker coding agent for your terminal
|
|
853
|
-
|
|
854
|
-
${bold("Usage")}
|
|
855
|
-
flint start an interactive session in this folder
|
|
856
|
-
flint "task" do one task, then exit
|
|
857
|
-
flint login connect this computer to your FlintMaker account
|
|
858
|
-
flint logout disconnect
|
|
859
|
-
flint whoami show account and credits
|
|
860
|
-
|
|
861
|
-
${bold("Options")}
|
|
862
|
-
--smart use the smart model (paid plans, more credits)
|
|
863
|
-
--yes approve every change and command (careful!)
|
|
2
|
+
import{a as ne,b as D,c as ie,d as re,e as R}from"./chunks/chunk-EUPBID7K.js";import{e as F,f as N,h as se,i as le,s as ae}from"./chunks/chunk-2DDAUP6P.js";import{b as g,c as oe}from"./chunks/chunk-5PRYZOW7.js";import{c as V,e as G,f as I,g as B,h as X,i as K,j as $,k as J,l as Q,m as Z,o as ee,p as te}from"./chunks/chunk-K3ZZ2VJH.js";import{spawn as we,execFileSync as ye}from"node:child_process";import{existsSync as xe,lstatSync as be,realpathSync as H}from"node:fs";import{mkdir as ve,readdir as Ee,readFile as U,stat as ce,writeFile as ke}from"node:fs/promises";import{basename as fe,dirname as _e,join as z,relative as de,resolve as Pe,sep as $e}from"node:path";var C=class{diff(e,n,t={}){let i;typeof t=="function"?(i=t,t={}):"callback"in t&&(i=t.callback);let c=this.castInput(e,t),s=this.castInput(n,t),l=this.removeEmpty(this.tokenize(c,t)),r=this.removeEmpty(this.tokenize(s,t));return this.diffWithOptionsObj(l,r,t,i)}diffWithOptionsObj(e,n,t,i){var c;let s=u=>{if(u=this.postProcess(u,t),i){setTimeout(function(){i(u)},0);return}else return u},l=n.length,r=e.length,f=1,a=l+r;t.maxEditLength!=null&&(a=Math.min(a,t.maxEditLength));let d=(c=t.timeout)!==null&&c!==void 0?c:1/0,m=Date.now()+d,h=[{oldPos:-1,lastComponent:void 0}],w=this.extractCommon(h[0],n,e,0,t);if(h[0].oldPos+1>=r&&w+1>=l)return s(this.buildValues(h[0].lastComponent,n,e));let y=-1/0,x=1/0,E=()=>{for(let u=Math.max(y,-f);u<=Math.min(x,f);u+=2){let p,b=h[u-1],v=h[u+1];b&&(h[u-1]=void 0);let P=!1;if(v){let q=v.oldPos-u;P=v&&0<=q&&q<l}let L=b&&b.oldPos+1<r;if(!P&&!L){h[u]=void 0;continue}if(!L||P&&b.oldPos<v.oldPos?p=this.addToPath(v,!0,!1,0,t):p=this.addToPath(b,!1,!0,1,t),w=this.extractCommon(p,n,e,u,t),p.oldPos+1>=r&&w+1>=l)return s(this.buildValues(p.lastComponent,n,e))||!0;h[u]=p,p.oldPos+1>=r&&(x=Math.min(x,u-1)),w+1>=l&&(y=Math.max(y,u+1))}f++};if(i)(function u(){setTimeout(function(){if(f>a||Date.now()>m)return i(void 0);E()||u()},0)})();else for(;f<=a&&Date.now()<=m;){let u=E();if(u)return u}}addToPath(e,n,t,i,c){let s=e.lastComponent;return s&&!c.oneChangePerToken&&s.added===n&&s.removed===t?{oldPos:e.oldPos+i,lastComponent:{count:s.count+1,added:n,removed:t,previousComponent:s.previousComponent}}:{oldPos:e.oldPos+i,lastComponent:{count:1,added:n,removed:t,previousComponent:s}}}extractCommon(e,n,t,i,c){let s=n.length,l=t.length,r=e.oldPos,f=r-i,a=0;for(;f+1<s&&r+1<l&&this.equals(t[r+1],n[f+1],c);)f++,r++,a++,c.oneChangePerToken&&(e.lastComponent={count:1,previousComponent:e.lastComponent,added:!1,removed:!1});return a&&!c.oneChangePerToken&&(e.lastComponent={count:a,previousComponent:e.lastComponent,added:!1,removed:!1}),e.oldPos=r,f}equals(e,n,t){return t.comparator?t.comparator(e,n):e===n||!!t.ignoreCase&&e.toLowerCase()===n.toLowerCase()}removeEmpty(e){let n=[];for(let t=0;t<e.length;t++)e[t]&&n.push(e[t]);return n}castInput(e,n){return e}tokenize(e,n){return Array.from(e)}join(e){return e.join("")}postProcess(e,n){return e}get useLongestToken(){return!1}buildValues(e,n,t){let i=[],c;for(;e;)i.push(e),c=e.previousComponent,delete e.previousComponent,e=c;i.reverse();let s=i.length,l=0,r=0,f=0;for(;l<s;l++){let a=i[l];if(a.removed)a.value=this.join(t.slice(f,f+a.count)),f+=a.count;else{if(!a.added&&this.useLongestToken){let d=n.slice(r,r+a.count);d=d.map(function(m,h){let w=t[f+h];return w.length>m.length?w:m}),a.value=this.join(d)}else a.value=this.join(n.slice(r,r+a.count));r+=a.count,a.added||(f+=a.count)}}return i}};var M=class extends C{constructor(){super(...arguments),this.tokenize=he}equals(e,n,t){return t.ignoreWhitespace?((!t.newlineIsToken||!e.includes(`
|
|
3
|
+
`))&&(e=e.trim()),(!t.newlineIsToken||!n.includes(`
|
|
4
|
+
`))&&(n=n.trim())):t.ignoreNewlineAtEof&&!t.newlineIsToken&&(e.endsWith(`
|
|
5
|
+
`)&&(e=e.slice(0,-1)),n.endsWith(`
|
|
6
|
+
`)&&(n=n.slice(0,-1))),super.equals(e,n,t)}},me=new M;function j(o,e,n){return me.diff(o,e,n)}function he(o,e){e.stripTrailingCr&&(o=o.replace(/\r\n/g,`
|
|
7
|
+
`));let n=[],t=o.split(/(\n|\r\n)/);t[t.length-1]||t.pop();for(let i=0;i<t.length;i++){let c=t[i];i%2&&!e.newlineIsToken?n[n.length-1]+=c:n.push(c)}return n}function pe(o){for(let e=0;e<o.length;e++)if(o[e]<" "||o[e]>"~"||o[e]==='"'||o[e]==="\\")return!0;return!1}function k(o){if(!pe(o))return o;let e='"',n=new TextEncoder().encode(o),t=0;for(;t<n.length;){let i=n[t];i===7?e+="\\a":i===8?e+="\\b":i===9?e+="\\t":i===10?e+="\\n":i===11?e+="\\v":i===12?e+="\\f":i===13?e+="\\r":i===34?e+='\\"':i===92?e+="\\\\":i>=32&&i<=126?e+=String.fromCharCode(i):e+="\\"+i.toString(8).padStart(3,"0"),t++}return e+='"',e}var A={includeIndex:!0,includeUnderline:!0,includeFileHeaders:!0};function O(o,e,n,t,i,c,s){let l;s?typeof s=="function"?l={callback:s}:l=s:l={},typeof l.context>"u"&&(l.context=4);let r=l.context;if(l.newlineIsToken)throw new Error("newlineIsToken may not be used with patch-generation functions, only with diffing functions");if(l.callback){let{callback:a}=l;j(n,t,Object.assign(Object.assign({},l),{callback:d=>{let m=f(d);a(m)}}))}else return f(j(n,t,l));function f(a){if(!a)return;a.push({value:"",lines:[]});function d(u){return u.map(function(p){return" "+p})}let m=[],h=0,w=0,y=[],x=1,E=1;for(let u=0;u<a.length;u++){let p=a[u],b=p.lines||ge(p.value);if(p.lines=b,p.added||p.removed){if(!h){let v=a[u-1];h=x,w=E,v&&(y=r>0?d(v.lines.slice(-r)):[],h-=y.length,w-=y.length)}for(let v of b)y.push((p.added?"+":"-")+v);p.added?E+=b.length:x+=b.length}else{if(h)if(b.length<=r*2&&u<a.length-2)for(let v of d(b))y.push(v);else{let v=Math.min(b.length,r);for(let L of d(b.slice(0,v)))y.push(L);let P={oldStart:h,oldLines:x-h+v,newStart:w,newLines:E-w+v,lines:y};m.push(P),h=0,w=0,y=[]}x+=b.length,E+=b.length}}for(let u of m)for(let p=0;p<u.lines.length;p++)u.lines[p].endsWith(`
|
|
8
|
+
`)?u.lines[p]=u.lines[p].slice(0,-1):(u.lines.splice(p+1,0,"\"),p++);return{oldFileName:o,newFileName:e,oldHeader:i,newHeader:c,hunks:m}}}function T(o,e){var n,t,i,c,s,l;if(e||(e=A),Array.isArray(o)){if(o.length>1&&!e.includeFileHeaders&&!o.every(a=>a.isGit))throw new Error("Cannot omit file headers on a multi-file patch. (The result would be unparseable; how would a tool trying to apply the patch know which changes are to which file?)");return o.map(a=>T(a,e)).join(`
|
|
9
|
+
`)}let r=[];if(o.isGit){if(e=A,!o.oldFileName)throw new Error("oldFileName must be specified for Git patches");if(!o.newFileName)throw new Error("newFileName must be specified for Git patches");let a=o.oldFileName,d=o.newFileName;o.isCreate&&a==="/dev/null"?a=d.replace(/^b\//,"a/"):o.isDelete&&d==="/dev/null"&&(d=a.replace(/^a\//,"b/")),r.push("diff --git "+k(a)+" "+k(d)),o.isDelete&&r.push("deleted file mode "+((n=o.oldMode)!==null&&n!==void 0?n:"100644")),o.isCreate&&r.push("new file mode "+((t=o.newMode)!==null&&t!==void 0?t:"100644")),o.oldMode&&o.newMode&&!o.isDelete&&!o.isCreate&&(r.push("old mode "+o.oldMode),r.push("new mode "+o.newMode)),o.isRename&&(r.push("rename from "+k(((i=o.oldFileName)!==null&&i!==void 0?i:"").replace(/^a\//,""))),r.push("rename to "+k(((c=o.newFileName)!==null&&c!==void 0?c:"").replace(/^b\//,"")))),o.isCopy&&(r.push("copy from "+k(((s=o.oldFileName)!==null&&s!==void 0?s:"").replace(/^a\//,""))),r.push("copy to "+k(((l=o.newFileName)!==null&&l!==void 0?l:"").replace(/^b\//,""))))}else e.includeIndex&&o.oldFileName==o.newFileName&&o.oldFileName!==void 0&&r.push("Index: "+o.oldFileName),e.includeUnderline&&r.push("===================================================================");let f=o.hunks.length>0;e.includeFileHeaders&&o.oldFileName!==void 0&&o.newFileName!==void 0&&(!o.isGit||f)&&(r.push("--- "+k(o.oldFileName)+(o.oldHeader?" "+o.oldHeader:"")),r.push("+++ "+k(o.newFileName)+(o.newHeader?" "+o.newHeader:"")));for(let a=0;a<o.hunks.length;a++){let d=o.hunks[a],m=d.oldLines===0?d.oldStart-1:d.oldStart,h=d.newLines===0?d.newStart-1:d.newStart;r.push("@@ -"+m+","+d.oldLines+" +"+h+","+d.newLines+" @@");for(let w of d.lines)r.push(w)}return r.join(`
|
|
10
|
+
`)+`
|
|
11
|
+
`}function W(o,e,n,t,i,c,s){if(typeof s=="function"&&(s={callback:s}),s?.callback){let{callback:l}=s;O(o,e,n,t,i,c,Object.assign(Object.assign({},s),{callback:r=>{l(r?T(r,s.headerOptions):void 0)}}))}else{let l=O(o,e,n,t,i,c,s);return l?T(l,s?.headerOptions):void 0}}function ge(o){let e=o.endsWith(`
|
|
12
|
+
`),n=o.split(`
|
|
13
|
+
`).map(t=>t+`
|
|
14
|
+
`);return e?n.pop():n.push(n.pop().slice(0,-1)),n}var Ce=new Set(["node_modules",".git","dist","build",".next",".nuxt",".svelte-kit","coverage",".venv","venv","__pycache__",".turbo",".cache","Pods","DerivedData"]),Le=/^(\.env(\..+)?|\.envrc|\.dev\.vars|\.netrc|\.git-credentials|\.npmrc|\.pypirc|.*\.(pem|key|p12|pfx|keystore|jks|tfvars|tfstate)|id_(rsa|dsa|ecdsa|ed25519)(\.pub)?|credentials(\.json)?|.*service[-_]?account.*\.json|.*-credentials\.json)$/i,Te=/^\.env\.(example|sample|template)$/i,Se=1e5,Y=400,Fe=12e3,S=class{root;constructor(e){this.root=H(e)}resolve(e){if(typeof e!="string"||e.trim()==="")throw new Error("path is required");let n=e.trim(),t=G(n);if(t)throw new Error(t);let i=Pe(this.root,n);if(!I(this.root,i))throw new Error(`${n} is outside the project`);let c=this.root;for(let l of de(this.root,i).split($e).filter(Boolean)){c=z(c,l);let r;try{r=be(c)}catch{break}if(r.isSymbolicLink()){let f;try{f=H(c)}catch{throw new Error(`${n} goes through a broken symlink; refusing`)}if(!I(this.root,f))throw new Error(`${n} is outside the project`)}}let s=[B(fe(i))];if(V())try{s.push(fe(H.native(i)))}catch{}if(s.some(l=>Le.test(l)&&!Te.test(l)))throw new Error(`${n} looks like a secret file; flint does not read or change it`);return i}rel(e){return X(de(this.root,e))||"."}async listFiles(e="."){let n=this.resolve(e),t;try{t=ye("git",["ls-files","--cached","--others","--exclude-standard","--",this.rel(n)],{cwd:this.root,encoding:"utf8",stdio:["ignore","pipe","ignore"],windowsHide:!0,maxBuffer:16*1024*1024}).split(/\r?\n/).filter(Boolean)}catch{t=[],await this.walk(n,t)}return t.sort(),t.slice(0,Y).join(`
|
|
15
|
+
`)+(t.length>Y?`
|
|
16
|
+
\u2026 ${t.length-Y} more files`:"")||"(empty)"}async walk(e,n){if(!(n.length>5e3))for(let t of await Ee(e,{withFileTypes:!0}))t.isDirectory()?Ce.has(t.name)||await this.walk(z(e,t.name),n):t.isFile()&&n.push(this.rel(z(e,t.name)))}async readFile(e,n,t){let i=this.resolve(e),c=await ce(i);if(!c.isFile())throw new Error(`${e} is not a file`);let s=await U(i);if(s.subarray(0,8e3).includes(0))return`[binary file, ${c.size} bytes]`;let l=s.toString("utf8").split(/\r?\n/);l.at(-1)===""&&l.pop();let r=Math.max(1,n??1),f=Math.min(l.length,t??l.length),a="";for(let d=r;d<=f;d++){let m=`${d} ${l[d-1]}`;if(a.length+m.length>Se){a+=`
|
|
17
|
+
[truncated at line ${d-1} of ${l.length}; read more with start_line]`;break}a+=(a?`
|
|
18
|
+
`:"")+m}return a}async search(e,n="."){let t;try{t=new RegExp(e)}catch{throw new Error("invalid regular expression")}let i=(await this.listFiles(n)).split(`
|
|
19
|
+
`).filter(s=>s&&!s.startsWith("\u2026 ")),c=[];for(let s of i){if(c.length>=200)break;let l;try{let r=this.resolve(s);if((await ce(r)).size>4e5)continue;let f=await U(r);if(f.subarray(0,8e3).includes(0))continue;l=f.toString("utf8")}catch{continue}l.split(/\r?\n/).forEach((r,f)=>{c.length<200&&t.test(r)&&c.push(`${s}:${f+1}:${r.slice(0,300)}`)})}return c.join(`
|
|
20
|
+
`)||"No matches."}async plan(e,n){let t=this.resolve(n.path),i=this.rel(t),c=xe(t)?await U(t,"utf8"):null,s=c!==null&&K(c)==="crlf",l=c!==null&&s?$(c):c,r;if(e==="write_file"){if(typeof n.content!="string")throw new Error("content is required");r=s?$(n.content):n.content}else{if(l===null)throw new Error(`${i} does not exist`);if(typeof n.old_text!="string"||n.old_text===""||typeof n.new_text!="string")throw new Error("old_text and new_text are required");let d=s?$(n.old_text):n.old_text,m=s?$(n.new_text):n.new_text,h=l.split(d).length-1;if(h===0)throw new Error(`old_text not found in ${i}`);if(h>1)throw new Error(`old_text matches ${h} times in ${i}; include more context`);r=l.replace(d,()=>m)}let f=W(`a/${i}`,`b/${i}`,l??"",r,"","",{context:3}),a=s?J(r):r;return{path:i,diff:f,created:c===null,apply:async()=>{await ve(_e(t),{recursive:!0}),await ke(t,a)}}}runCommand(e,n=12e4,t){return new Promise(i=>{if(t?.aborted)return i("[interrupted by the user before it started]");let c=Q(e),s;try{s=we(c.file,c.args,{...c.options,cwd:this.root,env:{...process.env,FORCE_COLOR:"0",CI:"1"}})}catch(x){return i(`[could not run command: ${x.message}]`)}let l="",r="",f=0,a=x=>{let E=x.toString("utf8");if(l.length<4e3){let u=4e3-l.length;l+=E.slice(0,u),r+=E.slice(u)}else r+=E;r.length>8e3&&(f+=r.length-8e3,r=r.slice(-8e3))};s.stdout.on("data",a),s.stderr.on("data",a);let d=!1,m=!1,h=()=>Z(s.pid,process.platform,()=>s.kill("SIGKILL")),w=setTimeout(()=>{d=!0,h()},n),y=()=>{m=!0,h()};t?.addEventListener("abort",y,{once:!0}),s.on("close",x=>{clearTimeout(w),t?.removeEventListener("abort",y);let E=(l+(f?`
|
|
21
|
+
[\u2026 ${f} characters omitted \u2026]
|
|
22
|
+
`:"")+r).replace(/\r\n/g,`
|
|
23
|
+
`),u=m?"[interrupted by the user]":d?`[timed out after ${Math.round(n/1e3)}s]`:`[exit code ${x}]`;i(`${E.slice(0,Fe)}
|
|
24
|
+
${u}`.trim())}),s.on("error",x=>{clearTimeout(w),t?.removeEventListener("abort",y),i(`[could not run command: ${x.message}]`)})})}};var _="0.2.1",Ne=()=>`
|
|
25
|
+
${g.ember("\u2726")} ${g.bold(oe("FlintMaker"))} ${g.bold("flint")} ${g.grey(`v${_}`)} ${g.grey("\u2014 the AI coding agent for your terminal")}
|
|
26
|
+
|
|
27
|
+
${g.bold("Usage")}
|
|
28
|
+
flint start an interactive session in this folder
|
|
29
|
+
flint "task" do one task, then exit (plain output, good for scripts)
|
|
30
|
+
flint login connect this computer to your FlintMaker account
|
|
31
|
+
flint logout disconnect
|
|
32
|
+
flint whoami show account and credits
|
|
33
|
+
flint update update flint now (it also updates itself automatically)
|
|
34
|
+
|
|
35
|
+
${g.bold("Options")}
|
|
36
|
+
--smart use the Smart model (paid plans, more credits)
|
|
37
|
+
--plan plan only: read the project, change nothing
|
|
38
|
+
--yes approve every change and command (careful!)
|
|
864
39
|
--help, --version
|
|
865
40
|
|
|
866
|
-
${bold("In a session")}
|
|
867
|
-
/help
|
|
868
|
-
|
|
869
|
-
function
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
async function login() {
|
|
883
|
-
let client = new Client(apiBase(null));
|
|
884
|
-
console.log();
|
|
885
|
-
let result = await client.deviceLogin((code, url) => {
|
|
886
|
-
console.log(` ${ember("\u2726")} Your code: ${bold(code)}`), console.log(` ${dim("Opening")} ${url}`), console.log(` ${dim("Approve it in the browser. Waiting\u2026")}`), openBrowser(url);
|
|
887
|
-
});
|
|
888
|
-
saveAuth({ token: result.token, email: result.user.email, api: client.base }), console.log(`
|
|
889
|
-
${ember("\u2713")} Signed in as ${bold(result.user.email)}. Run ${bold("flint")} in any project folder.
|
|
890
|
-
`);
|
|
891
|
-
}
|
|
892
|
-
async function main() {
|
|
893
|
-
let { flags, rest } = parseArgs(process.argv.slice(2));
|
|
894
|
-
if (flags.has("--version")) return console.log(VERSION);
|
|
895
|
-
if (flags.has("--help") || rest[0] === "help") return console.log(HELP);
|
|
896
|
-
if (rest[0] === "login") return login();
|
|
897
|
-
if (rest[0] === "logout")
|
|
898
|
-
return clearAuth(), console.log(`
|
|
899
|
-
Signed out. ${dim("Disconnect old terminals any time on your account page.")}
|
|
900
|
-
`);
|
|
901
|
-
if (!loadAuth() && (console.log(`
|
|
902
|
-
${ember("\u2726")} Welcome to flint. First, connect your FlintMaker account:
|
|
903
|
-
`), await login(), rest.length === 0 && !process.stdin.isTTY))
|
|
904
|
-
return;
|
|
905
|
-
let saved = loadAuth(), base = apiBase(saved);
|
|
906
|
-
if (base !== saved.api) throw new FlintError(`This token belongs to ${saved.api}. Run flint login to use ${base}.`);
|
|
907
|
-
let client = new Client(base, saved.token);
|
|
908
|
-
if (rest[0] === "whoami") {
|
|
909
|
-
let me2 = await client.me();
|
|
910
|
-
return console.log(`
|
|
911
|
-
${bold(me2.user.email)} \xB7 ${me2.user.plan} \xB7 ${creditsLine(me2.usage)}
|
|
912
|
-
`);
|
|
913
|
-
}
|
|
914
|
-
let ws = new Workspace(process.cwd()), model = flags.has("--smart") ? "smart" : "standard", autoYes = flags.has("--yes"), rl = process.stdin.isTTY && process.stdout.isTTY ? createInterface({ input: process.stdin, output: process.stdout, terminal: !0 }) : null, alwaysChanges = autoYes, alwaysCommands = autoYes, controller, lastUsage = null, ask = async (q, defaultYes) => {
|
|
915
|
-
if (!rl) return "n";
|
|
916
|
-
let a = (await rl.question(q)).trim().toLowerCase();
|
|
917
|
-
return a === "" ? defaultYes ? "y" : "n" : a === "a" ? "a" : a === "y" || a === "yes" ? "y" : "n";
|
|
918
|
-
}, md = new MarkdownStream(), spinner = new Spinner(), ui = {
|
|
919
|
-
onDelta: (t) => {
|
|
920
|
-
spinner.stop(), md.write(t);
|
|
921
|
-
},
|
|
922
|
-
onToolStart: (name, detail) => {
|
|
923
|
-
spinner.stop(), md.end();
|
|
924
|
-
let d = sanitize(detail);
|
|
925
|
-
console.log(` ${ember("\u23FA")} ${bold(name.replace("_", " "))} ${dim(name === "run_command" || d.length <= 80 ? d : `${d.slice(0, 77)}\u2026`)}`);
|
|
926
|
-
},
|
|
927
|
-
onToolResult: (_name, ok, summary) => {
|
|
928
|
-
ok || console.log(` ${crimson("\u2717")} ${dim(sanitize(summary ?? "failed"))}`), spinner.start("working");
|
|
929
|
-
},
|
|
930
|
-
approveChange: async (change) => {
|
|
931
|
-
if (spinner.stop(), printDiff(change.diff), alwaysChanges) return !0;
|
|
932
|
-
let a = await ask(` ${change.created ? "Create" : "Apply changes to"} ${bold(sanitize(change.path))}? ${dim("[Y]es / [n]o / [a]lways this session")} `, !0);
|
|
933
|
-
return a === "a" && (alwaysChanges = !0), a !== "n";
|
|
934
|
-
},
|
|
935
|
-
approveCommand: async (command) => {
|
|
936
|
-
if (spinner.stop(), alwaysCommands) return !0;
|
|
937
|
-
let a = await ask(` Run ${bold(JSON.stringify(sanitize(command)))}? ${dim("[y]es / [N]o / [a]lways this session")} `, !1);
|
|
938
|
-
return a === "a" && (alwaysCommands = !0), a !== "n";
|
|
939
|
-
}
|
|
940
|
-
}, step = client.stepper(
|
|
941
|
-
() => model,
|
|
942
|
-
() => controller?.signal,
|
|
943
|
-
(_credits, usage) => {
|
|
944
|
-
lastUsage = usage;
|
|
945
|
-
}
|
|
946
|
-
), history = [], run = async (task) => {
|
|
947
|
-
controller = new AbortController(), spinner.start();
|
|
948
|
-
try {
|
|
949
|
-
history = await runAgent(ws, [...history, { role: "user", content: task }], step, ui);
|
|
950
|
-
} catch (e) {
|
|
951
|
-
spinner.stop(), md.end(), e.name === "AbortError" ? console.log(dim(`
|
|
952
|
-
Stopped.`)) : console.log(`
|
|
953
|
-
${crimson("\u2717")} ${e.message}`), e instanceof FlintError && e.reason === "signed_out" && clearAuth();
|
|
954
|
-
} finally {
|
|
955
|
-
spinner.stop(), md.end(), controller = void 0;
|
|
956
|
-
}
|
|
957
|
-
lastUsage && console.log(dim(` ${creditsLine(lastUsage)}`));
|
|
958
|
-
};
|
|
959
|
-
if (rest.length > 0) {
|
|
960
|
-
await run(rest.join(" ")), rl?.close();
|
|
961
|
-
return;
|
|
962
|
-
}
|
|
963
|
-
let me = await client.me().catch((e) => {
|
|
964
|
-
throw e;
|
|
965
|
-
});
|
|
966
|
-
if (lastUsage = me.usage, banner(VERSION, [`${me.user.email} \xB7 ${me.user.plan} plan \xB7 ${creditsLine(me.usage)}`, shortPath(ws.root), "Describe a task. /help for commands, Ctrl+C to stop."]), !rl) throw new FlintError('Interactive mode needs a terminal. Pass a task: flint "fix the failing test"');
|
|
967
|
-
for (rl.on("SIGINT", () => {
|
|
968
|
-
controller ? controller.abort() : (console.log(dim(`
|
|
969
|
-
Bye.`)), process.exit(0));
|
|
970
|
-
}); ; ) {
|
|
971
|
-
let line = (await rl.question(`${crimson("\u276F")} `)).trim();
|
|
972
|
-
if (line) {
|
|
973
|
-
if (line === "/exit" || line === "/quit") break;
|
|
974
|
-
if (line === "/help") {
|
|
975
|
-
console.log(HELP);
|
|
976
|
-
continue;
|
|
977
|
-
}
|
|
978
|
-
if (line === "/clear") {
|
|
979
|
-
history = [], console.log(dim(" New conversation."));
|
|
980
|
-
continue;
|
|
981
|
-
}
|
|
982
|
-
if (line === "/smart" || line === "/standard") {
|
|
983
|
-
model = line.slice(1), console.log(dim(` Model: ${model}`));
|
|
984
|
-
continue;
|
|
985
|
-
}
|
|
986
|
-
if (line === "/credits") {
|
|
987
|
-
let u = (await client.me()).usage;
|
|
988
|
-
console.log(dim(` ${creditsLine(u)}`));
|
|
989
|
-
continue;
|
|
990
|
-
}
|
|
991
|
-
await run(line);
|
|
992
|
-
}
|
|
993
|
-
}
|
|
994
|
-
rl.close();
|
|
995
|
-
}
|
|
996
|
-
main().catch((e) => {
|
|
997
|
-
console.error(`
|
|
998
|
-
${crimson("\u2717")} ${e.message}
|
|
999
|
-
`), process.exit(1);
|
|
1000
|
-
});
|
|
41
|
+
${g.bold("In a session")}
|
|
42
|
+
/help /model /mode /clear /compact /credits /init /login /logout /exit
|
|
43
|
+
@path attaches a file \xB7 shift+tab switches mode \xB7 esc interrupts
|
|
44
|
+
`;function Ie(o){let e=new Set(o.filter(t=>t.startsWith("--"))),n=o.filter(t=>!t.startsWith("--"));return{flags:e,rest:n}}async function ue(){let o=new N(R(null));console.log();let e=await o.deviceLogin((n,t)=>{console.log(` ${g.ember("\u2726")} Your code: ${g.bold(g.gold(n))}`),console.log(` ${g.grey("Opening")} ${t}`),console.log(` ${g.grey("Approve it in the browser. Waiting\u2026")}`),ne(t)});ie({token:e.token,email:e.user.email,api:o.base}),console.log(`
|
|
45
|
+
${g.green("\u2713")} Signed in as ${g.bold(e.user.email)}. Run ${g.bold("flint")} in any project folder.
|
|
46
|
+
`)}async function De(){let o="";process.stdin.setEncoding("utf8");for await(let e of process.stdin)o+=e;return o.trim()}function Re(){setTimeout(()=>{import("./chunks/update-YGHTGBDV.js").then(o=>o.autoUpdate({current:_,notify:e=>le(e),preload:()=>Promise.all([import("./chunks/repl-LUJ6BCT7.js"),import("./chunks/plain-EQXWFHTX.js"),import("./chunks/highlight-QJIBHZTN.js")])})).catch(()=>{})},200).unref()}async function Me(){if(!ee())for(let m of[process.stdout,process.stderr])m.isTTY&&te(m);let{flags:o,rest:e}=Ie(process.argv.slice(2));if(o.has("--version")||o.has("-v"))return console.log(_);if(o.has("--help")||e[0]==="help"||e[0]==="-h")return console.log(Ne());if(e[0]==="login")return ue();if(e[0]==="__update"&&e[1]){let{updateWorker:m}=await import("./chunks/update-YGHTGBDV.js");return m(e[1])}if(e[0]==="update"&&e.length===1){let{updateNow:m}=await import("./chunks/update-YGHTGBDV.js");return m(_,h=>console.log(` ${h}`))}if(e[0]==="logout")return re(),console.log(`
|
|
47
|
+
Signed out. ${g.grey("Disconnect old terminals any time on your account page.")}
|
|
48
|
+
`);let n=!!process.stdin.isTTY&&!!process.stdout.isTTY;if(!D()){if(!n)return process.stderr.write(`flint: not signed in. Run: flint login
|
|
49
|
+
`),1;let{welcome:m,shortPath:h}=await import("./chunks/render-Q3KDKHPR.js"),{homedir:w}=await import("node:os"),y=Math.max(40,process.stdout.columns||80);console.log();let x={version:_,model:o.has("--smart")?"smart":"standard",cwd:h(process.cwd(),w(),28),status:"not signed in yet"};for(let E of m(x,y))console.log(E);console.log(`
|
|
50
|
+
${g.ember("\u2726")} Welcome to ${g.bold("flint")}. Let's connect your FlintMaker account (opening your browser):`),await ue()}let t=D(),i=R(t);if(i!==t.api)throw new F(`This token belongs to ${t.api}. Run flint login to use ${i}.`);let c=new N(i,t.token);if(e[0]==="whoami"){let m=await c.me();return console.log(`
|
|
51
|
+
${g.bold(m.user.email)} \xB7 ${m.user.plan} \xB7 ${ae(m.usage)}
|
|
52
|
+
`)}let s=new S(process.cwd()),l=o.has("--smart")?"smart":"standard",r=o.has("--plan")?"plan":"ask",f=o.has("--yes");Re();let a=e.join(" ").trim();if(!a&&!process.stdin.isTTY&&(a=await De()),a||!n){if(!a)throw new F('Nothing to do. Pass a task: flint "fix the failing test"');let{oneShot:m}=await import("./chunks/plain-EQXWFHTX.js");return m(a,{client:c,ws:s,model:l,autoYes:f,mode:r})}let{interactive:d}=await import("./chunks/repl-LUJ6BCT7.js");await d({version:_,client:c,ws:s,model:l,autoYes:f,mode:r})}Me().then(o=>{typeof o=="number"&&o!==0&&(process.exitCode=o),setTimeout(()=>process.exit(),20).unref()},o=>{let e=se(o);process.stderr.write(`
|
|
53
|
+
${g.red("\u2717")} ${g.bold(e.title)}
|
|
54
|
+
${e.lines.map(n=>` ${n}
|
|
55
|
+
`).join("")}
|
|
56
|
+
`),process.exit(1)});
|