deepcode-ai 1.1.25 → 1.1.27
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 +312 -165
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2281,8 +2281,8 @@ var PermissionDeniedError = class extends DeepCodeError {
|
|
|
2281
2281
|
}
|
|
2282
2282
|
};
|
|
2283
2283
|
var PathNotAllowedError = class extends DeepCodeError {
|
|
2284
|
-
constructor(
|
|
2285
|
-
super(`Path is not allowed: ${
|
|
2284
|
+
constructor(path152, reason) {
|
|
2285
|
+
super(`Path is not allowed: ${path152}. ${reason}`, "PATH_NOT_ALLOWED");
|
|
2286
2286
|
this.name = "PathNotAllowedError";
|
|
2287
2287
|
}
|
|
2288
2288
|
};
|
|
@@ -5043,13 +5043,13 @@ var GitHubClient = class {
|
|
|
5043
5043
|
}
|
|
5044
5044
|
return parseGitHubRemote(result.stdout.trim());
|
|
5045
5045
|
}
|
|
5046
|
-
async request(
|
|
5046
|
+
async request(path152, init = {}) {
|
|
5047
5047
|
if (!this.options.token) {
|
|
5048
5048
|
throw new Error(
|
|
5049
5049
|
"GitHub token is required. Set GITHUB_TOKEN or .deepcode/config.json github.token."
|
|
5050
5050
|
);
|
|
5051
5051
|
}
|
|
5052
|
-
const response = await fetch(`${this.apiBase}${
|
|
5052
|
+
const response = await fetch(`${this.apiBase}${path152}`, {
|
|
5053
5053
|
...init,
|
|
5054
5054
|
headers: {
|
|
5055
5055
|
accept: "application/vnd.github+json",
|
|
@@ -5065,13 +5065,13 @@ var GitHubClient = class {
|
|
|
5065
5065
|
if (response.status === 204) return void 0;
|
|
5066
5066
|
return await response.json();
|
|
5067
5067
|
}
|
|
5068
|
-
async requestText(
|
|
5068
|
+
async requestText(path152, init = {}) {
|
|
5069
5069
|
if (!this.options.token) {
|
|
5070
5070
|
throw new Error(
|
|
5071
5071
|
"GitHub token is required. Set GITHUB_TOKEN or .deepcode/config.json github.token."
|
|
5072
5072
|
);
|
|
5073
5073
|
}
|
|
5074
|
-
const response = await fetch(`${this.apiBase}${
|
|
5074
|
+
const response = await fetch(`${this.apiBase}${path152}`, {
|
|
5075
5075
|
...init,
|
|
5076
5076
|
headers: {
|
|
5077
5077
|
accept: "application/vnd.github+json",
|
|
@@ -5539,12 +5539,12 @@ function fromFileUri(uri) {
|
|
|
5539
5539
|
var SECRET_KEY_PATTERN = /(api[_-]?key|token|authorization|secret|password|passwd|credential|private[_-]?key)/i;
|
|
5540
5540
|
var MIN_SECRET_VALUE_LENGTH = 4;
|
|
5541
5541
|
function redactSecrets(value, options = {}) {
|
|
5542
|
-
const
|
|
5542
|
+
const path152 = options.path ?? [];
|
|
5543
5543
|
const secretPlaceholder = options.secretPlaceholder ?? "[redacted]";
|
|
5544
5544
|
const emptySecretPlaceholder = options.emptySecretPlaceholder ?? "[empty]";
|
|
5545
5545
|
const secretValues = options.secretValues ?? collectSecretValues();
|
|
5546
5546
|
if (typeof value === "string") {
|
|
5547
|
-
if (isSecretPath(
|
|
5547
|
+
if (isSecretPath(path152)) {
|
|
5548
5548
|
return value.length > 0 ? secretPlaceholder : emptySecretPlaceholder;
|
|
5549
5549
|
}
|
|
5550
5550
|
return redactText(value, secretValues, secretPlaceholder);
|
|
@@ -5553,7 +5553,7 @@ function redactSecrets(value, options = {}) {
|
|
|
5553
5553
|
return value.map(
|
|
5554
5554
|
(item, index) => redactSecrets(item, {
|
|
5555
5555
|
...options,
|
|
5556
|
-
path: [...
|
|
5556
|
+
path: [...path152, String(index)],
|
|
5557
5557
|
secretValues
|
|
5558
5558
|
})
|
|
5559
5559
|
);
|
|
@@ -5564,7 +5564,7 @@ function redactSecrets(value, options = {}) {
|
|
|
5564
5564
|
key,
|
|
5565
5565
|
redactSecrets(item, {
|
|
5566
5566
|
...options,
|
|
5567
|
-
path: [...
|
|
5567
|
+
path: [...path152, key],
|
|
5568
5568
|
secretValues
|
|
5569
5569
|
})
|
|
5570
5570
|
])
|
|
@@ -5602,8 +5602,8 @@ function collectSecretValues(config) {
|
|
|
5602
5602
|
}
|
|
5603
5603
|
return [...values].sort((left, right) => right.length - left.length);
|
|
5604
5604
|
}
|
|
5605
|
-
function isSecretPath(
|
|
5606
|
-
const key =
|
|
5605
|
+
function isSecretPath(path152) {
|
|
5606
|
+
const key = path152[path152.length - 1] ?? "";
|
|
5607
5607
|
if (/(api[_-]?key|token|secret|credential).*file/i.test(key)) return false;
|
|
5608
5608
|
return SECRET_KEY_PATTERN.test(key);
|
|
5609
5609
|
}
|
|
@@ -7824,8 +7824,8 @@ import { useState as useState22, useEffect as useEffect22, useCallback as useCal
|
|
|
7824
7824
|
import { Box as Box2, Text as Text22, useInput as useInput2, useApp as useApp2 } from "ink";
|
|
7825
7825
|
import path42 from "path";
|
|
7826
7826
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
7827
|
-
import
|
|
7828
|
-
import
|
|
7827
|
+
import fs8 from "fs";
|
|
7828
|
+
import path15 from "path";
|
|
7829
7829
|
import { isValidElement, useCallback as useCallback26, useEffect as useEffect27, useMemo as useMemo18, useRef as useRef17, useState as useState29 } from "react";
|
|
7830
7830
|
import { Box as Box42, Text as Text50, useInput as useInput6, useStdin as useStdin3 } from "ink";
|
|
7831
7831
|
import os4 from "os";
|
|
@@ -9810,6 +9810,9 @@ import { jsx as jsx39, jsxs as jsxs35 } from "react/jsx-runtime";
|
|
|
9810
9810
|
import { Box as Box31, Text as Text39 } from "ink";
|
|
9811
9811
|
import { jsx as jsx40, jsxs as jsxs36 } from "react/jsx-runtime";
|
|
9812
9812
|
import { jsx as jsx41, jsxs as jsxs37 } from "react/jsx-runtime";
|
|
9813
|
+
import fs6 from "fs";
|
|
9814
|
+
import os5 from "os";
|
|
9815
|
+
import path132 from "path";
|
|
9813
9816
|
import { Box as Box33, Text as Text41 } from "ink";
|
|
9814
9817
|
import { jsx as jsx42, jsxs as jsxs38 } from "react/jsx-runtime";
|
|
9815
9818
|
import { useCallback as useCallback19, useMemo as useMemo11, useRef as useRef14 } from "react";
|
|
@@ -9827,8 +9830,8 @@ import { jsx as jsx46, jsxs as jsxs42 } from "react/jsx-runtime";
|
|
|
9827
9830
|
import { useCallback as useCallback23, useEffect as useEffect25, useMemo as useMemo15, useRef as useRef16, useState as useState27 } from "react";
|
|
9828
9831
|
import { Box as Box38, Text as Text46, useInput as useInput4 } from "ink";
|
|
9829
9832
|
import { jsx as jsx47, jsxs as jsxs43 } from "react/jsx-runtime";
|
|
9830
|
-
import
|
|
9831
|
-
import
|
|
9833
|
+
import fs7 from "fs";
|
|
9834
|
+
import path142 from "path";
|
|
9832
9835
|
import { useCallback as useCallback24, useMemo as useMemo16 } from "react";
|
|
9833
9836
|
import { Box as Box39, Text as Text47 } from "ink";
|
|
9834
9837
|
import { jsx as jsx48, jsxs as jsxs44 } from "react/jsx-runtime";
|
|
@@ -10382,6 +10385,141 @@ function parseProviderId(value) {
|
|
|
10382
10385
|
if (parsed.success) return parsed.data;
|
|
10383
10386
|
throw new Error(`Invalid provider: ${value}. Expected one of: ${PROVIDER_IDS.join(", ")}`);
|
|
10384
10387
|
}
|
|
10388
|
+
var DIFF_MAX_CHARS = 2e4;
|
|
10389
|
+
function truncateDiff(raw, maxChars = DIFF_MAX_CHARS) {
|
|
10390
|
+
const fileChunks = raw.split(/(?=^diff --git )/m).filter(Boolean);
|
|
10391
|
+
const totalFiles = fileChunks.length;
|
|
10392
|
+
let result = "";
|
|
10393
|
+
let included = 0;
|
|
10394
|
+
for (const chunk of fileChunks) {
|
|
10395
|
+
if (result.length + chunk.length > maxChars) break;
|
|
10396
|
+
result += chunk;
|
|
10397
|
+
included++;
|
|
10398
|
+
}
|
|
10399
|
+
if (!result && fileChunks.length > 0) {
|
|
10400
|
+
result = fileChunks[0].slice(0, maxChars);
|
|
10401
|
+
included = 1;
|
|
10402
|
+
}
|
|
10403
|
+
return { diff: result.trimEnd(), omittedFiles: totalFiles - included, totalFiles };
|
|
10404
|
+
}
|
|
10405
|
+
async function runGit(cwd, args) {
|
|
10406
|
+
const result = await execFileAsync("git", args, { cwd, timeoutMs: 3e4 });
|
|
10407
|
+
if (result.exitCode !== 0) {
|
|
10408
|
+
throw new Error(result.stderr || `git ${args.join(" ")} failed`);
|
|
10409
|
+
}
|
|
10410
|
+
return result.stdout;
|
|
10411
|
+
}
|
|
10412
|
+
async function isGitRepo(cwd) {
|
|
10413
|
+
const result = await execFileAsync(
|
|
10414
|
+
"git",
|
|
10415
|
+
["rev-parse", "--is-inside-work-tree"],
|
|
10416
|
+
{ cwd, timeoutMs: 5e3 }
|
|
10417
|
+
);
|
|
10418
|
+
return result.exitCode === 0;
|
|
10419
|
+
}
|
|
10420
|
+
function buildDiffArgs(options) {
|
|
10421
|
+
if (options.staged) {
|
|
10422
|
+
const args2 = ["diff", "--cached"];
|
|
10423
|
+
if (options.file) args2.push("--", options.file);
|
|
10424
|
+
return { args: args2, label: "staged changes" };
|
|
10425
|
+
}
|
|
10426
|
+
if (options.ref) {
|
|
10427
|
+
const args2 = ["diff", options.ref];
|
|
10428
|
+
if (options.file) args2.push("--", options.file);
|
|
10429
|
+
return { args: args2, label: `diff vs ${options.ref}` };
|
|
10430
|
+
}
|
|
10431
|
+
const args = ["diff", "HEAD"];
|
|
10432
|
+
if (options.file) args.push("--", options.file);
|
|
10433
|
+
return { args, label: options.file ? `local changes in ${options.file}` : "local changes vs HEAD" };
|
|
10434
|
+
}
|
|
10435
|
+
function buildPrompt(diff, label, focus, truncation) {
|
|
10436
|
+
const focusLine = focus.length > 0 ? `
|
|
10437
|
+
Focus areas: ${focus.join(", ")}.` : "";
|
|
10438
|
+
const truncationNote = truncation.omittedFiles > 0 ? `
|
|
10439
|
+
(Showing ${truncation.totalFiles - truncation.omittedFiles} of ${truncation.totalFiles} changed files; ${truncation.omittedFiles} file(s) omitted due to size.)
|
|
10440
|
+
` : "";
|
|
10441
|
+
return [
|
|
10442
|
+
`Review the following local git diff (${label}).`,
|
|
10443
|
+
"Do not modify any files. Output the review only.",
|
|
10444
|
+
focusLine,
|
|
10445
|
+
"",
|
|
10446
|
+
`\`\`\`diff`,
|
|
10447
|
+
diff,
|
|
10448
|
+
`\`\`\``,
|
|
10449
|
+
truncationNote,
|
|
10450
|
+
"Produce a structured code review:",
|
|
10451
|
+
"1. **Summary** \u2014 what changed (inferred from the diff)",
|
|
10452
|
+
"2. **Issues** \u2014 bugs, security concerns, logic errors, missing error handling; quote the relevant lines",
|
|
10453
|
+
"3. **Suggestions** \u2014 improvements and nitpicks",
|
|
10454
|
+
"4. **Verdict** \u2014 Looks good / Has issues, with a one-line rationale"
|
|
10455
|
+
].filter((l) => l !== void 0).join("\n");
|
|
10456
|
+
}
|
|
10457
|
+
async function reviewCommand(options) {
|
|
10458
|
+
if (!await isGitRepo(options.cwd)) {
|
|
10459
|
+
await writeStderrLine("error: not inside a git repository");
|
|
10460
|
+
process.exit(1);
|
|
10461
|
+
}
|
|
10462
|
+
const { args, label } = buildDiffArgs(options);
|
|
10463
|
+
let rawDiff;
|
|
10464
|
+
try {
|
|
10465
|
+
rawDiff = await runGit(options.cwd, args);
|
|
10466
|
+
} catch (err) {
|
|
10467
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
10468
|
+
await writeStderrLine(`error: ${msg}`);
|
|
10469
|
+
process.exit(1);
|
|
10470
|
+
}
|
|
10471
|
+
const trimmed = rawDiff.trim();
|
|
10472
|
+
if (!trimmed) {
|
|
10473
|
+
await writeStdoutLine(`No changes to review (${label}).`);
|
|
10474
|
+
return;
|
|
10475
|
+
}
|
|
10476
|
+
const truncation = truncateDiff(trimmed);
|
|
10477
|
+
const runtime = await createRuntime({
|
|
10478
|
+
cwd: options.cwd,
|
|
10479
|
+
configPath: options.config,
|
|
10480
|
+
interactive: Boolean(options.yes)
|
|
10481
|
+
});
|
|
10482
|
+
if (options.yes) {
|
|
10483
|
+
runtime.events.on("approval:request", (request) => {
|
|
10484
|
+
runtime.events.emit("approval:decision", {
|
|
10485
|
+
requestId: request.id,
|
|
10486
|
+
decision: { allowed: true }
|
|
10487
|
+
});
|
|
10488
|
+
});
|
|
10489
|
+
}
|
|
10490
|
+
const target = resolveSessionTarget(runtime.config, {
|
|
10491
|
+
provider: options.provider,
|
|
10492
|
+
model: options.model
|
|
10493
|
+
});
|
|
10494
|
+
const session = runtime.sessions.create({
|
|
10495
|
+
provider: target.provider,
|
|
10496
|
+
model: target.model
|
|
10497
|
+
});
|
|
10498
|
+
const prompt = buildPrompt(truncation.diff, label, options.focus ?? [], truncation);
|
|
10499
|
+
const secretValues = collectSecretValues(runtime.config);
|
|
10500
|
+
await writeStdoutLine(`Reviewing ${label}\u2026
|
|
10501
|
+
`);
|
|
10502
|
+
let streamed = false;
|
|
10503
|
+
try {
|
|
10504
|
+
const output = await runtime.agent.run({
|
|
10505
|
+
session,
|
|
10506
|
+
input: prompt,
|
|
10507
|
+
mode: "plan",
|
|
10508
|
+
provider: target.provider,
|
|
10509
|
+
onChunk: (text) => {
|
|
10510
|
+
streamed = true;
|
|
10511
|
+
process.stdout.write(redactText(text, secretValues));
|
|
10512
|
+
}
|
|
10513
|
+
});
|
|
10514
|
+
if (!streamed && output) {
|
|
10515
|
+
process.stdout.write(redactText(output, secretValues));
|
|
10516
|
+
}
|
|
10517
|
+
if (!streamed || !output) process.stdout.write("\n");
|
|
10518
|
+
} finally {
|
|
10519
|
+
await runtime.sessions.persist(session.id).catch(() => {
|
|
10520
|
+
});
|
|
10521
|
+
}
|
|
10522
|
+
}
|
|
10385
10523
|
async function githubLoginCommand(options) {
|
|
10386
10524
|
const loader = new ConfigLoader();
|
|
10387
10525
|
const loadOptions = { cwd: options.cwd, configPath: options.config };
|
|
@@ -10561,8 +10699,8 @@ async function solveIssueCommand(issueNumber, options) {
|
|
|
10561
10699
|
const issue = await client.getIssue({ ...repo, number: issueNumber });
|
|
10562
10700
|
const base = options.base ?? "main";
|
|
10563
10701
|
const branch = `deepcode/issue-${issueNumber}-${slugify(issue.title)}`.slice(0, 80);
|
|
10564
|
-
await
|
|
10565
|
-
await
|
|
10702
|
+
await runGit2(options.cwd, ["fetch", "origin", base]);
|
|
10703
|
+
await runGit2(options.cwd, ["checkout", "-B", branch, `origin/${base}`]);
|
|
10566
10704
|
const target = resolveUsableProviderTarget(runtime.config, [runtime.config.defaultProvider]);
|
|
10567
10705
|
const session = runtime.sessions.create({
|
|
10568
10706
|
provider: target.provider,
|
|
@@ -10587,16 +10725,16 @@ async function solveIssueCommand(issueNumber, options) {
|
|
|
10587
10725
|
onChunk: (text) => void writeStdout(redactText(text, secretValues))
|
|
10588
10726
|
});
|
|
10589
10727
|
await writeStdout("\n");
|
|
10590
|
-
const status = await
|
|
10591
|
-
const aheadLog = await
|
|
10728
|
+
const status = await runGit2(options.cwd, ["status", "--porcelain"]);
|
|
10729
|
+
const aheadLog = await runGit2(options.cwd, ["log", `origin/${base}..HEAD`, "--oneline"]);
|
|
10592
10730
|
const hasUncommitted = Boolean(status.stdout.trim());
|
|
10593
10731
|
const hasCommits = Boolean(aheadLog.stdout.trim());
|
|
10594
10732
|
if (!hasUncommitted && !hasCommits) {
|
|
10595
10733
|
throw new Error("Agent completed without file changes; no PR was created.");
|
|
10596
10734
|
}
|
|
10597
10735
|
if (hasUncommitted) {
|
|
10598
|
-
await
|
|
10599
|
-
await
|
|
10736
|
+
await runGit2(options.cwd, ["add", "."]);
|
|
10737
|
+
await runGit2(options.cwd, [
|
|
10600
10738
|
"commit",
|
|
10601
10739
|
"-m",
|
|
10602
10740
|
`fix: resolve issue #${issue.number}`,
|
|
@@ -10606,7 +10744,7 @@ async function solveIssueCommand(issueNumber, options) {
|
|
|
10606
10744
|
Closes #${issue.number}`
|
|
10607
10745
|
]);
|
|
10608
10746
|
}
|
|
10609
|
-
await
|
|
10747
|
+
await runGit2(options.cwd, ["push", "-u", "origin", branch]);
|
|
10610
10748
|
const pr = await client.createPullRequest({
|
|
10611
10749
|
...repo,
|
|
10612
10750
|
title: `Fix: ${issue.title}`,
|
|
@@ -10645,6 +10783,10 @@ async function reviewPrCommand(prNumber, options) {
|
|
|
10645
10783
|
]);
|
|
10646
10784
|
const focusLine = options.focus && options.focus.length > 0 ? `
|
|
10647
10785
|
Focus areas: ${options.focus.join(", ")}.` : "";
|
|
10786
|
+
const truncation = truncateDiff(diff.trim());
|
|
10787
|
+
const truncationNote = truncation.omittedFiles > 0 ? `
|
|
10788
|
+
(Showing ${truncation.totalFiles - truncation.omittedFiles} of ${truncation.totalFiles} changed files; ${truncation.omittedFiles} file(s) omitted due to size.)
|
|
10789
|
+
` : "";
|
|
10648
10790
|
const prompt = [
|
|
10649
10791
|
`Review PR #${pr.number}: ${pr.title}`,
|
|
10650
10792
|
`Branch: ${pr.head ?? "?"} \u2192 ${pr.base ?? "?"}`,
|
|
@@ -10655,8 +10797,9 @@ ${pr.body}` : "No description provided.",
|
|
|
10655
10797
|
"",
|
|
10656
10798
|
`Diff:
|
|
10657
10799
|
\`\`\`diff
|
|
10658
|
-
${diff}
|
|
10800
|
+
${truncation.diff}
|
|
10659
10801
|
\`\`\``,
|
|
10802
|
+
truncationNote,
|
|
10660
10803
|
"",
|
|
10661
10804
|
`Produce a structured code review with:${focusLine}`,
|
|
10662
10805
|
"1. **Summary** \u2014 what the PR does",
|
|
@@ -10664,7 +10807,10 @@ ${diff}
|
|
|
10664
10807
|
"3. **Suggestions** \u2014 improvements and nitpicks",
|
|
10665
10808
|
"4. **Verdict** \u2014 Approve / Request Changes / Neutral with a one-line rationale"
|
|
10666
10809
|
].join("\n");
|
|
10667
|
-
const target = resolveSessionTarget(runtime.config, {
|
|
10810
|
+
const target = resolveSessionTarget(runtime.config, {
|
|
10811
|
+
provider: options.provider,
|
|
10812
|
+
model: options.model
|
|
10813
|
+
});
|
|
10668
10814
|
const session = runtime.sessions.create({
|
|
10669
10815
|
provider: target.provider,
|
|
10670
10816
|
model: target.model
|
|
@@ -10684,7 +10830,7 @@ ${diff}
|
|
|
10684
10830
|
}
|
|
10685
10831
|
await writeStdout("\n");
|
|
10686
10832
|
}
|
|
10687
|
-
async function
|
|
10833
|
+
async function runGit2(cwd, args) {
|
|
10688
10834
|
const result = await execFileAsync("git", args, { cwd, timeoutMs: 18e4 });
|
|
10689
10835
|
if (result.exitCode !== 0) {
|
|
10690
10836
|
throw new Error(result.stderr || result.stdout || `git ${args.join(" ")} failed`);
|
|
@@ -10990,130 +11136,6 @@ async function projectsCommand(options) {
|
|
|
10990
11136
|
);
|
|
10991
11137
|
await waitUntilExit();
|
|
10992
11138
|
}
|
|
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
11139
|
function sessionLabel(session) {
|
|
11118
11140
|
const name = typeof session.metadata["name"] === "string" ? session.metadata["name"] : void 0;
|
|
11119
11141
|
const firstUser = session.messages.find((m) => m.role === "user");
|
|
@@ -28020,6 +28042,109 @@ var compactCommand = {
|
|
|
28020
28042
|
await context.ui.compact();
|
|
28021
28043
|
}
|
|
28022
28044
|
};
|
|
28045
|
+
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
28046
|
+
var PACKAGE_NAME = "deepcode-ai";
|
|
28047
|
+
function cachePath() {
|
|
28048
|
+
const cacheHome = process.env["XDG_CACHE_HOME"] ?? path132.join(os5.homedir(), ".cache");
|
|
28049
|
+
return path132.join(cacheHome, "deepcode-ai", "update.json");
|
|
28050
|
+
}
|
|
28051
|
+
function readCache() {
|
|
28052
|
+
try {
|
|
28053
|
+
const raw = fs6.readFileSync(cachePath(), "utf8");
|
|
28054
|
+
const parsed = JSON.parse(raw);
|
|
28055
|
+
if (typeof parsed.checkedAt !== "number" || typeof parsed.latest !== "string" || Date.now() - parsed.checkedAt >= CACHE_TTL_MS) {
|
|
28056
|
+
return null;
|
|
28057
|
+
}
|
|
28058
|
+
return {
|
|
28059
|
+
checkedAt: parsed.checkedAt,
|
|
28060
|
+
latest: parsed.latest,
|
|
28061
|
+
stable: typeof parsed.stable === "string" ? parsed.stable : null
|
|
28062
|
+
};
|
|
28063
|
+
} catch {
|
|
28064
|
+
return null;
|
|
28065
|
+
}
|
|
28066
|
+
}
|
|
28067
|
+
function writeCache(cache) {
|
|
28068
|
+
try {
|
|
28069
|
+
const filePath = cachePath();
|
|
28070
|
+
fs6.mkdirSync(path132.dirname(filePath), { recursive: true });
|
|
28071
|
+
fs6.writeFileSync(filePath, `${JSON.stringify(cache)}
|
|
28072
|
+
`, "utf8");
|
|
28073
|
+
} catch {
|
|
28074
|
+
}
|
|
28075
|
+
}
|
|
28076
|
+
async function checkForUpdate(_currentVersion, options = {}) {
|
|
28077
|
+
if (process.env["CI"] || process.env["NODE_ENV"] === "test" || process.env["DEEPCODE_DISABLE_UPDATE_CHECK"] === "1") {
|
|
28078
|
+
return null;
|
|
28079
|
+
}
|
|
28080
|
+
if (!options.force) {
|
|
28081
|
+
const cached = readCache();
|
|
28082
|
+
if (cached) {
|
|
28083
|
+
return { latest: cached.latest, stable: cached.stable };
|
|
28084
|
+
}
|
|
28085
|
+
}
|
|
28086
|
+
try {
|
|
28087
|
+
const response = await fetch(
|
|
28088
|
+
`https://registry.npmjs.org/-/package/${PACKAGE_NAME}/dist-tags`,
|
|
28089
|
+
{ signal: AbortSignal.timeout(3e3) }
|
|
28090
|
+
);
|
|
28091
|
+
if (!response.ok) return null;
|
|
28092
|
+
const tags = await response.json();
|
|
28093
|
+
const latest = tags["latest"];
|
|
28094
|
+
if (typeof latest !== "string" || latest.length === 0) return null;
|
|
28095
|
+
const stable = typeof tags["stable"] === "string" && tags["stable"].length > 0 ? tags["stable"] : null;
|
|
28096
|
+
const update = { latest, stable };
|
|
28097
|
+
writeCache({ ...update, checkedAt: Date.now() });
|
|
28098
|
+
return update;
|
|
28099
|
+
} catch {
|
|
28100
|
+
return null;
|
|
28101
|
+
}
|
|
28102
|
+
}
|
|
28103
|
+
function isNewer(current, candidate) {
|
|
28104
|
+
const currentParts = parseVersion2(current);
|
|
28105
|
+
const candidateParts = parseVersion2(candidate);
|
|
28106
|
+
if (!currentParts || !candidateParts) return false;
|
|
28107
|
+
for (let index = 0; index < 3; index += 1) {
|
|
28108
|
+
const currentPart = currentParts[index] ?? 0;
|
|
28109
|
+
const candidatePart = candidateParts[index] ?? 0;
|
|
28110
|
+
if (candidatePart !== currentPart) {
|
|
28111
|
+
return candidatePart > currentPart;
|
|
28112
|
+
}
|
|
28113
|
+
}
|
|
28114
|
+
return false;
|
|
28115
|
+
}
|
|
28116
|
+
function parseVersion2(version) {
|
|
28117
|
+
const match = version.trim().match(/^v?(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/);
|
|
28118
|
+
if (!match) return null;
|
|
28119
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
28120
|
+
}
|
|
28121
|
+
var VERSION = "1.1.27".length > 0 ? "1.1.27" : "0.0.0-dev";
|
|
28122
|
+
var updateCommand = {
|
|
28123
|
+
name: "update",
|
|
28124
|
+
description: "Check published DeepCode versions",
|
|
28125
|
+
kind: "built-in",
|
|
28126
|
+
supportedModes: ["interactive"],
|
|
28127
|
+
action: async () => {
|
|
28128
|
+
const update = await checkForUpdate(VERSION, { force: true });
|
|
28129
|
+
const lines = [`Current version: ${VERSION}`];
|
|
28130
|
+
if (!update) {
|
|
28131
|
+
lines.push("Could not reach the npm registry right now.");
|
|
28132
|
+
} else {
|
|
28133
|
+
const latestStatus = isNewer(VERSION, update.latest) ? "available" : "current or older";
|
|
28134
|
+
lines.push(`Latest version: ${update.latest} (${latestStatus})`);
|
|
28135
|
+
if (update.stable) {
|
|
28136
|
+
const stableStatus = isNewer(VERSION, update.stable) ? "available" : "current or older";
|
|
28137
|
+
lines.push(`Stable version: ${update.stable} (${stableStatus})`);
|
|
28138
|
+
} else {
|
|
28139
|
+
lines.push("Stable version: not published yet");
|
|
28140
|
+
}
|
|
28141
|
+
}
|
|
28142
|
+
lines.push("");
|
|
28143
|
+
lines.push("Install latest: npm install -g deepcode-ai@latest");
|
|
28144
|
+
lines.push("Install stable: npm install -g deepcode-ai@stable");
|
|
28145
|
+
return { type: "message", messageType: "info", content: lines.join("\n") };
|
|
28146
|
+
}
|
|
28147
|
+
};
|
|
28023
28148
|
function sessionNotReady() {
|
|
28024
28149
|
return {
|
|
28025
28150
|
type: "message",
|
|
@@ -29074,11 +29199,11 @@ var RATINGS = [
|
|
|
29074
29199
|
];
|
|
29075
29200
|
var CANCEL_VALUE2 = "__cancel__";
|
|
29076
29201
|
function appendFeedbackEntry(cwd, rating, label) {
|
|
29077
|
-
const file =
|
|
29202
|
+
const file = path142.join(cwd, ".deepcode", "feedback.log");
|
|
29078
29203
|
const entry = JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), rating, label });
|
|
29079
29204
|
try {
|
|
29080
|
-
|
|
29081
|
-
|
|
29205
|
+
fs7.mkdirSync(path142.dirname(file), { recursive: true });
|
|
29206
|
+
fs7.appendFileSync(file, `${entry}
|
|
29082
29207
|
`, "utf8");
|
|
29083
29208
|
} catch {
|
|
29084
29209
|
}
|
|
@@ -29655,12 +29780,12 @@ var AppContainer = ({ cwd, config, provider, model, resumeSessionId }) => {
|
|
|
29655
29780
|
const configAdapter = configAdapterRef.current ?? new DeepCodeConfigAdapter(cwd);
|
|
29656
29781
|
const isValidPath = useCallback26(
|
|
29657
29782
|
(candidate) => {
|
|
29658
|
-
const resolved =
|
|
29659
|
-
const relative2 =
|
|
29660
|
-
if (relative2.startsWith("..") ||
|
|
29783
|
+
const resolved = path15.resolve(cwd, candidate);
|
|
29784
|
+
const relative2 = path15.relative(cwd, resolved);
|
|
29785
|
+
if (relative2.startsWith("..") || path15.isAbsolute(relative2)) {
|
|
29661
29786
|
return false;
|
|
29662
29787
|
}
|
|
29663
|
-
return
|
|
29788
|
+
return fs8.existsSync(resolved);
|
|
29664
29789
|
},
|
|
29665
29790
|
[cwd]
|
|
29666
29791
|
);
|
|
@@ -29690,6 +29815,7 @@ var AppContainer = ({ cwd, config, provider, model, resumeSessionId }) => {
|
|
|
29690
29815
|
modelCommand,
|
|
29691
29816
|
modeCommand,
|
|
29692
29817
|
renameCommand,
|
|
29818
|
+
updateCommand,
|
|
29693
29819
|
settingsDialogCommand,
|
|
29694
29820
|
themeDialogCommand,
|
|
29695
29821
|
permissionsDialogCommand,
|
|
@@ -30072,6 +30198,25 @@ var AppContainer = ({ cwd, config, provider, model, resumeSessionId }) => {
|
|
|
30072
30198
|
);
|
|
30073
30199
|
}
|
|
30074
30200
|
setIsInitializing(false);
|
|
30201
|
+
checkForUpdate(VERSION).then((update) => {
|
|
30202
|
+
if (!mounted || !update) return;
|
|
30203
|
+
const available = [];
|
|
30204
|
+
if (isNewer(VERSION, update.latest)) {
|
|
30205
|
+
available.push(`latest v${update.latest}`);
|
|
30206
|
+
}
|
|
30207
|
+
if (update.stable && isNewer(VERSION, update.stable)) {
|
|
30208
|
+
available.push(`stable v${update.stable}`);
|
|
30209
|
+
}
|
|
30210
|
+
if (available.length === 0) return;
|
|
30211
|
+
addHistoryItem(
|
|
30212
|
+
{
|
|
30213
|
+
type: "info",
|
|
30214
|
+
text: `Update available: ${available.join(", ")}. Run /update for install commands.`
|
|
30215
|
+
},
|
|
30216
|
+
Date.now()
|
|
30217
|
+
);
|
|
30218
|
+
}).catch(() => {
|
|
30219
|
+
});
|
|
30075
30220
|
} catch (error) {
|
|
30076
30221
|
if (!mounted) return;
|
|
30077
30222
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -30944,7 +31089,7 @@ var AppContainer = ({ cwd, config, provider, model, resumeSessionId }) => {
|
|
|
30944
31089
|
mainControlsRef,
|
|
30945
31090
|
constrainHeight: false,
|
|
30946
31091
|
currentModel,
|
|
30947
|
-
sessionName:
|
|
31092
|
+
sessionName: path15.basename(cwd),
|
|
30948
31093
|
isConfigInitialized: !isInitializing && !initError,
|
|
30949
31094
|
sessionStats: {
|
|
30950
31095
|
lastPromptTokenCount,
|
|
@@ -31183,11 +31328,11 @@ function isInteractiveDialog(dialog) {
|
|
|
31183
31328
|
return dialog === "theme" || dialog === "permissions" || dialog === "auth" || dialog === "provider" || dialog === "model" || dialog === "feedback" || dialog === "sessions";
|
|
31184
31329
|
}
|
|
31185
31330
|
function tuiThemeFilePath(cwd) {
|
|
31186
|
-
return
|
|
31331
|
+
return path15.join(cwd, ".deepcode", "tui-theme.json");
|
|
31187
31332
|
}
|
|
31188
31333
|
function readSavedTheme(cwd) {
|
|
31189
31334
|
try {
|
|
31190
|
-
const parsed = JSON.parse(
|
|
31335
|
+
const parsed = JSON.parse(fs8.readFileSync(tuiThemeFilePath(cwd), "utf8"));
|
|
31191
31336
|
return typeof parsed.theme === "string" ? parsed.theme : null;
|
|
31192
31337
|
} catch {
|
|
31193
31338
|
return null;
|
|
@@ -31195,8 +31340,8 @@ function readSavedTheme(cwd) {
|
|
|
31195
31340
|
}
|
|
31196
31341
|
function writeSavedTheme(cwd, themeName) {
|
|
31197
31342
|
const file = tuiThemeFilePath(cwd);
|
|
31198
|
-
|
|
31199
|
-
|
|
31343
|
+
fs8.mkdirSync(path15.dirname(file), { recursive: true });
|
|
31344
|
+
fs8.writeFileSync(file, `${JSON.stringify({ theme: themeName }, null, 2)}
|
|
31200
31345
|
`);
|
|
31201
31346
|
}
|
|
31202
31347
|
function errorMessage(error) {
|
|
@@ -31335,7 +31480,7 @@ function createProgram() {
|
|
|
31335
31480
|
writeOut: writeStdoutSync,
|
|
31336
31481
|
writeErr: writeStderrSync
|
|
31337
31482
|
});
|
|
31338
|
-
program.name("deepcode").description("AI coding agent for the terminal").version(
|
|
31483
|
+
program.name("deepcode").description("AI coding agent for the terminal").version(VERSION).option("-C, --cwd <path>", "working directory", process.cwd()).option("--config <path>", "config file path");
|
|
31339
31484
|
program.command("init").description("create .deepcode/config.json").action(async () => {
|
|
31340
31485
|
await initCommand(program.opts().cwd);
|
|
31341
31486
|
});
|
|
@@ -31484,7 +31629,7 @@ function createProgram() {
|
|
|
31484
31629
|
"focus area: security, performance, correctness, style; repeat for multiple",
|
|
31485
31630
|
collectOption,
|
|
31486
31631
|
[]
|
|
31487
|
-
).action(async (number, options) => {
|
|
31632
|
+
).option("--provider <provider>", "provider override").option("--model <model>", "model override").action(async (number, options) => {
|
|
31488
31633
|
const prNumber = Number.parseInt(number, 10);
|
|
31489
31634
|
if (!Number.isInteger(prNumber) || prNumber <= 0) {
|
|
31490
31635
|
throw new Error(`Invalid PR number: ${number}`);
|
|
@@ -31492,7 +31637,9 @@ function createProgram() {
|
|
|
31492
31637
|
await reviewPrCommand(prNumber, {
|
|
31493
31638
|
cwd: program.opts().cwd,
|
|
31494
31639
|
config: program.opts().config,
|
|
31495
|
-
focus: options.focus
|
|
31640
|
+
focus: options.focus,
|
|
31641
|
+
provider: options.provider,
|
|
31642
|
+
model: options.model
|
|
31496
31643
|
});
|
|
31497
31644
|
});
|
|
31498
31645
|
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) => {
|