@jjdenhertog/ai-driven-development
Version:
AI-driven development workflow with learning capabilities for Claude
518 lines • 19.3 kB
JavaScript
;
/* eslint-disable max-lines */
/* eslint-disable unicorn/prefer-module */
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const commander_1 = require("commander");
const node_fs_1 = require("node:fs");
const node_path_1 = require("node:path");
const clearCommand_1 = require("./commands/clearCommand");
const closeCommand_1 = require("./commands/closeCommand");
const containerLogsCommand_1 = require("./commands/containerLogsCommand");
const containerOpenCommand_1 = require("./commands/containerOpenCommand");
const containerStartCommand_1 = require("./commands/containerStartCommand");
const containerStatusCommand_1 = require("./commands/containerStatusCommand");
const containerStopCommand_1 = require("./commands/containerStopCommand");
const executeNextTaskCommand_1 = require("./commands/executeNextTaskCommand");
const proxyCommand_1 = require("./commands/proxyCommand");
const executeTaskCommand_1 = require("./commands/executeTaskCommand");
const initCommand_1 = require("./commands/initCommand");
const learningCommand_1 = require("./commands/learningCommand");
const logCommand_1 = require("./commands/logCommand");
const openCommand_1 = require("./commands/openCommand");
const saveCommand_1 = require("./commands/saveCommand");
const webCommand_1 = require("./commands/webCommand");
const getContainerName_1 = require("./utils/docker/getContainerName");
const getContainerStatus_1 = require("./utils/docker/getContainerStatus");
const getGitInstance_1 = require("./utils/git/getGitInstance");
const logger_1 = require("./utils/logger");
const sleep_1 = require("./utils/sleep");
const executePlanCommands_1 = require("./commands/executePlanCommands");
// Read version from package.json
const packageJsonPath = (0, node_path_1.join)(__dirname, '..', '..', 'package.json');
const { version } = JSON.parse((0, node_fs_1.readFileSync)(packageJsonPath, 'utf8'));
const program = new commander_1.Command();
// Program configuration
program
.name('aidev')
.description('AI-Driven Development CLI - Automated workflow management for Claude')
.version(version);
// Init command
program
.command('init')
.description('Initialize AI-driven development in current project')
.option('--force', 'Force initialization even if .aidev directory already exists', false)
.action((cmdObject) => __awaiter(void 0, void 0, void 0, function* () {
try {
yield (0, initCommand_1.initCommand)({
force: !!cmdObject.force
});
}
catch (error) {
if (error instanceof Error) {
(0, logger_1.logError)(error.message);
}
else {
(0, logger_1.logError)(String(error));
}
}
yield (0, getGitInstance_1.cleanupGitInstances)();
process.exit(0);
}));
program
.command('plan')
.description('Plan the project')
.option('--phase <phase>', 'Phase to start from', '0')
.option('--reset', 'Reset the planning process')
.option('--dangerously-skip-permissions, --dsp', 'Skip permission checks of Claude Code')
.action((cmdObject) => __awaiter(void 0, void 0, void 0, function* () {
try {
yield (0, executePlanCommands_1.executePlanCommand)({
dangerouslySkipPermission: !!cmdObject.dsp,
phase: Number(cmdObject.phase) + 1,
reset: !!cmdObject.reset
});
}
catch (error) {
if (error instanceof Error) {
(0, logger_1.logError)(error.message);
}
else {
(0, logger_1.logError)(String(error));
}
}
}));
// Execute task command
program
.command('execute <taskId>')
.description('Execute a specific task by ID')
.option('--dry-run', 'Show what would be executed without making changes')
.option('--phase <phase>', 'Phase to start from', '0')
.option('--force', 'Force execution even if task is already in progress')
.option('--dangerously-skip-permissions, --dsp', 'Skip permission checks of Claude Code')
.action((taskId, cmdObject) => __awaiter(void 0, void 0, void 0, function* () {
try {
yield (0, executeTaskCommand_1.executeTaskCommand)({
taskId,
dryRun: !!cmdObject.dryRun,
force: !!cmdObject.force,
dangerouslySkipPermission: !!cmdObject.dsp,
phase: Number(cmdObject.phase) + 1
});
}
catch (error) {
if (error instanceof Error) {
(0, logger_1.logError)(error.message);
}
else {
(0, logger_1.logError)(String(error));
}
}
yield (0, getGitInstance_1.cleanupGitInstances)();
process.exit(0);
}));
// Execute next task command
program
.command('execute-next')
.description('Find and execute the next available pending task')
.option('--dry-run', 'Show what would be executed without making changes')
.option('--force', 'Force execution even if task is already in progress')
.option('--dangerously-skip-permissions, --dsp', 'Skip permission checks of Claude Code')
.action((cmdObject) => __awaiter(void 0, void 0, void 0, function* () {
try {
yield (0, executeNextTaskCommand_1.executeNextTaskCommand)({
dryRun: !!cmdObject.dryRun,
force: !!cmdObject.force,
dangerouslySkipPermission: !!cmdObject.dsp
});
}
catch (error) {
if (error instanceof Error) {
(0, logger_1.logError)(error.message);
}
else {
(0, logger_1.logError)(String(error));
}
}
yield (0, getGitInstance_1.cleanupGitInstances)();
process.exit(0);
}));
// Loop tasks command
program
.command('loop-tasks')
.description('Continuously find and execute pending tasks')
.option('--dry-run', 'Show what would be executed without making changes')
.option('--force', 'Force execution even if task is already in progress')
.option('--dangerously-skip-permissions, --dsp', 'Skip permission checks of Claude Code')
.action((cmdObject) => __awaiter(void 0, void 0, void 0, function* () {
const TASK_EXECUTION_DELAY = 60 * 1000; // 60 seconds
const NO_TASKS_DELAY = 5 * 60 * 1000; // 5 minutes
(0, logger_1.log)('Starting continuous task execution loop...', 'info');
(0, logger_1.log)('Press Ctrl+C to stop', 'info');
// eslint-disable-next-line no-constant-condition
while (true) {
try {
const result = yield (0, executeNextTaskCommand_1.executeNextTaskCommand)({
dryRun: !!cmdObject.dryRun,
force: !!cmdObject.force,
dangerouslySkipPermission: !!cmdObject.dsp
});
if (result.noTasksFound) {
(0, logger_1.log)(`No tasks found. Waiting ${NO_TASKS_DELAY / 1000} seconds before checking again...`, 'info');
yield (0, sleep_1.sleep)(NO_TASKS_DELAY);
}
else if (result.taskExecuted) {
(0, logger_1.log)(`Task execution completed. Waiting ${TASK_EXECUTION_DELAY / 1000} seconds before next task...`, 'info');
yield (0, sleep_1.sleep)(TASK_EXECUTION_DELAY);
}
else {
// No executable tasks (all have dependencies or are in progress)
(0, logger_1.log)(`No executable tasks available. Waiting ${TASK_EXECUTION_DELAY / 1000} seconds before checking again...`, 'info');
yield (0, sleep_1.sleep)(TASK_EXECUTION_DELAY);
}
}
catch (error) {
if (error instanceof Error) {
(0, logger_1.logError)(`Error during task execution: ${error.message}`);
}
else {
(0, logger_1.logError)(`Error during task execution: ${String(error)}`);
}
(0, logger_1.log)(`Waiting ${TASK_EXECUTION_DELAY / 1000} seconds before retrying...`, 'info');
yield (0, sleep_1.sleep)(TASK_EXECUTION_DELAY);
}
}
}));
// Learning command
program
.command('learn')
.description('Monitor completed tasks and learn from user changes')
.option('--dangerously-skip-permissions, --dsp', 'Skip permission checks of Claude Code')
.action((cmdObject) => __awaiter(void 0, void 0, void 0, function* () {
try {
yield (0, learningCommand_1.learningCommand)({
dangerouslySkipPermission: !!cmdObject.dsp
});
}
catch (error) {
if (error instanceof Error) {
(0, logger_1.logError)(error.message);
}
else {
(0, logger_1.logError)(String(error));
}
}
(0, logger_1.log)("Cleaning up git instances", 'info');
yield (0, getGitInstance_1.cleanupGitInstances)();
process.exit(0);
}));
// Log command - for capturing output from Claude Code hooks
program
.command('log')
.description('Capture and log output from Claude Code hooks')
.action(() => __awaiter(void 0, void 0, void 0, function* () {
try {
yield (0, logCommand_1.logCommand)();
}
catch (error) {
if (error instanceof Error) {
(0, logger_1.logError)(error.message);
}
else {
(0, logger_1.logError)(String(error));
}
}
yield (0, getGitInstance_1.cleanupGitInstances)();
process.exit(0);
}));
// Clear command
program
.command('clear')
.alias('clean')
.description('Clear invalid worktrees')
.action(() => __awaiter(void 0, void 0, void 0, function* () {
try {
yield (0, clearCommand_1.clearCommand)();
}
catch (error) {
if (error instanceof Error) {
(0, logger_1.logError)(error.message);
}
else {
(0, logger_1.logError)(String(error));
}
}
yield (0, getGitInstance_1.cleanupGitInstances)();
process.exit(0);
}));
// Open command
program
.command('open <taskId>')
.description('Open a task worktree by ID, name, or "id-name" format')
.action((taskId) => __awaiter(void 0, void 0, void 0, function* () {
try {
yield (0, openCommand_1.openCommand)({
taskId
});
}
catch (error) {
if (error instanceof Error) {
(0, logger_1.logError)(error.message);
}
else {
(0, logger_1.logError)(String(error));
}
}
}));
program
.command('close <taskId>')
.description('Close a task worktree by ID, name, or "id-name" format')
.action((taskId) => __awaiter(void 0, void 0, void 0, function* () {
try {
yield (0, closeCommand_1.closeCommand)({
taskId
});
}
catch (error) {
if (error instanceof Error) {
(0, logger_1.logError)(error.message);
}
else {
(0, logger_1.logError)(String(error));
}
}
yield (0, getGitInstance_1.cleanupGitInstances)();
process.exit(0);
}));
program
.command('save <taskId>')
.description('Save a task worktree by ID, name, or "id-name" format')
.action((taskId) => __awaiter(void 0, void 0, void 0, function* () {
try {
yield (0, saveCommand_1.saveCommand)({
taskId
});
}
catch (error) {
if (error instanceof Error) {
(0, logger_1.logError)(error.message);
}
else {
(0, logger_1.logError)(String(error));
}
}
yield (0, getGitInstance_1.cleanupGitInstances)();
process.exit(0);
}));
// Web command
program
.command('web')
.description('Start the AIdev web interface for managing tasks and settings')
.option('--dev', 'Run in development mode (for npm link development)')
.option('--proxy-port <port>', 'Port to use for the proxy server', '8888')
.action((options) => __awaiter(void 0, void 0, void 0, function* () {
try {
yield (0, webCommand_1.webCommand)({
dev: options.dev,
proxyPort: Number(options.proxyPort)
});
}
catch (error) {
if (error instanceof Error) {
(0, logger_1.logError)(error.message);
}
else {
(0, logger_1.logError)(String(error));
}
}
}));
// Proxy command
program
.command('proxy <action>')
.description('Start or stop the container management proxy server')
.option('--port <port>', 'Port to use for the proxy server', '8888')
.action((action, cmdObject) => __awaiter(void 0, void 0, void 0, function* () {
if (action !== 'start' && action !== 'stop') {
(0, logger_1.logError)('Action must be either "start" or "stop"');
return;
}
try {
yield (0, proxyCommand_1.proxyCommand)({
action,
port: Number(cmdObject.port)
});
}
catch (error) {
if (error instanceof Error) {
(0, logger_1.logError)(error.message);
}
else {
(0, logger_1.logError)(String(error));
}
}
}));
// Container commands
const container = program
.command('container')
.description('Manage AI development containers');
container
.command('start <name>')
.description('Start a container with the given name')
.option('-t, --type <type>', 'Container type to use (code, learn, plan, or web). If not specified, uses the container name as the type')
.option('--path <path>', 'Change the current working directory', process.cwd())
.option('--proxy-port <port>', 'Port to use for the proxy server', '8888')
.option('--json', 'Log as JSON')
.action((name, cmdObject) => __awaiter(void 0, void 0, void 0, function* () {
global.log_as_json = !!cmdObject.json;
const validTypes = ['code', 'learn', 'plan', 'web'];
if (cmdObject.type && !validTypes.includes(cmdObject.type)) {
(0, logger_1.logError)(`Container type must be one of: ${validTypes.join(', ')}`);
return;
}
try {
yield (0, containerStartCommand_1.containerStartCommand)({
name,
type: cmdObject.type,
path: cmdObject.path,
proxyPort: Number(cmdObject.proxyPort)
});
}
catch (error) {
if (error instanceof Error) {
(0, logger_1.logError)(error.message);
}
else {
(0, logger_1.logError)(String(error));
}
}
yield (0, getGitInstance_1.cleanupGitInstances)();
process.exit(0);
}));
container
.command('status [name]')
.option('--path <path>', 'Change the current working directory', process.cwd())
.description('Show status of development containers (or all if no name specified)')
.option('--json', 'Log as JSON')
.action((name_1, ...args_1) => __awaiter(void 0, [name_1, ...args_1], void 0, function* (name, cmdObject = {}) {
global.log_as_json = !!cmdObject.json;
try {
yield (0, containerStatusCommand_1.containerStatusCommand)({
name
});
}
catch (error) {
if (error instanceof Error) {
(0, logger_1.logError)(error.message);
}
else {
(0, logger_1.logError)(String(error));
}
}
yield (0, getGitInstance_1.cleanupGitInstances)();
process.exit(0);
}));
container
.command('logs <name>')
.description('View logs from a development container')
.option('-n, --lines <number>', 'Number of lines to show', '50')
.option('--path <path>', 'Change the current working directory', process.cwd())
.option('--json', 'Log as JSON')
.action((name, cmdObject) => __awaiter(void 0, void 0, void 0, function* () {
try {
global.log_as_json = !!cmdObject.json;
yield (0, containerLogsCommand_1.containerLogsCommand)({
name,
lines: parseInt(cmdObject.lines, 10),
});
}
catch (error) {
if (error instanceof Error) {
(0, logger_1.logError)(error.message);
}
else {
(0, logger_1.logError)(String(error));
}
}
}));
container
.command('stop <name>')
.description('Stop a development container')
.option('-c, --clean', 'Remove the container after stopping')
.option('--path <path>', 'Change the current working directory', process.cwd())
.option('--json', 'Log as JSON')
.action((name, options) => __awaiter(void 0, void 0, void 0, function* () {
try {
global.log_as_json = !!options.json;
yield (0, containerStopCommand_1.containerStopCommand)({
name,
clean: options.clean
});
}
catch (error) {
if (error instanceof Error) {
(0, logger_1.logError)(error.message);
}
else {
(0, logger_1.logError)(String(error));
}
}
yield (0, getGitInstance_1.cleanupGitInstances)();
process.exit(0);
}));
container
.command('rebuild <name>')
.description('Rebuild a container')
.option('--path <path>', 'Change the current working directory', process.cwd())
.option('--json', 'Log as JSON')
.action((name, options) => __awaiter(void 0, void 0, void 0, function* () {
try {
global.log_as_json = !!options.json;
const containerName = (0, getContainerName_1.getContainerName)(name);
// Check if container exists
const status = yield (0, getContainerStatus_1.getContainerStatus)(containerName);
if (!status) {
(0, logger_1.log)(`Container ${containerName} not found`, 'error');
throw new Error(`Container ${containerName} does not exist`);
}
(0, logger_1.log)(`Rebuilding container ${name}...`, 'info');
yield (0, containerStopCommand_1.containerStopCommand)({ name, clean: true });
yield (0, containerStartCommand_1.containerStartCommand)({ name });
}
catch (error) {
if (error instanceof Error) {
(0, logger_1.logError)(error.message);
}
else {
(0, logger_1.logError)(String(error));
}
}
yield (0, getGitInstance_1.cleanupGitInstances)();
process.exit(0);
}));
container
.command('open <name>')
.description('Open a bash shell in a running container')
.action((name) => __awaiter(void 0, void 0, void 0, function* () {
try {
yield (0, containerOpenCommand_1.containerOpenCommand)({ name });
}
catch (error) {
if (error instanceof Error) {
(0, logger_1.logError)(error.message);
}
else {
(0, logger_1.logError)(String(error));
}
}
}));
program.parse(process.argv);
//# sourceMappingURL=index.js.map