@every-env/compound-plugin 0.9.0 → 2.34.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/.claude-plugin/marketplace.json +3 -3
- package/.github/workflows/publish.yml +20 -10
- package/.releaserc.json +31 -0
- package/AGENTS.md +6 -1
- package/CHANGELOG.md +76 -0
- package/CLAUDE.md +16 -3
- package/README.md +83 -16
- package/bun.lock +977 -0
- package/docs/plans/2026-02-14-feat-auto-detect-install-and-gemini-sync-plan.md +360 -0
- package/docs/plans/2026-02-25-feat-windsurf-global-scope-support-plan.md +627 -0
- package/docs/plans/2026-03-01-feat-ce-command-aliases-backwards-compatible-deprecation-plan.md +261 -0
- package/docs/plans/2026-03-01-fix-setup-skill-non-claude-llm-fallback-plan.md +140 -0
- package/docs/plans/2026-03-03-feat-sync-claude-mcp-all-supported-providers-plan.md +639 -0
- package/docs/plans/feature_opencode-commands-as-md-and-config-merge.md +574 -0
- package/docs/solutions/adding-converter-target-providers.md +693 -0
- package/docs/solutions/plugin-versioning-requirements.md +7 -3
- package/docs/specs/windsurf.md +477 -0
- package/package.json +10 -4
- package/plans/landing-page-launchkit-refresh.md +2 -2
- package/plugins/compound-engineering/.claude-plugin/plugin.json +2 -2
- package/plugins/compound-engineering/CHANGELOG.md +82 -1
- package/plugins/compound-engineering/CLAUDE.md +14 -7
- package/plugins/compound-engineering/README.md +10 -7
- package/plugins/compound-engineering/agents/research/git-history-analyzer.md +1 -1
- package/plugins/compound-engineering/agents/research/learnings-researcher.md +1 -1
- package/plugins/compound-engineering/agents/review/code-simplicity-reviewer.md +1 -1
- package/plugins/compound-engineering/commands/ce/brainstorm.md +145 -0
- package/plugins/compound-engineering/commands/ce/compound.md +240 -0
- package/plugins/compound-engineering/commands/ce/plan.md +636 -0
- package/plugins/compound-engineering/commands/ce/review.md +525 -0
- package/plugins/compound-engineering/commands/ce/work.md +470 -0
- package/plugins/compound-engineering/commands/create-agent-skill.md +1 -1
- package/plugins/compound-engineering/commands/deepen-plan.md +6 -6
- package/plugins/compound-engineering/commands/deploy-docs.md +1 -1
- package/plugins/compound-engineering/commands/feature-video.md +15 -6
- package/plugins/compound-engineering/commands/heal-skill.md +1 -1
- package/plugins/compound-engineering/commands/lfg.md +3 -3
- package/plugins/compound-engineering/commands/slfg.md +3 -3
- package/plugins/compound-engineering/commands/test-xcode.md +2 -2
- package/plugins/compound-engineering/commands/workflows/brainstorm.md +4 -123
- package/plugins/compound-engineering/commands/workflows/compound.md +4 -234
- package/plugins/compound-engineering/commands/workflows/plan.md +4 -562
- package/plugins/compound-engineering/commands/workflows/review.md +4 -522
- package/plugins/compound-engineering/commands/workflows/work.md +4 -448
- package/plugins/compound-engineering/skills/brainstorming/SKILL.md +3 -3
- package/plugins/compound-engineering/skills/create-agent-skills/workflows/add-workflow.md +6 -0
- package/plugins/compound-engineering/skills/create-agent-skills/workflows/create-new-skill.md +6 -0
- package/plugins/compound-engineering/skills/document-review/SKILL.md +1 -1
- package/plugins/compound-engineering/skills/file-todos/SKILL.md +1 -1
- package/plugins/compound-engineering/skills/git-worktree/SKILL.md +5 -5
- package/plugins/compound-engineering/skills/proof/SKILL.md +185 -0
- package/plugins/compound-engineering/skills/resolve-pr-parallel/SKILL.md +1 -1
- package/plugins/compound-engineering/skills/setup/SKILL.md +8 -2
- package/src/commands/convert.ts +101 -24
- package/src/commands/install.ts +102 -45
- package/src/commands/sync.ts +43 -62
- package/src/converters/claude-to-openclaw.ts +240 -0
- package/src/converters/claude-to-opencode.ts +12 -10
- package/src/converters/claude-to-qwen.ts +238 -0
- package/src/converters/claude-to-windsurf.ts +205 -0
- package/src/index.ts +2 -1
- package/src/parsers/claude-home.ts +55 -3
- package/src/sync/codex.ts +38 -62
- package/src/sync/commands.ts +198 -0
- package/src/sync/copilot.ts +14 -36
- package/src/sync/droid.ts +50 -9
- package/src/sync/gemini.ts +135 -0
- package/src/sync/json-config.ts +47 -0
- package/src/sync/kiro.ts +49 -0
- package/src/sync/mcp-transports.ts +19 -0
- package/src/sync/openclaw.ts +18 -0
- package/src/sync/opencode.ts +10 -30
- package/src/sync/pi.ts +12 -36
- package/src/sync/qwen.ts +66 -0
- package/src/sync/registry.ts +141 -0
- package/src/sync/skills.ts +21 -0
- package/src/sync/windsurf.ts +59 -0
- package/src/targets/index.ts +60 -1
- package/src/targets/openclaw.ts +96 -0
- package/src/targets/opencode.ts +76 -10
- package/src/targets/qwen.ts +64 -0
- package/src/targets/windsurf.ts +104 -0
- package/src/types/kiro.ts +3 -1
- package/src/types/openclaw.ts +52 -0
- package/src/types/opencode.ts +7 -8
- package/src/types/qwen.ts +51 -0
- package/src/types/windsurf.ts +35 -0
- package/src/utils/codex-agents.ts +1 -1
- package/src/utils/detect-tools.ts +37 -0
- package/src/utils/files.ts +14 -0
- package/src/utils/resolve-output.ts +50 -0
- package/src/utils/secrets.ts +24 -0
- package/src/utils/symlink.ts +4 -6
- package/tests/claude-home.test.ts +46 -0
- package/tests/cli.test.ts +180 -0
- package/tests/converter.test.ts +43 -10
- package/tests/detect-tools.test.ts +119 -0
- package/tests/openclaw-converter.test.ts +200 -0
- package/tests/opencode-writer.test.ts +142 -5
- package/tests/qwen-converter.test.ts +238 -0
- package/tests/resolve-output.test.ts +131 -0
- package/tests/sync-codex.test.ts +64 -0
- package/tests/sync-copilot.test.ts +60 -4
- package/tests/sync-droid.test.ts +44 -4
- package/tests/sync-gemini.test.ts +160 -0
- package/tests/sync-kiro.test.ts +83 -0
- package/tests/sync-openclaw.test.ts +51 -0
- package/tests/sync-qwen.test.ts +75 -0
- package/tests/sync-windsurf.test.ts +89 -0
- package/tests/windsurf-converter.test.ts +573 -0
- package/tests/windsurf-writer.test.ts +359 -0
- package/docs/css/docs.css +0 -675
- package/docs/css/style.css +0 -2886
- package/docs/index.html +0 -1046
- package/docs/js/main.js +0 -225
- package/docs/pages/agents.html +0 -649
- package/docs/pages/changelog.html +0 -534
- package/docs/pages/commands.html +0 -523
- package/docs/pages/getting-started.html +0 -582
- package/docs/pages/mcp-servers.html +0 -409
- package/docs/pages/skills.html +0 -611
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Auto-detect install targets and add Gemini sync
|
|
3
|
+
type: feat
|
|
4
|
+
status: completed
|
|
5
|
+
date: 2026-02-14
|
|
6
|
+
completed_date: 2026-02-14
|
|
7
|
+
completed_by: "Claude Opus 4.6"
|
|
8
|
+
actual_effort: "Completed in one session"
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Auto-detect Install Targets and Add Gemini Sync
|
|
12
|
+
|
|
13
|
+
## Overview
|
|
14
|
+
|
|
15
|
+
Two related improvements to the converter CLI:
|
|
16
|
+
|
|
17
|
+
1. **`install --to all`** — Auto-detect which AI coding tools are installed and convert to all of them in one command
|
|
18
|
+
2. **`sync --target gemini`** — Add Gemini CLI as a sync target (currently missing), then add `sync --target all` to sync personal config to every detected tool
|
|
19
|
+
|
|
20
|
+
## Problem Statement
|
|
21
|
+
|
|
22
|
+
Users currently must run 6 separate commands to install to all targets:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
bunx @every-env/compound-plugin install compound-engineering --to opencode
|
|
26
|
+
bunx @every-env/compound-plugin install compound-engineering --to codex
|
|
27
|
+
bunx @every-env/compound-plugin install compound-engineering --to droid
|
|
28
|
+
bunx @every-env/compound-plugin install compound-engineering --to cursor
|
|
29
|
+
bunx @every-env/compound-plugin install compound-engineering --to pi
|
|
30
|
+
bunx @every-env/compound-plugin install compound-engineering --to gemini
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Similarly, sync requires separate commands per target. And Gemini sync doesn't exist yet.
|
|
34
|
+
|
|
35
|
+
## Acceptance Criteria
|
|
36
|
+
|
|
37
|
+
### Auto-detect install
|
|
38
|
+
|
|
39
|
+
- [x]`install --to all` detects installed tools and installs to each
|
|
40
|
+
- [x]Detection checks config directories and/or binaries for each tool
|
|
41
|
+
- [x]Prints which tools were detected and which were skipped
|
|
42
|
+
- [x]Tools with no detection signal are skipped (not errored)
|
|
43
|
+
- [x]`convert --to all` also works (same detection logic)
|
|
44
|
+
- [x]Existing `--to <target>` behavior unchanged
|
|
45
|
+
- [x]Tests for detection logic and `all` target handling
|
|
46
|
+
|
|
47
|
+
### Gemini sync
|
|
48
|
+
|
|
49
|
+
- [x]`sync --target gemini` symlinks skills and writes MCP servers to `.gemini/settings.json`
|
|
50
|
+
- [x]MCP servers merged into existing `settings.json` (same pattern as writer)
|
|
51
|
+
- [x]`gemini` added to `validTargets` in `sync.ts`
|
|
52
|
+
- [x]Tests for Gemini sync
|
|
53
|
+
|
|
54
|
+
### Sync all
|
|
55
|
+
|
|
56
|
+
- [x]`sync --target all` syncs to all detected tools
|
|
57
|
+
- [x]Reuses same detection logic as install
|
|
58
|
+
- [x]Prints summary of what was synced where
|
|
59
|
+
|
|
60
|
+
## Implementation
|
|
61
|
+
|
|
62
|
+
### Phase 1: Tool Detection Utility
|
|
63
|
+
|
|
64
|
+
**Create `src/utils/detect-tools.ts`**
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
import os from "os"
|
|
68
|
+
import path from "path"
|
|
69
|
+
import { pathExists } from "./files"
|
|
70
|
+
|
|
71
|
+
export type DetectedTool = {
|
|
72
|
+
name: string
|
|
73
|
+
detected: boolean
|
|
74
|
+
reason: string // e.g. "found ~/.codex/" or "not found"
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export async function detectInstalledTools(): Promise<DetectedTool[]> {
|
|
78
|
+
const home = os.homedir()
|
|
79
|
+
const cwd = process.cwd()
|
|
80
|
+
|
|
81
|
+
const checks: Array<{ name: string; paths: string[] }> = [
|
|
82
|
+
{ name: "opencode", paths: [path.join(home, ".config", "opencode"), path.join(cwd, ".opencode")] },
|
|
83
|
+
{ name: "codex", paths: [path.join(home, ".codex")] },
|
|
84
|
+
{ name: "droid", paths: [path.join(home, ".factory")] },
|
|
85
|
+
{ name: "cursor", paths: [path.join(cwd, ".cursor"), path.join(home, ".cursor")] },
|
|
86
|
+
{ name: "pi", paths: [path.join(home, ".pi")] },
|
|
87
|
+
{ name: "gemini", paths: [path.join(cwd, ".gemini"), path.join(home, ".gemini")] },
|
|
88
|
+
]
|
|
89
|
+
|
|
90
|
+
const results: DetectedTool[] = []
|
|
91
|
+
for (const check of checks) {
|
|
92
|
+
let detected = false
|
|
93
|
+
let reason = "not found"
|
|
94
|
+
for (const p of check.paths) {
|
|
95
|
+
if (await pathExists(p)) {
|
|
96
|
+
detected = true
|
|
97
|
+
reason = `found ${p}`
|
|
98
|
+
break
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
results.push({ name: check.name, detected, reason })
|
|
102
|
+
}
|
|
103
|
+
return results
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export async function getDetectedTargetNames(): Promise<string[]> {
|
|
107
|
+
const tools = await detectInstalledTools()
|
|
108
|
+
return tools.filter((t) => t.detected).map((t) => t.name)
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**Detection heuristics:**
|
|
113
|
+
|
|
114
|
+
| Tool | Check paths | Notes |
|
|
115
|
+
|------|------------|-------|
|
|
116
|
+
| OpenCode | `~/.config/opencode/`, `.opencode/` | XDG config or project-local |
|
|
117
|
+
| Codex | `~/.codex/` | Global only |
|
|
118
|
+
| Droid | `~/.factory/` | Global only |
|
|
119
|
+
| Cursor | `.cursor/`, `~/.cursor/` | Project-local or global |
|
|
120
|
+
| Pi | `~/.pi/` | Global only |
|
|
121
|
+
| Gemini | `.gemini/`, `~/.gemini/` | Project-local or global |
|
|
122
|
+
|
|
123
|
+
### Phase 2: Gemini Sync
|
|
124
|
+
|
|
125
|
+
**Create `src/sync/gemini.ts`**
|
|
126
|
+
|
|
127
|
+
Follow the Cursor sync pattern (`src/sync/cursor.ts`) since both use JSON config with `mcpServers` key:
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
import path from "path"
|
|
131
|
+
import { symlinkSkills } from "../utils/symlink"
|
|
132
|
+
import { backupFile, pathExists, readJson, writeJson } from "../utils/files"
|
|
133
|
+
import type { ClaudeMcpServer } from "../types/claude"
|
|
134
|
+
|
|
135
|
+
export async function syncToGemini(
|
|
136
|
+
skills: { name: string; sourceDir: string }[],
|
|
137
|
+
mcpServers: Record<string, ClaudeMcpServer>,
|
|
138
|
+
outputRoot: string,
|
|
139
|
+
): Promise<void> {
|
|
140
|
+
const geminiDir = path.join(outputRoot, ".gemini")
|
|
141
|
+
|
|
142
|
+
// Symlink skills
|
|
143
|
+
if (skills.length > 0) {
|
|
144
|
+
const skillsDir = path.join(geminiDir, "skills")
|
|
145
|
+
await symlinkSkills(skills, skillsDir)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Merge MCP servers into settings.json
|
|
149
|
+
if (Object.keys(mcpServers).length > 0) {
|
|
150
|
+
const settingsPath = path.join(geminiDir, "settings.json")
|
|
151
|
+
let existing: Record<string, unknown> = {}
|
|
152
|
+
if (await pathExists(settingsPath)) {
|
|
153
|
+
await backupFile(settingsPath)
|
|
154
|
+
try {
|
|
155
|
+
existing = await readJson<Record<string, unknown>>(settingsPath)
|
|
156
|
+
} catch {
|
|
157
|
+
console.warn("Warning: existing settings.json could not be parsed and will be replaced.")
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const existingMcp = (existing.mcpServers && typeof existing.mcpServers === "object")
|
|
162
|
+
? existing.mcpServers as Record<string, unknown>
|
|
163
|
+
: {}
|
|
164
|
+
|
|
165
|
+
const merged = { ...existing, mcpServers: { ...existingMcp, ...convertMcpServers(mcpServers) } }
|
|
166
|
+
await writeJson(settingsPath, merged)
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function convertMcpServers(servers: Record<string, ClaudeMcpServer>) {
|
|
171
|
+
const result: Record<string, Record<string, unknown>> = {}
|
|
172
|
+
for (const [name, server] of Object.entries(servers)) {
|
|
173
|
+
const entry: Record<string, unknown> = {}
|
|
174
|
+
if (server.command) {
|
|
175
|
+
entry.command = server.command
|
|
176
|
+
if (server.args?.length) entry.args = server.args
|
|
177
|
+
if (server.env && Object.keys(server.env).length > 0) entry.env = server.env
|
|
178
|
+
} else if (server.url) {
|
|
179
|
+
entry.url = server.url
|
|
180
|
+
if (server.headers && Object.keys(server.headers).length > 0) entry.headers = server.headers
|
|
181
|
+
}
|
|
182
|
+
result[name] = entry
|
|
183
|
+
}
|
|
184
|
+
return result
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**Update `src/commands/sync.ts`:**
|
|
189
|
+
|
|
190
|
+
- Add `"gemini"` to `validTargets` array
|
|
191
|
+
- Import `syncToGemini` from `../sync/gemini`
|
|
192
|
+
- Add case in switch for `"gemini"` calling `syncToGemini(skills, mcpServers, outputRoot)`
|
|
193
|
+
|
|
194
|
+
### Phase 3: Wire `--to all` into Install and Convert
|
|
195
|
+
|
|
196
|
+
**Modify `src/commands/install.ts`:**
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
import { detectInstalledTools } from "../utils/detect-tools"
|
|
200
|
+
|
|
201
|
+
// In args definition, update --to description:
|
|
202
|
+
to: {
|
|
203
|
+
type: "string",
|
|
204
|
+
default: "opencode",
|
|
205
|
+
description: "Target format (opencode | codex | droid | cursor | pi | gemini | all)",
|
|
206
|
+
},
|
|
207
|
+
|
|
208
|
+
// In run(), before the existing target lookup:
|
|
209
|
+
if (targetName === "all") {
|
|
210
|
+
const detected = await detectInstalledTools()
|
|
211
|
+
const activeTargets = detected.filter((t) => t.detected)
|
|
212
|
+
|
|
213
|
+
if (activeTargets.length === 0) {
|
|
214
|
+
console.log("No AI coding tools detected. Install at least one tool first.")
|
|
215
|
+
return
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
console.log(`Detected ${activeTargets.length} tools:`)
|
|
219
|
+
for (const tool of detected) {
|
|
220
|
+
console.log(` ${tool.detected ? "✓" : "✗"} ${tool.name} — ${tool.reason}`)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Install to each detected target
|
|
224
|
+
for (const tool of activeTargets) {
|
|
225
|
+
const handler = targets[tool.name]
|
|
226
|
+
const bundle = handler.convert(plugin, options)
|
|
227
|
+
if (!bundle) continue
|
|
228
|
+
const root = resolveTargetOutputRoot(tool.name, outputRoot, codexHome, piHome, hasExplicitOutput)
|
|
229
|
+
await handler.write(root, bundle)
|
|
230
|
+
console.log(`Installed ${plugin.manifest.name} to ${tool.name} at ${root}`)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Codex post-processing
|
|
234
|
+
if (activeTargets.some((t) => t.name === "codex")) {
|
|
235
|
+
await ensureCodexAgentsFile(codexHome)
|
|
236
|
+
}
|
|
237
|
+
return
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
**Same change in `src/commands/convert.ts`** with its version of `resolveTargetOutputRoot`.
|
|
242
|
+
|
|
243
|
+
### Phase 4: Wire `--target all` into Sync
|
|
244
|
+
|
|
245
|
+
**Modify `src/commands/sync.ts`:**
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
import { detectInstalledTools } from "../utils/detect-tools"
|
|
249
|
+
|
|
250
|
+
// Update validTargets:
|
|
251
|
+
const validTargets = ["opencode", "codex", "pi", "droid", "cursor", "gemini", "all"] as const
|
|
252
|
+
|
|
253
|
+
// In run(), handle "all":
|
|
254
|
+
if (targetName === "all") {
|
|
255
|
+
const detected = await detectInstalledTools()
|
|
256
|
+
const activeTargets = detected.filter((t) => t.detected).map((t) => t.name)
|
|
257
|
+
|
|
258
|
+
if (activeTargets.length === 0) {
|
|
259
|
+
console.log("No AI coding tools detected.")
|
|
260
|
+
return
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
console.log(`Syncing to ${activeTargets.length} detected tools...`)
|
|
264
|
+
for (const name of activeTargets) {
|
|
265
|
+
// call existing sync logic for each target
|
|
266
|
+
}
|
|
267
|
+
return
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Phase 5: Tests
|
|
272
|
+
|
|
273
|
+
**Create `tests/detect-tools.test.ts`**
|
|
274
|
+
|
|
275
|
+
- Test detection with mocked directories (create temp dirs, check detection)
|
|
276
|
+
- Test `getDetectedTargetNames` returns only detected tools
|
|
277
|
+
- Test empty detection returns empty array
|
|
278
|
+
|
|
279
|
+
**Create `tests/gemini-sync.test.ts`**
|
|
280
|
+
|
|
281
|
+
Follow `tests/sync-cursor.test.ts` pattern:
|
|
282
|
+
|
|
283
|
+
- Test skills are symlinked to `.gemini/skills/`
|
|
284
|
+
- Test MCP servers merged into `settings.json`
|
|
285
|
+
- Test existing `settings.json` is backed up
|
|
286
|
+
- Test empty skills/servers produce no output
|
|
287
|
+
|
|
288
|
+
**Update `tests/cli.test.ts`**
|
|
289
|
+
|
|
290
|
+
- Test `--to all` flag is accepted
|
|
291
|
+
- Test `sync --target all` is accepted
|
|
292
|
+
- Test `sync --target gemini` is accepted
|
|
293
|
+
|
|
294
|
+
### Phase 6: Documentation
|
|
295
|
+
|
|
296
|
+
**Update `README.md`:**
|
|
297
|
+
|
|
298
|
+
Add to install section:
|
|
299
|
+
```bash
|
|
300
|
+
# auto-detect installed tools and install to all
|
|
301
|
+
bunx @every-env/compound-plugin install compound-engineering --to all
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
Add to sync section:
|
|
305
|
+
```bash
|
|
306
|
+
# Sync to Gemini
|
|
307
|
+
bunx @every-env/compound-plugin sync --target gemini
|
|
308
|
+
|
|
309
|
+
# Sync to all detected tools
|
|
310
|
+
bunx @every-env/compound-plugin sync --target all
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
## What We're NOT Doing
|
|
314
|
+
|
|
315
|
+
- Not adding binary detection (`which cursor`, `which gemini`) — directory checks are sufficient and don't require shell execution
|
|
316
|
+
- Not adding interactive prompts ("Install to Cursor? y/n") — auto-detect is fire-and-forget
|
|
317
|
+
- Not adding `--exclude` flag for skipping specific targets — can use `--to X --also Y` for manual selection
|
|
318
|
+
- Not adding Gemini to the `sync` symlink watcher (no watcher exists for any target)
|
|
319
|
+
|
|
320
|
+
## Complexity Assessment
|
|
321
|
+
|
|
322
|
+
**Low-medium change.** All patterns are established:
|
|
323
|
+
- Detection utility is new but simple (pathExists checks)
|
|
324
|
+
- Gemini sync follows cursor sync pattern exactly
|
|
325
|
+
- `--to all` is plumbing — iterate detected tools through existing handlers
|
|
326
|
+
- No new dependencies needed
|
|
327
|
+
|
|
328
|
+
## References
|
|
329
|
+
|
|
330
|
+
- Cursor sync (reference pattern): `src/sync/cursor.ts`
|
|
331
|
+
- Gemini writer (merge pattern): `src/targets/gemini.ts`
|
|
332
|
+
- Install command: `src/commands/install.ts`
|
|
333
|
+
- Sync command: `src/commands/sync.ts`
|
|
334
|
+
- File utilities: `src/utils/files.ts`
|
|
335
|
+
- Symlink utilities: `src/utils/symlink.ts`
|
|
336
|
+
|
|
337
|
+
## Completion Summary
|
|
338
|
+
|
|
339
|
+
### What Was Delivered
|
|
340
|
+
- Tool detection utility (`src/utils/detect-tools.ts`) with `detectInstalledTools()` and `getDetectedTargetNames()`
|
|
341
|
+
- Gemini sync (`src/sync/gemini.ts`) following cursor sync pattern — symlinks skills, merges MCP servers into `settings.json`
|
|
342
|
+
- `install --to all` and `convert --to all` auto-detect and install to all detected tools
|
|
343
|
+
- `sync --target gemini` added to sync command
|
|
344
|
+
- `sync --target all` syncs to all detected tools with summary output
|
|
345
|
+
- 8 new tests across 2 test files (detect-tools + sync-gemini)
|
|
346
|
+
|
|
347
|
+
### Implementation Statistics
|
|
348
|
+
- 4 new files, 3 modified files
|
|
349
|
+
- 139 tests passing (8 new + 131 existing)
|
|
350
|
+
- No new dependencies
|
|
351
|
+
|
|
352
|
+
### Git Commits
|
|
353
|
+
- `e4d730d` feat: add detect-tools utility and Gemini sync with tests
|
|
354
|
+
- `bc655f7` feat: wire --to all into install/convert and --target all/gemini into sync
|
|
355
|
+
- `877e265` docs: add auto-detect and Gemini sync to README, bump to 0.8.0
|
|
356
|
+
|
|
357
|
+
### Completion Details
|
|
358
|
+
- **Completed By:** Claude Opus 4.6
|
|
359
|
+
- **Date:** 2026-02-14
|
|
360
|
+
- **Session:** Single session, TDD approach
|