@agilemonster/mcp-server 1.0.23 → 1.0.25

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 CHANGED
@@ -91,6 +91,48 @@ Agents have access to these purpose-built lifecycle tools:
91
91
 
92
92
  ---
93
93
 
94
+ ## 🧠 Who works a card that is queued from the board
95
+
96
+ A card you assign to an agent in the web app is picked up by the background runner on your machine. The
97
+ runner hands it to a **real coding agent running headless in the project folder** — the same agent you use
98
+ in the IDE, with the same ability to search the code, open what it finds, run the build and fix what it broke:
99
+
100
+ | Card's agent | Command line agent used | One-time setup |
101
+ | :--- | :--- | :--- |
102
+ | Cursor | `cursor-agent -p --force` | `curl https://cursor.com/install -fsS \| bash`, then `cursor-agent login` |
103
+ | Claude Code | `claude -p --permission-mode acceptEdits` | `npm install -g @anthropic-ai/claude-code`, then `claude auth login` |
104
+ | Codex | `codex exec --sandbox workspace-write` | `npm install -g @openai/codex`, then `codex login` |
105
+
106
+ The runner also finds an agent that is not on your `PATH` — for example the Codex CLI that the Codex app and
107
+ its IDE plugin install, already signed in with your ChatGPT account — so there is often nothing to set up.
108
+ If the card's own agent cannot run here, any other ready one stands in and the card says which, and why.
109
+
110
+ To see what this machine will use, and the one step that would fix an agent that is not ready:
111
+
112
+ ```bash
113
+ npx @agilemonster/mcp-server agents
114
+ ```
115
+
116
+ The agent gets the card, the requester's replies on it, and three rules: decide open details the way the codebase
117
+ already does instead of asking, check your own work, and do not commit — the runner commits and deploys
118
+ exactly the files the agent changed, after its own build check.
119
+
120
+ - **No CLI installed or signed in?** The card falls back to the built-in engine (the server-side model picks
121
+ a few files and writes one patch). It cannot explore or debug, so it is noticeably weaker; the card tells
122
+ you what to install or sign in to.
123
+ - **Only your own cards reach the agent.** It can run commands on your machine, so a card or comment written
124
+ by another board member is never passed to it. Those cards use the built-in engine.
125
+ - **Your uncommitted work is safe.** If the agent touches a file you had open changes in, everything it did
126
+ is put back and the card asks you to commit or stash first.
127
+
128
+ | Variable | Effect |
129
+ | :--- | :--- |
130
+ | `AGILEMONSTER_AGENT_ENGINE` | `claude`, `cursor` or `codex` to pin one; `builtin` to never start an agent CLI. |
131
+ | `AGILEMONSTER_AGENT_COMMAND` | Your own command instead. It runs in the project folder with the brief in `$AGILEMONSTER_PROMPT_FILE`; whatever it prints last is the report. |
132
+ | `AGILEMONSTER_AGENT_TIMEOUT_MINUTES` | How long one agent run may take (default 30). |
133
+
134
+ ---
135
+
94
136
  ## 🚀 How deployment is decided (and remembered)
95
137
 
96
138
  Cards started from the Agile Monster web app run in the background without your IDE's context, so the
package/bin/mcp.js CHANGED
@@ -23,6 +23,14 @@ const firstArg = process.argv[2];
23
23
  if (firstArg === '--version' || firstArg === '-v' || firstArg === 'version') {
24
24
  console.log(MCP_SERVER_VERSION);
25
25
  process.exit(0);
26
+ } else if (firstArg === 'agents' || firstArg === 'doctor') {
27
+ // Which coding agent on this machine works a card queued from the board, and what would fix the others
28
+ const { describeAgents, forgetEngineReadiness } = await import('../dist/agent-engine.js');
29
+ const { ensureUserPath } = await import('../dist/runtime.js');
30
+ await ensureUserPath();
31
+ forgetEngineReadiness();
32
+ console.log(await describeAgents());
33
+ process.exit(0);
26
34
  } else if (firstArg === 'pulse-daemon' || firstArg === '--pulse-daemon') {
27
35
  runBackgroundPulseDaemon(20000).catch((err) => {
28
36
  console.error('Fatal Pulse Daemon Error:', err);
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Hands a card to a real coding agent running headless in the project folder — the same Cursor, Claude Code
3
+ * or Codex agent the developer uses interactively — instead of the built-in "pick six files, generate one
4
+ * search/replace patch" pipeline.
5
+ *
6
+ * The difference is the whole point: an agent with tools can search the repository for "Recently active
7
+ * projects", open the files it finds, run the type checker and fix what it broke. The built-in pipeline can
8
+ * do none of that, which is why it asked the requester where things live and gave up on a compile error.
9
+ *
10
+ * This module knows how to start each CLI, what the working tree looked like before, and what the agent
11
+ * changed. It never commits: the runner ships exactly the files reported here through its normal gates.
12
+ */
13
+ export type EngineId = 'claude' | 'cursor' | 'codex' | 'custom';
14
+ export interface EngineRunOptions {
15
+ prompt: string;
16
+ projectPath: string;
17
+ model?: string | null;
18
+ /** Continue the agent's own session (it remembers what it did) instead of starting cold. */
19
+ resumeSessionId?: string | null;
20
+ timeoutMs?: number;
21
+ }
22
+ export interface EngineRun {
23
+ ok: boolean;
24
+ /** The agent's final message. */
25
+ text: string;
26
+ sessionId?: string;
27
+ costUsd?: number;
28
+ timedOut: boolean;
29
+ durationMs: number;
30
+ /** Why the run produced nothing usable (for the log and the card). */
31
+ failure?: string;
32
+ }
33
+ export interface ResolvedEngine {
34
+ id: EngineId;
35
+ label: string;
36
+ bin: string;
37
+ }
38
+ /** Forget cached sign-in checks (a developer who just signed in should not wait out the cache; tests). */
39
+ export declare function forgetEngineReadiness(): void;
40
+ export interface EngineStatus {
41
+ id: EngineId;
42
+ label: string;
43
+ bin: string | null;
44
+ /** Null when this agent can take a card now, otherwise what stands in the way, in words for the developer. */
45
+ problem: string | null;
46
+ }
47
+ /** Every agent CLI this runner knows, and whether each can take a card on this machine right now. */
48
+ export declare function engineStatuses(): Promise<EngineStatus[]>;
49
+ /** What `agilemonster agents` prints: which agent works a queued card here, and the one step that would fix the others. */
50
+ export declare function describeAgents(): Promise<string>;
51
+ export declare function engineLabelFor(provider: string): string | null;
52
+ /**
53
+ * The agent CLIs this machine can run for a card, best first: the card's own agent, then any other one that
54
+ * is installed. `AGILEMONSTER_AGENT_ENGINE` pins the choice (`claude`, `cursor`, `codex`) or switches the
55
+ * whole thing off (`builtin`); `AGILEMONSTER_AGENT_COMMAND` replaces it with a command of the developer's own.
56
+ *
57
+ * `notes` say, in words for the card, why a better engine was passed over — an installed CLI that is not
58
+ * signed in is one command away from working, and the developer should hear that.
59
+ */
60
+ export declare function resolveEngines(provider: string): Promise<{
61
+ engines: ResolvedEngine[];
62
+ notes: string[];
63
+ }>;
64
+ export declare function agentTimeoutMs(): number;
65
+ /** Run one agent to completion. Never throws: a CLI that is missing, logged out or crashed comes back as `ok: false`. */
66
+ export declare function runEngine(engine: ResolvedEngine, opts: EngineRunOptions): Promise<EngineRun>;
67
+ export interface ConversationTurn {
68
+ who: 'requester' | 'agent';
69
+ text: string;
70
+ }
71
+ export interface AgentBrief {
72
+ title: string;
73
+ description: string;
74
+ projectPath: string;
75
+ boardName?: string | null;
76
+ conversation: ConversationTurn[];
77
+ /** Files with the developer's own uncommitted work: off limits. */
78
+ developerFiles: string[];
79
+ /** Uncommitted files an earlier attempt at this card left behind: the agent's to continue. */
80
+ earlierFiles: string[];
81
+ earlierSummary?: string | null;
82
+ }
83
+ /**
84
+ * What the agent is told. It reads like a request typed into the IDE — the card, the thread, and the few
85
+ * rules that differ from an interactive session (nobody is there to answer, and the runner does the shipping).
86
+ */
87
+ export declare function buildAgentPrompt(brief: AgentBrief): string;
88
+ export interface AgentReport {
89
+ status: 'done' | 'blocked' | 'unknown';
90
+ summary: string;
91
+ verificationSteps: string[];
92
+ question: string;
93
+ }
94
+ /** Read the closing JSON object out of the agent's final message. Tolerates code fences and prose around it. */
95
+ export declare function parseAgentReport(text: string): AgentReport;
96
+ /** The agent's prose without the closing JSON object: what to show a human when the report is missing a field. */
97
+ export declare function proseOf(text: string, maxChars?: number): string;
98
+ export interface TreeSnapshot {
99
+ head: string;
100
+ /** Every path Git reported as changed or untracked, with a fingerprint of its content at that moment. */
101
+ entries: Map<string, string>;
102
+ /** Contents of those files, so a file the developer had open can be put back exactly (null = did not exist). */
103
+ saved: Map<string, Buffer | null>;
104
+ }
105
+ /** Paths from `git status --porcelain -z`. A rename reports both its new and its old path. */
106
+ export declare function parsePorcelainZ(out: string): string[];
107
+ /** Record the working tree before the agent starts. Null when the folder is not a Git repository. */
108
+ export declare function snapshotWorkingTree(projectPath: string): Promise<TreeSnapshot | null>;
109
+ export interface TreeChange {
110
+ /** Every file that differs from the snapshot. */
111
+ changed: string[];
112
+ /** Of those, the ones that already had uncommitted changes before the agent started. */
113
+ overlapping: string[];
114
+ /** Set when the agent committed and the commit is already on a remote: it can no longer be folded back. */
115
+ pushedCommit?: string;
116
+ }
117
+ /**
118
+ * Compare the working tree with the snapshot. If the agent committed despite its instructions, the commits
119
+ * are undone first (soft, so nothing is lost) — unless they were pushed, which only a human can sort out.
120
+ */
121
+ export declare function changesSince(projectPath: string, snap: TreeSnapshot): Promise<TreeChange>;
122
+ /**
123
+ * Put the given files back the way the snapshot found them. Returns the files that could not be restored
124
+ * (only possible for a file the developer had changed that was too large to keep a copy of).
125
+ */
126
+ export declare function restoreWorkingTree(projectPath: string, snap: TreeSnapshot, paths: string[]): Promise<string[]>;
127
+ /**
128
+ * `git diff` does not show files Git has never seen. Mark the agent's new files as "to be added" (no content
129
+ * is staged) so the reviewer's diff includes them.
130
+ */
131
+ export declare function markNewFilesForDiff(projectPath: string, files: string[]): Promise<void>;
132
+ /**
133
+ * Snapshot the tree before this card's agent starts, and note that a run is in progress.
134
+ *
135
+ * `interrupted` is true when an earlier run of the same card never reached a conclusion (the machine slept,
136
+ * the daemon was killed). Whatever that run left in the tree cannot be told apart from the developer's own
137
+ * edits, so it is treated as theirs — never reverted, never committed — and the card can say where the
138
+ * leftovers probably came from.
139
+ */
140
+ export declare function baselineFor(cardId: string, projectPath: string): Promise<{
141
+ snapshot: TreeSnapshot;
142
+ interrupted: boolean;
143
+ } | null>;
144
+ /** The card's run reached a conclusion (shipped, reverted, or handed to the human): it is no longer in progress. */
145
+ export declare function clearBaseline(cardId: string): void;