internaltool-mcp 1.6.26 → 1.6.28
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 +80 -25
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -798,8 +798,8 @@ Call confirmed=false to preview the decomposition, confirmed=true to save it.`,
|
|
|
798
798
|
subtasksCreated: subtaskPlan.length,
|
|
799
799
|
message: `Decomposition saved. ${subtaskPlan.length} subtask(s) added to the board.`,
|
|
800
800
|
nextStep: parallelGroups.length > 0
|
|
801
|
-
?
|
|
802
|
-
:
|
|
801
|
+
? `⚡ COORDINATOR: Call get_parallel_kickoffs with taskId="${taskId}" NOW. It returns ready-to-paste prompts for each builder window. DO NOT implement code yourself — your job is to coordinate only.`
|
|
802
|
+
: `Call get_parallel_kickoffs with taskId="${taskId}" to get the builder prompt. DO NOT implement code yourself.`,
|
|
803
803
|
})
|
|
804
804
|
}
|
|
805
805
|
)
|
|
@@ -1618,41 +1618,86 @@ the human opens the windows. Each builder agent then runs independently.`,
|
|
|
1618
1618
|
})
|
|
1619
1619
|
}
|
|
1620
1620
|
|
|
1621
|
-
// Build one ready-to-paste prompt per pending subtask in this group
|
|
1621
|
+
// Build one ready-to-paste prompt per pending subtask in this group.
|
|
1622
|
+
// These prompts are designed to be pasted verbatim into a NEW Cursor Agent window.
|
|
1623
|
+
// They begin with explicit MCP calls so the agent has no ambiguity about what to do first.
|
|
1622
1624
|
const kickoffs = pendingInGroup.map((subtaskTitle, i) => {
|
|
1623
1625
|
const st = subtasks.find(s => s.title === subtaskTitle) || { title: subtaskTitle, role: 'builder', files: [], description: '' }
|
|
1624
|
-
const
|
|
1626
|
+
const filesArg = (st.files || []).map(f => `"${f}"`).join(', ')
|
|
1627
|
+
const fileList = (st.files || []).map(f => ` - \`${f}\``).join('\n')
|
|
1628
|
+
|
|
1629
|
+
// Prompt is structured as a direct agent instruction — no ambiguity, explicit MCP calls first
|
|
1625
1630
|
const prompt = [
|
|
1626
|
-
|
|
1631
|
+
`## Builder Agent — ${task.key} · Subtask ${i + 1} of ${parallelCount}`,
|
|
1632
|
+
``,
|
|
1633
|
+
`**Subtask:** ${st.title}`,
|
|
1634
|
+
`**Description:** ${st.description || '(see task description)'}`,
|
|
1635
|
+
`**Your exclusive files (do NOT touch any other file):**`,
|
|
1636
|
+
fileList || ' (none pre-assigned — use your judgement)',
|
|
1637
|
+
``,
|
|
1638
|
+
`## DO THIS NOW — in order, no skipping:`,
|
|
1639
|
+
``,
|
|
1640
|
+
`**Step 1** — Call this MCP tool immediately:`,
|
|
1641
|
+
` kickoff_task(taskId="${taskId}", agentRole="${st.role || 'builder'}", confirmed=true)`,
|
|
1642
|
+
``,
|
|
1643
|
+
`**Step 2** — Lock your files (prevents conflicts with the other parallel agent):`,
|
|
1644
|
+
` claim_files(taskId="${taskId}", files=[${filesArg}])`,
|
|
1645
|
+
``,
|
|
1646
|
+
`**Step 3** — Get full context:`,
|
|
1647
|
+
` get_agent_context(taskId="${taskId}", role="${st.role || 'builder'}")`,
|
|
1627
1648
|
``,
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
`**Your files:**`,
|
|
1632
|
-
fileList || ' (to be determined)',
|
|
1649
|
+
`**Step 4** — Implement the subtask`,
|
|
1650
|
+
` Modify ONLY your claimed files listed above.`,
|
|
1651
|
+
` Do NOT read or write files owned by another builder.`,
|
|
1633
1652
|
``,
|
|
1634
|
-
|
|
1635
|
-
`
|
|
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`,
|
|
1653
|
+
`**Step 5** — Verify`,
|
|
1654
|
+
` Run \`npm test\` — all tests must pass before committing.`,
|
|
1641
1655
|
``,
|
|
1642
|
-
|
|
1656
|
+
`**Step 6** — Commit`,
|
|
1657
|
+
` commit_helper(taskId="${taskId}")`,
|
|
1658
|
+
``,
|
|
1659
|
+
`**Step 7** — Mark done`,
|
|
1660
|
+
` Call update_task to tick the subtask checkbox for "${st.title}"`,
|
|
1661
|
+
``,
|
|
1662
|
+
`⚠️ You are 1 of ${parallelCount} parallel builders. Another agent is working simultaneously on different files.`,
|
|
1663
|
+
` Conflict prevention: claim_files will BLOCK you if another agent has already claimed your files.`,
|
|
1643
1664
|
].join('\n')
|
|
1644
|
-
|
|
1665
|
+
|
|
1666
|
+
return { subtask: st.title, role: st.role || 'builder', files: st.files || [], prompt }
|
|
1645
1667
|
})
|
|
1646
1668
|
|
|
1669
|
+
// Build the human-facing instruction block
|
|
1670
|
+
const howToOpen = [
|
|
1671
|
+
`In Cursor, open ${parallelCount} new Agent windows:`,
|
|
1672
|
+
` • Mac: Cmd+Shift+P → "New Agent" (or click "+" in the Composer panel)`,
|
|
1673
|
+
` • Windows: Ctrl+Shift+P → "New Agent"`,
|
|
1674
|
+
``,
|
|
1675
|
+
...kickoffs.map((k, i) => `Window ${i + 1}: paste the prompt for subtask "${k.subtask}"`),
|
|
1676
|
+
``,
|
|
1677
|
+
`Each window will independently call kickoff_task → claim_files → implement → commit.`,
|
|
1678
|
+
`They run at the same time — you don't need to wait for one to finish before starting the other.`,
|
|
1679
|
+
``,
|
|
1680
|
+
`After BOTH windows finish and commit, come back to this coordinator window and call:`,
|
|
1681
|
+
` get_parallel_kickoffs(taskId="${taskId}")`,
|
|
1682
|
+
`to get the next group's prompts.`,
|
|
1683
|
+
].join('\n')
|
|
1684
|
+
|
|
1647
1685
|
return text({
|
|
1648
1686
|
group: nextGroupIndex,
|
|
1649
1687
|
totalGroups: execOrder.length,
|
|
1650
1688
|
isParallel,
|
|
1651
1689
|
parallelCount,
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1690
|
+
READ_THIS_FIRST: '👇 READ THIS BEFORE DOING ANYTHING',
|
|
1691
|
+
COORDINATOR_INSTRUCTION: isParallel
|
|
1692
|
+
? `⚡ GROUP ${nextGroupIndex} of ${execOrder.length}: ${parallelCount} subtasks run IN PARALLEL.\n\n${howToOpen}`
|
|
1693
|
+
: `GROUP ${nextGroupIndex} of ${execOrder.length}: 1 sequential subtask.\nOpen 1 new Agent window and paste the prompt below.`,
|
|
1694
|
+
kickoffs: kickoffs.map((k, i) => ({
|
|
1695
|
+
window: i + 1,
|
|
1696
|
+
subtask: k.subtask,
|
|
1697
|
+
role: k.role,
|
|
1698
|
+
files: k.files,
|
|
1699
|
+
PASTE_THIS_IN_NEW_AGENT_WINDOW: k.prompt,
|
|
1700
|
+
})),
|
|
1656
1701
|
})
|
|
1657
1702
|
}
|
|
1658
1703
|
)
|
|
@@ -2418,8 +2463,11 @@ Use this when a developer says "start task", "brief me on", or "what do I need t
|
|
|
2418
2463
|
}
|
|
2419
2464
|
|
|
2420
2465
|
// Dynamically generate .cursor/agents, .cursor/skills, .cursor/commands
|
|
2421
|
-
// based on live task data — so every agent gets the right task ID, files, and role
|
|
2422
|
-
|
|
2466
|
+
// based on live task data — so every agent gets the right task ID, files, and role.
|
|
2467
|
+
// IMPORTANT: inject the NEW agentRole from this kickoff call (not the stale task.agentRole)
|
|
2468
|
+
// so active-agent.md reflects the role being kicked off, not the previous session's role.
|
|
2469
|
+
const taskForWorkspace = agentRole ? { ...task, agentRole } : task
|
|
2470
|
+
const workspaceResult = writeCursorWorkspace(taskForWorkspace, projectAgentConfig, repoPath || process.cwd())
|
|
2423
2471
|
|
|
2424
2472
|
// Persist agentRole + workspace status to server
|
|
2425
2473
|
const workspacePatch = {
|
|
@@ -3782,6 +3830,7 @@ If you have uncommitted tracked changes, it will tell you exactly what to do bef
|
|
|
3782
3830
|
if (scopedProjectId && projectId !== scopedProjectId) {
|
|
3783
3831
|
return errorText(`Access denied: session is scoped to project ${scopedProjectId}`)
|
|
3784
3832
|
}
|
|
3833
|
+
trackTaskActivity(taskId, 'create_branch', { summary: confirmed ? 'Branch created on GitHub' : 'Branch creation preview' })
|
|
3785
3834
|
|
|
3786
3835
|
const taskRes = await api.get(`/api/tasks/${taskId}`)
|
|
3787
3836
|
if (!taskRes?.success) return errorText('Task not found')
|
|
@@ -4045,6 +4094,7 @@ Set confirmed=false first to preview, then confirmed=true to park the task.`,
|
|
|
4045
4094
|
confirmed: z.boolean().optional().default(false).describe('Set true to park the task after reviewing the preview'),
|
|
4046
4095
|
},
|
|
4047
4096
|
async ({ taskId, summary = '', reason = '', confirmed = false }) => {
|
|
4097
|
+
trackTaskActivity(taskId, 'stash_changes', { summary: confirmed ? 'Task parked + stash instructions provided' : 'Stash preview' })
|
|
4048
4098
|
const taskRes = await api.get(`/api/tasks/${taskId}`)
|
|
4049
4099
|
const task = taskRes?.data?.task
|
|
4050
4100
|
|
|
@@ -4104,6 +4154,7 @@ Set confirmed=false first to review the park note, then confirmed=true to unpark
|
|
|
4104
4154
|
confirmed: z.boolean().optional().default(false).describe('Set true to unpark the task after reviewing'),
|
|
4105
4155
|
},
|
|
4106
4156
|
async ({ taskId, stashIndex = 0, confirmed = false }) => {
|
|
4157
|
+
trackTaskActivity(taskId, 'pop_stash', { summary: confirmed ? `Stash[${stashIndex}] popped + task unparked` : 'Pop stash preview' })
|
|
4107
4158
|
const taskRes = await api.get(`/api/tasks/${taskId}`)
|
|
4108
4159
|
if (!taskRes?.success) return errorText('Task not found')
|
|
4109
4160
|
const task = taskRes.data.task
|
|
@@ -4160,6 +4211,8 @@ Set confirmed=false first to preview the full plan, then confirmed=true to save
|
|
|
4160
4211
|
confirmed: z.boolean().optional().default(false).describe('Set true to park Task B and get the full command sequence'),
|
|
4161
4212
|
},
|
|
4162
4213
|
async ({ taskAId, taskBId, confirmed = false }) => {
|
|
4214
|
+
trackTaskActivity(taskAId, 'fix_pr_feedback', { summary: confirmed ? 'PR feedback fix plan confirmed' : 'PR feedback fix preview' })
|
|
4215
|
+
if (taskBId) trackTaskActivity(taskBId, 'fix_pr_feedback', { summary: 'Paused to fix PR feedback on another task' })
|
|
4163
4216
|
const taskARes = await api.get(`/api/tasks/${taskAId}`)
|
|
4164
4217
|
if (!taskARes?.success) return errorText('Task A not found')
|
|
4165
4218
|
const taskA = taskARes.data.task
|
|
@@ -4259,6 +4312,7 @@ Set confirmed=false first to preview the full PR content, then confirmed=true to
|
|
|
4259
4312
|
if (scopedProjectId && projectId !== scopedProjectId) {
|
|
4260
4313
|
return errorText(`Access denied: session is scoped to project ${scopedProjectId}`)
|
|
4261
4314
|
}
|
|
4315
|
+
trackTaskActivity(taskId, 'raise_pr', { summary: confirmed ? `PR raised: ${headBranch}${draft ? ' (draft)' : ''}` : 'PR preview' })
|
|
4262
4316
|
|
|
4263
4317
|
let taskRes
|
|
4264
4318
|
try { taskRes = await apiWithRetry(() => api.get(`/api/tasks/${taskId}`)) }
|
|
@@ -4404,6 +4458,7 @@ branchAction values (only needed when current branch ≠ task branch):
|
|
|
4404
4458
|
.describe('Required when confirmed=true and current branch ≠ task branch'),
|
|
4405
4459
|
},
|
|
4406
4460
|
async ({ taskId, repoPath, confirmed = false, branchAction }) => {
|
|
4461
|
+
if (taskId) trackTaskActivity(taskId, 'commit_helper', { summary: confirmed ? 'Commit command generated' : 'Commit preview' })
|
|
4407
4462
|
const cwd = repoPath || process.cwd()
|
|
4408
4463
|
|
|
4409
4464
|
// ── Read local git state ──────────────────────────────────────────────────
|
package/package.json
CHANGED