opencastle 0.5.1 → 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 +2 -2
- package/dist/cli/adapters/claude-code.d.ts.map +1 -1
- package/dist/cli/adapters/claude-code.js +31 -4
- package/dist/cli/adapters/claude-code.js.map +1 -1
- package/dist/cli/adapters/cursor.d.ts +2 -2
- package/dist/cli/adapters/cursor.d.ts.map +1 -1
- package/dist/cli/adapters/cursor.js +28 -4
- 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 +2 -2
- package/dist/cli/adapters/vscode.d.ts.map +1 -1
- package/dist/cli/adapters/vscode.js +38 -7
- 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 +18 -0
- package/dist/cli/detect.d.ts.map +1 -0
- package/dist/cli/detect.js +434 -0
- package/dist/cli/detect.js.map +1 -0
- package/dist/cli/gitignore.d.ts.map +1 -1
- package/dist/cli/gitignore.js +0 -2
- package/dist/cli/gitignore.js.map +1 -1
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +154 -91
- 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 +105 -34
- package/dist/cli/mcp.js.map +1 -1
- package/dist/cli/prompt.d.ts +22 -0
- package/dist/cli/prompt.d.ts.map +1 -1
- package/dist/cli/prompt.js +239 -0
- package/dist/cli/prompt.js.map +1 -1
- package/dist/cli/stack-config.d.ts +26 -3
- package/dist/cli/stack-config.d.ts.map +1 -1
- package/dist/cli/stack-config.js +140 -125
- package/dist/cli/stack-config.js.map +1 -1
- package/dist/cli/types.d.ts +46 -10
- 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 +66 -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 +40 -6
- package/src/cli/adapters/cursor.ts +46 -6
- package/src/cli/adapters/opencode.ts +320 -0
- package/src/cli/adapters/vscode.ts +43 -9
- package/src/cli/copy.ts +32 -0
- package/src/cli/detect.ts +483 -0
- package/src/cli/gitignore.ts +0 -3
- package/src/cli/init.ts +169 -96
- package/src/cli/manifest.ts +2 -1
- package/src/cli/mcp.ts +131 -51
- package/src/cli/prompt.ts +299 -0
- package/src/cli/stack-config.ts +187 -145
- package/src/cli/types.ts +60 -9
- package/src/cli/update.ts +78 -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 +2 -2
- package/src/orchestrator/agents/architect.agent.md +2 -2
- package/src/orchestrator/agents/content-engineer.agent.md +4 -4
- 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 +4 -4
- package/src/orchestrator/agents/developer.agent.md +5 -5
- 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 +3 -3
- package/src/orchestrator/agents/release-manager.agent.md +4 -4
- package/src/orchestrator/agents/researcher.agent.md +19 -3
- package/src/orchestrator/agents/reviewer.agent.md +2 -4
- 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 +97 -101
- package/src/orchestrator/agents/testing-expert.agent.md +5 -5
- package/src/orchestrator/agents/ui-ux-expert.agent.md +7 -7
- 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 +59 -12
- 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 -61
package/src/cli/stack-config.ts
CHANGED
|
@@ -1,110 +1,43 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
linear: { tech: 'Linear', skill: 'task-management' },
|
|
21
|
-
jira: { tech: 'Jira', skill: 'jira-management' },
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
/** Display name for each notifications choice */
|
|
25
|
-
const NOTIF_LABELS: Record<Exclude<NotifChoice, 'none'>, { tech: string; skill: string }> = {
|
|
26
|
-
slack: { tech: 'Slack', skill: 'slack-notifications' },
|
|
27
|
-
teams: { tech: 'Teams', skill: 'teams-notifications' },
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
// ── Exclusion / inclusion maps ────────────────────────────────
|
|
31
|
-
|
|
32
|
-
/** Skills to EXCLUDE based on CMS choice */
|
|
33
|
-
const CMS_SKILL_MAP: Record<CmsChoice, string[]> = {
|
|
34
|
-
sanity: ['contentful-cms', 'strapi-cms'],
|
|
35
|
-
contentful: ['sanity-cms', 'strapi-cms'],
|
|
36
|
-
strapi: ['sanity-cms', 'contentful-cms'],
|
|
37
|
-
none: ['sanity-cms', 'contentful-cms', 'strapi-cms'],
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
/** Skills to EXCLUDE based on DB choice */
|
|
41
|
-
const DB_SKILL_MAP: Record<DbChoice, string[]> = {
|
|
42
|
-
supabase: ['convex-database'],
|
|
43
|
-
convex: ['supabase-database'],
|
|
44
|
-
none: ['supabase-database', 'convex-database'],
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
/** Skills to EXCLUDE based on PM choice */
|
|
48
|
-
const PM_SKILL_MAP: Record<PmChoice, string[]> = {
|
|
49
|
-
linear: ['jira-management'],
|
|
50
|
-
jira: ['task-management'],
|
|
51
|
-
none: ['task-management', 'jira-management'],
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
/** Agents to EXCLUDE based on CMS choice */
|
|
55
|
-
const CMS_AGENT_EXCLUSIONS: Record<CmsChoice, string[]> = {
|
|
56
|
-
sanity: [],
|
|
57
|
-
contentful: [],
|
|
58
|
-
strapi: [],
|
|
59
|
-
none: ['content-engineer.agent.md'],
|
|
60
|
-
};
|
|
1
|
+
import type { TechTool, TeamTool, StackConfig, CopyDirOptions, RepoInfo } from './types.js';
|
|
2
|
+
import {
|
|
3
|
+
PLUGINS,
|
|
4
|
+
TECH_PLUGINS,
|
|
5
|
+
TEAM_PLUGINS,
|
|
6
|
+
CMS_PLUGINS,
|
|
7
|
+
DB_PLUGINS,
|
|
8
|
+
ALL_PLUGIN_SKILL_NAMES,
|
|
9
|
+
getSelectedSkillNames,
|
|
10
|
+
} from '../orchestrator/plugins/index.js';
|
|
11
|
+
import type { PluginConfig } from '../orchestrator/plugins/types.js';
|
|
12
|
+
|
|
13
|
+
// ── Tool registries (derived from plugins) ────────────────────
|
|
14
|
+
|
|
15
|
+
interface ToolInfo {
|
|
16
|
+
tech: string;
|
|
17
|
+
skill: string | null;
|
|
18
|
+
mcpServer: string | null;
|
|
19
|
+
}
|
|
61
20
|
|
|
62
|
-
/**
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
none: ['database-engineer.agent.md'],
|
|
67
|
-
};
|
|
21
|
+
/** All tech-tool metadata — derived from plugin configs. */
|
|
22
|
+
const TECH_TOOL_INFO: Record<TechTool, ToolInfo> = Object.fromEntries(
|
|
23
|
+
TECH_PLUGINS.map((p) => [p.id, { tech: p.name, skill: p.skillName, mcpServer: p.mcpServerKey }])
|
|
24
|
+
) as Record<TechTool, ToolInfo>;
|
|
68
25
|
|
|
69
|
-
/**
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
strapi: ['Strapi'],
|
|
74
|
-
none: [],
|
|
75
|
-
};
|
|
26
|
+
/** All team-tool metadata — derived from plugin configs. */
|
|
27
|
+
const TEAM_TOOL_INFO: Record<TeamTool, ToolInfo> = Object.fromEntries(
|
|
28
|
+
TEAM_PLUGINS.map((p) => [p.id, { tech: p.name, skill: p.skillName, mcpServer: p.mcpServerKey }])
|
|
29
|
+
) as Record<TeamTool, ToolInfo>;
|
|
76
30
|
|
|
77
|
-
/**
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
none: [],
|
|
82
|
-
};
|
|
31
|
+
/** CMS-related tech tools. */
|
|
32
|
+
const CMS_TOOLS: readonly TechTool[] = CMS_PLUGINS.map((p) => p.id) as TechTool[];
|
|
33
|
+
/** Database-related tech tools. */
|
|
34
|
+
const DB_TOOLS: readonly TechTool[] = DB_PLUGINS.map((p) => p.id) as TechTool[];
|
|
83
35
|
|
|
84
|
-
/** MCP
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
jira: ['Jira'],
|
|
88
|
-
none: [],
|
|
36
|
+
/** MCP servers auto-included when detected in the repo. */
|
|
37
|
+
const DETECTED_MCP_MAP: Record<string, string> = {
|
|
38
|
+
vercel: 'Vercel',
|
|
89
39
|
};
|
|
90
40
|
|
|
91
|
-
/** Skills to EXCLUDE based on notifications choice */
|
|
92
|
-
const NOTIF_SKILL_MAP: Record<NotifChoice, string[]> = {
|
|
93
|
-
slack: ['teams-notifications'],
|
|
94
|
-
teams: ['slack-notifications'],
|
|
95
|
-
none: ['slack-notifications', 'teams-notifications'],
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
/** MCP server keys to INCLUDE based on notifications choice */
|
|
99
|
-
const NOTIF_MCP_MAP: Record<NotifChoice, string[]> = {
|
|
100
|
-
slack: ['Slack'],
|
|
101
|
-
teams: ['Teams'],
|
|
102
|
-
none: [],
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
/** Always-included MCP servers */
|
|
106
|
-
const CORE_MCP_SERVERS = ['chrome-devtools', 'Vercel'];
|
|
107
|
-
|
|
108
41
|
// ── MCP environment variable requirements ─────────────────────
|
|
109
42
|
|
|
110
43
|
export interface McpEnvRequirement {
|
|
@@ -118,49 +51,85 @@ export interface McpEnvRequirement {
|
|
|
118
51
|
|
|
119
52
|
/**
|
|
120
53
|
* Registry of MCP servers that require API keys via environment variables.
|
|
121
|
-
*
|
|
122
|
-
* HTTP-based servers (Sanity, Slack, Vercel, etc.) handle auth via OAuth.
|
|
54
|
+
* Derived from plugin configs — only plugins with envVars are included.
|
|
123
55
|
*/
|
|
124
|
-
const MCP_ENV_REQUIREMENTS: McpEnvRequirement[] =
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
56
|
+
const MCP_ENV_REQUIREMENTS: McpEnvRequirement[] = Object.values(PLUGINS)
|
|
57
|
+
.filter((p) => p.envVars.length > 0 && p.mcpServerKey)
|
|
58
|
+
.flatMap((p) =>
|
|
59
|
+
p.envVars.map((ev) => ({
|
|
60
|
+
server: p.mcpServerKey!,
|
|
61
|
+
envVar: ev.name,
|
|
62
|
+
hint: ev.hint,
|
|
63
|
+
}))
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
// ── Exported helpers ──────────────────────────────────────────
|
|
131
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Skills to EXCLUDE — all tool-specific skills that are NOT selected.
|
|
70
|
+
*/
|
|
132
71
|
export function getExcludedSkills(stack: StackConfig): Set<string> {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
72
|
+
const selectedIds = [...stack.techTools, ...stack.teamTools] as string[];
|
|
73
|
+
const includedSkills = new Set(getSelectedSkillNames(selectedIds));
|
|
74
|
+
return new Set(ALL_PLUGIN_SKILL_NAMES.filter((s) => !includedSkills.has(s)));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Plugin IDs to INCLUDE — the user's selected tools.
|
|
79
|
+
*/
|
|
80
|
+
export function getIncludedPluginIds(stack: StackConfig): Set<string> {
|
|
81
|
+
return new Set([...stack.techTools, ...stack.teamTools]);
|
|
139
82
|
}
|
|
140
83
|
|
|
84
|
+
/**
|
|
85
|
+
* Agents to EXCLUDE — content-engineer if no CMS, database-engineer if no DB.
|
|
86
|
+
*/
|
|
141
87
|
export function getExcludedAgents(stack: StackConfig): Set<string> {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
88
|
+
const excluded = new Set<string>();
|
|
89
|
+
const hasCms = stack.techTools.some((t) => (CMS_TOOLS as readonly string[]).includes(t));
|
|
90
|
+
const hasDb = stack.techTools.some((t) => (DB_TOOLS as readonly string[]).includes(t));
|
|
91
|
+
|
|
92
|
+
if (!hasCms) excluded.add('content-engineer.agent.md');
|
|
93
|
+
if (!hasDb) excluded.add('database-engineer.agent.md');
|
|
94
|
+
|
|
95
|
+
return excluded;
|
|
146
96
|
}
|
|
147
97
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
98
|
+
/**
|
|
99
|
+
* MCP servers to INCLUDE — core + selected tools + auto-detected from repo.
|
|
100
|
+
*/
|
|
101
|
+
export function getIncludedMcpServers(stack: StackConfig, repoInfo?: RepoInfo): Set<string> {
|
|
102
|
+
const servers = new Set<string>();
|
|
103
|
+
|
|
104
|
+
for (const tool of stack.techTools) {
|
|
105
|
+
const server = TECH_TOOL_INFO[tool]?.mcpServer;
|
|
106
|
+
if (server) servers.add(server);
|
|
107
|
+
}
|
|
108
|
+
for (const tool of stack.teamTools) {
|
|
109
|
+
const server = TEAM_TOOL_INFO[tool]?.mcpServer;
|
|
110
|
+
if (server) servers.add(server);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Add servers for detected deployment targets
|
|
114
|
+
for (const dep of repoInfo?.deployment ?? []) {
|
|
115
|
+
const server = DETECTED_MCP_MAP[dep];
|
|
116
|
+
if (server) servers.add(server);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Auto-detect NX from monorepo info
|
|
120
|
+
if (repoInfo?.monorepo === 'nx' && !stack.techTools.includes('nx')) {
|
|
121
|
+
servers.add('Nx');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return servers;
|
|
156
125
|
}
|
|
157
126
|
|
|
158
127
|
/**
|
|
159
128
|
* Returns env var requirements for the MCP servers included in the stack.
|
|
160
129
|
* Only returns entries for servers that actually need API keys.
|
|
161
130
|
*/
|
|
162
|
-
export function getRequiredMcpEnvVars(stack: StackConfig): McpEnvRequirement[] {
|
|
163
|
-
const included = getIncludedMcpServers(stack);
|
|
131
|
+
export function getRequiredMcpEnvVars(stack: StackConfig, repoInfo?: RepoInfo): McpEnvRequirement[] {
|
|
132
|
+
const included = getIncludedMcpServers(stack, repoInfo);
|
|
164
133
|
return MCP_ENV_REQUIREMENTS.filter((req) => included.has(req.server));
|
|
165
134
|
}
|
|
166
135
|
|
|
@@ -176,7 +145,6 @@ export function getCustomizationsTransform(
|
|
|
176
145
|
stack: StackConfig
|
|
177
146
|
): NonNullable<CopyDirOptions['transform']> {
|
|
178
147
|
return (content: string, srcPath: string) => {
|
|
179
|
-
// Pre-fill skill matrix with CMS and DB bindings
|
|
180
148
|
if (srcPath.endsWith('skill-matrix.md')) {
|
|
181
149
|
return transformSkillMatrix(content, stack);
|
|
182
150
|
}
|
|
@@ -191,23 +159,97 @@ export function getCustomizationsTransform(
|
|
|
191
159
|
function transformSkillMatrix(content: string, stack: StackConfig): string {
|
|
192
160
|
let result = content;
|
|
193
161
|
|
|
194
|
-
//
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
162
|
+
// Find first selected DB tool
|
|
163
|
+
const db = stack.techTools.find((t) => (DB_TOOLS as readonly string[]).includes(t));
|
|
164
|
+
if (db) {
|
|
165
|
+
const info = TECH_TOOL_INFO[db as TechTool];
|
|
166
|
+
if (info?.skill) {
|
|
167
|
+
result = result.replace(
|
|
168
|
+
/(\| `database`\s*\|)\s*\|(\s*\|)/,
|
|
169
|
+
`$1 ${info.tech} | \`${info.skill}\` $2`
|
|
170
|
+
);
|
|
171
|
+
}
|
|
201
172
|
}
|
|
202
173
|
|
|
203
|
-
//
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
174
|
+
// Find first selected CMS tool
|
|
175
|
+
const cms = stack.techTools.find((t) => (CMS_TOOLS as readonly string[]).includes(t));
|
|
176
|
+
if (cms) {
|
|
177
|
+
const info = TECH_TOOL_INFO[cms as TechTool];
|
|
178
|
+
if (info?.skill) {
|
|
179
|
+
result = result.replace(
|
|
180
|
+
/(\| `cms`\s*\|)\s*\|(\s*\|)/,
|
|
181
|
+
`$1 ${info.tech} | \`${info.skill}\` $2`
|
|
182
|
+
);
|
|
183
|
+
}
|
|
210
184
|
}
|
|
211
185
|
|
|
212
186
|
return result;
|
|
213
187
|
}
|
|
188
|
+
|
|
189
|
+
// ── Agent tool injection ──────────────────────────────────────
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Compute tool injections per agent based on the user's selected stack.
|
|
193
|
+
* Returns a Map where key = agent name (e.g. 'content-engineer'), value = tools to inject.
|
|
194
|
+
*/
|
|
195
|
+
export function getAgentToolInjections(stack: StackConfig): Map<string, string[]> {
|
|
196
|
+
const injections = new Map<string, string[]>();
|
|
197
|
+
const selectedIds = [...stack.techTools, ...stack.teamTools] as string[];
|
|
198
|
+
|
|
199
|
+
for (const id of selectedIds) {
|
|
200
|
+
const plugin = PLUGINS[id];
|
|
201
|
+
if (!plugin?.agentToolMap) continue;
|
|
202
|
+
|
|
203
|
+
for (const [agentName, tools] of Object.entries(plugin.agentToolMap)) {
|
|
204
|
+
const existing = injections.get(agentName) ?? [];
|
|
205
|
+
existing.push(...tools);
|
|
206
|
+
injections.set(agentName, existing);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return injections;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Returns a transform callback that injects plugin-specific tools
|
|
215
|
+
* into agent file frontmatter based on the user's stack selection.
|
|
216
|
+
*/
|
|
217
|
+
export function getAgentTransform(
|
|
218
|
+
stack: StackConfig
|
|
219
|
+
): NonNullable<CopyDirOptions['transform']> {
|
|
220
|
+
const injections = getAgentToolInjections(stack);
|
|
221
|
+
|
|
222
|
+
return (content: string, srcPath: string) => {
|
|
223
|
+
// Extract agent name from filename (e.g., 'content-engineer' from 'content-engineer.agent.md')
|
|
224
|
+
const match = srcPath.match(/([^/\\]+)\.agent\.md$/);
|
|
225
|
+
if (!match) return content;
|
|
226
|
+
|
|
227
|
+
const agentName = match[1];
|
|
228
|
+
const toolsToInject = injections.get(agentName);
|
|
229
|
+
if (!toolsToInject || toolsToInject.length === 0) return content;
|
|
230
|
+
|
|
231
|
+
// Parse the frontmatter to find the tools array
|
|
232
|
+
const fmMatch = content.match(/^(---\n)([\s\S]*?)\n(---\n)([\s\S]*)$/);
|
|
233
|
+
if (!fmMatch) return content;
|
|
234
|
+
|
|
235
|
+
const frontmatter = fmMatch[2];
|
|
236
|
+
const body = fmMatch[4];
|
|
237
|
+
|
|
238
|
+
// Find and modify the tools line
|
|
239
|
+
const toolsMatch = frontmatter.match(/^(tools:\s*\[)(.*?)(\]\s*)$/m);
|
|
240
|
+
if (!toolsMatch) return content;
|
|
241
|
+
|
|
242
|
+
const existingTools = toolsMatch[2];
|
|
243
|
+
const injectedToolsList = toolsToInject.map((t) => `'${t}'`).join(', ');
|
|
244
|
+
const newTools = existingTools
|
|
245
|
+
? `${existingTools}, ${injectedToolsList}`
|
|
246
|
+
: injectedToolsList;
|
|
247
|
+
|
|
248
|
+
const newFrontmatter = frontmatter.replace(
|
|
249
|
+
toolsMatch[0],
|
|
250
|
+
`${toolsMatch[1]}${newTools}${toolsMatch[3]}`
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
return `---\n${newFrontmatter}\n---\n${body}`;
|
|
254
|
+
};
|
|
255
|
+
}
|
package/src/cli/types.ts
CHANGED
|
@@ -2,16 +2,36 @@ import type { ChildProcess } from 'node:child_process';
|
|
|
2
2
|
|
|
3
3
|
// ── Stack selection types ──────────────────────────────────────
|
|
4
4
|
|
|
5
|
-
export type
|
|
6
|
-
export type
|
|
7
|
-
export type
|
|
8
|
-
export type NotifChoice = 'slack' | 'teams' | 'none';
|
|
5
|
+
export type IdeChoice = 'vscode' | 'cursor' | 'claude-code' | 'opencode';
|
|
6
|
+
export type TechTool = 'sanity' | 'contentful' | 'strapi' | 'supabase' | 'convex' | 'vercel' | 'nx' | 'chrome-devtools';
|
|
7
|
+
export type TeamTool = 'linear' | 'jira' | 'slack' | 'teams';
|
|
9
8
|
|
|
10
9
|
export interface StackConfig {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
ides: IdeChoice[];
|
|
11
|
+
techTools: TechTool[];
|
|
12
|
+
teamTools: TeamTool[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Check if a stack config uses the legacy v1 format (individual choices with 'none'). */
|
|
16
|
+
export function isLegacyStack(stack: unknown): stack is { cms: string; db: string; pm?: string; notifications?: string } {
|
|
17
|
+
return typeof stack === 'object' && stack !== null && 'cms' in stack;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Migrate a legacy v1 stack config to the v2 array format. */
|
|
21
|
+
export function migrateStackConfig(
|
|
22
|
+
legacy: { cms: string; db: string; pm?: string; notifications?: string },
|
|
23
|
+
ide?: string
|
|
24
|
+
): StackConfig {
|
|
25
|
+
const techTools: TechTool[] = [];
|
|
26
|
+
const teamTools: TeamTool[] = [];
|
|
27
|
+
|
|
28
|
+
if (legacy.cms && legacy.cms !== 'none') techTools.push(legacy.cms as TechTool);
|
|
29
|
+
if (legacy.db && legacy.db !== 'none') techTools.push(legacy.db as TechTool);
|
|
30
|
+
if (legacy.pm && legacy.pm !== 'none') teamTools.push(legacy.pm as TeamTool);
|
|
31
|
+
if (legacy.notifications && legacy.notifications !== 'none') teamTools.push(legacy.notifications as TeamTool);
|
|
32
|
+
|
|
33
|
+
const ides: IdeChoice[] = ide ? [ide as IdeChoice] : [];
|
|
34
|
+
return { ides, techTools, teamTools };
|
|
15
35
|
}
|
|
16
36
|
|
|
17
37
|
/** Context passed from bin/cli.mjs to every command handler. */
|
|
@@ -37,14 +57,35 @@ export interface CopyDirOptions {
|
|
|
37
57
|
) => Promise<string | null> | string | null;
|
|
38
58
|
}
|
|
39
59
|
|
|
60
|
+
/** Combined repository tooling info — auto-detected + user-declared. */
|
|
61
|
+
export interface RepoInfo {
|
|
62
|
+
packageManager?: string;
|
|
63
|
+
monorepo?: string;
|
|
64
|
+
language?: string;
|
|
65
|
+
frameworks?: string[];
|
|
66
|
+
databases?: string[];
|
|
67
|
+
cms?: string[];
|
|
68
|
+
deployment?: string[];
|
|
69
|
+
testing?: string[];
|
|
70
|
+
cicd?: string[];
|
|
71
|
+
styling?: string[];
|
|
72
|
+
auth?: string[];
|
|
73
|
+
pm?: string[];
|
|
74
|
+
notifications?: string[];
|
|
75
|
+
mcpConfig?: boolean;
|
|
76
|
+
configFiles?: string[];
|
|
77
|
+
}
|
|
78
|
+
|
|
40
79
|
/** OpenCastle project manifest (.opencastle.json). */
|
|
41
80
|
export interface Manifest {
|
|
42
81
|
version: string;
|
|
43
82
|
ide: string;
|
|
83
|
+
ides?: string[];
|
|
44
84
|
installedAt: string;
|
|
45
85
|
updatedAt: string;
|
|
46
86
|
managedPaths?: ManagedPaths;
|
|
47
87
|
stack?: StackConfig;
|
|
88
|
+
repoInfo?: RepoInfo;
|
|
48
89
|
}
|
|
49
90
|
|
|
50
91
|
/** Framework vs customizable file paths. */
|
|
@@ -55,7 +96,7 @@ export interface ManagedPaths {
|
|
|
55
96
|
|
|
56
97
|
/** IDE adapter interface (init/update commands). */
|
|
57
98
|
export interface IdeAdapter {
|
|
58
|
-
install(_pkgRoot: string, _projectRoot: string, _stack?: StackConfig): Promise<CopyResults>;
|
|
99
|
+
install(_pkgRoot: string, _projectRoot: string, _stack?: StackConfig, _repoInfo?: RepoInfo): Promise<CopyResults>;
|
|
59
100
|
update(_pkgRoot: string, _projectRoot: string, _stack?: StackConfig): Promise<CopyResults>;
|
|
60
101
|
getManagedPaths(): ManagedPaths;
|
|
61
102
|
}
|
|
@@ -65,6 +106,8 @@ export interface SelectOption {
|
|
|
65
106
|
label: string;
|
|
66
107
|
hint?: string;
|
|
67
108
|
value: string;
|
|
109
|
+
/** Whether this option starts selected (preselected). */
|
|
110
|
+
selected?: boolean;
|
|
68
111
|
}
|
|
69
112
|
|
|
70
113
|
/** Scaffold result from MCP config. */
|
|
@@ -73,6 +116,14 @@ export interface ScaffoldResult {
|
|
|
73
116
|
action: 'created' | 'skipped';
|
|
74
117
|
}
|
|
75
118
|
|
|
119
|
+
/** IDE display labels. */
|
|
120
|
+
export const IDE_LABELS: Record<IdeChoice, string> = {
|
|
121
|
+
vscode: 'VS Code',
|
|
122
|
+
cursor: 'Cursor',
|
|
123
|
+
'claude-code': 'Claude Code',
|
|
124
|
+
opencode: 'OpenCode',
|
|
125
|
+
};
|
|
126
|
+
|
|
76
127
|
// ── Run command types ──────────────────────────────────────────
|
|
77
128
|
|
|
78
129
|
/** Validated task spec from YAML. */
|
package/src/cli/update.ts
CHANGED
|
@@ -1,18 +1,29 @@
|
|
|
1
1
|
import { resolve } from 'node:path'
|
|
2
2
|
import { readFile } from 'node:fs/promises'
|
|
3
3
|
import { readManifest, writeManifest } from './manifest.js'
|
|
4
|
-
import { confirm, closePrompts } from './prompt.js'
|
|
5
|
-
import
|
|
4
|
+
import { confirm, closePrompts, c } from './prompt.js'
|
|
5
|
+
import { isLegacyStack, migrateStackConfig } from './types.js'
|
|
6
|
+
import type { CliContext, IdeAdapter, IdeChoice } from './types.js'
|
|
6
7
|
|
|
7
8
|
const ADAPTERS: Record<string, () => Promise<IdeAdapter>> = {
|
|
8
9
|
vscode: () => import('./adapters/vscode.js') as Promise<IdeAdapter>,
|
|
9
10
|
cursor: () => import('./adapters/cursor.js') as Promise<IdeAdapter>,
|
|
10
11
|
'claude-code': () =>
|
|
11
12
|
import('./adapters/claude-code.js') as Promise<IdeAdapter>,
|
|
13
|
+
opencode: () =>
|
|
14
|
+
import('./adapters/opencode.js') as Promise<IdeAdapter>,
|
|
12
15
|
}
|
|
13
16
|
|
|
14
17
|
const VALID_IDES = Object.keys(ADAPTERS)
|
|
15
18
|
|
|
19
|
+
/** IDE display labels */
|
|
20
|
+
const IDE_DISPLAY: Record<IdeChoice, string> = {
|
|
21
|
+
vscode: 'VS Code',
|
|
22
|
+
cursor: 'Cursor',
|
|
23
|
+
'claude-code': 'Claude Code',
|
|
24
|
+
opencode: 'OpenCode',
|
|
25
|
+
}
|
|
26
|
+
|
|
16
27
|
export default async function update({
|
|
17
28
|
pkgRoot,
|
|
18
29
|
args,
|
|
@@ -22,18 +33,27 @@ export default async function update({
|
|
|
22
33
|
const manifest = await readManifest(projectRoot)
|
|
23
34
|
if (!manifest) {
|
|
24
35
|
console.error(
|
|
25
|
-
'
|
|
36
|
+
` ${c.red('✗')} No OpenCastle installation found. Run "npx opencastle init" first.`
|
|
26
37
|
)
|
|
27
38
|
process.exit(1)
|
|
28
39
|
}
|
|
29
40
|
|
|
30
|
-
|
|
41
|
+
// Determine list of IDEs to update (support legacy single-IDE manifests)
|
|
42
|
+
const ides = manifest.ides?.length ? manifest.ides : [manifest.ide]
|
|
43
|
+
const invalidIdes = ides.filter((id) => !VALID_IDES.includes(id))
|
|
44
|
+
if (invalidIdes.length > 0) {
|
|
31
45
|
console.error(
|
|
32
|
-
` ✗ Invalid IDE "${
|
|
46
|
+
` ${c.red('✗')} Invalid IDE(s) "${invalidIdes.join(', ')}" in .opencastle.json. Valid: ${VALID_IDES.join(', ')}`
|
|
33
47
|
)
|
|
34
48
|
process.exit(1)
|
|
35
49
|
}
|
|
36
50
|
|
|
51
|
+
// Migrate legacy stack config if needed
|
|
52
|
+
if (manifest.stack && isLegacyStack(manifest.stack)) {
|
|
53
|
+
manifest.stack = migrateStackConfig(manifest.stack, manifest.ide)
|
|
54
|
+
manifest.stack.ides = ides as IdeChoice[]
|
|
55
|
+
}
|
|
56
|
+
|
|
37
57
|
const pkg = JSON.parse(
|
|
38
58
|
await readFile(resolve(pkgRoot, 'package.json'), 'utf8')
|
|
39
59
|
) as { version: string }
|
|
@@ -45,23 +65,24 @@ export default async function update({
|
|
|
45
65
|
return
|
|
46
66
|
}
|
|
47
67
|
|
|
68
|
+
const ideNames = ides.map((id) => IDE_DISPLAY[id as IdeChoice] ?? id).join(', ')
|
|
48
69
|
console.log(
|
|
49
|
-
`\n 🏰 OpenCastle ${dryRun ? 'dry-run' : 'update'}: v${manifest.version} → v${pkg.version}\n`
|
|
70
|
+
`\n 🏰 ${c.bold('OpenCastle')} ${dryRun ? 'dry-run' : 'update'}: ${c.dim(`v${manifest.version}`)} → ${c.green(`v${pkg.version}`)}\n`
|
|
50
71
|
)
|
|
51
|
-
console.log(`
|
|
52
|
-
console.log('
|
|
53
|
-
console.log('
|
|
72
|
+
console.log(` IDEs: ${c.cyan(ideNames)}`)
|
|
73
|
+
console.log(` ${c.dim('Framework files will be overwritten.')}`)
|
|
74
|
+
console.log(` ${c.dim('Customization files will be preserved.')}\n`)
|
|
54
75
|
|
|
55
76
|
if (dryRun) {
|
|
56
|
-
console.log('
|
|
77
|
+
console.log(` ${c.dim('[dry-run]')} Framework files that would be updated:\n`)
|
|
57
78
|
for (const p of manifest.managedPaths?.framework ?? []) {
|
|
58
|
-
console.log(` ↻ ${p}`)
|
|
79
|
+
console.log(` ${c.yellow('↻')} ${p}`)
|
|
59
80
|
}
|
|
60
|
-
console.log(
|
|
81
|
+
console.log(`\n ${c.dim('[dry-run]')} Customization files that would be preserved:\n`)
|
|
61
82
|
for (const p of manifest.managedPaths?.customizable ?? []) {
|
|
62
|
-
console.log(` ✓ ${p}`)
|
|
83
|
+
console.log(` ${c.green('✓')} ${p}`)
|
|
63
84
|
}
|
|
64
|
-
console.log(
|
|
85
|
+
console.log(`\n ${c.dim('No files were written.')}\n`)
|
|
65
86
|
return
|
|
66
87
|
}
|
|
67
88
|
|
|
@@ -71,18 +92,55 @@ export default async function update({
|
|
|
71
92
|
return
|
|
72
93
|
}
|
|
73
94
|
|
|
74
|
-
|
|
75
|
-
|
|
95
|
+
// Update each IDE
|
|
96
|
+
let totalCopied = 0
|
|
97
|
+
let totalCreated = 0
|
|
98
|
+
const allManagedPaths = { framework: [] as string[], customizable: [] as string[] }
|
|
99
|
+
|
|
100
|
+
for (const ide of ides) {
|
|
101
|
+
const adapter = await ADAPTERS[ide]()
|
|
102
|
+
const results = await adapter.update(pkgRoot, projectRoot, manifest.stack)
|
|
103
|
+
totalCopied += results.copied.length
|
|
104
|
+
totalCreated += results.created.length
|
|
105
|
+
|
|
106
|
+
const managed = adapter.getManagedPaths()
|
|
107
|
+
allManagedPaths.framework.push(...managed.framework)
|
|
108
|
+
allManagedPaths.customizable.push(...managed.customizable)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Refresh repo research on update
|
|
112
|
+
const { detectRepoInfo, mergeStackIntoRepoInfo } = await import('./detect.js')
|
|
113
|
+
const repoInfo = await detectRepoInfo(projectRoot)
|
|
76
114
|
|
|
77
115
|
// Update manifest
|
|
78
116
|
manifest.version = pkg.version
|
|
117
|
+
manifest.ides = ides
|
|
79
118
|
manifest.updatedAt = new Date().toISOString()
|
|
80
|
-
manifest.managedPaths =
|
|
119
|
+
manifest.managedPaths = allManagedPaths
|
|
120
|
+
manifest.repoInfo = manifest.stack
|
|
121
|
+
? mergeStackIntoRepoInfo(repoInfo, manifest.stack)
|
|
122
|
+
: repoInfo
|
|
81
123
|
await writeManifest(projectRoot, manifest)
|
|
82
124
|
|
|
83
|
-
console.log(`\n ✓ Updated ${
|
|
84
|
-
if (
|
|
85
|
-
console.log(` + Created ${
|
|
125
|
+
console.log(`\n ${c.green('✓')} Updated ${c.bold(String(totalCopied))} framework files`)
|
|
126
|
+
if (totalCreated > 0) {
|
|
127
|
+
console.log(` ${c.green('+')} Created ${c.bold(String(totalCreated))} new files`)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ── Reload window message ─────────────────────────────────────
|
|
131
|
+
const needsReload = ides.filter((id) => ['vscode', 'cursor'].includes(id))
|
|
132
|
+
if (needsReload.length > 0) {
|
|
133
|
+
console.log()
|
|
134
|
+
if (needsReload.includes('vscode')) {
|
|
135
|
+
console.log(
|
|
136
|
+
` ${c.yellow('⟳')} Reload VS Code window (Cmd+Shift+P → "Developer: Reload Window") to pick up changes`
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
if (needsReload.includes('cursor')) {
|
|
140
|
+
console.log(
|
|
141
|
+
` ${c.yellow('⟳')} Reload Cursor window to pick up the updated rule files`
|
|
142
|
+
)
|
|
143
|
+
}
|
|
86
144
|
}
|
|
87
145
|
console.log()
|
|
88
146
|
|