internaltool-mcp 1.6.25 → 1.6.26
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/index.js +120 -1
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -1537,6 +1537,125 @@ The log is visible in InternalTool under the task's Session tab — gives the hu
|
|
|
1537
1537
|
return text({ logged: true, type, name, summary })
|
|
1538
1538
|
}
|
|
1539
1539
|
)
|
|
1540
|
+
|
|
1541
|
+
// ── get_parallel_kickoffs ─────────────────────────────────────────────────────
|
|
1542
|
+
server.tool(
|
|
1543
|
+
'get_parallel_kickoffs',
|
|
1544
|
+
`Read the decomposition plan and return ready-to-paste Cursor Agent prompts for the next group of parallel subtasks.
|
|
1545
|
+
|
|
1546
|
+
COORDINATOR WORKFLOW:
|
|
1547
|
+
1. Call decompose_task to create the plan
|
|
1548
|
+
2. Call get_parallel_kickoffs to get one paste-ready prompt per parallel subtask
|
|
1549
|
+
3. Tell the user exactly how many new Cursor Agent windows to open
|
|
1550
|
+
4. Give them each prompt to paste — one window per subtask
|
|
1551
|
+
|
|
1552
|
+
Cursor cannot spawn agent windows automatically. This tool generates the prompts;
|
|
1553
|
+
the human opens the windows. Each builder agent then runs independently.`,
|
|
1554
|
+
{
|
|
1555
|
+
taskId: z.string().describe("Task's MongoDB ObjectId"),
|
|
1556
|
+
},
|
|
1557
|
+
async ({ taskId }) => {
|
|
1558
|
+
trackTaskActivity(taskId, 'get_parallel_kickoffs')
|
|
1559
|
+
const taskRes = await apiWithRetry(() => api.get(`/api/tasks/${taskId}`))
|
|
1560
|
+
if (!taskRes?.success) return errorText('Task not found')
|
|
1561
|
+
const task = taskRes.data.task
|
|
1562
|
+
|
|
1563
|
+
if (!task.decomposition?.trim()) {
|
|
1564
|
+
return text({
|
|
1565
|
+
error: true,
|
|
1566
|
+
message: 'No decomposition plan found. Call decompose_task first to break the task into subtasks.',
|
|
1567
|
+
})
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
let dec
|
|
1571
|
+
try { dec = JSON.parse(task.decomposition) } catch {
|
|
1572
|
+
return errorText('Decomposition JSON is malformed — call decompose_task again.')
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
const subtasks = dec.subtasks || []
|
|
1576
|
+
const execOrder = dec.executionOrder || []
|
|
1577
|
+
|
|
1578
|
+
// Determine which group to run next: first group that has at least one
|
|
1579
|
+
// subtask not yet reflected in the board subtasks as done.
|
|
1580
|
+
const boardSubtasks = task.subtasks || []
|
|
1581
|
+
// Board subtask titles may carry a role prefix like "[BUILDER] Auth hardening..."
|
|
1582
|
+
// Match on bare title OR prefixed title
|
|
1583
|
+
const isDone = (title) => boardSubtasks.some(s => s.done && (s.title === title || s.title.endsWith(title)))
|
|
1584
|
+
|
|
1585
|
+
let nextGroup = null
|
|
1586
|
+
let nextGroupIndex = -1
|
|
1587
|
+
for (let i = 0; i < execOrder.length; i++) {
|
|
1588
|
+
const group = execOrder[i]
|
|
1589
|
+
const allDone = group.every(t => isDone(t))
|
|
1590
|
+
if (!allDone) {
|
|
1591
|
+
// Also check all prior groups are fully done (respects dependsOn)
|
|
1592
|
+
const priorAllDone = execOrder.slice(0, i).every(g => g.every(t => isDone(t)))
|
|
1593
|
+
if (priorAllDone) {
|
|
1594
|
+
nextGroup = group
|
|
1595
|
+
nextGroupIndex = i + 1
|
|
1596
|
+
break
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
if (!nextGroup) {
|
|
1602
|
+
return text({
|
|
1603
|
+
allDone: true,
|
|
1604
|
+
message: 'All subtask groups are complete. Move to the final review or run coverage tests.',
|
|
1605
|
+
})
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
// Only kick off subtasks that aren't done yet
|
|
1609
|
+
const pendingInGroup = nextGroup.filter(t => !isDone(t))
|
|
1610
|
+
const parallelCount = pendingInGroup.length
|
|
1611
|
+
const isParallel = parallelCount > 1
|
|
1612
|
+
|
|
1613
|
+
if (parallelCount === 0) {
|
|
1614
|
+
return text({
|
|
1615
|
+
group: nextGroupIndex,
|
|
1616
|
+
allDone: true,
|
|
1617
|
+
message: `All subtasks in Group ${nextGroupIndex} are already done. Call get_parallel_kickoffs again to get the next group.`,
|
|
1618
|
+
})
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
// Build one ready-to-paste prompt per pending subtask in this group
|
|
1622
|
+
const kickoffs = pendingInGroup.map((subtaskTitle, i) => {
|
|
1623
|
+
const st = subtasks.find(s => s.title === subtaskTitle) || { title: subtaskTitle, role: 'builder', files: [], description: '' }
|
|
1624
|
+
const fileList = (st.files || []).map(f => ` - ${f}`).join('\n')
|
|
1625
|
+
const prompt = [
|
|
1626
|
+
`You are a ${st.role || 'builder'} agent for TASK-011 (${task.key}).`,
|
|
1627
|
+
``,
|
|
1628
|
+
`## Your subtask (${i + 1} of ${parallelCount} running in parallel)`,
|
|
1629
|
+
`**Title:** ${st.title}`,
|
|
1630
|
+
`**Description:** ${st.description || ''}`,
|
|
1631
|
+
`**Your files:**`,
|
|
1632
|
+
fileList || ' (to be determined)',
|
|
1633
|
+
``,
|
|
1634
|
+
`## Steps`,
|
|
1635
|
+
`1. Call \`get_agent_context\` with taskId \`${taskId}\``,
|
|
1636
|
+
`2. Call \`claim_files\` with taskId \`${taskId}\` and files: [${(st.files || []).map(f => `"${f}"`).join(', ')}]`,
|
|
1637
|
+
`3. Implement the subtask — only modify your claimed files`,
|
|
1638
|
+
`4. Run tests to verify nothing is broken`,
|
|
1639
|
+
`5. Commit your changes with a clear message referencing ${task.key}`,
|
|
1640
|
+
`6. Mark the subtask done in InternalTool by calling \`update_task\` to tick off "${st.title}" in the subtasks list`,
|
|
1641
|
+
``,
|
|
1642
|
+
`Do NOT touch files owned by other parallel agents.`,
|
|
1643
|
+
].join('\n')
|
|
1644
|
+
return { subtask: st.title, role: st.role, files: st.files, prompt }
|
|
1645
|
+
})
|
|
1646
|
+
|
|
1647
|
+
return text({
|
|
1648
|
+
group: nextGroupIndex,
|
|
1649
|
+
totalGroups: execOrder.length,
|
|
1650
|
+
isParallel,
|
|
1651
|
+
parallelCount,
|
|
1652
|
+
instruction: isParallel
|
|
1653
|
+
? `⚡ GROUP ${nextGroupIndex} runs ${parallelCount} subtasks IN PARALLEL.\n\nTell the user:\n"Please open ${parallelCount} new Cursor Agent windows (Cmd+Shift+P → New Agent Session or open a new Composer tab set to Agent mode).\nPaste prompt 1 in window 1, prompt 2 in window 2. They will run independently."`
|
|
1654
|
+
: `GROUP ${nextGroupIndex} has 1 sequential subtask. Paste the prompt below in a new Agent window (or handle it yourself).`,
|
|
1655
|
+
kickoffs,
|
|
1656
|
+
})
|
|
1657
|
+
}
|
|
1658
|
+
)
|
|
1540
1659
|
}
|
|
1541
1660
|
|
|
1542
1661
|
// ── Standup activity formatter ────────────────────────────────────────────────
|
|
@@ -2967,7 +3086,7 @@ function writeCursorWorkspace(task, projectAgentConfig, startPath) {
|
|
|
2967
3086
|
const BUILT_IN = {
|
|
2968
3087
|
scout: `# Scout Agent — ${taskKey}\n\n**Task ID:** \`${taskId}\`\n**Task:** ${taskTitle}\n\n## Constraints\n- READ ONLY — do not write or modify any file\n- You CAN read files, run tests, and use MCP tools\n\n## Workflow\n1. Call \`get_agent_context\` with taskId \`${taskId}\`\n2. Read every source file systematically\n3. Call \`scout_task\` with confirmed=false, then confirmed=true + your report\n`,
|
|
2969
3088
|
builder: `# Builder Agent — ${taskKey}\n\n**Task ID:** \`${taskId}\`\n**Task:** ${taskTitle}\n\n**Claimed files:**\n${fileLine}\n\n${scoutLine}\n${decLine}\n\n## Constraints\n- Only modify your claimed files\n- Every change needs a test update\n- Run tests before marking done\n\n## Workflow\n1. Call \`get_agent_context\` with taskId \`${taskId}\`\n2. Implement the feature\n3. Use the \`run-tests\` skill to verify\n4. Call \`commit_helper\` then \`raise_pr\`\n`,
|
|
2970
|
-
coordinator: `# Coordinator Agent — ${taskKey}\n\n**Task ID:** \`${taskId}\`\n**Task:** ${taskTitle}\n\n## Constraints\n- Do NOT write code — plan and delegate only\n- Each subtask must have exclusive file ownership\n\n## Workflow\n1. Call \`get_agent_context\` with taskId \`${taskId}
|
|
3089
|
+
coordinator: `# Coordinator Agent — ${taskKey}\n\n**Task ID:** \`${taskId}\`\n**Task:** ${taskTitle}\n\n## Constraints\n- Do NOT write code — plan and delegate only\n- Each subtask must have exclusive file ownership\n- Cursor CANNOT auto-spawn agents — you generate prompts, the human opens windows\n\n## Workflow\n1. Call \`get_agent_context\` with taskId \`${taskId}\` to read the scout report\n2. Call \`decompose_task\` with taskId \`${taskId}\` to break work into parallel groups\n3. Call \`get_parallel_kickoffs\` with taskId \`${taskId}\` — this returns one ready-to-paste prompt per parallel subtask\n4. Tell the user exactly:\n - How many new Cursor Agent windows to open\n - Which prompt to paste in each window (copy verbatim from the kickoffs output)\n5. After all groups in a parallel batch are done, call \`get_parallel_kickoffs\` again for the next group\n\n## Important\nParallel = multiple Cursor Agent windows open at the same time.\nEach window is an independent builder — it claims its own files, implements, commits.\nYou (coordinator) do not implement anything — only plan and hand off.\n`,
|
|
2971
3090
|
reviewer: `# Reviewer Agent — ${taskKey}\n\n**Task ID:** \`${taskId}\`\n**Task:** ${taskTitle}\n\n## Constraints\n- Read the full diff before approving\n- Post specific, file-level comments\n\n## Workflow\n1. Call \`get_agent_context\` with taskId \`${taskId}\`\n2. Call \`review_pr\` to fetch the diff\n3. Call \`post_pr_review\` with your verdict\n`,
|
|
2972
3091
|
}
|
|
2973
3092
|
agentBody = BUILT_IN[role] || `# ${role} Agent — ${taskKey}\n\n**Task ID:** \`${taskId}\`\n**Task:** ${taskTitle}\n`
|
package/package.json
CHANGED