@masslessai/push-todo 4.1.8 → 4.1.9
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/lib/api.js +140 -0
- package/lib/cli.js +100 -0
- package/package.json +1 -1
package/lib/api.js
CHANGED
|
@@ -381,4 +381,144 @@ export async function learnVocabulary(todoId, keywords) {
|
|
|
381
381
|
return response.json();
|
|
382
382
|
}
|
|
383
383
|
|
|
384
|
+
// ==================== Phase 3: Extended Skill CLI ====================
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Report structured progress for a running task.
|
|
388
|
+
* Called by the agent during execution to push activity updates to Supabase.
|
|
389
|
+
*
|
|
390
|
+
* @param {string} todoId - UUID of the task
|
|
391
|
+
* @param {Object} options - Progress details
|
|
392
|
+
* @param {string} options.phase - Current phase (e.g., "testing", "implementing")
|
|
393
|
+
* @param {string} [options.detail] - Human-readable detail
|
|
394
|
+
* @returns {Promise<Object>}
|
|
395
|
+
*/
|
|
396
|
+
export async function reportProgress(todoId, { phase, detail }) {
|
|
397
|
+
const response = await apiRequest('update-task-execution', {
|
|
398
|
+
method: 'PATCH',
|
|
399
|
+
body: JSON.stringify({
|
|
400
|
+
todoId,
|
|
401
|
+
event: {
|
|
402
|
+
type: 'progress',
|
|
403
|
+
timestamp: new Date().toISOString(),
|
|
404
|
+
summary: detail ? `${phase}: ${detail}` : phase,
|
|
405
|
+
phase
|
|
406
|
+
}
|
|
407
|
+
})
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
if (!response.ok) {
|
|
411
|
+
const text = await response.text();
|
|
412
|
+
throw new Error(`Failed to report progress: ${text}`);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
return response.json();
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Update execution status/phase for a running task.
|
|
420
|
+
*
|
|
421
|
+
* @param {string} todoId - UUID of the task
|
|
422
|
+
* @param {string} status - New phase label (e.g., "implementing", "testing", "reviewing")
|
|
423
|
+
* @returns {Promise<Object>}
|
|
424
|
+
*/
|
|
425
|
+
export async function updateStatus(todoId, status) {
|
|
426
|
+
const response = await apiRequest('update-task-execution', {
|
|
427
|
+
method: 'PATCH',
|
|
428
|
+
body: JSON.stringify({
|
|
429
|
+
todoId,
|
|
430
|
+
event: {
|
|
431
|
+
type: 'progress',
|
|
432
|
+
timestamp: new Date().toISOString(),
|
|
433
|
+
summary: `Phase: ${status}`
|
|
434
|
+
}
|
|
435
|
+
})
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
if (!response.ok) {
|
|
439
|
+
const text = await response.text();
|
|
440
|
+
throw new Error(`Failed to update status: ${text}`);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
return response.json();
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Check for messages from the human for a running task.
|
|
448
|
+
* Returns pending messages or empty array.
|
|
449
|
+
*
|
|
450
|
+
* @param {string} todoId - UUID of the task
|
|
451
|
+
* @returns {Promise<Object[]>} Array of pending messages
|
|
452
|
+
*/
|
|
453
|
+
export async function checkMessages(todoId) {
|
|
454
|
+
const params = new URLSearchParams({ todo_id: todoId, direction: 'to_agent' });
|
|
455
|
+
const response = await apiRequest(`task-messages?${params}`, {
|
|
456
|
+
method: 'GET'
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
if (!response.ok) {
|
|
460
|
+
// 404 = no task_messages table/function yet — return empty gracefully
|
|
461
|
+
if (response.status === 404) return [];
|
|
462
|
+
const text = await response.text();
|
|
463
|
+
throw new Error(`Failed to check messages: ${text}`);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const result = await response.json();
|
|
467
|
+
return result.messages || [];
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Request input from the human for a running task.
|
|
472
|
+
* Posts a question and polls until the human responds or timeout.
|
|
473
|
+
*
|
|
474
|
+
* @param {string} todoId - UUID of the task
|
|
475
|
+
* @param {string} question - Question to ask the human
|
|
476
|
+
* @param {number} [timeoutMs=300000] - Timeout in ms (default 5 min)
|
|
477
|
+
* @returns {Promise<string|null>} Human's response or null if timeout
|
|
478
|
+
*/
|
|
479
|
+
export async function requestInput(todoId, question, timeoutMs = 300000) {
|
|
480
|
+
// Post the question
|
|
481
|
+
const postResponse = await apiRequest('task-messages', {
|
|
482
|
+
method: 'POST',
|
|
483
|
+
body: JSON.stringify({
|
|
484
|
+
todo_id: todoId,
|
|
485
|
+
direction: 'to_human',
|
|
486
|
+
message: question,
|
|
487
|
+
type: 'input_request'
|
|
488
|
+
})
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
if (!postResponse.ok) {
|
|
492
|
+
// 404 = task-messages endpoint not deployed yet
|
|
493
|
+
if (postResponse.status === 404) {
|
|
494
|
+
console.error('Message system not available yet. Use [push-confirm] pattern instead.');
|
|
495
|
+
return null;
|
|
496
|
+
}
|
|
497
|
+
const text = await postResponse.text();
|
|
498
|
+
throw new Error(`Failed to request input: ${text}`);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
const { message_id } = await postResponse.json();
|
|
502
|
+
|
|
503
|
+
// Poll for response
|
|
504
|
+
const startTime = Date.now();
|
|
505
|
+
const pollInterval = 5000; // 5s
|
|
506
|
+
|
|
507
|
+
while (Date.now() - startTime < timeoutMs) {
|
|
508
|
+
await new Promise(resolve => setTimeout(resolve, pollInterval));
|
|
509
|
+
|
|
510
|
+
const checkResponse = await apiRequest(`task-messages?message_id=${message_id}&direction=to_agent`, {
|
|
511
|
+
method: 'GET'
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
if (checkResponse.ok) {
|
|
515
|
+
const result = await checkResponse.json();
|
|
516
|
+
const reply = (result.messages || []).find(m => m.in_reply_to === message_id);
|
|
517
|
+
if (reply) return reply.message;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
return null; // Timeout
|
|
522
|
+
}
|
|
523
|
+
|
|
384
524
|
export { API_BASE };
|
package/lib/cli.js
CHANGED
|
@@ -68,6 +68,13 @@ ${bold('OPTIONS:')}
|
|
|
68
68
|
--view-screenshot <idx> Open screenshot for viewing (index or filename)
|
|
69
69
|
--learn-vocabulary <uuid> Contribute vocabulary for a task
|
|
70
70
|
--keywords <terms> Comma-separated vocabulary terms (with --learn-vocabulary)
|
|
71
|
+
--report-progress <uuid> Report progress for a running task
|
|
72
|
+
--phase <name> Phase name (with --report-progress)
|
|
73
|
+
--detail <text> Detail text (with --report-progress)
|
|
74
|
+
--update-status <uuid> Update execution phase for a running task
|
|
75
|
+
--check-messages <uuid> Check for messages from the human
|
|
76
|
+
--request-input <uuid> Request input from the human (blocking)
|
|
77
|
+
--question <text> Question to ask (with --request-input)
|
|
71
78
|
--set-batch-size <N> Set max tasks for batch queue (1-20)
|
|
72
79
|
--daemon-status Show daemon status
|
|
73
80
|
--daemon-start Start daemon manually
|
|
@@ -175,6 +182,14 @@ const options = {
|
|
|
175
182
|
'create-todo': { type: 'string' },
|
|
176
183
|
'notify': { type: 'string' },
|
|
177
184
|
'queue-execution': { type: 'string' },
|
|
185
|
+
// Skill CLI options (Phase 3)
|
|
186
|
+
'report-progress': { type: 'string' },
|
|
187
|
+
'phase': { type: 'string' },
|
|
188
|
+
'detail': { type: 'string' },
|
|
189
|
+
'update-status': { type: 'string' },
|
|
190
|
+
'check-messages': { type: 'string' },
|
|
191
|
+
'request-input': { type: 'string' },
|
|
192
|
+
'question': { type: 'string' },
|
|
178
193
|
};
|
|
179
194
|
|
|
180
195
|
/**
|
|
@@ -512,6 +527,91 @@ export async function run(argv) {
|
|
|
512
527
|
return;
|
|
513
528
|
}
|
|
514
529
|
|
|
530
|
+
// Handle --report-progress (report structured progress for a running task)
|
|
531
|
+
if (values['report-progress']) {
|
|
532
|
+
try {
|
|
533
|
+
const result = await api.reportProgress(values['report-progress'], {
|
|
534
|
+
phase: values.phase || 'progress',
|
|
535
|
+
detail: values.detail || undefined,
|
|
536
|
+
});
|
|
537
|
+
if (values.json) {
|
|
538
|
+
console.log(JSON.stringify(result, null, 2));
|
|
539
|
+
} else {
|
|
540
|
+
console.log(green('Progress reported.'));
|
|
541
|
+
}
|
|
542
|
+
} catch (error) {
|
|
543
|
+
console.error(red(`Failed to report progress: ${error.message}`));
|
|
544
|
+
process.exit(1);
|
|
545
|
+
}
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// Handle --update-status (update execution phase for a running task)
|
|
550
|
+
if (values['update-status']) {
|
|
551
|
+
const status = positionals[0] || values.phase;
|
|
552
|
+
if (!status) {
|
|
553
|
+
console.error(red('Status required. Usage: --update-status <uuid> <status>'));
|
|
554
|
+
process.exit(1);
|
|
555
|
+
}
|
|
556
|
+
try {
|
|
557
|
+
const result = await api.updateStatus(values['update-status'], status);
|
|
558
|
+
if (values.json) {
|
|
559
|
+
console.log(JSON.stringify(result, null, 2));
|
|
560
|
+
} else {
|
|
561
|
+
console.log(green(`Status updated to: ${status}`));
|
|
562
|
+
}
|
|
563
|
+
} catch (error) {
|
|
564
|
+
console.error(red(`Failed to update status: ${error.message}`));
|
|
565
|
+
process.exit(1);
|
|
566
|
+
}
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// Handle --check-messages (check for messages from the human)
|
|
571
|
+
if (values['check-messages']) {
|
|
572
|
+
try {
|
|
573
|
+
const messages = await api.checkMessages(values['check-messages']);
|
|
574
|
+
if (values.json) {
|
|
575
|
+
console.log(JSON.stringify(messages, null, 2));
|
|
576
|
+
} else if (messages.length === 0) {
|
|
577
|
+
console.log(dim('No pending messages.'));
|
|
578
|
+
} else {
|
|
579
|
+
for (const msg of messages) {
|
|
580
|
+
console.log(`[${msg.created_at || 'unknown'}] ${msg.message}`);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
} catch (error) {
|
|
584
|
+
console.error(red(`Failed to check messages: ${error.message}`));
|
|
585
|
+
process.exit(1);
|
|
586
|
+
}
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// Handle --request-input (ask the human a question, poll for response)
|
|
591
|
+
if (values['request-input']) {
|
|
592
|
+
const question = values.question || positionals.slice(0).join(' ');
|
|
593
|
+
if (!question) {
|
|
594
|
+
console.error(red('--question required with --request-input'));
|
|
595
|
+
console.error('Usage: --request-input <uuid> --question "What should I do?"');
|
|
596
|
+
process.exit(1);
|
|
597
|
+
}
|
|
598
|
+
try {
|
|
599
|
+
console.log(dim('Waiting for human response (timeout: 5 min)...'));
|
|
600
|
+
const reply = await api.requestInput(values['request-input'], question);
|
|
601
|
+
if (values.json) {
|
|
602
|
+
console.log(JSON.stringify({ reply }, null, 2));
|
|
603
|
+
} else if (reply) {
|
|
604
|
+
console.log(green('Response:'), reply);
|
|
605
|
+
} else {
|
|
606
|
+
console.log(dim('No response received (timeout).'));
|
|
607
|
+
}
|
|
608
|
+
} catch (error) {
|
|
609
|
+
console.error(red(`Failed to request input: ${error.message}`));
|
|
610
|
+
process.exit(1);
|
|
611
|
+
}
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
|
|
515
615
|
// Auto-start daemon on every command (self-healing behavior)
|
|
516
616
|
ensureDaemonRunning();
|
|
517
617
|
|