opencastle 0.6.0 → 0.7.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/README.md +5 -4
- package/dist/cli/adapters/claude-code.d.ts.map +1 -1
- package/dist/cli/adapters/claude-code.js +30 -3
- package/dist/cli/adapters/claude-code.js.map +1 -1
- package/dist/cli/adapters/cursor.d.ts.map +1 -1
- package/dist/cli/adapters/cursor.js +27 -3
- package/dist/cli/adapters/cursor.js.map +1 -1
- package/dist/cli/adapters/opencode.d.ts +20 -0
- package/dist/cli/adapters/opencode.d.ts.map +1 -0
- package/dist/cli/adapters/opencode.js +265 -0
- package/dist/cli/adapters/opencode.js.map +1 -0
- package/dist/cli/adapters/vscode.d.ts.map +1 -1
- package/dist/cli/adapters/vscode.js +37 -6
- package/dist/cli/adapters/vscode.js.map +1 -1
- package/dist/cli/copy.d.ts +12 -0
- package/dist/cli/copy.d.ts.map +1 -1
- package/dist/cli/copy.js +27 -0
- package/dist/cli/copy.js.map +1 -1
- package/dist/cli/detect.d.ts +1 -1
- package/dist/cli/detect.js +21 -15
- package/dist/cli/detect.js.map +1 -1
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +143 -94
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/manifest.d.ts +1 -1
- package/dist/cli/manifest.d.ts.map +1 -1
- package/dist/cli/manifest.js +2 -1
- package/dist/cli/manifest.js.map +1 -1
- package/dist/cli/mcp.d.ts +6 -6
- package/dist/cli/mcp.d.ts.map +1 -1
- package/dist/cli/mcp.js +104 -33
- package/dist/cli/mcp.js.map +1 -1
- package/dist/cli/prompt.d.ts +19 -0
- package/dist/cli/prompt.d.ts.map +1 -1
- package/dist/cli/prompt.js +143 -0
- package/dist/cli/prompt.js.map +1 -1
- package/dist/cli/stack-config.d.ts +23 -0
- package/dist/cli/stack-config.d.ts.map +1 -1
- package/dist/cli/stack-config.js +128 -124
- package/dist/cli/stack-config.js.map +1 -1
- package/dist/cli/types.d.ts +26 -9
- package/dist/cli/types.d.ts.map +1 -1
- package/dist/cli/types.js +26 -1
- package/dist/cli/types.js.map +1 -1
- package/dist/cli/update.d.ts.map +1 -1
- package/dist/cli/update.js +60 -19
- package/dist/cli/update.js.map +1 -1
- package/dist/orchestrator/plugins/chrome-devtools/config.d.ts +3 -0
- package/dist/orchestrator/plugins/chrome-devtools/config.d.ts.map +1 -0
- package/dist/orchestrator/plugins/chrome-devtools/config.js +28 -0
- package/dist/orchestrator/plugins/chrome-devtools/config.js.map +1 -0
- package/dist/orchestrator/plugins/contentful/config.d.ts +3 -0
- package/dist/orchestrator/plugins/contentful/config.d.ts.map +1 -0
- package/dist/orchestrator/plugins/contentful/config.js +48 -0
- package/dist/orchestrator/plugins/contentful/config.js.map +1 -0
- package/dist/orchestrator/plugins/convex/config.d.ts +3 -0
- package/dist/orchestrator/plugins/convex/config.d.ts.map +1 -0
- package/dist/orchestrator/plugins/convex/config.js +32 -0
- package/dist/orchestrator/plugins/convex/config.js.map +1 -0
- package/dist/orchestrator/plugins/index.d.ts +28 -0
- package/dist/orchestrator/plugins/index.d.ts.map +1 -0
- package/dist/orchestrator/plugins/index.js +63 -0
- package/dist/orchestrator/plugins/index.js.map +1 -0
- package/dist/orchestrator/plugins/jira/config.d.ts +3 -0
- package/dist/orchestrator/plugins/jira/config.d.ts.map +1 -0
- package/dist/orchestrator/plugins/jira/config.js +29 -0
- package/dist/orchestrator/plugins/jira/config.js.map +1 -0
- package/dist/orchestrator/plugins/linear/config.d.ts +3 -0
- package/dist/orchestrator/plugins/linear/config.d.ts.map +1 -0
- package/dist/orchestrator/plugins/linear/config.js +33 -0
- package/dist/orchestrator/plugins/linear/config.js.map +1 -0
- package/dist/orchestrator/plugins/nx/config.d.ts +3 -0
- package/dist/orchestrator/plugins/nx/config.d.ts.map +1 -0
- package/dist/orchestrator/plugins/nx/config.js +28 -0
- package/dist/orchestrator/plugins/nx/config.js.map +1 -0
- package/dist/orchestrator/plugins/sanity/config.d.ts +3 -0
- package/dist/orchestrator/plugins/sanity/config.d.ts.map +1 -0
- package/dist/orchestrator/plugins/sanity/config.js +43 -0
- package/dist/orchestrator/plugins/sanity/config.js.map +1 -0
- package/dist/orchestrator/plugins/slack/config.d.ts +3 -0
- package/dist/orchestrator/plugins/slack/config.d.ts.map +1 -0
- package/dist/orchestrator/plugins/slack/config.js +34 -0
- package/dist/orchestrator/plugins/slack/config.js.map +1 -0
- package/dist/orchestrator/plugins/strapi/config.d.ts +3 -0
- package/dist/orchestrator/plugins/strapi/config.d.ts.map +1 -0
- package/dist/orchestrator/plugins/strapi/config.js +40 -0
- package/dist/orchestrator/plugins/strapi/config.js.map +1 -0
- package/dist/orchestrator/plugins/supabase/config.d.ts +3 -0
- package/dist/orchestrator/plugins/supabase/config.d.ts.map +1 -0
- package/dist/orchestrator/plugins/supabase/config.js +33 -0
- package/dist/orchestrator/plugins/supabase/config.js.map +1 -0
- package/dist/orchestrator/plugins/teams/config.d.ts +3 -0
- package/dist/orchestrator/plugins/teams/config.d.ts.map +1 -0
- package/dist/orchestrator/plugins/teams/config.js +43 -0
- package/dist/orchestrator/plugins/teams/config.js.map +1 -0
- package/dist/orchestrator/plugins/types.d.ts +61 -0
- package/dist/orchestrator/plugins/types.d.ts.map +1 -0
- package/dist/orchestrator/plugins/types.js +2 -0
- package/dist/orchestrator/plugins/types.js.map +1 -0
- package/dist/orchestrator/plugins/vercel/config.d.ts +3 -0
- package/dist/orchestrator/plugins/vercel/config.d.ts.map +1 -0
- package/dist/orchestrator/plugins/vercel/config.js +32 -0
- package/dist/orchestrator/plugins/vercel/config.js.map +1 -0
- package/package.json +1 -1
- package/src/cli/adapters/claude-code.ts +36 -4
- package/src/cli/adapters/cursor.ts +42 -4
- package/src/cli/adapters/opencode.ts +320 -0
- package/src/cli/adapters/vscode.ts +40 -8
- package/src/cli/copy.ts +32 -0
- package/src/cli/detect.ts +17 -17
- package/src/cli/init.ts +157 -99
- package/src/cli/manifest.ts +2 -1
- package/src/cli/mcp.ts +129 -50
- package/src/cli/prompt.ts +176 -0
- package/src/cli/stack-config.ts +174 -145
- package/src/cli/types.ts +39 -8
- package/src/cli/update.ts +71 -20
- package/src/dashboard/node_modules/.vite/deps/_metadata.json +6 -6
- package/src/orchestrator/agent-workflows/README.md +1 -1
- package/src/orchestrator/agent-workflows/bug-fix.md +12 -12
- package/src/orchestrator/agent-workflows/data-pipeline.md +21 -20
- package/src/orchestrator/agent-workflows/database-migration.md +11 -11
- package/src/orchestrator/agent-workflows/feature-implementation.md +10 -10
- package/src/orchestrator/agent-workflows/performance-optimization.md +6 -6
- package/src/orchestrator/agent-workflows/refactoring.md +10 -10
- package/src/orchestrator/agent-workflows/schema-changes.md +8 -8
- package/src/orchestrator/agent-workflows/security-audit.md +12 -12
- package/src/orchestrator/agent-workflows/shared-delivery-phase.md +5 -5
- package/src/orchestrator/agents/api-designer.agent.md +1 -1
- package/src/orchestrator/agents/architect.agent.md +2 -2
- package/src/orchestrator/agents/content-engineer.agent.md +3 -3
- package/src/orchestrator/agents/copywriter.agent.md +2 -2
- package/src/orchestrator/agents/data-expert.agent.md +6 -6
- package/src/orchestrator/agents/database-engineer.agent.md +3 -3
- package/src/orchestrator/agents/developer.agent.md +4 -4
- package/src/orchestrator/agents/devops-expert.agent.md +5 -5
- package/src/orchestrator/agents/documentation-writer.agent.md +1 -1
- package/src/orchestrator/agents/performance-expert.agent.md +2 -2
- package/src/orchestrator/agents/release-manager.agent.md +4 -4
- package/src/orchestrator/agents/researcher.agent.md +3 -3
- package/src/orchestrator/agents/reviewer.agent.md +1 -1
- package/src/orchestrator/agents/security-expert.agent.md +4 -4
- package/src/orchestrator/agents/seo-specialist.agent.md +2 -2
- package/src/orchestrator/agents/team-lead.agent.md +56 -38
- package/src/orchestrator/agents/testing-expert.agent.md +5 -5
- package/src/orchestrator/agents/ui-ux-expert.agent.md +6 -6
- package/src/orchestrator/copilot-instructions.md +1 -1
- package/src/orchestrator/customizations/AGENT-FAILURES.md +1 -1
- package/src/orchestrator/customizations/AGENT-PERFORMANCE.md +12 -12
- package/src/orchestrator/customizations/DISPUTES.md +5 -5
- package/src/orchestrator/customizations/KNOWN-ISSUES.md +30 -0
- package/src/orchestrator/customizations/LESSONS-LEARNED.md +7 -7
- package/src/orchestrator/customizations/README.md +5 -2
- package/src/orchestrator/customizations/agents/agent-registry.md +1 -1
- package/src/orchestrator/customizations/agents/skill-matrix.md +12 -7
- package/src/orchestrator/customizations/logs/README.md +1 -1
- package/src/orchestrator/customizations/project/decisions.md +31 -0
- package/src/orchestrator/customizations/project/docs-structure.md +16 -5
- package/src/orchestrator/customizations/project/roadmap.md +24 -0
- package/src/orchestrator/customizations/project/tracker-config.md +1 -1
- package/src/orchestrator/customizations/stack/cms-config.md +1 -1
- package/src/orchestrator/customizations/stack/notifications-config.md +1 -1
- package/src/orchestrator/instructions/ai-optimization.instructions.md +2 -2
- package/src/orchestrator/instructions/general.instructions.md +102 -40
- package/src/orchestrator/{skills/browser-testing → plugins/chrome-devtools}/SKILL.md +1 -1
- package/src/orchestrator/plugins/chrome-devtools/config.ts +29 -0
- package/src/orchestrator/{skills/contentful-cms → plugins/contentful}/SKILL.md +1 -1
- package/src/orchestrator/plugins/contentful/config.ts +49 -0
- package/src/orchestrator/{skills/convex-database → plugins/convex}/SKILL.md +1 -1
- package/src/orchestrator/plugins/convex/config.ts +33 -0
- package/src/orchestrator/plugins/index.ts +85 -0
- package/src/orchestrator/{skills/jira-management → plugins/jira}/SKILL.md +3 -3
- package/src/orchestrator/plugins/jira/config.ts +30 -0
- package/src/orchestrator/{skills/task-management → plugins/linear}/SKILL.md +3 -3
- package/src/orchestrator/plugins/linear/config.ts +34 -0
- package/src/orchestrator/{skills/nx-workspace → plugins/nx}/SKILL.md +1 -1
- package/src/orchestrator/plugins/nx/config.ts +29 -0
- package/src/orchestrator/{skills/sanity-cms → plugins/sanity}/SKILL.md +1 -1
- package/src/orchestrator/plugins/sanity/config.ts +44 -0
- package/src/orchestrator/{skills/slack-notifications → plugins/slack}/SKILL.md +2 -2
- package/src/orchestrator/plugins/slack/config.ts +35 -0
- package/src/orchestrator/{skills/strapi-cms → plugins/strapi}/SKILL.md +1 -1
- package/src/orchestrator/plugins/strapi/config.ts +41 -0
- package/src/orchestrator/{skills/supabase-database → plugins/supabase}/SKILL.md +1 -1
- package/src/orchestrator/plugins/supabase/config.ts +34 -0
- package/src/orchestrator/{skills/teams-notifications → plugins/teams}/SKILL.md +2 -2
- package/src/orchestrator/plugins/teams/config.ts +44 -0
- package/src/orchestrator/plugins/types.ts +79 -0
- package/src/orchestrator/plugins/vercel/config.ts +33 -0
- package/src/orchestrator/prompts/bootstrap-customizations.prompt.md +8 -8
- package/src/orchestrator/prompts/brainstorm.prompt.md +3 -3
- package/src/orchestrator/prompts/bug-fix.prompt.md +18 -18
- package/src/orchestrator/prompts/create-skill.prompt.md +50 -32
- package/src/orchestrator/prompts/generate-task-spec.prompt.md +3 -3
- package/src/orchestrator/prompts/implement-feature.prompt.md +26 -26
- package/src/orchestrator/prompts/metrics-report.prompt.md +11 -11
- package/src/orchestrator/prompts/quick-refinement.prompt.md +16 -16
- package/src/orchestrator/prompts/resolve-pr-comments.prompt.md +2 -2
- package/src/orchestrator/skills/accessibility-standards/SKILL.md +1 -1
- package/src/orchestrator/skills/agent-hooks/SKILL.md +27 -18
- package/src/orchestrator/skills/agent-memory/SKILL.md +7 -7
- package/src/orchestrator/skills/api-patterns/SKILL.md +6 -6
- package/src/orchestrator/skills/code-commenting/SKILL.md +1 -1
- package/src/orchestrator/skills/context-map/SKILL.md +4 -4
- package/src/orchestrator/skills/data-engineering/SKILL.md +7 -4
- package/src/orchestrator/skills/deployment-infrastructure/SKILL.md +2 -2
- package/src/orchestrator/skills/documentation-standards/SKILL.md +1 -1
- package/src/orchestrator/skills/fast-review/SKILL.md +3 -3
- package/src/orchestrator/skills/frontend-design/SKILL.md +1 -1
- package/src/orchestrator/skills/memory-merger/SKILL.md +8 -8
- package/src/orchestrator/skills/nextjs-patterns/SKILL.md +1 -1
- package/src/orchestrator/skills/panel-majority-vote/SKILL.md +2 -2
- package/src/orchestrator/skills/panel-majority-vote/panel-report.template.md +1 -1
- package/src/orchestrator/skills/performance-optimization/SKILL.md +1 -1
- package/src/orchestrator/skills/react-development/SKILL.md +3 -3
- package/src/orchestrator/skills/security-hardening/SKILL.md +27 -27
- package/src/orchestrator/skills/self-improvement/SKILL.md +14 -13
- package/src/orchestrator/skills/seo-patterns/SKILL.md +1 -1
- package/src/orchestrator/skills/session-checkpoints/SKILL.md +19 -19
- package/src/orchestrator/skills/team-lead-reference/SKILL.md +9 -9
- package/src/orchestrator/skills/testing-workflow/SKILL.md +13 -13
- package/src/orchestrator/skills/validation-gates/SKILL.md +8 -15
- package/src/orchestrator/mcp.json +0 -69
package/src/cli/init.ts
CHANGED
|
@@ -1,19 +1,30 @@
|
|
|
1
1
|
import { resolve } from 'node:path'
|
|
2
2
|
import { readFile, unlink } from 'node:fs/promises'
|
|
3
3
|
import { existsSync } from 'node:fs'
|
|
4
|
-
import {
|
|
4
|
+
import { multiselect, confirm, closePrompts, c } from './prompt.js'
|
|
5
5
|
import { readManifest, writeManifest, createManifest } from './manifest.js'
|
|
6
6
|
import { removeDirIfExists } from './copy.js'
|
|
7
7
|
import { updateGitignore } from './gitignore.js'
|
|
8
8
|
import { getRequiredMcpEnvVars } from './stack-config.js'
|
|
9
|
+
import { TECH_PLUGINS, TEAM_PLUGINS } from '../orchestrator/plugins/index.js'
|
|
9
10
|
import { detectRepoInfo, mergeStackIntoRepoInfo, formatRepoInfo } from './detect.js'
|
|
10
|
-
import type { CliContext, IdeAdapter,
|
|
11
|
+
import type { CliContext, IdeAdapter, IdeChoice, TechTool, TeamTool, StackConfig } from './types.js'
|
|
11
12
|
|
|
12
13
|
const ADAPTERS: Record<string, () => Promise<IdeAdapter>> = {
|
|
13
14
|
vscode: () => import('./adapters/vscode.js') as Promise<IdeAdapter>,
|
|
14
15
|
cursor: () => import('./adapters/cursor.js') as Promise<IdeAdapter>,
|
|
15
16
|
'claude-code': () =>
|
|
16
17
|
import('./adapters/claude-code.js') as Promise<IdeAdapter>,
|
|
18
|
+
opencode: () =>
|
|
19
|
+
import('./adapters/opencode.js') as Promise<IdeAdapter>,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** IDE display labels */
|
|
23
|
+
const IDE_DISPLAY: Record<IdeChoice, string> = {
|
|
24
|
+
vscode: 'VS Code',
|
|
25
|
+
cursor: 'Cursor',
|
|
26
|
+
'claude-code': 'Claude Code',
|
|
27
|
+
opencode: 'OpenCode',
|
|
17
28
|
}
|
|
18
29
|
|
|
19
30
|
export default async function init({ pkgRoot, args }: CliContext): Promise<void> {
|
|
@@ -25,7 +36,7 @@ export default async function init({ pkgRoot, args }: CliContext): Promise<void>
|
|
|
25
36
|
let isReinit = false
|
|
26
37
|
if (existing) {
|
|
27
38
|
const proceed = await confirm(
|
|
28
|
-
`OpenCastle already installed (v${existing.version}
|
|
39
|
+
`OpenCastle already installed (v${existing.version}). Re-initialize?`,
|
|
29
40
|
false
|
|
30
41
|
)
|
|
31
42
|
if (!proceed) {
|
|
@@ -39,91 +50,117 @@ export default async function init({ pkgRoot, args }: CliContext): Promise<void>
|
|
|
39
50
|
await readFile(resolve(pkgRoot, 'package.json'), 'utf8')
|
|
40
51
|
) as { version: string }
|
|
41
52
|
|
|
42
|
-
console.log(`\n 🏰 OpenCastle v${pkg.version}`)
|
|
53
|
+
console.log(`\n 🏰 ${c.bold('OpenCastle')} ${c.dim(`v${pkg.version}`)}`)
|
|
43
54
|
console.log(
|
|
44
|
-
'
|
|
55
|
+
` ${c.dim('Multi-agent orchestration framework for AI coding assistants')}\n`
|
|
45
56
|
)
|
|
46
57
|
|
|
47
58
|
// ── Repo research ───────────────────────────────────────────────
|
|
48
|
-
console.log('
|
|
59
|
+
console.log(` ${c.dim('Scanning repository...')}`)
|
|
49
60
|
const repoInfo = await detectRepoInfo(projectRoot)
|
|
50
61
|
const summary = formatRepoInfo(repoInfo)
|
|
51
62
|
if (summary) {
|
|
52
|
-
console.log('
|
|
63
|
+
console.log(` ${c.green('Detected:')}\n` + summary + '\n')
|
|
53
64
|
} else {
|
|
54
|
-
console.log('
|
|
65
|
+
console.log(` ${c.dim('No tooling detected (empty project?)')}\n`)
|
|
55
66
|
}
|
|
56
67
|
|
|
57
|
-
// ──
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
68
|
+
// ── IDEs (multiselect, at least 1) ─────────────────────────────
|
|
69
|
+
console.log(` ${c.bold('── IDEs ──────────────────────────────────────')}`)
|
|
70
|
+
let ides: string[] = []
|
|
71
|
+
while (ides.length === 0) {
|
|
72
|
+
ides = await multiselect('Which IDEs do you use?', [
|
|
73
|
+
{
|
|
74
|
+
label: 'VS Code',
|
|
75
|
+
hint: 'GitHub Copilot agents, instructions, skills',
|
|
76
|
+
value: 'vscode',
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
label: 'Cursor',
|
|
80
|
+
hint: '.cursorrules & .cursor/rules/*.mdc',
|
|
81
|
+
value: 'cursor',
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
label: 'Claude Code',
|
|
85
|
+
hint: 'CLAUDE.md & .claude/ commands, skills',
|
|
86
|
+
value: 'claude-code',
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
label: 'OpenCode',
|
|
90
|
+
hint: 'AGENTS.md & opencode.json',
|
|
91
|
+
value: 'opencode',
|
|
92
|
+
},
|
|
93
|
+
])
|
|
94
|
+
if (ides.length === 0) {
|
|
95
|
+
console.log(` ${c.yellow('Please select at least one IDE.')}`)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
83
98
|
|
|
84
|
-
// ──
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
99
|
+
// ── Tech Tools (multiselect, 0-N) ──────────────────────────────
|
|
100
|
+
// Pre-select tools already detected in the repo
|
|
101
|
+
const detectedTools = new Set([
|
|
102
|
+
...(repoInfo.cms ?? []),
|
|
103
|
+
...(repoInfo.databases ?? []),
|
|
104
|
+
...(repoInfo.deployment ?? []),
|
|
105
|
+
...(repoInfo.monorepo ? [repoInfo.monorepo] : []),
|
|
89
106
|
])
|
|
90
107
|
|
|
91
|
-
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
108
|
+
console.log(` ${c.bold('── Tech Tools ────────────────────────────────')}`)
|
|
109
|
+
const techTools = await multiselect('Which tools does your project use?',
|
|
110
|
+
TECH_PLUGINS.map((p) => ({
|
|
111
|
+
label: p.label,
|
|
112
|
+
hint: p.hint,
|
|
113
|
+
value: p.id,
|
|
114
|
+
...((p.preselected || detectedTools.has(p.id)) && { selected: true }),
|
|
115
|
+
}))
|
|
116
|
+
)
|
|
97
117
|
|
|
98
|
-
// ──
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
118
|
+
// ── Team Tools (multiselect, 0-N) ──────────────────────────────
|
|
119
|
+
console.log(` ${c.bold('── Team Tools ────────────────────────────────')}`)
|
|
120
|
+
const teamTools = await multiselect('Which team tools do you use?',
|
|
121
|
+
TEAM_PLUGINS.map((p) => ({
|
|
122
|
+
label: p.label,
|
|
123
|
+
hint: p.hint,
|
|
124
|
+
value: p.id,
|
|
125
|
+
...(p.preselected && { selected: true }),
|
|
126
|
+
}))
|
|
127
|
+
)
|
|
104
128
|
|
|
105
|
-
const stack: StackConfig = {
|
|
129
|
+
const stack: StackConfig = {
|
|
130
|
+
ides: ides as IdeChoice[],
|
|
131
|
+
techTools: techTools as TechTool[],
|
|
132
|
+
teamTools: teamTools as TeamTool[],
|
|
133
|
+
}
|
|
106
134
|
|
|
107
135
|
// ── Merge user choices into detected info ────────────────────
|
|
108
136
|
const combinedRepoInfo = mergeStackIntoRepoInfo(repoInfo, stack)
|
|
109
137
|
|
|
110
|
-
|
|
111
|
-
console.log(
|
|
138
|
+
const ideNames = ides.map((id) => IDE_DISPLAY[id as IdeChoice]).join(', ')
|
|
139
|
+
console.log(`\n Installing for ${c.cyan(ideNames)}...`)
|
|
140
|
+
if (techTools.length > 0) {
|
|
141
|
+
console.log(` Tech: ${c.green(techTools.join(', '))}`)
|
|
142
|
+
}
|
|
143
|
+
if (teamTools.length > 0) {
|
|
144
|
+
console.log(` Team: ${c.green(teamTools.join(', '))}`)
|
|
145
|
+
}
|
|
146
|
+
console.log()
|
|
112
147
|
|
|
113
148
|
// ── Dry run ─────────────────────────────────────────────────────
|
|
114
149
|
if (dryRun) {
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
|
|
150
|
+
for (const ide of ides) {
|
|
151
|
+
const adapter = await ADAPTERS[ide]()
|
|
152
|
+
const managed = adapter.getManagedPaths()
|
|
153
|
+
console.log(` ${c.dim(`[dry-run] ${IDE_DISPLAY[ide as IdeChoice]} files:`)}\n`)
|
|
154
|
+
for (const p of managed.framework) {
|
|
155
|
+
console.log(` ${c.green('+')} ${p}`)
|
|
156
|
+
}
|
|
157
|
+
for (const p of managed.customizable) {
|
|
158
|
+
console.log(` ${c.green('+')} ${p}`)
|
|
159
|
+
}
|
|
123
160
|
}
|
|
124
|
-
console.log(` + .opencastle.json`)
|
|
125
|
-
console.log(` + .gitignore (OpenCastle entries)`)
|
|
126
|
-
console.log(
|
|
161
|
+
console.log(` ${c.green('+')} .opencastle.json`)
|
|
162
|
+
console.log(` ${c.green('+')} .gitignore (OpenCastle entries)`)
|
|
163
|
+
console.log(`\n ${c.dim('No files were written.')}\n`)
|
|
127
164
|
closePrompts()
|
|
128
165
|
return
|
|
129
166
|
}
|
|
@@ -139,11 +176,12 @@ export default async function init({ pkgRoot, args }: CliContext): Promise<void>
|
|
|
139
176
|
await unlink(fullPath)
|
|
140
177
|
}
|
|
141
178
|
}
|
|
142
|
-
// Remove MCP
|
|
179
|
+
// Remove MCP configs so they get regenerated with new stack
|
|
143
180
|
const mcpCandidates = [
|
|
144
181
|
'.vscode/mcp.json',
|
|
145
182
|
'.cursor/mcp.json',
|
|
146
183
|
'.claude/mcp.json',
|
|
184
|
+
'opencode.json',
|
|
147
185
|
]
|
|
148
186
|
for (const mcpPath of mcpCandidates) {
|
|
149
187
|
const fullPath = resolve(projectRoot, mcpPath)
|
|
@@ -153,70 +191,90 @@ export default async function init({ pkgRoot, args }: CliContext): Promise<void>
|
|
|
153
191
|
}
|
|
154
192
|
}
|
|
155
193
|
|
|
156
|
-
// ── Run
|
|
157
|
-
|
|
158
|
-
|
|
194
|
+
// ── Run adapters for each selected IDE ──────────────────────────
|
|
195
|
+
let totalCreated = 0
|
|
196
|
+
let totalSkipped = 0
|
|
197
|
+
const allManagedPaths = { framework: [] as string[], customizable: [] as string[] }
|
|
198
|
+
|
|
199
|
+
for (const ide of ides) {
|
|
200
|
+
const adapter = await ADAPTERS[ide]()
|
|
201
|
+
const results = await adapter.install(pkgRoot, projectRoot, stack, combinedRepoInfo)
|
|
202
|
+
totalCreated += results.created.length
|
|
203
|
+
totalSkipped += results.skipped.length
|
|
204
|
+
|
|
205
|
+
const managed = adapter.getManagedPaths()
|
|
206
|
+
allManagedPaths.framework.push(...managed.framework)
|
|
207
|
+
allManagedPaths.customizable.push(...managed.customizable)
|
|
208
|
+
}
|
|
159
209
|
|
|
160
210
|
// ── Write manifest ──────────────────────────────────────────────
|
|
161
|
-
const manifest = createManifest(pkg.version,
|
|
162
|
-
manifest.managedPaths =
|
|
211
|
+
const manifest = createManifest(pkg.version, ides[0], ides)
|
|
212
|
+
manifest.managedPaths = allManagedPaths
|
|
163
213
|
manifest.stack = stack
|
|
164
214
|
manifest.repoInfo = combinedRepoInfo
|
|
165
215
|
await writeManifest(projectRoot, manifest)
|
|
166
216
|
|
|
167
217
|
// ── Update .gitignore ───────────────────────────────────────────
|
|
168
|
-
const
|
|
169
|
-
const gitignoreResult = await updateGitignore(projectRoot, managedPaths)
|
|
218
|
+
const gitignoreResult = await updateGitignore(projectRoot, allManagedPaths)
|
|
170
219
|
|
|
171
220
|
// ── Summary ─────────────────────────────────────────────────────
|
|
172
|
-
|
|
173
|
-
const skipped = results.skipped.length
|
|
174
|
-
|
|
175
|
-
console.log(` ✓ Created ${created} files`)
|
|
221
|
+
console.log(` ${c.green('✓')} Created ${c.bold(String(totalCreated))} files`)
|
|
176
222
|
if (gitignoreResult === 'created') {
|
|
177
|
-
console.log('
|
|
223
|
+
console.log(` ${c.green('✓')} Created .gitignore with OpenCastle entries`)
|
|
178
224
|
} else if (gitignoreResult === 'updated') {
|
|
179
|
-
console.log('
|
|
225
|
+
console.log(` ${c.green('✓')} Updated .gitignore with OpenCastle entries`)
|
|
180
226
|
}
|
|
181
|
-
if (
|
|
182
|
-
console.log(` → Skipped ${
|
|
227
|
+
if (totalSkipped > 0) {
|
|
228
|
+
console.log(` ${c.dim('→')} Skipped ${totalSkipped} existing files`)
|
|
183
229
|
}
|
|
184
230
|
|
|
185
231
|
// ── Env var notice ──────────────────────────────────────────────
|
|
186
232
|
const envVars = getRequiredMcpEnvVars(stack, combinedRepoInfo)
|
|
187
233
|
if (envVars.length > 0) {
|
|
188
|
-
console.log(`\n ⚠ Required environment variables for MCP servers:\n`)
|
|
234
|
+
console.log(`\n ${c.yellow('⚠')} Required environment variables for MCP servers:\n`)
|
|
189
235
|
for (const { envVar, hint } of envVars) {
|
|
190
|
-
console.log(` ${envVar}`)
|
|
191
|
-
console.log(` └ ${hint}\n`)
|
|
236
|
+
console.log(` ${c.bold(envVar)}`)
|
|
237
|
+
console.log(` ${c.dim('└')} ${c.dim(hint)}\n`)
|
|
192
238
|
}
|
|
193
239
|
}
|
|
194
240
|
|
|
195
241
|
// ── OAuth setup guides ────────────────────────────────────────
|
|
196
|
-
if (
|
|
197
|
-
console.log(` 📖 Slack MCP requires a Slack App with a bot token.`)
|
|
198
|
-
console.log(` Setup guide: https://www.opencastle.dev/guides/slack
|
|
242
|
+
if (teamTools.includes('slack')) {
|
|
243
|
+
console.log(` ${c.cyan('📖')} Slack MCP requires a Slack App with a bot token.`)
|
|
244
|
+
console.log(` Setup guide: ${c.cyan('https://www.opencastle.dev/guides/plugins#slack')}\n`)
|
|
199
245
|
}
|
|
200
246
|
|
|
201
|
-
console.log(`\n Next steps
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
247
|
+
console.log(`\n ${c.bold('Next steps:')}`)
|
|
248
|
+
|
|
249
|
+
let step = 0
|
|
250
|
+
// Reload window messages for relevant IDEs
|
|
251
|
+
const needsReload = ides.filter((id) => ['vscode', 'cursor'].includes(id))
|
|
252
|
+
if (needsReload.length > 0) {
|
|
253
|
+
step++
|
|
254
|
+
if (needsReload.includes('vscode')) {
|
|
255
|
+
console.log(
|
|
256
|
+
` ${step}. ${c.yellow('Reload VS Code window')} (Cmd+Shift+P → "Developer: Reload Window")`
|
|
257
|
+
)
|
|
258
|
+
}
|
|
259
|
+
if (needsReload.includes('cursor')) {
|
|
260
|
+
console.log(
|
|
261
|
+
` ${step}. ${c.yellow('Reload Cursor window')} to pick up the new rule files`
|
|
262
|
+
)
|
|
263
|
+
}
|
|
210
264
|
}
|
|
265
|
+
|
|
211
266
|
if (envVars.length > 0) {
|
|
267
|
+
step++
|
|
212
268
|
console.log(
|
|
213
|
-
`
|
|
269
|
+
` ${step}. Set the environment variable${envVars.length > 1 ? 's' : ''} listed above`
|
|
214
270
|
)
|
|
215
271
|
}
|
|
272
|
+
step++
|
|
216
273
|
console.log(
|
|
217
|
-
` ${
|
|
274
|
+
` ${step}. Run the ${c.cyan('"Bootstrap Customizations"')} prompt to configure for your project`
|
|
218
275
|
)
|
|
219
|
-
|
|
276
|
+
step++
|
|
277
|
+
console.log(` ${step}. Commit the customizations/ folder to your repository`)
|
|
220
278
|
console.log()
|
|
221
279
|
|
|
222
280
|
closePrompts()
|
package/src/cli/manifest.ts
CHANGED
|
@@ -35,10 +35,11 @@ export async function writeManifest(
|
|
|
35
35
|
/**
|
|
36
36
|
* Create a fresh manifest object.
|
|
37
37
|
*/
|
|
38
|
-
export function createManifest(version: string, ide: string): Manifest {
|
|
38
|
+
export function createManifest(version: string, ide: string, ides?: string[]): Manifest {
|
|
39
39
|
return {
|
|
40
40
|
version,
|
|
41
41
|
ide,
|
|
42
|
+
ides: ides ?? [ide],
|
|
42
43
|
installedAt: new Date().toISOString(),
|
|
43
44
|
updatedAt: new Date().toISOString(),
|
|
44
45
|
};
|
package/src/cli/mcp.ts
CHANGED
|
@@ -1,92 +1,171 @@
|
|
|
1
1
|
import { resolve, dirname } from 'node:path';
|
|
2
2
|
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
3
3
|
import { existsSync } from 'node:fs';
|
|
4
|
-
import { getOrchestratorRoot } from './copy.js';
|
|
5
4
|
import { getIncludedMcpServers } from './stack-config.js';
|
|
6
|
-
import
|
|
5
|
+
import { PLUGINS } from '../orchestrator/plugins/index.js';
|
|
6
|
+
import type { McpInput } from '../orchestrator/plugins/types.js';
|
|
7
|
+
import type { ScaffoldResult, StackConfig, RepoInfo, IdeChoice } from './types.js';
|
|
8
|
+
|
|
9
|
+
// ── IDE-specific MCP format transformation ────────────────────
|
|
10
|
+
|
|
11
|
+
interface VsCodeServer {
|
|
12
|
+
type: 'stdio' | 'http';
|
|
13
|
+
command?: string;
|
|
14
|
+
args?: string[];
|
|
15
|
+
url?: string;
|
|
16
|
+
env?: Record<string, string>;
|
|
17
|
+
envFile?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Transform a VS Code–format MCP config into the format
|
|
22
|
+
* expected by the given IDE.
|
|
23
|
+
*/
|
|
24
|
+
function transformMcpForIde(
|
|
25
|
+
ide: IdeChoice,
|
|
26
|
+
servers: Record<string, VsCodeServer>,
|
|
27
|
+
inputs?: McpInput[]
|
|
28
|
+
): Record<string, unknown> {
|
|
29
|
+
switch (ide) {
|
|
30
|
+
case 'cursor':
|
|
31
|
+
case 'claude-code': {
|
|
32
|
+
// mcpServers format — no 'type' field
|
|
33
|
+
const mcpServers: Record<string, unknown> = {};
|
|
34
|
+
for (const [name, server] of Object.entries(servers)) {
|
|
35
|
+
if (server.type === 'stdio') {
|
|
36
|
+
mcpServers[name] = {
|
|
37
|
+
command: server.command,
|
|
38
|
+
args: server.args,
|
|
39
|
+
...(server.env && { env: server.env }),
|
|
40
|
+
};
|
|
41
|
+
} else if (server.type === 'http') {
|
|
42
|
+
// Strip VS Code ${input:...} placeholders for non-VS Code IDEs
|
|
43
|
+
let url = server.url ?? '';
|
|
44
|
+
url = url.replace(/\$\{input:\w+\}/g, 'REPLACE_ME');
|
|
45
|
+
mcpServers[name] = { url };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return { mcpServers };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
case 'opencode': {
|
|
52
|
+
// OpenCode format — type: "local"/"remote", command as array
|
|
53
|
+
const mcp: Record<string, unknown> = {};
|
|
54
|
+
for (const [name, server] of Object.entries(servers)) {
|
|
55
|
+
if (server.type === 'stdio') {
|
|
56
|
+
mcp[name] = {
|
|
57
|
+
type: 'local',
|
|
58
|
+
command: [server.command, ...(server.args ?? [])],
|
|
59
|
+
...(server.env && { environment: server.env }),
|
|
60
|
+
};
|
|
61
|
+
} else if (server.type === 'http') {
|
|
62
|
+
let url = server.url ?? '';
|
|
63
|
+
url = url.replace(/\$\{input:\w+\}/g, 'REPLACE_ME');
|
|
64
|
+
mcp[name] = {
|
|
65
|
+
type: 'remote',
|
|
66
|
+
url,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return { mcp };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
default: {
|
|
74
|
+
// VS Code — return as-is (keep type, inputs, envFile)
|
|
75
|
+
const result: Record<string, unknown> = { servers };
|
|
76
|
+
if (inputs && inputs.length > 0) {
|
|
77
|
+
result.inputs = inputs;
|
|
78
|
+
}
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
7
83
|
|
|
8
84
|
/**
|
|
9
85
|
* Scaffold or merge the MCP server config into the target project.
|
|
10
86
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
87
|
+
* Builds the server list from plugin configs based on the user's
|
|
88
|
+
* stack selection. Writes to `<projectRoot>/<destRelPath>`
|
|
89
|
+
* (e.g. `.vscode/mcp.json`).
|
|
13
90
|
*
|
|
14
|
-
*
|
|
15
|
-
* CMS/DB stack (plus core servers) are included.
|
|
91
|
+
* The output format is adapted to match the target IDE's expectations.
|
|
16
92
|
*
|
|
17
93
|
* If the file already exists, missing servers are merged in without
|
|
18
94
|
* overwriting any existing server configs.
|
|
19
95
|
*/
|
|
20
96
|
export async function scaffoldMcpConfig(
|
|
21
|
-
pkgRoot: string,
|
|
22
97
|
projectRoot: string,
|
|
23
98
|
destRelPath: string,
|
|
24
99
|
stack?: StackConfig,
|
|
25
|
-
repoInfo?: RepoInfo
|
|
100
|
+
repoInfo?: RepoInfo,
|
|
101
|
+
ide?: IdeChoice
|
|
26
102
|
): Promise<ScaffoldResult> {
|
|
27
103
|
const destPath = resolve(projectRoot, destRelPath);
|
|
28
104
|
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const template = JSON.parse(content) as {
|
|
34
|
-
servers: Record<string, unknown>;
|
|
35
|
-
inputs?: Array<{ id: string; [key: string]: unknown }>;
|
|
36
|
-
};
|
|
105
|
+
// Build server list from plugin configs
|
|
106
|
+
const servers: Record<string, VsCodeServer> = {};
|
|
107
|
+
let inputs: McpInput[] = [];
|
|
37
108
|
|
|
38
|
-
// Filter servers based on stack config
|
|
39
109
|
if (stack) {
|
|
40
110
|
const included = getIncludedMcpServers(stack, repoInfo);
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
template.inputs = template.inputs.filter(
|
|
49
|
-
(input) => serverJson.includes(`\${input:${input.id}}`)
|
|
50
|
-
);
|
|
51
|
-
if (template.inputs.length === 0) {
|
|
52
|
-
delete template.inputs;
|
|
111
|
+
|
|
112
|
+
for (const plugin of Object.values(PLUGINS)) {
|
|
113
|
+
if (plugin.mcpServerKey && included.has(plugin.mcpServerKey)) {
|
|
114
|
+
servers[plugin.mcpServerKey] = plugin.mcpConfig as VsCodeServer;
|
|
115
|
+
if (plugin.mcpInputs) {
|
|
116
|
+
inputs.push(...plugin.mcpInputs);
|
|
117
|
+
}
|
|
53
118
|
}
|
|
54
119
|
}
|
|
55
120
|
}
|
|
56
121
|
|
|
122
|
+
// Transform to IDE-specific format
|
|
123
|
+
const resolvedIde = ide ?? 'vscode';
|
|
124
|
+
const output = transformMcpForIde(resolvedIde, servers, inputs.length > 0 ? inputs : undefined);
|
|
125
|
+
|
|
57
126
|
if (existsSync(destPath)) {
|
|
58
127
|
// Merge: add missing servers without overwriting existing ones
|
|
59
128
|
const existingContent = await readFile(destPath, 'utf8');
|
|
60
|
-
const existing = JSON.parse(existingContent) as
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
129
|
+
const existing = JSON.parse(existingContent) as Record<string, unknown>;
|
|
130
|
+
|
|
131
|
+
// Determine the server container key for this IDE
|
|
132
|
+
const containerKey = resolvedIde === 'opencode'
|
|
133
|
+
? 'mcp'
|
|
134
|
+
: resolvedIde === 'vscode'
|
|
135
|
+
? 'servers'
|
|
136
|
+
: 'mcpServers';
|
|
137
|
+
|
|
138
|
+
if (!existing[containerKey]) {
|
|
139
|
+
existing[containerKey] = {};
|
|
68
140
|
}
|
|
69
141
|
|
|
142
|
+
const existingServers = existing[containerKey] as Record<string, unknown>;
|
|
143
|
+
const newServers = (output as Record<string, unknown>)[containerKey] as Record<string, unknown> | undefined;
|
|
144
|
+
|
|
70
145
|
let added = 0;
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
146
|
+
if (newServers) {
|
|
147
|
+
for (const [key, value] of Object.entries(newServers)) {
|
|
148
|
+
if (!(key in existingServers)) {
|
|
149
|
+
existingServers[key] = value;
|
|
150
|
+
added++;
|
|
151
|
+
}
|
|
75
152
|
}
|
|
76
153
|
}
|
|
77
154
|
|
|
78
|
-
//
|
|
79
|
-
if (
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const
|
|
84
|
-
for (const input of template.inputs) {
|
|
155
|
+
// For VS Code: merge inputs
|
|
156
|
+
if (resolvedIde === 'vscode' && output.inputs) {
|
|
157
|
+
const existingInputs = (existing.inputs as McpInput[]) ?? [];
|
|
158
|
+
const existingIds = new Set(existingInputs.map((i) => i.id));
|
|
159
|
+
const newInputs = output.inputs as McpInput[];
|
|
160
|
+
for (const input of newInputs) {
|
|
85
161
|
if (!existingIds.has(input.id)) {
|
|
86
|
-
|
|
162
|
+
existingInputs.push(input);
|
|
87
163
|
added++;
|
|
88
164
|
}
|
|
89
165
|
}
|
|
166
|
+
if (existingInputs.length > 0) {
|
|
167
|
+
existing.inputs = existingInputs;
|
|
168
|
+
}
|
|
90
169
|
}
|
|
91
170
|
|
|
92
171
|
if (added === 0) {
|
|
@@ -98,7 +177,7 @@ export async function scaffoldMcpConfig(
|
|
|
98
177
|
}
|
|
99
178
|
|
|
100
179
|
await mkdir(dirname(destPath), { recursive: true });
|
|
101
|
-
await writeFile(destPath, JSON.stringify(
|
|
180
|
+
await writeFile(destPath, JSON.stringify(output, null, 2) + '\n');
|
|
102
181
|
|
|
103
182
|
return { path: destPath, action: 'created' };
|
|
104
183
|
}
|