docks-kit 0.15.5 → 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 +103 -36
- package/README.md +21 -14
- package/cli/docs/sync-layers.md +32 -2
- package/cli/src/commands/harnesses.ts +64 -0
- package/cli/src/commands/sync.ts +2 -2
- package/cli/src/engine-native/deps.ts +7 -0
- package/cli/src/engine-native/harnesses.ts +75 -0
- package/cli/src/engine-native/index.ts +31 -4
- package/cli/src/engine-native/ompPaths.ts +101 -0
- package/cli/src/engine-native/ompSync.ts +421 -0
- package/cli/src/engine-native/ompYaml.ts +107 -0
- package/cli/src/engine-native/parseArgs.ts +36 -11
- package/cli/src/engine-native/toolchain.ts +3 -1
- package/cli/src/generated/sotPayload.ts +12 -4
- package/cli/src/main.ts +4 -1
- package/package.json +3 -2
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.
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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/
|
|
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
|
-
|
|
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 `
|
|
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
|
|
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/.
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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
|
-
|
|
118
|
-
|
|
119
|
-
`##
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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`
|
|
129
|
-
|
|
130
|
-
only.
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
`docs/plans/
|
|
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
|
|
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
|
|
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]
|
|
45
|
-
docks-kit
|
|
46
|
-
docks-kit
|
|
47
|
-
docks-kit
|
|
48
|
-
docks-kit
|
|
49
|
-
docks-kit
|
|
50
|
-
docks-kit
|
|
51
|
-
docks-kit
|
|
52
|
-
docks-kit
|
|
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/
|
|
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.
|
|
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
|
package/cli/docs/sync-layers.md
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# Sync layers
|
|
2
2
|
|
|
3
|
-
`docks-kit sync [claude] [codex] [agents]` — targets are positional
|
|
4
|
-
no target means
|
|
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
|
+
)
|
package/cli/src/commands/sync.ts
CHANGED
|
@@ -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:
|
|
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
|
+
}
|