docks-kit 0.15.4 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -4,17 +4,20 @@ Canonical instructions for coding agents working on this project. Compatible wit
4
4
 
5
5
  ## Repository purpose
6
6
 
7
- Portable configuration kit for AI coding agents. Per-tool Single Source of Truth
8
- (SoT) directories deploy to each tool's user-config location through
9
- `./docks-kit sync`. Clone once to get a consistent AI-assisted environment on
10
- supported Linux, macOS, and Windows hosts. The kit focuses on **token efficiency
11
- without sacrificing quality**. Every setting and hook minimizes token use while
7
+ Portable configuration kit for AI coding agents. The kit manages three coding
8
+ harnesses: Claude Code, Codex, and Oh My Pi (`omp`). Per-tool Single Source of
9
+ Truth (SoT) directories deploy to each harness's user-config location through
10
+ `./docks-kit sync`. The kit also deploys shared [agentskills.io](https://agentskills.io)
11
+ skills. Clone once to get a consistent AI-assisted environment on supported
12
+ Linux, macOS, and Windows hosts. The kit focuses on **token efficiency without
13
+ sacrificing quality**. Every setting and hook minimizes token use while
12
14
  preserving rigorous output. When you add or edit anything, ask: *does this
13
15
  change reduce tokens without weakening correctness?*
14
16
 
15
17
  Tool-specific instructions live alongside this file:
16
18
  - **`CLAUDE.md`** — Claude Code SoT (`SoT/.claude/`), env vars, hooks, plugins, status line, session management, permission mode, open concerns.
17
19
  - Codex uses this `AGENTS.md` file plus the Codex SoT under `SoT/.codex/`; no separate root `CODEX.md` is needed.
20
+ - omp uses this `AGENTS.md` file plus the omp SoT under `SoT/.omp/`.
18
21
 
19
22
 
20
23
  docks-kit runtime and standalone binary support covers Linux x64/arm64, macOS
@@ -27,17 +30,19 @@ launcher can fall back to Bun source.
27
30
  |------|---------|
28
31
  | `docks-kit` / `docks-kit.ps1` | POSIX and Windows CLI launchers. On supported hosts, each runs the matching binary in `cli/dist/` only when its `--version` matches `package.json`. Otherwise it runs Bun-from-source and auto-installs Bun plus `node_modules`. Hosts outside the support matrix fail before source fallback. The standalone platform release binary provides no-Bun recovery. |
29
32
  | `cli/src/engine-native/` | EngineNative implementation for `sync`, `model`, and `toolchain`; idempotent, flag-gated for destructive reconciliation |
33
+ | `cli/src/engine-native/ompSync.ts` | omp file deployment, marketplace registration, and plugin synchronization |
30
34
  | `cli/` | Effect 4 RC CLI + bundled docs topics |
31
35
  | `SoT/models.json` | Kit-verified Claude and Codex model catalog |
32
36
  | `SoT/toolchain.json` | Toolchain floors manifest (verified pins consumed by EngineNative) |
33
37
  | `SoT/.claude/bin/` | Dependency-free Bun runtime programs for Claude's statusline, SessionStart, and Notification |
38
+ | `SoT/.omp/` | Kit-owned omp SoT: `AGENTS.md`, `config.yml`, `mcp.json`, and `intercom.json` |
34
39
  | `install.sh` / `install.ps1` | POSIX and Windows global installers |
35
40
  | `.github/workflows/release-cli.yml` | `cli-v*` release: six binaries for Linux, macOS, and Windows on x64 and arm64, plus `SHA256SUMS` and npm publish |
36
41
  | `README.md` | Front door |
37
42
  | `package.json` / `bun.lock` | npm package: `bin` = `cli/src/main.ts`; bundles `cli/` with the generated in-memory SoT payload |
38
43
  | `SoT/.agents/skills.txt` | Universal-skill manifest, intentionally empty by default. Adding an [agentskills.io](https://agentskills.io/specification) slug opts it into EngineNative's shared `~/.agents/skills/` bootstrap and Claude symlink. |
39
44
  | `notification.mp3` | Audio asset for Notification hooks (consumed by Claude Code today; tool-agnostic file) |
40
- | `docs/plans/` | Multi-commit work-item plans (`active/` with status in frontmatter, plus `finished/` archive). Convention: `docs/plans/AGENTS.md` |
45
+ | `docs/PLAN.md` | Plan record standard. The plan record is a GitHub issue; no plan markdown is tracked. `docs/AGENTS.md` routes to it and `docs/CLAUDE.md` contains only `@AGENTS.md`. |
41
46
  | `CLAUDE.md` | Claude-specific instructions; imports this `AGENTS.md` |
42
47
  | `AGENTS.md` | This file — tool-agnostic instructions |
43
48
 
@@ -48,10 +53,19 @@ Codex SoT notes:
48
53
  - `SoT/.codex/plugins/marketplace.json` deploys to Codex's personal marketplace path at `~/.agents/plugins/marketplace.json`; when the `codex` CLI is available, sync reruns `codex plugin add <plugin@marketplace>` for enabled SoT plugins so stale cached installs are refreshed.
49
54
  - The global prompt SoTs carry the owner's standing authorization for Docks cross-company plan review, which never overrides host or platform denial.
50
55
  - The `codex` CLI binary is upstream-owned, not kit-owned. The official standalone installer keeps package metadata under `$CODEX_HOME/packages/standalone` and places the `codex` symlink in `~/.local/bin` by default; sync only warns with a download-then-run installer command when the CLI is missing. Existing installs can self-update with `codex update`; npm and Homebrew remain upstream alternatives.
56
+
51
57
  - Claude runtime settings are an authoring template with sentinels. `claudeRuntime.ts` materializes absolute Bun/script paths only after the shared `bun.ts` bootstrap is ready; `claudeSync.ts` writes all runtime assets before atomically committing settings, then prunes the legacy shell scripts and Stop hook. Native `rate_limits` is the sole quota source, so jq/curl/OAuth caches are not runtime dependencies. A missing Bun defers only this cutover and preserves legacy pointers/files.
52
58
  - Claude's deployed SoT defaults are `model: opus` and `effortLevel: high`; `advisorModel` is deliberately absent/off. `--claude-advisor=on` is the per-machine opt-in and writes `advisorModel: fable` after the settings merge. `opus` is the alias, not a pinned id: the `minimumVersion` floor of 2.1.219 ensures Claude Code can resolve it to the newest Opus its provider offers — Opus 5 on the Anthropic API or Opus 4.6 on Microsoft Foundry — instead of silently capping Anthropic API users at Opus 4.8 under the former 2.1.170 floor. Keeping the alias provides provider portability and tracks future Opus releases; the literal `claude-opus-5` is unavailable on Foundry. The floor also subsumes Fable 5's older 2.1.170 requirement.
53
59
 
54
- For per-tool SoT layouts (`SoT/.claude/`, `SoT/.codex/`), see the matching SoT directory.
60
+ omp SoT notes:
61
+ - `SoT/.omp/AGENTS.md`, `config.yml`, and `mcp.json` deploy to `~/.omp/agent/`.
62
+ - `SoT/.omp/intercom.json` deploys to `$PI_CODING_AGENT_DIR/intercom/config.json`. The default root is `~/.pi/agent`.
63
+ - `ompSync.ts syncConfig` deep-merges `config.yml` through `mergeOmpConfig`.
64
+ - Sync registers the `docks` marketplace. It installs or upgrades `docks@docks` and `plan-lifecycle@docks` at user scope.
65
+ - Sync installs `pi-intercom` at the verified version from `SoT/toolchain.json`.
66
+ - The omp CLI is upstream-owned and self-updating through `omp update`. Sync never installs or upgrades the CLI.
67
+
68
+ For per-tool SoT layouts (`SoT/.claude/`, `SoT/.codex/`, `SoT/.omp/`), see the matching SoT directory.
55
69
 
56
70
  ## Engineering rules
57
71
 
@@ -59,19 +73,20 @@ For per-tool SoT layouts (`SoT/.claude/`, `SoT/.codex/`), see the matching SoT d
59
73
  - **Removed bash engine.** The bash engine was removed after the `bash-engine-final` tag. `DOCKS_KIT_ENGINE=bash` must fail with the removed-engine message; engine bugs are fixed forward in EngineNative.
60
74
  - **Effect 4 CLI stack.** The CLI pins `effect@4.0.0-rc.109` (including `effect/unstable/cli`), `@effect/platform-bun@4.0.0-rc.109` (`BunServices.layer`, `BunRuntime.runMain`), `@effect/vitest@4.0.0-rc.109`, and `vitest@4.1.10` (required by the `@effect/vitest` peer range). `@effect/cli` and `@effect/platform` are removed and must not be reintroduced.
61
75
  - **Effect skill routing.** Effect work in this checkout must verify migration and API call shapes against the installed declarations under `node_modules/effect/dist/unstable/cli/`, never from memory or a mutable dist-tag. The `effect-ts-setup`, `effect-ts-port`, and `effect-ts-specialist` skills target Effect 3.x and do not apply.
62
- - **Targeted syncs.** `./docks-kit sync` accepts positional targets: `claude`, `codex`, and `agents`. Use the narrowest target that matches the SoT change (for example, `./docks-kit sync codex` for Codex-only config edits); targets can be combined with `--dry-run`, `--skip-bubblewrap` (skip optional bubblewrap bootstrap for the Codex Linux sandbox), `--skip-plugin-refresh` (install missing plugins without refreshing existing caches; used by `docks-kit update`), `--reconcile`, `--prune`, and the deploy-time modifiers `--claude-compact-window=<tokens>` / `--claude-permissive` / `--claude-model=<m>` / `--claude-effort=<level>` / `--claude-advisor=<on|off|default>` / `--codex-model=<m>` / `--codex-effort=<level>` (see `CLAUDE.md` § Deploy-time modifiers).
76
+ - **Targeted syncs.** `./docks-kit sync` accepts positional targets: `claude`, `codex`, `agents`, and `omp`. Use the narrowest target that matches the SoT change (for example, `./docks-kit sync omp` for omp-only config edits); targets can be combined with `--dry-run`, `--skip-bubblewrap` (skip optional bubblewrap bootstrap for the Codex Linux sandbox), `--skip-plugin-refresh` (install missing plugins without refreshing existing caches; used by `docks-kit update`), `--reconcile`, `--prune`, and the deploy-time modifiers `--claude-compact-window=<tokens>` / `--claude-permissive` / `--claude-model=<m>` / `--claude-effort=<level>` / `--claude-advisor=<on|off|default>` / `--codex-model=<m>` / `--codex-effort=<level>` (see `CLAUDE.md` § Deploy-time modifiers).
77
+ - **Per-machine harness selection.** `~/.docks-kit/state.json` drives a flag-less sync. A missing file selects `claude`, `codex`, and `agents`; it never selects `omp` implicitly. `sync` never prompts and never writes the selection file. `docks-kit harnesses` is the only command that writes the selection.
63
78
  - **Additive by default.** Keys present in deployed config but absent from SoT are preserved on default sync. This protects user-only additions, but means drift accumulates — neither flag-less reset can clean it up. The one exception is the Claude `removed` manifest (`claude::_removed_manifest`), a curated list of unambiguous kit-owned artifacts that `claude::sync_removals` force-prunes on every sync, including the home-relative `~/.local/bin/session-relay` artifact installed outside `~/.claude`; see `CLAUDE.md` § Pruning stale artifacts.
64
79
  - **`--reconcile` / `--prune` are the kit-owned reconcile flags.** Orthogonal — `--reconcile` reconciles the settings layer (SoT-declared keys/tables/arrays win; user-only keys and nested objects are preserved; permissions arrays are replaced wholesale by SoT). `--prune` uninstalls kit-managed installations not in the SoT (plugins, marketplaces, and `~/.agents/skills/*` entries tracked in `~/.agents/.kit-managed-skills`). Combine for a full reset to SoT's kit-managed scope. User-only additions outside the kit's scope (custom env vars, mcpServers, manually-installed skills, third-party plugins not declared in SoT) are always preserved. Each tool's per-tool file documents the specific paths and diff recipes.
65
- - **SOLID-aligned modules.** `cli/src/engine-native/parseArgs.ts` owns flag parsing/validation. `toolchain.ts` owns verified-version floor reporting over `SoT/toolchain.json`; `bun.ts` owns the shared, memoized Bun bootstrap; `claudeRuntime.ts` owns Claude settings materialization. `claudeSync.ts`, `codexSync.ts`, and `skillsSync.ts` own tool-specific sync logic. `index.ts` is the thin orchestrator. The public CLI seam is `cli/src/engine.ts`.
80
+ - **SOLID-aligned modules.** `cli/src/engine-native/parseArgs.ts` owns flag parsing and validation. `toolchain.ts` owns verified-version floor reporting over `SoT/toolchain.json`; `bun.ts` owns the shared, memoized Bun bootstrap; `claudeRuntime.ts` owns Claude settings materialization. `claudeSync.ts`, `codexSync.ts`, and `skillsSync.ts` own their tool-specific sync logic. `ompSync.ts` owns omp file and plugin sync. `ompYaml.ts` owns the omp YAML merge. `harnesses.ts` owns per-machine harness selection. `index.ts` is the thin orchestrator. The public CLI seam is `cli/src/engine.ts`.
66
81
  - **Small, reviewable changes.** Bundled multi-concern PRs are harder to review and revert. Split an engine/CLI change and a per-tool config change unless the change requires atomicity.
67
82
  - **Dry-run before destructive flags.** Always preview with `./docks-kit sync --dry-run` (or the relevant `diff <(jq -S …)` recipe in the per-tool file) before invoking `--reconcile` or `--prune`. User-added permissions / env vars / plugins absent from SoT will be discarded.
68
- - **SoT prompt files are rules, not explanation.** `SoT/.claude/CLAUDE.md` and `SoT/.codex/AGENTS.md` are loaded into every agent session's prompt context — every line costs prompt tokens on every turn for every user. Restrict their content to rules, heuristics, and `<constraint>` blocks the agent must *act on* during a turn. Do NOT add inline source citations (`Source: …`, attributed quotes), "why this rule exists" preface text, version-watermarking trivia (e.g. "Distilled from X v2.0, captured 2025-11-07"), per-bug workarounds, or installation instructions. Provenance, motivation, and historical context belong in `CLAUDE.md` / `AGENTS.md` at the repo root (humans read once) or in commit messages — never in the SoT. For every line, apply the official test: would removing it cause the agent to make mistakes? If not, cut it — over-instruction degrades adherence on current frontier models.
83
+ - **SoT prompt files are rules, not explanation.** `SoT/.claude/CLAUDE.md`, `SoT/.codex/AGENTS.md`, and `SoT/.omp/AGENTS.md` are loaded into agent sessions' prompt context — every line costs prompt tokens on every turn for every user. Restrict their content to rules, heuristics, and `<constraint>` blocks the agent must *act on* during a turn. Do NOT add inline source citations (`Source: …`, attributed quotes), "why this rule exists" preface text, version-watermarking trivia (e.g. "Distilled from X v2.0, captured 2025-11-07"), per-bug workarounds, or installation instructions. Provenance, motivation, and historical context belong in `CLAUDE.md` / `AGENTS.md` at the repo root (humans read once) or in commit messages — never in the SoT. For every line, apply the official test:
69
84
  - **Cache-invariance for kit-authored prompt surfaces.** Never put timestamps, counters, or mutable state into SoT prompt files, hook outputs that land in the cached prefix, or tool definitions — cache breaks force cold-start writes. Dynamic context belongs in runtime-injected messages (e.g. SessionStart hook output), which is exactly how the kit's date/config injection works.
70
85
 
71
86
  ## Code style
72
87
 
73
88
  - Bash: for launchers/installers/hook assets, use `set -euo pipefail`, quoted variables, `[[ ]]` over `[ ]`, and function-scoped `local`.
74
- - JSON config: edit the SoT (`SoT/<tool>/`) and run `./docks-kit sync`. Never edit deployed config (`~/.claude/`, `~/.codex/`) directly.
89
+ - JSON and YAML config: edit the SoT (`SoT/<tool>/`) and run `./docks-kit sync`. Never edit deployed config under `~/.claude/`, `~/.codex/`, or `~/.omp/agent/` directly.
75
90
 
76
91
  ## Security
77
92
 
@@ -87,7 +102,23 @@ Use direct acceptance and focused regressions while iterating, then run the full
87
102
 
88
103
  ## Skills
89
104
 
90
- This project ships **kit-mechanic skills** under `.claude/skills/` — narrowly-scoped references for how EngineNative works (settings merge, plugin bootstrap, universal-skill install, Codex TOML merge, sync orchestration). They cost prompt tokens only inside this repo's sessions and document regression-prone TypeScript sync logic in `cli/src/engine-native/`. **Pipeline content** (multi-agent slash commands, refactor/security/docs workflows, parallel-scanner agents) belongs in the separate [DocksDocks/docks](https://github.com/DocksDocks/docks) plugin — not here. Project-level agents under `.claude/agents/` follow the same rule: kit-mechanic agents that wrap kit-mechanic skills are permitted; pipeline agents live in the docks plugin.
105
+ **Project-skill scope.** Keep project skills within two classes: kit-mechanic skills and the `unslop` repo-prose skill.
106
+ Canonical project skills live in `.agents/skills/<name>/SKILL.md`.
107
+ Each `.claude/skills/<name>` entry is a relative symlink to its canonical directory.
108
+ Codex reads `.agents/skills/` natively, so one copy serves both tools.
109
+ Kit-mechanic skills document regression-prone TypeScript sync logic in `cli/src/engine-native/`.
110
+ Kit-mechanic skills: `codex-config-merge-context`, `engine-native-context`, `plugin-bootstrap-context`, `settings-merge-context`, `sync-orchestration-context`, `toolchain-context`, `universal-skills-context`, and `omp-sync-context`.
111
+ **Pipeline content** (multi-agent slash commands, refactor/security/docs workflows, parallel-scanner agents) belongs in the separate [DocksDocks/docks](https://github.com/DocksDocks/docks) plugin — not here. Project-level agents under `.claude/agents/` follow the same rule: kit-mechanic agents that wrap kit-mechanic skills are permitted; pipeline agents live in the docks plugin.
112
+
113
+ **Windows checkouts.** These nine entries are the only symlinks this repository tracks. Git for Windows defaults to `core.symlinks=false`, which checks a symlink out as a plain file holding the target path, so Claude Code finds no `SKILL.md` and silently loads no project skill. Codex is unaffected, because it reads the real directories under `.agents/skills/`. To restore Claude Code on Windows, enable Developer Mode or run as administrator, then `git config core.symlinks true` and re-checkout the paths: `git checkout -- .claude/skills`. Never repair this by replacing an entry with a copy; two copies drift, which is the failure the canonical layout removes. This limitation is confined to project skills in this checkout. It does not affect the deployed user-level skills, where `skillsSync.ts linkOrCopyWithWarnings` already falls back from symlink to junction to marked copy.
114
+
115
+ `npx skills add cursor/plugins -s unslop -y -a claude-code codex` installs `unslop`, and root `skills-lock.json` pins it.
116
+ The repository vendors upstream `unslop` text and pins it by the lockfile hash, so never edit it locally.
117
+ Upstream licenses `pstack/` under MIT (Copyright (c) 2026 Lauren Tan), so redistribution is permitted.
118
+ MIT requires the notice to travel with the copy, so `.agents/skills/unslop/LICENSE` vendors it verbatim and must stay beside `SKILL.md`.
119
+ The `unslop` "adding soul" guidance conflicts with the global Simplified Technical English Output Standard.
120
+ The Output Standard wins.
121
+ Use `unslop` only for its pattern-detection lists.
91
122
 
92
123
  <constraint>
93
124
  When a kit-mechanic skill, its `references/`, or a wrapper agent (`.claude/agents/*.md` + its `.codex/agents/*.toml` twin) cites EngineNative internals, name the **module + exported/local function + semantic anchor** (e.g. `claudeSync.ts syncPlugins, pass 5 uninstall guard`) — never a raw `file:NNN` line number, which goes stale on every refactor. Keep exactly one coarse `metadata.source_files[].lines` range per skill file as the sole intentional line-number touchpoint.
@@ -98,42 +129,78 @@ When a kit-mechanic skill, its `references/`, or a wrapper agent (`.claude/agent
98
129
  ## Plans
99
130
 
100
131
  Use direct implementation for one clear, reversible, low-risk local diff with one
101
- bounded acceptance path; it creates no tracked plan, reviewer, or automatic
132
+ bounded acceptance path; it creates no plan issue, reviewer, or automatic
102
133
  commit. Use a canonical plan for explicit planning, multi-commit or
103
134
  cross-repository work, cold handoff, an unresolved decision, a cross-subsystem or
104
135
  public-contract change, security-sensitive or destructive work, or any
105
136
  non-`local` effect.
106
137
 
107
138
  <constraint>
108
- Canonical plans live in `docs/plans/active/`; status is frontmatter and
109
- `docs/plans/finished/` is terminal. Exactly three skills own the workflow:
110
- `plan-workspace` maintains the workspace; main-context `plan-manager` runs six
111
- phases decide, draft, research, one plan review, implement, code review and
112
- archives; internal `plan-reviewer` returns a readable pre-implementation verdict.
113
- Two read-only reviewer wrappers ship, `plan-reviewer` and `code-reviewer`, and
114
- nothing else in the lifecycle has a wrapper.
139
+ The plan record is a GitHub issue. Its body starts with
140
+ `<!-- plan-contract: v3 -->`, then a blank line and the exact eight `##`
141
+ sections; it has no frontmatter. GitHub owns title, open-work phase, owner,
142
+ timestamps, and completion, and no plan markdown is tracked in the repository.
143
+ Exactly three skills own the workflow: `plan-workspace` maintains the workspace;
144
+ main-context `plan-manager` runs six phases - decide, draft, research, plan
145
+ review, implement, code review - with bounded repair and fresh re-review in both
146
+ review phases, then archives; internal `plan-reviewer` returns one readable
147
+ pre-implementation verdict block per round. Two read-only reviewer wrappers
148
+ ship, `plan-reviewer` and `code-reviewer`, and nothing else in the lifecycle has
149
+ a wrapper.
115
150
  </constraint>
116
151
 
117
- The record is markdown only: `plan_contract: v2` frontmatter plus eight `##`
118
- sections — `## Goal`, `## Research`, `## Steps`, `## Acceptance`,
119
- `## Do not touch`, `## Open questions`, `## Review`, `## Verification Results`.
120
- There are no hashes, permits, run identities, locks, bundles, or `v2`/`vN` plan
121
- files, and the `plan.mjs` shipped inside the installed `plan-lifecycle` plugin
122
- is the only lifecycle tool. This lifecycle creates zero commits and never
123
- pushes; commit when the user asks, under `docks:commit-discipline`.
152
+ After the marker and blank line, the record carries exactly `## Goal`,
153
+ `## Research`, `## Steps`, `## Acceptance`, `## Do not touch`,
154
+ `## Open questions`, `## Review`, and `## Verification Results`, in that order
155
+ and once each. `## Goal` carries exactly one mode line. Open-work phase is one
156
+ of `drafting`, `planned`, `ongoing`, or `blocked` in a `plan:<phase>` label; a
157
+ blocked plan starts `## Open questions` with `Blocked: <one-line reason>`.
158
+ Closed completion derives from GitHub `state` and `stateReason`. `## Review`
159
+ contains exactly `_Review records are stored in issue comments._`. Each reviewer
160
+ returns one markdown block, and the manager posts that whole block as one issue
161
+ comment. The latest trusted well-formed record per review kind wins; its author
162
+ must equal the plan's sole assignee. A legacy body verdict is consulted only
163
+ when no trusted comment record exists for that kind. Both review phases use
164
+ fresh inputs and run at most five rounds, stopping on pass, no progress, a
165
+ finding surviving its fix, or `repair` or `fixes-required` in round five. A
166
+ plan-review `blocked` verdict always routes its user-only decision through
167
+ `## Open questions` and `ask`.
168
+
169
+ The record carries no hash, permit, run identity, lock, or bundle, and the
170
+ `plan.mjs` shipped inside the installed `plan-lifecycle` plugin is the only
171
+ lifecycle tool. An `export` writes the sha256 of the body it copied beside the
172
+ copy so a stale copy cannot revert the record; that digest detects staleness and
173
+ authorizes nothing. Routine plan issue publication, implement-start linked
174
+ branch creation, commits, normal pushes, and the closing pull request carry the
175
+ settled mode's authorization and need no repeated prompt. Before any branch
176
+ checkout, including `gh issue develop --checkout`, require
177
+ `git status --porcelain` to be empty. If it is dirty, never stash, move, or
178
+ commit ambient work; set the plan `blocked` and name the dirty paths, or use an
179
+ authorized clean worktree.
180
+ Immediately after setting the plan `ongoing`, every `gh issue develop` call uses
181
+ `--repo`; the manager reuses a linked branch or creates one with
182
+ `--base <default> --checkout`, then re-lists and recovers after failure.
183
+ Implementation stops when no linked branch can be verified; there is no local
184
+ fallback. After the checks policy passes, the manager asks immediately before
185
+ merge. Without a fresh `Merge now` answer, it leaves the pull request and issue
186
+ open. `plan.mjs archive` verifies the latest trusted code-review result and
187
+ merged closing pull request after landing.
124
188
 
125
189
  Every Steps row carries an `Effect` of exactly
126
190
  `local|probe|production_access|publish|push|release|deploy`. A step whose
127
191
  `Effect` is not `local` requires an in-session `ask` confirmation immediately
128
- before it runs; when `ask` is unavailable the step is set `blocked` with
129
- `blocked_reason` naming the unconfirmed effect. Persisted effects record intent
130
- only.
131
-
132
- A plan carrying a `Plan-run:` line is a v1 plan: render it, never parse or
133
- migrate it, and finish it by hand by moving the file byte-unchanged to
134
- `docs/plans/finished/<YYYY-MM-DD>-<slug>.md` with a `## Retirement` section
135
- appended. The complete contract lives in `docs/plans/AGENTS.md`;
136
- `docs/plans/CLAUDE.md` contains only `@AGENTS.md`.
192
+ before it runs; when `ask` is unavailable the step is set `blocked` and the plan
193
+ reason becomes the first `## Open questions` line, `Blocked: <reason>`.
194
+ Persisted effects record intent only. Routine issue publication and landing
195
+ actions are outside the Steps table.
196
+
197
+ Render a plan body verbatim only when the user names that plan and asks to see it. After a write, report the one-line header strip and the changed lines only; a write never re-renders the body.
198
+
199
+ No plan markdown is tracked. The pre-GitHub records under
200
+ `docs/plans/finished/` were deleted; `git log -- docs/plans/finished/` recovers
201
+ them as history, and no lifecycle command restores them. The complete contract
202
+ lives in `docs/PLAN.md`; `docs/AGENTS.md` routes to it and `docs/CLAUDE.md`
203
+ contains only `@AGENTS.md`.
137
204
 
138
205
  Distinct from per-tool **Open Concerns** sections (wait-on-upstream
139
206
  blockers tied to a vendor shipping a fix — these live inside the per-tool
package/README.md CHANGED
@@ -5,15 +5,17 @@ Truth (SoT)** directories deploy to each tool's user-config location. Clone
5
5
  once to get a consistent AI-assisted environment on supported Linux, macOS,
6
6
  and Windows machines. Tuned for **token efficiency without sacrificing quality**.
7
7
 
8
- Supported tools: **Claude Code**, **Codex**, and any agent consuming
9
- [agentskills.io](https://agentskills.io) universal skills.
8
+ Supported harnesses: **Claude Code**, **Codex**, and **Oh My Pi (`omp`)**.
9
+ The kit also deploys [agentskills.io](https://agentskills.io) universal skills.
10
10
 
11
11
  ## Quick start
12
12
 
13
13
  ```bash
14
14
  git clone https://github.com/DocksDocks/public.git ~/projects/public
15
15
  cd ~/projects/public
16
- ./docks-kit sync # deploy everything (Claude Code + Codex + skills)
16
+ ./docks-kit harnesses # choose this machine's flag-less sync harnesses
17
+ ./docks-kit sync # deploy this machine's harness selection
18
+ ./docks-kit sync omp # deploy omp explicitly
17
19
  ./docks-kit status # doctor view: drift, toolchain, plugins, skills
18
20
  ```
19
21
 
@@ -41,15 +43,16 @@ PowerShell.
41
43
  ## CLI
42
44
 
43
45
  ```text
44
- docks-kit sync [claude] [codex] [agents] deploy the SoT (default: all three)
45
- docks-kit update [--no-sync] self-update the kit (autodetects checkout vs global install), then sync
46
- docks-kit model <claude|codex> [value] get/set the DEPLOYED model (TTY picker)
47
- docks-kit models [claude|codex] model catalogs (`--json`)
48
- docks-kit toolchain [check|ensure <tool>] verified-version floors for external tools
49
- docks-kit status [--json] deployed-vs-SoT drift + toolchain + counts
50
- docks-kit plugins list [--json] enabledPlugins tri-state vs installed
51
- docks-kit skills list [--json] universal skills vs manifest
52
- docks-kit docs [topic] self-documentation (9 topics)
46
+ docks-kit sync [claude] [codex] [agents] [omp] deploy explicit targets or the machine selection
47
+ docks-kit harnesses view or change this machine's selection
48
+ docks-kit update [--no-sync] self-update the kit (autodetects checkout vs global install), then sync
49
+ docks-kit model <claude|codex> [value] get/set the DEPLOYED model (TTY picker)
50
+ docks-kit models [claude|codex] model catalogs (`--json`)
51
+ docks-kit toolchain [check|ensure <tool>] verified-version floors for external tools
52
+ docks-kit status [--json] deployed-vs-SoT drift + toolchain + counts
53
+ docks-kit plugins list [--json] enabledPlugins tri-state vs installed
54
+ docks-kit skills list [--json] universal skills vs manifest
55
+ docks-kit docs [topic] self-documentation (9 topics)
53
56
  --help --version --wizard --completions built-in
54
57
  ```
55
58
 
@@ -87,6 +90,9 @@ and a later flag-less sync reverts them. Full reference: `docks-kit docs flags`
87
90
  - **Additive by default** — user-only settings keys, plugins, and skills
88
91
  survive a plain sync. Reconciliation toward the SoT is explicit
89
92
  (`--reconcile` / `--prune`).
93
+ - **Per-machine selection** — `~/.docks-kit/state.json` drives a flag-less sync.
94
+ A missing file selects Claude Code, Codex, and universal skills. It does not
95
+ select omp. Use `docks-kit harnesses` to view or change the selection.
90
96
  - **Idempotent** — every step is safe to re-run; no-change syncs are no-ops.
91
97
  - **Toolchain floors** — `SoT/toolchain.json` records the kit-verified version
92
98
  floors for external tools (bun, bwrap, …). `docks-kit toolchain check` prints
@@ -106,6 +112,7 @@ and a later flag-less sync reverts them. Full reference: `docks-kit docs flags`
106
112
  |------|---------|
107
113
  | `SoT/.claude/` | Claude Code SoT (settings template, Bun runtime programs, CLAUDE.md) |
108
114
  | `SoT/.codex/` | Codex SoT (config.toml, rules, AGENTS.md, marketplace) |
115
+ | `SoT/.omp/` | omp SoT (AGENTS.md, config.yml, mcp.json, intercom.json) |
109
116
  | `SoT/.agents/` | Universal-skill manifest |
110
117
  | `SoT/models.json` | Kit-verified Claude and Codex model catalog |
111
118
  | `SoT/toolchain.json` | Verified-version floors |
@@ -114,7 +121,7 @@ and a later flag-less sync reverts them. Full reference: `docks-kit docs flags`
114
121
  | `cli/` | docks-kit CLI (Effect 4 RC on Bun) + bundled docs topics |
115
122
  | `docks-kit` / `docks-kit.ps1` | POSIX and Windows launchers (binary → Bun-from-source) |
116
123
  | `install.sh` / `install.ps1` | POSIX and Windows global installers |
117
- | `docs/plans/` | Multi-commit work-item plans |
124
+ | `docs/PLAN.md` | Plan record standard; plans are GitHub issues |
118
125
  | `AGENTS.md` / `CLAUDE.md` | Agent-facing engineering rules / Claude Code specifics |
119
126
 
120
127
  ## Platform support
@@ -136,7 +143,7 @@ Tagging `cli-v*` builds six standalone binaries for Linux, macOS, and Windows
136
143
  on x64 and arm64. The workflow attaches them plus `SHA256SUMS` to the GitHub
137
144
  release. npm publishes the exact package tarball through trusted publishing
138
145
  with OIDC provenance.
139
- Package `docks-kit` 0.15.4 bundles the CLI + generated payload, so npm releases
146
+ Package `docks-kit` 0.16.0 bundles the CLI + generated payload, so npm releases
140
147
  are versioned config snapshots without shipping the authoring `SoT/` tree.
141
148
 
142
149
  ## Deeper docs
@@ -1,7 +1,9 @@
1
1
  # Sync layers
2
2
 
3
- `docks-kit sync [claude] [codex] [agents]` — targets are positional words;
4
- no target means all three.
3
+ `docks-kit sync [claude] [codex] [agents] [omp]` — targets are positional
4
+ words; no target means this machine's harness selection in
5
+ `~/.docks-kit/state.json`. The default is `claude codex agents`, and
6
+ `docks-kit harnesses` changes it.
5
7
 
6
8
  ## claude (→ ~/.claude, ~/.claude.json, shell rc)
7
9
 
@@ -46,6 +48,34 @@ and `codex plugin add` refresh.
46
48
  `npx skills add` per missing manifest slug, Claude symlink healing, and the
47
49
  kit-managed snapshot that `--prune` reconciles against.
48
50
 
51
+ ## omp (→ resolved agent dir, intercom root)
52
+
53
+ AGENTS.md, mcp.json, and intercom config.json are whole-file deploys;
54
+ config.yml is deep-merged (SoT keys win, mapping nodes merge, deployed-only
55
+ keys survive, stale slash-bearing `retry.fallbackChains` wildcards pruned).
56
+ Directories are mode 0700 and files mode 0600. Then the `docks` marketplace is
57
+ registered, `docks@docks` and `plan-lifecycle@docks` are installed or upgraded
58
+ through `omp plugin`, and `pi-intercom` is installed at its
59
+ `SoT/toolchain.json` pin. Inventory comes from `omp plugin list --json`, where a
60
+ marketplace row counts as installed only when its `scope` is `user`, so a repeat
61
+ run is a no-op and a project-only row still installs. When just the legacy
62
+ config-root registry lists `docks`, omp copies it forward itself: the sync runs
63
+ `marketplace update`, or the read-only `marketplace list` under
64
+ `--skip-plugin-refresh`, which keeps installs but fetches nothing. A dry run
65
+ invokes no omp subcommand.
66
+
67
+ Paths mirror upstream `dirs.ts` `DirResolver`, resolved from the environment
68
+ plus directory probes, so a dry run still runs no omp subcommand. The agent
69
+ directory is `<config root>/agent`, where the config root is
70
+ `~/<PI_CONFIG_DIR or .omp>` plus `profiles/<name>` under an active profile
71
+ (`OMP_PROFILE`, else legacy `PI_PROFILE`). For the default profile only,
72
+ `PI_CODING_AGENT_DIR` replaces the agent directory outright. The agent
73
+ directory never moves under XDG. Only the data root that holds
74
+ `marketplaces.json` does: on Linux and macOS, with no agent-dir override
75
+ active, an existing `$XDG_DATA_HOME/omp` (or `$XDG_DATA_HOME/omp/profiles/<name>`
76
+ for a named profile) is adopted. The intercom file keeps pi-intercom's own
77
+ root: `$PI_CODING_AGENT_DIR/intercom`, else `~/.pi/agent/intercom`.
78
+
49
79
  ## Reconcile flags
50
80
 
51
81
  - `--reconcile` — settings layer: SoT-declared keys win; user-only preserved.
@@ -0,0 +1,64 @@
1
+ import { Command, Prompt } from "effect/unstable/cli"
2
+ import { Console, Effect } from "effect"
3
+ import { bail } from "../engine"
4
+ import {
5
+ engineHome,
6
+ HARNESSES,
7
+ harnessStateFile,
8
+ LEGACY_SELECTION,
9
+ readHarnessSelection,
10
+ writeHarnessSelection,
11
+ type Harness
12
+ } from "../engine-native/harnesses"
13
+
14
+ const DESCRIPTIONS: Record<Harness, string> = {
15
+ claude: "Claude Code user configuration",
16
+ codex: "Codex user configuration",
17
+ agents: "universal agent skills",
18
+ omp: "Oh My Pi user configuration"
19
+ }
20
+
21
+ export const harnessesCommand = Command.make(
22
+ "harnesses",
23
+ {},
24
+ () =>
25
+ Effect.gen(function* () {
26
+ const home = engineHome(process.env)
27
+ const stored = yield* Effect.sync(() => readHarnessSelection(home))
28
+ const selection = stored ?? LEGACY_SELECTION
29
+ const names = selection.join(", ")
30
+
31
+ if (stored === undefined) {
32
+ yield* Console.log(
33
+ `Harness selection: ${names} (no selection is stored yet; the legacy default applies)`
34
+ )
35
+ } else {
36
+ yield* Console.log(`Harness selection: ${names}`)
37
+ }
38
+
39
+ if (!process.stdout.isTTY) return
40
+
41
+ const answer = yield* Prompt.multiSelect({
42
+ message: "Choose the harnesses for a flag-less docks-kit sync",
43
+ choices: HARNESSES.map((harness) => ({
44
+ title: harness,
45
+ value: harness,
46
+ description: DESCRIPTIONS[harness],
47
+ selected: selection.includes(harness)
48
+ }))
49
+ })
50
+
51
+ if (answer.length === 0) {
52
+ return yield* bail("Select at least one harness. The stored selection was not changed.")
53
+ }
54
+
55
+ yield* Effect.sync(() => writeHarnessSelection(home, answer))
56
+ yield* Console.log(
57
+ `Saved harness selection: ${answer.join(", ")} (${harnessStateFile(home)})`
58
+ )
59
+ })
60
+ ).pipe(
61
+ Command.withDescription(
62
+ "Choose the harness selection that drives a flag-less docks-kit sync."
63
+ )
64
+ )
@@ -29,11 +29,11 @@ const updateNudge = (logger: Logger): void => {
29
29
  }
30
30
  }
31
31
 
32
- const VALID_TARGETS = ["claude", "codex", "agents"]
32
+ const VALID_TARGETS = ["claude", "codex", "agents", "omp"]
33
33
 
34
34
  const targets = Argument.variadic(
35
35
  Argument.string("target").pipe(
36
- Argument.withDescription("Sync targets: claude, codex, agents (default: all three)")
36
+ Argument.withDescription("Sync targets: claude, codex, agents, omp (default: selected harnesses)")
37
37
  )
38
38
  )
39
39
 
@@ -23,6 +23,7 @@ export type ToolId =
23
23
  | "npx"
24
24
  | "claude"
25
25
  | "codex"
26
+ | "omp"
26
27
  | "bun"
27
28
  | "bwrap"
28
29
  | "ffplay"
@@ -181,6 +182,12 @@ export const DEPENDENCIES: Record<ToolId, DependencySpec> = {
181
182
  (pf = rawPlatform()) => hostOs(platformName(pf)).installHint("codex"),
182
183
  { version: versionProbe("codex") }
183
184
  ),
185
+ omp: spec(
186
+ "omp",
187
+ "optional",
188
+ () => "install omp from https://github.com/can1357/oh-my-pi (an existing install self-updates with `omp update`)",
189
+ { version: versionProbe("omp", ["--version"], (out) => out.trim().replace(/^omp[/v]/, "")) }
190
+ ),
184
191
  bun: spec(
185
192
  "bun",
186
193
  "optional",
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Per-machine harness selection at ~/.docks-kit/state.json. The selection keeps
3
+ * the omp harness opt-in. A missing or unreadable state file is represented by
4
+ * undefined so callers resolve it to LEGACY_SELECTION and existing machines
5
+ * keep today's behavior.
6
+ */
7
+ import { chmodSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"
8
+ import { homedir } from "node:os"
9
+
10
+ import { p } from "./exec"
11
+
12
+ export type Harness = "claude" | "codex" | "agents" | "omp"
13
+
14
+ export const HARNESSES: ReadonlyArray<Harness> = ["claude", "codex", "agents", "omp"]
15
+ export const LEGACY_SELECTION: ReadonlyArray<Harness> = ["claude", "codex", "agents"]
16
+
17
+ function isHarness(value: unknown): value is Harness {
18
+ return value === "claude" || value === "codex" || value === "agents" || value === "omp"
19
+ }
20
+
21
+ function normalizeHarnesses(selection: ReadonlyArray<unknown>): Array<Harness> {
22
+ const selected = new Set<Harness>()
23
+ for (const value of selection) {
24
+ if (isHarness(value)) selected.add(value)
25
+ }
26
+ return HARNESSES.filter((harness) => selected.has(harness))
27
+ }
28
+
29
+ /** Resolve the engine home root from HOME with the platform home as fallback. */
30
+ export function engineHome(env: NodeJS.ProcessEnv = process.env): string {
31
+ const home = env["HOME"]
32
+ return home !== undefined && home !== "" ? home : homedir()
33
+ }
34
+
35
+ export function harnessStateFile(home: string): string {
36
+ return p(home, ".docks-kit", "state.json")
37
+ }
38
+
39
+ /** Read valid local state without allowing corruption to make sync unusable. */
40
+ export function readHarnessSelection(home: string): ReadonlyArray<Harness> | undefined {
41
+ let parsed: unknown
42
+ try {
43
+ parsed = JSON.parse(readFileSync(harnessStateFile(home), "utf8")) as unknown
44
+ } catch {
45
+ return undefined
46
+ }
47
+
48
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return undefined
49
+ const state = parsed as Record<string, unknown>
50
+ if (state["version"] !== 1 || !Array.isArray(state["harnesses"])) return undefined
51
+
52
+ const selection = normalizeHarnesses(state["harnesses"])
53
+ return selection.length > 0 ? selection : undefined
54
+ }
55
+
56
+ export function writeHarnessSelection(home: string, selection: ReadonlyArray<Harness>): void {
57
+ if (selection.length === 0) {
58
+ throw new Error("Cannot write an empty harness selection because sync would become a no-op")
59
+ }
60
+
61
+ const harnesses = normalizeHarnesses(selection)
62
+ if (harnesses.length === 0) {
63
+ throw new Error("Harness selection must contain at least one known harness name")
64
+ }
65
+
66
+ const directory = p(home, ".docks-kit")
67
+ const file = harnessStateFile(home)
68
+ const text = `${JSON.stringify({ version: 1, harnesses }, null, 2)}\n`
69
+ // `mode` applies only when mkdir creates the path, so an existing permissive
70
+ // ~/.docks-kit would keep its mode.
71
+ mkdirSync(directory, { recursive: true, mode: 0o700 })
72
+ chmodSync(directory, 0o700)
73
+ writeFileSync(file, text, { mode: 0o600 })
74
+ chmodSync(file, 0o600)
75
+ }