@agilemonster/mcp-server 1.0.10 → 1.0.11
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/dist/client.d.ts +1 -1
- package/dist/client.js +1 -1
- package/dist/server.d.ts +1 -0
- package/dist/server.js +131 -4
- package/dist/types.d.ts +1 -0
- package/package.json +1 -1
package/dist/client.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AgileMonsterProjectConfig, AutonomousStepResponse, Board, BoardAgentConfig, Card, ContextResponse, DiscoveredProject, PulseResponse } from './types.js';
|
|
2
|
-
export declare const MCP_SERVER_VERSION = "1.0.
|
|
2
|
+
export declare const MCP_SERVER_VERSION = "1.0.11";
|
|
3
3
|
export declare class AgileMonsterClient {
|
|
4
4
|
private apiKey;
|
|
5
5
|
private baseUrl;
|
package/dist/client.js
CHANGED
package/dist/server.d.ts
CHANGED
|
@@ -36,6 +36,7 @@ export declare function createServer(client?: AgileMonsterClient): Server<{
|
|
|
36
36
|
} | undefined;
|
|
37
37
|
}>;
|
|
38
38
|
export declare function executeQueuedCardAutonomously(client: AgileMonsterClient, card: Card, projectPath: string): Promise<boolean>;
|
|
39
|
+
export declare function deployApprovedCardIfNeeded(client: AgileMonsterClient, card: Card, projectPath: string): Promise<boolean>;
|
|
39
40
|
export declare function startBuiltInPullPulse(server: Server | null, client: AgileMonsterClient, intervalMs?: number): NodeJS.Timeout;
|
|
40
41
|
export declare function runStdioServer(): Promise<void>;
|
|
41
42
|
export declare function runBackgroundPulseDaemon(intervalMs?: number): Promise<void>;
|
package/dist/server.js
CHANGED
|
@@ -3,7 +3,8 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
|
3
3
|
import { CallToolRequestSchema, ListToolsRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import fs from 'node:fs';
|
|
6
|
-
import
|
|
6
|
+
import os from 'node:os';
|
|
7
|
+
import { spawn, execSync } from 'node:child_process';
|
|
7
8
|
import { AgileMonsterClient, MCP_SERVER_VERSION } from './client.js';
|
|
8
9
|
import { AGENT_GUIDELINES_MARKDOWN } from './guidelines.js';
|
|
9
10
|
export function createServer(client = new AgileMonsterClient()) {
|
|
@@ -876,12 +877,22 @@ function collectProjectSourceFiles(projectPath, keywords) {
|
|
|
876
877
|
results.sort((a, b) => b.score - a.score || a.rel.localeCompare(b.rel));
|
|
877
878
|
return results.slice(0, 220).map((r) => r.rel);
|
|
878
879
|
}
|
|
880
|
+
function getAgileMonsterLocksDir() {
|
|
881
|
+
const dir = path.join(os.homedir(), '.agilemonster', 'locks');
|
|
882
|
+
try {
|
|
883
|
+
if (!fs.existsSync(dir)) {
|
|
884
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
catch { }
|
|
888
|
+
return dir;
|
|
889
|
+
}
|
|
879
890
|
export async function executeQueuedCardAutonomously(client, card, projectPath) {
|
|
880
891
|
if (!projectPath || !fs.existsSync(projectPath))
|
|
881
892
|
return false;
|
|
882
893
|
if (activeAutonomousCards.has(card.id))
|
|
883
894
|
return false;
|
|
884
|
-
const lockFile = path.join(
|
|
895
|
+
const lockFile = path.join(getAgileMonsterLocksDir(), `task-${card.id.slice(0, 8)}.lock`);
|
|
885
896
|
try {
|
|
886
897
|
if (fs.existsSync(lockFile)) {
|
|
887
898
|
const stat = fs.statSync(lockFile);
|
|
@@ -947,6 +958,7 @@ export async function executeQueuedCardAutonomously(client, card, projectPath) {
|
|
|
947
958
|
});
|
|
948
959
|
const edits = editsRes.edits || [];
|
|
949
960
|
const modifiedFiles = [];
|
|
961
|
+
const originalContents = new Map();
|
|
950
962
|
for (const edit of edits) {
|
|
951
963
|
const relPath = edit.file || edit.path;
|
|
952
964
|
if (!relPath || !edit.search || typeof edit.replace !== 'string')
|
|
@@ -957,9 +969,19 @@ export async function executeQueuedCardAutonomously(client, card, projectPath) {
|
|
|
957
969
|
if (!fs.existsSync(fullPath))
|
|
958
970
|
continue;
|
|
959
971
|
const currentContent = fs.readFileSync(fullPath, 'utf8');
|
|
972
|
+
if (!originalContents.has(fullPath)) {
|
|
973
|
+
originalContents.set(fullPath, currentContent);
|
|
974
|
+
}
|
|
960
975
|
if (currentContent.includes(edit.search)) {
|
|
961
976
|
const updatedContent = currentContent.replace(edit.search, edit.replace);
|
|
962
977
|
if (updatedContent !== currentContent) {
|
|
978
|
+
// Guard against unbalanced braces introduced by partial block replacement
|
|
979
|
+
const countChar = (s, ch) => s.split(ch).length - 1;
|
|
980
|
+
const searchBraceDelta = countChar(edit.search, '{') - countChar(edit.search, '}');
|
|
981
|
+
const replaceBraceDelta = countChar(edit.replace, '{') - countChar(edit.replace, '}');
|
|
982
|
+
if (searchBraceDelta !== replaceBraceDelta) {
|
|
983
|
+
continue;
|
|
984
|
+
}
|
|
963
985
|
fs.writeFileSync(fullPath, updatedContent, 'utf8');
|
|
964
986
|
if (!modifiedFiles.includes(relPath)) {
|
|
965
987
|
modifiedFiles.push(relPath);
|
|
@@ -968,10 +990,25 @@ export async function executeQueuedCardAutonomously(client, card, projectPath) {
|
|
|
968
990
|
}
|
|
969
991
|
}
|
|
970
992
|
if (modifiedFiles.length > 0) {
|
|
971
|
-
|
|
993
|
+
let diffSnippet = '';
|
|
994
|
+
try {
|
|
995
|
+
const quotedFiles = modifiedFiles.map((f) => `"${f.replace(/"/g, '\\"')}"`).join(' ');
|
|
996
|
+
const rawDiff = execSync(`git diff -- ${quotedFiles}`, {
|
|
997
|
+
cwd: projectPath,
|
|
998
|
+
encoding: 'utf8',
|
|
999
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
1000
|
+
}).trim();
|
|
1001
|
+
if (rawDiff) {
|
|
1002
|
+
const clipped = rawDiff.length > 2500 ? rawDiff.slice(0, 2500) + '\n... (diff truncated)' : rawDiff;
|
|
1003
|
+
diffSnippet = `\n\n### Code Changes (\`git diff\`)\n\`\`\`diff\n${clipped}\n\`\`\``;
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
catch { }
|
|
1007
|
+
const baseSummary = editsRes.summary ||
|
|
972
1008
|
`Implemented "${card.title}" autonomously across ${modifiedFiles.map((f) => `\`${f}\``).join(', ')}.`;
|
|
1009
|
+
const summary = `${baseSummary}${diffSnippet}`;
|
|
973
1010
|
const verificationSteps = editsRes.verification_steps ||
|
|
974
|
-
`1.
|
|
1011
|
+
`1. Review the code diff above in ${modifiedFiles.join(', ')}\n2. Click **Approve & Move to Production** (or drag to Production) to automatically commit, push, and deploy!`;
|
|
975
1012
|
await client.submitReview(card.id, provider, summary, verificationSteps);
|
|
976
1013
|
sendDesktopNotification(`Agile Monster -> Ready for Review`, `Completed "${card.title}" (${modifiedFiles.length} file(s) updated) and moved to Testing / Review!`);
|
|
977
1014
|
return true;
|
|
@@ -994,6 +1031,87 @@ export async function executeQueuedCardAutonomously(client, card, projectPath) {
|
|
|
994
1031
|
activeAutonomousCards.delete(card.id);
|
|
995
1032
|
}
|
|
996
1033
|
}
|
|
1034
|
+
export async function deployApprovedCardIfNeeded(client, card, projectPath) {
|
|
1035
|
+
if (!projectPath || !fs.existsSync(projectPath))
|
|
1036
|
+
return false;
|
|
1037
|
+
const shortId = card.id.slice(0, 8);
|
|
1038
|
+
const deployLock = path.join(getAgileMonsterLocksDir(), `deployed-${shortId}.json`);
|
|
1039
|
+
if (fs.existsSync(deployLock)) {
|
|
1040
|
+
return false;
|
|
1041
|
+
}
|
|
1042
|
+
const provider = card.agent_provider || 'cursor';
|
|
1043
|
+
const cleanTitle = (card.title || 'update').replace(/["`\r\n]+/g, ' ').trim();
|
|
1044
|
+
try {
|
|
1045
|
+
fs.writeFileSync(deployLock, JSON.stringify({ card_id: card.id, title: card.title, started_at: new Date().toISOString() }), 'utf8');
|
|
1046
|
+
const branch = execSync('git rev-parse --abbrev-ref HEAD', {
|
|
1047
|
+
cwd: projectPath,
|
|
1048
|
+
encoding: 'utf8',
|
|
1049
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
1050
|
+
}).trim();
|
|
1051
|
+
if (!branch || branch === 'HEAD')
|
|
1052
|
+
return false;
|
|
1053
|
+
const statusOut = execSync('git status --porcelain -uno', {
|
|
1054
|
+
cwd: projectPath,
|
|
1055
|
+
encoding: 'utf8',
|
|
1056
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
1057
|
+
}).trim();
|
|
1058
|
+
const rawText = `${card.title || ''} ${card.description || ''}`;
|
|
1059
|
+
const isContainerRestartCard = /\b(restart|redeploy|rebuild)\b.*\b(container|docker|server|prod)/i.test(rawText);
|
|
1060
|
+
if (statusOut.length > 0) {
|
|
1061
|
+
execSync('git add -u', { cwd: projectPath, stdio: 'ignore' });
|
|
1062
|
+
execSync(`git commit -m "feat: ${cleanTitle} [AM-${shortId}]"`, {
|
|
1063
|
+
cwd: projectPath,
|
|
1064
|
+
stdio: 'ignore',
|
|
1065
|
+
});
|
|
1066
|
+
execSync(`git push origin ${branch}`, {
|
|
1067
|
+
cwd: projectPath,
|
|
1068
|
+
stdio: 'ignore',
|
|
1069
|
+
});
|
|
1070
|
+
const commitHash = execSync('git rev-parse --short HEAD', {
|
|
1071
|
+
cwd: projectPath,
|
|
1072
|
+
encoding: 'utf8',
|
|
1073
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
1074
|
+
}).trim();
|
|
1075
|
+
await client.addProgress(card.id, provider, `🚀 **[Auto-Deployed to Production]** Card approved into **${card.list_name || 'Production'}** — automatically committed and pushed \`${commitHash}\` to \`origin/${branch}\` to trigger the production CI/CD & container deployment pipeline!`);
|
|
1076
|
+
sendDesktopNotification(`Agile Monster -> Production Deploy`, `Pushed "${card.title}" (${commitHash}) to origin/${branch}!`);
|
|
1077
|
+
return true;
|
|
1078
|
+
}
|
|
1079
|
+
else if (isContainerRestartCard) {
|
|
1080
|
+
execSync(`git commit --allow-empty -m "chore: trigger container redeploy [AM-${shortId}]"`, {
|
|
1081
|
+
cwd: projectPath,
|
|
1082
|
+
stdio: 'ignore',
|
|
1083
|
+
});
|
|
1084
|
+
execSync(`git push origin ${branch}`, {
|
|
1085
|
+
cwd: projectPath,
|
|
1086
|
+
stdio: 'ignore',
|
|
1087
|
+
});
|
|
1088
|
+
const commitHash = execSync('git rev-parse --short HEAD', {
|
|
1089
|
+
cwd: projectPath,
|
|
1090
|
+
encoding: 'utf8',
|
|
1091
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
1092
|
+
}).trim();
|
|
1093
|
+
await client.addProgress(card.id, provider, `🚀 **[Auto-Deployed to Production]** Triggered production container rebuild & restart via \`${commitHash}\` on \`origin/${branch}\`.`);
|
|
1094
|
+
sendDesktopNotification(`Agile Monster -> Container Redeploy`, `Triggered container restart (${commitHash}) on origin/${branch}!`);
|
|
1095
|
+
return true;
|
|
1096
|
+
}
|
|
1097
|
+
else {
|
|
1098
|
+
const headHash = execSync('git rev-parse --short HEAD', {
|
|
1099
|
+
cwd: projectPath,
|
|
1100
|
+
encoding: 'utf8',
|
|
1101
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
1102
|
+
}).trim();
|
|
1103
|
+
await client.addProgress(card.id, provider, `🚀 **[Auto-Deployed to Production]** Verified latest commit \`${headHash}\` is pushed to \`origin/${branch}\` and live in the production CI/CD pipeline.`);
|
|
1104
|
+
return true;
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
catch (err) {
|
|
1108
|
+
try {
|
|
1109
|
+
await client.addProgress(card.id, provider, `⚠️ **[Auto-Deploy Note]** Attempted to auto-push approved changes in \`${projectPath}\`: ${err?.message || String(err)}`);
|
|
1110
|
+
}
|
|
1111
|
+
catch { }
|
|
1112
|
+
return false;
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
997
1115
|
export function startBuiltInPullPulse(server, client, intervalMs = 20000) {
|
|
998
1116
|
const notifiedCardIds = new Set();
|
|
999
1117
|
let updateTriggered = false;
|
|
@@ -1098,6 +1216,15 @@ export function startBuiltInPullPulse(server, client, intervalMs = 20000) {
|
|
|
1098
1216
|
await executeQueuedCardAutonomously(client, qCard, targetPath);
|
|
1099
1217
|
}
|
|
1100
1218
|
}
|
|
1219
|
+
// Zero-touch auto-commit & push (deploy to production) for cards approved into Production / Done!
|
|
1220
|
+
const boundDone = res.all_bound_done_cards || [];
|
|
1221
|
+
for (const dCard of boundDone) {
|
|
1222
|
+
const targetPath = (dCard.bound_project_path || '').trim() ||
|
|
1223
|
+
(dCard.board_id === config.board_id ? config.project_path || process.cwd() : '');
|
|
1224
|
+
if (targetPath && fs.existsSync(targetPath)) {
|
|
1225
|
+
await deployApprovedCardIfNeeded(client, dCard, targetPath);
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1101
1228
|
if ((res.version?.force_update_requested || res.version?.update_available) &&
|
|
1102
1229
|
!updateTriggered) {
|
|
1103
1230
|
updateTriggered = true;
|
package/dist/types.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agilemonster/mcp-server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.11",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Official Model Context Protocol (MCP) server & Orchestrator for Agile Monster (Cursor, Claude Code, Antigravity, Codex, Hermes, Windsurf)",
|
|
6
6
|
"main": "dist/server.js",
|