golembot / rules
Cursor rule326 starsChanged 9 months agoMITView on GitHub
Coding Agent engine implementation details (Cursor + Claude Code) — reference when modifying engine.ts or debugging Agent behavior
- Reads credentials
Where files are headingNew Cursor rule files have more rules about planning and tracking tasks, and fewer about code style.
Of the Cursor rule files we read, the 4,183 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.
In this repository11 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- .cursor/rules/architecture-constraints.mdcCursor rule
- .cursor/rules/testing-conventions.mdcCursor rule
- escalationSkill
- generalSkill
- im-adapterSkill
- kb-guideSkill
- message-pushSkill
- multi-botSkill
- task-managerSkill
The file
We split this engine-internals.mdc into the rules it gives an agent
7 rules. About 0 are common, 4 are distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈4
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 56
When apiKey is not passed, the engine inherits process.env (including existing CURSOR_API_KEY or ANTHROPIC_API_KEY).
- Distinctive: line 78
Before each invoke, remove old symlinks (only those that are isSymbolicLink), then recreate
- Very specific: line 83
After reading AGENTS.md and SKILL.md from .cursor/skills/, the Coding Agent decides on its own when to use which Skill.
- Distinctive: line 84
Test prompts should not explicitly say "use xxx script" or "follow xxx Skill"
- Very specific: line 91
Must maintain a buffer, split on \n, keep the last incomplete line
- Distinctive: line 93
After close, check whether done/error was already yielded.
- Distinctive: line 106
No ANSI escape sequences (plain stdout JSON).
≈ “Common” is an estimate: at least 1 in 5 of the 6,668 Cursor rule files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
---
description: Coding Agent engine implementation details (Cursor + Claude Code) — reference when modifying engine.ts or debugging Agent behavior
globs: src/engine.ts, src/index.ts, examples/e2e-test.ts, examples/e2e-headless.ts, examples/e2e-claude-code.ts
alwaysApply: false
---
# Cursor Engine Implementation Details
## Agent CLI Invocation
- Binary path: `~/.local/bin/agent` (not `cursor`, nor `cursor agent`)
- Uses standard `child_process.spawn` — no PTY (node-pty) needed since CLI version 2026.02+
- Key flags: `--output-format stream-json --stream-partial-output --force --trust --sandbox disabled --approve-mcps`
## stream-json Output Format
Output is clean NDJSON on stdout; `stripAnsi()` is retained as a safety net but is not expected to be needed.
One JSON object per line, main types:
```
{ "type": "system", "subtype": "init", "session_id": "xxx", ... } → Init; does not signal end of conversation
{ "type": "assistant", "message": { "content": [{ "type": "text", "text": "..." }] } } → Assistant text delta
{ "type": "tool_call", "subtype": "started", "tool_call": { "<XxxToolCall>": { "args": {...} } } } → Tool call started
{ "type": "tool_call", "subtype": "completed", "tool_call": { "<XxxToolCall>": { "args": {...}, "result": {...} } } } → Tool call completed
{ "type": "result", "subtype": "success", "is_error": false, "session_id": "xxx", "duration_ms": 1234 } → Normal end
{ "type": "result", "subtype": "error", "is_error": true, "result": "error msg" } → Error end
```
### Key Notes
1. **tool_call has both started and completed events** — started → yield `tool_call`, completed → yield `tool_result`; do not treat both as tool_call (would duplicate)
2. **tool_call name is not a fixed field** — iterate over the `tool_call` object keys and find the one ending with `ToolCall`; some tools use `function` format (`{ "name": "...", "arguments": "..." }`)
3. **`--stream-partial-output` changes assistant event granularity** — with it, each assistant event is character-level delta; without it, full paragraphs. **Critical**: after all deltas for each segment (text between tool calls), Cursor sends one more summary event (content = concatenation of that segment’s deltas). CursorEngine uses accumulated text comparison to detect and skip summary, avoiding duplicate text
4. **assistant.message.content is an array** — multiple blocks; filter those with `type === 'text'` and concatenate
5. **system event is not a termination signal** — do not end the stream on system event
6. **session_id appears in both system and result** — use the one in result as the final session_id
## apiKey Authentication Passthrough (Cursor)
Headless environments (cloud servers, CI/CD) have no `agent login` state; must authenticate via API Key. Full passthrough chain:
```
CreateAssistantOpts.apiKey (user-provided)
→ index.ts: stored in closure, passed to engine.invoke() on each doChat()
→ InvokeOpts.apiKey
→ CursorEngine:
1. args.push('--api-key', opts.apiKey) // CLI argument
2. env.CURSOR_API_KEY = opts.apiKey // env var (backup)
→ ClaudeCodeEngine:
1. env.ANTHROPIC_API_KEY = opts.apiKey // env var
```
CLI layer (`cli.ts`) `run` and `serve` commands both support `--api-key <key>` (generic, any engine).
When apiKey is not passed, the engine inherits `process.env` (including existing `CURSOR_API_KEY` or `ANTHROPIC_API_KEY`); works if the host process already sets those env vars.
## durationMs Extraction
Cursor result event includes `duration_ms` (integer, milliseconds). `parseStreamLine` extracts it and puts it in the `done` event’s `durationMs` field.
- Has `duration_ms` → `{ type: 'done', sessionId, durationMs: 4500 }`
- No `duration_ms` → `{ type: 'done', sessionId, durationMs: undefined }`
- `duration_ms: 0` → `{ type: 'done', sessionId, durationMs: 0 }` (valid value)
- error result → do not expose durationMs (return error event)
## Session Resume
- Flag: `--resume <sessionId>`
- On resume failure, Agent process may exit with error (exit code != 0) or return is_error in result event
- GolemBot fallback: on resume-related error → clearSession → retry once without --resume
- Resume failure keywords: error message contains "resume" or "session" (case-insensitive)
## Skill Injection
- Cursor discovers Skills from `.cursor/skills/` directory
- GolemBot strategy: symlink `skills/<name>` to `.cursor/skills/<name>`
- Before each invoke, remove old symlinks (only those that are isSymbolicLink), then recreate
- Ensures Skill list always matches `skills/` directory
## Skill Auto-Discovery and Usage (Important)
After reading AGENTS.md and SKILL.md from .cursor/skills/, the Coding Agent **decides on its own** when to use which Skill.
- Test prompts should not explicitly say "use xxx script" or "follow xxx Skill"
- Correct approach: describe the task in natural language and let the Agent decide
- Assertions should be looser (Agent may use different filenames or output directly in the reply without writing a file)
## stdout Buffer Handling
- stdout `data` events may fire at arbitrary byte boundaries (not by line)
- Must maintain a buffer, split on `\n`, keep the last incomplete line
- On process close, drain the remaining buffer (append `\n` then process)
- After close, check whether done/error was already yielded; if not, emit an error
# Claude Code Engine Implementation Details
## CLI Invocation
- Binary path: `~/.local/bin/claude`
- Uses standard `child_process.spawn` (**no PTY needed**); reads stream-json from stdout
- Key flags: `-p <prompt> --output-format stream-json --verbose --dangerously-skip-permissions`
- `--verbose` is **required**; without it, stream-json only outputs the final result event, not intermediate assistant/user events
## stream-json Output Format
No ANSI escape sequences (plain stdout JSON); no stripAnsi needed.
One JSON object per line, main types:
```
{ "type": "system", "subtype": "init", "session_id": "xxx", ... } → Ignore
{ "type": "assistant", "message": { "content": [{ "type": "text", "text": "..." }, { "type": "tool_use", "name": "Bash", "input": {...} }] } } → May yield multiple StreamEvents
{ "type": "user", "message": { "content": [{ "type": "tool_result", "content": "..." }] } } → Tool execution result
{ "type": "result", "is_error": false, "session_id": "xxx", "duration_ms": 1234, "total_cost_usd": 0.01, "num_turns": 2 } → done
```
### Key Notes
1. **Single message can have mixed content blocks** — assistant message content array can include both text and tool_use. `parseClaudeStreamLine()` returns `StreamEvent[]` not a single event
2. **`--verbose` is required** — without it, only result is output; no intermediate stream events at all
3. **tool_result content has two formats** — string or `[{ type: "text", text: "..." }]` array; parser must handle both
4. **result event extended fields** — `total_cost_usd`, `num_turns`, `duration_api_ms`, `usage`; mapped to `StreamEvent.done`’s `costUsd` and `numTurns`
5. **No segmentAccum dedup needed** — Claude Code does not have summary events like Cursor
## Skill Injection
- Claude Code natively discovers Skills from `.claude/skills/` directory (mirrors Cursor’s `.cursor/skills/`)
- `injectClaudeSkills()` also generates `CLAUDE.md` (extra context)
- Skill frontmatter (name, description, etc.) is natively recognized by Claude Code
## Authentication
- `claude auth login` for interactive login
- `ANTHROPIC_API_KEY` env var (headless)
- apiKey passthrough: `InvokeOpts.apiKey → env.ANTHROPIC_API_KEY`
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

