@every-env/compound-plugin 0.5.2 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.cursor-plugin/marketplace.json +25 -0
- package/CHANGELOG.md +47 -0
- package/README.md +29 -6
- package/bun.lock +1 -0
- package/docs/brainstorms/2026-02-14-copilot-converter-target-brainstorm.md +117 -0
- package/docs/brainstorms/2026-02-17-copilot-skill-naming-brainstorm.md +30 -0
- package/docs/plans/2026-02-14-feat-add-copilot-converter-target-plan.md +328 -0
- package/docs/plans/2026-02-14-feat-add-gemini-cli-target-provider-plan.md +370 -0
- package/docs/specs/copilot.md +122 -0
- package/docs/specs/gemini.md +122 -0
- package/package.json +1 -1
- package/plugins/coding-tutor/.cursor-plugin/plugin.json +21 -0
- package/plugins/compound-engineering/.claude-plugin/plugin.json +1 -1
- package/plugins/compound-engineering/.cursor-plugin/plugin.json +31 -0
- package/plugins/compound-engineering/.mcp.json +8 -0
- package/plugins/compound-engineering/CHANGELOG.md +27 -0
- package/plugins/compound-engineering/commands/lfg.md +3 -3
- package/plugins/compound-engineering/commands/slfg.md +2 -2
- package/plugins/compound-engineering/commands/workflows/plan.md +18 -1
- package/plugins/compound-engineering/commands/workflows/work.md +8 -1
- package/src/commands/convert.ts +14 -25
- package/src/commands/install.ts +27 -25
- package/src/commands/sync.ts +44 -21
- package/src/converters/{claude-to-cursor.ts → claude-to-copilot.ts} +93 -49
- package/src/converters/claude-to-gemini.ts +193 -0
- package/src/converters/claude-to-opencode.ts +16 -0
- package/src/converters/claude-to-pi.ts +205 -0
- package/src/sync/copilot.ts +100 -0
- package/src/sync/droid.ts +21 -0
- package/src/sync/pi.ts +88 -0
- package/src/targets/copilot.ts +48 -0
- package/src/targets/gemini.ts +68 -0
- package/src/targets/index.ts +25 -7
- package/src/targets/pi.ts +131 -0
- package/src/templates/pi/compat-extension.ts +452 -0
- package/src/types/copilot.ts +31 -0
- package/src/types/gemini.ts +29 -0
- package/src/types/pi.ts +40 -0
- package/src/utils/frontmatter.ts +1 -1
- package/src/utils/resolve-home.ts +17 -0
- package/tests/cli.test.ts +76 -0
- package/tests/converter.test.ts +29 -0
- package/tests/copilot-converter.test.ts +467 -0
- package/tests/copilot-writer.test.ts +189 -0
- package/tests/gemini-converter.test.ts +373 -0
- package/tests/gemini-writer.test.ts +181 -0
- package/tests/pi-converter.test.ts +116 -0
- package/tests/pi-writer.test.ts +99 -0
- package/tests/sync-copilot.test.ts +148 -0
- package/tests/sync-droid.test.ts +57 -0
- package/tests/sync-pi.test.ts +68 -0
- package/src/targets/cursor.ts +0 -48
- package/src/types/cursor.ts +0 -29
- package/tests/cursor-converter.test.ts +0 -347
- package/tests/cursor-writer.test.ts +0 -137
|
@@ -5,6 +5,33 @@ All notable changes to the compound-engineering plugin will be documented in thi
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.35.0] - 2026-02-17
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **`/lfg` and `/slfg` first-run failures** — Made ralph-loop step optional with graceful fallback when `ralph-wiggum` skill is not installed (#154). Added explicit "do not stop" instruction across all steps (#134).
|
|
13
|
+
- **`/workflows:plan` not writing file in pipeline** — Added mandatory "Write Plan File" step with explicit Write tool instructions before Post-Generation Options. The file is now always written to disk before any interactive prompts (#155). Also adds pipeline-mode note to skip AskUserQuestion calls when invoked from LFG/SLFG (#134).
|
|
14
|
+
- **Agent namespace typo in `/workflows:plan`** — `Task spec-flow-analyzer(...)` now uses the full qualified name `Task compound-engineering:workflow:spec-flow-analyzer(...)` to prevent Claude from prepending the wrong `workflows:` prefix (#193).
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## [2.34.0] - 2026-02-14
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- **Gemini CLI target** — New converter target for [Gemini CLI](https://github.com/google-gemini/gemini-cli). Install with `--to gemini` to convert agents to `.gemini/skills/*/SKILL.md`, commands to `.gemini/commands/*.toml` (TOML format with `description` + `prompt`), and MCP servers to `.gemini/settings.json`. Skills pass through unchanged (identical SKILL.md standard). Namespaced commands create directory structure (`workflows:plan` → `commands/workflows/plan.toml`). 29 new tests. ([#190](https://github.com/EveryInc/compound-engineering-plugin/pull/190))
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## [2.33.1] - 2026-02-13
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
- **`/workflows:plan` command** - All plan templates now include `status: active` in YAML frontmatter. Plans are created with `status: active` and marked `status: completed` when work finishes.
|
|
31
|
+
- **`/workflows:work` command** - Phase 4 now updates plan frontmatter from `status: active` to `status: completed` after shipping. Agents can grep for status to distinguish current vs historical plans.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
8
35
|
## [2.33.0] - 2026-02-12
|
|
9
36
|
|
|
10
37
|
### Added
|
|
@@ -5,9 +5,9 @@ argument-hint: "[feature description]"
|
|
|
5
5
|
disable-model-invocation: true
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
Run these slash commands in order. Do not do anything else.
|
|
8
|
+
Run these slash commands in order. Do not do anything else. Do not stop between steps — complete every step through to the end.
|
|
9
9
|
|
|
10
|
-
1. `/ralph-wiggum:ralph-loop "finish all slash commands" --completion-promise "DONE"
|
|
10
|
+
1. **Optional:** If the `ralph-wiggum` skill is available, run `/ralph-wiggum:ralph-loop "finish all slash commands" --completion-promise "DONE"`. If not available or it fails, skip and continue to step 2 immediately.
|
|
11
11
|
2. `/workflows:plan $ARGUMENTS`
|
|
12
12
|
3. `/compound-engineering:deepen-plan`
|
|
13
13
|
4. `/workflows:work`
|
|
@@ -17,4 +17,4 @@ Run these slash commands in order. Do not do anything else.
|
|
|
17
17
|
8. `/compound-engineering:feature-video`
|
|
18
18
|
9. Output `<promise>DONE</promise>` when video is in PR
|
|
19
19
|
|
|
20
|
-
Start with step 1
|
|
20
|
+
Start with step 2 now (or step 1 if ralph-wiggum is available).
|
|
@@ -5,11 +5,11 @@ argument-hint: "[feature description]"
|
|
|
5
5
|
disable-model-invocation: true
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
Swarm-enabled LFG. Run these steps in order, parallelizing where indicated.
|
|
8
|
+
Swarm-enabled LFG. Run these steps in order, parallelizing where indicated. Do not stop between steps — complete every step through to the end.
|
|
9
9
|
|
|
10
10
|
## Sequential Phase
|
|
11
11
|
|
|
12
|
-
1. `/ralph-wiggum:ralph-loop "finish all slash commands" --completion-promise "DONE"
|
|
12
|
+
1. **Optional:** If the `ralph-wiggum` skill is available, run `/ralph-wiggum:ralph-loop "finish all slash commands" --completion-promise "DONE"`. If not available or it fails, skip and continue to step 2 immediately.
|
|
13
13
|
2. `/workflows:plan $ARGUMENTS`
|
|
14
14
|
3. `/compound-engineering:deepen-plan`
|
|
15
15
|
4. `/workflows:work` — **Use swarm mode**: Make a Task list and launch an army of agent swarm subagents to build the plan
|
|
@@ -150,7 +150,7 @@ Think like a product manager - what would make this issue clear and actionable?
|
|
|
150
150
|
|
|
151
151
|
After planning the issue structure, run SpecFlow Analyzer to validate and refine the feature specification:
|
|
152
152
|
|
|
153
|
-
- Task spec-flow-analyzer(feature_description, research_findings)
|
|
153
|
+
- Task compound-engineering:workflow:spec-flow-analyzer(feature_description, research_findings)
|
|
154
154
|
|
|
155
155
|
**SpecFlow Analyzer Output:**
|
|
156
156
|
|
|
@@ -178,6 +178,7 @@ Select how comprehensive you want the issue to be, simpler is mostly better.
|
|
|
178
178
|
---
|
|
179
179
|
title: [Issue Title]
|
|
180
180
|
type: [feat|fix|refactor]
|
|
181
|
+
status: active
|
|
181
182
|
date: YYYY-MM-DD
|
|
182
183
|
---
|
|
183
184
|
|
|
@@ -230,6 +231,7 @@ end
|
|
|
230
231
|
---
|
|
231
232
|
title: [Issue Title]
|
|
232
233
|
type: [feat|fix|refactor]
|
|
234
|
+
status: active
|
|
233
235
|
date: YYYY-MM-DD
|
|
234
236
|
---
|
|
235
237
|
|
|
@@ -294,6 +296,7 @@ date: YYYY-MM-DD
|
|
|
294
296
|
---
|
|
295
297
|
title: [Issue Title]
|
|
296
298
|
type: [feat|fix|refactor]
|
|
299
|
+
status: active
|
|
297
300
|
date: YYYY-MM-DD
|
|
298
301
|
---
|
|
299
302
|
|
|
@@ -472,6 +475,20 @@ end
|
|
|
472
475
|
- [ ] Add names of files in pseudo code examples and todo lists
|
|
473
476
|
- [ ] Add an ERD mermaid diagram if applicable for new model changes
|
|
474
477
|
|
|
478
|
+
## Write Plan File
|
|
479
|
+
|
|
480
|
+
**REQUIRED: Write the plan file to disk before presenting any options.**
|
|
481
|
+
|
|
482
|
+
```bash
|
|
483
|
+
mkdir -p docs/plans/
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
Use the Write tool to save the complete plan to `docs/plans/YYYY-MM-DD-<type>-<descriptive-name>-plan.md`. This step is mandatory and cannot be skipped — even when running as part of LFG/SLFG or other automated pipelines.
|
|
487
|
+
|
|
488
|
+
Confirm: "Plan written to docs/plans/[filename]"
|
|
489
|
+
|
|
490
|
+
**Pipeline mode:** If invoked from an automated workflow (LFG, SLFG, or any `disable-model-invocation` context), skip all AskUserQuestion calls. Make decisions automatically and proceed to writing the plan without interactive prompts.
|
|
491
|
+
|
|
475
492
|
## Output Format
|
|
476
493
|
|
|
477
494
|
**Filename:** Use the date and kebab-case filename from Step 2 Title & Categorization.
|
|
@@ -297,7 +297,14 @@ This command takes a work document (plan, specification, or todo file) and execu
|
|
|
297
297
|
)"
|
|
298
298
|
```
|
|
299
299
|
|
|
300
|
-
4. **
|
|
300
|
+
4. **Update Plan Status**
|
|
301
|
+
|
|
302
|
+
If the input document has YAML frontmatter with a `status` field, update it to `completed`:
|
|
303
|
+
```
|
|
304
|
+
status: active → status: completed
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
5. **Notify User**
|
|
301
308
|
- Summarize what was completed
|
|
302
309
|
- Link to PR
|
|
303
310
|
- Note any follow-up work needed
|
package/src/commands/convert.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { loadClaudePlugin } from "../parsers/claude"
|
|
|
5
5
|
import { targets } from "../targets"
|
|
6
6
|
import type { PermissionMode } from "../converters/claude-to-opencode"
|
|
7
7
|
import { ensureCodexAgentsFile } from "../utils/codex-agents"
|
|
8
|
+
import { expandHome, resolveTargetHome } from "../utils/resolve-home"
|
|
8
9
|
|
|
9
10
|
const permissionModes: PermissionMode[] = ["none", "broad", "from-commands"]
|
|
10
11
|
|
|
@@ -22,7 +23,7 @@ export default defineCommand({
|
|
|
22
23
|
to: {
|
|
23
24
|
type: "string",
|
|
24
25
|
default: "opencode",
|
|
25
|
-
description: "Target format (opencode | codex | droid | cursor)",
|
|
26
|
+
description: "Target format (opencode | codex | droid | cursor | pi | gemini)",
|
|
26
27
|
},
|
|
27
28
|
output: {
|
|
28
29
|
type: "string",
|
|
@@ -34,6 +35,11 @@ export default defineCommand({
|
|
|
34
35
|
alias: "codex-home",
|
|
35
36
|
description: "Write Codex output to this .codex root (ex: ~/.codex)",
|
|
36
37
|
},
|
|
38
|
+
piHome: {
|
|
39
|
+
type: "string",
|
|
40
|
+
alias: "pi-home",
|
|
41
|
+
description: "Write Pi output to this Pi root (ex: ~/.pi/agent or ./.pi)",
|
|
42
|
+
},
|
|
37
43
|
also: {
|
|
38
44
|
type: "string",
|
|
39
45
|
description: "Comma-separated extra targets to generate (ex: codex)",
|
|
@@ -72,7 +78,8 @@ export default defineCommand({
|
|
|
72
78
|
|
|
73
79
|
const plugin = await loadClaudePlugin(String(args.source))
|
|
74
80
|
const outputRoot = resolveOutputRoot(args.output)
|
|
75
|
-
const codexHome =
|
|
81
|
+
const codexHome = resolveTargetHome(args.codexHome, path.join(os.homedir(), ".codex"))
|
|
82
|
+
const piHome = resolveTargetHome(args.piHome, path.join(os.homedir(), ".pi", "agent"))
|
|
76
83
|
|
|
77
84
|
const options = {
|
|
78
85
|
agentMode: String(args.agentMode) === "primary" ? "primary" : "subagent",
|
|
@@ -80,7 +87,7 @@ export default defineCommand({
|
|
|
80
87
|
permissions: permissions as PermissionMode,
|
|
81
88
|
}
|
|
82
89
|
|
|
83
|
-
const primaryOutputRoot = resolveTargetOutputRoot(targetName, outputRoot, codexHome)
|
|
90
|
+
const primaryOutputRoot = resolveTargetOutputRoot(targetName, outputRoot, codexHome, piHome)
|
|
84
91
|
const bundle = target.convert(plugin, options)
|
|
85
92
|
if (!bundle) {
|
|
86
93
|
throw new Error(`Target ${targetName} did not return a bundle.`)
|
|
@@ -106,7 +113,7 @@ export default defineCommand({
|
|
|
106
113
|
console.warn(`Skipping ${extra}: no output returned.`)
|
|
107
114
|
continue
|
|
108
115
|
}
|
|
109
|
-
const extraRoot = resolveTargetOutputRoot(extra, path.join(outputRoot, extra), codexHome)
|
|
116
|
+
const extraRoot = resolveTargetOutputRoot(extra, path.join(outputRoot, extra), codexHome, piHome)
|
|
110
117
|
await handler.write(extraRoot, extraBundle)
|
|
111
118
|
console.log(`Converted ${plugin.manifest.name} to ${extra} at ${extraRoot}`)
|
|
112
119
|
}
|
|
@@ -125,26 +132,6 @@ function parseExtraTargets(value: unknown): string[] {
|
|
|
125
132
|
.filter(Boolean)
|
|
126
133
|
}
|
|
127
134
|
|
|
128
|
-
function resolveCodexHome(value: unknown): string | null {
|
|
129
|
-
if (!value) return null
|
|
130
|
-
const raw = String(value).trim()
|
|
131
|
-
if (!raw) return null
|
|
132
|
-
const expanded = expandHome(raw)
|
|
133
|
-
return path.resolve(expanded)
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function resolveCodexRoot(value: unknown): string {
|
|
137
|
-
return resolveCodexHome(value) ?? path.join(os.homedir(), ".codex")
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function expandHome(value: string): string {
|
|
141
|
-
if (value === "~") return os.homedir()
|
|
142
|
-
if (value.startsWith(`~${path.sep}`)) {
|
|
143
|
-
return path.join(os.homedir(), value.slice(2))
|
|
144
|
-
}
|
|
145
|
-
return value
|
|
146
|
-
}
|
|
147
|
-
|
|
148
135
|
function resolveOutputRoot(value: unknown): string {
|
|
149
136
|
if (value && String(value).trim()) {
|
|
150
137
|
const expanded = expandHome(String(value).trim())
|
|
@@ -153,9 +140,11 @@ function resolveOutputRoot(value: unknown): string {
|
|
|
153
140
|
return process.cwd()
|
|
154
141
|
}
|
|
155
142
|
|
|
156
|
-
function resolveTargetOutputRoot(targetName: string, outputRoot: string, codexHome: string): string {
|
|
143
|
+
function resolveTargetOutputRoot(targetName: string, outputRoot: string, codexHome: string, piHome: string): string {
|
|
157
144
|
if (targetName === "codex") return codexHome
|
|
145
|
+
if (targetName === "pi") return piHome
|
|
158
146
|
if (targetName === "droid") return path.join(os.homedir(), ".factory")
|
|
159
147
|
if (targetName === "cursor") return path.join(outputRoot, ".cursor")
|
|
148
|
+
if (targetName === "gemini") return path.join(outputRoot, ".gemini")
|
|
160
149
|
return outputRoot
|
|
161
150
|
}
|
package/src/commands/install.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { targets } from "../targets"
|
|
|
7
7
|
import { pathExists } from "../utils/files"
|
|
8
8
|
import type { PermissionMode } from "../converters/claude-to-opencode"
|
|
9
9
|
import { ensureCodexAgentsFile } from "../utils/codex-agents"
|
|
10
|
+
import { expandHome, resolveTargetHome } from "../utils/resolve-home"
|
|
10
11
|
|
|
11
12
|
const permissionModes: PermissionMode[] = ["none", "broad", "from-commands"]
|
|
12
13
|
|
|
@@ -24,7 +25,7 @@ export default defineCommand({
|
|
|
24
25
|
to: {
|
|
25
26
|
type: "string",
|
|
26
27
|
default: "opencode",
|
|
27
|
-
description: "Target format (opencode | codex | droid | cursor)",
|
|
28
|
+
description: "Target format (opencode | codex | droid | cursor | pi | copilot | gemini)",
|
|
28
29
|
},
|
|
29
30
|
output: {
|
|
30
31
|
type: "string",
|
|
@@ -36,6 +37,11 @@ export default defineCommand({
|
|
|
36
37
|
alias: "codex-home",
|
|
37
38
|
description: "Write Codex output to this .codex root (ex: ~/.codex)",
|
|
38
39
|
},
|
|
40
|
+
piHome: {
|
|
41
|
+
type: "string",
|
|
42
|
+
alias: "pi-home",
|
|
43
|
+
description: "Write Pi output to this Pi root (ex: ~/.pi/agent or ./.pi)",
|
|
44
|
+
},
|
|
39
45
|
also: {
|
|
40
46
|
type: "string",
|
|
41
47
|
description: "Comma-separated extra targets to generate (ex: codex)",
|
|
@@ -76,7 +82,8 @@ export default defineCommand({
|
|
|
76
82
|
try {
|
|
77
83
|
const plugin = await loadClaudePlugin(resolvedPlugin.path)
|
|
78
84
|
const outputRoot = resolveOutputRoot(args.output)
|
|
79
|
-
const codexHome =
|
|
85
|
+
const codexHome = resolveTargetHome(args.codexHome, path.join(os.homedir(), ".codex"))
|
|
86
|
+
const piHome = resolveTargetHome(args.piHome, path.join(os.homedir(), ".pi", "agent"))
|
|
80
87
|
|
|
81
88
|
const options = {
|
|
82
89
|
agentMode: String(args.agentMode) === "primary" ? "primary" : "subagent",
|
|
@@ -89,7 +96,7 @@ export default defineCommand({
|
|
|
89
96
|
throw new Error(`Target ${targetName} did not return a bundle.`)
|
|
90
97
|
}
|
|
91
98
|
const hasExplicitOutput = Boolean(args.output && String(args.output).trim())
|
|
92
|
-
const primaryOutputRoot = resolveTargetOutputRoot(targetName, outputRoot, codexHome, hasExplicitOutput)
|
|
99
|
+
const primaryOutputRoot = resolveTargetOutputRoot(targetName, outputRoot, codexHome, piHome, hasExplicitOutput)
|
|
93
100
|
await target.write(primaryOutputRoot, bundle)
|
|
94
101
|
console.log(`Installed ${plugin.manifest.name} to ${primaryOutputRoot}`)
|
|
95
102
|
|
|
@@ -110,7 +117,7 @@ export default defineCommand({
|
|
|
110
117
|
console.warn(`Skipping ${extra}: no output returned.`)
|
|
111
118
|
continue
|
|
112
119
|
}
|
|
113
|
-
const extraRoot = resolveTargetOutputRoot(extra, path.join(outputRoot, extra), codexHome, hasExplicitOutput)
|
|
120
|
+
const extraRoot = resolveTargetOutputRoot(extra, path.join(outputRoot, extra), codexHome, piHome, hasExplicitOutput)
|
|
114
121
|
await handler.write(extraRoot, extraBundle)
|
|
115
122
|
console.log(`Installed ${plugin.manifest.name} to ${extraRoot}`)
|
|
116
123
|
}
|
|
@@ -152,26 +159,6 @@ function parseExtraTargets(value: unknown): string[] {
|
|
|
152
159
|
.filter(Boolean)
|
|
153
160
|
}
|
|
154
161
|
|
|
155
|
-
function resolveCodexHome(value: unknown): string | null {
|
|
156
|
-
if (!value) return null
|
|
157
|
-
const raw = String(value).trim()
|
|
158
|
-
if (!raw) return null
|
|
159
|
-
const expanded = expandHome(raw)
|
|
160
|
-
return path.resolve(expanded)
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
function resolveCodexRoot(value: unknown): string {
|
|
164
|
-
return resolveCodexHome(value) ?? path.join(os.homedir(), ".codex")
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
function expandHome(value: string): string {
|
|
168
|
-
if (value === "~") return os.homedir()
|
|
169
|
-
if (value.startsWith(`~${path.sep}`)) {
|
|
170
|
-
return path.join(os.homedir(), value.slice(2))
|
|
171
|
-
}
|
|
172
|
-
return value
|
|
173
|
-
}
|
|
174
|
-
|
|
175
162
|
function resolveOutputRoot(value: unknown): string {
|
|
176
163
|
if (value && String(value).trim()) {
|
|
177
164
|
const expanded = expandHome(String(value).trim())
|
|
@@ -182,13 +169,28 @@ function resolveOutputRoot(value: unknown): string {
|
|
|
182
169
|
return path.join(os.homedir(), ".config", "opencode")
|
|
183
170
|
}
|
|
184
171
|
|
|
185
|
-
function resolveTargetOutputRoot(
|
|
172
|
+
function resolveTargetOutputRoot(
|
|
173
|
+
targetName: string,
|
|
174
|
+
outputRoot: string,
|
|
175
|
+
codexHome: string,
|
|
176
|
+
piHome: string,
|
|
177
|
+
hasExplicitOutput: boolean,
|
|
178
|
+
): string {
|
|
186
179
|
if (targetName === "codex") return codexHome
|
|
180
|
+
if (targetName === "pi") return piHome
|
|
187
181
|
if (targetName === "droid") return path.join(os.homedir(), ".factory")
|
|
188
182
|
if (targetName === "cursor") {
|
|
189
183
|
const base = hasExplicitOutput ? outputRoot : process.cwd()
|
|
190
184
|
return path.join(base, ".cursor")
|
|
191
185
|
}
|
|
186
|
+
if (targetName === "gemini") {
|
|
187
|
+
const base = hasExplicitOutput ? outputRoot : process.cwd()
|
|
188
|
+
return path.join(base, ".gemini")
|
|
189
|
+
}
|
|
190
|
+
if (targetName === "copilot") {
|
|
191
|
+
const base = hasExplicitOutput ? outputRoot : process.cwd()
|
|
192
|
+
return path.join(base, ".github")
|
|
193
|
+
}
|
|
192
194
|
return outputRoot
|
|
193
195
|
}
|
|
194
196
|
|
package/src/commands/sync.ts
CHANGED
|
@@ -4,9 +4,16 @@ import path from "path"
|
|
|
4
4
|
import { loadClaudeHome } from "../parsers/claude-home"
|
|
5
5
|
import { syncToOpenCode } from "../sync/opencode"
|
|
6
6
|
import { syncToCodex } from "../sync/codex"
|
|
7
|
+
import { syncToPi } from "../sync/pi"
|
|
8
|
+
import { syncToDroid } from "../sync/droid"
|
|
9
|
+
import { syncToCopilot } from "../sync/copilot"
|
|
10
|
+
import { expandHome } from "../utils/resolve-home"
|
|
7
11
|
|
|
8
|
-
|
|
9
|
-
|
|
12
|
+
const validTargets = ["opencode", "codex", "pi", "droid", "copilot"] as const
|
|
13
|
+
type SyncTarget = (typeof validTargets)[number]
|
|
14
|
+
|
|
15
|
+
function isValidTarget(value: string): value is SyncTarget {
|
|
16
|
+
return (validTargets as readonly string[]).includes(value)
|
|
10
17
|
}
|
|
11
18
|
|
|
12
19
|
/** Check if any MCP servers have env vars that might contain secrets */
|
|
@@ -23,16 +30,31 @@ function hasPotentialSecrets(mcpServers: Record<string, unknown>): boolean {
|
|
|
23
30
|
return false
|
|
24
31
|
}
|
|
25
32
|
|
|
33
|
+
function resolveOutputRoot(target: SyncTarget): string {
|
|
34
|
+
switch (target) {
|
|
35
|
+
case "opencode":
|
|
36
|
+
return path.join(os.homedir(), ".config", "opencode")
|
|
37
|
+
case "codex":
|
|
38
|
+
return path.join(os.homedir(), ".codex")
|
|
39
|
+
case "pi":
|
|
40
|
+
return path.join(os.homedir(), ".pi", "agent")
|
|
41
|
+
case "droid":
|
|
42
|
+
return path.join(os.homedir(), ".factory")
|
|
43
|
+
case "copilot":
|
|
44
|
+
return path.join(process.cwd(), ".github")
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
26
48
|
export default defineCommand({
|
|
27
49
|
meta: {
|
|
28
50
|
name: "sync",
|
|
29
|
-
description: "Sync Claude Code config (~/.claude/) to OpenCode or
|
|
51
|
+
description: "Sync Claude Code config (~/.claude/) to OpenCode, Codex, Pi, Droid, or Copilot",
|
|
30
52
|
},
|
|
31
53
|
args: {
|
|
32
54
|
target: {
|
|
33
55
|
type: "string",
|
|
34
56
|
required: true,
|
|
35
|
-
description: "Target: opencode | codex",
|
|
57
|
+
description: "Target: opencode | codex | pi | droid | copilot",
|
|
36
58
|
},
|
|
37
59
|
claudeHome: {
|
|
38
60
|
type: "string",
|
|
@@ -42,7 +64,7 @@ export default defineCommand({
|
|
|
42
64
|
},
|
|
43
65
|
async run({ args }) {
|
|
44
66
|
if (!isValidTarget(args.target)) {
|
|
45
|
-
throw new Error(`Unknown target: ${args.target}. Use
|
|
67
|
+
throw new Error(`Unknown target: ${args.target}. Use one of: ${validTargets.join(", ")}`)
|
|
46
68
|
}
|
|
47
69
|
|
|
48
70
|
const claudeHome = expandHome(args.claudeHome ?? path.join(os.homedir(), ".claude"))
|
|
@@ -60,25 +82,26 @@ export default defineCommand({
|
|
|
60
82
|
`Syncing ${config.skills.length} skills, ${Object.keys(config.mcpServers).length} MCP servers...`,
|
|
61
83
|
)
|
|
62
84
|
|
|
63
|
-
const outputRoot =
|
|
64
|
-
args.target === "opencode"
|
|
65
|
-
? path.join(os.homedir(), ".config", "opencode")
|
|
66
|
-
: path.join(os.homedir(), ".codex")
|
|
85
|
+
const outputRoot = resolveOutputRoot(args.target)
|
|
67
86
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
87
|
+
switch (args.target) {
|
|
88
|
+
case "opencode":
|
|
89
|
+
await syncToOpenCode(config, outputRoot)
|
|
90
|
+
break
|
|
91
|
+
case "codex":
|
|
92
|
+
await syncToCodex(config, outputRoot)
|
|
93
|
+
break
|
|
94
|
+
case "pi":
|
|
95
|
+
await syncToPi(config, outputRoot)
|
|
96
|
+
break
|
|
97
|
+
case "droid":
|
|
98
|
+
await syncToDroid(config, outputRoot)
|
|
99
|
+
break
|
|
100
|
+
case "copilot":
|
|
101
|
+
await syncToCopilot(config, outputRoot)
|
|
102
|
+
break
|
|
72
103
|
}
|
|
73
104
|
|
|
74
105
|
console.log(`✓ Synced to ${args.target}: ${outputRoot}`)
|
|
75
106
|
},
|
|
76
107
|
})
|
|
77
|
-
|
|
78
|
-
function expandHome(value: string): string {
|
|
79
|
-
if (value === "~") return os.homedir()
|
|
80
|
-
if (value.startsWith(`~${path.sep}`)) {
|
|
81
|
-
return path.join(os.homedir(), value.slice(2))
|
|
82
|
-
}
|
|
83
|
-
return value
|
|
84
|
-
}
|