internaltool-mcp 1.6.6 → 1.6.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/index.js +64 -4
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
19
19
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
20
20
|
import { execSync } from 'child_process'
|
|
21
|
+
import { mkdirSync, writeFileSync, unlinkSync, existsSync } from 'fs'
|
|
22
|
+
import { join } from 'path'
|
|
21
23
|
import { z } from 'zod'
|
|
22
24
|
import { api, login, configure } from './api-client.js'
|
|
23
25
|
|
|
@@ -832,8 +834,14 @@ The README/implementation plan IS the brief. Always read it fully and present it
|
|
|
832
834
|
before they write a single line of code. If no README exists, warn the developer that the plan is
|
|
833
835
|
missing and suggest they write one before starting.
|
|
834
836
|
|
|
837
|
+
CURSOR RULES (MANDATORY):
|
|
838
|
+
When the response contains cursorRules.active === true, you MUST:
|
|
839
|
+
1. Display the full cursorRules.rules content to the user FIRST, before anything else, formatted as a visible block
|
|
840
|
+
2. Explicitly tell the user: "These rules are active for this task and override your defaults"
|
|
841
|
+
3. Follow every rule in cursorRules.rules for the ENTIRE duration of this task without exception
|
|
842
|
+
|
|
835
843
|
Workflow:
|
|
836
|
-
1. confirmed=false → show the full README brief + subtask checklist
|
|
844
|
+
1. confirmed=false → show cursorRules (if active) FIRST, then the full README brief + subtask checklist
|
|
837
845
|
2. confirmed=true → move task to in_progress, return branch name and recent repo commits
|
|
838
846
|
|
|
839
847
|
Use this when a developer says "start task", "brief me on", or "what do I need to do for TASK-X".`,
|
|
@@ -897,6 +905,13 @@ Use this when a developer says "start task", "brief me on", or "what do I need t
|
|
|
897
905
|
|
|
898
906
|
if (!confirmed) {
|
|
899
907
|
return text({
|
|
908
|
+
CURSOR_RULES: hasCursorRules
|
|
909
|
+
? {
|
|
910
|
+
ACTIVE: true,
|
|
911
|
+
DISPLAY_FIRST: 'Show this rules block to the user BEFORE the brief. Tell them these rules override defaults and apply for the entire task.',
|
|
912
|
+
rules: task.cursorRules,
|
|
913
|
+
}
|
|
914
|
+
: { ACTIVE: false },
|
|
900
915
|
brief: {
|
|
901
916
|
key: task.key,
|
|
902
917
|
title: task.title,
|
|
@@ -920,9 +935,6 @@ Use this when a developer says "start task", "brief me on", or "what do I need t
|
|
|
920
935
|
? `⏳ Plan is submitted and awaiting approval — you cannot create a branch until it is approved.`
|
|
921
936
|
: `⚠️ Plan is not yet submitted for approval. Submit it first, then create the branch.`
|
|
922
937
|
: null,
|
|
923
|
-
cursorRules: hasCursorRules
|
|
924
|
-
? { active: true, rules: task.cursorRules, instruction: '⚠️ CURSOR RULES ACTIVE — You MUST follow every rule in the "rules" field above for the entire duration of this task. These rules override your defaults.' }
|
|
925
|
-
: { active: false, rules: null, instruction: 'No task-specific Cursor rules set for this task.' },
|
|
926
938
|
requiresConfirmation: true,
|
|
927
939
|
message: approvalBlocks
|
|
928
940
|
? `Read the plan above, then follow workflowRoadmap — approval is required before you can branch and start coding.`
|
|
@@ -1217,6 +1229,36 @@ function runGit(args, cwd) {
|
|
|
1217
1229
|
}).trim()
|
|
1218
1230
|
}
|
|
1219
1231
|
|
|
1232
|
+
/** Write task-specific cursor rules to .cursor/rules/<taskKey>.mdc in the local repo root. */
|
|
1233
|
+
function writeCursorRulesFile(taskKey, rulesMarkdown) {
|
|
1234
|
+
try {
|
|
1235
|
+
const repoRoot = runGit('rev-parse --show-toplevel', process.cwd())
|
|
1236
|
+
const rulesDir = join(repoRoot, '.cursor', 'rules')
|
|
1237
|
+
mkdirSync(rulesDir, { recursive: true })
|
|
1238
|
+
const filePath = join(rulesDir, `${taskKey.toLowerCase()}.mdc`)
|
|
1239
|
+
const content = `---\ndescription: Task-specific rules for ${taskKey} — auto-generated by InternalTool MCP. Do not edit manually.\nalwaysApply: true\n---\n\n${rulesMarkdown}\n`
|
|
1240
|
+
writeFileSync(filePath, content, 'utf8')
|
|
1241
|
+
return filePath
|
|
1242
|
+
} catch {
|
|
1243
|
+
return null
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
/** Delete the task-specific cursor rules file when work is complete. */
|
|
1248
|
+
function deleteCursorRulesFile(taskKey) {
|
|
1249
|
+
try {
|
|
1250
|
+
const repoRoot = runGit('rev-parse --show-toplevel', process.cwd())
|
|
1251
|
+
const filePath = join(repoRoot, '.cursor', 'rules', `${taskKey.toLowerCase()}.mdc`)
|
|
1252
|
+
if (existsSync(filePath)) {
|
|
1253
|
+
unlinkSync(filePath)
|
|
1254
|
+
return filePath
|
|
1255
|
+
}
|
|
1256
|
+
return null
|
|
1257
|
+
} catch {
|
|
1258
|
+
return null
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1220
1262
|
function parseGitStatus(porcelain) {
|
|
1221
1263
|
const lines = porcelain.split('\n').filter(Boolean)
|
|
1222
1264
|
const staged = lines.filter(l => !' ?!'.includes(l[0])).map(l => ({ xy: l.slice(0, 2), file: l.slice(3) }))
|
|
@@ -1634,6 +1676,14 @@ If you have uncommitted tracked changes, it will tell you exactly what to do bef
|
|
|
1634
1676
|
}
|
|
1635
1677
|
} catch { /* non-fatal */ }
|
|
1636
1678
|
|
|
1679
|
+
// Write cursor rules file to local repo so Cursor enforces them natively
|
|
1680
|
+
let cursorRulesFile = null
|
|
1681
|
+
const freshTaskForRules = await api.get(`/api/tasks/${taskId}`).catch(() => null)
|
|
1682
|
+
const cursorRulesContent = freshTaskForRules?.data?.task?.cursorRules
|
|
1683
|
+
if (cursorRulesContent?.trim()) {
|
|
1684
|
+
cursorRulesFile = writeCursorRulesFile(freshTaskForRules.data.task.key, cursorRulesContent)
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1637
1687
|
const checkoutSteps = [
|
|
1638
1688
|
'git fetch origin',
|
|
1639
1689
|
`git checkout ${branchName}`,
|
|
@@ -1660,6 +1710,9 @@ If you have uncommitted tracked changes, it will tell you exactly what to do bef
|
|
|
1660
1710
|
message: statusMsg,
|
|
1661
1711
|
gitSteps: checkoutSteps,
|
|
1662
1712
|
localStateNote,
|
|
1713
|
+
cursorRulesFile: cursorRulesFile
|
|
1714
|
+
? { written: true, path: cursorRulesFile, note: 'Task-specific Cursor rules written to this file. Cursor will enforce them automatically. The file will be deleted when you raise a PR.' }
|
|
1715
|
+
: { written: false },
|
|
1663
1716
|
nextStep: 'Run the git steps above to switch locally, then start coding. When commits are pushed, use raise_pr.',
|
|
1664
1717
|
})
|
|
1665
1718
|
} catch (e) {
|
|
@@ -1962,12 +2015,19 @@ Set confirmed=false first to preview the full PR content, then confirmed=true to
|
|
|
1962
2015
|
draft,
|
|
1963
2016
|
})
|
|
1964
2017
|
if (!res?.success) return errorText(res?.message || 'Could not create PR')
|
|
2018
|
+
|
|
2019
|
+
// Delete the task-specific cursor rules file — coding is done
|
|
2020
|
+
const deletedRulesFile = deleteCursorRulesFile(task.key)
|
|
2021
|
+
|
|
1965
2022
|
return text({
|
|
1966
2023
|
prNumber: res.data.prNumber,
|
|
1967
2024
|
prUrl: res.data.prUrl,
|
|
1968
2025
|
title: prTitle,
|
|
1969
2026
|
draft,
|
|
1970
2027
|
message: `PR #${res.data.prNumber} created.`,
|
|
2028
|
+
cursorRulesCleared: deletedRulesFile
|
|
2029
|
+
? { cleared: true, path: deletedRulesFile, note: 'Task-specific Cursor rules file deleted — coding is complete.' }
|
|
2030
|
+
: { cleared: false },
|
|
1971
2031
|
nextStep: draft
|
|
1972
2032
|
? 'PR is a draft. Mark it ready for review on GitHub when you want reviewer notifications to fire.'
|
|
1973
2033
|
: 'PR is live. The GitHub webhook will move the task to in_review and notify the reviewer within seconds.',
|
package/package.json
CHANGED