diff --git a/CHANGELOG.md b/CHANGELOG.md index f16e81a..5067424 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,8 +9,15 @@ Update policy and release automation live in [`.claude/rules/git.md`](.claude/ru ## [Unreleased] ### Added +- `docs/connecting.md` — new runbook documenting the VSCode Attach + port-forwarding workflow for remote dev servers. Covers the "machine unreachable" debugging pattern: VS Code Remote SSH's automatic port-forwarding, manual fallback for direct-terminal users, and the `localhost:` prefix convention for accessing services on the Docker host. Referenced from `intro.md`, `quickstart.md`, `harnesses/overview.md`, `harnesses/t3code.md`, and `integrations/slack.md`. ([#300](https://github.com/ryaneggz/open-harness/issues/300)) +- **Agents catalog**: new `docs/agents/` section (renamed from `docs/agents/` → `docs/harnesses/` after initial creation to match sidebar label). Includes `_category_.json` organizing the section, `overview.md` as the hub page, and five per-branch playbook pages (`portfolio-mgr.md`, `blog-writer.md`, `linkedin-ghostwriter.md`, `dc-designer.md`, `uat-tester.md`), each documenting use-case, harness selection, auth flow, and tmux launch pattern. ([#300](https://github.com/ryaneggz/open-harness/issues/300)) - `.devcontainer/docker-compose.yml` publishes container port 1455 to the host loopback (`127.0.0.1:1455:1455`) for the **Pi harness** login flow, whose OAuth callback binds `http://localhost:1455`. With the port on the host loopback, VS Code Remote SSH's automatic port-forwarding carries the redirect back to the laptop and Pi login completes out of the box; direct-terminal (plain `ssh`) users still need to forward the port manually with `ssh -L 1455:localhost:1455 user@host`. The Codex CLI is unaffected — it has its own headless path via `codex login --device-auth`. ([#287](https://github.com/ryaneggz/open-harness/issues/287)) +### Changed +- Renamed `docs/agents/` folder to `docs/harnesses/` so the folder name matches the sidebar label; added `@docusaurus/plugin-client-redirects` to `apps/docs/` with seven redirects from `/docs/agents/*` → `/docs/harnesses/*` preserving URLs from the initial agents-catalog PR. ([#300](https://github.com/ryaneggz/open-harness/issues/300)) +- Cross-linked `docs/connecting.md` from `intro.md`, `quickstart.md`, `harnesses/overview.md`, `harnesses/t3code.md`, and `integrations/slack.md` so the VSCode attach pattern is discoverable from each major entry point. ([#300](https://github.com/ryaneggz/open-harness/issues/300)) +- Fixed `apps/README.md` script-name drift: `pnpm --filter @openharness/docs dev` → `pnpm --filter @openharness/docs start` to match the actual `start` script in `apps/docs/package.json`. ([#300](https://github.com/ryaneggz/open-harness/issues/300)) + ### Removed - In-tree docker-compose overlays under `.devcontainer/` except `docker-compose.postgres.yml`: the five host-auth overlays (`claude-host.yml`, `codex-host.yml`, `opencode-host.yml`, `pi-host.yml`, `deepagents-host.yml`), the three SSH overlays (`ssh.yml`, `ssh-generate.yml`, `sshd.yml`), `git.yml`, and `cloudflared.yml`. The host-auth overlays existed to share host agent credentials when the host UID matched the sandbox's UID 1000; `.devcontainer/entrypoint.sh` now syncs the sandbox user's UID/GID to the bind-mounted repo's owner on boot (replacing the host-group-membership workaround), so files written in either direction stay readable without per-agent overlays. Agent auth still persists in named volumes (`claude-auth`, `codex-auth`, `pi-auth`, `opencode-auth`, `deepagents-auth`, `cloudflared-auth`, `gh-config`); `gh auth login` covers git over HTTPS without per-overlay setup. Postgres remains opt-in via `docker-compose.postgres.yml`. Removed companion infrastructure: `install/cloudflared-tunnel.sh`, the `/cloudflared-tunnel` skill, the `*_HOST_BIND_MOUNT` / `SANDBOX_PASSWORD` / `INSTALL_BROWSER` / `INSTALL_CLOUDFLARED` / `GIT_COMMON_DIR` / `HOST_SSH_DIR` / `HOST_*_DIR` env plumbing in `.devcontainer/entrypoint.sh`, and the host-UID overlay-stripping block in `scripts/install.sh`. Deleted doc: `docs/integrations/cloudflare.md`. The `composeOverrides[]` mechanism in `config.json` is retained so downstream packs (Pi extensions, BYO harness packs) can still register their own overlays. ([#283](https://github.com/ryaneggz/open-harness/issues/283)) - Orphaned `.pi/onboard-steps/slack.ts` (and the empty `.pi/onboard-steps/` directory). The wizard logic lives in `apps/oh/src/config/slack.ts` now, with the bugs from the original mifune port fixed (correct tmux session name `agent-pi`, real `pi` launch path, token-prefix validation, allowlist-ID validation, atomic env-file merge). ([#281](https://github.com/ryaneggz/open-harness/issues/281)) @@ -18,7 +25,7 @@ Update policy and release automation live in [`.claude/rules/git.md`](.claude/ru - `.pi/overlays/docker-compose.slack.yml` and all references to it. The in-tree Pi extension at `.pi/extensions/slack/` reads `SLACK_APP_TOKEN` / `SLACK_BOT_TOKEN` directly from `process.env` at `session_start`, so the compose-time wiring overlay is redundant. `docs/integrations/slack.md`, `README.md`, `docs/architecture/container-runtime.md`, `docs/guide/bring-your-own-harness.md`, `.devcontainer/docker-compose.yml`, and `.devcontainer/.example.env` no longer reference the overlay; `.pi/overlays/` directory removed. Source of truth for Slack env wiring is `.pi/extensions/slack/README.md`. ### Changed -- Docs frame the orchestrator-via-VS-Code-attach as the canonical workflow. `docs/quickstart.md` "Enter the sandbox" now leads with the VS Code Dev Containers attach flow (Command Palette → `Dev Containers: Attach to Running Container...` → `openharness`), since it works identically on localhost and over Remote-SSH; `make shell` is demoted to a terminal fallback. `docs/intro.md` "How it works" names the **orchestrator** (the agent session attached at the project root that owns git, lifecycle, and edits, drives other containers over the Docker socket, and only needs host-shell intervention for things the running container can't do for itself — typically a new bind-mounted volume), frames a second sandbox as opt-in for isolation only, and closes with a Mermaid architecture diagram (orchestrator at the center of the sandbox subgraph, tmux session row, `docker.sock` → optional second sandbox, external GitHub / LLM-provider edges, host bind-mount). `docs/agents/overview.md` replaces the redundant "Running an agent in tmux" section with "Default surfaces" (Pi+Slack, T3 Code, docs app on `:3000`), each as a named tmux session, with explicit guidance to open the two browser surfaces in VS Code's Simple Browser (`Ctrl+Shift+P` → `Simple Browser: Show`; `Cmd+Shift+P` on macOS) so the live UI sits next to the code being edited. Documented tmux sessions renamed `agent-pi` → `harness-pi` and `agent-t3code` → `harness-t3code` in `docs/agents/overview.md` only — the `oh config slack` wizard at `apps/oh/src/config/slack.ts` still creates `agent-pi`, and `context/rules/sandbox-processes.md` still defines the `agent-` prefix; full rename across wizard code, rule, and sibling docs (`docs/intro.md`, `docs/integrations/slack.md`, `docs/agents/t3code.md`, `.pi/extensions/slack/README.md`) will follow. ([#302](https://github.com/ryaneggz/open-harness/issues/302)) +- Docs frame the orchestrator-via-VS-Code-attach as the canonical workflow. `docs/quickstart.md` "Enter the sandbox" now leads with the VS Code Dev Containers attach flow (Command Palette → `Dev Containers: Attach to Running Container...` → `openharness`), since it works identically on localhost and over Remote-SSH; `make shell` is demoted to a terminal fallback. `docs/intro.md` "How it works" names the **orchestrator** (the agent session attached at the project root that owns git, lifecycle, and edits, drives other containers over the Docker socket, and only needs host-shell intervention for things the running container can't do for itself — typically a new bind-mounted volume), frames a second sandbox as opt-in for isolation only, and closes with a Mermaid architecture diagram (orchestrator at the center of the sandbox subgraph, tmux session row, `docker.sock` → optional second sandbox, external GitHub / LLM-provider edges, host bind-mount). `docs/harnesses/overview.md` replaces the redundant "Running an agent in tmux" section with "Default surfaces" (Pi+Slack, T3 Code, docs app on `:3000`), each as a named tmux session, with explicit guidance to open the two browser surfaces in VS Code's Simple Browser (`Ctrl+Shift+P` → `Simple Browser: Show`; `Cmd+Shift+P` on macOS) so the live UI sits next to the code being edited. Documented tmux sessions renamed `agent-pi` → `harness-pi` and `agent-t3code` → `harness-t3code` in `docs/harnesses/overview.md` only — the `oh config slack` wizard at `apps/oh/src/config/slack.ts` still creates `agent-pi`, and `context/rules/sandbox-processes.md` still defines the `agent-` prefix; full rename across wizard code, rule, and sibling docs (`docs/intro.md`, `docs/integrations/slack.md`, `docs/harnesses/t3code.md`, `.pi/extensions/slack/README.md`) will follow. ([#302](https://github.com/ryaneggz/open-harness/issues/302)) - `.worktrees/README.md` now reserves `.worktrees/project///` for durable standalone repo clones and narrows `.worktrees/agent/` to harness agent branches / legacy agent-owned clones, using generic examples so the layout applies cleanly to new users. ([#295](https://github.com/ryaneggz/open-harness/issues/295), [#298](https://github.com/ryaneggz/open-harness/issues/298)) - `oh config slack` wizard UX overhaul. Token prompts now echo `●` per keystroke (was: silent — looked frozen) via a TTY-aware raw-mode `askSecret` that handles backspace, Ctrl-U, and Ctrl-C; piped/non-TTY stdin still uses the legacy readline fallback as `askSecretPiped` so `echo "" | oh config slack` keeps working. Flow is reframed into four numbered steps `[1/4] … [4/4]` with inline "Where:" hints and clickable URLs (OSC 8 hyperlinks; fall back to `label (url)` on legacy terminals). Each input shows `✓ valid prefix` / `✓ N user(s) accepted` after validation. A final summary screen lists the keys (tokens redacted, IDs visible) and gates the env-file write behind a `Proceed? [Y/n]` confirmation. New helpers exported from `apps/oh/src/lib/prompt.ts`: `step()`, `link()`, `bold()`. Zero new runtime dependencies. ([#290](https://github.com/ryaneggz/open-harness/issues/290)) - Migrate `.pi/extensions/slack/{index,tools}.ts` from the deprecated `@mariozechner/pi-coding-agent` (and `@mariozechner/pi-agent-core`) to the upstream-recommended successors `@earendil-works/pi-coding-agent` (and `@earendil-works/pi-agent-core`). Per `npm view @mariozechner/pi-coding-agent`'s deprecation notice, the old package is no longer maintained. The other two in-tree extensions (`path-guard.ts`, `plan-mode/index.ts`) had already migrated; slack was the laggard. `.devcontainer/Dockerfile`'s `[pi-coding-agent]` mapping in the `PKG` array, `docs/agents/pi.md`'s upstream link, `docs/installation.md`'s Pi row, and `.pi/UPSTREAM.md`'s lineage label all updated to match. ([#281](https://github.com/ryaneggz/open-harness/issues/281)) diff --git a/apps/README.md b/apps/README.md index b88b3bb..592f731 100644 --- a/apps/README.md +++ b/apps/README.md @@ -21,7 +21,7 @@ root. From the repo root: ```bash -pnpm --filter @openharness/docs dev # run a workspace package +pnpm --filter @openharness/docs start # run a workspace package pnpm --filter @openharness/docs build # build it ``` diff --git a/apps/docs/docusaurus.config.ts b/apps/docs/docusaurus.config.ts index 1ef12b4..11720ef 100644 --- a/apps/docs/docusaurus.config.ts +++ b/apps/docs/docusaurus.config.ts @@ -74,6 +74,27 @@ const config: Config = { ], ], + plugins: [ + [ + "@docusaurus/plugin-client-redirects", + { + // Redirects for the renamed engine pages: docs/agents/ was renamed + // to docs/harnesses/. A new "Agents" section now lives at + // docs/agents/, so /docs/agents and /docs/agents/overview are real + // pages again and cannot be redirected. The 6 engine-name redirects + // below preserve old deep-links into the harness pages. + redirects: [ + { from: "/docs/agents/claude-code", to: "/docs/harnesses/claude-code" }, + { from: "/docs/agents/codex", to: "/docs/harnesses/codex" }, + { from: "/docs/agents/deepagents", to: "/docs/harnesses/deepagents" }, + { from: "/docs/agents/opencode", to: "/docs/harnesses/opencode" }, + { from: "/docs/agents/pi", to: "/docs/harnesses/pi" }, + { from: "/docs/agents/t3code", to: "/docs/harnesses/t3code" }, + ], + }, + ], + ], + presets: [ [ "classic", diff --git a/apps/docs/package.json b/apps/docs/package.json index dac2ae4..4b3ccff 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -11,6 +11,7 @@ }, "dependencies": { "@docusaurus/core": "3.7.0", + "@docusaurus/plugin-client-redirects": "3.7.0", "@docusaurus/preset-classic": "3.7.0", "@docusaurus/theme-mermaid": "3.7.0", "@easyops-cn/docusaurus-search-local": "^0.55.1", diff --git a/apps/docs/src/pages/index.tsx b/apps/docs/src/pages/index.tsx index 3dd69f4..94ebe54 100644 --- a/apps/docs/src/pages/index.tsx +++ b/apps/docs/src/pages/index.tsx @@ -22,31 +22,31 @@ const AGENTS: Array<{ { name: "Claude Code", description: "Anthropic's terminal coding agent.", - href: "/docs/agents/claude-code", + href: "/docs/harnesses/claude-code", icon: , }, { name: "Codex", description: "OpenAI's CLI coding agent.", - href: "/docs/agents/codex", + href: "/docs/harnesses/codex", icon: , }, { name: "OpenCode", description: "Terminal agent with OpenAI OAuth support.", - href: "/docs/agents/opencode", + href: "/docs/harnesses/opencode", icon: , }, { name: "Pi", description: "A lightweight, customizable harness.", - href: "/docs/agents/pi", + href: "/docs/harnesses/pi", icon: , }, { name: "DeepAgents", description: "LangChain's multi-provider terminal agent.", - href: "/docs/agents/deepagents", + href: "/docs/harnesses/deepagents", icon: ( -`), never directly on `main`, keeping the trunk stable while the hourly pipeline runs autonomously. diff --git a/docs/agents/linkedin-ghostwriter.md b/docs/agents/linkedin-ghostwriter.md new file mode 100644 index 0000000..abf59c9 --- /dev/null +++ b/docs/agents/linkedin-ghostwriter.md @@ -0,0 +1,67 @@ +--- +id: linkedin-ghostwriter +title: "LinkedIn Ghostwriter" +sidebar_position: 5 +--- + +# LinkedIn Ghostwriter + +The LinkedIn Ghostwriter is a content agent that drafts LinkedIn posts in Ryan Eggleston's voice to grow Open Harness adoption. Each heartbeat cycle it consults a style guide, reads reference posts for voice calibration, picks a topic from a rotating queue, and writes a 50–200 word post that must pass a five-item mandatory checklist: a link to the Open Harness repo, at least one concrete proof point (number, command, or file name), an engagement hook, a connection to a specific Open Harness feature, and a closer not used in any previous draft. Drafts are saved to timestamped files for human review — never published automatically. The funnel goal, as stated in the skill: "Followers → Stars → Contributors → Customers (SMB automation services in Southern Utah)." + +## Branch + +[github.com/ryaneggz/open-harness@agent/linkedin-ghostwriter](https://github.com/ryaneggz/open-harness/tree/agent/linkedin-ghostwriter) + +## Spin up + +```bash +# Clone the harness and check out this agent's branch in a worktree: +git clone https://github.com/ryaneggz/open-harness.git ~/.openharness +cd ~/.openharness +mkdir -p .worktrees/agent +git worktree add .worktrees/agent/linkedin-ghostwriter origin/agent/linkedin-ghostwriter +cd .worktrees/agent/linkedin-ghostwriter +make sandbox +make shell +``` + +First files to read inside the sandbox: +- [`AGENTS.md`](https://github.com/ryaneggz/open-harness/blob/agent/linkedin-ghostwriter/AGENTS.md) +- [`workspace/AGENTS.md`](https://github.com/ryaneggz/open-harness/blob/agent/linkedin-ghostwriter/workspace/AGENTS.md) + +## Workspace contents + +**Skills:** +- [`workspace/.claude/skills/linkedin-ghostwriter/SKILL.md`](https://github.com/ryaneggz/open-harness/blob/agent/linkedin-ghostwriter/workspace/.claude/skills/linkedin-ghostwriter/SKILL.md) — primary skill: content pillar rotation, mandatory checklist, post structure, draft save workflow +- [`workspace/.claude/skills/post-bridge/SKILL.md`](https://github.com/ryaneggz/open-harness/blob/agent/linkedin-ghostwriter/workspace/.claude/skills/post-bridge/SKILL.md) — Post Bridge API integration for social publishing + +**Reference library:** +- [`workspace/.claude/skills/linkedin-ghostwriter/references/style-guide.md`](https://github.com/ryaneggz/open-harness/blob/agent/linkedin-ghostwriter/workspace/.claude/skills/linkedin-ghostwriter/references/style-guide.md) — voice and content strategy guide +- [`workspace/.claude/skills/linkedin-ghostwriter/references/open-harness.md`](https://github.com/ryaneggz/open-harness/blob/agent/linkedin-ghostwriter/workspace/.claude/skills/linkedin-ghostwriter/references/open-harness.md) — repo context and business goals +- [`workspace/.claude/skills/linkedin-ghostwriter/references/posts/`](https://github.com/ryaneggz/open-harness/blob/agent/linkedin-ghostwriter/workspace/.claude/skills/linkedin-ghostwriter/references/posts/) — 9 reference posts with screenshots for voice calibration +- [`workspace/.claude/skills/linkedin-ghostwriter/assets/drafts/`](https://github.com/ryaneggz/open-harness/blob/agent/linkedin-ghostwriter/workspace/.claude/skills/linkedin-ghostwriter/assets/drafts/) — timestamped draft output (100+ drafts generated) +- [`workspace/.claude/skills/linkedin-ghostwriter/assets/drafts/queue.md`](https://github.com/ryaneggz/open-harness/blob/agent/linkedin-ghostwriter/workspace/.claude/skills/linkedin-ghostwriter/assets/drafts/queue.md) — pending/done topic queue + +**Heartbeat:** +- [`workspace/HEARTBEAT.md`](https://github.com/ryaneggz/open-harness/blob/agent/linkedin-ghostwriter/workspace/HEARTBEAT.md) — heartbeat instructions (LinkedIn Ghostwriter task runs each cycle) + +**Custom banner:** +- [`workspace/.pi/banner.json`](https://github.com/ryaneggz/open-harness/blob/agent/linkedin-ghostwriter/workspace/.pi/banner.json) — Pi agent custom banner +- [`workspace/.pi/extensions/custom-banner.ts`](https://github.com/ryaneggz/open-harness/blob/agent/linkedin-ghostwriter/workspace/.pi/extensions/custom-banner.ts) — Pi banner extension + +**Memory:** +- [`workspace/memory/linkedin-ghostwriter-iterations.md`](https://github.com/ryaneggz/open-harness/blob/agent/linkedin-ghostwriter/workspace/memory/linkedin-ghostwriter-iterations.md) — per-cycle iteration log (pillar balance, checklist pass/fail, lessons) + +## How to use it + +Start the sandbox and enter with `make shell`. The agent runs Claude Code inside `workspace/`. Each heartbeat cycle it reads the iteration memory for past learnings, consults the style guide and two reference posts, checks the topic queue, drafts a post with a content-pillar label (Pain→Solution, Build Log, Steal My Workflow, or Honest Reflection), verifies the mandatory checklist, saves the draft to a timestamped file under `assets/drafts/`, updates the queue, and writes a cycle summary to `memory/linkedin-ghostwriter-iterations.md`. You can also invoke the `/linkedin-ghostwriter` skill interactively to draft a post for a specific topic or angle. To publish, copy a draft and use the `/post-bridge` skill or your own publishing workflow. + +## Customization tips + +Content pillar rotation, topic history, and the mandatory closer log are all tracked in `workspace/memory/linkedin-ghostwriter-iterations.md` and `assets/drafts/queue.md`. Seed the queue's `## Pending` section with specific topics or angles to steer future drafts. The style guide at `references/style-guide.md` defines anti-patterns and formatting conventions — edit it to adjust voice or structural rules. The heartbeat interval is controlled by the host-side `HEARTBEAT_INTERVAL` environment variable (default 1800 seconds). See `workspace/AGENTS.md` on the branch for env-var and channel customization. + +## Best practices observed + +- Mandatory five-item checklist enforced per draft (repo link, proof point, engagement hook, OH feature connection, unique closer) ensures consistent post quality without relying on the agent's discretion. +- Iteration memory (`linkedin-ghostwriter-iterations.md`) tracks pillar balance across cycles so the agent can self-correct if it clusters on one content type. +- Draft queue (`assets/drafts/queue.md`) maintains a Pending/Done topic ledger, letting the heartbeat pick up pre-seeded topics or generate fresh ones from a different pillar than the previous cycle. diff --git a/docs/agents/overview.md b/docs/agents/overview.md index 40c9116..28dacf5 100644 --- a/docs/agents/overview.md +++ b/docs/agents/overview.md @@ -1,92 +1,53 @@ --- +id: overview +title: "Agents Overview" sidebar_position: 1 -title: "Harnesses Overview" --- -# Harnesses Overview +# Agents Overview -Open Harness ships with five agent CLIs preinstalled in the sandbox image: **Claude Code** (default), **Codex**, **OpenCode**, **Pi**, and **DeepAgents**. A sixth harness, **T3 Code**, runs on demand via `npx t3` as a browser UI on port 3773. Inside the sandbox, launch whichever you prefer — switch between them at any time, or keep long-running sessions in tmux. +Open Harness ships a set of pre-built agent identities on public `agent/*` branches. Each branch contains a fully-scaffolded `workspace/` — identity files (`SOUL.md`, `AGENTS.md`), skills, heartbeats, and memory — that gives a harness a specific purpose. Check out the branch, run `make sandbox`, and the agent is live. -The sandbox is the product; the harness is your call. To go beyond the preinstalled options, install via `npm` / `pip` / `cargo` inside the sandbox, edit the Dockerfile, or layer in a harness pack such as [`@ryaneggz/mifune`](https://github.com/ryaneggz/mifune). For Pi+Slack specifically, the recommended path is the in-tree extension at [`.pi/extensions/slack/`](../integrations/slack.md). The product surface is one developer, one project, one harness — not racing or stacking multiple CLIs against each other. +## Agents vs. Harnesses -## Supported agents +| | Harnesses | Agents | +|---|---|---| +| **What** | The CLI engine you run (`claude`, `codex`, `pi`, …) | A configured workspace that gives an engine purpose | +| **Where** | Preinstalled in the sandbox image | `agent/*` branches; `workspace/` bind-mounted at runtime | +| **Analogy** | The interpreter | The program | -| Agent | Role | Start command | Source | -|---|---|---|---| -| [Claude Code](./claude-code.md) | Anthropic's terminal coding agent (default) | `claude` | preinstalled | -| [Codex](./codex.md) | OpenAI's CLI coding agent | `codex` | preinstalled | -| [OpenCode](./opencode.md) | Terminal coding agent with OpenAI OAuth support | `opencode` | preinstalled | -| [Pi](./pi.md) | Lightweight, customizable harness | `pi` | preinstalled | -| [DeepAgents](./deepagents.md) | LangChain's multi-provider terminal agent | `deepagents` | preinstalled | -| [T3 Code](./t3code.md) | Browser UI over Claude/Codex/OpenCode (port 3773) | `npx t3` | on-demand | +An agent identity is engine-agnostic: the same `workspace/` can drive Claude Code, Codex, or Pi — whichever harness is authenticated. See [Harnesses](../harnesses/overview.md) for the engine-level docs. -## Verifying installation +## Adopting an agent + +**Option A — worktree (keeps your main checkout intact):** ```bash -claude --version -codex --version -opencode --version -pi --version -deepagents -v -npx t3 --version # T3 Code (not preinstalled — fetched on demand) +git fetch origin +git worktree add .worktrees/agent/ agent/ +cd .worktrees/agent/ +make sandbox ``` -## Authentication - -Open Harness ships Claude Code, Codex, OpenCode, Pi, and DeepAgents preinstalled. Authenticate at least one harness before use: - -- **Claude Code**: run `claude` and follow the OAuth prompt (see [Claude Code](./claude-code.md)). -- **Codex**: run `codex login` (see [Codex](./codex.md)). -- **OpenCode**: run `opencode auth login` (see [OpenCode](./opencode.md)). -- **Pi**: configure provider keys via environment variables (see [Pi](./pi.md)). -- **DeepAgents**: write provider keys to `~/.deepagents/.env` (see [DeepAgents](./deepagents.md)). -- **T3 Code**: authenticate one of Claude / Codex / OpenCode first, then `npx t3` and open the printed pairing URL (see [T3 Code](./t3code.md)). - -## Default surfaces - -Three surfaces cover most day-to-day use: - -- **Pi+Slack** — chat with the agent from Slack instead of the terminal. -- **T3 Code** — browser UI on port `3773` driving Claude / Codex / OpenCode. -- **Docs app** — the Docusaurus site you're reading now, on port `3000`. - -Each runs in its own named tmux session per [`context/rules/sandbox-processes.md`](https://github.com/ryaneggz/open-harness/blob/development/context/rules/sandbox-processes.md). For the two browser surfaces, open them in **VS Code's Simple Browser** (`Ctrl+Shift+P` → `Simple Browser: Show`; `Cmd+Shift+P` on macOS) so the live UI sits in a tab next to the code you're editing. - -### Pi+Slack - -The Pi agent with the Slack bridge loaded. The wizard writes `.devcontainer/.env`, launches the `harness-pi` session, and waits for the Socket Mode connection to come up: +**Option B — fresh clone:** ```bash -oh config slack # interactive wizard -tmux attach -t harness-pi # watch the live log +git clone https://github.com/ryaneggz/open-harness.git +cd open-harness +git checkout agent/ +make sandbox ``` -Talk to the agent from Slack (DM or `@mention` in an allow-listed channel). Full setup: [Slack integration](../integrations/slack.md). +Both paths land you at the standard `make sandbox` flow described in [Installation](/docs/installation). -### T3 Code +## Available agents -Web UI on `http://localhost:3773` over an already-authenticated provider: +| Agent | Description | Branch | +|---|---|---| +| [Blog Writer](./blog-writer) | Writes technical blog posts for ruska.ai in Ryan Eggleston's voice — developer-bookmark quality, SMB-forward | [agent/blog-writer](https://github.com/ryaneggz/open-harness/tree/agent/blog-writer) | +| [DC Designer](./dc-designer) | Provisions and maintains a sandbox for an AI-driven 50MW–200MW hyperscale datacenter facility design | [agent/dc-designer](https://github.com/ryaneggz/open-harness/tree/agent/dc-designer) | +| [LinkedIn Ghostwriter](./linkedin-ghostwriter) | General-purpose coding agent scaffolded for LinkedIn content drafting workflows | [agent/linkedin-ghostwriter](https://github.com/ryaneggz/open-harness/tree/agent/linkedin-ghostwriter) | +| [Portfolio Manager](./portfolio-mgr) | Runs a mock $100K portfolio informed by Bridgewater's 13F filing and Ray Dalio's All Weather strategy | [agent/portfolio-mgr](https://github.com/ryaneggz/open-harness/tree/agent/portfolio-mgr) | +| [UAT Tester](./uat-tester) | Visual acceptance testing agent — methodical, screenshot-driven, ranked by user impact | [agent/uat-tester](https://github.com/ryaneggz/open-harness/tree/agent/uat-tester) | -```bash -tmux new-session -d -s harness-t3code 'npx t3 2>&1 | tee /tmp/harness-t3code.log' -tmux capture-pane -t harness-t3code -p | grep -i pairingUrl -``` - -Open the printed pairing URL (`http://localhost:3773/pair#token=…`) in the Simple Browser tab. Full setup: [T3 Code](./t3code.md). - -### Docs app - -The site you're reading now, served locally: - -```bash -tmux new-session -d -s app-docs 'pnpm --filter @openharness/docs dev 2>&1 | tee /tmp/app-docs.log' -``` - -Open `http://localhost:3000` in the Simple Browser tab for hot-reload feedback on doc edits without leaving the editor. - -### Reattach to any session - -```bash -tmux ls # list sessions -tmux attach -t # reattach (Ctrl-b d to detach) -``` +Each page documents the agent's purpose, workspace layout, and any setup steps beyond `make sandbox`. diff --git a/docs/agents/portfolio-mgr.md b/docs/agents/portfolio-mgr.md new file mode 100644 index 0000000..4c79040 --- /dev/null +++ b/docs/agents/portfolio-mgr.md @@ -0,0 +1,60 @@ +--- +id: portfolio-mgr +title: "Portfolio Manager" +sidebar_position: 7 +--- + +# Portfolio Manager + +Portfolio Manager is a quantitative portfolio-management agent that tracks and rebalances a mock $100K portfolio modeled on Ray Dalio's All Weather strategy and Bridgewater Associates' latest 13F filing. It pulls live prices via yfinance, scores market sentiment with WebSearch, computes risk metrics (Sharpe, Sortino, max drawdown, beta, volatility), and flags allocation drift before it exceeds the 5% rebalancing threshold. As the agent's own SOUL.md states: "You are data-driven: cite numbers, percentages, and Sharpe ratios, not vibes." This is an educational simulation — all trades are mock, and the agent never presents output as financial advice. + +## Branch + +[github.com/ryaneggz/open-harness@agent/portfolio-mgr](https://github.com/ryaneggz/open-harness/tree/agent/portfolio-mgr) + +## Spin up + +```bash +# Clone the harness and check out this agent's branch in a worktree: +git clone https://github.com/ryaneggz/open-harness.git ~/.openharness +cd ~/.openharness +mkdir -p .worktrees/agent +git worktree add .worktrees/agent/portfolio-mgr origin/agent/portfolio-mgr +cd .worktrees/agent/portfolio-mgr +make sandbox +make shell +``` + +First files to read inside the sandbox: +- [`AGENTS.md`](https://github.com/ryaneggz/open-harness/blob/agent/portfolio-mgr/AGENTS.md) +- [`workspace/AGENTS.md`](https://github.com/ryaneggz/open-harness/blob/agent/portfolio-mgr/workspace/AGENTS.md) + +## Workspace contents + +- **Skills** + - [`/allocation-check`](https://github.com/ryaneggz/open-harness/blob/agent/portfolio-mgr/workspace/.claude/skills/allocation-check/SKILL.md) — validate current weights against target allocation before any rebalance + - [`/risk-metrics`](https://github.com/ryaneggz/open-harness/blob/agent/portfolio-mgr/workspace/.claude/skills/risk-metrics/SKILL.md) — compute Sharpe, Sortino, max drawdown, beta, and volatility + - [`/sentiment-score`](https://github.com/ryaneggz/open-harness/blob/agent/portfolio-mgr/workspace/.claude/skills/sentiment-score/SKILL.md) — WebSearch-driven macro sentiment composite + - [`/strategy-review`](https://github.com/ryaneggz/open-harness/blob/agent/portfolio-mgr/workspace/.claude/skills/strategy-review/SKILL.md) — measure decision quality over rolling periods +- **Heartbeats** (defined in [`heartbeats.conf`](https://github.com/ryaneggz/open-harness/blob/agent/portfolio-mgr/workspace/heartbeats.conf)) + - [`market-check.md`](https://github.com/ryaneggz/open-harness/blob/agent/portfolio-mgr/workspace/heartbeats/market-check.md) — daily at 4:30 PM ET after market close: fetch prices, compute P&L, run risk-metrics and sentiment-score, flag drift > 3% + - [`rebalance-review.md`](https://github.com/ryaneggz/open-harness/blob/agent/portfolio-mgr/workspace/heartbeats/rebalance-review.md) — Friday 5 PM MT: weekly rebalance decision + - [`monthly-retro.md`](https://github.com/ryaneggz/open-harness/blob/agent/portfolio-mgr/workspace/heartbeats/monthly-retro.md) — 1st of each month: rolling performance retrospective + - [`memory-distill.md`](https://github.com/ryaneggz/open-harness/blob/agent/portfolio-mgr/workspace/heartbeats/memory-distill.md) — Sunday 8 PM MT: distill daily logs into MEMORY.md +- **Portfolio data** — [`portfolio/state.json`](https://github.com/ryaneggz/open-harness/blob/agent/portfolio-mgr/workspace/portfolio/state.json) (current holdings), [`portfolio/ledger.json`](https://github.com/ryaneggz/open-harness/blob/agent/portfolio-mgr/workspace/portfolio/ledger.json) (strategy versions + forward performance), [`portfolio/scorecard.md`](https://github.com/ryaneggz/open-harness/blob/agent/portfolio-mgr/workspace/portfolio/scorecard.md) (rolling summary) +- **Pi banner** — [`workspace/.pi/banner.json`](https://github.com/ryaneggz/open-harness/blob/agent/portfolio-mgr/workspace/.pi/banner.json) with a custom extension at [`workspace/.pi/extensions/custom-banner.ts`](https://github.com/ryaneggz/open-harness/blob/agent/portfolio-mgr/workspace/.pi/extensions/custom-banner.ts) +- **Sub-agents** — agent-builder, command-builder, skill-builder under [`workspace/.claude/agents/`](https://github.com/ryaneggz/open-harness/blob/agent/portfolio-mgr/workspace/.claude/agents/) + +## How to use it + +Once the sandbox is running and Claude Code is started (`claude`), the agent enters an autonomous daily cycle driven by the `market-check` heartbeat. During interactive sessions you can ask the agent to run any skill directly — for example, ask it to run `/risk-metrics` or `/allocation-check` to get an on-demand snapshot. The portfolio state lives in `portfolio/state.json`; the agent reads it at every heartbeat and writes updates to the daily memory log. When allocation drift on any position exceeds 5%, the agent proposes a rebalance and runs the quality-gate chain (`/risk-metrics` then `/allocation-check`) before confirming the mock trade. + +## Customization tips + +The target tickers and allocation weights are stored in `MEMORY.md` and `portfolio/state.json` on the branch — update them to swap the underlying 13F or All Weather mix. The heartbeat schedule (market-close time, rebalance day) is in [`heartbeats.conf`](https://github.com/ryaneggz/open-harness/blob/agent/portfolio-mgr/workspace/heartbeats.conf); edit the cron expressions there and run `make heartbeat` to re-sync the daemon. See `workspace/AGENTS.md` on the branch for the full environment and tool reference. + +## Best practices observed + +- Dual-skill quality gate (`/risk-metrics` → `/allocation-check`) runs before every rebalance decision, preventing premature trades on noisy single-day moves. +- Market-check heartbeat scheduled via [`heartbeats/market-check.md`](https://github.com/ryaneggz/open-harness/blob/agent/portfolio-mgr/workspace/heartbeats/market-check.md) fires daily at 4:30 PM ET after market close, keeping the agent on a market-hours rhythm without manual triggers. +- `portfolio/ledger.json` tracks strategy versions alongside forward performance, enabling before/after comparison whenever the All Weather weights are changed. diff --git a/docs/agents/uat-tester.md b/docs/agents/uat-tester.md new file mode 100644 index 0000000..61efca0 --- /dev/null +++ b/docs/agents/uat-tester.md @@ -0,0 +1,69 @@ +--- +id: uat-tester +title: "UAT Tester" +sidebar_position: 4 +--- + +# UAT Tester + +UAT Tester is a visual acceptance-testing agent that exercises deployed web applications as a real user would. Using headless Chromium via the `agent-browser` CLI, it navigates every user flow — authentication, CRUD operations, navigation, forms — across desktop, tablet, and mobile viewports. Its README describes its scope precisely: "A visual UAT agent that tests deployed web applications as a real user would. Identifies visual bugs, broken flows, accessibility violations, and UX issues. Produces a deduplicated, impact-ranked top-20 findings list per project as user stories." The agent supports multiple concurrent projects, each with its own scoped findings directory, and includes a recheck loop for verifying deployed fixes. It never writes application code, deploys changes, or merges PRs. + +## Branch + +[github.com/ryaneggz/open-harness@agent/uat-tester](https://github.com/ryaneggz/open-harness/tree/agent/uat-tester) + +## Spin up + +```bash +# Clone the harness and check out this agent's branch in a worktree: +git clone https://github.com/ryaneggz/open-harness.git ~/.openharness +cd ~/.openharness +mkdir -p .worktrees/agent +git worktree add .worktrees/agent/uat-tester origin/agent/uat-tester +cd .worktrees/agent/uat-tester +make sandbox +make shell +``` + +First files to read inside the sandbox: +- [`AGENTS.md`](https://github.com/ryaneggz/open-harness/blob/agent/uat-tester/AGENTS.md) +- [`workspace/AGENTS.md`](https://github.com/ryaneggz/open-harness/blob/agent/uat-tester/workspace/AGENTS.md) + +## Workspace contents + +- **Core testing skills** (under [`workspace/.claude/skills/`](https://github.com/ryaneggz/open-harness/blob/agent/uat-tester/workspace/.claude/skills/)) + - [`/visual-uat`](https://github.com/ryaneggz/open-harness/blob/agent/uat-tester/workspace/.claude/skills/visual-uat/SKILL.md) — full 6-phase UAT sweep of a registered project + - [`/recheck`](https://github.com/ryaneggz/open-harness/blob/agent/uat-tester/workspace/.claude/skills/recheck/SKILL.md) — re-verify specific findings after fixes are deployed + - [`/test-auth`](https://github.com/ryaneggz/open-harness/blob/agent/uat-tester/workspace/.claude/skills/test-auth/SKILL.md), [`/test-forms`](https://github.com/ryaneggz/open-harness/blob/agent/uat-tester/workspace/.claude/skills/test-forms/SKILL.md), [`/test-nav`](https://github.com/ryaneggz/open-harness/blob/agent/uat-tester/workspace/.claude/skills/test-nav/SKILL.md), [`/test-crud`](https://github.com/ryaneggz/open-harness/blob/agent/uat-tester/workspace/.claude/skills/test-crud/SKILL.md), [`/test-search`](https://github.com/ryaneggz/open-harness/blob/agent/uat-tester/workspace/.claude/skills/test-search/SKILL.md) + - [`/test-a11y`](https://github.com/ryaneggz/open-harness/blob/agent/uat-tester/workspace/.claude/skills/test-a11y/SKILL.md) — WCAG A/AA, keyboard nav, ARIA, alt text, focus checks + - [`/test-responsive`](https://github.com/ryaneggz/open-harness/blob/agent/uat-tester/workspace/.claude/skills/test-responsive/SKILL.md) — 3-viewport sweep (desktop, tablet, mobile) + - [`/test-visual-regression`](https://github.com/ryaneggz/open-harness/blob/agent/uat-tester/workspace/.claude/skills/test-visual-regression/SKILL.md) — before/after screenshot comparison + - [`/quality-gate`](https://github.com/ryaneggz/open-harness/blob/agent/uat-tester/workspace/.claude/skills/quality-gate/SKILL.md), [`/strategy-review`](https://github.com/ryaneggz/open-harness/blob/agent/uat-tester/workspace/.claude/skills/strategy-review/SKILL.md) +- **Specialist sub-agents** (spawned in parallel, under [`workspace/.claude/agents/`](https://github.com/ryaneggz/open-harness/blob/agent/uat-tester/workspace/.claude/agents/)) + - [`a11y-auditor.md`](https://github.com/ryaneggz/open-harness/blob/agent/uat-tester/workspace/.claude/agents/a11y-auditor.md) — WCAG compliance sweep + - [`responsive-tester.md`](https://github.com/ryaneggz/open-harness/blob/agent/uat-tester/workspace/.claude/agents/responsive-tester.md) — 3-viewport coverage + - [`flow-walker.md`](https://github.com/ryaneggz/open-harness/blob/agent/uat-tester/workspace/.claude/agents/flow-walker.md) — happy-path and edge-case user flows + - [`visual-diff.md`](https://github.com/ryaneggz/open-harness/blob/agent/uat-tester/workspace/.claude/agents/visual-diff.md) — regression detection for rechecked items +- **Rules** (auto-loaded from [`workspace/.claude/rules/`](https://github.com/ryaneggz/open-harness/blob/agent/uat-tester/workspace/.claude/rules/)) + - `uat-testing.md` — core protocol: evidence required, dedup, top-20 cap, user story format, no app code + - `agent-browser.md` — browser hygiene: session isolation, wait-before-capture, naming conventions + - `findings-management.md` — JSON source of truth, severity criteria, dedup logic, archive protocol + - `multi-project.md` — project isolation: scoped to slug, registration required +- **Heartbeat** + - [`heartbeats/uat-report.md`](https://github.com/ryaneggz/open-harness/blob/agent/uat-tester/workspace/heartbeats/uat-report.md) — every 4 hours, 9 AM–9 PM: status report across registered projects +- **Project registry** — [`uat/projects.json`](https://github.com/ryaneggz/open-harness/blob/agent/uat-tester/workspace/uat/projects.json) — register each app under test with a slug, URL, and login instructions +- **Slack integration** — [`workspace/.claude/hooks/notify_slack.sh`](https://github.com/ryaneggz/open-harness/blob/agent/uat-tester/workspace/.claude/hooks/notify_slack.sh); `.slack/skills` for shared skill access + +## How to use it + +Register the target application in `uat/projects.json` with a slug, URL, and login instructions. Then ask the agent to run `/visual-uat ` — it spawns the four specialist sub-agents in parallel (a11y auditor, responsive tester, flow walker, visual diff), collects their findings, deduplicates them, and produces a top-20 impact-ranked list as user stories in both JSON and Markdown. When developers report fixes, run `/recheck [IDs]` and the agent re-plays the original reproduction steps, returning `PASS`, `FAIL`, or `CHANGED` for each item. The `uat-report` heartbeat fires every 4 hours during active hours (9 AM–9 PM) to surface any status changes without manual prompting. + +## Customization tips + +Each project is registered independently in `uat/projects.json` — add as many projects as needed, each with its own slug and findings directory. The active window for the heartbeat (currently 9–21) and frequency (every 4 hours) are set in [`heartbeats.conf`](https://github.com/ryaneggz/open-harness/blob/agent/uat-tester/workspace/heartbeats.conf). Severity thresholds and the top-20 cap are governed by `workspace/.claude/rules/findings-management.md`. See `workspace/AGENTS.md` on the branch for the full environment and tool reference, including the `INSTALL_BROWSER=true` requirement for the sandbox build. + +## Best practices observed + +- Four specialist sub-agents (a11y auditor, responsive tester, flow walker, visual diff) spawn in parallel during a `/visual-uat` run, giving simultaneous coverage across accessibility, responsiveness, flows, and regression without sequential bottlenecks. +- A hard top-20 cap per project with an archive-and-promote mechanism keeps the findings list actionable — overflow is preserved in `findings-archive.json` and promoted back when higher-priority items are resolved. +- The self-improving autoresearch loop (Karpathy pattern) runs after every skill execution: hypothesize a change, apply it, measure on the next run, keep or revert — skills and rules tighten automatically based on real finding data rather than speculation. diff --git a/docs/connecting.md b/docs/connecting.md new file mode 100644 index 0000000..4957fac --- /dev/null +++ b/docs/connecting.md @@ -0,0 +1,165 @@ +--- +id: connecting +slug: /connecting +sidebar_position: 4 +title: "Connecting to the Sandbox" +--- + +# Connecting to the Sandbox + +The sandbox is a Docker container running on your host (or a remote server). Getting UI apps like the docs site and T3 Code onto your laptop browser depends on **how** you connect — not every connection method forwards ports. This page covers your options, explains which one to use, and walks an end-to-end recipe. + +## Three ways to connect + +| Option | Command / action | Port forwarding to laptop | +|--------|-----------------|--------------------------| +| **A — Terminal** | `make shell` from the host | None — plain shell only | +| **B — VSCode Attach (local)** | Dev Containers extension → "Attach to Running Container" → `openharness` | Automatic while attached | +| **C — VSCode Remote-SSH + Attach (remote host)** | SSH into your host in VSCode, then Attach to Container | Automatic while attached | + +### Option A — Terminal + +```bash +cd ~/.openharness +make shell +``` + +You land inside the container as the `sandbox` user. This is enough to run CLI agents and configure Slack. Container ports are **not** forwarded to your laptop — you cannot open `localhost:3000` in your browser via this method alone. + +### Option B — VSCode Attach to Running Container (local host) + +1. Install the [Dev Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) extension. +2. Open the Command Palette (`Ctrl+Shift+P` / `Cmd+Shift+P`) → **Dev Containers: Attach to Running Container**. +3. Select **openharness**. + +VSCode opens a remote window connected to the container and **automatically forwards container ports to `localhost`** on your laptop for the duration of the session. + +### Option C — VSCode Remote-SSH + Attach (remote host) + +If the sandbox runs on a remote server: + +1. Connect to the server via **Remote-SSH** in VSCode. +2. From that SSH window, follow Option B to attach to the `openharness` container. + +Port forwarding works identically — VSCode tunnels the container ports through the SSH connection to your laptop `localhost`. No manual `ssh -L` required. + +## Why VSCode Attach is the recommended path + +Attaching via VSCode is the easiest way to reach container UIs on your laptop browser. The auto-forwarding is session-scoped: ports appear under the **Ports** panel while attached and disappear when you close or detach from the VSCode window. + +If you only need a terminal (no browser UI), Option A is fine. + +## What happens when you close VSCode + +When you close the VSCode remote window or detach from the container, the port forwards drop. Apps in tmux keep running inside the container — they are unaffected — but `localhost:` on your laptop no longer resolves until you re-attach. + +## Default exposure posture + +The sandbox publishes **one port** by default: + +``` +127.0.0.1:1455:1455 # Pi harness OAuth login callback (host loopback only) +``` + +Nothing else is published to the host. The `127.0.0.1` binding ensures port 1455 is reachable from `localhost` on the host machine — which VSCode's Remote-SSH forwarding can carry back to your laptop — without exposing it on any public interface. + +All other container ports (3000, 3773, etc.) are only reachable via VSCode's auto-forwarding or a manual `ssh -L` tunnel. + +## Opt-in public exposure + +If you need a port reachable beyond your laptop — for example, to share a preview with a teammate — there are two opt-in paths: + +**1. Compose overlay binding `0.0.0.0`** + +Add a custom compose file that binds the port on all interfaces and merge it in with `composeOverrides[]` in `config.json`: + +```yaml +# docker-compose.my-expose.yml +services: + sandbox: + ports: + - "0.0.0.0:3000:3000" +``` + +This is NOT the default; you opt in explicitly. Be aware that binding to `0.0.0.0` exposes the port on the host's public interface. + +**2. External tunnel** + +The harness ships no built-in tunnel tool. For public access, bring your own: `cloudflared`, `ngrok`, `tailscale funnel`, or an nginx/Caddy reverse proxy. Start the tunnel inside the sandbox in a named tmux session (see [tmux conventions](#tmux-session-naming)). + +## tmux session naming + +All long-running processes inside the sandbox run in named tmux sessions. The naming convention is `-`: + +| Category | Example | Purpose | +|----------|---------|---------| +| `agent-` | `agent-claude`, `agent-pi` | AI agent processes | +| `app-` | `app-docs`, `app-api` | Dev servers | + +For the full convention see [`context/rules/sandbox-processes.md`](https://github.com/ryaneggz/open-harness/blob/development/context/rules/sandbox-processes.md). + +## End-to-end recipe + +This recipe assumes the sandbox is already running (`make ps` confirms the `openharness` container is up). Steps run inside the sandbox unless noted. + +### Step 1 — Attach via VSCode + +Follow Option B (or C for a remote host). The Ports panel in VSCode shows forwarded ports as you launch apps. + +### Step 2 — Configure Slack + +```bash +# Inside the sandbox (already attached via VSCode or make shell) +oh config slack +``` + +The interactive wizard collects your Slack tokens, validates them, writes `.devcontainer/.env`, and restarts the `agent-pi` tmux session in Socket Mode. For the full wizard walkthrough see [Integrations → Slack](/docs/integrations/slack). + +After the wizard completes, verify the bridge is live: + +```bash +tmux capture-pane -t agent-pi -p | grep -i 'connected\|listening' +``` + +### Step 3 — Launch T3 Code + +T3 Code is not preinstalled; the first invocation downloads it via `npx`. + +```bash +tmux new-session -d -s agent-t3code 'npx t3 2>&1 | tee /tmp/agent-t3code.log' +tmux attach -t agent-t3code +``` + +Watch the session output — T3 Code prints a pairing URL. Open that URL in your browser to complete the browser-based pairing step. After pairing, the UI is available at `localhost:3773` on your laptop (via VSCode auto-forwarding). + +Detach from the tmux session without stopping it: `Ctrl-b d`. + +For more on T3 Code setup see [Harnesses → T3 Code](/docs/harnesses/t3code). + +### Step 4 — Launch the docs site + +```bash +tmux new-session -d -s app-docs 'pnpm --filter @openharness/docs start 2>&1 | tee /tmp/app-docs.log' +``` + +The Docusaurus dev server binds port 3000 inside the container. Via VSCode auto-forwarding, open `localhost:3000` in your laptop browser. + +### Step 5 — Confirm ports in VSCode + +Open the **Ports** panel (bottom status bar → Ports, or `Ctrl+Shift+P` → "Focus on Ports"). You should see: + +| Port | Forwarded to | App | +|------|-------------|-----| +| 1455 | localhost:1455 | Pi OAuth callback | +| 3000 | localhost:3000 | Docs site | +| 3773 | localhost:3773 | T3 Code UI | + +If a port is missing, confirm the tmux session is running (`tmux ls`) and that you are still attached via VSCode. + +## Quick-reference: reach `localhost` from your laptop + +| App | Container port | Laptop URL (VSCode attached) | +|-----|---------------|------------------------------| +| Docs site | 3000 | `http://localhost:3000` | +| T3 Code UI | 3773 | `http://localhost:3773` | +| Pi OAuth callback | 1455 | `http://localhost:1455` | diff --git a/docs/harnesses/_category_.json b/docs/harnesses/_category_.json new file mode 100644 index 0000000..1864603 --- /dev/null +++ b/docs/harnesses/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Harnesses", + "position": 9, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "harnesses/overview" + } +} diff --git a/docs/agents/claude-code.md b/docs/harnesses/claude-code.md similarity index 100% rename from docs/agents/claude-code.md rename to docs/harnesses/claude-code.md diff --git a/docs/agents/codex.md b/docs/harnesses/codex.md similarity index 100% rename from docs/agents/codex.md rename to docs/harnesses/codex.md diff --git a/docs/agents/deepagents.md b/docs/harnesses/deepagents.md similarity index 100% rename from docs/agents/deepagents.md rename to docs/harnesses/deepagents.md diff --git a/docs/agents/opencode.md b/docs/harnesses/opencode.md similarity index 100% rename from docs/agents/opencode.md rename to docs/harnesses/opencode.md diff --git a/docs/harnesses/overview.md b/docs/harnesses/overview.md new file mode 100644 index 0000000..1763348 --- /dev/null +++ b/docs/harnesses/overview.md @@ -0,0 +1,94 @@ +--- +sidebar_position: 1 +title: "Harnesses Overview" +--- + +# Harnesses Overview + +Open Harness ships with five agent CLIs preinstalled in the sandbox image: **Claude Code** (default), **Codex**, **OpenCode**, **Pi**, and **DeepAgents**. A sixth harness, **T3 Code**, runs on demand via `npx t3` as a browser UI on port 3773. Inside the sandbox, launch whichever you prefer — switch between them at any time, or keep long-running sessions in tmux. + +The sandbox is the product; the harness is your call. To go beyond the preinstalled options, install via `npm` / `pip` / `cargo` inside the sandbox, edit the Dockerfile, or layer in a harness pack such as [`@ryaneggz/mifune`](https://github.com/ryaneggz/mifune). For Pi+Slack specifically, the recommended path is the in-tree extension at [`.pi/extensions/slack/`](../integrations/slack.md). The product surface is one developer, one project, one harness — not racing or stacking multiple CLIs against each other. + +## Supported agents + +| Agent | Role | Start command | Source | +|---|---|---|---| +| [Claude Code](./claude-code.md) | Anthropic's terminal coding agent (default) | `claude` | preinstalled | +| [Codex](./codex.md) | OpenAI's CLI coding agent | `codex` | preinstalled | +| [OpenCode](./opencode.md) | Terminal coding agent with OpenAI OAuth support | `opencode` | preinstalled | +| [Pi](./pi.md) | Lightweight, customizable harness | `pi` | preinstalled | +| [DeepAgents](./deepagents.md) | LangChain's multi-provider terminal agent | `deepagents` | preinstalled | +| [T3 Code](./t3code.md) | Browser UI over Claude/Codex/OpenCode (port 3773) | `npx t3` | on-demand | + +## Verifying installation + +```bash +claude --version +codex --version +opencode --version +pi --version +deepagents -v +npx t3 --version # T3 Code (not preinstalled — fetched on demand) +``` + +## Authentication + +Open Harness ships Claude Code, Codex, OpenCode, Pi, and DeepAgents preinstalled. Authenticate at least one harness before use: + +- **Claude Code**: run `claude` and follow the OAuth prompt (see [Claude Code](./claude-code.md)). +- **Codex**: run `codex login` (see [Codex](./codex.md)). +- **OpenCode**: run `opencode auth login` (see [OpenCode](./opencode.md)). +- **Pi**: configure provider keys via environment variables (see [Pi](./pi.md)). +- **DeepAgents**: write provider keys to `~/.deepagents/.env` (see [DeepAgents](./deepagents.md)). +- **T3 Code**: authenticate one of Claude / Codex / OpenCode first, then `npx t3` and open the printed pairing URL (see [T3 Code](./t3code.md)). + +## Default surfaces + +Three surfaces cover most day-to-day use: + +- **Pi+Slack** — chat with the agent from Slack instead of the terminal. +- **T3 Code** — browser UI on port `3773` driving Claude / Codex / OpenCode. +- **Docs app** — the Docusaurus site you're reading now, on port `3000`. + +Each runs in its own named tmux session per [`context/rules/sandbox-processes.md`](https://github.com/ryaneggz/open-harness/blob/development/context/rules/sandbox-processes.md). For the two browser surfaces, open them in **VS Code's Simple Browser** (`Ctrl+Shift+P` → `Simple Browser: Show`; `Cmd+Shift+P` on macOS) so the live UI sits in a tab next to the code you're editing. + +### Pi+Slack + +The Pi agent with the Slack bridge loaded. The wizard writes `.devcontainer/.env`, launches the `harness-pi` session, and waits for the Socket Mode connection to come up: + +```bash +oh config slack # interactive wizard +tmux attach -t harness-pi # watch the live log +``` + +Talk to the agent from Slack (DM or `@mention` in an allow-listed channel). Full setup: [Slack integration](../integrations/slack.md). + +### T3 Code + +Web UI on `http://localhost:3773` over an already-authenticated provider: + +```bash +tmux new-session -d -s harness-t3code 'npx t3 2>&1 | tee /tmp/harness-t3code.log' +tmux capture-pane -t harness-t3code -p | grep -i pairingUrl +``` + +Open the printed pairing URL (`http://localhost:3773/pair#token=…`) in the Simple Browser tab. Full setup: [T3 Code](./t3code.md). + +### Docs app + +The site you're reading now, served locally: + +```bash +tmux new-session -d -s app-docs 'pnpm --filter @openharness/docs dev 2>&1 | tee /tmp/app-docs.log' +``` + +Open `http://localhost:3000` in the Simple Browser tab for hot-reload feedback on doc edits without leaving the editor. + +### Reattach to any session + +```bash +tmux ls # list sessions +tmux attach -t # reattach (Ctrl-b d to detach) +``` + +[Connecting to the Sandbox](/docs/connecting) diff --git a/docs/agents/pi.md b/docs/harnesses/pi.md similarity index 100% rename from docs/agents/pi.md rename to docs/harnesses/pi.md diff --git a/docs/agents/t3code.md b/docs/harnesses/t3code.md similarity index 98% rename from docs/agents/t3code.md rename to docs/harnesses/t3code.md index f845d16..c2a443c 100644 --- a/docs/agents/t3code.md +++ b/docs/harnesses/t3code.md @@ -67,3 +67,5 @@ If you can't reach `localhost:3773` from the host, expose port 3773 from the san ## Upstream documentation - [`pingdotgg/t3code` on GitHub](https://github.com/pingdotgg/t3code) + +[Connecting to the Sandbox](/docs/connecting) diff --git a/docs/integrations/slack.md b/docs/integrations/slack.md index 6396ad5..5b9e789 100644 --- a/docs/integrations/slack.md +++ b/docs/integrations/slack.md @@ -182,3 +182,5 @@ file inventory, env var reference, and build/test instructions, see For upstream lineage, sync model, and divergence history, see [`.pi/UPSTREAM.md`](https://github.com/ryaneggz/open-harness/blob/development/.pi/UPSTREAM.md). + +[Connecting to the Sandbox](/docs/connecting) diff --git a/docs/intro.md b/docs/intro.md index 4624fa3..cba6a90 100644 --- a/docs/intro.md +++ b/docs/intro.md @@ -67,3 +67,5 @@ If you already have a sandbox running, jump directly to the page you need. ## Where to get help - Source code and issues: [github.com/ryaneggz/open-harness](https://github.com/ryaneggz/open-harness) + +[Connecting to the Sandbox](/docs/connecting) diff --git a/docs/quickstart.md b/docs/quickstart.md index 63509ef..7972f97 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -45,17 +45,19 @@ The sandbox ships with Claude Code, Codex, OpenCode, Pi, and DeepAgents preinstalled, plus T3 Code on demand via `npx`. Authenticate at least one before use: -- **[Claude Code](./agents/claude-code.md)**: run `claude` and follow the OAuth prompt -- **[Codex](./agents/codex.md)**: run `codex login` -- **[OpenCode](./agents/opencode.md)**: run `opencode auth login` -- **[Pi](./agents/pi.md)**: configure provider keys via environment variables -- **[DeepAgents](./agents/deepagents.md)**: write provider keys to `~/.deepagents/.env` -- **[T3 Code](./agents/t3code.md)**: authenticate one of Claude / Codex / OpenCode, then `npx t3` (browser UI on port 3773) +- **[Claude Code](./harnesses/claude-code.md)**: run `claude` and follow the OAuth prompt +- **[Codex](./harnesses/codex.md)**: run `codex login` +- **[OpenCode](./harnesses/opencode.md)**: run `opencode auth login` +- **[Pi](./harnesses/pi.md)**: configure provider keys via environment variables +- **[DeepAgents](./harnesses/deepagents.md)**: write provider keys to `~/.deepagents/.env` +- **[T3 Code](./harnesses/t3code.md)**: authenticate one of Claude / Codex / OpenCode, then `npx t3` (browser UI on port 3773) Claude Code remains the documented default. See -[the harnesses overview](./agents/overview) for the full list and +[the harnesses overview](./harnesses/overview) for the full list and per-harness setup. +[Connecting to the Sandbox](/docs/connecting) + If `GH_TOKEN` was set during install, the entrypoint already ran `gh auth login` and `gh auth setup-git` for you. Otherwise run them once inside the shell: diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 89602de..e8e1014 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -42,6 +42,9 @@ importers: '@docusaurus/core': specifier: 3.7.0 version: 3.7.0(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@18.3.1))(debug@4.4.3)(eslint@9.39.4(jiti@2.6.1))(lightningcss@1.32.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.3) + '@docusaurus/plugin-client-redirects': + specifier: 3.7.0 + version: 3.7.0(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@18.3.1))(eslint@9.39.4(jiti@2.6.1))(lightningcss@1.32.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.3) '@docusaurus/preset-classic': specifier: 3.7.0 version: 3.7.0(@algolia/client-search@5.51.0)(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@18.3.1))(@types/react@19.2.14)(eslint@9.39.4(jiti@2.6.1))(lightningcss@1.32.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.17.3)(typescript@5.6.3) @@ -1175,6 +1178,13 @@ packages: react: '*' react-dom: '*' + '@docusaurus/plugin-client-redirects@3.7.0': + resolution: {integrity: sha512-6B4XAtE5ZVKOyhPgpgMkb7LwCkN+Hgd4vOnlbwR8nCdTQhLjz8MHbGlwwvZ/cay2SPNRX5KssqKAlcHVZP2m8g==} + engines: {node: '>=18.0'} + peerDependencies: + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + '@docusaurus/plugin-content-blog@3.7.0': resolution: {integrity: sha512-EFLgEz6tGHYWdPU0rK8tSscZwx+AsyuBW/r+tNig2kbccHYGUJmZtYN38GjAa3Fda4NU+6wqUO5kTXQSRBQD3g==} engines: {node: '>=18.0'} @@ -3698,15 +3708,6 @@ packages: flatted@3.4.2: resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} - follow-redirects@1.15.11: - resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true - follow-redirects@1.16.0: resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} engines: {node: '>=4.0'} @@ -6398,6 +6399,7 @@ packages: uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). hasBin: true value-equal@1.0.1: @@ -8198,6 +8200,39 @@ snapshots: - uglify-js - webpack-cli + '@docusaurus/plugin-client-redirects@3.7.0(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@18.3.1))(eslint@9.39.4(jiti@2.6.1))(lightningcss@1.32.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.3)': + dependencies: + '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@18.3.1))(debug@4.4.3)(eslint@9.39.4(jiti@2.6.1))(lightningcss@1.32.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.3) + '@docusaurus/logger': 3.7.0 + '@docusaurus/utils': 3.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-common': 3.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + eta: 2.2.0 + fs-extra: 11.3.4 + lodash: 4.18.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + tslib: 2.8.1 + transitivePeerDependencies: + - '@docusaurus/faster' + - '@mdx-js/react' + - '@parcel/css' + - '@rspack/core' + - '@swc/core' + - '@swc/css' + - bufferutil + - csso + - debug + - esbuild + - eslint + - lightningcss + - supports-color + - typescript + - uglify-js + - utf-8-validate + - vue-template-compiler + - webpack-cli + '@docusaurus/plugin-content-blog@3.7.0(@docusaurus/plugin-content-docs@3.7.0(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@18.3.1))(eslint@9.39.4(jiti@2.6.1))(lightningcss@1.32.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.3))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@18.3.1))(eslint@9.39.4(jiti@2.6.1))(lightningcss@1.32.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.3)': dependencies: '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@18.3.1))(debug@4.4.3)(eslint@9.39.4(jiti@2.6.1))(lightningcss@1.32.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.3) @@ -9049,7 +9084,7 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 24.12.2 + '@types/node': 25.6.0 '@types/yargs': 17.0.35 chalk: 4.1.2 @@ -9489,11 +9524,11 @@ snapshots: '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 - '@types/node': 24.12.2 + '@types/node': 25.6.0 '@types/bonjour@3.5.13': dependencies: - '@types/node': 24.12.2 + '@types/node': 25.6.0 '@types/chai@5.2.3': dependencies: @@ -9503,11 +9538,11 @@ snapshots: '@types/connect-history-api-fallback@1.5.4': dependencies: '@types/express-serve-static-core': 4.19.8 - '@types/node': 24.12.2 + '@types/node': 25.6.0 '@types/connect@3.4.38': dependencies: - '@types/node': 24.12.2 + '@types/node': 25.6.0 '@types/d3-array@3.2.2': {} @@ -9650,7 +9685,7 @@ snapshots: '@types/express-serve-static-core@4.19.8': dependencies: - '@types/node': 24.12.2 + '@types/node': 25.6.0 '@types/qs': 6.15.0 '@types/range-parser': 1.2.7 '@types/send': 1.2.1 @@ -9680,7 +9715,7 @@ snapshots: '@types/http-proxy@1.17.17': dependencies: - '@types/node': 24.12.2 + '@types/node': 25.6.0 '@types/istanbul-lib-coverage@2.0.6': {} @@ -9706,7 +9741,7 @@ snapshots: '@types/node-forge@1.3.14': dependencies: - '@types/node': 24.12.2 + '@types/node': 25.6.0 '@types/node@17.0.45': {} @@ -9751,16 +9786,16 @@ snapshots: '@types/sax@1.2.7': dependencies: - '@types/node': 24.12.2 + '@types/node': 25.6.0 '@types/send@0.17.6': dependencies: '@types/mime': 1.3.5 - '@types/node': 24.12.2 + '@types/node': 25.6.0 '@types/send@1.2.1': dependencies: - '@types/node': 24.12.2 + '@types/node': 25.6.0 '@types/serve-index@1.9.4': dependencies: @@ -9769,7 +9804,7 @@ snapshots: '@types/serve-static@1.15.10': dependencies: '@types/http-errors': 2.0.5 - '@types/node': 24.12.2 + '@types/node': 25.6.0 '@types/send': 0.17.6 '@types/set-cookie-parser@2.4.10': @@ -9779,7 +9814,7 @@ snapshots: '@types/sockjs@0.3.36': dependencies: - '@types/node': 24.12.2 + '@types/node': 25.6.0 '@types/statuses@2.0.6': optional: true @@ -10058,7 +10093,7 @@ snapshots: axios@1.16.0: dependencies: - follow-redirects: 1.16.0 + follow-redirects: 1.16.0(debug@4.4.3) form-data: 4.0.5 proxy-from-env: 2.1.0 transitivePeerDependencies: @@ -11426,12 +11461,10 @@ snapshots: flatted@3.4.2: optional: true - follow-redirects@1.15.11(debug@4.4.3): + follow-redirects@1.16.0(debug@4.4.3): optionalDependencies: debug: 4.4.3 - follow-redirects@1.16.0: {} - fork-ts-checker-webpack-plugin@6.5.3(eslint@9.39.4(jiti@2.6.1))(typescript@5.6.3)(webpack@5.98.0): dependencies: '@babel/code-frame': 7.29.0 @@ -11861,7 +11894,7 @@ snapshots: http-proxy@1.18.1(debug@4.4.3): dependencies: eventemitter3: 4.0.7 - follow-redirects: 1.15.11(debug@4.4.3) + follow-redirects: 1.16.0(debug@4.4.3) requires-port: 1.0.0 transitivePeerDependencies: - debug @@ -12028,7 +12061,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 24.12.2 + '@types/node': 25.6.0 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -12036,13 +12069,13 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 24.12.2 + '@types/node': 25.6.0 merge-stream: 2.0.0 supports-color: 8.1.1 jest-worker@29.7.0: dependencies: - '@types/node': 24.12.2 + '@types/node': 25.6.0 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1