opencode-manifold 0.5.6 → 0.5.8
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/index.js +214 -334
- package/package.json +1 -1
- package/src/templates/agents/clerk.md +22 -72
- package/src/templates/agents/debug.md +1 -1
- package/src/templates/agents/junior-dev.md +1 -1
- package/src/templates/agents/manifold.md +200 -35
- package/src/templates/agents/senior-dev.md +37 -34
- package/src/templates/agents/todo.md +84 -22
- package/src/templates/manifold/settings.json +1 -1
- package/src/templates/skills/research/SKILL.md +75 -0
package/dist/index.js
CHANGED
|
@@ -320,342 +320,206 @@ function extractTaskFromPlan(planContent, taskNumber) {
|
|
|
320
320
|
}
|
|
321
321
|
return null;
|
|
322
322
|
}
|
|
323
|
-
function runDispatcherLogic(args, state, settings) {
|
|
324
|
-
const
|
|
325
|
-
const taskNumber = args.task_number || (state?.task_number || 0);
|
|
326
|
-
const planFile = args.plan_file || (state?.plan_file || "");
|
|
323
|
+
function runDispatcherLogic(args, state, settings, taskDescription) {
|
|
324
|
+
const taskNumber = args.task_number || state?.task_number || 0;
|
|
327
325
|
const slug = state?.slug || "";
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
currentState = {
|
|
326
|
+
if (!state || state.phase === "init" || state.phase === "complete" || state.phase === "escalated") {
|
|
327
|
+
const newState = {
|
|
331
328
|
task_number: taskNumber,
|
|
332
|
-
plan_file:
|
|
329
|
+
plan_file: args.plan_file || state?.plan_file || "",
|
|
333
330
|
slug,
|
|
331
|
+
phase: "awaiting_execution",
|
|
332
|
+
senior_output: "",
|
|
333
|
+
clerk_summary: "",
|
|
334
334
|
loops: 0,
|
|
335
|
-
|
|
336
|
-
researchSummary: "",
|
|
337
|
-
implementationOutput: "",
|
|
338
|
-
reviewFeedback: "",
|
|
339
|
-
debugAnalysis: ""
|
|
335
|
+
clerk_retries: 0
|
|
340
336
|
};
|
|
341
|
-
} else {
|
|
342
|
-
currentState = { ...state };
|
|
343
|
-
}
|
|
344
|
-
if (args.researchSummary)
|
|
345
|
-
currentState.researchSummary = args.researchSummary;
|
|
346
|
-
if (args.implementationOutput)
|
|
347
|
-
currentState.implementationOutput = args.implementationOutput;
|
|
348
|
-
if (args.reviewFeedback)
|
|
349
|
-
currentState.reviewFeedback = args.reviewFeedback;
|
|
350
|
-
if (args.debugAnalysis)
|
|
351
|
-
currentState.debugAnalysis = args.debugAnalysis;
|
|
352
|
-
if (isNewTask) {
|
|
353
337
|
return {
|
|
354
|
-
route: "
|
|
338
|
+
route: "execute",
|
|
355
339
|
agent: "clerk",
|
|
356
|
-
prompt:
|
|
357
|
-
state: {
|
|
358
|
-
task_id: `${currentState.slug}-${currentState.task_number.toString().padStart(3, "0")}`,
|
|
359
|
-
loops: 0,
|
|
360
|
-
maxLoops: settings.maxLoops,
|
|
361
|
-
clerkRetries: 0
|
|
362
|
-
},
|
|
363
|
-
message: "Researching codebase and wiki context"
|
|
364
|
-
};
|
|
365
|
-
}
|
|
366
|
-
if (args.researchComplete === true && !args.hasImplementation) {
|
|
367
|
-
return {
|
|
368
|
-
route: "implement",
|
|
369
|
-
agent: "senior-dev",
|
|
370
|
-
prompt: buildScopedPrompt(currentState.researchSummary, args),
|
|
371
|
-
state: {
|
|
372
|
-
task_id: `${currentState.slug}-${currentState.task_number.toString().padStart(3, "0")}`,
|
|
373
|
-
loops: 0,
|
|
374
|
-
maxLoops: settings.maxLoops,
|
|
375
|
-
clerkRetries: 0
|
|
376
|
-
},
|
|
377
|
-
message: "Sending to senior-dev for implementation"
|
|
378
|
-
};
|
|
379
|
-
}
|
|
380
|
-
if (args.hasImplementation === true && args.reviewDone === false) {
|
|
381
|
-
return {
|
|
382
|
-
route: "review",
|
|
383
|
-
agent: "junior-dev",
|
|
384
|
-
prompt: buildReviewPrompt(currentState.implementationOutput, args),
|
|
340
|
+
prompt: buildExecutePrompt(taskNumber, taskDescription),
|
|
385
341
|
state: {
|
|
386
|
-
task_id: `${
|
|
387
|
-
|
|
388
|
-
maxLoops: settings.maxLoops,
|
|
389
|
-
clerkRetries: 0
|
|
342
|
+
task_id: `${slug}-${taskNumber.toString().padStart(3, "0")}`,
|
|
343
|
+
phase: "awaiting_execution"
|
|
390
344
|
},
|
|
391
|
-
message: "
|
|
345
|
+
message: "Ready to execute task"
|
|
392
346
|
};
|
|
393
347
|
}
|
|
394
|
-
if (
|
|
348
|
+
if (state.phase === "awaiting_execution") {
|
|
395
349
|
return {
|
|
396
|
-
route: "
|
|
350
|
+
route: "outcome_check",
|
|
397
351
|
agent: "clerk",
|
|
398
|
-
prompt:
|
|
352
|
+
prompt: buildOutcomeCheckPrompt(),
|
|
399
353
|
state: {
|
|
400
|
-
task_id: `${
|
|
401
|
-
|
|
402
|
-
maxLoops: settings.maxLoops,
|
|
403
|
-
clerkRetries: currentState.clerkRetries
|
|
354
|
+
task_id: `${slug}-${taskNumber.toString().padStart(3, "0")}`,
|
|
355
|
+
phase: "awaiting_outcome"
|
|
404
356
|
},
|
|
405
|
-
message: "
|
|
357
|
+
message: "Waiting for senior dev outcome"
|
|
406
358
|
};
|
|
407
359
|
}
|
|
408
|
-
if (
|
|
409
|
-
|
|
410
|
-
if (
|
|
411
|
-
|
|
360
|
+
if (state.phase === "awaiting_outcome") {
|
|
361
|
+
const isSuccess = args.success === true;
|
|
362
|
+
if (isSuccess) {
|
|
363
|
+
return {
|
|
364
|
+
route: "logging",
|
|
365
|
+
agent: "clerk",
|
|
366
|
+
prompt: buildLoggingPrompt(state, taskDescription),
|
|
367
|
+
state: {
|
|
368
|
+
task_id: `${slug}-${taskNumber.toString().padStart(3, "0")}`,
|
|
369
|
+
phase: "logging"
|
|
370
|
+
},
|
|
371
|
+
message: "Task succeeded - logging results"
|
|
372
|
+
};
|
|
373
|
+
} else {
|
|
374
|
+
if (state.clerk_retries < settings.maxRetries) {
|
|
412
375
|
return {
|
|
413
|
-
route: "
|
|
414
|
-
agent: "
|
|
415
|
-
prompt:
|
|
376
|
+
route: "reprompt",
|
|
377
|
+
agent: "clerk",
|
|
378
|
+
prompt: buildRePromptPrompt(state, taskDescription),
|
|
416
379
|
state: {
|
|
417
|
-
task_id: `${
|
|
418
|
-
|
|
419
|
-
maxLoops: settings.maxLoops,
|
|
420
|
-
clerkRetries: 0
|
|
380
|
+
task_id: `${slug}-${taskNumber.toString().padStart(3, "0")}`,
|
|
381
|
+
phase: "awaiting_execution"
|
|
421
382
|
},
|
|
422
|
-
message: `
|
|
383
|
+
message: `Retrying task (attempt ${state.clerk_retries + 1}/${settings.maxRetries})`
|
|
423
384
|
};
|
|
424
|
-
}
|
|
425
|
-
if (args.debugComplete === true && settings.clerkRetryEnabled && currentState.clerkRetries === 0) {
|
|
426
|
-
currentState.clerkRetries = 1;
|
|
427
|
-
currentState.loops = 0;
|
|
385
|
+
} else {
|
|
428
386
|
return {
|
|
429
|
-
route: "
|
|
430
|
-
agent:
|
|
431
|
-
prompt:
|
|
387
|
+
route: "escalate",
|
|
388
|
+
agent: null,
|
|
389
|
+
prompt: buildEscalationPrompt(state, taskDescription),
|
|
432
390
|
state: {
|
|
433
|
-
task_id: `${
|
|
434
|
-
|
|
435
|
-
maxLoops: settings.maxLoops,
|
|
436
|
-
clerkRetries: 1
|
|
391
|
+
task_id: `${slug}-${taskNumber.toString().padStart(3, "0")}`,
|
|
392
|
+
phase: "escalated"
|
|
437
393
|
},
|
|
438
|
-
message: "
|
|
394
|
+
message: "Escalating to user - all retries exhausted"
|
|
439
395
|
};
|
|
440
396
|
}
|
|
441
|
-
return {
|
|
442
|
-
route: "escalate_user",
|
|
443
|
-
agent: null,
|
|
444
|
-
prompt: `Task ${taskNumber} has exhausted all loops (${settings.maxLoops}) and debug analysis. Last feedback: ${currentState.reviewFeedback}`,
|
|
445
|
-
state: {
|
|
446
|
-
task_id: `${currentState.slug}-${currentState.task_number.toString().padStart(3, "0")}`,
|
|
447
|
-
loops: currentState.loops,
|
|
448
|
-
maxLoops: settings.maxLoops,
|
|
449
|
-
clerkRetries: currentState.clerkRetries
|
|
450
|
-
},
|
|
451
|
-
message: "ESCALATING TO USER - all loops exhausted"
|
|
452
|
-
};
|
|
453
397
|
}
|
|
454
|
-
return {
|
|
455
|
-
route: "re-implement",
|
|
456
|
-
agent: "senior-dev",
|
|
457
|
-
prompt: buildReImplementPrompt(currentState, args),
|
|
458
|
-
state: {
|
|
459
|
-
task_id: `${currentState.slug}-${currentState.task_number.toString().padStart(3, "0")}`,
|
|
460
|
-
loops: currentState.loops,
|
|
461
|
-
maxLoops: settings.maxLoops,
|
|
462
|
-
clerkRetries: currentState.clerkRetries
|
|
463
|
-
},
|
|
464
|
-
message: `Loop ${currentState.loops}/${settings.maxLoops} - re-implementing with junior feedback`
|
|
465
|
-
};
|
|
466
|
-
}
|
|
467
|
-
if (args.debugComplete === true && args.reResearched === false) {
|
|
468
|
-
return {
|
|
469
|
-
route: "re-implement",
|
|
470
|
-
agent: "senior-dev",
|
|
471
|
-
prompt: buildDebugImplementPrompt(currentState, args),
|
|
472
|
-
state: {
|
|
473
|
-
task_id: `${currentState.slug}-${currentState.task_number.toString().padStart(3, "0")}`,
|
|
474
|
-
loops: currentState.loops,
|
|
475
|
-
maxLoops: settings.maxLoops,
|
|
476
|
-
clerkRetries: currentState.clerkRetries
|
|
477
|
-
},
|
|
478
|
-
message: "Re-implementing with debug suggestion"
|
|
479
|
-
};
|
|
480
|
-
}
|
|
481
|
-
if (args.reResearched === true) {
|
|
482
|
-
currentState.loops = 0;
|
|
483
|
-
return {
|
|
484
|
-
route: "implement",
|
|
485
|
-
agent: "senior-dev",
|
|
486
|
-
prompt: buildScopedPrompt(currentState.researchSummary, args),
|
|
487
|
-
state: {
|
|
488
|
-
task_id: `${currentState.slug}-${currentState.task_number.toString().padStart(3, "0")}`,
|
|
489
|
-
loops: 0,
|
|
490
|
-
maxLoops: settings.maxLoops,
|
|
491
|
-
clerkRetries: currentState.clerkRetries
|
|
492
|
-
},
|
|
493
|
-
message: "Starting fresh loop with new research"
|
|
494
|
-
};
|
|
495
398
|
}
|
|
496
399
|
return {
|
|
497
|
-
route: "
|
|
400
|
+
route: "escalate",
|
|
498
401
|
agent: null,
|
|
499
|
-
prompt: "Dispatcher encountered
|
|
402
|
+
prompt: "Dispatcher encountered unexpected state",
|
|
500
403
|
state: {
|
|
501
|
-
task_id: `${
|
|
502
|
-
|
|
503
|
-
maxLoops: settings.maxLoops,
|
|
504
|
-
clerkRetries: currentState.clerkRetries
|
|
404
|
+
task_id: `${slug}-${taskNumber.toString().padStart(3, "0")}`,
|
|
405
|
+
phase: "escalated"
|
|
505
406
|
},
|
|
506
|
-
message: "
|
|
407
|
+
message: "Unexpected state - escalating"
|
|
507
408
|
};
|
|
508
409
|
}
|
|
509
|
-
function
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
1.
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
-
|
|
523
|
-
-
|
|
524
|
-
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
410
|
+
function buildExecutePrompt(taskNumber, taskDescription) {
|
|
411
|
+
return `CLERK - EXECUTE TASK
|
|
412
|
+
TASK NUMBER: ${taskNumber}
|
|
413
|
+
TASK: ${taskDescription}
|
|
414
|
+
|
|
415
|
+
INSTRUCTIONS:
|
|
416
|
+
|
|
417
|
+
1. RESEARCH
|
|
418
|
+
Use the Research skill with the given task to gather potentially relevant context.
|
|
419
|
+
|
|
420
|
+
2. COMPOSE SCOPED PROMPT
|
|
421
|
+
Combine into a scoped prompt:
|
|
422
|
+
- Task goal (what Senior Dev must accomplish)
|
|
423
|
+
- Relevant code snippets from research (3-10, include file paths)
|
|
424
|
+
- Prior decisions from wiki logs
|
|
425
|
+
- Design guidelines (language/framework conventions, patterns)
|
|
426
|
+
Balance: enough to be useful, not overwhelming. Target ~800 words max.
|
|
427
|
+
|
|
428
|
+
3. APPLY FRAMING
|
|
429
|
+
- Prefer "what data goes in and what comes out" framing when it makes the goal clearer
|
|
430
|
+
- If task has purity tag ([pure], [shell], [mixed]):
|
|
431
|
+
- [pure]: Frame as data transformation, specify input/output types, emphasize no IO/side effects
|
|
432
|
+
- [shell]: Clarify IO boundary, what external system involved
|
|
433
|
+
- [mixed]: Note where logic/IO boundary falls
|
|
434
|
+
- Don't force functional style if task is naturally imperative
|
|
435
|
+
|
|
436
|
+
4. CREATE TASK LOG
|
|
437
|
+
Write initial entry to \`Manifold/tasks/<slug>-<task-number>.md\` (e.g., auth-fix-001.md):
|
|
438
|
+
- Header: Date, Status=IN_PROGRESS, Task Description
|
|
439
|
+
- Include the scoped prompt you composed
|
|
440
|
+
- List context documents used (files, wiki logs, graphs)
|
|
441
|
+
|
|
442
|
+
5. CALL SENIOR-DEV
|
|
443
|
+
Call @senior-dev with the finalized scoped prompt. Wait for response.
|
|
444
|
+
|
|
445
|
+
6. REPORT OUTCOME
|
|
446
|
+
When senior-dev returns, determine if it reported "task complete" or "task failure".
|
|
447
|
+
Call dispatchTask() again (no arguments needed - the tool will ask for the result next).
|
|
448
|
+
|
|
449
|
+
CONSTRAINTS:
|
|
450
|
+
- Do not make autonomous decisions about the task lifecycle.
|
|
451
|
+
- Wait for the dispatcher's next instruction after senior-dev completes.`;
|
|
535
452
|
}
|
|
536
|
-
function
|
|
537
|
-
return `
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
}
|
|
546
|
-
function buildReviewPrompt(implementation, args) {
|
|
547
|
-
return `You are the Junior Developer reviewing the Senior Developer's implementation.
|
|
548
|
-
|
|
549
|
-
Task: ${args.description}
|
|
550
|
-
|
|
551
|
-
Senior Developer's Implementation:
|
|
552
|
-
${implementation}
|
|
553
|
-
|
|
554
|
-
Review the implementation against the task requirements. Your response MUST begin with exactly "COMPLETE" or "QUESTIONS" as the first word.
|
|
555
|
-
|
|
556
|
-
If COMPLETE: Briefly explain why the implementation is acceptable.
|
|
557
|
-
If QUESTIONS: List specific issues blocking approval with actionable feedback.`;
|
|
558
|
-
}
|
|
559
|
-
function buildReImplementPrompt(state, args) {
|
|
560
|
-
return `You are the Senior Developer. Re-implement this task addressing the Junior Developer's feedback.
|
|
561
|
-
|
|
562
|
-
Task: ${args.description}
|
|
563
|
-
|
|
564
|
-
Original Scoped Prompt:
|
|
565
|
-
${state.researchSummary}
|
|
566
|
-
|
|
567
|
-
Junior Developer's Feedback:
|
|
568
|
-
${state.reviewFeedback}
|
|
569
|
-
|
|
570
|
-
Address these issues and resubmit your implementation.`;
|
|
571
|
-
}
|
|
572
|
-
function buildDebugPrompt(state, args) {
|
|
573
|
-
return `You are the Debug Agent. The Senior/Junior loop has failed ${state.loops} times.
|
|
574
|
-
|
|
575
|
-
Task: ${args.description}
|
|
576
|
-
|
|
577
|
-
Research Context:
|
|
578
|
-
${state.researchSummary}
|
|
579
|
-
|
|
580
|
-
Failed Implementation:
|
|
581
|
-
${state.implementationOutput}
|
|
582
|
-
|
|
583
|
-
Junior Developer's Feedback:
|
|
584
|
-
${state.reviewFeedback}
|
|
585
|
-
|
|
586
|
-
Analyze why the loop is stuck and suggest a concrete alternative approach.`;
|
|
587
|
-
}
|
|
588
|
-
function buildDebugImplementPrompt(state, args) {
|
|
589
|
-
return `You are the Senior Developer. Implement the Debug Agent's suggestion.
|
|
590
|
-
|
|
591
|
-
Task: ${args.description}
|
|
592
|
-
|
|
593
|
-
Debug Agent's Analysis:
|
|
594
|
-
${state.debugAnalysis}
|
|
595
|
-
|
|
596
|
-
Implement the task following the Debug agent's suggestion.`;
|
|
453
|
+
function buildOutcomeCheckPrompt() {
|
|
454
|
+
return `CLERK - OUTCOME CHECK
|
|
455
|
+
INSTRUCTIONS:
|
|
456
|
+
1. Based on the senior-dev's response you just received, determine the outcome:
|
|
457
|
+
- If senior-dev said "task complete" (or similar), pass \`success: true\` on your next dispatchTask call
|
|
458
|
+
- If senior-dev said "task failure" (or similar), pass \`success: false\` on your next dispatchTask call
|
|
459
|
+
|
|
460
|
+
NEXT CALL:
|
|
461
|
+
Call dispatchTask({ success: true }) or dispatchTask({ success: false }) based on the outcome.`;
|
|
597
462
|
}
|
|
598
|
-
function
|
|
599
|
-
return `You are the Clerk. The previous implementation attempt failed. Re-research with failure context.
|
|
600
|
-
|
|
601
|
-
Original Task: ${args.description}
|
|
602
|
-
|
|
603
|
-
Previous Implementation (failed):
|
|
604
|
-
${state.implementationOutput}
|
|
605
|
-
|
|
606
|
-
Junior Developer's Feedback:
|
|
607
|
-
${state.reviewFeedback}
|
|
608
|
-
|
|
609
|
-
Debug Agent's Suggestion (also didn't work):
|
|
610
|
-
${state.debugAnalysis}
|
|
611
|
-
|
|
612
|
-
Re-research the task with this failure context and compose a new scoped prompt that addresses the underlying issues.`;
|
|
613
|
-
}
|
|
614
|
-
function buildLoggingPrompt(state, args) {
|
|
463
|
+
function buildLoggingPrompt(state, taskDescription) {
|
|
615
464
|
const taskId = `${state.slug}-${state.task_number.toString().padStart(3, "0")}`;
|
|
616
465
|
const date = new Date().toISOString().split("T")[0];
|
|
617
|
-
return `
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
${
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
${
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
-
|
|
638
|
-
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
466
|
+
return `CLERK - LOGGING PHASE
|
|
467
|
+
TASK: ${taskDescription}
|
|
468
|
+
TASK ID: ${taskId}
|
|
469
|
+
|
|
470
|
+
INSTRUCTIONS:
|
|
471
|
+
1. Update task log at \`Manifold/tasks/${taskId}.md\`:
|
|
472
|
+
- Change Status from IN_PROGRESS to COMPLETED (or FAILED if applicable)
|
|
473
|
+
- Add senior-dev's final implementation summary
|
|
474
|
+
- Document what was done and files changed
|
|
475
|
+
- If failed: document what was tried and why it failed
|
|
476
|
+
|
|
477
|
+
2. Update index at \`Manifold/index.md\`:
|
|
478
|
+
- Add: \`- [[${taskId}]] — ${taskDescription} | ${date} | COMPLETED\`
|
|
479
|
+
|
|
480
|
+
3. Append to log at \`Manifold/log.md\`:
|
|
481
|
+
- Add: \`## [${date}] ${taskId} | ${taskDescription} | COMPLETED\`
|
|
482
|
+
|
|
483
|
+
4. Update graph files for touched files:
|
|
484
|
+
- For each file touched, update/add \`Manifold/graph/<graph-name>.md\`
|
|
485
|
+
- Add task ID to "Tasks That Edited" section
|
|
486
|
+
- Replace \`/\` with \`__SL__\` and \`.\` with \`__DT__\` in filenames
|
|
487
|
+
- Example: \`src/middleware/auth.ts\` → \`Manifold/graph/src__SL__middleware__SL__auth__DT__ts.md\`
|
|
488
|
+
|
|
489
|
+
AFTER COMPLETING LOGGING:
|
|
490
|
+
Call dispatchTask() with no arguments. The dispatcher will complete the task and reset state.`;
|
|
491
|
+
}
|
|
492
|
+
function buildRePromptPrompt(state, taskDescription) {
|
|
493
|
+
const attemptNumber = state.clerk_retries + 1;
|
|
494
|
+
return `CLERK - RECOVERY ATTEMPT ${attemptNumber}
|
|
495
|
+
TASK: ${taskDescription}
|
|
496
|
+
PREVIOUS ATTEMPT FAILED
|
|
497
|
+
|
|
498
|
+
INSTRUCTIONS:
|
|
499
|
+
1. Call @senior-dev again with the same task description
|
|
500
|
+
2. Include context from the previous failure:
|
|
501
|
+
- The previous implementation was not accepted
|
|
502
|
+
- Senior-dev should try a different approach
|
|
503
|
+
3. Senior-dev will again manage the review loop internally
|
|
504
|
+
|
|
505
|
+
NEXT:
|
|
506
|
+
After senior-dev completes, call dispatchTask() again to report outcome.`;
|
|
507
|
+
}
|
|
508
|
+
function buildEscalationPrompt(state, taskDescription) {
|
|
509
|
+
return `CLERK - ESCALATION
|
|
510
|
+
TASK: ${taskDescription}
|
|
646
511
|
|
|
647
|
-
|
|
648
|
-
\`## [${date}] ${taskId} | ${args.description} | COMPLETED | ${state.loops} loops\`
|
|
512
|
+
The task has failed after ${state.clerk_retries} recovery attempts.
|
|
649
513
|
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
-
|
|
654
|
-
-
|
|
655
|
-
-
|
|
656
|
-
-
|
|
514
|
+
INSTRUCTIONS:
|
|
515
|
+
1. Log the failure to \`Manifold/tasks/<task-id>.md\` with status=FAILED
|
|
516
|
+
2. Report to the user:
|
|
517
|
+
- Task description: ${taskDescription}
|
|
518
|
+
- Status: FAILED
|
|
519
|
+
- Attempts made: ${state.clerk_retries}
|
|
520
|
+
- Summary of what was tried and why it failed
|
|
657
521
|
|
|
658
|
-
|
|
522
|
+
This task cannot be completed automatically. Human intervention required.`;
|
|
659
523
|
}
|
|
660
524
|
function getClient() {
|
|
661
525
|
if (!pluginClient) {
|
|
@@ -664,44 +528,38 @@ function getClient() {
|
|
|
664
528
|
return pluginClient;
|
|
665
529
|
}
|
|
666
530
|
var dispatchTaskTool = tool({
|
|
667
|
-
description: "Dispatcher for the multi-agent development system. Returns routing instructions based on
|
|
531
|
+
description: "Dispatcher for the multi-agent development system. Returns routing instructions based on task state. Clerk calls this with no arguments to get next prompt.",
|
|
668
532
|
args: {
|
|
669
|
-
task_number: tool.schema.number().optional().describe("
|
|
670
|
-
plan_file: tool.schema.string().optional().describe("Path to
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
reviewPassed: tool.schema.boolean().optional().describe("TRUE: Junior-dev responded with COMPLETE"),
|
|
676
|
-
needsDecomposition: tool.schema.boolean().optional().describe("TRUE: Task requires project-wide decomposition"),
|
|
677
|
-
debugComplete: tool.schema.boolean().optional().describe("TRUE: Debug agent has finished analyzing"),
|
|
678
|
-
reResearched: tool.schema.boolean().optional().describe("TRUE: Clerk has re-researched with failure context"),
|
|
679
|
-
researchSummary: tool.schema.string().optional().describe("Clerk's research findings"),
|
|
680
|
-
implementationOutput: tool.schema.string().optional().describe("Senior-dev's full output"),
|
|
681
|
-
reviewFeedback: tool.schema.string().optional().describe("Junior-dev's full response"),
|
|
682
|
-
debugAnalysis: tool.schema.string().optional().describe("Debug agent's full analysis"),
|
|
683
|
-
notes: tool.schema.string().optional().describe("Optional free-text reasoning")
|
|
533
|
+
task_number: tool.schema.number().optional().describe("Task number"),
|
|
534
|
+
plan_file: tool.schema.string().optional().describe("Path to plan document"),
|
|
535
|
+
description: tool.schema.string().optional().describe("Task description"),
|
|
536
|
+
success: tool.schema.boolean().optional().describe("TRUE if senior-dev completed task successfully, FALSE if failed"),
|
|
537
|
+
senior_output: tool.schema.string().optional().describe("Senior dev's output/summary"),
|
|
538
|
+
clerk_summary: tool.schema.string().optional().describe("Clerk's summary of the task")
|
|
684
539
|
},
|
|
685
540
|
async execute(args, context) {
|
|
686
|
-
const { task_number, plan_file } = args;
|
|
541
|
+
const { task_number, plan_file, description } = args;
|
|
687
542
|
const client = getClient();
|
|
688
543
|
const directory = context.directory;
|
|
689
544
|
await client.app.log({
|
|
690
545
|
body: {
|
|
691
546
|
service: "opencode-manifold",
|
|
692
547
|
level: "info",
|
|
693
|
-
message: `dispatchTask called: task ${task_number || "unknown"}
|
|
548
|
+
message: `dispatchTask called: task ${task_number || "unknown"}`
|
|
694
549
|
}
|
|
695
550
|
});
|
|
696
551
|
const settings = await readSettings(directory);
|
|
697
552
|
const existingState = task_number ? await readDispatcherState(directory, task_number) : null;
|
|
698
|
-
let
|
|
699
|
-
if (args.
|
|
553
|
+
let taskDescription = description || "";
|
|
554
|
+
if (!taskDescription && args.plan_file && task_number) {
|
|
700
555
|
try {
|
|
701
|
-
const planPath = join2(directory, plan_file);
|
|
556
|
+
const planPath = join2(directory, args.plan_file);
|
|
702
557
|
if (existsSync2(planPath)) {
|
|
703
558
|
const planContent = await readFile2(planPath, "utf-8");
|
|
704
|
-
taskInfo = extractTaskFromPlan(planContent, task_number);
|
|
559
|
+
const taskInfo = extractTaskFromPlan(planContent, task_number);
|
|
560
|
+
if (taskInfo) {
|
|
561
|
+
taskDescription = taskInfo.description;
|
|
562
|
+
}
|
|
705
563
|
}
|
|
706
564
|
} catch (error) {
|
|
707
565
|
await client.app.log({
|
|
@@ -713,24 +571,46 @@ var dispatchTaskTool = tool({
|
|
|
713
571
|
});
|
|
714
572
|
}
|
|
715
573
|
}
|
|
716
|
-
const
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
574
|
+
const result = runDispatcherLogic(args, existingState, settings, taskDescription);
|
|
575
|
+
let newPhase = "init";
|
|
576
|
+
if (result.route === "execute")
|
|
577
|
+
newPhase = "awaiting_execution";
|
|
578
|
+
else if (result.route === "outcome_check")
|
|
579
|
+
newPhase = "awaiting_outcome";
|
|
580
|
+
else if (result.route === "logging")
|
|
581
|
+
newPhase = "logging";
|
|
582
|
+
else if (result.route === "complete")
|
|
583
|
+
newPhase = "complete";
|
|
584
|
+
else if (result.route === "escalate")
|
|
585
|
+
newPhase = "escalated";
|
|
586
|
+
else if (result.route === "reprompt")
|
|
587
|
+
newPhase = "awaiting_execution";
|
|
721
588
|
if (task_number && result.state.task_id) {
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
589
|
+
if (result.route === "logging" || result.route === "escalate") {
|
|
590
|
+
const newState = {
|
|
591
|
+
task_number,
|
|
592
|
+
plan_file: plan_file || "",
|
|
593
|
+
slug: result.state.task_id.split("-")[0],
|
|
594
|
+
phase: newPhase,
|
|
595
|
+
senior_output: args.senior_output || existingState?.senior_output || "",
|
|
596
|
+
clerk_summary: args.clerk_summary || existingState?.clerk_summary || "",
|
|
597
|
+
loops: existingState?.loops || 0,
|
|
598
|
+
clerk_retries: existingState?.clerk_retries || 0
|
|
599
|
+
};
|
|
600
|
+
await writeDispatcherState(directory, newState);
|
|
601
|
+
} else {
|
|
602
|
+
const newState = {
|
|
603
|
+
task_number,
|
|
604
|
+
plan_file: plan_file || "",
|
|
605
|
+
slug: result.state.task_id.split("-")[0],
|
|
606
|
+
phase: newPhase,
|
|
607
|
+
senior_output: args.senior_output || existingState?.senior_output || "",
|
|
608
|
+
clerk_summary: args.clerk_summary || existingState?.clerk_summary || "",
|
|
609
|
+
loops: existingState?.loops || 0,
|
|
610
|
+
clerk_retries: existingState?.clerk_retries || 0
|
|
611
|
+
};
|
|
612
|
+
await writeDispatcherState(directory, newState);
|
|
613
|
+
}
|
|
734
614
|
}
|
|
735
615
|
await client.app.log({
|
|
736
616
|
body: {
|