bjir-cli 0.1.0
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.md +129 -0
- package/bin/bjir.cjs +97 -0
- package/package.json +27 -0
- package/scripts/postinstall.cjs +29 -0
- package/share/opencode.json +20 -0
- package/vendor/rtk/rtk-linux-x64 +0 -0
package/README.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
<h1 align="center">BJiR</h1>
|
|
2
|
+
|
|
3
|
+
<p align="center">A token-lean AI coding agent for the terminal.</p>
|
|
4
|
+
|
|
5
|
+
<p align="center"><b>One obsession: do the same work for far fewer tokens.</b></p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="README.md">English</a> |
|
|
9
|
+
<a href="README.id.md">Bahasa Indonesia</a>
|
|
10
|
+
</p>
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## What is BJiR?
|
|
15
|
+
|
|
16
|
+
BJiR is an AI coding agent you run in your terminal. It reads and edits code, runs commands, and talks to a model (Claude, GPT, Gemini, MiMo, local models).
|
|
17
|
+
|
|
18
|
+
The difference is token usage. Every request is trimmed, every reply is steered to be terse, and noisy tool output (test logs, big JSON, `git status`) is compressed before it reaches the model. These optimizations are compiled in and always on, so you save without configuring anything. Less context in and less text out means lower cost, faster replies, and longer sessions.
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
### npm (recommended)
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm i -g bjir # or: bun add -g bjir / pnpm add -g bjir / yarn global add bjir
|
|
26
|
+
bjir # launch
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### From source
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
git clone https://github.com/gogetrekt/bjir
|
|
33
|
+
cd bjir
|
|
34
|
+
bun install
|
|
35
|
+
./bin/bjir.sh
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
To run `bjir` from anywhere on a source checkout, symlink it onto your PATH:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
ln -s "$PWD/bin/bjir.sh" ~/.local/bin/bjir
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### API key
|
|
45
|
+
|
|
46
|
+
BJiR needs a model provider. Launch `bjir`, open the provider dialog, and add one (OpenAI-compatible, Anthropic-compatible, or a preset). Name, base URL, key, and models are saved and persist across restarts.
|
|
47
|
+
|
|
48
|
+
## Quick start
|
|
49
|
+
|
|
50
|
+
Run `bjir` (just `bjir`) to open the interactive TUI:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
bjir # open the TUI in the current directory
|
|
54
|
+
bjir /path/to/project # open the TUI in a specific project
|
|
55
|
+
bjir run "summarize today's changes" # one-shot, non-interactive
|
|
56
|
+
bjir gain # show tokens saved
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
In the TUI: type to chat, press `Tab` to switch between the build agent (full access) and the plan agent (read-only), run `/reducer` to change optimization intensity, and watch the sidebar for a live tokens-saved counter.
|
|
60
|
+
|
|
61
|
+
## Optimizations
|
|
62
|
+
|
|
63
|
+
Built into the binary, always on. Nothing to configure.
|
|
64
|
+
|
|
65
|
+
| Optimization | What it does |
|
|
66
|
+
| --- | --- |
|
|
67
|
+
| Ponytail | Code-minimalism rules. The agent writes the smallest correct change, so replies and diffs stay small. |
|
|
68
|
+
| Caveman | Response-style rules that make the model answer tersely. The biggest cut to output tokens. Three intensity levels (see `/reducer`). |
|
|
69
|
+
| I/O Refiner | Trims the assembled prompt right before it is sent, cutting input tokens. |
|
|
70
|
+
| Context Prune | Drops stale history each turn so old context stops costing tokens. |
|
|
71
|
+
| Read Dedup | Skips re-sending a file that was already read. |
|
|
72
|
+
| Semantic Read | Returns only the relevant parts of a file when that is enough. |
|
|
73
|
+
| Tool-output compression | Big tool results are compressed by type: SmartCrusher for large JSON, Log Compressor for build and shell logs, and CCR, which stashes the full output and lets the agent pull back any part on demand. Lossless or skipped. |
|
|
74
|
+
| RTK | Wraps shell commands (`git`, `cargo`, `npm`, `bun`, `docker`) so their output is compressed before the agent sees it. |
|
|
75
|
+
| BJiR Gateway | A local OpenAI-compatible gateway that routes across providers with automatic fallback and meters token usage. |
|
|
76
|
+
|
|
77
|
+
### Intensity
|
|
78
|
+
|
|
79
|
+
Caveman has three levels: `lite`, `standard`, `ultra`. Switch live in the TUI with `/reducer` (also `/caveman`, `/ponytail`); it applies on your next message. From the CLI, `bjir profile [explain|balanced|ultra]` sets a persistent profile. Set `BJIR_OPTIMIZE=0` to turn the layer off.
|
|
80
|
+
|
|
81
|
+
## Savings
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
bjir gain # ranked table of tokens saved
|
|
85
|
+
bjir gain --json # machine-readable
|
|
86
|
+
bjir gain --reset # clear history
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Numbers come from real session data. The TUI sidebar shows a running total.
|
|
90
|
+
|
|
91
|
+
## Commands
|
|
92
|
+
|
|
93
|
+
Run `bjir <command> --help` for options.
|
|
94
|
+
|
|
95
|
+
| Command | Description |
|
|
96
|
+
| --- | --- |
|
|
97
|
+
| `bjir` | Launch the interactive TUI (default). |
|
|
98
|
+
| `bjir run "<prompt>"` | One-shot, non-interactive run. |
|
|
99
|
+
| `bjir serve` | Run the headless HTTP API server. |
|
|
100
|
+
| `bjir attach` | Connect to a running server. |
|
|
101
|
+
| `bjir gain` | Show the token-savings summary. |
|
|
102
|
+
| `bjir compress` | Caveman-compress memory files (`CLAUDE.md`, `AGENTS.md`, `.opencode/memory.md`). |
|
|
103
|
+
| `bjir profile [name]` | Show or switch the optimization profile. |
|
|
104
|
+
| `bjir gateway` | Start the BJiR Gateway (usually automatic). |
|
|
105
|
+
| `bjir models` | List available models. |
|
|
106
|
+
| `bjir providers` | Manage providers and credentials. |
|
|
107
|
+
| `bjir revoke [id]` | Remove a connected provider. |
|
|
108
|
+
| `bjir agent` | Create and manage agents. |
|
|
109
|
+
| `bjir mcp` | Manage MCP servers. |
|
|
110
|
+
| `bjir github` | Set up and run the GitHub agent. |
|
|
111
|
+
| `bjir upgrade` | Update to the latest version. |
|
|
112
|
+
| `bjir uninstall` | Uninstall BJiR. |
|
|
113
|
+
|
|
114
|
+
## Configuration
|
|
115
|
+
|
|
116
|
+
BJiR reads config from `~/.config/opencode/opencode.json` (seeded on first run) and a project-local `opencode.json` or `.opencode/opencode.json`. Providers added through the connect dialog are written here. Built-in optimizations are not config; tune them with `/reducer`, `bjir profile`, and `BJIR_*` environment variables.
|
|
117
|
+
|
|
118
|
+
## Agents
|
|
119
|
+
|
|
120
|
+
Switch with `Tab`:
|
|
121
|
+
|
|
122
|
+
- build: full-access agent for writing and changing code.
|
|
123
|
+
- plan: read-only, denies edits and asks before running commands.
|
|
124
|
+
|
|
125
|
+
Invoke the general subagent for complex searches with `@general`.
|
|
126
|
+
|
|
127
|
+
## License
|
|
128
|
+
|
|
129
|
+
MIT. BJiR is a fork of [opencode](https://github.com/anomalyco/opencode) and is not affiliated with or endorsed by the opencode team.
|
package/bin/bjir.cjs
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// bjir launcher — find binary, start gateway if needed, run agent.
|
|
3
|
+
const childProcess = require("child_process")
|
|
4
|
+
const fs = require("fs")
|
|
5
|
+
const path = require("path")
|
|
6
|
+
const os = require("os")
|
|
7
|
+
const http = require("http")
|
|
8
|
+
|
|
9
|
+
const platformMap = { darwin: "darwin", linux: "linux", win32: "windows" }
|
|
10
|
+
const archMap = { x64: "x64", arm64: "arm64", arm: "arm" }
|
|
11
|
+
const platform = platformMap[os.platform()] || os.platform()
|
|
12
|
+
const arch = archMap[os.arch()] || os.arch()
|
|
13
|
+
const base = "bjir-" + platform + "-" + arch
|
|
14
|
+
const binName = platform === "windows" ? "bjir.exe" : "bjir"
|
|
15
|
+
const names = [base, base + "-baseline", base + "-musl", base + "-baseline-musl"]
|
|
16
|
+
|
|
17
|
+
function findBinary(startDir) {
|
|
18
|
+
let current = startDir
|
|
19
|
+
for (;;) {
|
|
20
|
+
const modules = path.join(current, "node_modules")
|
|
21
|
+
if (fs.existsSync(modules)) {
|
|
22
|
+
for (const name of names) {
|
|
23
|
+
const candidate = path.join(modules, name, "bin", binName)
|
|
24
|
+
if (fs.existsSync(candidate)) return candidate
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const parent = path.dirname(current)
|
|
28
|
+
if (parent === current) return
|
|
29
|
+
current = parent
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const binary = process.env.BJIR_BIN_PATH || findBinary(path.dirname(fs.realpathSync(__filename)))
|
|
34
|
+
if (!binary) {
|
|
35
|
+
console.error("bjir: could not find the platform binary (" + names.join(" / ") + "). Reinstall `bjir`.")
|
|
36
|
+
process.exit(1)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const PORT = Number(process.env.BJIR_GATEWAY_PORT || 9090)
|
|
40
|
+
const args = process.argv.slice(2)
|
|
41
|
+
|
|
42
|
+
// `bjir gateway ...` -> run the gateway directly (no wrapper orchestration).
|
|
43
|
+
if (args[0] === "gateway") {
|
|
44
|
+
childProcess.spawn(binary, args, { stdio: "inherit" }).on("exit", (c) => process.exit(c || 0))
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Ensure a default config exists (never clobber an existing one).
|
|
49
|
+
try {
|
|
50
|
+
const cfgDir = path.join(process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config"), "opencode")
|
|
51
|
+
const cfg = path.join(cfgDir, "opencode.json")
|
|
52
|
+
const def = path.join(__dirname, "..", "share", "opencode.json")
|
|
53
|
+
if (!fs.existsSync(cfg) && fs.existsSync(def)) {
|
|
54
|
+
fs.mkdirSync(cfgDir, { recursive: true })
|
|
55
|
+
fs.copyFileSync(def, cfg)
|
|
56
|
+
}
|
|
57
|
+
} catch {}
|
|
58
|
+
|
|
59
|
+
function health(cb) {
|
|
60
|
+
const req = http.get({ host: "localhost", port: PORT, path: "/health", timeout: 500 }, (res) => {
|
|
61
|
+
res.resume()
|
|
62
|
+
cb(res.statusCode === 200)
|
|
63
|
+
})
|
|
64
|
+
req.on("error", () => cb(false))
|
|
65
|
+
req.on("timeout", () => {
|
|
66
|
+
req.destroy()
|
|
67
|
+
cb(false)
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function startAgent(routerProc) {
|
|
72
|
+
// Bare launch (no subcommand) -> open the TUI in the user's cwd.
|
|
73
|
+
const passed = args.length === 0 ? [process.cwd()] : args
|
|
74
|
+
const child = childProcess.spawn(binary, passed, { stdio: "inherit" })
|
|
75
|
+
const cleanup = () => {
|
|
76
|
+
if (routerProc) try { routerProc.kill() } catch {}
|
|
77
|
+
}
|
|
78
|
+
for (const s of ["SIGINT", "SIGTERM", "SIGHUP"]) process.on(s, () => { try { child.kill(s) } catch {} })
|
|
79
|
+
child.on("exit", (code, signal) => {
|
|
80
|
+
cleanup()
|
|
81
|
+
if (signal) return process.kill(process.pid, signal)
|
|
82
|
+
process.exit(typeof code === "number" ? code : 0)
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
health((up) => {
|
|
87
|
+
if (up) return startAgent(null)
|
|
88
|
+
const router = childProcess.spawn(binary, ["gateway"], { stdio: "ignore", detached: false })
|
|
89
|
+
let waited = 0
|
|
90
|
+
const tick = () =>
|
|
91
|
+
health((ok) => {
|
|
92
|
+
if (ok || waited >= 6000) return startAgent(router)
|
|
93
|
+
waited += 250
|
|
94
|
+
setTimeout(tick, 250)
|
|
95
|
+
})
|
|
96
|
+
setTimeout(tick, 250)
|
|
97
|
+
})
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "bjir-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "BJiR: Bloat Judgement & I/O Refiner, a token-lean AI coding agent for the terminal.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"homepage": "https://github.com/gogetrekt/bjir",
|
|
7
|
+
"repository": { "type": "git", "url": "git+https://github.com/gogetrekt/bjir.git" },
|
|
8
|
+
"keywords": ["ai", "agent", "cli", "tui", "coding", "tokens", "llm", "terminal"],
|
|
9
|
+
"bin": { "bjir": "./bin/bjir.cjs" },
|
|
10
|
+
"files": ["bin", "scripts", "share", "vendor"],
|
|
11
|
+
"scripts": { "postinstall": "node scripts/postinstall.cjs" },
|
|
12
|
+
"optionalDependencies": {
|
|
13
|
+
"bjir-linux-x64": "0.1.0",
|
|
14
|
+
"bjir-linux-x64-baseline": "0.1.0",
|
|
15
|
+
"bjir-linux-x64-musl": "0.1.0",
|
|
16
|
+
"bjir-linux-x64-baseline-musl": "0.1.0",
|
|
17
|
+
"bjir-linux-arm64": "0.1.0",
|
|
18
|
+
"bjir-linux-arm64-musl": "0.1.0",
|
|
19
|
+
"bjir-darwin-arm64": "0.1.0",
|
|
20
|
+
"bjir-darwin-x64": "0.1.0",
|
|
21
|
+
"bjir-darwin-x64-baseline": "0.1.0",
|
|
22
|
+
"bjir-windows-x64": "0.1.0",
|
|
23
|
+
"bjir-windows-x64-baseline": "0.1.0",
|
|
24
|
+
"bjir-windows-arm64": "0.1.0"
|
|
25
|
+
},
|
|
26
|
+
"preferUnplugged": true
|
|
27
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* bjir postinstall — copy the matching rtk binary to ~/.bjir/bin/rtk so the bash
|
|
4
|
+
* tool finds it (shell-output compression). Best-effort: never fails install.
|
|
5
|
+
* The default opencode config is written on first run by the launcher.
|
|
6
|
+
*/
|
|
7
|
+
const fs = require("fs")
|
|
8
|
+
const path = require("path")
|
|
9
|
+
const os = require("os")
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
const platformMap = { darwin: "darwin", linux: "linux", win32: "windows" }
|
|
13
|
+
const platform = platformMap[os.platform()] || os.platform()
|
|
14
|
+
const arch = os.arch() === "arm64" ? "arm64" : "x64"
|
|
15
|
+
const ext = platform === "windows" ? ".exe" : ""
|
|
16
|
+
const src = path.join(__dirname, "..", "vendor", "rtk", `rtk-${platform}-${arch}${ext}`)
|
|
17
|
+
if (!fs.existsSync(src)) {
|
|
18
|
+
console.warn(`[bjir] no bundled rtk for ${platform}-${arch}; shell compression disabled until rtk is on PATH`)
|
|
19
|
+
return
|
|
20
|
+
}
|
|
21
|
+
const destDir = path.join(os.homedir(), ".bjir", "bin")
|
|
22
|
+
const dest = path.join(destDir, `rtk${ext}`)
|
|
23
|
+
fs.mkdirSync(destDir, { recursive: true })
|
|
24
|
+
fs.copyFileSync(src, dest)
|
|
25
|
+
if (platform !== "windows") fs.chmodSync(dest, 0o755)
|
|
26
|
+
console.log(`[bjir] rtk installed -> ${dest}`)
|
|
27
|
+
} catch (e) {
|
|
28
|
+
console.warn(`[bjir] postinstall skipped: ${e && e.message ? e.message : e}`)
|
|
29
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://opencode.ai/config.json",
|
|
3
|
+
"provider": {
|
|
4
|
+
"bjir-gw": {
|
|
5
|
+
"npm": "@ai-sdk/openai-compatible",
|
|
6
|
+
"name": "BJiR Gateway",
|
|
7
|
+
"options": { "baseURL": "http://localhost:9090/v1", "apiKey": "local" },
|
|
8
|
+
"models": {
|
|
9
|
+
"auto": { "name": "auto" },
|
|
10
|
+
"claude-sonnet-4-6": { "name": "Claude Sonnet 4.6" },
|
|
11
|
+
"gpt-4o": { "name": "GPT-4o" },
|
|
12
|
+
"gemini/gemini-2.0-flash": { "name": "Gemini 2.0 Flash" },
|
|
13
|
+
"ollama:qwen2.5-coder": { "name": "Qwen2.5 Coder (Ollama)" }
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"model": "bjir-gw/auto",
|
|
18
|
+
"lsp": true,
|
|
19
|
+
"reducer": "standard"
|
|
20
|
+
}
|
|
Binary file
|