Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

- Preserve single-PR push preferences on resume, and keep fixes local when an
older run has no saved preference unless pushing is explicitly enabled.
- Scope agent temporary files and default Go caches to owned invocation directories,
reclaim them after completion, and add `jury cache clean` for stopped runs (#122).
- Retain Windows agent caches when descendant termination cannot be confirmed,
Expand Down
8 changes: 5 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,13 @@ These tests use saved review data; they do not call live model providers.

The Node/OS matrix runs the same `npm test` and `npm run test:package` commands
through `node scripts/ci-check.mjs test` and `node scripts/ci-check.mjs test:package`.
Each phase has a three-minute watchdog, within the existing fifteen-minute job
limit. A timeout remains a failed check and terminates that phase's process group.
The source suite has a three-minute watchdog; package validation has five minutes
for packing, installation, and subprocess-heavy installed CLI regressions on slower
runners. Both remain within the fifteen-minute job limit. A timeout remains a
failed check and terminates that phase's process group.
Tests and their parallelism are unchanged.

Package validation prints named subprocess start/end markers. The independent
Package validation streams test results and prints named subprocess start/end markers. The independent
watchdog streams Node process lifecycle/resource records and snapshots of its own
process group and descendants, without command arguments or environment values.
CI uploads these records as `test-diagnostics-node<version>-<os>` artifacts for
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,9 @@ Start a new Codex chat, select **CodeJury**, and ask:

> Review https://github.com/OWNER/REPO/pull/123

Use your PR link. Reviews run locally and can make fixes; changes stay local unless
you ask to push. See [setup and usage](docs/codex-plugin.md) for prerequisites,
agent selection, and saved reviews.
Use your PR link. Reviews run locally and push fixes to the PR by default. Ask to
keep fixes local to disable pushing. See [setup and usage](docs/codex-plugin.md)
for prerequisites, agent selection, and saved reviews.

## Claude Code plugin

Expand Down
9 changes: 6 additions & 3 deletions bin/jury.js
Original file line number Diff line number Diff line change
Expand Up @@ -538,8 +538,10 @@ async function cmdAgent(argv) {
const language = normalizeLanguage(requestedLanguage ?? languageIn(priorEvents));
if (priorTargets?.targets && urls.length < 2) throw new Error("resuming a related-PR task requires all original PR URLs");
if (urls.length > 1 && values.resume && !priorTargets?.targets) throw new Error("no related-PR task found to resume");
if (priorTargets?.targets && !argv.some(a => a === "--push" || a.startsWith("--push="))) {
values.push = priorTargets.pushEnabled ?? false;
// Older single-PR runs did not save this preference. Keep their fixes local
// unless the caller explicitly enables pushing on resume.
if (values.resume && !argv.some(a => a === "--push" || a.startsWith("--push="))) {
values.push = priorTargets?.pushEnabled ?? false;
}
const group = urls.length > 1 ? await prepareGroup(urls, {
root: requestedWorktree, dir: resolveFrom, allowPush: values.push, trunk: values.trunk, previous: priorTargets,
Expand Down Expand Up @@ -604,12 +606,13 @@ async function cmdAgent(argv) {
title: values.title ?? pr.title ?? git.subject,
branch: resolved?.branch ?? git.branch,
trunk,
pushEnabled: values.push,
state: "review",
stateNote: "",
...(language ? { language } : {}),
};
if (group) Object.assign(target, {
targets: group.targets, workspace: group.worktree, pushEnabled: values.push, repo: "related-prs",
targets: group.targets, workspace: group.worktree, repo: "related-prs",
reviewedCommit: null, reviewedCommits: null,
id: groupId(urls), url: "", branch: "", summary: values.summary ?? "",
});
Expand Down
16 changes: 9 additions & 7 deletions docs/codex-plugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ In a new Codex chat, select **CodeJury** and ask:

> Set up CodeJury and check which agents I can use.

Setup checks prerequisites without starting a review. The plugin installs its
CodeJury CLI automatically on first use; agent installation and login are separate.
Setup checks prerequisites without starting a review. The plugin uses your global
CodeJury CLI if available on PATH, or installs its fallback release automatically.
Agent installation and login are separate.

## Review a PR

Expand All @@ -26,9 +27,10 @@ Provide a PR link when starting a review. For example:
Replace the example URL with your PR and choose agents you have installed. The
plugin shares a live browser console link so you can follow the review.

The judge can edit files and create commits. Fixes remain in a persistent local
checkout unless you ask to push them. Local files or folders alone are not
supported review targets for this plugin.
The judge can edit files, create commits, and push fixes to the PR's source branch.
Pushing is enabled by default (`--push=true`). Ask to keep fixes local, or specify
`--push=false`, to retain them in a persistent local checkout instead. Local files
or folders alone are not supported review targets for this plugin.

To continue or inspect a saved review, ask:

Expand All @@ -39,8 +41,8 @@ To continue or inspect a saved review, ask:
## Development and packaging

The plugin source is in [`plugins/codejury`](../plugins/codejury). Its launcher
automatically downloads the pinned CLI release from npm, independently of any
globally installed `jury`. The plugin and CLI have separate versions.
checks for an installed `jury` or `codejury` on PATH before downloading its fallback
CLI release from npm. The plugin and CLI have separate versions.

Build the submission archive from the repository root:

Expand Down
6 changes: 4 additions & 2 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,10 @@ jury review <same-pr-url-1> <same-pr-url-2> --resume <slug> --push=false
```

Repeat the same `--dir` root if you used one. Keep related-PR URLs in the same order.
Related-PR resume retains its previous push setting unless explicitly overridden;
`--push=true` publishes retained fixes with fast-forward pushes. If a remote branch changes
All reviews save their push setting and retain it on resume unless explicitly overridden.
Older runs without a saved setting resume with pushing disabled; pass `--push=true`
to enable it explicitly. For related PRs, `--push=true` publishes retained fixes
with fast-forward pushes. If a remote branch changes
independently, start a new task rather than resuming obsolete heads.

`--rounds 3` caps review rounds; it is not a monetary budget. Each round may involve multiple
Expand Down
8 changes: 4 additions & 4 deletions plugins/codejury/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
"name": "codejury",
"version": "0.1.2",
"version": "0.1.3",
"description": "Review pull requests supplied by URL with CodeJury's independent AI reviewers and judge, resume PR reviews, and inspect saved findings.",
"author": {
"name": "ZHOU ZHENGXI",
Expand All @@ -22,7 +22,7 @@
"interface": {
"displayName": "CodeJury",
"shortDescription": "AI pull request review",
"longDescription": "Review pull requests supplied by URL with independent coding agents and a judge that investigates findings, applies fixes, and asks reviewers to check again. Set up the CLI, run or resume a PR review, and inspect saved findings and conversations. A new review requires a PR link; arbitrary local files and folders are not supported plugin review targets. PR reviews use a local checkout. Desktop only: requires Node.js 20.10+, npm, Git, and separately installed and authenticated coding-agent CLIs. GitHub PRs also require GitHub CLI. This plugin uses CodeJury CLI 0.9.0, downloaded from npm on first use. Reviews can edit files and create commits; pushes are disabled unless requested. Code and prompts are processed by your selected agents and their providers, subject to their terms and usage charges. CodeJury keeps review records locally and operates no hosted review service. The included console screenshot shows a real prior review; results vary by repository and agent.",
"longDescription": "Review pull requests supplied by URL with independent coding agents and a judge that investigates findings, applies fixes, and asks reviewers to check again. Set up the CLI, run or resume a PR review, and inspect saved findings and conversations. A new review requires a PR link; arbitrary local files and folders are not supported plugin review targets. PR reviews use a local checkout. Desktop only: requires Node.js 20.10+, npm, Git, and separately installed and authenticated coding-agent CLIs. GitHub PRs also require GitHub CLI. This plugin uses your installed CodeJury CLI when available on PATH, or downloads CodeJury CLI 0.9.0 from npm as a fallback. Reviews can edit files, create commits, and push fixes to the PR by default; ask to keep fixes local or use --push=false to disable pushing. Code and prompts are processed by your selected agents and their providers, subject to their terms and usage charges. CodeJury keeps review records locally and operates no hosted review service. The included console screenshot shows a real prior review; results vary by repository and agent.",
"developerName": "ZHOU ZHENGXI",
"category": "Developer Tools",
"capabilities": [
Expand All @@ -36,7 +36,7 @@
"termsOfServiceURL": "https://codejury.ai-infra.org/terms/",
"defaultPrompt": [
"Set up CodeJury and check which coding agents I can use.",
"Review this PR link with CodeJury and keep fixes local: <PR URL>",
"Review this PR link with CodeJury: <PR URL>",
"Summarize the findings from my latest saved CodeJury review."
],
"brandColor": "#315BDC",
Expand All @@ -47,7 +47,7 @@
]
},
"publication": {
"release_notes": "Use stable CodeJury CLI 0.9.0 with token usage reporting, separate sessions for same-agent reviewer and judge roles, and fixes for review readiness, saved runs, replies, and console startup. Reuse a complete CLI cache installed by a concurrent launcher. New reviews require PR links."
"release_notes": "Push PR fixes by default, while preserving explicit local-only requests. Reuse an installed CodeJury CLI on PATH before downloading the fallback release."
}
}
}
Expand Down
95 changes: 61 additions & 34 deletions plugins/codejury/scripts/jury.mjs
Original file line number Diff line number Diff line change
@@ -1,47 +1,74 @@
// Invoke the exact released CLI, independent of local/global jury executables.
// Reuse an installed CodeJury CLI, or download the fallback release.
import { spawnSync } from 'node:child_process';
import { existsSync, mkdirSync, mkdtempSync, readFileSync, renameSync, rmSync } from 'node:fs';
import { accessSync, constants, existsSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, renameSync, rmSync } from 'node:fs';
import { homedir } from 'node:os';
import path from 'node:path';
import { pathToFileURL } from 'node:url';

const version = '0.9.0';
const [major, minor] = process.versions.node.split('.').map(Number);
if (major < 20 || (major === 20 && minor < 10)) throw new Error('CodeJury requires Node.js 20.10+.');
const cache = path.resolve(process.env.CODEJURY_PLUGIN_CACHE || path.join(homedir(), '.cache', 'codejury', 'plugin-cli'));
const install = path.join(cache, version);
const packageRoot = path.join(install, 'node_modules', '@agentsdance', 'codejury');
const entry = path.join(packageRoot, 'bin', 'jury.js');
function installed() {
try {
const metadata = JSON.parse(readFileSync(path.join(packageRoot, 'package.json'), 'utf8'));
return metadata.name === '@agentsdance/codejury' && metadata.version === version && existsSync(entry);
} catch { return false; }
}
let ready = installed();
if (!ready && existsSync(install)) {
// A concurrent launcher may have published a complete cache after our miss.
ready = installed();
if (!ready) throw new Error(`Incomplete CLI cache at ${install}; move it aside and retry.`);

function installedCli() {
for (const directory of (process.env.PATH || '').split(path.delimiter).filter(Boolean)) {
for (const command of ['jury', 'codejury']) {
const executable = path.resolve(directory, command + (process.platform === 'win32' ? '.cmd' : ''));
try {
accessSync(executable, process.platform === 'win32' ? constants.F_OK : constants.X_OK);
const target = realpathSync(executable);
// npm uses symlinks on POSIX and .cmd shims beside node_modules on Windows.
const packageRoot = process.platform === 'win32'
? path.join(directory, 'node_modules', '@agentsdance', 'codejury')
: path.dirname(path.dirname(target));
const metadata = JSON.parse(readFileSync(path.join(packageRoot, 'package.json'), 'utf8'));
const entry = realpathSync(path.join(packageRoot, 'bin', 'jury.js'));
if (metadata.name === '@agentsdance/codejury' &&
(process.platform === 'win32' || entry === target)) return entry;
} catch { /* No usable CodeJury package behind this executable. */ }
}
}
return null;
}
if (!ready) {
mkdirSync(cache, { recursive: true });
const staging = mkdtempSync(path.join(cache, '.install-'));
try {
console.error(`Downloading CodeJury ${version} from npm into ${cache}`);
// cwd is the staging directory; keep cache paths out of the Windows shell command.
const result = spawnSync(process.platform === 'win32' ? 'npm.cmd' : 'npm', [
'install', '--prefix', '.', '--no-save', '--package-lock=false',
'--ignore-scripts', '--no-audit', '--no-fund', '--registry=https://registry.npmjs.org',
`@agentsdance/codejury@${version}`,
], { cwd: staging, stdio: 'inherit', shell: process.platform === 'win32' });
if (result.error) throw result.error;
if (result.status !== 0) throw new Error(`npm installation failed (${result.status ?? result.signal}).`);
try { renameSync(staging, install); }
catch (error) { if (!installed()) throw error; } // Another concurrent invocation may have installed it.
} finally { rmSync(staging, { recursive: true, force: true }); }

function cachedCli() {
const cache = path.resolve(process.env.CODEJURY_PLUGIN_CACHE || path.join(homedir(), '.cache', 'codejury', 'plugin-cli'));
const install = path.join(cache, version);
const packageRoot = path.join(install, 'node_modules', '@agentsdance', 'codejury');
const entry = path.join(packageRoot, 'bin', 'jury.js');
function installed() {
try {
const metadata = JSON.parse(readFileSync(path.join(packageRoot, 'package.json'), 'utf8'));
return metadata.name === '@agentsdance/codejury' && metadata.version === version && existsSync(entry);
} catch { return false; }
}
let ready = installed();
if (!ready && existsSync(install)) {
// A concurrent launcher may have published a complete cache after our miss.
ready = installed();
if (!ready) throw new Error(`Incomplete CLI cache at ${install}; move it aside and retry.`);
}
if (!ready) {
mkdirSync(cache, { recursive: true });
const staging = mkdtempSync(path.join(cache, '.install-'));
try {
console.error(`Downloading CodeJury ${version} from npm into ${cache}`);
// cwd is the staging directory; keep cache paths out of the Windows shell command.
const result = spawnSync(process.platform === 'win32' ? 'npm.cmd' : 'npm', [
'install', '--prefix', '.', '--no-save', '--package-lock=false',
'--ignore-scripts', '--no-audit', '--no-fund', '--registry=https://registry.npmjs.org',
`@agentsdance/codejury@${version}`,
], { cwd: staging, stdio: 'inherit', shell: process.platform === 'win32' });
if (result.error) throw result.error;
if (result.status !== 0) throw new Error(`npm installation failed (${result.status ?? result.signal}).`);
try { renameSync(staging, install); }
catch (error) { if (!installed()) throw error; } // Another concurrent invocation may have installed it.
} finally { rmSync(staging, { recursive: true, force: true }); }
}
if (!installed()) throw new Error(`Expected CodeJury ${version} at ${install}.`);
return entry;
}
if (!installed()) throw new Error(`Expected CodeJury ${version} at ${install}.`);

const entry = installedCli() || cachedCli();
// Import in this process so cancellation reaches the CLI and its agent cleanup.
process.argv = [process.execPath, entry, ...process.argv.slice(2)];
await import(pathToFileURL(entry).href);
8 changes: 4 additions & 4 deletions plugins/codejury/skills/codejury-results/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ description: List, inspect, or summarize saved CodeJury reviews and findings, or
# Inspect saved CodeJury reviews

Resolve `<plugin-root>` as two directories above this skill folder. Use
`node "<plugin-root>/scripts/jury.mjs"` followed by command arguments. This invokes
the exact published stable release, downloading it into
`~/.cache/codejury/plugin-cli/` on first use. Respect an explicitly requested
CLI version.
`node "<plugin-root>/scripts/jury.mjs"` followed by command arguments. This uses an
installed CodeJury CLI on PATH first, or downloads the fallback release into
`~/.cache/codejury/plugin-cli/` if none is found. Respect an explicitly requested
CLI version and check its help for supported flags.

1. Identify the original state root. URL reviews default to `~/.jury`; use
`--dir ""` to select it explicitly. A local-checkout review stores `runs/`
Expand Down
Loading
Loading