claude-flow 3.5.58 → 3.5.59
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/package.json +1 -1
- package/v3/@claude-flow/cli/dist/src/mcp-server.js +3 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/daa-tools.js +25 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/github-tools.d.ts +2 -6
- package/v3/@claude-flow/cli/dist/src/mcp-tools/github-tools.js +277 -121
- package/v3/@claude-flow/cli/dist/src/mcp-tools/hive-mind-tools.js +30 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/hooks-tools.js +106 -12
- package/v3/@claude-flow/cli/dist/src/mcp-tools/neural-tools.js +177 -12
- package/v3/@claude-flow/cli/dist/src/mcp-tools/performance-tools.js +214 -11
- package/v3/@claude-flow/cli/dist/src/mcp-tools/request-tracker.d.ts +17 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/request-tracker.js +27 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/system-tools.js +11 -1
- package/v3/@claude-flow/cli/package.json +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-flow",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.59",
|
|
4
4
|
"description": "Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -23,6 +23,7 @@ import * as fs from 'fs';
|
|
|
23
23
|
import * as os from 'os';
|
|
24
24
|
import { fileURLToPath } from 'url';
|
|
25
25
|
import { dirname } from 'path';
|
|
26
|
+
import { trackRequest } from './mcp-tools/request-tracker.js';
|
|
26
27
|
// ESM-compatible __dirname
|
|
27
28
|
const __filename = fileURLToPath(import.meta.url);
|
|
28
29
|
const __dirname = dirname(__filename);
|
|
@@ -389,6 +390,7 @@ export class MCPServerManager extends EventEmitter {
|
|
|
389
390
|
}
|
|
390
391
|
try {
|
|
391
392
|
const result = await callMCPTool(toolName, toolParams, { sessionId });
|
|
393
|
+
trackRequest(toolName, true);
|
|
392
394
|
return {
|
|
393
395
|
jsonrpc: '2.0',
|
|
394
396
|
id: message.id,
|
|
@@ -396,6 +398,7 @@ export class MCPServerManager extends EventEmitter {
|
|
|
396
398
|
};
|
|
397
399
|
}
|
|
398
400
|
catch (error) {
|
|
401
|
+
trackRequest(toolName, false);
|
|
399
402
|
return {
|
|
400
403
|
jsonrpc: '2.0',
|
|
401
404
|
id: message.id,
|
|
@@ -83,6 +83,17 @@ export const daaTools = [
|
|
|
83
83
|
};
|
|
84
84
|
store.agents[id] = agent;
|
|
85
85
|
saveDAAStore(store);
|
|
86
|
+
// Store agent in AgentDB for searchable agent registry
|
|
87
|
+
try {
|
|
88
|
+
const bridge = await import('../memory/memory-bridge.js');
|
|
89
|
+
await bridge.bridgeStoreEntry({
|
|
90
|
+
key: `daa-agent-${id}`,
|
|
91
|
+
value: JSON.stringify({ id: agent.id, name: agent.name, type: agent.type, cognitivePattern: agent.cognitivePattern }),
|
|
92
|
+
namespace: 'daa-agents',
|
|
93
|
+
tags: [agent.type, agent.cognitivePattern],
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
catch { /* AgentDB not available */ }
|
|
86
97
|
return {
|
|
87
98
|
success: true,
|
|
88
99
|
agent: {
|
|
@@ -215,6 +226,20 @@ export const daaTools = [
|
|
|
215
226
|
}
|
|
216
227
|
workflow.status = 'running';
|
|
217
228
|
saveDAAStore(store);
|
|
229
|
+
// Store workflow state in AgentDB for tracking
|
|
230
|
+
try {
|
|
231
|
+
const bridge = await import('../memory/memory-bridge.js');
|
|
232
|
+
await bridge.bridgeStoreEntry({
|
|
233
|
+
key: `workflow-${workflowId}`,
|
|
234
|
+
value: JSON.stringify({
|
|
235
|
+
id: workflowId, status: 'running',
|
|
236
|
+
steps: workflow.steps.length, strategy: workflow.strategy,
|
|
237
|
+
startedAt: new Date().toISOString(),
|
|
238
|
+
}),
|
|
239
|
+
namespace: 'daa-workflows',
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
catch { /* AgentDB not available */ }
|
|
218
243
|
return {
|
|
219
244
|
success: true,
|
|
220
245
|
workflowId,
|
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* GitHub MCP Tools for CLI
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* ⚠️ IMPORTANT: These tools provide LOCAL STATE MANAGEMENT only.
|
|
7
|
-
* - NO actual GitHub API calls are made
|
|
8
|
-
* - Data is stored locally for workflow coordination
|
|
9
|
-
* - For real GitHub operations, use `gh` CLI or GitHub MCP server
|
|
4
|
+
* Real GitHub integration via `gh` CLI and `git` commands.
|
|
5
|
+
* Falls back to local state management when CLI tools are unavailable.
|
|
10
6
|
*/
|
|
11
7
|
import { type MCPTool } from './types.js';
|
|
12
8
|
export declare const githubTools: MCPTool[];
|
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* GitHub MCP Tools for CLI
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* ⚠️ IMPORTANT: These tools provide LOCAL STATE MANAGEMENT only.
|
|
7
|
-
* - NO actual GitHub API calls are made
|
|
8
|
-
* - Data is stored locally for workflow coordination
|
|
9
|
-
* - For real GitHub operations, use `gh` CLI or GitHub MCP server
|
|
4
|
+
* Real GitHub integration via `gh` CLI and `git` commands.
|
|
5
|
+
* Falls back to local state management when CLI tools are unavailable.
|
|
10
6
|
*/
|
|
11
7
|
import { getProjectCwd } from './types.js';
|
|
12
8
|
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
13
9
|
import { join } from 'node:path';
|
|
10
|
+
import { execSync } from 'node:child_process';
|
|
14
11
|
// Storage paths
|
|
15
12
|
const STORAGE_DIR = '.claude-flow';
|
|
16
13
|
const GITHUB_DIR = 'github';
|
|
@@ -43,6 +40,19 @@ function saveGitHubStore(store) {
|
|
|
43
40
|
ensureGitHubDir();
|
|
44
41
|
writeFileSync(getGitHubPath(), JSON.stringify(store, null, 2), 'utf-8');
|
|
45
42
|
}
|
|
43
|
+
/** Run a shell command, return stdout or null on failure */
|
|
44
|
+
function run(cmd, cwd) {
|
|
45
|
+
try {
|
|
46
|
+
return execSync(cmd, { encoding: 'utf-8', timeout: 15000, cwd: cwd || getProjectCwd(), stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/** Check if gh CLI is available */
|
|
53
|
+
function hasGhCli() {
|
|
54
|
+
return run('gh --version') !== null;
|
|
55
|
+
}
|
|
46
56
|
export const githubTools = [
|
|
47
57
|
{
|
|
48
58
|
name: 'github_repo_analyze',
|
|
@@ -59,28 +69,66 @@ export const githubTools = [
|
|
|
59
69
|
},
|
|
60
70
|
handler: async (input) => {
|
|
61
71
|
const store = loadGitHubStore();
|
|
62
|
-
const owner = input.owner || 'owner';
|
|
63
|
-
const repo = input.repo || 'repo';
|
|
64
72
|
const branch = input.branch || 'main';
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
73
|
+
const cwd = getProjectCwd();
|
|
74
|
+
// Try real git analysis first
|
|
75
|
+
const commitCount = run('git rev-list --count HEAD', cwd);
|
|
76
|
+
const branchCount = run('git branch -a --no-color | wc -l', cwd);
|
|
77
|
+
const contributors = run('git shortlog -sn --no-merges HEAD | wc -l', cwd);
|
|
78
|
+
const currentBranch = run('git rev-parse --abbrev-ref HEAD', cwd);
|
|
79
|
+
const remoteUrl = run('git remote get-url origin', cwd);
|
|
80
|
+
// Parse owner/repo from remote URL
|
|
81
|
+
let owner = input.owner || '';
|
|
82
|
+
let repo = input.repo || '';
|
|
83
|
+
if (remoteUrl && (!owner || !repo)) {
|
|
84
|
+
const m = remoteUrl.match(/[:/]([^/]+)\/([^/.]+?)(?:\.git)?$/);
|
|
85
|
+
if (m) {
|
|
86
|
+
owner = owner || m[1];
|
|
87
|
+
repo = repo || m[2];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const repoKey = `${owner || 'local'}/${repo || 'repo'}`;
|
|
91
|
+
if (commitCount !== null) {
|
|
92
|
+
// Real git data available
|
|
93
|
+
const repoInfo = {
|
|
94
|
+
owner: owner || 'local',
|
|
95
|
+
name: repo || 'repo',
|
|
96
|
+
branch: currentBranch || branch,
|
|
97
|
+
lastAnalyzed: new Date().toISOString(),
|
|
98
|
+
metrics: {
|
|
99
|
+
commits: parseInt(commitCount, 10) || 0,
|
|
100
|
+
branches: parseInt(branchCount || '0', 10) || 0,
|
|
101
|
+
contributors: parseInt(contributors || '0', 10) || 0,
|
|
102
|
+
openIssues: 0,
|
|
103
|
+
openPRs: 0,
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
// Try gh CLI for issue/PR counts
|
|
107
|
+
if (hasGhCli()) {
|
|
108
|
+
const issueCount = run(`gh issue list --state open --limit 1000 --json number --jq 'length'`);
|
|
109
|
+
const prCount = run(`gh pr list --state open --limit 1000 --json number --jq 'length'`);
|
|
110
|
+
if (issueCount !== null)
|
|
111
|
+
repoInfo.metrics.openIssues = parseInt(issueCount, 10) || 0;
|
|
112
|
+
if (prCount !== null)
|
|
113
|
+
repoInfo.metrics.openPRs = parseInt(prCount, 10) || 0;
|
|
114
|
+
}
|
|
115
|
+
store.repos[repoKey] = repoInfo;
|
|
116
|
+
saveGitHubStore(store);
|
|
117
|
+
return {
|
|
118
|
+
success: true,
|
|
119
|
+
_real: true,
|
|
79
120
|
repository: repoKey,
|
|
80
|
-
branch,
|
|
121
|
+
branch: repoInfo.branch,
|
|
122
|
+
metrics: repoInfo.metrics,
|
|
123
|
+
remoteUrl: remoteUrl || null,
|
|
81
124
|
lastAnalyzed: repoInfo.lastAnalyzed,
|
|
82
|
-
|
|
83
|
-
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
// No git — return local store data
|
|
128
|
+
return {
|
|
129
|
+
success: false,
|
|
130
|
+
error: 'Not a git repository or git not available.',
|
|
131
|
+
localData: { storedRepos: Object.keys(store.repos) },
|
|
84
132
|
};
|
|
85
133
|
},
|
|
86
134
|
},
|
|
@@ -104,73 +152,82 @@ export const githubTools = [
|
|
|
104
152
|
handler: async (input) => {
|
|
105
153
|
const store = loadGitHubStore();
|
|
106
154
|
const action = input.action || 'list';
|
|
107
|
-
const
|
|
108
|
-
const repo = input.repo || 'repo';
|
|
155
|
+
const gh = hasGhCli();
|
|
109
156
|
if (action === 'list') {
|
|
157
|
+
if (gh) {
|
|
158
|
+
const raw = run('gh pr list --state all --limit 20 --json number,title,state,headRefName,createdAt');
|
|
159
|
+
if (raw) {
|
|
160
|
+
try {
|
|
161
|
+
const prs = JSON.parse(raw);
|
|
162
|
+
return { success: true, _real: true, source: 'gh-cli', pullRequests: prs, total: prs.length };
|
|
163
|
+
}
|
|
164
|
+
catch { /* fall through */ }
|
|
165
|
+
}
|
|
166
|
+
}
|
|
110
167
|
const prs = Object.values(store.prs);
|
|
111
|
-
return {
|
|
112
|
-
success: true,
|
|
113
|
-
pullRequests: prs,
|
|
114
|
-
total: prs.length,
|
|
115
|
-
open: prs.filter(pr => pr.status === 'open').length,
|
|
116
|
-
};
|
|
168
|
+
return { success: true, source: 'local-store', pullRequests: prs, total: prs.length, open: prs.filter(pr => pr.status === 'open').length };
|
|
117
169
|
}
|
|
118
170
|
if (action === 'create') {
|
|
171
|
+
if (gh) {
|
|
172
|
+
const title = input.title || 'New PR';
|
|
173
|
+
const headBranch = input.branch || run('git rev-parse --abbrev-ref HEAD') || 'feature';
|
|
174
|
+
const baseBranch = input.baseBranch || 'main';
|
|
175
|
+
const body = input.body || '';
|
|
176
|
+
const result = run(`gh pr create --title "${title.replace(/"/g, '\\"')}" --base "${baseBranch}" --head "${headBranch}" --body "${body.replace(/"/g, '\\"')}"`);
|
|
177
|
+
if (result) {
|
|
178
|
+
return { success: true, _real: true, action: 'created', url: result };
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// Fallback: local store
|
|
119
182
|
const prId = `pr-${Date.now()}`;
|
|
120
|
-
const pr = {
|
|
121
|
-
id: prId,
|
|
122
|
-
title: input.title || 'New PR',
|
|
123
|
-
status: 'open',
|
|
124
|
-
branch: input.branch || 'feature',
|
|
125
|
-
baseBranch: input.baseBranch || 'main',
|
|
126
|
-
createdAt: new Date().toISOString(),
|
|
127
|
-
};
|
|
183
|
+
const pr = { id: prId, title: input.title || 'New PR', status: 'open', branch: input.branch || 'feature', baseBranch: input.baseBranch || 'main', createdAt: new Date().toISOString() };
|
|
128
184
|
store.prs[prId] = pr;
|
|
129
185
|
saveGitHubStore(store);
|
|
130
|
-
return {
|
|
131
|
-
success: true,
|
|
132
|
-
action: 'created',
|
|
133
|
-
pullRequest: pr,
|
|
134
|
-
url: `https://github.com/${owner}/${repo}/pull/${prId}`,
|
|
135
|
-
};
|
|
186
|
+
return { success: true, source: 'local-store', action: 'created', pullRequest: pr };
|
|
136
187
|
}
|
|
137
188
|
if (action === 'review') {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
189
|
+
const prNumber = input.prNumber;
|
|
190
|
+
if (gh && prNumber) {
|
|
191
|
+
const raw = run(`gh pr view ${prNumber} --json number,title,state,body,additions,deletions,changedFiles,reviews,mergeable,statusCheckRollup`);
|
|
192
|
+
if (raw) {
|
|
193
|
+
try {
|
|
194
|
+
return { success: true, _real: true, action: 'review', pullRequest: JSON.parse(raw) };
|
|
195
|
+
}
|
|
196
|
+
catch { /* fall through */ }
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return { success: false, error: prNumber ? 'gh CLI not available or PR not found. Install gh: https://cli.github.com' : 'prNumber is required for review.' };
|
|
146
200
|
}
|
|
147
201
|
if (action === 'merge') {
|
|
148
202
|
const prNumber = input.prNumber;
|
|
203
|
+
if (gh && prNumber) {
|
|
204
|
+
const result = run(`gh pr merge ${prNumber} --merge`);
|
|
205
|
+
if (result !== null) {
|
|
206
|
+
return { success: true, _real: true, action: 'merged', prNumber, mergedAt: new Date().toISOString() };
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
// Fallback: local store
|
|
149
210
|
const prKey = Object.keys(store.prs).find(k => k.includes(String(prNumber)));
|
|
150
211
|
if (prKey && store.prs[prKey]) {
|
|
151
212
|
store.prs[prKey].status = 'merged';
|
|
152
213
|
saveGitHubStore(store);
|
|
153
214
|
}
|
|
154
|
-
return {
|
|
155
|
-
success: true,
|
|
156
|
-
action: 'merged',
|
|
157
|
-
prNumber,
|
|
158
|
-
mergedAt: new Date().toISOString(),
|
|
159
|
-
};
|
|
215
|
+
return { success: true, source: 'local-store', action: 'merged', prNumber, mergedAt: new Date().toISOString() };
|
|
160
216
|
}
|
|
161
217
|
if (action === 'close') {
|
|
162
218
|
const prNumber = input.prNumber;
|
|
219
|
+
if (gh && prNumber) {
|
|
220
|
+
const result = run(`gh pr close ${prNumber}`);
|
|
221
|
+
if (result !== null) {
|
|
222
|
+
return { success: true, _real: true, action: 'closed', prNumber, closedAt: new Date().toISOString() };
|
|
223
|
+
}
|
|
224
|
+
}
|
|
163
225
|
const prKey = Object.keys(store.prs).find(k => k.includes(String(prNumber)));
|
|
164
226
|
if (prKey && store.prs[prKey]) {
|
|
165
227
|
store.prs[prKey].status = 'closed';
|
|
166
228
|
saveGitHubStore(store);
|
|
167
229
|
}
|
|
168
|
-
return {
|
|
169
|
-
success: true,
|
|
170
|
-
action: 'closed',
|
|
171
|
-
prNumber,
|
|
172
|
-
closedAt: new Date().toISOString(),
|
|
173
|
-
};
|
|
230
|
+
return { success: true, source: 'local-store', action: 'closed', prNumber, closedAt: new Date().toISOString() };
|
|
174
231
|
}
|
|
175
232
|
return { success: false, error: 'Unknown action' };
|
|
176
233
|
},
|
|
@@ -195,34 +252,52 @@ export const githubTools = [
|
|
|
195
252
|
handler: async (input) => {
|
|
196
253
|
const store = loadGitHubStore();
|
|
197
254
|
const action = input.action || 'list';
|
|
255
|
+
const gh = hasGhCli();
|
|
198
256
|
if (action === 'list') {
|
|
257
|
+
if (gh) {
|
|
258
|
+
const raw = run('gh issue list --state all --limit 20 --json number,title,state,labels,createdAt');
|
|
259
|
+
if (raw) {
|
|
260
|
+
try {
|
|
261
|
+
const issues = JSON.parse(raw);
|
|
262
|
+
return { success: true, _real: true, source: 'gh-cli', issues, total: issues.length };
|
|
263
|
+
}
|
|
264
|
+
catch { /* fall through */ }
|
|
265
|
+
}
|
|
266
|
+
}
|
|
199
267
|
const issues = Object.values(store.issues);
|
|
200
|
-
return {
|
|
201
|
-
success: true,
|
|
202
|
-
issues,
|
|
203
|
-
total: issues.length,
|
|
204
|
-
open: issues.filter(i => i.status === 'open').length,
|
|
205
|
-
};
|
|
268
|
+
return { success: true, source: 'local-store', issues, total: issues.length, open: issues.filter(i => i.status === 'open').length };
|
|
206
269
|
}
|
|
207
270
|
if (action === 'create') {
|
|
271
|
+
const title = input.title || 'New Issue';
|
|
272
|
+
const body = input.body || '';
|
|
273
|
+
const labels = input.labels || [];
|
|
274
|
+
if (gh) {
|
|
275
|
+
const labelArg = labels.length > 0 ? ` --label "${labels.join(',')}"` : '';
|
|
276
|
+
const result = run(`gh issue create --title "${title.replace(/"/g, '\\"')}" --body "${body.replace(/"/g, '\\"')}"${labelArg}`);
|
|
277
|
+
if (result) {
|
|
278
|
+
return { success: true, _real: true, action: 'created', url: result };
|
|
279
|
+
}
|
|
280
|
+
}
|
|
208
281
|
const issueId = `issue-${Date.now()}`;
|
|
209
|
-
const issue = {
|
|
210
|
-
id: issueId,
|
|
211
|
-
title: input.title || 'New Issue',
|
|
212
|
-
status: 'open',
|
|
213
|
-
labels: input.labels || [],
|
|
214
|
-
createdAt: new Date().toISOString(),
|
|
215
|
-
};
|
|
282
|
+
const issue = { id: issueId, title, status: 'open', labels, createdAt: new Date().toISOString() };
|
|
216
283
|
store.issues[issueId] = issue;
|
|
217
284
|
saveGitHubStore(store);
|
|
218
|
-
return {
|
|
219
|
-
success: true,
|
|
220
|
-
action: 'created',
|
|
221
|
-
issue,
|
|
222
|
-
};
|
|
285
|
+
return { success: true, source: 'local-store', action: 'created', issue };
|
|
223
286
|
}
|
|
224
287
|
if (action === 'update') {
|
|
225
288
|
const issueNumber = input.issueNumber;
|
|
289
|
+
if (gh && issueNumber) {
|
|
290
|
+
const parts = [];
|
|
291
|
+
if (input.title)
|
|
292
|
+
parts.push(`--title "${input.title.replace(/"/g, '\\"')}"`);
|
|
293
|
+
if (input.labels)
|
|
294
|
+
parts.push(`--add-label "${input.labels.join(',')}"`);
|
|
295
|
+
if (parts.length > 0) {
|
|
296
|
+
const result = run(`gh issue edit ${issueNumber} ${parts.join(' ')}`);
|
|
297
|
+
if (result !== null)
|
|
298
|
+
return { success: true, _real: true, action: 'updated', issueNumber };
|
|
299
|
+
}
|
|
300
|
+
}
|
|
226
301
|
const issueKey = Object.keys(store.issues).find(k => k.includes(String(issueNumber)));
|
|
227
302
|
if (issueKey && store.issues[issueKey]) {
|
|
228
303
|
if (input.title)
|
|
@@ -231,25 +306,21 @@ export const githubTools = [
|
|
|
231
306
|
store.issues[issueKey].labels = input.labels;
|
|
232
307
|
saveGitHubStore(store);
|
|
233
308
|
}
|
|
234
|
-
return {
|
|
235
|
-
success: true,
|
|
236
|
-
action: 'updated',
|
|
237
|
-
issueNumber,
|
|
238
|
-
};
|
|
309
|
+
return { success: true, source: 'local-store', action: 'updated', issueNumber };
|
|
239
310
|
}
|
|
240
311
|
if (action === 'close') {
|
|
241
312
|
const issueNumber = input.issueNumber;
|
|
313
|
+
if (gh && issueNumber) {
|
|
314
|
+
const result = run(`gh issue close ${issueNumber}`);
|
|
315
|
+
if (result !== null)
|
|
316
|
+
return { success: true, _real: true, action: 'closed', issueNumber, closedAt: new Date().toISOString() };
|
|
317
|
+
}
|
|
242
318
|
const issueKey = Object.keys(store.issues).find(k => k.includes(String(issueNumber)));
|
|
243
319
|
if (issueKey && store.issues[issueKey]) {
|
|
244
320
|
store.issues[issueKey].status = 'closed';
|
|
245
321
|
saveGitHubStore(store);
|
|
246
322
|
}
|
|
247
|
-
return {
|
|
248
|
-
success: true,
|
|
249
|
-
action: 'closed',
|
|
250
|
-
issueNumber,
|
|
251
|
-
closedAt: new Date().toISOString(),
|
|
252
|
-
};
|
|
323
|
+
return { success: true, source: 'local-store', action: 'closed', issueNumber, closedAt: new Date().toISOString() };
|
|
253
324
|
}
|
|
254
325
|
return { success: false, error: 'Unknown action' };
|
|
255
326
|
},
|
|
@@ -270,16 +341,66 @@ export const githubTools = [
|
|
|
270
341
|
},
|
|
271
342
|
handler: async (input) => {
|
|
272
343
|
const action = input.action || 'list';
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
344
|
+
const gh = hasGhCli();
|
|
345
|
+
if (!gh) {
|
|
346
|
+
return { success: false, error: 'gh CLI not available. Install: https://cli.github.com' };
|
|
347
|
+
}
|
|
348
|
+
if (action === 'list') {
|
|
349
|
+
const raw = run('gh run list --limit 10 --json databaseId,displayTitle,status,conclusion,headBranch,createdAt');
|
|
350
|
+
if (raw) {
|
|
351
|
+
try {
|
|
352
|
+
return { success: true, _real: true, runs: JSON.parse(raw) };
|
|
353
|
+
}
|
|
354
|
+
catch { /* fall through */ }
|
|
355
|
+
}
|
|
356
|
+
const workflows = run('gh workflow list --json id,name,state');
|
|
357
|
+
if (workflows) {
|
|
358
|
+
try {
|
|
359
|
+
return { success: true, _real: true, workflows: JSON.parse(workflows) };
|
|
360
|
+
}
|
|
361
|
+
catch { /* fall through */ }
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
if (action === 'status') {
|
|
365
|
+
const workflowId = input.workflowId;
|
|
366
|
+
if (workflowId) {
|
|
367
|
+
const raw = run(`gh run view ${workflowId} --json databaseId,displayTitle,status,conclusion,jobs`);
|
|
368
|
+
if (raw) {
|
|
369
|
+
try {
|
|
370
|
+
return { success: true, _real: true, run: JSON.parse(raw) };
|
|
371
|
+
}
|
|
372
|
+
catch { /* fall through */ }
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
// List recent runs as fallback
|
|
376
|
+
const recent = run('gh run list --limit 5 --json databaseId,displayTitle,status,conclusion');
|
|
377
|
+
if (recent) {
|
|
378
|
+
try {
|
|
379
|
+
return { success: true, _real: true, recentRuns: JSON.parse(recent) };
|
|
380
|
+
}
|
|
381
|
+
catch { /* fall through */ }
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
if (action === 'trigger') {
|
|
385
|
+
const workflowId = input.workflowId;
|
|
386
|
+
const ref = input.ref || 'main';
|
|
387
|
+
if (workflowId) {
|
|
388
|
+
const result = run(`gh workflow run "${workflowId}" --ref "${ref}"`);
|
|
389
|
+
if (result !== null)
|
|
390
|
+
return { success: true, _real: true, action: 'triggered', workflowId, ref };
|
|
391
|
+
}
|
|
392
|
+
return { success: false, error: 'workflowId is required to trigger a workflow.' };
|
|
393
|
+
}
|
|
394
|
+
if (action === 'cancel') {
|
|
395
|
+
const workflowId = input.workflowId;
|
|
396
|
+
if (workflowId) {
|
|
397
|
+
const result = run(`gh run cancel ${workflowId}`);
|
|
398
|
+
if (result !== null)
|
|
399
|
+
return { success: true, _real: true, action: 'cancelled', runId: workflowId };
|
|
400
|
+
}
|
|
401
|
+
return { success: false, error: 'workflowId (run ID) is required to cancel.' };
|
|
402
|
+
}
|
|
403
|
+
return { success: false, error: `Unknown action: ${action}` };
|
|
283
404
|
},
|
|
284
405
|
},
|
|
285
406
|
{
|
|
@@ -292,24 +413,59 @@ export const githubTools = [
|
|
|
292
413
|
owner: { type: 'string', description: 'Repository owner' },
|
|
293
414
|
repo: { type: 'string', description: 'Repository name' },
|
|
294
415
|
metric: { type: 'string', enum: ['all', 'commits', 'contributors', 'traffic', 'releases'], description: 'Metric type' },
|
|
295
|
-
timeRange: { type: 'string', description: 'Time range' },
|
|
416
|
+
timeRange: { type: 'string', description: 'Time range (e.g., "7d", "30d", "90d")' },
|
|
296
417
|
},
|
|
297
418
|
},
|
|
298
419
|
handler: async (input) => {
|
|
299
|
-
const
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
420
|
+
const metric = input.metric || 'all';
|
|
421
|
+
const timeRange = input.timeRange || '30d';
|
|
422
|
+
const cwd = getProjectCwd();
|
|
423
|
+
// Parse time range
|
|
424
|
+
const days = parseInt(timeRange, 10) || 30;
|
|
425
|
+
const since = new Date(Date.now() - days * 86400000).toISOString().split('T')[0];
|
|
426
|
+
const result = { _real: true, timeRange: `${days}d`, since };
|
|
427
|
+
const wantAll = metric === 'all';
|
|
428
|
+
if (wantAll || metric === 'commits') {
|
|
429
|
+
const total = run(`git rev-list --count HEAD`, cwd);
|
|
430
|
+
const recent = run(`git rev-list --count --since="${since}" HEAD`, cwd);
|
|
431
|
+
result.commits = {
|
|
432
|
+
total: parseInt(total || '0', 10),
|
|
433
|
+
sincePeriod: parseInt(recent || '0', 10),
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
if (wantAll || metric === 'contributors') {
|
|
437
|
+
const allContrib = run('git shortlog -sn --no-merges HEAD', cwd);
|
|
438
|
+
if (allContrib) {
|
|
439
|
+
const lines = allContrib.split('\n').filter(Boolean);
|
|
440
|
+
result.contributors = {
|
|
441
|
+
total: lines.length,
|
|
442
|
+
top: lines.slice(0, 10).map(l => {
|
|
443
|
+
const m = l.trim().match(/^(\d+)\t(.+)$/);
|
|
444
|
+
return m ? { commits: parseInt(m[1], 10), name: m[2].trim() } : null;
|
|
445
|
+
}).filter(Boolean),
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
if (wantAll || metric === 'releases') {
|
|
450
|
+
if (hasGhCli()) {
|
|
451
|
+
const raw = run('gh release list --limit 10 --json tagName,name,publishedAt,isPrerelease');
|
|
452
|
+
if (raw) {
|
|
453
|
+
try {
|
|
454
|
+
result.releases = JSON.parse(raw);
|
|
455
|
+
}
|
|
456
|
+
catch { /* skip */ }
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
if (!result.releases) {
|
|
460
|
+
const tags = run('git tag --sort=-creatordate | head -10', cwd);
|
|
461
|
+
result.releases = tags ? tags.split('\n').filter(Boolean).map(t => ({ tagName: t })) : [];
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
// Always include branch info
|
|
465
|
+
const branchCount = run('git branch -a --no-color | wc -l', cwd);
|
|
466
|
+
const currentBranch = run('git rev-parse --abbrev-ref HEAD', cwd);
|
|
467
|
+
result.branches = { total: parseInt(branchCount || '0', 10), current: currentBranch };
|
|
468
|
+
return { success: true, ...result };
|
|
313
469
|
},
|
|
314
470
|
},
|
|
315
471
|
];
|
|
@@ -574,6 +574,26 @@ export const hiveMindTools = [
|
|
|
574
574
|
state.consensus.pending = state.consensus.pending.filter(p => p.proposalId !== proposal.proposalId);
|
|
575
575
|
}
|
|
576
576
|
saveHiveState(state);
|
|
577
|
+
// Persist consensus result in AgentDB for searchable history
|
|
578
|
+
if (resolved) {
|
|
579
|
+
try {
|
|
580
|
+
const bridge = await import('../memory/memory-bridge.js');
|
|
581
|
+
await bridge.bridgeStoreEntry({
|
|
582
|
+
key: `consensus-${proposal.proposalId}`,
|
|
583
|
+
value: JSON.stringify({
|
|
584
|
+
proposalId: proposal.proposalId,
|
|
585
|
+
type: proposal.type,
|
|
586
|
+
strategy: proposalStrategy,
|
|
587
|
+
status: proposal.status,
|
|
588
|
+
votes: proposal.votes,
|
|
589
|
+
resolvedAt: new Date().toISOString(),
|
|
590
|
+
}),
|
|
591
|
+
namespace: 'hive-consensus',
|
|
592
|
+
tags: [proposal.type, proposalStrategy || 'raft', proposal.status],
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
catch { /* AgentDB not available — JSON store is primary */ }
|
|
596
|
+
}
|
|
577
597
|
return {
|
|
578
598
|
action,
|
|
579
599
|
proposalId: proposal.proposalId,
|
|
@@ -779,6 +799,16 @@ export const hiveMindTools = [
|
|
|
779
799
|
return { action, error: 'Key required' };
|
|
780
800
|
state.sharedMemory[key] = input.value;
|
|
781
801
|
saveHiveState(state);
|
|
802
|
+
// Also store in AgentDB for searchable hive memory
|
|
803
|
+
try {
|
|
804
|
+
const bridge = await import('../memory/memory-bridge.js');
|
|
805
|
+
await bridge.bridgeStoreEntry({
|
|
806
|
+
key: `hive-memory-${key}`,
|
|
807
|
+
value: JSON.stringify(input.value),
|
|
808
|
+
namespace: 'hive-memory',
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
catch { /* AgentDB not available */ }
|
|
782
812
|
return {
|
|
783
813
|
action,
|
|
784
814
|
key,
|