Skip to content
76 changes: 38 additions & 38 deletions cli/DEVELOPMENT.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# Dirac CLI
# ISAAC CLI

The official CLI for Dirac. Run Dirac tasks directly from the terminal with the same underlying functionality as the VS Code extension.
The official CLI for ISAAC. Run ISAAC tasks directly from the terminal with the same underlying functionality as the VS Code extension.

## Features

- **Reuses Core Codebase**: Shares the same Controller, Task, and API handling as the VS Code extension
- **Terminal Output**: Displays Dirac messages directly in your terminal with colored output
- **Terminal Output**: Displays ISAAC messages directly in your terminal with colored output
- **Task History**: Access your task history from the command line
- **Configurable**: Use custom configuration directories and working directories
- **Image Support**: Attach images to your prompts using file paths or inline references
Expand All @@ -14,7 +14,7 @@ The official CLI for Dirac. Run Dirac tasks directly from the terminal with the

- Node.js 20.x or later
- npm or yarn
- The parent Dirac project dependencies installed
- The parent ISAAC project dependencies installed

## Installation

Expand Down Expand Up @@ -43,17 +43,17 @@ npm run cli:link

### Interactive Mode (Default)

When you run `dirac` without any command, it launches an interactive welcome prompt:
When you run `isaac` without any command, it launches an interactive welcome prompt:

```bash
# Launch interactive mode
dirac
isaac

# Or run a task directly
dirac "Create a hello world function in Python"
isaac "Create a hello world function in Python"

# With options
dirac -v --thinking "Analyze this codebase"
isaac -v --thinking "Analyze this codebase"
```

### Commands
Expand All @@ -63,8 +63,8 @@ dirac -v --thinking "Analyze this codebase"
Run a new task with a prompt.

```bash
dirac task "Create a hello world function in Python"
dirac t "Create a hello world function"
isaac task "Create a hello world function in Python"
isaac t "Create a hello world function"
```

**Options:**
Expand All @@ -78,38 +78,38 @@ dirac t "Create a hello world function"
| `-i, --images <paths...>` | Image file paths to include with the task |
| `-v, --verbose` | Show verbose output including reasoning |
| `-c, --cwd <path>` | Working directory for the task |
| `--config <path>` | Path to Dirac configuration directory |
| `--config <path>` | Path to ISAAC configuration directory |
| `-t, --thinking` | Enable extended thinking (1024 token budget) |

**Examples:**

```bash
# Run in plan mode with verbose output
dirac task -p -v "Design a REST API"
isaac task -p -v "Design a REST API"

# Use a specific model with yolo mode
dirac task -m claude-sonnet-4-5-20250929 -y "Refactor this function"
isaac task -m claude-sonnet-4-5-20250929 -y "Refactor this function"

# Include images with your prompt
dirac task -i screenshot.png diagram.jpg "Fix the UI based on these images"
isaac task -i screenshot.png diagram.jpg "Fix the UI based on these images"

# Or use inline image references in the prompt
dirac task "Fix the layout shown in @./screenshot.png"
isaac task "Fix the layout shown in @./screenshot.png"

# Enable extended thinking for complex tasks
dirac task -t "Architect a microservices system"
isaac task -t "Architect a microservices system"

# Specify working directory
dirac task -c /path/to/project "Add unit tests"
isaac task -c /path/to/project "Add unit tests"
```

#### `history` (alias: `h`)

List task history with pagination support.

```bash
dirac history
dirac h
isaac history
isaac h
```

**Options:**
Expand All @@ -118,41 +118,41 @@ dirac h
|--------|-------------|
| `-n, --limit <number>` | Number of tasks to show (default: 10) |
| `-p, --page <number>` | Page number, 1-based (default: 1) |
| `--config <path>` | Path to Dirac configuration directory |
| `--config <path>` | Path to ISAAC configuration directory |

**Examples:**

```bash
# Show last 10 tasks (default)
dirac history
isaac history

# Show 20 tasks
dirac history -n 20
isaac history -n 20

# Show page 2 with 5 tasks per page
dirac history -n 5 -p 2
isaac history -n 5 -p 2
```

#### `config`

Show current configuration including global and workspace state.

```bash
dirac config
isaac config
```

**Options:**

| Option | Description |
|--------|-------------|
| `--config <path>` | Path to Dirac configuration directory |
| `--config <path>` | Path to ISAAC configuration directory |

#### `auth`

Authenticate a provider and configure what model is used.

```bash
dirac auth
isaac auth
```

**Options:**
Expand All @@ -165,22 +165,22 @@ dirac auth
| `-b, --baseurl <url>` | Base URL (optional, only for openai provider) |
| `-v, --verbose` | Show verbose output |
| `-c, --cwd <path>` | Working directory for the task |
| `--config <path>` | Path to Dirac configuration directory |
| `--config <path>` | Path to ISAAC configuration directory |

**Examples:**

```bash
# Interactive authentication
dirac auth
isaac auth

# Quick setup with provider and API key
dirac auth -p anthropic -k sk-ant-xxxxx
isaac auth -p anthropic -k sk-ant-xxxxx

# Full quick setup with model
dirac auth -p openai-native -k sk-xxxxx -m gpt-4o
isaac auth -p openai-native -k sk-xxxxx -m gpt-4o

# OpenAI-compatible provider with custom base URL
dirac auth -p openai -k your-api-key -b https://api.example.com/v1
isaac auth -p openai -k your-api-key -b https://api.example.com/v1
```

### Global Options
Expand All @@ -191,7 +191,7 @@ These options are available for the default command (running a task directly):
|--------|-------------|
| `-v, --verbose` | Show verbose output |
| `-c, --cwd <path>` | Working directory |
| `--config <path>` | Configuration directory |
| `--config <path>` | ISAAC configuration directory |
| `--thinking` | Enable extended thinking (1024 token budget) |

## Development
Expand All @@ -202,11 +202,11 @@ These options are available for the default command (running a task directly):
# 1. Install all dependencies (root, webview-ui, cli)
npm run install:all

# 2. Build and link globally so you can run `dirac` from anywhere
# 2. Build and link globally so you can run `isaac` from anywhere
npm run cli:link

# 3. Test it
dirac --help
isaac --help
```

### Scripts
Expand All @@ -218,8 +218,8 @@ Run these from the repository root:
| `npm run install:all` | Install deps for root, webview-ui, and cli |
| `npm run cli:build` | Generate protos and build CLI |
| `npm run cli:build:production` | Production build (minified) |
| `npm run cli:link` | Build and `npm link` so you can run `dirac` from anywhere |
| `npm run cli:unlink` | Remove the global `dirac` symlink |
| `npm run cli:link` | Build and `npm link` so you can run `isaac` from anywhere |
| `npm run cli:unlink` | Remove the global `isaac` symlink |
| `npm run cli:dev` | Link + watch mode for development |
| `npm run cli:watch` | Watch mode only (no initial build) |
| `npm run cli:test` | Run CLI tests |
Expand All @@ -229,7 +229,7 @@ Run these from the repository root:
1. Run `npm run cli:dev` - this links the CLI globally and starts watch mode
2. Make changes to files in `cli/src/`
3. The build automatically rebuilds on save
4. Test your changes by running `dirac` in another terminal
4. Test your changes by running `isaac` in another terminal
5. When done, run `npm run cli:unlink` to clean up

### Proto Generation
Expand Down Expand Up @@ -352,7 +352,7 @@ npm run protos
npm run cli:build
```

### "command not found: dirac"
### "command not found: isaac"

The CLI isn't linked globally. Run:

Expand Down
1 change: 1 addition & 0 deletions cli/src/context/StdinContext.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Text } from "ink"
import { render } from "ink-testing-library"
// biome-ignore lint/correctness/noUnusedImports: required in scope for the classic JSX runtime (tsconfig jsx: "react")
import React from "react"
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"
import { StdinProvider } from "./StdinContext"
Expand Down
2 changes: 1 addition & 1 deletion cli/src/context/StdinContext.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* Context for tracking stdin raw mode support
* Used to conditionally disable input handling when stdin doesn't support raw mode
* (e.g., when input is piped: echo "..." | diracdev)
* (e.g., when input is piped: echo "..." | isaac)
*/

import { useStdin } from "ink"
Expand Down
10 changes: 5 additions & 5 deletions cli/src/exports.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
/**
* Dirac Library Exports
* ISAAC Library Exports
*
* This file exports the public API for programmatic use of Dirac.
* Use these classes and types to embed Dirac into your applications.
* This file exports the public API for programmatic use of ISAAC.
* Use these classes and types to embed ISAAC into your applications.
*
* @example
* ```typescript
* import { DiracAgent } from "dirac"
* import { DiracAgent } from "isaac-cli"
*
* const agent = new DiracAgent()
* await agent.initialize({ clientCapabilities: {} })
* const session = await agent.newSession({ cwd: process.cwd() })
* ```
* @module dirac
* @module isaac-cli
*/

export { DiracAgent } from "./agent/DiracAgent.js"
Expand Down
20 changes: 1 addition & 19 deletions cli/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ describe("CLI Commands", () => {
beforeEach(() => {
// Create a fresh program instance for each test
program = new Command()
program.name("dirac").description("Dirac CLI - AI coding assistant").version("0.0.0")
program.name("isaac").description("ISAAC — Intelligence Souveraine Ailiance Agent Codeur").version("0.0.0")
program.enablePositionalOptions()

// Define commands matching index.ts
Expand Down Expand Up @@ -67,11 +67,6 @@ describe("CLI Commands", () => {
.option("--config <path>", "Configuration directory")
.action(() => {})

program
.command("kanban")
.description("Run npx kanban --agent dirac")
.action(() => {})

// Default command for interactive mode
program
.argument("[prompt]", "Task prompt")
Expand All @@ -86,7 +81,6 @@ describe("CLI Commands", () => {
.option("--auto-condense", "Enable AI-powered context compaction instead of mechanical truncation")
.option("--hooks-dir <path>", "Additional hooks directory")
.option("--auto-approve-all", "Enable auto-approve all")
.option("--kanban", "Run npx kanban --agent dirac")
.action(() => {})
})

Expand Down Expand Up @@ -280,13 +274,6 @@ describe("CLI Commands", () => {
})
})

describe("kanban command", () => {
it("should parse kanban command", () => {
const args = ["node", "cli", "kanban"]
program.parse(args)
})
})

describe("auth command", () => {
it("should parse auth command", () => {
const args = ["node", "cli", "auth"]
Expand Down Expand Up @@ -382,10 +369,6 @@ describe("CLI Commands", () => {
expect(program.opts().autoApproveAll).toBe(true)
})

it("should parse --kanban flag", () => {
program.parse(["node", "cli", "--kanban"])
expect(program.opts().kanban).toBe(true)
})
})

describe("command structure", () => {
Expand All @@ -395,7 +378,6 @@ describe("CLI Commands", () => {
expect(commandNames).toContain("history")
expect(commandNames).toContain("config")
expect(commandNames).toContain("auth")
expect(commandNames).toContain("kanban")
})

it("should have correct aliases", () => {
Expand Down
2 changes: 1 addition & 1 deletion cli/src/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export async function initializeCli(options: InitOptions): Promise<CliContext> {
const controller = webview.controller as any

await telemetryService.captureExtensionActivated()
await telemetryService.captureHostEvent("dirac_cli", "initialized")
await telemetryService.captureHostEvent("isaac_cli", "initialized")

// =============== Symbol Index Service ===============
// Initialize symbol index for the project in background
Expand Down
4 changes: 2 additions & 2 deletions cli/src/utils/piped.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import * as fs from "node:fs"
* Read piped input from stdin (non-blocking)
*
* This function is designed to work with piped input, including chained commands:
* git diff | dirac 'explain' | dirac 'summarize'
* git diff | isaac 'explain' | isaac 'summarize'
*
* The challenge is that when chaining dirac commands, the first command may take
* The challenge is that when chaining isaac commands, the first command may take
* several seconds to complete, so we can't use a short timeout. Instead, we wait
* for EOF which signals that the previous command has finished writing.
*/
Expand Down
34 changes: 34 additions & 0 deletions cli/tests/tracing/JsonlTracer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -339,4 +339,38 @@ describe("JsonlTracer", () => {
tracer.appendTurn({ phase: "execute" })
tracer.close("aborted", 1)
})

it("scrubs inline credentials from gateway_url before writing meta.json", () => {
const tracer = new JsonlTracer("task-metascrub", tmpDir)
tracer.writeMeta({
task: "task-metascrub",
mode: "act",
approval_mode: "manual",
ailiance_agent_version: "0.1.0",
gateway_url: "http://admin:hunter2@studio:9300",
})
const raw = fs.readFileSync(path.join(tmpDir, TRACING_DIR_NAME, "task-metascrub", "meta.json"), "utf8")
expect(raw).not.toContain("hunter2")
expect(raw).toContain("[REDACTED]")
// in-memory meta stays intact for later merges (only the on-disk copy is scrubbed)
tracer.mergeStats({ turns: 1 })
const after = JSON.parse(fs.readFileSync(path.join(tmpDir, TRACING_DIR_NAME, "task-metascrub", "meta.json"), "utf8"))
expect(after.stats.turns).toBe(1)
})

it("scrubs secrets that surface in error strings", () => {
const tracer = new JsonlTracer("task-errscrub", tmpDir)
tracer.writeMeta({
task: "task-errscrub",
mode: "act",
approval_mode: "manual",
ailiance_agent_version: "0.1.0",
gateway_url: "http://studio:9300",
})
tracer.recordPlannerTurn("nope", 3, ["auth failed for token=supersecretvalue"])
const line = JSON.parse(
fs.readFileSync(path.join(tmpDir, TRACING_DIR_NAME, "task-errscrub", "trace.jsonl"), "utf8").trim().split("\n")[0],
)
expect(JSON.stringify(line.errors)).not.toContain("supersecretvalue")
})
})
Loading
Loading