@agilemonster/mcp-server 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +130 -2
- package/bin/mcp.js +30 -0
- package/bin/orchestrator.js +92 -0
- package/bin/worker.js +69 -0
- package/dist/client.d.ts +48 -0
- package/dist/client.js +245 -0
- package/dist/guidelines.d.ts +1 -0
- package/dist/guidelines.js +51 -0
- package/dist/orchestrator.d.ts +27 -0
- package/dist/orchestrator.js +306 -0
- package/dist/server.d.ts +37 -0
- package/dist/server.js +680 -0
- package/dist/types.d.ts +47 -0
- package/dist/types.js +1 -0
- package/dist/worker.d.ts +40 -0
- package/dist/worker.js +227 -0
- package/package.json +44 -4
package/README.md
CHANGED
|
@@ -1,3 +1,131 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @agilemonster/mcp-server & Agent Orchestrator
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Official Model Context Protocol (MCP) Server and Autonomous Agent Orchestrator for **Agile Monster** ([https://app.agilemonster.com](https://app.agilemonster.com)).
|
|
4
|
+
|
|
5
|
+
Seamlessly connects your Agile Monster boards with your favorite developer AI agents:
|
|
6
|
+
- **Cursor**
|
|
7
|
+
- **Claude Code**
|
|
8
|
+
- **Antigravity / Gemini Code Assist**
|
|
9
|
+
- **Codex**
|
|
10
|
+
- **Hermes Agent**
|
|
11
|
+
- **Windsurf / Aider**
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## ⚡ Quick Start: Adding to your AI Agent
|
|
16
|
+
|
|
17
|
+
Generate a **Personal API Key** in Agile Monster ([https://app.agilemonster.com](https://app.agilemonster.com) -> Profile -> API Keys). Your key will look like `am_live_...`.
|
|
18
|
+
|
|
19
|
+
### 1. Cursor (`.cursor/mcp.json` or Global Settings)
|
|
20
|
+
```json
|
|
21
|
+
{
|
|
22
|
+
"mcpServers": {
|
|
23
|
+
"agilemonster": {
|
|
24
|
+
"command": "npx",
|
|
25
|
+
"args": ["-y", "@agilemonster/mcp-server"],
|
|
26
|
+
"env": {
|
|
27
|
+
"AGILEMONSTER_API_KEY": "am_live_your_token_here",
|
|
28
|
+
"AGILEMONSTER_API_URL": "https://api.agilemonster.com"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 2. Claude Code CLI
|
|
36
|
+
```bash
|
|
37
|
+
claude mcp add agilemonster npx -y @agilemonster/mcp-server \
|
|
38
|
+
-e AGILEMONSTER_API_KEY="am_live_your_token_here" \
|
|
39
|
+
-e AGILEMONSTER_API_URL="https://api.agilemonster.com"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### 3. Antigravity / Gemini
|
|
43
|
+
Add to your Antigravity MCP directory (`~/.gemini/antigravity/mcp/agilemonster` or project settings):
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"mcpServers": {
|
|
47
|
+
"agilemonster": {
|
|
48
|
+
"command": "node",
|
|
49
|
+
"args": ["/path/to/agilemonster/packages/mcp-server/bin/mcp.js"],
|
|
50
|
+
"env": {
|
|
51
|
+
"AGILEMONSTER_API_KEY": "am_live_your_token_here",
|
|
52
|
+
"AGILEMONSTER_API_URL": "https://api.agilemonster.com"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 📁 1-Click Project Initialization (`init`)
|
|
62
|
+
|
|
63
|
+
To automatically bind your repository to an Agile Monster board and generate operational instructions for your AI agents in one command, run:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npx @agilemonster/mcp-server init
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
This generates:
|
|
70
|
+
1. `.agilemonster.json` — Binds the local repository to the board UUID, list columns, and default agent provider.
|
|
71
|
+
2. `AGENTS.md` — Codified operational rules for autonomous agents (Cursor, Claude Code, Antigravity, etc.) so they know when to claim tasks, ask clarifying questions, log progress, and submit work for human testing.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 🛠️ Available MCP Tools
|
|
76
|
+
|
|
77
|
+
Agents have access to 8 purpose-built lifecycle tools:
|
|
78
|
+
|
|
79
|
+
| Tool | Purpose |
|
|
80
|
+
| :--- | :--- |
|
|
81
|
+
| `agilemonster_get_context` | Inspects board, columns, and active tasks. Auto-detects project from `.agilemonster.json` or directory name. |
|
|
82
|
+
| `agilemonster_init_project` | Binds current repo to an Agile Monster board and creates `AGENTS.md`. |
|
|
83
|
+
| `agilemonster_list_my_tasks` | Lists cards assigned to the AI agent (`queued`, `in_progress`, `waiting_for_input`, `review`). |
|
|
84
|
+
| `agilemonster_claim_task` | Claims a task, marking it `in_progress` on the board before code edits start. |
|
|
85
|
+
| `agilemonster_ask_question` | Posts a question comment on the card and marks status as `waiting_for_input` so the user is alerted. |
|
|
86
|
+
| `agilemonster_add_progress` | Logs implementation progress notes to task comments. |
|
|
87
|
+
| `agilemonster_submit_for_review` | Marks work complete, moves card to Review column, reassigns to human member for confirmation, and posts test instructions. |
|
|
88
|
+
| `agilemonster_create_followup_tasks` | Creates follow-up cards (e.g. "Deploy to Test", "Deploy to Production", "Write integration tests"). |
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 💡 MCP Native Prompts & Resources
|
|
93
|
+
|
|
94
|
+
The server exposes Model Context Protocol **Prompts** and **Resources** so agents can automatically load operational instructions into their context:
|
|
95
|
+
|
|
96
|
+
### Prompts
|
|
97
|
+
- `agilemonster_agent_protocol`: System instructions explaining the full task lifecycle protocol.
|
|
98
|
+
- `agilemonster_work_on_task`: Guided workflow prompt for an agent to claim, investigate, implement, test, submit for review, and propose follow-up tasks for a specific card ID.
|
|
99
|
+
|
|
100
|
+
### Resources
|
|
101
|
+
- `agilemonster://guidelines/agents`: Live markdown text of the official Agent Guidelines (`text/markdown`).
|
|
102
|
+
- `agilemonster://project/context`: Current repository board, list structure, and active agent tasks as live JSON (`application/json`).
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## 🤖 The Autonomous Agent Orchestrator CLI
|
|
107
|
+
|
|
108
|
+
Use the built-in orchestrator to monitor tasks, dispatch work, or run a background watcher:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
# Check current tasks assigned to agents for this project
|
|
112
|
+
npx @agilemonster/mcp-server status
|
|
113
|
+
|
|
114
|
+
# Watch for newly assigned tasks in real-time
|
|
115
|
+
npx @agilemonster/mcp-server watch
|
|
116
|
+
|
|
117
|
+
# Claim a task
|
|
118
|
+
npx @agilemonster/mcp-server claim <card_id>
|
|
119
|
+
|
|
120
|
+
# Submit for user testing & confirmation
|
|
121
|
+
npx @agilemonster/mcp-server review <card_id> "Implemented dark mode toggle" "Test via Settings screen"
|
|
122
|
+
|
|
123
|
+
# Ask the user a question
|
|
124
|
+
npx @agilemonster/mcp-server question <card_id> "Should we support OAuth tokens as well?"
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## 📚 REST API Reference
|
|
130
|
+
|
|
131
|
+
For developers building custom scripts, CI/CD bots, or proprietary agent orchestrators, refer to the full REST API specification in [`docs/AGENT-API.md`](../../docs/AGENT-API.md).
|
package/bin/mcp.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { runStdioServer } from '../dist/server.js';
|
|
3
|
+
|
|
4
|
+
const cliCommands = new Set([
|
|
5
|
+
'bootstrap',
|
|
6
|
+
'setup',
|
|
7
|
+
'init',
|
|
8
|
+
'status',
|
|
9
|
+
'check',
|
|
10
|
+
'list',
|
|
11
|
+
'watch',
|
|
12
|
+
'daemon',
|
|
13
|
+
'claim',
|
|
14
|
+
'review',
|
|
15
|
+
'question',
|
|
16
|
+
'help',
|
|
17
|
+
'--help',
|
|
18
|
+
'-h',
|
|
19
|
+
]);
|
|
20
|
+
|
|
21
|
+
const firstArg = process.argv[2];
|
|
22
|
+
if (firstArg && cliCommands.has(firstArg)) {
|
|
23
|
+
// Delegate to orchestrator CLI
|
|
24
|
+
await import('./orchestrator.js');
|
|
25
|
+
} else {
|
|
26
|
+
runStdioServer().catch((error) => {
|
|
27
|
+
console.error('Fatal MCP Server Error:', error);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { AgileMonsterOrchestrator } from '../dist/orchestrator.js';
|
|
3
|
+
import { AgileMonsterClient } from '../dist/client.js';
|
|
4
|
+
|
|
5
|
+
const client = new AgileMonsterClient();
|
|
6
|
+
const orchestrator = new AgileMonsterOrchestrator(client);
|
|
7
|
+
|
|
8
|
+
const args = process.argv.slice(2);
|
|
9
|
+
const command = args[0] || 'status';
|
|
10
|
+
|
|
11
|
+
async function main() {
|
|
12
|
+
switch (command) {
|
|
13
|
+
case 'status':
|
|
14
|
+
case 'check':
|
|
15
|
+
case 'list':
|
|
16
|
+
await orchestrator.printDashboard();
|
|
17
|
+
break;
|
|
18
|
+
|
|
19
|
+
case 'watch':
|
|
20
|
+
case 'daemon':
|
|
21
|
+
await orchestrator.runDaemon({
|
|
22
|
+
intervalMs: 10000,
|
|
23
|
+
});
|
|
24
|
+
break;
|
|
25
|
+
|
|
26
|
+
case 'bootstrap':
|
|
27
|
+
case 'setup':
|
|
28
|
+
case 'init': {
|
|
29
|
+
const ideArg = args.find((a) => a.startsWith('--ide='))?.split('=')[1] || args[1];
|
|
30
|
+
await orchestrator.initInteractive({ ide: ideArg });
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
case 'claim': {
|
|
35
|
+
const cardId = args[1];
|
|
36
|
+
if (!cardId) {
|
|
37
|
+
console.error('Usage: agilemonster-orchestrator claim <cardId> [provider]');
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
const provider = args[2] || client.loadProjectConfig()?.provider || 'antigravity';
|
|
41
|
+
const result = await client.claimTask(cardId, provider);
|
|
42
|
+
console.log(`✅ Claimed task: ${result.title} (${result.id})`);
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
case 'review': {
|
|
47
|
+
const cardId = args[1];
|
|
48
|
+
const summary = args[2];
|
|
49
|
+
if (!cardId || !summary) {
|
|
50
|
+
console.error('Usage: agilemonster-orchestrator review <cardId> "<summary>" [verificationSteps]');
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
const verificationSteps = args[3];
|
|
54
|
+
const provider = client.loadProjectConfig()?.provider || 'antigravity';
|
|
55
|
+
const result = await client.submitReview(cardId, provider, summary, verificationSteps);
|
|
56
|
+
console.log(`✅ Submitted task for review & testing: ${result.title} (${result.id})`);
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
case 'question': {
|
|
61
|
+
const cardId = args[1];
|
|
62
|
+
const question = args[2];
|
|
63
|
+
if (!cardId || !question) {
|
|
64
|
+
console.error('Usage: agilemonster-orchestrator question <cardId> "<question>"');
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
const provider = client.loadProjectConfig()?.provider || 'antigravity';
|
|
68
|
+
const result = await client.askQuestion(cardId, provider, question);
|
|
69
|
+
console.log(`✅ Question posted to task: ${result.title}`);
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
default:
|
|
74
|
+
console.log(`
|
|
75
|
+
Agile Monster Agent Orchestrator CLI
|
|
76
|
+
|
|
77
|
+
Commands:
|
|
78
|
+
agilemonster-orchestrator (or status) Show active agent tasks and project binding
|
|
79
|
+
agilemonster-orchestrator watch Run background daemon to watch for newly queued tasks
|
|
80
|
+
agilemonster-orchestrator init Bind current directory to an Agile Monster board
|
|
81
|
+
agilemonster-orchestrator claim <id> Claim a task and mark it in-progress
|
|
82
|
+
agilemonster-orchestrator review <id> "..." Submit task for user review & confirmation
|
|
83
|
+
agilemonster-orchestrator question <id> "..." Ask the user a clarifying question
|
|
84
|
+
`);
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
main().catch((err) => {
|
|
90
|
+
console.error('Orchestrator Error:', err.message || err);
|
|
91
|
+
process.exit(1);
|
|
92
|
+
});
|
package/bin/worker.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { AgileMonsterWorker } from '../dist/worker.js';
|
|
3
|
+
import { AgileMonsterClient } from '../dist/client.js';
|
|
4
|
+
|
|
5
|
+
const client = new AgileMonsterClient();
|
|
6
|
+
const worker = new AgileMonsterWorker(client);
|
|
7
|
+
|
|
8
|
+
const args = process.argv.slice(2);
|
|
9
|
+
|
|
10
|
+
function getArg(prefix, fallback) {
|
|
11
|
+
const found = args.find((a) => a.startsWith(prefix));
|
|
12
|
+
if (found) {
|
|
13
|
+
return found.split('=')[1];
|
|
14
|
+
}
|
|
15
|
+
return fallback;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const hasFlag = (flag) => args.includes(flag);
|
|
19
|
+
|
|
20
|
+
if (hasFlag('--help') || hasFlag('-h')) {
|
|
21
|
+
console.log(`
|
|
22
|
+
Agile Monster Autonomous Agent Worker
|
|
23
|
+
|
|
24
|
+
Usage:
|
|
25
|
+
agilemonster-worker [options]
|
|
26
|
+
|
|
27
|
+
Options:
|
|
28
|
+
--runner=<engine> Execution engine: simulated (default), aider, claude, shell
|
|
29
|
+
--command="<cmd>" Shell command to execute when runner=shell
|
|
30
|
+
--provider=<agent> Target agent provider: antigravity (default), cursor, aider, etc.
|
|
31
|
+
--interval=<seconds> Polling interval in seconds (default: 15)
|
|
32
|
+
--heartbeat=<seconds> Heartbeat progress update interval (default: 30)
|
|
33
|
+
--max-budget=<usd> Circuit-breaker USD budget limit per task (default: 5.00)
|
|
34
|
+
--max-tokens=<count> Circuit-breaker token limit per task (default: 200000)
|
|
35
|
+
--no-branch Do not create an isolated git branch for the task
|
|
36
|
+
--once Process at most one queued task and exit
|
|
37
|
+
--help, -h Show this help screen
|
|
38
|
+
`);
|
|
39
|
+
process.exit(0);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const runner = getArg('--runner', 'simulated');
|
|
43
|
+
const customCommand = getArg('--command', undefined);
|
|
44
|
+
const provider = getArg('--provider', undefined);
|
|
45
|
+
const intervalSec = parseInt(getArg('--interval', '15'), 10);
|
|
46
|
+
const heartbeatSec = parseInt(getArg('--heartbeat', '30'), 10);
|
|
47
|
+
const maxBudgetUsd = parseFloat(getArg('--max-budget', '5.0'));
|
|
48
|
+
const maxTokens = parseInt(getArg('--max-tokens', '200000'), 10);
|
|
49
|
+
const gitBranch = !hasFlag('--no-branch');
|
|
50
|
+
const once = hasFlag('--once');
|
|
51
|
+
|
|
52
|
+
async function main() {
|
|
53
|
+
await worker.start({
|
|
54
|
+
runner,
|
|
55
|
+
customCommand,
|
|
56
|
+
provider,
|
|
57
|
+
intervalMs: intervalSec * 1000,
|
|
58
|
+
heartbeatMs: heartbeatSec * 1000,
|
|
59
|
+
maxBudgetUsd,
|
|
60
|
+
maxTokens,
|
|
61
|
+
gitBranch,
|
|
62
|
+
once,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
main().catch((err) => {
|
|
67
|
+
console.error('Fatal Worker Error:', err.message || err);
|
|
68
|
+
process.exit(1);
|
|
69
|
+
});
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { AgileMonsterProjectConfig, Card, ContextResponse } from './types.js';
|
|
2
|
+
export declare class AgileMonsterClient {
|
|
3
|
+
private apiKey;
|
|
4
|
+
private baseUrl;
|
|
5
|
+
private cwd;
|
|
6
|
+
private cachedConfig;
|
|
7
|
+
constructor(options?: {
|
|
8
|
+
apiKey?: string;
|
|
9
|
+
baseUrl?: string;
|
|
10
|
+
cwd?: string;
|
|
11
|
+
});
|
|
12
|
+
private resolveApiKey;
|
|
13
|
+
getApiKey(): string;
|
|
14
|
+
setApiKey(key: string): void;
|
|
15
|
+
loadProjectConfig(): AgileMonsterProjectConfig | null;
|
|
16
|
+
saveProjectConfig(config: AgileMonsterProjectConfig, dir?: string): string;
|
|
17
|
+
static saveGlobalConfig(data: {
|
|
18
|
+
api_key?: string;
|
|
19
|
+
}): string;
|
|
20
|
+
private request;
|
|
21
|
+
me(): Promise<{
|
|
22
|
+
id: string;
|
|
23
|
+
email: string;
|
|
24
|
+
first_name?: string;
|
|
25
|
+
last_name?: string;
|
|
26
|
+
}>;
|
|
27
|
+
getContext(projectName?: string, boardId?: string): Promise<ContextResponse>;
|
|
28
|
+
listAssigned(provider?: string, status?: string, boardId?: string, workspaceId?: string): Promise<{
|
|
29
|
+
provider: string;
|
|
30
|
+
cards: Card[];
|
|
31
|
+
}>;
|
|
32
|
+
claimTask(cardId: string, provider: string, runRef?: string): Promise<Card>;
|
|
33
|
+
askQuestion(cardId: string, provider: string, question: string): Promise<Card>;
|
|
34
|
+
addProgress(cardId: string, provider: string, update: string, tokensUsed?: number, costUsd?: number): Promise<Card>;
|
|
35
|
+
recordSpend(cardId: string, provider: string, tokens: number, costUsd: number, note?: string): Promise<Card>;
|
|
36
|
+
setBudget(cardId: string, maxBudgetUsd?: number | null, maxTokens?: number | null): Promise<Card>;
|
|
37
|
+
submitReview(cardId: string, provider: string, summary: string, verificationSteps?: string, targetList?: string, assignToUserId?: string): Promise<Card>;
|
|
38
|
+
completeTask(cardId: string, provider: string, comment?: string): Promise<Card>;
|
|
39
|
+
blockTask(cardId: string, provider: string, comment?: string): Promise<Card>;
|
|
40
|
+
createFollowupTasks(cardId: string, provider: string, tasks: Array<{
|
|
41
|
+
title: string;
|
|
42
|
+
description?: string;
|
|
43
|
+
list_id?: string;
|
|
44
|
+
agent_provider?: string;
|
|
45
|
+
}>): Promise<{
|
|
46
|
+
tasks: Card[];
|
|
47
|
+
}>;
|
|
48
|
+
}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
export class AgileMonsterClient {
|
|
4
|
+
apiKey = '';
|
|
5
|
+
baseUrl;
|
|
6
|
+
cwd;
|
|
7
|
+
cachedConfig = undefined;
|
|
8
|
+
constructor(options) {
|
|
9
|
+
this.cwd = options?.cwd || process.cwd();
|
|
10
|
+
this.baseUrl = (options?.baseUrl ||
|
|
11
|
+
process.env.AGILEMONSTER_API_URL ||
|
|
12
|
+
'https://api.agilemonster.com').replace(/\/+$/, '');
|
|
13
|
+
this.apiKey = options?.apiKey || this.resolveApiKey();
|
|
14
|
+
}
|
|
15
|
+
resolveApiKey() {
|
|
16
|
+
// 1. Process environment variables
|
|
17
|
+
if (process.env.AGILEMONSTER_API_KEY && process.env.AGILEMONSTER_API_KEY.trim() !== '') {
|
|
18
|
+
return process.env.AGILEMONSTER_API_KEY.trim();
|
|
19
|
+
}
|
|
20
|
+
if (process.env.AM_API_KEY && process.env.AM_API_KEY.trim() !== '') {
|
|
21
|
+
return process.env.AM_API_KEY.trim();
|
|
22
|
+
}
|
|
23
|
+
// 2. Project config (.agilemonster.json)
|
|
24
|
+
const projectConfig = this.loadProjectConfig();
|
|
25
|
+
if (projectConfig?.api_key && projectConfig.api_key.trim() !== '') {
|
|
26
|
+
return projectConfig.api_key.trim();
|
|
27
|
+
}
|
|
28
|
+
// 3. Local .env file in cwd
|
|
29
|
+
try {
|
|
30
|
+
const envPath = path.join(this.cwd, '.env');
|
|
31
|
+
if (fs.existsSync(envPath)) {
|
|
32
|
+
const envContent = fs.readFileSync(envPath, 'utf8');
|
|
33
|
+
const match = envContent.match(/(?:AGILEMONSTER_API_KEY|AM_API_KEY)\s*=\s*["']?([a-zA-Z0-9_-]+)["']?/);
|
|
34
|
+
if (match && match[1]) {
|
|
35
|
+
return match[1].trim();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// Ignore .env read error
|
|
41
|
+
}
|
|
42
|
+
// 4. Global developer config (~/.agilemonster/config.json)
|
|
43
|
+
try {
|
|
44
|
+
const homedir = process.env.HOME || process.env.USERPROFILE || '';
|
|
45
|
+
if (homedir) {
|
|
46
|
+
const globalConfigPath = path.join(homedir, '.agilemonster', 'config.json');
|
|
47
|
+
if (fs.existsSync(globalConfigPath)) {
|
|
48
|
+
const globalRaw = fs.readFileSync(globalConfigPath, 'utf8');
|
|
49
|
+
const parsed = JSON.parse(globalRaw);
|
|
50
|
+
if (parsed.api_key && typeof parsed.api_key === 'string') {
|
|
51
|
+
return parsed.api_key.trim();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// Ignore global config read error
|
|
58
|
+
}
|
|
59
|
+
return '';
|
|
60
|
+
}
|
|
61
|
+
getApiKey() {
|
|
62
|
+
return this.apiKey;
|
|
63
|
+
}
|
|
64
|
+
setApiKey(key) {
|
|
65
|
+
this.apiKey = key;
|
|
66
|
+
}
|
|
67
|
+
loadProjectConfig() {
|
|
68
|
+
if (this.cachedConfig !== undefined) {
|
|
69
|
+
return this.cachedConfig;
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
let currentDir = this.cwd;
|
|
73
|
+
while (currentDir !== path.parse(currentDir).root) {
|
|
74
|
+
const configPath = path.join(currentDir, '.agilemonster.json');
|
|
75
|
+
if (fs.existsSync(configPath)) {
|
|
76
|
+
const content = fs.readFileSync(configPath, 'utf8');
|
|
77
|
+
this.cachedConfig = JSON.parse(content);
|
|
78
|
+
return this.cachedConfig;
|
|
79
|
+
}
|
|
80
|
+
currentDir = path.dirname(currentDir);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// Ignore reading error
|
|
85
|
+
}
|
|
86
|
+
this.cachedConfig = null;
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
saveProjectConfig(config, dir) {
|
|
90
|
+
const targetDir = dir || this.cwd;
|
|
91
|
+
const configPath = path.join(targetDir, '.agilemonster.json');
|
|
92
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf8');
|
|
93
|
+
this.cachedConfig = config;
|
|
94
|
+
return configPath;
|
|
95
|
+
}
|
|
96
|
+
static saveGlobalConfig(data) {
|
|
97
|
+
const homedir = process.env.HOME || process.env.USERPROFILE || '';
|
|
98
|
+
if (!homedir)
|
|
99
|
+
throw new Error('Cannot locate home directory');
|
|
100
|
+
const dir = path.join(homedir, '.agilemonster');
|
|
101
|
+
if (!fs.existsSync(dir)) {
|
|
102
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
103
|
+
}
|
|
104
|
+
const configPath = path.join(dir, 'config.json');
|
|
105
|
+
let existing = {};
|
|
106
|
+
try {
|
|
107
|
+
if (fs.existsSync(configPath)) {
|
|
108
|
+
existing = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
catch { }
|
|
112
|
+
const merged = { ...existing, ...data };
|
|
113
|
+
fs.writeFileSync(configPath, JSON.stringify(merged, null, 2), 'utf8');
|
|
114
|
+
return configPath;
|
|
115
|
+
}
|
|
116
|
+
async request(endpoint, options = {}) {
|
|
117
|
+
if (!this.apiKey) {
|
|
118
|
+
throw new Error('Missing AGILEMONSTER_API_KEY. Generate a Personal API Key in Agile Monster (Profile -> API Keys) and set it in your environment or MCP config.');
|
|
119
|
+
}
|
|
120
|
+
const url = `${this.baseUrl}${endpoint.startsWith('/') ? '' : '/'}${endpoint}`;
|
|
121
|
+
const headers = {
|
|
122
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
123
|
+
'Content-Type': 'application/json',
|
|
124
|
+
Accept: 'application/json',
|
|
125
|
+
...(options.headers || {}),
|
|
126
|
+
};
|
|
127
|
+
const res = await fetch(url, {
|
|
128
|
+
...options,
|
|
129
|
+
headers,
|
|
130
|
+
});
|
|
131
|
+
const text = await res.text();
|
|
132
|
+
let json = null;
|
|
133
|
+
try {
|
|
134
|
+
json = JSON.parse(text);
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
// Not JSON
|
|
138
|
+
}
|
|
139
|
+
if (!res.ok) {
|
|
140
|
+
const msg = json?.message || json?.error || `HTTP ${res.status}: ${text.slice(0, 150)}`;
|
|
141
|
+
throw new Error(`Agile Monster API Error: ${msg}`);
|
|
142
|
+
}
|
|
143
|
+
return json;
|
|
144
|
+
}
|
|
145
|
+
async me() {
|
|
146
|
+
return this.request('/api/agent/me');
|
|
147
|
+
}
|
|
148
|
+
async getContext(projectName, boardId) {
|
|
149
|
+
const params = new URLSearchParams();
|
|
150
|
+
if (projectName)
|
|
151
|
+
params.set('project_name', projectName);
|
|
152
|
+
if (boardId)
|
|
153
|
+
params.set('board_id', boardId);
|
|
154
|
+
const qs = params.toString();
|
|
155
|
+
return this.request(`/api/agent/context${qs ? `?${qs}` : ''}`);
|
|
156
|
+
}
|
|
157
|
+
async listAssigned(provider = 'any', status, boardId, workspaceId) {
|
|
158
|
+
const params = new URLSearchParams();
|
|
159
|
+
if (provider)
|
|
160
|
+
params.set('provider', provider);
|
|
161
|
+
if (status)
|
|
162
|
+
params.set('status', status);
|
|
163
|
+
if (boardId)
|
|
164
|
+
params.set('board_id', boardId);
|
|
165
|
+
if (workspaceId)
|
|
166
|
+
params.set('workspace_id', workspaceId);
|
|
167
|
+
const qs = params.toString();
|
|
168
|
+
return this.request(`/api/agent/assigned${qs ? `?${qs}` : ''}`);
|
|
169
|
+
}
|
|
170
|
+
async claimTask(cardId, provider, runRef) {
|
|
171
|
+
return this.request(`/api/agent/cards/${cardId}/claim`, {
|
|
172
|
+
method: 'POST',
|
|
173
|
+
body: JSON.stringify({ provider, run_ref: runRef }),
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
async askQuestion(cardId, provider, question) {
|
|
177
|
+
return this.request(`/api/agent/cards/${cardId}/ask-question`, {
|
|
178
|
+
method: 'POST',
|
|
179
|
+
body: JSON.stringify({ provider, question }),
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
async addProgress(cardId, provider, update, tokensUsed, costUsd) {
|
|
183
|
+
return this.request(`/api/agent/cards/${cardId}/progress`, {
|
|
184
|
+
method: 'POST',
|
|
185
|
+
body: JSON.stringify({
|
|
186
|
+
provider,
|
|
187
|
+
update,
|
|
188
|
+
tokens_used: tokensUsed,
|
|
189
|
+
cost_usd: costUsd,
|
|
190
|
+
}),
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
async recordSpend(cardId, provider, tokens, costUsd, note) {
|
|
194
|
+
return this.request(`/api/agent/cards/${cardId}/spend`, {
|
|
195
|
+
method: 'POST',
|
|
196
|
+
body: JSON.stringify({
|
|
197
|
+
provider,
|
|
198
|
+
tokens,
|
|
199
|
+
cost_usd: costUsd,
|
|
200
|
+
note,
|
|
201
|
+
}),
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
async setBudget(cardId, maxBudgetUsd, maxTokens) {
|
|
205
|
+
return this.request(`/api/agent/cards/${cardId}/budget`, {
|
|
206
|
+
method: 'POST',
|
|
207
|
+
body: JSON.stringify({
|
|
208
|
+
max_budget_usd: maxBudgetUsd,
|
|
209
|
+
max_tokens: maxTokens,
|
|
210
|
+
}),
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
async submitReview(cardId, provider, summary, verificationSteps, targetList, assignToUserId) {
|
|
214
|
+
return this.request(`/api/agent/cards/${cardId}/submit-review`, {
|
|
215
|
+
method: 'POST',
|
|
216
|
+
body: JSON.stringify({
|
|
217
|
+
provider,
|
|
218
|
+
summary,
|
|
219
|
+
verification_steps: verificationSteps,
|
|
220
|
+
target_list: targetList,
|
|
221
|
+
target_list_id: targetList,
|
|
222
|
+
target_list_name: targetList,
|
|
223
|
+
assign_to_user_id: assignToUserId,
|
|
224
|
+
}),
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
async completeTask(cardId, provider, comment) {
|
|
228
|
+
return this.request(`/api/agent/cards/${cardId}/complete`, {
|
|
229
|
+
method: 'POST',
|
|
230
|
+
body: JSON.stringify({ provider, comment }),
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
async blockTask(cardId, provider, comment) {
|
|
234
|
+
return this.request(`/api/agent/cards/${cardId}/block`, {
|
|
235
|
+
method: 'POST',
|
|
236
|
+
body: JSON.stringify({ provider, comment }),
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
async createFollowupTasks(cardId, provider, tasks) {
|
|
240
|
+
return this.request(`/api/agent/cards/${cardId}/followup-tasks`, {
|
|
241
|
+
method: 'POST',
|
|
242
|
+
body: JSON.stringify({ provider, tasks }),
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const AGENT_GUIDELINES_MARKDOWN = "# Autonomous AI Agent Operational Guidelines (Agile Monster)\n\nThis document provides mandatory operational protocols for AI coding assistants (**Cursor, Claude Code, Antigravity, Codex, Hermes Agent, Windsurf, Aider**) working inside repositories integrated with **Agile Monster** (https://app.agilemonster.com).\n\n---\n\n## \uD83E\uDDED Core Principles\n\n1. **The Agile Monster Board is the Source of Truth**: \n All non-trivial feature additions, bug fixes, refactors, and deployments must correspond to a task on the project's Agile Monster board.\n2. **One Active Assignee**: \n Work on cards assigned specifically to your agent provider (`cursor`, `claude_code`, `antigravity`, `codex`, `hermes`, `windsurf`, `aider`, or `custom`). Never claim or alter tasks assigned to other agents or human teammates without explicit user direction.\n3. **No Hallucinated Requirements**: \n When a specification, API schema, or environment variable is ambiguous, stop and ask the user rather than guessing.\n4. **Human Verification Gate**: \n Autonomous agents must **never** close a task directly to `Done`. You must submit work for human testing and verification using `agilemonster_submit_for_review`.\n\n---\n\n## \uD83D\uDD04 Task Lifecycle Protocol\n\nFollow this exact sequential protocol when working on any task:\n\n1. **Context & Discovery**:\n - Inspect `.agilemonster.json` in the project root if present.\n - Run `agilemonster_get_context` to retrieve the board ID, list columns (`Backlog`, `In Progress`, `Review`, `Done`), and active tasks.\n\n2. **Task Intake**:\n - Check which tasks are queued for your agent using `agilemonster_list_my_tasks({ \"status\": \"queued\" })`.\n - Confirm the card is assigned to your provider before proceeding.\n\n3. **Claiming the Task**:\n - Before editing files or running build tools, claim the task:\n `agilemonster_claim_task({ \"card_id\": \"<card_uuid>\" })`\n - This moves the card status to `in_progress` and logs an activity event on the board.\n\n4. **Implementation, Questions & Progress**:\n - **When Blocked or Unclear**: Call `agilemonster_ask_question({ \"card_id\": \"<card_uuid>\", \"question\": \"...\" })`. This sets status to `waiting_for_input` so the user is alerted.\n - **Logging Progress**: For non-trivial milestones, call `agilemonster_add_progress({ \"card_id\": \"<card_uuid>\", \"update\": \"...\", \"tokens_used\": 1500, \"cost_usd\": 0.003 })`.\n - **Cost Safeguards**: Use `agilemonster_set_budget` and `agilemonster_record_spend` to protect against runaway billing loops.\n\n5. **Submission for Human Review & Testing**:\n - When code is implemented, compiled, and tested locally:\n - Call `agilemonster_submit_for_review({ \"card_id\": \"<card_uuid>\", \"summary\": \"...\", \"verification_steps\": \"...\" })`.\n - This automatically moves the card to the board's **Testing / QA / Review** column, reassigns the card back to the human who requested it, sets `agent_status` to `review`, and posts step-by-step verification instructions.\n - Never mark the card directly as \"Done\". The user will verify your test steps and sign off.\n\n6. **Suggest Follow-Up Tasks**:\n - Proactively suggest follow-up cards (deploy to test/staging, deploy to production, add integration tests) using:\n `agilemonster_create_followup_tasks({ \"card_id\": \"<card_uuid>\", \"tasks\": [...] })`.\n";
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export const AGENT_GUIDELINES_MARKDOWN = `# Autonomous AI Agent Operational Guidelines (Agile Monster)
|
|
2
|
+
|
|
3
|
+
This document provides mandatory operational protocols for AI coding assistants (**Cursor, Claude Code, Antigravity, Codex, Hermes Agent, Windsurf, Aider**) working inside repositories integrated with **Agile Monster** (https://app.agilemonster.com).
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 🧭 Core Principles
|
|
8
|
+
|
|
9
|
+
1. **The Agile Monster Board is the Source of Truth**:
|
|
10
|
+
All non-trivial feature additions, bug fixes, refactors, and deployments must correspond to a task on the project's Agile Monster board.
|
|
11
|
+
2. **One Active Assignee**:
|
|
12
|
+
Work on cards assigned specifically to your agent provider (\`cursor\`, \`claude_code\`, \`antigravity\`, \`codex\`, \`hermes\`, \`windsurf\`, \`aider\`, or \`custom\`). Never claim or alter tasks assigned to other agents or human teammates without explicit user direction.
|
|
13
|
+
3. **No Hallucinated Requirements**:
|
|
14
|
+
When a specification, API schema, or environment variable is ambiguous, stop and ask the user rather than guessing.
|
|
15
|
+
4. **Human Verification Gate**:
|
|
16
|
+
Autonomous agents must **never** close a task directly to \`Done\`. You must submit work for human testing and verification using \`agilemonster_submit_for_review\`.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 🔄 Task Lifecycle Protocol
|
|
21
|
+
|
|
22
|
+
Follow this exact sequential protocol when working on any task:
|
|
23
|
+
|
|
24
|
+
1. **Context & Discovery**:
|
|
25
|
+
- Inspect \`.agilemonster.json\` in the project root if present.
|
|
26
|
+
- Run \`agilemonster_get_context\` to retrieve the board ID, list columns (\`Backlog\`, \`In Progress\`, \`Review\`, \`Done\`), and active tasks.
|
|
27
|
+
|
|
28
|
+
2. **Task Intake**:
|
|
29
|
+
- Check which tasks are queued for your agent using \`agilemonster_list_my_tasks({ "status": "queued" })\`.
|
|
30
|
+
- Confirm the card is assigned to your provider before proceeding.
|
|
31
|
+
|
|
32
|
+
3. **Claiming the Task**:
|
|
33
|
+
- Before editing files or running build tools, claim the task:
|
|
34
|
+
\`agilemonster_claim_task({ "card_id": "<card_uuid>" })\`
|
|
35
|
+
- This moves the card status to \`in_progress\` and logs an activity event on the board.
|
|
36
|
+
|
|
37
|
+
4. **Implementation, Questions & Progress**:
|
|
38
|
+
- **When Blocked or Unclear**: Call \`agilemonster_ask_question({ "card_id": "<card_uuid>", "question": "..." })\`. This sets status to \`waiting_for_input\` so the user is alerted.
|
|
39
|
+
- **Logging Progress**: For non-trivial milestones, call \`agilemonster_add_progress({ "card_id": "<card_uuid>", "update": "...", "tokens_used": 1500, "cost_usd": 0.003 })\`.
|
|
40
|
+
- **Cost Safeguards**: Use \`agilemonster_set_budget\` and \`agilemonster_record_spend\` to protect against runaway billing loops.
|
|
41
|
+
|
|
42
|
+
5. **Submission for Human Review & Testing**:
|
|
43
|
+
- When code is implemented, compiled, and tested locally:
|
|
44
|
+
- Call \`agilemonster_submit_for_review({ "card_id": "<card_uuid>", "summary": "...", "verification_steps": "..." })\`.
|
|
45
|
+
- This automatically moves the card to the board's **Testing / QA / Review** column, reassigns the card back to the human who requested it, sets \`agent_status\` to \`review\`, and posts step-by-step verification instructions.
|
|
46
|
+
- Never mark the card directly as "Done". The user will verify your test steps and sign off.
|
|
47
|
+
|
|
48
|
+
6. **Suggest Follow-Up Tasks**:
|
|
49
|
+
- Proactively suggest follow-up cards (deploy to test/staging, deploy to production, add integration tests) using:
|
|
50
|
+
\`agilemonster_create_followup_tasks({ "card_id": "<card_uuid>", "tasks": [...] })\`.
|
|
51
|
+
`;
|