jinzd-ai-cli 0.4.215 → 0.4.216
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 +2 -0
- package/dist/{batch-74H5SA7P.js → batch-C6HCXZIG.js} +2 -2
- package/dist/{chunk-ZOF5NFKW.js → chunk-6HLKCSB3.js} +1 -1
- package/dist/{chunk-X6OXS7KU.js → chunk-77HDCGTN.js} +1 -1
- package/dist/{chunk-7JES2NWR.js → chunk-7YWS3JLU.js} +1 -1
- package/dist/{ci-3ALK2XJN.js → chunk-GY3C3C2Y.js} +40 -46
- package/dist/{chunk-RS4WBI73.js → chunk-OLN7VUZA.js} +1 -1
- package/dist/{chunk-KL7UBVSQ.js → chunk-PNTLA3MN.js} +2 -2
- package/dist/{chunk-6C3JYNM6.js → chunk-TCOC4AUI.js} +3 -3
- package/dist/chunk-TJ6GGN4B.js +105 -0
- package/dist/{chunk-3TSHNZKI.js → chunk-VW7Y27WW.js} +1 -1
- package/dist/{chunk-2224JGA6.js → chunk-WAI3WPV2.js} +1 -1
- package/dist/ci-6ZTFO3LX.js +19 -0
- package/dist/ci-format-CLZ6QJRL.js +18 -0
- package/dist/{constants-IN2HXJK7.js → constants-APSORFOH.js} +1 -1
- package/dist/{doctor-cli-7XL4TCVT.js → doctor-cli-GUIX4X5F.js} +4 -4
- package/dist/electron-server.js +2 -2
- package/dist/{hub-VAOG5EY6.js → hub-ZGHQWNWE.js} +1 -1
- package/dist/index.js +58 -22
- package/dist/pr-JOL3IAGV.js +267 -0
- package/dist/{run-tests-J2JQ57R3.js → run-tests-4PKSIVK5.js} +1 -1
- package/dist/{run-tests-QJD43AML.js → run-tests-ZRK4TQUN.js} +2 -2
- package/dist/{server-VGPZOISQ.js → server-FXUF5P64.js} +7 -7
- package/dist/{server-WDLV3W4F.js → server-O3XHT56X.js} +4 -4
- package/dist/{task-orchestrator-QFOCO3N7.js → task-orchestrator-TUMDTOAX.js} +4 -4
- package/dist/{usage-IEB476NE.js → usage-X7MJX4YD.js} +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -47,6 +47,8 @@
|
|
|
47
47
|
- **Headless Mode** — `ai-cli -p "prompt"` for CI/CD pipelines and scripting
|
|
48
48
|
- **44 REPL Commands** — Session management, checkpointing, code review, security review/scan, rewind, scaffolding, cross-session history search, chat-memory recall, smart model routing (`/route`), and more
|
|
49
49
|
- **GitHub Actions CI/CD** — Automated testing on Node 20/22 + npm publish on release tags
|
|
50
|
+
- **PR Review CLI** *(v0.4.216+)* — `aicli pr review|security-review|summarize` reviews `main...HEAD`, custom `--base/--head`, or GitHub PR URLs; optional `--agents security,bugs,tests,maintainability`; never posts comments unless `--post` is explicit
|
|
51
|
+
- **CI Review Artifacts** *(v0.4.216+)* — `aicli ci` emits Markdown, JSON, or SARIF and gates on security-high, test-failure, and lint-failure findings in read-only review mode
|
|
50
52
|
- **Cross-Platform** — Windows, macOS, Linux
|
|
51
53
|
|
|
52
54
|
## Installation
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
ConfigManager
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-WAI3WPV2.js";
|
|
5
5
|
import "./chunk-TZQHYZKT.js";
|
|
6
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-7YWS3JLU.js";
|
|
7
7
|
import {
|
|
8
8
|
atomicWriteFileSync
|
|
9
9
|
} from "./chunk-IW3Q7AE5.js";
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
evaluateCiGate,
|
|
4
|
+
normalizeCiThresholds
|
|
5
|
+
} from "./chunk-TJ6GGN4B.js";
|
|
2
6
|
import {
|
|
3
7
|
buildReviewPrompt,
|
|
4
8
|
buildSecurityReviewPrompt,
|
|
@@ -7,19 +11,30 @@ import {
|
|
|
7
11
|
import {
|
|
8
12
|
ProviderRegistry
|
|
9
13
|
} from "./chunk-QMXC327F.js";
|
|
10
|
-
import "./chunk-XPBEJB27.js";
|
|
11
14
|
import {
|
|
12
15
|
ConfigManager
|
|
13
|
-
} from "./chunk-
|
|
14
|
-
import "./chunk-TZQHYZKT.js";
|
|
16
|
+
} from "./chunk-WAI3WPV2.js";
|
|
15
17
|
import {
|
|
16
18
|
VERSION
|
|
17
|
-
} from "./chunk-
|
|
18
|
-
import "./chunk-IW3Q7AE5.js";
|
|
19
|
+
} from "./chunk-7YWS3JLU.js";
|
|
19
20
|
|
|
20
21
|
// src/cli/ci.ts
|
|
21
22
|
import { execFileSync } from "child_process";
|
|
22
23
|
var CI_COMMENT_MARKER = "<!-- aicli-ci-review -->";
|
|
24
|
+
function zeroSeverity() {
|
|
25
|
+
return { critical: 0, high: 0, warning: 0, info: 0 };
|
|
26
|
+
}
|
|
27
|
+
function earlyResult(exitCode, markdown, thresholds) {
|
|
28
|
+
const gate = evaluateCiGate(markdown, thresholds);
|
|
29
|
+
return {
|
|
30
|
+
exitCode: exitCode === 0 && gate.failed ? 1 : exitCode,
|
|
31
|
+
markdown,
|
|
32
|
+
posted: false,
|
|
33
|
+
severity: zeroSeverity(),
|
|
34
|
+
gate,
|
|
35
|
+
meta: { mode: "read-only", thresholds }
|
|
36
|
+
};
|
|
37
|
+
}
|
|
23
38
|
function fetchDiff(opts) {
|
|
24
39
|
if (opts.diffOverride != null) return opts.diffOverride;
|
|
25
40
|
if (opts.pr != null) {
|
|
@@ -48,9 +63,7 @@ function fetchDiff(opts) {
|
|
|
48
63
|
throw new Error("aicli ci: must supply --pr <num> or --base <ref> to determine the diff source");
|
|
49
64
|
}
|
|
50
65
|
function validateGitRef(ref) {
|
|
51
|
-
if (!/^[A-Za-z0-9._/\-]+$/.test(ref)) {
|
|
52
|
-
throw new Error(`unsafe ref: ${ref}`);
|
|
53
|
-
}
|
|
66
|
+
if (!/^[A-Za-z0-9._/\-]+$/.test(ref)) throw new Error(`unsafe ref: ${ref}`);
|
|
54
67
|
}
|
|
55
68
|
function buildGitContextStr(opts) {
|
|
56
69
|
const parts = [];
|
|
@@ -62,6 +75,7 @@ function buildGitContextStr(opts) {
|
|
|
62
75
|
} catch {
|
|
63
76
|
}
|
|
64
77
|
parts.push(`Reviewer: aicli v${VERSION}`);
|
|
78
|
+
parts.push("Mode: read-only CI review (no tools executed)");
|
|
65
79
|
return parts.join(" | ");
|
|
66
80
|
}
|
|
67
81
|
function countSeverity(md) {
|
|
@@ -118,9 +132,7 @@ ${CI_COMMENT_MARKER}`;
|
|
|
118
132
|
function gqlIdToRest(graphqlId) {
|
|
119
133
|
if (/^\d+$/.test(graphqlId)) return graphqlId;
|
|
120
134
|
const query = `query($id:ID!){ node(id:$id){ ... on IssueComment { databaseId } } }`;
|
|
121
|
-
const out = execFileSync("gh", ["api", "graphql", "-f", `query=${query}`, "-F", `id=${graphqlId}`], {
|
|
122
|
-
encoding: "utf-8"
|
|
123
|
-
});
|
|
135
|
+
const out = execFileSync("gh", ["api", "graphql", "-f", `query=${query}`, "-F", `id=${graphqlId}`], { encoding: "utf-8" });
|
|
124
136
|
const parsed = JSON.parse(out);
|
|
125
137
|
const id = parsed.data?.node?.databaseId;
|
|
126
138
|
if (!id) throw new Error(`could not resolve comment id ${graphqlId}`);
|
|
@@ -128,25 +140,14 @@ function gqlIdToRest(graphqlId) {
|
|
|
128
140
|
}
|
|
129
141
|
async function runCi(opts) {
|
|
130
142
|
const maxDiff = opts.maxDiffChars ?? 3e4;
|
|
143
|
+
const thresholds = normalizeCiThresholds(opts.thresholds);
|
|
131
144
|
let diff;
|
|
132
145
|
try {
|
|
133
146
|
diff = fetchDiff(opts).trim();
|
|
134
147
|
} catch (err) {
|
|
135
|
-
return {
|
|
136
|
-
exitCode: 2,
|
|
137
|
-
markdown: `\u274C ${err.message}`,
|
|
138
|
-
posted: false,
|
|
139
|
-
severity: { critical: 0, high: 0, warning: 0, info: 0 }
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
if (!diff) {
|
|
143
|
-
return {
|
|
144
|
-
exitCode: 0,
|
|
145
|
-
markdown: "\u2705 No changes to review.",
|
|
146
|
-
posted: false,
|
|
147
|
-
severity: { critical: 0, high: 0, warning: 0, info: 0 }
|
|
148
|
-
};
|
|
148
|
+
return earlyResult(2, `\u274C ${err.message}`, thresholds);
|
|
149
149
|
}
|
|
150
|
+
if (!diff) return earlyResult(0, "\u2705 No changes to review.", thresholds);
|
|
150
151
|
const { diff: trimmedDiff, truncated } = truncateDiff(diff, maxDiff);
|
|
151
152
|
const gitCtx = buildGitContextStr(opts);
|
|
152
153
|
const config = new ConfigManager();
|
|
@@ -162,18 +163,14 @@ async function runCi(opts) {
|
|
|
162
163
|
);
|
|
163
164
|
const providerId = opts.provider ?? config.getDefaultProvider();
|
|
164
165
|
if (!registry.has(providerId)) {
|
|
165
|
-
return {
|
|
166
|
-
exitCode: 2,
|
|
167
|
-
markdown: `\u274C Provider '${providerId}' is not configured. Set the right AICLI_API_KEY_* env var or pass --provider.`,
|
|
168
|
-
posted: false,
|
|
169
|
-
severity: { critical: 0, high: 0, warning: 0, info: 0 }
|
|
170
|
-
};
|
|
166
|
+
return earlyResult(2, `\u274C Provider '${providerId}' is not configured. Set the right AICLI_API_KEY_* env var or pass --provider.`, thresholds);
|
|
171
167
|
}
|
|
172
168
|
const providerInfo = registry.get(providerId).info;
|
|
173
169
|
const modelId = opts.model ?? config.get("defaultModels")[providerId] ?? providerInfo.defaultModel;
|
|
170
|
+
const meta = { provider: providerId, model: modelId, mode: "read-only", thresholds };
|
|
174
171
|
const sections = [];
|
|
175
|
-
sections.push(
|
|
176
|
-
sections.push(`*Provider: \`${providerId}\` \xB7 Model: \`${modelId}\` \xB7 aicli v${VERSION}*`);
|
|
172
|
+
sections.push("## \u{1F916} aicli code review");
|
|
173
|
+
sections.push(`*Provider: \`${providerId}\` \xB7 Model: \`${modelId}\` \xB7 Mode: \`read-only CI review\` \xB7 aicli v${VERSION}*`);
|
|
177
174
|
sections.push("");
|
|
178
175
|
try {
|
|
179
176
|
if (!opts.skipCode) {
|
|
@@ -195,14 +192,15 @@ async function runCi(opts) {
|
|
|
195
192
|
exitCode: 2,
|
|
196
193
|
markdown: `\u274C Review call failed: ${err.message}`,
|
|
197
194
|
posted: false,
|
|
198
|
-
severity:
|
|
195
|
+
severity: zeroSeverity(),
|
|
196
|
+
gate: evaluateCiGate("", thresholds),
|
|
197
|
+
meta
|
|
199
198
|
};
|
|
200
199
|
}
|
|
201
|
-
if (truncated) {
|
|
202
|
-
sections.push(`> \u26A0 Diff was truncated to ${maxDiff} chars (original: ${diff.length}). Consider splitting large PRs.`);
|
|
203
|
-
}
|
|
200
|
+
if (truncated) sections.push(`> \u26A0 Diff was truncated to ${maxDiff} chars (original: ${diff.length}). Consider splitting large PRs.`);
|
|
204
201
|
const markdown = sections.join("\n");
|
|
205
202
|
const severity = countSeverity(markdown);
|
|
203
|
+
const gate = evaluateCiGate(markdown, thresholds);
|
|
206
204
|
let posted = false;
|
|
207
205
|
let updatedCommentId;
|
|
208
206
|
if (opts.post && opts.pr != null && !opts.dryRun) {
|
|
@@ -219,19 +217,15 @@ async function runCi(opts) {
|
|
|
219
217
|
|
|
220
218
|
\u274C Failed to post comment: ${err.message}`,
|
|
221
219
|
posted: false,
|
|
222
|
-
severity
|
|
220
|
+
severity,
|
|
221
|
+
gate,
|
|
222
|
+
meta
|
|
223
223
|
};
|
|
224
224
|
}
|
|
225
225
|
}
|
|
226
|
-
|
|
227
|
-
return {
|
|
228
|
-
exitCode: hasBlocker ? 1 : 0,
|
|
229
|
-
markdown,
|
|
230
|
-
posted,
|
|
231
|
-
updatedCommentId,
|
|
232
|
-
severity
|
|
233
|
-
};
|
|
226
|
+
return { exitCode: gate.failed ? 1 : 0, markdown, posted, updatedCommentId, severity, gate, meta };
|
|
234
227
|
}
|
|
228
|
+
|
|
235
229
|
export {
|
|
236
230
|
CI_COMMENT_MARKER,
|
|
237
231
|
countSeverity,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
truncateForPersist
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-TCOC4AUI.js";
|
|
5
5
|
import {
|
|
6
6
|
APP_NAME,
|
|
7
7
|
CONFIG_DIR_NAME,
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
MCP_PROTOCOL_VERSION,
|
|
14
14
|
MCP_TOOL_PREFIX,
|
|
15
15
|
VERSION
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-7YWS3JLU.js";
|
|
17
17
|
import {
|
|
18
18
|
atomicWriteFileSync
|
|
19
19
|
} from "./chunk-IW3Q7AE5.js";
|
|
@@ -5,10 +5,10 @@ import {
|
|
|
5
5
|
} from "./chunk-T2NL5ZIA.js";
|
|
6
6
|
import {
|
|
7
7
|
runTestsTool
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-VW7Y27WW.js";
|
|
9
9
|
import {
|
|
10
10
|
runTool
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-77HDCGTN.js";
|
|
12
12
|
import {
|
|
13
13
|
getDangerLevel,
|
|
14
14
|
isFileWriteTool,
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
SUBAGENT_ALLOWED_TOOLS,
|
|
27
27
|
SUBAGENT_DEFAULT_MAX_ROUNDS,
|
|
28
28
|
SUBAGENT_MAX_ROUNDS_LIMIT
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-7YWS3JLU.js";
|
|
30
30
|
import {
|
|
31
31
|
fileCheckpoints
|
|
32
32
|
} from "./chunk-4BKXL7SM.js";
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
VERSION
|
|
4
|
+
} from "./chunk-7YWS3JLU.js";
|
|
5
|
+
|
|
6
|
+
// src/cli/ci-format.ts
|
|
7
|
+
var DEFAULT_CI_THRESHOLDS = {
|
|
8
|
+
securityHigh: true,
|
|
9
|
+
testFailure: true,
|
|
10
|
+
lintFailure: true
|
|
11
|
+
};
|
|
12
|
+
function normalizeCiThresholds(input) {
|
|
13
|
+
return {
|
|
14
|
+
securityHigh: input?.securityHigh ?? DEFAULT_CI_THRESHOLDS.securityHigh,
|
|
15
|
+
testFailure: input?.testFailure ?? DEFAULT_CI_THRESHOLDS.testFailure,
|
|
16
|
+
lintFailure: input?.lintFailure ?? DEFAULT_CI_THRESHOLDS.lintFailure
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function extractSection(markdown, heading) {
|
|
20
|
+
const start = markdown.indexOf(heading);
|
|
21
|
+
if (start < 0) return "";
|
|
22
|
+
const rest = markdown.slice(start + heading.length);
|
|
23
|
+
const next = rest.search(/\n###\s+/);
|
|
24
|
+
return next >= 0 ? rest.slice(0, next) : rest;
|
|
25
|
+
}
|
|
26
|
+
function detectCiGateSignals(markdown) {
|
|
27
|
+
const security = extractSection(markdown, "### Security Review");
|
|
28
|
+
const securityHigh = (security.match(/(?:🔴\s*CRITICAL|🔴\s*Critical|🟠\s*HIGH|🟠\s*High)/g) ?? []).length;
|
|
29
|
+
const testFailure = (markdown.match(/(?:test(?:s|ing)?\s+(?:failed|failure|failures)|failing\s+test|npm\s+test\s+failed|pytest\s+failed|vitest\s+failed)/gi) ?? []).length;
|
|
30
|
+
const lintFailure = (markdown.match(/(?:lint\s+(?:failed|failure|failures)|eslint\s+failed|ruff\s+failed|biome\s+failed|typecheck\s+failed|tsc\s+failed)/gi) ?? []).length;
|
|
31
|
+
return { securityHigh, testFailure, lintFailure };
|
|
32
|
+
}
|
|
33
|
+
function evaluateCiGate(markdown, thresholds) {
|
|
34
|
+
const normalized = normalizeCiThresholds(thresholds);
|
|
35
|
+
const signals = detectCiGateSignals(markdown);
|
|
36
|
+
const reasons = [];
|
|
37
|
+
if (normalized.securityHigh && signals.securityHigh > 0) reasons.push(`security-high:${signals.securityHigh}`);
|
|
38
|
+
if (normalized.testFailure && signals.testFailure > 0) reasons.push(`test-failure:${signals.testFailure}`);
|
|
39
|
+
if (normalized.lintFailure && signals.lintFailure > 0) reasons.push(`lint-failure:${signals.lintFailure}`);
|
|
40
|
+
return { thresholds: normalized, signals, failed: reasons.length > 0, reasons };
|
|
41
|
+
}
|
|
42
|
+
function sarifLevel(line) {
|
|
43
|
+
if (/🔴|🟠|CRITICAL|HIGH/i.test(line)) return "error";
|
|
44
|
+
if (/🟡|WARNING|MEDIUM/i.test(line)) return "warning";
|
|
45
|
+
return "note";
|
|
46
|
+
}
|
|
47
|
+
function stripMarkdown(line) {
|
|
48
|
+
return line.replace(/^[\s>*-]+/, "").replace(/[`*_]/g, "").trim();
|
|
49
|
+
}
|
|
50
|
+
function formatCiSarif(result) {
|
|
51
|
+
const lines = result.markdown.split(/\r?\n/);
|
|
52
|
+
const findingLines = lines.map((line, idx) => ({ line, idx })).filter(({ line }) => /🔴|🟠|🟡|🔵|CRITICAL|HIGH|MEDIUM|LOW|WARNING|INFO/i.test(line));
|
|
53
|
+
const results = findingLines.map(({ line, idx }, n) => ({
|
|
54
|
+
ruleId: "aicli-review-finding",
|
|
55
|
+
level: sarifLevel(line),
|
|
56
|
+
message: { text: stripMarkdown(line) || line.trim() },
|
|
57
|
+
locations: [{
|
|
58
|
+
physicalLocation: {
|
|
59
|
+
artifactLocation: { uri: "aicli-review.md" },
|
|
60
|
+
region: { startLine: idx + 1 }
|
|
61
|
+
}
|
|
62
|
+
}],
|
|
63
|
+
partialFingerprints: { aicliLine: String(n + 1) }
|
|
64
|
+
}));
|
|
65
|
+
return JSON.stringify({
|
|
66
|
+
version: "2.1.0",
|
|
67
|
+
$schema: "https://json.schemastore.org/sarif-2.1.0.json",
|
|
68
|
+
runs: [{
|
|
69
|
+
tool: {
|
|
70
|
+
driver: {
|
|
71
|
+
name: "aicli",
|
|
72
|
+
informationUri: "https://github.com/jinzhengdong/ai-cli",
|
|
73
|
+
version: VERSION,
|
|
74
|
+
rules: [{
|
|
75
|
+
id: "aicli-review-finding",
|
|
76
|
+
name: "aicli review finding",
|
|
77
|
+
shortDescription: { text: "Finding emitted by aicli CI review" },
|
|
78
|
+
helpUri: "https://github.com/jinzhengdong/ai-cli"
|
|
79
|
+
}]
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
invocations: [{ executionSuccessful: !result.gate.failed }],
|
|
83
|
+
results,
|
|
84
|
+
properties: {
|
|
85
|
+
severity: result.severity,
|
|
86
|
+
gate: result.gate,
|
|
87
|
+
posted: result.posted
|
|
88
|
+
}
|
|
89
|
+
}]
|
|
90
|
+
}, null, 2);
|
|
91
|
+
}
|
|
92
|
+
function formatCiResult(result, format) {
|
|
93
|
+
if (format === "json") return JSON.stringify(result, null, 2);
|
|
94
|
+
if (format === "sarif") return formatCiSarif(result);
|
|
95
|
+
return result.markdown;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export {
|
|
99
|
+
DEFAULT_CI_THRESHOLDS,
|
|
100
|
+
normalizeCiThresholds,
|
|
101
|
+
detectCiGateSignals,
|
|
102
|
+
evaluateCiGate,
|
|
103
|
+
formatCiSarif,
|
|
104
|
+
formatCiResult
|
|
105
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
CI_COMMENT_MARKER,
|
|
4
|
+
countSeverity,
|
|
5
|
+
runCi
|
|
6
|
+
} from "./chunk-GY3C3C2Y.js";
|
|
7
|
+
import "./chunk-TJ6GGN4B.js";
|
|
8
|
+
import "./chunk-HLWUDRBO.js";
|
|
9
|
+
import "./chunk-QMXC327F.js";
|
|
10
|
+
import "./chunk-XPBEJB27.js";
|
|
11
|
+
import "./chunk-WAI3WPV2.js";
|
|
12
|
+
import "./chunk-TZQHYZKT.js";
|
|
13
|
+
import "./chunk-7YWS3JLU.js";
|
|
14
|
+
import "./chunk-IW3Q7AE5.js";
|
|
15
|
+
export {
|
|
16
|
+
CI_COMMENT_MARKER,
|
|
17
|
+
countSeverity,
|
|
18
|
+
runCi
|
|
19
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_CI_THRESHOLDS,
|
|
4
|
+
detectCiGateSignals,
|
|
5
|
+
evaluateCiGate,
|
|
6
|
+
formatCiResult,
|
|
7
|
+
formatCiSarif,
|
|
8
|
+
normalizeCiThresholds
|
|
9
|
+
} from "./chunk-TJ6GGN4B.js";
|
|
10
|
+
import "./chunk-7YWS3JLU.js";
|
|
11
|
+
export {
|
|
12
|
+
DEFAULT_CI_THRESHOLDS,
|
|
13
|
+
detectCiGateSignals,
|
|
14
|
+
evaluateCiGate,
|
|
15
|
+
formatCiResult,
|
|
16
|
+
formatCiSarif,
|
|
17
|
+
normalizeCiThresholds
|
|
18
|
+
};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
getConfigDirUsage,
|
|
4
4
|
listRecentCrashes
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-6HLKCSB3.js";
|
|
6
6
|
import {
|
|
7
7
|
ProviderRegistry
|
|
8
8
|
} from "./chunk-QMXC327F.js";
|
|
@@ -11,17 +11,17 @@ import {
|
|
|
11
11
|
getTopFailingTools,
|
|
12
12
|
getTopUsedTools,
|
|
13
13
|
resetStats
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-77HDCGTN.js";
|
|
15
15
|
import "./chunk-XPBEJB27.js";
|
|
16
16
|
import {
|
|
17
17
|
ConfigManager
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-WAI3WPV2.js";
|
|
19
19
|
import "./chunk-TZQHYZKT.js";
|
|
20
20
|
import {
|
|
21
21
|
DEV_STATE_FILE_NAME,
|
|
22
22
|
MEMORY_FILE_NAME,
|
|
23
23
|
VERSION
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-7YWS3JLU.js";
|
|
25
25
|
import "./chunk-IW3Q7AE5.js";
|
|
26
26
|
|
|
27
27
|
// src/diagnostics/doctor-cli.ts
|
package/dist/electron-server.js
CHANGED
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
VERSION,
|
|
38
38
|
buildUserIdentityPrompt,
|
|
39
39
|
runTestsTool
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-OLN7VUZA.js";
|
|
41
41
|
import {
|
|
42
42
|
hasSemanticIndex,
|
|
43
43
|
semanticSearch
|
|
@@ -16070,7 +16070,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
16070
16070
|
case "test": {
|
|
16071
16071
|
this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
|
|
16072
16072
|
try {
|
|
16073
|
-
const { executeTests } = await import("./run-tests-
|
|
16073
|
+
const { executeTests } = await import("./run-tests-4PKSIVK5.js");
|
|
16074
16074
|
const argStr = args.join(" ").trim();
|
|
16075
16075
|
let testArgs = {};
|
|
16076
16076
|
if (argStr) {
|
|
@@ -154,7 +154,7 @@ ${content}`);
|
|
|
154
154
|
}
|
|
155
155
|
}
|
|
156
156
|
async function runTaskMode(config, providers, configManager, topic) {
|
|
157
|
-
const { TaskOrchestrator } = await import("./task-orchestrator-
|
|
157
|
+
const { TaskOrchestrator } = await import("./task-orchestrator-TUMDTOAX.js");
|
|
158
158
|
const orchestrator = new TaskOrchestrator(config, providers, configManager);
|
|
159
159
|
let interrupted = false;
|
|
160
160
|
const onSigint = () => {
|
package/dist/index.js
CHANGED
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
scanProject,
|
|
16
16
|
sessionHasMeaningfulContent,
|
|
17
17
|
setupProxy
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-PNTLA3MN.js";
|
|
19
19
|
import {
|
|
20
20
|
buildReviewPrompt,
|
|
21
21
|
buildSecurityReviewPrompt
|
|
@@ -47,10 +47,10 @@ import {
|
|
|
47
47
|
trustHook,
|
|
48
48
|
undoStack,
|
|
49
49
|
untrustHook
|
|
50
|
-
} from "./chunk-
|
|
50
|
+
} from "./chunk-TCOC4AUI.js";
|
|
51
51
|
import "./chunk-T2NL5ZIA.js";
|
|
52
52
|
import "./chunk-BXP6YZ2P.js";
|
|
53
|
-
import "./chunk-
|
|
53
|
+
import "./chunk-VW7Y27WW.js";
|
|
54
54
|
import {
|
|
55
55
|
SessionManager,
|
|
56
56
|
getContentText
|
|
@@ -67,7 +67,7 @@ import {
|
|
|
67
67
|
getConfigDirUsage,
|
|
68
68
|
listRecentCrashes,
|
|
69
69
|
writeCrashLog
|
|
70
|
-
} from "./chunk-
|
|
70
|
+
} from "./chunk-6HLKCSB3.js";
|
|
71
71
|
import {
|
|
72
72
|
ProviderRegistry
|
|
73
73
|
} from "./chunk-QMXC327F.js";
|
|
@@ -76,7 +76,7 @@ import {
|
|
|
76
76
|
getTopFailingTools,
|
|
77
77
|
getTopUsedTools,
|
|
78
78
|
installFlushOnExit
|
|
79
|
-
} from "./chunk-
|
|
79
|
+
} from "./chunk-77HDCGTN.js";
|
|
80
80
|
import {
|
|
81
81
|
CONTENT_ONLY_STREAM_REMINDER,
|
|
82
82
|
TEE_FINAL_USER_NUDGE,
|
|
@@ -98,7 +98,7 @@ import {
|
|
|
98
98
|
} from "./chunk-XPBEJB27.js";
|
|
99
99
|
import {
|
|
100
100
|
ConfigManager
|
|
101
|
-
} from "./chunk-
|
|
101
|
+
} from "./chunk-WAI3WPV2.js";
|
|
102
102
|
import {
|
|
103
103
|
AuthError,
|
|
104
104
|
ProviderError,
|
|
@@ -124,7 +124,7 @@ import {
|
|
|
124
124
|
SKILLS_DIR_NAME,
|
|
125
125
|
VERSION,
|
|
126
126
|
buildUserIdentityPrompt
|
|
127
|
-
} from "./chunk-
|
|
127
|
+
} from "./chunk-7YWS3JLU.js";
|
|
128
128
|
import {
|
|
129
129
|
formatGitContextForPrompt,
|
|
130
130
|
getGitContext,
|
|
@@ -1661,7 +1661,7 @@ No tools match "${filter}".
|
|
|
1661
1661
|
const { join: join5 } = await import("path");
|
|
1662
1662
|
const { existsSync: existsSync5 } = await import("fs");
|
|
1663
1663
|
const { getGitRoot: getGitRoot2 } = await import("./git-context-EXOEHQSF.js");
|
|
1664
|
-
const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-
|
|
1664
|
+
const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-APSORFOH.js");
|
|
1665
1665
|
const { approveProject, hashMcpFile } = await import("./project-trust-NKYHL3VZ.js");
|
|
1666
1666
|
const cwd = process.cwd();
|
|
1667
1667
|
const projectRoot = getGitRoot2(cwd) ?? cwd;
|
|
@@ -2720,7 +2720,7 @@ ${hint}` : "")
|
|
|
2720
2720
|
usage: "/test [command|filter]",
|
|
2721
2721
|
async execute(args, ctx) {
|
|
2722
2722
|
try {
|
|
2723
|
-
const { executeTests } = await import("./run-tests-
|
|
2723
|
+
const { executeTests } = await import("./run-tests-ZRK4TQUN.js");
|
|
2724
2724
|
const argStr = args.join(" ").trim();
|
|
2725
2725
|
let testArgs = {};
|
|
2726
2726
|
if (argStr) {
|
|
@@ -7207,7 +7207,7 @@ program.command("web").description("Start Web UI server with browser-based chat
|
|
|
7207
7207
|
console.error("Error: Invalid port number. Must be between 1 and 65535.");
|
|
7208
7208
|
process.exit(1);
|
|
7209
7209
|
}
|
|
7210
|
-
const { startWebServer } = await import("./server-
|
|
7210
|
+
const { startWebServer } = await import("./server-FXUF5P64.js");
|
|
7211
7211
|
await startWebServer({ port, host: options.host });
|
|
7212
7212
|
});
|
|
7213
7213
|
program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | logout-all <name> | migrate <name>)").action(async (action, username) => {
|
|
@@ -7374,16 +7374,16 @@ program.command("sessions").description("List recent conversation sessions").opt
|
|
|
7374
7374
|
console.log(footer + "\n");
|
|
7375
7375
|
});
|
|
7376
7376
|
program.command("usage").description("Show token + cost usage grouped by provider/model (cross-session)").option("--days <n>", "Only the last N days (inclusive of today)").option("--month <ym>", "Only a specific month, format YYYY-MM (e.g. 2026-06)").option("--json", "Output as JSON (for scripting)").action(async (options) => {
|
|
7377
|
-
const { runUsageCli } = await import("./usage-
|
|
7377
|
+
const { runUsageCli } = await import("./usage-X7MJX4YD.js");
|
|
7378
7378
|
await runUsageCli(options);
|
|
7379
7379
|
});
|
|
7380
7380
|
program.command("doctor").description("Health check: API keys, config, MCP, recent crashes, tool usage, disk usage").option("--json", "Output as JSON (for scripting)").option("--reset-stats", "Reset accumulated tool usage statistics").action(async (options) => {
|
|
7381
|
-
const { runDoctorCli } = await import("./doctor-cli-
|
|
7381
|
+
const { runDoctorCli } = await import("./doctor-cli-GUIX4X5F.js");
|
|
7382
7382
|
await runDoctorCli({ json: !!options.json, resetStats: !!options.resetStats });
|
|
7383
7383
|
});
|
|
7384
7384
|
program.command("batch <action> [arg] [arg2]").description("Anthropic Message Batches: submit | list | status <id> | results <id> [out] | cancel <id>").option("--dry-run", "Parse and validate input without submitting (submit only)").action(async (action, arg, arg2, options) => {
|
|
7385
7385
|
try {
|
|
7386
|
-
const batch = await import("./batch-
|
|
7386
|
+
const batch = await import("./batch-C6HCXZIG.js");
|
|
7387
7387
|
switch (action) {
|
|
7388
7388
|
case "submit":
|
|
7389
7389
|
if (!arg) {
|
|
@@ -7426,7 +7426,7 @@ program.command("batch <action> [arg] [arg2]").description("Anthropic Message Ba
|
|
|
7426
7426
|
}
|
|
7427
7427
|
});
|
|
7428
7428
|
program.command("mcp-serve").description("Start an MCP server over STDIO, exposing aicli's built-in tools to Claude Desktop / Cursor / other MCP clients").option("--allow-destructive", "Allow bash / run_interactive / task_create (always destructive in MCP mode)").option("--allow-outside-cwd", "Allow tool path arguments to escape the sandbox root \u2014 disabled by default").option("--tools <list>", "Comma-separated whitelist of tools to expose (default: all eligible tools)").option("--cwd <path>", "Working directory AND sandbox root (default: current directory)").action(async (options) => {
|
|
7429
|
-
const { startMcpServer } = await import("./server-
|
|
7429
|
+
const { startMcpServer } = await import("./server-O3XHT56X.js");
|
|
7430
7430
|
await startMcpServer({
|
|
7431
7431
|
allowDestructive: !!options.allowDestructive,
|
|
7432
7432
|
allowOutsideCwd: !!options.allowOutsideCwd,
|
|
@@ -7434,8 +7434,39 @@ program.command("mcp-serve").description("Start an MCP server over STDIO, exposi
|
|
|
7434
7434
|
cwd: options.cwd
|
|
7435
7435
|
});
|
|
7436
7436
|
});
|
|
7437
|
-
program.command("
|
|
7438
|
-
|
|
7437
|
+
program.command("pr <action> [target]").description("PR review helpers: review | security-review | summarize. Defaults to git diff main...HEAD.").option("--base <ref>", "Base ref for git diff <ref>...HEAD (ignored when --pr set)").option("--head <ref>", "Head ref for local git diff (default: HEAD)").option("--post", "Post/update a PR comment; only when target is a GitHub PR number or URL").option("--no-update", "Always create a new PR comment instead of updating the previous aicli review").option("--detailed", "Use the detailed code-review prompt").option("--agents <list>", "Comma-separated review lenses: security,bugs,tests,maintainability").option("--max-diff <n>", "Max diff chars sent to the model (default 30000)", (v) => parseInt(v, 10)).option("--provider <id>", "Override provider (default: config.defaultProvider)").option("--model <id>", "Override model").option("--dry-run", "Print result to stdout instead of posting (overrides --post)").action(async (action, target, options) => {
|
|
7438
|
+
if (!["review", "security-review", "summarize"].includes(action)) {
|
|
7439
|
+
console.error(`Unknown pr action: ${action}. Use review | security-review | summarize.`);
|
|
7440
|
+
process.exit(1);
|
|
7441
|
+
}
|
|
7442
|
+
const { runPrCli } = await import("./pr-JOL3IAGV.js");
|
|
7443
|
+
const result = await runPrCli({
|
|
7444
|
+
action,
|
|
7445
|
+
target,
|
|
7446
|
+
base: options.base,
|
|
7447
|
+
head: options.head,
|
|
7448
|
+
post: !!options.post,
|
|
7449
|
+
update: options.update !== false,
|
|
7450
|
+
detailed: !!options.detailed,
|
|
7451
|
+
agents: options.agents,
|
|
7452
|
+
maxDiffChars: options.maxDiff,
|
|
7453
|
+
provider: options.provider,
|
|
7454
|
+
model: options.model,
|
|
7455
|
+
dryRun: !!options.dryRun
|
|
7456
|
+
});
|
|
7457
|
+
process.stdout.write(result.markdown + "\n");
|
|
7458
|
+
if (result.posted) process.stderr.write("\n\u2705 Posted PR review comment\n");
|
|
7459
|
+
process.exit(result.exitCode);
|
|
7460
|
+
});
|
|
7461
|
+
program.command("ci").description("Headless PR review (code + security) \u2014 reads git/gh diff, optionally posts to PR. Designed for GitHub Actions.").option("--pr <num>", "PR number or URL; diff fetched via gh pr diff <value>", (v) => /^#?\d+$/.test(v) ? parseInt(v.replace(/^#/, ""), 10) : v).option("--base <ref>", "Base ref for git diff <ref>...HEAD (ignored when --pr set)").option("--post", "Post review as a PR comment (requires gh CLI + GH_TOKEN, needs --pr)").option("--no-update", "Always create a new comment instead of updating the previous aicli review").option("--skip-code", "Skip the code review section").option("--skip-security", "Skip the security review section").option("--detailed", "Use the detailed code-review prompt").option("--max-diff <n>", "Max diff chars sent to the model (default 30000)", (v) => parseInt(v, 10)).option("--provider <id>", "Override provider (default: config.defaultProvider)").option("--model <id>", "Override model").option("--dry-run", "Print result to stdout instead of posting (overrides --post)").option("--json", "Output a structured JSON report").option("--markdown", "Output Markdown report (default)").option("--sarif", "Output SARIF 2.1.0 for code scanning artifacts").option("--fail-on-security-high", "Fail when the security review contains HIGH/CRITICAL findings (default)").option("--no-fail-on-security-high", "Do not fail on security HIGH/CRITICAL findings").option("--fail-on-test-failure", "Fail when the report mentions test failures (default)").option("--no-fail-on-test-failure", "Do not fail on test failure findings").option("--fail-on-lint-failure", "Fail when the report mentions lint/typecheck failures (default)").option("--no-fail-on-lint-failure", "Do not fail on lint/typecheck failure findings").action(async (options) => {
|
|
7462
|
+
const { runCi } = await import("./ci-6ZTFO3LX.js");
|
|
7463
|
+
const { formatCiResult } = await import("./ci-format-CLZ6QJRL.js");
|
|
7464
|
+
const formats = [options.json, options.markdown, options.sarif].filter(Boolean).length;
|
|
7465
|
+
if (formats > 1) {
|
|
7466
|
+
console.error("Choose only one output format: --json, --markdown, or --sarif.");
|
|
7467
|
+
process.exit(2);
|
|
7468
|
+
}
|
|
7469
|
+
const outputFormat = options.json ? "json" : options.sarif ? "sarif" : "markdown";
|
|
7439
7470
|
const result = await runCi({
|
|
7440
7471
|
pr: options.pr,
|
|
7441
7472
|
base: options.base,
|
|
@@ -7447,13 +7478,16 @@ program.command("ci").description("Headless PR review (code + security) \u2014 r
|
|
|
7447
7478
|
maxDiffChars: options.maxDiff,
|
|
7448
7479
|
provider: options.provider,
|
|
7449
7480
|
model: options.model,
|
|
7450
|
-
dryRun: !!options.dryRun
|
|
7481
|
+
dryRun: !!options.dryRun,
|
|
7482
|
+
thresholds: {
|
|
7483
|
+
securityHigh: options.failOnSecurityHigh !== false,
|
|
7484
|
+
testFailure: options.failOnTestFailure !== false,
|
|
7485
|
+
lintFailure: options.failOnLintFailure !== false
|
|
7486
|
+
}
|
|
7451
7487
|
});
|
|
7452
|
-
process.stdout.write(result
|
|
7488
|
+
process.stdout.write(formatCiResult({ version: VERSION, ...result }, outputFormat) + "\n");
|
|
7453
7489
|
if (result.posted) {
|
|
7454
|
-
process.stderr.write(
|
|
7455
|
-
\u2705 Posted review to PR #${options.pr}${result.updatedCommentId ? " (updated existing comment)" : ""}
|
|
7456
|
-
`);
|
|
7490
|
+
process.stderr.write("\n\u2705 Posted review to PR #" + options.pr + (result.updatedCommentId ? " (updated existing comment)" : "") + "\n");
|
|
7457
7491
|
}
|
|
7458
7492
|
process.exit(result.exitCode);
|
|
7459
7493
|
});
|
|
@@ -7484,6 +7518,8 @@ program.command("help").description("Show a comprehensive guide to all aicli fea
|
|
|
7484
7518
|
` ${G}aicli usage${R} Token + cost usage by provider/model (--days/--month/--json)`,
|
|
7485
7519
|
` ${G}aicli user <action>${R} User management (list/create/delete)`,
|
|
7486
7520
|
` ${G}aicli batch <action>${R} Anthropic Batches API (50% off, 24h): submit/list/status/results/cancel`,
|
|
7521
|
+
` ${G}aicli pr <action>${R} Local PR review/summarize commands`,
|
|
7522
|
+
` ${G}aicli ci [options]${R} Headless PR review for GitHub Actions`,
|
|
7487
7523
|
` ${G}aicli mcp-serve${R} MCP server over STDIO (expose tools to Claude Desktop / Cursor)`,
|
|
7488
7524
|
"",
|
|
7489
7525
|
`${B}${C} \u25A0 STARTUP OPTIONS${R}`,
|
|
@@ -7581,7 +7617,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
|
|
|
7581
7617
|
}),
|
|
7582
7618
|
config.get("customProviders")
|
|
7583
7619
|
);
|
|
7584
|
-
const { startHub } = await import("./hub-
|
|
7620
|
+
const { startHub } = await import("./hub-ZGHQWNWE.js");
|
|
7585
7621
|
await startHub(
|
|
7586
7622
|
{
|
|
7587
7623
|
topic: topic ?? "",
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
countSeverity,
|
|
4
|
+
runCi
|
|
5
|
+
} from "./chunk-GY3C3C2Y.js";
|
|
6
|
+
import "./chunk-TJ6GGN4B.js";
|
|
7
|
+
import {
|
|
8
|
+
buildReviewPrompt,
|
|
9
|
+
buildSecurityReviewPrompt,
|
|
10
|
+
truncateDiff
|
|
11
|
+
} from "./chunk-HLWUDRBO.js";
|
|
12
|
+
import {
|
|
13
|
+
ProviderRegistry
|
|
14
|
+
} from "./chunk-QMXC327F.js";
|
|
15
|
+
import "./chunk-XPBEJB27.js";
|
|
16
|
+
import {
|
|
17
|
+
ConfigManager
|
|
18
|
+
} from "./chunk-WAI3WPV2.js";
|
|
19
|
+
import "./chunk-TZQHYZKT.js";
|
|
20
|
+
import {
|
|
21
|
+
VERSION
|
|
22
|
+
} from "./chunk-7YWS3JLU.js";
|
|
23
|
+
import "./chunk-IW3Q7AE5.js";
|
|
24
|
+
|
|
25
|
+
// src/cli/pr.ts
|
|
26
|
+
import { execFileSync } from "child_process";
|
|
27
|
+
var ALLOWED_AGENTS = /* @__PURE__ */ new Set(["security", "bugs", "tests", "maintainability"]);
|
|
28
|
+
function parsePrTarget(input) {
|
|
29
|
+
if (!input) return {};
|
|
30
|
+
const s = input.trim();
|
|
31
|
+
if (/^https:\/\/github\.com\/[^/]+\/[^/]+\/pull\/\d+(?:[/?#].*)?$/.test(s)) return { pr: s };
|
|
32
|
+
if (/^#?\d+$/.test(s)) return { pr: parseInt(s.replace(/^#/, ""), 10) };
|
|
33
|
+
const range = s.match(/^([^\s.][^\s]*?)\.\.\.([^\s]+)$/);
|
|
34
|
+
if (range) return { base: range[1], head: range[2] };
|
|
35
|
+
return { base: s, head: "HEAD" };
|
|
36
|
+
}
|
|
37
|
+
function parseAgentList(input) {
|
|
38
|
+
if (!input) return [];
|
|
39
|
+
const agents = input.split(",").map((s) => s.trim()).filter(Boolean);
|
|
40
|
+
const invalid = agents.filter((a) => !ALLOWED_AGENTS.has(a));
|
|
41
|
+
if (invalid.length) throw new Error(`unknown PR review agent(s): ${invalid.join(", ")}. Allowed: ${Array.from(ALLOWED_AGENTS).join(", ")}`);
|
|
42
|
+
return [...new Set(agents)];
|
|
43
|
+
}
|
|
44
|
+
function validateGitRef(ref) {
|
|
45
|
+
if (!/^[A-Za-z0-9._/\-]+$/.test(ref)) throw new Error(`unsafe ref: ${ref}`);
|
|
46
|
+
}
|
|
47
|
+
function hasGitHubToken() {
|
|
48
|
+
return !!(process.env.GH_TOKEN || process.env.GITHUB_TOKEN);
|
|
49
|
+
}
|
|
50
|
+
function fetchPrMetadata(pr) {
|
|
51
|
+
if (!hasGitHubToken()) return null;
|
|
52
|
+
try {
|
|
53
|
+
const out = execFileSync("gh", ["pr", "view", String(pr), "--json", "number,url,title,author,state,baseRefName,headRefName"], {
|
|
54
|
+
encoding: "utf-8",
|
|
55
|
+
maxBuffer: 5 * 1024 * 1024,
|
|
56
|
+
timeout: 3e4
|
|
57
|
+
});
|
|
58
|
+
const parsed = JSON.parse(out);
|
|
59
|
+
return {
|
|
60
|
+
number: parsed.number,
|
|
61
|
+
url: parsed.url,
|
|
62
|
+
title: parsed.title,
|
|
63
|
+
state: parsed.state,
|
|
64
|
+
baseRefName: parsed.baseRefName,
|
|
65
|
+
headRefName: parsed.headRefName,
|
|
66
|
+
author: parsed.author?.login
|
|
67
|
+
};
|
|
68
|
+
} catch {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function fetchDiffSource(opts) {
|
|
73
|
+
const target = parsePrTarget(opts.target);
|
|
74
|
+
if (opts.diffOverride != null) {
|
|
75
|
+
const isPr = target.pr != null;
|
|
76
|
+
return {
|
|
77
|
+
kind: isPr ? "github-pr" : "git",
|
|
78
|
+
label: isPr ? `PR ${String(target.pr)}` : opts.base ? `${opts.base}...${opts.head || "HEAD"}` : "injected diff",
|
|
79
|
+
diff: opts.diffOverride,
|
|
80
|
+
metadata: opts.metadataOverride ?? null
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
const pr = target.pr;
|
|
84
|
+
const base = opts.base ?? target.base ?? "main";
|
|
85
|
+
const head = opts.head ?? target.head ?? "HEAD";
|
|
86
|
+
if (pr != null) {
|
|
87
|
+
try {
|
|
88
|
+
const diff = execFileSync("gh", ["pr", "diff", String(pr)], {
|
|
89
|
+
encoding: "utf-8",
|
|
90
|
+
maxBuffer: 50 * 1024 * 1024,
|
|
91
|
+
timeout: 6e4
|
|
92
|
+
});
|
|
93
|
+
return { kind: "github-pr", label: `PR ${String(pr)}`, diff, metadata: fetchPrMetadata(pr) };
|
|
94
|
+
} catch (err) {
|
|
95
|
+
const msg = err.stderr?.toString() ?? err.message;
|
|
96
|
+
throw new Error(`gh pr diff ${String(pr)} failed: ${msg.trim() || "unknown error"}. For local review without GitHub auth, use --base/--head instead.`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
validateGitRef(base);
|
|
100
|
+
validateGitRef(head);
|
|
101
|
+
try {
|
|
102
|
+
const diff = execFileSync("git", ["diff", `${base}...${head}`], {
|
|
103
|
+
encoding: "utf-8",
|
|
104
|
+
maxBuffer: 50 * 1024 * 1024,
|
|
105
|
+
timeout: 3e4
|
|
106
|
+
});
|
|
107
|
+
return { kind: "git", label: `${base}...${head}`, diff, metadata: null };
|
|
108
|
+
} catch (err) {
|
|
109
|
+
throw new Error(`git diff ${base}...${head} failed: ${err.message}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function metadataMarkdown(meta) {
|
|
113
|
+
if (!meta) return "";
|
|
114
|
+
const parts = [
|
|
115
|
+
meta.number != null ? `#${meta.number}` : void 0,
|
|
116
|
+
meta.title,
|
|
117
|
+
meta.state,
|
|
118
|
+
meta.author ? `by @${meta.author}` : void 0,
|
|
119
|
+
meta.baseRefName && meta.headRefName ? `${meta.baseRefName} \u2190 ${meta.headRefName}` : void 0,
|
|
120
|
+
meta.url
|
|
121
|
+
].filter(Boolean);
|
|
122
|
+
return parts.length ? `
|
|
123
|
+
PR metadata: ${parts.join(" \xB7 ")}` : "";
|
|
124
|
+
}
|
|
125
|
+
function buildGitContext(src) {
|
|
126
|
+
const parts = [`Source: ${src.label}`, `Reviewer: aicli v${VERSION}`];
|
|
127
|
+
if (src.metadata?.number != null) parts.push(`PR: #${src.metadata.number}`);
|
|
128
|
+
if (src.metadata?.title) parts.push(`Title: ${src.metadata.title}`);
|
|
129
|
+
if (src.metadata?.baseRefName && src.metadata?.headRefName) parts.push(`Base/Head: ${src.metadata.baseRefName}...${src.metadata.headRefName}`);
|
|
130
|
+
return parts.join(" | ");
|
|
131
|
+
}
|
|
132
|
+
function buildSummaryPrompt(diff, gitContext) {
|
|
133
|
+
return `# Pull Request Summary Request
|
|
134
|
+
|
|
135
|
+
Summarize this pull request for maintainers. Focus on what changed, why it matters, risk areas, tests implied by the diff, and release-note worthy behavior.
|
|
136
|
+
|
|
137
|
+
## Git Context
|
|
138
|
+
${gitContext}
|
|
139
|
+
|
|
140
|
+
## Code Changes (diff)
|
|
141
|
+
\`\`\`diff
|
|
142
|
+
${diff}
|
|
143
|
+
\`\`\`
|
|
144
|
+
|
|
145
|
+
## Output Format
|
|
146
|
+
1. **Summary**: 2-4 bullets
|
|
147
|
+
2. **Key Files**: important files and why they changed
|
|
148
|
+
3. **Risk / Review Focus**: areas reviewers should inspect
|
|
149
|
+
4. **Testing Notes**: tests present or missing
|
|
150
|
+
5. **Suggested PR Description**: concise markdown description`;
|
|
151
|
+
}
|
|
152
|
+
function buildAgentPrompt(agent, diff, gitContext) {
|
|
153
|
+
const focus = {
|
|
154
|
+
security: "security vulnerabilities, secret handling, authz/authn, injection, unsafe network/file behavior",
|
|
155
|
+
bugs: "logic bugs, edge cases, race conditions, incorrect assumptions, regressions",
|
|
156
|
+
tests: "missing tests, weak assertions, CI breakage risk, testability gaps",
|
|
157
|
+
maintainability: "complexity, naming, duplication, API shape, future maintenance risk"
|
|
158
|
+
};
|
|
159
|
+
return `# PR Review Agent: ${agent}
|
|
160
|
+
|
|
161
|
+
Review the diff only from this lens: ${focus[agent]}. Be concise and cite file paths/lines when possible.
|
|
162
|
+
|
|
163
|
+
## Git Context
|
|
164
|
+
${gitContext}
|
|
165
|
+
|
|
166
|
+
## Diff
|
|
167
|
+
\`\`\`diff
|
|
168
|
+
${diff}
|
|
169
|
+
\`\`\`
|
|
170
|
+
|
|
171
|
+
## Output Format
|
|
172
|
+
- Findings first, ordered by severity
|
|
173
|
+
- If no material findings, say so clearly
|
|
174
|
+
- Keep recommendations actionable`;
|
|
175
|
+
}
|
|
176
|
+
async function getProvider(opts) {
|
|
177
|
+
const config = new ConfigManager();
|
|
178
|
+
const registry = new ProviderRegistry();
|
|
179
|
+
await registry.initialize(
|
|
180
|
+
(id) => config.getApiKey(id),
|
|
181
|
+
(id) => ({ baseUrl: config.get("customBaseUrls")[id], timeout: config.get("timeouts")[id], proxy: config.get("proxy") }),
|
|
182
|
+
config.get("customProviders")
|
|
183
|
+
);
|
|
184
|
+
const providerId = opts.provider ?? config.getDefaultProvider();
|
|
185
|
+
if (!registry.has(providerId)) throw new Error(`Provider '${providerId}' is not configured. Set the right AICLI_API_KEY_* env var or pass --provider.`);
|
|
186
|
+
const info = registry.get(providerId).info;
|
|
187
|
+
return { registry, providerId, modelId: opts.model ?? config.get("defaultModels")[providerId] ?? info.defaultModel };
|
|
188
|
+
}
|
|
189
|
+
async function runPrompt(registry, providerId, modelId, prompt) {
|
|
190
|
+
const provider = registry.get(providerId);
|
|
191
|
+
const resp = await provider.chat({
|
|
192
|
+
messages: [{ role: "user", content: prompt, timestamp: /* @__PURE__ */ new Date() }],
|
|
193
|
+
model: modelId,
|
|
194
|
+
stream: false,
|
|
195
|
+
temperature: 0.3,
|
|
196
|
+
maxTokens: 8192
|
|
197
|
+
});
|
|
198
|
+
return resp.content?.trim() ?? "";
|
|
199
|
+
}
|
|
200
|
+
async function runPrCli(opts) {
|
|
201
|
+
let source;
|
|
202
|
+
try {
|
|
203
|
+
source = fetchDiffSource(opts);
|
|
204
|
+
} catch (err) {
|
|
205
|
+
return { exitCode: 2, markdown: `\u274C ${err.message}`, posted: false, severity: { critical: 0, high: 0, warning: 0, info: 0 } };
|
|
206
|
+
}
|
|
207
|
+
const diff = source.diff.trim();
|
|
208
|
+
if (!diff) return { exitCode: 0, markdown: `\u2705 No changes to review (${source.label}).`, posted: false, severity: { critical: 0, high: 0, warning: 0, info: 0 } };
|
|
209
|
+
const parsedTarget = parsePrTarget(opts.target);
|
|
210
|
+
const requestedAgents = parseAgentList(opts.agents);
|
|
211
|
+
if (opts.post && source.kind !== "github-pr") {
|
|
212
|
+
return { exitCode: 2, markdown: "\u274C --post requires a GitHub PR number or URL target.", posted: false, severity: { critical: 0, high: 0, warning: 0, info: 0 } };
|
|
213
|
+
}
|
|
214
|
+
if (opts.post && opts.action === "summarize") {
|
|
215
|
+
return { exitCode: 2, markdown: "\u274C --post is currently supported for pr review and pr security-review only.", posted: false, severity: { critical: 0, high: 0, warning: 0, info: 0 } };
|
|
216
|
+
}
|
|
217
|
+
if (opts.post && requestedAgents.length > 0) {
|
|
218
|
+
return { exitCode: 2, markdown: "\u274C --post is currently not supported together with --agents; run without --agents or post the Markdown manually.", posted: false, severity: { critical: 0, high: 0, warning: 0, info: 0 } };
|
|
219
|
+
}
|
|
220
|
+
if ((opts.action === "review" || opts.action === "security-review") && requestedAgents.length === 0) {
|
|
221
|
+
const ci = await runCi({
|
|
222
|
+
pr: source.kind === "github-pr" ? parsedTarget.pr : void 0,
|
|
223
|
+
diffOverride: diff,
|
|
224
|
+
base: source.kind === "git" ? source.label.split("...")[0] : void 0,
|
|
225
|
+
post: !!opts.post,
|
|
226
|
+
update: opts.update !== false,
|
|
227
|
+
skipCode: opts.action === "security-review",
|
|
228
|
+
skipSecurity: opts.action === "review",
|
|
229
|
+
detailed: !!opts.detailed,
|
|
230
|
+
maxDiffChars: opts.maxDiffChars,
|
|
231
|
+
provider: opts.provider,
|
|
232
|
+
model: opts.model,
|
|
233
|
+
dryRun: !!opts.dryRun
|
|
234
|
+
});
|
|
235
|
+
return { exitCode: ci.exitCode, markdown: ci.markdown + metadataMarkdown(source.metadata), posted: ci.posted, severity: ci.severity };
|
|
236
|
+
}
|
|
237
|
+
const maxDiff = opts.maxDiffChars ?? 3e4;
|
|
238
|
+
const { diff: trimmedDiff, truncated } = truncateDiff(diff, maxDiff);
|
|
239
|
+
const gitContext = buildGitContext(source);
|
|
240
|
+
let providerBundle;
|
|
241
|
+
try {
|
|
242
|
+
providerBundle = await getProvider(opts);
|
|
243
|
+
} catch (err) {
|
|
244
|
+
return { exitCode: 2, markdown: `\u274C ${err.message}`, posted: false, severity: { critical: 0, high: 0, warning: 0, info: 0 } };
|
|
245
|
+
}
|
|
246
|
+
const sections = [`## \u{1F916} aicli pr ${opts.action}`, `*Provider: \`${providerBundle.providerId}\` \xB7 Model: \`${providerBundle.modelId}\` \xB7 Source: \`${source.label}\` \xB7 aicli v${VERSION}*`, ""];
|
|
247
|
+
if (source.metadata) sections.push(`> ${metadataMarkdown(source.metadata).trim()}`, "");
|
|
248
|
+
try {
|
|
249
|
+
const prompt = opts.action === "security-review" ? buildSecurityReviewPrompt(trimmedDiff, gitContext) : opts.action === "summarize" ? buildSummaryPrompt(trimmedDiff, gitContext) : buildReviewPrompt(trimmedDiff, gitContext, !!opts.detailed);
|
|
250
|
+
sections.push(await runPrompt(providerBundle.registry, providerBundle.providerId, providerBundle.modelId, prompt), "");
|
|
251
|
+
if (requestedAgents.length) {
|
|
252
|
+
const outputs = await Promise.all(requestedAgents.map(async (agent) => ({ agent, output: await runPrompt(providerBundle.registry, providerBundle.providerId, providerBundle.modelId, buildAgentPrompt(agent, trimmedDiff, gitContext)) })));
|
|
253
|
+
sections.push("### Agent Lenses", "");
|
|
254
|
+
for (const item of outputs) sections.push(`#### ${item.agent}`, item.output, "");
|
|
255
|
+
}
|
|
256
|
+
} catch (err) {
|
|
257
|
+
return { exitCode: 2, markdown: `\u274C PR review call failed: ${err.message}`, posted: false, severity: { critical: 0, high: 0, warning: 0, info: 0 } };
|
|
258
|
+
}
|
|
259
|
+
if (truncated) sections.push(`> \u26A0 Diff was truncated to ${maxDiff} chars (original: ${diff.length}). Consider splitting large PRs.`);
|
|
260
|
+
const markdown = sections.join("\n");
|
|
261
|
+
return { exitCode: 0, markdown, posted: false, severity: countSeverity(markdown) };
|
|
262
|
+
}
|
|
263
|
+
export {
|
|
264
|
+
parseAgentList,
|
|
265
|
+
parsePrTarget,
|
|
266
|
+
runPrCli
|
|
267
|
+
};
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
scanDirTree,
|
|
25
25
|
scanProject,
|
|
26
26
|
setupProxy
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-PNTLA3MN.js";
|
|
28
28
|
import {
|
|
29
29
|
buildReviewPrompt,
|
|
30
30
|
buildSecurityReviewPrompt
|
|
@@ -61,10 +61,10 @@ import {
|
|
|
61
61
|
spawnAgentContext,
|
|
62
62
|
truncateOutput,
|
|
63
63
|
undoStack
|
|
64
|
-
} from "./chunk-
|
|
64
|
+
} from "./chunk-TCOC4AUI.js";
|
|
65
65
|
import "./chunk-T2NL5ZIA.js";
|
|
66
66
|
import "./chunk-BXP6YZ2P.js";
|
|
67
|
-
import "./chunk-
|
|
67
|
+
import "./chunk-VW7Y27WW.js";
|
|
68
68
|
import {
|
|
69
69
|
SessionManager,
|
|
70
70
|
getContentText
|
|
@@ -78,7 +78,7 @@ import {
|
|
|
78
78
|
} from "./chunk-QMXC327F.js";
|
|
79
79
|
import {
|
|
80
80
|
runTool
|
|
81
|
-
} from "./chunk-
|
|
81
|
+
} from "./chunk-77HDCGTN.js";
|
|
82
82
|
import {
|
|
83
83
|
CONTENT_ONLY_STREAM_REMINDER,
|
|
84
84
|
TEE_FINAL_USER_NUDGE,
|
|
@@ -97,7 +97,7 @@ import {
|
|
|
97
97
|
} from "./chunk-XPBEJB27.js";
|
|
98
98
|
import {
|
|
99
99
|
ConfigManager
|
|
100
|
-
} from "./chunk-
|
|
100
|
+
} from "./chunk-WAI3WPV2.js";
|
|
101
101
|
import "./chunk-TZQHYZKT.js";
|
|
102
102
|
import {
|
|
103
103
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
@@ -116,7 +116,7 @@ import {
|
|
|
116
116
|
SKILLS_DIR_NAME,
|
|
117
117
|
VERSION,
|
|
118
118
|
buildUserIdentityPrompt
|
|
119
|
-
} from "./chunk-
|
|
119
|
+
} from "./chunk-7YWS3JLU.js";
|
|
120
120
|
import {
|
|
121
121
|
formatGitContextForPrompt,
|
|
122
122
|
getGitContext,
|
|
@@ -2755,7 +2755,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
2755
2755
|
case "test": {
|
|
2756
2756
|
this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
|
|
2757
2757
|
try {
|
|
2758
|
-
const { executeTests } = await import("./run-tests-
|
|
2758
|
+
const { executeTests } = await import("./run-tests-ZRK4TQUN.js");
|
|
2759
2759
|
const argStr = args.join(" ").trim();
|
|
2760
2760
|
let testArgs = {};
|
|
2761
2761
|
if (argStr) {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
ToolRegistry
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-TCOC4AUI.js";
|
|
5
5
|
import "./chunk-T2NL5ZIA.js";
|
|
6
6
|
import "./chunk-BXP6YZ2P.js";
|
|
7
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-VW7Y27WW.js";
|
|
8
8
|
import {
|
|
9
9
|
runTool
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-77HDCGTN.js";
|
|
11
11
|
import {
|
|
12
12
|
getDangerLevel,
|
|
13
13
|
schemaToJsonSchema
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
import "./chunk-TZQHYZKT.js";
|
|
16
16
|
import {
|
|
17
17
|
VERSION
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-7YWS3JLU.js";
|
|
19
19
|
import "./chunk-4BKXL7SM.js";
|
|
20
20
|
import "./chunk-TB4W4Y4T.js";
|
|
21
21
|
import "./chunk-KHYD3WXE.js";
|
|
@@ -3,13 +3,13 @@ import {
|
|
|
3
3
|
ToolRegistry,
|
|
4
4
|
googleSearchContext,
|
|
5
5
|
truncateOutput
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-TCOC4AUI.js";
|
|
7
7
|
import "./chunk-T2NL5ZIA.js";
|
|
8
8
|
import "./chunk-BXP6YZ2P.js";
|
|
9
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-VW7Y27WW.js";
|
|
10
10
|
import {
|
|
11
11
|
runTool
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-77HDCGTN.js";
|
|
13
13
|
import {
|
|
14
14
|
getDangerLevel,
|
|
15
15
|
runLeanAgentLoop
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
import "./chunk-TZQHYZKT.js";
|
|
18
18
|
import {
|
|
19
19
|
SUBAGENT_ALLOWED_TOOLS
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-7YWS3JLU.js";
|
|
21
21
|
import "./chunk-4BKXL7SM.js";
|
|
22
22
|
import "./chunk-TB4W4Y4T.js";
|
|
23
23
|
import "./chunk-KHYD3WXE.js";
|
|
@@ -8,9 +8,9 @@ import {
|
|
|
8
8
|
} from "./chunk-E44DTERW.js";
|
|
9
9
|
import {
|
|
10
10
|
ConfigManager
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-WAI3WPV2.js";
|
|
12
12
|
import "./chunk-TZQHYZKT.js";
|
|
13
|
-
import "./chunk-
|
|
13
|
+
import "./chunk-7YWS3JLU.js";
|
|
14
14
|
import "./chunk-IW3Q7AE5.js";
|
|
15
15
|
|
|
16
16
|
// src/cli/usage.ts
|