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
72 changes: 68 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,79 @@
# CoDev CLI
# CoDev

A CLI tool to install and configure coding agents (Claude Code, OpenCode).
CoDev — AI Coding Agent Hub. Install, configure, and launch AI coding agents (Claude Code, OpenCode) from a single CLI.

## Install

```bash
npm install -g codev-cli
npm install -g codev-ai
```

Then run:

```bash
codev
codev install
```

## Commands

| Command | What it does |
| -------------------------- | ----------------------------------------------------- |
| `codev` | Show version and help |
| `codev --help` | Show version and help |
| `codev install` | Install and configure AI coding agents |
| `codev claude` | Run the `claude` CLI (forwards remaining arguments) |
| `codev claude --restore` | Restore `~/.claude/` from `~/.claude.backup/` |
| `codev opencode` | Run the `opencode` CLI (forwards remaining arguments) |
| `codev opencode --restore` | Restore `~/.config/opencode/` from its backup |
| `codev logout` | Sign out of SSO |

## Restoring a previous configuration

Before writing its own config, `codev` backs up the directory it would
replace. The backup set depends on which agents you chose in Step 1:

| Selection | Backed up |
| ----------- | --------------------- |
| Claude Code | `~/.claude/` |
| OpenCode | `~/.config/opencode/` |

If you do **not** select Claude Code, `codev` does not touch `~/.claude/` or
`~/.claude.json` at all. When Claude Code **is** selected, `~/.claude.json` is
read in-place and `"hasCompletedOnboarding": true` is added if missing — the
file is not backed up (only that single flag is touched).

`settings.json` and `opencode.json` are **replaced** (not merged), so any keys
you had before live only in the directory backup.

### Existing backups

If a backup already exists from a prior `codev` run (`*.backup`), `codev`
pauses in Step 3 and asks whether to overwrite it — default is **No**, which
keeps the existing backup (usually your original, pre-`codev` state). Answer
`y` to replace it with your current contents.

### Restore

Use the built-in restore shortcut:

```bash
codev claude --restore
codev opencode --restore
```

Each command removes the active directory and renames the corresponding
`*.backup` back into place. If no backup exists, the command prints a
"No backup found" message and exits with code 1.

Or do it manually:

```bash
# Claude Code
rm -rf ~/.claude && mv ~/.claude.backup ~/.claude

# OpenCode
rm -rf ~/.config/opencode && mv ~/.config/opencode.backup ~/.config/opencode
```

The restore command for each backup is also printed in the CLI after each
tool is configured.
45 changes: 30 additions & 15 deletions codev-cli/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# codev-ai
# CoDev

CoDev — AI Coding Agent Hub. Install, configure, and launch AI coding agents (Claude Code, OpenCode) from a single CLI.

Expand All @@ -16,24 +16,26 @@ codev install

## Commands

| Command | What it does |
| --------------- | --------------------------------------------------- |
| `codev` | Show version and help |
| `codev --help` | Show version and help |
| `codev install` | Install and configure AI coding agents |
| `codev claude` | Run the `claude` CLI (forwards remaining arguments) |
| `codev opencode`| Run the `opencode` CLI (forwards remaining arguments) |
| `codev logout` | Sign out of SSO |
| Command | What it does |
| -------------------------- | ----------------------------------------------------- |
| `codev` | Show version and help |
| `codev --help` | Show version and help |
| `codev install` | Install and configure AI coding agents |
| `codev claude` | Run the `claude` CLI (forwards remaining arguments) |
| `codev claude --restore` | Restore `~/.claude/` from `~/.claude.backup/` |
| `codev opencode` | Run the `opencode` CLI (forwards remaining arguments) |
| `codev opencode --restore` | Restore `~/.config/opencode/` from its backup |
| `codev logout` | Sign out of SSO |

## Restoring a previous configuration

Before writing its own config, `codev` backs up the directory it would
replace. The backup set depends on which agents you chose in Step 1:

| Selection | Backed up |
| ------------ | ---------------------- |
| Claude Code | `~/.claude/` |
| OpenCode | `~/.config/opencode/` |
| Selection | Backed up |
| ----------- | --------------------- |
| Claude Code | `~/.claude/` |
| OpenCode | `~/.config/opencode/` |

If you do **not** select Claude Code, `codev` does not touch `~/.claude/` or
`~/.claude.json` at all. When Claude Code **is** selected, `~/.claude.json` is
Expand All @@ -52,6 +54,19 @@ keeps the existing backup (usually your original, pre-`codev` state). Answer

### Restore

Use the built-in restore shortcut:

```bash
codev claude --restore
codev opencode --restore
```

Each command removes the active directory and renames the corresponding
`*.backup` back into place. If no backup exists, the command prints a
"No backup found" message and exits with code 1.

Or do it manually:

```bash
# Claude Code
rm -rf ~/.claude && mv ~/.claude.backup ~/.claude
Expand All @@ -60,8 +75,8 @@ rm -rf ~/.claude && mv ~/.claude.backup ~/.claude
rm -rf ~/.config/opencode && mv ~/.config/opencode.backup ~/.config/opencode
```

The exact restore command for each backup is also printed in the CLI after
each tool is configured.
The restore command for each backup is also printed in the CLI after each
tool is configured.

## Development

Expand Down
9 changes: 6 additions & 3 deletions codev-cli/src/components/Configure.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ const LABEL: Record<BackupKind, string> = {
"opencode-dir": "OpenCode",
};

const RESTORE_CMD: Record<BackupKind, string> = {
"claude-dir": "codev claude --restore",
"opencode-dir": "codev opencode --restore",
};

function scanConflicts(tools: Tool[]): Conflict[] {
const out: Conflict[] = [];
for (const tool of tools) {
Expand All @@ -43,9 +48,7 @@ function describeResult(r: ConfigureResult): string[] {
const lines = [`Configured ${LABEL[r.kind]}`];
if (r.backupPath) {
lines.push(` Backup: ${r.backupPath}`);
lines.push(
` Restore: rm -rf ${r.sourcePath} && mv ${r.backupPath} ${r.sourcePath}`,
);
lines.push(` Restore: ${RESTORE_CMD[r.kind]}`);
}
return lines;
}
Expand Down
9 changes: 6 additions & 3 deletions codev-cli/src/components/Confirm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ const CONFIG_FILE: Record<Tool, string> = {
opencode: "opencode.json",
};

const RESTORE_CMD: Record<Tool, string> = {
"claude-code": "codev claude --restore",
opencode: "codev opencode --restore",
};

export function Confirm({ tools, onConfirm, readOnly = false }: ConfirmProps) {
useInput(
(input, key) => {
Expand Down Expand Up @@ -47,9 +52,7 @@ export function Confirm({ tools, onConfirm, readOnly = false }: ConfirmProps) {
{` Backup: ${status.sourcePath} → ${status.backupPath}`}
</Text>
)}
<Text>
{` Restore: rm -rf ${status.sourcePath} && mv ${status.backupPath} ${status.sourcePath}`}
</Text>
<Text>{` Restore: ${RESTORE_CMD[tool]}`}</Text>
</Box>
);
})}
Expand Down
22 changes: 11 additions & 11 deletions codev-cli/src/help.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { VERSION } from "@/const.js";

function link(url: string): string {
if (!process.stdout.isTTY) return url;
return `\x1b]8;;${url}\x1b\\${url}\x1b]8;;\x1b\\`;
}

export function printHelp() {
console.log(`CoDev v${VERSION} — AI Coding Agent Hub
console.log(`CoDev — AI Coding Agent Hub

Usage: codev <command> [options]

Usage:
codev install Install and configure AI coding agents
codev claude Run the Claude Code CLI
Docs: ${link("https://code.claude.com/docs/en/cli-reference")}
codev opencode Run the OpenCode CLI
Docs: ${link("https://opencode.ai/docs/cli/")}
codev logout Sign out of SSO
codev --help, -h Show this help
Commands:
install Install and configure AI coding agents
claude Run the Claude Code CLI (${link("https://code.claude.com/docs/en/cli-reference")})
claude --restore Restore ~/.claude from ~/.claude.backup
opencode Run the OpenCode CLI (${link("https://opencode.ai/docs/cli/")})
opencode --restore Restore ~/.config/opencode from ~/.config/opencode.backup
logout Sign out of SSO
--help, -h Show this help
`);
}
7 changes: 7 additions & 0 deletions codev-cli/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { render } from "ink";
import { App } from "@/App.js";
import { logout } from "@/auth.js";
import { printHelp } from "@/help.js";
import { runRestore } from "@/restore.js";
import { runAgent } from "@/run.js";

const [command, ...args] = process.argv.slice(2);
Expand All @@ -28,9 +29,15 @@ switch (command) {
break;
}
case "claude":
if (args[0] === "--restore") {
process.exit(runRestore("claude-code"));
}
process.exit(await runAgent("claude", args));
break;
case "opencode":
if (args[0] === "--restore") {
process.exit(runRestore("opencode"));
}
process.exit(await runAgent("opencode", args));
break;
default:
Expand Down
11 changes: 11 additions & 0 deletions codev-cli/src/restore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { restoreTool, type Tool } from "@/setup.js";

export function runRestore(tool: Tool): number {
const result = restoreTool(tool);
if (result.status === "no-backup") {
console.error(`No backup found at ${result.backupPath}.`);
return 1;
}
console.log(`Restored ${result.sourcePath} from ${result.backupPath}.`);
return 0;
}
24 changes: 24 additions & 0 deletions codev-cli/src/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
existsSync,
mkdirSync,
readFileSync,
renameSync,
rmSync,
writeFileSync,
} from "node:fs";
Expand Down Expand Up @@ -129,6 +130,29 @@ export function configureClaudeCode(
return [{ kind: "claude-dir", sourcePath: dirPath, backupPath: dirBackup }];
}

export type RestoreStatus = "restored" | "no-backup";

export interface RestoreResult {
status: RestoreStatus;
sourcePath: string;
backupPath: string;
}

export function restoreTool(tool: Tool): RestoreResult {
const kind: BackupKind =
tool === "claude-code" ? "claude-dir" : "opencode-dir";
const sourcePath = sourcePathOf(kind);
const backupPath = `${sourcePath}.backup`;

if (!existsSync(backupPath)) {
return { status: "no-backup", sourcePath, backupPath };
}

rmSync(sourcePath, { recursive: true, force: true });
renameSync(backupPath, sourcePath);
return { status: "restored", sourcePath, backupPath };
}

export function configureOpenCode(
apiKey: string,
opts: ConfigureOptions = {},
Expand Down
Loading
Loading