agenter-app 1.1.0 → 1.2.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 +166 -130
- package/cli.js +43 -0
- package/lib/memory.js +101 -0
- package/lib/terminal.js +127 -0
- package/lib/update.js +69 -0
- package/package.json +1 -1
- package/public/app.js +335 -13
- package/public/index.html +83 -9
- package/public/style.css +106 -10
- package/scripts/pick-folder.ps1 +85 -0
- package/server.js +142 -9
package/README.md
CHANGED
|
@@ -1,181 +1,217 @@
|
|
|
1
1
|
# Agenter
|
|
2
2
|
|
|
3
|
-
One dashboard
|
|
3
|
+
**One local dashboard to watch, approve and steer every Claude Code and Codex session on your machine.**
|
|
4
4
|
|
|
5
|
-
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
+
[](https://www.npmjs.com/package/agenter-app)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+

|
|
10
8
|
|
|
11
|
-
|
|
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
|
-
|
|
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
|
|
25
|
+
agenter install
|
|
26
|
+
agenter
|
|
18
27
|
```
|
|
19
28
|
|
|
20
|
-
Then
|
|
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
|
-
|
|
31
|
+
Install with `-g`, not `npx`: the hooks point at the installed files, so those files need to stay put.
|
|
23
32
|
|
|
24
|
-
|
|
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
|
-
|
|
35
|
+
## Commands
|
|
28
36
|
|
|
29
|
-
|
|
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
|
-
|
|
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
|
-
**
|
|
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
|
-
|
|
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
|
-
|
|
48
|
-
Dashboard settings are kept in `~/.agenter/settings.json`.
|
|
54
|
+
## Features
|
|
49
55
|
|
|
50
|
-
|
|
56
|
+
### Inbox and sessions
|
|
51
57
|
|
|
52
|
-
|
|
58
|
+
The inbox collects everything waiting on you, across all sessions. The session list has two groups:
|
|
53
59
|
|
|
54
|
-
- **Live
|
|
55
|
-
- **Earlier today
|
|
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
|
-
|
|
65
|
+
### Steering
|
|
63
66
|
|
|
64
|
-
Every live session has a message box
|
|
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
|
|
69
|
-
| Working |
|
|
70
|
-
| Idle, driven from VS Code |
|
|
71
|
-
| Idle, driven from Agenter | Starts a new turn right away
|
|
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
|
|
|
75
78
|
### Drive from VS Code or Agenter
|
|
76
79
|
|
|
77
|
-
Each session has a **Drive from** switch
|
|
80
|
+
Each session has a **Drive from** switch:
|
|
81
|
+
|
|
82
|
+
- **VS Code** (default): Claude runs in the VS Code panel, and Agenter answers inside its turns.
|
|
83
|
+
- **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.
|
|
84
|
+
|
|
85
|
+
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.
|
|
86
|
+
|
|
87
|
+
### New sessions
|
|
88
|
+
|
|
89
|
+
**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.
|
|
78
90
|
|
|
79
|
-
|
|
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.
|
|
91
|
+
If the folder window doesn't come to the front, look for **Agenter: choose a folder** on the taskbar.
|
|
87
92
|
|
|
88
|
-
|
|
93
|
+
### Memory
|
|
89
94
|
|
|
90
|
-
|
|
95
|
+
The **⋯** menu in a session's header has two tools for carrying knowledge between chats:
|
|
91
96
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
it in VS Code later from the Claude panel's session history.
|
|
97
|
+
- **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.
|
|
98
|
+
- **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.
|
|
95
99
|
|
|
96
|
-
|
|
100
|
+
### Preview
|
|
97
101
|
|
|
98
|
-
|
|
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.
|
|
102
|
+
**Preview** shows your running app next to the conversation.
|
|
101
103
|
|
|
102
|
-
-
|
|
103
|
-
|
|
104
|
-
-
|
|
105
|
-
|
|
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.
|
|
104
|
+
- 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.
|
|
105
|
+
- 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.
|
|
106
|
+
- Drag the divider to resize, or double-click it to reset.
|
|
107
|
+
- Some apps refuse to appear inside another page. If the preview stays blank, open it in your browser with ↗.
|
|
112
108
|
|
|
113
|
-
|
|
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%).
|
|
109
|
+
### Terminal
|
|
118
110
|
|
|
119
|
-
|
|
111
|
+
**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.
|
|
120
112
|
|
|
121
|
-
|
|
113
|
+
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.
|
|
114
|
+
|
|
115
|
+
### Codex
|
|
116
|
+
|
|
117
|
+
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.
|
|
118
|
+
|
|
119
|
+
- **Codex app** (default): your message is queued with `codex queue` and runs as the next turn when the session is open in Codex.
|
|
120
|
+
- **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.
|
|
121
|
+
- **Plan usage**: your 5-hour and weekly limits, as of your last Codex turn.
|
|
122
|
+
|
|
123
|
+
### Plan usage
|
|
124
|
+
|
|
125
|
+
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.
|
|
126
|
+
|
|
127
|
+
### Themes
|
|
128
|
+
|
|
129
|
+
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.
|
|
130
|
+
|
|
131
|
+
## Keyboard shortcuts
|
|
132
|
+
|
|
133
|
+
| Key | Action |
|
|
134
|
+
|---|---|
|
|
135
|
+
| <kbd>A</kbd> | Allow the current permission request. Off for commands that look destructive. |
|
|
136
|
+
| <kbd>Shift</kbd>+<kbd>A</kbd> | Always allow it |
|
|
137
|
+
| <kbd>D</kbd> | Deny it |
|
|
138
|
+
| <kbd>R</kbd> | Jump to the reply box |
|
|
139
|
+
| <kbd>J</kbd> / <kbd>K</kbd> | Next / previous session |
|
|
140
|
+
| <kbd>N</kbd> | Next session that needs you |
|
|
141
|
+
| <kbd>I</kbd> | Open the inbox |
|
|
142
|
+
| <kbd>/</kbd> | Filter sessions |
|
|
143
|
+
| <kbd>`</kbd> | Show or hide the terminal |
|
|
144
|
+
| <kbd>B</kbd> | Collapse or expand the sidebar |
|
|
145
|
+
| <kbd>T</kbd> | Switch theme |
|
|
146
|
+
| <kbd>?</kbd> | Show all shortcuts |
|
|
122
147
|
|
|
123
|
-
|
|
124
|
-
can steer Claude and watch the result in one window.
|
|
148
|
+
## Updating
|
|
125
149
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
150
|
+
Every `agenter` command tells you when a newer version is on npm, and so does a bar in the dashboard.
|
|
151
|
+
|
|
152
|
+
- **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.
|
|
153
|
+
- **In a terminal:** run `agenter update`, then restart from the dashboard, or Quit from the tray and run `agenter`.
|
|
154
|
+
|
|
155
|
+
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.
|
|
156
|
+
|
|
157
|
+
## Configuration
|
|
158
|
+
|
|
159
|
+
Dashboard settings are kept in `~/.agenter/settings.json`. Everything else is set with environment variables:
|
|
160
|
+
|
|
161
|
+
| Variable | Effect |
|
|
162
|
+
|---|---|
|
|
163
|
+
| `AGENTER_PORT` | Port to use instead of 4317. Set it for both the server and your environment. |
|
|
164
|
+
| `AGENTER_AUTOSTART=0` | Stops hooks from starting the server. |
|
|
165
|
+
| `AGENTER_TRAY=0` | Hides the tray icon. |
|
|
166
|
+
| `AGENTER_TERMINAL=0` | Turns off the terminal panel. |
|
|
167
|
+
| `AGENTER_UPDATE_CHECK=0` | Turns off update checks. |
|
|
168
|
+
| `AGENTER_USAGE=0` | Turns off Claude plan usage. |
|
|
169
|
+
| `AGENTER_CODEX=0` | Turns off Codex support. |
|
|
170
|
+
| `AGENTER_CLAUDE` | Path to `claude.exe`, if it isn't found automatically. |
|
|
171
|
+
| `AGENTER_CODEX_BIN` | Path to `codex.exe`, if it isn't found automatically. |
|
|
172
|
+
| `CODEX_HOME` | Where Codex keeps its data, if not `~/.codex`. |
|
|
134
173
|
|
|
135
174
|
## How it works
|
|
136
175
|
|
|
137
|
-
Claude Code runs [hooks](https://docs.claude.com/en/docs/claude-code/hooks) on events
|
|
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.
|
|
176
|
+
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
177
|
|
|
141
178
|
| Event | What Agenter does |
|
|
142
179
|
|---|---|
|
|
143
|
-
| `PermissionRequest` | Holds the prompt until you click **Allow
|
|
144
|
-
| `PreToolUse` (AskUserQuestion) | Shows the question
|
|
145
|
-
| `Stop` |
|
|
146
|
-
| Everything else |
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
- **
|
|
154
|
-
- **
|
|
155
|
-
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
- `npm start -- --no-open` skips opening the browser.
|
|
180
|
+
| `PermissionRequest` | Holds the prompt until you click **Allow**, **Always allow**, **Deny** or **Answer in VS Code**. A deny reason is passed to Claude. |
|
|
181
|
+
| `PreToolUse` (AskUserQuestion) | Shows the question and sends your answers back to Claude. |
|
|
182
|
+
| `Stop` | With **Hold for reply** on, the session waits for your next instruction. |
|
|
183
|
+
| Everything else | Updates the activity and status, and never blocks. |
|
|
184
|
+
|
|
185
|
+
## Safety
|
|
186
|
+
|
|
187
|
+
Agenter is designed to get out of the way when you aren't using it:
|
|
188
|
+
|
|
189
|
+
- **Server not running?** Hooks exit instantly, and Claude behaves as if Agenter didn't exist.
|
|
190
|
+
- **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.
|
|
191
|
+
- **Dashboard closed?** The same, after 15 seconds.
|
|
192
|
+
- **Left something waiting?** It goes back to VS Code after 3 minutes by default, with a countdown on the card.
|
|
193
|
+
|
|
194
|
+
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.
|
|
195
|
+
|
|
196
|
+
## Limits
|
|
197
|
+
|
|
198
|
+
- **Windows is the supported platform.** The tray icon, shortcuts, terminal, folder window and app detection are Windows only.
|
|
199
|
+
- **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.
|
|
200
|
+
- **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.
|
|
201
|
+
- **Answering questions** depends on your Claude Code version accepting hook-supplied answers. If it doesn't, use **Answer in VS Code**.
|
|
202
|
+
|
|
203
|
+
## Development
|
|
204
|
+
|
|
205
|
+
```powershell
|
|
206
|
+
git clone https://github.com/zxcoding123/agenter_app.git
|
|
207
|
+
cd agenter_app
|
|
208
|
+
npm run install-hooks
|
|
209
|
+
npm start
|
|
210
|
+
npm test
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
`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`.
|
|
214
|
+
|
|
215
|
+
## License
|
|
216
|
+
|
|
217
|
+
[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 };
|