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
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
node_modules/
dist/
runtime/
.env
*.pem
*.sqlite
*.sqlite-shm
*.sqlite-wal
38 changes: 36 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,36 @@
# evaos-code-review-bot
Local ZCode-backed PR review bot pilot for evaOS/Electric Sheep repos
# evaOS Code Review Bot

Pilot local worker for ZCode/GLM-5.2 pull request reviews.

## Safety Defaults

- Reviews only the allowlisted pilot repos in `config.example.json`.
- Skips draft PRs by default.
- Posts at most one review per `{repo, pr, head_sha}`.
- Never submits `APPROVE`.
- Uses `REQUEST_CHANGES` only for validated P0/P1 findings.
- Drops findings that cannot be placed on current RIGHT-side diff lines.
- Drops secret-looking findings instead of redacting and posting them.
- Redacts secret-looking material from local evidence logs before writing them.
- Verifies the ZCode worktree is clean, including untracked files, after every review run.
- Caps ZCode prompt patch bytes and kills long ZCode runs with `zcode.timeoutMs`.
- Installs a temporary per-worktree ZCode policy that allows read-only file tools, disables Bash/mutation/subagents, then restores/removes it before the clean check.
- Sets `ZCODE_MODEL_RETRY_MAX_RETRIES=0` by default so provider rate limits fail fast instead of multiplying requests.

## Commands

```bash
npm run doctor
npm run run-once -- --config /Volumes/LEXAR/Codex/evaos-code-review-bot/config/canary-dry-run.json --dry-run true --repo electricsheephq/WorldOS --pr 1161
npm run daemon -- --config /Volumes/LEXAR/Codex/evaos-code-review-bot/config/canary-dry-run.json --dry-run true
```

Posting reviews requires a GitHub App installed on the pilot repos:

```bash
export EVAOS_REVIEW_BOT_APP_ID=...
export EVAOS_REVIEW_BOT_PRIVATE_KEY_PATH=/path/to/private-key.pem
npm run run-once -- --config /Volumes/LEXAR/Codex/evaos-code-review-bot/config/canary-live.json --dry-run false
```

The worker derives transient ZCode CLI model environment from the existing ZCode app config at `/Volumes/LEXAR/zcode/.zcode/v2/config.json`; it does not copy the Z.ai API key into this repository.
22 changes: 22 additions & 0 deletions config.example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"pilotRepos": ["electricsheephq/WorldOS", "100yenadmin/evaOS-GUI"],
"pollIntervalMs": 90000,
"skipDrafts": true,
"canaryPulls": ["electricsheephq/WorldOS#1161", "100yenadmin/evaOS-GUI#497"],
"workRoot": "/Volumes/LEXAR/repos/evaos-code-review-bot/runtime",
"statePath": "/Volumes/LEXAR/Codex/evaos-code-review-bot/state/reviews.sqlite",
"evidenceDir": "/Volumes/LEXAR/Codex/evaos-code-review-bot/evidence",
"zcode": {
"cliPath": "/Applications/ZCode.app/Contents/Resources/glm/zcode.cjs",
"appConfigPath": "/Volumes/LEXAR/zcode/.zcode/v2/config.json",
"model": "GLM-5.2",
"timeoutMs": 180000,
"maxPatchBytes": 80000,
"retryMaxRetries": 0
},
"github": {
"appId": "set via EVAOS_REVIEW_BOT_APP_ID",
"privateKeyPath": "set via EVAOS_REVIEW_BOT_PRIVATE_KEY_PATH",
"token": "optional fallback token for local development only"
}
}
29 changes: 29 additions & 0 deletions docs/github-app-setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# GitHub App Setup

Create a GitHub App named `evaOS Code Review Bot` with slug `evaos-code-review-bot`.

GitHub does not provide a direct noninteractive `gh app create` command for this. Use the GitHub App manifest handshake or the GitHub web UI. The manifest flow requires a signed-in user to open the organization app-registration URL, approve the manifest, then exchange GitHub's temporary `code` for the app ID and PEM within one hour.

Required repository permissions:

- Contents: read
- Pull requests: read/write
- Checks: read
- Actions: read
- Metadata: read

Install the app only on:

- `electricsheephq/WorldOS`
- `100yenadmin/evaOS-GUI`

After installation, save the generated private key outside the repository and launch the worker with:

```bash
export EVAOS_REVIEW_BOT_APP_ID=...
export EVAOS_REVIEW_BOT_PRIVATE_KEY_PATH=/absolute/path/to/evaos-code-review-bot.private-key.pem
npm run doctor
npm run run-once -- --dry-run true
```

Do not run with `--dry-run false` until dry-run evidence exists for both pilot repos and `npm test` plus `npm run build` pass.
31 changes: 31 additions & 0 deletions docs/launchd.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Launchd Pilot

Launchd should stay disabled until GitHub App installation is complete and a real ZCode dry-run succeeds without rate limiting.

Recommended first live command:

```bash
cd /Volumes/LEXAR/repos/evaos-code-review-bot
export EVAOS_REVIEW_BOT_APP_ID=4184532
export EVAOS_REVIEW_BOT_PRIVATE_KEY_PATH=/Volumes/LEXAR/Codex/evaos-code-review-bot/secrets/evaos-code-review-bot.private-key.pem
npm run run-once -- --config /Volumes/LEXAR/Codex/evaos-code-review-bot/config/canary-dry-run.json --dry-run true --repo electricsheephq/WorldOS --pr 1161
```

After the GitHub App is installed, use app credentials and keep `--dry-run true` for the first observation window:

```bash
export EVAOS_REVIEW_BOT_APP_ID=4184532
export EVAOS_REVIEW_BOT_PRIVATE_KEY_PATH=/Volumes/LEXAR/Codex/evaos-code-review-bot/secrets/evaos-code-review-bot.private-key.pem
npm run daemon -- --config /Volumes/LEXAR/Codex/evaos-code-review-bot/config/canary-dry-run.json --dry-run true
```

When installed as a LaunchAgent, write stdout/stderr to `~/Library/Logs/evaos-code-review-bot/`. On this Mac, launchd failed with `EX_CONFIG` when those paths pointed directly at the Lexar volume; copy the local launch logs into the Lexar evidence packet after each proof window.

Only switch to `--dry-run false` after:

- current-head duplicate reruns post nothing,
- review-plan JSON contains only valid current-diff lines,
- no secret-like text appears in comments or logs,
- ZCode worktrees stay clean after runs,
- GLM/Z.ai rate limits are not firing,
- `npm run doctor` reports `readMode: "app_installation"` and successful read checks for every pilot repo.
47 changes: 47 additions & 0 deletions launchd/evaos-code-review-bot.plist.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.electricsheephq.evaos-code-review-bot</string>
<key>WorkingDirectory</key>
<string>/Volumes/LEXAR/repos/evaos-code-review-bot</string>
<key>ProgramArguments</key>
<array>
<string>/opt/homebrew/bin/node</string>
<string>/Volumes/LEXAR/repos/evaos-code-review-bot/node_modules/tsx/dist/cli.mjs</string>
<string>src/cli.ts</string>
<string>daemon</string>
<string>--config</string>
<string>/Volumes/LEXAR/Codex/evaos-code-review-bot/config/canary-dry-run.json</string>
<string>--dry-run</string>
<string>true</string>
</array>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/opt/homebrew/bin:/opt/homebrew/sbin:/Users/lume/.bun/bin:/Users/lume/gbrain/bin:/Users/lume/.local/bin:/Users/lume/.openclaw/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
<key>HOME</key>
<string>/Users/lume</string>
<key>USER</key>
<string>lume</string>
<key>LOGNAME</key>
<string>lume</string>
<key>SHELL</key>
<string>/bin/zsh</string>
<key>EVAOS_REVIEW_BOT_APP_ID</key>
<string>REPLACE_WITH_APP_ID</string>
<key>EVAOS_REVIEW_BOT_PRIVATE_KEY_PATH</key>
<string>/absolute/path/to/private-key.pem</string>
</dict>
<key>RunAtLoad</key>
<false/>
<key>KeepAlive</key>
<false/>
<key>StandardOutPath</key>
<string>/Users/lume/Library/Logs/evaos-code-review-bot/launchd.out.log</string>
<key>StandardErrorPath</key>
<string>/Users/lume/Library/Logs/evaos-code-review-bot/launchd.err.log</string>
</dict>
</plist>
Loading