@bill10/agent-007 0.6.3000 → 0.6.4001

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/.env.example ADDED
@@ -0,0 +1,54 @@
1
+ # Agent 007 settings. Everything is optional and commented out: the app runs on
2
+ # its defaults (the values shown). Uncomment a line to change it, then restart.
3
+ #
4
+ # Read from ~/.agent-007/.env (`npx @bill10/agent-007 init` writes this file
5
+ # there) and from a .env in the directory you start the app from. Precedence,
6
+ # highest first: command-line flags, the real environment, ./.env,
7
+ # ~/.agent-007/.env. AGENT007_CONFIG_DIR moves ~/.agent-007.
8
+
9
+ # Port to listen on.
10
+ # PORT=7007
11
+
12
+ # Bind interface. 127.0.0.1 = localhost-only (safe default).
13
+ # Set to 0.0.0.0 to accept remote connections — only behind Tailscale or another
14
+ # trusted network, since the server spawns real shells. See docs/REMOTE.md.
15
+ # HOST=127.0.0.1
16
+
17
+ # Comma-separated extra browser origins allowed by the cross-origin check.
18
+ # localhost and 127.0.0.1 are always allowed. Add your tailnet hostname to reach
19
+ # the app from another machine. Bare hostnames or full origins both work; only
20
+ # the hostname is used. A single `*` disables the check (not recommended).
21
+ # ALLOWED_ORIGINS=mac-mini.tailXXXX.ts.net
22
+
23
+ # The permission mode every Claude Code / Codex agent the app starts runs in,
24
+ # unless something more specific says otherwise: flags already in the command
25
+ # you type, a job card's own mode, or a mode picked in the job board's
26
+ # "permissions" dropdown. One of: auto, acceptEdits, bypassPermissions, manual,
27
+ # dontAsk, plan. Unset: each CLI's own default. Billion keeps its own mode.
28
+ # CLAUDE_PERMISSION_MODE=bypassPermissions
29
+ # CODEX_PERMISSION_MODE=bypassPermissions
30
+
31
+ # Who may message whom. By default an agent that asks before acting cannot
32
+ # message one that never asks, so it cannot borrow the other's permissions.
33
+ # `open` lifts that rule for all your agents (messages are still labelled as
34
+ # coming from another agent). It changes nothing when every agent runs in the
35
+ # same kind of mode.
36
+ # AGENT_MESSAGING=open
37
+
38
+ # Billion, the always-on agent you talk to (docs/BILLION.md). On by default;
39
+ # 0/false/off/no turns it off. It is also off whenever user accounts exist.
40
+ # BILLION=0
41
+
42
+ # Billion's own folder and git repo. Must be new or empty the first time.
43
+ # BILLION_DIR=~/.agent-007/billion
44
+
45
+ # Claude Code stops at a "do you trust this folder?" dialog the first time it
46
+ # runs anywhere, and every job-board worker gets a brand-new worktree. By default
47
+ # the app accepts it for board workers (it adds the worktree to ~/.claude.json
48
+ # before the spawn, and answers "Yes" if the dialog still appears), so queued
49
+ # jobs start on their own. A trusted worktree loads that repo's own
50
+ # .claude/settings.json: its hooks run, and its permission allow rules can
51
+ # pre-approve what a strict board mode would ask about. 0/false/off/no keeps
52
+ # the dialog.
53
+ # Hand-started agents always keep it.
54
+ # TRUST_BOARD_WORKTREES=0
package/README.md CHANGED
@@ -3,14 +3,23 @@
3
3
  [![Tests (Ubuntu)](https://github.com/bill10/agent-007/actions/workflows/test-ubuntu.yml/badge.svg)](https://github.com/bill10/agent-007/actions/workflows/test-ubuntu.yml)
4
4
  [![Tests (Windows)](https://github.com/bill10/agent-007/actions/workflows/test-windows.yml/badge.svg)](https://github.com/bill10/agent-007/actions/workflows/test-windows.yml)
5
5
 
6
- **Queue coding jobs, walk away, review the PRs.**
7
-
8
- Agents run in parallel, each in its own git worktree; one boss agent runs the board for you; and a pixel office shows who's working and who's waiting on you.
6
+ **From web terminals for your coding agents to a self-running agent company.**
9
7
 
10
8
  ![An agent walks to its desk and starts work, a job card is posted and dispatched to a second desk, and an agent turns orange when it stops to ask a question](docs/demo.gif)
11
9
 
12
10
  *Recorded from the running app. If the capture does not load, there is a [still screenshot](docs/screenshot.png).*
13
11
 
12
+ Use it as far along as you need:
13
+
14
+ 1. **One task: one agent in a web terminal.** Start Claude Code, Codex or any CLI agent in the browser. Add a repo once; every agent gets its own git worktree and branch, so you never set one up by hand.
15
+ 2. **Many tasks across projects: many terminals, one window.** Every repo and every agent in one place, with live terminals, a file explorer, inline diffs, and a pixel office where each agent faces its screen while it works and turns to you when it needs you.
16
+ 3. **Stop watching them: a job board.** Put tasks on the board and Claude Code or Codex workers pick them up, each in its own worktree, and move them To do -> In progress -> Review on their own, landing as a pull request (or a summary, for work that isn't code). Cards can run on a cron schedule, and agents can post cards and message each other.
17
+ 4. **Stop posting jobs: give Billion a goal.** Billion is one always-on agent that plans, posts the jobs, reviews what comes back and merges the PRs. It only asks you about money, access or anything irreversible.
18
+
19
+ Claude Code, Codex, any terminal agent is supported -- use your existing subscriptions, no extra charge.
20
+
21
+ It runs locally on your machine, so agents work while it is on and awake. Billion's plan and memory live in a git repo, so it picks up where it left off after a restart. Every worker is a real terminal you can open and type into, from your phone too ([remote access](docs/REMOTE.md)).
22
+
14
23
  ## Quick Start
15
24
 
16
25
  ```bash
@@ -29,15 +38,30 @@ npm start
29
38
 
30
39
  Open [http://localhost:7007](http://localhost:7007). Click **+ Job** to queue work on the board, or **+ Agent** to start one by hand -- preset buttons (Claude Code, Codex, Gemini, Bash; PowerShell on a Windows server) fill in the command, or type your own under Advanced. Needs Node.js 20.12+ and Git ([full requirements](#requirements)).
31
40
 
32
- ## Highlights
41
+ ### Settings
42
+
43
+ It works with no configuration. To change something, create a settings file:
44
+
45
+ ```bash
46
+ npx @bill10/agent-007 init # writes ~/.agent-007/.env, every line commented out
47
+ ```
48
+
49
+ Uncomment what you want, then restart. The ones people change:
33
50
 
34
- - **A job board, not a babysitting job** -- Each card gets a fresh agent on its own worktree and branch. It moves To do -> In progress -> Review on its own and lands as a pull request (or a summary, for work that isn't code). Cards can also run on a cron schedule.
35
- - **Billion, the one agent you talk to** -- Give it a mission and it plans, posts cards, reviews what comes back, merges PRs and answers its workers' permission requests. It comes to you only for money, access, anything irreversible and real forks in direction.
36
- - **Your agents, your subscriptions** -- Claude Code, Codex, any terminal agent is supported -- use your existing subscriptions, no extra charge.
37
- - **See everything at a glance** -- Every agent gets a desk in the pixel office: facing the screen while it works, turning to face you when it needs you. One window for every repo, with live terminals, a file explorer and inline diffs.
38
- - **Agents that talk to each other** -- Tell one to "add that to the job board" or "ask Viper what it changed", and it does it over MCP.
51
+ | Setting | What it does |
52
+ |---------|--------------|
53
+ | `BILLION=0` | Turns off Billion, the always-on agent |
54
+ | `HOST=0.0.0.0` + `ALLOWED_ORIGINS=<tailnet name>` | Reach it from your phone or another machine (behind Tailscale only, see [docs/REMOTE.md](docs/REMOTE.md)) |
55
+ | `CLAUDE_PERMISSION_MODE` | Mode Claude Code agents start in, e.g. `bypassPermissions` |
56
+ | `CODEX_PERMISSION_MODE` | The same for Codex |
57
+ | `TRUST_BOARD_WORKTREES=0` | Keeps Claude Code's folder-trust prompt for job board workers |
58
+ | `PORT` | Port to listen on (default `7007`) |
39
59
 
40
- Everything else -- scheduled jobs, phone layout, voice input, themes, and the details and caveats of each feature -- is in [docs/FEATURES.md](docs/FEATURES.md).
60
+ Highest wins: command-line flags (`--port`), then environment variables, then
61
+ a `.env` in the directory you start it from, then `~/.agent-007/.env`. The full
62
+ list is in [Configuration](#configuration) and `--help`.
63
+
64
+ The details and caveats of every feature -- phone layout, voice input, themes and more -- are in [docs/FEATURES.md](docs/FEATURES.md).
41
65
 
42
66
  ## Keyboard Shortcuts
43
67
 
@@ -71,20 +95,15 @@ The job board reuses that same machinery: a dispatched job is an ordinary agent,
71
95
 
72
96
  ## Configuration
73
97
 
74
- Configure via environment variables, either inline or in a `.env` file. On
75
- startup `npm start` auto-loads `.env` if present (via Node's built-in
76
- `--env-file-if-exists`), and `npx @bill10/agent-007` loads a `.env` in the
77
- directory you run it from; variables already set in your environment win over
78
- the file. `npx @bill10/agent-007 --port 8080` overrides `PORT`, and
79
- `npx @bill10/agent-007 --help` lists the options. Everything the app saves
80
- lives in `~/.agent-007`. Copy the template to get going:
81
-
82
- ```bash
83
- cp .env.example .env # then edit; .env is gitignored
84
- npm start
85
- ```
86
-
87
- Or set them inline:
98
+ Settings are environment variables. Set them inline, in `~/.agent-007/.env`
99
+ (`npx @bill10/agent-007 init` creates it from [`.env.example`](.env.example),
100
+ every setting explained and commented out), or in a `.env` in the directory you
101
+ start from. `npx @bill10/agent-007`, a global `agent-007` and `npm start` in a
102
+ clone all read both files, and the startup log names the ones it loaded. Highest
103
+ wins: flags (`--port 8080` overrides `PORT`), then the environment, then
104
+ `./.env`, then `~/.agent-007/.env`. `--help` lists them all. Everything the app
105
+ saves lives in `~/.agent-007` (`AGENT007_CONFIG_DIR` moves it, and the settings
106
+ file with it).
88
107
 
89
108
  ```bash
90
109
  PORT=8080 npm start # Custom port (default: 7007)
@@ -164,6 +183,7 @@ Agent 007 runs on macOS, Linux, and Windows -- spawning agents, adding repos, an
164
183
  server.js Entry point + orchestrators (createSession, killSession)
165
184
  server/
166
185
  state.js Shared mutable state (sessions, orphans, pools, config)
186
+ settings.js Config dir and the settings files (./.env, ~/.agent-007/.env)
167
187
  config.js Config persistence (load, save, crash recovery)
168
188
  direct-run.js Entry-point detection (symlink/space-safe `npm start` guard)
169
189
  git.js Git operations (worktree, file tree, diff)
package/VERSION CHANGED
@@ -1 +1 @@
1
- 0.6.3.0
1
+ 0.6.4.1
package/bin/agent-007.js CHANGED
@@ -1,34 +1,55 @@
1
1
  #!/usr/bin/env node
2
- // agent-007 — the command `npx @bill10/agent-007` (or a global install) runs.
2
+ // agent-007 — the command `npx @bill10/agent-007` (or a global install, or
3
+ // `npm start` in a clone) runs.
3
4
  //
4
- // Loads ./.env from the current directory when there is one (the same file
5
- // `npm start` reads in a clone; variables already in the environment win),
6
- // applies --port over both, then starts server.js. Everything the server reads
7
- // or writes resolves from the package or from ~/.agent-007, so it runs the
8
- // same from node_modules as from a clone.
5
+ // Loads settings (server/settings.js: ./.env, then ~/.agent-007/.env; the real
6
+ // environment and --port win over both), then starts server.js. Everything the
7
+ // server reads or writes resolves from the package or from ~/.agent-007, so it
8
+ // runs the same from node_modules as from a clone.
9
9
 
10
- import { existsSync, readFileSync } from 'fs';
11
- import { resolve } from 'path';
10
+ import { mkdirSync, readFileSync, writeFileSync } from 'fs';
11
+ import { basename, join } from 'path';
12
12
  import { fileURLToPath } from 'url';
13
13
  import { parseArgs } from 'util';
14
+ import { configDir, loadSettings, settingsLine } from '../server/settings.js';
14
15
 
16
+ // Each .env.example setting has a line here; test/cli.test.js checks that.
15
17
  const HELP = `Usage: agent-007 [--port <n>]
18
+ agent-007 init
16
19
  agent-007 adduser "Display Name"
17
20
 
18
21
  Starts Agent 007 at http://localhost:7007 (or --port).
19
22
 
23
+ Commands:
24
+ init Create ~/.agent-007/.env, a commented settings template
25
+ adduser Create a login user and turn on login for the server
26
+
20
27
  Options:
21
28
  -p, --port <n> Port to listen on (overrides PORT)
22
29
  -h, --help Show this help
23
30
  -v, --version Print the version
24
31
 
25
- Settings come from environment variables, or a .env file in the current
26
- directory: PORT, HOST, ALLOWED_ORIGINS, CLAUDE_PERMISSION_MODE,
27
- CODEX_PERMISSION_MODE, AGENT_MESSAGING, BILLION, BILLION_DIR.
28
- See https://github.com/bill10/agent-007#configuration
32
+ Settings (default in brackets):
33
+ PORT Port to listen on [7007]
34
+ HOST Interface to bind; 0.0.0.0 to reach it from a phone
35
+ or over Tailscale [127.0.0.1]
36
+ ALLOWED_ORIGINS Extra hostnames the browser may use, comma-separated,
37
+ e.g. your tailnet name [none]
38
+ CLAUDE_PERMISSION_MODE Mode Claude Code agents start in: auto, acceptEdits,
39
+ bypassPermissions, manual, dontAsk, plan [Claude's own]
40
+ CODEX_PERMISSION_MODE The same for Codex agents [Codex's own]
41
+ AGENT_MESSAGING open = any agent may message any other [guarded]
42
+ BILLION 0 turns off Billion, the always-on agent [on]
43
+ BILLION_DIR Billion's folder and repo [~/.agent-007/billion]
44
+ TRUST_BOARD_WORKTREES 0 keeps Claude Code's folder-trust prompt for job
45
+ board workers [on]
46
+
47
+ Set them in the environment, in ~/.agent-007/.env (\`agent-007 init\` writes it,
48
+ every setting explained and commented out), or in a .env in the current
49
+ directory. Highest first: flags, environment, ./.env, ~/.agent-007/.env.
29
50
 
30
51
  State lives in ~/.agent-007 (AGENT007_CONFIG_DIR to move it).
31
- \`adduser\` creates a login user and turns on login for the server.
52
+ See https://github.com/bill10/agent-007#settings
32
53
  `;
33
54
 
34
55
  let parsed;
@@ -56,30 +77,49 @@ if (values.version) {
56
77
  process.exit(0);
57
78
  }
58
79
 
59
- // Said out loud: run from inside another project, its .env (a HOST=0.0.0.0,
60
- // say) would otherwise change this server without a word.
61
- if (existsSync('.env')) {
62
- process.loadEnvFile('.env');
63
- console.error(` Loaded settings from ${resolve('.env')}`);
80
+ if (values.port !== undefined) {
81
+ const port = Number(values.port);
82
+ if (!Number.isInteger(port) || port < 1 || port > 65535) {
83
+ console.error(`--port must be a whole number from 1 to 65535, not "${values.port}"`);
84
+ process.exit(2);
85
+ }
86
+ // Before the files load, which never overwrite a variable already set.
87
+ process.env.PORT = String(port);
64
88
  }
89
+ if (positionals.length && !['init', 'adduser'].includes(positionals[0])) {
90
+ console.error(`Unknown command: ${positionals[0]}\n\n${HELP}`);
91
+ process.exit(2);
92
+ }
93
+
94
+ const settingsFiles = loadSettings();
65
95
 
66
- if (positionals[0] === 'adduser') {
96
+ // Said the way it was launched, so it can be pasted back.
97
+ function initCommand() {
98
+ if (process.env.npm_command === 'exec') return 'npx @bill10/agent-007 init';
99
+ if (process.env.npm_lifecycle_event) return 'npm start -- init';
100
+ if (basename(process.argv[1] || '') === 'agent-007') return 'agent-007 init';
101
+ return 'npx @bill10/agent-007 init';
102
+ }
103
+
104
+ if (positionals[0] === 'init') {
105
+ const file = join(configDir(), '.env');
106
+ mkdirSync(configDir(), { recursive: true });
107
+ try {
108
+ // wx: never overwrite someone's settings.
109
+ writeFileSync(file, readFileSync(new URL('../.env.example', import.meta.url)), { flag: 'wx' });
110
+ console.log(`Created ${file}\nEvery setting in it is commented out, so nothing changes yet. Edit it, then restart agent-007.`);
111
+ } catch (err) {
112
+ if (err.code !== 'EEXIST') throw err;
113
+ console.log(`${file} already exists, left as it is. Edit it, then restart agent-007.`);
114
+ }
115
+ } else if (positionals[0] === 'adduser') {
67
116
  // adduser.js reads the display name from argv[2..].
68
117
  process.argv = [process.argv[0], fileURLToPath(new URL('./adduser.js', import.meta.url)), ...positionals.slice(1)];
69
118
  await import('./adduser.js');
70
119
  } else {
71
- if (positionals.length) {
72
- console.error(`Unknown command: ${positionals[0]}\n\n${HELP}`);
73
- process.exit(2);
74
- }
75
- if (values.port !== undefined) {
76
- const port = Number(values.port);
77
- if (!Number.isInteger(port) || port < 1 || port > 65535) {
78
- console.error(`--port must be a whole number from 1 to 65535, not "${values.port}"`);
79
- process.exit(2);
80
- }
81
- process.env.PORT = String(port);
82
- }
120
+ // Said out loud: run from inside another project, its .env (a HOST=0.0.0.0,
121
+ // say) would otherwise change this server without a word.
122
+ console.log(` ${settingsLine(settingsFiles, initCommand())}`);
83
123
  // Imported only now: server/state.js reads PORT when it loads.
84
124
  const { startup, gracefulShutdown } = await import('../server.js');
85
125
  startup();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bill10/agent-007",
3
- "version": "0.6.3000",
4
- "description": "Self-hosted office where a manager agent posts jobs to Claude Code and Codex workers in parallel git worktrees, then reviews and merges their PRs. Cron jobs, agent-to-agent messaging, pixel-art status view.",
3
+ "version": "0.6.4001",
4
+ "description": "From web terminals for your coding agents to a self-running agent company: Claude Code and Codex in parallel git worktrees, a job board they pick work from, and one agent that runs the board from a goal.",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
@@ -14,6 +14,7 @@
14
14
  "agent-007": "bin/agent-007.js"
15
15
  },
16
16
  "files": [
17
+ ".env.example",
17
18
  "bin/",
18
19
  "lib/",
19
20
  "public/",
@@ -26,9 +27,9 @@
26
27
  "node": ">=20.12"
27
28
  },
28
29
  "scripts": {
29
- "start": "node --env-file-if-exists=.env server.js",
30
- "dev": "node --env-file-if-exists=.env --watch server.js",
31
- "adduser": "node --env-file-if-exists=.env bin/adduser.js",
30
+ "start": "node bin/agent-007.js",
31
+ "dev": "node --watch bin/agent-007.js",
32
+ "adduser": "node bin/agent-007.js adduser",
32
33
  "test": "vitest run",
33
34
  "test:watch": "vitest"
34
35
  },
@@ -51,6 +52,7 @@
51
52
  "coding-agents",
52
53
  "ai-agents",
53
54
  "agent-orchestration",
55
+ "autonomous-agents",
54
56
  "parallel-agents",
55
57
  "git-worktree",
56
58
  "job-board",
@@ -0,0 +1,42 @@
1
+ // Where settings come from, for bin/agent-007.js (which `npm start` runs too).
2
+ // Imports nothing from state.js: that module reads PORT and HOST when it loads,
3
+ // so it may only be imported once the files below are in process.env.
4
+
5
+ import { existsSync } from 'fs';
6
+ import { homedir } from 'os';
7
+ import { join, resolve } from 'path';
8
+
9
+ // Everything the app saves, and the settings file `agent-007 init` writes.
10
+ export function configDir(env = process.env) {
11
+ return env.AGENT007_CONFIG_DIR || join(homedir(), '.agent-007');
12
+ }
13
+
14
+ // ./.env first, then <config dir>/.env. process.loadEnvFile never overwrites a
15
+ // variable that is already set, so the real environment beats ./.env, and
16
+ // ./.env beats the config-dir file. Returns the files it loaded.
17
+ export function loadSettings() {
18
+ const files = [];
19
+ if (existsSync('.env')) {
20
+ process.loadEnvFile('.env');
21
+ files.push(resolve('.env'));
22
+ }
23
+ // Resolved after ./.env, which may set AGENT007_CONFIG_DIR.
24
+ const shared = join(configDir(), '.env');
25
+ if (existsSync(shared) && !files.includes(resolve(shared))) {
26
+ process.loadEnvFile(shared);
27
+ files.push(resolve(shared));
28
+ }
29
+ return files;
30
+ }
31
+
32
+ const tilde = (p) => {
33
+ const home = homedir();
34
+ return p.startsWith(home + '/') || p.startsWith(home + '\\') ? '~' + p.slice(home.length) : p;
35
+ };
36
+
37
+ // One startup line saying where settings came from, or how to make the file.
38
+ export function settingsLine(files, initCommand) {
39
+ return files.length
40
+ ? `Settings: ${files.map(tilde).join(', ')}`
41
+ : `Settings: defaults (run \`${initCommand}\` to create ${tilde(join(configDir(), '.env'))})`;
42
+ }
package/server/state.js CHANGED
@@ -1,8 +1,8 @@
1
1
  // Shared mutable state — single owner for all Maps, Sets, and singletons.
2
2
  // All other modules import from here. No circular deps.
3
3
 
4
- import { homedir } from 'os';
5
4
  import { join } from 'path';
5
+ import { configDir } from './settings.js';
6
6
  import {
7
7
  createCodenamePool, createCocktailPool, createColorCycler,
8
8
  } from '../lib/helpers.js';
@@ -69,7 +69,7 @@ export const GIT_USER_TIMEOUT = 30000;
69
69
  // (mirrors AGENT007_WORKTREE_DIR and AGENT007_USERS_PATH, both set by test/setup.js).
70
70
  // saveConfig() rewrites this file wholesale from in-memory state, so a test that
71
71
  // touches config would otherwise silently destroy the real repo/orphan list.
72
- export const CONFIG_DIR = process.env.AGENT007_CONFIG_DIR || join(homedir(), '.agent-007');
72
+ export const CONFIG_DIR = configDir();
73
73
  // Overridable so tests never create worktrees in the developer's live
74
74
  // ~/.agent-007/worktrees (mirrors AGENT007_USERS_PATH, which test/setup.js uses).
75
75
  export const WORKTREE_DIR = process.env.AGENT007_WORKTREE_DIR || join(CONFIG_DIR, 'worktrees');