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 +22 -2
- package/README.md +22 -2
- package/bin/rune.js +25 -49
- package/package.json +1 -1
package/README.ko.md
CHANGED
|
@@ -31,9 +31,29 @@ npm install -g @anthropic-ai/claude-code
|
|
|
31
31
|
claude # 로그인이 안 되어 있다면 실행
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
### Rune의 작동 방식
|
|
35
35
|
|
|
36
|
-
|
|
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
|
-
|
|
34
|
+
### How Rune works
|
|
35
35
|
|
|
36
|
-
|
|
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
|
-
|
|
318
|
-
|
|
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:
|
|
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
|
|
512
|
-
const claudeArgs = [
|
|
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:
|
|
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
|
|
760
|
-
|
|
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:
|
|
744
|
+
cwd: folderPath,
|
|
769
745
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
770
746
|
env: { ...process.env },
|
|
771
747
|
})
|
package/package.json
CHANGED