internaltool-mcp 1.6.7 → 1.6.9

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.
Files changed (2) hide show
  1. package/index.js +59 -0
  2. 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
 
@@ -958,6 +960,12 @@ Use this when a developer says "start task", "brief me on", or "what do I need t
958
960
  } catch { /* might already be in_progress */ }
959
961
  }
960
962
 
963
+ // Write cursor rules file to local repo immediately on kickoff
964
+ let cursorRulesFile = null
965
+ if (hasCursorRules) {
966
+ cursorRulesFile = writeCursorRulesFile(task.key, task.cursorRules)
967
+ }
968
+
961
969
  return text({
962
970
  started: {
963
971
  key: task.key,
@@ -971,6 +979,9 @@ Use this when a developer says "start task", "brief me on", or "what do I need t
971
979
  cursorRules: hasCursorRules
972
980
  ? { 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.' }
973
981
  : { active: false },
982
+ cursorRulesFile: cursorRulesFile
983
+ ? { written: true, path: cursorRulesFile, note: 'Rules file written to your repo. Cursor enforces it automatically on every prompt.' }
984
+ : hasCursorRules ? { written: false, note: 'Could not write rules file — not inside a git repo.' } : null,
974
985
  recentCommits: recentCommits.slice(0, 5).map(c => ({
975
986
  sha: c.sha?.slice(0, 7),
976
987
  message: c.commit?.message?.split('\n')[0],
@@ -1227,6 +1238,36 @@ function runGit(args, cwd) {
1227
1238
  }).trim()
1228
1239
  }
1229
1240
 
1241
+ /** Write task-specific cursor rules to .cursor/rules/<taskKey>.mdc in the local repo root. */
1242
+ function writeCursorRulesFile(taskKey, rulesMarkdown) {
1243
+ try {
1244
+ const repoRoot = runGit('rev-parse --show-toplevel', process.cwd())
1245
+ const rulesDir = join(repoRoot, '.cursor', 'rules')
1246
+ mkdirSync(rulesDir, { recursive: true })
1247
+ const filePath = join(rulesDir, `${taskKey.toLowerCase()}.mdc`)
1248
+ const content = `---\ndescription: Task-specific rules for ${taskKey} — auto-generated by InternalTool MCP. Do not edit manually.\nalwaysApply: true\n---\n\n${rulesMarkdown}\n`
1249
+ writeFileSync(filePath, content, 'utf8')
1250
+ return filePath
1251
+ } catch {
1252
+ return null
1253
+ }
1254
+ }
1255
+
1256
+ /** Delete the task-specific cursor rules file when work is complete. */
1257
+ function deleteCursorRulesFile(taskKey) {
1258
+ try {
1259
+ const repoRoot = runGit('rev-parse --show-toplevel', process.cwd())
1260
+ const filePath = join(repoRoot, '.cursor', 'rules', `${taskKey.toLowerCase()}.mdc`)
1261
+ if (existsSync(filePath)) {
1262
+ unlinkSync(filePath)
1263
+ return filePath
1264
+ }
1265
+ return null
1266
+ } catch {
1267
+ return null
1268
+ }
1269
+ }
1270
+
1230
1271
  function parseGitStatus(porcelain) {
1231
1272
  const lines = porcelain.split('\n').filter(Boolean)
1232
1273
  const staged = lines.filter(l => !' ?!'.includes(l[0])).map(l => ({ xy: l.slice(0, 2), file: l.slice(3) }))
@@ -1644,6 +1685,14 @@ If you have uncommitted tracked changes, it will tell you exactly what to do bef
1644
1685
  }
1645
1686
  } catch { /* non-fatal */ }
1646
1687
 
1688
+ // Write cursor rules file to local repo so Cursor enforces them natively
1689
+ let cursorRulesFile = null
1690
+ const freshTaskForRules = await api.get(`/api/tasks/${taskId}`).catch(() => null)
1691
+ const cursorRulesContent = freshTaskForRules?.data?.task?.cursorRules
1692
+ if (cursorRulesContent?.trim()) {
1693
+ cursorRulesFile = writeCursorRulesFile(freshTaskForRules.data.task.key, cursorRulesContent)
1694
+ }
1695
+
1647
1696
  const checkoutSteps = [
1648
1697
  'git fetch origin',
1649
1698
  `git checkout ${branchName}`,
@@ -1670,6 +1719,9 @@ If you have uncommitted tracked changes, it will tell you exactly what to do bef
1670
1719
  message: statusMsg,
1671
1720
  gitSteps: checkoutSteps,
1672
1721
  localStateNote,
1722
+ cursorRulesFile: cursorRulesFile
1723
+ ? { 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.' }
1724
+ : { written: false },
1673
1725
  nextStep: 'Run the git steps above to switch locally, then start coding. When commits are pushed, use raise_pr.',
1674
1726
  })
1675
1727
  } catch (e) {
@@ -1972,12 +2024,19 @@ Set confirmed=false first to preview the full PR content, then confirmed=true to
1972
2024
  draft,
1973
2025
  })
1974
2026
  if (!res?.success) return errorText(res?.message || 'Could not create PR')
2027
+
2028
+ // Delete the task-specific cursor rules file — coding is done
2029
+ const deletedRulesFile = deleteCursorRulesFile(task.key)
2030
+
1975
2031
  return text({
1976
2032
  prNumber: res.data.prNumber,
1977
2033
  prUrl: res.data.prUrl,
1978
2034
  title: prTitle,
1979
2035
  draft,
1980
2036
  message: `PR #${res.data.prNumber} created.`,
2037
+ cursorRulesCleared: deletedRulesFile
2038
+ ? { cleared: true, path: deletedRulesFile, note: 'Task-specific Cursor rules file deleted — coding is complete.' }
2039
+ : { cleared: false },
1981
2040
  nextStep: draft
1982
2041
  ? 'PR is a draft. Mark it ready for review on GitHub when you want reviewer notifications to fire.'
1983
2042
  : 'PR is live. The GitHub webhook will move the task to in_review and notify the reviewer within seconds.',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "internaltool-mcp",
3
- "version": "1.6.7",
3
+ "version": "1.6.9",
4
4
  "description": "MCP server for InternalTool — connect AI assistants (Claude Code, Cursor) to your project and task management platform",
5
5
  "type": "module",
6
6
  "main": "index.js",