Cursorにおける「型駆動開発(Type-Driven Development)」:確率論的AIを決定論的コンパイラで支配する静的解析連携術
AIによるコード生成が日常化した現代の開発シーンにおいて、最も致命的でありながら見落とされがちな問題がある。それは「LLM(大規模言語モデル)の非決定性とハルシネーション(幻覚)」だ。どれほど洗練されたプロンプトを与えても、確率統計モデルであるLLMは、実在しないシグネチャの関数呼び出しや、破壊的な型不整合を含むコードを平然と出力する。
この確率論的カオスを統制できる唯一の防壁が、「静的型システム」と「コンパイラ」による絶対的な決定性だ。
本稿では、Cursor内部のコンテキスト収集機構とLSP(Language Server Protocol)のデータフローを低レイヤから解剖し、TypeScriptやRustの型定義(`.d.ts` / `traits`)をLLMの思考境界として強制注入する手法、そしてリアルタイム静的解析の診断ストリーム(Diagnostics)をプロンプトへ自動フィードバックさせる極限のアーキテクチャを解説する。
—
1. 内部アーキテクチャ解剖:CursorのContext EngineとLSP Diagnosticsの力学
Cursorが他のAIエディタと一線を画す理由は、VS Codeフォークとしての利点を活かしたエディタ内部イベントとLLMプロンプトアセンブラの結合度にある。
+————————————————————————-+
| Cursor Editor |
| |
| +——————-+ LSP Notification +—————–+ |
| | Language Server | ————————–> | Diagnostics | |
| | (tsserver/rust-a) | (textDocument/publishDiag) | Store (AST/Pos) | |
| +——————-+ +——–+——–+ |
| ^ | |
| | File Change / Trigger | Stream |
| v v |
| +——————-+ Shadow Workspace / Symb +——————-+ |
| | Working Directory | <------------------------ | Cursor Context | |
| | (.d.ts / Traits) | | Engine (Merger) | |
| +-------------------+ +---------+---------+ |
+------------------------------------------------------------|------------+
| Context + Diag
v
+-------------------+
| LLM Inference |
| (Zero-Err Loop) |
+-------------------+
通常、LLMにコード全体を食わせるとコンテキストウィンドウの枯渇と「Attentionの希釈」を招く。Cursorの内部エンジンは、以下の優先順位でコンテキストを動的にアセンブルしている。
1. アクティブファイルの可視領域とカーソル周辺トークン
2. Mercurial/Git差分およびインデックス化されたベクター類似度(Embeddings)
3. LSP Diagnostics Store(`textDocument/publishDiagnostics`で通知されたコンパイルエラー情報)
AIにエラーを出させないための本質は、「型境界(Type Boundary)をあらかじめ最小のトークン数でインデックスさせ、型違反が発生した瞬間にLSPのError Diagnosticをプロンプトへダイレクトにインジェクションして自己修復ループを回す」ことにある。
—
2. 型境界の絶対固定:`.cursorrules` と Ambient Types の超高精度プロジェクション
巨大なモノレポやエンタープライズコードベースにおいて、実装コードをAIに読ませて型を推論させるのは最悪のアプローチだ。AIには「実装」ではなく「シグネチャ(型定義)」のみを極小トークンでインジェクションしなければならない。
2.1. TypeScriptにおける型抽出(Ambient Declarations Generator)
実装を剥ぎ取り、型定義のみを凝縮した`.cursor/types-manifest.d.ts`を自動生成するパイプラインスクリプトを定義する。
// scripts/generate-cursor-types.ts
import as ts from ‘typescript’;
import as fs from ‘fs’;
import as path from ‘path’;
/
- プロジェクト内の主要モジュールから型定義のみを抽出し、
- Cursorが最小トークンで参照可能な単一のAmbient型ファイルへ集約する
/
function extractTypeSignatures(rootDirs: string[], outputFile: string): void {
const options: ts.CompilerOptions = {
declaration: true,
emitDeclarationOnly: true,
stripInternal: true,
target: ts.ScriptTarget.ESNext,
moduleResolution: ts.ModuleResolutionKind.NodeNext,
};
const fileNames: string[] = [];
const walk = (dir: string) => {
for (const file of fs.readdirSync(dir)) {
const fullPath = path.join(dir, file);
if (fs.statSync(fullPath).isDirectory()) {
if (file !== ‘node_modules’ && file !== ‘dist’) walk(fullPath);
} else if (file.endsWith(‘.ts’) && !file.endsWith(‘.d.ts’) && !file.endsWith(‘.test.ts’)) {
fileNames.push(fullPath);
}
}
};
rootDirs.forEach(walk);
const createdFiles: Record
const host = ts.createCompilerHost(options);
host.writeFile = (fileName, contents) => {
createdFiles[fileName] = contents;
};
const program = ts.createProgram(fileNames, options, host);
program.emit();
// 単一の.d.tsに圧縮統合してCursorのインデックス負荷を下げる
let bundleContent = ‘/ Auto-generated for Cursor Context Injection – DO NOT EDIT /\n’;
for (const [declPath, content] of Object.entries(createdFiles)) {
// コメントと不要なホワイトスペースを極限まで削る
const minified = content
.replace(/\/\[\s\S]?\\/|([^:]|^)\/\/.$/gm, ”)
.replace(/^\s[\r\n]/gm, ”);
bundleContent += `\n// Decl: ${path.basename(declPath)}\n${minified}`;
}
const outputDir = path.dirname(outputFile);
if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true });
fs.writeFileSync(outputFile, bundleContent, ‘utf-8’);
console.log(`[+] Type Manifest generated: ${outputFile} (${bundleContent.length} bytes)`);
}
extractTypeSignatures([‘./src/domain’, ‘./src/application’], ‘./.cursor/types-manifest.d.ts’);
2.2. 型駆動を強制する `.cursorrules` の設計
Cursorの挙動を支配する`.cursorrules`(または`.cursor/rules/.mdc`)に、コンパイラを絶対的な審判とする制約をハードコードする。
.cursorrules: Type-Driven Constraints Protocol
Core Directives
1. Strict Type First: コード生成前に、対象機能に必要なインターフェース(Interface/Type/Trait)を確定させよ。
2. Ambient Types Observation: `.cursor/types-manifest.d.ts` に定義された型シグネチャを唯一の真実(Single Source of Truth)として扱え。
3. No ‘any’ / No Force Unwraps:
- TS: `any`, `as unknown as T` 等の型アサーションによる逃げを全面禁止する。
- Rust: `unwrap()`, `expect()` の使用を禁止し、全て `Result
` / `Option ` のモナディックチェーンまたは `?` 演算子で伝播させよ。
Context Attachment Rules
- TypeScriptの修正を行う際は、関連する `.d.ts` の型境界に違反していないことを内省(Self-Reflect)せよ。
- LSPのエラー診断(Diagnostics)が渡された場合、実装側を無理にキャストするのではなく、型定義との整合性を最優先で修正せよ。
—
3. リアルタイム・フィードバック・ループ:LSP Diagnostics 自動注入とSelf-Healing
CursorでAIがコードを生成した直後、LSPが発火してDiagnostics(エラー情報)がメモリ上に展開される。このエラーシグネチャを即座にAIへ再帰投入し、「エラーが0になるまで自動修正させる」仕組みを構築する。
3.1. VS Code / Cursor Tasks による Diagnostics Watcher
エディタ内部でバックグラウンドコンパイルを常時稼働させ、LSPのインテリセンスキャッシュを最新に保つ。
// .vscode/tasks.json
{
“version”: “2.0.0”,
“tasks”: [
{
“label”: “Watch: TS Typecheck”,
“type”: “shell”,
“command”: “npx”,
“args”: [
“tsc”,
“–noEmit”,
“–watch”,
“–preserveWatchOutput”,
“–incremental”
],
“isBackground”: true,
“problemMatcher”: “$tsc-watch”,
“presentation”: {
“reveal”: “never”,
“panel”: “dedicated”
},
“runOptions”: {
“runOn”: “folderOpen”
}
},
{
“label”: “Watch: Rust Check”,
“type”: “shell”,
“command”: “cargo”,
“args”: [“check”, “–message-format=json”],
“isBackground”: true,
“problemMatcher”: “$rustc-watch”,
“presentation”: {
“reveal”: “never”,
“panel”: “dedicated”
}
}
]
}
3.2. LSP Diagnosticsを直接Cursor CLIにパイプする自己修復スクリプト
ローカルおよびCI環境で、コンパイラエラーを構造化JSONとして抽出し、AIに修復プロンプトとしてフィードバックさせる自動化CLIスクリプト(TypeScript/Rust両対応)を配備する。
!/usr/bin/env python3
scripts/ai-type-healer.py
“””
コンパイラ(tsc/cargo)の診断ストリームをパースし、
型違反のコンテキストを構造化してCursorの自己修復プロンプトを生成する。
“””
import subprocess
import json
import sys
import os
def run_typecheck_ts():
cmd = [“npx”, “tsc”, “–noEmit”, “–pretty”, “false”]
res = subprocess.run(cmd, capture_output=True, text=True)
if res.returncode == 0:
return []
errors = []
for line in res.stdout.strip().split(“\n”):
if “: error TS” in line:
parts = line.split(“: error “)
loc = parts[0]
msg = parts[1]
errors.append({“location”: loc, “error”: msg})
return errors
def run_typecheck_rust():
cmd = [“cargo”, “check”, “–message-format=json”]
res = subprocess.run(cmd, capture_output=True, text=True)
errors = []
for line in res.stdout.strip().split(“\n”):
if not line: continue
try:
data = json.loads(line)
if data.get(“reason”) == “compiler-message”:
msg = data[“message”]
if msg[“level”] == “error”:
errors.append({
“location”: f”{msg[‘spans’][0][‘file_name’]}:{msg[‘spans’][0][‘line_start’]}” if msg[‘spans’] else “unknown”,
“error”: msg[“rendered”]
})
except Exception:
continue
return errors
def construct_ai_prompt(errors):
if not errors:
print(“[+] No type errors found. Codebase is completely sound.”)
return None
prompt = “
CRITICAL TYPE COMPILATION FAILURES DETECTED #\n”
prompt += “以下の静的解析エラーを解消するようにコードを修正してください。\n”
prompt += “型シグネチャを改ざんせず、実装コードを型制約に厳格に適合させること。\n\n”
for idx, err in enumerate(errors[:10], 1): # トークン溢れを防ぐため上位10個に集約
prompt += f”— Error #{idx} —\n”
prompt += f”Location: {err[‘location’]}\n”
prompt += f”Diagnostic: {err[‘error’]}\n\n”
return prompt
if __name__ == “__main__”:
lang = sys.argv[1] if len(sys.argv) > 1 else “ts”
errors = run_typecheck_ts() if lang == “ts” else run_typecheck_rust()
prompt = construct_ai_prompt(errors)
if prompt:
# Cursorの自動修正コンテキスト用バッファファイルに出力
with open(“.cursor/ACTIVE_ERROR_CONTEXT.md”, “w”) as f:
f.write(prompt)
print(f”[!] {len(errors)} errors detected. Context written to .cursor/ACTIVE_ERROR_CONTEXT.md”)
sys.exit(1)
else:
if os.path.exists(“.cursor/ACTIVE_ERROR_CONTEXT.md”):
os.remove(“.cursor/ACTIVE_ERROR_CONTEXT.md”)
sys.exit(0)
これをGit HookやCursorのComposer実行前のプレトリガーとして仕込むことで、「エラーが発生している箇所と、コンパイラが吐き出した根本理由」が常にAIのワーキングメモリの最上位に位置する状態を確立できる。
—
4. Docker / DevContainer による決定論的開発基盤の完全コード化
LSPのバージョン不一致や、ホストOS固有のバイナリ差異は、AIに誤った型解釈をさせる元凶となる。型駆動開発のためのDevContainer環境を完全固定化する。
.devcontainer/Dockerfile
FROM mcr.microsoft.com/devcontainers/base:ubuntu-22.04
言語ランタイムと超高速LSPサーバーのインストール
ENV RUST_VERSION=1.78.0
ENV NODE_VERSION=20.x
RUN apt-get update && apt-get install -y –no-install-recommends \
curl build-essential git jq \
&& curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION} | bash – \
&& apt-get install -y nodejs \
&& curl –proto ‘=https’ –tlsv1.2 -sSf https://sh.rustup.rs | sh -s — -y –default-toolchain ${RUST_VERSION} \
&& apt-get clean && rm -rf /var/lib/apt/lists/
ENV PATH=”/root/.cargo/bin:${PATH}”
Rust高速解析用 rust-analyzer と TypeScript LSP をグローバル配備
RUN rustup component add rust-analyzer clippy rustfmt \
&& npm install -g typescript ts-node pyright
WORKDIR /workspace
// .devcontainer/devcontainer.json
{
“name”: “Cursor Type-Driven Enclave”,
“build”: {
“dockerfile”: “Dockerfile”
},
“customizations”: {
“vscode”: {
“settings”: {
// LSPのレスポンス速度を最大化しCursor Context Engineへのフィードバックを高速化
“typescript.tsserver.maxTsServerMemory”: 8192,
“typescript.tsserver.experimental.enableProjectDiagnostics”: true,
“rust-analyzer.check.command”: “clippy”,
“rust-analyzer.diagnostics.enable”: true,
“rust-analyzer.procMacro.enable”: true,
“rust-analyzer.cargo.buildScripts.enable”: true,
“editor.formatOnSave”: true,
“editor.codeActionsOnSave”: {
“source.fixAll”: “always”,
“source.organizeImports”: “always”
}
},
“extensions”: [
“ms-vscode.vscode-typescript-next”,
“rust-lang.rust-analyzer”,
“tamasfe.even-better-toml”
]
}
},
“postCreateCommand”: “npm ci && cargo check”
}
—
5. CI/CDパイプライン:Type GatekeeperによるAI生成コードの完全自動検疫
開発者がローカルのCursorでどれほどAIと対話してコードを生成しようとも、CIパイプラインにおいて「型安全性の低下」や「型定義と実装の乖離」を1ミリも許却しない厳格なGatekeeperジョブを構築する。
.github/workflows/type-integrity-gate.yml
name: Type-Driven Integrity Gatekeeper
on:
pull_request:
branches: [main, develop]
push:
branches: [main]
jobs:
type-integrity-check:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout Source Code
uses: actions/checkout@v4
- name: Setup Node.js Toolchain
uses: actions/setup-node@v4
with:
node-