agileflow 3.1.0 → 3.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/README.md +57 -85
- package/lib/dashboard-automations.js +130 -0
- package/lib/dashboard-git.js +254 -0
- package/lib/dashboard-inbox.js +64 -0
- package/lib/dashboard-protocol.js +1 -0
- package/lib/dashboard-server.js +114 -924
- package/lib/dashboard-session.js +136 -0
- package/lib/dashboard-status.js +72 -0
- package/lib/dashboard-terminal.js +354 -0
- package/lib/dashboard-websocket.js +88 -0
- package/lib/drivers/codex-driver.ts +4 -4
- package/lib/logger.js +106 -0
- package/package.json +4 -2
- package/scripts/agileflow-configure.js +2 -2
- package/scripts/agileflow-welcome.js +409 -434
- package/scripts/claude-tmux.sh +80 -2
- package/scripts/context-loader.js +4 -9
- package/scripts/lib/browser-qa-evidence.js +409 -0
- package/scripts/lib/browser-qa-status.js +192 -0
- package/scripts/lib/command-prereqs.js +280 -0
- package/scripts/lib/configure-detect.js +92 -2
- package/scripts/lib/configure-features.js +295 -1
- package/scripts/lib/context-formatter.js +468 -233
- package/scripts/lib/context-loader.js +27 -15
- package/scripts/lib/damage-control-utils.js +8 -1
- package/scripts/lib/feature-catalog.js +321 -0
- package/scripts/lib/portable-tasks-cli.js +274 -0
- package/scripts/lib/portable-tasks.js +479 -0
- package/scripts/lib/signal-detectors.js +1 -1
- package/scripts/lib/team-events.js +86 -1
- package/scripts/obtain-context.js +28 -4
- package/scripts/smart-detect.js +17 -0
- package/scripts/strip-ai-attribution.js +63 -0
- package/scripts/team-manager.js +7 -2
- package/scripts/welcome-deferred.js +437 -0
- package/src/core/agents/browser-qa.md +328 -0
- package/src/core/agents/perf-analyzer-assets.md +174 -0
- package/src/core/agents/perf-analyzer-bundle.md +165 -0
- package/src/core/agents/perf-analyzer-caching.md +160 -0
- package/src/core/agents/perf-analyzer-compute.md +165 -0
- package/src/core/agents/perf-analyzer-memory.md +182 -0
- package/src/core/agents/perf-analyzer-network.md +157 -0
- package/src/core/agents/perf-analyzer-queries.md +155 -0
- package/src/core/agents/perf-analyzer-rendering.md +156 -0
- package/src/core/agents/perf-consensus.md +280 -0
- package/src/core/agents/security-analyzer-api.md +199 -0
- package/src/core/agents/security-analyzer-auth.md +160 -0
- package/src/core/agents/security-analyzer-authz.md +168 -0
- package/src/core/agents/security-analyzer-deps.md +147 -0
- package/src/core/agents/security-analyzer-infra.md +176 -0
- package/src/core/agents/security-analyzer-injection.md +148 -0
- package/src/core/agents/security-analyzer-input.md +191 -0
- package/src/core/agents/security-analyzer-secrets.md +175 -0
- package/src/core/agents/security-consensus.md +276 -0
- package/src/core/agents/test-analyzer-assertions.md +181 -0
- package/src/core/agents/test-analyzer-coverage.md +183 -0
- package/src/core/agents/test-analyzer-fragility.md +185 -0
- package/src/core/agents/test-analyzer-integration.md +155 -0
- package/src/core/agents/test-analyzer-maintenance.md +173 -0
- package/src/core/agents/test-analyzer-mocking.md +178 -0
- package/src/core/agents/test-analyzer-patterns.md +189 -0
- package/src/core/agents/test-analyzer-structure.md +177 -0
- package/src/core/agents/test-consensus.md +294 -0
- package/src/core/commands/{legal/audit.md → audit/legal.md} +13 -13
- package/src/core/commands/{logic/audit.md → audit/logic.md} +12 -12
- package/src/core/commands/audit/performance.md +443 -0
- package/src/core/commands/audit/security.md +443 -0
- package/src/core/commands/audit/test.md +442 -0
- package/src/core/commands/babysit.md +505 -463
- package/src/core/commands/browser-qa.md +240 -0
- package/src/core/commands/configure.md +8 -8
- package/src/core/commands/research/ask.md +42 -9
- package/src/core/commands/research/import.md +14 -8
- package/src/core/commands/research/list.md +17 -16
- package/src/core/commands/research/synthesize.md +8 -8
- package/src/core/commands/research/view.md +28 -4
- package/src/core/commands/whats-new.md +2 -2
- package/src/core/experts/devops/expertise.yaml +13 -2
- package/src/core/experts/documentation/expertise.yaml +26 -4
- package/src/core/profiles/COMPARISON.md +170 -0
- package/src/core/profiles/README.md +178 -0
- package/src/core/profiles/claude-code.yaml +111 -0
- package/src/core/profiles/codex.yaml +103 -0
- package/src/core/profiles/cursor.yaml +134 -0
- package/src/core/profiles/examples.js +250 -0
- package/src/core/profiles/loader.js +235 -0
- package/src/core/profiles/windsurf.yaml +159 -0
- package/src/core/teams/logic-audit.json +6 -0
- package/src/core/teams/perf-audit.json +71 -0
- package/src/core/teams/security-audit.json +71 -0
- package/src/core/teams/test-audit.json +71 -0
- package/src/core/templates/browser-qa-spec.yaml +94 -0
- package/src/core/templates/command-prerequisites.yaml +169 -0
- package/src/core/templates/damage-control-patterns.yaml +9 -0
- package/tools/cli/installers/ide/_base-ide.js +33 -3
- package/tools/cli/installers/ide/claude-code.js +2 -69
- package/tools/cli/installers/ide/codex.js +9 -9
- package/tools/cli/installers/ide/cursor.js +165 -4
- package/tools/cli/installers/ide/windsurf.js +237 -6
- package/tools/cli/lib/content-transformer.js +234 -9
- package/tools/cli/lib/docs-setup.js +1 -1
- package/tools/cli/lib/ide-generator.js +357 -0
- package/tools/cli/lib/ide-registry.js +2 -2
- package/scripts/tmux-task-name.sh +0 -105
- package/scripts/tmux-task-watcher.sh +0 -344
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Cursor IDE Capability Profile
|
|
2
|
+
# v2.5 (Feb 2026) - Closest competitor to Claude Code
|
|
3
|
+
# Reference: Cross-IDE Compatibility Findings (20260220)
|
|
4
|
+
|
|
5
|
+
ide:
|
|
6
|
+
id: cursor
|
|
7
|
+
name: Cursor
|
|
8
|
+
displayName: Cursor
|
|
9
|
+
description: AI-native code editor with async subagents and hooks
|
|
10
|
+
configDir: .cursor
|
|
11
|
+
instructionsFile: CURSOR.md
|
|
12
|
+
fileFormat: markdown
|
|
13
|
+
commandPrefix: "/"
|
|
14
|
+
agentPrefix: "agileflow-"
|
|
15
|
+
marketplaceId: cursor.com/marketplace
|
|
16
|
+
|
|
17
|
+
paths:
|
|
18
|
+
commands: .cursor/commands/
|
|
19
|
+
agents: .cursor/agents/
|
|
20
|
+
skills: null # Uses agents instead
|
|
21
|
+
rules: .cursor/rules/
|
|
22
|
+
hooks: .cursor/hooks.json
|
|
23
|
+
mcp: .cursor/mcp.json
|
|
24
|
+
|
|
25
|
+
capabilities:
|
|
26
|
+
core:
|
|
27
|
+
fileOperations: true # read_file, edit_file, ApplyPatch
|
|
28
|
+
shell: true # run_terminal_cmd, YOLO mode for auto-execution
|
|
29
|
+
fileSearch: true # codebase_search, grep_search, file_search, list_dir
|
|
30
|
+
interactiveInput: true # Conversational only (no structured AskUserQuestion)
|
|
31
|
+
subAgents: true # Async subagents with nesting
|
|
32
|
+
nestedSubAgents: true # Sub-agents can spawn sub-sub-agents
|
|
33
|
+
planning:
|
|
34
|
+
planMode: true # Native plan mode with Markdown plans
|
|
35
|
+
planModeEditable: true # User can edit plans during execution
|
|
36
|
+
lifecycle:
|
|
37
|
+
hooks: true # Beta - 6 hook events
|
|
38
|
+
hookEvents:
|
|
39
|
+
- beforeSubmitPrompt
|
|
40
|
+
- beforeShellExecution
|
|
41
|
+
- beforeMCPExecution
|
|
42
|
+
- beforeReadFile
|
|
43
|
+
- afterFileEdit
|
|
44
|
+
- stop
|
|
45
|
+
external:
|
|
46
|
+
webSearch: true # Native web_search + @web syntax + MCP
|
|
47
|
+
webFetch: true # Can fetch URLs
|
|
48
|
+
mcp: true # Full MCP support
|
|
49
|
+
mcpToolLimit: 40 # 40-tool cap (binding constraint)
|
|
50
|
+
browser: true # Native browser tool (v2.0+)
|
|
51
|
+
collaboration:
|
|
52
|
+
taskTracking: false # Plan mode checklists only (not persistent)
|
|
53
|
+
persistentTasks: false
|
|
54
|
+
worktrees: false # Background agents use branches instead
|
|
55
|
+
|
|
56
|
+
toolNames:
|
|
57
|
+
# Interactive
|
|
58
|
+
askUser: null # Conversational only, no AskUserQuestion
|
|
59
|
+
# Delegation
|
|
60
|
+
spawnAgent: null # Use native async subagents in .cursor/agents/
|
|
61
|
+
# Planning
|
|
62
|
+
planModeEnter: null # Native, activated via interface
|
|
63
|
+
planModeExit: null # Native
|
|
64
|
+
# Execution
|
|
65
|
+
bash: run_terminal_cmd
|
|
66
|
+
# Files
|
|
67
|
+
read: read_file
|
|
68
|
+
write: null # Use ApplyPatch or edit_file
|
|
69
|
+
edit: edit_file
|
|
70
|
+
# Search
|
|
71
|
+
glob: file_search
|
|
72
|
+
grep: grep_search
|
|
73
|
+
# Web
|
|
74
|
+
webSearch: web_search
|
|
75
|
+
webFetch: null # Use web_search with @web
|
|
76
|
+
# Browser
|
|
77
|
+
browser: native_browser
|
|
78
|
+
|
|
79
|
+
frontmatter:
|
|
80
|
+
agents:
|
|
81
|
+
format: yaml
|
|
82
|
+
fields:
|
|
83
|
+
- name
|
|
84
|
+
- description
|
|
85
|
+
- model
|
|
86
|
+
- readonly
|
|
87
|
+
- is_background
|
|
88
|
+
commands:
|
|
89
|
+
format: markdown # Slash commands
|
|
90
|
+
rules:
|
|
91
|
+
format: mdc # MDC format (YAML: description, globs, alwaysApply)
|
|
92
|
+
|
|
93
|
+
subAgentConfig:
|
|
94
|
+
format: yaml
|
|
95
|
+
fields:
|
|
96
|
+
name: "Agent display name"
|
|
97
|
+
description: "What this agent does"
|
|
98
|
+
model: "claude-3-5-sonnet or gpt-4-turbo"
|
|
99
|
+
readonly: "boolean - read-only workspace"
|
|
100
|
+
is_background: "boolean - runs without user interaction"
|
|
101
|
+
async: true
|
|
102
|
+
supportsNesting: true
|
|
103
|
+
resultsAreAccessible: true
|
|
104
|
+
|
|
105
|
+
limits:
|
|
106
|
+
maxFileSize: 0 # 0 = unlimited
|
|
107
|
+
maxCommandLength: 0 # 0 = unlimited
|
|
108
|
+
maxToolsPerMCP: 40 # Hard limit
|
|
109
|
+
maxBackgroundAgents: 5 # Practical limit
|
|
110
|
+
|
|
111
|
+
installation:
|
|
112
|
+
supportsFileOps: true
|
|
113
|
+
supportsHooks: true # Beta support
|
|
114
|
+
supportsMCP: true
|
|
115
|
+
commandsRequireApproval: false
|
|
116
|
+
agentFilesCanBeNestedDirs: false
|
|
117
|
+
rulesFormat: MDC # Modern YAML-based rule format
|
|
118
|
+
|
|
119
|
+
features:
|
|
120
|
+
marketplace: "Plugin marketplace at cursor.com/marketplace"
|
|
121
|
+
backendAgents: "Can run agents in background without UI blocking"
|
|
122
|
+
asyncModel: "Agents don't wait for each other - true parallelism"
|
|
123
|
+
hookBeta: "6 hooks in beta; more events coming in Wave 2.6"
|
|
124
|
+
|
|
125
|
+
notes:
|
|
126
|
+
- "Cursor v2.5 now rivals Claude Code with async subagents"
|
|
127
|
+
- "Agents are native; no separate Task tool needed"
|
|
128
|
+
- "Conversational input instead of structured menus (AskUserQuestion replacement)"
|
|
129
|
+
- "40-tool MCP limit is binding constraint for multi-IDE installations"
|
|
130
|
+
- "MDC rule format is modern YAML-based (legacy .cursorrules still supported)"
|
|
131
|
+
- "Plan mode fully built-in; no explicit enter/exit needed"
|
|
132
|
+
- "Hook events have different names but similar functionality to Claude Code"
|
|
133
|
+
- "CAVEAT: Task tools (TaskCreate/TaskUpdate) bypass PreToolUse/PostToolUse hooks"
|
|
134
|
+
- "Background agents enable true parallel task execution"
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IDE Capability Profile - Usage Examples
|
|
3
|
+
*
|
|
4
|
+
* This file demonstrates how to use the profile loader
|
|
5
|
+
* in installers, generators, and feature-detection code.
|
|
6
|
+
*
|
|
7
|
+
* Run: node examples.js
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const loader = require('./loader');
|
|
11
|
+
|
|
12
|
+
console.log('IDE Capability Profile - Usage Examples\n');
|
|
13
|
+
console.log('='.repeat(60) + '\n');
|
|
14
|
+
|
|
15
|
+
// ============================================================
|
|
16
|
+
// Example 1: Load a single profile
|
|
17
|
+
// ============================================================
|
|
18
|
+
console.log('1. LOAD A SINGLE PROFILE\n');
|
|
19
|
+
|
|
20
|
+
const claudeCodeProfile = loader.load('claude-code');
|
|
21
|
+
console.log(`Loaded: ${claudeCodeProfile.ide.displayName}`);
|
|
22
|
+
console.log(`Config dir: ${claudeCodeProfile.paths.commands}`);
|
|
23
|
+
console.log(`Instructions: ${claudeCodeProfile.paths.instructionsFile}`);
|
|
24
|
+
console.log();
|
|
25
|
+
|
|
26
|
+
// ============================================================
|
|
27
|
+
// Example 2: Load all profiles
|
|
28
|
+
// ============================================================
|
|
29
|
+
console.log('2. LOAD ALL PROFILES\n');
|
|
30
|
+
|
|
31
|
+
const allProfiles = loader.loadAll();
|
|
32
|
+
console.log('Available IDEs:');
|
|
33
|
+
Object.keys(allProfiles).sort().forEach(ide => {
|
|
34
|
+
const profile = allProfiles[ide];
|
|
35
|
+
console.log(` - ${profile.ide.displayName}`);
|
|
36
|
+
});
|
|
37
|
+
console.log();
|
|
38
|
+
|
|
39
|
+
// ============================================================
|
|
40
|
+
// Example 3: List available profiles
|
|
41
|
+
// ============================================================
|
|
42
|
+
console.log('3. LIST AVAILABLE PROFILES\n');
|
|
43
|
+
|
|
44
|
+
const available = loader.listAvailable();
|
|
45
|
+
console.log(`Supported IDEs: ${available.join(', ')}`);
|
|
46
|
+
console.log();
|
|
47
|
+
|
|
48
|
+
// ============================================================
|
|
49
|
+
// Example 4: Check if IDE is supported
|
|
50
|
+
// ============================================================
|
|
51
|
+
console.log('4. CHECK IDE SUPPORT\n');
|
|
52
|
+
|
|
53
|
+
['claude-code', 'cursor', 'unknown-ide'].forEach(ide => {
|
|
54
|
+
const supported = loader.isSupported(ide);
|
|
55
|
+
console.log(`${ide}: ${supported ? '✓ supported' : '✗ not supported'}`);
|
|
56
|
+
});
|
|
57
|
+
console.log();
|
|
58
|
+
|
|
59
|
+
// ============================================================
|
|
60
|
+
// Example 5: Check specific capability
|
|
61
|
+
// ============================================================
|
|
62
|
+
console.log('5. CHECK SPECIFIC CAPABILITIES\n');
|
|
63
|
+
|
|
64
|
+
const capabilities = [
|
|
65
|
+
{ ide: 'claude-code', group: 'core', cap: 'planMode' },
|
|
66
|
+
{ ide: 'cursor', group: 'core', cap: 'planMode' },
|
|
67
|
+
{ ide: 'windsurf', group: 'collaboration', cap: 'taskTracking' },
|
|
68
|
+
{ ide: 'codex', group: 'planning', cap: 'planMode' },
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
capabilities.forEach(({ ide, group, cap }) => {
|
|
72
|
+
const has = loader.hasCapability(ide, group, cap);
|
|
73
|
+
const status = has ? '✅' : '❌';
|
|
74
|
+
console.log(`${ide}: ${cap} ${status}`);
|
|
75
|
+
});
|
|
76
|
+
console.log();
|
|
77
|
+
|
|
78
|
+
// ============================================================
|
|
79
|
+
// Example 6: Get tool name mapping
|
|
80
|
+
// ============================================================
|
|
81
|
+
console.log('6. TOOL NAME MAPPINGS (IDE-specific equivalents)\n');
|
|
82
|
+
|
|
83
|
+
const toolAlias = 'bash';
|
|
84
|
+
console.log(`Standard tool alias: "${toolAlias}"`);
|
|
85
|
+
console.log('IDE-specific names:');
|
|
86
|
+
available.forEach(ide => {
|
|
87
|
+
const toolName = loader.getToolName(ide, toolAlias);
|
|
88
|
+
console.log(` ${ide}: ${toolName || '(not available)'}`);
|
|
89
|
+
});
|
|
90
|
+
console.log();
|
|
91
|
+
|
|
92
|
+
// ============================================================
|
|
93
|
+
// Example 7: Get all capabilities for an IDE
|
|
94
|
+
// ============================================================
|
|
95
|
+
console.log('7. ALL CAPABILITIES FOR CURSOR\n');
|
|
96
|
+
|
|
97
|
+
const cursorCaps = loader.getAllCapabilities('cursor');
|
|
98
|
+
const enabledCaps = Object.entries(cursorCaps)
|
|
99
|
+
.filter(([_, enabled]) => enabled)
|
|
100
|
+
.map(([name, _]) => name);
|
|
101
|
+
|
|
102
|
+
console.log(`Cursor has ${enabledCaps.length} capabilities enabled:`);
|
|
103
|
+
enabledCaps.slice(0, 8).forEach(cap => console.log(` ✓ ${cap}`));
|
|
104
|
+
console.log(` ... and ${enabledCaps.length - 8} more`);
|
|
105
|
+
console.log();
|
|
106
|
+
|
|
107
|
+
// ============================================================
|
|
108
|
+
// Example 8: Find IDEs with a capability
|
|
109
|
+
// ============================================================
|
|
110
|
+
console.log('8. FIND IDEs WITH SPECIFIC CAPABILITY\n');
|
|
111
|
+
|
|
112
|
+
const idsWithPlanMode = loader.findIDEsWithCapability('planning', 'planMode');
|
|
113
|
+
console.log(`IDEs with plan mode: ${idsWithPlanMode.join(', ')}`);
|
|
114
|
+
|
|
115
|
+
const idsWithTasks = loader.findIDEsWithCapability('collaboration', 'taskTracking');
|
|
116
|
+
console.log(`IDEs with task tracking: ${idsWithTasks.join(', ')}`);
|
|
117
|
+
|
|
118
|
+
const idsWithMCP = loader.findIDEsWithCapability('external', 'mcp');
|
|
119
|
+
console.log(`IDEs with MCP: ${idsWithMCP.join(', ')}`);
|
|
120
|
+
console.log();
|
|
121
|
+
|
|
122
|
+
// ============================================================
|
|
123
|
+
// Example 9: Compare capability across IDEs
|
|
124
|
+
// ============================================================
|
|
125
|
+
console.log('9. COMPARE CAPABILITY ACROSS ALL IDEs\n');
|
|
126
|
+
|
|
127
|
+
const subAgentComparison = loader.compareCapability('core', 'subAgents');
|
|
128
|
+
console.log('Sub-agent support:');
|
|
129
|
+
Object.entries(subAgentComparison).forEach(([ide, supported]) => {
|
|
130
|
+
console.log(` ${ide}: ${supported ? '✅' : '❌'}`);
|
|
131
|
+
});
|
|
132
|
+
console.log();
|
|
133
|
+
|
|
134
|
+
// ============================================================
|
|
135
|
+
// Example 10: Design pattern - Feature detection in installer
|
|
136
|
+
// ============================================================
|
|
137
|
+
console.log('10. DESIGN PATTERN: FEATURE-BASED INSTALLATION\n');
|
|
138
|
+
|
|
139
|
+
function installForIDE(ideId) {
|
|
140
|
+
console.log(`Installing for ${ideId}...\n`);
|
|
141
|
+
|
|
142
|
+
const profile = loader.load(ideId);
|
|
143
|
+
|
|
144
|
+
// Install commands if supported
|
|
145
|
+
if (profile.paths.commands) {
|
|
146
|
+
console.log(` → Install commands to: ${profile.paths.commands}`);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Install hooks if supported
|
|
150
|
+
if (loader.hasCapability(ideId, 'lifecycle', 'hooks')) {
|
|
151
|
+
console.log(` → Install lifecycle hooks (${profile.capabilities.lifecycle.hookEvents.length} events)`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Install MCP if supported
|
|
155
|
+
if (loader.hasCapability(ideId, 'external', 'mcp')) {
|
|
156
|
+
const limit = profile.capabilities.external.mcpToolLimit;
|
|
157
|
+
if (limit === 0) {
|
|
158
|
+
console.log(` → Install MCP tools (unlimited)`);
|
|
159
|
+
} else {
|
|
160
|
+
console.log(` → Install MCP tools (cap: ${limit})`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Install plan mode prompts if supported
|
|
165
|
+
if (loader.hasCapability(ideId, 'planning', 'planMode')) {
|
|
166
|
+
console.log(` → Install plan mode instructions`);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
console.log();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
installForIDE('claude-code');
|
|
173
|
+
installForIDE('windsurf');
|
|
174
|
+
installForIDE('codex');
|
|
175
|
+
|
|
176
|
+
// ============================================================
|
|
177
|
+
// Example 11: Design pattern - Capability fallback chain
|
|
178
|
+
// ============================================================
|
|
179
|
+
console.log('11. DESIGN PATTERN: CAPABILITY FALLBACK\n');
|
|
180
|
+
|
|
181
|
+
function getInteractiveInputMethod(ideId) {
|
|
182
|
+
const profile = loader.load(ideId);
|
|
183
|
+
|
|
184
|
+
if (loader.hasCapability(ideId, 'core', 'interactiveInput')) {
|
|
185
|
+
if (loader.getToolName(ideId, 'askUser') === 'AskUserQuestion') {
|
|
186
|
+
return 'Structured menus (AskUserQuestion)';
|
|
187
|
+
} else if (loader.getToolName(ideId, 'askUser')) {
|
|
188
|
+
return `Text input (${loader.getToolName(ideId, 'askUser')})`;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return 'Conversational clarification';
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
console.log('Interactive input methods by IDE:');
|
|
196
|
+
available.forEach(ide => {
|
|
197
|
+
const method = getInteractiveInputMethod(ide);
|
|
198
|
+
console.log(` ${ide}: ${method}`);
|
|
199
|
+
});
|
|
200
|
+
console.log();
|
|
201
|
+
|
|
202
|
+
// ============================================================
|
|
203
|
+
// Example 12: Design pattern - Multi-IDE constraint analysis
|
|
204
|
+
// ============================================================
|
|
205
|
+
console.log('12. DESIGN PATTERN: MULTI-IDE CONSTRAINT ANALYSIS\n');
|
|
206
|
+
|
|
207
|
+
console.log('Planning a multi-IDE deployment...\n');
|
|
208
|
+
|
|
209
|
+
const allCaps = loader.loadAll();
|
|
210
|
+
const mpcConstraints = [];
|
|
211
|
+
|
|
212
|
+
available.forEach(ide => {
|
|
213
|
+
const limit = allCaps[ide].capabilities.external.mcpToolLimit;
|
|
214
|
+
if (limit > 0) {
|
|
215
|
+
mpcConstraints.push({ ide, limit });
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
mpcConstraints.sort((a, b) => a.limit - b.limit);
|
|
220
|
+
|
|
221
|
+
if (mpcConstraints.length > 0) {
|
|
222
|
+
const minLimit = mpcConstraints[0].limit;
|
|
223
|
+
console.log(`Binding constraint (most restrictive IDE): ${minLimit} MCP tools`);
|
|
224
|
+
console.log(`Affected IDEs:`);
|
|
225
|
+
mpcConstraints.forEach(({ ide, limit }) => {
|
|
226
|
+
const marker = limit === minLimit ? '🔴 (CONSTRAINT)' : '🟢';
|
|
227
|
+
console.log(` ${marker} ${ide}: ${limit} tools`);
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
console.log();
|
|
231
|
+
|
|
232
|
+
// ============================================================
|
|
233
|
+
// Summary
|
|
234
|
+
// ============================================================
|
|
235
|
+
console.log('='.repeat(60));
|
|
236
|
+
console.log('Profile System Ready for Use');
|
|
237
|
+
console.log('='.repeat(60));
|
|
238
|
+
console.log(`
|
|
239
|
+
Available utilities:
|
|
240
|
+
loader.load(ideId) - Load single profile
|
|
241
|
+
loader.loadAll() - Load all profiles
|
|
242
|
+
loader.listAvailable() - List IDE names
|
|
243
|
+
loader.isSupported(ideId) - Check if IDE supported
|
|
244
|
+
loader.hasCapability(ideId, group, cap) - Check feature support
|
|
245
|
+
loader.getToolName(ideId, alias) - Get IDE-specific tool name
|
|
246
|
+
loader.getAllCapabilities(ideId) - Get all IDE capabilities
|
|
247
|
+
loader.findIDEsWithCapability(group, cap) - Find IDEs with feature
|
|
248
|
+
loader.compareCapability(group, cap) - Compare across all IDEs
|
|
249
|
+
loader.clearCache() - Clear cached profiles
|
|
250
|
+
`);
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IDE Capability Profile Loader
|
|
3
|
+
*
|
|
4
|
+
* Loads YAML capability profiles with validation and caching.
|
|
5
|
+
* Used by installers, generators, and feature-detection code.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* const loader = require('./profiles/loader');
|
|
9
|
+
* const profile = loader.load('claude-code');
|
|
10
|
+
* if (profile.capabilities.core.planMode) { ... }
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const yaml = require('js-yaml');
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
|
|
17
|
+
const PROFILES_DIR = __dirname;
|
|
18
|
+
const CACHE = {};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Load a profile by IDE name
|
|
22
|
+
* @param {string} ideId - IDE identifier (claude-code, cursor, windsurf, codex)
|
|
23
|
+
* @returns {Object} Profile object
|
|
24
|
+
* @throws {Error} If profile not found or invalid YAML
|
|
25
|
+
*/
|
|
26
|
+
function load(ideId) {
|
|
27
|
+
if (CACHE[ideId]) {
|
|
28
|
+
return CACHE[ideId];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const filePath = path.join(PROFILES_DIR, `${ideId}.yaml`);
|
|
32
|
+
|
|
33
|
+
if (!fs.existsSync(filePath)) {
|
|
34
|
+
throw new Error(`Profile not found: ${ideId}`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
39
|
+
const profile = yaml.load(content);
|
|
40
|
+
|
|
41
|
+
// Validate profile structure
|
|
42
|
+
validateProfile(profile, ideId);
|
|
43
|
+
|
|
44
|
+
CACHE[ideId] = profile;
|
|
45
|
+
return profile;
|
|
46
|
+
} catch (e) {
|
|
47
|
+
if (e instanceof yaml.YAMLException) {
|
|
48
|
+
throw new Error(`Invalid YAML in profile ${ideId}: ${e.message}`);
|
|
49
|
+
}
|
|
50
|
+
throw e;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Load all available profiles
|
|
56
|
+
* @returns {Object} Map of IDE ID to profile
|
|
57
|
+
*/
|
|
58
|
+
function loadAll() {
|
|
59
|
+
const profiles = {};
|
|
60
|
+
const files = fs.readdirSync(PROFILES_DIR)
|
|
61
|
+
.filter(f => f.endsWith('.yaml'));
|
|
62
|
+
|
|
63
|
+
files.forEach(file => {
|
|
64
|
+
const ideId = file.replace('.yaml', '');
|
|
65
|
+
try {
|
|
66
|
+
profiles[ideId] = load(ideId);
|
|
67
|
+
} catch (e) {
|
|
68
|
+
console.error(`Failed to load profile ${ideId}: ${e.message}`);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
return profiles;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Get list of available profiles
|
|
77
|
+
* @returns {Array<string>} List of IDE IDs
|
|
78
|
+
*/
|
|
79
|
+
function listAvailable() {
|
|
80
|
+
return fs.readdirSync(PROFILES_DIR)
|
|
81
|
+
.filter(f => f.endsWith('.yaml'))
|
|
82
|
+
.map(f => f.replace('.yaml', ''))
|
|
83
|
+
.sort();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Check if IDE is supported
|
|
88
|
+
* @param {string} ideId - IDE identifier
|
|
89
|
+
* @returns {boolean} True if IDE profile exists
|
|
90
|
+
*/
|
|
91
|
+
function isSupported(ideId) {
|
|
92
|
+
return fs.existsSync(path.join(PROFILES_DIR, `${ideId}.yaml`));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Validate profile structure
|
|
97
|
+
* @private
|
|
98
|
+
* @param {Object} profile - Profile object
|
|
99
|
+
* @param {string} ideId - IDE identifier
|
|
100
|
+
* @throws {Error} If validation fails
|
|
101
|
+
*/
|
|
102
|
+
function validateProfile(profile, ideId) {
|
|
103
|
+
const required = ['ide', 'paths', 'capabilities', 'toolNames'];
|
|
104
|
+
|
|
105
|
+
for (const field of required) {
|
|
106
|
+
if (!profile[field]) {
|
|
107
|
+
throw new Error(`Missing required field: ${field}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Validate ide object
|
|
112
|
+
if (!profile.ide.id || !profile.ide.name) {
|
|
113
|
+
throw new Error('ide object must have id and name fields');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Validate capabilities structure
|
|
117
|
+
const validCapGroups = ['core', 'planning', 'lifecycle', 'external', 'collaboration'];
|
|
118
|
+
for (const group of Object.keys(profile.capabilities)) {
|
|
119
|
+
if (!validCapGroups.includes(group)) {
|
|
120
|
+
console.warn(`Unknown capability group in ${ideId}: ${group}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Get capability status for an IDE
|
|
127
|
+
* @param {string} ideId - IDE identifier
|
|
128
|
+
* @param {string} group - Capability group (core, planning, lifecycle, external, collaboration)
|
|
129
|
+
* @param {string} capability - Capability name
|
|
130
|
+
* @returns {boolean|null} True if supported, false if not, null if unknown
|
|
131
|
+
*/
|
|
132
|
+
function hasCapability(ideId, group, capability) {
|
|
133
|
+
const profile = load(ideId);
|
|
134
|
+
const groupCapabilities = profile.capabilities[group];
|
|
135
|
+
|
|
136
|
+
if (!groupCapabilities) {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return groupCapabilities[capability] === true;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Get tool name for an IDE
|
|
145
|
+
* Returns the tool name used by the IDE, or null if not supported
|
|
146
|
+
*
|
|
147
|
+
* @param {string} ideId - IDE identifier
|
|
148
|
+
* @param {string} toolAlias - Standard tool alias (askUser, bash, read, etc.)
|
|
149
|
+
* @returns {string|null} IDE-specific tool name or null
|
|
150
|
+
*/
|
|
151
|
+
function getToolName(ideId, toolAlias) {
|
|
152
|
+
const profile = load(ideId);
|
|
153
|
+
return profile.toolNames[toolAlias] || null;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Get all capabilities for an IDE (flattened)
|
|
158
|
+
* @param {string} ideId - IDE identifier
|
|
159
|
+
* @returns {Object} Map of capability name to boolean
|
|
160
|
+
*/
|
|
161
|
+
function getAllCapabilities(ideId) {
|
|
162
|
+
const profile = load(ideId);
|
|
163
|
+
const capabilities = {};
|
|
164
|
+
|
|
165
|
+
for (const [group, groupCaps] of Object.entries(profile.capabilities)) {
|
|
166
|
+
if (typeof groupCaps === 'object' && !Array.isArray(groupCaps)) {
|
|
167
|
+
for (const [cap, value] of Object.entries(groupCaps)) {
|
|
168
|
+
if (typeof value === 'boolean') {
|
|
169
|
+
capabilities[`${group}.${cap}`] = value;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return capabilities;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Find all IDEs supporting a capability
|
|
180
|
+
* @param {string} group - Capability group
|
|
181
|
+
* @param {string} capability - Capability name
|
|
182
|
+
* @returns {Array<string>} List of IDE IDs that support this capability
|
|
183
|
+
*/
|
|
184
|
+
function findIDEsWithCapability(group, capability) {
|
|
185
|
+
const ides = [];
|
|
186
|
+
const all = loadAll();
|
|
187
|
+
|
|
188
|
+
for (const [ideId, profile] of Object.entries(all)) {
|
|
189
|
+
if (profile.capabilities[group] && profile.capabilities[group][capability] === true) {
|
|
190
|
+
ides.push(ideId);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return ides.sort();
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Get comparison table for a specific capability across all IDEs
|
|
199
|
+
* @param {string} group - Capability group
|
|
200
|
+
* @param {string} capability - Capability name
|
|
201
|
+
* @returns {Object} Map of IDE ID to boolean
|
|
202
|
+
*/
|
|
203
|
+
function compareCapability(group, capability) {
|
|
204
|
+
const result = {};
|
|
205
|
+
const all = loadAll();
|
|
206
|
+
|
|
207
|
+
for (const [ideId, profile] of Object.entries(all)) {
|
|
208
|
+
result[ideId] = profile.capabilities[group] && profile.capabilities[group][capability] === true;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return result;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Clear profile cache
|
|
216
|
+
* Useful for testing or reloading profiles after updates
|
|
217
|
+
*/
|
|
218
|
+
function clearCache() {
|
|
219
|
+
for (const key of Object.keys(CACHE)) {
|
|
220
|
+
delete CACHE[key];
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
module.exports = {
|
|
225
|
+
load,
|
|
226
|
+
loadAll,
|
|
227
|
+
listAvailable,
|
|
228
|
+
isSupported,
|
|
229
|
+
hasCapability,
|
|
230
|
+
getToolName,
|
|
231
|
+
getAllCapabilities,
|
|
232
|
+
findIDEsWithCapability,
|
|
233
|
+
compareCapability,
|
|
234
|
+
clearCache,
|
|
235
|
+
};
|