@dalmasonto/taskflow-mcp 1.0.35 → 2.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 +138 -193
- package/dist/attachment-download.d.ts +74 -0
- package/dist/attachment-download.js +193 -0
- package/dist/attachment-download.js.map +1 -0
- package/dist/attachments.d.ts +23 -0
- package/dist/attachments.js +66 -0
- package/dist/attachments.js.map +1 -0
- package/dist/client.d.ts +206 -0
- package/dist/client.js +279 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +137 -18
- package/dist/config.js +187 -106
- package/dist/config.js.map +1 -0
- package/dist/connect.d.ts +89 -0
- package/dist/connect.js +269 -0
- package/dist/connect.js.map +1 -0
- package/dist/doctor.d.ts +24 -0
- package/dist/doctor.js +120 -0
- package/dist/doctor.js.map +1 -0
- package/dist/events.d.ts +186 -0
- package/dist/events.js +407 -0
- package/dist/events.js.map +1 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.js +121 -218
- package/dist/index.js.map +1 -0
- package/dist/instructions.d.ts +12 -0
- package/dist/instructions.js +114 -0
- package/dist/instructions.js.map +1 -0
- package/dist/mint.d.ts +62 -0
- package/dist/mint.js +135 -0
- package/dist/mint.js.map +1 -0
- package/dist/mirror.d.ts +68 -0
- package/dist/mirror.js +103 -0
- package/dist/mirror.js.map +1 -0
- package/dist/pane-queue.d.ts +29 -0
- package/dist/pane-queue.js +35 -0
- package/dist/pane-queue.js.map +1 -0
- package/dist/prompts.d.ts +79 -0
- package/dist/prompts.js +211 -0
- package/dist/prompts.js.map +1 -0
- package/dist/resolve.d.ts +72 -0
- package/dist/resolve.js +89 -0
- package/dist/resolve.js.map +1 -0
- package/dist/runtime.d.ts +54 -0
- package/dist/runtime.js +321 -0
- package/dist/runtime.js.map +1 -0
- package/dist/server.d.ts +56 -0
- package/dist/server.js +793 -0
- package/dist/server.js.map +1 -0
- package/dist/session-identifier.d.ts +48 -0
- package/dist/session-identifier.js +44 -0
- package/dist/session-identifier.js.map +1 -0
- package/dist/sessions-store.d.ts +38 -0
- package/dist/sessions-store.js +88 -0
- package/dist/sessions-store.js.map +1 -0
- package/dist/tmux.d.ts +200 -0
- package/dist/tmux.js +580 -0
- package/dist/tmux.js.map +1 -0
- package/hooks/metadata.mjs +99 -0
- package/hooks/permission-prompt.mjs +100 -0
- package/hooks/taskflow-hook.mjs +499 -0
- package/hooks/tool-logging.mjs +63 -0
- package/package.json +38 -29
- package/dist/agent-registry.d.ts +0 -28
- package/dist/agent-registry.js +0 -158
- package/dist/db.d.ts +0 -5
- package/dist/db.js +0 -220
- package/dist/helpers.d.ts +0 -21
- package/dist/helpers.js +0 -27
- package/dist/resources.d.ts +0 -2
- package/dist/resources.js +0 -89
- package/dist/retry.d.ts +0 -34
- package/dist/retry.js +0 -94
- package/dist/sse.d.ts +0 -10
- package/dist/sse.js +0 -824
- package/dist/tmux-bridge.d.ts +0 -13
- package/dist/tmux-bridge.js +0 -217
- package/dist/tools/activity.d.ts +0 -39
- package/dist/tools/activity.js +0 -152
- package/dist/tools/agent-inbox.d.ts +0 -12
- package/dist/tools/agent-inbox.js +0 -272
- package/dist/tools/agent.d.ts +0 -14
- package/dist/tools/agent.js +0 -168
- package/dist/tools/analytics.d.ts +0 -21
- package/dist/tools/analytics.js +0 -191
- package/dist/tools/checkpoint.d.ts +0 -27
- package/dist/tools/checkpoint.js +0 -105
- package/dist/tools/notifications.d.ts +0 -31
- package/dist/tools/notifications.js +0 -59
- package/dist/tools/projects.d.ts +0 -55
- package/dist/tools/projects.js +0 -112
- package/dist/tools/settings.d.ts +0 -19
- package/dist/tools/settings.js +0 -73
- package/dist/tools/tasks.d.ts +0 -105
- package/dist/tools/tasks.js +0 -403
- package/dist/tools/terminal.d.ts +0 -4
- package/dist/tools/terminal.js +0 -98
- package/dist/tools/timer.d.ts +0 -37
- package/dist/tools/timer.js +0 -154
- package/dist/types.d.ts +0 -83
- package/dist/types.js +0 -30
package/dist/index.js
CHANGED
|
@@ -1,232 +1,135 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Bin entry: load `.taskflow.json`, build the MCP server, and serve over stdio.
|
|
4
|
+
*
|
|
5
|
+
* A configuration failure prints a clear message to stderr and exits non-zero so
|
|
6
|
+
* the human fixes the credential file rather than getting a silent dead server.
|
|
7
|
+
*/
|
|
8
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
9
|
+
import { buildServer } from "./server.js";
|
|
10
|
+
import { ConfigError } from "./config.js";
|
|
11
|
+
import { runDoctor } from "./doctor.js";
|
|
12
|
+
import { runTmuxMirror } from "./tmux.js";
|
|
13
|
+
import { runMint } from "./mint.js";
|
|
14
|
+
import { startAgent } from "./runtime.js";
|
|
15
|
+
const USAGE = `taskflow-mcp — TaskFlow MCP server
|
|
16
|
+
|
|
17
|
+
taskflow-mcp Serve over stdio (how an MCP client runs it).
|
|
18
|
+
taskflow-mcp --check Verify config + backend auth, then exit.
|
|
19
|
+
taskflow-mcp --tmux [target] Mirror a tmux pane into the dashboard terminal.
|
|
20
|
+
taskflow-mcp --mint <name> Create a NEW agent identity + profile, then exit.
|
|
21
|
+
taskflow-mcp --help This message.
|
|
22
|
+
|
|
23
|
+
GIVING A SECOND TERMINAL ITS OWN IDENTITY. An agent is identified by
|
|
24
|
+
project + profile, so two terminals sharing the default \`main\` profile are ONE
|
|
25
|
+
agent: one row in the dashboard, one DM inbox, one shared read cursor. --mint
|
|
26
|
+
creates a separate identity and writes it to .taskflow.json:
|
|
27
|
+
|
|
28
|
+
taskflow-mcp --mint bear --display-name "Claude (bear)"
|
|
29
|
+
export TASKFLOW_PROFILE=bear # then start that terminal's agent
|
|
30
|
+
|
|
31
|
+
It needs YOUR user token (--token, or TASKFLOW_USER_TOKEN), not an agent key:
|
|
32
|
+
linking an agent is human-authorized and records who vouched for it, which is
|
|
33
|
+
why this is a command you run and not a tool the agent can call. An existing
|
|
34
|
+
profile is never overwritten and \`default_profile\` never moves, so terminals
|
|
35
|
+
already running keep the identity they have.
|
|
36
|
+
|
|
37
|
+
The MCP tools (whoami, create_task, ...) are called by the MODEL through an MCP
|
|
38
|
+
client, not typed as commands. To check the setup yourself, use --check.
|
|
39
|
+
|
|
40
|
+
TERMINAL MIRRORING IS AUTOMATIC. When the agent runs inside tmux, the server
|
|
41
|
+
finds its own pane and streams it to the dashboard — nothing to launch, no pane
|
|
42
|
+
id to look up. Set TASKFLOW_MIRROR=off to disable it. The agent still connects
|
|
43
|
+
and appears online without tmux; only the streamed terminal needs a pane.
|
|
44
|
+
|
|
45
|
+
WHICH IDENTITY AM I? With one profile in .taskflow.json the server connects as
|
|
46
|
+
it silently. With several, it connects as NONE of them and the agent must ask
|
|
47
|
+
you which this terminal is, then call select_profile — the pick is remembered
|
|
48
|
+
per terminal in .taskflow/sessions.json. Set TASKFLOW_PROFILE to skip the ask.
|
|
49
|
+
|
|
50
|
+
--tmux is only for mirroring a pane the agent is NOT running in (say, watching a
|
|
51
|
+
build in another window). It runs in the foreground until Ctrl-C; the target
|
|
52
|
+
defaults to tmux's active pane, or pass one from \`tmux list-panes -a\`. Options:
|
|
53
|
+
--interval=<ms> Capture cadence (default 2000).
|
|
54
|
+
--profile=<name> Act as a non-default .taskflow.json profile.
|
|
55
|
+
--notify Type a one-line unread notice INTO the pane when messages
|
|
56
|
+
arrive. Off by default: this writes to a live session.
|
|
57
|
+
--notify-submit Also press Enter, so the agent acts on it unprompted.
|
|
58
|
+
Anyone who can post to a channel can then wake the agent —
|
|
59
|
+
enable it only for sessions you control.`;
|
|
60
|
+
async function main() {
|
|
61
|
+
const argv = process.argv.slice(2);
|
|
62
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
63
|
+
process.stdout.write(`${USAGE}\n`);
|
|
20
64
|
return;
|
|
21
|
-
// Close each orphaned session with end = now (preserves the real elapsed time)
|
|
22
|
-
for (const session of orphaned) {
|
|
23
|
-
db.prepare('UPDATE sessions SET end = ? WHERE id = ?').run(nowIso, session.id);
|
|
24
65
|
}
|
|
25
|
-
//
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (task?.status === 'in_progress') {
|
|
30
|
-
db.prepare("UPDATE tasks SET status = 'paused', updated_at = ? WHERE id = ?").run(nowIso, taskId);
|
|
31
|
-
broadcast('task_updated', { entity: 'task', action: 'task_status_changed', payload: db.prepare('SELECT * FROM tasks WHERE id = ?').get(taskId) });
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
cleanupOrphanedSessions();
|
|
36
|
-
// Always start the HTTP/SSE server (probes for existing instances, finds fallback port)
|
|
37
|
-
await startSSEServer();
|
|
38
|
-
// Liveness checker — periodically check registered agents and mark dead ones
|
|
39
|
-
const cfg = getConfig();
|
|
40
|
-
setInterval(async () => {
|
|
41
|
-
try {
|
|
42
|
-
const { checkAgentLiveness } = await import('./agent-registry.js');
|
|
43
|
-
checkAgentLiveness();
|
|
66
|
+
// --check is a human-facing diagnostic, so it prints to stdout and exits;
|
|
67
|
+
// it never opens the MCP transport.
|
|
68
|
+
if (argv.includes("--check") || argv.includes("--doctor")) {
|
|
69
|
+
process.exit(await runDoctor());
|
|
44
70
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
//
|
|
48
|
-
if (
|
|
49
|
-
const { McpServer } = await import('@modelcontextprotocol/sdk/server/mcp.js');
|
|
50
|
-
const { StdioServerTransport } = await import('@modelcontextprotocol/sdk/server/stdio.js');
|
|
51
|
-
const { registerTaskTools } = await import('./tools/tasks.js');
|
|
52
|
-
const { registerProjectTools } = await import('./tools/projects.js');
|
|
53
|
-
const { registerTimerTools } = await import('./tools/timer.js');
|
|
54
|
-
const { registerAnalyticsTools } = await import('./tools/analytics.js');
|
|
55
|
-
const { registerActivityTools } = await import('./tools/activity.js');
|
|
56
|
-
const { registerNotificationTools } = await import('./tools/notifications.js');
|
|
57
|
-
const { registerSettingsTools } = await import('./tools/settings.js');
|
|
58
|
-
const { registerAgentTools } = await import('./tools/agent.js');
|
|
59
|
-
const { registerAgentInboxTools } = await import('./tools/agent-inbox.js');
|
|
60
|
-
const { registerTerminalTools } = await import('./tools/terminal.js');
|
|
61
|
-
const { registerResources } = await import('./resources.js');
|
|
62
|
-
const { registerCheckpointTools } = await import('./tools/checkpoint.js');
|
|
63
|
-
const server = new McpServer({ name: 'taskflow', version: '1.0.25' }, {
|
|
64
|
-
instructions: [
|
|
65
|
-
'You MUST call bootstrap (preferred) or get_agent_instructions before doing any work. This loads your task management rules and project context.',
|
|
66
|
-
'You MUST create a task in TaskFlow before editing files or running commands. search_tasks first to avoid duplicates.',
|
|
67
|
-
'You MUST call start_timer when beginning work and stop_timer when done. Timer lifecycle: start_timer → work → stop_timer(final_status).',
|
|
68
|
-
'Never silently fix bugs — always create or reopen a task first, then track the fix.',
|
|
69
|
-
'When you need user input, call ask_user to post it to the Agent Inbox so the user can respond remotely.',
|
|
70
|
-
'Use log_debug to document your work — it is shared memory visible to the user and other agents.',
|
|
71
|
-
].join('\n'),
|
|
72
|
-
});
|
|
73
|
-
// Filled in after agent registration — used by the inbox drain below
|
|
74
|
-
let agentNameGetter = () => 'unknown';
|
|
75
|
-
// Drain any undelivered inbox messages for this agent and prepend them to a
|
|
76
|
-
// tool result. This is the non-tmux delivery path: since we can't inject into
|
|
77
|
-
// stdin, we piggyback pending messages onto the next tool response so Claude
|
|
78
|
-
// sees them in its context without any terminal tricks.
|
|
79
|
-
function drainInboxNotice() {
|
|
71
|
+
// --mint is a human-facing command for the same reason it is not an MCP tool:
|
|
72
|
+
// `POST /agents/link` is RequireAuth-gated, so it needs a USER token that the
|
|
73
|
+
// agent does not have. Prints and exits; never opens the MCP transport.
|
|
74
|
+
if (argv.includes("--mint")) {
|
|
80
75
|
try {
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const db = getDb();
|
|
85
|
-
const pending = db.prepare(`SELECT id, sender_name, question FROM agent_messages
|
|
86
|
-
WHERE recipient_name = ? AND status = 'pending' AND delivered IS NULL
|
|
87
|
-
ORDER BY created_at ASC`).all(name);
|
|
88
|
-
if (pending.length === 0)
|
|
89
|
-
return '';
|
|
90
|
-
for (const msg of pending) {
|
|
91
|
-
db.prepare('UPDATE agent_messages SET delivered = 1 WHERE id = ?').run(msg.id);
|
|
92
|
-
}
|
|
93
|
-
const lines = pending.map(m => ` [Inbox #${m.id} from ${m.sender_name}]: ${m.question}`).join('\n');
|
|
94
|
-
return `\n⚠️ INBOX MESSAGE(S) — respond before continuing:\n${lines}\n`;
|
|
76
|
+
for (const line of await runMint(argv))
|
|
77
|
+
process.stdout.write(`${line}\n`);
|
|
78
|
+
process.exit(0);
|
|
95
79
|
}
|
|
96
|
-
catch {
|
|
97
|
-
|
|
80
|
+
catch (err) {
|
|
81
|
+
// A MintError is a message written for the operator; anything else is a
|
|
82
|
+
// surprise and keeps its own text.
|
|
83
|
+
process.stderr.write(`${err.message}\n`);
|
|
84
|
+
process.exit(1);
|
|
98
85
|
}
|
|
99
86
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
if (task.denied_tools) {
|
|
117
|
-
const denied = JSON.parse(task.denied_tools);
|
|
118
|
-
if (denied.includes(toolName)) {
|
|
119
|
-
return { isError: true, content: [{ type: 'text', text: JSON.stringify({ error: `Tool "${toolName}" is denied for task #${activeSession.task_id}`, code: 'TOOL_DENIED' }) }] };
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
if (task.allowed_tools) {
|
|
123
|
-
const allowed = JSON.parse(task.allowed_tools);
|
|
124
|
-
if (!allowed.includes(toolName)) {
|
|
125
|
-
return { isError: true, content: [{ type: 'text', text: JSON.stringify({ error: `Tool "${toolName}" is not in the allowlist for task #${activeSession.task_id}`, code: 'TOOL_NOT_ALLOWED' }) }] };
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
catch { /* don't break tool execution on allowlist check failure */ }
|
|
132
|
-
const start = Date.now();
|
|
133
|
-
try {
|
|
134
|
-
const result = await originalCb(...cbArgs);
|
|
135
|
-
const duration = Date.now() - start;
|
|
136
|
-
// Record execution and broadcast event
|
|
137
|
-
try {
|
|
138
|
-
const ts = new Date().toISOString();
|
|
139
|
-
const db = getDb();
|
|
140
|
-
db.prepare('INSERT INTO tool_executions (tool_name, duration_ms, success, created_at) VALUES (?, ?, 1, ?)').run(toolName, duration, ts);
|
|
141
|
-
broadcast('tool_executed', { entity: 'tool', action: 'tool_executed', payload: { tool_name: toolName, duration_ms: duration, success: true, created_at: ts } });
|
|
142
|
-
}
|
|
143
|
-
catch { /* don't break tool execution */ }
|
|
144
|
-
// Non-tmux inbox delivery: prepend any pending messages to the result
|
|
145
|
-
const notice = drainInboxNotice();
|
|
146
|
-
if (notice && result?.content) {
|
|
147
|
-
const first = result.content.find((c) => c.type === 'text');
|
|
148
|
-
if (first)
|
|
149
|
-
first.text = notice + first.text;
|
|
150
|
-
}
|
|
151
|
-
return result;
|
|
152
|
-
}
|
|
153
|
-
catch (err) {
|
|
154
|
-
const duration = Date.now() - start;
|
|
155
|
-
try {
|
|
156
|
-
const ts = new Date().toISOString();
|
|
157
|
-
const db = getDb();
|
|
158
|
-
db.prepare('INSERT INTO tool_executions (tool_name, duration_ms, success, error_message, created_at) VALUES (?, ?, 0, ?, ?)').run(toolName, duration, err.message ?? String(err), ts);
|
|
159
|
-
broadcast('tool_failed', { entity: 'tool', action: 'tool_failed', payload: { tool_name: toolName, duration_ms: duration, success: false, error: err.message, created_at: ts } });
|
|
160
|
-
}
|
|
161
|
-
catch { /* don't break tool execution */ }
|
|
162
|
-
throw err;
|
|
163
|
-
}
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
return originalTool.apply(server, args);
|
|
167
|
-
};
|
|
168
|
-
registerAgentTools(server);
|
|
169
|
-
registerTaskTools(server);
|
|
170
|
-
registerProjectTools(server);
|
|
171
|
-
registerTimerTools(server);
|
|
172
|
-
registerAnalyticsTools(server);
|
|
173
|
-
registerActivityTools(server);
|
|
174
|
-
registerNotificationTools(server);
|
|
175
|
-
registerSettingsTools(server);
|
|
176
|
-
registerAgentInboxTools(server);
|
|
177
|
-
registerTerminalTools(server);
|
|
178
|
-
registerCheckpointTools(server);
|
|
179
|
-
registerResources(server);
|
|
180
|
-
const transport = new StdioServerTransport();
|
|
181
|
-
await server.connect(transport);
|
|
182
|
-
const { registerAgent, unregisterAgent } = await import('./agent-registry.js');
|
|
183
|
-
const { setAgentName, getAgentName } = await import('./tools/agent-inbox.js');
|
|
184
|
-
// Auto-register this agent and sync name to agent-inbox tools
|
|
185
|
-
const agentName = registerAgent();
|
|
186
|
-
setAgentName(agentName);
|
|
187
|
-
agentNameGetter = getAgentName; // wire up inbox drain now that we have a name
|
|
188
|
-
const agentPid = process.ppid;
|
|
189
|
-
console.error(`[agent] registered as "${agentName}"`);
|
|
190
|
-
let cleanup = () => { try {
|
|
191
|
-
unregisterAgent(getAgentName());
|
|
87
|
+
const tmuxIndex = argv.indexOf("--tmux");
|
|
88
|
+
if (tmuxIndex !== -1) {
|
|
89
|
+
const flag = (name) => {
|
|
90
|
+
const hit = argv.find((a) => a.startsWith(`--${name}=`));
|
|
91
|
+
return hit ? hit.slice(name.length + 3) : undefined;
|
|
92
|
+
};
|
|
93
|
+
// The first non-flag argument after --tmux is the pane target.
|
|
94
|
+
const target = argv.slice(tmuxIndex + 1).find((a) => !a.startsWith("--"));
|
|
95
|
+
const interval = flag("interval");
|
|
96
|
+
process.exit(await runTmuxMirror({
|
|
97
|
+
target,
|
|
98
|
+
profile: flag("profile"),
|
|
99
|
+
notify: argv.includes("--notify") || argv.includes("--notify-submit"),
|
|
100
|
+
notifySubmit: argv.includes("--notify-submit"),
|
|
101
|
+
...(interval ? { intervalMs: Number(interval) } : {}),
|
|
102
|
+
}));
|
|
192
103
|
}
|
|
193
|
-
|
|
194
|
-
process.on('SIGINT', cleanup);
|
|
195
|
-
process.on('SIGTERM', cleanup);
|
|
196
|
-
// Tmux bridge: SSE listener for instant delivery + capture for terminal→chat
|
|
197
|
-
let tmuxTarget = null;
|
|
104
|
+
let server;
|
|
198
105
|
try {
|
|
199
|
-
|
|
200
|
-
const ptsPath = exec(`readlink /proc/${agentPid}/fd/0`).toString().trim();
|
|
201
|
-
const panes = exec('tmux list-panes -a -F "#{pane_id} #{pane_tty}"').toString().trim().split('\n');
|
|
202
|
-
for (const line of panes) {
|
|
203
|
-
const [paneId, paneTty] = line.split(' ');
|
|
204
|
-
if (paneTty === ptsPath) {
|
|
205
|
-
tmuxTarget = paneId;
|
|
206
|
-
break;
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
catch {
|
|
211
|
-
console.error('[bridge] tmux not available');
|
|
106
|
+
server = buildServer();
|
|
212
107
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
const stopBridge = startTmuxBridge({
|
|
218
|
-
getAgentName,
|
|
219
|
-
agentPid,
|
|
220
|
-
tmuxPane: tmuxTarget,
|
|
221
|
-
});
|
|
222
|
-
if (!tmuxTarget) {
|
|
223
|
-
console.error('[bridge] agent not in tmux — using stderr delivery for inbox messages');
|
|
108
|
+
catch (err) {
|
|
109
|
+
if (err instanceof ConfigError) {
|
|
110
|
+
process.stderr.write(`taskflow-mcp: ${err.message}\n`);
|
|
111
|
+
process.exit(1);
|
|
224
112
|
}
|
|
225
|
-
|
|
226
|
-
cleanup = () => { stopBridge(); originalCleanup(); };
|
|
227
|
-
process.removeListener('SIGINT', originalCleanup);
|
|
228
|
-
process.removeListener('SIGTERM', originalCleanup);
|
|
229
|
-
process.on('SIGINT', cleanup);
|
|
230
|
-
process.on('SIGTERM', cleanup);
|
|
113
|
+
throw err;
|
|
231
114
|
}
|
|
115
|
+
const transport = new StdioServerTransport();
|
|
116
|
+
await server.connect(transport);
|
|
117
|
+
// Stderr only — stdout is the MCP transport and must stay clean.
|
|
118
|
+
process.stderr.write("taskflow-mcp: connected (stdio)\n");
|
|
119
|
+
// Bring the agent online. This is NOT conditional on tmux: registering a
|
|
120
|
+
// session is what makes the agent visible and reachable, and it must happen
|
|
121
|
+
// whether or not there is a pane to mirror. Best-effort in every direction —
|
|
122
|
+
// no credential, or a backend that is down, degrades to retrying quietly.
|
|
123
|
+
//
|
|
124
|
+
// Not awaited, and its rejection is caught here rather than by main()'s
|
|
125
|
+
// handler: the transport is already serving, so a bad TASKFLOW_PROFILE must
|
|
126
|
+
// cost the connection, not the tool server.
|
|
127
|
+
void startAgent().catch((err) => {
|
|
128
|
+
process.stderr.write(`taskflow-mcp: could not start agent (${err.message})\n`);
|
|
129
|
+
});
|
|
232
130
|
}
|
|
131
|
+
main().catch((err) => {
|
|
132
|
+
process.stderr.write(`taskflow-mcp: fatal: ${err.stack ?? err}\n`);
|
|
133
|
+
process.exit(1);
|
|
134
|
+
});
|
|
135
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent-facing guidance surfaced to the model on connect.
|
|
3
|
+
*
|
|
4
|
+
* The MCP `initialize` result carries an optional `instructions` string that the
|
|
5
|
+
* client shows the model before it uses any tool. Without it an agent only sees
|
|
6
|
+
* per-tool schemas and misses what TaskFlow *is*, the workflow, and the
|
|
7
|
+
* conventions — e.g. attaching a file with `send_message`'s `files` array
|
|
8
|
+
* instead of pasting its contents inline. This is that overview; keep it
|
|
9
|
+
* accurate to the registered tools. Style follows the v1 taskflow-mcp
|
|
10
|
+
* onboarding: a plain "what this is", then imperative rules.
|
|
11
|
+
*/
|
|
12
|
+
export declare const AGENT_INSTRUCTIONS = "# TaskFlow\n\nTaskFlow is a **local-first task board and realtime chat with MCP integration**,\nbuilt for **multi-agent collaboration**: several AI agents and humans work in one\nshared project, discover each other, message in channels, claim and review tasks,\nstream their terminals live, and coordinate to build software together. You are\n**one agent** in such a project \u2014 act like a good teammate and keep people in the\nloop.\n\n## Use TaskFlow by default\n**Proactively track your work in TaskFlow unless the user explicitly tells you\nnot to.** At the start of a session, confirm your identity with **whoami** and\nfind your project's board with **list_tasks**. As you work, keep the board the\nsource of truth: create a task for every substantial piece of work (features,\nbugs, refactors, and investigation/debugging alike), claim it, move it through\nits statuses, and log activity as you go. Debugging is real work \u2014 open or reopen\na task for it, never silently fix a bug. The only time you skip this is when the\nuser says something like \"don't use TaskFlow\" or \"don't track this\" \u2014 then honor\nthat for the rest of the session.\n\n## Identity & connecting\n- One credential maps to **one agent identity in one project**. The optional\n `profile` argument on every tool selects which identity to act as (use the\n `reviewer` profile for review work).\n- **Connecting is automatic.** The server registers your session and keeps\n heartbeating on its own \u2014 you do not need to call `register_session` or\n `heartbeat` to appear online. Call **whoami** to CONFIRM your identity,\n project, connection and terminal mirror. A `mirror.state` of `off` only\n means there is no tmux pane to stream; you are still connected.\n- **If a tool returns `profile_ambiguous`**, this repo defines several\n identities and nothing says which one this terminal is. Never guess a\n profile. Do not guess based on cwd, hostname, or any other assumption \u2014 ask\n your human which to use: show each `display_name`, note which is\n `recommended`, and warn that an `in_use` one is already taken by another\n terminal \u2014 then call **select_profile** with their answer. Picking wrong\n makes two terminals the same agent, sharing one inbox and one read cursor.\n You are asked once per terminal; the choice is remembered across reconnects.\n If **select_profile** returns a `warning`, repeat it to your human before you\n do anything else \u2014 another terminal is already using that identity.\n- Call **list_agents** to see who else is on the project and **list_channels** for\n the rooms you can post in. If other agents are active, coordinate rather than\n duplicate work.\n\n## Messaging \u2014 stay in the loop\n- Call **check_messages** regularly \u2014 at minimum when you finish a task, before\n you start a new one, and after a long stretch of work. It returns only what you\n have NOT marked read, across every channel you're in. After you've read AND\n acted on them, call **mark_read** for each channel with the highest message id\n you handled \u2014 otherwise you'll be handed the same messages again.\n- Messages are either **broadcast** or **directed** at specific members. If a\n message is prefixed with **\"\uD83D\uDEAB DO NOT ACT, YOU ARE NOT THE TARGET\"**, someone\n else owns that work: you may chat or add information, but must NOT execute the\n task.\n- **send_message** posts markdown to a channel (find ids with **list_channels**,\n people with **list_agents**). To share a file \u2014 a spec, log, diff, screenshot,\n or anything a teammate should open as a file \u2014 pass its path in the **`files`\n array**. Do NOT paste large file contents into the message body; attach the\n file instead.\n- **Referencing tasks vs GitHub issues.** In any message, activity note or task\n description, `#12` and `TASK#12` render as a clickable **TaskFlow task** chip.\n So write a **GitHub issue** as `#gh12` (or `#GH12`) and a **chat message**\n as `#msg12` \u2014 never a bare `#12` for either.\n A bare `#12` meaning a GitHub issue becomes a task chip pointing at TaskFlow\n task 12, which is a different thing or does not exist at all, and it misleads\n whoever reads it. `#gh12` renders as a distinct chip linking to the issue on\n the project's linked repo.\n- **download_attachment** fetches a file a teammate attached. It returns a PATH,\n not the contents \u2014 open it with your own file tools, and check `size_bytes`\n before reading anything wholesale.\n\n## Tasks \u2014 the work\n- Track real work as tasks. **list_tasks** (filter by `status`, or\n `assigned='me'`), **create_task**, **claim_task**, **update_task_status**,\n **report_review**. Search/list before creating so you don't duplicate an\n existing task.\n- Typical lifecycle: pick a ready task \u2192 **claim_task** \u2192 set **in_progress** \u2192\n do the work \u2192 set **partial_done** to request human review \u2192 a reviewer runs\n **report_review** (`approved` | `changes_requested`) \u2192 **done** on approval,\n or back to **in_progress** to address changes.\n- Statuses are a fixed set: `not_started`, `in_progress`, `paused`,\n `blocked`, `partial_done`, `done`, `archived`. Don't invent values. Never\n silently fix a bug \u2014 open or reopen a task first, then track the fix.\n\n## Activity & terminal \u2014 be transparent\n- **log_activity** records a notable action (optionally linked to a task);\n **get_activity** reads recent history. Use it as a running journal so humans and\n other agents can follow what you did and why, without reading all of chat.\n To mirror a note onto a task's linked GitHub issue, call it with a `task` and\n `post_to_github: true` \u2014 it posts as a comment under your owner's identity, but\n only if the project is GitHub-linked, the task is published as an issue, and your\n owner is connected + opted in (`post_as_me`); otherwise it just records the\n activity. Don't spam issues \u2014 reserve it for updates worth a public comment.\n- **capture_terminal** streams terminal output to the dashboard so humans can\n watch your work live.\n- Tell people what you're doing: announce significant actions in chat or activity,\n so the user can reconstruct what happened from your messages alone.\n\n## Etiquette\nKeep humans informed through messages and activity; prefer attaching files over\ndumping large content inline; always **mark_read** after handling messages;\nrespect the **\uD83D\uDEAB DO NOT ACT** marker; coordinate with other agents instead of\nstepping on their work; and act under the right **profile** for the job.";
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent-facing guidance surfaced to the model on connect.
|
|
3
|
+
*
|
|
4
|
+
* The MCP `initialize` result carries an optional `instructions` string that the
|
|
5
|
+
* client shows the model before it uses any tool. Without it an agent only sees
|
|
6
|
+
* per-tool schemas and misses what TaskFlow *is*, the workflow, and the
|
|
7
|
+
* conventions — e.g. attaching a file with `send_message`'s `files` array
|
|
8
|
+
* instead of pasting its contents inline. This is that overview; keep it
|
|
9
|
+
* accurate to the registered tools. Style follows the v1 taskflow-mcp
|
|
10
|
+
* onboarding: a plain "what this is", then imperative rules.
|
|
11
|
+
*/
|
|
12
|
+
export const AGENT_INSTRUCTIONS = `# TaskFlow
|
|
13
|
+
|
|
14
|
+
TaskFlow is a **local-first task board and realtime chat with MCP integration**,
|
|
15
|
+
built for **multi-agent collaboration**: several AI agents and humans work in one
|
|
16
|
+
shared project, discover each other, message in channels, claim and review tasks,
|
|
17
|
+
stream their terminals live, and coordinate to build software together. You are
|
|
18
|
+
**one agent** in such a project — act like a good teammate and keep people in the
|
|
19
|
+
loop.
|
|
20
|
+
|
|
21
|
+
## Use TaskFlow by default
|
|
22
|
+
**Proactively track your work in TaskFlow unless the user explicitly tells you
|
|
23
|
+
not to.** At the start of a session, confirm your identity with **whoami** and
|
|
24
|
+
find your project's board with **list_tasks**. As you work, keep the board the
|
|
25
|
+
source of truth: create a task for every substantial piece of work (features,
|
|
26
|
+
bugs, refactors, and investigation/debugging alike), claim it, move it through
|
|
27
|
+
its statuses, and log activity as you go. Debugging is real work — open or reopen
|
|
28
|
+
a task for it, never silently fix a bug. The only time you skip this is when the
|
|
29
|
+
user says something like "don't use TaskFlow" or "don't track this" — then honor
|
|
30
|
+
that for the rest of the session.
|
|
31
|
+
|
|
32
|
+
## Identity & connecting
|
|
33
|
+
- One credential maps to **one agent identity in one project**. The optional
|
|
34
|
+
\`profile\` argument on every tool selects which identity to act as (use the
|
|
35
|
+
\`reviewer\` profile for review work).
|
|
36
|
+
- **Connecting is automatic.** The server registers your session and keeps
|
|
37
|
+
heartbeating on its own — you do not need to call \`register_session\` or
|
|
38
|
+
\`heartbeat\` to appear online. Call **whoami** to CONFIRM your identity,
|
|
39
|
+
project, connection and terminal mirror. A \`mirror.state\` of \`off\` only
|
|
40
|
+
means there is no tmux pane to stream; you are still connected.
|
|
41
|
+
- **If a tool returns \`profile_ambiguous\`**, this repo defines several
|
|
42
|
+
identities and nothing says which one this terminal is. Never guess a
|
|
43
|
+
profile. Do not guess based on cwd, hostname, or any other assumption — ask
|
|
44
|
+
your human which to use: show each \`display_name\`, note which is
|
|
45
|
+
\`recommended\`, and warn that an \`in_use\` one is already taken by another
|
|
46
|
+
terminal — then call **select_profile** with their answer. Picking wrong
|
|
47
|
+
makes two terminals the same agent, sharing one inbox and one read cursor.
|
|
48
|
+
You are asked once per terminal; the choice is remembered across reconnects.
|
|
49
|
+
If **select_profile** returns a \`warning\`, repeat it to your human before you
|
|
50
|
+
do anything else — another terminal is already using that identity.
|
|
51
|
+
- Call **list_agents** to see who else is on the project and **list_channels** for
|
|
52
|
+
the rooms you can post in. If other agents are active, coordinate rather than
|
|
53
|
+
duplicate work.
|
|
54
|
+
|
|
55
|
+
## Messaging — stay in the loop
|
|
56
|
+
- Call **check_messages** regularly — at minimum when you finish a task, before
|
|
57
|
+
you start a new one, and after a long stretch of work. It returns only what you
|
|
58
|
+
have NOT marked read, across every channel you're in. After you've read AND
|
|
59
|
+
acted on them, call **mark_read** for each channel with the highest message id
|
|
60
|
+
you handled — otherwise you'll be handed the same messages again.
|
|
61
|
+
- Messages are either **broadcast** or **directed** at specific members. If a
|
|
62
|
+
message is prefixed with **"🚫 DO NOT ACT, YOU ARE NOT THE TARGET"**, someone
|
|
63
|
+
else owns that work: you may chat or add information, but must NOT execute the
|
|
64
|
+
task.
|
|
65
|
+
- **send_message** posts markdown to a channel (find ids with **list_channels**,
|
|
66
|
+
people with **list_agents**). To share a file — a spec, log, diff, screenshot,
|
|
67
|
+
or anything a teammate should open as a file — pass its path in the **\`files\`
|
|
68
|
+
array**. Do NOT paste large file contents into the message body; attach the
|
|
69
|
+
file instead.
|
|
70
|
+
- **Referencing tasks vs GitHub issues.** In any message, activity note or task
|
|
71
|
+
description, \`#12\` and \`TASK#12\` render as a clickable **TaskFlow task** chip.
|
|
72
|
+
So write a **GitHub issue** as \`#gh12\` (or \`#GH12\`) and a **chat message**
|
|
73
|
+
as \`#msg12\` — never a bare \`#12\` for either.
|
|
74
|
+
A bare \`#12\` meaning a GitHub issue becomes a task chip pointing at TaskFlow
|
|
75
|
+
task 12, which is a different thing or does not exist at all, and it misleads
|
|
76
|
+
whoever reads it. \`#gh12\` renders as a distinct chip linking to the issue on
|
|
77
|
+
the project's linked repo.
|
|
78
|
+
- **download_attachment** fetches a file a teammate attached. It returns a PATH,
|
|
79
|
+
not the contents — open it with your own file tools, and check \`size_bytes\`
|
|
80
|
+
before reading anything wholesale.
|
|
81
|
+
|
|
82
|
+
## Tasks — the work
|
|
83
|
+
- Track real work as tasks. **list_tasks** (filter by \`status\`, or
|
|
84
|
+
\`assigned='me'\`), **create_task**, **claim_task**, **update_task_status**,
|
|
85
|
+
**report_review**. Search/list before creating so you don't duplicate an
|
|
86
|
+
existing task.
|
|
87
|
+
- Typical lifecycle: pick a ready task → **claim_task** → set **in_progress** →
|
|
88
|
+
do the work → set **partial_done** to request human review → a reviewer runs
|
|
89
|
+
**report_review** (\`approved\` | \`changes_requested\`) → **done** on approval,
|
|
90
|
+
or back to **in_progress** to address changes.
|
|
91
|
+
- Statuses are a fixed set: \`not_started\`, \`in_progress\`, \`paused\`,
|
|
92
|
+
\`blocked\`, \`partial_done\`, \`done\`, \`archived\`. Don't invent values. Never
|
|
93
|
+
silently fix a bug — open or reopen a task first, then track the fix.
|
|
94
|
+
|
|
95
|
+
## Activity & terminal — be transparent
|
|
96
|
+
- **log_activity** records a notable action (optionally linked to a task);
|
|
97
|
+
**get_activity** reads recent history. Use it as a running journal so humans and
|
|
98
|
+
other agents can follow what you did and why, without reading all of chat.
|
|
99
|
+
To mirror a note onto a task's linked GitHub issue, call it with a \`task\` and
|
|
100
|
+
\`post_to_github: true\` — it posts as a comment under your owner's identity, but
|
|
101
|
+
only if the project is GitHub-linked, the task is published as an issue, and your
|
|
102
|
+
owner is connected + opted in (\`post_as_me\`); otherwise it just records the
|
|
103
|
+
activity. Don't spam issues — reserve it for updates worth a public comment.
|
|
104
|
+
- **capture_terminal** streams terminal output to the dashboard so humans can
|
|
105
|
+
watch your work live.
|
|
106
|
+
- Tell people what you're doing: announce significant actions in chat or activity,
|
|
107
|
+
so the user can reconstruct what happened from your messages alone.
|
|
108
|
+
|
|
109
|
+
## Etiquette
|
|
110
|
+
Keep humans informed through messages and activity; prefer attaching files over
|
|
111
|
+
dumping large content inline; always **mark_read** after handling messages;
|
|
112
|
+
respect the **🚫 DO NOT ACT** marker; coordinate with other agents instead of
|
|
113
|
+
stepping on their work; and act under the right **profile** for the job.`;
|
|
114
|
+
//# sourceMappingURL=instructions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instructions.js","sourceRoot":"","sources":["../src/instructions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yEAqGuC,CAAC"}
|
package/dist/mint.d.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `taskflow-mcp --mint <name>` — create a NEW agent identity and record it as
|
|
3
|
+
* a profile in `.taskflow.json`.
|
|
4
|
+
*
|
|
5
|
+
* ## Why this is a CLI and not an MCP tool
|
|
6
|
+
*
|
|
7
|
+
* `POST /api/taskflow/agents/link` is gated on `RequireAuth` plus an active
|
|
8
|
+
* project membership, and it stamps the linking human onto the agent. An agent
|
|
9
|
+
* authenticates with `Authorization: Agent <key>` and would simply be refused.
|
|
10
|
+
*
|
|
11
|
+
* That gate is correct and this command deliberately does not work around it:
|
|
12
|
+
* an agent able to mint sibling agents could grow an unbounded roster that no
|
|
13
|
+
* human vouched for. Creating an identity is a human act, so a human runs this.
|
|
14
|
+
*
|
|
15
|
+
* ## What it is for
|
|
16
|
+
*
|
|
17
|
+
* Identity is `project + profile`. Two terminals sharing the `main` profile are
|
|
18
|
+
* ONE agent — same roster entry, and, more importantly, one shared DM inbox and
|
|
19
|
+
* one shared read cursor. Giving the second terminal its own profile is what
|
|
20
|
+
* makes them distinguishable.
|
|
21
|
+
*/
|
|
22
|
+
import { type RawProfile, type TaskflowConfig } from "./config.js";
|
|
23
|
+
/** A clear, user-facing failure (never a raw stack). */
|
|
24
|
+
export declare class MintError extends Error {
|
|
25
|
+
constructor(message: string);
|
|
26
|
+
}
|
|
27
|
+
export interface MintArgs {
|
|
28
|
+
name: string;
|
|
29
|
+
displayName: string;
|
|
30
|
+
token: string | undefined;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Parse `--mint <name> [--display-name <s>] [--token <t>]`.
|
|
34
|
+
*
|
|
35
|
+
* The token falls back to `TASKFLOW_USER_TOKEN`, but an explicit `--token`
|
|
36
|
+
* wins — a one-off mint against another account must not silently pick up
|
|
37
|
+
* whatever is exported in the shell.
|
|
38
|
+
*/
|
|
39
|
+
export declare function parseMintArgs(argv: string[], env?: NodeJS.ProcessEnv): MintArgs;
|
|
40
|
+
/**
|
|
41
|
+
* Return a copy of `config` with `profile` recorded under `name`.
|
|
42
|
+
*
|
|
43
|
+
* Pure, and deliberately non-destructive in three ways:
|
|
44
|
+
* - an existing profile is never overwritten (its key is unrecoverable, and it
|
|
45
|
+
* is what a running terminal authenticates with)
|
|
46
|
+
* - `default_profile` never moves, so existing terminals keep their identity
|
|
47
|
+
* - every other field is carried through untouched
|
|
48
|
+
*/
|
|
49
|
+
export declare function addProfile(config: TaskflowConfig, name: string, profile: RawProfile): TaskflowConfig;
|
|
50
|
+
/** The line the operator runs to start the other terminal as the new identity. */
|
|
51
|
+
export declare function exportHint(name: string): string;
|
|
52
|
+
/** The subset of `fetch` this module uses; injectable so tests see the real request. */
|
|
53
|
+
export type FetchLike = (url: string, init: RequestInit) => Promise<Response>;
|
|
54
|
+
/** Mint against the server and return the profile block it hands back. */
|
|
55
|
+
export declare function requestMint(server: string, project: number, args: MintArgs, token: string, doFetch?: FetchLike): Promise<RawProfile>;
|
|
56
|
+
/**
|
|
57
|
+
* Run the command. Returns the lines to print.
|
|
58
|
+
*
|
|
59
|
+
* The write is last: a failed mint must not leave a half-edited config, and a
|
|
60
|
+
* successful mint must not lose its key to a formatting error.
|
|
61
|
+
*/
|
|
62
|
+
export declare function runMint(argv: string[], env?: NodeJS.ProcessEnv): Promise<string[]>;
|