@dalmasonto/taskflow-mcp 1.0.15 → 1.0.17

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/sse.js CHANGED
@@ -51,6 +51,61 @@ function readBody(req) {
51
51
  req.on('end', () => resolve(body));
52
52
  });
53
53
  }
54
+ const PROMPT_PATTERNS = [
55
+ /Esc to cancel/i,
56
+ /Tab to amend/i,
57
+ /\(y\/n\)/i,
58
+ /\(yes\/no\)/i,
59
+ /Do you want to/i,
60
+ /Allow .+ for this/i,
61
+ ];
62
+ function detectPrompt(content) {
63
+ const tail = content.split('\n').slice(-25).join('\n');
64
+ const hints = [];
65
+ if (/Esc to cancel/i.test(tail))
66
+ hints.push('Esc to cancel');
67
+ if (/Tab to amend/i.test(tail))
68
+ hints.push('Tab to amend');
69
+ if (/shift\+tab/i.test(tail))
70
+ hints.push('Shift+Tab');
71
+ const detected = PROMPT_PATTERNS.some(p => p.test(tail));
72
+ return { detected, hints };
73
+ }
74
+ function pollAgentPrompts(prevState) {
75
+ const db = getDb();
76
+ const agents = db.prepare("SELECT name, tmux_pane, status, pid FROM agent_registry WHERE status = 'connected' AND tmux_pane IS NOT NULL").all();
77
+ for (const agent of agents) {
78
+ try {
79
+ const output = execFileSync('tmux', ['capture-pane', '-p', '-S', '-', '-t', agent.tmux_pane], { timeout: 5000 }).toString();
80
+ const { detected, hints } = detectPrompt(output);
81
+ const wasAwaiting = prevState.get(agent.name) ?? false;
82
+ if (detected && !wasAwaiting) {
83
+ // Transition: not awaiting → awaiting — fire notification + broadcast
84
+ prevState.set(agent.name, true);
85
+ const ts = new Date().toISOString();
86
+ const hintsText = hints.length > 0 ? ` (${hints.join(' · ')})` : '';
87
+ db.prepare('INSERT INTO notifications (title, message, type, created_at) VALUES (?, ?, ?, ?)').run(`Agent "${agent.name}" needs input`, `A permission prompt is waiting for your response${hintsText}`, 'warning', ts);
88
+ const notification = db.prepare('SELECT * FROM notifications ORDER BY id DESC LIMIT 1').get();
89
+ broadcast('notification_created', { entity: 'notification', action: 'notification_created', payload: notification });
90
+ broadcast('agent_awaiting_input', { entity: 'agent', action: 'agent_awaiting_input', payload: { name: agent.name, hints, awaiting: true } });
91
+ }
92
+ else if (!detected && wasAwaiting) {
93
+ // Transition: awaiting → resolved
94
+ prevState.set(agent.name, false);
95
+ broadcast('agent_input_resolved', { entity: 'agent', action: 'agent_input_resolved', payload: { name: agent.name, awaiting: false } });
96
+ }
97
+ }
98
+ catch {
99
+ // tmux capture failed — agent pane may be gone
100
+ }
101
+ }
102
+ // Clean up agents that disconnected
103
+ for (const [name] of prevState) {
104
+ if (!agents.some(a => a.name === name)) {
105
+ prevState.delete(name);
106
+ }
107
+ }
108
+ }
54
109
  export async function startSSEServer() {
55
110
  const server = createServer(async (req, res) => {
56
111
  // CORS headers for all requests
@@ -362,7 +417,7 @@ export async function startSSEServer() {
362
417
  return;
363
418
  }
364
419
  try {
365
- const output = execFileSync('tmux', ['capture-pane', '-p', '-t', agent.tmux_pane], { timeout: 5000 }).toString();
420
+ const output = execFileSync('tmux', ['capture-pane', '-p', '-S', '-', '-t', agent.tmux_pane], { timeout: 5000 }).toString();
366
421
  jsonResponse(res, 200, { agent: agentName, pane: agent.tmux_pane, content: output });
367
422
  }
368
423
  catch (err) {
@@ -475,6 +530,14 @@ export async function startSSEServer() {
475
530
  }
476
531
  }
477
532
  }, 30_000);
533
+ // Prompt detection — poll connected agents for input-required state
534
+ const promptState = new Map();
535
+ setInterval(() => {
536
+ try {
537
+ pollAgentPrompts(promptState);
538
+ }
539
+ catch { /* ignore */ }
540
+ }, 5_000);
478
541
  if (port !== PREFERRED_PORT) {
479
542
  console.log(`[SSE] listening on fallback port ${port} (preferred ${PREFERRED_PORT} was unavailable)`);
480
543
  }
@@ -19,7 +19,7 @@ export function registerTerminalTools(server) {
19
19
  if (result.error)
20
20
  return result.error;
21
21
  try {
22
- const output = execFileSync('tmux', ['capture-pane', '-p', '-t', result.agent.tmux_pane], { timeout: 5000 }).toString();
22
+ const output = execFileSync('tmux', ['capture-pane', '-p', '-S', '-', '-t', result.agent.tmux_pane], { timeout: 5000 }).toString();
23
23
  return successResponse({
24
24
  agent: params.agent_name,
25
25
  pane: result.agent.tmux_pane,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dalmasonto/taskflow-mcp",
3
- "version": "1.0.15",
3
+ "version": "1.0.17",
4
4
  "description": "MCP server for TaskFlow — manage projects, tasks, timers, analytics via AI agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",