docks-kit 0.20.0 → 0.20.2

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
@@ -57,7 +57,7 @@ launcher can fall back to Bun source.
57
57
 
58
58
  Codex SoT notes:
59
59
  - `SoT/.codex/AGENTS.md` deploys to `~/.codex/AGENTS.md` as global Codex instructions.
60
- - `SoT/.codex/config.toml` pins Codex to `model = "gpt-6-sol"`, sets normal and plan reasoning to `high` with concise summaries, and sets `model_verbosity = "low"`, `personality`, live top-level `web_search`, workspace-write sandboxing with sandboxed command network access, cross-session `memories` (+ dedicated note tools), `[agents]` subagent limits (`max_threads = 12`, `max_depth = 2` — intentionally above Codex defaults for broad parallel kit work; deeper recursion increases cost and predictability risk), a 128 KiB `project_doc_max_bytes` budget for the repo-side AGENTS.md chain (the global `~/.codex/AGENTS.md` is uncapped and not counted), and enables the two Docks plugins `docks@docks` and `plan-lifecycle@docks` (the shared plan lifecycle).
60
+ - `SoT/.codex/config.toml` pins Codex to `model = "gpt-6.1-sol"`, sets normal and plan reasoning to `high` with concise summaries, and sets `model_verbosity = "low"`, `personality`, live top-level `web_search`, workspace-write sandboxing with sandboxed command network access, cross-session `memories` (+ dedicated note tools), `[agents]` subagent limits (`max_threads = 12`, `max_depth = 2` — intentionally above Codex defaults for broad parallel kit work; deeper recursion increases cost and predictability risk), a 128 KiB `project_doc_max_bytes` budget for the repo-side AGENTS.md chain (the global `~/.codex/AGENTS.md` is uncapped and not counted), and enables the two Docks plugins `docks@docks` and `plan-lifecycle@docks` (the shared plan lifecycle).
61
61
  - `SoT/.codex/rules/*.rules` deploys to `~/.codex/rules/` as kit-managed Codex command policy. This is Codex's equivalent of permission allow/prompt/block rules; user-learned approvals in `~/.codex/rules/default.rules` are preserved.
62
62
  - `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.
63
63
  - Codex `/import` can copy Claude hooks into `~/.codex/hooks.json`. `codexSync.ts removeRetiredImportedHooks, legacy SessionStart cleanup` removes only recognized hooks from retired docks-kit Claude settings, backs up a changed file, and preserves user-authored hooks. The current Claude SessionStart program emits the structured JSON shape shared by both tools.
@@ -75,10 +75,10 @@ omp SoT notes:
75
75
  - `ompSync.ts syncMergedYaml` deep-merges `config.yml` through `ompYaml.ts mergeOmpConfig` and `models.yml` through `mergeOmpModels`. Both wrap one generic mapping merge; only the config wrapper prunes stale `retry.fallbackChains` wildcards.
76
76
  - `ompSync.ts` runs `ompRemovals.ts syncOmpRemovals, retired-key inventory` right after the config merge, and that pass force-prunes retired kit-owned keys from `~/.omp/agent/config.yml` on every sync, without `--reconcile`. The pass is required because `mergeOmpConfig` is additive, so removing a key from the SoT alone never removes it from a deployed file. A key retired with a recorded value is pruned only while the deployed value still matches that value, so a user edit survives; a key retired outright, such as `providers.webSearchOrder`, is pruned at any value.
77
77
  - `cycleOrder` ends with `astra` as its fifth stop. `modelRoles.astra` is `openai-codex/gpt-6-astra:xhigh`, and `modelTags.astra` is visible. Astra and Fable fall back to each other through concrete selectors. `modelRoles.fable` remains `anthropic/claude-fable-5-1:medium`, with visible `modelTags.fable`. The hidden `switch_fable` role uses the same Fable selector and keeps an empty fallback chain.
78
- - `modelRoles.task` is `openai-codex/gpt-6-sol:high`. `advisor` is `anthropic/claude-opus-5-5:medium` with an empty fallback chain, because a GPT-6 Sol advisor looped on repeated reads under an Opus 5.5 session; never put GPT-6 Sol in the advisor role or chain. `smol`, `commit`, and `tiny` use `openai-codex/gpt-6-luna`. The four reviewer entries in `task.agentModelOverrides` inherit `task` through `@task`. Only bundled `reviewer` and `security-reviewer` are discoverable OMP agents; `code-reviewer` and `plan-reviewer` stay dormant. omp 18.2.9 did not list `gpt-6-sol` or `gpt-6-luna` in its `openai-codex` catalog and fuzzy-matched both selectors to the GPT-5.6 models. omp 18.3.1 lists both ids and serves them as named. The `SoT/toolchain.json` omp floor is 18.3.1 because it is the oldest release the kit tested; releases 18.2.10 to 18.3.0 were not tested. `cli/docs/omp-models.md` records the checks.
79
- - The five Anthropic roles (`default`, `slow`, `plan`, `designer`, `vision`) and the five Anthropic retry chains use `anthropic/claude-opus-5-5` at the levels the role map records. `cli/docs/omp-models.md` carries the Artificial Analysis capture behind the role map, read 2026-09-22 at Intelligence Index v4.3.2 and Coding Agent Index v1.5 from the AA comparison-page metric tables. Opus 5.5 max has the highest index in that topic. AA has not measured speed or latency for Opus 5.5 max, GPT-6 Sol, or GPT-6 Luna.
78
+ - `modelRoles.task` is `openai-codex/gpt-6.1-sol:high`. `advisor` is `anthropic/claude-opus-5-5:medium` with an empty fallback chain, because a GPT-6 Sol advisor looped on repeated reads under an Opus 5.5 session. Never put any GPT model (`openai-codex/gpt-*`, including GPT-6.1 Sol and GPT-6 Astra) in the advisor role or chain; Astra also costs too much for this role. `smol`, `commit`, and `tiny` use `openai-codex/gpt-6-luna`. The four reviewer entries in `task.agentModelOverrides` inherit `task` through `@task`. Only bundled `reviewer` and `security-reviewer` are discoverable OMP agents; `code-reviewer` and `plan-reviewer` stay dormant. The `SoT/toolchain.json` omp floor is 18.4.4, the oldest release the kit tested with `gpt-6.1-sol`. That release lists and serves the model through the `openai-codex` provider. `cli/docs/omp-models.md` records the checks.
79
+ - The five Anthropic roles (`default`, `slow`, `plan`, `designer`, `vision`) and the five Anthropic retry chains use `anthropic/claude-opus-5-5` at the levels the role map records. `cli/docs/omp-models.md` carries the Artificial Analysis capture behind the role map, read 2026-09-29 at Intelligence Index v4.3.2 and Coding Agent Index v1.5 from the AA comparison-page metric tables. Opus 5.5 max has the highest index in that topic, at 58. AA has measured speed and latency for Opus 5.5 max and every GPT-6.1 Sol level; GPT-6 Luna medium is the only unmeasured speed and latency row.
80
80
  - `modelRoles.web` is `web/firecrawl` and `retry.fallbackChains.web` carries the explicit 20-entry provider order. The kit declares both keys because the legacy `providers.webSearchOrder` key is retired: omp expands it in memory into these two keys and then drops it, and never writes that expansion back to disk. An explicit chain replaces omp's built-in web order wholesale, so every entry left out is a provider omp never tries. The owner removed the seven entries that named older models (Gemini 2.5 Flash, Claude Haiku 4.5, GPT-5.6, GPT-5.5, and Grok 4.5); keep every other provider.
81
- - `SoT/.omp/models.yml` declares Astra's full `low, medium, high, xhigh, max` ladder with `defaultLevel: xhigh` as the worked provider ladder-override example. It also carries a temporary `anthropic.modelOverrides.claude-opus-5-5` block with that model's limits, ladder, and prices, because the shared catalog still serves the id as a stub with null limits and zero cost; remove the block once the catalog publishes the row. `ompYaml.ts mergeOmpModels` preserves deployed-only keys in `~/.omp/agent/models.yml`, because a user file may carry provider credentials. Whole-file replacement is wrong.
81
+ - `SoT/.omp/models.yml` declares Astra's full `low, medium, high, xhigh, max` ladder with `defaultLevel: xhigh` as the worked provider ladder-override example. The temporary `anthropic.modelOverrides.claude-opus-5-5` block is gone, because the shared catalog now publishes the same limits, ladder, and prices. The removal switches omp from the block's `thinking.mode: effort` (a `budget_tokens` value per level from `ANTHROPIC_THINKING`, no effort value) to the catalog's `anthropic-adaptive` (adaptive thinking with the role's effort level); every kit Opus selector names its level, so the block's bare-selector `defaultLevel: high` is not needed. `ompRemovals.ts syncOmpModelRemovals, retired-block inventory` prunes it from `~/.omp/agent/models.yml` after the models merge, but only while the deployed block still equals the shipped one. `ompYaml.ts mergeOmpModels` preserves deployed-only keys in `~/.omp/agent/models.yml`, because a user file may carry provider credentials. Whole-file replacement is wrong.
82
82
  - `SoT/.omp/AGENTS.md` carries the rule `Please remove all mannered prose.` Anthropic's Fable 5.1 prompting guide documents mannered prose as a Fable 5.1 behavior and gives that sentence as its short-version fix: https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/prompting-claude-fable-5-1
83
83
  - `cli/docs/omp-models.md` (topic `omp-models`) records the role map rationale and the Artificial Analysis snapshot behind it. Model choices change with published benchmarks, so update that topic in the same commit as a role change.
84
84
  - `SoT/.omp/config.yml` sets `compaction.thresholdTokens` to `-1`, omp's schema default sentinel selecting reserve-based behavior: the trigger becomes `contextWindow` minus `max(floor(contextWindow * 0.15), 16384)`, which is 231,200 on the 272,000-token Codex window and 850,000 on the 1,000,000-token Anthropic window. The key ships as `-1` rather than being deleted because `ompYaml.ts mergeOmpConfig` is additive, so removing a key from SoT never removes it from a deployed file. `cli/docs/omp-context.md` (topic `omp-context`) carries the derivation and the measured evidence, so update that topic in the same commit as any compaction-setting change.
@@ -8,30 +8,29 @@ against.
8
8
 
9
9
  | Role | Model | Level | Index | Cost/task | TTFT |
10
10
  |---|---|---|---:|---:|---:|
11
- | `default` | `anthropic/claude-opus-5-5` | high | 54 | $1.82 | 12.49 s |
12
- | `slow` | `anthropic/claude-opus-5-5` | xhigh | 56 | $3.46 | 165.20 s |
13
- | `plan` | `anthropic/claude-opus-5-5` | xhigh | 56 | $3.46 | 165.20 s |
14
- | `task` | `openai-codex/gpt-6-sol` | high | 43 | $0.37 | n/a |
15
- | `advisor` | `anthropic/claude-opus-5-5` | medium | 51 | $1.34 | 22.17 s |
16
- | `designer` | `anthropic/claude-opus-5-5` | high | 54 | $1.82 | 12.49 s |
17
- | `vision` | `anthropic/claude-opus-5-5` | medium | 51 | $1.34 | 22.17 s |
11
+ | `default` | `anthropic/claude-opus-5-5` | high | 54 | $1.82 | 52.85 s |
12
+ | `slow` | `anthropic/claude-opus-5-5` | xhigh | 56 | $3.46 | 136.30 s |
13
+ | `plan` | `anthropic/claude-opus-5-5` | xhigh | 56 | $3.46 | 136.30 s |
14
+ | `task` | `openai-codex/gpt-6.1-sol` | high | 50 | $0.32 | 57.26 s |
15
+ | `advisor` | `anthropic/claude-opus-5-5` | medium | 51 | $1.34 | 21.87 s |
16
+ | `designer` | `anthropic/claude-opus-5-5` | high | 54 | $1.82 | 52.85 s |
17
+ | `vision` | `anthropic/claude-opus-5-5` | medium | 51 | $1.34 | 21.87 s |
18
18
  | `smol` / `commit` | `openai-codex/gpt-6-luna` | medium | 29 | $0.02 | n/a |
19
- | `tiny` | `openai-codex/gpt-6-luna` | low | 21 | $0.0045 | n/a |
20
- | `fable` | `anthropic/claude-fable-5-1` | medium | 49 | $2.98 | 8.61 s |
21
- | `switch_fable` | `anthropic/claude-fable-5-1` | medium | 49 | $2.98 | 8.61 s |
22
- | `astra` | `openai-codex/gpt-6-astra` | xhigh | 52 | $2.31 | 188.20 s |
19
+ | `tiny` | `openai-codex/gpt-6-luna` | low | 21 | $0.0045 | 2.18 s |
20
+ | `fable` | `anthropic/claude-fable-5-1` | medium | 49 | $2.98 | 8.00 s |
21
+ | `switch_fable` | `anthropic/claude-fable-5-1` | medium | 49 | $2.98 | 8.00 s |
22
+ | `astra` | `openai-codex/gpt-6-astra` | xhigh | 52 | $2.31 | 126.90 s |
23
23
  | `web` | `web/firecrawl` | n/a | n/a | n/a | n/a |
24
24
 
25
25
  The table reports the measured Artificial Analysis figures for each assigned
26
26
  model and level. It states no motive that the config or omp's own
27
27
  documentation does not establish. AA has not measured output speed or latency
28
- for GPT-6 Sol or GPT-6 Luna at any level, so those rows carry `n/a` for TTFT.
29
- AA measures no web search provider, so the `web` row carries no figures.
28
+ for GPT-6 Luna medium, so that row carries `n/a` for TTFT. AA measures no web
29
+ search provider, so the `web` row carries no figures.
30
30
 
31
- The role map carries no Coding Agent Index column. That index publishes one
32
- entry per harness and model, and the Codex entries for GPT-6 Sol and GPT-6
33
- Luna run at `max`, a level no role here uses. The snapshot section below
34
- lists the entries.
31
+ The role map carries no Coding Agent Index column. That index measures specific
32
+ agent harnesses and model levels, not omp roles. The snapshot section below
33
+ lists the relevant entries, including Codex with GPT-6.1 Sol high.
35
34
 
36
35
  ### GPT-6 Sol and GPT-6 Luna availability
37
36
 
@@ -56,9 +55,18 @@ omp 18.2.9 and codex-cli 0.153.3:
56
55
 
57
56
  On 2026-09-25, omp 18.3.1 listed `gpt-6-sol` and `gpt-6-luna` in
58
57
  `omp models openai-codex`. An `omp -p --mode json` run on each selector
59
- recorded `gpt-6-sol` and `gpt-6-luna` as the serving model, so the omp roles
60
- now run GPT-6. To check again, run `omp models openai-codex`: the `gpt-6-sol`
61
- and `gpt-6-luna` rows must appear.
58
+ recorded `gpt-6-sol` and `gpt-6-luna` as the serving model, so the selected
59
+ omp roles ran GPT-6 on that date.
60
+
61
+ On 2026-09-29, omp 18.4.4 listed `gpt-6.1-sol` at low, medium, high, xhigh,
62
+ and max, with a 272K context window and 128K maximum output. An
63
+ `omp -p --mode json --model openai-codex/gpt-6.1-sol:low` run recorded
64
+ `gpt-6.1-sol` as the serving model. To recheck, run
65
+ `omp models openai-codex`; the `gpt-6.1-sol` row must appear. Codex-cli
66
+ 0.159.0 could not complete `codex exec -m gpt-6.1-sol`: the login had ended,
67
+ and the request returned HTTP 401 `refresh_token_invalidated`. The Codex
68
+ model cache was last fetched on 2026-09-22 and does not list `gpt-6.1-sol`,
69
+ so that Codex run did not establish model availability.
62
70
 
63
71
  What omp's settings catalog establishes about these roles:
64
72
 
@@ -82,7 +90,7 @@ cross-vendor fallback.
82
90
  `retry.fallbackChains.fable` holds `openai-codex/gpt-6-astra:xhigh`.
83
91
  Each deliberate cycle stop falls to the other vendor. Without these explicit
84
92
  chains, `retry.fallbackChains.default` would send either stop to
85
- `openai-codex/gpt-6-sol:high`.
93
+ `openai-codex/gpt-6.1-sol:high`.
86
94
  Chain entries are concrete selectors, not role aliases, so this pair cannot
87
95
  recurse. The hidden `switch_fable` chain stays empty.
88
96
 
@@ -97,7 +105,11 @@ on `openai-codex/gpt-6-sol:medium` looped. In one session the advisor made
97
105
  window omp lists for GPT-6 Sol. With the advisor on Opus 5.5 medium, the
98
106
  same kind of session made about one advisor request per main-agent request
99
107
  and called `advise` normally. The owner excluded GPT-6 Sol from the advisor
100
- role and its fallback chain.
108
+ role and its fallback chain. On 2026-09-29 the owner extended the exclusion to
109
+ every GPT model, including GPT-6.1 Sol and GPT-6 Astra. No GPT model was
110
+ tested as the advisor again, and Astra costs too much for this role. Do not
111
+ put an `openai-codex/gpt-*` selector in `modelRoles.advisor` or
112
+ `retry.fallbackChains.advisor`.
101
113
 
102
114
  `modelRoles.web` is `web/firecrawl`, and `retry.fallbackChains.web` lists the
103
115
  explicit 20-entry provider order that follows it. The two keys replace the
@@ -124,31 +136,38 @@ The order keeps Firecrawl, Exa, Perplexity, and Codex first. The remaining
124
136
 
125
137
  ## Artificial Analysis snapshot
126
138
 
127
- Source: `https://artificialanalysis.ai`, read on 2026-09-22. Every figure below
128
- comes from that one capture at Intelligence Index v4.3.2 and Coding Agent Index
129
- v1.5. Each per-level row was read from the metric table of the comparison page
130
- `/models/comparisons/<level-slug>-vs-gpt-5-6-sol-high`. The page title named
131
- the requested model and level, and the page printed index v4.3.2. AA serves
132
- the `max` level under the bare model slug. Index composition changed in v4.2,
133
- again in v4.3, and again in v4.3.2, so figures from an earlier capture cannot
134
- be mixed with these.
135
-
136
- Coding Agent Index v1.5 (`/agents/coding-agents`) lists 12 harness and model
137
- entries. Most carry `max`. Grok Build with Grok 4.7 runs at `xhigh`, and
138
- Antigravity SDK with Gemini 3.8 Flash runs at `high`. Opencode with GLM-5.3
139
- and Kimi Code CLI with Kimi K3 name no level. The entries that involve a model
140
- family in this topic:
139
+ Source: `https://artificialanalysis.ai`, read on 2026-09-29 at Intelligence
140
+ Index v4.3.2 and Coding Agent Index v1.5. Each per-level row comes from the
141
+ metric table of `/models/comparisons/<level-slug>-vs-gpt-5-6-sol-high`.
142
+ Each page title names the requested model and level and prints the same
143
+ Intelligence Index version. AA serves `max` under the bare model slug.
144
+ The GPT-5.6 Sol high self-comparison URL does not load, so that baseline row
145
+ comes from the GPT-5.6 Sol high column of the GPT-6.1 Sol high comparison.
146
+ Index composition changes between versions; figures from an earlier capture
147
+ cannot be mixed with these.
148
+
149
+ The Coding Agent Index page (`/agents/coding-agents`) lists 30 harness, model,
150
+ and level entries. It covers GPT-6.1 Sol with Codex at low, medium, high,
151
+ xhigh, and max; Opus 5.5 and Fable 5.1 with Claude Code at max; and Astra and
152
+ Luna with Codex at max. It also covers Grok Build with Grok 4.7 at xhigh,
153
+ Antigravity SDK with Gemini 3.8 Flash at high, and Opencode with GLM-5.3 and
154
+ Kimi Code CLI with Kimi K3 without levels. The relevant published scores are:
141
155
 
142
156
  | Harness and model | Coding Agent Index |
143
157
  |---|---:|
144
- | Claude Code - Fable 5.1 (max, with fallback) | 62.2 |
145
- | Codex - GPT-6 Astra (max) | 61.6 |
146
- | Claude Code - Opus 5 (max) | 59.7 |
147
- | Codex - GPT-6 Sol (max) | 56.7 |
148
- | Codex - GPT-6 Luna (max) | 41.1 |
149
-
150
- AA lists no Opus 5.5 entry. The page stores each score as a fraction, such as
151
- `0.6222`, and this table shows it multiplied by 100.
158
+ | Claude Code - Opus 5.5 (max) | 66 |
159
+ | Claude Code - Fable 5.1 (max, with fallback) | 62 |
160
+ | Codex - GPT-6 Astra (max) | 62 |
161
+ | Codex - GPT-6.1 Sol (xhigh) | 63 |
162
+ | Codex - GPT-6.1 Sol (medium) | 61 |
163
+ | Codex - GPT-6.1 Sol (high) | 60 |
164
+ | Codex - GPT-6.1 Sol (max) | 60 |
165
+ | Codex - GPT-6.1 Sol (low) | 57 |
166
+ | Claude Code - Opus 5 (max) | 60 |
167
+ | Codex - GPT-6 Luna (max) | 41 |
168
+
169
+ These are coding-agent scores for specific harnesses and levels, not scores
170
+ for the omp role map.
152
171
 
153
172
  Column meanings:
154
173
 
@@ -167,35 +186,52 @@ Column meanings:
167
186
 
168
187
  | Level | Index | Cost/task | Tokens/task | Index tokens | Speed t/s | TTFT s | TB 4.0 |
169
188
  |---|---:|---:|---:|---:|---:|---:|---:|
170
- | max | 58 | $5.98 | 119k | 260M | n/a | n/a | 60% |
171
- | xhigh | 56 | $3.46 | 66k | 100M | 72 | 165.20 | 60% |
172
- | high | 54 | $1.82 | 36k | 53M | 91 | 12.49 | 57% |
173
- | medium | 51 | $1.34 | 26k | 38M | 76 | 22.17 | 53% |
174
- | low | 42 | $0.55 | 10k | 20M | 94 | 4.79 | 31% |
189
+ | max | 58 | $5.98 | 119k | 260M | 93 | 692.63 | 60% |
190
+ | xhigh | 56 | $3.46 | 66k | 100M | 80 | 136.30 | 60% |
191
+ | high | 54 | $1.82 | 36k | 53M | 74 | 52.85 | 57% |
192
+ | medium | 51 | $1.34 | 26k | 38M | 74 | 21.87 | 53% |
193
+ | low | 42 | $0.55 | 10k | 20M | 74 | 12.49 | 31% |
175
194
 
176
195
  Price: $4.00 in, $20.00 out, $0.20 cache hit per 1M. The Anthropic platform
177
- documentation read the same day lists the same input, output, and cache-read
178
- prices. It adds a $5.00 five-minute cache write and an $8.00 one-hour cache
179
- write, which the AA comparison table does not show. Context 1M, maximum output
180
- 128K. Adaptive thinking is always on, and the Claude API default effort is
181
- `medium`. All AA levels run with fallback. Max is the highest Intelligence
182
- Index in this topic at this capture. AA has not measured speed or latency for
183
- max. It measures a longer TTFT for medium than for high.
184
-
185
- `SoT/.omp/models.yml` carries the Anthropic limits and prices as an `anthropic`
186
- `modelOverrides` block, because the shared catalog still serves this id as a
187
- stub with null limits and zero cost. Remove that block once the catalog
188
- publishes the row.
196
+ documentation lists the same input, output, and cache-read prices. It adds a
197
+ $5.00 five-minute cache write and an $8.00 one-hour cache write, which the AA
198
+ comparison table does not show. Context is 1M, with 128K maximum output.
199
+ Adaptive thinking is always on, and the Claude API default effort is
200
+ `medium`. All AA levels run with fallback. Max has the highest Intelligence
201
+ Index in this topic at this capture. AA now measures speed and latency for
202
+ every Opus 5.5 level; medium has a shorter TTFT than high.
203
+
204
+ Until 0.20.1, `SoT/.omp/models.yml` carried the Anthropic limits and prices as
205
+ an `anthropic` `modelOverrides` block, because the shared catalog served this
206
+ id as a stub with null limits and zero cost. On 2026-09-25 the catalog at
207
+ `catalog.stencil.so` published the same context window, output cap, ladder,
208
+ and prices, so the block was removed. Sync prunes the deployed copy only while
209
+ it still equals the shipped block.
210
+
211
+ The removal fixes how the Opus role levels reach the API. The block set
212
+ `thinking.mode: effort`. For that mode, omp 18.3.1 (`packages/ai/src/stream.ts`,
213
+ tag `v18.3.1`) sends `thinking: {type: "enabled", budget_tokens}` and no
214
+ `output_config.effort`. The level only picked the budget from
215
+ `ANTHROPIC_THINKING`: `medium` 8,192, `high` 16,384, and `xhigh` and `max`
216
+ both 32,768 tokens. So before 0.20.1 the levels in the role table above were
217
+ not the effort levels that Artificial Analysis measured, and `xhigh` equaled
218
+ `max`. The catalog row sets `thinking.mode: anthropic-adaptive`, so omp now
219
+ sends `thinking: {type: "adaptive"}` with `output_config.effort` set to the
220
+ role level. The block also set `defaultLevel: high` for a bare
221
+ `claude-opus-5-5` selector. `SoT/.omp/config.yml` names a level on every Opus
222
+ selector, so no kit role depends on that default. Runs on
223
+ `anthropic/claude-opus-5-5:high` and `:max` after the prune answered with
224
+ exit 0.
189
225
 
190
226
  ### Claude Fable 5.1 (Anthropic) - `anthropic/claude-fable-5-1`
191
227
 
192
228
  | Level | Index | Cost/task | Tokens/task | Index tokens | Speed t/s | TTFT s | TB 4.0 |
193
229
  |---|---:|---:|---:|---:|---:|---:|---:|
194
- | max | 53 | $7.63 | 78k | 188M | 66 | 311.51 | 52% |
195
- | xhigh | 53 | $5.98 | 61k | 121M | 61 | 164.82 | 55% |
196
- | high | 51 | $3.91 | 38k | 62M | 55 | 26.22 | 52% |
197
- | medium | 49 | $2.98 | 28k | 44M | 55 | 8.61 | 45% |
198
- | low | 47 | $2.37 | 22k | 33M | 54 | 7.28 | 40% |
230
+ | max | 53 | $7.63 | 78k | 188M | 69 | 285.98 | 52% |
231
+ | xhigh | 53 | $5.98 | 61k | 121M | 57 | 104.51 | 55% |
232
+ | high | 51 | $3.91 | 38k | 62M | 52 | 26.01 | 52% |
233
+ | medium | 49 | $2.98 | 28k | 44M | 50 | 8.00 | 45% |
234
+ | low | 47 | $2.37 | 22k | 33M | 48 | 4.83 | 40% |
199
235
 
200
236
  Price: $10.00 in, $50.00 out, $0.25 cache hit per 1M. Context 1M. All levels
201
237
  run with fallback.
@@ -204,62 +240,63 @@ run with fallback.
204
240
 
205
241
  | Level | Index | Cost/task | Tokens/task | Index tokens | Speed t/s | TTFT s | TB 4.0 |
206
242
  |---|---:|---:|---:|---:|---:|---:|---:|
207
- | max | 53 | $3.26 | 27k | 60M | 61 | 322.65 | 59% |
208
- | xhigh | 52 | $2.31 | 17k | 38M | 55 | 188.20 | 60% |
209
- | high | 51 | $1.73 | 12k | 26M | 50 | 79.00 | 54% |
210
- | medium | 50 | $1.54 | 10k | 19M | 48 | 6.19 | 49% |
211
- | low | 46 | $0.82 | 4k | 10M | 51 | 2.76 | 42% |
243
+ | max | 53 | $3.26 | 27k | 60M | 57 | 305.56 | 59% |
244
+ | xhigh | 52 | $2.31 | 17k | 38M | 49 | 126.90 | 60% |
245
+ | high | 51 | $1.73 | 12k | 26M | 50 | 41.09 | 54% |
246
+ | medium | 50 | $1.54 | 10k | 19M | 47 | 4.88 | 49% |
247
+ | low | 46 | $0.82 | 4k | 10M | 49 | 2.81 | 42% |
212
248
 
213
249
  Price: $10.00 in, $50.00 out, $1.00 cache hit per 1M. Context 1M. Knowledge
214
250
  cutoff 2026-04-30. AA publishes no non-reasoning Astra row.
215
251
 
216
- ### GPT-6 Sol (OpenAI) - `openai-codex/gpt-6-sol`
252
+ ### GPT-6.1 Sol (OpenAI) - `openai-codex/gpt-6.1-sol`
217
253
 
218
254
  | Level | Index | Cost/task | Tokens/task | Index tokens | Speed t/s | TTFT s | TB 4.0 |
219
255
  |---|---:|---:|---:|---:|---:|---:|---:|
220
- | max | 48 | $1.06 | 31k | 77M | n/a | n/a | 44% |
221
- | xhigh | 44 | $0.53 | 16k | 40M | n/a | n/a | 30% |
222
- | high | 43 | $0.37 | 10k | 25M | n/a | n/a | 26% |
223
- | medium | 40 | $0.25 | 6k | 16M | n/a | n/a | 19% |
224
- | low | 34 | $0.13 | 3k | 9M | n/a | n/a | 9% |
225
- | non-reasoning | 28 | $0.33 | 5k | 8M | n/a | n/a | 13% |
226
-
227
- Price: $2.00 in, $10.00 out, $0.20 cache hit per 1M. OpenAI's model page lists
228
- a $2.50 cache write and a 1,050,000-token context with 128,000 maximum output
229
- tokens. It bills a prompt above 272K input tokens at 2x input and cache rates
230
- and 1.5x output for the full request. Knowledge cutoff 2026-04-20. The API
231
- effort ladder is `none, low, medium, high, xhigh, max`, with `medium` as the
232
- default. AA has not measured speed or latency for any level.
256
+ | max | 52 | $0.72 | 38k | 67M | 67 | 267.64 | 56% |
257
+ | xhigh | 51 | $0.39 | 18k | 36M | 64 | 68.65 | 54% |
258
+ | high | 50 | $0.32 | 13k | 25M | 66 | 57.26 | 52% |
259
+ | medium | 48 | $0.21 | 8k | 15M | 62 | 5.29 | 48% |
260
+ | low | 42 | $0.13 | 4k | 9M | 74 | 1.84 | 31% |
261
+
262
+ AA lists $2.00 input, $10.00 output, and $0.10 cached input per 1M tokens.
263
+ OpenAI also lists $2.50 per 1M cache-write tokens, a 1,050,000-token context,
264
+ 922,000 maximum input tokens, and 128,000 maximum output tokens. Prompts above
265
+ 272K input tokens cost 2x input and cache rates and 1.5x output rates for the
266
+ full request. The knowledge cutoff is 2026-04-30. The API effort ladder is
267
+ `low, medium, high, xhigh, max`, with `medium` as the default. There is no
268
+ `none`, `minimal`, or non-reasoning level.
233
269
 
234
270
  ### GPT-6 Luna (OpenAI) - `openai-codex/gpt-6-luna`
235
271
 
236
272
  | Level | Index | Cost/task | Tokens/task | Index tokens | Speed t/s | TTFT s | TB 4.0 |
237
273
  |---|---:|---:|---:|---:|---:|---:|---:|
238
- | max | 37 | $0.07 | 51k | 145M | n/a | n/a | 13% |
239
- | xhigh | 34 | $0.04 | 27k | 68M | n/a | n/a | 8% |
240
- | high | 32 | $0.03 | 20k | 47M | n/a | n/a | 5% |
241
- | medium | 29 | $0.02 | 11k | 28M | n/a | n/a | 3% |
242
- | low | 21 | $0.0045 | 2k | 8M | n/a | n/a | 0% |
243
- | non-reasoning | 18 | $0.01 | 4k | 7M | n/a | n/a | 2% |
274
+ | max | 37 | $0.07 | 50k | 145M | 148 | 96.96 | 13% |
275
+ | xhigh | 34 | $0.04 | 27k | 69M | 133 | 16.87 | 8% |
276
+ | high | 32 | $0.03 | 20k | 47M | 132 | 10.97 | 5% |
277
+ | medium | 29 | $0.02 | 11k | 29M | n/a | n/a | 3% |
278
+ | low | 21 | $0.0045 | 2k | 8M | 124 | 2.18 | 0% |
279
+ | non-reasoning | 18 | $0.01 | 4k | 7M | 142 | 0.80 | 2% |
244
280
 
245
281
  Price: $0.10 in, $0.50 out, $0.01 cache hit per 1M. OpenAI's model page lists
246
- a $0.125 cache write, the same context, output, long-prompt billing, and
247
- effort ladder as Sol, and a 2026-05-18 knowledge cutoff. AA has not measured
248
- speed or latency for any level.
282
+ a $0.125 cache write, the same context, output, and long-prompt billing as
283
+ GPT-6.1 Sol, and a 2026-05-18 knowledge cutoff. Luna also offers a
284
+ non-reasoning level. AA has not measured speed or latency for `medium`; it
285
+ has measured both at the other levels.
249
286
 
250
287
  ### GPT-5.6 Sol (OpenAI) - previous generation
251
288
 
252
289
  The role map no longer uses GPT-5.6 Sol. This table stays as the measured
253
- baseline for the GPT-6 Sol switch.
290
+ baseline for the current Sol task role.
254
291
 
255
292
  | Level | Index | Cost/task | Tokens/task | Index tokens | Speed t/s | TTFT s | TB 4.0 |
256
293
  |---|---:|---:|---:|---:|---:|---:|---:|
257
- | max | 47 | $1.99 | 29k | 90M | 82 | 130.17 | 40% |
258
- | xhigh | 44 | $1.18 | 20k | 51M | 72 | 35.55 | 25% |
259
- | high | 42 | $0.81 | 13k | 34M | 68 | 17.49 | 21% |
260
- | medium | 39 | $0.50 | 8k | 21M | 58 | 5.07 | 15% |
261
- | low | 33 | $0.26 | 4k | 13M | 58 | 3.85 | 1% |
262
- | non-reasoning | 28 (estimated) | n/a | n/a | n/a | 64 | 1.22 | n/a |
294
+ | max | 47 | $1.99 | 29k | 90M | 85 | 98.68 | 40% |
295
+ | xhigh | 44 | $1.18 | 20k | 51M | 77 | 24.79 | 25% |
296
+ | high | 42 | $0.81 | 13k | 34M | 75 | 9.40 | 21% |
297
+ | medium | 39 | $0.50 | 8k | 21M | 73 | 4.92 | 15% |
298
+ | low | 33 | $0.26 | 4k | 13M | 74 | 2.26 | 1% |
299
+ | non-reasoning | 28 (estimated) | n/a | n/a | n/a | 67 | 1.03 | n/a |
263
300
 
264
301
  Price: $4.00 in, $20.00 out, $0.40 cache hit per 1M. Context 1M. AA marks the
265
302
  non-reasoning index score as estimated.
@@ -271,52 +308,54 @@ baseline for the GPT-6 Luna switch.
271
308
 
272
309
  | Level | Index | Cost/task | Tokens/task | Index tokens | Speed t/s | TTFT s | TB 4.0 |
273
310
  |---|---:|---:|---:|---:|---:|---:|---:|
274
- | max | 37 | $0.18 | 41k | 154M | 145 | 122.15 | 12% |
275
- | xhigh | 35 | $0.09 | 24k | 85M | 143 | 40.24 | 4% |
276
- | high | 32 | $0.04 | 14k | 50M | 132 | 15.15 | 3% |
277
- | medium | 25 | $0.02 | 4k | 18M | 133 | 2.57 | 1% |
278
- | low | 21 | $0.01 | 3k | 10M | 131 | 1.69 | 0% |
279
- | non-reasoning | 16 | $0.01 | 2k | 5M | 140 | 0.81 | 1% |
311
+ | max | 37 | $0.18 | 41k | 154M | 119 | 106.29 | 12% |
312
+ | xhigh | 35 | $0.09 | 24k | 85M | 115 | 43.42 | 4% |
313
+ | high | 32 | $0.04 | 14k | 50M | 110 | 14.13 | 3% |
314
+ | medium | 25 | $0.02 | 4k | 18M | 114 | 2.28 | 1% |
315
+ | low | 21 | $0.01 | 3k | 10M | 116 | 1.56 | 0% |
316
+ | non-reasoning | 16 | $0.01 | 2k | 5M | 113 | 0.68 | 1% |
280
317
 
281
318
  Price: $0.20 in, $1.20 out, $0.02 cache hit per 1M. Context 1M.
282
319
 
283
- ## Why `task` runs GPT-6 Sol high
320
+ ## Why `task` runs GPT-6.1 Sol high
284
321
 
285
- `task` runs `openai-codex/gpt-6-sol:high`, the same level GPT-5.6 Sol ran
322
+ `task` runs `openai-codex/gpt-6.1-sol:high`, the same level GPT-5.6 Sol ran
286
323
  before it. The owner uses Astra only for main orchestration, so Astra has a
287
- dedicated `astra` cycle stop at `xhigh`. The comparison below records the
288
- retired Astra-low and GPT-5.6 Sol choices beside the current GPT-6 Sol choice.
324
+ dedicated `astra` cycle stop at `xhigh`. This comparison keeps the retired
325
+ Astra-low and GPT-5.6 Sol choices beside the current Sol levels.
289
326
 
290
- | Metric | Astra low, retired | GPT-5.6 Sol high, previous | GPT-6 Sol high, current | GPT-6 Sol max | Opus 5.5 high, `default` |
327
+ | Metric | Astra low, retired | GPT-5.6 Sol high, previous generation | GPT-6.1 Sol high, current | GPT-6.1 Sol max | Opus 5.5 high, `default` |
291
328
  |---|---:|---:|---:|---:|---:|
292
- | Intelligence Index | 46 | 42 | 43 | 48 | 54 |
293
- | Cost per Index task | $0.82 | $0.81 | $0.37 | $1.06 | $1.82 |
294
- | Output tokens per task | 4k | 13k | 10k | 31k | 36k |
295
- | Index output tokens | 10M | 34M | 25M | 77M | 53M |
296
- | Answer TTFT | 2.76 s | 17.49 s | n/a | n/a | 12.49 s |
297
- | End-to-end response time | 12.49 s | 24.87 s | n/a | n/a | 18.00 s |
298
- | Time per index task | 87.88 s | 189.16 s | n/a | n/a | 244.35 s |
299
- | Terminal-Bench 4.0 | 42% | 21% | 26% | 44% | 57% |
300
- | AA-Briefcase v1.1 | 1261 | 1370 | 1289 | 1483 | 1705 |
301
- | AA-Omniscience | 41 | 20 | 27 | 27 | 41 |
302
-
303
- GPT-6 Sol high scores one index point above GPT-5.6 Sol high. It costs $0.37
304
- against $0.81 per index task and uses 10k output tokens per task against 13k.
305
- It also scores 26% against 21% on Terminal-Bench 4.0. AA-Briefcase is the one
306
- metric where the older model leads, at 1370 against 1289. AA has not measured
307
- GPT-6 Sol latency, so the latency comparison is open.
308
-
309
- Astra low still beats GPT-6 Sol high on the index, at 46 against 43, and on
310
- Terminal-Bench 4.0, at 42% against 26%. It costs $0.82 against $0.37 per index
311
- task. The owner reserves Astra for interactive orchestration.
312
-
313
- Opus 5.5 high scores 11 points above GPT-6 Sol high and costs $1.82 against
314
- $0.37 per index task. It is the `default`, `designer`, and fallback model, not
315
- the `task` model, so the subagent fan-out keeps the cheaper Sol.
329
+ | Intelligence Index | 46 | 42 | 50 | 52 | 54 |
330
+ | Cost per Index task | $0.82 | $0.81 | $0.32 | $0.72 | $1.82 |
331
+ | Output tokens per task | 4k | 13k | 13k | 38k | 36k |
332
+ | Index output tokens | 10M | 34M | 25M | 67M | 53M |
333
+ | Answer TTFT | 2.81 s | 9.40 s | 57.26 s | 267.64 s | 52.85 s |
334
+ | End-to-end response time | 13.08 s | 16.04 s | 64.81 s | 275.12 s | 59.60 s |
335
+ | Time per index task | 91.73 s | 176.91 s | 202.65 s | 568.66 s | 294.20 s |
336
+ | Terminal-Bench 4.0 | 42% | 21% | 52% | 56% | 57% |
337
+ | AA-Briefcase v1.1 | 1261 | 1370 | 1471 | 1564 | 1705 |
338
+ | AA-Omniscience | 41 | 20 | 41 | 42 | 41 |
339
+
340
+ GPT-6.1 Sol high scores 50 against GPT-5.6 Sol high at 42. It costs $0.32
341
+ against $0.81 per index task, and both use 13k output tokens per task. It
342
+ scores 52% against 21% on Terminal-Bench 4.0 and 1471 against 1370 on
343
+ AA-Briefcase. The new high level has a longer answer TTFT, 57.26 s against
344
+ 9.40 s, and longer end-to-end response time, 64.81 s against 16.04 s.
345
+
346
+ Astra low scores 46 against GPT-6.1 Sol high at 50 and costs $0.82 against
347
+ $0.32 per index task. Astra low has the shorter TTFT, 2.81 s against
348
+ 57.26 s. The owner reserves Astra for interactive orchestration.
349
+
350
+ GPT-6.1 Sol max scores two index points above high, at 52 against 50. It
351
+ costs $0.72 against $0.32 per index task, with TTFT of 267.64 s against
352
+ 57.26 s. Opus 5.5 high scores 54 against GPT-6.1 Sol high at 50 and costs
353
+ $1.82 against $0.32. Opus is the `default`, `designer`, and fallback model,
354
+ not the `task` model.
316
355
 
317
356
  The `astra` cycle stop runs xhigh: index 52, $2.31 per index task, and
318
- 188.20 s TTFT. AA publishes a Coding Agent Index entry for Astra only at max,
319
- paired with Codex, where it scores 61.6.
357
+ 126.90 s TTFT. AA publishes a Coding Agent Index entry for Astra only at
358
+ max with Codex, where it scores 62.
320
359
 
321
360
  ## How Astra is selected in practice
322
361
 
@@ -334,19 +373,19 @@ quick answer.
334
373
 
335
374
  - The bundled `scout` and `sonic` agents carry `model: "@smol"` and
336
375
  `thinking-level: medium` in their embedded frontmatter, so they run Luna,
337
- not Sol or Astra. To move them, change `modelRoles.smol` or add a
376
+ not GPT-6.1 Sol or Astra. To move them, change `modelRoles.smol` or add a
338
377
  `task.agentModelOverrides` entry for the agent name.
339
378
  - The bundled `task` agent carries `model: "@task"` and
340
- `thinking-level: auto`. It resolves GPT-6 Sol, and `auto` classifies each prompt
341
- to choose a thinking level.
379
+ `thinking-level: auto`. It resolves GPT-6.1 Sol high, and `auto`
380
+ classifies each prompt to choose a thinking level.
342
381
  - `task.enableEffort` is `true`, so a caller can pass `effort: lo`, `med`, or
343
382
  `hi`, which overrides `auto`.
344
383
  - `task.maxEffort` is `max`, so `scout` and `sonic` run GPT-6 Luna `medium`
345
384
  by default and GPT-6 Luna `max` with `effort: hi`.
346
- - The bundled `reviewer` and `security-reviewer` inherit `@task`, now GPT-6
347
- Sol high.
385
+ - The bundled `reviewer` and `security-reviewer` inherit `@task`, now
386
+ GPT-6.1 Sol high.
348
387
  - The `code-reviewer` and `plan-reviewer` override entries remain dormant.
349
- Both point to `@task`, now GPT-6 Sol high. omp's task tool rejects both
388
+ Both point to `@task`, now GPT-6.1 Sol high. omp's task tool rejects both
350
389
  names as unknown agents, so neither can spawn.
351
390
 
352
391
  The runtime per-agent measurement from fresh `omp -p` runs on 2026-09-09
@@ -78,7 +78,11 @@ these alone and warns:
78
78
  - `typescript-language-server` when Node is older than the `node` floor.
79
79
 
80
80
  When the PATH copy of an npm-owned server is outside `npm prefix -g`, it warns
81
- that the upgrade changes only the npm copy. A package that is not installed
81
+ that the upgrade changes only the npm copy. The check follows links and
82
+ accepts only a file inside the package directory
83
+ (`<prefix>/lib/node_modules/<pkg>` on POSIX), so a link from another PATH
84
+ directory into it counts as the npm copy, while a distro binary under a
85
+ `/usr` prefix does not. On Windows, a shim in `<prefix>` counts. A package that is not installed
82
86
  stays missing, because `sync claude` owns first installs. A failed npm call
83
87
  exits 1. After the install, it reads `npm ls -g` again and exits 1 when a
84
88
  package is not at its pin.
@@ -6,7 +6,8 @@
6
6
  * and spawned argv are part of the contract.
7
7
  */
8
8
  import { defaultProbeExecutor, npmGlobalVersions, type ToolId } from "./deps";
9
- import { capture, p, spawnProcess } from "./exec";
9
+ import { realpathSync } from "node:fs";
10
+ import { capture, spawnProcess } from "./exec";
10
11
  import type { Ctx } from "./index";
11
12
  import { isObject, parseJson } from "./jq";
12
13
  import { belowFloor, field, installedVersion, isNewer } from "./toolchain";
@@ -177,12 +178,30 @@ export async function upgradeLspServers(ctx: Ctx): Promise<number> {
177
178
  const owned = await npmGlobalVersions(defaultProbeExecutor);
178
179
  const prefix = await capture("npm", ["prefix", "-g"]);
179
180
  const windows = ctx.services.platform.name() === "windows";
180
- // npm links global executables into <prefix>/bin on POSIX and into <prefix> on Windows.
181
- const npmBin = prefix === "" ? "" : windows ? prefix : p(prefix, "bin");
182
181
  const comparable = (path: string): string => {
183
182
  const slashed = path.replaceAll("\\", "/");
184
183
  return windows ? slashed.toLowerCase() : slashed;
185
184
  };
185
+ const resolved = (path: string): string => {
186
+ try {
187
+ return realpathSync(path);
188
+ } catch {
189
+ return path;
190
+ }
191
+ };
192
+ // A PATH entry is the npm copy of `pkg` when the file it resolves to lies in
193
+ // that package's own directory: <prefix>/lib/node_modules/<pkg> on POSIX,
194
+ // where npm's bin entries are links into it. Any other file under the prefix
195
+ // does not count, because a system Node uses /usr or /usr/local as its prefix
196
+ // and distro binaries live there too. npm writes Windows shims straight into
197
+ // <prefix> instead of linking, so there a shim in <prefix> itself counts.
198
+ const npmRoot = prefix === "" ? "" : `${comparable(resolved(prefix))}/`;
199
+ const isNpmCopy = (path: string, pkg: string): boolean => {
200
+ const packageDir = `${npmRoot}${windows ? "" : "lib/"}node_modules/${comparable(pkg)}/`;
201
+ if (comparable(resolved(path)).startsWith(packageDir)) return true;
202
+ const entry = comparable(path);
203
+ return windows && entry.slice(0, entry.lastIndexOf("/") + 1) === `${comparable(prefix)}/`;
204
+ };
186
205
 
187
206
  const targets: Array<readonly [string, string]> = [];
188
207
  const moves: Array<string> = [];
@@ -202,13 +221,9 @@ export async function upgradeLspServers(ctx: Ctx): Promise<number> {
202
221
  }
203
222
  continue;
204
223
  }
205
- if (
206
- onPath !== "" &&
207
- npmBin !== "" &&
208
- !comparable(onPath).startsWith(`${comparable(npmBin)}/`)
209
- ) {
224
+ if (onPath !== "" && npmRoot !== "" && !isNpmCopy(onPath, pkg)) {
210
225
  warn(
211
- `${tool} on PATH is ${onPath}, not the npm global copy in ${npmBin}; an upgrade changes only the npm copy`,
226
+ `${tool} on PATH is ${onPath}, not the npm global copy under ${prefix}; an upgrade changes only the npm copy`,
212
227
  );
213
228
  }
214
229
  if (!isNewer(verified, installed)) {
@@ -1,13 +1,14 @@
1
1
  /**
2
- * EngineNative `sync omp` retired-key pruning. `ompYaml.ts mergeOmpConfig` is
3
- * additive: its `mergeMappings, deployed-key retention loop` returns every
4
- * deployed key absent from the SoT to the merged result. Removing a key from
5
- * `SoT/.omp/config.yml` therefore never removes it from an existing
6
- * `~/.omp/agent/config.yml`. This pass force-prunes an inventory of retired
7
- * kit-owned keys on every sync, without `--reconcile`. Message strings and
8
- * prune semantics are part of the contract.
2
+ * EngineNative `sync omp` retired-key pruning. `ompYaml.ts mergeOmpConfig` and
3
+ * `mergeOmpModels` are additive: their `mergeMappings, deployed-key retention
4
+ * loop` returns every deployed key absent from the SoT to the merged result.
5
+ * Removing a key from `SoT/.omp/config.yml` or `SoT/.omp/models.yml` therefore
6
+ * never removes it from the deployed file. This pass force-prunes an inventory
7
+ * of retired kit-owned keys on every sync, without `--reconcile`. Message
8
+ * strings and prune semantics are part of the contract.
9
9
  */
10
10
  import { existsSync, readFileSync, writeFileSync } from "node:fs";
11
+ import { isDeepStrictEqual } from "node:util";
11
12
  import { isMap, isScalar, parseDocument, type YAMLMap } from "yaml";
12
13
  import type { Ctx } from "./index";
13
14
 
@@ -49,6 +50,37 @@ const OMP_RETIRED_VALUES: ReadonlyArray<readonly [string, RetiredScalar]> = [
49
50
  */
50
51
  const OMP_RETIRED_KEYS: ReadonlyArray<string> = ["providers.webSearchOrder"];
51
52
 
53
+ /**
54
+ * models.yml blocks the kit used to deploy, each with the exact value it
55
+ * deployed. A block is pruned only while the deployed block still equals that
56
+ * value, so a user edit survives.
57
+ *
58
+ * `claude-opus-5-5` carried limits, ladder, and prices while the shared catalog
59
+ * served the id as an empty stub. The catalog now publishes the same limits,
60
+ * prices, and ladder. The block's `defaultLevel: high` applied only to a bare
61
+ * selector, and every kit selector names its level.
62
+ */
63
+ const OMP_RETIRED_MODEL_BLOCKS: ReadonlyArray<readonly [string, unknown]> = [
64
+ [
65
+ "providers.anthropic.modelOverrides.claude-opus-5-5",
66
+ {
67
+ name: "Claude Opus 5.5",
68
+ reasoning: true,
69
+ input: ["text", "image"],
70
+ contextWindow: 1000000,
71
+ maxTokens: 128000,
72
+ cost: { input: 4, output: 20, cacheRead: 0.2, cacheWrite: 5 },
73
+ thinking: {
74
+ mode: "effort",
75
+ efforts: ["low", "medium", "high", "xhigh", "max"],
76
+ defaultLevel: "high",
77
+ },
78
+ },
79
+ ],
80
+ ];
81
+
82
+ type RetiredEntry = readonly [string, (node: unknown) => boolean];
83
+
52
84
  function indexOfKey(mapping: YAMLMap, key: string): number {
53
85
  return mapping.items.findIndex((pair) => {
54
86
  const name: unknown = pair.key;
@@ -122,38 +154,63 @@ function deletePath(
122
154
  return true;
123
155
  }
124
156
 
125
- /** Prunes retired kit-owned keys from a deployed omp config.yml. */
126
- export function syncOmpRemovals(ctx: Ctx, configFile: string): number {
157
+ /** Prunes each matching retired entry from one deployed omp YAML file. */
158
+ function pruneRetired(
159
+ ctx: Ctx,
160
+ file: string,
161
+ label: string,
162
+ entries: ReadonlyArray<RetiredEntry>,
163
+ ): number {
127
164
  const { change, echo, verbose, warn } = ctx.services.logger;
128
- if (!existsSync(configFile)) return 0;
165
+ if (!existsSync(file)) return 0;
129
166
 
130
- const doc = parseDocument(readFileSync(configFile, "utf8"));
167
+ const doc = parseDocument(readFileSync(file, "utf8"));
131
168
  const parseError = doc.errors[0];
132
169
  if (parseError !== undefined) {
133
- warn(`omp config.yml unreadable, retired keys not pruned: ${parseError.message}`);
170
+ warn(`omp ${label} unreadable, retired keys not pruned: ${parseError.message}`);
134
171
  return 0;
135
172
  }
136
173
  const contents = doc.contents;
137
174
  if (!isMap(contents)) return 0;
138
175
 
139
176
  const pruned: Array<string> = [];
140
- for (const [path, value] of OMP_RETIRED_VALUES) {
141
- const matches = (node: unknown): boolean => isScalar(node) && node.value === value;
177
+ for (const [path, matches] of entries) {
142
178
  if (deletePath(contents, path.split("."), matches)) pruned.push(path);
143
179
  }
144
- for (const path of OMP_RETIRED_KEYS) {
145
- if (deletePath(contents, path.split("."), () => true)) pruned.push(path);
146
- }
147
180
 
148
181
  if (pruned.length === 0) {
149
- verbose("omp config.yml carries no retired keys");
182
+ verbose(`omp ${label} carries no retired keys`);
150
183
  return 0;
151
184
  }
152
185
  if (ctx.dryRun) {
153
- echo(`[dry-run] prune ${configFile}: ${pruned.join(", ")}`);
186
+ echo(`[dry-run] prune ${file}: ${pruned.join(", ")}`);
154
187
  return pruned.length;
155
188
  }
156
- writeFileSync(configFile, String(doc));
157
- change(`omp config.yml pruned ${pruned.length} retired key(s): ${pruned.join(", ")}`);
189
+ writeFileSync(file, String(doc));
190
+ change(`omp ${label} pruned ${pruned.length} retired key(s): ${pruned.join(", ")}`);
158
191
  return pruned.length;
159
192
  }
193
+
194
+ /** Prunes retired kit-owned keys from a deployed omp config.yml. */
195
+ export function syncOmpRemovals(ctx: Ctx, configFile: string): number {
196
+ return pruneRetired(ctx, configFile, "config.yml", [
197
+ ...OMP_RETIRED_VALUES.map(([path, value]): RetiredEntry => [
198
+ path,
199
+ (node) => isScalar(node) && node.value === value,
200
+ ]),
201
+ ...OMP_RETIRED_KEYS.map((path): RetiredEntry => [path, () => true]),
202
+ ]);
203
+ }
204
+
205
+ /** Prunes retired kit-owned blocks from a deployed omp models.yml. */
206
+ export function syncOmpModelRemovals(ctx: Ctx, modelsFile: string): number {
207
+ return pruneRetired(
208
+ ctx,
209
+ modelsFile,
210
+ "models.yml",
211
+ OMP_RETIRED_MODEL_BLOCKS.map(([path, block]): RetiredEntry => [
212
+ path,
213
+ (node) => isMap(node) && isDeepStrictEqual(node.toJSON(), block),
214
+ ]),
215
+ );
216
+ }
@@ -21,7 +21,7 @@ import { mergeOmpConfig, mergeOmpModels } from "./ompYaml";
21
21
  import { ensureDirectory, syncMergedYaml, syncWholeFile } from "./ompFileDeploy";
22
22
  import { syncMarketplace } from "./ompMarketplace";
23
23
  import { syncPlugins } from "./ompPlugins";
24
- import { syncOmpRemovals } from "./ompRemovals";
24
+ import { syncOmpModelRemovals, syncOmpRemovals } from "./ompRemovals";
25
25
 
26
26
  export interface OmpState {
27
27
  readonly pluginsInstalled: number;
@@ -56,6 +56,7 @@ export async function ompSync(ctx: Ctx): Promise<OmpState> {
56
56
  // mergeOmpConfig is additive, so the prune must run on the merged result.
57
57
  syncOmpRemovals(ctx, p(agentDir, "config.yml"));
58
58
  syncMergedYaml(ctx, "SoT/.omp/models.yml", p(agentDir, "models.yml"), mergeOmpModels);
59
+ syncOmpModelRemovals(ctx, p(agentDir, "models.yml"));
59
60
 
60
61
  const intercomRootSetting = process.env["PI_CODING_AGENT_DIR"];
61
62
  const intercomRoot =
@@ -1,12 +1,12 @@
1
1
  // Generated by cli/scripts/generate-sot-payload.ts. DO NOT EDIT.
2
2
  // Edit SoT/, notification.mp3, or package.json, then run: bun cli/scripts/generate-sot-payload.ts
3
3
 
4
- export const GENERATED_PACKAGE_VERSION = "0.20.0"
4
+ export const GENERATED_PACKAGE_VERSION = "0.20.2"
5
5
 
6
6
  export const GENERATED_PAYLOAD_TEXT = {
7
7
  "SoT/.agents/skills.txt": "# Universal AI-agent skill manifest intentionally empty.\n# Global skill discovery is opt-in: add one <owner>/<repo> slug per line.\n# EngineNative ignores comments and blank lines.\n",
8
- "SoT/models.json": "{\n \"$comment\": \"Curated overlay for docks-kit model listings: aliases (kind alias) and notes always apply; the id rows are the fallback when the harness's live list is unavailable (harness not enabled, no login or cache, offline). Live sources: Claude via the Claude Code login against the Anthropic models API (cached 6 h in ~/.docks-kit/kit.db), Codex via ~/.codex/models_cache.json, omp via omp models --json.\",\n \"claude\": {\n \"verified\": \"2026-09-22\",\n \"models\": [\n { \"id\": \"best\", \"kind\": \"alias\", \"note\": \"Fable 5.1 where the org has access, latest Opus otherwise (Claude Code >=2.1.257; Claude apps gateway sessions still resolve Fable 5)\" },\n { \"id\": \"opus\", \"kind\": \"alias\", \"note\": \"latest Opus — the kit SoT default (Opus 5.5 from Claude Code >=2.1.280)\" },\n { \"id\": \"fable\", \"kind\": \"alias\", \"note\": \"Fable 5.1 — needs org access + Claude Code >=2.1.257 (Claude apps gateway sessions still resolve Fable 5)\" },\n { \"id\": \"sonnet\", \"kind\": \"alias\", \"note\": \"latest Sonnet (currently Sonnet 5)\" },\n { \"id\": \"haiku\", \"kind\": \"alias\", \"note\": \"latest Haiku (currently Haiku 4.5)\" },\n { \"id\": \"default\", \"kind\": \"alias\", \"note\": \"engine pseudo-value: deletes the deployed model key so the account default applies\" },\n { \"id\": \"claude-opus-5-5\", \"kind\": \"id\", \"note\": \"Opus 5.5 — needs Claude Code >=2.1.280\" },\n { \"id\": \"claude-fable-5-1\", \"kind\": \"id\", \"note\": \"Fable 5.1 — needs Claude Code >=2.1.257\" },\n { \"id\": \"claude-fable-5\", \"kind\": \"id\", \"note\": \"Fable 5 (legacy)\" },\n { \"id\": \"claude-opus-5\", \"kind\": \"id\", \"note\": \"Opus 5 (legacy)\" },\n { \"id\": \"claude-opus-4-8\", \"kind\": \"id\", \"note\": \"Opus 4.8 (legacy)\" },\n { \"id\": \"claude-sonnet-5\", \"kind\": \"id\", \"note\": \"Sonnet 5\" },\n { \"id\": \"claude-haiku-4-5-20251001\", \"kind\": \"id\", \"note\": \"Haiku 4.5\" }\n ]\n },\n \"codex\": {\n \"verified\": \"2026-09-22\",\n \"models\": [\n { \"id\": \"gpt-6-sol\", \"kind\": \"id\", \"note\": \"GPT-6 Sol — complex coding and agentic work, recommended default; the kit SoT pin\" },\n { \"id\": \"gpt-6-luna\", \"kind\": \"id\", \"note\": \"GPT-6 Luna — fast/light tier\" },\n { \"id\": \"gpt-6-astra\", \"kind\": \"id\", \"note\": \"GPT-6 Astra — most capable, highest cost\" },\n { \"id\": \"gpt-5.6-sol\", \"kind\": \"id\", \"note\": \"previous generation\" },\n { \"id\": \"gpt-5.6-terra\", \"kind\": \"id\", \"note\": \"previous generation, balanced tier\" },\n { \"id\": \"gpt-5.6-luna\", \"kind\": \"id\", \"note\": \"previous generation\" },\n { \"id\": \"gpt-5.5\", \"kind\": \"id\", \"note\": \"previous generation\" },\n { \"id\": \"gpt-5.5-codex\", \"kind\": \"id\", \"note\": \"codex-tuned gpt-5.5\" },\n { \"id\": \"gpt-5.1\", \"kind\": \"id\", \"note\": \"previous generation\" },\n { \"id\": \"gpt-5\", \"kind\": \"id\", \"note\": \"previous generation\" },\n { \"id\": \"gpt-5-codex\", \"kind\": \"id\", \"note\": \"codex-tuned gpt-5\" }\n ]\n }\n}\n",
9
- "SoT/toolchain.json": "{\n \"$comment\": \"Kit toolchain manifest - DATA only (versions, floors, policy); version probing and the doctor report live in cli/src/engine-native/toolchain.ts, and the one managed install lives in cli/src/engine-native/bun.ts bunBootstrap. kind: check (doctor visibility only) | managed (kit installs it when missing) | pin (no binary probe - a version pin for a package the kit installs through another tool, such as npx or `omp install`). policy (managed only): present (install when missing, never upgrade). `verified` = last kit-tested version; `pinnable` marks a tool whose `verified` release the bootstrap can install by exact tag. Supply-chain stance: every kit-driven install is pinned to `verified` - never floating @latest (npm-worm/Shai-Hulud surface). Update `verified` after testing a new release. upstream (optional) names where `docks-kit toolchain outdated` reads the newest release: npm package (optional major line) or GitHub repo releases with a tag prefix; results cache 24 h in ~/.docks-kit/kit.db; the report never installs or edits pins.\",\n \"tools\": {\n \"jq\": { \"kind\": \"check\", \"note\": \"optional operator CLI; EngineNative JSON and Claude runtime do not invoke it\" },\n \"curl\": { \"kind\": \"check\", \"note\": \"POSIX installer transport for the Bun bootstrap\" },\n \"git\": { \"kind\": \"check\", \"note\": \"plugin marketplaces (claude/codex clone them) + kit checkout updates\" },\n \"node\": { \"kind\": \"check\", \"floor\": \"22.22.2\",\n \"note\": \"hosts the npm globals installed for the Claude LSP plugins; the floor is typescript-language-server 6's engines requirement, so an older Node reports `below-floor` in `docks-kit toolchain` instead of silently installing an LSP server that cannot start\" },\n \"npm\": { \"kind\": \"check\", \"note\": \"npm-global installer; also backs the intelephense version probe (`npm ls -g`)\" },\n \"claude\": { \"kind\": \"check\", \"floor\": \"2.1.280\", \"note\": \"kit floor — lets the `opus` alias resolve to Opus 5.5, the default Opus from Claude Code 2.1.280, and subsumes the older Fable 5.1 (>=2.1.257) and Opus 5 (>=2.1.219) requirements (mirrors settings minimumVersion)\" },\n \"codex\": { \"kind\": \"check\", \"floor\": \"0.157.0\", \"note\": \"upstream-owned; standalone installer prints when missing. The floor is the Codex release installed on the owner machine on 2026-09-25, by owner decision; no older release was checked against the keys in SoT/.codex/config.toml\" },\n \"omp\": { \"kind\": \"check\", \"floor\": \"18.3.1\", \"verified\": \"18.3.1\",\n \"upstream\": { \"github\": \"can1357/oh-my-pi\", \"tagPrefix\": \"v\" },\n \"note\": \"Oh My Pi harness (https://github.com/can1357/oh-my-pi); upstream-owned and self-updating through `omp update`, so sync never installs or upgrades it. The floor is the oldest release the kit tested: 18.3.1 lists and serves gpt-6-sol and gpt-6-luna, while 18.2.9 fuzzy-matched both selectors to GPT-5.6. `sync omp` needs the CLI only for the marketplace and plugin passes; the file deploys proceed without it\" },\n \"ffplay\": { \"kind\": \"check\", \"note\": \"Notification hook sound; distro-installed, so no kit floor applies\" },\n \"bwrap\": { \"kind\": \"check\", \"os\": \"linux\", \"floor\": \"0.9.0\",\n \"note\": \"Codex Linux sandbox runtime; sync installs it via the distro package manager, so the floor is the kit-tested baseline (Ubuntu 24.04 LTS) and no verified pin applies\" },\n \"intelephense\": { \"kind\": \"check\", \"floor\": \"1.18.5\", \"verified\": \"1.18.5\",\n \"upstream\": { \"npm\": \"intelephense\" },\n \"note\": \"php-lsp server; `verified` pins claudeSync syncLspServers' npm install. Version comes from `npm ls -g` — its own --version prints minified source\" },\n \"typescript-language-server\": { \"kind\": \"check\", \"floor\": \"6.0.0\", \"verified\": \"6.0.1\",\n \"upstream\": { \"npm\": \"typescript-language-server\" },\n \"note\": \"typescript-lsp server binary; `verified` pins claudeSync syncLspServers' npm install. Version 6 requires Node >=22.22.2 (see the `node` floor)\" },\n \"rust-analyzer\": { \"kind\": \"check\",\n \"note\": \"rust-analyzer-lsp server binary; claudeSync syncLspServers installs it with `rustup component add rust-analyzer` when rustup is present, so the version follows the host Rust toolchain and no verified pin applies (the bubblewrap stance for a tool the kit does not publish). A host without rustup is skipped in silence\" },\n \"tsc\": { \"kind\": \"check\", \"floor\": \"6.0.3\", \"verified\": \"6.0.3\",\n \"upstream\": { \"npm\": \"typescript\", \"line\": \"6\" }, \"note\": \"typescript-lsp dependency (npm package `typescript`); `verified` pins claudeSync syncLspServers' npm install. Deliberately on the 6.x line: typescript-language-server embeds TypeScript's programmatic API, which TS7 (native) doesn't yet expose — the repo's own devDependency runs TS7 for tsc --noEmit\" },\n \"bun\": { \"kind\": \"managed\", \"policy\": \"present\", \"floor\": \"1.4.2\", \"verified\": \"1.4.2\", \"pinnable\": true,\n \"upstream\": { \"github\": \"oven-sh/bun\", \"tagPrefix\": \"bun-v\" },\n \"note\": \"runtime for the docks-kit CLI and the Claude statusline/hook programs; bootstrap installs the verified release (installer takes bun-vX.Y.Z); self-updates via `bun upgrade` when wanted. The floor is 1.4.2 by owner decision (2026-09-25): CI and the verified pin run 1.4.2, and the checkout launchers refuse an older Bun\" },\n \"skills-cli\": { \"kind\": \"pin\", \"verified\": \"1.7.0\",\n \"upstream\": { \"npm\": \"skills\" },\n \"note\": \"the `skills` npm package the kit runs via `npx skills@<verified>` when SoT/.agents/skills.txt names a slug (it is empty by default) — pinned, never @latest\" },\n \"pi-intercom\": { \"kind\": \"pin\", \"verified\": \"0.14.0\",\n \"upstream\": { \"npm\": \"pi-intercom\" },\n \"note\": \"the cross-session messaging plugin ompSync installs with `omp install pi-intercom@<verified>` - pinned, never floating. Its broker runs under Bun because omp's flat plugin store cannot resolve the default `npx --no-install tsx` launcher\" }\n }\n}\n",
8
+ "SoT/models.json": "{\n \"$comment\": \"Curated overlay for docks-kit model listings: aliases (kind alias) and notes always apply; the id rows are the fallback when the harness's live list is unavailable (harness not enabled, no login or cache, offline). Live sources: Claude via the Claude Code login against the Anthropic models API (cached 6 h in ~/.docks-kit/kit.db), Codex via ~/.codex/models_cache.json, omp via omp models --json.\",\n \"claude\": {\n \"verified\": \"2026-09-22\",\n \"models\": [\n { \"id\": \"best\", \"kind\": \"alias\", \"note\": \"Fable 5.1 where the org has access, latest Opus otherwise (Claude Code >=2.1.257; Claude apps gateway sessions still resolve Fable 5)\" },\n { \"id\": \"opus\", \"kind\": \"alias\", \"note\": \"latest Opus — the kit SoT default (Opus 5.5 from Claude Code >=2.1.280)\" },\n { \"id\": \"fable\", \"kind\": \"alias\", \"note\": \"Fable 5.1 — needs org access + Claude Code >=2.1.257 (Claude apps gateway sessions still resolve Fable 5)\" },\n { \"id\": \"sonnet\", \"kind\": \"alias\", \"note\": \"latest Sonnet (currently Sonnet 5)\" },\n { \"id\": \"haiku\", \"kind\": \"alias\", \"note\": \"latest Haiku (currently Haiku 4.5)\" },\n { \"id\": \"default\", \"kind\": \"alias\", \"note\": \"engine pseudo-value: deletes the deployed model key so the account default applies\" },\n { \"id\": \"claude-opus-5-5\", \"kind\": \"id\", \"note\": \"Opus 5.5 — needs Claude Code >=2.1.280\" },\n { \"id\": \"claude-fable-5-1\", \"kind\": \"id\", \"note\": \"Fable 5.1 — needs Claude Code >=2.1.257\" },\n { \"id\": \"claude-fable-5\", \"kind\": \"id\", \"note\": \"Fable 5 (legacy)\" },\n { \"id\": \"claude-opus-5\", \"kind\": \"id\", \"note\": \"Opus 5 (legacy)\" },\n { \"id\": \"claude-opus-4-8\", \"kind\": \"id\", \"note\": \"Opus 4.8 (legacy)\" },\n { \"id\": \"claude-sonnet-5\", \"kind\": \"id\", \"note\": \"Sonnet 5\" },\n { \"id\": \"claude-haiku-4-5-20251001\", \"kind\": \"id\", \"note\": \"Haiku 4.5\" }\n ]\n },\n \"codex\": {\n \"verified\": \"2026-09-29\",\n \"models\": [\n { \"id\": \"gpt-6.1-sol\", \"kind\": \"id\", \"note\": \"GPT-6.1 Sol — complex coding and agentic work, recommended default; the kit SoT pin\" },\n { \"id\": \"gpt-6-sol\", \"kind\": \"id\", \"note\": \"GPT-6 Sol — previous Sol release\" },\n { \"id\": \"gpt-6-luna\", \"kind\": \"id\", \"note\": \"GPT-6 Luna — fast/light tier\" },\n { \"id\": \"gpt-6-astra\", \"kind\": \"id\", \"note\": \"GPT-6 Astra — most capable, highest cost\" },\n { \"id\": \"gpt-5.6-sol\", \"kind\": \"id\", \"note\": \"previous generation\" },\n { \"id\": \"gpt-5.6-terra\", \"kind\": \"id\", \"note\": \"previous generation, balanced tier\" },\n { \"id\": \"gpt-5.6-luna\", \"kind\": \"id\", \"note\": \"previous generation\" },\n { \"id\": \"gpt-5.5\", \"kind\": \"id\", \"note\": \"previous generation\" },\n { \"id\": \"gpt-5.5-codex\", \"kind\": \"id\", \"note\": \"codex-tuned gpt-5.5\" },\n { \"id\": \"gpt-5.1\", \"kind\": \"id\", \"note\": \"previous generation\" },\n { \"id\": \"gpt-5\", \"kind\": \"id\", \"note\": \"previous generation\" },\n { \"id\": \"gpt-5-codex\", \"kind\": \"id\", \"note\": \"codex-tuned gpt-5\" }\n ]\n }\n}\n",
9
+ "SoT/toolchain.json": "{\n \"$comment\": \"Kit toolchain manifest - DATA only (versions, floors, policy); version probing and the doctor report live in cli/src/engine-native/toolchain.ts, and the one managed install lives in cli/src/engine-native/bun.ts bunBootstrap. kind: check (doctor visibility only) | managed (kit installs it when missing) | pin (no binary probe - a version pin for a package the kit installs through another tool, such as npx or `omp install`). policy (managed only): present (install when missing, never upgrade). `verified` = last kit-tested version; `pinnable` marks a tool whose `verified` release the bootstrap can install by exact tag. Supply-chain stance: every kit-driven install is pinned to `verified` - never floating @latest (npm-worm/Shai-Hulud surface). Update `verified` after testing a new release. upstream (optional) names where `docks-kit toolchain outdated` reads the newest release: npm package (optional major line) or GitHub repo releases with a tag prefix; results cache 24 h in ~/.docks-kit/kit.db; the report never installs or edits pins.\",\n \"tools\": {\n \"jq\": { \"kind\": \"check\", \"note\": \"optional operator CLI; EngineNative JSON and Claude runtime do not invoke it\" },\n \"curl\": { \"kind\": \"check\", \"note\": \"POSIX installer transport for the Bun bootstrap\" },\n \"git\": { \"kind\": \"check\", \"note\": \"plugin marketplaces (claude/codex clone them) + kit checkout updates\" },\n \"node\": { \"kind\": \"check\", \"floor\": \"22.22.2\",\n \"note\": \"hosts the npm globals installed for the Claude LSP plugins; the floor is typescript-language-server 6's engines requirement, so an older Node reports `below-floor` in `docks-kit toolchain` instead of silently installing an LSP server that cannot start\" },\n \"npm\": { \"kind\": \"check\", \"note\": \"npm-global installer; also backs the intelephense version probe (`npm ls -g`)\" },\n \"claude\": { \"kind\": \"check\", \"floor\": \"2.1.280\", \"note\": \"kit floor — lets the `opus` alias resolve to Opus 5.5, the default Opus from Claude Code 2.1.280, and subsumes the older Fable 5.1 (>=2.1.257) and Opus 5 (>=2.1.219) requirements (mirrors settings minimumVersion)\" },\n \"codex\": { \"kind\": \"check\", \"floor\": \"0.159.0\", \"note\": \"upstream-owned; standalone installer prints when missing. The floor is the Codex release installed on the owner machine on 2026-09-29, by owner decision; no older release was checked against the keys in SoT/.codex/config.toml\" },\n \"omp\": { \"kind\": \"check\", \"floor\": \"18.4.4\", \"verified\": \"18.4.4\",\n \"upstream\": { \"github\": \"can1357/oh-my-pi\", \"tagPrefix\": \"v\" },\n \"note\": \"Oh My Pi harness (https://github.com/can1357/oh-my-pi); upstream-owned and self-updating through `omp update`, so sync never installs or upgrades it. The floor is the oldest release the kit tested: 18.4.4 lists and serves gpt-6.1-sol. `sync omp` needs the CLI only for the marketplace and plugin passes; the file deploys proceed without it\" },\n \"ffplay\": { \"kind\": \"check\", \"note\": \"Notification hook sound; distro-installed, so no kit floor applies\" },\n \"bwrap\": { \"kind\": \"check\", \"os\": \"linux\", \"floor\": \"0.9.0\",\n \"note\": \"Codex Linux sandbox runtime; sync installs it via the distro package manager, so the floor is the kit-tested baseline (Ubuntu 24.04 LTS) and no verified pin applies\" },\n \"intelephense\": { \"kind\": \"check\", \"floor\": \"1.18.5\", \"verified\": \"1.18.5\",\n \"upstream\": { \"npm\": \"intelephense\" },\n \"note\": \"php-lsp server; `verified` pins claudeSync syncLspServers' npm install. Version comes from `npm ls -g` — its own --version prints minified source\" },\n \"typescript-language-server\": { \"kind\": \"check\", \"floor\": \"6.0.0\", \"verified\": \"6.0.1\",\n \"upstream\": { \"npm\": \"typescript-language-server\" },\n \"note\": \"typescript-lsp server binary; `verified` pins claudeSync syncLspServers' npm install. Version 6 requires Node >=22.22.2 (see the `node` floor)\" },\n \"rust-analyzer\": { \"kind\": \"check\",\n \"note\": \"rust-analyzer-lsp server binary; claudeSync syncLspServers installs it with `rustup component add rust-analyzer` when rustup is present, so the version follows the host Rust toolchain and no verified pin applies (the bubblewrap stance for a tool the kit does not publish). A host without rustup is skipped in silence\" },\n \"tsc\": { \"kind\": \"check\", \"floor\": \"6.0.3\", \"verified\": \"6.0.3\",\n \"upstream\": { \"npm\": \"typescript\", \"line\": \"6\" }, \"note\": \"typescript-lsp dependency (npm package `typescript`); `verified` pins claudeSync syncLspServers' npm install. Deliberately on the 6.x line: typescript-language-server embeds TypeScript's programmatic API, which TS7 (native) doesn't yet expose — the repo's own devDependency runs TS7 for tsc --noEmit\" },\n \"bun\": { \"kind\": \"managed\", \"policy\": \"present\", \"floor\": \"1.4.2\", \"verified\": \"1.4.2\", \"pinnable\": true,\n \"upstream\": { \"github\": \"oven-sh/bun\", \"tagPrefix\": \"bun-v\" },\n \"note\": \"runtime for the docks-kit CLI and the Claude statusline/hook programs; bootstrap installs the verified release (installer takes bun-vX.Y.Z); self-updates via `bun upgrade` when wanted. The floor is 1.4.2 by owner decision (2026-09-25): CI and the verified pin run 1.4.2, and the checkout launchers refuse an older Bun\" },\n \"skills-cli\": { \"kind\": \"pin\", \"verified\": \"1.7.0\",\n \"upstream\": { \"npm\": \"skills\" },\n \"note\": \"the `skills` npm package the kit runs via `npx skills@<verified>` when SoT/.agents/skills.txt names a slug (it is empty by default) — pinned, never @latest\" },\n \"pi-intercom\": { \"kind\": \"pin\", \"verified\": \"0.14.0\",\n \"upstream\": { \"npm\": \"pi-intercom\" },\n \"note\": \"the cross-session messaging plugin ompSync installs with `omp install pi-intercom@<verified>` - pinned, never floating. Its broker runs under Bun because omp's flat plugin store cannot resolve the default `npx --no-install tsx` launcher\" }\n }\n}\n",
10
10
  "SoT/.claude/CLAUDE.md": "## Research Before Implementation\n\nBefore writing or modifying code that uses an API, hook, method, or config surface you have not verified in this session, research current documentation first.\n\nResearch workflow:\n1. Prefer official documentation and primary sources for the specific library, framework, or API.\n2. If a local docs or MCP tool is available, use it before broad web search.\n3. Only then proceed to implementation.\n\nResearch when:\n- Installing or configuring a dependency.\n- Using an API, hook, method, or pattern not verified in this session.\n- Upgrading or migrating between versions.\n- Any task where relying on memory could cause stale syntax or behavior.\n\nDo not:\n- Assume API signatures, method names, or config options from memory.\n- Generate framework code without checking current docs first.\n- Skip research because the library seems familiar.\n\n<constraint>\nResearch the codebase before editing. Never change code you have not read.\n</constraint>\n\n## Agentic Harness Heuristics\n\n**1. Persistence.** Keep going until the user's query is completely resolved. Only yield when sure the problem is solved. Before ending a turn, check the last paragraph: if it is a plan, a question you can answer yourself, or a promise of work not done (\"I'll…\"), do that work now.\n\n**2. Default to parallel.** Whenever you have multiple independent operations (reads, greps, web fetches, independent edits), invoke them in a single response with multiple tool-use blocks. Sequential calls only when output of one operation is required as input to the next.\n\n**3. Multi-pass search.** First-pass search often misses — vary the wording (colleague-questions over keywords) before concluding something doesn't exist.\n\n**4. Trace symbols.** Before modifying a symbol, trace it to its definitions and all usages. Don't assume a function's behavior or a type's shape from the call site alone.\n\n**5. Linter-loop 3-strike rule.** Don't loop more than 3 times fixing linter errors on the same file. On the third attempt, stop and ask the user — repeated failure usually means the diagnosis is wrong, not the code.\n\n**6. Read-before-Edit TTL.** If you haven't read a file with the Read tool in the last ~5 messages, re-read it before editing. Cached file content goes stale silently when the user edits between turns.\n\n**7. Big-file rule.** For files >1000 lines, prefer Grep + scoped Read (`offset` + `limit`) over reading the entire file. Whole-file reads bloat context; targeted reads keep the working set small.\n\n**8. Todo hygiene.** Use TaskCreate for items with meaningful outcome (≥5 min, distinct deliverable). Never include operational sub-actions (linting, testing, searching, examining the codebase) as their own todos — those are sub-steps in service of higher-level tasks. Mark complete immediately when done, never in batches.\n\n**9. Literal-instruction rule.** Current frontier models follow instructions literally — they do not silently generalize from intent. Phrase requirements as explicit checklists with success criteria, not narrative.\n\n**10. Context hygiene.** Prefer `/clear` at task boundaries and `/rewind` for wrong-path detours over carrying rot forward (corrections accumulate noise; rewinds preserve the prefix and discard the bad branch). On a continuing task, run `/compact` with steering before context quality degrades. Never stop, summarize, or suggest a new session on account of context limits.\n\n**11. Autonomy calibration.** For minor choices (naming, formatting, default values, which of two equivalent approaches), pick a reasonable option and note it — don't ask. Ask first only for scope changes, destructive actions, or decisions that change the deliverable. When the user is describing a problem or asking a question rather than requesting a change, the deliverable is your assessment — report findings and stop; don't apply fixes until asked. Don't close a finished task with \"Want me to also…?\" — run the obvious verification, then stop cleanly.\n\n**12. Capability triggering.** When the answer depends on current or version-specific information, search or fetch before answering — never answer from memory. When work fans out across independent items (many files to read, many tests to run, many candidates to check), delegate to parallel subagents; never spawn one for work you can complete directly. For verification, prefer a fresh-context subagent over self-critique. On tasks longer than a few turns, keep a running notes file and re-read it before each phase.\n\n<constraint>\nTreat the 12 heuristics above as protocol, not preference. If a turn ends without honoring an applicable one (e.g., lint-loop guard not respected, edit without re-read), self-correct in the next turn before continuing.\n</constraint>\n\nReuse before invention: inventory existing code, components, conventions, and dependencies; extend them instead of creating a parallel pattern. Load only the narrow skills supported by the task and repository evidence. If a request establishes a new React/Tailwind system and no convention exists, prefer current shadcn/ui `base-*` components backed by Base UI; otherwise preserve the existing stack. Treat probe, production access, publish, push, release, and deploy as literal current-request effects—never infer external authority from a plan, schedule, review, or old receipt.\n\nFor Docks plan reviews, cross-company review is standing-authorized; do not ask for export consent. This never overrides a host or platform security denial.\n\nUse a narrow-to-broad verification ladder: direct acceptance while iterating, focused regressions next, and one full CI at the pre-commit or release boundary. Reuse still-matching evidence; rerun full CI only after a relevant edit invalidates it.\n\n<constraint>\nNo secrets in committed config. Treat plugin marketplaces, installers, and downloaded artifacts as untrusted until verified.\n</constraint>\n",
11
11
  "SoT/.claude/mcp-servers.json": "{\n \"mcpServers\": {}\n}\n",
12
12
  "SoT/.claude/settings.json": "{\n \"$schema\": \"https://json.schemastore.org/claude-code-settings.json\",\n \"minimumVersion\": \"2.1.280\",\n \"model\": \"opus\",\n \"effortLevel\": \"high\",\n \"autoMemoryEnabled\": false,\n \"autoDreamEnabled\": false,\n \"skillListingMaxDescChars\": 2048,\n \"respectGitignore\": true,\n \"cleanupPeriodDays\": 14,\n \"skillListingBudgetFraction\": 0.05,\n \"env\": {\n \"CLAUDE_CODE_MAX_OUTPUT_TOKENS\": \"64000\",\n \"CLAUDE_BASH_MAINTAIN_PROJECT_WORKING_DIR\": \"1\",\n \"CLAUDE_CODE_AUTO_COMPACT_WINDOW\": \"468000\",\n \"CLAUDE_CODE_NO_FLICKER\": \"1\"\n },\n \"permissions\": {\n \"defaultMode\": \"auto\",\n \"allow\": [\n \"Read\",\n \"Glob\",\n \"Grep\",\n \"WebSearch\",\n \"Edit(./)\"\n ],\n \"deny\": [\n \"Read(**/.env)\",\n \"Read(**/.env.local)\",\n \"Read(**/secrets/**)\",\n \"Read(**/*.key)\",\n \"Read(**/*.pem)\",\n \"Read(**/*.p12)\",\n \"Read(**/.credentials*)\",\n \"Edit(**/.env)\",\n \"Edit(**/.env.local)\",\n \"Edit(**/secrets/**)\",\n \"Bash(sudo *)\",\n \"Bash(rm -rf /)\",\n \"Bash(rm -rf / *)\",\n \"Bash(rm -rf ~)\",\n \"Bash(rm -rf ~ *)\",\n \"Bash(rm -rf $HOME)\",\n \"Bash(rm -rf $HOME *)\",\n \"Bash(> /dev *)\",\n \"Bash(dd if= *)\",\n \"Bash(mkfs *)\",\n \"Bash(eval *)\",\n \"Bash(chmod 777 *)\",\n \"Bash(chmod -R 777 *)\",\n \"Bash(git push --force origin main *)\",\n \"Bash(git push --force origin master *)\",\n \"Bash(git push -f origin main *)\",\n \"Bash(git push -f origin master *)\",\n \"PowerShell(sudo *)\",\n \"PowerShell(rm -rf /)\",\n \"PowerShell(rm -rf / *)\",\n \"PowerShell(rm -rf ~)\",\n \"PowerShell(rm -rf ~ *)\",\n \"PowerShell(rm -rf $HOME)\",\n \"PowerShell(rm -rf $HOME *)\",\n \"PowerShell(> /dev *)\",\n \"PowerShell(dd if= *)\",\n \"PowerShell(mkfs *)\",\n \"PowerShell(eval *)\",\n \"PowerShell(chmod 777 *)\",\n \"PowerShell(chmod -R 777 *)\",\n \"PowerShell(git push --force origin main *)\",\n \"PowerShell(git push --force origin master *)\",\n \"PowerShell(git push -f origin main *)\",\n \"PowerShell(git push -f origin master *)\",\n \"PowerShell(Remove-Item *-Recurse* /)\",\n \"PowerShell(Remove-Item *-Recurse* / *)\",\n \"PowerShell(Remove-Item / *-Recurse*)\",\n \"PowerShell(Remove-Item -Path / *-Recurse*)\",\n \"PowerShell(Remove-Item -LiteralPath / *-Recurse*)\",\n \"PowerShell(Remove-Item *-Recurse* ~)\",\n \"PowerShell(Remove-Item *-Recurse* ~ *)\",\n \"PowerShell(Remove-Item ~ *-Recurse*)\",\n \"PowerShell(Remove-Item -Path ~ *-Recurse*)\",\n \"PowerShell(Remove-Item -LiteralPath ~ *-Recurse*)\",\n \"PowerShell(Remove-Item *-Recurse* $HOME)\",\n \"PowerShell(Remove-Item *-Recurse* $HOME *)\",\n \"PowerShell(Remove-Item $HOME *-Recurse*)\",\n \"PowerShell(Remove-Item -Path $HOME *-Recurse*)\",\n \"PowerShell(Remove-Item -LiteralPath $HOME *-Recurse*)\",\n \"PowerShell(Remove-Item *-Recurse* $env:USERPROFILE)\",\n \"PowerShell(Remove-Item *-Recurse* $env:USERPROFILE *)\",\n \"PowerShell(Remove-Item $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(Remove-Item -Path $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(Remove-Item -LiteralPath $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(Remove-Item *-Recurse* \\\\\\\\)\",\n \"PowerShell(Remove-Item *-Recurse* \\\\ *)\",\n \"PowerShell(Remove-Item \\\\ *-Recurse*)\",\n \"PowerShell(Remove-Item -Path \\\\ *-Recurse*)\",\n \"PowerShell(Remove-Item -LiteralPath \\\\ *-Recurse*)\",\n \"PowerShell(Remove-Item *-Recurse* *:\\\\\\\\)\",\n \"PowerShell(Remove-Item *-Recurse* *:\\\\ *)\",\n \"PowerShell(Remove-Item *:\\\\ *-Recurse*)\",\n \"PowerShell(Remove-Item -Path *:\\\\ *-Recurse*)\",\n \"PowerShell(Remove-Item -LiteralPath *:\\\\ *-Recurse*)\",\n \"PowerShell(del *-Recurse* /)\",\n \"PowerShell(del *-Recurse* / *)\",\n \"PowerShell(del / *-Recurse*)\",\n \"PowerShell(del -Path / *-Recurse*)\",\n \"PowerShell(del -LiteralPath / *-Recurse*)\",\n \"PowerShell(del *-Recurse* ~)\",\n \"PowerShell(del *-Recurse* ~ *)\",\n \"PowerShell(del ~ *-Recurse*)\",\n \"PowerShell(del -Path ~ *-Recurse*)\",\n \"PowerShell(del -LiteralPath ~ *-Recurse*)\",\n \"PowerShell(del *-Recurse* $HOME)\",\n \"PowerShell(del *-Recurse* $HOME *)\",\n \"PowerShell(del $HOME *-Recurse*)\",\n \"PowerShell(del -Path $HOME *-Recurse*)\",\n \"PowerShell(del -LiteralPath $HOME *-Recurse*)\",\n \"PowerShell(del *-Recurse* $env:USERPROFILE)\",\n \"PowerShell(del *-Recurse* $env:USERPROFILE *)\",\n \"PowerShell(del $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(del -Path $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(del -LiteralPath $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(del *-Recurse* \\\\\\\\)\",\n \"PowerShell(del *-Recurse* \\\\ *)\",\n \"PowerShell(del \\\\ *-Recurse*)\",\n \"PowerShell(del -Path \\\\ *-Recurse*)\",\n \"PowerShell(del -LiteralPath \\\\ *-Recurse*)\",\n \"PowerShell(del *-Recurse* *:\\\\\\\\)\",\n \"PowerShell(del *-Recurse* *:\\\\ *)\",\n \"PowerShell(del *:\\\\ *-Recurse*)\",\n \"PowerShell(del -Path *:\\\\ *-Recurse*)\",\n \"PowerShell(del -LiteralPath *:\\\\ *-Recurse*)\",\n \"PowerShell(erase *-Recurse* /)\",\n \"PowerShell(erase *-Recurse* / *)\",\n \"PowerShell(erase / *-Recurse*)\",\n \"PowerShell(erase -Path / *-Recurse*)\",\n \"PowerShell(erase -LiteralPath / *-Recurse*)\",\n \"PowerShell(erase *-Recurse* ~)\",\n \"PowerShell(erase *-Recurse* ~ *)\",\n \"PowerShell(erase ~ *-Recurse*)\",\n \"PowerShell(erase -Path ~ *-Recurse*)\",\n \"PowerShell(erase -LiteralPath ~ *-Recurse*)\",\n \"PowerShell(erase *-Recurse* $HOME)\",\n \"PowerShell(erase *-Recurse* $HOME *)\",\n \"PowerShell(erase $HOME *-Recurse*)\",\n \"PowerShell(erase -Path $HOME *-Recurse*)\",\n \"PowerShell(erase -LiteralPath $HOME *-Recurse*)\",\n \"PowerShell(erase *-Recurse* $env:USERPROFILE)\",\n \"PowerShell(erase *-Recurse* $env:USERPROFILE *)\",\n \"PowerShell(erase $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(erase -Path $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(erase -LiteralPath $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(erase *-Recurse* \\\\\\\\)\",\n \"PowerShell(erase *-Recurse* \\\\ *)\",\n \"PowerShell(erase \\\\ *-Recurse*)\",\n \"PowerShell(erase -Path \\\\ *-Recurse*)\",\n \"PowerShell(erase -LiteralPath \\\\ *-Recurse*)\",\n \"PowerShell(erase *-Recurse* *:\\\\\\\\)\",\n \"PowerShell(erase *-Recurse* *:\\\\ *)\",\n \"PowerShell(erase *:\\\\ *-Recurse*)\",\n \"PowerShell(erase -Path *:\\\\ *-Recurse*)\",\n \"PowerShell(erase -LiteralPath *:\\\\ *-Recurse*)\",\n \"PowerShell(rd *-Recurse* /)\",\n \"PowerShell(rd *-Recurse* / *)\",\n \"PowerShell(rd / *-Recurse*)\",\n \"PowerShell(rd -Path / *-Recurse*)\",\n \"PowerShell(rd -LiteralPath / *-Recurse*)\",\n \"PowerShell(rd *-Recurse* ~)\",\n \"PowerShell(rd *-Recurse* ~ *)\",\n \"PowerShell(rd ~ *-Recurse*)\",\n \"PowerShell(rd -Path ~ *-Recurse*)\",\n \"PowerShell(rd -LiteralPath ~ *-Recurse*)\",\n \"PowerShell(rd *-Recurse* $HOME)\",\n \"PowerShell(rd *-Recurse* $HOME *)\",\n \"PowerShell(rd $HOME *-Recurse*)\",\n \"PowerShell(rd -Path $HOME *-Recurse*)\",\n \"PowerShell(rd -LiteralPath $HOME *-Recurse*)\",\n \"PowerShell(rd *-Recurse* $env:USERPROFILE)\",\n \"PowerShell(rd *-Recurse* $env:USERPROFILE *)\",\n \"PowerShell(rd $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(rd -Path $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(rd -LiteralPath $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(rd *-Recurse* \\\\\\\\)\",\n \"PowerShell(rd *-Recurse* \\\\ *)\",\n \"PowerShell(rd \\\\ *-Recurse*)\",\n \"PowerShell(rd -Path \\\\ *-Recurse*)\",\n \"PowerShell(rd -LiteralPath \\\\ *-Recurse*)\",\n \"PowerShell(rd *-Recurse* *:\\\\\\\\)\",\n \"PowerShell(rd *-Recurse* *:\\\\ *)\",\n \"PowerShell(rd *:\\\\ *-Recurse*)\",\n \"PowerShell(rd -Path *:\\\\ *-Recurse*)\",\n \"PowerShell(rd -LiteralPath *:\\\\ *-Recurse*)\",\n \"PowerShell(ri *-Recurse* /)\",\n \"PowerShell(ri *-Recurse* / *)\",\n \"PowerShell(ri / *-Recurse*)\",\n \"PowerShell(ri -Path / *-Recurse*)\",\n \"PowerShell(ri -LiteralPath / *-Recurse*)\",\n \"PowerShell(ri *-Recurse* ~)\",\n \"PowerShell(ri *-Recurse* ~ *)\",\n \"PowerShell(ri ~ *-Recurse*)\",\n \"PowerShell(ri -Path ~ *-Recurse*)\",\n \"PowerShell(ri -LiteralPath ~ *-Recurse*)\",\n \"PowerShell(ri *-Recurse* $HOME)\",\n \"PowerShell(ri *-Recurse* $HOME *)\",\n \"PowerShell(ri $HOME *-Recurse*)\",\n \"PowerShell(ri -Path $HOME *-Recurse*)\",\n \"PowerShell(ri -LiteralPath $HOME *-Recurse*)\",\n \"PowerShell(ri *-Recurse* $env:USERPROFILE)\",\n \"PowerShell(ri *-Recurse* $env:USERPROFILE *)\",\n \"PowerShell(ri $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(ri -Path $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(ri -LiteralPath $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(ri *-Recurse* \\\\\\\\)\",\n \"PowerShell(ri *-Recurse* \\\\ *)\",\n \"PowerShell(ri \\\\ *-Recurse*)\",\n \"PowerShell(ri -Path \\\\ *-Recurse*)\",\n \"PowerShell(ri -LiteralPath \\\\ *-Recurse*)\",\n \"PowerShell(ri *-Recurse* *:\\\\\\\\)\",\n \"PowerShell(ri *-Recurse* *:\\\\ *)\",\n \"PowerShell(ri *:\\\\ *-Recurse*)\",\n \"PowerShell(ri -Path *:\\\\ *-Recurse*)\",\n \"PowerShell(ri -LiteralPath *:\\\\ *-Recurse*)\",\n \"PowerShell(rm *-Recurse* /)\",\n \"PowerShell(rm *-Recurse* / *)\",\n \"PowerShell(rm / *-Recurse*)\",\n \"PowerShell(rm -Path / *-Recurse*)\",\n \"PowerShell(rm -LiteralPath / *-Recurse*)\",\n \"PowerShell(rm *-Recurse* ~)\",\n \"PowerShell(rm *-Recurse* ~ *)\",\n \"PowerShell(rm ~ *-Recurse*)\",\n \"PowerShell(rm -Path ~ *-Recurse*)\",\n \"PowerShell(rm -LiteralPath ~ *-Recurse*)\",\n \"PowerShell(rm *-Recurse* $HOME)\",\n \"PowerShell(rm *-Recurse* $HOME *)\",\n \"PowerShell(rm $HOME *-Recurse*)\",\n \"PowerShell(rm -Path $HOME *-Recurse*)\",\n \"PowerShell(rm -LiteralPath $HOME *-Recurse*)\",\n \"PowerShell(rm *-Recurse* $env:USERPROFILE)\",\n \"PowerShell(rm *-Recurse* $env:USERPROFILE *)\",\n \"PowerShell(rm $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(rm -Path $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(rm -LiteralPath $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(rm *-Recurse* \\\\\\\\)\",\n \"PowerShell(rm *-Recurse* \\\\ *)\",\n \"PowerShell(rm \\\\ *-Recurse*)\",\n \"PowerShell(rm -Path \\\\ *-Recurse*)\",\n \"PowerShell(rm -LiteralPath \\\\ *-Recurse*)\",\n \"PowerShell(rm *-Recurse* *:\\\\\\\\)\",\n \"PowerShell(rm *-Recurse* *:\\\\ *)\",\n \"PowerShell(rm *:\\\\ *-Recurse*)\",\n \"PowerShell(rm -Path *:\\\\ *-Recurse*)\",\n \"PowerShell(rm -LiteralPath *:\\\\ *-Recurse*)\",\n \"PowerShell(rmdir *-Recurse* /)\",\n \"PowerShell(rmdir *-Recurse* / *)\",\n \"PowerShell(rmdir / *-Recurse*)\",\n \"PowerShell(rmdir -Path / *-Recurse*)\",\n \"PowerShell(rmdir -LiteralPath / *-Recurse*)\",\n \"PowerShell(rmdir *-Recurse* ~)\",\n \"PowerShell(rmdir *-Recurse* ~ *)\",\n \"PowerShell(rmdir ~ *-Recurse*)\",\n \"PowerShell(rmdir -Path ~ *-Recurse*)\",\n \"PowerShell(rmdir -LiteralPath ~ *-Recurse*)\",\n \"PowerShell(rmdir *-Recurse* $HOME)\",\n \"PowerShell(rmdir *-Recurse* $HOME *)\",\n \"PowerShell(rmdir $HOME *-Recurse*)\",\n \"PowerShell(rmdir -Path $HOME *-Recurse*)\",\n \"PowerShell(rmdir -LiteralPath $HOME *-Recurse*)\",\n \"PowerShell(rmdir *-Recurse* $env:USERPROFILE)\",\n \"PowerShell(rmdir *-Recurse* $env:USERPROFILE *)\",\n \"PowerShell(rmdir $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(rmdir -Path $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(rmdir -LiteralPath $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(rmdir *-Recurse* \\\\\\\\)\",\n \"PowerShell(rmdir *-Recurse* \\\\ *)\",\n \"PowerShell(rmdir \\\\ *-Recurse*)\",\n \"PowerShell(rmdir -Path \\\\ *-Recurse*)\",\n \"PowerShell(rmdir -LiteralPath \\\\ *-Recurse*)\",\n \"PowerShell(rmdir *-Recurse* *:\\\\\\\\)\",\n \"PowerShell(rmdir *-Recurse* *:\\\\ *)\",\n \"PowerShell(rmdir *:\\\\ *-Recurse*)\",\n \"PowerShell(rmdir -Path *:\\\\ *-Recurse*)\",\n \"PowerShell(rmdir -LiteralPath *:\\\\ *-Recurse*)\",\n \"PowerShell(Start-Process *-Verb RunAs*)\",\n \"PowerShell(Invoke-Expression *)\",\n \"PowerShell(iex *)\",\n \"PowerShell(Format-Volume *)\",\n \"PowerShell(icacls */grant*Everyone:F*)\",\n \"PowerShell(icacls */grant*Everyone:(F)*)\"\n ],\n \"ask\": [\n \"Bash(git clean *)\",\n \"Bash(docker volume rm *)\",\n \"Bash(docker system prune *)\",\n \"PowerShell(git clean *)\",\n \"PowerShell(docker volume rm *)\",\n \"PowerShell(docker system prune *)\"\n ]\n },\n \"hooks\": {\n \"SessionStart\": [\n {\n \"hooks\": [\n {\n \"type\": \"command\",\n \"command\": \"__DOCKS_KIT_BUN__\",\n \"args\": [\"__DOCKS_KIT_SESSION_START__\"],\n \"timeout\": 5\n }\n ]\n }\n ],\n \"Notification\": [\n {\n \"hooks\": [\n {\n \"type\": \"command\",\n \"command\": \"__DOCKS_KIT_BUN__\",\n \"args\": [\"__DOCKS_KIT_NOTIFY__\"],\n \"timeout\": 10,\n \"async\": true\n }\n ]\n }\n ],\n \"PostToolUseFailure\": [\n {\n \"matcher\": \"Bash|PowerShell\",\n \"hooks\": [\n {\n \"type\": \"command\",\n \"command\": \"echo '{\\\"hookSpecificOutput\\\":{\\\"hookEventName\\\":\\\"PostToolUseFailure\\\",\\\"additionalContext\\\":\\\"Last bash command failed. Repository / file state may have shifted \\u2014 re-read affected files before retrying. If the failure is a missing dependency or env mismatch, surface it to the user rather than retrying blindly.\\\"}}'\",\n \"timeout\": 5\n }\n ]\n }\n ],\n \"SubagentStop\": [\n {\n \"hooks\": [\n {\n \"type\": \"prompt\",\n \"prompt\": \"You are a quality gate for subagent outputs in a multi-agent code-analysis pipeline.\\n\\nEvaluate the subagent's `last_assistant_message` field (in the JSON below) against these requirements:\\n\\n1. ALLOW (return `{}`): Mode-selection or no-issues responses. Examples: \\\"Which mode do you prefer\\\", \\\"select an option\\\", \\\"no issues / problems / violations / blockers found\\\".\\n\\n2. ALLOW (return `{}`): Output contains at least one concrete file:line citation \\u2014 e.g. `src/auth.ts:42`, `lib/db.ts:100-115`, or path references that include line numbers.\\n\\n3. BLOCK (return `{\\\"decision\\\":\\\"block\\\",\\\"reason\\\":\\\"<one-line explanation>\\\"}`): Output claims about code or findings WITHOUT concrete file:line citations. Vague references like \\\"the auth handler\\\" or \\\"near the database code\\\" are not acceptable as the only evidence.\\n\\nSubagent invocation JSON:\\n$ARGUMENTS\\n\\nReturn ONLY the JSON decision (no commentary, no markdown fences).\",\n \"timeout\": 30\n }\n ]\n }\n ]\n },\n \"statusLine\": {\n \"type\": \"command\",\n \"command\": \"__DOCKS_KIT_STATUSLINE__\",\n \"refreshInterval\": 5\n },\n \"enabledPlugins\": {\n \"docks@docks\": true,\n \"plan-lifecycle@docks\": true,\n \"php-lsp@claude-plugins-official\": true,\n \"rust-analyzer-lsp@claude-plugins-official\": true,\n \"typescript-lsp@claude-plugins-official\": true\n },\n \"extraKnownMarketplaces\": {\n \"docks\": {\n \"source\": {\n \"source\": \"github\",\n \"repo\": \"DocksDocks/docks\"\n }\n }\n },\n \"alwaysThinkingEnabled\": true,\n \"showThinkingSummaries\": true,\n \"viewMode\": \"default\",\n \"theme\": \"dark-daltonized\",\n \"skipDangerousModePermissionPrompt\": true\n}\n",
@@ -14,14 +14,14 @@ export const GENERATED_PAYLOAD_TEXT = {
14
14
  "SoT/.claude/bin/session-start.mjs": "/**\n * @typedef {string | number | boolean | JsonRecord | JsonArray | null | undefined} JsonValue\n * @typedef {{ [key: string]: JsonValue }} JsonRecord\n * @typedef {Array<JsonValue>} JsonArray\n * @typedef {Record<string, string | undefined> | JsonRecord} EnvInput\n * @typedef {object} SessionStartOptions\n * @property {EnvInput} [env]\n * @property {Date} [now]\n * @property {string} [home]\n * @property {(path: string) => string} [readText]\n * @property {(value: string) => void} [writeStdout]\n */\nimport { readFileSync } from \"node:fs\"\nimport { homedir } from \"node:os\"\n\n/**\n * @param {object | string | number | boolean | null | undefined} value\n * @returns {value is JsonRecord}\n */\nfunction isRecord(value) {\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n}\n\n/**\n * @param {JsonValue} value\n * @param {string} fallback\n * @returns {string}\n */\nfunction nonEmpty(value, fallback) {\n return typeof value === \"string\" && value !== \"\" ? value : fallback\n}\n\n/**\n * @param {number} value\n * @returns {string}\n */\nfunction pad(value) {\n return String(value).padStart(2, \"0\")\n}\n\n/**\n * @param {string} home\n * @param {(path: string) => string} readText\n * @returns {string}\n */\nfunction configuredEffort(home, readText) {\n try {\n const parsed = JSON.parse(readText(`${home}/.claude/settings.json`))\n return isRecord(parsed) ? nonEmpty(parsed.effortLevel, \"default\") : \"default\"\n } catch {\n return \"default\"\n }\n}\n\n/**\n * @param {Date} now\n * @returns {string}\n */\nfunction localZone(now) {\n const part = new Intl.DateTimeFormat(\"en-US\", { timeZoneName: \"short\" })\n .formatToParts(now)\n .find((value) => value.type === \"timeZoneName\")\n return part?.value ?? \"\"\n}\n\n/**\n * @param {SessionStartOptions} [options]\n * @returns {string[]}\n */\nexport function sessionStartLines(options = {}) {\n const env = isRecord(options.env) ? options.env : process.env\n const now = options.now instanceof Date ? options.now : new Date()\n const home = typeof options.home === \"string\" ? options.home : homedir()\n const readText = options.readText ?? ((path) => readFileSync(path, \"utf8\"))\n const weekday = new Intl.DateTimeFormat(\"en-US\", { weekday: \"long\" }).format(now)\n const date = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`\n const time = `${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`\n const effort = nonEmpty(env.CLAUDE_CODE_EFFORT_LEVEL, configuredEffort(home, readText))\n const context = env.CLAUDE_CODE_DISABLE_1M_CONTEXT === \"1\" ? \"200K\" : \"1M\"\n const compactWindow = nonEmpty(env.CLAUDE_CODE_AUTO_COMPACT_WINDOW, \"full\")\n const subagent = nonEmpty(env.CLAUDE_CODE_SUBAGENT_MODEL, \"default\")\n return [\n `[CONTEXT] Current date: ${weekday}, ${date} ${time} ${localZone(now)}`,\n `[CONFIG] Context: ${context} | Compact-window: ${compactWindow} | Effort: ${effort} | Thinking: adaptive | Subagent: ${subagent}`\n ]\n}\n\n/**\n * @param {SessionStartOptions} [options]\n * @returns {Promise<number>}\n */\nexport async function main(options = {}) {\n const writeStdout = options.writeStdout ?? ((value) => process.stdout.write(value))\n const output = {\n hookSpecificOutput: {\n hookEventName: \"SessionStart\",\n additionalContext: sessionStartLines(options).join(\"\\n\")\n }\n }\n writeStdout(`${JSON.stringify(output)}\\n`)\n return 0\n}\n\nif (import.meta.main) process.exit(await main())\n",
15
15
  "SoT/.claude/bin/notify.mjs": "/**\n * @typedef {(name: string) => string | null | undefined} WhichFn\n * @typedef {(path: string) => Promise<boolean>} FileExistsFn\n * @typedef {object} QuietSpawnOptions\n * @property {\"ignore\" | \"pipe\" | \"inherit\"} [stdin]\n * @property {\"ignore\" | \"pipe\" | \"inherit\"} [stdout]\n * @property {\"ignore\" | \"pipe\" | \"inherit\"} [stderr]\n * @typedef {(argv: string[], options: QuietSpawnOptions) => void} QuietSpawner\n * @typedef {object} PlayerOptions\n * @property {string} [platform]\n * @property {string} [sound]\n * @property {WhichFn} [which]\n * @typedef {object} NotifyOptions\n * @property {string} [platform]\n * @property {string} [sound]\n * @property {WhichFn} [which]\n * @property {FileExistsFn} [fileExists]\n * @property {QuietSpawner} [spawnSync]\n */\nconst DEFAULT_SOUND = `${import.meta.dir}/../notification.mp3`\n\n/**\n * @param {PlayerOptions} [options]\n * @returns {string[] | undefined}\n */\nexport function selectPlayer(options = {}) {\n const platform = options.platform ?? process.platform\n const sound = options.sound ?? DEFAULT_SOUND\n const which = options.which ?? ((name) => Bun.which(name))\n if (platform === \"darwin\") {\n const afplay = which(\"afplay\")\n if (typeof afplay === \"string\" && afplay !== \"\") return [afplay, sound]\n }\n const ffplay = which(\"ffplay\")\n if (typeof ffplay === \"string\" && ffplay !== \"\") {\n return [ffplay, \"-nodisp\", \"-autoexit\", \"-loglevel\", \"quiet\", sound]\n }\n const paplay = which(\"paplay\")\n if (typeof paplay === \"string\" && paplay !== \"\") return [paplay, sound]\n const aplay = which(\"aplay\")\n if (typeof aplay === \"string\" && aplay !== \"\") return [aplay, \"-q\", sound]\n return undefined\n}\n\n/**\n * @param {NotifyOptions} [options]\n * @returns {Promise<number>}\n */\nexport async function main(options = {}) {\n const sound = options.sound ?? DEFAULT_SOUND\n const fileExists = options.fileExists ?? ((path) => Bun.file(path).exists())\n if (!await fileExists(sound)) return 0\n const command = selectPlayer({ ...options, sound })\n if (command === undefined) return 0\n const spawnSync = options.spawnSync ?? ((argv, spawnOptions) => Bun.spawnSync(argv, spawnOptions))\n spawnSync(command, { stdin: \"ignore\", stdout: \"ignore\", stderr: \"ignore\" })\n return 0\n}\n\nif (import.meta.main) process.exit(await main())\n",
16
16
  "SoT/.codex/AGENTS.md": "# AGENTS.md\n\n## Research Before Implementation\n\nBefore writing or modifying code that uses an API, hook, method, or config surface you have not verified in this session, research current documentation first.\n\nResearch workflow:\n1. Prefer official documentation and primary sources for the specific library, framework, or API.\n2. If a local docs or MCP tool is available, use it before broad web search.\n3. Only then proceed to implementation.\n\nResearch when:\n- Installing or configuring a dependency.\n- Using an API, hook, method, or pattern not verified in this session.\n- Upgrading or migrating between versions.\n- Any task where relying on memory could cause stale syntax or behavior.\n\nDo not:\n- Assume API signatures, method names, or config options from memory.\n- Generate framework code without checking current docs first.\n- Skip research because the library seems familiar.\n\n<constraint>\nResearch the codebase before editing. Never change code you have not read.\n</constraint>\n\n## Agentic Harness Heuristics\n\nModel-agnostic operating rules for coding-agent work.\n\n1. Persistence. Keep going until the user's request is actually handled. Only yield when the problem is solved or a concrete blocker is identified. Resolve in the fewest useful tool loops — once you can answer the core request with evidence, answer. Before ending a turn, check the last paragraph: if it is a plan, a question you can answer yourself, or a promise of work not done, do that work now.\n2. Default to parallel. When multiple reads, searches, inspections, or independent checks can run without depending on each other, run them together.\n3. Multi-pass search. First-pass search often misses — vary the wording before concluding something does not exist.\n4. Trace symbols. Before modifying a symbol, trace its definition and usages. Do not infer behavior from one call site.\n5. Linter-loop 3-strike rule. Do not loop more than 3 times fixing the same lint/test failure without reassessing the diagnosis.\n6. Read-before-edit TTL. If you have not read a file recently, re-read it before editing. User edits can make cached context stale.\n7. Big-file rule. For files over 1000 lines, prefer targeted search plus scoped reads over whole-file reads.\n8. Task hygiene. Track meaningful deliverables, not operational sub-steps. Mark work complete as soon as it is done.\n9. Literal-instruction rule. Treat explicit user requirements as checklists with success criteria. Do not silently broaden scope.\n10. Context hygiene. Prefer a fresh session at task boundaries over carrying stale context; preserve useful state before quality decays. Never stop, summarize, or suggest a new session on account of context limits.\n11. Autonomy calibration. For minor choices (naming, formatting, defaults, equivalent approaches), pick a reasonable option and note it — do not ask. Ask first only for scope changes, destructive actions, or decisions that change the deliverable. When the user is describing a problem or asking a question rather than requesting a change, the deliverable is your assessment — report findings and stop; do not apply fixes until asked.\n12. Capability triggering. Search or fetch current documentation when the answer depends on current or version-specific information. When work fans out across independent items, parallelize or delegate; never delegate work you can complete directly. For verification, prefer a fresh-context check over self-critique. On long tasks, keep running notes and re-read them between phases.\n\n<constraint>\nTreat these heuristics as protocol. If a turn violates an applicable rule, self-correct before continuing.\n</constraint>\n\nReuse before invention: inventory existing code, components, conventions, and dependencies; extend them instead of creating a parallel pattern. Load only the narrow skills supported by the task and repository evidence. If a request establishes a new React/Tailwind system and no convention exists, prefer current shadcn/ui `base-*` components backed by Base UI; otherwise preserve the existing stack. Treat probe, production access, publish, push, release, and deploy as literal current-request effects—never infer external authority from a plan, schedule, review, or old receipt.\n\nFor Docks plan reviews, cross-company review is standing-authorized; do not ask for export consent. This never overrides a host or platform security denial.\n\nUse a narrow-to-broad verification ladder: direct acceptance while iterating, focused regressions next, and one full CI at the pre-commit or release boundary. Reuse still-matching evidence; rerun full CI only after a relevant edit invalidates it.\n\n<constraint>\nNo secrets in committed config. Treat plugin marketplaces, installers, and downloaded artifacts as untrusted until verified.\n</constraint>\n",
17
- "SoT/.codex/config.toml": "model = \"gpt-6-sol\"\nmodel_reasoning_effort = \"high\"\nplan_mode_reasoning_effort = \"high\"\nmodel_reasoning_summary = \"concise\"\nmodel_verbosity = \"low\"\npersonality = \"pragmatic\"\nweb_search = \"live\"\nproject_doc_max_bytes = 131072\napproval_policy = \"on-request\"\nsandbox_mode = \"workspace-write\"\napprovals_reviewer = \"auto_review\"\n\n[sandbox_workspace_write]\nnetwork_access = true\n\n[windows]\nsandbox = \"elevated\"\n\n[features]\nmemories = true\n\n[memories]\ndedicated_tools = true\nmax_rollout_age_days = 30\n\n[agents]\nmax_threads = 12\nmax_depth = 2\n\n[tui]\nstatus_line_use_colors = true\nstatus_line = [\n \"model-with-reasoning\",\n \"current-dir\",\n \"git-branch\",\n \"context-used\",\n \"five-hour-limit\",\n \"weekly-limit\",\n]\n\n[plugins.\"docks@docks\"]\nenabled = true\n\n[plugins.\"plan-lifecycle@docks\"]\nenabled = true\n",
17
+ "SoT/.codex/config.toml": "model = \"gpt-6.1-sol\"\nmodel_reasoning_effort = \"high\"\nplan_mode_reasoning_effort = \"high\"\nmodel_reasoning_summary = \"concise\"\nmodel_verbosity = \"low\"\npersonality = \"pragmatic\"\nweb_search = \"live\"\nproject_doc_max_bytes = 131072\napproval_policy = \"on-request\"\nsandbox_mode = \"workspace-write\"\napprovals_reviewer = \"auto_review\"\n\n[sandbox_workspace_write]\nnetwork_access = true\n\n[windows]\nsandbox = \"elevated\"\n\n[features]\nmemories = true\n\n[memories]\ndedicated_tools = true\nmax_rollout_age_days = 30\n\n[agents]\nmax_threads = 12\nmax_depth = 2\n\n[tui]\nstatus_line_use_colors = true\nstatus_line = [\n \"model-with-reasoning\",\n \"current-dir\",\n \"git-branch\",\n \"context-used\",\n \"five-hour-limit\",\n \"weekly-limit\",\n]\n\n[plugins.\"docks@docks\"]\nenabled = true\n\n[plugins.\"plan-lifecycle@docks\"]\nenabled = true\n",
18
18
  "SoT/.codex/plugins/marketplace.json": "{\n \"name\": \"docks\",\n \"interface\": {\n \"displayName\": \"DocksDocks\"\n },\n \"plugins\": [\n {\n \"name\": \"docks\",\n \"source\": {\n \"source\": \"git-subdir\",\n \"url\": \"https://github.com/DocksDocks/docks.git\",\n \"path\": \"./plugins/docks\",\n \"ref\": \"main\"\n },\n \"policy\": {\n \"installation\": \"AVAILABLE\",\n \"authentication\": \"ON_INSTALL\"\n },\n \"category\": \"Productivity\"\n },\n {\n \"name\": \"plan-lifecycle\",\n \"source\": {\n \"source\": \"git-subdir\",\n \"url\": \"https://github.com/DocksDocks/docks.git\",\n \"path\": \"./plugins/plan-lifecycle\",\n \"ref\": \"main\"\n },\n \"policy\": {\n \"installation\": \"AVAILABLE\",\n \"authentication\": \"ON_INSTALL\"\n },\n \"category\": \"Productivity\"\n }\n ]\n}\n",
19
19
  "SoT/.codex/rules/docks.rules": "prefix_rule(pattern=[\"pwd\"], decision=\"allow\")\nprefix_rule(pattern=[\"ls\"], decision=\"allow\")\nprefix_rule(pattern=[\"cat\"], decision=\"allow\")\nprefix_rule(pattern=[\"head\"], decision=\"allow\")\nprefix_rule(pattern=[\"tail\"], decision=\"allow\")\nprefix_rule(pattern=[\"wc\"], decision=\"allow\")\nprefix_rule(pattern=[\"nl\"], decision=\"allow\")\nprefix_rule(pattern=[\"grep\"], decision=\"allow\")\nprefix_rule(pattern=[\"sort\"], decision=\"allow\")\nprefix_rule(pattern=[\"uniq\"], decision=\"allow\")\nprefix_rule(pattern=[\"diff\"], decision=\"allow\")\nprefix_rule(pattern=[\"which\"], decision=\"allow\")\nprefix_rule(pattern=[\"date\"], decision=\"allow\")\nprefix_rule(pattern=[\"basename\"], decision=\"allow\")\nprefix_rule(pattern=[\"dirname\"], decision=\"allow\")\nprefix_rule(pattern=[\"realpath\"], decision=\"allow\")\nprefix_rule(pattern=[\"readlink\"], decision=\"allow\")\nprefix_rule(pattern=[\"jq\"], decision=\"allow\")\nprefix_rule(pattern=[\"tree\"], decision=\"allow\")\nprefix_rule(pattern=[\"cut\"], decision=\"allow\")\nprefix_rule(pattern=[\"tr\"], decision=\"allow\")\nprefix_rule(pattern=[\"echo\"], decision=\"allow\")\nprefix_rule(pattern=[\"printf\"], decision=\"allow\")\nprefix_rule(pattern=[\"printenv\"], decision=\"allow\")\nprefix_rule(pattern=[\"uname\"], decision=\"allow\")\nprefix_rule(pattern=[\"file\"], decision=\"allow\")\nprefix_rule(pattern=[\"stat\"], decision=\"allow\")\nprefix_rule(pattern=[\"du\"], decision=\"allow\")\nprefix_rule(pattern=[\"id\"], decision=\"allow\")\nprefix_rule(pattern=[\"whoami\"], decision=\"allow\")\n\nprefix_rule(pattern=[\"git\", \"status\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"diff\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"log\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"show\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"blame\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"rev-parse\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"ls-files\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"grep\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"ls-tree\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"branch\", \"--show-current\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"branch\", \"-vv\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"mv\"], decision=\"allow\")\n\nprefix_rule(pattern=[\"gh\", \"pr\", \"view\"], decision=\"allow\")\nprefix_rule(pattern=[\"gh\", \"pr\", \"list\"], decision=\"allow\")\nprefix_rule(pattern=[\"gh\", \"pr\", \"diff\"], decision=\"allow\")\nprefix_rule(pattern=[\"gh\", \"pr\", \"status\"], decision=\"allow\")\nprefix_rule(pattern=[\"gh\", \"pr\", \"checks\"], decision=\"allow\")\n\nprefix_rule(pattern=[\"docker\", \"ps\"], decision=\"allow\")\n\nprefix_rule(pattern=[\"git\", \"push\"], decision=\"prompt\")\nprefix_rule(pattern=[\"git\", \"reset\"], decision=\"prompt\")\nprefix_rule(pattern=[\"git\", \"clean\"], decision=\"prompt\")\nprefix_rule(pattern=[\"git\", \"merge\"], decision=\"prompt\")\nprefix_rule(pattern=[\"git\", \"rebase\"], decision=\"prompt\")\nprefix_rule(pattern=[\"git\", \"checkout\"], decision=\"prompt\")\n\nprefix_rule(pattern=[\"rm\"], decision=\"prompt\")\nprefix_rule(pattern=[\"mv\"], decision=\"prompt\")\nprefix_rule(pattern=[\"chmod\"], decision=\"prompt\")\nprefix_rule(pattern=[\"chown\"], decision=\"prompt\")\nprefix_rule(pattern=[\"kill\"], decision=\"prompt\")\nprefix_rule(pattern=[\"pkill\"], decision=\"prompt\")\n\nprefix_rule(pattern=[\"npm\", \"install\"], decision=\"prompt\")\nprefix_rule(pattern=[\"npm\", \"uninstall\"], decision=\"prompt\")\nprefix_rule(pattern=[\"pnpm\", \"add\"], decision=\"prompt\")\nprefix_rule(pattern=[\"pnpm\", \"remove\"], decision=\"prompt\")\nprefix_rule(pattern=[\"yarn\", \"add\"], decision=\"prompt\")\nprefix_rule(pattern=[\"yarn\", \"remove\"], decision=\"prompt\")\nprefix_rule(pattern=[\"pip\", \"install\"], decision=\"prompt\")\nprefix_rule(pattern=[\"pip\", \"uninstall\"], decision=\"prompt\")\nprefix_rule(pattern=[\"pip3\", \"install\"], decision=\"prompt\")\nprefix_rule(pattern=[\"pip3\", \"uninstall\"], decision=\"prompt\")\n\nprefix_rule(pattern=[\"docker\", \"run\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker\", \"rm\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker\", \"stop\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker\", \"volume\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker\", \"system\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker\", \"compose\", \"up\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker\", \"compose\", \"down\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker\", \"compose\", \"rm\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker\", \"compose\", \"stop\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker-compose\", \"up\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker-compose\", \"down\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker-compose\", \"rm\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker-compose\", \"stop\"], decision=\"prompt\")\n\n# `tail -f`/`--follow` never returns and hangs the agent (bare `tail` stays allowed above).\nprefix_rule(pattern=[\"tail\", \"-f\"], decision=\"prompt\")\nprefix_rule(pattern=[\"tail\", \"--follow\"], decision=\"prompt\")\n# rg and `sed -n` are prompt, NOT allow: argv-prefix matching cannot gate their\n# code-exec forms (rg --pre=CMD or a reordered --pre; sed -n 'e CMD' / -ni) while a\n# shorter allow prefix would auto-approve the whole command.\nprefix_rule(pattern=[\"rg\"], decision=\"prompt\")\nprefix_rule(pattern=[\"sed\", \"-n\"], decision=\"prompt\")\nprefix_rule(pattern=[\"find\"], decision=\"prompt\")\nprefix_rule(pattern=[\"sed\", \"-i\"], decision=\"prompt\")\nprefix_rule(pattern=[\"sed\", \"--in-place\"], decision=\"prompt\")\nprefix_rule(pattern=[\"awk\"], decision=\"prompt\")\nprefix_rule(pattern=[\"xargs\"], decision=\"prompt\")\nprefix_rule(pattern=[\"tee\"], decision=\"prompt\")\nprefix_rule(pattern=[\"curl\"], decision=\"prompt\")\nprefix_rule(pattern=[\"env\"], decision=\"prompt\")\n\nprefix_rule(pattern=[\"sudo\"], decision=\"forbidden\")\nprefix_rule(pattern=[\"eval\"], decision=\"forbidden\")\nprefix_rule(pattern=[\"mkfs\"], decision=\"forbidden\")\nprefix_rule(pattern=[\"dd\"], decision=\"forbidden\")\nprefix_rule(pattern=[\"git\", \"push\", \"--force\", \"origin\", \"main\"], decision=\"forbidden\")\nprefix_rule(pattern=[\"git\", \"push\", \"--force\", \"origin\", \"master\"], decision=\"forbidden\")\nprefix_rule(pattern=[\"git\", \"push\", \"-f\", \"origin\", \"main\"], decision=\"forbidden\")\nprefix_rule(pattern=[\"git\", \"push\", \"-f\", \"origin\", \"master\"], decision=\"forbidden\")\n",
20
20
  "SoT/.omp/AGENTS.md": "# Global OMP guidance\n\n- Verify unfamiliar or version-sensitive APIs and configuration against current official documentation before implementation.\n- When the user asks for an assessment rather than a change, report findings without editing.\n- For Docks plan reviews, cross-company review is standing-authorized; host security policy still applies.\n- Please remove all mannered prose.\n\n## Asking me things\n\nA question typed in prose is just text I may or may not act on. The `ask` tool renders a\nblocking picker, waits indefinitely (`ask.timeout = 0`), and records my answer in the\ntranscript. If you actually need an answer, it MUST go through `ask`.\n\nMUST use `ask` before:\n- Anything irreversible or destructive: deleting/overwriting files or data you did not create,\n force-push, history rewrite, dropping tables, running migrations, mass rename, touching\n secrets/credentials, or publishing outward (release, upstream PR, issue, comment).\n- Two or more viable approaches whose tradeoffs are mine to own: schema/API/protocol shape,\n adding a dependency, or establishing a convention this repo does not already have.\n- A fact only I hold: intended semantics of an ambiguous requirement, which of several\n conflicting existing patterns is canonical, or which environment/account/target to use.\n- A request that contradicts the repo: surface the conflict and let me resolve it; never\n silently pick one side.\n\nIf `ask` is not registered — subagent, headless, or `-p` print runs, where `hasUI` is false —\nthe MUST above cannot be satisfied: do not fabricate the call and do not stall on it. Take the\nconservative reversible option and put the question, plus the assumption you made, in your\nfinal report so whoever spawned you can decide.\n\nNEVER use `ask` for:\n- Permission to begin, or to confirm scope already stated in the request.\n- Anything a tool, grep, or doc can answer — go read it.\n- A cheap reversible choice — take the conservative option and say which you took.\n- Something already answered earlier in the conversation.\n\nBatch every open question into one `ask` call with multiple questions; do not serialize\nround trips. Being overruled ends the discussion — execute my call without relitigating.\n\n## Output Standard\n\nApply Simplified Technical English to all agent text. This includes responses, messages,\ndocumentation, comments, and interface text.\n\nReply in the language I use, and apply every rule below to that language.\n\nA rule that names English grammar applies only to English. The contraction ban is one\nsuch rule. In another language, follow the normal grammar of that language. Portuguese,\nSpanish, French, Italian, and German merge a preposition with an article, and that merge\nis required, not optional.\n\nTreat the word limits as approximate outside English. Some languages need more words to\ncarry the same content.\n\n- Use the simplest precise technical term.\n- Use each term consistently.\n- Expand an abbreviation at its first occurrence.\n- Explain a technical term when I ask for an explanation.\n- Write complete and grammatically correct sentences.\n- Use active voice and identify the actor.\n- Use the imperative form for instructions.\n- Put only one action in each instruction sentence.\n- Put a necessary condition before its instruction.\n- Use simple verb tenses.\n- Do not use contractions, idioms, or slang. Avoid humor and rhetorical questions.\n- Keep procedural sentences to 20 words or fewer.\n- Keep descriptive sentences to 25 words or fewer.\n- Keep each paragraph to one topic and six sentences or fewer.\n- Do not use more than three nouns together.\n- Use vertical lists for complex information.\n- Put a warning or caution before a related hazardous instruction.\n\n### Naming\n\n- Say what the thing does before you name it. Put the technical term after the plain\n description, once, in parentheses.\n- Do not use a technical term as the only name for something you just introduced.\n- Prefer the short common word. Use \"use\", not \"utilize\". Use \"set up\", not \"provision\".\n- Do not explain by metaphor alone. A metaphor may follow a literal statement.\n",
21
- "SoT/.omp/config.yml": "statusLine:\n compactThinkingLevel: false\n\ndefaultThinkingLevel: high\n\ncycleOrder:\n - smol\n - default\n - slow\n - fable\n - astra\n\ntier:\n openai: none\n anthropic: none\n\nadvisor:\n enabled: true\n syncBacklog: \"1\"\n\ngithub:\n enabled: true\n\nfind:\n enabled: true\n\ntask:\n eager: always\n showResolvedModelBadge: true\n softRequestBudget: 200\n softRequestBudgetNotice: true\n batch: true\n enableLsp: true\n enableEffort: true\n maxEffort: max\n maxConcurrency: 16\n maxRecursionDepth: 2\n maxRuntimeMs: 3600000\n agentModelOverrides:\n reviewer: \"@task\"\n security-reviewer: \"@task\"\n code-reviewer: \"@task\"\n plan-reviewer: \"@task\"\n\nmodelRoles:\n smol: openai-codex/gpt-6-luna:medium\n advisor: anthropic/claude-opus-5-5:medium\n designer: anthropic/claude-opus-5-5:high\n plan: anthropic/claude-opus-5-5:xhigh\n commit: openai-codex/gpt-6-luna:medium\n task: openai-codex/gpt-6-sol:high\n vision: anthropic/claude-opus-5-5:medium\n tiny: openai-codex/gpt-6-luna:low\n default: anthropic/claude-opus-5-5:high\n slow: anthropic/claude-opus-5-5:xhigh\n fable: anthropic/claude-fable-5-1:medium\n switch_fable: anthropic/claude-fable-5-1:medium\n astra: openai-codex/gpt-6-astra:xhigh\n web: web/firecrawl\n\nmodelTags:\n fable:\n name: Fable 5.1\n switch_fable:\n name: Fable switch default\n hidden: true\n astra:\n name: GPT-6 Astra\n\ndisplay:\n showTokenUsage: true\n\nproviders:\n anthropic:\n serverSideFallback: false\n fetch: auto\n webSearchTimeoutSeconds: 30\n\nretry:\n usageAwareFallback: true\n fallbackChains:\n default:\n - openai-codex/gpt-6-sol:high\n advisor: []\n task:\n - anthropic/claude-opus-5-5:high\n vision:\n - openai-codex/gpt-6-sol:medium\n smol:\n - anthropic/claude-opus-5-5:low\n tiny:\n - anthropic/claude-opus-5-5:low\n commit:\n - anthropic/claude-opus-5-5:medium\n switch_fable: []\n fable:\n - openai-codex/gpt-6-astra:xhigh\n astra:\n - anthropic/claude-fable-5-1:medium\n web:\n - web/exa\n - web/perplexity\n - openai-codex/gpt-6-luna\n - web/parallel\n - web/zai\n - web/tinyfish\n - web/jina\n - web/kagi\n - web/tavily\n - web/brave\n - web/kimi\n - web/synthetic\n - web/ollama\n - web/searxng\n - web/startpage\n - web/duckduckgo\n - web/ecosia\n - web/google\n - web/mojeek\n - web/public\n\nincludeWorkspaceTree: false\n\nbranchSummary:\n enabled: true\n\ncommands:\n enableOpencodeProject: false\n enableOpencodeUser: false\n\nskills:\n enableClaudeUser: false\n enableCodexUser: false\n enableAgentsUser: true\n\nsteeringMode: all\n\ndev:\n autoqa: false\n autoqaConsent: denied\n\ncompaction:\n thresholdTokens: -1\n idleEnabled: true\n handoffSaveToDisk: true\n\ndoubleEscapeAction: tree\ntextVerbosity: low\n\nfeatures:\n unexpectedStopDetection: smart\n\ncodexResets:\n autoRedeem: \"no\"\n\nstartup:\n quiet: true\n\ntools:\n approvalMode: yolo\n",
21
+ "SoT/.omp/config.yml": "statusLine:\n compactThinkingLevel: false\n\ndefaultThinkingLevel: high\n\ncycleOrder:\n - smol\n - default\n - slow\n - fable\n - astra\n\ntier:\n openai: none\n anthropic: none\n\nadvisor:\n enabled: true\n syncBacklog: \"1\"\n\ngithub:\n enabled: true\n\nfind:\n enabled: true\n\ntask:\n eager: always\n showResolvedModelBadge: true\n softRequestBudget: 200\n softRequestBudgetNotice: true\n batch: true\n enableLsp: true\n enableEffort: true\n maxEffort: max\n maxConcurrency: 16\n maxRecursionDepth: 2\n maxRuntimeMs: 3600000\n agentModelOverrides:\n reviewer: \"@task\"\n security-reviewer: \"@task\"\n code-reviewer: \"@task\"\n plan-reviewer: \"@task\"\n\nmodelRoles:\n smol: openai-codex/gpt-6-luna:medium\n advisor: anthropic/claude-opus-5-5:medium\n designer: anthropic/claude-opus-5-5:high\n plan: anthropic/claude-opus-5-5:xhigh\n commit: openai-codex/gpt-6-luna:medium\n task: openai-codex/gpt-6.1-sol:high\n vision: anthropic/claude-opus-5-5:medium\n tiny: openai-codex/gpt-6-luna:low\n default: anthropic/claude-opus-5-5:high\n slow: anthropic/claude-opus-5-5:xhigh\n fable: anthropic/claude-fable-5-1:medium\n switch_fable: anthropic/claude-fable-5-1:medium\n astra: openai-codex/gpt-6-astra:xhigh\n web: web/firecrawl\n\nmodelTags:\n fable:\n name: Fable 5.1\n switch_fable:\n name: Fable switch default\n hidden: true\n astra:\n name: GPT-6 Astra\n\ndisplay:\n showTokenUsage: true\n\nproviders:\n anthropic:\n serverSideFallback: false\n fetch: auto\n webSearchTimeoutSeconds: 30\n\nretry:\n usageAwareFallback: true\n fallbackChains:\n default:\n - openai-codex/gpt-6.1-sol:high\n advisor: []\n task:\n - anthropic/claude-opus-5-5:high\n vision:\n - openai-codex/gpt-6.1-sol:medium\n smol:\n - anthropic/claude-opus-5-5:low\n tiny:\n - anthropic/claude-opus-5-5:low\n commit:\n - anthropic/claude-opus-5-5:medium\n switch_fable: []\n fable:\n - openai-codex/gpt-6-astra:xhigh\n astra:\n - anthropic/claude-fable-5-1:medium\n web:\n - web/exa\n - web/perplexity\n - openai-codex/gpt-6-luna\n - web/parallel\n - web/zai\n - web/tinyfish\n - web/jina\n - web/kagi\n - web/tavily\n - web/brave\n - web/kimi\n - web/synthetic\n - web/ollama\n - web/searxng\n - web/startpage\n - web/duckduckgo\n - web/ecosia\n - web/google\n - web/mojeek\n - web/public\n\nincludeWorkspaceTree: false\n\nbranchSummary:\n enabled: true\n\ncommands:\n enableOpencodeProject: false\n enableOpencodeUser: false\n\nskills:\n enableClaudeUser: false\n enableCodexUser: false\n enableAgentsUser: true\n\nsteeringMode: all\n\ndev:\n autoqa: false\n autoqaConsent: denied\n\ncompaction:\n thresholdTokens: -1\n idleEnabled: true\n handoffSaveToDisk: true\n\ndoubleEscapeAction: tree\ntextVerbosity: low\n\nfeatures:\n unexpectedStopDetection: smart\n\ncodexResets:\n autoRedeem: \"no\"\n\nstartup:\n quiet: true\n\ntools:\n approvalMode: yolo\n",
22
22
  "SoT/.omp/intercom.json": "{\n \"brokerCommand\": \"bun\",\n \"brokerArgs\": []\n}\n",
23
23
  "SoT/.omp/mcp.json": "{\n \"$schema\": \"https://raw.githubusercontent.com/can1357/oh-my-pi/main/packages/coding-agent/src/config/mcp-schema.json\",\n \"disabledServers\": [\n \"chrome-devtools\",\n \"context7:context7\",\n \"openaiDeveloperDocs\"\n ]\n}\n",
24
- "SoT/.omp/models.yml": "providers:\n # Temporary metadata for an id the shared models.dev catalog still publishes\n # as an empty stub. Remove this block once that catalog row carries the real\n # context window, output cap, ladder, and prices. `defaultLevel` is the kit's\n # choice for a bare selector, not the Claude API default, which is `medium`.\n anthropic:\n modelOverrides:\n claude-opus-5-5:\n name: Claude Opus 5.5\n reasoning: true\n input:\n - text\n - image\n contextWindow: 1000000\n maxTokens: 128000\n cost:\n input: 4\n output: 20\n cacheRead: 0.2\n cacheWrite: 5\n thinking:\n mode: effort\n efforts:\n - low\n - medium\n - high\n - xhigh\n - max\n defaultLevel: high\n openai-codex:\n modelOverrides:\n gpt-6-astra:\n thinking:\n mode: effort\n efforts:\n - low\n - medium\n - high\n - xhigh\n - max\n defaultLevel: xhigh\n"
24
+ "SoT/.omp/models.yml": "providers:\n openai-codex:\n modelOverrides:\n gpt-6-astra:\n thinking:\n mode: effort\n efforts:\n - low\n - medium\n - high\n - xhigh\n - max\n defaultLevel: xhigh\n"
25
25
  } as const
26
26
 
27
27
  export const GENERATED_PAYLOAD_BASE64 = {
@@ -50,4 +50,4 @@ export const GENERATED_PAYLOAD_PATHS = [
50
50
  "notification.mp3"
51
51
  ] as const
52
52
 
53
- export const GENERATED_PAYLOAD_HASH = "f57eb42f09dac1f0336ff710bf9e6f3cf3ac68d8f72c9ff5212348ab943d1931"
53
+ export const GENERATED_PAYLOAD_HASH = "8e424ac754febd4df7181a749b2dcf93340532371edcda83801ffd3717fe592c"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "docks-kit",
3
- "version": "0.20.0",
3
+ "version": "0.20.2",
4
4
  "description": "Portable AI coding agent config kit — SoT sync engine + typed CLI for Claude Code, Codex, and universal agent skills",
5
5
  "type": "module",
6
6
  "license": "MIT",