Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ shaped the way it is, what broke before, which invariants matter, and how
workflows cross files and services. The wiki is plain markdown in your repo,
indexed locally, and reviewed in Git like any other code change.

**Supported today:** macOS with Codex or Claude Code. Requires Python 3.12+.
**Supported today:** macOS with Codex, Claude Code, or OpenCode. Requires Python 3.12+.

## Quickstart

Expand Down Expand Up @@ -138,14 +138,14 @@ Setup installs agent instructions for your chosen tools and three local macOS

| Job | Default schedule | What it does |
| --- | ---: | --- |
| Sync | Every 5 hours | Scans recent Codex and Claude conversations and queues useful knowledge for the relevant registered wiki. |
| Sync | Every 5 hours | Scans recent Codex, Claude, and OpenCode conversations and queues useful knowledge for the relevant registered wiki. |
| Garden | Every 4 hours | Reviews every registered wiki for stale, duplicated, or poorly connected knowledge. |
| Update | Every 24 hours | Checks for and installs CodeAlmanac CLI updates when it is safe to do so. |

These schedules run locally in the background. Use
`codealmanac automation status` to see what is installed.

If you don't have Codex or prefer Claude, use `--runner claude`.
If you don't have Codex, use `--runner claude` or `--runner opencode`.

`--target` only chooses which global agent instruction files to install; it does
not choose the AI runner:
Expand Down Expand Up @@ -231,7 +231,7 @@ should leave the wiki unchanged.
CodeAlmanac can keep registered wikis current without requiring you to remember
maintenance commands.

**Sync** scans local Codex and Claude transcript stores for conversations active
**Sync** scans local Codex, Claude, and OpenCode transcript stores for conversations active
since the previous completed sync. Conversations associated with registered
repositories are queued as ordinary ingest jobs. Sync may decide that a
conversation contains no durable knowledge and leave the wiki unchanged.
Expand Down Expand Up @@ -299,16 +299,18 @@ a script.

CodeAlmanac uses `almanac-yoke` as its single provider boundary. Codex runs
through app-server; Claude uses Yoke's default Claude surface (currently the
Python Agent SDK). Existing Codex or Claude Code OAuth sessions are reused, and
API credentials can be supplied through Yoke when embedding the SDK.
Python Agent SDK); OpenCode runs through its local HTTP server. Existing Codex
or Claude Code OAuth sessions are reused, and API credentials can be supplied
through Yoke when embedding the SDK.

Build, ingest, and garden are packaged as a Yoke agent collection under
`src/codealmanac/agents/`. Each agent uses Yoke's native folder contract:
`agent.yaml` describes tools and permissions, while `instructions.md` contains
the durable agent instructions. A lifecycle run passes only its typed runtime
context as the task prompt. Optional Yoke `skills/`, `subagents/`, and
`workflows/` folders can be added to an agent when the product needs them;
native Claude or Codex execution still decides how and when to use them.
native Claude, Codex, or OpenCode execution still decides how and when to use
them.

```bash
codex login
Expand Down
4 changes: 2 additions & 2 deletions almanac/architecture/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ sources:
- id: provider-adapters
type: wiki
path: architecture/agent-runs/provider-adapters
note: Architecture page for Codex and Claude provider adapters.
note: Architecture page for Codex, Claude, and OpenCode provider adapters.
- id: source-runtime
type: wiki
path: architecture/sources/source-resolution-and-runtime
Expand Down Expand Up @@ -134,7 +134,7 @@ For authored wiki contracts, read [Page identity](wiki/page-identity), [Path nor

## Edges And Interfaces

Use [CLI adapter boundary](cli/adapter-boundary) and [Terminal output](cli/terminal-output) for command entrypoints and rendering [@cli-adapter] [@terminal-output]. Use [Harness contract](agent-runs/harness-contract) and [Provider adapters](agent-runs/provider-adapters) for Codex and Claude execution boundaries [@harness-contract] [@provider-adapters]. Use [Source resolution and runtime](sources/source-resolution-and-runtime) when changing ingest inputs or source adapters [@source-runtime].
Use [CLI adapter boundary](cli/adapter-boundary) and [Terminal output](cli/terminal-output) for command entrypoints and rendering [@cli-adapter] [@terminal-output]. Use [Harness contract](agent-runs/harness-contract) and [Provider adapters](agent-runs/provider-adapters) for Codex, Claude, and OpenCode execution boundaries [@harness-contract] [@provider-adapters]. Use [Source resolution and runtime](sources/source-resolution-and-runtime) when changing ingest inputs or source adapters [@source-runtime].

[Agents and manuals](runtime-resources/prompts-and-manuals) covers the packaged Yoke agents and writing references used by lifecycle runs, and [Setup automation and update](setup/automation-and-update) covers setup-owned scheduler and update behavior [@agents-manuals] [@setup-automation].

Expand Down
2 changes: 1 addition & 1 deletion almanac/architecture/agent-runs/harness-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ A harness adapter has one `kind`, a `check()` method, and a `run()` method [@har

`HarnessesService` indexes adapters by kind, rejects duplicate adapters, and provides the single service entrypoint used by workflows [@harness-service]. Before running an adapter, it calls `ensure_ready`; unavailable harnesses fail early with a message that can include a repair hint and a command to switch to another registered harness [@harness-service].

The supported harness kinds are currently `codex` and `claude`. The terminal run statuses are `succeeded`, `failed`, and `cancelled` [@harness-kinds].
The supported harness kinds are currently `codex`, `claude`, and `opencode`. The terminal run statuses are `succeeded`, `failed`, and `cancelled` [@harness-kinds].

## Run Requests

Expand Down
22 changes: 14 additions & 8 deletions almanac/architecture/agent-runs/provider-adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,20 @@ sources:
## What It Owns

CodeAlmanac has one provider integration: `YokeHarnessAdapter`. It implements
the service-owned harness port for Claude and Codex, selects CodeAlmanac's Yoke
surface and run options, and converts Yoke runs and events into durable product
models [@adapter] [@contract].

The adapter explicitly selects Codex app-server and leaves Claude on Yoke's
default Claude surface. It loads the requested build, ingest, or garden agent
from the packaged Yoke collection, forwards the task prompt unchanged, and
applies the trusted non-interactive permission and timeout policy [@adapter].
the service-owned harness port for Claude, Codex, and OpenCode, selects
CodeAlmanac's Yoke surface and run options, and converts Yoke runs and events
into durable product models [@adapter] [@contract].

The adapter explicitly selects Codex app-server and leaves Claude and OpenCode
on Yoke's own default surface for each — Claude and OpenCode each currently
have exactly one implemented Yoke surface, so there is nothing to disambiguate
by pinning, unlike Codex's three. It loads the requested build, ingest, or
garden agent from the packaged Yoke collection, forwards the task prompt
unchanged, and applies the trusted non-interactive permission and timeout
policy [@adapter]. OpenCode has no dedicated `ProviderOptions` surface in Yoke
yet, so `provider_options()` passes an empty `ProviderOptions()` for it —
OpenCode's adapter always applies its own allow-all permission block at
session creation regardless [@adapter].

## Runtime Root

Expand Down
2 changes: 1 addition & 1 deletion almanac/architecture/composition-root.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ The composition root is `src/codealmanac/app.py`. It is the one place that build

`AppAdapters` is the injection surface for outside systems. It can carry harness adapters, transcript discovery adapters, source runtime adapters, a scheduler, a worker spawner, update command providers, instruction installers, global-state removers, and package uninstallers [@app-root]. Each field is optional, so production construction can use defaults while tests can pass fakes.

This design keeps provider selection at the edge. If no harness adapters are supplied, the root uses `default_harness_adapters(local_state.harness_runtime_dir)`, giving both the Claude and Codex adapters a product-owned cache directory under local state instead of the target repository [@app-root]. If no scheduler is supplied, automation uses `LaunchdSchedulerAdapter`. If no source adapters are supplied, the source service receives the default transcript discovery and runtime adapters [@app-root]. The services receive port implementations, not provider-specific branching logic. See [Yoke harness boundary](agent-runs/provider-adapters) for what the runtime directory is used for.
This design keeps provider selection at the edge. If no harness adapters are supplied, the root uses `default_harness_adapters(local_state.harness_runtime_dir)`, giving the Claude, Codex, and OpenCode adapters a product-owned cache directory under local state instead of the target repository [@app-root]. If no scheduler is supplied, automation uses `LaunchdSchedulerAdapter`. If no source adapters are supplied, the source service receives the default transcript discovery and runtime adapters [@app-root]. The services receive port implementations, not provider-specific branching logic. See [Yoke harness boundary](agent-runs/provider-adapters) for what the runtime directory is used for.

## Service Assembly

Expand Down
22 changes: 17 additions & 5 deletions almanac/architecture/setup/instruction-installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ sources:
type: file
path: src/codealmanac/integrations/setup/codex.py
note: Codex installation mechanics (managed block inside AGENTS.md).
- id: opencode-installer
type: file
path: src/codealmanac/integrations/setup/opencode.py
note: OpenCode installation mechanics (managed block inside its own global AGENTS.md).
- id: managed-blocks
type: file
path: src/codealmanac/integrations/setup/managed_blocks.py
Expand Down Expand Up @@ -82,8 +86,8 @@ package stable Yoke agent instructions and expose the manuals those operations u

## Per-Target Mechanics

Claude and Codex are installed differently because their global instruction
files have different conventions.
Claude, Codex, and OpenCode are installed differently because their global
instruction files have different conventions.

For Claude, the installer writes the guide verbatim to a dedicated file,
`~/.claude/codealmanac.md`, and ensures `~/.claude/CLAUDE.md` contains a
Expand All @@ -101,9 +105,17 @@ non-empty content, otherwise `~/.codex/AGENTS.md` [@codex-installer].
Uninstall removes the managed block from both possible paths and deletes a
file that becomes empty [@codex-installer].

Both installers report an `InstructionChange` naming the `SetupTarget`,
For OpenCode, the installer upserts the guide into OpenCode's own global
rules file, `~/.config/opencode/AGENTS.md`, using the same managed-block
markers as Codex [@opencode-installer] [@managed-blocks]. Unlike Codex there
is no override-file convention here — OpenCode has no documented equivalent
of `AGENTS.override.md`, so this installer only ever targets the one path
[@opencode-installer]. Uninstall removes the managed block and deletes the
file if it becomes empty [@opencode-installer].

All installers report an `InstructionChange` naming the `SetupTarget`,
whether anything changed, which paths were touched, and a human-readable
message, so `setup`'s JSON and interactive output can say "already installed"
without rewriting untouched files [@setup-models] [@claude-installer]
[@codex-installer]. This mirrors the idempotence expectations described in
[Setup automation and update](automation-and-update).
[@codex-installer] [@opencode-installer]. This mirrors the idempotence
expectations described in [Setup automation and update](automation-and-update).
12 changes: 11 additions & 1 deletion almanac/architecture/sources/source-resolution-and-runtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ sources:
type: file
path: src/codealmanac/integrations/sources/transcripts/claude.py
note: Claude transcript discovery path and metadata parsing.
- id: opencode_transcripts
type: file
path: src/codealmanac/integrations/sources/transcripts/opencode.py
note: OpenCode transcript discovery, querying its own SQLite session table.
- id: opencode_transcript_runtime
type: file
path: src/codealmanac/integrations/sources/transcripts/opencode_runtime.py
note: OpenCode transcript runtime reading from its message/part tables.
- id: sync_evaluation
type: file
path: src/codealmanac/workflows/sync/evaluation.py
Expand Down Expand Up @@ -54,10 +62,12 @@ Ingest uses this boundary before it renders the writing prompt. It resolves the

## Transcript Discovery

Transcript discovery is a separate source path used by sync. The default discovery set has two adapters: Claude and Codex [@transcript_adapters]. The source model has the same two transcript app values, `claude` and `codex`, so there is no separate app identity for Codex app, Claude Desktop, Claude web, or editor-specific surfaces [@source_models].
Transcript discovery is a separate source path used by sync. The default discovery set has three adapters: Claude, Codex, and OpenCode [@transcript_adapters]. The source model has the matching transcript app values, `claude`, `codex`, and `opencode` [@source_models].

The Codex adapter scans `.codex/sessions` under the configured home directory, reads the first JSONL lines for session metadata, and skips transcripts whose metadata marks `thread_source` as `subagent` [@codex_transcripts]. The Claude adapter scans `.claude/projects` under the configured home directory and skips paths that contain `subagents` [@claude_transcripts].

OpenCode has no per-session file to scan — its session history lives as rows in one shared SQLite database (`~/.local/share/opencode/opencode.db`), not one file per session. Its discovery adapter queries that database's `session` table directly, filtering to `parent_id IS NULL` to exclude sub-agent sessions, the same idea as Codex's `thread_source` filtering [@opencode_transcripts]. Because there is no file to point at, `TranscriptCandidate.transcript_path` holds a synthetic, non-filesystem identity string (`opencode-session:<id>`) for OpenCode candidates instead of a real path — every existing consumer of that field (sync's evaluation, guidance, summary, and queue rendering) only ever displays or sorts it as an opaque string, so this needed no changes there [@transcript_adapters]. `OpencodeTranscriptRuntimeAdapter` recognizes that scheme prefix and resolves it by querying the database's `message`/`part` tables for the session's assistant-authored content, rather than opening a file [@opencode_transcript_runtime]. It is registered ahead of the generic `TranscriptSourceRuntimeAdapter` in the runtime adapter list specifically because that adapter claims every `SourceKind.TRANSCRIPT` ref unconditionally — dispatch order here is a real invariant, not incidental [@transcript_adapters].

Sync does not ingest every discovered transcript. It matches each transcript `cwd` to a registered repository root, skips unregistered working directories, and skips transcripts older than the active sync window as `inactive` [@sync_evaluation]. That means a transcript can be discovered correctly but still not become ingest input for the current sync run.

## Related Reference
Expand Down
4 changes: 2 additions & 2 deletions almanac/reference/local-state-layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ This layout follows the local-only Python product decision: authored Markdown st
| `~/.codealmanac/repos/<repo-id>/index.db` | Derived runtime | Per-repository search and graph index. |
| `~/.codealmanac/update.lock` | Local runtime | Global update lock. |
| `~/.codealmanac/logs/` | Local runtime | Scheduler stdout and stderr logs. |
| `~/.codealmanac/harnesses/` | Local runtime | Product-owned cache root Yoke uses to compile Claude and Codex provider resources, so provider config is never written into a repository. |
| `~/.codealmanac/harnesses/` | Local runtime | Product-owned cache root Yoke uses to compile Claude, Codex, and OpenCode provider resources, so provider config is never written into a repository. |

`core.paths` defines the default state directory as `Path.home() / ".codealmanac"`, the default database as `~/.codealmanac/codealmanac.db`, the default config as `~/.codealmanac/config.toml`, and scheduler logs as `~/.codealmanac/logs` [@core-paths].

Expand All @@ -66,7 +66,7 @@ This layout follows the local-only Python product decision: authored Markdown st

The database path must live directly inside the state directory. `LocalStatePaths` rejects a shape where the database is nested more deeply than `state_dir / "codealmanac.db"` would imply [@settings].

`LocalStatePaths.harness_runtime_dir` is `state_dir / "harnesses"` [@settings]. The composition root passes it to both provider adapters so Yoke's native Claude and Codex compilation caches under CodeAlmanac's own local state instead of the target repository; see [Yoke harness boundary](../architecture/agent-runs/provider-adapters) for how the adapter uses this path for run caching and readiness checks [@yoke-adapter].
`LocalStatePaths.harness_runtime_dir` is `state_dir / "harnesses"` [@settings]. The composition root passes it to all three provider adapters so Yoke's native Claude, Codex, and OpenCode compilation caches under CodeAlmanac's own local state instead of the target repository; see [Yoke harness boundary](../architecture/agent-runs/provider-adapters) for how the adapter uses this path for run caching and readiness checks [@yoke-adapter].

## Per-Repository Runtime

Expand Down
Loading