internaltool-mcp 1.6.29 → 1.6.31
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 +217 -45
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -760,8 +760,20 @@ Call confirmed=false to preview the decomposition, confirmed=true to save it.`,
|
|
|
760
760
|
}
|
|
761
761
|
|
|
762
762
|
if (!confirmed) {
|
|
763
|
+
// Include any project-level decomposition templates as hints
|
|
764
|
+
const projectRes = task.project?._id || task.project
|
|
765
|
+
let decompositionTemplates = []
|
|
766
|
+
try {
|
|
767
|
+
const projRes = await apiWithRetry(() => api.get(`/api/projects/${projectRes}`))
|
|
768
|
+
decompositionTemplates = projRes?.data?.project?.agentConfig?.decompositionTemplates || []
|
|
769
|
+
} catch { /* non-fatal */ }
|
|
770
|
+
|
|
763
771
|
return text({
|
|
764
772
|
decomposition: executionPlan,
|
|
773
|
+
decompositionTemplates: decompositionTemplates.length > 0 ? {
|
|
774
|
+
note: 'These project templates may match your task. Use them as a starting point for subtaskPlan.',
|
|
775
|
+
templates: decompositionTemplates,
|
|
776
|
+
} : null,
|
|
765
777
|
warnings: [
|
|
766
778
|
!executionPlan.scoutReportPresent ? '⚠️ No scout report on this task. Consider running a scout agent first to map the codebase before builders start.' : null,
|
|
767
779
|
!executionPlan.readmePresent ? '⚠️ No implementation plan (README). Builders need a spec to work from.' : null,
|
|
@@ -798,8 +810,8 @@ Call confirmed=false to preview the decomposition, confirmed=true to save it.`,
|
|
|
798
810
|
subtasksCreated: subtaskPlan.length,
|
|
799
811
|
message: `Decomposition saved. ${subtaskPlan.length} subtask(s) added to the board.`,
|
|
800
812
|
nextStep: parallelGroups.length > 0
|
|
801
|
-
? `⚡ COORDINATOR: Call get_parallel_kickoffs with taskId="${taskId}" NOW. It
|
|
802
|
-
: `Call get_parallel_kickoffs with taskId="${taskId}"
|
|
813
|
+
? `⚡ COORDINATOR: Call get_parallel_kickoffs with taskId="${taskId}" NOW. It writes Cursor Background Agent files for each parallel builder automatically. Then tell the user to open Background Agents panel (⌘⇧J) and click Start. DO NOT implement code yourself.`
|
|
814
|
+
: `Call get_parallel_kickoffs with taskId="${taskId}". It writes a Cursor Background Agent file for the builder. Tell the user to open Background Agents panel (⌘⇧J) and start it.`,
|
|
803
815
|
})
|
|
804
816
|
}
|
|
805
817
|
)
|
|
@@ -927,17 +939,32 @@ Returns systemPrompt ready to use as a Claude system prompt.`,
|
|
|
927
939
|
} catch { /* non-fatal */ }
|
|
928
940
|
}
|
|
929
941
|
|
|
942
|
+
// Suggest relevant skills based on role and available project skills
|
|
943
|
+
const availableSkills = ctx.project?.agentConfig?.skills || []
|
|
944
|
+
const suggestedSkills = availableSkills.length > 0
|
|
945
|
+
? availableSkills.map(s => ({
|
|
946
|
+
name: s.name,
|
|
947
|
+
description: s.description,
|
|
948
|
+
path: `.cursor/skills/${s.name}.md`,
|
|
949
|
+
}))
|
|
950
|
+
: effectiveRole === 'builder'
|
|
951
|
+
? [{ name: 'run-tests', description: 'How to run and validate tests', path: '.cursor/skills/run-tests.md' }]
|
|
952
|
+
: effectiveRole === 'scout'
|
|
953
|
+
? [{ name: 'scout-codebase', description: 'How to systematically explore and map the codebase', path: '.cursor/skills/scout-codebase.md' }]
|
|
954
|
+
: []
|
|
955
|
+
|
|
930
956
|
return text({
|
|
931
|
-
role:
|
|
932
|
-
taskKey:
|
|
933
|
-
taskTitle:
|
|
934
|
-
systemPrompt:
|
|
935
|
-
allowedTools:
|
|
936
|
-
claimedFiles:
|
|
937
|
-
warnings:
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
957
|
+
role: effectiveRole,
|
|
958
|
+
taskKey: ctx.taskKey,
|
|
959
|
+
taskTitle: ctx.taskTitle,
|
|
960
|
+
systemPrompt: parts.join('\n\n'),
|
|
961
|
+
allowedTools: ctx.customAllowedTools || null,
|
|
962
|
+
claimedFiles: ctx.task?.claimedFiles || [],
|
|
963
|
+
warnings: ctx.warnings || [],
|
|
964
|
+
suggestedSkills,
|
|
965
|
+
task: ctx.task,
|
|
966
|
+
project: ctx.project,
|
|
967
|
+
usage: 'Use systemPrompt as your Claude system prompt. If allowedTools is set, restrict your MCP tool calls to that list. Read and follow each skill in suggestedSkills before starting work.',
|
|
941
968
|
})
|
|
942
969
|
}
|
|
943
970
|
)
|
|
@@ -1541,16 +1568,15 @@ The log is visible in InternalTool under the task's Session tab — gives the hu
|
|
|
1541
1568
|
// ── get_parallel_kickoffs ─────────────────────────────────────────────────────
|
|
1542
1569
|
server.tool(
|
|
1543
1570
|
'get_parallel_kickoffs',
|
|
1544
|
-
`Read the decomposition plan
|
|
1571
|
+
`Read the decomposition plan, write Cursor Background Agent files for each parallel subtask, and return instructions.
|
|
1545
1572
|
|
|
1546
1573
|
COORDINATOR WORKFLOW:
|
|
1547
1574
|
1. Call decompose_task to create the plan
|
|
1548
|
-
2. Call get_parallel_kickoffs
|
|
1549
|
-
3.
|
|
1550
|
-
4.
|
|
1575
|
+
2. Call get_parallel_kickoffs — it writes .cursor/agents/builder-N.md files automatically
|
|
1576
|
+
3. Open the Cursor Background Agents panel (⌘⇧J or ⌘⇧P → "Background Agents")
|
|
1577
|
+
4. Click "Start" for each builder agent — they run in parallel in the background
|
|
1551
1578
|
|
|
1552
|
-
|
|
1553
|
-
the human opens the windows. Each builder agent then runs independently.`,
|
|
1579
|
+
The agent files are fully self-contained: each builder knows exactly what MCP tools to call.`,
|
|
1554
1580
|
{
|
|
1555
1581
|
taskId: z.string().describe("Task's MongoDB ObjectId"),
|
|
1556
1582
|
},
|
|
@@ -1635,15 +1661,19 @@ the human opens the windows. Each builder agent then runs independently.`,
|
|
|
1635
1661
|
``,
|
|
1636
1662
|
`Do these steps immediately in order — do not ask for confirmation, do not skip any step:`,
|
|
1637
1663
|
``,
|
|
1638
|
-
`1. Call
|
|
1639
|
-
`2. Call
|
|
1640
|
-
`3. Call
|
|
1641
|
-
`4.
|
|
1664
|
+
`1. Call log_session_event with taskId "${taskId}", type "subagent", name "builder-${i + 1}", summary "Builder ${i + 1} started: ${st.title}"`,
|
|
1665
|
+
`2. Call kickoff_task with taskId "${taskId}", agentRole "builder", confirmed true`,
|
|
1666
|
+
`3. Call claim_files with taskId "${taskId}" and files [${filesArg}]`,
|
|
1667
|
+
`4. Call get_agent_context with taskId "${taskId}" to read the full task plan and suggested skills`,
|
|
1668
|
+
`5. Implement the subtask: ${st.description || st.title}`,
|
|
1642
1669
|
` - You may ONLY modify these files: ${fileListPlain}`,
|
|
1643
1670
|
` - Do NOT touch any other file`,
|
|
1644
|
-
`
|
|
1645
|
-
`6.
|
|
1646
|
-
`7. Call
|
|
1671
|
+
` - Follow any skills listed in get_agent_context suggestedSkills`,
|
|
1672
|
+
`6. Run npm test — all tests must pass before continuing`,
|
|
1673
|
+
`7. Call commit_helper with taskId "${taskId}"`,
|
|
1674
|
+
`8. Call update_task with taskId "${taskId}" and mark the subtask "${st.title}" as done`,
|
|
1675
|
+
`9. Call resume_task with taskId "${taskId}", agentRole "reviewer", confirmed true — transition to reviewer role`,
|
|
1676
|
+
`10. Call log_session_event with taskId "${taskId}", type "subagent", name "builder-${i + 1}", summary "Builder ${i + 1} finished: ${st.title}"`,
|
|
1647
1677
|
``,
|
|
1648
1678
|
`You are builder ${i + 1} of ${parallelCount} running in parallel. Another builder is working on different files at the same time.`,
|
|
1649
1679
|
].join('\n')
|
|
@@ -1651,19 +1681,45 @@ the human opens the windows. Each builder agent then runs independently.`,
|
|
|
1651
1681
|
return { subtask: st.title, role: st.role || 'builder', files: st.files || [], prompt }
|
|
1652
1682
|
})
|
|
1653
1683
|
|
|
1684
|
+
// Write each builder prompt as a Cursor Background Agent file in .cursor/agents/
|
|
1685
|
+
// Cursor Background Agents panel reads these files and lets you start each one independently.
|
|
1686
|
+
const repoRoot = process.cwd()
|
|
1687
|
+
const agentsDir = join(repoRoot, '.cursor', 'agents')
|
|
1688
|
+
mkdirSync(agentsDir, { recursive: true })
|
|
1689
|
+
|
|
1690
|
+
const writtenFiles = []
|
|
1691
|
+
kickoffs.forEach((k, i) => {
|
|
1692
|
+
const safeName = k.subtask.replace(/[^a-zA-Z0-9-_]/g, '-').replace(/-+/g, '-').slice(0, 40)
|
|
1693
|
+
const fileName = `builder-${i + 1}-${safeName}.md`
|
|
1694
|
+
const filePath = join(agentsDir, fileName)
|
|
1695
|
+
|
|
1696
|
+
const agentFileContent = [
|
|
1697
|
+
`---`,
|
|
1698
|
+
`description: Builder ${i + 1} of ${parallelCount} — ${k.subtask} (${task.key})`,
|
|
1699
|
+
`---`,
|
|
1700
|
+
``,
|
|
1701
|
+
k.prompt,
|
|
1702
|
+
].join('\n')
|
|
1703
|
+
|
|
1704
|
+
writeFileSync(filePath, agentFileContent, 'utf8')
|
|
1705
|
+
writtenFiles.push({ index: i + 1, file: `.cursor/agents/${fileName}`, subtask: k.subtask })
|
|
1706
|
+
})
|
|
1707
|
+
|
|
1654
1708
|
// Build the human-facing instruction block
|
|
1655
|
-
const
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
`
|
|
1709
|
+
const howToStart = [
|
|
1710
|
+
`✅ ${parallelCount} builder agent file(s) written to .cursor/agents/`,
|
|
1711
|
+
``,
|
|
1712
|
+
`TO START PARALLEL BUILDERS IN CURSOR:`,
|
|
1713
|
+
` 1. Open Background Agents panel: ⌘⇧J (or ⌘⇧P → "Background Agents")`,
|
|
1714
|
+
` 2. You'll see ${parallelCount} new builder agent(s) listed`,
|
|
1715
|
+
` 3. Click "Start" for each one — they run in parallel automatically`,
|
|
1659
1716
|
``,
|
|
1660
|
-
...
|
|
1717
|
+
...writtenFiles.map(f => ` • Builder ${f.index}: "${f.subtask}" → ${f.file}`),
|
|
1661
1718
|
``,
|
|
1662
|
-
`Each
|
|
1663
|
-
`They
|
|
1719
|
+
`Each builder calls kickoff_task → claim_files → implements → runs tests → commits.`,
|
|
1720
|
+
`They work on different files simultaneously — no need to wait for one before starting the other.`,
|
|
1664
1721
|
``,
|
|
1665
|
-
`After
|
|
1666
|
-
` get_parallel_kickoffs(taskId="${taskId}")`,
|
|
1722
|
+
`After all builders finish, come back here and call get_parallel_kickoffs again`,
|
|
1667
1723
|
`to get the next group's prompts.`,
|
|
1668
1724
|
].join('\n')
|
|
1669
1725
|
|
|
@@ -1672,17 +1728,104 @@ the human opens the windows. Each builder agent then runs independently.`,
|
|
|
1672
1728
|
totalGroups: execOrder.length,
|
|
1673
1729
|
isParallel,
|
|
1674
1730
|
parallelCount,
|
|
1675
|
-
READ_THIS_FIRST: '
|
|
1731
|
+
READ_THIS_FIRST: '⚡ BUILDER AGENT FILES WRITTEN — SEE INSTRUCTIONS BELOW',
|
|
1676
1732
|
COORDINATOR_INSTRUCTION: isParallel
|
|
1677
|
-
? `⚡ GROUP ${nextGroupIndex} of ${execOrder.length}: ${parallelCount}
|
|
1678
|
-
: `GROUP ${nextGroupIndex} of ${execOrder.length}: 1 sequential
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1733
|
+
? `⚡ GROUP ${nextGroupIndex} of ${execOrder.length}: ${parallelCount} builders written as Cursor Background Agents.\n\n${howToStart}`
|
|
1734
|
+
: `GROUP ${nextGroupIndex} of ${execOrder.length}: 1 sequential builder written as a Cursor Background Agent.\n\n${howToStart}`,
|
|
1735
|
+
agentFilesWritten: writtenFiles,
|
|
1736
|
+
})
|
|
1737
|
+
}
|
|
1738
|
+
)
|
|
1739
|
+
|
|
1740
|
+
// ── wait_for_group ─────────────────────────────────────────────────────────────
|
|
1741
|
+
server.tool(
|
|
1742
|
+
'wait_for_group',
|
|
1743
|
+
`Poll until all subtasks in a decomposition group are marked done. Use after get_parallel_kickoffs.
|
|
1744
|
+
|
|
1745
|
+
COORDINATOR WORKFLOW:
|
|
1746
|
+
1. Call get_parallel_kickoffs — writes builder agent files, user starts them
|
|
1747
|
+
2. Call wait_for_group with the groupIndex returned — blocks until all builders finish
|
|
1748
|
+
3. Call get_parallel_kickoffs again for the next group
|
|
1749
|
+
|
|
1750
|
+
Polls every 10 s, times out after 30 min by default. Returns immediately if already done.`,
|
|
1751
|
+
{
|
|
1752
|
+
taskId: z.string().describe("Task's MongoDB ObjectId"),
|
|
1753
|
+
groupIndex: z.number().int().min(1).describe('1-based group number to wait for (matches group from get_parallel_kickoffs)'),
|
|
1754
|
+
pollIntervalMs: z.number().optional().default(10000).describe('Poll interval in ms (default 10 s)'),
|
|
1755
|
+
timeoutMs: z.number().optional().default(1800000).describe('Max wait in ms (default 30 min)'),
|
|
1756
|
+
},
|
|
1757
|
+
async ({ taskId, groupIndex, pollIntervalMs = 10000, timeoutMs = 1800000 }) => {
|
|
1758
|
+
trackTaskActivity(taskId, 'wait_for_group', { summary: `Waiting for Group ${groupIndex}` })
|
|
1759
|
+
const deadline = Date.now() + timeoutMs
|
|
1760
|
+
let attempts = 0
|
|
1761
|
+
|
|
1762
|
+
while (Date.now() < deadline) {
|
|
1763
|
+
attempts++
|
|
1764
|
+
const taskRes = await apiWithRetry(() => api.get(`/api/tasks/${taskId}`))
|
|
1765
|
+
if (!taskRes?.success) return errorText('Task not found')
|
|
1766
|
+
const task = taskRes.data.task
|
|
1767
|
+
|
|
1768
|
+
if (!task.decomposition?.trim()) return errorText('No decomposition found. Call decompose_task first.')
|
|
1769
|
+
let dec
|
|
1770
|
+
try { dec = JSON.parse(task.decomposition) } catch {
|
|
1771
|
+
return errorText('Decomposition JSON is malformed.')
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1774
|
+
const execOrder = dec.executionOrder || []
|
|
1775
|
+
const targetGroup = execOrder[groupIndex - 1]
|
|
1776
|
+
if (!targetGroup) return errorText(`Group ${groupIndex} does not exist. Decomposition has ${execOrder.length} group(s).`)
|
|
1777
|
+
|
|
1778
|
+
const boardSubtasks = task.subtasks || []
|
|
1779
|
+
const isDone = (title) => boardSubtasks.some(s => s.done && (s.title === title || s.title.endsWith(title)))
|
|
1780
|
+
|
|
1781
|
+
const doneTitles = targetGroup.filter(t => isDone(t))
|
|
1782
|
+
const pendingTitles = targetGroup.filter(t => !isDone(t))
|
|
1783
|
+
|
|
1784
|
+
if (pendingTitles.length === 0) {
|
|
1785
|
+
return text({
|
|
1786
|
+
done: true,
|
|
1787
|
+
group: groupIndex,
|
|
1788
|
+
subtasks: targetGroup,
|
|
1789
|
+
done_count: doneTitles.length,
|
|
1790
|
+
attempts,
|
|
1791
|
+
message: `✅ All ${targetGroup.length} subtask(s) in Group ${groupIndex} are done after ${attempts} poll(s).`,
|
|
1792
|
+
nextStep: groupIndex < execOrder.length
|
|
1793
|
+
? `Call get_parallel_kickoffs with taskId="${taskId}" to get Group ${groupIndex + 1}'s builder prompts.`
|
|
1794
|
+
: 'All groups complete. Run final tests then call raise_pr.',
|
|
1795
|
+
})
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
// Log progress every 6 polls (~1 min)
|
|
1799
|
+
if (attempts % 6 === 0) {
|
|
1800
|
+
trackTaskActivity(taskId, 'wait_for_group', {
|
|
1801
|
+
summary: `Still waiting for Group ${groupIndex}: ${pendingTitles.length} pending (${doneTitles.length}/${targetGroup.length} done)`,
|
|
1802
|
+
})
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
if (Date.now() + pollIntervalMs < deadline) {
|
|
1806
|
+
await new Promise(resolve => setTimeout(resolve, pollIntervalMs))
|
|
1807
|
+
} else {
|
|
1808
|
+
break
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
|
|
1812
|
+
// Timed out — fetch final state
|
|
1813
|
+
const finalRes = await apiWithRetry(() => api.get(`/api/tasks/${taskId}`))
|
|
1814
|
+
const finalTask = finalRes?.data?.task
|
|
1815
|
+
let finalDec
|
|
1816
|
+
try { finalDec = JSON.parse(finalTask?.decomposition || '{}') } catch { finalDec = {} }
|
|
1817
|
+
const finalGroup = (finalDec.executionOrder || [])[groupIndex - 1] || []
|
|
1818
|
+
const finalBoard = finalTask?.subtasks || []
|
|
1819
|
+
const finalIsDone = (t) => finalBoard.some(s => s.done && (s.title === t || s.title.endsWith(t)))
|
|
1820
|
+
const stillPending = finalGroup.filter(t => !finalIsDone(t))
|
|
1821
|
+
|
|
1822
|
+
return text({
|
|
1823
|
+
timedOut: true,
|
|
1824
|
+
group: groupIndex,
|
|
1825
|
+
pending: stillPending,
|
|
1826
|
+
attempts,
|
|
1827
|
+
message: `⏱ Timed out after ${attempts} poll(s). ${stillPending.length} subtask(s) still pending: ${stillPending.join(', ')}`,
|
|
1828
|
+
hint: 'Check the Background Agents panel — a builder may have crashed. Call wait_for_group again to resume waiting, or proceed manually.',
|
|
1686
1829
|
})
|
|
1687
1830
|
}
|
|
1688
1831
|
)
|
|
@@ -3009,11 +3152,18 @@ You are a COORDINATOR agent. Your behavioral constraints for this session:
|
|
|
3009
3152
|
- Bypass the decomposition step — always decompose before builders start
|
|
3010
3153
|
- Start coding without a scout report when the codebase is unfamiliar
|
|
3011
3154
|
|
|
3155
|
+
**PARALLEL BUILDER WORKFLOW:**
|
|
3156
|
+
1. Call decompose_task — creates subtasks with file ownership
|
|
3157
|
+
2. Call get_parallel_kickoffs — writes .cursor/agents/builder-N.md files automatically
|
|
3158
|
+
3. Tell the user: "Open Background Agents panel (⌘⇧J) and click Start for each builder"
|
|
3159
|
+
4. Wait for builders to finish, then call get_parallel_kickoffs again for the next group
|
|
3160
|
+
|
|
3012
3161
|
**WORK STYLE:**
|
|
3013
3162
|
- Start with decompose_task to get the structured execution plan
|
|
3014
3163
|
- Ensure no two builder subtasks claim the same file
|
|
3015
3164
|
- Scout report must exist before builders begin
|
|
3016
|
-
- Each builder subtask must have explicit file ownership and a clear scope
|
|
3165
|
+
- Each builder subtask must have explicit file ownership and a clear scope
|
|
3166
|
+
- NEVER implement code yourself — your job ends after get_parallel_kickoffs writes the agent files`,
|
|
3017
3167
|
}
|
|
3018
3168
|
|
|
3019
3169
|
/** Write task-specific cursor rules to .cursor/rules/<taskKey>.mdc in the local repo root.
|
|
@@ -4528,6 +4678,19 @@ branchAction values (only needed when current branch ≠ task branch):
|
|
|
4528
4678
|
...unsafeUntracked.map(f => `⚠️ SKIP: ${f} ← do not commit this`),
|
|
4529
4679
|
]
|
|
4530
4680
|
|
|
4681
|
+
// ── Claims validation ─────────────────────────────────────────────────────
|
|
4682
|
+
// If this task has claimed files, warn if staged changes include unclaimed files.
|
|
4683
|
+
let claimsViolations = []
|
|
4684
|
+
if (taskId) {
|
|
4685
|
+
try {
|
|
4686
|
+
const claimRes = await apiWithRetry(() => api.get(`/api/tasks/${taskId}`))
|
|
4687
|
+
const claimedFiles = claimRes?.data?.task?.claimedFiles || []
|
|
4688
|
+
if (claimedFiles.length > 0) {
|
|
4689
|
+
claimsViolations = changedFilesList.filter(f => !claimedFiles.some(cf => f.endsWith(cf) || cf.endsWith(f)))
|
|
4690
|
+
}
|
|
4691
|
+
} catch { /* non-fatal */ }
|
|
4692
|
+
}
|
|
4693
|
+
|
|
4531
4694
|
// ── Block if task is parked — prevent Dev A pushing after handoff ────────
|
|
4532
4695
|
if (task) {
|
|
4533
4696
|
const isParked = !!(task.parkNote?.parkedAt)
|
|
@@ -4640,6 +4803,11 @@ branchAction values (only needed when current branch ≠ task branch):
|
|
|
4640
4803
|
: null,
|
|
4641
4804
|
commands: [addCmd, `git commit -m "${commitMsg}"`, pushCmd].filter(Boolean),
|
|
4642
4805
|
requiresConfirmation: true,
|
|
4806
|
+
claimsViolations: claimsViolations.length > 0 ? {
|
|
4807
|
+
warning: `⚠️ ${claimsViolations.length} file(s) in your staged changes are NOT in your claimed files list. You may only edit claimed files.`,
|
|
4808
|
+
unclaimed: claimsViolations,
|
|
4809
|
+
claimedFiles: changedFilesList.filter(f => !claimsViolations.includes(f)),
|
|
4810
|
+
} : null,
|
|
4643
4811
|
message: `Suggested: "${commitMsg}". Call commit_helper again with confirmed=true to get the final commands.`,
|
|
4644
4812
|
})
|
|
4645
4813
|
}
|
|
@@ -4706,6 +4874,10 @@ branchAction values (only needed when current branch ≠ task branch):
|
|
|
4706
4874
|
unsafeUntrackedWarning: unsafeUntracked.length
|
|
4707
4875
|
? `These were excluded from git add — add them to .gitignore: ${unsafeUntracked.join(', ')}`
|
|
4708
4876
|
: null,
|
|
4877
|
+
claimsViolations: claimsViolations.length > 0 ? {
|
|
4878
|
+
warning: `⚠️ ${claimsViolations.length} file(s) modified that are NOT in your claimed files. Review before committing.`,
|
|
4879
|
+
unclaimed: claimsViolations,
|
|
4880
|
+
} : null,
|
|
4709
4881
|
message: `Copy-paste these commands in order.`,
|
|
4710
4882
|
nextStep: taskBranch && !branchMismatch ? `After pushing, call raise_pr to open the pull request.` : null,
|
|
4711
4883
|
})
|
package/package.json
CHANGED