questionably-ultrathink 1.0.0 → 1.0.1
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/README.md +39 -9
- package/dist/claude-code/skills/questionably-ultrathink-skill/SKILL.md +28 -0
- package/dist/opencode/agent/aot-recompute.md +0 -1
- package/dist/opencode/agent/atom-of-thoughts.md +0 -1
- package/dist/opencode/agent/chain-of-verification.md +0 -1
- package/dist/opencode/agent/questionably-ultrathink.md +49 -16
- package/dist/opencode/command/questionably-ultrathink.md +2 -22
- package/package.json +2 -4
- package/scripts/build-dist.ts +13 -46
- package/scripts/check-frontmatter.py +26 -11
- package/scripts/install-plugin.ts +35 -60
- package/scripts/validate-plugin.sh +26 -2
- package/dist/claude-code/.claude-plugin/marketplace.json +0 -17
- package/scripts/sync-opencode.ts +0 -349
package/README.md
CHANGED
|
@@ -12,11 +12,45 @@ UltraThink enhances Claude's reasoning with two research-backed frameworks:
|
|
|
12
12
|
## Installation
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
bun add -g questionably-ultrathink
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Then run the interactive installer:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
questionably-ultrathink install
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Or install directly to a specific platform:
|
|
17
25
|
|
|
18
|
-
|
|
19
|
-
|
|
26
|
+
```bash
|
|
27
|
+
questionably-ultrathink install --claude-code # Claude Code only
|
|
28
|
+
questionably-ultrathink install --opencode # OpenCode only
|
|
29
|
+
questionably-ultrathink install --both # Both platforms
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Install locations:**
|
|
33
|
+
- Claude Code: `~/.claude/plugins/questionably-ultrathink/`
|
|
34
|
+
- OpenCode: `~/.config/opencode/`
|
|
35
|
+
|
|
36
|
+
> **Alternative:** Claude Code users can also install via marketplace:
|
|
37
|
+
> ```bash
|
|
38
|
+
> /plugin marketplace add snowmead/questionably-ultrathink
|
|
39
|
+
> /plugin install questionably-ultrathink@snowmead-marketplace
|
|
40
|
+
> ```
|
|
41
|
+
|
|
42
|
+
## Usage
|
|
43
|
+
|
|
44
|
+
### Claude Code
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
/questionably-ultrathink analyze whether this authentication approach is secure
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### OpenCode
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
@questionably-ultrathink analyze whether this authentication approach is secure
|
|
20
54
|
```
|
|
21
55
|
|
|
22
56
|
## Development Setup
|
|
@@ -31,7 +65,7 @@ This installs dependencies (lefthook, comrak) if missing and sets up git hooks f
|
|
|
31
65
|
|
|
32
66
|
## Commands
|
|
33
67
|
|
|
34
|
-
### `/questionably-ultrathink`
|
|
68
|
+
### `/questionably-ultrathink` (Claude Code) / `@questionably-ultrathink` (OpenCode)
|
|
35
69
|
|
|
36
70
|
Run the full reasoning pipeline on a problem:
|
|
37
71
|
|
|
@@ -43,10 +77,6 @@ Run the full reasoning pipeline on a problem:
|
|
|
43
77
|
6. Synthesizes and verifies final response
|
|
44
78
|
7. Iterates if confidence is below threshold (thorough/high-stakes only)
|
|
45
79
|
|
|
46
|
-
```
|
|
47
|
-
/questionably-ultrathink analyze whether this authentication approach is secure
|
|
48
|
-
```
|
|
49
|
-
|
|
50
80
|
### `/decompose`
|
|
51
81
|
|
|
52
82
|
Break down a complex problem into atomic sub-questions:
|
|
@@ -18,6 +18,26 @@ allowed-tools: [Task, Read, Grep, Glob, WebSearch, WebFetch, AskUserQuestion, Ba
|
|
|
18
18
|
|
|
19
19
|
You now have access to advanced reasoning agents for rigorous analysis. Use them when problems require more than surface-level analysis.
|
|
20
20
|
|
|
21
|
+
\<critical\_warning\>
|
|
22
|
+
|
|
23
|
+
## ⚠️ CRITICAL: DO NOT INVOKE YOURSELF
|
|
24
|
+
|
|
25
|
+
You ARE the `questionably-ultrathink` orchestrator. You must **NEVER** call:
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
subagent_type: "questionably-ultrathink" ← FORBIDDEN (infinite recursion)
|
|
29
|
+
subagent_type: "questionably-ultrathink-skill" ← FORBIDDEN (infinite recursion)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
You can ONLY invoke these subagents:
|
|
33
|
+
|
|
34
|
+
- `subagent_type: "questionably-ultrathink:atom-of-thoughts"` ← Use this for decomposition
|
|
35
|
+
- `subagent_type: "questionably-ultrathink:chain-of-verification"` ← Use this for verification
|
|
36
|
+
- `subagent_type: "questionably-ultrathink:aot-recompute"` ← Use this for recomputation
|
|
37
|
+
|
|
38
|
+
Calling yourself causes infinite recursion and task failure.
|
|
39
|
+
\</critical\_warning\>
|
|
40
|
+
|
|
21
41
|
\<clarification\_first\>
|
|
22
42
|
|
|
23
43
|
## Step 0: Clarify Intent First (MANDATORY)
|
|
@@ -72,6 +92,14 @@ After clarifying intent, use `AskUserQuestion` to determine the analysis depth:
|
|
|
72
92
|
|
|
73
93
|
## Available Agents
|
|
74
94
|
|
|
95
|
+
**CRITICAL WARNING:** You are the orchestrator. You must NEVER invoke yourself. NEVER use `subagent_type: "questionably-ultrathink"` or `subagent_type: "questionably-ultrathink-skill"`. You can ONLY invoke these three subagents:
|
|
96
|
+
|
|
97
|
+
- `questionably-ultrathink:atom-of-thoughts` - for decomposition
|
|
98
|
+
- `questionably-ultrathink:chain-of-verification` - for verification
|
|
99
|
+
- `questionably-ultrathink:aot-recompute` - for recomputation after corrections
|
|
100
|
+
|
|
101
|
+
If you call yourself, you create infinite recursion and fail the task.
|
|
102
|
+
|
|
75
103
|
### atom-of-thoughts
|
|
76
104
|
|
|
77
105
|
**Purpose:** Decompose complex problems into atomic sub-questions
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: |-
|
|
3
|
+
CRITICAL: You ARE this agent. NEVER call subagent_type:"questionably-ultrathink" (infinite recursion).
|
|
4
|
+
ONLY use: subagent_type:"atom-of-thoughts", subagent_type:"chain-of-verification", or subagent_type:"aot-recompute".
|
|
5
|
+
|
|
3
6
|
Use this skill when facing complex problems requiring rigorous reasoning, systematic decomposition, or factual verification.
|
|
4
7
|
|
|
5
8
|
Activation triggers:
|
|
@@ -11,7 +14,6 @@ description: |-
|
|
|
11
14
|
- High-stakes technical decisions
|
|
12
15
|
- Debugging complex issues
|
|
13
16
|
mode: primary
|
|
14
|
-
model: anthropic/claude-sonnet-4-20250514
|
|
15
17
|
permission:
|
|
16
18
|
task: allow
|
|
17
19
|
read: allow
|
|
@@ -26,6 +28,25 @@ permission:
|
|
|
26
28
|
|
|
27
29
|
You now have access to advanced reasoning agents for rigorous analysis. Use them when problems require more than surface-level analysis.
|
|
28
30
|
|
|
31
|
+
\<critical\_warning\>
|
|
32
|
+
|
|
33
|
+
## ⚠️ CRITICAL: DO NOT INVOKE YOURSELF
|
|
34
|
+
|
|
35
|
+
You ARE the `questionably-ultrathink` orchestrator. You must **NEVER** call:
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
subagent_type: "questionably-ultrathink" ← FORBIDDEN (infinite recursion)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
You can ONLY invoke these subagents:
|
|
42
|
+
|
|
43
|
+
- `subagent_type: "atom-of-thoughts"` ← Use this for decomposition
|
|
44
|
+
- `subagent_type: "chain-of-verification"` ← Use this for verification
|
|
45
|
+
- `subagent_type: "aot-recompute"` ← Use this for recomputation
|
|
46
|
+
|
|
47
|
+
Calling yourself causes infinite recursion and task failure.
|
|
48
|
+
\</critical\_warning\>
|
|
49
|
+
|
|
29
50
|
\<clarification\_first\>
|
|
30
51
|
|
|
31
52
|
## Step 0: Clarify Intent First (MANDATORY)
|
|
@@ -80,10 +101,18 @@ After clarifying intent, use `AskUserQuestion` to determine the analysis depth:
|
|
|
80
101
|
|
|
81
102
|
## Available Agents
|
|
82
103
|
|
|
104
|
+
**CRITICAL WARNING:** You are `questionably-ultrathink`, the orchestrator. You must NEVER invoke yourself. NEVER use `subagent_type: "questionably-ultrathink"`. You can ONLY invoke these three subagents:
|
|
105
|
+
|
|
106
|
+
- `atom-of-thoughts` - for decomposition
|
|
107
|
+
- `chain-of-verification` - for verification
|
|
108
|
+
- `aot-recompute` - for recomputation after corrections
|
|
109
|
+
|
|
110
|
+
If you call `subagent_type: "questionably-ultrathink"`, you create infinite recursion and fail the task.
|
|
111
|
+
|
|
83
112
|
### atom-of-thoughts
|
|
84
113
|
|
|
85
114
|
**Purpose:** Decompose complex problems into atomic sub-questions
|
|
86
|
-
**Invoke:** `Task` tool with
|
|
115
|
+
**Invoke:** `Task` tool with `subagent_type="atom-of-thoughts"`
|
|
87
116
|
|
|
88
117
|
Use when:
|
|
89
118
|
|
|
@@ -95,7 +124,7 @@ Use when:
|
|
|
95
124
|
### chain-of-verification
|
|
96
125
|
|
|
97
126
|
**Purpose:** Verify factual claims to reduce hallucinations
|
|
98
|
-
**Invoke:** `Task` tool with
|
|
127
|
+
**Invoke:** `Task` tool with `subagent_type="chain-of-verification"`
|
|
99
128
|
|
|
100
129
|
Use when:
|
|
101
130
|
|
|
@@ -107,7 +136,7 @@ Use when:
|
|
|
107
136
|
### aot-recompute
|
|
108
137
|
|
|
109
138
|
**Purpose:** Recompute atoms after CoV finds corrections
|
|
110
|
-
**Invoke:** `Task` tool with
|
|
139
|
+
**Invoke:** `Task` tool with `subagent_type="aot-recompute"`
|
|
111
140
|
|
|
112
141
|
Use when:
|
|
113
142
|
|
|
@@ -141,8 +170,8 @@ Example: `a1b2c3d4`
|
|
|
141
170
|
|
|
142
171
|
**Step 2: Decompose with AoT**
|
|
143
172
|
|
|
144
|
-
Use the
|
|
145
|
-
-
|
|
173
|
+
Use the Task tool to invoke the subagent:
|
|
174
|
+
- subagent_type: "atom-of-thoughts"
|
|
146
175
|
- prompt: "Session ID: {session-id}. Rigor: {rigor-level}. Decompose this query into atomic sub-questions: {clarified query}"
|
|
147
176
|
|
|
148
177
|
**IMPORTANT:**
|
|
@@ -181,8 +210,9 @@ Process each level in order:
|
|
|
181
210
|
|
|
182
211
|
For each atom requiring CoV, invoke the chain-of-verification agent:
|
|
183
212
|
|
|
184
|
-
Task:
|
|
185
|
-
|
|
213
|
+
Task tool:
|
|
214
|
+
subagent_type: "chain-of-verification"
|
|
215
|
+
prompt: "Session ID: {session-id}. Verify atom {atom-id}. Read the reasoning from .questionably-ultrathink/{session-id}/atoms/{atom-id}.md and verify both the factual claims AND the reasoning chain."
|
|
186
216
|
|
|
187
217
|
**Parallel execution:** Invoke ALL atoms at the same level in a SINGLE message with multiple Task calls. Wait for all results before proceeding to the next level.
|
|
188
218
|
|
|
@@ -203,8 +233,9 @@ When CoV finds errors, it writes correction files to `.questionably-ultrathink/{
|
|
|
203
233
|
|
|
204
234
|
3. **Invoke aot-recompute:**
|
|
205
235
|
|
|
206
|
-
Task:
|
|
207
|
-
|
|
236
|
+
Task tool:
|
|
237
|
+
subagent_type: "aot-recompute"
|
|
238
|
+
prompt: "Session ID: {session-id}. Corrected atoms: [A1]. Atoms to recompute: [A3, FINAL]."
|
|
208
239
|
|
|
209
240
|
4. **Re-verify recomputed atoms:**
|
|
210
241
|
Add recomputed atoms back to the verification queue at their dependency level.
|
|
@@ -232,8 +263,8 @@ Display under "Phase 3: Synthesis".
|
|
|
232
263
|
|
|
233
264
|
**Step 5: Final Verification (MANDATORY)**
|
|
234
265
|
|
|
235
|
-
Use the
|
|
236
|
-
-
|
|
266
|
+
Use the Task tool to invoke the subagent:
|
|
267
|
+
- subagent_type: "chain-of-verification"
|
|
237
268
|
- prompt: "Verify this synthesized response: {synthesis}"
|
|
238
269
|
|
|
239
270
|
**DO NOT SKIP THIS STEP.** The final response must be verified.
|
|
@@ -276,13 +307,15 @@ After Phase 4 (Final Verification), check if iteration is needed based on the us
|
|
|
276
307
|
|
|
277
308
|
**Iteration invocation (new decomposition):**
|
|
278
309
|
|
|
279
|
-
Task:
|
|
280
|
-
|
|
310
|
+
Task tool:
|
|
311
|
+
subagent_type: "atom-of-thoughts"
|
|
312
|
+
prompt: "Session ID: {session-id}. Rigor: {rigor}. Re-analyze these problematic areas with fresh perspective: {list of issues}"
|
|
281
313
|
|
|
282
314
|
**Iteration invocation (correction-based update):**
|
|
283
315
|
|
|
284
|
-
Task:
|
|
285
|
-
|
|
316
|
+
Task tool:
|
|
317
|
+
subagent_type: "aot-recompute"
|
|
318
|
+
prompt: "Session ID: {session-id}. Corrected atoms: [...]. Atoms to recompute: [...]."
|
|
286
319
|
|
|
287
320
|
Then continue with Steps 3-5 for the new/revised atoms.
|
|
288
321
|
\</iterative\_refinement\>
|
|
@@ -1,26 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Apply full UltraThink reasoning pipeline (AoT + CoVe)
|
|
2
|
+
description: Apply full UltraThink reasoning pipeline (AoT + CoVe)
|
|
3
3
|
---
|
|
4
4
|
Switch to the @questionably-ultrathink agent.
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
# /questionably-ultrathink Command
|
|
8
|
-
|
|
9
|
-
The user has requested full UltraThink analysis.
|
|
10
|
-
|
|
11
|
-
**Immediately invoke the questionably-ultrathink-skill** using the Skill tool to execute the full pipeline:
|
|
12
|
-
|
|
13
|
-
Use the Skill tool with:
|
|
14
|
-
|
|
15
|
-
- skill: "questionably-ultrathink-skill"
|
|
16
|
-
- args: "$ARGUMENTS"
|
|
17
|
-
|
|
18
|
-
The skill contains the complete orchestration protocol for:
|
|
19
|
-
|
|
20
|
-
1. Intent clarification (AskUserQuestion if needed)
|
|
21
|
-
2. Atom of Thoughts decomposition (with complexity flagging)
|
|
22
|
-
3. Parallel Chain of Verification for critical atoms (by dependency level)
|
|
23
|
-
4. Synthesis and final verification
|
|
24
|
-
5. Optional iterative refinement for high-stakes or low-confidence results
|
|
25
|
-
|
|
26
|
-
Do not duplicate the orchestration logic here—let the skill handle it.
|
|
6
|
+
User query: $ARGUMENTS
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "questionably-ultrathink",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Claude Code plugin integrating Atom of Thoughts (AoT) and Chain of Verification (CoVe) reasoning frameworks",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -12,9 +12,7 @@
|
|
|
12
12
|
],
|
|
13
13
|
"scripts": {
|
|
14
14
|
"install": "bun scripts/install-plugin.ts",
|
|
15
|
-
"
|
|
16
|
-
"watch:opencode": "bun scripts/sync-opencode.ts --watch",
|
|
17
|
-
"build": "bun scripts/sync-opencode.ts && bun scripts/build-dist.ts",
|
|
15
|
+
"build": "bun scripts/build-dist.ts",
|
|
18
16
|
"postinstall": "bun scripts/install-plugin.ts --auto"
|
|
19
17
|
},
|
|
20
18
|
"dependencies": {
|
package/scripts/build-dist.ts
CHANGED
|
@@ -3,9 +3,8 @@
|
|
|
3
3
|
* Build script to prepare distribution files for npm publishing.
|
|
4
4
|
*
|
|
5
5
|
* This script:
|
|
6
|
-
* 1.
|
|
7
|
-
* 2. Copies
|
|
8
|
-
* 3. Copies OpenCode files to dist/opencode/
|
|
6
|
+
* 1. Copies Claude Code files from claude-code/ to dist/claude-code/
|
|
7
|
+
* 2. Copies OpenCode files from opencode/ to dist/opencode/
|
|
9
8
|
*
|
|
10
9
|
* Run: bun scripts/build-dist.ts
|
|
11
10
|
*/
|
|
@@ -23,12 +22,9 @@ const DIST_DIR = join(ROOT_DIR, "dist");
|
|
|
23
22
|
const DIST_CLAUDE_CODE = join(DIST_DIR, "claude-code");
|
|
24
23
|
const DIST_OPENCODE = join(DIST_DIR, "opencode");
|
|
25
24
|
|
|
26
|
-
// Source directories
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
const SKILLS_DIR = join(ROOT_DIR, "skills");
|
|
30
|
-
const PLUGIN_DIR = join(ROOT_DIR, ".claude-plugin");
|
|
31
|
-
const OPENCODE_DIR = join(ROOT_DIR, ".opencode");
|
|
25
|
+
// Source directories (separate sources for each platform)
|
|
26
|
+
const CLAUDE_CODE_DIR = join(ROOT_DIR, "claude-code");
|
|
27
|
+
const OPENCODE_DIR = join(ROOT_DIR, "opencode");
|
|
32
28
|
|
|
33
29
|
// ============================================================================
|
|
34
30
|
// Utilities
|
|
@@ -65,55 +61,26 @@ async function cleanDist(): Promise<void> {
|
|
|
65
61
|
|
|
66
62
|
async function buildClaudeCode(): Promise<void> {
|
|
67
63
|
console.log("\nBuilding Claude Code distribution...");
|
|
68
|
-
await mkdir(DIST_CLAUDE_CODE, { recursive: true });
|
|
69
64
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
console.log(" ✓ agents/");
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Copy commands
|
|
77
|
-
if (existsSync(COMMANDS_DIR)) {
|
|
78
|
-
await copyDir(COMMANDS_DIR, join(DIST_CLAUDE_CODE, "commands"));
|
|
79
|
-
console.log(" ✓ commands/");
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Copy skills
|
|
83
|
-
if (existsSync(SKILLS_DIR)) {
|
|
84
|
-
await copyDir(SKILLS_DIR, join(DIST_CLAUDE_CODE, "skills"));
|
|
85
|
-
console.log(" ✓ skills/");
|
|
65
|
+
if (!existsSync(CLAUDE_CODE_DIR)) {
|
|
66
|
+
console.log(" ⚠ claude-code/ not found.");
|
|
67
|
+
return;
|
|
86
68
|
}
|
|
87
69
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
await copyDir(PLUGIN_DIR, join(DIST_CLAUDE_CODE, ".claude-plugin"));
|
|
91
|
-
console.log(" ✓ .claude-plugin/");
|
|
92
|
-
}
|
|
70
|
+
await copyDir(CLAUDE_CODE_DIR, DIST_CLAUDE_CODE);
|
|
71
|
+
console.log(" ✓ Copied claude-code/ → dist/claude-code/");
|
|
93
72
|
}
|
|
94
73
|
|
|
95
74
|
async function buildOpenCode(): Promise<void> {
|
|
96
75
|
console.log("\nBuilding OpenCode distribution...");
|
|
97
|
-
await mkdir(DIST_OPENCODE, { recursive: true });
|
|
98
76
|
|
|
99
77
|
if (!existsSync(OPENCODE_DIR)) {
|
|
100
|
-
console.log(" ⚠
|
|
78
|
+
console.log(" ⚠ opencode/ not found.");
|
|
101
79
|
return;
|
|
102
80
|
}
|
|
103
81
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
if (existsSync(agentDir)) {
|
|
107
|
-
await copyDir(agentDir, join(DIST_OPENCODE, "agent"));
|
|
108
|
-
console.log(" ✓ agent/");
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Copy command files
|
|
112
|
-
const commandDir = join(OPENCODE_DIR, "command");
|
|
113
|
-
if (existsSync(commandDir)) {
|
|
114
|
-
await copyDir(commandDir, join(DIST_OPENCODE, "command"));
|
|
115
|
-
console.log(" ✓ command/");
|
|
116
|
-
}
|
|
82
|
+
await copyDir(OPENCODE_DIR, DIST_OPENCODE);
|
|
83
|
+
console.log(" ✓ Copied opencode/ → dist/opencode/");
|
|
117
84
|
}
|
|
118
85
|
|
|
119
86
|
// ============================================================================
|
|
@@ -62,19 +62,29 @@ def validate_command_frontmatter(
|
|
|
62
62
|
return errors
|
|
63
63
|
|
|
64
64
|
|
|
65
|
+
def is_opencode_path(file_path: Path) -> bool:
|
|
66
|
+
"""Check if the file path is in the OpenCode directory structure."""
|
|
67
|
+
return "opencode" in file_path.parts
|
|
68
|
+
|
|
69
|
+
|
|
65
70
|
def validate_agent_frontmatter(
|
|
66
71
|
frontmatter: dict[str, Any], file_path: Path
|
|
67
72
|
) -> list[str]:
|
|
68
|
-
"""Validate agent file frontmatter.
|
|
73
|
+
"""Validate agent file frontmatter.
|
|
74
|
+
|
|
75
|
+
Claude Code agents require: name, description
|
|
76
|
+
OpenCode agents require: description (name is optional, uses filename)
|
|
77
|
+
"""
|
|
69
78
|
errors = []
|
|
70
79
|
|
|
71
|
-
#
|
|
72
|
-
if
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
80
|
+
# OpenCode agents don't require name - they use the filename
|
|
81
|
+
if not is_opencode_path(file_path):
|
|
82
|
+
if "name" not in frontmatter:
|
|
83
|
+
errors.append("Missing required field 'name'")
|
|
84
|
+
elif (
|
|
85
|
+
not isinstance(frontmatter["name"], str) or not frontmatter["name"].strip()
|
|
86
|
+
):
|
|
87
|
+
errors.append("Field 'name' must be a non-empty string")
|
|
78
88
|
|
|
79
89
|
if "description" not in frontmatter:
|
|
80
90
|
errors.append("Missing required field 'description'")
|
|
@@ -88,14 +98,19 @@ def validate_agent_frontmatter(
|
|
|
88
98
|
|
|
89
99
|
|
|
90
100
|
def get_file_type(file_path: Path) -> str | None:
|
|
91
|
-
"""Determine the type of file based on its directory.
|
|
101
|
+
"""Determine the type of file based on its directory.
|
|
102
|
+
|
|
103
|
+
Handles both Claude Code and OpenCode directory structures:
|
|
104
|
+
- Claude Code: commands/, skills/, agents/
|
|
105
|
+
- OpenCode: command/, agent/
|
|
106
|
+
"""
|
|
92
107
|
parts = file_path.parts
|
|
93
108
|
for i, part in enumerate(parts):
|
|
94
109
|
if part == "skills":
|
|
95
110
|
return "skill"
|
|
96
|
-
elif part
|
|
111
|
+
elif part in ("commands", "command"):
|
|
97
112
|
return "command"
|
|
98
|
-
elif part
|
|
113
|
+
elif part in ("agents", "agent"):
|
|
99
114
|
return "agent"
|
|
100
115
|
return None
|
|
101
116
|
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* OpenCode: ~/.config/opencode/
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import { copyFile, readdir, mkdir, stat } from "fs/promises";
|
|
20
|
+
import { copyFile, readdir, mkdir, stat, rm } from "fs/promises";
|
|
21
21
|
import { existsSync } from "fs";
|
|
22
22
|
import { join } from "path";
|
|
23
23
|
import { homedir } from "os";
|
|
@@ -35,9 +35,9 @@ const ROOT_DIR = join(SCRIPT_DIR, "..");
|
|
|
35
35
|
const DIST_DIR = join(ROOT_DIR, "dist");
|
|
36
36
|
const USE_DIST = existsSync(DIST_DIR);
|
|
37
37
|
|
|
38
|
-
// Source paths (development)
|
|
39
|
-
const DEV_CLAUDE_CODE = ROOT_DIR;
|
|
40
|
-
const DEV_OPENCODE = join(ROOT_DIR, "
|
|
38
|
+
// Source paths (development) - separate directories for each platform
|
|
39
|
+
const DEV_CLAUDE_CODE = join(ROOT_DIR, "claude-code");
|
|
40
|
+
const DEV_OPENCODE = join(ROOT_DIR, "opencode");
|
|
41
41
|
|
|
42
42
|
// Dist paths (installed)
|
|
43
43
|
const DIST_CLAUDE_CODE = join(DIST_DIR, "claude-code");
|
|
@@ -48,7 +48,7 @@ const CLAUDE_CODE_PLUGINS_DIR = join(
|
|
|
48
48
|
homedir(),
|
|
49
49
|
".claude",
|
|
50
50
|
"plugins",
|
|
51
|
-
"questionably-ultrathink"
|
|
51
|
+
"questionably-ultrathink",
|
|
52
52
|
);
|
|
53
53
|
const OPENCODE_CONFIG_DIR = join(homedir(), ".config", "opencode");
|
|
54
54
|
|
|
@@ -109,26 +109,6 @@ function getDetectedList(detected: DetectionResult): Platform[] {
|
|
|
109
109
|
return platforms;
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
// ============================================================================
|
|
113
|
-
// OpenCode Sync (imports sync logic if needed in development)
|
|
114
|
-
// ============================================================================
|
|
115
|
-
|
|
116
|
-
async function syncOpenCode(): Promise<boolean> {
|
|
117
|
-
// Only needed in development mode when .opencode doesn't exist
|
|
118
|
-
if (USE_DIST) return true;
|
|
119
|
-
|
|
120
|
-
if (!existsSync(DEV_OPENCODE)) {
|
|
121
|
-
// Run the sync script
|
|
122
|
-
const proc = Bun.spawn(["bun", join(SCRIPT_DIR, "sync-opencode.ts")], {
|
|
123
|
-
stdout: "pipe",
|
|
124
|
-
stderr: "pipe",
|
|
125
|
-
});
|
|
126
|
-
await proc.exited;
|
|
127
|
-
return proc.exitCode === 0;
|
|
128
|
-
}
|
|
129
|
-
return true;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
112
|
// ============================================================================
|
|
133
113
|
// Installation Functions
|
|
134
114
|
// ============================================================================
|
|
@@ -141,6 +121,10 @@ async function installClaudeCode(): Promise<boolean> {
|
|
|
141
121
|
}
|
|
142
122
|
|
|
143
123
|
try {
|
|
124
|
+
// Clean existing installation for a fresh update
|
|
125
|
+
if (existsSync(CLAUDE_CODE_PLUGINS_DIR)) {
|
|
126
|
+
await rm(CLAUDE_CODE_PLUGINS_DIR, { recursive: true });
|
|
127
|
+
}
|
|
144
128
|
await mkdir(CLAUDE_CODE_PLUGINS_DIR, { recursive: true });
|
|
145
129
|
|
|
146
130
|
// Copy agents
|
|
@@ -164,10 +148,7 @@ async function installClaudeCode(): Promise<boolean> {
|
|
|
164
148
|
// Copy plugin metadata
|
|
165
149
|
const pluginDir = join(sourceDir, ".claude-plugin");
|
|
166
150
|
if (existsSync(pluginDir)) {
|
|
167
|
-
await copyDir(
|
|
168
|
-
pluginDir,
|
|
169
|
-
join(CLAUDE_CODE_PLUGINS_DIR, ".claude-plugin")
|
|
170
|
-
);
|
|
151
|
+
await copyDir(pluginDir, join(CLAUDE_CODE_PLUGINS_DIR, ".claude-plugin"));
|
|
171
152
|
}
|
|
172
153
|
|
|
173
154
|
return true;
|
|
@@ -186,7 +167,7 @@ async function installOpenCode(): Promise<boolean> {
|
|
|
186
167
|
try {
|
|
187
168
|
await mkdir(OPENCODE_CONFIG_DIR, { recursive: true });
|
|
188
169
|
|
|
189
|
-
// Copy agent files
|
|
170
|
+
// Copy agent files (delete existing first for clean update)
|
|
190
171
|
const agentDir = join(sourceDir, "agent");
|
|
191
172
|
if (existsSync(agentDir)) {
|
|
192
173
|
const destAgentDir = join(OPENCODE_CONFIG_DIR, "agent");
|
|
@@ -195,12 +176,17 @@ async function installOpenCode(): Promise<boolean> {
|
|
|
195
176
|
const files = await readdir(agentDir);
|
|
196
177
|
for (const file of files) {
|
|
197
178
|
if (file.endsWith(".md")) {
|
|
198
|
-
|
|
179
|
+
const destPath = join(destAgentDir, file);
|
|
180
|
+
// Remove existing file first for clean update
|
|
181
|
+
if (existsSync(destPath)) {
|
|
182
|
+
await rm(destPath);
|
|
183
|
+
}
|
|
184
|
+
await copyFile(join(agentDir, file), destPath);
|
|
199
185
|
}
|
|
200
186
|
}
|
|
201
187
|
}
|
|
202
188
|
|
|
203
|
-
// Copy command files
|
|
189
|
+
// Copy command files (delete existing first for clean update)
|
|
204
190
|
const commandDir = join(sourceDir, "command");
|
|
205
191
|
if (existsSync(commandDir)) {
|
|
206
192
|
const destCommandDir = join(OPENCODE_CONFIG_DIR, "command");
|
|
@@ -209,7 +195,12 @@ async function installOpenCode(): Promise<boolean> {
|
|
|
209
195
|
const files = await readdir(commandDir);
|
|
210
196
|
for (const file of files) {
|
|
211
197
|
if (file.endsWith(".md")) {
|
|
212
|
-
|
|
198
|
+
const destPath = join(destCommandDir, file);
|
|
199
|
+
// Remove existing file first for clean update
|
|
200
|
+
if (existsSync(destPath)) {
|
|
201
|
+
await rm(destPath);
|
|
202
|
+
}
|
|
203
|
+
await copyFile(join(commandDir, file), destPath);
|
|
213
204
|
}
|
|
214
205
|
}
|
|
215
206
|
}
|
|
@@ -258,7 +249,8 @@ async function runInteractive(): Promise<void> {
|
|
|
258
249
|
hint: "~/.config/opencode/",
|
|
259
250
|
},
|
|
260
251
|
],
|
|
261
|
-
initialValues:
|
|
252
|
+
initialValues:
|
|
253
|
+
detectedList.length > 0 ? detectedList : ["claude-code", "opencode"],
|
|
262
254
|
required: true,
|
|
263
255
|
});
|
|
264
256
|
|
|
@@ -273,34 +265,27 @@ async function runInteractive(): Promise<void> {
|
|
|
273
265
|
|
|
274
266
|
async function runNonInteractive(
|
|
275
267
|
installClaudeCodeFlag: boolean,
|
|
276
|
-
installOpenCodeFlag: boolean
|
|
268
|
+
installOpenCodeFlag: boolean,
|
|
277
269
|
): Promise<void> {
|
|
278
270
|
console.log("UltraThink Plugin Installer");
|
|
279
271
|
console.log("===========================");
|
|
280
|
-
console.log(
|
|
272
|
+
console.log(
|
|
273
|
+
`Source: ${USE_DIST ? "dist/ (installed)" : "local (development)"}`,
|
|
274
|
+
);
|
|
281
275
|
|
|
282
276
|
const platforms: Platform[] = [];
|
|
283
277
|
if (installClaudeCodeFlag) platforms.push("claude-code");
|
|
284
278
|
if (installOpenCodeFlag) platforms.push("opencode");
|
|
285
279
|
|
|
286
280
|
if (platforms.length === 0) {
|
|
287
|
-
console.log(
|
|
281
|
+
console.log(
|
|
282
|
+
"\nNo platforms specified. Use --claude-code, --opencode, or --both.",
|
|
283
|
+
);
|
|
288
284
|
process.exit(1);
|
|
289
285
|
}
|
|
290
286
|
|
|
291
287
|
console.log("\nInstalling to:", platforms.join(", "));
|
|
292
288
|
|
|
293
|
-
// Sync OpenCode if needed
|
|
294
|
-
if (platforms.includes("opencode") && !USE_DIST) {
|
|
295
|
-
console.log("\nSyncing OpenCode files...");
|
|
296
|
-
const synced = await syncOpenCode();
|
|
297
|
-
if (!synced) {
|
|
298
|
-
console.log(" ⚠ Failed to sync OpenCode files");
|
|
299
|
-
} else {
|
|
300
|
-
console.log(" ✓ Synced OpenCode files");
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
289
|
let success = true;
|
|
305
290
|
|
|
306
291
|
if (platforms.includes("claude-code")) {
|
|
@@ -342,16 +327,6 @@ async function performInstallation(platforms: Platform[]): Promise<void> {
|
|
|
342
327
|
|
|
343
328
|
const results: string[] = [];
|
|
344
329
|
|
|
345
|
-
// Sync OpenCode if needed
|
|
346
|
-
if (platforms.includes("opencode") && !USE_DIST) {
|
|
347
|
-
const synced = await syncOpenCode();
|
|
348
|
-
if (synced) {
|
|
349
|
-
results.push("✓ Synced OpenCode files");
|
|
350
|
-
} else {
|
|
351
|
-
results.push("⚠ Failed to sync OpenCode files");
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
|
|
355
330
|
// Install to Claude Code
|
|
356
331
|
if (platforms.includes("claude-code")) {
|
|
357
332
|
const success = await installClaudeCode();
|
|
@@ -449,7 +424,7 @@ async function main(): Promise<void> {
|
|
|
449
424
|
if (hasClaudeCodeFlag || hasOpenCodeFlag || hasBothFlag) {
|
|
450
425
|
await runNonInteractive(
|
|
451
426
|
hasClaudeCodeFlag || hasBothFlag,
|
|
452
|
-
hasOpenCodeFlag || hasBothFlag
|
|
427
|
+
hasOpenCodeFlag || hasBothFlag,
|
|
453
428
|
);
|
|
454
429
|
return;
|
|
455
430
|
}
|
|
@@ -469,7 +444,7 @@ async function main(): Promise<void> {
|
|
|
469
444
|
|
|
470
445
|
await runNonInteractive(
|
|
471
446
|
platforms.includes("claude-code"),
|
|
472
|
-
platforms.includes("opencode")
|
|
447
|
+
platforms.includes("opencode"),
|
|
473
448
|
);
|
|
474
449
|
return;
|
|
475
450
|
}
|
|
@@ -106,7 +106,31 @@ validate_directory_structure() {
|
|
|
106
106
|
|
|
107
107
|
local has_content=false
|
|
108
108
|
|
|
109
|
-
# Check
|
|
109
|
+
# Check Claude Code directories (claude-code/commands/, claude-code/skills/, claude-code/agents/)
|
|
110
|
+
for dir in "claude-code/commands" "claude-code/skills" "claude-code/agents"; do
|
|
111
|
+
if [[ -d "$dir" ]]; then
|
|
112
|
+
local md_count
|
|
113
|
+
md_count=$(find "$dir" -name "*.md" | wc -l)
|
|
114
|
+
if [[ $md_count -gt 0 ]]; then
|
|
115
|
+
has_content=true
|
|
116
|
+
echo " Found $md_count .md file(s) in $dir/"
|
|
117
|
+
fi
|
|
118
|
+
fi
|
|
119
|
+
done
|
|
120
|
+
|
|
121
|
+
# Check OpenCode directories (opencode/command/, opencode/agent/)
|
|
122
|
+
for dir in "opencode/command" "opencode/agent"; do
|
|
123
|
+
if [[ -d "$dir" ]]; then
|
|
124
|
+
local md_count
|
|
125
|
+
md_count=$(find "$dir" -name "*.md" | wc -l)
|
|
126
|
+
if [[ $md_count -gt 0 ]]; then
|
|
127
|
+
has_content=true
|
|
128
|
+
echo " Found $md_count .md file(s) in $dir/"
|
|
129
|
+
fi
|
|
130
|
+
fi
|
|
131
|
+
done
|
|
132
|
+
|
|
133
|
+
# Fallback: check legacy root directories (commands/, skills/, agents/)
|
|
110
134
|
for dir in "commands" "skills" "agents"; do
|
|
111
135
|
if [[ -d "$dir" ]]; then
|
|
112
136
|
local md_count
|
|
@@ -119,7 +143,7 @@ validate_directory_structure() {
|
|
|
119
143
|
done
|
|
120
144
|
|
|
121
145
|
if [[ "$has_content" == "false" ]]; then
|
|
122
|
-
error "Plugin must have at least one of: commands/, skills/, or
|
|
146
|
+
error "Plugin must have at least one of: commands/, skills/, agents/, claude-code/*/, or opencode/*/ with .md files"
|
|
123
147
|
exit 1
|
|
124
148
|
fi
|
|
125
149
|
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "questionably-ultrathink",
|
|
3
|
-
"owner": {
|
|
4
|
-
"name": "snowmead"
|
|
5
|
-
},
|
|
6
|
-
"metadata": {
|
|
7
|
-
"description": "Plugin marketplace for UltraThink reasoning framework integrating Chain of Verification and Atom of Thoughts",
|
|
8
|
-
"version": "1.0.0"
|
|
9
|
-
},
|
|
10
|
-
"plugins": [
|
|
11
|
-
{
|
|
12
|
-
"name": "questionably-ultrathink",
|
|
13
|
-
"description": "Advanced reasoning plugin integrating Chain of Verification (CoVe) and Atom of Thoughts (AoT) frameworks for rigorous, verifiable analysis",
|
|
14
|
-
"source": "./"
|
|
15
|
-
}
|
|
16
|
-
]
|
|
17
|
-
}
|
package/scripts/sync-opencode.ts
DELETED
|
@@ -1,349 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
/**
|
|
3
|
-
* Sync script to translate Claude Code plugin files to OpenCode format.
|
|
4
|
-
*
|
|
5
|
-
* This script maintains Claude Code as the source of truth and generates
|
|
6
|
-
* OpenCode-compatible files in .opencode/
|
|
7
|
-
*
|
|
8
|
-
* Run: bun scripts/sync-opencode.ts
|
|
9
|
-
* Watch: bun scripts/sync-opencode.ts --watch
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { readdir, readFile, writeFile, mkdir, rm } from "fs/promises";
|
|
13
|
-
import { existsSync } from "fs";
|
|
14
|
-
import { join, basename } from "path";
|
|
15
|
-
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
16
|
-
|
|
17
|
-
// ============================================================================
|
|
18
|
-
// Types
|
|
19
|
-
// ============================================================================
|
|
20
|
-
|
|
21
|
-
interface ClaudeCodeAgentFrontmatter {
|
|
22
|
-
name: string;
|
|
23
|
-
description: string;
|
|
24
|
-
model?: string;
|
|
25
|
-
tools?: string[];
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
interface ClaudeCodeCommandFrontmatter {
|
|
29
|
-
name: string;
|
|
30
|
-
description: string;
|
|
31
|
-
"allowed-tools"?: string[];
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
interface ClaudeCodeSkillFrontmatter {
|
|
35
|
-
name: string;
|
|
36
|
-
description: string;
|
|
37
|
-
"allowed-tools"?: string[];
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
interface OpenCodeAgentFrontmatter {
|
|
41
|
-
description: string;
|
|
42
|
-
mode: "primary" | "subagent";
|
|
43
|
-
model?: string;
|
|
44
|
-
permission?: Record<string, "allow" | "ask" | "deny">;
|
|
45
|
-
hidden?: boolean;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
interface OpenCodeCommandFrontmatter {
|
|
49
|
-
description: string;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
interface ParsedFile<T> {
|
|
53
|
-
frontmatter: T;
|
|
54
|
-
body: string;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// ============================================================================
|
|
58
|
-
// Configuration
|
|
59
|
-
// ============================================================================
|
|
60
|
-
|
|
61
|
-
const ROOT_DIR = process.cwd();
|
|
62
|
-
const AGENTS_DIR = join(ROOT_DIR, "agents");
|
|
63
|
-
const COMMANDS_DIR = join(ROOT_DIR, "commands");
|
|
64
|
-
const SKILLS_DIR = join(ROOT_DIR, "skills");
|
|
65
|
-
const OPENCODE_AGENT_DIR = join(ROOT_DIR, ".opencode", "agent");
|
|
66
|
-
const OPENCODE_COMMAND_DIR = join(ROOT_DIR, ".opencode", "command");
|
|
67
|
-
|
|
68
|
-
// Model mapping from Claude Code shorthand to OpenCode full IDs
|
|
69
|
-
const MODEL_MAP: Record<string, string> = {
|
|
70
|
-
haiku: "anthropic/claude-3-5-haiku",
|
|
71
|
-
sonnet: "anthropic/claude-sonnet-4-20250514",
|
|
72
|
-
opus: "anthropic/claude-opus-4-20250514",
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
// Tool name mapping from Claude Code to OpenCode permission names
|
|
76
|
-
const TOOL_MAP: Record<string, string> = {
|
|
77
|
-
Read: "read",
|
|
78
|
-
Write: "write",
|
|
79
|
-
Edit: "edit",
|
|
80
|
-
Bash: "bash",
|
|
81
|
-
Grep: "grep",
|
|
82
|
-
Glob: "glob",
|
|
83
|
-
WebFetch: "webfetch",
|
|
84
|
-
WebSearch: "webfetch",
|
|
85
|
-
AskUserQuestion: "ask",
|
|
86
|
-
Task: "task",
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
// ============================================================================
|
|
90
|
-
// Utilities
|
|
91
|
-
// ============================================================================
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Parse a markdown file with YAML frontmatter
|
|
95
|
-
*/
|
|
96
|
-
function parseFrontmatter<T>(content: string): ParsedFile<T> {
|
|
97
|
-
const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
98
|
-
if (!match) {
|
|
99
|
-
throw new Error("Invalid frontmatter format");
|
|
100
|
-
}
|
|
101
|
-
return {
|
|
102
|
-
frontmatter: parseYaml(match[1]) as T,
|
|
103
|
-
body: match[2],
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Format a file with YAML frontmatter
|
|
109
|
-
*/
|
|
110
|
-
function formatWithFrontmatter<T>(frontmatter: T, body: string): string {
|
|
111
|
-
const yamlContent = stringifyYaml(frontmatter, { lineWidth: 0 });
|
|
112
|
-
return `---\n${yamlContent}---\n${body}`;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Translate Claude Code model name to OpenCode model ID
|
|
117
|
-
*/
|
|
118
|
-
function translateModel(model?: string): string | undefined {
|
|
119
|
-
if (!model) return undefined;
|
|
120
|
-
return MODEL_MAP[model] || model;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Translate Claude Code tools array to OpenCode permission object
|
|
125
|
-
*/
|
|
126
|
-
function translateTools(
|
|
127
|
-
tools?: string[]
|
|
128
|
-
): Record<string, "allow"> | undefined {
|
|
129
|
-
if (!tools || tools.length === 0) return undefined;
|
|
130
|
-
|
|
131
|
-
const result: Record<string, "allow"> = {};
|
|
132
|
-
for (const tool of tools) {
|
|
133
|
-
// Handle MCP tool patterns (e.g., mcp__parallel-search__*)
|
|
134
|
-
if (tool.startsWith("mcp__")) {
|
|
135
|
-
result["mcp"] = "allow";
|
|
136
|
-
continue;
|
|
137
|
-
}
|
|
138
|
-
const mapped = TOOL_MAP[tool];
|
|
139
|
-
if (mapped) {
|
|
140
|
-
result[mapped] = "allow";
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
return Object.keys(result).length > 0 ? result : undefined;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Translate body content from Claude Code patterns to OpenCode patterns
|
|
148
|
-
*/
|
|
149
|
-
function translateBody(body: string): string {
|
|
150
|
-
return (
|
|
151
|
-
body
|
|
152
|
-
// Translate subagent_type references in Task tool invocations
|
|
153
|
-
.replace(
|
|
154
|
-
/subagent_type:\s*["']questionably-ultrathink:([^"']+)["']/g,
|
|
155
|
-
"@$1"
|
|
156
|
-
)
|
|
157
|
-
// Also handle unquoted variants
|
|
158
|
-
.replace(/subagent_type:\s*questionably-ultrathink:(\S+)/g, "@$1")
|
|
159
|
-
// Translate "Invoke Task tool" phrases
|
|
160
|
-
.replace(
|
|
161
|
-
/Invoke (?:the )?Task tool/gi,
|
|
162
|
-
"Use the task tool to invoke the subagent"
|
|
163
|
-
)
|
|
164
|
-
// Translate "Task tool" in code blocks
|
|
165
|
-
.replace(/Task tool:\s*\n/g, "Task:\n")
|
|
166
|
-
);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Clean the output directories
|
|
171
|
-
*/
|
|
172
|
-
async function cleanOutputDirs(): Promise<void> {
|
|
173
|
-
if (existsSync(OPENCODE_AGENT_DIR)) {
|
|
174
|
-
await rm(OPENCODE_AGENT_DIR, { recursive: true });
|
|
175
|
-
}
|
|
176
|
-
if (existsSync(OPENCODE_COMMAND_DIR)) {
|
|
177
|
-
await rm(OPENCODE_COMMAND_DIR, { recursive: true });
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// ============================================================================
|
|
182
|
-
// Sync Functions
|
|
183
|
-
// ============================================================================
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Sync agents from agents/ to .opencode/agent/
|
|
187
|
-
*/
|
|
188
|
-
async function syncAgents(): Promise<void> {
|
|
189
|
-
console.log("Syncing agents...");
|
|
190
|
-
await mkdir(OPENCODE_AGENT_DIR, { recursive: true });
|
|
191
|
-
|
|
192
|
-
const files = await readdir(AGENTS_DIR);
|
|
193
|
-
const mdFiles = files.filter((f) => f.endsWith(".md"));
|
|
194
|
-
|
|
195
|
-
for (const file of mdFiles) {
|
|
196
|
-
const content = await readFile(join(AGENTS_DIR, file), "utf-8");
|
|
197
|
-
const { frontmatter, body } =
|
|
198
|
-
parseFrontmatter<ClaudeCodeAgentFrontmatter>(content);
|
|
199
|
-
|
|
200
|
-
const openCodeFrontmatter: OpenCodeAgentFrontmatter = {
|
|
201
|
-
description: frontmatter.description.trim(),
|
|
202
|
-
mode: "subagent",
|
|
203
|
-
model: translateModel(frontmatter.model),
|
|
204
|
-
permission: translateTools(frontmatter.tools),
|
|
205
|
-
hidden: true, // Hide subagents from @ menu
|
|
206
|
-
};
|
|
207
|
-
|
|
208
|
-
// Remove undefined fields
|
|
209
|
-
if (!openCodeFrontmatter.model) delete openCodeFrontmatter.model;
|
|
210
|
-
if (!openCodeFrontmatter.permission) delete openCodeFrontmatter.permission;
|
|
211
|
-
|
|
212
|
-
const translatedBody = translateBody(body);
|
|
213
|
-
const outputContent = formatWithFrontmatter(
|
|
214
|
-
openCodeFrontmatter,
|
|
215
|
-
translatedBody
|
|
216
|
-
);
|
|
217
|
-
|
|
218
|
-
await writeFile(join(OPENCODE_AGENT_DIR, file), outputContent);
|
|
219
|
-
console.log(` ✓ ${file}`);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
* Sync the skill as the primary orchestrator agent
|
|
225
|
-
*/
|
|
226
|
-
async function syncSkill(): Promise<void> {
|
|
227
|
-
console.log("Syncing skill as orchestrator...");
|
|
228
|
-
|
|
229
|
-
const skillDir = join(SKILLS_DIR, "questionably-ultrathink-skill");
|
|
230
|
-
const skillPath = join(skillDir, "SKILL.md");
|
|
231
|
-
|
|
232
|
-
if (!existsSync(skillPath)) {
|
|
233
|
-
console.log(" ⚠ No skill found, skipping orchestrator");
|
|
234
|
-
return;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
const content = await readFile(skillPath, "utf-8");
|
|
238
|
-
const { frontmatter, body } =
|
|
239
|
-
parseFrontmatter<ClaudeCodeSkillFrontmatter>(content);
|
|
240
|
-
|
|
241
|
-
// Build permission with task subagent permissions
|
|
242
|
-
const basePermissions = translateTools(frontmatter["allowed-tools"]) || {};
|
|
243
|
-
|
|
244
|
-
const openCodeFrontmatter: OpenCodeAgentFrontmatter = {
|
|
245
|
-
description: frontmatter.description.trim(),
|
|
246
|
-
mode: "primary",
|
|
247
|
-
model: "anthropic/claude-sonnet-4-20250514",
|
|
248
|
-
permission: {
|
|
249
|
-
...basePermissions,
|
|
250
|
-
task: "allow", // Allow invoking subagents
|
|
251
|
-
},
|
|
252
|
-
};
|
|
253
|
-
|
|
254
|
-
const translatedBody = translateBody(body);
|
|
255
|
-
const outputContent = formatWithFrontmatter(
|
|
256
|
-
openCodeFrontmatter,
|
|
257
|
-
translatedBody
|
|
258
|
-
);
|
|
259
|
-
|
|
260
|
-
await mkdir(OPENCODE_AGENT_DIR, { recursive: true });
|
|
261
|
-
await writeFile(
|
|
262
|
-
join(OPENCODE_AGENT_DIR, "questionably-ultrathink.md"),
|
|
263
|
-
outputContent
|
|
264
|
-
);
|
|
265
|
-
console.log(" ✓ questionably-ultrathink.md (orchestrator)");
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
/**
|
|
269
|
-
* Sync commands from commands/ to .opencode/command/
|
|
270
|
-
*/
|
|
271
|
-
async function syncCommands(): Promise<void> {
|
|
272
|
-
console.log("Syncing commands...");
|
|
273
|
-
await mkdir(OPENCODE_COMMAND_DIR, { recursive: true });
|
|
274
|
-
|
|
275
|
-
const files = await readdir(COMMANDS_DIR);
|
|
276
|
-
const mdFiles = files.filter((f) => f.endsWith(".md"));
|
|
277
|
-
|
|
278
|
-
for (const file of mdFiles) {
|
|
279
|
-
const content = await readFile(join(COMMANDS_DIR, file), "utf-8");
|
|
280
|
-
const { frontmatter, body } =
|
|
281
|
-
parseFrontmatter<ClaudeCodeCommandFrontmatter>(content);
|
|
282
|
-
|
|
283
|
-
const openCodeFrontmatter: OpenCodeCommandFrontmatter = {
|
|
284
|
-
description: frontmatter.description,
|
|
285
|
-
};
|
|
286
|
-
|
|
287
|
-
// Translate body and add agent switch directive for skill-invoking commands
|
|
288
|
-
let translatedBody = translateBody(body);
|
|
289
|
-
|
|
290
|
-
// If the command invokes the Skill tool, add switch directive
|
|
291
|
-
if (body.includes("Skill tool") || body.includes("skill:")) {
|
|
292
|
-
translatedBody = `Switch to the @questionably-ultrathink agent.\n\n${translatedBody}`;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
const outputContent = formatWithFrontmatter(
|
|
296
|
-
openCodeFrontmatter,
|
|
297
|
-
translatedBody
|
|
298
|
-
);
|
|
299
|
-
|
|
300
|
-
await writeFile(join(OPENCODE_COMMAND_DIR, file), outputContent);
|
|
301
|
-
console.log(` ✓ ${file}`);
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
// ============================================================================
|
|
306
|
-
// Main
|
|
307
|
-
// ============================================================================
|
|
308
|
-
|
|
309
|
-
async function main(): Promise<void> {
|
|
310
|
-
const isWatch = process.argv.includes("--watch");
|
|
311
|
-
|
|
312
|
-
console.log("OpenCode Sync Script");
|
|
313
|
-
console.log("====================\n");
|
|
314
|
-
|
|
315
|
-
if (isWatch) {
|
|
316
|
-
console.log("Watch mode not yet implemented. Running single sync.\n");
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
try {
|
|
320
|
-
// Clean output directories
|
|
321
|
-
console.log("Cleaning output directories...");
|
|
322
|
-
await cleanOutputDirs();
|
|
323
|
-
console.log(" ✓ Done\n");
|
|
324
|
-
|
|
325
|
-
// Sync all components
|
|
326
|
-
await syncAgents();
|
|
327
|
-
console.log("");
|
|
328
|
-
await syncSkill();
|
|
329
|
-
console.log("");
|
|
330
|
-
await syncCommands();
|
|
331
|
-
|
|
332
|
-
console.log("\n✅ Sync complete!");
|
|
333
|
-
console.log("\nGenerated files:");
|
|
334
|
-
console.log(" .opencode/agent/");
|
|
335
|
-
console.log(" - atom-of-thoughts.md");
|
|
336
|
-
console.log(" - chain-of-verification.md");
|
|
337
|
-
console.log(" - aot-recompute.md");
|
|
338
|
-
console.log(" - questionably-ultrathink.md (orchestrator)");
|
|
339
|
-
console.log(" .opencode/command/");
|
|
340
|
-
console.log(" - questionably-ultrathink.md");
|
|
341
|
-
console.log(" - decompose.md");
|
|
342
|
-
console.log(" - verify.md");
|
|
343
|
-
} catch (error) {
|
|
344
|
-
console.error("\n❌ Sync failed:", error);
|
|
345
|
-
process.exit(1);
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
main();
|