talking-stick 0.4.11 → 0.4.13

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.
@@ -0,0 +1,793 @@
1
+ # Plan: Antigravity (agy) harness support + shared `~/.agents/skills` install trimming
2
+
3
+ - **Date:** 2026-06-13
4
+ - **Author:** Claude (draft) + Codex (adversarial review) + Claude (convergence) + Claude (convergence review)
5
+ - **Status:** IMPLEMENTED & VERIFIED (2026-06-14). All §12.5 steps 1-9 done. Codex implemented; Claude
6
+ reviewed + tested. Verification: typecheck clean, 386 tests pass, build clean, antigravity identity
7
+ (conversation-id + trajectory fallback) confirmed, real `tt install --all` dogfood migrated to shared
8
+ `~/.agents/skills/talking-stick` + pruned codex/grok/opencode duplicates (claude proprietary & gemini
9
+ symlink left intact), Option B uninstall confirmed (single-harness leaves shared + hint; `agents`/
10
+ `--shared`/`--all` remove shared). §13.2 resolved Option B. SHIPPED to branch `antigravity-agents-skill`
11
+ (feature commit 0c2f3ca + version commit c0f82b1, tag v0.4.13 pushed; draft GitHub release created).
12
+ REMAINING (operator-gated): `npm publish` (2FA), publish the draft release, and land the branch on
13
+ master (prefer fast-forward/merge over squash to keep tag v0.4.13 valid).
14
+ - **Phase:** draft -> adversarial review -> convergence (done) -> implementation
15
+ - **Scope:** Planning only. No code changes in this turn beyond this document.
16
+ - **Implementation entry point:** §12 (converged decisions) is authoritative; §1-11 are the
17
+ reasoning trail. Build from §12.5 (build sequence).
18
+
19
+ ## 1. Summary / goals
20
+
21
+ Two coupled changes:
22
+
23
+ 1. **Add Antigravity CLI (`agy`) as a first-class harness**, replacing the now-deprecated
24
+ Gemini CLI. Antigravity announces itself with env markers `ANTIGRAVITY_AGENT=1`,
25
+ `ANTIGRAVITY_CONVERSATION_ID`, and `ANTIGRAVITY_TRAJECTORY_ID`.
26
+ 2. **Trim skill-install behavior so Talking Stick does not create duplicate or conflicting
27
+ `talking-stick` skill entries** for harnesses that load skills from a shared
28
+ `~/.agents/skills` directory in addition to (or instead of) their proprietary
29
+ `~/.<harness>/skills` folder.
30
+
31
+ These are coupled because the most likely reason Antigravity matters here is that Antigravity
32
+ reads the shared `~/.agents/skills` location rather than a proprietary folder, which is exactly
33
+ the duplication problem we need to solve generally.
34
+
35
+ The two changes share one root design decision: **install the skill once per physical location,
36
+ keyed by a per-harness "skill loading model," instead of once per harness name.**
37
+
38
+ ## 2. Current state (evidence)
39
+
40
+ All paths below are in this repo at the time of writing (package version 0.4.12).
41
+
42
+ ### 2.1 Harness enumeration is duplicated across several modules
43
+
44
+ There is no single registry of harnesses; the set is re-declared per concern, so adding/removing
45
+ a harness touches many files:
46
+
47
+ - `src/install.ts:7` — `SUPPORTED_HARNESSES = ["claude-code", "codex", "gemini", "grok", "opencode"]`
48
+ (install ids; drives `tt install`, `--all`, `parseHarnessList`, stale-MCP cleanup).
49
+ - `src/skill-install.ts:15` — `FILE_SKILL_HARNESSES = ["claude-code", "codex", "grok", "opencode"]`
50
+ (harnesses that get a **file** skill install — copy/symlink into a proprietary folder). Gemini
51
+ is deliberately excluded because it is registry-managed.
52
+ - `src/identity.ts:58` — `HarnessCliHarness = "claude" | "codex" | "gemini" | "grok" | "opencode"`
53
+ (CLI identity prefixes / display names).
54
+ - `src/identity.ts:406` — `HARNESS_COMMAND_MAPPING` maps process-command labels
55
+ (`claude`, `claude-code`, `codex`, `gemini`, `grok`, `opencode`) → harness, used by ancestry walking.
56
+ - `src/instructions.ts:8` — `InstructionHarness` union includes `gemini`.
57
+ - `src/instructions.ts:95` — `HARNESS_ALIASES` (`gemini: "gemini"`, etc.).
58
+ - `src/instructions.ts:82` — `## Gemini` section inside `DEFAULT_INSTRUCTIONS_MARKDOWN`, plus the
59
+ prose line at `src/instructions.ts:68` ("Gemini and OpenCode start with conservative local guidance...").
60
+ - `src/cli/output.ts:204` — help text enumerates `claude|codex|gemini|grok|opencode`.
61
+
62
+ ### 2.2 How identity detection works (Gemini today)
63
+
64
+ - `detectHarnessSignal` (`src/identity.ts:444`) checks env markers in order: `CLAUDECODE`,
65
+ `CODEX_MANAGED_BY_NPM`/`CODEX_THREAD_ID`, `GEMINI_CLI` (`src/identity.ts:459`), `OPENCODE`, then
66
+ cmux launch kind. Gemini returns `{ harness: "gemini", sessionId: null, pidHint: null }` — it has
67
+ **no env session id**, so the session id falls back to ancestry root pid/startTime
68
+ (`resolveHarnessSessionId`, `src/identity.ts:272`).
69
+ - Ancestry detection (`detectHarnessViaAncestry`, `src/identity.ts:415`) and
70
+ `findHarnessRootInAncestry` (`src/identity.ts:346`) rely on `HARNESS_COMMAND_MAPPING`, i.e. a
71
+ process whose command basename is `gemini`.
72
+
73
+ ### 2.3 How skill install works
74
+
75
+ `tt install <harness>` (`runInstallCommand`, `src/cli/install-commands.ts:39`) does two things per
76
+ harness: (a) install/refresh the bundled `talking-stick` skill, and (b) clean up stale MCP
77
+ registrations from older releases. `tt install` no longer **adds** MCP servers (README:164).
78
+
79
+ Skill install targets (`resolveSkillTargetPath`, `src/skill-install.ts:42`):
80
+
81
+ - `claude-code` → `~/.claude/skills/talking-stick`
82
+ - `codex` → `~/.codex/skills/talking-stick`
83
+ - `grok` → `<grok config dir>/skills/talking-stick` (default `~/.grok`)
84
+ - `opencode` → `<opencode config dir>/skills/talking-stick` (default `~/.config/opencode`, honors `XDG_CONFIG_HOME`)
85
+ - `gemini` → **special-cased** (`planSkillInstall`, `src/skill-install.ts:80-105`): runs
86
+ `gemini skills link <src> --scope user --consent` (or `gemini skills install ...` with `--copy`).
87
+ The on-disk location it *inspects* is `~/.gemini/skills/talking-stick`.
88
+
89
+ For the four `FILE_SKILL_HARNESSES`, `installSkillDirectory` (`src/skill-install.ts:188`) writes a
90
+ **symlink** (default) or a recursive **copy** (`--copy`) into the proprietary folder. Each harness
91
+ gets its **own physical copy/symlink**. Idempotence is via `inspectInstalledSkill`
92
+ (`src/skill-install.ts:291`): symlink → bundled source = `present`; matching copy digest = `present`.
93
+
94
+ `syncInstalledSkills` (`src/skill-install.ts:161`) silently realigns the four
95
+ `FILE_SKILL_HARNESSES` on human CLI runs (called from `runStartupMaintenance`,
96
+ `src/cli/startup-maintenance.ts:32`). Gemini is excluded from silent sync (README:176, SKILL.md:52).
97
+
98
+ ### 2.4 How uninstall / stale cleanup works
99
+
100
+ - `planSkillUninstall` (`src/skill-install.ts:129`) removes the proprietary folder, or for gemini
101
+ runs `gemini skills uninstall talking-stick --scope user`.
102
+ - `planUninstall` (`src/install.ts:274`) removes legacy MCP registrations: `claude mcp remove`,
103
+ `codex mcp remove`, `gemini mcp remove -s user` (`src/install.ts:303`), OpenCode JSON patch.
104
+ Grok is skipped (`src/install.ts:316`).
105
+ - `removeStaleMcpRegistrations` (`src/install-migration.ts:35`) + `runStaleMcpCleanup`
106
+ (`src/update-migration.ts:58`) run on install, uninstall, self-update, and first-run after a
107
+ version change. It is **strict** for OpenCode (only removes the canonical `["tt","mcp"]` shape;
108
+ preserves hand-edited entries) and name-only for the exec harnesses
109
+ (`src/install-migration.ts:18-26`).
110
+
111
+ ### 2.5 There is no `~/.agents/skills` concept anywhere yet
112
+
113
+ `grep -rn "\.agents\|agents/skills"` across `src/ tests/ docs/ skills/ README.md` returns **nothing**.
114
+ A shared agents skills directory is entirely net-new. Likewise `antigravity`/`agy` appears nowhere.
115
+
116
+ ### 2.5a Codex read-only recon (2026-06-13, `codex:b71174a5`)
117
+
118
+ Folded in from Codex's pre-review recon on this machine:
119
+
120
+ - **`agy` has no `skills` subcommand.** `agy --help` exposes `plugin import/install/link` (a plugin
121
+ system), not `agy skills ...`. → **Do not** model Antigravity as `gemini`-style registry-exec via a
122
+ fake `agy skills`. Antigravity skills are file-based via `~/.agents/skills`. (If a plugin-based path
123
+ is ever wanted, it would be `agy plugin`, but file-based shared install is the primary route.)
124
+ - **Official Antigravity migration guidance:** workspace `.gemini/skills` should move/rename to
125
+ `.agents/skills`. This confirms `.agents/skills` as the convergent convention (project scope; the
126
+ user-scope analogue is `~/.agents/skills`).
127
+ - **Live duplication evidence on this machine:** `~/.agents/skills` exists but has **no** talking-stick
128
+ entry; meanwhile `talking-stick` is currently linked into `~/.claude`, `~/.codex`, `~/.gemini`,
129
+ `~/.grok`, **and** `~/.opencode`. Separately, **both** `~/.opencode/skills` and
130
+ `~/.config/opencode/skills` exist — note our code targets only `~/.config/opencode`
131
+ (`resolveOpencodeConfigDirFromResolved`, `src/install.ts:241`), so an `~/.opencode/skills` copy is
132
+ either user-created or from another tool. This is concrete evidence that multi-location skill state
133
+ already exists and will duplicate once a harness reads both shared and proprietary.
134
+ - **Codex design steer:** make the shared `agents` target **first-class**, and keep harness-specific
135
+ targets only where needed — Claude Code (`~/.claude/skills`, if it does not read `~/.agents/skills`)
136
+ and harness-only extras like the Grok session hook. Antigravity identity from `ANTIGRAVITY_AGENT`
137
+ plus conversation/trajectory IDs and ancestry label `agy`. Recorded as the leading **D1** option
138
+ in §8.
139
+
140
+ ### 2.6 Key existing invariant we must preserve
141
+
142
+ The bundled `SKILL.md` **self-gates**: its `description` says to use it only when the workspace
143
+ coordinates with Talking Stick or contains a `.talking-stick/` marker (`skills/talking-stick/SKILL.md:3`,
144
+ lines 16-22). This matters: installing into a **broadly shared** `~/.agents/skills` means every agent
145
+ reading that directory will see the skill. Because the skill self-gates, a broad install is acceptable
146
+ and does not force coordination behavior onto unrelated repos.
147
+
148
+ ## 3. Problem statement (the duplication bug we are preventing)
149
+
150
+ If a harness loads skills from **both** a shared `~/.agents/skills/talking-stick` **and** its
151
+ proprietary `~/.<harness>/skills/talking-stick`, and `tt install` writes both, the agent sees the
152
+ `talking-stick` skill **twice**. Consequences:
153
+
154
+ - Two skill entries with the same name → ambiguous/duplicated tool surface.
155
+ - If one location is stale (older bundled version) and the other fresh, the agent may load
156
+ conflicting instructions depending on the harness's precedence rules (which are undefined / vary).
157
+ - Silent sync (`syncInstalledSkills`) keeps re-asserting the proprietary copy, so the duplicate
158
+ never self-heals.
159
+
160
+ Today this does not bite because (as far as the current code assumes) each file harness reads only
161
+ its own proprietary folder. The moment a harness (Antigravity is the immediate case; possibly Codex
162
+ or others later) also reads `~/.agents/skills`, the duplication appears. We want to ship the model
163
+ that prevents it **before** Antigravity lands, and clean up any duplicates older `tt` versions created.
164
+
165
+ ## 4. Proposed design
166
+
167
+ ### 4.1 Introduce a single harness capability registry
168
+
169
+ Create one authoritative table describing each harness's skill-loading model and config locations,
170
+ and derive the scattered lists from it. Proposed location: `src/harness-registry.ts` (new), or extend
171
+ `src/install.ts`. Shape (illustrative, not final):
172
+
173
+ ```ts
174
+ type SkillLoadingModel =
175
+ | { kind: "shared" } // reads ~/.agents/skills only
176
+ | { kind: "proprietary" } // reads ~/.<harness>/skills only
177
+ | { kind: "shared+proprietary" } // reads both -> install ONCE (prefer shared)
178
+ | { kind: "registry-exec" }; // managed by the harness's own `skills` CLI (legacy gemini)
179
+
180
+ interface HarnessSpec {
181
+ installId: HarnessId; // e.g. "antigravity"
182
+ identityName: HarnessCliHarness; // e.g. "antigravity"
183
+ commandLabels: string[]; // process basenames -> e.g. ["agy", "antigravity"]
184
+ envDetect: (env) => HarnessSignal | null;
185
+ configDir: (resolved) => string; // proprietary config dir (may be unused for shared-only)
186
+ skill: SkillLoadingModel;
187
+ mcpCleanup: "none" | "exec" | "opencode-json";
188
+ deprecated?: boolean;
189
+ }
190
+ ```
191
+
192
+ The registry becomes the single source of truth. `SUPPORTED_HARNESSES`, `FILE_SKILL_HARNESSES`,
193
+ `HARNESS_COMMAND_MAPPING`, `HarnessCliHarness`, and the instruction aliases are derived from it (or at
194
+ minimum cross-checked by a test that fails if they drift). This directly mitigates the "enumeration
195
+ duplicated across modules" fragility from §2.1 and makes the Antigravity addition and any future
196
+ correction to a harness's skill model a one-line change.
197
+
198
+ > Scope control for the reviewer: a full registry refactor may be larger than the operator wants in
199
+ > one PR. A lighter alternative is to add `antigravity` to the existing lists and add a small
200
+ > `SKILL_LOADING_MODEL: Record<HarnessId, SkillLoadingModel>` map plus a shared-dir resolver, deferring
201
+ > the broader registry consolidation. Flagged as decision **D1** in §8.
202
+
203
+ ### 4.2 Shared agents skills directory resolver
204
+
205
+ Add `resolveSharedAgentsSkillsDir(options)` returning `path.join(homeDir, ".agents", "skills")` by
206
+ default. Keep it override-aware (e.g. an `AGENTS_DIR`/`AGENTS_HOME` env var) **only if** such a
207
+ convention is confirmed — otherwise hardcode `~/.agents/skills` and leave a TODO. The skill target
208
+ becomes `<shared>/talking-stick`. (Verification needed — open question **Q2**.)
209
+
210
+ ### 4.3 Install algorithm: one physical install per location, with dedup
211
+
212
+ `runInstallCommand` / `planInstallActionsForHarness` change from "one skill action per harness" to:
213
+
214
+ 1. Resolve the target harness set (explicit args or `--all` detected).
215
+ 2. For each harness, look up its `SkillLoadingModel`:
216
+ - `shared` or `shared+proprietary` → contributes a skill install at `<shared>/talking-stick`.
217
+ - `proprietary` → contributes a skill install at `~/.<harness>/skills/talking-stick`.
218
+ - `registry-exec` (legacy gemini) → no new install (deprecated; see §4.6).
219
+ 3. **Deduplicate skill actions by absolute target path** so that, e.g., `tt install --all` with two
220
+ shared-reading harnesses writes `~/.agents/skills/talking-stick` exactly once and reports it once.
221
+ (Today actions run via `Promise.all`, `src/cli/install-commands.ts:57-61`; two parallel writes to
222
+ the same path are individually idempotent but race on the `removeInstalledSkill`→`symlink` window
223
+ in `installSkillDirectory`, `src/skill-install.ts:200-212`. Dedup removes the race and the
224
+ double-report.)
225
+ 4. For `shared+proprietary` harnesses, also enqueue a **proprietary-copy cleanup** action (see §4.5)
226
+ so the redundant `~/.<harness>/skills/talking-stick` is removed once the shared install exists.
227
+
228
+ The skill action itself (symlink-or-copy, idempotent inspect) is unchanged; only the **set and
229
+ location** of actions changes. Reuse `installSkillDirectory`/`inspectInstalledSkill` for the shared
230
+ target — they are already path-agnostic.
231
+
232
+ ### 4.4 Antigravity specifics
233
+
234
+ - **Detection** (`detectHarnessSignal`, `src/identity.ts:444`): add, ordered before/after Gemini:
235
+ ```ts
236
+ if (env.ANTIGRAVITY_AGENT === "1" ||
237
+ nonEmpty(env.ANTIGRAVITY_CONVERSATION_ID) ||
238
+ nonEmpty(env.ANTIGRAVITY_TRAJECTORY_ID)) {
239
+ return {
240
+ harness: "antigravity",
241
+ sessionId: nonEmpty(env.ANTIGRAVITY_CONVERSATION_ID) ?? nonEmpty(env.ANTIGRAVITY_TRAJECTORY_ID),
242
+ pidHint: null
243
+ };
244
+ }
245
+ ```
246
+ Recommend `ANTIGRAVITY_CONVERSATION_ID` as the primary session anchor (stable per conversation);
247
+ `ANTIGRAVITY_TRAJECTORY_ID` is likely finer-grained (per turn/sub-task) and would fragment identity
248
+ if used as the anchor. Decision **D2** / open question **Q3**.
249
+ - **Command mapping** (`HARNESS_COMMAND_MAPPING`, `src/identity.ts:406`): add `agy → antigravity` and
250
+ `antigravity → antigravity` so ancestry detection works when env markers are absent (consistent with
251
+ how Grok is detected by ancestry).
252
+ - **Identity prefix / display name**: recommend `antigravity` (matches the env-marker family and reads
253
+ clearly in `agent_id`s like `antigravity:ab12cd34`). The binary stays `agy`. Decision **D2**.
254
+ - **Skill loading model**: `shared` (reads `~/.agents/skills`). **Confirmed file-based** by Codex
255
+ recon (§2.5a): `agy` has no `skills` subcommand, and Antigravity migration guidance routes skills to
256
+ `.agents/skills`. Install target therefore `~/.agents/skills/talking-stick`, file copy/symlink —
257
+ **not** an exec. (A plugin path `agy plugin import/install/link` exists but is not the route here.)
258
+ Still verify *which other* harnesses share that dir (open question **Q1**).
259
+ - **MCP cleanup**: `none` (like Grok). `agy` has no `mcp`/`skills` registry surface we use, and `tt`
260
+ no longer adds MCP servers.
261
+ - **`detectHarness`** (`src/install.ts:532`, used by `--all`): add an `antigravity` branch. Use
262
+ `which agy` as the **strong** presence signal. Do **not** key presence on `~/.agents/skills` alone —
263
+ it is shared and may exist for unrelated reasons (it already exists empty on this machine, §2.5a), so
264
+ it would over-claim Antigravity under `--all`. Antigravity has no confirmed proprietary config dir
265
+ (**Q4**), so absent `agy` on PATH, treat as not-detected.
266
+ - **Instruction routing**: add `antigravity`/`agy` to `HARNESS_ALIASES` (`src/instructions.ts:95`),
267
+ add `antigravity` to `InstructionHarness`, add a `## Antigravity` section to
268
+ `DEFAULT_INSTRUCTIONS_MARKDOWN`, and update the prose line at `src/instructions.ts:68`.
269
+
270
+ ### 4.5 Migration / cleanup of existing installs
271
+
272
+ Three classes of existing state to handle on `tt install`, `tt uninstall`, `tt self-update`, and
273
+ first-run-after-version-change (reuse the `runStaleMcpCleanup` plumbing in `src/update-migration.ts`):
274
+
275
+ 1. **Redundant proprietary copies for shared-reading harnesses.** When a harness's model is
276
+ `shared`/`shared+proprietary` and a tt-managed `talking-stick` skill exists in its **proprietary**
277
+ folder, remove the proprietary copy once the shared install exists. **Safety gate (strict, mirrors
278
+ the OpenCode MCP cleanup philosophy):** only remove when the proprietary entry is clearly
279
+ tt-managed — a **symlink** whose realpath resolves to the bundled skill source, OR a directory whose
280
+ digest matches a **known-tt-bundled** digest (current or a recorded set of prior-release digests).
281
+ A `talking-stick` directory that is neither (i.e. hand-authored or unknown) is **preserved** and
282
+ reported, never deleted. This avoids nuking a user's own skill or a copy a still-proprietary-only
283
+ harness genuinely needs.
284
+ - Open question **Q5**: maintaining a set of prior-release bundled digests is the only robust way
285
+ to recognize a *stale tt copy*. Alternative: only auto-remove **symlinks** (cheap, unambiguous)
286
+ and leave copies to a `--prune-duplicates` opt-in. Recommend symlink-only auto-removal + opt-in
287
+ prune for copies, to stay conservative.
288
+ 2. **Gemini deprecation cleanup.** See §4.6.
289
+ 3. **Audit.** Every removal appends a JSONL audit entry (extend `install-audit.ts` reasons/targets to
290
+ cover skill-dir cleanups, not just MCP). The existing audit currently only models MCP cleanup
291
+ (`AuditAction`, `src/install-migration.ts`); a parallel "skill cleanup" audit path or a generalized
292
+ action enum is needed. Decision **D3**.
293
+
294
+ `syncInstalledSkills` (silent human-CLI sync) must also be updated to (a) sync the shared location for
295
+ shared-reading harnesses, and (b) **stop re-creating** proprietary copies for shared-reading harnesses
296
+ (otherwise it re-introduces the duplicate the migration just removed). This is the most important
297
+ behavioral edit in the silent path.
298
+
299
+ ### 4.6 Gemini deprecation
300
+
301
+ The operator states Gemini CLI support is being deprecated/replaced by Antigravity. Proposed handling
302
+ (conservative, non-stranding):
303
+
304
+ - **Keep identity detection** for `GEMINI_CLI=1` and `gemini` ancestry so any still-running Gemini
305
+ session keeps a stable identity and can coordinate during the transition. (Decision **D4**: fully
306
+ drop vs keep detection. Recommend keep for now.)
307
+ - **Stop installing** the Gemini skill: `gemini` is dropped from default `--all` install targets and
308
+ from any "install the skill" path. `tt install gemini` prints a deprecation notice and performs
309
+ cleanup-only (see below) rather than `gemini skills link`.
310
+ - **Active cleanup on update/first-run:** best-effort `gemini skills uninstall talking-stick --scope user`
311
+ and `gemini mcp remove -s user talking-stick`, guarded by `which gemini` / `skipMissing` so machines
312
+ without Gemini are silent no-ops. This removes the old registry skill so it cannot duplicate the new
313
+ Antigravity/shared skill.
314
+ - **Recognize-but-deprecate the token:** keep `gemini` accepted by `parseHarnessList` and the
315
+ `## Gemini` alias in instruction parsing (so existing project instruction files with `## Gemini`
316
+ sections still extract) but mark it deprecated. Replace the `## Gemini` block in the **default**
317
+ instructions template with `## Antigravity`.
318
+ - Decision **D5**: timeline — do we remove Gemini entirely in a later release, or keep the deprecated
319
+ shim indefinitely? Recommend: ship deprecation + cleanup now, schedule full removal for a later
320
+ major.
321
+
322
+ ### 4.7 Uninstall semantics for the shared dir
323
+
324
+ `tt uninstall` must remove only `<shared>/talking-stick`, **never** the whole `~/.agents/skills`
325
+ directory (it is shared with other tools). After removing the subdir, if `~/.agents/skills` is now
326
+ empty, leaving it is fine; do not attempt to remove the parent. Mirror the existing
327
+ `removeInstalledSkill` symlink-vs-dir handling (`src/skill-install.ts:324`).
328
+
329
+ ## 5. CLI / API surface changes
330
+
331
+ - `tt install antigravity` → writes `~/.agents/skills/talking-stick` (shared model).
332
+ - `tt install --all` → dedups shared targets; one shared write covers all shared-reading harnesses.
333
+ - `tt install gemini` → deprecation notice + cleanup-only (no new skill).
334
+ - `tt uninstall antigravity` → removes `<shared>/talking-stick` only.
335
+ - Help text (`src/cli/output.ts:204`) and README enumerations updated to
336
+ `claude|codex|antigravity|grok|opencode` with `gemini` noted as deprecated.
337
+ - New `SkillLoadingModel`/registry exports from `src/skill-install.ts` (or new module) consumed by
338
+ `install-commands.ts`.
339
+ - (Optional) `tt install --prune-duplicates` opt-in to remove tt-managed proprietary **copies** that
340
+ the conservative auto-cleanup leaves in place (see **Q5**).
341
+
342
+ No new MCP behavior; `tt install` still does not add MCP servers.
343
+
344
+ ## 6. Tests to add / update
345
+
346
+ Existing suites that reference gemini or harness lists and will need updates:
347
+ `tests/identity.test.ts`, `tests/install.test.ts`, `tests/skill-install.test.ts`,
348
+ `tests/install-commands.test.ts`, `tests/install-migration.test.ts`, `tests/instructions.test.ts`,
349
+ `tests/cli.test.ts`, `tests/talking-stick.test.ts`, `tests/oob-substrate.test.ts`, `tests/notes.test.ts`.
350
+
351
+ New coverage:
352
+
353
+ 1. **Antigravity detection** — `detectHarnessSignal` returns `antigravity` for `ANTIGRAVITY_AGENT=1`,
354
+ for `ANTIGRAVITY_CONVERSATION_ID` only, and for `ANTIGRAVITY_TRAJECTORY_ID` only; session anchor is
355
+ `CONVERSATION_ID` when both present; `agy` ancestry detection when env markers absent.
356
+ 2. **Shared skill install** — `tt install antigravity` writes a symlink/copy at
357
+ `<home>/.agents/skills/talking-stick`; idempotent re-run reports `already_present`.
358
+ 3. **Dedup** — `tt install --all` with ≥2 shared-reading harnesses produces exactly one shared skill
359
+ action / one report line for `~/.agents/skills/talking-stick`; no race/`ENOENT` flake.
360
+ 4. **Proprietary-copy migration** — given a pre-existing tt-managed symlink at
361
+ `~/.codex/skills/talking-stick` (only if Codex's model becomes shared+proprietary — gate on the
362
+ verified model), migration removes it once the shared install exists; a **hand-authored** dir at the
363
+ same path is **preserved** and reported.
364
+ 5. **Gemini deprecation** — `tt install gemini` does not call `gemini skills link`; update/first-run
365
+ issues best-effort `gemini skills uninstall` + `gemini mcp remove`; both are silent no-ops when
366
+ `which gemini` is null (`skipMissing`).
367
+ 6. **Uninstall** — `tt uninstall antigravity` removes only the `talking-stick` subdir, never the
368
+ `~/.agents/skills` parent.
369
+ 7. **Instructions** — `## Antigravity` extracted for antigravity identity; legacy `## Gemini` sections
370
+ still extract under the deprecated alias; `--harness antigravity` accepted; default template shows
371
+ Antigravity not Gemini.
372
+ 8. **Registry/lists in sync** — a guard test asserting the derived lists
373
+ (`SUPPORTED_HARNESSES`, `FILE_SKILL_HARNESSES`, command mapping, instruction aliases) match the
374
+ registry, so future drift fails loudly.
375
+
376
+ Use the existing install-test patterns: injected `homeDir`, `pathExists`, `readFile`, `writeFile`,
377
+ `run`, `which` hooks (`InstallerHooks`, `src/install.ts:29`) and `TALKING_STICK_DATA_DIR` temp dirs
378
+ (CLAUDE.md). No real `~/.agents` or `~/.gemini` writes in tests.
379
+
380
+ ## 7. Docs to update
381
+
382
+ - `README.md`: harness list (line 5), "How installation works per harness" (lines 162-176), identity
383
+ markers (lines 220), instructions `--harness` enumeration (line 193), command reference (203-204).
384
+ Document the shared `~/.agents/skills` model, the one-install-per-location rule, and Gemini
385
+ deprecation.
386
+ - `skills/talking-stick/SKILL.md`: description harness list (line 3) and the silent-sync note
387
+ (line 52) — replace Gemini with Antigravity, mention shared skills directory.
388
+ - `CLAUDE.md` "Runtime & Dogfooding Notes": the identity-markers bullet and the
389
+ `~/.claude/skills`/`~/.grok/skills` symlink bullet need an Antigravity + `~/.agents/skills` line.
390
+ - `docs/releases/<next>.md` + `CHANGELOG.md`: new entry.
391
+ - This file moves draft → converged once Codex review lands.
392
+
393
+ ## 8. Decisions for reviewer (recommendations in **bold**)
394
+
395
+ - **D1 — Shared-first vs per-harness model.** Two framings of the same trim:
396
+ - *(1a) Codex's shared-first (leading):* treat the shared `~/.agents/skills` target as first-class
397
+ and the default for every harness that reads it; keep harness-specific skill targets only where a
398
+ harness does **not** read shared (Claude Code is the likely holdout via `~/.claude/skills`), plus
399
+ harness-only extras (Grok session hook). Fewest physical installs, smallest duplication surface.
400
+ - *(1b) Per-harness model map:* each harness carries a `SkillLoadingModel`
401
+ (`shared`/`proprietary`/`shared+proprietary`/`registry-exec`); the installer derives locations from
402
+ it. More explicit, tolerates a messy reality where harnesses differ.
403
+ These converge in code (1a is 1b with most harnesses set to `shared`). **Recommend implementing the
404
+ 1b map (it is the honest data model) but defaulting most harnesses to `shared` per 1a once Q1
405
+ confirms who reads the dir.** Plus a drift-guard test. Defer a full `src/harness-registry.ts`
406
+ consolidation unless the reviewer/operator wants the bigger refactor in this PR.
407
+ - **D2 — Antigravity identity:** prefix `antigravity` (display) with binary label `agy`. **Recommend
408
+ `antigravity` prefix.**
409
+ - **D3 — Audit model:** generalize `install-audit` to cover skill-dir cleanup vs add a parallel skill
410
+ audit path. **Recommend generalizing the action enum.**
411
+ - **D4 — Gemini detection:** keep vs drop. **Recommend keep detection, drop install.**
412
+ - **D5 — Gemini removal timeline:** **Recommend deprecate+cleanup now, full removal in a later major.**
413
+
414
+ ## 9. Risks
415
+
416
+ 1. **Unverified `~/.agents/skills` adoption (highest).** The entire trim hinges on *which* harnesses
417
+ actually read `~/.agents/skills` today. Wrong model → either the skill is not loaded (installed only
418
+ to shared, but harness reads only proprietary) or duplicates persist (installed to both). Mitigation:
419
+ verify per harness (Q1), keep the model in one table, ship conservative cleanup (symlink-only
420
+ auto-removal).
421
+ 2. **Aggressive proprietary cleanup deletes user content.** A `talking-stick` dir that is not
422
+ tt-managed could be a user's own skill. Mitigation: strict tt-managed gate (symlink→bundled, or
423
+ known-digest), preserve-and-report otherwise — mirrors the strict OpenCode MCP-cleanup precedent.
424
+ 3. **Shared-dir blast radius.** Installing into `~/.agents/skills` exposes the skill to every agent
425
+ reading that dir, including in non-coordinating repos. Mitigated by the skill's self-gating
426
+ description (§2.6), but worth an explicit note and operator sign-off.
427
+ 4. **Uninstall over-reach.** Removing the shared parent dir would affect other tools. Mitigation:
428
+ only ever remove the `talking-stick` subdir.
429
+ 5. **Stranding a live Gemini.** Cutting Gemini detection mid-session would drop a running agent out of
430
+ coordination. Mitigation: keep detection, only stop installing (D4).
431
+ 6. **Antigravity unknowns.** Config dir, whether `agy` has `skills`/`mcp` subcommands, and the right
432
+ session anchor are not confirmed (Q3/Q4). Wrong guesses are low-blast (detection/identity only) but
433
+ could pick a poor session granularity.
434
+ 7. **Dedup race.** Parallel `Promise.all` install of two shared-reading harnesses both hitting
435
+ `installSkillDirectory` on the same path can `ENOENT`-flake in the remove→symlink window. Mitigation:
436
+ dedup actions by path before running (§4.3).
437
+ 8. **List drift.** Harness enumeration lives in ~7 places; missing one (e.g. `HARNESS_COMMAND_MAPPING`)
438
+ yields silent mis-detection. Mitigation: registry + drift-guard test (§6.8).
439
+ 9. **Windows.** Shared-dir symlink uses `junction` like existing installs; confirm `~/.agents` parent
440
+ creation and junction semantics on win32 (existing code already branches, `src/skill-install.ts:206`).
441
+
442
+ ## 10. Open questions (for Codex review + operator)
443
+
444
+ - **Q1 (still blocking):** Exactly which harnesses read `~/.agents/skills` today — Antigravity is
445
+ confirmed (§2.5a); do Codex / Gemini / OpenCode / Grok also read it, and does Claude Code? This
446
+ determines each harness's `SkillLoadingModel`, the shared-first default (D1/1a), and the migration
447
+ set. Needs per-harness confirmation.
448
+ - **Q2 (partly resolved):** `.agents/skills` confirmed as the convention (§2.5a); the user-scope path
449
+ is `~/.agents/skills` and it already exists on this machine. Still confirm: env override
450
+ (`AGENTS_DIR`/`AGENTS_HOME`/XDG-style)? Nesting as `<dir>/<skill-name>/SKILL.md` like the proprietary
451
+ folders (assumed yes)?
452
+ - **Q3:** Antigravity session anchor — `ANTIGRAVITY_CONVERSATION_ID` vs `ANTIGRAVITY_TRAJECTORY_ID`.
453
+ What is each one's lifetime/granularity? (Recommend CONVERSATION_ID; see D2.)
454
+ - **Q4 (resolved):** `agy` has **no** `skills`/`mcp` subcommands (it has `plugin import/install/link`).
455
+ Antigravity is file-based via `~/.agents/skills`. No confirmed proprietary config dir — so
456
+ `detectHarness` presence keys on `which agy`, not a config dir.
457
+ - **Q5:** Proprietary-copy cleanup — auto-remove only symlinks (conservative) and gate copy removal
458
+ behind `--prune-duplicates`, or maintain prior-release bundled digests to recognize stale tt copies?
459
+ - **Q6:** Should shared-skill install be the **default** for shared-reading harnesses, or opt-in for
460
+ one release while we observe? (Affects rollout risk.)
461
+ - **Q7:** Gemini removal timeline (D5) and whether `--all` should auto-target Antigravity purely from
462
+ `which agy` vs requiring an explicit token.
463
+ - **Q8 (new, from §2.5a):** OpenCode has **both** `~/.opencode/skills` and `~/.config/opencode/skills`
464
+ on this machine, but `tt` only manages `~/.config/opencode`. Should the OpenCode model account for
465
+ the `~/.opencode` location (read it? clean a stale tt copy there?), or is `~/.opencode/skills`
466
+ out of scope (user/other-tool owned)? Relevant to whether OpenCode is `shared`, `proprietary`, or
467
+ `shared+proprietary`.
468
+
469
+ ## 11. Codex adversarial review (2026-06-13)
470
+
471
+ ### 11.1 Verdict
472
+
473
+ The draft has the right core direction: **one install per physical skill location, with a
474
+ per-harness loading model, and no fake `agy skills` integration.** The implementation should not
475
+ start with a broad harness-registry refactor, though. Start with a narrower `SkillTargetSpec` map
476
+ that resolves install targets and duplicate-cleanup targets. A full registry can follow once the
477
+ model is proven.
478
+
479
+ The main correction is that Q1 is now partly answered by live inspection:
480
+
481
+ - **Codex** exposes both `/Users/wojtek/.codex/skills` and `/Users/wojtek/.agents/skills` in
482
+ `codex debug prompt-input`.
483
+ - **Grok** exposes `/Users/wojtek/.grok/skills`, `/Users/wojtek/.agents/skills`, and
484
+ `/Users/wojtek/.claude/skills` in `grok inspect --json`.
485
+ - **OpenCode** exposes `/Users/wojtek/.agents/skills`, `/Users/wojtek/.config/opencode/skills`, and
486
+ `/Users/wojtek/.opencode/skills` in `opencode debug skill`.
487
+ - **Claude Code** is still only locally confirmed at `/Users/wojtek/.claude/skills`; no live evidence
488
+ yet that it reads `/Users/wojtek/.agents/skills`.
489
+ - **Antigravity** docs and migration guidance point to `.agents/skills`, and `agy` has no `skills`
490
+ subcommand. However, local state also has `/Users/wojtek/.gemini/antigravity/skills`, so the
491
+ implementation should include a live `agy` discovery experiment before deleting any Antigravity-
492
+ specific or Gemini-era skill copy.
493
+
494
+ ### 11.2 Required changes before implementation
495
+
496
+ 1. **Make the first implementation map-driven, not registry-driven.** Add a focused map like:
497
+ `installId -> { primarySkillTarget, duplicateSkillTargets, extras, deprecated }`. Let
498
+ `SUPPORTED_HARNESSES` remain explicit for now, backed by drift tests. This avoids turning a
499
+ harness-path bug fix into a broad architecture change.
500
+
501
+ 2. **Treat shared install as primary for Codex, Grok, OpenCode, and Antigravity only after their live
502
+ loader is verified.** Based on local evidence, Codex/Grok/OpenCode all read `~/.agents/skills`.
503
+ Claude should stay proprietary unless proven otherwise. Grok still needs the proprietary hook
504
+ install at `~/.grok/hooks/talking-stick-session.json`; only the skill moves to shared.
505
+
506
+ 3. **OpenCode cleanup is more urgent than the draft implies.** A stale
507
+ `~/.opencode/skills/talking-stick` can shadow both `~/.config/opencode/skills/talking-stick` and
508
+ `~/.agents/skills/talking-stick`. If shared becomes primary, the cleanup set for OpenCode must
509
+ include both proprietary roots:
510
+ `~/.opencode/skills/talking-stick` and `<XDG_CONFIG_HOME|~/.config>/opencode/skills/talking-stick`.
511
+ Auto-remove only if the entry is a symlink to the bundled skill. Preserve copies or unknown dirs
512
+ unless an explicit prune flag is added.
513
+
514
+ 4. **Use symlink-only automatic duplicate removal for v1.** Do not maintain historical bundled
515
+ digests in the first pass. A digest table sounds precise but becomes release-state bookkeeping
516
+ and still cannot prove user intent. For v1, auto-remove only symlinks that resolve to the bundled
517
+ `skills/talking-stick` source. Report copied/unknown duplicate directories as preserved.
518
+
519
+ 5. **Sequence install and cleanup deterministically.** Install or verify the shared target first.
520
+ Only then remove tt-managed proprietary duplicates. Do not run same-path or related cleanup actions
521
+ in the existing per-harness `Promise.all` path. Dedup by real target path before execution.
522
+
523
+ 6. **Keep Gemini identity, but make install behavior unmistakable.** `tt install gemini` should print
524
+ a deprecation message that points to `tt install antigravity`, then run cleanup-only if possible.
525
+ `tt install --all` should not select Gemini merely because `~/.gemini` exists; it should prefer
526
+ Antigravity when `agy` is present.
527
+
528
+ 7. **Do not use `~/.agents/skills` existence as harness detection.** It is a shared substrate. Use
529
+ `which agy` for Antigravity install detection, and runtime env/ancestry for identity.
530
+
531
+ 8. **Add an explicit manual/live verification checklist before coding cleanup.** The plan should require
532
+ commands equivalent to:
533
+ `codex debug prompt-input`, `grok inspect --json`, `opencode debug skill`, and an `agy` discovery
534
+ check. Unit tests should not depend on those commands, but the implementation branch should record
535
+ the observed loader paths in the final handoff.
536
+
537
+ ### 11.3 Revised loading model recommendation
538
+
539
+ Initial `SkillLoadingModel` values for implementation:
540
+
541
+ - `claude-code`: `proprietary`, target `~/.claude/skills/talking-stick`.
542
+ - `codex`: `shared+proprietary`, primary `~/.agents/skills/talking-stick`, duplicate cleanup
543
+ `~/.codex/skills/talking-stick`.
544
+ - `antigravity`: `shared`, primary `~/.agents/skills/talking-stick`; keep an open verification item
545
+ for any `~/.gemini/antigravity/skills` compatibility path.
546
+ - `grok`: `shared+proprietary`, primary `~/.agents/skills/talking-stick`, duplicate cleanup
547
+ `~/.grok/skills/talking-stick`, plus keep the Grok session hook install in `~/.grok/hooks`.
548
+ - `opencode`: `shared+proprietary`, primary `~/.agents/skills/talking-stick`, duplicate cleanup
549
+ for both `~/.opencode/skills/talking-stick` and
550
+ `<XDG_CONFIG_HOME|~/.config>/opencode/skills/talking-stick`.
551
+ - `gemini`: deprecated, identity-only plus cleanup-only.
552
+
553
+ This is intentionally conservative: it removes duplicate `talking-stick` entries while preserving
554
+ loader-specific extras and avoiding destructive cleanup of unknown copies.
555
+
556
+ ### 11.4 Additional tests to add
557
+
558
+ Add or revise these specific tests beyond the draft list:
559
+
560
+ 1. `tt install --all` with Codex, Grok, OpenCode, and Antigravity detected plans exactly one shared
561
+ skill install action and no proprietary skill install actions for those harnesses.
562
+ 2. OpenCode duplicate cleanup considers both legacy proprietary roots and removes only symlinks to the
563
+ bundled source.
564
+ 3. A preserved unknown proprietary duplicate is reported but does not fail install.
565
+ 4. `syncInstalledSkills` updates the shared target and does not recreate proprietary duplicates for
566
+ shared-reading harnesses.
567
+ 5. `tt install gemini --print` shows cleanup/deprecation actions, not `gemini skills link`.
568
+ 6. `detectHarness("antigravity")` is true only from `agy` on PATH or an explicit Antigravity config
569
+ signal if later discovered, not from `~/.agents/skills`.
570
+
571
+ ### 11.5 Convergence questions for Claude/operator
572
+
573
+ 1. Should `tt install --all` install `~/.agents/skills/talking-stick` when only Codex/Grok/OpenCode are
574
+ detected but `agy` is not installed? Codex says yes if any shared-reading harness is detected,
575
+ because shared is now their primary target.
576
+ 2. Should `tt uninstall codex` remove shared `~/.agents/skills/talking-stick` if Grok/OpenCode also
577
+ use it? Codex says no: uninstall must be target-aware. Removing the shared skill should happen only
578
+ when uninstalling the last selected shared-reading harness or via an explicit `tt uninstall agents`
579
+ style target. This needs a concrete CLI rule before implementation.
580
+ 3. Is symlink-only duplicate pruning enough for this release? Codex says yes; copied unknown dirs can be
581
+ reported with a next command or manual path.
582
+
583
+ ## 12. Convergence (Claude, 2026-06-13) — AUTHORITATIVE
584
+
585
+ This section supersedes the recommendations in §8/§10/§11 where they differ. Codex's review (§11) is
586
+ accepted almost entirely. Two new pieces of independent verification close the last gaps.
587
+
588
+ ### 12.1 New verification done this pass (Claude, `claude:529d0454`)
589
+
590
+ - **`agy` has no `skills`/`mcp` surface — re-confirmed locally.** `agy --help` subcommands are exactly:
591
+ `changelog`, `help`, `install`, `models`, `plugin`/`plugins`, `update`. No `skills`, no `mcp`.
592
+ → Antigravity is file-based via `~/.agents/skills`; presence detection keys on `which agy`. **Q4 closed.**
593
+ - **Claude Code does NOT read `~/.agents/skills` — authoritatively confirmed.** Per official docs
594
+ (https://code.claude.com/docs/en/skills.md#where-skills-live), Claude Code's only skill discovery
595
+ locations are `~/.claude/skills/` (personal), `.claude/skills/` (project), plugin-bundled skills, and
596
+ `--add-dir` `.claude/skills` subdirs. There is **no** `~/.agents/skills` support. → Installing into
597
+ the shared dir cannot create a duplicate for Claude. **Claude Code stays `proprietary`. Q1 closed for Claude.**
598
+ - **All current proprietary installs are symlinks to the bundled source.** Verified `talking-stick` is a
599
+ symlink (not a copy) in `~/.claude`, `~/.codex`, `~/.grok`, `~/.opencode`, `~/.config/opencode`, and
600
+ `~/.gemini`, every one resolving to `dev/ai/talking-stick/skills/talking-stick`. → The conservative
601
+ **symlink-only auto-prune fully covers the real migration set on this machine** (no copies to leave behind).
602
+ - `~/.agents/skills` exists and is populated by *other* tools (agent-browser, clawdapus-cli, cmux-browser,
603
+ mcporter, microsoft-foundry, openclaw-cli-reference) but has **no** talking-stick entry yet — confirms
604
+ the shared dir is a real shared substrate (do not treat its existence as Antigravity detection; **Q8/D-detect**).
605
+ - Note: `~/.gemini/antigravity/skills` was **not** present on re-check (only `~/.gemini/skills/talking-stick`
606
+ symlink exists). The Antigravity-compat verification item from §11.1 stays as a live implementation check
607
+ but there is no `~/.gemini/antigravity` copy to migrate here today.
608
+
609
+ ### 12.2 Decisions — resolved
610
+
611
+ - **D1 — model shape:** Implement the **map-driven `SkillLoadingModel`** (§11.2.1), *not* a broad
612
+ `src/harness-registry.ts` refactor in this PR. A focused
613
+ `Record<HarnessId, { model, primaryTarget, duplicateTargets[], extras[], deprecated }>` plus a
614
+ **drift-guard test** that asserts the existing scattered lists agree with it. Full registry
615
+ consolidation is explicitly deferred to a later PR.
616
+ - **D2 — Antigravity identity:** prefix/display = **`antigravity`**, binary/command label = **`agy`**
617
+ (also map `antigravity` as a command label). Session anchor = **`ANTIGRAVITY_CONVERSATION_ID`**, falling
618
+ back to `ANTIGRAVITY_TRAJECTORY_ID`, then ancestry. **Q3 resolved** this way; trajectory is presumed
619
+ finer-grained and would fragment identity.
620
+ - **D3 — audit:** **generalize** the `install-audit`/`AuditAction` enum to cover skill-dir cleanup
621
+ (reasons: `skill_duplicate_symlink`, `gemini_deprecated_skill`, plus the existing MCP reasons), so every
622
+ skill removal is journaled the same way MCP cleanups already are.
623
+ - **D4 — Gemini detection:** **keep** identity detection (`GEMINI_CLI=1` + `gemini` ancestry); **drop**
624
+ the install. `tt install gemini` becomes deprecation-notice + cleanup-only.
625
+ - **D5 — Gemini removal timeline:** ship **deprecate + cleanup now**; schedule full identity/token removal
626
+ for a later major. Keep `gemini` accepted by `parseHarnessList` and the `## Gemini` instruction alias
627
+ (so existing project instruction files still extract), but replace `## Gemini` in the **default** template
628
+ with `## Antigravity`.
629
+ - **D6 (new) — shared install is default, not opt-in (Q6).** Shared-reading harnesses install to
630
+ `~/.agents/skills/talking-stick` immediately on ship. Low risk because (a) the bundled `SKILL.md`
631
+ self-gates (§2.6) so a broad install does not force behavior, and (b) we clean the duplicates in the
632
+ same release. No staged opt-in.
633
+
634
+ ### 12.3 Final per-harness loading model (authoritative)
635
+
636
+ | installId | identity | command labels | model | primary target | duplicate-cleanup targets (symlink-only auto-prune) | extras / notes |
637
+ |--------------|------------|------------------------|---------------------|--------------------------------------|---------------------------------------------------------------------------------------|----------------|
638
+ | claude-code | claude | claude, claude-code | `proprietary` | `~/.claude/skills/talking-stick` | — | docs-confirmed: does not read shared |
639
+ | codex | codex | codex | `shared+proprietary`| `~/.agents/skills/talking-stick` | `~/.codex/skills/talking-stick` | |
640
+ | antigravity | antigravity| agy, antigravity | `shared` | `~/.agents/skills/talking-stick` | — | presence = `which agy`; identity env above; live-verify any `~/.gemini/antigravity` compat path |
641
+ | grok | grok | grok | `shared+proprietary`| `~/.agents/skills/talking-stick` | `~/.grok/skills/talking-stick` | **keep** Grok session hook at `~/.grok/hooks/talking-stick-session.json` (extra, not pruned) |
642
+ | opencode | opencode | opencode | `shared+proprietary`| `~/.agents/skills/talking-stick` | `~/.opencode/skills/talking-stick` **and** `<XDG_CONFIG_HOME|~/.config>/opencode/skills/talking-stick` | both legacy roots cleaned |
643
+ | gemini | gemini | gemini | `deprecated` | — (no install) | `~/.gemini/skills/talking-stick` (symlink) + best-effort `gemini skills uninstall` / `gemini mcp remove` | identity kept; install cleanup-only |
644
+
645
+ Shared resolver: `resolveSharedAgentsSkillsDir(opts) = path.join(homeDir, ".agents", "skills")`, using the
646
+ same injectable `homeDir` hook the other resolvers use. **No new env override** (Q2): derive from `homeDir`;
647
+ revisit only if an `AGENTS_DIR` convention is later confirmed. Skill nests as `<shared>/talking-stick/SKILL.md`,
648
+ matching the proprietary layout.
649
+
650
+ ### 12.4 Codex's three convergence questions — answered
651
+
652
+ 1. **`tt install --all` with Codex/Grok/OpenCode detected but `agy` absent → still write shared?**
653
+ **YES.** `~/.agents/skills/talking-stick` is the *primary* target for those harnesses, independent of
654
+ Antigravity. `--all` writes the shared skill once whenever ≥1 shared-reading harness is detected.
655
+ Antigravity detection (`which agy`) only adds nothing extra (same shared target, deduped).
656
+
657
+ 2. **Does `tt uninstall codex` remove the shared skill when Grok/OpenCode also use it?**
658
+ **NO — shared removal is explicit, never a side effect of a single harness uninstall.** Concrete rule:
659
+ - `tt uninstall <single shared+proprietary harness>` (codex|grok|opencode) removes **only** that
660
+ harness's own targets: its proprietary duplicate(s) and its extras (e.g. Grok hook). It **leaves the
661
+ shared skill in place** and prints: *"Left ~/.agents/skills/talking-stick (shared with other agents).
662
+ Run `tt uninstall --all` or `tt uninstall antigravity` to remove the shared skill."*
663
+ - The shared `~/.agents/skills/talking-stick` is removed **iff** the uninstall target set is `--all`,
664
+ **or** includes `antigravity` (shared-only → its uninstall *is* the shared removal), **or** an explicit
665
+ future `agents` pseudo-target. Rejected: reference-counting by "detected" harnesses (fragile, depends
666
+ on transient PATH/config state). Deterministic-by-target-set is the rule.
667
+ - Uninstall **never** removes the `~/.agents/skills` parent, only the `talking-stick` subdir (§4.7).
668
+
669
+ 3. **Is symlink-only duplicate pruning enough for v1?**
670
+ **YES.** Confirmed by §12.1: every existing install is a symlink, so symlink-only auto-prune covers the
671
+ entire real migration set. Copied/unknown `talking-stick` dirs are **preserved and reported**, not
672
+ deleted; a `--prune-duplicates` opt-in for copies is deferred (only build it if a real copy appears).
673
+
674
+ ### 12.5 Implementation build sequence (for the post-compaction execution turn)
675
+
676
+ Ordered so each step is independently testable and the cleanup never runs before the shared install exists:
677
+
678
+ 1. **Model map + resolver.** Add `SkillLoadingModel` + the per-harness map (§12.3) and
679
+ `resolveSharedAgentsSkillsDir`. Add the **drift-guard test** asserting `SUPPORTED_HARNESSES`,
680
+ `FILE_SKILL_HARNESSES`, `HARNESS_COMMAND_MAPPING`, `HarnessCliHarness`, instruction aliases agree with it.
681
+ 2. **Antigravity identity.** `detectHarnessSignal` env branch (CONVERSATION_ID anchor → TRAJECTORY_ID →
682
+ ancestry), `HARNESS_COMMAND_MAPPING` (`agy`,`antigravity` → antigravity), `HarnessCliHarness` union,
683
+ `detectHarness` (`which agy`). Tests per §6.1 + §11.4.6.
684
+ 3. **Install planning rewrite.** `planInstallActionsForHarness` derives target(s) from the model; dedup
685
+ actions by absolute real path **before** the `Promise.all` run (kills the remove→symlink race, §4.3/§11.2.5).
686
+ Shared-reading harnesses → shared primary install + enqueue proprietary-dup cleanup actions (ordered
687
+ *after* the shared install/verify).
688
+ 4. **Duplicate cleanup (symlink-only).** New action that removes a proprietary `talking-stick` **iff** it is
689
+ a symlink resolving to the bundled source; else preserve + report. Journal via the generalized audit (D3).
690
+ Wire into install, uninstall, self-update, first-run-after-version-change (reuse `update-migration.ts`).
691
+ 5. **`syncInstalledSkills` update.** Sync the **shared** target for shared-reading harnesses and **stop
692
+ recreating** their proprietary copies (else silent sync re-introduces the duplicate). Most important
693
+ silent-path edit (§4.5). Test §11.4.4.
694
+ 6. **Gemini deprecation.** `tt install gemini` → deprecation notice + cleanup-only; drop from `--all`
695
+ install set; best-effort `gemini skills uninstall` + `gemini mcp remove` guarded by `which gemini`.
696
+ Keep detection + token + `## Gemini` parse alias. Tests §6.5 / §11.4.5.
697
+ 7. **Uninstall target-awareness.** Implement the §13.2 Option B rule (operator-confirmed): single-harness
698
+ uninstall — **including `antigravity`** — leaves the shared skill and prints the explicit-removal hint;
699
+ shared `~/.agents/skills/talking-stick` is removed **iff** target set is `--all` **or** the explicit
700
+ pseudo-target `tt uninstall agents` (== `--shared`); never remove the parent dir. Test §6.6.
701
+ 8. **Instructions + docs.** `## Antigravity` in default template (replacing `## Gemini`), `InstructionHarness`
702
+ + aliases, help text `claude|codex|antigravity|grok|opencode` (gemini deprecated), README §"How
703
+ installation works per harness", SKILL.md harness list + silent-sync note, CLAUDE.md dogfooding notes,
704
+ CHANGELOG + `docs/releases/<next>.md`.
705
+ 9. **Full suite + typecheck + build + dist smoke + real `tt install --all` dogfood**, then release.
706
+
707
+ Risk register (§9) and the strict tt-managed safety gate (§4.5/§11.2.3-4) remain in force. No remaining
708
+ blocking open questions — Q1, Q3, Q4, Q6, Q8 resolved above; Q5/Q7 resolved as symlink-only + later-major.
709
+
710
+ ## 13. Second-pass convergence review (Claude `claude:b6de450a`, 2026-06-13)
711
+
712
+ Independent review of Codex's §11 and the first convergence pass (§12). §12 is accepted as
713
+ authoritative; this pass confirms one of Codex's three convergence questions, refines a second, and
714
+ pins down the third. Net: the design stands; **one CLI-contract refinement (§13.2) needs operator
715
+ confirmation** before §12.5 step 7. No code changes this turn.
716
+
717
+ ### 13.1 Symlink-only duplicate pruning (Codex Q3 / §12.4.3) — AGREE
718
+
719
+ Confirmed. §12.1's live check (every current install is a symlink resolving to the bundled source)
720
+ means symlink-only auto-prune covers the entire real migration set; copies/unknown dirs are
721
+ preserved-and-reported, and a `--prune-duplicates` opt-in for copies is correctly deferred until a
722
+ real copy appears. No digest table for v1. Nothing to add.
723
+
724
+ ### 13.2 Shared uninstall semantics (Codex Q2 / §12.4.2) — AGREE on the principle, DISAGREE on one clause
725
+
726
+ > **RESOLVED 2026-06-13 (operator): Option B.** `tt uninstall antigravity` (like every single-harness
727
+ > uninstall) **leaves** the shared `~/.agents/skills/talking-stick` and prints the explicit-removal hint.
728
+ > The shared skill is removed **only** by `tt uninstall --all` or the new explicit pseudo-target
729
+ > `tt uninstall agents` (== `--shared`). The "or includes antigravity" clause from §12.4.2 is **dropped**.
730
+ > This is the authoritative contract for §12.5 step 7; the discussion below is the rationale.
731
+
732
+ The principle in §12.4.2 is right: shared removal must be **explicit** and never a side effect of a
733
+ single-harness uninstall, and reference-counting by "detected" harnesses is correctly rejected as
734
+ fragile. **But the clause "shared is removed iff the target set ... includes `antigravity`"
735
+ reintroduces exactly the side effect the rule forbids.**
736
+
737
+ Why it breaks: after migration (§12.3) Codex, Grok, and OpenCode have their proprietary copies pruned
738
+ and read `talking-stick` **only** from `~/.agents/skills/talking-stick`. Antigravity is `shared` —
739
+ the same single location. So `tt uninstall antigravity` removing the shared skill silently strips
740
+ `talking-stick` from Codex, Grok, and OpenCode as well: a destructive cross-harness side effect, no
741
+ different in kind from `tt uninstall codex` nuking the shared dir, which §12.4.2 rightly forbids.
742
+
743
+ **Proposed resolution (stays within "deterministic by target set, no reference counting"):**
744
+
745
+ - Shared `~/.agents/skills/talking-stick` is removed **iff** the target set is `--all` **or** an
746
+ explicit shared pseudo-target (`tt uninstall agents`, equivalently `--shared`). Promote that
747
+ pseudo-target from "future" (§12.4.2) to the **v1** mechanism.
748
+ - **Drop the `or includes antigravity` clause.** `tt uninstall antigravity` behaves like the other
749
+ single-harness uninstalls: it removes only Antigravity's own extras (none today) and **leaves** the
750
+ shared skill in place, printing the same message — *"Left ~/.agents/skills/talking-stick (shared
751
+ with other agents). Run `tt uninstall --all` or `tt uninstall agents` to remove the shared skill."*
752
+ - Rationale: Antigravity reads only the shared dir, so "uninstall `talking-stick` for just
753
+ Antigravity" is not physically separable from the other shared-readers. The honest behavior is to
754
+ refuse the silent shared deletion and name the explicit command. Least-surprise and least-harm both
755
+ favor this.
756
+ - **Operator confirmation requested:** this changes a user-facing *destructive* command contract
757
+ (whether `tt uninstall antigravity` ever removes the shared skill). Resolve at/before §12.5 step 7.
758
+ - If the operator prefers §12's original clause instead, it must be guarded: `tt uninstall antigravity`
759
+ would have to refuse/warn while any other shared-reading harness is still installed — which is the
760
+ reference-counting we are avoiding. Option B above is the cleaner contract.
761
+
762
+ ### 13.3 Exact live `agy` discovery before cleanup (Codex §11.1 / §11.2.8) — CALL OUT + pin procedure
763
+
764
+ §12 keeps the live `agy` check as an implementation-time item but never specifies the *method*,
765
+ because (Q4, re-confirmed) `agy` has **no** `skills`/`mcp` introspection subcommand. Unlike
766
+ `codex debug prompt-input`, `grok inspect --json`, and `opencode debug skill`, there is no documented
767
+ `agy` command that prints its skill-loader paths — so "discover what `agy` reads" is not yet a known
768
+ command. Pin the procedure as:
769
+
770
+ 1. **Re-confirm no CLI surface:** `agy --help`, `agy plugin --help`, `agy changelog` (Q4 re-check at
771
+ implementation time).
772
+ 2. **Empirical loader probe:** create a unique sentinel skill at
773
+ `~/.agents/skills/tt-probe-<rand>/SKILL.md`, start an `agy` session, confirm `agy` surfaces/loads
774
+ the sentinel, then delete it. This is the concrete evidence that `agy` reads `~/.agents/skills` —
775
+ required **before** treating an install there as effective for Antigravity.
776
+ 3. **Gemini-era compat:** only if `~/.gemini/antigravity/skills` (or any `~/.gemini/antigravity` skill
777
+ path) exists at implementation time, repeat the probe there before deleting any `talking-stick`
778
+ entry — migrate-then-remove if read, treat as stale if not. Not present on this machine today
779
+ (§12.1), so there is **no** Antigravity cleanup to gate in v1.
780
+ 4. **Record** observed loader paths in the implementation handoff (per §11.2.8).
781
+
782
+ Scope note: because Antigravity's duplicate-cleanup target set is empty (§12.3), the `agy`-discovery
783
+ gate is really a **pre-install** verification ("does `agy` read `~/.agents/skills`?") plus a defensive
784
+ guard on the hypothetical `~/.gemini/antigravity` copy — not a gate on any deletion that exists today.
785
+
786
+ ### 13.4 Status after this pass
787
+
788
+ Design converged. §12 stands; §13.2 is the single refinement requiring operator confirmation
789
+ (the `tt uninstall antigravity` → shared-removal contract), to be settled at/before §12.5 step 7.
790
+ §13.3 pins the `agy`-discovery procedure for the implementation turn. Everything else in §12 —
791
+ the map-driven model (D1), Antigravity identity (D2), audit generalization (D3), Gemini
792
+ deprecate-keep-detection (D4/D5), shared-default (D6), the per-harness table (§12.3), and the
793
+ build sequence (§12.5) — is accepted as-is.