internaltool-mcp 1.6.8 → 1.6.10
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 +44 -9
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
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'
|
|
21
|
+
import { mkdirSync, writeFileSync, unlinkSync, existsSync, readdirSync, statSync } from 'fs'
|
|
22
22
|
import { join } from 'path'
|
|
23
23
|
import { z } from 'zod'
|
|
24
24
|
import { api, login, configure } from './api-client.js'
|
|
@@ -848,8 +848,9 @@ Use this when a developer says "start task", "brief me on", or "what do I need t
|
|
|
848
848
|
{
|
|
849
849
|
taskId: z.string().describe("Task's MongoDB ObjectId"),
|
|
850
850
|
confirmed: z.boolean().optional().default(false).describe('Set true after reading the plan to move the task to in_progress'),
|
|
851
|
+
repoPath: z.string().optional().describe('Absolute path to the local git repo (defaults to MCP process working directory). Used to write cursor rules file.'),
|
|
851
852
|
},
|
|
852
|
-
async ({ taskId, confirmed = false }) => {
|
|
853
|
+
async ({ taskId, confirmed = false, repoPath }) => {
|
|
853
854
|
const taskRes = await api.get(`/api/tasks/${taskId}`)
|
|
854
855
|
if (!taskRes?.success) return errorText('Task not found')
|
|
855
856
|
const task = taskRes.data.task
|
|
@@ -960,6 +961,12 @@ Use this when a developer says "start task", "brief me on", or "what do I need t
|
|
|
960
961
|
} catch { /* might already be in_progress */ }
|
|
961
962
|
}
|
|
962
963
|
|
|
964
|
+
// Write cursor rules file to local repo immediately on kickoff
|
|
965
|
+
let cursorRulesFile = null
|
|
966
|
+
if (hasCursorRules) {
|
|
967
|
+
cursorRulesFile = writeCursorRulesFile(task.key, task.cursorRules, repoPath)
|
|
968
|
+
}
|
|
969
|
+
|
|
963
970
|
return text({
|
|
964
971
|
started: {
|
|
965
972
|
key: task.key,
|
|
@@ -973,6 +980,9 @@ Use this when a developer says "start task", "brief me on", or "what do I need t
|
|
|
973
980
|
cursorRules: hasCursorRules
|
|
974
981
|
? { active: true, rules: task.cursorRules, instruction: '⚠️ CURSOR RULES ACTIVE — You MUST follow every rule in the "rules" field for the entire duration of this task.' }
|
|
975
982
|
: { active: false },
|
|
983
|
+
cursorRulesFile: cursorRulesFile
|
|
984
|
+
? { written: true, path: cursorRulesFile, note: 'Rules file written to your repo. Cursor enforces it automatically on every prompt.' }
|
|
985
|
+
: hasCursorRules ? { written: false, note: 'Could not write rules file — not inside a git repo.' } : null,
|
|
976
986
|
recentCommits: recentCommits.slice(0, 5).map(c => ({
|
|
977
987
|
sha: c.sha?.slice(0, 7),
|
|
978
988
|
message: c.commit?.message?.split('\n')[0],
|
|
@@ -1229,10 +1239,33 @@ function runGit(args, cwd) {
|
|
|
1229
1239
|
}).trim()
|
|
1230
1240
|
}
|
|
1231
1241
|
|
|
1242
|
+
/**
|
|
1243
|
+
* Find the root of the git repo starting from `startPath`.
|
|
1244
|
+
* If `startPath` itself is not a git repo, scan one level of subdirectories.
|
|
1245
|
+
* Returns the repo root string or null.
|
|
1246
|
+
*/
|
|
1247
|
+
function findRepoRoot(startPath) {
|
|
1248
|
+
const base = startPath || process.cwd()
|
|
1249
|
+
try {
|
|
1250
|
+
return runGit('rev-parse --show-toplevel', base)
|
|
1251
|
+
} catch { /* not a git repo — try subdirectories */ }
|
|
1252
|
+
try {
|
|
1253
|
+
const entries = readdirSync(base, { withFileTypes: true })
|
|
1254
|
+
for (const entry of entries) {
|
|
1255
|
+
if (!entry.isDirectory()) continue
|
|
1256
|
+
try {
|
|
1257
|
+
return runGit('rev-parse --show-toplevel', join(base, entry.name))
|
|
1258
|
+
} catch { /* not a git repo */ }
|
|
1259
|
+
}
|
|
1260
|
+
} catch { /* can't read dir */ }
|
|
1261
|
+
return null
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1232
1264
|
/** Write task-specific cursor rules to .cursor/rules/<taskKey>.mdc in the local repo root. */
|
|
1233
|
-
function writeCursorRulesFile(taskKey, rulesMarkdown) {
|
|
1265
|
+
function writeCursorRulesFile(taskKey, rulesMarkdown, startPath) {
|
|
1234
1266
|
try {
|
|
1235
|
-
const repoRoot =
|
|
1267
|
+
const repoRoot = findRepoRoot(startPath)
|
|
1268
|
+
if (!repoRoot) return null
|
|
1236
1269
|
const rulesDir = join(repoRoot, '.cursor', 'rules')
|
|
1237
1270
|
mkdirSync(rulesDir, { recursive: true })
|
|
1238
1271
|
const filePath = join(rulesDir, `${taskKey.toLowerCase()}.mdc`)
|
|
@@ -1245,9 +1278,10 @@ function writeCursorRulesFile(taskKey, rulesMarkdown) {
|
|
|
1245
1278
|
}
|
|
1246
1279
|
|
|
1247
1280
|
/** Delete the task-specific cursor rules file when work is complete. */
|
|
1248
|
-
function deleteCursorRulesFile(taskKey) {
|
|
1281
|
+
function deleteCursorRulesFile(taskKey, startPath) {
|
|
1249
1282
|
try {
|
|
1250
|
-
const repoRoot =
|
|
1283
|
+
const repoRoot = findRepoRoot(startPath)
|
|
1284
|
+
if (!repoRoot) return null
|
|
1251
1285
|
const filePath = join(repoRoot, '.cursor', 'rules', `${taskKey.toLowerCase()}.mdc`)
|
|
1252
1286
|
if (existsSync(filePath)) {
|
|
1253
1287
|
unlinkSync(filePath)
|
|
@@ -1681,7 +1715,7 @@ If you have uncommitted tracked changes, it will tell you exactly what to do bef
|
|
|
1681
1715
|
const freshTaskForRules = await api.get(`/api/tasks/${taskId}`).catch(() => null)
|
|
1682
1716
|
const cursorRulesContent = freshTaskForRules?.data?.task?.cursorRules
|
|
1683
1717
|
if (cursorRulesContent?.trim()) {
|
|
1684
|
-
cursorRulesFile = writeCursorRulesFile(freshTaskForRules.data.task.key, cursorRulesContent)
|
|
1718
|
+
cursorRulesFile = writeCursorRulesFile(freshTaskForRules.data.task.key, cursorRulesContent, cwd)
|
|
1685
1719
|
}
|
|
1686
1720
|
|
|
1687
1721
|
const checkoutSteps = [
|
|
@@ -1968,8 +2002,9 @@ Set confirmed=false first to preview the full PR content, then confirmed=true to
|
|
|
1968
2002
|
additionalNotes: z.string().optional().describe('Extra context to add to the PR body'),
|
|
1969
2003
|
draft: z.boolean().optional().default(false).describe('Open as a draft PR (not yet ready for review)'),
|
|
1970
2004
|
confirmed: z.boolean().optional().default(false).describe('Set true to create the PR after reviewing the preview'),
|
|
2005
|
+
repoPath: z.string().optional().describe('Absolute path to the local git repo (defaults to MCP process working directory). Used to delete cursor rules file.'),
|
|
1971
2006
|
},
|
|
1972
|
-
async ({ taskId, projectId, headBranch, additionalNotes = '', draft = false, confirmed = false }) => {
|
|
2007
|
+
async ({ taskId, projectId, headBranch, additionalNotes = '', draft = false, confirmed = false, repoPath }) => {
|
|
1973
2008
|
if (scopedProjectId && projectId !== scopedProjectId) {
|
|
1974
2009
|
return errorText(`Access denied: session is scoped to project ${scopedProjectId}`)
|
|
1975
2010
|
}
|
|
@@ -2017,7 +2052,7 @@ Set confirmed=false first to preview the full PR content, then confirmed=true to
|
|
|
2017
2052
|
if (!res?.success) return errorText(res?.message || 'Could not create PR')
|
|
2018
2053
|
|
|
2019
2054
|
// Delete the task-specific cursor rules file — coding is done
|
|
2020
|
-
const deletedRulesFile = deleteCursorRulesFile(task.key)
|
|
2055
|
+
const deletedRulesFile = deleteCursorRulesFile(task.key, repoPath)
|
|
2021
2056
|
|
|
2022
2057
|
return text({
|
|
2023
2058
|
prNumber: res.data.prNumber,
|
package/package.json
CHANGED