Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
4f2319c
Commit unstaged plans
ryaneggz Apr 1, 2026
8df2a27
feat: add plan for openharness CLI built on Pi SDK (#17)
ryaneggz Apr 2, 2026
1c8a630
feat: implement openharness CLI built on Pi Agent SDK (#17)
ryaneggz Apr 2, 2026
149ad8a
feat: custom --help with openharness branding and sandbox commands se…
ryaneggz Apr 2, 2026
02f4272
feat: add CLI subcommands, remove Makefile (#17)
ryaneggz Apr 2, 2026
f1838ad
feat: rebrand banner to Open Harness
ryaneggz Apr 2, 2026
1a19453
feat: rename .pi/ to .openharness/ across workspace
ryaneggz Apr 2, 2026
e54d8cd
feat: split openharness into core agent + @openharness/sandbox package
ryaneggz Apr 2, 2026
d63885a
feat: update plan for core agent + sandbox package split
ryaneggz Apr 2, 2026
c79886d
commit plans
ryaneggz Apr 2, 2026
b937433
Adds orchestrator dev container
ryaneggz Apr 4, 2026
c4fc131
Fix pipeline
ryaneggz Apr 4, 2026
a573d4d
Merge pull request #18 from ryaneggz/feat/cli
ryaneggz Apr 4, 2026
9c25b89
feat: add Claude Code and Codex CLI to devcontainer
ryaneggz Apr 4, 2026
65cafee
feat: fix devcontainer auth, restructure .openharness config to proje…
ryaneggz Apr 4, 2026
6277067
Update make -> openharness in repo
ryaneggz Apr 5, 2026
f2722a8
Lock down w/ settings.json
ryaneggz Apr 5, 2026
7c1ca5b
feat: cloudflared in setup, compose override config, provision skill …
ryaneggz Apr 7, 2026
1e5224f
feat: add root package.json with npm workspaces and setup script
ryaneggz Apr 7, 2026
f0727ca
task: update quickstart to use npm run setup
ryaneggz Apr 7, 2026
9dc3fb7
task: update workspace template to OpenClaw 7-file structure
ryaneggz Apr 7, 2026
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
49 changes: 49 additions & 0 deletions .claude/plans/curious-rolling-rabin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Fix Claude CLI Auth in Dev Container

## Context
When using VS Code "Reopen in Container", Claude CLI can't authenticate because:
1. `~/.claude/` (auth tokens) lives in ephemeral container filesystem β€” lost on rebuild
2. OAuth callback port isn't forwarded, so re-auth can't complete either

When "attaching" to a running container it works because the container persists and auth tokens survive in memory.

## Plan

### 1. Mount host `~/.claude` into the container
**File:** `.devcontainer/docker-compose.yml`

Add a volume mount so auth tokens persist across container rebuilds:
```yaml
volumes:
- ..:/home/orchestrator/project
- /var/run/docker.sock:/var/run/docker.sock
- ~/.claude:/home/orchestrator/.claude # <-- add this
```

### 2. Set bypass permissions as the default
**File:** `.devcontainer/docker-compose.yml`

Add environment variable:
```yaml
environment:
- CLAUDE_DANGEROUSLY_SKIP_PERMISSIONS=true
```

### 3. Ensure correct ownership in entrypoint
**File:** `.devcontainer/entrypoint.sh`

Add a `chown` for the mounted `.claude` directory so the `orchestrator` user can read/write auth tokens:
```bash
if [ -d /home/orchestrator/.claude ]; then
chown -R orchestrator:orchestrator /home/orchestrator/.claude 2>/dev/null || true
fi
```

## Files to modify
- `.devcontainer/docker-compose.yml` β€” volume mount + env var
- `.devcontainer/entrypoint.sh` β€” chown for mounted auth dir

## Verification
1. `docker compose -f .devcontainer/docker-compose.yml down`
2. `docker compose -f .devcontainer/docker-compose.yml up -d --build`
3. VS Code "Reopen in Container" β†’ open terminal β†’ `claude` should authenticate using host tokens
264 changes: 264 additions & 0 deletions .claude/plans/jolly-questing-shell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
# Plan: Split openharness into Core Agent + Sandbox Package

## Context

`openharness` currently bundles all 11 sandbox management tools directly into the CLI. Users who just want the agent (no Docker, no sandboxing) get sandbox code they don't need. Splitting into a core agent and a separate sandbox package lets `openharness` be installed standalone, with sandboxing added via `openharness install @openharness/sandbox`.

## Architecture

```
BEFORE: AFTER:
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ openharness CLI β”‚ β”‚ openharness CLI β”‚
β”‚ β”œβ”€ Pi SDK β”‚ β”‚ β”œβ”€ Pi SDK β”‚
β”‚ β”œβ”€ 11 sandbox tools β”‚ β”‚ β”œβ”€ banner extension β”‚
β”‚ β”œβ”€ banner extension β”‚ β”‚ └─ (no sandbox) β”‚
β”‚ └─ slash commands β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ @openharness/sandbox β”‚ ← Pi package
β”‚ β”œβ”€ 11 sandbox tools β”‚
β”‚ β”œβ”€ slash commands β”‚
β”‚ β”œβ”€ lib/ (config, β”‚
β”‚ β”‚ docker, exec) β”‚
β”‚ └─ CLI subcommands β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```

**User flow:**
```bash
# Install core agent
npm install -g openharness
openharness # AI agent, no sandbox tools

# Add sandboxing
openharness install @openharness/sandbox
openharness list # now works
openharness quickstart my-agent # now works
```

## File Structure (after split)

```
cli/ # openharness core agent
package.json # name: openharness, NO sandbox deps
src/
index.ts # entry point: subcommand dispatch β†’ package, else Pi main()
extension.ts # core extension: banner only (no sandbox tools)
# tests, config files stay

packages/
sandbox/ # @openharness/sandbox β€” Pi package
package.json # name: @openharness/sandbox, keywords: ["pi-package"]
tsconfig.json
extensions/
sandbox.ts # Pi extension: registers tools + slash commands
src/
tools/ # MOVED from cli/src/tools/
list.ts
quickstart.ts
build.ts
rebuild.ts
run.ts
shell.ts
stop.ts
clean.ts
push.ts
heartbeat.ts
worktree.ts
index.ts
lib/ # MOVED from cli/src/lib/
config.ts
docker.ts
exec.ts
__tests__/ # MOVED from cli/src/__tests__/
config.test.ts
docker.test.ts
tools.test.ts
```

## Key Design Decisions

### Sandbox package as a Pi package

`packages/sandbox/package.json`:
```json
{
"name": "@openharness/sandbox",
"version": "0.1.0",
"keywords": ["pi-package"],
"pi": {
"extensions": ["./extensions"]
},
"dependencies": {
"@mariozechner/pi-coding-agent": "latest",
"@sinclair/typebox": "^0.34.0"
}
}
```

Pi auto-discovers `extensions/sandbox.ts` and loads it. The extension registers all 11 tools + slash commands β€” identical to current `cli/src/extension.ts`.

### CLI subcommand dispatch with optional package

The core `cli/src/index.ts` changes:
- Subcommands (`list`, `quickstart`, etc.) try to dynamically import from the sandbox package
- If the package isn't installed, print a helpful error: `"Sandbox tools not installed. Run: openharness install @openharness/sandbox"`
- Agent mode (no subcommand) just forwards to Pi `main()` β€” sandbox tools appear automatically if the package is installed

```typescript
// cli/src/index.ts β€” subcommand dispatch
async function runSubcommand(command: string, args: string[]) {
try {
const sandbox = await import("@openharness/sandbox");
// execute tool...
} catch {
console.error(`Sandbox tools not installed. Run: openharness install @openharness/sandbox`);
process.exit(1);
}
}
```

### Core extension (banner only)

`cli/src/extension.ts` becomes lean β€” just the banner extension (or loads it from `workspace/.openharness/extensions/`). No sandbox tool registration.

### Sandbox package exports

`packages/sandbox/src/index.ts` exports everything needed for CLI subcommand dispatch:

```typescript
export { sandboxTools } from "./tools/index.js";
export { listTool, quickstartTool, buildTool, ... } from "./tools/index.js";
export { SandboxConfig } from "./lib/config.js";
```

## Branch Strategy

All work continues on `feat/cli` (current branch) β€” it's the only branch with CLI changes. No new branch needed. PR #18 (`feat/cli` β†’ `development`) will include the full split.

## Implementation Order

### Phase 1: Create sandbox package
1. Create `packages/sandbox/package.json` with Pi package manifest
2. Create `packages/sandbox/tsconfig.json`
3. Move `cli/src/tools/` β†’ `packages/sandbox/src/tools/`
4. Move `cli/src/lib/` β†’ `packages/sandbox/src/lib/`
5. Move `cli/src/__tests__/` β†’ `packages/sandbox/src/__tests__/`
6. Create `packages/sandbox/extensions/sandbox.ts` β€” the Pi extension (from current `cli/src/extension.ts`)
7. Create `packages/sandbox/src/index.ts` β€” barrel exports
8. Add vitest, eslint, prettier configs to sandbox package

### Phase 2: Slim down core CLI
9. Update `cli/src/index.ts` β€” dynamic import for subcommands with fallback error
10. Update `cli/src/extension.ts` β€” remove sandbox tool registration, keep banner only
11. Remove `cli/src/tools/`, `cli/src/lib/` (moved to sandbox package)
12. Remove sandbox-related tests from `cli/src/__tests__/`
13. Update `cli/package.json` β€” remove `@sinclair/typebox` dep (only needed by sandbox)

### Phase 3: Wire up local development
14. Add `@openharness/sandbox` as optional dependency or use npm workspaces
15. For local dev: `cd packages/sandbox && npm link` then `openharness install ./packages/sandbox`
16. Update `.gitignore` for `packages/sandbox/dist/`, `packages/sandbox/node_modules/`

## AI Smoke Test (automated, run by Claude)

```
1. Build both packages
cd packages/sandbox && npm install && npm run build
cd cli && npm install && npm run build

2. CI checks β€” both packages
cd packages/sandbox && npm run ci # lint + format + vitest (42 tests)
cd cli && npm run ci # lint + format + vitest (core tests)

3. Verify sandbox package structure
- package.json has "keywords": ["pi-package"] and "pi" manifest
- extensions/sandbox.ts exports default function
- src/index.ts barrel-exports all tools
- All 11 tools importable from @openharness/sandbox

4. Verify core CLI without sandbox
node cli/dist/index.js --version # prints openharness version
node cli/dist/index.js --help # shows Commands section
node cli/dist/index.js list # prints install prompt, exits 1

5. Install sandbox package locally
openharness install ./packages/sandbox

6. Verify core CLI with sandbox
node cli/dist/index.js list # shows containers + worktrees
node cli/dist/index.js --help # same output

7. Verify extension module shape
node -e "import('./packages/sandbox/dist/index.js').then(m => {
console.log('Tools:', m.sandboxTools.length);
console.log('All have execute:', m.sandboxTools.every(t => typeof t.execute === 'function'));
})"

8. Pre-commit hook
git add . && git commit -m "test" # lint-staged runs
```

## Human Smoke Test (manual, requires Docker + interactive terminal)

### Without sandbox package

```bash
# 1. Fresh install
cd cli && npm install && npm run build && npm link

# 2. Verify standalone agent works
openharness --version # β†’ openharness 0.1.0 (pi X.Y.Z)
openharness --help # β†’ Commands section visible

# 3. Sandbox commands fail gracefully
openharness list # β†’ "Sandbox tools not installed..."
openharness quickstart foo # β†’ same error

# 4. Agent mode works (TUI)
openharness # β†’ Pi TUI launches
# Verify: no sandbox tools in tool list
# Verify: banner shows "Open Harness"
# Ctrl-C to exit
```

### With sandbox package

```bash
# 5. Install sandbox package
openharness install ./packages/sandbox

# 6. CLI subcommands work
openharness list # β†’ shows containers + worktrees

# 7. Full lifecycle (requires Docker)
openharness quickstart test-smoke --base-branch main
openharness list # β†’ test-smoke appears
openharness shell test-smoke # β†’ bash shell opens, exit
openharness heartbeat status test-smoke
openharness stop test-smoke
openharness clean test-smoke # β†’ container + worktree removed
openharness list # β†’ test-smoke gone

# 8. Agent mode with sandbox tools
openharness
# Inside TUI:
# /list β†’ works
# "list all running sandboxes" β†’ LLM calls sandbox_list tool
# Ctrl-C to exit

# 9. Uninstall sandbox
openharness remove @openharness/sandbox
openharness list # β†’ back to "not installed" error
```

## Files Modified
- `cli/src/index.ts` β€” dynamic import for subcommands
- `cli/src/extension.ts` β€” strip sandbox tools, keep banner
- `cli/package.json` β€” remove @sinclair/typebox
- `cli/src/tools/` β€” DELETE (moved)
- `cli/src/lib/` β€” DELETE (moved)
- `cli/src/__tests__/` β€” remove sandbox tests
- `packages/sandbox/` β€” ALL NEW
- `.gitignore` β€” add packages/sandbox artifacts
Loading
Loading