@agentskit/code-review 0.1.0 → 0.4.2
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/.doc-bridge/capabilities.json +34 -0
- package/.doc-bridge/index.json +94 -0
- package/.pre-commit-hooks.yaml +9 -0
- package/AGENTS.md +20 -0
- package/CHANGELOG.md +91 -0
- package/CONTRIBUTING.md +45 -0
- package/README.md +238 -33
- package/ROADMAP.md +20 -0
- package/SECURITY.md +16 -0
- package/action.yml +120 -0
- package/dist/agents/code-review/agent.js +454 -76
- package/dist/agents/code-review/agent.js.map +1 -1
- package/dist/agents/code-review/lenses.js +17 -1
- package/dist/agents/code-review/lenses.js.map +1 -1
- package/dist/agents/code-review/reporters.js +83 -7
- package/dist/agents/code-review/reporters.js.map +1 -1
- package/dist/agents/code-review/sources.js +324 -73
- package/dist/agents/code-review/sources.js.map +1 -1
- package/dist/src/acp-cli-adapter.js +127 -0
- package/dist/src/acp-cli-adapter.js.map +1 -0
- package/dist/src/batch-coverage.js +111 -0
- package/dist/src/batch-coverage.js.map +1 -0
- package/dist/src/claude-code-adapter.js +44 -60
- package/dist/src/claude-code-adapter.js.map +1 -1
- package/dist/src/cli.js +347 -50
- package/dist/src/cli.js.map +1 -1
- package/dist/src/codex-adapter.js +114 -69
- package/dist/src/codex-adapter.js.map +1 -1
- package/dist/src/github-review-state.js +134 -0
- package/dist/src/github-review-state.js.map +1 -0
- package/dist/src/grok-cli-adapter.js +27 -0
- package/dist/src/grok-cli-adapter.js.map +1 -0
- package/dist/src/headless-cli-adapter.js +78 -0
- package/dist/src/headless-cli-adapter.js.map +1 -0
- package/dist/src/local-cli-process.js +328 -0
- package/dist/src/local-cli-process.js.map +1 -0
- package/dist/src/local-cli-timeout.js +14 -0
- package/dist/src/local-cli-timeout.js.map +1 -0
- package/dist/src/ollama-adapter.js +155 -0
- package/dist/src/ollama-adapter.js.map +1 -0
- package/dist/src/opencode-cli-adapter.js +57 -0
- package/dist/src/opencode-cli-adapter.js.map +1 -0
- package/dist/src/provider-circuit-breaker.js +52 -0
- package/dist/src/provider-circuit-breaker.js.map +1 -0
- package/dist/src/provider-registry.js +168 -0
- package/dist/src/provider-registry.js.map +1 -0
- package/dist/src/review-config.js +145 -0
- package/dist/src/review-config.js.map +1 -0
- package/doc-bridge.config.json +116 -0
- package/docs/OPERATIONS.md +358 -0
- package/docs/assets/agentskit-mark.svg +10 -0
- package/docs/assets/code-review-terminal.png +0 -0
- package/docs/continuous-improvement.md +35 -0
- package/docs/for-agents/code-review-cli.md +70 -0
- package/docs/for-agents/index.md +5 -0
- package/docs/plans/ecosystem-doc-quality-code-review.md +61 -0
- package/docs/provider-compatibility.json +35 -0
- package/ecosystem-claims.json +187 -0
- package/ecosystem.json +277 -0
- package/examples/pull-request.yml +29 -0
- package/llms-full.txt +1055 -0
- package/llms.txt +24 -0
- package/package.json +53 -7
- package/scripts/generate-llms-full.mjs +68 -0
- package/scripts/run-cycle-benchmark.mjs +64 -0
- package/test/cli-smoke.test.mjs +522 -0
- package/test/continuous-improvement.test.mjs +20 -0
- package/test/documentation.test.mjs +238 -0
- package/test/release-workflow.test.mjs +31 -0
- package/dist/agents/code-review/agent.d.ts +0 -115
- package/dist/agents/code-review/lenses.d.ts +0 -10
- package/dist/agents/code-review/reporters.d.ts +0 -31
- package/dist/agents/code-review/sources.d.ts +0 -27
- package/dist/src/claude-code-adapter.d.ts +0 -4
- package/dist/src/cli.d.ts +0 -2
- package/dist/src/codex-adapter.d.ts +0 -4
|
@@ -5,90 +5,135 @@
|
|
|
5
5
|
* prompt goes in as an arg, the final message is written to a temp file via
|
|
6
6
|
* `-o`, sandbox is read-only (the agent only emits JSON, runs no tools).
|
|
7
7
|
*/
|
|
8
|
-
import {
|
|
9
|
-
import { mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
8
|
+
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
10
9
|
import { tmpdir } from "node:os";
|
|
11
10
|
import { join } from "node:path";
|
|
12
|
-
|
|
11
|
+
import { runLocalCli } from "./local-cli-process.js";
|
|
12
|
+
import { DEFAULT_CODEX_CLI_TIMEOUT_MS, localCliTimeoutMs } from "./local-cli-timeout.js";
|
|
13
|
+
/**
|
|
14
|
+
* Pull a JSON object out of a reply while rejecting prose/braces that are not
|
|
15
|
+
* part of the response. Codex is also constrained with --output-schema below,
|
|
16
|
+
* but this fallback tolerates wrapper text in the captured output.
|
|
17
|
+
*/
|
|
13
18
|
function extractJson(text) {
|
|
19
|
+
const candidates = [text.trim()];
|
|
20
|
+
const fenced = [...text.matchAll(/```[^\n]*\n([\s\S]*?)\n?```/g)];
|
|
21
|
+
if (fenced.length > 1)
|
|
22
|
+
throw new Error(`codex output contained multiple fenced blocks (${fenced.length})`);
|
|
23
|
+
if (fenced.length === 1)
|
|
24
|
+
candidates.push(fenced[0][1].trim());
|
|
14
25
|
const start = text.indexOf("{");
|
|
15
26
|
const end = text.lastIndexOf("}");
|
|
16
|
-
if (start
|
|
17
|
-
|
|
27
|
+
if (start >= 0 && end >= start)
|
|
28
|
+
candidates.push(text.slice(start, end + 1));
|
|
29
|
+
for (const candidate of candidates) {
|
|
30
|
+
try {
|
|
31
|
+
const value = JSON.parse(candidate);
|
|
32
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
33
|
+
return JSON.stringify(value);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// Try the next bounded candidate and report only a safe summary below.
|
|
38
|
+
}
|
|
18
39
|
}
|
|
19
|
-
|
|
40
|
+
throw new Error(`codex output was not a JSON object (${text.length} characters)`);
|
|
41
|
+
}
|
|
42
|
+
function isOutputSchemaRejection(error) {
|
|
43
|
+
const e = error;
|
|
44
|
+
const detail = [e.message, e.stderr].filter(Boolean).join(" ");
|
|
45
|
+
return /invalid[_ -]?json[_ -]?schema/i.test(detail)
|
|
46
|
+
|| /(?:unknown|unrecognized|unexpected|invalid|unsupported)\s+(?:option|flag|argument)[^\n]*--output-schema/i.test(detail)
|
|
47
|
+
|| /--output-schema[^\n]*(?:unsupported|not supported|rejected|invalid)/i.test(detail);
|
|
20
48
|
}
|
|
21
49
|
/** Run `codex exec` and return its final message (captured via -o). */
|
|
22
|
-
function runCodex(prompt, model) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
message = readFileSync(outFile, "utf8");
|
|
50
|
+
async function runCodex(prompt, schema, model, signal, mode, worker) {
|
|
51
|
+
const dir = mkdtempSync(join(tmpdir(), "cr-codex-"));
|
|
52
|
+
const outFile = join(dir, "out.txt");
|
|
53
|
+
try {
|
|
54
|
+
const run = async (useSchema) => {
|
|
55
|
+
const args = [
|
|
56
|
+
"exec",
|
|
57
|
+
"--skip-git-repo-check",
|
|
58
|
+
"-s",
|
|
59
|
+
"read-only",
|
|
60
|
+
"-o",
|
|
61
|
+
outFile,
|
|
62
|
+
];
|
|
63
|
+
if (useSchema && schema !== undefined) {
|
|
64
|
+
const schemaFile = join(dir, "output-schema.json");
|
|
65
|
+
const serializedSchema = JSON.stringify(schema);
|
|
66
|
+
if (serializedSchema === undefined)
|
|
67
|
+
throw new Error("codex output schema could not be serialized");
|
|
68
|
+
writeFileSync(schemaFile, serializedSchema, { encoding: "utf8", mode: 0o600 });
|
|
69
|
+
args.push("--output-schema", schemaFile);
|
|
43
70
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
71
|
+
if (model)
|
|
72
|
+
args.push("-m", model);
|
|
73
|
+
args.push(prompt);
|
|
74
|
+
await runLocalCli("codex", args, {
|
|
75
|
+
signal,
|
|
76
|
+
mode,
|
|
77
|
+
timeoutMs: worker?.timeoutMs ?? localCliTimeoutMs(DEFAULT_CODEX_CLI_TIMEOUT_MS),
|
|
78
|
+
maxOutputBytes: worker?.maxOutputBytes,
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
try {
|
|
82
|
+
await run(schema !== undefined);
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
// Codex versions can reject an otherwise valid JSON Schema at request time
|
|
86
|
+
// (for example, when they require every object property to be required).
|
|
87
|
+
// Retry only that compatibility failure; auth, timeout, and process failures
|
|
88
|
+
// must not double the provider calls.
|
|
89
|
+
if (schema === undefined || !isOutputSchemaRejection(error))
|
|
90
|
+
throw error;
|
|
91
|
+
await run(false);
|
|
92
|
+
}
|
|
93
|
+
return readFileSync(outFile, "utf8");
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
rmSync(dir, { recursive: true, force: true });
|
|
97
|
+
}
|
|
57
98
|
}
|
|
58
99
|
export function codexCli(opts = {}) {
|
|
59
100
|
return {
|
|
60
101
|
capabilities: { streaming: false, tools: true, structuredOutput: true },
|
|
61
|
-
createSource: (request) =>
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
102
|
+
createSource: (request) => {
|
|
103
|
+
const controller = new AbortController();
|
|
104
|
+
return {
|
|
105
|
+
stream: async function* () {
|
|
106
|
+
try {
|
|
107
|
+
const system = request.messages.find((m) => m.role === "system")?.content ?? "";
|
|
108
|
+
const convo = request.messages
|
|
109
|
+
.filter((m) => m.role !== "system")
|
|
110
|
+
.map((m) => `${m.role.toUpperCase()}: ${m.content}`)
|
|
111
|
+
.join("\n\n");
|
|
112
|
+
const tools = request.context?.tools ?? [];
|
|
113
|
+
let prompt = `${system}\n\n${convo}`;
|
|
114
|
+
if (tools.length === 1) {
|
|
115
|
+
const t = tools[0];
|
|
116
|
+
prompt += `\n\nReturn ONLY a JSON object that is the argument to the "${t.name}" tool, matching this JSON Schema exactly. No prose, no code fences:\n${JSON.stringify(t.schema)}`;
|
|
117
|
+
}
|
|
118
|
+
const schema = tools.length === 1 ? tools[0].schema : undefined;
|
|
119
|
+
const out = (await runCodex(prompt, schema, opts.model, controller.signal, opts.mode, opts.worker)).trim();
|
|
120
|
+
if (tools.length === 1) {
|
|
121
|
+
yield { type: "tool_call", toolCall: { id: `tc-${Date.now()}`, name: tools[0].name, args: extractJson(out) } };
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
yield { type: "text", content: out };
|
|
125
|
+
}
|
|
126
|
+
yield { type: "done" };
|
|
78
127
|
}
|
|
79
|
-
|
|
80
|
-
|
|
128
|
+
catch (err) {
|
|
129
|
+
const e = err;
|
|
130
|
+
const detail = e.code === "ETIMEDOUT" ? e.message : [e.stderr, e.stdout].filter(Boolean).join(" ").trim();
|
|
131
|
+
yield { type: "error", content: `codex exec failed${detail ? `: ${detail.slice(0, 400)}` : ` (no output): ${(e.message ?? "").split("\n")[0]}`}` };
|
|
81
132
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const detail = [e.stderr, e.stdout].filter(Boolean).join(" ").trim();
|
|
87
|
-
yield { type: "error", content: `codex exec failed${detail ? `: ${detail.slice(0, 400)}` : ` (no output): ${(e.message ?? "").split("\n")[0]}`}` };
|
|
88
|
-
}
|
|
89
|
-
},
|
|
90
|
-
abort: () => { },
|
|
91
|
-
}),
|
|
133
|
+
},
|
|
134
|
+
abort: () => controller.abort(),
|
|
135
|
+
};
|
|
136
|
+
},
|
|
92
137
|
};
|
|
93
138
|
}
|
|
94
139
|
//# sourceMappingURL=codex-adapter.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codex-adapter.js","sourceRoot":"","sources":["../../src/codex-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"codex-adapter.js","sourceRoot":"","sources":["../../src/codex-adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
const API = 'https://api.github.com';
|
|
3
|
+
export const GITHUB_REQUEST_TIMEOUT_MS = 30_000;
|
|
4
|
+
export const MAX_GITHUB_RESPONSE_BYTES = 25 * 1024 * 1024;
|
|
5
|
+
const MARKER_PREFIX = '<!-- agentskit-code-review:v1';
|
|
6
|
+
const COMMENT_PAGE_SIZE = 100;
|
|
7
|
+
const MAX_COMMENT_PAGES = 10;
|
|
8
|
+
export class GithubResponseLimitError extends Error {
|
|
9
|
+
maxBytes;
|
|
10
|
+
constructor(maxBytes) {
|
|
11
|
+
super(`GitHub response exceeded ${maxBytes} byte limit`);
|
|
12
|
+
this.maxBytes = maxBytes;
|
|
13
|
+
this.name = 'GithubResponseLimitError';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export function reviewFingerprint(value) {
|
|
17
|
+
return createHash('sha256').update(JSON.stringify(value)).digest('hex');
|
|
18
|
+
}
|
|
19
|
+
export function reviewMarker(sha, fingerprint) {
|
|
20
|
+
return `${MARKER_PREFIX} sha=${sha} fingerprint=${fingerprint} -->`;
|
|
21
|
+
}
|
|
22
|
+
function retryableGithubGet(error) {
|
|
23
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
24
|
+
return /GitHub GET .* → (429|5\d\d)/.test(message)
|
|
25
|
+
|| /(?:aborted|timed out|fetch failed|network)/i.test(message);
|
|
26
|
+
}
|
|
27
|
+
export async function readGithubResponseText(response, maxBytes = MAX_GITHUB_RESPONSE_BYTES) {
|
|
28
|
+
const reader = response.body?.getReader();
|
|
29
|
+
if (!reader) {
|
|
30
|
+
const text = await response.text();
|
|
31
|
+
if (Buffer.byteLength(text, 'utf8') > maxBytes)
|
|
32
|
+
throw new GithubResponseLimitError(maxBytes);
|
|
33
|
+
return text;
|
|
34
|
+
}
|
|
35
|
+
const chunks = [];
|
|
36
|
+
let bytes = 0;
|
|
37
|
+
try {
|
|
38
|
+
for (;;) {
|
|
39
|
+
const { done, value } = await reader.read();
|
|
40
|
+
if (done)
|
|
41
|
+
break;
|
|
42
|
+
bytes += value.byteLength;
|
|
43
|
+
if (bytes > maxBytes) {
|
|
44
|
+
await reader.cancel();
|
|
45
|
+
throw new GithubResponseLimitError(maxBytes);
|
|
46
|
+
}
|
|
47
|
+
chunks.push(value);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
reader.releaseLock();
|
|
52
|
+
}
|
|
53
|
+
return Buffer.concat(chunks.map((chunk) => Buffer.from(chunk))).toString('utf8');
|
|
54
|
+
}
|
|
55
|
+
async function responseDetail(response) {
|
|
56
|
+
try {
|
|
57
|
+
return (await readGithubResponseText(response, 4096)).trim().slice(0, 300);
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
return error instanceof GithubResponseLimitError ? error.message : '';
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
export async function githubFetch(token, url, accept = 'application/vnd.github+json') {
|
|
64
|
+
let lastError;
|
|
65
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
66
|
+
try {
|
|
67
|
+
const response = await fetch(url, {
|
|
68
|
+
headers: { authorization: `Bearer ${token}`, accept, 'user-agent': 'agentskit-code-review' },
|
|
69
|
+
signal: AbortSignal.timeout(GITHUB_REQUEST_TIMEOUT_MS),
|
|
70
|
+
});
|
|
71
|
+
if (!response.ok) {
|
|
72
|
+
const detail = await responseDetail(response);
|
|
73
|
+
throw new Error(`GitHub GET ${url} → ${response.status}${detail ? `: ${detail}` : ''}`);
|
|
74
|
+
}
|
|
75
|
+
return response;
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
lastError = error;
|
|
79
|
+
if (attempt === 1 || !retryableGithubGet(error))
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
throw lastError instanceof Error ? lastError : new Error('GitHub GET failed');
|
|
84
|
+
}
|
|
85
|
+
export async function githubGet(token, path) {
|
|
86
|
+
const response = await githubFetch(token, `${API}${path}`);
|
|
87
|
+
return JSON.parse(await readGithubResponseText(response));
|
|
88
|
+
}
|
|
89
|
+
export async function githubIssueComments(token, owner, repo, number) {
|
|
90
|
+
const comments = [];
|
|
91
|
+
for (let page = 1; page <= MAX_COMMENT_PAGES; page++) {
|
|
92
|
+
const batch = await githubGet(token, `/repos/${owner}/${repo}/issues/${number}/comments?per_page=${COMMENT_PAGE_SIZE}&page=${page}&sort=created&direction=desc`);
|
|
93
|
+
comments.push(...batch);
|
|
94
|
+
if (batch.length < COMMENT_PAGE_SIZE)
|
|
95
|
+
return { comments, truncated: false };
|
|
96
|
+
}
|
|
97
|
+
return { comments, truncated: true };
|
|
98
|
+
}
|
|
99
|
+
export function markerIn(body, marker) {
|
|
100
|
+
return body?.includes(marker) ?? false;
|
|
101
|
+
}
|
|
102
|
+
function previousMarker(body, fingerprint) {
|
|
103
|
+
const match = body?.match(new RegExp(`${MARKER_PREFIX} sha=([^ ]+) fingerprint=${fingerprint} -->`));
|
|
104
|
+
return match?.[1];
|
|
105
|
+
}
|
|
106
|
+
export async function getGithubReviewState(input) {
|
|
107
|
+
const pr = await githubGet(input.token, `/repos/${input.owner}/${input.repo}/pulls/${input.number}`);
|
|
108
|
+
const marker = reviewMarker(pr.head.sha, input.fingerprint);
|
|
109
|
+
const history = await githubIssueComments(input.token, input.owner, input.repo, input.number);
|
|
110
|
+
if (history.truncated)
|
|
111
|
+
throw new Error(`GitHub review comment history exceeded ${MAX_COMMENT_PAGES * COMMENT_PAGE_SIZE} comments; refusing to post without idempotency proof`);
|
|
112
|
+
const comments = history.comments;
|
|
113
|
+
const previousSha = comments.map((comment) => previousMarker(comment.body, input.fingerprint)).find(Boolean);
|
|
114
|
+
let scope = 'full';
|
|
115
|
+
if (previousSha && previousSha !== pr.head.sha) {
|
|
116
|
+
const comparison = await githubGet(input.token, `/repos/${input.owner}/${input.repo}/compare/${previousSha}...${pr.head.sha}`);
|
|
117
|
+
if (comparison.status === 'ahead')
|
|
118
|
+
scope = 'incremental';
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
owner: input.owner,
|
|
122
|
+
repo: input.repo,
|
|
123
|
+
number: input.number,
|
|
124
|
+
sha: pr.head.sha,
|
|
125
|
+
baseSha: pr.base.sha,
|
|
126
|
+
fork: pr.head.repo?.full_name !== undefined && pr.head.repo.full_name !== pr.base.repo?.full_name,
|
|
127
|
+
fingerprint: input.fingerprint,
|
|
128
|
+
marker,
|
|
129
|
+
alreadyReviewed: comments.some((comment) => markerIn(comment.body, marker)),
|
|
130
|
+
scope,
|
|
131
|
+
...(scope === 'incremental' && previousSha ? { baselineSha: previousSha } : {}),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=github-review-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-review-state.js","sourceRoot":"","sources":["../../src/github-review-state.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { createAcpCliAdapter } from './acp-cli-adapter.js';
|
|
2
|
+
import { createHeadlessCliAdapter } from './headless-cli-adapter.js';
|
|
3
|
+
export function grokCli(options = {}) {
|
|
4
|
+
return createAcpCliAdapter({
|
|
5
|
+
label: 'grok agent stdio',
|
|
6
|
+
outputLabel: 'Grok ACP',
|
|
7
|
+
command: options.command ?? 'grok',
|
|
8
|
+
args: ['agent', 'stdio', '--no-auto-update'],
|
|
9
|
+
authenticate: 'xai',
|
|
10
|
+
credentialEnv: 'XAI_API_KEY',
|
|
11
|
+
...options,
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
export function grokHeadless(options = {}) {
|
|
15
|
+
return createHeadlessCliAdapter({
|
|
16
|
+
label: 'grok headless',
|
|
17
|
+
outputLabel: 'Grok headless',
|
|
18
|
+
command: options.command ?? 'grok',
|
|
19
|
+
model: options.model,
|
|
20
|
+
args: (prompt, model) => ['--no-auto-update', '-p', prompt, '--output-format', 'json', ...(model ? ['--model', model] : [])],
|
|
21
|
+
apiKey: options.apiKey,
|
|
22
|
+
credentialEnv: 'XAI_API_KEY',
|
|
23
|
+
mode: options.mode,
|
|
24
|
+
worker: options.worker,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=grok-cli-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grok-cli-adapter.js","sourceRoot":"","sources":["../../src/grok-cli-adapter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAsB,MAAM,sBAAsB,CAAA;AAC9E,OAAO,EAAE,wBAAwB,EAA2B,MAAM,2BAA2B,CAAA;AAM7F,MAAM,UAAU,OAAO,CAAC,UAA0B,EAAE;IAClD,OAAO,mBAAmB,CAAC;QACzB,KAAK,EAAE,kBAAkB;QACzB,WAAW,EAAE,UAAU;QACvB,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,MAAM;QAClC,IAAI,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,kBAAkB,CAAC;QAC5C,YAAY,EAAE,KAAK;QACnB,aAAa,EAAE,aAAa;QAC5B,GAAG,OAAO;KACX,CAAC,CAAA;AACJ,CAAC;AAUD,MAAM,UAAU,YAAY,CAAC,UAA+B,EAAE;IAC5D,OAAO,wBAAwB,CAAC;QAC9B,KAAK,EAAE,eAAe;QACtB,WAAW,EAAE,eAAe;QAC5B,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,MAAM;QAClC,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,IAAI,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,kBAAkB,EAAE,IAAI,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC5H,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,aAAa,EAAE,aAAa;QAC5B,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,MAAM,EAAE,OAAO,CAAC,MAAM;KACvB,CAAC,CAAA;AACJ,CAAC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { buildReviewPrompt, parseReviewEnvelope, InvalidReviewOutputError } from './acp-cli-adapter.js';
|
|
2
|
+
import { runLocalCli } from './local-cli-process.js';
|
|
3
|
+
function defaultParseOutput(stdout, label) {
|
|
4
|
+
return parseReviewEnvelope(stdout, label);
|
|
5
|
+
}
|
|
6
|
+
export function createHeadlessCliAdapter(options) {
|
|
7
|
+
return {
|
|
8
|
+
capabilities: { streaming: false, tools: true, structuredOutput: true },
|
|
9
|
+
createSource: (request) => {
|
|
10
|
+
const controller = new AbortController();
|
|
11
|
+
return {
|
|
12
|
+
stream: async function* () {
|
|
13
|
+
try {
|
|
14
|
+
const tools = request.context?.tools ?? [];
|
|
15
|
+
if (tools.length !== 1)
|
|
16
|
+
throw new Error(`${options.label} review requires exactly one tool`);
|
|
17
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
18
|
+
try {
|
|
19
|
+
const { stdout } = await runLocalCli(options.command, [...options.args(buildReviewPrompt(request), options.model)], {
|
|
20
|
+
mode: options.mode,
|
|
21
|
+
signal: controller.signal,
|
|
22
|
+
providerCredential: options.apiKey ? { name: options.credentialEnv ?? 'API_KEY', value: options.apiKey } : undefined,
|
|
23
|
+
...options.worker,
|
|
24
|
+
});
|
|
25
|
+
const args = (options.parseOutput ?? defaultParseOutput)(stdout, options.outputLabel ?? options.label);
|
|
26
|
+
yield { type: 'tool_call', toolCall: { id: `tc-${Date.now()}`, name: tools[0].name, args } };
|
|
27
|
+
yield { type: 'done' };
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
if (!(error instanceof InvalidReviewOutputError) || attempt === 1)
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
const e = error;
|
|
38
|
+
const detail = [e.message, e.stderr, e.stdout].filter(Boolean).join(' ').slice(0, 400);
|
|
39
|
+
yield { type: 'error', content: `${options.label} failed${detail ? `: ${detail}` : ''}` };
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
abort: () => controller.abort(),
|
|
43
|
+
};
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export function createAutoCliAdapter(options) {
|
|
48
|
+
return {
|
|
49
|
+
capabilities: options.primary.capabilities,
|
|
50
|
+
createSource: (request) => {
|
|
51
|
+
const primary = options.primary.createSource(request);
|
|
52
|
+
const fallback = options.fallback.createSource(request);
|
|
53
|
+
return {
|
|
54
|
+
stream: async function* () {
|
|
55
|
+
let primaryError;
|
|
56
|
+
for await (const chunk of primary.stream()) {
|
|
57
|
+
if (chunk.type === 'error') {
|
|
58
|
+
primaryError = chunk.content;
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
yield chunk;
|
|
62
|
+
}
|
|
63
|
+
if (!primaryError)
|
|
64
|
+
return;
|
|
65
|
+
options.onFallback?.(`${options.provider}: ${primaryError}`);
|
|
66
|
+
for await (const chunk of fallback.stream()) {
|
|
67
|
+
if (chunk.type === 'error')
|
|
68
|
+
yield { ...chunk, content: `${chunk.content}; ACP fallback reason: ${primaryError}` };
|
|
69
|
+
else
|
|
70
|
+
yield chunk;
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
abort: () => { primary.abort(); fallback.abort(); },
|
|
74
|
+
};
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=headless-cli-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"headless-cli-adapter.js","sourceRoot":"","sources":["../../src/headless-cli-adapter.ts"],"names":[],"mappings":"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"}
|