forked from claude-did-this/claude-hub
* feat: Add CLI for managing autonomous Claude Code container sessions This commit implements a new CLI tool 'claude-hub' for managing autonomous Claude Code container sessions. The CLI provides commands for: - Starting autonomous sessions (start) - Listing active/completed sessions (list) - Viewing session logs (logs) - Continuing sessions with new commands (continue) - Stopping sessions (stop) Each session runs in an isolated Docker container and maintains its state across interactions. The implementation includes session management, Docker container operations, and a comprehensive command-line interface. Resolves #133 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * feat: Complete autonomous CLI feature implementation This commit adds the following enhancements to the autonomous Claude CLI: - Add --issue flag to start command for GitHub issue context - Implement start-batch command with tasks.yaml support - Enhance PR flag functionality for better context integration - Implement session recovery mechanism with recover and sync commands - Add comprehensive documentation for all CLI commands Resolves all remaining requirements from issue #133 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * test: Add comprehensive test coverage for CLI - Add unit tests for SessionManager utility - Add simplified unit tests for DockerUtils utility - Add integration tests for start and start-batch commands - Configure Jest with TypeScript support - Add test mocks for Docker API and filesystem - Add test fixtures for batch processing - Document testing approach in README - Add code coverage reporting 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * ci: Add CLI tests workflow and configure stable test suite - Create dedicated GitHub workflow for CLI tests - Update CLI test script to run only stable tests - Add test:all script for running all tests locally 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Improve CLI with TypeScript fixes and CI enhancements - Fix TypeScript Promise handling in list.ts and stop.ts - Update CI workflow to add build step and run all tests - Move ora dependency from devDependencies to dependencies - Update Docker build path to use repository root - Improve CLI script organization in package.json 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Skip Docker-dependent tests in CI - Update test scripts to exclude dockerUtils tests - Add SKIP_DOCKER_TESTS environment variable to CI workflow - Remove dockerUtils.simple.test.ts from specific tests This prevents timeouts in CI caused by Docker tests. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Refine test patterns to exclude only full Docker tests - Replace testPathIgnorePatterns with more precise glob patterns - Ensure dockerUtils.simple.test.ts is still included in the test runs - Keep specific tests command with all relevant tests 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Update Jest test patterns to correctly match test files The previous glob pattern '__tests__/\!(utils/dockerUtils.test).ts' was not finding any tests because it was looking for .ts files directly in the __tests__ folder, but all test files are in subdirectories. Fixed by using Jest's testPathIgnorePatterns option instead. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * test: Add tests for CLI list and continue commands Added comprehensive test coverage for the CLI list and continue commands: - Added list.test.ts with tests for all filtering options and edge cases - Added continue.test.ts with tests for successful continuation and error cases - Both files achieve full coverage of their respective commands These new tests help improve the overall test coverage for the CLI commands module. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * test: Add comprehensive tests for CLI logs, recover, and stop commands Added test coverage for remaining CLI commands: - logs.test.ts - tests for logs command functionality (94.54% coverage) - recover.test.ts - tests for recover and sync commands (100% coverage) - stop.test.ts - tests for stop command with single and all sessions (95.71% coverage) These tests dramatically improve the overall commands module coverage from 56% to 97%. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Align PR review prompt header with test expectations The PR review prompt header in githubController.ts now matches what the test expects in githubController-check-suite.test.js, fixing the failing test. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
91 lines
3.2 KiB
TypeScript
91 lines
3.2 KiB
TypeScript
import { Command } from 'commander';
|
|
import { SessionManager } from '../utils/sessionManager';
|
|
import { DockerUtils } from '../utils/dockerUtils';
|
|
import chalk from 'chalk';
|
|
import ora from 'ora';
|
|
|
|
export function registerContinueCommand(program: Command): void {
|
|
program
|
|
.command('continue')
|
|
.description('Continue an autonomous Claude Code session with a new command')
|
|
.argument('<id>', 'Session ID')
|
|
.argument('<command>', 'Additional command to send to Claude')
|
|
.action(async (id, command) => {
|
|
await continueSession(id, command);
|
|
});
|
|
}
|
|
|
|
async function continueSession(id: string, command: string): Promise<void> {
|
|
const spinner = ora('Continuing session...').start();
|
|
|
|
try {
|
|
const sessionManager = new SessionManager();
|
|
const dockerUtils = new DockerUtils();
|
|
|
|
// Get session by ID
|
|
const session = sessionManager.getSession(id);
|
|
if (!session) {
|
|
spinner.fail(`Session with ID ${id} not found`);
|
|
return;
|
|
}
|
|
|
|
// Check if container is running
|
|
const isRunning = await dockerUtils.isContainerRunning(session.containerId);
|
|
if (!isRunning) {
|
|
if (session.status === 'running') {
|
|
// Update session status to stopped
|
|
sessionManager.updateSessionStatus(id, 'stopped');
|
|
}
|
|
|
|
spinner.fail(`Session ${id} is not running (status: ${session.status}). Cannot continue.`);
|
|
return;
|
|
}
|
|
|
|
// Prepare the continuation command
|
|
spinner.text = 'Sending command to session...';
|
|
|
|
// Create a script to execute in the container
|
|
const continuationScript = `
|
|
#!/bin/bash
|
|
cd /workspace/repo
|
|
|
|
# Save the command to a file
|
|
cat > /tmp/continuation_command.txt << 'EOL'
|
|
${command}
|
|
EOL
|
|
|
|
# Run Claude with the continuation command
|
|
sudo -u node -E env \\
|
|
HOME="${process.env.HOME || '/home/node'}" \\
|
|
PATH="/usr/local/bin:/usr/local/share/npm-global/bin:$PATH" \\
|
|
ANTHROPIC_API_KEY="${process.env.ANTHROPIC_API_KEY || ''}" \\
|
|
GH_TOKEN="${process.env.GITHUB_TOKEN || ''}" \\
|
|
GITHUB_TOKEN="${process.env.GITHUB_TOKEN || ''}" \\
|
|
/usr/local/share/npm-global/bin/claude \\
|
|
--allowedTools "Bash,Create,Edit,Read,Write,GitHub" \\
|
|
--verbose \\
|
|
--print "$(cat /tmp/continuation_command.txt)"
|
|
`;
|
|
|
|
// Execute the script in the container
|
|
await dockerUtils.executeCommand(session.containerId, continuationScript);
|
|
|
|
// Update session with the additional command
|
|
session.command += `\n\nContinuation: ${command}`;
|
|
session.updatedAt = new Date().toISOString();
|
|
sessionManager.saveSession(session);
|
|
|
|
spinner.succeed(`Command sent to session ${chalk.green(id)}`);
|
|
console.log();
|
|
console.log(`${chalk.blue('Session details:')}`);
|
|
console.log(` ${chalk.yellow('Repository:')} ${session.repoFullName}`);
|
|
console.log(` ${chalk.yellow('Status:')} ${chalk.green('running')}`);
|
|
console.log(` ${chalk.yellow('Container:')} ${session.containerId}`);
|
|
console.log();
|
|
console.log(`To view logs: ${chalk.cyan(`claude-hub logs ${session.id}`)}`);
|
|
console.log(`To stop session: ${chalk.cyan(`claude-hub stop ${session.id}`)}`);
|
|
|
|
} catch (error) {
|
|
spinner.fail(`Failed to continue session: ${error instanceof Error ? error.message : String(error)}`);
|
|
}
|
|
} |