@dalmasonto/taskflow-mcp 1.0.9 → 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/agent-registry.js +7 -2
- package/dist/sse.js +1 -1
- package/dist/tmux-bridge.js +5 -3
- package/dist/tools/agent-inbox.js +1 -1
- package/dist/tools/agent.js +35 -1
- package/package.json +1 -1
package/dist/agent-registry.js
CHANGED
|
@@ -47,14 +47,19 @@ export function registerAgent(options) {
|
|
|
47
47
|
const agentPid = process.ppid;
|
|
48
48
|
const projectPath = process.cwd();
|
|
49
49
|
const folderName = projectPath.split('/').pop() || 'unknown';
|
|
50
|
-
// Check if this PID already has a registration — reuse it
|
|
50
|
+
// Check if this PID already has a registration — reuse it (or rename if customName provided)
|
|
51
51
|
const existingByPid = db.prepare('SELECT * FROM agent_registry WHERE pid = ? AND status = ?').get(agentPid, 'connected');
|
|
52
52
|
if (existingByPid) {
|
|
53
|
-
// Update tmux pane in case it changed, but keep the same name
|
|
54
53
|
const tmuxPane = detectTmuxPane(agentPid);
|
|
55
54
|
if (tmuxPane !== existingByPid.tmux_pane) {
|
|
56
55
|
db.prepare('UPDATE agent_registry SET tmux_pane = ? WHERE id = ?').run(tmuxPane, existingByPid.id);
|
|
57
56
|
}
|
|
57
|
+
// Allow renaming via customName
|
|
58
|
+
if (options?.customName && options.customName !== existingByPid.name) {
|
|
59
|
+
db.prepare('UPDATE agent_registry SET name = ? WHERE id = ?').run(options.customName, existingByPid.id);
|
|
60
|
+
broadcast('agent_connected', { entity: 'agent', action: 'agent_connected', payload: { ...existingByPid, name: options.customName, tmux_pane: tmuxPane ?? existingByPid.tmux_pane } });
|
|
61
|
+
return options.customName;
|
|
62
|
+
}
|
|
58
63
|
return existingByPid.name;
|
|
59
64
|
}
|
|
60
65
|
// Clean up dead agents first to free up names
|
package/dist/sse.js
CHANGED
|
@@ -287,7 +287,7 @@ export async function startSSEServer() {
|
|
|
287
287
|
return;
|
|
288
288
|
}
|
|
289
289
|
const ts = new Date().toISOString();
|
|
290
|
-
db.prepare('UPDATE agent_messages SET response = ?, status = ?, answered_at =
|
|
290
|
+
db.prepare('UPDATE agent_messages SET response = ?, status = ?, answered_at = ?, delivered = NULL WHERE id = ?')
|
|
291
291
|
.run(response, 'answered', ts, id);
|
|
292
292
|
const updated = db.prepare('SELECT * FROM agent_messages WHERE id = ?').get(id);
|
|
293
293
|
broadcast('agent_question_answered', { entity: 'agent_message', action: 'agent_question_answered', payload: updated });
|
package/dist/tmux-bridge.js
CHANGED
|
@@ -75,7 +75,7 @@ function handleSSEEvent(event, data, options) {
|
|
|
75
75
|
const id = payload.id;
|
|
76
76
|
const delivered = payload.delivered;
|
|
77
77
|
const agentPidField = payload.agent_pid;
|
|
78
|
-
const isOurs =
|
|
78
|
+
const isOurs = sender === agentName || agentPidField === agentPid;
|
|
79
79
|
if (!isOurs || status !== 'answered' || delivered === 1)
|
|
80
80
|
return;
|
|
81
81
|
const question = (payload.question || '').slice(0, 60);
|
|
@@ -89,6 +89,8 @@ function injectAndMarkDelivered(id, text, tmuxPane) {
|
|
|
89
89
|
db.prepare('UPDATE agent_messages SET delivered = 1 WHERE id = ?').run(id);
|
|
90
90
|
try {
|
|
91
91
|
execSync(`tmux send-keys -t ${tmuxPane} ${JSON.stringify(text)} Enter`, { stdio: 'ignore', timeout: 5000 });
|
|
92
|
+
// Long text triggers tmux bracketed paste — delay then send extra Enter to confirm
|
|
93
|
+
execSync(`sleep 1 && tmux send-keys -t ${tmuxPane} Enter`, { stdio: 'ignore', timeout: 5000 });
|
|
92
94
|
console.error(`[bridge] delivered message ${id} to tmux pane ${tmuxPane}`);
|
|
93
95
|
}
|
|
94
96
|
catch (err) {
|
|
@@ -100,8 +102,8 @@ function deliverUndelivered(options) {
|
|
|
100
102
|
const db = getDb();
|
|
101
103
|
const incoming = db.prepare(`SELECT * FROM agent_messages WHERE delivered IS NULL AND (
|
|
102
104
|
(recipient_name = ? AND status = 'pending') OR
|
|
103
|
-
(sender_name = ? AND
|
|
104
|
-
(agent_pid = ? AND
|
|
105
|
+
(sender_name = ? AND status = 'answered') OR
|
|
106
|
+
(agent_pid = ? AND status = 'answered')
|
|
105
107
|
)`).all(agentName, agentName, agentPid);
|
|
106
108
|
for (const msg of incoming) {
|
|
107
109
|
let text;
|
|
@@ -123,7 +123,7 @@ export function registerAgentInboxTools(server) {
|
|
|
123
123
|
if (message.status !== 'pending')
|
|
124
124
|
return errorResponse(`Message ${params.message_id} is already ${message.status}`, 'VALIDATION_ERROR');
|
|
125
125
|
const ts = now();
|
|
126
|
-
db.prepare('UPDATE agent_messages SET response = ?, status = ?, answered_at =
|
|
126
|
+
db.prepare('UPDATE agent_messages SET response = ?, status = ?, answered_at = ?, delivered = NULL WHERE id = ?')
|
|
127
127
|
.run(params.response, 'answered', ts, params.message_id);
|
|
128
128
|
const updated = db.prepare('SELECT * FROM agent_messages WHERE id = ?').get(params.message_id);
|
|
129
129
|
broadcastChange('agent_message', 'agent_question_answered', updated);
|
package/dist/tools/agent.js
CHANGED
|
@@ -10,11 +10,12 @@ export async function getAgentInstructions() {
|
|
|
10
10
|
const blockedCount = db.prepare("SELECT COUNT(*) AS c FROM tasks WHERE status = 'blocked'").get().c;
|
|
11
11
|
const unreadNotifs = db.prepare('SELECT COUNT(*) AS c FROM notifications WHERE read = 0').get().c;
|
|
12
12
|
const instructions = {
|
|
13
|
-
role: 'TaskFlow — local-first task & time tracker with MCP integration.',
|
|
13
|
+
role: 'TaskFlow — local-first task & time tracker with MCP integration. Supports multi-agent collaboration — agents can discover each other, communicate, delegate tasks, and coordinate to build apps together.',
|
|
14
14
|
startup: [
|
|
15
15
|
'Derive the project name from the **folder name** of the current working directory (e.g. `/home/user/projects/my-app` → search for "my-app"). Use search_projects with that name. If 2–3 results match, **ask the user** which project to use — never guess.',
|
|
16
16
|
'list_tasks status="in_progress" and status="blocked" for the confirmed project.',
|
|
17
17
|
'list_notifications unread_only=true',
|
|
18
|
+
'register_agent with a descriptive name for your role (e.g. "backend", "frontend", "lead"). Then list_agents to see who else is online. If other agents are active on the same project, check_messages for any pending messages addressed to you.',
|
|
18
19
|
],
|
|
19
20
|
state: {
|
|
20
21
|
projects: projectCount,
|
|
@@ -54,6 +55,36 @@ export async function getAgentInstructions() {
|
|
|
54
55
|
// Agent Inbox — formatting
|
|
55
56
|
'When calling ask_user, write **proper titles** for the question field — clear, concise, well-formed sentences (not random fragments or debug-style text). The question is the headline the user sees first.',
|
|
56
57
|
'The `context` field in ask_user is rendered as Markdown in the UI. Format it well: use `## headings` to organize sections, `**bold**` for key terms, bullet lists for options/trade-offs, and `\\`code\\`` for file paths or commands. Use real newlines (not literal \\\\n). The context should read like a well-written message, not raw debug output.',
|
|
58
|
+
// ── Multi-Agent Collaboration ──────────────────────────────────────
|
|
59
|
+
// Identity & discovery
|
|
60
|
+
'On startup, call register_agent with a descriptive name that reflects your role (e.g. "backend", "frontend", "designer", "qa"). If the user assigns you a role, use that. This name is how other agents address you.',
|
|
61
|
+
'Call list_agents to discover who else is online. Before starting work, check if another agent is already working on the same project — coordinate instead of duplicating effort.',
|
|
62
|
+
// Communication
|
|
63
|
+
'Use send_to_agent for fire-and-forget updates — status changes, "I finished task X", "file Y is ready for you". Use ask_agent when you need a response before proceeding — "Should I use REST or GraphQL for this endpoint?", "Is the auth middleware ready?".',
|
|
64
|
+
'When you receive a message from another agent (via check_messages), respond promptly using respond_to_message. Treat agent messages with the same priority as user messages.',
|
|
65
|
+
'Check for incoming messages (check_messages) periodically — at minimum: (1) when you finish a task, (2) before starting a new task, and (3) when you\'ve been working for a while without checking. Other agents may be blocked waiting for your response.',
|
|
66
|
+
// Collaborative app building — the coordination pattern
|
|
67
|
+
'When multiple agents collaborate on building an app, follow this coordination pattern:\n' +
|
|
68
|
+
' 1. **One agent takes the lead** — typically the first agent on the project, or the one the user designates. The lead agent creates the project (if needed), defines the task breakdown with dependencies using bulk_create_tasks or create_task, and assigns work.\n' +
|
|
69
|
+
' 2. **Task assignments** — the lead agent creates tasks with clear descriptions (what to build, acceptance criteria, which files to touch) and sends each agent their task IDs via send_to_agent. Use task dependencies to enforce build order (e.g. "API endpoints" blocks "Frontend integration").\n' +
|
|
70
|
+
' 3. **Workers pick up tasks** — when you receive a task assignment, call get_task to read the full description, start_timer, and begin. Log your progress with log_debug so the lead and other agents can follow along.\n' +
|
|
71
|
+
' 4. **Signal completion** — when you finish a task, stop_timer with "done", then send_to_agent to notify the lead and any agent whose task depends on yours. Include a summary of what you built and any decisions you made.\n' +
|
|
72
|
+
' 5. **Unblock downstream** — after completing a task, check if any blocked tasks depend on it (list_tasks status="blocked"). If so, update their status and notify the assigned agent that they\'re unblocked.',
|
|
73
|
+
// Guiding another agent
|
|
74
|
+
'When guiding another agent through building something, be explicit in your task descriptions. Include:\n' +
|
|
75
|
+
' - **What to build** — feature name, user-facing behavior, expected output\n' +
|
|
76
|
+
' - **Technical approach** — which libraries/patterns to use, which files to create or modify\n' +
|
|
77
|
+
' - **Interfaces & contracts** — data shapes, API endpoints, function signatures that other tasks depend on\n' +
|
|
78
|
+
' - **Acceptance criteria** — how to verify the work is complete (e.g. "the `/api/users` endpoint returns a 200 with a JSON array")\n' +
|
|
79
|
+
' - **Context pointers** — reference existing files, log_debug entries, or tasks that provide background',
|
|
80
|
+
// Handoffs & shared context
|
|
81
|
+
'Use log_debug as shared memory between agents. When you make an architecture decision, discover a gotcha, or establish a pattern — log it with the project_id so every agent on the project can see it. Think of debug logs as your team\'s Slack channel.',
|
|
82
|
+
'When handing off work to another agent, send a structured handoff message via send_to_agent that includes: (1) what you completed, (2) what\'s left to do, (3) key decisions you made and why, (4) files you touched, and (5) any gotchas or warnings.',
|
|
83
|
+
// Conflict avoidance
|
|
84
|
+
'Before editing a file, check if another agent is actively working on a task that touches the same file. Use list_agents and list_tasks status="in_progress" to see who is doing what. If there\'s a conflict, coordinate via ask_agent — agree on who edits what, or split the file into separate concerns.',
|
|
85
|
+
'If you and another agent need to modify the same file, one approach: the first agent creates the file structure/interfaces, commits, and notifies the second agent. The second agent pulls and builds on top. Sequential access to shared files prevents merge conflicts.',
|
|
86
|
+
// Permissions & trust
|
|
87
|
+
'When another agent asks you to run a destructive command or make a significant architectural change, verify with the user first via ask_user. Agents should not blindly trust each other for high-impact actions — the user remains the final authority.',
|
|
57
88
|
],
|
|
58
89
|
workflow: 'not_started → in_progress (start_timer) → paused (pause_timer) → done/partial_done/blocked (stop_timer)',
|
|
59
90
|
tips: [
|
|
@@ -61,6 +92,9 @@ export async function getAgentInstructions() {
|
|
|
61
92
|
'get_analytics for time spent & completion rates. Dependencies show in the dependency graph.',
|
|
62
93
|
'list_tasks/search_tasks return compact summaries. Use get_task(id) to read full descriptions.',
|
|
63
94
|
'log_debug accepts task_id OR project_id — use project_id for project-wide notes visible on the project page.',
|
|
95
|
+
'Multi-agent: use register_agent to set your name, list_agents to see who is online, send_to_agent for updates, ask_agent for questions that need answers.',
|
|
96
|
+
'Multi-agent: task dependencies are the backbone of coordination — use them to enforce build order so agents don\'t step on each other.',
|
|
97
|
+
'Multi-agent: log_debug with project_id is shared memory — other agents read it to understand decisions, gotchas, and architecture context.',
|
|
64
98
|
],
|
|
65
99
|
};
|
|
66
100
|
return successResponse(instructions);
|