agenter-app 1.1.0 → 1.3.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 CHANGED
@@ -1,181 +1,219 @@
1
1
  # Agenter
2
2
 
3
- One dashboard for every Claude Code and Codex session on this machine, across all your VS Code windows.
3
+ **One local dashboard to watch, approve and steer every Claude Code and Codex session on your machine.**
4
4
 
5
- - See every session live: what it's doing, which command it's running, and the full conversation.
6
- - Approve or deny permission prompts (PowerShell/Bash commands, file edits, web fetches…) from one place.
7
- - Answer Claude's multiple-choice questions.
8
- - **Message any session at any time**, drive it from VS Code or from Agenter, or start a new one.
9
- - Get a sound and a desktop notification whenever a session needs you.
5
+ [![npm](https://img.shields.io/npm/v/agenter-app)](https://www.npmjs.com/package/agenter-app)
6
+ [![license: MIT](https://img.shields.io/npm/l/agenter-app)](LICENSE)
7
+ ![dependencies: none](https://img.shields.io/badge/dependencies-none-brightgreen)
10
8
 
11
- No dependencies, just Node 18+. Built for Windows.
9
+ If you run several agents across several VS Code windows, you spend your day hunting for the one that's waiting on you. Agenter puts them all in one inbox, so you can answer from one place and keep working.
12
10
 
13
- ## Install
11
+ - **See everything live:** what each session is doing, which command it's running, and the full conversation.
12
+ - **Approve from one place:** allow or deny commands, file edits and web fetches, with a diff of what will change.
13
+ - **Answer and steer:** reply to questions, or message any session at any time, even while it's working.
14
+ - **Run sessions without VS Code:** start a new one, or continue a closed one, right from the dashboard.
15
+ - **Never miss a prompt:** get a sound and a desktop notification when a session needs you.
16
+ - **Watch your limits:** see your Claude and Codex plan usage, with alerts at 80% and 95%.
17
+ - **Work next to your app:** preview it, open a terminal, and edit project memory beside the conversation.
18
+
19
+ Everything runs on your machine, on `127.0.0.1`. It needs Node 18+ and installs no dependencies.
20
+
21
+ ## Quick start
14
22
 
15
23
  ```powershell
16
24
  npm install -g agenter-app
17
- agenter install # adds hooks to ~/.claude/settings.json (a backup is made) + Desktop/Start Menu shortcuts
25
+ agenter install
26
+ agenter
18
27
  ```
19
28
 
20
- Then **reload each VS Code window** (or start new Claude sessions). Hooks are read when a session starts.
29
+ `agenter install` adds hooks to `~/.claude/settings.json` (a backup is made first) and creates Desktop and Start Menu shortcuts. **Then reload each VS Code window**, because Claude Code reads hooks when a session starts.
21
30
 
22
- Skipped `agenter install`? Running `agenter` notices the hooks are missing and offers to set them up.
31
+ Install with `-g`, not `npx`: the hooks point at the installed files, so those files need to stay put.
23
32
 
24
- Install it globally (`-g`), not with `npx`: the hooks point at the installed files, so they need to stay put.
25
- If you move Node (a new Node version manager install, say), run `agenter install` again; it replaces the old hooks.
33
+ Sessions not showing up? Run `agenter doctor`.
26
34
 
27
- From a clone instead: `npm run install-hooks` and `npm run shortcuts`, then `npm run open`.
35
+ ## Commands
28
36
 
29
- ## Running it
37
+ | Command | What it does |
38
+ |---|---|
39
+ | `agenter` | Opens the dashboard, starting the server if needed. Offers setup if the hooks are missing. |
40
+ | `agenter install` | Adds the hooks and shortcuts. Run it again if you move Node. |
41
+ | `agenter update` | Updates to the latest version from npm. |
42
+ | `agenter doctor` | Checks the hooks, port, Claude Code and login, and says how to fix what's wrong. |
43
+ | `agenter start` | Runs the server in the foreground with logs. |
44
+ | `agenter shortcuts` | Recreates the shortcuts, and starts Agenter in the tray when you log in. |
45
+ | `agenter uninstall` | Removes the hooks and shortcuts. Follow it with `npm uninstall -g agenter-app`. |
46
+ | `agenter version` | Prints the version. |
30
47
 
31
- You mostly don't have to: **the server starts itself** the first time any Claude session fires a hook,
32
- and keeps running in the background, with an icon in the Windows system tray. Closing the window leaves
33
- it running there. Click the icon to open the dashboard.
48
+ ## Running it
34
49
 
35
- **To stop it, right-click the tray icon and choose Quit Agenter.** That closes the windows and stops the
36
- server. Anything waiting goes back to VS Code, and turns Agenter is running are stopped. Hooks won't restart
37
- it; it stays off until you open Agenter again.
50
+ You mostly don't have to. **The server starts itself** the first time a Claude session fires a hook, then keeps running with an icon in the Windows system tray. Closing the window leaves it running; click the icon to reopen the dashboard.
38
51
 
39
- | Way | What it does |
40
- |---|---|
41
- | **Agenter shortcut** (Desktop / Start Menu / taskbar) | Opens the dashboard in its own app window and starts the server if needed. No console. |
42
- | `agenter` | Same thing, from a terminal. |
43
- | `agenter start` | Runs the server in the foreground with logs (handy for debugging). |
44
- | `agenter doctor` | Checks the hooks, port, Claude Code and login, and says how to fix anything that's wrong. Start here if sessions don't show up. |
45
- | `agenter shortcuts` | Creates the shortcuts, and starts Agenter in the system tray when you log in to Windows. |
52
+ **To stop it, right-click the tray icon and choose Quit Agenter.** Anything waiting goes back to VS Code, and turns Agenter is running are stopped. It stays off until you open Agenter again.
46
53
 
47
- Set `AGENTER_AUTOSTART=0` to stop hooks from starting the server, or `AGENTER_TRAY=0` to hide the tray icon.
48
- Dashboard settings are kept in `~/.agenter/settings.json`.
54
+ ## Features
49
55
 
50
- To remove everything: `agenter uninstall`, then `npm uninstall -g agenter-app`.
56
+ ### Inbox and sessions
51
57
 
52
- ## Sessions you see
58
+ The inbox collects everything waiting on you, across all sessions. The session list has two groups:
53
59
 
54
- - **Live**: sessions talking to Agenter right now (working, waiting for you, or idle).
55
- - **Earlier today**: every session you used today, found from Claude's transcripts in `~/.claude/projects`,
56
- including ones started before Agenter was running and ones you've closed. You can read their full
57
- conversation and click **Open in VS Code** to jump to the project. When you continue one in VS Code,
58
- it moves to Live.
60
+ - **Live:** sessions talking to Agenter right now.
61
+ - **Earlier today:** every session you used today, found from Claude's transcripts in `~/.claude/projects`, including ones you've closed or that started before Agenter was running.
59
62
 
60
63
  **Clear earlier** hides those until they become active again.
61
64
 
62
- ## Steer from Agenter
65
+ ### Steering
63
66
 
64
- Every live session has a message box at the bottom, so you never have to switch to VS Code to tell Claude something.
67
+ Every live session has a message box. What happens to your message depends on the session:
65
68
 
66
69
  | Claude is… | Your message |
67
70
  |---|---|
68
- | Waiting for your reply (finished turn) | Goes in right away and Claude continues. |
69
- | Working | Is queued and reaches Claude after its current step (the next tool call finishes). If the turn ends first, Claude continues with it. |
70
- | Idle, driven from VS Code | Is queued and goes along with your next prompt from VS Code. Claude Code can't be woken up from outside. |
71
- | Idle, driven from Agenter | Starts a new turn right away, run by Agenter. |
71
+ | Waiting for your reply | Goes in right away, and Claude continues. |
72
+ | Working | Reaches Claude after its current step. If the turn ends first, Claude continues with it. |
73
+ | Idle, driven from VS Code | Goes with your next prompt from VS Code. Claude Code can't be woken from outside. |
74
+ | Idle, driven from Agenter | Starts a new turn right away. |
72
75
 
73
76
  Queued messages show above the box, with **Cancel**.
74
77
 
78
+ **Images:** paste them into any message box, drop them on it, or use the image button. This works in the steer box, in the reply after a finished turn, and in **New session**. You can attach up to 8 PNG, JPEG, GIF or WebP images per message, each under 10 MB. Agenter saves them to `~/.agenter/uploads` and adds their paths to your message, and Claude or Codex opens them from there. Uploads older than 30 days are removed when Agenter starts.
79
+
75
80
  ### Drive from VS Code or Agenter
76
81
 
77
- Each session has a **Drive from** switch in its header.
82
+ Each session has a **Drive from** switch:
83
+
84
+ - **VS Code** (default): Claude runs in the VS Code panel, and Agenter answers inside its turns.
85
+ - **Agenter**: Agenter runs the turns itself with `claude -p --resume <session>`, using the Claude Code that ships with the VS Code extension. Nothing waits on VS Code, closed sessions can be continued, and **Stop** ends a turn along with everything it started.
86
+
87
+ VS Code's panel doesn't show Agenter's turns until you reopen the session there, so don't type in that panel for the same session meanwhile.
88
+
89
+ ### New sessions
90
+
91
+ **New session** opens the Windows folder window. Choose the project folder (cancelling it cancels New session), type the first message, and it runs in Agenter, with Claude or Codex. You can open a Claude session in VS Code later from the Claude panel's session history.
92
+
93
+ If the folder window doesn't come to the front, look for **Agenter: choose a folder** on the taskbar.
94
+
95
+ ### Memory
96
+
97
+ The **⋯** menu in a session's header has two tools for carrying knowledge between chats:
98
+
99
+ - **Memory** shows and edits the files the agent reads for this project: `CLAUDE.md` (project and personal) and the notes Claude keeps in its memory folder, or `AGENTS.md` for Codex. Saving is refused if the file changed since you opened it.
100
+ - **Carry to a new chat** starts a new Claude or Codex session in the same folder, with the recent conversation (up to about 12,000 characters) attached after your first message. It's an excerpt of what was said, not an AI summary, and tool output isn't included.
101
+
102
+ ### Preview
103
+
104
+ **Preview** shows your running app next to the conversation.
105
+
106
+ - Agenter finds web servers started from the project's folder (`npm run dev`, Vite, Next and so on). Other servers it finds appear as chips you can switch between.
107
+ - Or type an address: `5173` is short for `http://localhost:5173`. Each chat remembers its own, and new chats in the project start from the last one used.
108
+ - Drag the divider to resize, or double-click it to reset.
109
+ - Some apps refuse to appear inside another page. If the preview stays blank, open it in your browser with ↗.
110
+
111
+ ### Terminal
112
+
113
+ **Terminal** (or <kbd>`</kbd>) opens PowerShell below the conversation, in the session's project folder, or your home folder from the inbox. Each folder keeps its own shell, so `cd` and variables carry over between commands.
78
114
 
79
- - **VS Code** (default): Claude runs in the VS Code panel. Agenter answers inside its turns, so while you
80
- reply from Agenter the VS Code panel shows its spinner, and an idle session waits for VS Code.
81
- - **Agenter**: Agenter runs the turns itself with the Claude Code that ships with the VS Code extension
82
- (`claude -p --resume <session>`), in the same session. Nothing ever waits on VS Code, idle and closed
83
- sessions can be continued, permission prompts and questions come to Agenter and wait there for your answer
84
- while a dashboard is open, and **Stop** ends a running turn and everything it started.
85
- The VS Code panel doesn't show these turns until you reopen the session there (Claude panel → session
86
- history), and don't type in VS Code's panel for that session at the same time.
115
+ It's a command shell, not a full terminal emulator. Output streams live, and you can type a line to answer a program's prompt, but programs that take over the screen (vim, an interactive `claude` session) won't work. **Stop** ends the shell along with what's running. Your PowerShell profile isn't loaded.
87
116
 
88
- If Claude Code isn't found automatically, set `AGENTER_CLAUDE` to the path of `claude.exe`.
117
+ ### Codex
89
118
 
90
- ### Start a session in Agenter
119
+ Agenter also shows your [Codex](https://developers.openai.com/codex) sessions from the VS Code extension, desktop app and CLI, read from Codex's own logs in `~/.codex/sessions`. Nothing is sent anywhere, and Codex's settings aren't changed.
91
120
 
92
- **New session** (sidebar) starts a Claude session without VS Code: pick the project folder (folders you've
93
- used are suggested), type the first message, and it runs in Agenter, driven from Agenter. You can still open
94
- it in VS Code later from the Claude panel's session history.
121
+ - **Codex app** (default): your message is queued with `codex queue` and runs as the next turn when the session is open in Codex.
122
+ - **Agenter**: turns run here with `codex exec resume`, sandboxed to the project folder (`workspace-write`, no network), since nobody is there to approve anything more.
123
+ - **Plan usage**: your 5-hour and weekly limits, as of your last Codex turn.
95
124
 
96
- ## Codex
125
+ ### Plan usage
97
126
 
98
- Agenter also shows your [Codex](https://developers.openai.com/codex) sessions (VS Code extension, desktop app
99
- and CLI), read from Codex's own session logs in `~/.codex/sessions`. Nothing is sent anywhere and Codex's
100
- settings aren't changed.
127
+ The sidebar shows your Claude **5-hour** and **weekly** limits with their reset times, refreshed every 2 minutes. It uses the login Claude Code already saved in `~/.claude/.credentials.json`, the same way `/usage` does. The token only goes to `api.anthropic.com`, and Agenter never refreshes it. This endpoint isn't officially documented, so it could change.
101
128
 
102
- - **Sessions**: today's Codex sessions appear in the list with a **Codex** tag: the conversation, the
103
- commands it ran, and whether it's working right now. Codex's helper threads (like its auto-review) are left out.
104
- - **Steering**: each Codex session has a **Drive from** switch and a message box.
105
- - **Codex app** (default): your message is queued in Codex (`codex queue`) and runs as the session's next
106
- turn when it's open in Codex (VS Code extension or desktop app).
107
- - **Agenter**: Agenter runs the turns itself (`codex exec resume`), sandboxed to the project folder
108
- (`workspace-write`, no network), since nobody is there to approve anything more. A message sent while a
109
- turn is running goes next. **Stop** ends a running turn.
110
- - **New session** can start a Codex session too (pick **Codex** in the dialog). It runs in Agenter and also
111
- shows up in the Codex app.
129
+ ### Themes
112
130
 
113
- Agenter uses the Codex that ships with the Codex VS Code extension, or `codex` on your PATH. Set
114
- `AGENTER_CODEX_BIN` to point at another `codex.exe`.
115
- - **Plan usage**: the sidebar shows your Codex **5-hour** and **weekly** limits with reset times, with the
116
- same alerts at 80% and 95%. Codex records these with each turn, so they're as of your last Codex turn
117
- (a window that has reset since shows 0%).
131
+ Choose light, dark or system, plus 14 accent colors, under **Settings**. Collapse the sidebar to a narrow rail with the button next to the logo.
118
132
 
119
- Set `CODEX_HOME` if Codex keeps its data somewhere else, or `AGENTER_CODEX=0` to turn this off.
133
+ ## Keyboard shortcuts
120
134
 
121
- ## Preview the app side by side
135
+ | Key | Action |
136
+ |---|---|
137
+ | <kbd>A</kbd> | Allow the current permission request. Off for commands that look destructive. |
138
+ | <kbd>Shift</kbd>+<kbd>A</kbd> | Always allow it |
139
+ | <kbd>D</kbd> | Deny it |
140
+ | <kbd>R</kbd> | Jump to the reply box |
141
+ | <kbd>J</kbd> / <kbd>K</kbd> | Next / previous session |
142
+ | <kbd>N</kbd> | Next session that needs you |
143
+ | <kbd>I</kbd> | Open the inbox |
144
+ | <kbd>/</kbd> | Filter sessions |
145
+ | <kbd>`</kbd> | Show or hide the terminal |
146
+ | <kbd>B</kbd> | Collapse or expand the sidebar |
147
+ | <kbd>T</kbd> | Switch theme |
148
+ | <kbd>?</kbd> | Show all shortcuts |
149
+
150
+ ## Updating
151
+
152
+ Every `agenter` command tells you when a newer version is on npm, and so does a bar in the dashboard.
153
+
154
+ - **In the dashboard:** click **Update now**, then **Restart** when it finishes. Restarting stops any turns Agenter is running and hands anything waiting back to VS Code, so you choose when.
155
+ - **In a terminal:** run `agenter update`, then restart from the dashboard, or Quit from the tray and run `agenter`.
156
+
157
+ Coming from 1.1.0 or earlier? Those versions don't have this command. Quit from the tray, then run `npm install -g agenter-app@latest` once.
122
158
 
123
- Click **Preview** in a session's header to show the app you're building next to the conversation, so you
124
- can steer Claude and watch the result in one window.
159
+ ## Configuration
125
160
 
126
- - Agenter finds web servers started from that project's folder (`npm run dev`, Vite, Next, etc.) and shows
127
- the first page it finds. Other servers it finds appear as chips you can switch between.
128
- Start the dev server later and it shows up within a few seconds.
129
- - Or type any address in the bar (`5173` is short for `http://localhost:5173`). It's remembered per project;
130
- clear it to go back to the detected app.
131
- - Drag the divider to resize (double-click to reset). **Open** (↗) opens the app in your normal browser.
132
- - Some apps refuse to be shown inside another page. If the preview stays blank, use **Open**.
133
- - Finding apps works on Windows. Elsewhere, type the address.
161
+ Dashboard settings are kept in `~/.agenter/settings.json`. Everything else is set with environment variables:
162
+
163
+ | Variable | Effect |
164
+ |---|---|
165
+ | `AGENTER_PORT` | Port to use instead of 4317. Set it for both the server and your environment. |
166
+ | `AGENTER_AUTOSTART=0` | Stops hooks from starting the server. |
167
+ | `AGENTER_TRAY=0` | Hides the tray icon. |
168
+ | `AGENTER_TERMINAL=0` | Turns off the terminal panel. |
169
+ | `AGENTER_UPDATE_CHECK=0` | Turns off update checks. |
170
+ | `AGENTER_USAGE=0` | Turns off Claude plan usage. |
171
+ | `AGENTER_CODEX=0` | Turns off Codex support. |
172
+ | `AGENTER_CLAUDE` | Path to `claude.exe`, if it isn't found automatically. |
173
+ | `AGENTER_CODEX_BIN` | Path to `codex.exe`, if it isn't found automatically. |
174
+ | `CODEX_HOME` | Where Codex keeps its data, if not `~/.codex`. |
134
175
 
135
176
  ## How it works
136
177
 
137
- Claude Code runs [hooks](https://docs.claude.com/en/docs/claude-code/hooks) on events such as
138
- "about to use a tool", "needs permission" and "finished its turn". Each hook runs `hook.js`, which
139
- forwards the event to the local server (`server.js`) and prints the server's answer back to Claude.
178
+ Claude Code runs [hooks](https://docs.claude.com/en/docs/claude-code/hooks) on events like "needs permission" and "finished its turn". Each hook runs `hook.js`, which forwards the event to the local server and prints the server's answer back to Claude.
140
179
 
141
180
  | Event | What Agenter does |
142
181
  |---|---|
143
- | `PermissionRequest` | Holds the prompt until you click **Allow / Always allow / Deny / Answer in VS Code**. A deny reason is passed to Claude, so you can say "no, do X instead". |
144
- | `PreToolUse` (AskUserQuestion) | Shows the question with its options; your answers go back to Claude. |
145
- | `Stop` | If **Hold for reply** is on, the session waits for your next instruction. **Send** continues it; **Release to VS Code** lets it stop normally. |
146
- | Everything else | Activity feed and status only (never blocks). |
147
-
148
- ### Safety valves
149
-
150
- - **Server not running?** Hooks exit instantly and Claude behaves as if Agenter didn't exist.
151
- - **Working in VS Code?** Sessions are only held while the dashboard window has focus. Switch to VS Code
152
- and nothing new gets held, and anything already waiting goes back to VS Code after 5 seconds.
153
- - **Dashboard closed?** Same, after 15 seconds.
154
- - **Interrupt in VS Code** while something is waiting? The dashboard item disappears.
155
- - The server only listens on `127.0.0.1`, rejects requests from other websites, and requires the token in
156
- `~/.agenter/token`. Hooks read it from there, and the Agenter shortcut and `agenter` sign the dashboard in,
157
- so open it that way rather than from a bookmark on a new browser. The token keeps out other Windows users
158
- and anything that can only make HTTP requests. A program running as you can still read the file.
159
-
160
- ## Plan usage
161
-
162
- (Codex usage is covered under [Codex](#codex).) The sidebar shows how much of your Claude plan you've used: the **5-hour session** limit and the **weekly**
163
- limit, each with when it resets. You get an alert at 80% and 95%, and a banner in the inbox when the session
164
- limit is nearly used up. It refreshes every 2 minutes (or click ↻).
165
-
166
- It asks Anthropic using the login Claude Code already saved on this machine (`~/.claude/.credentials.json`),
167
- the same way Claude Code's `/usage` does. The token only goes to `api.anthropic.com`, and Agenter never
168
- refreshes it itself. This endpoint isn't officially documented, so it could change. Set `AGENTER_USAGE=0` to turn it off.
169
-
170
- ## Tips and limits
171
-
172
- - **Hold for reply** is the global default (sidebar) and can be turned off per session. While a session is
173
- held, its VS Code panel shows the "Clauding…" spinner. That's expected. To get it back in VS Code:
174
- click **Release to VS Code** in the dashboard, press **Esc** in VS Code, or just wait. Anything waiting is
175
- handed back automatically after **Hand back to VS Code after** (3 min by default, with a countdown on the card).
176
- - An idle session that has been *released* can't be woken up from the dashboard, because Claude Code only
177
- hands control to hooks at events. A message you send then waits for your next prompt from VS Code.
178
- - Answering `AskUserQuestion` from the dashboard depends on your Claude Code version accepting
179
- hook-supplied answers. If it doesn't, use **Answer in VS Code**.
180
- - Port: set `AGENTER_PORT` for both the server and your environment if 4317 is taken.
181
- - `npm start -- --no-open` skips opening the browser.
182
+ | `PermissionRequest` | Holds the prompt until you click **Allow**, **Always allow**, **Deny** or **Answer in VS Code**. A deny reason is passed to Claude. |
183
+ | `PreToolUse` (AskUserQuestion) | Shows the question and sends your answers back to Claude. |
184
+ | `Stop` | With **Hold for reply** on, the session waits for your next instruction. |
185
+ | Everything else | Updates the activity and status, and never blocks. |
186
+
187
+ ## Safety
188
+
189
+ Agenter is designed to get out of the way when you aren't using it:
190
+
191
+ - **Server not running?** Hooks exit instantly, and Claude behaves as if Agenter didn't exist.
192
+ - **Working in VS Code?** Sessions are only held while the dashboard has focus. Switch away and anything waiting goes back to VS Code after 5 seconds.
193
+ - **Dashboard closed?** The same, after 15 seconds.
194
+ - **Left something waiting?** It goes back to VS Code after 3 minutes by default, with a countdown on the card.
195
+
196
+ The server only listens on `127.0.0.1`, rejects requests from other websites, and requires the token in `~/.agenter/token`. That keeps out other Windows users and anything that can only make HTTP requests. **A program running as you can still read that file**, and anyone with the token can approve prompts and run commands in the terminal, so treat it like a password.
197
+
198
+ ## Limits
199
+
200
+ - **Windows is the supported platform.** The tray icon, shortcuts, terminal, folder window and app detection are Windows only.
201
+ - **An idle session driven from VS Code can't be woken.** Claude Code only hands control to hooks at events, so your message waits for your next prompt there. Switch to **Drive from Agenter** to avoid this.
202
+ - **VS Code shows a spinner while a session is held.** That's expected. Click **Release to VS Code**, press **Esc** in VS Code, or wait for the handback.
203
+ - **Answering questions** depends on your Claude Code version accepting hook-supplied answers. If it doesn't, use **Answer in VS Code**.
204
+
205
+ ## Development
206
+
207
+ ```powershell
208
+ git clone https://github.com/zxcoding123/agenter_app.git
209
+ cd agenter_app
210
+ npm run install-hooks
211
+ npm start
212
+ npm test
213
+ ```
214
+
215
+ `npm start -- --no-open` skips opening the browser, and `npm run shortcuts` creates the Windows shortcuts. A git clone updates with `git pull`, not `agenter update`.
216
+
217
+ ## License
218
+
219
+ [MIT](LICENSE) © Ahmad Pandaog Aquino
package/cli.js CHANGED
@@ -7,6 +7,7 @@ const path = require('node:path');
7
7
  const readline = require('node:readline/promises');
8
8
  const { spawnSync } = require('node:child_process');
9
9
  const { version } = require('./package.json');
10
+ const updates = require('./lib/update');
10
11
 
11
12
  const CLAUDE_SETTINGS = path.join(os.homedir(), '.claude', 'settings.json');
12
13
  // The same path install.js puts in the hook commands.
@@ -75,6 +76,43 @@ async function offerSetup() {
75
76
  console.log("OK. Run 'agenter install' whenever you want them.");
76
77
  }
77
78
 
79
+ // Printed to stderr so it never ends up in something like $(agenter version).
80
+ async function updateNotice() {
81
+ const line = updates.notice(version, await updates.checkLatest());
82
+ if (line) console.error(`${line}\n`);
83
+ }
84
+
85
+ // Replaces the installed copy with the latest from npm. A running server keeps the old code
86
+ // until it restarts, which is left to you: it would stop any turns it's running.
87
+ async function selfUpdate() {
88
+ if (!updates.canSelfUpdate()) {
89
+ console.log('This copy of Agenter is a git clone. Update it with: git pull');
90
+ return 1;
91
+ }
92
+ console.log('Checking npm for updates…');
93
+ const latest = await updates.checkLatest({ timeoutMs: 15000 });
94
+ if (!latest) {
95
+ console.error("Couldn't reach npm. Check your connection and try again.");
96
+ return 1;
97
+ }
98
+ if (!updates.isNewer(latest, version)) {
99
+ console.log(`Agenter ${version} is up to date.`);
100
+ return 0;
101
+ }
102
+ console.log(`Updating Agenter ${version} → ${latest}…`);
103
+ const npm = process.env.AGENTER_NPM || (isWindows ? 'npm.cmd' : 'npm');
104
+ // latest is checked to be a plain x.y.z, so it's safe through the shell .cmd files need.
105
+ const { status } = spawnSync(npm, ['install', '-g', `${updates.PACKAGE}@${latest}`], { stdio: 'inherit', shell: isWindows });
106
+ if (status !== 0) {
107
+ console.error(`The update failed. To try it by hand: npm install -g ${updates.PACKAGE}@latest`);
108
+ return status ?? 1;
109
+ }
110
+ // New versions can add hook events.
111
+ if (hookState() === 'ok') node('install.js');
112
+ console.log(`\nInstalled Agenter ${latest}. Restart Agenter to start using it: click Restart in the dashboard, or Quit from the tray and run agenter.`);
113
+ return 0;
114
+ }
115
+
78
116
  const help = `Agenter ${version}: one inbox for every Claude Code and Codex session.
79
117
 
80
118
  Usage: agenter [command]
@@ -84,6 +122,7 @@ Usage: agenter [command]
84
122
  install Add Agenter's hooks to ~/.claude/settings.json (backed up first)
85
123
  and, on Windows, Desktop and Start Menu shortcuts
86
124
  uninstall Remove the hooks and shortcuts
125
+ update Update Agenter to the latest version from npm
87
126
  doctor Check hooks, port, Claude Code and login, and say how to fix what's wrong
88
127
  start Run the server in the foreground with logs
89
128
  shortcuts (Re)create the Windows shortcuts, and start Agenter in the tray at login
@@ -93,6 +132,7 @@ After installing, reload your VS Code windows so Claude sessions pick up the hoo
93
132
 
94
133
  (async () => {
95
134
  let status = 0;
135
+ if (updates.enabled() && !['update', 'uninstall'].includes(cmd)) await updateNotice();
96
136
  switch (cmd) {
97
137
  case 'open':
98
138
  await offerSetup();
@@ -108,6 +148,9 @@ After installing, reload your VS Code windows so Claude sessions pick up the hoo
108
148
  case 'start':
109
149
  status = node('server.js', rest);
110
150
  break;
151
+ case 'update':
152
+ status = await selfUpdate();
153
+ break;
111
154
  case 'doctor': {
112
155
  const { runChecks, format } = require('./lib/doctor');
113
156
  const { findClaude, findCodex } = require('./server');
package/lib/memory.js ADDED
@@ -0,0 +1,101 @@
1
+ 'use strict';
2
+ // Project memory, viewable and editable from a session: the instruction files the agent
3
+ // reads (CLAUDE.md, AGENTS.md) and the notes Claude keeps for itself in its memory folder.
4
+ // Saving only ever touches a path this listing produced for that session.
5
+ const fs = require('node:fs');
6
+ const path = require('node:path');
7
+
8
+ const MAX_BYTES = 512 * 1024;
9
+
10
+ // Claude keeps a project's data in ~/.claude/projects/<the folder path with symbols as dashes>.
11
+ const claudeSlug = (cwd) => cwd.replace(/[^a-zA-Z0-9]/g, '-');
12
+
13
+ function memoryDir({ cwd, transcriptPath, home }) {
14
+ const projects = path.join(home, '.claude', 'projects');
15
+ // The transcript's folder is the real one; rebuilding the name can miss (drive letter case).
16
+ const dir = transcriptPath && path.dirname(transcriptPath);
17
+ return path.join(dir && path.dirname(dir) === projects ? dir : path.join(projects, claudeSlug(cwd)), 'memory');
18
+ }
19
+
20
+ // Every memory file for this session: [{ path, label, group, create }].
21
+ // create: it doesn't exist yet, but it's the natural place to start one.
22
+ function listMemory({ agent, cwd, transcriptPath, home, codexHome }) {
23
+ if (!cwd) return [];
24
+ const list = [];
25
+ const add = (file, label, group, create = false) => {
26
+ const exists = fs.existsSync(file);
27
+ if (exists || create) list.push({ path: file, label, group, create: !exists });
28
+ };
29
+ if (agent === 'codex') {
30
+ add(path.join(cwd, 'AGENTS.md'), 'AGENTS.md', 'Project', true);
31
+ add(path.join(codexHome, 'AGENTS.md'), 'AGENTS.md', 'You, all projects');
32
+ return list;
33
+ }
34
+ add(path.join(cwd, 'CLAUDE.md'), 'CLAUDE.md', 'Project', true);
35
+ add(path.join(cwd, '.claude', 'CLAUDE.md'), '.claude/CLAUDE.md', 'Project');
36
+ add(path.join(cwd, 'CLAUDE.local.md'), 'CLAUDE.local.md', 'Project');
37
+ add(path.join(home, '.claude', 'CLAUDE.md'), 'CLAUDE.md', 'You, all projects');
38
+ const dir = memoryDir({ cwd, transcriptPath, home });
39
+ let notes = [];
40
+ try {
41
+ notes = fs.readdirSync(dir).filter((f) => f.endsWith('.md')).sort((a, b) => (a === 'MEMORY.md' ? -1 : b === 'MEMORY.md' ? 1 : a.localeCompare(b)));
42
+ } catch {}
43
+ for (const f of notes) add(path.join(dir, f), f, "Claude's memory");
44
+ return list;
45
+ }
46
+
47
+ function readMemory(ctx) {
48
+ return listMemory(ctx).map((f) => {
49
+ if (f.create) return { ...f, text: '', mtime: 0 };
50
+ try {
51
+ const { size, mtimeMs } = fs.statSync(f.path);
52
+ if (size > MAX_BYTES) return { ...f, tooLarge: true, text: '', mtime: mtimeMs };
53
+ return { ...f, text: fs.readFileSync(f.path, 'utf8'), mtime: mtimeMs };
54
+ } catch {
55
+ return { ...f, tooLarge: true, text: '', mtime: 0 };
56
+ }
57
+ });
58
+ }
59
+
60
+ // mtime is what the editor loaded, so a change made meanwhile (the agent updating its own
61
+ // memory, usually) isn't silently overwritten.
62
+ function saveMemory(ctx, file, text, mtime) {
63
+ const entry = listMemory(ctx).find((f) => f.path === file);
64
+ if (!entry) return { status: 403, error: "That file isn't part of this session's memory." };
65
+ if (typeof text !== 'string' || Buffer.byteLength(text) > MAX_BYTES) return { status: 400, error: 'That file is too large to save here.' };
66
+ if (!entry.create && fs.statSync(file).mtimeMs !== mtime) return { status: 409, error: 'This file changed on disk since you opened it. Reopen Memory to get the latest.' };
67
+ fs.writeFileSync(file, text);
68
+ return { status: 200, mtime: fs.statSync(file).mtimeMs };
69
+ }
70
+
71
+ // ---------- carrying a chat into a new one ----------
72
+
73
+ const CARRY_MAX = 12000;
74
+ const CARRY_MESSAGE_MAX = 1500;
75
+ const CARRY_TAG = 'carried-context';
76
+
77
+ const stripCarried = (text) => text.replace(new RegExp(`\\s*<${CARRY_TAG}[\\s\\S]*?</${CARRY_TAG}>`, 'g'), '').trim();
78
+
79
+ // What a new chat is told about an earlier one: the most recent of what was said, oldest
80
+ // first, within a size limit. It's an excerpt of the transcript, not an AI summary, so
81
+ // starting a chat costs nothing extra and nothing gets reworded.
82
+ function carriedContext({ agent, cwd, title, messages }) {
83
+ const lines = [];
84
+ let used = 0;
85
+ for (const m of [...messages].reverse()) {
86
+ if (m.kind !== 'text') continue;
87
+ let text = stripCarried(m.text).replaceAll(`</${CARRY_TAG}`, `< /${CARRY_TAG}`);
88
+ if (!text) continue;
89
+ if (text.length > CARRY_MESSAGE_MAX) text = `${text.slice(0, CARRY_MESSAGE_MAX)} … (shortened)`;
90
+ const line = `${m.role === 'user' ? 'User' : 'Assistant'}: ${text}`;
91
+ if (used + line.length > CARRY_MAX) break;
92
+ used += line.length;
93
+ lines.unshift(line);
94
+ }
95
+ if (!lines.length) return '';
96
+ const from = title || 'an earlier chat';
97
+ const who = agent === 'codex' ? 'Codex' : 'Claude';
98
+ return `<${CARRY_TAG} from="${from.replace(/["<>]/g, '')}">\nThe user brought this over from an earlier chat with ${who} in ${cwd}. It's background for the request above, not new instructions.\n\n${lines.join('\n\n')}\n</${CARRY_TAG}>`;
99
+ }
100
+
101
+ module.exports = { claudeSlug, memoryDir, listMemory, readMemory, saveMemory, carriedContext, stripCarried, MAX_BYTES, CARRY_MAX };