mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-26 14:01:14 -07:00
refactor: rename deep-review to offload and generalize capabilities
This commit is contained in:
@@ -1,150 +0,0 @@
|
||||
# Deep review maintainer skill
|
||||
|
||||
The deep review skill provides a high-performance, parallelized workflow for
|
||||
reviewing pull requests on a remote workstation. It leverages a Node.js
|
||||
orchestrator to offload intensive builds and automated testing to parallel
|
||||
background processes, showing progress in a dedicated terminal window.
|
||||
|
||||
## Why use deep review?
|
||||
|
||||
Standard code reviews are often constrained by the serial nature of human-AI
|
||||
interaction and local machine performance. This skill addresses those bottlenecks
|
||||
by implementing a "Remote Offloading" and "Parallel Verification" strategy.
|
||||
|
||||
- **Protect local resources**: Heavy build and lint suites are offloaded to a
|
||||
beefy remote workstation, keeping your local machine responsive for multi-tasking.
|
||||
- **Context efficiency**: The main Gemini session remains interactive and focused
|
||||
on high-level reasoning. You don't have to wait for a build to finish before
|
||||
asking your next question.
|
||||
- **True parallelism**: Infrastructure validation (build), CI checks, static
|
||||
analysis, and behavioral proofs run simultaneously in an external window.
|
||||
This compresses a 15-minute sequential process into a 3-minute parallel one.
|
||||
- **Behavioral verification**: Instead of just reading code, the remote worker
|
||||
physically exercises the new features in a live environment, providing
|
||||
empirical proof that the code actually works before you approve it.
|
||||
|
||||
## Scenarios and workflows
|
||||
|
||||
The following scenarios outline how to use the deep review skill effectively.
|
||||
|
||||
### First-time setup
|
||||
|
||||
Before running your first deep review, you must configure your remote
|
||||
workstation.
|
||||
|
||||
1. Run the setup command:
|
||||
```bash
|
||||
npm run review:setup
|
||||
```
|
||||
2. Follow the interactive prompts to specify:
|
||||
- **Remote SSH Host**: The alias for your remote machine (default: `cli`).
|
||||
- **Remote Work Directory**: Where PR worktrees will be provisioned.
|
||||
- **Identity Synchronization**: Whether to mirror your local `.gemini`
|
||||
credentials to the remote host.
|
||||
- **Terminal Automation**: Your preferred terminal emulator (for example,
|
||||
`iterm2` or `terminal`).
|
||||
|
||||
**Pre-existing environments**: If your remote host is already set up for Gemini
|
||||
CLI development, you can point the **Remote Work Directory** to your existing
|
||||
repository instance. The skill will detect your existing GitHub CLI and Gemini
|
||||
CLI installations and use them directly without redundant setup.
|
||||
|
||||
**Isolation and safety**: By default, the skill attempts to isolate its
|
||||
configuration using a dedicated profile (`~/.gemini-deep-review`) on the remote
|
||||
host. This ensures that the automated verification tasks do not impact your
|
||||
primary Gemini CLI environment or machine state, following development best
|
||||
practices.
|
||||
|
||||
### Launching a review
|
||||
|
||||
To start a deep review for a specific pull request, use the `review` script.
|
||||
|
||||
1. Execute the review command with the PR number:
|
||||
```bash
|
||||
npm run review <PR_NUMBER>
|
||||
```
|
||||
2. The orchestrator performs the following actions:
|
||||
- Fetches PR metadata and branch information.
|
||||
- Synchronizes the latest review scripts to your remote host.
|
||||
- Mirrors your local environment and credentials (if configured).
|
||||
- Opens a new terminal window and connects to a remote `tmux` session.
|
||||
|
||||
### Remote parallel execution
|
||||
|
||||
Once the remote session starts, the worker process automatically provisions a
|
||||
blobless git clone for the PR and launches four tasks in parallel:
|
||||
|
||||
- **Fast Build**: Executes `npm ci && npm run build` to prepare the environment.
|
||||
- **CI Checks**: Monitors the status of GitHub Actions for the PR.
|
||||
- **Gemini Analysis**: Performs a static analysis using the `/review-frontend`
|
||||
command.
|
||||
- **Behavioral Proof**: Exercises the new code in the terminal to verify
|
||||
functionality. This task waits for the **Fast Build** to complete.
|
||||
|
||||
You can monitor the live status and logs for all these tasks in the dedicated
|
||||
terminal window.
|
||||
|
||||
### Monitoring and synthesis
|
||||
|
||||
While verification is running, your main Gemini CLI session remains interactive.
|
||||
|
||||
1. To check progress from your local shell without switching windows:
|
||||
```bash
|
||||
npm run review:check <PR_NUMBER>
|
||||
```
|
||||
2. Once all tasks show as **SUCCESS**, the remote session automatically
|
||||
launches an interactive Gemini session.
|
||||
3. Gemini CLI reads the captured logs from `.gemini/logs/review-<PR_NUMBER>/`
|
||||
and presents a synthesized final assessment.
|
||||
|
||||
### Cleanup
|
||||
|
||||
To keep your remote workstation tidy, you can wipe old PR directories and kill
|
||||
inactive sessions.
|
||||
|
||||
1. Run the cleanup command:
|
||||
```bash
|
||||
npm run review:clean
|
||||
```
|
||||
2. You can choose to perform a standard cleanup (remove worktrees) or a full
|
||||
wipe of the remote work directory.
|
||||
|
||||
## Best practices
|
||||
|
||||
Adhere to these best practices when using the deep review skill.
|
||||
|
||||
- **Trust the worker**: Avoid running manual builds or linting in your main
|
||||
session. The parallel worker is more efficient and captures structured logs
|
||||
for the agent to read later.
|
||||
- **Verify behavior**: Pay close attention to the `test-execution.log`. It
|
||||
provides the physical proof that the feature actually works in a live
|
||||
environment.
|
||||
- **Use isolated profiles**: The skill uses `~/.gemini-deep-review` on the
|
||||
remote host to avoid interfering with your primary Gemini CLI configuration.
|
||||
|
||||
## Technical details
|
||||
|
||||
This skill uses an isolated Gemini profile on the remote host (`~/.gemini-deep-review`) to ensure that verification tasks do not interfere with your primary configuration.
|
||||
|
||||
### Directory structure
|
||||
- `scripts/review.ts`: Local orchestrator (syncs scripts and pops terminal).
|
||||
- `scripts/worker.ts`: Remote engine (provisions worktree and runs parallel tasks).
|
||||
- `scripts/check.ts`: Local status poller.
|
||||
- `scripts/clean.ts`: Remote cleanup utility.
|
||||
- `SKILL.md`: Instructional body used by the Gemini CLI agent.
|
||||
|
||||
## Contributing
|
||||
|
||||
If you want to improve this skill:
|
||||
1. Modify the TypeScript scripts in `scripts/`.
|
||||
2. Update `SKILL.md` if the agent's instructions need to change.
|
||||
3. Test your changes locally using `npm run review <PR>`.
|
||||
|
||||
## Testing
|
||||
|
||||
The orchestration logic for this skill is fully tested. To run the tests:
|
||||
```bash
|
||||
npx vitest .gemini/skills/deep-review/tests/orchestration.test.ts
|
||||
```
|
||||
These tests mock the external environment (SSH, GitHub CLI, and the file system) to ensure that the orchestration scripts generate the correct commands and handle environment isolation accurately.
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
# Deep Review Skill
|
||||
|
||||
This skill provides a high-performance, parallelized workflow for reviewing Pull Requests on a remote workstation. It leverages a Node.js orchestrator to offload intensive builds and automated testing to parallel background processes with a dedicated status dashboard in a separate terminal window.
|
||||
|
||||
## Overview
|
||||
|
||||
The Deep Review workflow follows a "Verify then Synthesize" pattern:
|
||||
1. **Orchestration**: Launch the local Node.js orchestrator which handles remote synchronization and terminal automation.
|
||||
2. **Parallel Verification**: Monitor the 5 parallel tasks (Build, CI Check, Static Analysis, Behavioral Testing, and Diagnostics) in the dedicated review window.
|
||||
3. **Synthesis**: Interactively analyze the results generated by the worker to provide a final recommendation.
|
||||
|
||||
## Workflows
|
||||
|
||||
### 1. Setup (First-time use)
|
||||
When the user wants to enable deep reviews or if the configuration is missing:
|
||||
* **Action**: Execute the setup command locally:
|
||||
`npm run review:setup`
|
||||
* **Response**: The agent will interactively provision the remote workstation and save settings.
|
||||
|
||||
### 2. Starting the Review
|
||||
When the user asks to "Deep review" a PR:
|
||||
* **Command**: `npm run review <PR_NUMBER>`
|
||||
* **Action**: This will sync scripts, provision a worktree, and pop a new terminal window.
|
||||
* **Response**: Inform the user that the review is starting in a new window.
|
||||
### 3. Monitoring and Synthesis
|
||||
Once the review is launched, the agent should monitor its progress.
|
||||
* **Action**: Execute the status check command locally:
|
||||
`npm run review:check <PR_NUMBER>`
|
||||
* **Evaluation**:
|
||||
* If tasks are `RUNNING`, tell the user the review is still in progress.
|
||||
* If all tasks are `SUCCESS`, prepare for synthesis by reading the logs.
|
||||
|
||||
#### Reading Results
|
||||
To provide the final assessment, the agent must fetch the following files from the remote host using `ssh cli "cat ..."`:
|
||||
* `review.md` (Static analysis findings).
|
||||
* `build.log` (Build and runtime environment status).
|
||||
* `test-execution.log` (Behavioral proof results).
|
||||
* `ci.exit` (GitHub Check status).
|
||||
|
||||
### 4. Cleanup
|
||||
(existing content...)
|
||||
When the user asks to "cleanup" or "start fresh" on the remote machine:
|
||||
* **Action**: Execute the following cleanup command locally:
|
||||
`npm run review:clean`
|
||||
* **Response**: Confirm that the remote workstation (tmux sessions and worktrees) has been cleared based on the project settings.
|
||||
|
||||
### 5. Final Recommendation
|
||||
Provide a structured assessment based on the physical proof and logs:
|
||||
* **Status**: Does it build? Does it pass CI? Did the behavioral tests prove the feature works?
|
||||
* **Findings**: Categorize by Critical, Improvements, or Nitpicks.
|
||||
* **Conclusion**: Approve or Request Changes.
|
||||
|
||||
## Best Practices
|
||||
* **Trust the Worker**: Do not re-run build or lint tasks manually in the main Gemini window; the parallel worker handles this more efficiently.
|
||||
* **Be Behavioral**: Focus on the results in `test-execution.log` to prove the code actually works in a live environment.
|
||||
* **Acknowledge the Window**: Remind the user they can see real-time progress and logs in the separate terminal window we opened for them.
|
||||
76
.gemini/skills/offload/README.md
Normal file
76
.gemini/skills/offload/README.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# Offload maintainer skill
|
||||
|
||||
The `offload` skill provides a high-performance, parallelized workflow for
|
||||
offloading intensive developer tasks to a remote workstation. It leverages a
|
||||
Node.js orchestrator to run complex validation playbooks concurrently in a
|
||||
dedicated terminal window.
|
||||
|
||||
## Why use offload?
|
||||
|
||||
- **Protect local resources**: Heavy build and lint suites are offloaded to a
|
||||
beefy remote workstation.
|
||||
- **Context efficiency**: The main Gemini session remains interactive while
|
||||
background tasks provide high-fidelity feedback.
|
||||
- **True parallelism**: Infrastructure validation, CI checks, and behavioral
|
||||
proofs run simultaneously, compressing a 15-minute process into 3 minutes.
|
||||
- **Behavioral verification**: The worker physically exercises the new code
|
||||
to provide empirical proof that it works.
|
||||
|
||||
## Playbooks
|
||||
|
||||
- **`review`** (default): Build, CI check, static analysis, and behavioral proofs.
|
||||
- **`fix`**: Iterative fixing of CI failures and review comments.
|
||||
- **`ready`**: Final full validation (clean install + preflight) before merge.
|
||||
- **`open`**: Provision a worktree and drop directly into a remote tmux session.
|
||||
|
||||
## Scenarios and workflows
|
||||
|
||||
### First-time setup
|
||||
Run the setup command once to configure your remote environment:
|
||||
```bash
|
||||
npm run offload:setup
|
||||
```
|
||||
|
||||
### Offloading a task
|
||||
To start an offload task for a pull request:
|
||||
```bash
|
||||
npm run offload <PR_NUMBER> [action]
|
||||
```
|
||||
|
||||
### Monitoring progress
|
||||
Check status from your local shell without switching windows:
|
||||
```bash
|
||||
npm run offload:check <PR_NUMBER>
|
||||
```
|
||||
|
||||
### Cleanup
|
||||
Wipe old PR worktrees and kill inactive sessions:
|
||||
```bash
|
||||
npm run offload:clean
|
||||
```
|
||||
|
||||
## Technical details
|
||||
|
||||
This skill uses an isolated Gemini profile on the remote host (`~/.gemini-deep-review`) to ensure that verification tasks do not interfere with your primary configuration.
|
||||
|
||||
### Directory structure
|
||||
- `scripts/orchestrator.ts`: Local orchestrator (syncs scripts and pops terminal).
|
||||
- `scripts/worker.ts`: Remote engine (provisions worktree and runs playbooks).
|
||||
- `scripts/check.ts`: Local status poller.
|
||||
- `scripts/clean.ts`: Remote cleanup utility.
|
||||
- `SKILL.md`: Instructional body used by the Gemini CLI agent.
|
||||
|
||||
## Contributing
|
||||
|
||||
If you want to improve this skill:
|
||||
1. Modify the TypeScript scripts in `scripts/`.
|
||||
2. Update `SKILL.md` if the agent's instructions need to change.
|
||||
3. Test your changes locally using `npm run offload <PR>`.
|
||||
|
||||
## Testing
|
||||
|
||||
The orchestration logic for this skill is fully tested. To run the tests:
|
||||
```bash
|
||||
npx vitest .gemini/skills/offload/tests/orchestration.test.ts
|
||||
```
|
||||
These tests mock the external environment (SSH, GitHub CLI, and the file system) to ensure that the orchestration scripts generate the correct commands and handle environment isolation accurately.
|
||||
37
.gemini/skills/offload/SKILL.md
Normal file
37
.gemini/skills/offload/SKILL.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# Offload Skill
|
||||
|
||||
This skill provides a high-performance, parallelized workflow for offloading intensive developer tasks (PR reviews, fixing CI, preparing merges) to a remote workstation. It leverages a Node.js orchestration engine to run complex validation playbooks concurrently in a dedicated terminal window.
|
||||
|
||||
## Playbooks
|
||||
|
||||
The `offload` skill supports the following specialized playbooks:
|
||||
|
||||
- **`review`** (default): Clean build, CI status check, static analysis, and behavioral proofs.
|
||||
- **`fix`**: Build + Log analysis of CI failures + Iterative Gemini-led fixing and pushing.
|
||||
- **`ready`**: Final full validation (clean install, full preflight, and conflict checks).
|
||||
- **`open`**: Provision a worktree and drop the user directly into a remote shell/tmux session.
|
||||
- **`implement`**: Read an issue → Research → Implement → Verify → Create PR.
|
||||
|
||||
## Workflow
|
||||
|
||||
### 1. Initializing an Offload Task
|
||||
When the user asks to offload a task (e.g., "Offload PR 123 fix" or "Make PR 123 ready"), use the `run_shell_command` tool to execute the orchestrator:
|
||||
* **Command**: `npm run offload <PR_NUMBER> [action]`
|
||||
* **Action**: This will sync scripts to the remote host, provision a worktree, and pop a new terminal window for the playbook dashboard.
|
||||
* **Response**: Inform the user which playbook has been launched.
|
||||
|
||||
### 2. Monitoring and Synthesis
|
||||
The remote worker saves all results into `.gemini/logs/offload-<PR_NUMBER>/`. Once the playbook finishes, the agent should synthesize the results:
|
||||
* Read logs corresponding to the playbook tasks (e.g., `build.log`, `review.md`, `test-execution.log`, `diagnostics.log`).
|
||||
* Check the `.exit` files to confirm success of each parallel stage.
|
||||
|
||||
### 3. Final Recommendation
|
||||
Provide a structured assessment based on the physical proof and logs:
|
||||
* **Status**: PASS / FAIL / NEEDS_WORK.
|
||||
* **Findings**: Categorized by Critical, Improvements, or Nitpicks.
|
||||
* **Conclusion**: A clear next step for the maintainer.
|
||||
|
||||
## Best Practices
|
||||
* **Isolation First**: Always respect the user's isolation choices (`~/.gemini-deep-review`).
|
||||
* **Be Behavioral**: Prioritize results from live execution (behavioral proofs) over static reading.
|
||||
* **Multi-tasking**: Remind the user they can continue chatting in the main window while the heavy offloaded task runs in the separate window.
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Universal Deep Review Orchestrator (Local)
|
||||
* Universal Offload Orchestrator (Local)
|
||||
*/
|
||||
import { spawnSync } from 'child_process';
|
||||
import path from 'path';
|
||||
@@ -13,8 +13,10 @@ const q = (str: string) => `'${str.replace(/'/g, "'\\''")}'`;
|
||||
|
||||
export async function runOrchestrator(args: string[], env: NodeJS.ProcessEnv = process.env) {
|
||||
const prNumber = args[0];
|
||||
const action = args[1] || 'review'; // Default action is review
|
||||
|
||||
if (!prNumber) {
|
||||
console.error('Usage: npm run review <PR_NUMBER>');
|
||||
console.error('Usage: npm run offload <PR_NUMBER> [action]');
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -27,10 +29,10 @@ export async function runOrchestrator(args: string[], env: NodeJS.ProcessEnv = p
|
||||
|
||||
let config = settings.maintainer?.deepReview;
|
||||
if (!config) {
|
||||
console.log('⚠️ Deep Review configuration not found. Launching setup...');
|
||||
const setupResult = spawnSync('npm', ['run', 'review:setup'], { stdio: 'inherit' });
|
||||
console.log('⚠️ Offload configuration not found. Launching setup...');
|
||||
const setupResult = spawnSync('npm', ['run', 'offload:setup'], { stdio: 'inherit' });
|
||||
if (setupResult.status !== 0) {
|
||||
console.error('❌ Setup failed. Please run "npm run review:setup" manually.');
|
||||
console.error('❌ Setup failed. Please run "npm run offload:setup" manually.');
|
||||
return 1;
|
||||
}
|
||||
// Reload settings after setup
|
||||
@@ -56,12 +58,12 @@ export async function runOrchestrator(args: string[], env: NodeJS.ProcessEnv = p
|
||||
const remotePolicyPath = `${ISOLATED_GEMINI}/policies/deep-review-policy.toml`;
|
||||
|
||||
console.log(`📡 Mirroring environment to ${remoteHost}...`);
|
||||
spawnSync('ssh', [remoteHost, `mkdir -p ${remoteWorkDir}/.gemini/skills/deep-review/scripts/ ${ISOLATED_GEMINI}/policies/`]);
|
||||
spawnSync('ssh', [remoteHost, `mkdir -p ${remoteWorkDir}/.gemini/skills/offload/scripts/ ${ISOLATED_GEMINI}/policies/`]);
|
||||
|
||||
// Sync the policy file specifically
|
||||
spawnSync('rsync', ['-avz', path.join(REPO_ROOT, '.gemini/skills/deep-review/policy.toml'), `${remoteHost}:${remotePolicyPath}`]);
|
||||
spawnSync('rsync', ['-avz', path.join(REPO_ROOT, '.gemini/skills/offload/policy.toml'), `${remoteHost}:${remotePolicyPath}`]);
|
||||
|
||||
spawnSync('rsync', ['-avz', '--delete', path.join(REPO_ROOT, '.gemini/skills/deep-review/scripts/'), `${remoteHost}:${remoteWorkDir}/.gemini/skills/deep-review/scripts/`]);
|
||||
spawnSync('rsync', ['-avz', '--delete', path.join(REPO_ROOT, '.gemini/skills/offload/scripts/'), `${remoteHost}:${remoteWorkDir}/.gemini/skills/offload/scripts/`]);
|
||||
|
||||
if (syncAuth) {
|
||||
const homeDir = env.HOME || '';
|
||||
@@ -76,54 +78,48 @@ export async function runOrchestrator(args: string[], env: NodeJS.ProcessEnv = p
|
||||
const localEnv = path.join(REPO_ROOT, '.env');
|
||||
if (fs.existsSync(localEnv)) spawnSync('rsync', ['-avz', localEnv, `${remoteHost}:${remoteWorkDir}/.env`]);
|
||||
}
|
||||
// 4. Construct Clean Command
|
||||
const envLoader = 'export NVM_DIR="$HOME/.nvm"; [ -s "$NVM_DIR/nvm.sh" ] && \\. "$NVM_DIR/nvm.sh"';
|
||||
// Set FORCE_LOCAL_OPEN=1 to signal the worker to use OSC 1337 for links
|
||||
const remoteWorker = `export FORCE_LOCAL_OPEN=1 && export GEMINI_CLI_HOME=${ISOLATED_GEMINI} && export GH_CONFIG_DIR=${ISOLATED_GH} && ./node_modules/.bin/tsx .gemini/skills/deep-review/scripts/entrypoint.ts ${prNumber} ${branchName} ${remotePolicyPath}`;
|
||||
|
||||
const tmuxCmd = `cd ${remoteWorkDir} && ${envLoader} && ${remoteWorker}; exec $SHELL`;
|
||||
const sshInternal = `tmux attach-session -t ${sessionName} 2>/dev/null || tmux new-session -s ${sessionName} -n ${q(branchName)} ${q(tmuxCmd)}`;
|
||||
const sshCmd = `ssh -t ${remoteHost} ${q(sshInternal)}`;
|
||||
// 3. Construct Clean Command
|
||||
const envLoader = 'export NVM_DIR="$HOME/.nvm"; [ -s "$NVM_DIR/nvm.sh" ] && \\. "$NVM_DIR/nvm.sh"';
|
||||
// Set FORCE_LOCAL_OPEN=1 to signal the worker to use OSC 1337 for links
|
||||
const remoteWorker = `export FORCE_LOCAL_OPEN=1 && export GEMINI_CLI_HOME=${ISOLATED_GEMINI} && export GH_CONFIG_DIR=${ISOLATED_GH} && ./node_modules/.bin/tsx .gemini/skills/offload/scripts/entrypoint.ts ${prNumber} ${branchName} ${remotePolicyPath} ${action}`;
|
||||
|
||||
const tmuxCmd = `cd ${remoteWorkDir} && ${envLoader} && ${remoteWorker}; exec $SHELL`;
|
||||
const sshInternal = `tmux attach-session -t ${sessionName} 2>/dev/null || tmux new-session -s ${sessionName} -n ${q(branchName)} ${q(tmuxCmd)}`;
|
||||
const sshCmd = `ssh -t ${remoteHost} ${q(sshInternal)}`;
|
||||
|
||||
// 5. Smart Context Execution
|
||||
// Check if we are running inside a Gemini CLI session or a plain shell
|
||||
const isWithinGemini = !!env.GEMINI_SESSION_ID || !!env.GCLI_SESSION_ID;
|
||||
const forceBackground = args.includes('--background');
|
||||
// 4. Smart Context Execution
|
||||
const isWithinGemini = !!env.GEMINI_SESSION_ID || !!env.GCLI_SESSION_ID;
|
||||
const forceBackground = args.includes('--background');
|
||||
|
||||
if (isWithinGemini || forceBackground) {
|
||||
if (process.platform === 'darwin' && terminalType !== 'none' && !forceBackground) {
|
||||
// macOS: Use Window Automation (Interactive Mode)
|
||||
|
||||
let appleScript = '';
|
||||
if (terminalType === 'iterm2') {
|
||||
appleScript = `on run argv\n set theCommand to item 1 of argv\n tell application "iTerm"\n set newWindow to (create window with default profile)\n tell current session of newWindow\n write text theCommand\n end tell\n activate\n end tell\n end run`;
|
||||
} else if (terminalType === 'terminal') {
|
||||
if (isWithinGemini || forceBackground) {
|
||||
if (process.platform === 'darwin' && terminalType !== 'none' && !forceBackground) {
|
||||
// macOS: Use Window Automation
|
||||
let appleScript = `on run argv\n set theCommand to item 1 of argv\n tell application "iTerm"\n set newWindow to (create window with default profile)\n tell current session of newWindow\n write text theCommand\n end tell\n activate\n end tell\n end run`;
|
||||
if (terminalType === 'terminal') {
|
||||
appleScript = `on run argv\n set theCommand to item 1 of argv\n tell application "Terminal"\n do script theCommand\n activate\n end tell\n end run`;
|
||||
}
|
||||
|
||||
if (appleScript) {
|
||||
spawnSync('osascript', ['-', sshCmd], { input: appleScript });
|
||||
console.log(`✅ ${terminalType.toUpperCase()} window opened for verification.`);
|
||||
return 0;
|
||||
}
|
||||
spawnSync('osascript', ['-', sshCmd], { input: appleScript });
|
||||
console.log(`✅ ${terminalType.toUpperCase()} window opened for verification.`);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Cross-Platform Background Mode (within Gemini session)
|
||||
// Cross-Platform Background Mode
|
||||
console.log(`📡 Launching remote verification in background mode...`);
|
||||
const logFile = path.join(REPO_ROOT, `.gemini/logs/review-${prNumber}/background.log`);
|
||||
const logFile = path.join(REPO_ROOT, `.gemini/logs/offload-${prNumber}/background.log`);
|
||||
fs.mkdirSync(path.dirname(logFile), { recursive: true });
|
||||
|
||||
// Use tmuxCmd for background mode to ensure persistence even in background
|
||||
const backgroundCmd = `ssh ${remoteHost} ${q(tmuxCmd)} > ${q(logFile)} 2>&1 &`;
|
||||
spawnSync(backgroundCmd, { shell: true });
|
||||
|
||||
console.log(`⏳ Remote worker started in background.`);
|
||||
console.log(`📄 Tailing logs to: .gemini/logs/review-${prNumber}/background.log`);
|
||||
console.log(`📄 Tailing logs to: .gemini/logs/offload-${prNumber}/background.log`);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Direct Shell Mode: Execute SSH in-place
|
||||
console.log(`🚀 Launching review session in current terminal...`);
|
||||
console.log(`🚀 Launching offload session in current terminal...`);
|
||||
const result = spawnSync(sshCmd, { stdio: 'inherit', shell: true });
|
||||
return result.status || 0;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Universal Deep Review Worker (Remote)
|
||||
* Universal Offload Worker (Remote)
|
||||
*
|
||||
* Handles worktree provisioning and parallel task execution.
|
||||
* Handles worktree provisioning and parallel task execution based on 'playbooks'.
|
||||
*/
|
||||
import { spawn, spawnSync } from 'child_process';
|
||||
import path from 'path';
|
||||
@@ -10,21 +10,18 @@ import fs from 'fs';
|
||||
const prNumber = process.argv[2];
|
||||
const branchName = process.argv[3];
|
||||
const policyPath = process.argv[4];
|
||||
const action = process.argv[5] || 'review';
|
||||
|
||||
export async function runWorker(args: string[]) {
|
||||
const prNumber = args[0];
|
||||
const branchName = args[1];
|
||||
const policyPath = args[2];
|
||||
|
||||
async function main() {
|
||||
if (!prNumber || !branchName || !policyPath) {
|
||||
console.error('Usage: tsx worker.ts <PR_NUMBER> <BRANCH_NAME> <POLICY_PATH>');
|
||||
console.error('Usage: tsx worker.ts <PR_NUMBER> <BRANCH_NAME> <POLICY_PATH> [action]');
|
||||
return 1;
|
||||
}
|
||||
|
||||
const workDir = process.cwd(); // This is remoteWorkDir
|
||||
const targetDir = path.join(workDir, branchName);
|
||||
|
||||
// 1. Provision PR Directory (Fast Blobless Clone)
|
||||
// 1. Provision PR Directory
|
||||
if (!fs.existsSync(targetDir)) {
|
||||
console.log(`🌿 Provisioning PR #${prNumber} into ${branchName}...`);
|
||||
const cloneCmd = `git clone --filter=blob:none https://github.com/google-gemini/gemini-cli.git ${targetDir}`;
|
||||
@@ -36,18 +33,37 @@ export async function runWorker(args: string[]) {
|
||||
process.chdir(targetDir);
|
||||
}
|
||||
|
||||
const logDir = path.join(targetDir, `.gemini/logs/review-${prNumber}`);
|
||||
const logDir = path.join(targetDir, `.gemini/logs/offload-${prNumber}`);
|
||||
fs.mkdirSync(logDir, { recursive: true });
|
||||
|
||||
const geminiBin = path.join(workDir, 'node_modules/.bin/gemini');
|
||||
|
||||
// 2. Define Parallel Tasks
|
||||
const tasks = [
|
||||
{ id: 'build', name: 'Fast Build', cmd: `cd ${targetDir} && npm ci && npm run build` },
|
||||
{ id: 'ci', name: 'CI Checks', cmd: `gh pr checks ${prNumber}` },
|
||||
{ id: 'review', name: 'Gemini Analysis', cmd: `${geminiBin} --policy ${policyPath} --cwd ${targetDir} -p "/review-frontend ${prNumber}"` },
|
||||
{ id: 'verify', name: 'Behavioral Proof', cmd: `${geminiBin} --policy ${policyPath} --cwd ${targetDir} -p "Analyze the code in ${targetDir} and exercise it to prove it works."`, dep: 'build' }
|
||||
];
|
||||
// 2. Define Playbooks
|
||||
let tasks: any[] = [];
|
||||
|
||||
if (action === 'review') {
|
||||
tasks = [
|
||||
{ id: 'build', name: 'Fast Build', cmd: `cd ${targetDir} && npm ci && npm run build` },
|
||||
{ id: 'ci', name: 'CI Checks', cmd: `gh pr checks ${prNumber}` },
|
||||
{ id: 'review', name: 'Gemini Analysis', cmd: `${geminiBin} --policy ${policyPath} --cwd ${targetDir} -p "/review-frontend ${prNumber}"` },
|
||||
{ id: 'verify', name: 'Behavioral Proof', cmd: `${geminiBin} --policy ${policyPath} --cwd ${targetDir} -p "Analyze the code in ${targetDir} and exercise it to prove it works."`, dep: 'build' }
|
||||
];
|
||||
} else if (action === 'fix') {
|
||||
tasks = [
|
||||
{ id: 'build', name: 'Fast Build', cmd: `cd ${targetDir} && npm ci && npm run build` },
|
||||
{ id: 'failures', name: 'Find Failures', cmd: `gh run view --log-failed` },
|
||||
{ id: 'fix', name: 'Iterative Fix', cmd: `${geminiBin} --policy ${policyPath} --cwd ${targetDir} -p "Address review comments and fix failing tests for PR ${prNumber}. Repeat until CI is green."`, dep: 'build' }
|
||||
];
|
||||
} else if (action === 'ready') {
|
||||
tasks = [
|
||||
{ id: 'clean', name: 'Clean Install', cmd: `npm run clean && npm ci` },
|
||||
{ id: 'preflight', name: 'Full Preflight', cmd: `npm run preflight`, dep: 'clean' },
|
||||
{ id: 'conflicts', name: 'Conflict Check', cmd: `git fetch origin main && git merge-base --is-ancestor origin/main HEAD || echo "CONFLICT"` }
|
||||
];
|
||||
} else if (action === 'open') {
|
||||
console.log(`🚀 Dropping into manual session for ${branchName}...`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const state: Record<string, any> = {};
|
||||
tasks.forEach(t => state[t.id] = { status: 'PENDING' });
|
||||
@@ -76,8 +92,8 @@ export async function runWorker(args: string[]) {
|
||||
function render() {
|
||||
console.clear();
|
||||
console.log(`==================================================`);
|
||||
console.log(`🚀 Deep Review | PR #${prNumber} | ${branchName}`);
|
||||
console.log(`📂 PR Target: ${targetDir}`);
|
||||
console.log(`🚀 Offload | ${action.toUpperCase()} | PR #${prNumber}`);
|
||||
console.log(`📂 Worktree: ${targetDir}`);
|
||||
console.log(`==================================================\n`);
|
||||
|
||||
tasks.forEach(t => {
|
||||
@@ -88,7 +104,7 @@ export async function runWorker(args: string[]) {
|
||||
|
||||
const allDone = tasks.every(t => ['SUCCESS', 'FAILED'].includes(state[t.id].status));
|
||||
if (allDone) {
|
||||
console.log(`\n✨ Verification complete. Launching interactive session...`);
|
||||
console.log(`\n✨ Playbook complete. Launching interactive session...`);
|
||||
resolve(0);
|
||||
}
|
||||
}
|
||||
@@ -97,7 +113,6 @@ export async function runWorker(args: string[]) {
|
||||
tasks.filter(t => t.dep).forEach(runTask);
|
||||
const intervalId = setInterval(render, 1500);
|
||||
|
||||
// Ensure the promise resolves and the interval is cleared when all done
|
||||
const checkAllDone = setInterval(() => {
|
||||
if (tasks.every(t => ['SUCCESS', 'FAILED'].includes(state[t.id].status))) {
|
||||
clearInterval(intervalId);
|
||||
@@ -2,7 +2,7 @@ import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { spawnSync, spawn } from 'child_process';
|
||||
import fs from 'fs';
|
||||
import readline from 'readline';
|
||||
import { runOrchestrator } from '../scripts/review.ts';
|
||||
import { runOrchestrator } from '../scripts/orchestrator.ts';
|
||||
import { runSetup } from '../scripts/setup.ts';
|
||||
import { runWorker } from '../scripts/worker.ts';
|
||||
import { runChecker } from '../scripts/check.ts';
|
||||
@@ -233,32 +233,44 @@ describe('Deep Review Orchestration', () => {
|
||||
const savedSettings = JSON.parse(writeCall![1] as string);
|
||||
expect(savedSettings.maintainer.deepReview.geminiSetup).toBe('isolated');
|
||||
expect(savedSettings.maintainer.deepReview.ghSetup).toBe('preexisting');
|
||||
expect(savedSettings.maintainer.deepReview.syncAuth).toBe(true);
|
||||
});
|
||||
});
|
||||
describe('orchestrator.ts (offload)', () => {
|
||||
it('should default to review action and pass it to remote', async () => {
|
||||
await runOrchestrator(['123'], {});
|
||||
|
||||
describe('worker.ts', () => {
|
||||
it('should launch parallel tasks and write exit codes', async () => {
|
||||
// Mock targetDir existing
|
||||
vi.mocked(fs.existsSync).mockImplementation((p) => p.toString().includes('test-branch'));
|
||||
|
||||
const workerPromise = runWorker(['123', 'test-branch', '/test-policy.toml']);
|
||||
|
||||
// Since worker uses setInterval/setTimeout, we might need to advance timers
|
||||
// or ensure the close event triggers everything
|
||||
await workerPromise;
|
||||
const spawnCalls = vi.mocked(spawnSync).mock.calls;
|
||||
const sshCall = spawnCalls.find(call => typeof call[0] === 'string' && call[0].includes('entrypoint.ts 123'));
|
||||
expect(sshCall![0]).toContain('review'); // Default action
|
||||
});
|
||||
|
||||
const spawnCalls = vi.mocked(spawn).mock.calls;
|
||||
expect(spawnCalls.length).toBeGreaterThanOrEqual(4); // build, ci, review, verify
|
||||
|
||||
const buildCall = spawnCalls.find(call => call[0].includes('npm ci'));
|
||||
expect(buildCall).toBeDefined();
|
||||
it('should pass explicit actions (like fix) to remote', async () => {
|
||||
await runOrchestrator(['123', 'fix'], {});
|
||||
|
||||
const writeCalls = vi.mocked(fs.writeFileSync).mock.calls;
|
||||
const exitFileCall = writeCalls.find(call => call[0].toString().includes('build.exit'));
|
||||
expect(exitFileCall).toBeDefined();
|
||||
expect(exitFileCall![1]).toBe('0');
|
||||
});
|
||||
const spawnCalls = vi.mocked(spawnSync).mock.calls;
|
||||
const sshCall = spawnCalls.find(call => typeof call[0] === 'string' && call[0].includes('entrypoint.ts 123'));
|
||||
expect(sshCall![0]).toContain('fix');
|
||||
});
|
||||
|
||||
it('should construct the correct tmux session name from branch', async () => {
|
||||
...
|
||||
describe('worker.ts (playbooks)', () => {
|
||||
it('should launch the review playbook by default', async () => {
|
||||
vi.mocked(fs.existsSync).mockReturnValue(true);
|
||||
await runWorker(['123', 'test-branch', '/test-policy.toml', 'review']);
|
||||
|
||||
const spawnCalls = vi.mocked(spawn).mock.calls;
|
||||
const analysisCall = spawnCalls.find(call => call[0].includes('/review-frontend'));
|
||||
expect(analysisCall).toBeDefined();
|
||||
});
|
||||
|
||||
it('should launch the fix playbook when requested', async () => {
|
||||
vi.mocked(fs.existsSync).mockReturnValue(true);
|
||||
await runWorker(['123', 'test-branch', '/test-policy.toml', 'fix']);
|
||||
|
||||
const spawnCalls = vi.mocked(spawn).mock.calls;
|
||||
const fixCall = spawnCalls.find(call => call[0].includes('Address review comments'));
|
||||
expect(fixCall).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('check.ts', () => {
|
||||
@@ -63,10 +63,10 @@
|
||||
"telemetry": "node scripts/telemetry.js",
|
||||
"check:lockfile": "node scripts/check-lockfile.js",
|
||||
"clean": "node scripts/clean.js",
|
||||
"review": "tsx .gemini/skills/deep-review/scripts/review.ts",
|
||||
"review:setup": "tsx .gemini/skills/deep-review/scripts/setup.ts",
|
||||
"review:check": "tsx .gemini/skills/deep-review/scripts/check.ts",
|
||||
"review:clean": "tsx .gemini/skills/deep-review/scripts/clean.ts",
|
||||
"offload": "tsx .gemini/skills/offload/scripts/orchestrator.ts",
|
||||
"offload:setup": "tsx .gemini/skills/offload/scripts/setup.ts",
|
||||
"offload:check": "tsx .gemini/skills/offload/scripts/check.ts",
|
||||
"offload:clean": "tsx .gemini/skills/offload/scripts/clean.ts",
|
||||
"pre-commit": "node scripts/pre-commit.js"
|
||||
},
|
||||
"overrides": {
|
||||
|
||||
Reference in New Issue
Block a user