openrune 2.0.0 → 2.0.1

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/README.ko.md CHANGED
@@ -31,9 +31,29 @@ npm install -g @anthropic-ai/claude-code
31
31
  claude # 로그인이 안 되어 있다면 실행
32
32
  ```
33
33
 
34
- > **Rune의 작동 방식:** Rune은 각 에이전트 호출 시 Claude Code를 서브프로세스로 실행합니다. Claude API에 직접 접근하거나 인증을 처리하지 않으며, 모든 실행은 공식 Claude Code CLI를 통해 이루어집니다.
34
+ ### Rune의 작동 방식
35
35
 
36
- > **사용량:** Rune은 사용자의 Claude Code CLI 세션을 통해 실행됩니다. 사용량은 일반 Claude Code 구독에서 차감됩니다.
36
+ Rune은 Claude API를 호출하거나, 인증 정보를 다루거나, Claude Code 내부를 래핑하지 않습니다. 모든 에이전트 호출은 공식 `claude` CLI에 대한 단순한 서브프로세스 호출입니다:
37
+
38
+ ```
39
+ rune run reviewer.rune "..."
40
+
41
+
42
+ spawn('claude', ['-p', '--print',
43
+ '--mcp-config', '{"mcpServers":{}}',
44
+ '--strict-mcp-config',
45
+ '--system-prompt', <역할 + 메모리 + 최근 대화>,
46
+ '--', <사용자 프롬프트>])
47
+
48
+
49
+ Claude Code CLI (이미 로그인된 세션)
50
+ ```
51
+
52
+ 핵심 요약:
53
+ - **API 키 없음, OAuth 가로채기 없음.** Rune은 이미 인증된 `claude` CLI를 그대로 사용합니다.
54
+ - **MCP 격리.** `--mcp-config '{"mcpServers":{}}' --strict-mcp-config` 를 넘겨서 프로젝트의 `.mcp.json`이 에이전트 실행 중 자동 로드되지 않도록 합니다. 작업 폴더의 파일은 건드리지 않습니다.
55
+ - **상태는 `.rune` 파일에.** 역할, 메모리, 대화 히스토리는 디스크에 있는 평범한 JSON입니다. Rune은 매 실행마다 이를 시스템 프롬프트에 주입합니다 — 이것이 서버 없이 영구 저장이 가능한 이유입니다.
56
+ - **사용량:** Claude Code CLI 세션을 통해 실행되므로 일반 Claude Code 구독에서 차감됩니다.
37
57
 
38
58
  ## 설치
39
59
 
package/README.md CHANGED
@@ -31,9 +31,29 @@ npm install -g @anthropic-ai/claude-code
31
31
  claude # login if you haven't
32
32
  ```
33
33
 
34
- > **How Rune works:** Rune spawns Claude Code as a subprocess for each agent invocation. It does not access the Claude API directly or handle any authentication — all execution goes through the official Claude Code CLI.
34
+ ### How Rune works
35
35
 
36
- > **Usage:** Rune runs through your Claude Code CLI session. Usage counts toward your normal Claude Code subscription.
36
+ Rune does not call the Claude API, handle any credentials, or wrap Claude Code's internals. Every agent invocation is a plain subprocess call to the official `claude` CLI:
37
+
38
+ ```
39
+ rune run reviewer.rune "..."
40
+
41
+
42
+ spawn('claude', ['-p', '--print',
43
+ '--mcp-config', '{"mcpServers":{}}',
44
+ '--strict-mcp-config',
45
+ '--system-prompt', <role + memory + recent history>,
46
+ '--', <your prompt>])
47
+
48
+
49
+ Claude Code CLI (your logged-in session)
50
+ ```
51
+
52
+ Key points:
53
+ - **No API key, no OAuth shim.** Rune uses your already-authenticated `claude` CLI as-is.
54
+ - **MCP isolation.** Rune passes `--mcp-config '{"mcpServers":{}}' --strict-mcp-config` so your project's `.mcp.json` never gets auto-loaded during an agent run. Nothing in your working folder is touched.
55
+ - **State lives in the `.rune` file.** Role, memory, and conversation history are plain JSON on your disk. Rune injects them into the system prompt each run — that's how persistence works without any server.
56
+ - **Usage:** runs through your Claude Code CLI session, so usage counts toward your normal Claude Code subscription.
37
57
 
38
58
  ## Install
39
59
 
package/bin/rune.js CHANGED
@@ -3,7 +3,6 @@
3
3
  const { spawn } = require('child_process')
4
4
  const path = require('path')
5
5
  const fs = require('fs')
6
- const os = require('os')
7
6
 
8
7
  const [,, command, ...args] = process.argv
9
8
 
@@ -180,16 +179,9 @@ function runRune(file, restArgs) {
180
179
  if (autoMode) {
181
180
  console.log(`🔮 [auto] ${rune.name} is working on: ${prompt}\n`)
182
181
 
183
- // Temporarily hide .mcp.json to prevent MCP interference
184
- const mcpPath = path.join(folderPath, '.mcp.json')
185
- const mcpBackup = path.join(folderPath, '.mcp.json.bak')
186
- let mcpHidden = false
187
- if (fs.existsSync(mcpPath)) {
188
- fs.renameSync(mcpPath, mcpBackup)
189
- mcpHidden = true
190
- }
191
-
192
182
  const claudeArgs = ['-p', '--print',
183
+ '--mcp-config', '{"mcpServers":{}}',
184
+ '--strict-mcp-config',
193
185
  '--dangerously-skip-permissions',
194
186
  '--verbose',
195
187
  '--output-format', 'stream-json',
@@ -203,12 +195,6 @@ function runRune(file, restArgs) {
203
195
  }
204
196
  claudeArgs.push(prompt)
205
197
 
206
- const restoreMcp = () => {
207
- if (mcpHidden && fs.existsSync(mcpBackup)) {
208
- fs.renameSync(mcpBackup, mcpPath)
209
- }
210
- }
211
-
212
198
  const child = spawn('claude', claudeArgs, {
213
199
  cwd: folderPath,
214
200
  stdio: ['ignore', 'pipe', 'pipe'],
@@ -273,7 +259,6 @@ function runRune(file, restArgs) {
273
259
  child.stderr.on('data', (d) => { process.stderr.write(d) })
274
260
 
275
261
  child.on('close', (code) => {
276
- restoreMcp()
277
262
  // Save to history
278
263
  rune.history = rune.history || []
279
264
  rune.history.push({ role: 'user', text: prompt, ts: Date.now() })
@@ -306,16 +291,15 @@ function runRune(file, restArgs) {
306
291
  process.exit(code || 0)
307
292
  })
308
293
 
309
- // Restore .mcp.json if process is killed
310
- process.on('SIGINT', restoreMcp)
311
- process.on('SIGTERM', restoreMcp)
312
-
313
294
  return
314
295
  }
315
296
 
316
297
  // Normal mode: print-only, no tool execution
317
- // Run from tmpdir to avoid .mcp.json interference, add project folder via --add-dir
318
- const claudeArgs = ['-p', '--print', '--add-dir', folderPath]
298
+ const claudeArgs = [
299
+ '-p', '--print',
300
+ '--mcp-config', '{"mcpServers":{}}',
301
+ '--strict-mcp-config',
302
+ ]
319
303
  if (systemPrompt) {
320
304
  claudeArgs.push('--system-prompt', systemPrompt + `\nWorking folder: ${folderPath}`)
321
305
  }
@@ -325,7 +309,7 @@ function runRune(file, restArgs) {
325
309
  claudeArgs.push('--', prompt)
326
310
 
327
311
  const child = spawn('claude', claudeArgs, {
328
- cwd: os.tmpdir(),
312
+ cwd: folderPath,
329
313
  stdio: ['ignore', 'pipe', 'pipe'],
330
314
  env: { ...process.env },
331
315
  })
@@ -434,16 +418,9 @@ async function pipeRunes(args) {
434
418
  const useAuto = autoMode && isLast
435
419
 
436
420
  if (useAuto) {
437
- // Temporarily hide .mcp.json
438
- const mcpPath = path.join(folderPath, '.mcp.json')
439
- const mcpBackup = path.join(folderPath, '.mcp.json.pipe.bak')
440
- let mcpHidden = false
441
- if (fs.existsSync(mcpPath)) {
442
- fs.renameSync(mcpPath, mcpBackup)
443
- mcpHidden = true
444
- }
445
-
446
421
  const claudeArgs = ['-p', '--print',
422
+ '--mcp-config', '{"mcpServers":{}}',
423
+ '--strict-mcp-config',
447
424
  '--dangerously-skip-permissions',
448
425
  '--verbose',
449
426
  '--output-format', 'stream-json',
@@ -494,7 +471,6 @@ async function pipeRunes(args) {
494
471
  })
495
472
  child.stderr.on('data', (d) => { process.stderr.write(d) })
496
473
  child.on('close', (code) => {
497
- if (mcpHidden && fs.existsSync(mcpBackup)) fs.renameSync(mcpBackup, mcpPath)
498
474
  if (code !== 0) reject(new Error(`Agent ${rune.name} exited with code ${code}`))
499
475
  else resolve(fullOutput.trim())
500
476
  })
@@ -508,8 +484,12 @@ async function pipeRunes(args) {
508
484
  currentInput = output
509
485
 
510
486
  } else {
511
- // Normal pipe step: text output only, run from tmpdir to avoid .mcp.json
512
- const claudeArgs = ['-p', '--print', '--add-dir', folderPath]
487
+ // Normal pipe step: text output only
488
+ const claudeArgs = [
489
+ '-p', '--print',
490
+ '--mcp-config', '{"mcpServers":{}}',
491
+ '--strict-mcp-config',
492
+ ]
513
493
  if (systemParts.length > 0) {
514
494
  claudeArgs.push('--system-prompt', systemParts.join('\n') + `\nWorking folder: ${folderPath}`)
515
495
  }
@@ -517,7 +497,7 @@ async function pipeRunes(args) {
517
497
 
518
498
  const output = await new Promise((resolve, reject) => {
519
499
  const child = spawn('claude', claudeArgs, {
520
- cwd: os.tmpdir(),
500
+ cwd: folderPath,
521
501
  stdio: ['ignore', 'pipe', 'pipe'],
522
502
  env: { ...process.env },
523
503
  })
@@ -690,15 +670,9 @@ async function loopRunes(args) {
690
670
 
691
671
  async function runAgent(name, folderPath, systemParts, prompt, autoMode) {
692
672
  if (autoMode) {
693
- const mcpPath = path.join(folderPath, '.mcp.json')
694
- const mcpBackup = path.join(folderPath, '.mcp.json.loop.bak')
695
- let mcpHidden = false
696
- if (fs.existsSync(mcpPath)) {
697
- fs.renameSync(mcpPath, mcpBackup)
698
- mcpHidden = true
699
- }
700
-
701
673
  const claudeArgs = ['-p', '--print',
674
+ '--mcp-config', '{"mcpServers":{}}',
675
+ '--strict-mcp-config',
702
676
  '--dangerously-skip-permissions',
703
677
  '--verbose',
704
678
  '--output-format', 'stream-json',
@@ -748,7 +722,6 @@ async function runAgent(name, folderPath, systemParts, prompt, autoMode) {
748
722
  })
749
723
  child.stderr.on('data', (d) => { process.stderr.write(d) })
750
724
  child.on('close', (code) => {
751
- if (mcpHidden && fs.existsSync(mcpBackup)) fs.renameSync(mcpBackup, mcpPath)
752
725
  if (code !== 0) reject(new Error(`Agent ${name} exited with code ${code}`))
753
726
  else resolve(fullOutput.trim())
754
727
  })
@@ -756,8 +729,11 @@ async function runAgent(name, folderPath, systemParts, prompt, autoMode) {
756
729
 
757
730
  return output
758
731
  } else {
759
- const tmpdir = require('os').tmpdir()
760
- const claudeArgs = ['-p', '--print', '--add-dir', folderPath]
732
+ const claudeArgs = [
733
+ '-p', '--print',
734
+ '--mcp-config', '{"mcpServers":{}}',
735
+ '--strict-mcp-config',
736
+ ]
761
737
  if (systemParts.length > 0) {
762
738
  claudeArgs.push('--system-prompt', systemParts.join('\n') + `\nWorking folder: ${folderPath}`)
763
739
  }
@@ -765,7 +741,7 @@ async function runAgent(name, folderPath, systemParts, prompt, autoMode) {
765
741
 
766
742
  const output = await new Promise((resolve, reject) => {
767
743
  const child = spawn('claude', claudeArgs, {
768
- cwd: tmpdir,
744
+ cwd: folderPath,
769
745
  stdio: ['ignore', 'pipe', 'pipe'],
770
746
  env: { ...process.env },
771
747
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openrune",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "Persistent AI agents for Claude Code — build once, run forever.",
5
5
  "keywords": ["ai", "agent", "claude", "claude-code", "cli", "toolkit", "automation"],
6
6
  "repository": {