agileflow 3.4.0 → 3.4.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 +5 -0
- package/README.md +4 -4
- package/package.json +1 -1
- package/scripts/agileflow-welcome.js +79 -0
- package/scripts/claude-tmux.sh +12 -36
- package/scripts/lib/ac-test-matcher.js +452 -0
- package/scripts/lib/audit-registry.js +58 -2
- package/scripts/lib/configure-features.js +35 -0
- package/scripts/lib/model-profiles.js +25 -5
- package/scripts/lib/quality-gates.js +163 -0
- package/scripts/lib/signal-detectors.js +43 -0
- package/scripts/lib/status-writer.js +255 -0
- package/scripts/lib/story-claiming.js +128 -45
- package/scripts/lib/task-sync.js +32 -38
- package/scripts/lib/tmux-audit-monitor.js +611 -0
- package/scripts/lib/tool-registry.yaml +241 -0
- package/scripts/lib/tool-shed.js +441 -0
- package/scripts/native-team-observer.js +219 -0
- package/scripts/obtain-context.js +14 -0
- package/scripts/ralph-loop.js +30 -5
- package/scripts/smart-detect.js +21 -0
- package/scripts/spawn-audit-sessions.js +372 -44
- package/scripts/team-manager.js +19 -0
- package/src/core/agents/a11y-analyzer-aria.md +155 -0
- package/src/core/agents/a11y-analyzer-forms.md +162 -0
- package/src/core/agents/a11y-analyzer-keyboard.md +175 -0
- package/src/core/agents/a11y-analyzer-semantic.md +153 -0
- package/src/core/agents/a11y-analyzer-visual.md +158 -0
- package/src/core/agents/a11y-consensus.md +248 -0
- package/src/core/agents/ads-consensus.md +74 -0
- package/src/core/agents/ads-generate.md +145 -0
- package/src/core/agents/ads-performance-tracker.md +197 -0
- package/src/core/agents/api-quality-analyzer-conventions.md +148 -0
- package/src/core/agents/api-quality-analyzer-docs.md +176 -0
- package/src/core/agents/api-quality-analyzer-errors.md +183 -0
- package/src/core/agents/api-quality-analyzer-pagination.md +171 -0
- package/src/core/agents/api-quality-analyzer-versioning.md +143 -0
- package/src/core/agents/api-quality-consensus.md +214 -0
- package/src/core/agents/arch-analyzer-circular.md +148 -0
- package/src/core/agents/arch-analyzer-complexity.md +171 -0
- package/src/core/agents/arch-analyzer-coupling.md +146 -0
- package/src/core/agents/arch-analyzer-layering.md +151 -0
- package/src/core/agents/arch-analyzer-patterns.md +162 -0
- package/src/core/agents/arch-consensus.md +227 -0
- package/src/core/commands/adr.md +1 -0
- package/src/core/commands/ads/generate.md +238 -0
- package/src/core/commands/ads/health.md +327 -0
- package/src/core/commands/ads/test-plan.md +317 -0
- package/src/core/commands/ads/track.md +288 -0
- package/src/core/commands/ads.md +28 -16
- package/src/core/commands/assign.md +1 -0
- package/src/core/commands/audit.md +43 -6
- package/src/core/commands/babysit.md +90 -6
- package/src/core/commands/baseline.md +1 -0
- package/src/core/commands/blockers.md +1 -0
- package/src/core/commands/board.md +1 -0
- package/src/core/commands/changelog.md +1 -0
- package/src/core/commands/choose.md +1 -0
- package/src/core/commands/ci.md +1 -0
- package/src/core/commands/code/accessibility.md +347 -0
- package/src/core/commands/code/api.md +297 -0
- package/src/core/commands/code/architecture.md +297 -0
- package/src/core/commands/code/completeness.md +43 -6
- package/src/core/commands/code/legal.md +43 -6
- package/src/core/commands/code/logic.md +43 -6
- package/src/core/commands/code/performance.md +43 -6
- package/src/core/commands/code/security.md +43 -6
- package/src/core/commands/code/test.md +43 -6
- package/src/core/commands/configure.md +1 -0
- package/src/core/commands/council.md +1 -0
- package/src/core/commands/deploy.md +1 -0
- package/src/core/commands/diagnose.md +1 -0
- package/src/core/commands/docs.md +1 -0
- package/src/core/commands/epic/edit.md +213 -0
- package/src/core/commands/epic.md +1 -0
- package/src/core/commands/export.md +238 -0
- package/src/core/commands/help.md +16 -1
- package/src/core/commands/ideate/discover.md +7 -3
- package/src/core/commands/ideate/features.md +65 -4
- package/src/core/commands/ideate/new.md +158 -124
- package/src/core/commands/impact.md +1 -0
- package/src/core/commands/learn/explain.md +118 -0
- package/src/core/commands/learn/glossary.md +135 -0
- package/src/core/commands/learn/patterns.md +138 -0
- package/src/core/commands/learn/tour.md +126 -0
- package/src/core/commands/migrate/codemods.md +151 -0
- package/src/core/commands/migrate/plan.md +131 -0
- package/src/core/commands/migrate/scan.md +114 -0
- package/src/core/commands/migrate/validate.md +119 -0
- package/src/core/commands/multi-expert.md +1 -0
- package/src/core/commands/pr.md +1 -0
- package/src/core/commands/review.md +1 -0
- package/src/core/commands/sprint.md +1 -0
- package/src/core/commands/status/undo.md +191 -0
- package/src/core/commands/status.md +1 -0
- package/src/core/commands/story/edit.md +204 -0
- package/src/core/commands/story/view.md +29 -7
- package/src/core/commands/story-validate.md +1 -0
- package/src/core/commands/story.md +1 -0
- package/src/core/commands/tdd.md +1 -0
- package/src/core/commands/team/start.md +10 -6
- package/src/core/commands/tests.md +1 -0
- package/src/core/commands/verify.md +27 -1
- package/src/core/commands/workflow.md +2 -0
- package/src/core/teams/backend.json +41 -0
- package/src/core/teams/frontend.json +41 -0
- package/src/core/teams/qa.json +41 -0
- package/src/core/teams/solo.json +35 -0
- package/src/core/templates/agileflow-metadata.json +5 -0
- package/tools/cli/commands/setup.js +85 -3
- package/tools/cli/commands/update.js +42 -0
- package/tools/cli/installers/ide/claude-code.js +68 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
# Tool Shed Registry - MCP Tool Definitions
|
|
2
|
+
#
|
|
3
|
+
# Central registry of MCP tools and their capabilities.
|
|
4
|
+
# Used by tool-shed.js to recommend relevant tools per task.
|
|
5
|
+
#
|
|
6
|
+
# Format:
|
|
7
|
+
# - name: Tool name as exposed by MCP server
|
|
8
|
+
# server: MCP server name
|
|
9
|
+
# category: Grouping category
|
|
10
|
+
# description: What the tool does
|
|
11
|
+
# keywords: Search terms for matching tasks to tools
|
|
12
|
+
# cost: token cost estimate (low/medium/high)
|
|
13
|
+
# requires_auth: Whether the tool needs credentials
|
|
14
|
+
|
|
15
|
+
# =============================================================================
|
|
16
|
+
# GitHub Tools (via @anthropic/mcp-server-github or similar)
|
|
17
|
+
# =============================================================================
|
|
18
|
+
github:
|
|
19
|
+
- name: create_issue
|
|
20
|
+
description: Create a new GitHub issue
|
|
21
|
+
keywords: [issue, bug, feature request, ticket, report]
|
|
22
|
+
cost: low
|
|
23
|
+
requires_auth: true
|
|
24
|
+
|
|
25
|
+
- name: list_issues
|
|
26
|
+
description: List and search GitHub issues
|
|
27
|
+
keywords: [issues, bugs, search, filter, backlog]
|
|
28
|
+
cost: low
|
|
29
|
+
requires_auth: true
|
|
30
|
+
|
|
31
|
+
- name: create_pull_request
|
|
32
|
+
description: Create a pull request
|
|
33
|
+
keywords: [pr, pull request, merge, review, code review]
|
|
34
|
+
cost: low
|
|
35
|
+
requires_auth: true
|
|
36
|
+
|
|
37
|
+
- name: get_pull_request
|
|
38
|
+
description: Get pull request details and diff
|
|
39
|
+
keywords: [pr, pull request, diff, changes, review]
|
|
40
|
+
cost: medium
|
|
41
|
+
requires_auth: true
|
|
42
|
+
|
|
43
|
+
- name: list_pull_requests
|
|
44
|
+
description: List pull requests with filters
|
|
45
|
+
keywords: [prs, pull requests, open, merged, review]
|
|
46
|
+
cost: low
|
|
47
|
+
requires_auth: true
|
|
48
|
+
|
|
49
|
+
- name: create_branch
|
|
50
|
+
description: Create a new git branch
|
|
51
|
+
keywords: [branch, feature branch, git]
|
|
52
|
+
cost: low
|
|
53
|
+
requires_auth: true
|
|
54
|
+
|
|
55
|
+
- name: search_code
|
|
56
|
+
description: Search code across repositories
|
|
57
|
+
keywords: [search, find, code, grep, pattern]
|
|
58
|
+
cost: medium
|
|
59
|
+
requires_auth: true
|
|
60
|
+
|
|
61
|
+
- name: get_file_contents
|
|
62
|
+
description: Get file contents from a repository
|
|
63
|
+
keywords: [read, file, contents, source]
|
|
64
|
+
cost: low
|
|
65
|
+
requires_auth: true
|
|
66
|
+
|
|
67
|
+
# =============================================================================
|
|
68
|
+
# Filesystem Tools (via @anthropic/mcp-server-filesystem)
|
|
69
|
+
# =============================================================================
|
|
70
|
+
filesystem:
|
|
71
|
+
- name: read_file
|
|
72
|
+
description: Read contents of a file
|
|
73
|
+
keywords: [read, file, contents, view]
|
|
74
|
+
cost: low
|
|
75
|
+
requires_auth: false
|
|
76
|
+
|
|
77
|
+
- name: write_file
|
|
78
|
+
description: Write contents to a file
|
|
79
|
+
keywords: [write, file, create, save]
|
|
80
|
+
cost: low
|
|
81
|
+
requires_auth: false
|
|
82
|
+
|
|
83
|
+
- name: list_directory
|
|
84
|
+
description: List directory contents
|
|
85
|
+
keywords: [list, directory, files, ls, find]
|
|
86
|
+
cost: low
|
|
87
|
+
requires_auth: false
|
|
88
|
+
|
|
89
|
+
- name: search_files
|
|
90
|
+
description: Search for files by name pattern
|
|
91
|
+
keywords: [search, find, glob, pattern, files]
|
|
92
|
+
cost: low
|
|
93
|
+
requires_auth: false
|
|
94
|
+
|
|
95
|
+
# =============================================================================
|
|
96
|
+
# Database Tools (via various MCP servers)
|
|
97
|
+
# =============================================================================
|
|
98
|
+
database:
|
|
99
|
+
- name: query
|
|
100
|
+
description: Execute a SQL query
|
|
101
|
+
keywords: [sql, query, select, database, table, join]
|
|
102
|
+
cost: medium
|
|
103
|
+
requires_auth: true
|
|
104
|
+
|
|
105
|
+
- name: list_tables
|
|
106
|
+
description: List database tables
|
|
107
|
+
keywords: [tables, schema, database, structure]
|
|
108
|
+
cost: low
|
|
109
|
+
requires_auth: true
|
|
110
|
+
|
|
111
|
+
- name: describe_table
|
|
112
|
+
description: Get table schema and columns
|
|
113
|
+
keywords: [schema, columns, table, types, structure]
|
|
114
|
+
cost: low
|
|
115
|
+
requires_auth: true
|
|
116
|
+
|
|
117
|
+
# =============================================================================
|
|
118
|
+
# Browser / Web Tools
|
|
119
|
+
# =============================================================================
|
|
120
|
+
browser:
|
|
121
|
+
- name: navigate
|
|
122
|
+
description: Navigate browser to a URL
|
|
123
|
+
keywords: [browse, navigate, url, page, website]
|
|
124
|
+
cost: medium
|
|
125
|
+
requires_auth: false
|
|
126
|
+
|
|
127
|
+
- name: screenshot
|
|
128
|
+
description: Take a screenshot of the current page
|
|
129
|
+
keywords: [screenshot, capture, visual, image, qa]
|
|
130
|
+
cost: medium
|
|
131
|
+
requires_auth: false
|
|
132
|
+
|
|
133
|
+
- name: click
|
|
134
|
+
description: Click an element on the page
|
|
135
|
+
keywords: [click, interact, button, link]
|
|
136
|
+
cost: low
|
|
137
|
+
requires_auth: false
|
|
138
|
+
|
|
139
|
+
- name: fill
|
|
140
|
+
description: Fill in a form field
|
|
141
|
+
keywords: [fill, input, form, type, text]
|
|
142
|
+
cost: low
|
|
143
|
+
requires_auth: false
|
|
144
|
+
|
|
145
|
+
# =============================================================================
|
|
146
|
+
# Slack / Communication Tools
|
|
147
|
+
# =============================================================================
|
|
148
|
+
slack:
|
|
149
|
+
- name: send_message
|
|
150
|
+
description: Send a message to a Slack channel
|
|
151
|
+
keywords: [slack, message, notify, channel, team]
|
|
152
|
+
cost: low
|
|
153
|
+
requires_auth: true
|
|
154
|
+
|
|
155
|
+
- name: search_messages
|
|
156
|
+
description: Search Slack message history
|
|
157
|
+
keywords: [slack, search, messages, history, find]
|
|
158
|
+
cost: medium
|
|
159
|
+
requires_auth: true
|
|
160
|
+
|
|
161
|
+
# =============================================================================
|
|
162
|
+
# Notion / Documentation Tools
|
|
163
|
+
# =============================================================================
|
|
164
|
+
notion:
|
|
165
|
+
- name: search
|
|
166
|
+
description: Search Notion pages and databases
|
|
167
|
+
keywords: [notion, search, wiki, docs, knowledge]
|
|
168
|
+
cost: medium
|
|
169
|
+
requires_auth: true
|
|
170
|
+
|
|
171
|
+
- name: create_page
|
|
172
|
+
description: Create a new Notion page
|
|
173
|
+
keywords: [notion, create, page, document, wiki]
|
|
174
|
+
cost: low
|
|
175
|
+
requires_auth: true
|
|
176
|
+
|
|
177
|
+
- name: update_page
|
|
178
|
+
description: Update an existing Notion page
|
|
179
|
+
keywords: [notion, update, edit, page, wiki]
|
|
180
|
+
cost: low
|
|
181
|
+
requires_auth: true
|
|
182
|
+
|
|
183
|
+
# =============================================================================
|
|
184
|
+
# Memory / Knowledge Tools
|
|
185
|
+
# =============================================================================
|
|
186
|
+
memory:
|
|
187
|
+
- name: store
|
|
188
|
+
description: Store a key-value pair in persistent memory
|
|
189
|
+
keywords: [remember, store, save, memory, persist]
|
|
190
|
+
cost: low
|
|
191
|
+
requires_auth: false
|
|
192
|
+
|
|
193
|
+
- name: retrieve
|
|
194
|
+
description: Retrieve a value from persistent memory
|
|
195
|
+
keywords: [recall, retrieve, get, memory, remember]
|
|
196
|
+
cost: low
|
|
197
|
+
requires_auth: false
|
|
198
|
+
|
|
199
|
+
- name: search_memory
|
|
200
|
+
description: Search across stored memories
|
|
201
|
+
keywords: [search, memory, find, recall]
|
|
202
|
+
cost: low
|
|
203
|
+
requires_auth: false
|
|
204
|
+
|
|
205
|
+
# =============================================================================
|
|
206
|
+
# Sentry / Error Tracking Tools
|
|
207
|
+
# =============================================================================
|
|
208
|
+
sentry:
|
|
209
|
+
- name: list_issues
|
|
210
|
+
description: List Sentry error issues
|
|
211
|
+
keywords: [errors, bugs, sentry, tracking, crashes, exceptions]
|
|
212
|
+
cost: medium
|
|
213
|
+
requires_auth: true
|
|
214
|
+
|
|
215
|
+
- name: get_issue
|
|
216
|
+
description: Get details of a specific Sentry issue
|
|
217
|
+
keywords: [error, bug, sentry, stack trace, crash]
|
|
218
|
+
cost: medium
|
|
219
|
+
requires_auth: true
|
|
220
|
+
|
|
221
|
+
# =============================================================================
|
|
222
|
+
# Linear / Project Management Tools
|
|
223
|
+
# =============================================================================
|
|
224
|
+
linear:
|
|
225
|
+
- name: create_issue
|
|
226
|
+
description: Create a Linear issue
|
|
227
|
+
keywords: [ticket, issue, task, linear, project]
|
|
228
|
+
cost: low
|
|
229
|
+
requires_auth: true
|
|
230
|
+
|
|
231
|
+
- name: list_issues
|
|
232
|
+
description: List Linear issues with filters
|
|
233
|
+
keywords: [tickets, issues, tasks, linear, backlog, sprint]
|
|
234
|
+
cost: low
|
|
235
|
+
requires_auth: true
|
|
236
|
+
|
|
237
|
+
- name: update_issue
|
|
238
|
+
description: Update a Linear issue status or details
|
|
239
|
+
keywords: [update, ticket, status, assign, linear]
|
|
240
|
+
cost: low
|
|
241
|
+
requires_auth: true
|
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* tool-shed.js - MCP Meta-Tool Registry
|
|
4
|
+
*
|
|
5
|
+
* Central registry for MCP tools. When agents need tools, this module
|
|
6
|
+
* queries the registry by task description and returns a filtered list
|
|
7
|
+
* of relevant tools, avoiding token explosion from sending all tools.
|
|
8
|
+
*
|
|
9
|
+
* Inspired by Stripe's "Tool Shed" pattern where a meta-tool selects
|
|
10
|
+
* relevant tools per task from a large registry.
|
|
11
|
+
*
|
|
12
|
+
* Usage (as module):
|
|
13
|
+
* const { getToolsForTask, getAvailableServers, getToolCount } = require('./tool-shed');
|
|
14
|
+
* const tools = getToolsForTask('create a pull request for auth changes', { projectRoot });
|
|
15
|
+
*
|
|
16
|
+
* Usage (standalone):
|
|
17
|
+
* node scripts/lib/tool-shed.js --task "search for bugs" --project-root /path/to/project
|
|
18
|
+
* node scripts/lib/tool-shed.js --list-servers --project-root /path/to/project
|
|
19
|
+
* node scripts/lib/tool-shed.js --stats
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
'use strict';
|
|
23
|
+
|
|
24
|
+
const fs = require('fs');
|
|
25
|
+
const path = require('path');
|
|
26
|
+
|
|
27
|
+
// Lazy-load js-yaml only when needed
|
|
28
|
+
let _yaml;
|
|
29
|
+
function getYaml() {
|
|
30
|
+
if (!_yaml) {
|
|
31
|
+
try {
|
|
32
|
+
_yaml = require('js-yaml');
|
|
33
|
+
} catch {
|
|
34
|
+
// Fallback: provide a minimal YAML parser for simple structures
|
|
35
|
+
_yaml = null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return _yaml;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// =============================================================================
|
|
42
|
+
// Registry Loading
|
|
43
|
+
// =============================================================================
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Load the built-in tool registry from YAML
|
|
47
|
+
* @returns {Object} Parsed registry object keyed by server name
|
|
48
|
+
*/
|
|
49
|
+
function loadBuiltinRegistry() {
|
|
50
|
+
const registryPath = path.join(__dirname, 'tool-registry.yaml');
|
|
51
|
+
try {
|
|
52
|
+
const content = fs.readFileSync(registryPath, 'utf8');
|
|
53
|
+
const yaml = getYaml();
|
|
54
|
+
if (yaml) {
|
|
55
|
+
return yaml.load(content) || {};
|
|
56
|
+
}
|
|
57
|
+
// Minimal fallback if js-yaml not available
|
|
58
|
+
return {};
|
|
59
|
+
} catch {
|
|
60
|
+
return {};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Detect which MCP servers are configured in the project
|
|
66
|
+
* @param {string} projectRoot - Project root directory
|
|
67
|
+
* @returns {string[]} List of configured server names
|
|
68
|
+
*/
|
|
69
|
+
function detectConfiguredServers(projectRoot) {
|
|
70
|
+
const servers = [];
|
|
71
|
+
|
|
72
|
+
// Check .mcp.json (Claude Code MCP config)
|
|
73
|
+
const mcpJsonPath = path.join(projectRoot, '.mcp.json');
|
|
74
|
+
if (fs.existsSync(mcpJsonPath)) {
|
|
75
|
+
try {
|
|
76
|
+
const mcpConfig = JSON.parse(fs.readFileSync(mcpJsonPath, 'utf8'));
|
|
77
|
+
if (mcpConfig.mcpServers) {
|
|
78
|
+
servers.push(...Object.keys(mcpConfig.mcpServers));
|
|
79
|
+
}
|
|
80
|
+
} catch {
|
|
81
|
+
// Ignore parse errors
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Check .claude/mcp.json (alternative location)
|
|
86
|
+
const claudeMcpPath = path.join(projectRoot, '.claude', 'mcp.json');
|
|
87
|
+
if (fs.existsSync(claudeMcpPath)) {
|
|
88
|
+
try {
|
|
89
|
+
const mcpConfig = JSON.parse(fs.readFileSync(claudeMcpPath, 'utf8'));
|
|
90
|
+
if (mcpConfig.mcpServers) {
|
|
91
|
+
servers.push(...Object.keys(mcpConfig.mcpServers));
|
|
92
|
+
}
|
|
93
|
+
} catch {
|
|
94
|
+
// Ignore parse errors
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Check agileflow-metadata.json for enabled MCP features
|
|
99
|
+
const metadataPath = path.join(projectRoot, 'docs', '00-meta', 'agileflow-metadata.json');
|
|
100
|
+
if (fs.existsSync(metadataPath)) {
|
|
101
|
+
try {
|
|
102
|
+
const metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf8'));
|
|
103
|
+
const mcp =
|
|
104
|
+
metadata.agileflow && metadata.agileflow.features && metadata.agileflow.features.mcp;
|
|
105
|
+
if (mcp) {
|
|
106
|
+
for (const [server, enabled] of Object.entries(mcp)) {
|
|
107
|
+
if (enabled && !servers.includes(server)) {
|
|
108
|
+
servers.push(server);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
} catch {
|
|
113
|
+
// Ignore parse errors
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return [...new Set(servers)];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// =============================================================================
|
|
121
|
+
// Tool Matching
|
|
122
|
+
// =============================================================================
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Tokenize a task description into searchable terms
|
|
126
|
+
* @param {string} text - Text to tokenize
|
|
127
|
+
* @returns {string[]} Lowercased tokens
|
|
128
|
+
*/
|
|
129
|
+
function tokenize(text) {
|
|
130
|
+
if (!text) return [];
|
|
131
|
+
return text
|
|
132
|
+
.toLowerCase()
|
|
133
|
+
.replace(/[^a-z0-9\s-]/g, ' ')
|
|
134
|
+
.split(/\s+/)
|
|
135
|
+
.filter(t => t.length > 1);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Score a tool's relevance to a task description
|
|
140
|
+
* @param {Object} tool - Tool definition from registry
|
|
141
|
+
* @param {string[]} taskTokens - Tokenized task description
|
|
142
|
+
* @returns {number} Relevance score (0-100)
|
|
143
|
+
*/
|
|
144
|
+
function scoreToolRelevance(tool, taskTokens) {
|
|
145
|
+
if (!tool.keywords || !Array.isArray(tool.keywords)) return 0;
|
|
146
|
+
if (taskTokens.length === 0) return 0;
|
|
147
|
+
|
|
148
|
+
let matchCount = 0;
|
|
149
|
+
const toolKeywords = tool.keywords.map(k => k.toLowerCase());
|
|
150
|
+
|
|
151
|
+
for (const token of taskTokens) {
|
|
152
|
+
for (const keyword of toolKeywords) {
|
|
153
|
+
// Exact match or substring match
|
|
154
|
+
if (keyword === token || keyword.includes(token) || token.includes(keyword)) {
|
|
155
|
+
matchCount++;
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (matchCount === 0) return 0;
|
|
162
|
+
|
|
163
|
+
// Score based on how many task tokens matched tool keywords
|
|
164
|
+
const coverage = matchCount / taskTokens.length;
|
|
165
|
+
// Bonus for tools where many keywords matched
|
|
166
|
+
const density = matchCount / toolKeywords.length;
|
|
167
|
+
|
|
168
|
+
return Math.round(coverage * 60 + density * 40);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// =============================================================================
|
|
172
|
+
// Public API
|
|
173
|
+
// =============================================================================
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Get tools relevant to a task description.
|
|
177
|
+
* Filters by configured MCP servers and scores by keyword relevance.
|
|
178
|
+
*
|
|
179
|
+
* @param {string} taskDescription - Natural language task description
|
|
180
|
+
* @param {Object} [options] - Options
|
|
181
|
+
* @param {string} [options.projectRoot] - Project root directory
|
|
182
|
+
* @param {number} [options.minScore] - Minimum relevance score (default: 15)
|
|
183
|
+
* @param {number} [options.maxResults] - Maximum tools to return (default: 10)
|
|
184
|
+
* @param {boolean} [options.includeUnconfigured] - Include tools from unconfigured servers (default: false)
|
|
185
|
+
* @returns {Object} Results with matched tools and metadata
|
|
186
|
+
*/
|
|
187
|
+
function getToolsForTask(taskDescription, options = {}) {
|
|
188
|
+
const {
|
|
189
|
+
projectRoot = process.cwd(),
|
|
190
|
+
minScore = 15,
|
|
191
|
+
maxResults = 10,
|
|
192
|
+
includeUnconfigured = false,
|
|
193
|
+
} = options;
|
|
194
|
+
|
|
195
|
+
const registry = loadBuiltinRegistry();
|
|
196
|
+
const configuredServers = detectConfiguredServers(projectRoot);
|
|
197
|
+
const taskTokens = tokenize(taskDescription);
|
|
198
|
+
|
|
199
|
+
const scoredTools = [];
|
|
200
|
+
|
|
201
|
+
for (const [serverName, tools] of Object.entries(registry)) {
|
|
202
|
+
if (!Array.isArray(tools)) continue;
|
|
203
|
+
|
|
204
|
+
const isConfigured = configuredServers.some(
|
|
205
|
+
s =>
|
|
206
|
+
s.toLowerCase().includes(serverName.toLowerCase()) ||
|
|
207
|
+
serverName.toLowerCase().includes(s.toLowerCase())
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
if (!isConfigured && !includeUnconfigured) continue;
|
|
211
|
+
|
|
212
|
+
for (const tool of tools) {
|
|
213
|
+
const score = scoreToolRelevance(tool, taskTokens);
|
|
214
|
+
if (score >= minScore) {
|
|
215
|
+
scoredTools.push({
|
|
216
|
+
...tool,
|
|
217
|
+
server: serverName,
|
|
218
|
+
score,
|
|
219
|
+
configured: isConfigured,
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Sort by score descending, then by configured status
|
|
226
|
+
scoredTools.sort((a, b) => {
|
|
227
|
+
if (a.configured !== b.configured) return a.configured ? -1 : 1;
|
|
228
|
+
return b.score - a.score;
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
const results = scoredTools.slice(0, maxResults);
|
|
232
|
+
|
|
233
|
+
return {
|
|
234
|
+
task: taskDescription,
|
|
235
|
+
configured_servers: configuredServers,
|
|
236
|
+
total_matched: scoredTools.length,
|
|
237
|
+
tools: results,
|
|
238
|
+
suggestions:
|
|
239
|
+
!includeUnconfigured && scoredTools.length === 0
|
|
240
|
+
? getUnconfiguredSuggestions(taskDescription, registry, configuredServers)
|
|
241
|
+
: [],
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Get suggestions for unconfigured servers that could help with a task
|
|
247
|
+
* @param {string} taskDescription - Task description
|
|
248
|
+
* @param {Object} registry - Full registry
|
|
249
|
+
* @param {string[]} configuredServers - Already configured servers
|
|
250
|
+
* @returns {Object[]} Suggested servers to configure
|
|
251
|
+
*/
|
|
252
|
+
function getUnconfiguredSuggestions(taskDescription, registry, configuredServers) {
|
|
253
|
+
const taskTokens = tokenize(taskDescription);
|
|
254
|
+
const suggestions = [];
|
|
255
|
+
|
|
256
|
+
for (const [serverName, tools] of Object.entries(registry)) {
|
|
257
|
+
if (!Array.isArray(tools)) continue;
|
|
258
|
+
|
|
259
|
+
const isConfigured = configuredServers.some(s =>
|
|
260
|
+
s.toLowerCase().includes(serverName.toLowerCase())
|
|
261
|
+
);
|
|
262
|
+
if (isConfigured) continue;
|
|
263
|
+
|
|
264
|
+
let bestScore = 0;
|
|
265
|
+
let bestTool = null;
|
|
266
|
+
for (const tool of tools) {
|
|
267
|
+
const score = scoreToolRelevance(tool, taskTokens);
|
|
268
|
+
if (score > bestScore) {
|
|
269
|
+
bestScore = score;
|
|
270
|
+
bestTool = tool;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (bestScore >= 20 && bestTool) {
|
|
275
|
+
suggestions.push({
|
|
276
|
+
server: serverName,
|
|
277
|
+
best_match: bestTool.name,
|
|
278
|
+
score: bestScore,
|
|
279
|
+
description: `Configure ${serverName} MCP server to use ${bestTool.name} (${bestTool.description})`,
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return suggestions.sort((a, b) => b.score - a.score).slice(0, 3);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Get list of available MCP servers (both configured and in registry)
|
|
289
|
+
* @param {string} [projectRoot] - Project root directory
|
|
290
|
+
* @returns {Object} Server listing
|
|
291
|
+
*/
|
|
292
|
+
function getAvailableServers(projectRoot = process.cwd()) {
|
|
293
|
+
const registry = loadBuiltinRegistry();
|
|
294
|
+
const configuredServers = detectConfiguredServers(projectRoot);
|
|
295
|
+
|
|
296
|
+
const servers = {};
|
|
297
|
+
for (const [name, tools] of Object.entries(registry)) {
|
|
298
|
+
if (!Array.isArray(tools)) continue;
|
|
299
|
+
servers[name] = {
|
|
300
|
+
tool_count: tools.length,
|
|
301
|
+
configured: configuredServers.some(
|
|
302
|
+
s =>
|
|
303
|
+
s.toLowerCase().includes(name.toLowerCase()) ||
|
|
304
|
+
name.toLowerCase().includes(s.toLowerCase())
|
|
305
|
+
),
|
|
306
|
+
tools: tools.map(t => t.name),
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return {
|
|
311
|
+
configured: configuredServers,
|
|
312
|
+
registry: servers,
|
|
313
|
+
total_servers: Object.keys(servers).length,
|
|
314
|
+
total_tools: Object.values(servers).reduce((sum, s) => sum + s.tool_count, 0),
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Get tool count statistics
|
|
320
|
+
* @returns {Object} Statistics
|
|
321
|
+
*/
|
|
322
|
+
function getToolCount() {
|
|
323
|
+
const registry = loadBuiltinRegistry();
|
|
324
|
+
let total = 0;
|
|
325
|
+
const byServer = {};
|
|
326
|
+
|
|
327
|
+
for (const [name, tools] of Object.entries(registry)) {
|
|
328
|
+
if (!Array.isArray(tools)) continue;
|
|
329
|
+
byServer[name] = tools.length;
|
|
330
|
+
total += tools.length;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return { total, byServer };
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Format tool results for display
|
|
338
|
+
* @param {Object} result - Result from getToolsForTask
|
|
339
|
+
* @returns {string} Formatted display text
|
|
340
|
+
*/
|
|
341
|
+
function formatToolResults(result) {
|
|
342
|
+
const lines = [];
|
|
343
|
+
|
|
344
|
+
lines.push(`## Tool Shed Results for: "${result.task}"`);
|
|
345
|
+
lines.push('');
|
|
346
|
+
|
|
347
|
+
if (result.configured_servers.length > 0) {
|
|
348
|
+
lines.push(`**Configured MCP servers**: ${result.configured_servers.join(', ')}`);
|
|
349
|
+
} else {
|
|
350
|
+
lines.push('**No MCP servers configured.** Tools are recommended based on registry only.');
|
|
351
|
+
}
|
|
352
|
+
lines.push('');
|
|
353
|
+
|
|
354
|
+
if (result.tools.length === 0) {
|
|
355
|
+
lines.push('No matching tools found.');
|
|
356
|
+
lines.push('');
|
|
357
|
+
} else {
|
|
358
|
+
lines.push(
|
|
359
|
+
`### Matched Tools (${result.total_matched} total, showing top ${result.tools.length})`
|
|
360
|
+
);
|
|
361
|
+
lines.push('');
|
|
362
|
+
|
|
363
|
+
for (const tool of result.tools) {
|
|
364
|
+
const status = tool.configured ? '✅' : '⚠️';
|
|
365
|
+
lines.push(`- ${status} **${tool.server}/${tool.name}** (score: ${tool.score})`);
|
|
366
|
+
lines.push(` ${tool.description}`);
|
|
367
|
+
if (!tool.configured) {
|
|
368
|
+
lines.push(` _Server not configured - add ${tool.server} to .mcp.json_`);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
lines.push('');
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
if (result.suggestions.length > 0) {
|
|
375
|
+
lines.push('### Suggested Servers to Configure');
|
|
376
|
+
lines.push('');
|
|
377
|
+
for (const suggestion of result.suggestions) {
|
|
378
|
+
lines.push(`- **${suggestion.server}** - ${suggestion.description}`);
|
|
379
|
+
}
|
|
380
|
+
lines.push('');
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
return lines.join('\n');
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// =============================================================================
|
|
387
|
+
// CLI
|
|
388
|
+
// =============================================================================
|
|
389
|
+
|
|
390
|
+
if (require.main === module) {
|
|
391
|
+
const args = process.argv.slice(2);
|
|
392
|
+
const projectRoot = getArg(args, '--project-root') || process.cwd();
|
|
393
|
+
|
|
394
|
+
if (args.includes('--list-servers')) {
|
|
395
|
+
const servers = getAvailableServers(projectRoot);
|
|
396
|
+
console.log(JSON.stringify(servers, null, 2));
|
|
397
|
+
} else if (args.includes('--stats')) {
|
|
398
|
+
const stats = getToolCount();
|
|
399
|
+
console.log(JSON.stringify(stats, null, 2));
|
|
400
|
+
} else {
|
|
401
|
+
const task = getArg(args, '--task');
|
|
402
|
+
if (!task) {
|
|
403
|
+
console.error('Usage: node tool-shed.js --task "description" [--project-root /path]');
|
|
404
|
+
console.error(' node tool-shed.js --list-servers [--project-root /path]');
|
|
405
|
+
console.error(' node tool-shed.js --stats');
|
|
406
|
+
process.exit(1);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const result = getToolsForTask(task, {
|
|
410
|
+
projectRoot,
|
|
411
|
+
includeUnconfigured: args.includes('--include-unconfigured'),
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
if (args.includes('--json')) {
|
|
415
|
+
console.log(JSON.stringify(result, null, 2));
|
|
416
|
+
} else {
|
|
417
|
+
console.log(formatToolResults(result));
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function getArg(args, flag) {
|
|
423
|
+
const idx = args.indexOf(flag);
|
|
424
|
+
if (idx === -1 || idx + 1 >= args.length) return null;
|
|
425
|
+
return args[idx + 1];
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// =============================================================================
|
|
429
|
+
// Exports
|
|
430
|
+
// =============================================================================
|
|
431
|
+
|
|
432
|
+
module.exports = {
|
|
433
|
+
loadBuiltinRegistry,
|
|
434
|
+
detectConfiguredServers,
|
|
435
|
+
tokenize,
|
|
436
|
+
scoreToolRelevance,
|
|
437
|
+
getToolsForTask,
|
|
438
|
+
getAvailableServers,
|
|
439
|
+
getToolCount,
|
|
440
|
+
formatToolResults,
|
|
441
|
+
};
|