deepcode-ai 1.1.25 → 1.1.26
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/dist/index.js +157 -136
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -10382,6 +10382,141 @@ function parseProviderId(value) {
|
|
|
10382
10382
|
if (parsed.success) return parsed.data;
|
|
10383
10383
|
throw new Error(`Invalid provider: ${value}. Expected one of: ${PROVIDER_IDS.join(", ")}`);
|
|
10384
10384
|
}
|
|
10385
|
+
var DIFF_MAX_CHARS = 2e4;
|
|
10386
|
+
function truncateDiff(raw, maxChars = DIFF_MAX_CHARS) {
|
|
10387
|
+
const fileChunks = raw.split(/(?=^diff --git )/m).filter(Boolean);
|
|
10388
|
+
const totalFiles = fileChunks.length;
|
|
10389
|
+
let result = "";
|
|
10390
|
+
let included = 0;
|
|
10391
|
+
for (const chunk of fileChunks) {
|
|
10392
|
+
if (result.length + chunk.length > maxChars) break;
|
|
10393
|
+
result += chunk;
|
|
10394
|
+
included++;
|
|
10395
|
+
}
|
|
10396
|
+
if (!result && fileChunks.length > 0) {
|
|
10397
|
+
result = fileChunks[0].slice(0, maxChars);
|
|
10398
|
+
included = 1;
|
|
10399
|
+
}
|
|
10400
|
+
return { diff: result.trimEnd(), omittedFiles: totalFiles - included, totalFiles };
|
|
10401
|
+
}
|
|
10402
|
+
async function runGit(cwd, args) {
|
|
10403
|
+
const result = await execFileAsync("git", args, { cwd, timeoutMs: 3e4 });
|
|
10404
|
+
if (result.exitCode !== 0) {
|
|
10405
|
+
throw new Error(result.stderr || `git ${args.join(" ")} failed`);
|
|
10406
|
+
}
|
|
10407
|
+
return result.stdout;
|
|
10408
|
+
}
|
|
10409
|
+
async function isGitRepo(cwd) {
|
|
10410
|
+
const result = await execFileAsync(
|
|
10411
|
+
"git",
|
|
10412
|
+
["rev-parse", "--is-inside-work-tree"],
|
|
10413
|
+
{ cwd, timeoutMs: 5e3 }
|
|
10414
|
+
);
|
|
10415
|
+
return result.exitCode === 0;
|
|
10416
|
+
}
|
|
10417
|
+
function buildDiffArgs(options) {
|
|
10418
|
+
if (options.staged) {
|
|
10419
|
+
const args2 = ["diff", "--cached"];
|
|
10420
|
+
if (options.file) args2.push("--", options.file);
|
|
10421
|
+
return { args: args2, label: "staged changes" };
|
|
10422
|
+
}
|
|
10423
|
+
if (options.ref) {
|
|
10424
|
+
const args2 = ["diff", options.ref];
|
|
10425
|
+
if (options.file) args2.push("--", options.file);
|
|
10426
|
+
return { args: args2, label: `diff vs ${options.ref}` };
|
|
10427
|
+
}
|
|
10428
|
+
const args = ["diff", "HEAD"];
|
|
10429
|
+
if (options.file) args.push("--", options.file);
|
|
10430
|
+
return { args, label: options.file ? `local changes in ${options.file}` : "local changes vs HEAD" };
|
|
10431
|
+
}
|
|
10432
|
+
function buildPrompt(diff, label, focus, truncation) {
|
|
10433
|
+
const focusLine = focus.length > 0 ? `
|
|
10434
|
+
Focus areas: ${focus.join(", ")}.` : "";
|
|
10435
|
+
const truncationNote = truncation.omittedFiles > 0 ? `
|
|
10436
|
+
(Showing ${truncation.totalFiles - truncation.omittedFiles} of ${truncation.totalFiles} changed files; ${truncation.omittedFiles} file(s) omitted due to size.)
|
|
10437
|
+
` : "";
|
|
10438
|
+
return [
|
|
10439
|
+
`Review the following local git diff (${label}).`,
|
|
10440
|
+
"Do not modify any files. Output the review only.",
|
|
10441
|
+
focusLine,
|
|
10442
|
+
"",
|
|
10443
|
+
`\`\`\`diff`,
|
|
10444
|
+
diff,
|
|
10445
|
+
`\`\`\``,
|
|
10446
|
+
truncationNote,
|
|
10447
|
+
"Produce a structured code review:",
|
|
10448
|
+
"1. **Summary** \u2014 what changed (inferred from the diff)",
|
|
10449
|
+
"2. **Issues** \u2014 bugs, security concerns, logic errors, missing error handling; quote the relevant lines",
|
|
10450
|
+
"3. **Suggestions** \u2014 improvements and nitpicks",
|
|
10451
|
+
"4. **Verdict** \u2014 Looks good / Has issues, with a one-line rationale"
|
|
10452
|
+
].filter((l) => l !== void 0).join("\n");
|
|
10453
|
+
}
|
|
10454
|
+
async function reviewCommand(options) {
|
|
10455
|
+
if (!await isGitRepo(options.cwd)) {
|
|
10456
|
+
await writeStderrLine("error: not inside a git repository");
|
|
10457
|
+
process.exit(1);
|
|
10458
|
+
}
|
|
10459
|
+
const { args, label } = buildDiffArgs(options);
|
|
10460
|
+
let rawDiff;
|
|
10461
|
+
try {
|
|
10462
|
+
rawDiff = await runGit(options.cwd, args);
|
|
10463
|
+
} catch (err) {
|
|
10464
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
10465
|
+
await writeStderrLine(`error: ${msg}`);
|
|
10466
|
+
process.exit(1);
|
|
10467
|
+
}
|
|
10468
|
+
const trimmed = rawDiff.trim();
|
|
10469
|
+
if (!trimmed) {
|
|
10470
|
+
await writeStdoutLine(`No changes to review (${label}).`);
|
|
10471
|
+
return;
|
|
10472
|
+
}
|
|
10473
|
+
const truncation = truncateDiff(trimmed);
|
|
10474
|
+
const runtime = await createRuntime({
|
|
10475
|
+
cwd: options.cwd,
|
|
10476
|
+
configPath: options.config,
|
|
10477
|
+
interactive: Boolean(options.yes)
|
|
10478
|
+
});
|
|
10479
|
+
if (options.yes) {
|
|
10480
|
+
runtime.events.on("approval:request", (request) => {
|
|
10481
|
+
runtime.events.emit("approval:decision", {
|
|
10482
|
+
requestId: request.id,
|
|
10483
|
+
decision: { allowed: true }
|
|
10484
|
+
});
|
|
10485
|
+
});
|
|
10486
|
+
}
|
|
10487
|
+
const target = resolveSessionTarget(runtime.config, {
|
|
10488
|
+
provider: options.provider,
|
|
10489
|
+
model: options.model
|
|
10490
|
+
});
|
|
10491
|
+
const session = runtime.sessions.create({
|
|
10492
|
+
provider: target.provider,
|
|
10493
|
+
model: target.model
|
|
10494
|
+
});
|
|
10495
|
+
const prompt = buildPrompt(truncation.diff, label, options.focus ?? [], truncation);
|
|
10496
|
+
const secretValues = collectSecretValues(runtime.config);
|
|
10497
|
+
await writeStdoutLine(`Reviewing ${label}\u2026
|
|
10498
|
+
`);
|
|
10499
|
+
let streamed = false;
|
|
10500
|
+
try {
|
|
10501
|
+
const output = await runtime.agent.run({
|
|
10502
|
+
session,
|
|
10503
|
+
input: prompt,
|
|
10504
|
+
mode: "plan",
|
|
10505
|
+
provider: target.provider,
|
|
10506
|
+
onChunk: (text) => {
|
|
10507
|
+
streamed = true;
|
|
10508
|
+
process.stdout.write(redactText(text, secretValues));
|
|
10509
|
+
}
|
|
10510
|
+
});
|
|
10511
|
+
if (!streamed && output) {
|
|
10512
|
+
process.stdout.write(redactText(output, secretValues));
|
|
10513
|
+
}
|
|
10514
|
+
if (!streamed || !output) process.stdout.write("\n");
|
|
10515
|
+
} finally {
|
|
10516
|
+
await runtime.sessions.persist(session.id).catch(() => {
|
|
10517
|
+
});
|
|
10518
|
+
}
|
|
10519
|
+
}
|
|
10385
10520
|
async function githubLoginCommand(options) {
|
|
10386
10521
|
const loader = new ConfigLoader();
|
|
10387
10522
|
const loadOptions = { cwd: options.cwd, configPath: options.config };
|
|
@@ -10561,8 +10696,8 @@ async function solveIssueCommand(issueNumber, options) {
|
|
|
10561
10696
|
const issue = await client.getIssue({ ...repo, number: issueNumber });
|
|
10562
10697
|
const base = options.base ?? "main";
|
|
10563
10698
|
const branch = `deepcode/issue-${issueNumber}-${slugify(issue.title)}`.slice(0, 80);
|
|
10564
|
-
await
|
|
10565
|
-
await
|
|
10699
|
+
await runGit2(options.cwd, ["fetch", "origin", base]);
|
|
10700
|
+
await runGit2(options.cwd, ["checkout", "-B", branch, `origin/${base}`]);
|
|
10566
10701
|
const target = resolveUsableProviderTarget(runtime.config, [runtime.config.defaultProvider]);
|
|
10567
10702
|
const session = runtime.sessions.create({
|
|
10568
10703
|
provider: target.provider,
|
|
@@ -10587,16 +10722,16 @@ async function solveIssueCommand(issueNumber, options) {
|
|
|
10587
10722
|
onChunk: (text) => void writeStdout(redactText(text, secretValues))
|
|
10588
10723
|
});
|
|
10589
10724
|
await writeStdout("\n");
|
|
10590
|
-
const status = await
|
|
10591
|
-
const aheadLog = await
|
|
10725
|
+
const status = await runGit2(options.cwd, ["status", "--porcelain"]);
|
|
10726
|
+
const aheadLog = await runGit2(options.cwd, ["log", `origin/${base}..HEAD`, "--oneline"]);
|
|
10592
10727
|
const hasUncommitted = Boolean(status.stdout.trim());
|
|
10593
10728
|
const hasCommits = Boolean(aheadLog.stdout.trim());
|
|
10594
10729
|
if (!hasUncommitted && !hasCommits) {
|
|
10595
10730
|
throw new Error("Agent completed without file changes; no PR was created.");
|
|
10596
10731
|
}
|
|
10597
10732
|
if (hasUncommitted) {
|
|
10598
|
-
await
|
|
10599
|
-
await
|
|
10733
|
+
await runGit2(options.cwd, ["add", "."]);
|
|
10734
|
+
await runGit2(options.cwd, [
|
|
10600
10735
|
"commit",
|
|
10601
10736
|
"-m",
|
|
10602
10737
|
`fix: resolve issue #${issue.number}`,
|
|
@@ -10606,7 +10741,7 @@ async function solveIssueCommand(issueNumber, options) {
|
|
|
10606
10741
|
Closes #${issue.number}`
|
|
10607
10742
|
]);
|
|
10608
10743
|
}
|
|
10609
|
-
await
|
|
10744
|
+
await runGit2(options.cwd, ["push", "-u", "origin", branch]);
|
|
10610
10745
|
const pr = await client.createPullRequest({
|
|
10611
10746
|
...repo,
|
|
10612
10747
|
title: `Fix: ${issue.title}`,
|
|
@@ -10645,6 +10780,10 @@ async function reviewPrCommand(prNumber, options) {
|
|
|
10645
10780
|
]);
|
|
10646
10781
|
const focusLine = options.focus && options.focus.length > 0 ? `
|
|
10647
10782
|
Focus areas: ${options.focus.join(", ")}.` : "";
|
|
10783
|
+
const truncation = truncateDiff(diff.trim());
|
|
10784
|
+
const truncationNote = truncation.omittedFiles > 0 ? `
|
|
10785
|
+
(Showing ${truncation.totalFiles - truncation.omittedFiles} of ${truncation.totalFiles} changed files; ${truncation.omittedFiles} file(s) omitted due to size.)
|
|
10786
|
+
` : "";
|
|
10648
10787
|
const prompt = [
|
|
10649
10788
|
`Review PR #${pr.number}: ${pr.title}`,
|
|
10650
10789
|
`Branch: ${pr.head ?? "?"} \u2192 ${pr.base ?? "?"}`,
|
|
@@ -10655,8 +10794,9 @@ ${pr.body}` : "No description provided.",
|
|
|
10655
10794
|
"",
|
|
10656
10795
|
`Diff:
|
|
10657
10796
|
\`\`\`diff
|
|
10658
|
-
${diff}
|
|
10797
|
+
${truncation.diff}
|
|
10659
10798
|
\`\`\``,
|
|
10799
|
+
truncationNote,
|
|
10660
10800
|
"",
|
|
10661
10801
|
`Produce a structured code review with:${focusLine}`,
|
|
10662
10802
|
"1. **Summary** \u2014 what the PR does",
|
|
@@ -10664,7 +10804,10 @@ ${diff}
|
|
|
10664
10804
|
"3. **Suggestions** \u2014 improvements and nitpicks",
|
|
10665
10805
|
"4. **Verdict** \u2014 Approve / Request Changes / Neutral with a one-line rationale"
|
|
10666
10806
|
].join("\n");
|
|
10667
|
-
const target = resolveSessionTarget(runtime.config, {
|
|
10807
|
+
const target = resolveSessionTarget(runtime.config, {
|
|
10808
|
+
provider: options.provider,
|
|
10809
|
+
model: options.model
|
|
10810
|
+
});
|
|
10668
10811
|
const session = runtime.sessions.create({
|
|
10669
10812
|
provider: target.provider,
|
|
10670
10813
|
model: target.model
|
|
@@ -10684,7 +10827,7 @@ ${diff}
|
|
|
10684
10827
|
}
|
|
10685
10828
|
await writeStdout("\n");
|
|
10686
10829
|
}
|
|
10687
|
-
async function
|
|
10830
|
+
async function runGit2(cwd, args) {
|
|
10688
10831
|
const result = await execFileAsync("git", args, { cwd, timeoutMs: 18e4 });
|
|
10689
10832
|
if (result.exitCode !== 0) {
|
|
10690
10833
|
throw new Error(result.stderr || result.stdout || `git ${args.join(" ")} failed`);
|
|
@@ -10990,130 +11133,6 @@ async function projectsCommand(options) {
|
|
|
10990
11133
|
);
|
|
10991
11134
|
await waitUntilExit();
|
|
10992
11135
|
}
|
|
10993
|
-
var DIFF_MAX_CHARS = 2e4;
|
|
10994
|
-
async function runGit2(cwd, args) {
|
|
10995
|
-
const result = await execFileAsync("git", args, { cwd, timeoutMs: 3e4 });
|
|
10996
|
-
if (result.exitCode !== 0) {
|
|
10997
|
-
throw new Error(result.stderr || `git ${args.join(" ")} failed`);
|
|
10998
|
-
}
|
|
10999
|
-
return result.stdout;
|
|
11000
|
-
}
|
|
11001
|
-
async function isGitRepo(cwd) {
|
|
11002
|
-
const result = await execFileAsync(
|
|
11003
|
-
"git",
|
|
11004
|
-
["rev-parse", "--is-inside-work-tree"],
|
|
11005
|
-
{ cwd, timeoutMs: 5e3 }
|
|
11006
|
-
);
|
|
11007
|
-
return result.exitCode === 0;
|
|
11008
|
-
}
|
|
11009
|
-
function buildDiffArgs(options) {
|
|
11010
|
-
if (options.staged) {
|
|
11011
|
-
const args2 = ["diff", "--cached"];
|
|
11012
|
-
if (options.file) args2.push("--", options.file);
|
|
11013
|
-
return { args: args2, label: "staged changes" };
|
|
11014
|
-
}
|
|
11015
|
-
if (options.ref) {
|
|
11016
|
-
const args2 = ["diff", options.ref];
|
|
11017
|
-
if (options.file) args2.push("--", options.file);
|
|
11018
|
-
return { args: args2, label: `diff vs ${options.ref}` };
|
|
11019
|
-
}
|
|
11020
|
-
const args = ["diff", "HEAD"];
|
|
11021
|
-
if (options.file) args.push("--", options.file);
|
|
11022
|
-
return { args, label: options.file ? `local changes in ${options.file}` : "local changes vs HEAD" };
|
|
11023
|
-
}
|
|
11024
|
-
function buildPrompt(diff, label, focus, truncated) {
|
|
11025
|
-
const focusLine = focus.length > 0 ? `
|
|
11026
|
-
Focus areas: ${focus.join(", ")}.` : "";
|
|
11027
|
-
const truncationNote = truncated ? `
|
|
11028
|
-
(Diff truncated at ${DIFF_MAX_CHARS} characters; some changes are not shown.)
|
|
11029
|
-
` : "";
|
|
11030
|
-
return [
|
|
11031
|
-
`Review the following local git diff (${label}).`,
|
|
11032
|
-
"Do not modify any files. Output the review only.",
|
|
11033
|
-
focusLine,
|
|
11034
|
-
"",
|
|
11035
|
-
`\`\`\`diff`,
|
|
11036
|
-
diff,
|
|
11037
|
-
`\`\`\``,
|
|
11038
|
-
truncationNote,
|
|
11039
|
-
"Produce a structured code review:",
|
|
11040
|
-
"1. **Summary** \u2014 what changed (inferred from the diff)",
|
|
11041
|
-
"2. **Issues** \u2014 bugs, security concerns, logic errors, missing error handling; quote the relevant lines",
|
|
11042
|
-
"3. **Suggestions** \u2014 improvements and nitpicks",
|
|
11043
|
-
"4. **Verdict** \u2014 Looks good / Has issues, with a one-line rationale"
|
|
11044
|
-
].filter((l) => l !== void 0).join("\n");
|
|
11045
|
-
}
|
|
11046
|
-
async function reviewCommand(options) {
|
|
11047
|
-
if (!await isGitRepo(options.cwd)) {
|
|
11048
|
-
await writeStderrLine("error: not inside a git repository");
|
|
11049
|
-
process.exit(1);
|
|
11050
|
-
}
|
|
11051
|
-
const { args, label } = buildDiffArgs(options);
|
|
11052
|
-
let rawDiff;
|
|
11053
|
-
try {
|
|
11054
|
-
rawDiff = await runGit2(options.cwd, args);
|
|
11055
|
-
} catch (err) {
|
|
11056
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
11057
|
-
await writeStderrLine(`error: ${msg}`);
|
|
11058
|
-
process.exit(1);
|
|
11059
|
-
}
|
|
11060
|
-
const trimmed = rawDiff.trim();
|
|
11061
|
-
if (!trimmed) {
|
|
11062
|
-
await writeStdoutLine(`No changes to review (${label}).`);
|
|
11063
|
-
return;
|
|
11064
|
-
}
|
|
11065
|
-
let diff = trimmed;
|
|
11066
|
-
let truncated = false;
|
|
11067
|
-
if (diff.length > DIFF_MAX_CHARS) {
|
|
11068
|
-
diff = diff.slice(0, DIFF_MAX_CHARS);
|
|
11069
|
-
truncated = true;
|
|
11070
|
-
}
|
|
11071
|
-
const runtime = await createRuntime({
|
|
11072
|
-
cwd: options.cwd,
|
|
11073
|
-
configPath: options.config,
|
|
11074
|
-
interactive: Boolean(options.yes)
|
|
11075
|
-
});
|
|
11076
|
-
if (options.yes) {
|
|
11077
|
-
runtime.events.on("approval:request", (request) => {
|
|
11078
|
-
runtime.events.emit("approval:decision", {
|
|
11079
|
-
requestId: request.id,
|
|
11080
|
-
decision: { allowed: true }
|
|
11081
|
-
});
|
|
11082
|
-
});
|
|
11083
|
-
}
|
|
11084
|
-
const target = resolveSessionTarget(runtime.config, {
|
|
11085
|
-
provider: options.provider,
|
|
11086
|
-
model: options.model
|
|
11087
|
-
});
|
|
11088
|
-
const session = runtime.sessions.create({
|
|
11089
|
-
provider: target.provider,
|
|
11090
|
-
model: target.model
|
|
11091
|
-
});
|
|
11092
|
-
const prompt = buildPrompt(diff, label, options.focus ?? [], truncated);
|
|
11093
|
-
const secretValues = collectSecretValues(runtime.config);
|
|
11094
|
-
await writeStdoutLine(`Reviewing ${label}\u2026
|
|
11095
|
-
`);
|
|
11096
|
-
let streamed = false;
|
|
11097
|
-
try {
|
|
11098
|
-
const output = await runtime.agent.run({
|
|
11099
|
-
session,
|
|
11100
|
-
input: prompt,
|
|
11101
|
-
mode: "plan",
|
|
11102
|
-
provider: target.provider,
|
|
11103
|
-
onChunk: (text) => {
|
|
11104
|
-
streamed = true;
|
|
11105
|
-
process.stdout.write(redactText(text, secretValues));
|
|
11106
|
-
}
|
|
11107
|
-
});
|
|
11108
|
-
if (!streamed && output) {
|
|
11109
|
-
process.stdout.write(redactText(output, secretValues));
|
|
11110
|
-
}
|
|
11111
|
-
if (!streamed || !output) process.stdout.write("\n");
|
|
11112
|
-
} finally {
|
|
11113
|
-
await runtime.sessions.persist(session.id).catch(() => {
|
|
11114
|
-
});
|
|
11115
|
-
}
|
|
11116
|
-
}
|
|
11117
11136
|
function sessionLabel(session) {
|
|
11118
11137
|
const name = typeof session.metadata["name"] === "string" ? session.metadata["name"] : void 0;
|
|
11119
11138
|
const firstUser = session.messages.find((m) => m.role === "user");
|
|
@@ -31484,7 +31503,7 @@ function createProgram() {
|
|
|
31484
31503
|
"focus area: security, performance, correctness, style; repeat for multiple",
|
|
31485
31504
|
collectOption,
|
|
31486
31505
|
[]
|
|
31487
|
-
).action(async (number, options) => {
|
|
31506
|
+
).option("--provider <provider>", "provider override").option("--model <model>", "model override").action(async (number, options) => {
|
|
31488
31507
|
const prNumber = Number.parseInt(number, 10);
|
|
31489
31508
|
if (!Number.isInteger(prNumber) || prNumber <= 0) {
|
|
31490
31509
|
throw new Error(`Invalid PR number: ${number}`);
|
|
@@ -31492,7 +31511,9 @@ function createProgram() {
|
|
|
31492
31511
|
await reviewPrCommand(prNumber, {
|
|
31493
31512
|
cwd: program.opts().cwd,
|
|
31494
31513
|
config: program.opts().config,
|
|
31495
|
-
focus: options.focus
|
|
31514
|
+
focus: options.focus,
|
|
31515
|
+
provider: options.provider,
|
|
31516
|
+
model: options.model
|
|
31496
31517
|
});
|
|
31497
31518
|
});
|
|
31498
31519
|
github.command("solve").description("solve a GitHub issue end-to-end with branch, commit, push, and PR").argument("<number>", "issue number").option("--base <base>", "base branch", "main").option("-y, --yes", "approve commit/push/PR workflow").action(async (number, options) => {
|