diffsplain 0.8.3 → 0.10.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/README.md +26 -5
- package/dist/assets/{angular-html-e4D_THVy.js → angular-html-2u-u5H-N.js} +1 -1
- package/dist/assets/{angular-ts-Cx3870JF.js → angular-ts-4_HVGlKp.js} +1 -1
- package/dist/assets/{apl-DZuu22Wh.js → apl-icaNWZX3.js} +1 -1
- package/dist/assets/{astro-K-NemFAk.js → astro-HvIOE4vz.js} +1 -1
- package/dist/assets/{blade-ConbVu3U.js → blade-BpKJV8ev.js} +1 -1
- package/dist/assets/{c-DtmaBqln.js → c-BNvtxyRQ.js} +1 -1
- package/dist/assets/{cobol-BMHoPOfB.js → cobol-8js4hfxa.js} +1 -1
- package/dist/assets/{coffee-DfweJ19Y.js → coffee-DXg_piQ9.js} +1 -1
- package/dist/assets/{cpp-CPh_zIOW.js → cpp-BZDFAjyh.js} +1 -1
- package/dist/assets/{crystal-CAEX7C1J.js → crystal-aCF3ira4.js} +1 -1
- package/dist/assets/{css-C8ihGqCA.js → css-DfRtUkhE.js} +1 -1
- package/dist/assets/{edge-DVZPwQ9j.js → edge-vS6_AmQv.js} +1 -1
- package/dist/assets/{elixir-D03TKulP.js → elixir-Ce0nUHSq.js} +1 -1
- package/dist/assets/{elm-JUX-2xVV.js → elm-DmaeyizZ.js} +1 -1
- package/dist/assets/{erb-HbktAFZJ.js → erb-DD6OUyvt.js} +1 -1
- package/dist/assets/{git-rebase-Ccd_ELAP.js → git-rebase-HbJMn7iS.js} +1 -1
- package/dist/assets/{glimmer-js-BcinUfi2.js → glimmer-js-BRuqbNaV.js} +1 -1
- package/dist/assets/{glimmer-ts-DuN2UkN9.js → glimmer-ts-ZBpkdEEA.js} +1 -1
- package/dist/assets/{glsl-BWmCEPsR.js → glsl-CrQ4GScf.js} +1 -1
- package/dist/assets/{graphql-DJGYoBFW.js → graphql-Tn6PBdQY.js} +1 -1
- package/dist/assets/{hack-ZPqBE9w3.js → hack-CoApvrbo.js} +1 -1
- package/dist/assets/{haml-CZDZk6Gw.js → haml-COKq8Xjp.js} +1 -1
- package/dist/assets/{handlebars-d23PDS0n.js → handlebars-CAna2DTA.js} +1 -1
- package/dist/assets/{html-BgzX2DyB.js → html-Ch4xJ7YF.js} +1 -1
- package/dist/assets/{html-derivative-Bq6M-ZFw.js → html-derivative-0ITK-MAI.js} +1 -1
- package/dist/assets/{http-DVhMl_cW.js → http-DiMWQ8Qr.js} +1 -1
- package/dist/assets/{hurl-CbspBAqF.js → hurl-OcXymC0J.js} +1 -1
- package/dist/assets/index-BmcCug2M.js +1574 -0
- package/dist/assets/index-CADUNiTy.css +2 -0
- package/dist/assets/{java--YZbq0z2.js → java-CFb3qN4r.js} +1 -1
- package/dist/assets/{javascript-cvRuifRJ.js → javascript-CRmk2JrF.js} +1 -1
- package/dist/assets/{jinja-BeZmr3LP.js → jinja-Da6lDTEy.js} +1 -1
- package/dist/assets/{jison-qrpGZaxs.js → jison-Bnivzmtu.js} +1 -1
- package/dist/assets/{json-4xUzsNJn.js → json-m8vJP_KK.js} +1 -1
- package/dist/assets/{jsx-BlXrTlSg.js → jsx-C3HX_Sbu.js} +1 -1
- package/dist/assets/{julia-IN3jzmBc.js → julia-Ckg-wSHA.js} +1 -1
- package/dist/assets/{just-C_50nLQH.js → just-DCjIAyvO.js} +1 -1
- package/dist/assets/{latex-Cd-_2IS1.js → latex-CrYSm4pl.js} +1 -1
- package/dist/assets/{liquid-CEJf6Xg5.js → liquid-MQUFSPYU.js} +1 -1
- package/dist/assets/{lua-BlaV_n8i.js → lua-DcuzJJ77.js} +1 -1
- package/dist/assets/{marko-Cb-NPdc_.js → marko-4JNmQaB4.js} +1 -1
- package/dist/assets/{mdc-Dt4ywVAS.js → mdc-C_j8HvRL.js} +1 -1
- package/dist/assets/{nginx-C_6SKViP.js → nginx-ta3Gn1HE.js} +1 -1
- package/dist/assets/{nim-BbkBjoeZ.js → nim-BwWLst9M.js} +1 -1
- package/dist/assets/{perl-CYPU6TVa.js → perl-DfW7Uq5O.js} +1 -1
- package/dist/assets/{php-B6Oqilrc.js → php-DGf5G3Cf.js} +1 -1
- package/dist/assets/{pug-C9Poo2KU.js → pug-CWI-NaYz.js} +1 -1
- package/dist/assets/{qml-DkMURMPv.js → qml-Csblgh7U.js} +1 -1
- package/dist/assets/{r-Bm-Gfi0p.js → r-QGooaGDI.js} +1 -1
- package/dist/assets/{razor-B0LqOok3.js → razor-Dp9ihLYf.js} +1 -1
- package/dist/assets/{regexp-jAbMHzwe.js → regexp-DwUnLcVr.js} +1 -1
- package/dist/assets/{rst-BlWHfg8c.js → rst-BY8GIwDJ.js} +1 -1
- package/dist/assets/{ruby-Bt5lKwPL.js → ruby-VgzI9htT.js} +1 -1
- package/dist/assets/{sas-TuY2-8EF.js → sas-DYLsm-0j.js} +1 -1
- package/dist/assets/{scss-Dh1SsWlK.js → scss-tIFkeUQX.js} +1 -1
- package/dist/assets/{shellscript-CThiwmnp.js → shellscript-CHjuQ4Pl.js} +1 -1
- package/dist/assets/{shellsession-DQ5iRUWd.js → shellsession-Bv0S0hj9.js} +1 -1
- package/dist/assets/{soy-BfM-BrZG.js → soy-D6U1tZod.js} +1 -1
- package/dist/assets/{sql-F617W1uN.js → sql-CGz8kjKv.js} +1 -1
- package/dist/assets/{stata-Ctov3ki2.js → stata-C9ytERUq.js} +1 -1
- package/dist/assets/{surrealql-C-i2bJGB.js → surrealql-DRfRnaYu.js} +1 -1
- package/dist/assets/{svelte-oZT41pTl.js → svelte-wYnk_1sI.js} +1 -1
- package/dist/assets/{templ-CxX9Hf3e.js → templ-DWrGVE0u.js} +1 -1
- package/dist/assets/{tex-GyzJq8GJ.js → tex-CYTzoUJh.js} +1 -1
- package/dist/assets/{ts-tags-BugzKu0x.js → ts-tags-1TIXEGea.js} +1 -1
- package/dist/assets/{tsx-DLqwDEUq.js → tsx-a3BoAoOh.js} +1 -1
- package/dist/assets/{twig-DADiR9Vq.js → twig-e3dvNv6o.js} +1 -1
- package/dist/assets/{typescript-BlDIG_FO.js → typescript-BBhnpnTf.js} +1 -1
- package/dist/assets/{vue-COXmmg2A.js → vue-DzzVbu9q.js} +1 -1
- package/dist/assets/{vue-html-Cy_3KYj1.js → vue-html-quBNv38K.js} +1 -1
- package/dist/assets/{vue-vine-CUqOY8dY.js → vue-vine-CDuR69FE.js} +1 -1
- package/dist/assets/{xml-C9JY7Wf6.js → xml-DMifOLSe.js} +1 -1
- package/dist/assets/{xsl-DBCl8hSu.js → xsl-D8Ef-5HK.js} +1 -1
- package/dist/assets/{yaml-BbMebKkS.js → yaml-5c38gpmA.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +12 -3
- package/scripts/agent-config.mjs +184 -0
- package/scripts/agent-exclusions.mjs +10 -0
- package/scripts/agent-review.mjs +94 -0
- package/scripts/build-diff-data.mjs +211 -49
- package/scripts/cli-args.mjs +63 -9
- package/scripts/coding-agents.mjs +255 -77
- package/scripts/generate-summaries.mjs +872 -426
- package/scripts/local-target.mjs +106 -0
- package/scripts/present.mjs +333 -117
- package/scripts/review-chat-context.mjs +510 -0
- package/scripts/review-chat-controller.mjs +691 -0
- package/scripts/review-chat-provider.mjs +363 -0
- package/scripts/review-chat.mjs +8 -0
- package/scripts/serve-built.mjs +245 -27
- package/scripts/summary-path.mjs +32 -11
- package/dist/assets/index-C2p_1iAk.css +0 -2
- package/dist/assets/index-divlsuKs.js +0 -1568
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { constants } from 'node:fs';
|
|
2
2
|
import { access } from 'node:fs/promises';
|
|
3
3
|
import { spawnSync } from 'node:child_process';
|
|
4
|
+
import { createInterface } from 'node:readline';
|
|
4
5
|
import {
|
|
5
|
-
basename,
|
|
6
6
|
delimiter,
|
|
7
7
|
dirname,
|
|
8
8
|
isAbsolute,
|
|
9
9
|
join,
|
|
10
10
|
} from 'node:path';
|
|
11
|
+
import { readConfiguredAgent } from './agent-config.mjs';
|
|
11
12
|
|
|
12
13
|
export const codingAgentCapabilities = {
|
|
13
14
|
codex: { binary: 'codex', model: true, reasoning: true },
|
|
@@ -207,7 +208,7 @@ export async function findCommand(
|
|
|
207
208
|
return findOnPath(command, env, platform);
|
|
208
209
|
}
|
|
209
210
|
|
|
210
|
-
|
|
211
|
+
async function commandAvailable(command, options) {
|
|
211
212
|
return Boolean(await findCommand(command, options));
|
|
212
213
|
}
|
|
213
214
|
|
|
@@ -234,44 +235,177 @@ export async function codingAgentAvailability(
|
|
|
234
235
|
};
|
|
235
236
|
}
|
|
236
237
|
|
|
237
|
-
|
|
238
|
-
|
|
238
|
+
function availableResult(result) {
|
|
239
|
+
return typeof result === 'object' ? result.available : result;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function unavailableAgentError(requested, result) {
|
|
243
|
+
if (typeof result === 'object' && result.reason) {
|
|
244
|
+
return new Error(result.reason);
|
|
245
|
+
}
|
|
246
|
+
return new Error(`Coding agent "${requested}" is not available.`);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function noAvailableAgentError(cursorReason) {
|
|
250
|
+
return new Error(
|
|
251
|
+
`No coding agent is available. Install one of: ${enabledCodingAgents.join(', ')}. Pass --agent NAME to choose an agent or --no-agent for a plain review.${cursorReason ? ` ${cursorReason}` : ''}`,
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function selectionCancelledError() {
|
|
256
|
+
return new Error(
|
|
257
|
+
'Agent selection was cancelled. Pass --agent NAME to choose an agent or --no-agent for a plain review.',
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function nonInteractiveSelectionError() {
|
|
262
|
+
return new Error(
|
|
263
|
+
'An interactive terminal is required to choose a coding agent. Pass --agent NAME to choose an agent or --no-agent for a plain review.',
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
class CodingAgentSelectionError extends Error {
|
|
268
|
+
constructor(agent, message) {
|
|
269
|
+
super(message);
|
|
270
|
+
this.agent = agent;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export function codingAgentFromSelectionError(error) {
|
|
275
|
+
return error instanceof CodingAgentSelectionError
|
|
276
|
+
? error.agent
|
|
277
|
+
: undefined;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function unsupportedSelectionError(requested, configured) {
|
|
281
|
+
if (configured) {
|
|
282
|
+
return new CodingAgentSelectionError(
|
|
283
|
+
requested,
|
|
284
|
+
`Configured coding agent "${requested}" is unsupported. Choose ${enabledCodingAgents.join(', ')} with diffsplain config agent NAME, or use --agent NAME for this run.`,
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
return new CodingAgentSelectionError(
|
|
288
|
+
requested,
|
|
289
|
+
`Unsupported agent "${requested}". Choose ${enabledCodingAgents.join(', ')}.`,
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function unavailableSelectionError(requested, result, configured) {
|
|
294
|
+
if (!configured) {
|
|
295
|
+
return new CodingAgentSelectionError(
|
|
296
|
+
requested,
|
|
297
|
+
unavailableAgentError(requested, result).message,
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
const reason = typeof result === 'object' ? result.reason : undefined;
|
|
301
|
+
return new CodingAgentSelectionError(
|
|
302
|
+
requested,
|
|
303
|
+
`Configured coding agent "${requested}" is not available.${reason ? ` ${reason}` : ''} Change it with diffsplain config agent NAME, or use --agent NAME for this run.`,
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
async function selectRequestedCodingAgent(
|
|
239
308
|
requested,
|
|
240
|
-
available
|
|
309
|
+
available,
|
|
310
|
+
{ configured = false } = {},
|
|
241
311
|
) {
|
|
242
|
-
if (requested) {
|
|
243
|
-
|
|
244
|
-
throw new Error(
|
|
245
|
-
`Unsupported agent "${requested}". Choose ${enabledCodingAgents.join(', ')}.`,
|
|
246
|
-
);
|
|
247
|
-
}
|
|
248
|
-
const result = await available(requested);
|
|
249
|
-
const availableResult = typeof result === 'object'
|
|
250
|
-
? result.available
|
|
251
|
-
: result;
|
|
252
|
-
if (!availableResult) {
|
|
253
|
-
if (typeof result === 'object' && result.reason) {
|
|
254
|
-
throw new Error(result.reason);
|
|
255
|
-
}
|
|
256
|
-
throw new Error(`Coding agent "${requested}" is not available.`);
|
|
257
|
-
}
|
|
258
|
-
return requested;
|
|
312
|
+
if (!codingAgents.includes(requested)) {
|
|
313
|
+
throw unsupportedSelectionError(requested, configured);
|
|
259
314
|
}
|
|
315
|
+
const result = await available(requested);
|
|
316
|
+
if (!availableResult(result)) {
|
|
317
|
+
throw unavailableSelectionError(requested, result, configured);
|
|
318
|
+
}
|
|
319
|
+
return requested;
|
|
320
|
+
}
|
|
260
321
|
|
|
322
|
+
async function usableCodingAgents(available) {
|
|
323
|
+
const usable = [];
|
|
261
324
|
let cursorReason;
|
|
262
325
|
for (const agent of enabledCodingAgents) {
|
|
263
326
|
const result = await available(agent);
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
if (availableResult) return agent;
|
|
268
|
-
if (agent === 'cursor' && typeof result === 'object') {
|
|
327
|
+
if (availableResult(result)) {
|
|
328
|
+
usable.push(agent);
|
|
329
|
+
} else if (agent === 'cursor' && typeof result === 'object') {
|
|
269
330
|
cursorReason = result.reason;
|
|
270
331
|
}
|
|
271
332
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
333
|
+
return { usable, cursorReason };
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function selectFromTerminal(agents, { input, output, signal }) {
|
|
337
|
+
output.write('Choose a coding agent:\n');
|
|
338
|
+
for (const [index, agent] of agents.entries()) {
|
|
339
|
+
output.write(`${index + 1}. ${agent}\n`);
|
|
340
|
+
}
|
|
341
|
+
output.write('Enter a number, or press Ctrl+C to cancel: ');
|
|
342
|
+
|
|
343
|
+
return new Promise((resolve, reject) => {
|
|
344
|
+
const picker = createInterface({ input, output, terminal: true });
|
|
345
|
+
let settled = false;
|
|
346
|
+
const finish = (callback, value) => {
|
|
347
|
+
if (settled) return;
|
|
348
|
+
settled = true;
|
|
349
|
+
signal?.removeEventListener('abort', onAbort);
|
|
350
|
+
picker.close();
|
|
351
|
+
callback(value);
|
|
352
|
+
};
|
|
353
|
+
const onAbort = () => {
|
|
354
|
+
finish(reject, selectionCancelledError());
|
|
355
|
+
};
|
|
356
|
+
const promptAgain = () => {
|
|
357
|
+
output.write(`Choose a number from 1 to ${agents.length}, or press Ctrl+C to cancel: `);
|
|
358
|
+
};
|
|
359
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
360
|
+
if (signal?.aborted) onAbort();
|
|
361
|
+
picker.on('SIGINT', () => {
|
|
362
|
+
finish(reject, selectionCancelledError());
|
|
363
|
+
});
|
|
364
|
+
picker.on('close', () => {
|
|
365
|
+
finish(reject, selectionCancelledError());
|
|
366
|
+
});
|
|
367
|
+
picker.on('line', (line) => {
|
|
368
|
+
const selected = Number(line.trim());
|
|
369
|
+
if (
|
|
370
|
+
!Number.isInteger(selected) ||
|
|
371
|
+
selected < 1 ||
|
|
372
|
+
selected > agents.length
|
|
373
|
+
) {
|
|
374
|
+
promptAgain();
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
finish(resolve, agents[selected - 1]);
|
|
378
|
+
});
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
export async function selectCodingAgent(
|
|
383
|
+
requested,
|
|
384
|
+
{
|
|
385
|
+
available = commandAvailable,
|
|
386
|
+
configuredAgent = readConfiguredAgent,
|
|
387
|
+
input = process.stdin,
|
|
388
|
+
output = process.stderr,
|
|
389
|
+
signal,
|
|
390
|
+
} = {},
|
|
391
|
+
) {
|
|
392
|
+
if (requested !== undefined) {
|
|
393
|
+
return selectRequestedCodingAgent(requested, available);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
const configured = await configuredAgent();
|
|
397
|
+
if (configured !== undefined) {
|
|
398
|
+
return selectRequestedCodingAgent(configured, available, {
|
|
399
|
+
configured: true,
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if (!input?.isTTY || !output?.isTTY) {
|
|
404
|
+
throw nonInteractiveSelectionError();
|
|
405
|
+
}
|
|
406
|
+
const { usable, cursorReason } = await usableCodingAgents(available);
|
|
407
|
+
if (!usable.length) throw noAvailableAgentError(cursorReason);
|
|
408
|
+
return selectFromTerminal(usable, { input, output, signal });
|
|
275
409
|
}
|
|
276
410
|
|
|
277
411
|
// fallow-ignore-next-line complexity -- each supported agent has one override rule.
|
|
@@ -391,6 +525,33 @@ export function parseAgentResponse(agent, stdout) {
|
|
|
391
525
|
return parseJsonText(stdout, label);
|
|
392
526
|
}
|
|
393
527
|
|
|
528
|
+
function codexIsolationArgs(snapshotOnly) {
|
|
529
|
+
return snapshotOnly
|
|
530
|
+
? ['--ignore-user-config', '--ignore-rules']
|
|
531
|
+
: [];
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
function codexConfigArgs(snapshotOnly) {
|
|
535
|
+
return snapshotOnly
|
|
536
|
+
? [
|
|
537
|
+
'--config',
|
|
538
|
+
'mcp_servers={}',
|
|
539
|
+
'--config',
|
|
540
|
+
'plugins={}',
|
|
541
|
+
'--config',
|
|
542
|
+
'shell_environment_policy.inherit="none"',
|
|
543
|
+
'--config',
|
|
544
|
+
'sandbox_workspace_write.network_access=false',
|
|
545
|
+
'--config',
|
|
546
|
+
'web_search="disabled"',
|
|
547
|
+
]
|
|
548
|
+
: [];
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
function codexRepoArgs(snapshotOnly) {
|
|
552
|
+
return snapshotOnly ? ['--skip-git-repo-check'] : [];
|
|
553
|
+
}
|
|
554
|
+
|
|
394
555
|
function codexCommand({
|
|
395
556
|
binary,
|
|
396
557
|
model,
|
|
@@ -399,31 +560,22 @@ function codexCommand({
|
|
|
399
560
|
schemaPath,
|
|
400
561
|
summaryDirectory,
|
|
401
562
|
summaryEnv,
|
|
563
|
+
snapshotOnly,
|
|
402
564
|
}) {
|
|
403
565
|
const args = [
|
|
404
566
|
'exec',
|
|
405
567
|
'--ephemeral',
|
|
406
568
|
'--sandbox',
|
|
407
569
|
'read-only',
|
|
408
|
-
|
|
409
|
-
'--ignore-rules',
|
|
570
|
+
...codexIsolationArgs(snapshotOnly),
|
|
410
571
|
'--color',
|
|
411
572
|
'never',
|
|
412
|
-
|
|
573
|
+
...codexRepoArgs(snapshotOnly),
|
|
413
574
|
'-C',
|
|
414
575
|
summaryDirectory,
|
|
415
576
|
'--output-schema',
|
|
416
577
|
schemaPath,
|
|
417
|
-
|
|
418
|
-
'mcp_servers={}',
|
|
419
|
-
'--config',
|
|
420
|
-
'plugins={}',
|
|
421
|
-
'--config',
|
|
422
|
-
'shell_environment_policy.inherit="none"',
|
|
423
|
-
'--config',
|
|
424
|
-
'sandbox_workspace_write.network_access=false',
|
|
425
|
-
'--config',
|
|
426
|
-
'web_search="disabled"',
|
|
578
|
+
...codexConfigArgs(snapshotOnly),
|
|
427
579
|
];
|
|
428
580
|
if (model) args.push('--model', model);
|
|
429
581
|
if (reasoning) {
|
|
@@ -449,6 +601,7 @@ function claudeCommand({
|
|
|
449
601
|
schema,
|
|
450
602
|
summaryDirectory,
|
|
451
603
|
summaryEnv,
|
|
604
|
+
snapshotOnly,
|
|
452
605
|
}) {
|
|
453
606
|
const args = [
|
|
454
607
|
'--print',
|
|
@@ -456,8 +609,7 @@ function claudeCommand({
|
|
|
456
609
|
'json',
|
|
457
610
|
'--json-schema',
|
|
458
611
|
JSON.stringify(schema),
|
|
459
|
-
'--tools',
|
|
460
|
-
'',
|
|
612
|
+
...(snapshotOnly ? ['--tools', ''] : ['--permission-mode', 'plan']),
|
|
461
613
|
'--no-session-persistence',
|
|
462
614
|
];
|
|
463
615
|
if (model) args.push('--model', model);
|
|
@@ -479,13 +631,14 @@ function copilotCommand({
|
|
|
479
631
|
schema,
|
|
480
632
|
summaryDirectory,
|
|
481
633
|
summaryEnv,
|
|
634
|
+
snapshotOnly,
|
|
482
635
|
}) {
|
|
483
636
|
const schemaText = JSON.stringify(schema);
|
|
484
637
|
const args = [
|
|
485
638
|
'--silent',
|
|
486
|
-
'--no-ask-user',
|
|
639
|
+
...(snapshotOnly ? ['--no-ask-user'] : []),
|
|
487
640
|
'--no-color',
|
|
488
|
-
'--no-custom-instructions',
|
|
641
|
+
...(snapshotOnly ? ['--no-custom-instructions'] : []),
|
|
489
642
|
'--no-remote',
|
|
490
643
|
'--no-remote-export',
|
|
491
644
|
`--add-dir=${dirname(inputPath)}`,
|
|
@@ -504,6 +657,26 @@ function copilotCommand({
|
|
|
504
657
|
};
|
|
505
658
|
}
|
|
506
659
|
|
|
660
|
+
function openCodeIsolationArgs(snapshotOnly) {
|
|
661
|
+
return snapshotOnly ? ['--pure'] : [];
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
function openCodeEnvironment(snapshotOnly, summaryEnv) {
|
|
665
|
+
if (!snapshotOnly) return summaryEnv;
|
|
666
|
+
return {
|
|
667
|
+
...summaryEnv,
|
|
668
|
+
OPENCODE_DB: ':memory:',
|
|
669
|
+
OPENCODE_CONFIG_CONTENT: JSON.stringify({
|
|
670
|
+
permission: { '*': 'deny' },
|
|
671
|
+
agent: {
|
|
672
|
+
build: {
|
|
673
|
+
permission: { '*': 'deny' },
|
|
674
|
+
},
|
|
675
|
+
},
|
|
676
|
+
}),
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
|
|
507
680
|
function openCodeCommand({
|
|
508
681
|
binary,
|
|
509
682
|
model,
|
|
@@ -512,10 +685,11 @@ function openCodeCommand({
|
|
|
512
685
|
schema,
|
|
513
686
|
summaryDirectory,
|
|
514
687
|
summaryEnv,
|
|
688
|
+
snapshotOnly,
|
|
515
689
|
}) {
|
|
516
690
|
const args = [
|
|
517
691
|
'run',
|
|
518
|
-
|
|
692
|
+
...openCodeIsolationArgs(snapshotOnly),
|
|
519
693
|
'--format',
|
|
520
694
|
'json',
|
|
521
695
|
'--dir',
|
|
@@ -533,24 +707,12 @@ function openCodeCommand({
|
|
|
533
707
|
args,
|
|
534
708
|
input: 'stdin',
|
|
535
709
|
cwd: summaryDirectory,
|
|
536
|
-
env:
|
|
537
|
-
...summaryEnv,
|
|
538
|
-
OPENCODE_DB: ':memory:',
|
|
539
|
-
OPENCODE_CONFIG_CONTENT: JSON.stringify({
|
|
540
|
-
permission: { '*': 'deny' },
|
|
541
|
-
agent: {
|
|
542
|
-
build: {
|
|
543
|
-
permission: { '*': 'deny' },
|
|
544
|
-
},
|
|
545
|
-
},
|
|
546
|
-
}),
|
|
547
|
-
},
|
|
710
|
+
env: openCodeEnvironment(snapshotOnly, summaryEnv),
|
|
548
711
|
};
|
|
549
712
|
}
|
|
550
713
|
|
|
551
714
|
function cursorCommand({
|
|
552
715
|
binary,
|
|
553
|
-
inputPath,
|
|
554
716
|
model,
|
|
555
717
|
prompt,
|
|
556
718
|
schema,
|
|
@@ -572,26 +734,28 @@ function cursorCommand({
|
|
|
572
734
|
];
|
|
573
735
|
if (model) args.push('--model', model);
|
|
574
736
|
args.push(
|
|
575
|
-
`${prompt}\n\
|
|
737
|
+
`${prompt}\n\nThe snapshot JSON follows this prompt on standard input. Return JSON that matches this schema:\n${JSON.stringify(schema)}`,
|
|
576
738
|
);
|
|
577
739
|
return {
|
|
578
740
|
command: binary,
|
|
579
741
|
args,
|
|
580
742
|
input: 'stdin',
|
|
581
743
|
cwd: summaryDirectory,
|
|
582
|
-
env:
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
744
|
+
env: summaryEnv === sourceEnv
|
|
745
|
+
? summaryEnv
|
|
746
|
+
: {
|
|
747
|
+
...summaryEnv,
|
|
748
|
+
...(sourceEnv.XDG_CONFIG_HOME
|
|
749
|
+
? { XDG_CONFIG_HOME: sourceEnv.XDG_CONFIG_HOME }
|
|
750
|
+
: {}),
|
|
751
|
+
...(sourceEnv.APPDATA ? { APPDATA: sourceEnv.APPDATA } : {}),
|
|
752
|
+
...(sourceEnv.CURSOR_API_KEY
|
|
753
|
+
? { CURSOR_API_KEY: sourceEnv.CURSOR_API_KEY }
|
|
754
|
+
: {}),
|
|
755
|
+
...(sourceEnv.CURSOR_AUTH_TOKEN
|
|
756
|
+
? { CURSOR_AUTH_TOKEN: sourceEnv.CURSOR_AUTH_TOKEN }
|
|
757
|
+
: {}),
|
|
758
|
+
},
|
|
595
759
|
};
|
|
596
760
|
}
|
|
597
761
|
|
|
@@ -604,8 +768,11 @@ export function agentCommand({
|
|
|
604
768
|
schema,
|
|
605
769
|
schemaPath,
|
|
606
770
|
inputPath,
|
|
771
|
+
accessMode,
|
|
607
772
|
env = process.env,
|
|
608
773
|
}) {
|
|
774
|
+
const snapshotDirectory = dirname(inputPath);
|
|
775
|
+
const checkoutAccess = accessMode?.mode === 'checkout-read-only';
|
|
609
776
|
const options = {
|
|
610
777
|
binary,
|
|
611
778
|
inputPath,
|
|
@@ -614,8 +781,9 @@ export function agentCommand({
|
|
|
614
781
|
reasoning,
|
|
615
782
|
schema,
|
|
616
783
|
schemaPath,
|
|
617
|
-
summaryDirectory:
|
|
618
|
-
summaryEnv: summaryAgentEnvironment(env),
|
|
784
|
+
summaryDirectory: checkoutAccess ? accessMode.root : snapshotDirectory,
|
|
785
|
+
summaryEnv: checkoutAccess ? env : summaryAgentEnvironment(env),
|
|
786
|
+
snapshotOnly: !checkoutAccess,
|
|
619
787
|
sourceEnv: env,
|
|
620
788
|
};
|
|
621
789
|
if (agent === 'codex') return codexCommand(options);
|
|
@@ -624,3 +792,13 @@ export function agentCommand({
|
|
|
624
792
|
if (agent === 'cursor') return cursorCommand(options);
|
|
625
793
|
return openCodeCommand(options);
|
|
626
794
|
}
|
|
795
|
+
|
|
796
|
+
const readOnlyWarnings = {
|
|
797
|
+
copilot: 'Warning: Copilot has no proven native read-only mode. It runs with your normal permissions.',
|
|
798
|
+
opencode: 'Warning: OpenCode has no proven native read-only mode. It runs with your normal permissions.',
|
|
799
|
+
};
|
|
800
|
+
|
|
801
|
+
export function agentReadOnlyWarning(agent, accessMode) {
|
|
802
|
+
if (accessMode?.mode !== 'checkout-read-only') return undefined;
|
|
803
|
+
return readOnlyWarnings[agent];
|
|
804
|
+
}
|