opencastle 0.8.0 → 0.8.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.
Files changed (87) hide show
  1. package/README.md +27 -0
  2. package/bin/cli.mjs +2 -0
  3. package/dist/cli/adapters/claude-code.d.ts +2 -5
  4. package/dist/cli/adapters/claude-code.d.ts.map +1 -1
  5. package/dist/cli/adapters/claude-code.js +12 -251
  6. package/dist/cli/adapters/claude-code.js.map +1 -1
  7. package/dist/cli/adapters/cursor.d.ts.map +1 -1
  8. package/dist/cli/adapters/cursor.js +3 -17
  9. package/dist/cli/adapters/cursor.js.map +1 -1
  10. package/dist/cli/adapters/frontmatter.d.ts +26 -0
  11. package/dist/cli/adapters/frontmatter.d.ts.map +1 -0
  12. package/dist/cli/adapters/frontmatter.js +40 -0
  13. package/dist/cli/adapters/frontmatter.js.map +1 -0
  14. package/dist/cli/adapters/index.d.ts +5 -0
  15. package/dist/cli/adapters/index.d.ts.map +1 -0
  16. package/dist/cli/adapters/index.js +9 -0
  17. package/dist/cli/adapters/index.js.map +1 -0
  18. package/dist/cli/adapters/opencode.d.ts +2 -5
  19. package/dist/cli/adapters/opencode.d.ts.map +1 -1
  20. package/dist/cli/adapters/opencode.js +12 -250
  21. package/dist/cli/adapters/opencode.js.map +1 -1
  22. package/dist/cli/adapters/single-file-base.d.ts +40 -0
  23. package/dist/cli/adapters/single-file-base.d.ts.map +1 -0
  24. package/dist/cli/adapters/single-file-base.js +246 -0
  25. package/dist/cli/adapters/single-file-base.js.map +1 -0
  26. package/dist/cli/dashboard.d.ts.map +1 -1
  27. package/dist/cli/dashboard.js +3 -2
  28. package/dist/cli/dashboard.js.map +1 -1
  29. package/dist/cli/detect.d.ts.map +1 -1
  30. package/dist/cli/detect.js +13 -11
  31. package/dist/cli/detect.js.map +1 -1
  32. package/dist/cli/doctor.d.ts +3 -0
  33. package/dist/cli/doctor.d.ts.map +1 -0
  34. package/dist/cli/doctor.js +205 -0
  35. package/dist/cli/doctor.js.map +1 -0
  36. package/dist/cli/init.d.ts.map +1 -1
  37. package/dist/cli/init.js +31 -19
  38. package/dist/cli/init.js.map +1 -1
  39. package/dist/cli/run/schema.d.ts +1 -5
  40. package/dist/cli/run/schema.d.ts.map +1 -1
  41. package/dist/cli/run/schema.js +6 -330
  42. package/dist/cli/run/schema.js.map +1 -1
  43. package/dist/cli/run.d.ts.map +1 -1
  44. package/dist/cli/run.js +14 -1
  45. package/dist/cli/run.js.map +1 -1
  46. package/dist/cli/types.d.ts +0 -5
  47. package/dist/cli/types.d.ts.map +1 -1
  48. package/dist/cli/update.d.ts.map +1 -1
  49. package/dist/cli/update.js +4 -17
  50. package/dist/cli/update.js.map +1 -1
  51. package/package.json +7 -2
  52. package/src/cli/adapters/claude-code.ts +13 -304
  53. package/src/cli/adapters/cursor.ts +3 -23
  54. package/src/cli/adapters/frontmatter.ts +47 -0
  55. package/src/cli/adapters/index.ts +13 -0
  56. package/src/cli/adapters/opencode.ts +12 -301
  57. package/src/cli/adapters/single-file-base.ts +320 -0
  58. package/src/cli/dashboard.ts +3 -2
  59. package/src/cli/detect.ts +19 -15
  60. package/src/cli/doctor.ts +235 -0
  61. package/src/cli/init.ts +31 -24
  62. package/src/cli/run/schema.ts +7 -365
  63. package/src/cli/run.ts +17 -1
  64. package/src/cli/types.ts +0 -6
  65. package/src/cli/update.ts +5 -23
  66. package/src/dashboard/dist/_astro/{index.CWVzbF4T.css → index.Bnq19_1M.css} +1 -1
  67. package/src/dashboard/dist/index.html +170 -11
  68. package/src/dashboard/node_modules/.vite/deps/_metadata.json +6 -6
  69. package/src/dashboard/seed-data/reviews.ndjson +6 -0
  70. package/src/dashboard/src/pages/index.astro +213 -10
  71. package/src/dashboard/src/styles/dashboard.css +196 -0
  72. package/src/orchestrator/agent-workflows/bug-fix.md +2 -2
  73. package/src/orchestrator/agent-workflows/data-pipeline.md +8 -8
  74. package/src/orchestrator/agent-workflows/database-migration.md +2 -2
  75. package/src/orchestrator/agent-workflows/feature-implementation.md +2 -2
  76. package/src/orchestrator/agent-workflows/performance-optimization.md +2 -2
  77. package/src/orchestrator/agent-workflows/refactoring.md +2 -2
  78. package/src/orchestrator/agent-workflows/schema-changes.md +2 -2
  79. package/src/orchestrator/agent-workflows/security-audit.md +2 -2
  80. package/src/orchestrator/agents/data-expert.agent.md +2 -2
  81. package/src/orchestrator/agents/researcher.agent.md +0 -16
  82. package/src/orchestrator/agents/team-lead.agent.md +17 -6
  83. package/src/orchestrator/customizations/AGENT-PERFORMANCE.md +1 -3
  84. package/src/orchestrator/prompts/bootstrap-customizations.prompt.md +1 -1
  85. package/src/orchestrator/skills/agent-hooks/SKILL.md +4 -2
  86. package/src/orchestrator/skills/self-improvement/SKILL.md +1 -1
  87. package/src/orchestrator/prompts/metrics-report.prompt.md +0 -144
@@ -1,10 +1,4 @@
1
- import { resolve, basename } from 'node:path'
2
- import { mkdir, writeFile, readdir, readFile, unlink, rm } from 'node:fs/promises'
3
- import { existsSync } from 'node:fs'
4
- import { copyDir, getOrchestratorRoot, getPluginsRoot, getPluginSkillEntries } from '../copy.js'
5
- import { scaffoldMcpConfig } from '../mcp.js'
6
- import { getExcludedSkills, getExcludedAgents, getCustomizationsTransform, getIncludedPluginIds } from '../stack-config.js'
7
- import type { CopyResults, ManagedPaths, RepoInfo, StackConfig } from '../types.js'
1
+ import { createSingleFileAdapter } from './single-file-base.js'
8
2
 
9
3
  /**
10
4
  * Claude Code adapter.
@@ -23,301 +17,16 @@ import type { CopyResults, ManagedPaths, RepoInfo, StackConfig } from '../types.
23
17
  */
24
18
 
25
19
  export const IDE_ID = 'claude-code'
26
- export const IDE_LABEL = 'Claude Code'
27
20
 
28
- // ─── Helpers ──────────────────────────────────────────────────────
29
-
30
- function stripFrontmatter(content: string): string {
31
- const m = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/)
32
- return m ? m[2].trim() : content.trim()
33
- }
34
-
35
- function parseFrontmatterMeta(content: string): Record<string, string> {
36
- const m = content.match(/^---\n([\s\S]*?)\n---/)
37
- if (!m) return {}
38
- const meta: Record<string, string> = {}
39
- for (const line of m[1].split('\n')) {
40
- const kv = line.match(/^(\w[\w-]*):\s*['"]?(.+?)['"]?\s*$/)
41
- if (kv) meta[kv[1]] = kv[2]
42
- }
43
- return meta
44
- }
45
-
46
- // ─── Install ──────────────────────────────────────────────────────
47
-
48
- export async function install(
49
- pkgRoot: string,
50
- projectRoot: string,
51
- stack?: StackConfig,
52
- repoInfo?: RepoInfo
53
- ): Promise<CopyResults> {
54
- const srcRoot = getOrchestratorRoot(pkgRoot)
55
- const results: CopyResults = { copied: [], skipped: [], created: [] }
56
-
57
- const excludedSkills = stack ? getExcludedSkills(stack) : new Set<string>()
58
- const excludedAgents = stack ? getExcludedAgents(stack) : new Set<string>()
59
-
60
- // 1. Build CLAUDE.md ← copilot-instructions + instructions/* + agent index + skill index
61
- const claudeMd = resolve(projectRoot, 'CLAUDE.md')
62
- if (!existsSync(claudeMd)) {
63
- const sections: string[] = []
64
-
65
- // Claude-specific header (not copilot-instructions.md verbatim)
66
- sections.push(
67
- '# Project Instructions\n\n' +
68
- 'All conventions, architecture, and project context are embedded below. ' +
69
- 'Skills are in `.claude/skills/` — read them when a task matches. ' +
70
- 'Agent definitions are in `.claude/agents/` — read the relevant file when adopting a persona.'
71
- )
72
-
73
- // Always-loaded instruction files
74
- const instDir = resolve(srcRoot, 'instructions')
75
- if (existsSync(instDir)) {
76
- for (const file of (await readdir(instDir)).sort()) {
77
- if (!file.endsWith('.md')) continue
78
- const content = await readFile(resolve(instDir, file), 'utf8')
79
- sections.push(
80
- `\n---\n\n<!-- Source: instructions/${file} -->\n\n${stripFrontmatter(content)}`
81
- )
82
- }
83
- }
84
-
85
- // Agent reference (so Claude can adopt personas)
86
- const agentsDir = resolve(srcRoot, 'agents')
87
- if (existsSync(agentsDir)) {
88
- const agentLines: string[] = ['\n---\n\n## Agent Definitions\n']
89
- agentLines.push(
90
- 'The following agent personas are available. Adopt the appropriate persona when asked.\n'
91
- )
92
- for (const file of (await readdir(agentsDir)).sort()) {
93
- if (!file.endsWith('.md')) continue
94
- if (excludedAgents.has(file)) continue
95
- const meta = parseFrontmatterMeta(
96
- await readFile(resolve(agentsDir, file), 'utf8')
97
- )
98
- const name = meta['name'] ?? basename(file, '.agent.md')
99
- const desc = meta['description'] ?? ''
100
- agentLines.push(`- **${name}**: ${desc}`)
101
- }
102
- agentLines.push(
103
- '\nFull agent definitions are in `.claude/agents/`. Read the relevant file when adopting a persona.'
104
- )
105
- sections.push(agentLines.join('\n'))
106
- }
107
-
108
- // Skill index
109
- const skillsDir = resolve(srcRoot, 'skills')
110
- if (existsSync(skillsDir)) {
111
- const skillLines: string[] = ['\n---\n\n## Available Skills\n']
112
- skillLines.push(
113
- 'Skills are on-demand knowledge files. Read the file when the task matches.\n'
114
- )
115
- const subdirs = (
116
- await readdir(skillsDir, { withFileTypes: true })
117
- ).filter((e) => e.isDirectory())
118
- for (const entry of subdirs.sort((a, b) =>
119
- a.name.localeCompare(b.name)
120
- )) {
121
- if (excludedSkills.has(entry.name)) continue
122
- const skillFile = resolve(skillsDir, entry.name, 'SKILL.md')
123
- if (!existsSync(skillFile)) continue
124
- const meta = parseFrontmatterMeta(await readFile(skillFile, 'utf8'))
125
- const desc = meta['description'] ?? ''
126
- skillLines.push(
127
- `- **${entry.name}** (\`.claude/skills/${entry.name}.md\`): ${desc}`
128
- )
129
- }
130
-
131
- // Plugin skills
132
- const pluginsRoot = getPluginsRoot(pkgRoot)
133
- const includedPlugins = stack ? getIncludedPluginIds(stack) : undefined
134
- const pluginEntries = await getPluginSkillEntries(pluginsRoot, includedPlugins)
135
- for (const { id, skillPath } of pluginEntries.sort((a, b) => a.id.localeCompare(b.id))) {
136
- const pluginMeta = parseFrontmatterMeta(await readFile(skillPath, 'utf8'))
137
- const pluginDesc = pluginMeta['description'] ?? ''
138
- skillLines.push(
139
- `- **${id}** (\`.claude/skills/${id}.md\`): ${pluginDesc}`
140
- )
141
- }
142
-
143
- sections.push(skillLines.join('\n'))
144
- }
145
-
146
- await writeFile(claudeMd, sections.join('\n') + '\n')
147
- results.created.push(claudeMd)
148
- } else {
149
- results.skipped.push(claudeMd)
150
- }
151
-
152
- const claudeDir = resolve(projectRoot, '.claude')
153
-
154
- // 2. Agent definitions → .claude/agents/
155
- const agentsDir = resolve(srcRoot, 'agents')
156
- if (existsSync(agentsDir)) {
157
- const destAgents = resolve(claudeDir, 'agents')
158
- await mkdir(destAgents, { recursive: true })
159
- for (const file of await readdir(agentsDir)) {
160
- if (!file.endsWith('.md')) continue
161
- if (excludedAgents.has(file)) continue
162
- const destPath = resolve(destAgents, file)
163
- if (existsSync(destPath)) {
164
- results.skipped.push(destPath)
165
- continue
166
- }
167
- const content = await readFile(resolve(agentsDir, file), 'utf8')
168
- await writeFile(destPath, stripFrontmatter(content) + '\n')
169
- results.created.push(destPath)
170
- }
171
- }
172
-
173
- // 3. Skills → .claude/skills/<name>.md
174
- const skillsDir = resolve(srcRoot, 'skills')
175
- if (existsSync(skillsDir)) {
176
- const destSkills = resolve(claudeDir, 'skills')
177
- await mkdir(destSkills, { recursive: true })
178
- const subdirs = (
179
- await readdir(skillsDir, { withFileTypes: true })
180
- ).filter((e) => e.isDirectory())
181
- for (const entry of subdirs) {
182
- if (excludedSkills.has(entry.name)) continue
183
- const skillFile = resolve(skillsDir, entry.name, 'SKILL.md')
184
- if (!existsSync(skillFile)) continue
185
- const destPath = resolve(destSkills, `${entry.name}.md`)
186
- if (existsSync(destPath)) {
187
- results.skipped.push(destPath)
188
- continue
189
- }
190
- const content = await readFile(skillFile, 'utf8')
191
- await writeFile(destPath, stripFrontmatter(content) + '\n')
192
- results.created.push(destPath)
193
- }
194
- }
195
-
196
- // 3b. Plugin skills → .claude/skills/<plugin-id>.md
197
- {
198
- const pluginsRoot = getPluginsRoot(pkgRoot)
199
- const includedPlugins = stack ? getIncludedPluginIds(stack) : undefined
200
- const pluginEntries = await getPluginSkillEntries(pluginsRoot, includedPlugins)
201
- const destSkills = resolve(claudeDir, 'skills')
202
- await mkdir(destSkills, { recursive: true })
203
- for (const { id, skillPath } of pluginEntries) {
204
- const destPath = resolve(destSkills, `${id}.md`)
205
- if (existsSync(destPath)) {
206
- results.skipped.push(destPath)
207
- continue
208
- }
209
- const content = await readFile(skillPath, 'utf8')
210
- await writeFile(destPath, stripFrontmatter(content) + '\n')
211
- results.created.push(destPath)
212
- }
213
- }
214
-
215
- // 4. Prompts → .claude/commands/<name>.md
216
- const promptDir = resolve(srcRoot, 'prompts')
217
- if (existsSync(promptDir)) {
218
- const destCmds = resolve(claudeDir, 'commands')
219
- await mkdir(destCmds, { recursive: true })
220
- for (const file of await readdir(promptDir)) {
221
- if (!file.endsWith('.md')) continue
222
- const name = basename(file, '.prompt.md') || basename(file, '.md')
223
- const destPath = resolve(destCmds, `${name}.md`)
224
- if (existsSync(destPath)) {
225
- results.skipped.push(destPath)
226
- continue
227
- }
228
- const content = await readFile(resolve(promptDir, file), 'utf8')
229
- await writeFile(destPath, stripFrontmatter(content) + '\n')
230
- results.created.push(destPath)
231
- }
232
- }
233
-
234
- // 5. Agent Workflows → .claude/commands/workflow-<name>.md
235
- const wfDir = resolve(srcRoot, 'agent-workflows')
236
- if (existsSync(wfDir)) {
237
- const destCmds = resolve(claudeDir, 'commands')
238
- await mkdir(destCmds, { recursive: true })
239
- for (const file of await readdir(wfDir)) {
240
- if (!file.endsWith('.md')) continue
241
- if (file === 'README.md') continue // Skip README — not a workflow
242
- const name = basename(file, '.md')
243
- const destPath = resolve(destCmds, `workflow-${name}.md`)
244
- if (existsSync(destPath)) {
245
- results.skipped.push(destPath)
246
- continue
247
- }
248
- const content = await readFile(resolve(wfDir, file), 'utf8')
249
- await writeFile(destPath, stripFrontmatter(content) + '\n')
250
- results.created.push(destPath)
251
- }
252
- }
253
-
254
- // 6. Customizations (scaffold once, pre-populated with stack choices)
255
- const custDir = resolve(srcRoot, 'customizations')
256
- if (existsSync(custDir)) {
257
- const destCust = resolve(claudeDir, 'customizations')
258
- const custTransform = stack ? getCustomizationsTransform(stack) : undefined
259
- const sub = await copyDir(custDir, destCust, { transform: custTransform })
260
- results.created.push(...sub.created)
261
- results.skipped.push(...sub.skipped)
262
- }
263
-
264
- // 7. MCP server config → .claude/mcp.json (scaffold once)
265
- const mcpResult = await scaffoldMcpConfig(
266
- projectRoot,
267
- '.claude/mcp.json',
268
- stack,
269
- repoInfo,
270
- 'claude-code'
271
- )
272
- results[mcpResult.action].push(mcpResult.path)
273
-
274
- return results
275
- }
276
-
277
- // ─── Update ───────────────────────────────────────────────────────
278
-
279
- export async function update(
280
- pkgRoot: string,
281
- projectRoot: string,
282
- stack?: StackConfig
283
- ): Promise<CopyResults> {
284
- const results: CopyResults = { copied: [], skipped: [], created: [] }
285
- const claudeDir = resolve(projectRoot, '.claude')
286
-
287
- // 1. Regenerate CLAUDE.md (overwrite)
288
- const claudeMd = resolve(projectRoot, 'CLAUDE.md')
289
- if (existsSync(claudeMd)) {
290
- await unlink(claudeMd)
291
- }
292
-
293
- // 2. Remove existing framework files so install() recreates them
294
- const frameworkDirs = ['agents', 'skills', 'commands']
295
- for (const dir of frameworkDirs) {
296
- const dirPath = resolve(claudeDir, dir)
297
- if (existsSync(dirPath)) {
298
- await rm(dirPath, { recursive: true })
299
- }
300
- }
301
-
302
- // 3. Re-run full install (CLAUDE.md + agents + skills + commands)
303
- const installResult = await install(pkgRoot, projectRoot, stack)
304
- // Everything install created is an "update" copy
305
- results.copied.push(...installResult.created)
306
- results.skipped.push(...installResult.skipped)
307
-
308
- return results
309
- }
310
-
311
- // ─── Managed paths ────────────────────────────────────────────────
312
-
313
- export function getManagedPaths(): ManagedPaths {
314
- return {
315
- framework: [
316
- 'CLAUDE.md',
317
- '.claude/agents/',
318
- '.claude/skills/',
319
- '.claude/commands/',
320
- ],
321
- customizable: ['.claude/customizations/', '.claude/mcp.json'],
322
- }
323
- }
21
+ const { install, update, getManagedPaths } = createSingleFileAdapter({
22
+ rootFile: 'CLAUDE.md',
23
+ dotDir: '.claude',
24
+ mcpConfigPath: '.claude/mcp.json',
25
+ mcpFormat: 'claude-code',
26
+ promptsDir: 'commands',
27
+ workflowsDir: 'commands',
28
+ workflowPrefix: 'workflow-',
29
+ frameworkDirs: ['agents', 'skills', 'commands'],
30
+ })
31
+
32
+ export { install, update, getManagedPaths }
@@ -5,6 +5,7 @@ import { copyDir, getOrchestratorRoot, removeDirIfExists, getPluginsRoot, getPlu
5
5
  import { scaffoldMcpConfig } from '../mcp.js'
6
6
  import { getExcludedSkills, getExcludedAgents, getCustomizationsTransform, getIncludedPluginIds } from '../stack-config.js'
7
7
  import type { CopyResults, ManagedPaths, RepoInfo, StackConfig } from '../types.js'
8
+ import { splitFrontmatter, parseFrontmatterString } from './frontmatter.js'
8
9
 
9
10
  /**
10
11
  * Cursor adapter.
@@ -25,27 +26,6 @@ export const IDE_LABEL = 'Cursor'
25
26
 
26
27
  // ─── Helpers ──────────────────────────────────────────────────────
27
28
 
28
- interface FrontmatterResult {
29
- frontmatter: string
30
- body: string
31
- }
32
-
33
- function stripFrontmatter(content: string): FrontmatterResult {
34
- const m = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/)
35
- return m
36
- ? { frontmatter: m[1], body: m[2] }
37
- : { frontmatter: '', body: content }
38
- }
39
-
40
- function parseFrontmatter(fm: string): Record<string, string> {
41
- const result: Record<string, string> = {}
42
- for (const line of fm.split('\n')) {
43
- const m = line.match(/^(\w[\w-]*):\s*['"]?(.+?)['"]?\s*$/)
44
- if (m) result[m[1]] = m[2]
45
- }
46
- return result
47
- }
48
-
49
29
  interface MdcOptions {
50
30
  description?: string
51
31
  globs?: string[]
@@ -82,8 +62,8 @@ async function convertFile(
82
62
  { alwaysApply = false, descriptionFallback = '' }: ConvertFileOptions = {}
83
63
  ): Promise<string> {
84
64
  const content = await readFile(srcPath, 'utf8')
85
- const { frontmatter, body } = stripFrontmatter(content)
86
- const meta = parseFrontmatter(frontmatter)
65
+ const { frontmatter, body } = splitFrontmatter(content)
66
+ const meta = parseFrontmatterString(frontmatter)
87
67
 
88
68
  // Description: frontmatter > fallback > first heading
89
69
  let description = meta['description'] ?? descriptionFallback
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Shared frontmatter utilities for IDE adapters.
3
+ */
4
+
5
+ export interface FrontmatterResult {
6
+ frontmatter: string
7
+ body: string
8
+ }
9
+
10
+ /**
11
+ * Split content into frontmatter and body.
12
+ * Frontmatter is the YAML block between `---` delimiters.
13
+ */
14
+ export function splitFrontmatter(content: string): FrontmatterResult {
15
+ const m = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/)
16
+ return m
17
+ ? { frontmatter: m[1], body: m[2] }
18
+ : { frontmatter: '', body: content }
19
+ }
20
+
21
+ /**
22
+ * Strip frontmatter and return only the body, trimmed.
23
+ */
24
+ export function stripFrontmatter(content: string): string {
25
+ return splitFrontmatter(content).body.trim()
26
+ }
27
+
28
+ /**
29
+ * Parse frontmatter YAML into a key-value record.
30
+ * Handles simple `key: value` and `key: 'value'` / `key: "value"` patterns.
31
+ */
32
+ export function parseFrontmatterMeta(content: string): Record<string, string> {
33
+ const { frontmatter } = splitFrontmatter(content)
34
+ return parseFrontmatterString(frontmatter)
35
+ }
36
+
37
+ /**
38
+ * Parse a raw frontmatter string (without delimiters) into key-value pairs.
39
+ */
40
+ export function parseFrontmatterString(fm: string): Record<string, string> {
41
+ const result: Record<string, string> = {}
42
+ for (const line of fm.split('\n')) {
43
+ const m = line.match(/^(\w[\w-]*):\s*['"]?(.+?)['"]?\s*$/)
44
+ if (m) result[m[1]] = m[2]
45
+ }
46
+ return result
47
+ }
@@ -0,0 +1,13 @@
1
+ import type { IdeAdapter } from '../types.js'
2
+
3
+ /** Lazy-loaded IDE adapters for init/update commands. */
4
+ export const IDE_ADAPTERS: Record<string, () => Promise<IdeAdapter>> = {
5
+ vscode: () => import('./vscode.js') as Promise<IdeAdapter>,
6
+ cursor: () => import('./cursor.js') as Promise<IdeAdapter>,
7
+ 'claude-code': () =>
8
+ import('./claude-code.js') as Promise<IdeAdapter>,
9
+ opencode: () =>
10
+ import('./opencode.js') as Promise<IdeAdapter>,
11
+ }
12
+
13
+ export const VALID_IDES = Object.keys(IDE_ADAPTERS)