@ocis/myagent-cli 0.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 +357 -0
- package/dist/agent/context.d.ts +33 -0
- package/dist/agent/context.js +169 -0
- package/dist/agent/modes.d.ts +21 -0
- package/dist/agent/modes.js +84 -0
- package/dist/agent/prompt-builder.d.ts +9 -0
- package/dist/agent/prompt-builder.js +31 -0
- package/dist/agent/sessions.d.ts +38 -0
- package/dist/agent/sessions.js +130 -0
- package/dist/agent/todo.d.ts +18 -0
- package/dist/agent/todo.js +61 -0
- package/dist/agent/turn.d.ts +309 -0
- package/dist/agent/turn.js +1253 -0
- package/dist/approval/policy.d.ts +81 -0
- package/dist/approval/policy.js +157 -0
- package/dist/config.d.ts +49 -0
- package/dist/config.js +156 -0
- package/dist/git/status.d.ts +89 -0
- package/dist/git/status.js +226 -0
- package/dist/headless.d.ts +72 -0
- package/dist/headless.js +330 -0
- package/dist/index.d.ts +60 -0
- package/dist/index.js +511 -0
- package/dist/protocol/client.d.ts +123 -0
- package/dist/protocol/client.js +250 -0
- package/dist/protocol/sse-frames.d.ts +6 -0
- package/dist/protocol/sse-frames.js +75 -0
- package/dist/protocol/types.d.ts +200 -0
- package/dist/protocol/types.js +8 -0
- package/dist/runtime.d.ts +38 -0
- package/dist/runtime.js +166 -0
- package/dist/sanitize.d.ts +1 -0
- package/dist/sanitize.js +21 -0
- package/dist/skills/discovery.d.ts +24 -0
- package/dist/skills/discovery.js +109 -0
- package/dist/tools/binary.d.ts +2 -0
- package/dist/tools/binary.js +22 -0
- package/dist/tools/diff.d.ts +1 -0
- package/dist/tools/diff.js +49 -0
- package/dist/tools/find.d.ts +2 -0
- package/dist/tools/find.js +61 -0
- package/dist/tools/fs.d.ts +2 -0
- package/dist/tools/fs.js +276 -0
- package/dist/tools/glob.d.ts +6 -0
- package/dist/tools/glob.js +131 -0
- package/dist/tools/grep.d.ts +3 -0
- package/dist/tools/grep.js +228 -0
- package/dist/tools/paths.d.ts +27 -0
- package/dist/tools/paths.js +124 -0
- package/dist/tools/registry.d.ts +13 -0
- package/dist/tools/registry.js +38 -0
- package/dist/tools/shell.d.ts +2 -0
- package/dist/tools/shell.js +136 -0
- package/dist/tools/skills.d.ts +2 -0
- package/dist/tools/skills.js +36 -0
- package/dist/tools/todo.d.ts +2 -0
- package/dist/tools/todo.js +43 -0
- package/dist/tools/transfer.d.ts +2 -0
- package/dist/tools/transfer.js +145 -0
- package/dist/tools/truncate.d.ts +12 -0
- package/dist/tools/truncate.js +46 -0
- package/dist/tools/types.d.ts +85 -0
- package/dist/tools/types.js +63 -0
- package/dist/ui/app.d.ts +39 -0
- package/dist/ui/app.js +1061 -0
- package/dist/ui/colors.d.ts +100 -0
- package/dist/ui/colors.js +169 -0
- package/dist/ui/components.d.ts +267 -0
- package/dist/ui/components.js +811 -0
- package/dist/ui/diff.d.ts +37 -0
- package/dist/ui/diff.js +143 -0
- package/dist/ui/format.d.ts +28 -0
- package/dist/ui/format.js +76 -0
- package/dist/ui/help.d.ts +6 -0
- package/dist/ui/help.js +45 -0
- package/dist/ui/highlight.d.ts +20 -0
- package/dist/ui/highlight.js +210 -0
- package/dist/ui/logo.d.ts +24 -0
- package/dist/ui/logo.js +106 -0
- package/dist/ui/model-list.d.ts +10 -0
- package/dist/ui/model-list.js +33 -0
- package/dist/ui/quit-confirm.d.ts +8 -0
- package/dist/ui/quit-confirm.js +40 -0
- package/dist/ui/select-popup.d.ts +30 -0
- package/dist/ui/select-popup.js +54 -0
- package/dist/ui/theme.d.ts +4 -0
- package/dist/ui/theme.js +41 -0
- package/dist/ui/tool-view.d.ts +20 -0
- package/dist/ui/tool-view.js +326 -0
- package/package.json +44 -0
- package/skills/git-commit/SKILL.md +27 -0
package/README.md
ADDED
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
# myagent CLI
|
|
2
|
+
|
|
3
|
+
A coding agent for **your local repository**, powered by a remote MyAgent.
|
|
4
|
+
The CLI is the hands and eyes; the agent's brain — model, conversation,
|
|
5
|
+
compaction, cost accounting — stays on your MyAgent server.
|
|
6
|
+
Not everything is code: **COWORK** mode turns the same agent into a general assistant
|
|
7
|
+
(questions, lookups, summarizing, organizing) with the full tool set still available —
|
|
8
|
+
see [Plan / Coding / Cowork modes](#plan--coding--cowork-modes).
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
local repo (cwd) remote myagent
|
|
12
|
+
┌───────────────────────────────┐ ┌────────────────────────────────┐
|
|
13
|
+
│ myagent CLI │ │ agent loop (pi-agent-core) │
|
|
14
|
+
│ TUI: transcript · sidebar │ SSE / REST │ model / prompt cache / retry │
|
|
15
|
+
│ context: AGENTS.md/CLAUDE.md │◄──────────────►│ history / auto-compact / guard │
|
|
16
|
+
│ client tools ─────────────────┼─ AG-UI run line│ steering / stop / usage │
|
|
17
|
+
│ session index (local) │ + tool results│ (optional) server web_fetch │
|
|
18
|
+
└───────────────────────────────┘ └────────────────────────────────┘
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
- **No provider keys on the client.** Models are configured on the MyAgent Models page;
|
|
22
|
+
the CLI only holds an integration key (`iak_…`). When the integration is set to *Client
|
|
23
|
+
choice*, `/model` selects among the server's models per new session.
|
|
24
|
+
- **Every coding tool runs on your machine.** The CLI advertises `local_*` tools to the
|
|
25
|
+
remote agent; when the agent calls one, it executes locally and submits the result.
|
|
26
|
+
- **Agent management stays remote.** Sessions, history, auto-compaction, prompt caching and
|
|
27
|
+
usage/cost accounting all live on MyAgent and are surfaced in the TUI through the
|
|
28
|
+
integration API.
|
|
29
|
+
- **One protocol: the AG-UI run line.** Each turn is a single `POST /v1/runs` stream that
|
|
30
|
+
stays open through tool waits — client tool calls (`CUSTOM_TOOL_CALL`), the agent's
|
|
31
|
+
continuation, and release signals all arrive on it. Tool results go back as protocol-native
|
|
32
|
+
`role: "tool"` messages to `POST /v1/runs/:threadId/messages` (single-frame
|
|
33
|
+
`INPUT_ACCEPTED`/`INPUT_REJECTED` ack). The CLI and the server deploy in lockstep — the
|
|
34
|
+
CLI assumes the AG-UI line exists.
|
|
35
|
+
|
|
36
|
+
## Install
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm install -g @ocis/myagent-cli # global (Node ≥22.19)
|
|
40
|
+
bunx @ocis/myagent-cli # one-off, no install (Bun)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The CLI runs on **Node.js ≥22.19** or **Bun ≥1.0** — it detects the runtime it was
|
|
44
|
+
launched with. The published bin uses a Node shebang, so `bun install -g` also runs
|
|
45
|
+
it under Node; use `bunx` (or `bun <path-to-bin>`) to run it on Bun. Configure
|
|
46
|
+
credentials with `myagent config`, or set `MYAGENT_BASE_URL` /
|
|
47
|
+
`MYAGENT_INTEGRATION_ID` / `MYAGENT_API_KEY`.
|
|
48
|
+
|
|
49
|
+
## Plan / Coding / Cowork modes
|
|
50
|
+
|
|
51
|
+
| Mode | Behavior |
|
|
52
|
+
|------|----------|
|
|
53
|
+
| **PLAN** (orange) | Investigate and propose. The mutating tools (`local_write`, `local_edit`, `local_shell`) refuse at execution time with a model-readable error; everything else (including `local_update_todo`) keeps working. |
|
|
54
|
+
| **CODING** (green, default) | Software work: implement, verify, report. |
|
|
55
|
+
| **COWORK** (purple) | General tasks — questions, lookups, summarizing, organizing, drafting. The full tool set stays available (including `local_write`/`local_edit`/`local_shell`); the mode only changes the framing, not the permissions. |
|
|
56
|
+
|
|
57
|
+
Switch with `Tab` (plan → coding → cowork → plan), `/plan`, `/coding`, `/cowork`, or start
|
|
58
|
+
with `--plan` / `--coding` / `--cowork`.
|
|
59
|
+
|
|
60
|
+
Modes are designed to be prompt-cache-friendly:
|
|
61
|
+
|
|
62
|
+
- **The advertised tool set never changes** — all tools are always in the schema, so the
|
|
63
|
+
cached tool block survives a switch.
|
|
64
|
+
- **The system prompt never changes with the mode** — it statically defines all modes'
|
|
65
|
+
behavior and the announcement protocol.
|
|
66
|
+
- **The mode is announced per user message** — when it changes (or on the first message of
|
|
67
|
+
a session), the CLI prepends `[mode: plan]` / `[mode: coding]` / `[mode: cowork]` to that
|
|
68
|
+
message. It never enters the system prompt, so the cached conversation prefix stays intact.
|
|
69
|
+
|
|
70
|
+
## Tools (all client-side)
|
|
71
|
+
|
|
72
|
+
| Tool | Purpose |
|
|
73
|
+
|------|---------|
|
|
74
|
+
| `local_read` | Read a text file with line numbers (`offset`/`limit`, binary/size guards) |
|
|
75
|
+
| `local_write` | Create/overwrite (or append) a file, creating parent directories |
|
|
76
|
+
| `local_edit` | Exact-string replacement (unique or `replace_all`; batch `edits`), returns a unified diff. Literal replacement — `$&` in the new text is safe. |
|
|
77
|
+
| `local_shell` | Shell command on your machine, rooted at the workspace; 120s default timeout; process-group kill; 50KB UTF-8-safe output cap |
|
|
78
|
+
| `local_grep` | Regex search (ripgrep when installed, pure-JS fallback), 100-match cap, skips build/VCS dirs |
|
|
79
|
+
| `local_find` | Glob file discovery (`*`, `?`, `**`, `{a,b}`), 500-result cap |
|
|
80
|
+
| `local_ls` | Directory listing (optional recursive + glob filter) |
|
|
81
|
+
| `load_local_skill` | Load a `SKILL.md` playbook from the machine |
|
|
82
|
+
| `local_update_todo` | Maintain the session TODO list (rendered in the right sidebar). Batched into the same assistant turn as other tool calls to avoid an extra model round-trip. |
|
|
83
|
+
| `local_transfer_files` | Move single files between your machine and the myagent workspace (`direction: get`/`put`, plus `list`). Uses the workspace API; `get` writes locally (ASK/plan-mode rules apply), `put` does not touch your machine. Content never enters the model transcript. |
|
|
84
|
+
|
|
85
|
+
Side-effecting tools honor the approval mode: **AUTO** (default) runs in-workspace work
|
|
86
|
+
unattended but still asks before touching paths outside the workspace, while **ASK**
|
|
87
|
+
confirms every mutation (and outside reads). `Shift+Tab` or `/edit` toggles it per session
|
|
88
|
+
(changeable mid-run, it takes effect immediately); the current approval state is always
|
|
89
|
+
visible in the status bar (AUTO is amber), and the active mode in the prompt's bottom
|
|
90
|
+
edge. `--ask` starts in ASK, `--auto` (or the config default) starts in AUTO;
|
|
91
|
+
`--allow-outside` pre-approves outside paths so they never prompt.
|
|
92
|
+
|
|
93
|
+
**Workspace transfers.** `local_transfer_files` moves single files between your machine and
|
|
94
|
+
the myagent server's shared workspace (the same files the web file browser/WebDAV expose):
|
|
95
|
+
`{"direction":"put","from":"notes.txt"}` uploads, `{"direction":"get","from":"reports/q3.md"}`
|
|
96
|
+
downloads, `{"direction":"list","path":"/"}` browses. Uploads are capped at ~37 MB (the
|
|
97
|
+
server's 50 MB body limit after base64); both directions overwrite. Chat attachments made in
|
|
98
|
+
the web UI are stored outside the workspace and are not reachable this way.
|
|
99
|
+
|
|
100
|
+
### Approval prompts
|
|
101
|
+
|
|
102
|
+
An approval floats directly above the prompt input (the editor keeps focus —
|
|
103
|
+
you can keep typing) and is answered with **ctrl+y** (approve) or **ctrl+n**
|
|
104
|
+
(deny). The wait is bounded: after **10 minutes** the approval times out and
|
|
105
|
+
the agent receives an error result telling it to stop and wait for you — a
|
|
106
|
+
run never hangs on an unanswered prompt.
|
|
107
|
+
|
|
108
|
+
Approvals **serialize**: one card is shown at a time and the rest queue in
|
|
109
|
+
arrival order (a round's parallel calls, or a second tool round starting while
|
|
110
|
+
you decide, can each need one). Each waiting approval keeps its own deadline
|
|
111
|
+
and its own cancellation, so a release of one call never cancels another and
|
|
112
|
+
`/stop` cancels every one of them.
|
|
113
|
+
|
|
114
|
+
While you decide, the CLI watches the session's event channel (when the server
|
|
115
|
+
advertises `sessionEvents`) and reports when the channel itself drops and is
|
|
116
|
+
being reconnected. If the server releases the call while you decide (the
|
|
117
|
+
30-minute lease expired, the sandbox restarted, or the run ended), the
|
|
118
|
+
approval is **cancelled** — the tool never runs, and the tool card in the
|
|
119
|
+
transcript says so. Stopping the run (Esc, Ctrl+C, `/stop`) cancels a pending
|
|
120
|
+
approval the same way. If the sandbox was paused while you were away, the next
|
|
121
|
+
request wakes it automatically.
|
|
122
|
+
|
|
123
|
+
## Context files
|
|
124
|
+
|
|
125
|
+
Every turn ships a stable system block containing:
|
|
126
|
+
|
|
127
|
+
- **`AGENTS.override.md` → `AGENTS.md` → `CLAUDE.md`** discovered per directory from the
|
|
128
|
+
filesystem root down to the workspace (outermost first), plus the global
|
|
129
|
+
`~/.config/myagent/AGENTS.md` and `.myagent/AGENTS.md`. Disable with `--no-context-files`.
|
|
130
|
+
- Environment facts (workspace, OS + release + arch). Date and git branch are deliberately
|
|
131
|
+
omitted — the agent can query them with `local_shell` when needed.
|
|
132
|
+
- The local `<available_skills>` catalog.
|
|
133
|
+
- Both modes' instructions and the `[mode: x]` announcement protocol (static — the active
|
|
134
|
+
mode never changes this block). The TODO list lives in the `local_update_todo` tool
|
|
135
|
+
calls, not here.
|
|
136
|
+
|
|
137
|
+
The block is re-sent each turn because the integration API treats system messages as
|
|
138
|
+
per-request instructions — it is kept byte-stable across turns (no mode, todos, date or
|
|
139
|
+
branch) so the provider prompt cache survives.
|
|
140
|
+
|
|
141
|
+
## TUI
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
┌──────────────────────────────────────────────┬──────────────────────┐
|
|
145
|
+
│ transcript │ session title │
|
|
146
|
+
│ messages · tool calls · thinking · diffs │ CONTEXT │
|
|
147
|
+
│ │ 84k/200k (42% used) │
|
|
148
|
+
│ │ ~$0.42 (27% cached) │
|
|
149
|
+
│ │ TODO (2/5) │
|
|
150
|
+
│ │ CHANGES (12) │
|
|
151
|
+
│ ╭──────────────────────────────────────╮ │ M src/a.ts +18 -4 │
|
|
152
|
+
│ │ > editor (multi-line, / commands…) │ │ │
|
|
153
|
+
│ ╰─ CODING · sonnet-4-6 · high ─────────╯ │ │
|
|
154
|
+
│ AUTO · working · ↑ ↓ $ ~/repo:main │ │
|
|
155
|
+
└──────────────────────────────────────────────┴──────────────────────┘
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
The layout splits left/right first: the transcript, prompt and status bar live in the
|
|
159
|
+
left column, so they never affect the right panel, which runs the full height of the
|
|
160
|
+
terminal.
|
|
161
|
+
|
|
162
|
+
- **Sidebar** (Ctrl+P toggles; hidden below 100 columns; ~25% of the terminal width):
|
|
163
|
+
the session title, a `CONTEXT` section (shown once usage is reported: `used / window
|
|
164
|
+
(% used)` on the first content line, cost with the cached-token share below), a `TODO`
|
|
165
|
+
section (only once the todo tool has been used), and `CHANGES` from `git`. Each section
|
|
166
|
+
title is prefixed with a hue-colored accent bar (`▌`) and sections have breathing room
|
|
167
|
+
above and below; the panel has a slightly lighter background (from the terminal's OSC 11
|
|
168
|
+
reply). Scroll with the mouse wheel or `Alt+↑/↓`; click `… N more` / press `Ctrl+E` to
|
|
169
|
+
expand the file list (collapsed shows 10, expanded shows every changed file).
|
|
170
|
+
- **Diff viewer**: click a changed file in the sidebar. The viewer fills the screen — a
|
|
171
|
+
gray rounded frame inset from the edges (nothing behind it leaks through), with the file
|
|
172
|
+
title, position and view mode centered in the top edge. Only the diff content is shown
|
|
173
|
+
(`diff --git`, `index`, `---`/`+++` and `@@` headers are hidden); changed lines carry
|
|
174
|
+
pre/post line numbers, `↑/↓ PgUp/PgDn` or the mouse wheel scrolls, `t` toggles
|
|
175
|
+
inline/side-by-side (`i`/`p` pick a view), `Esc` closes, and opening another file
|
|
176
|
+
replaces the popup. The card refits the screen on terminal resize/zoom (the scroll
|
|
177
|
+
position is clamped to the new height).
|
|
178
|
+
- **Prompt**: the editor sits in a rounded, inset frame (no background of its own); its
|
|
179
|
+
bottom edge carries the active mode (`PLAN` orange / `CODING` green / `COWORK` purple — `Tab`
|
|
180
|
+
cycles, click the label for the mode picker) followed by the model and thinking level in
|
|
181
|
+
gray (`·`-separated, both clickable to open their pickers), and a `↓ N more`
|
|
182
|
+
indicator while the input is scrolled.
|
|
183
|
+
- **Status bar**: gray text on the terminal's own background, separated from the prompt by a
|
|
184
|
+
blank row; approval (`AUTO`/`ASK`, switchable mid-run with Shift+Tab or by clicking the
|
|
185
|
+
badge), run state, the current/last run's token totals and cost sit on the left, and
|
|
186
|
+
`path:branch` is flush right.
|
|
187
|
+
- **Transcript**: assistant markdown (gray prose, indented `•` bullets and tables), a
|
|
188
|
+
collapsed thinking block (Ctrl+T), and tool calls collapsed to their header — e.g.
|
|
189
|
+
`▸ ✓ read src/app.ts · lines 1–4` (click one, or Ctrl+O for all, to expand). Expanded
|
|
190
|
+
results render by kind: syntax-highlighted file reads, side-by-side diffs with line
|
|
191
|
+
numbers for edits, `$ command` plus the last 30 output lines for bash (older lines
|
|
192
|
+
collapse to `... skipped`, failures are red). Server-tool activity and notices follow.
|
|
193
|
+
- **Approvals** appear as an overlay (`y`/`n`).
|
|
194
|
+
- **Editor**: `/` slash-command autocomplete, `@` file references, Shift+Enter or
|
|
195
|
+
Alt+Enter for a newline. (`Tab` is reserved for the mode cycle.)
|
|
196
|
+
|
|
197
|
+
### Keys
|
|
198
|
+
|
|
199
|
+
| Key | Action |
|
|
200
|
+
|-----|--------|
|
|
201
|
+
| `Tab` | Cycle PLAN/CODING/COWORK |
|
|
202
|
+
| `Shift+Tab` | Toggle AUTO/ASK approval |
|
|
203
|
+
| `Enter` | Send; while running: **steer** the message into the in-flight turn |
|
|
204
|
+
| `Ctrl+Y` / `Ctrl+N` | Answer a pending approval (approve / deny) |
|
|
205
|
+
| `Esc` | Force-stop the run (`POST /stop`) |
|
|
206
|
+
| `Ctrl+C` | Stop while running; while idle, press twice within 2s to quit (`/quit` exits directly) |
|
|
207
|
+
| `Ctrl+P` | Toggle sidebar |
|
|
208
|
+
| `Ctrl+E` | Expand/collapse the changed-files list |
|
|
209
|
+
| `Alt+↑` / `Alt+↓` | Scroll the sidebar (mouse wheel works too) |
|
|
210
|
+
| Click a file | Open its diff in a popup |
|
|
211
|
+
| `t` / `i` / `p` | In the diff popup: toggle / inline / side-by-side pair |
|
|
212
|
+
| `Ctrl+O` | Expand/collapse all tool output (a single tool call toggles on click) |
|
|
213
|
+
| `Ctrl+T` | Expand/collapse thinking |
|
|
214
|
+
| `Shift+Enter` / `Alt+Enter` | Newline |
|
|
215
|
+
|
|
216
|
+
### Commands
|
|
217
|
+
|
|
218
|
+
`/help` `/new` `/session [all]` (`/sessions`, `/resume` — aliases) `/plan` `/coding` `/cowork` `/mode` `/edit` `/thinking` `/model` `/compact`
|
|
219
|
+
`/stop` `/reload` `/export` `/config` `/quit`
|
|
220
|
+
|
|
221
|
+
## Sessions
|
|
222
|
+
|
|
223
|
+
The remote session is the source of truth. The CLI keeps a small local index
|
|
224
|
+
(`~/.config/myagent/sessions.json`) mapping working directories to thread ids and TODO
|
|
225
|
+
lists so `-c` continues the right conversation.
|
|
226
|
+
|
|
227
|
+
- `/new` — start a fresh session (the remote thread is created on the next prompt).
|
|
228
|
+
- `/session` — list **this folder's** sessions and switch. The remote API has no working-
|
|
229
|
+
directory concept, so the filter comes from the local index; a "Show all folders…" entry
|
|
230
|
+
(or `/session all`) reveals every session on the integration, annotated with its source
|
|
231
|
+
folder. Switching to a session from another folder warns that tools still run in the
|
|
232
|
+
current workspace.
|
|
233
|
+
- `/export` writes a markdown transcript; `/sessions` and `/resume` are aliases of `/session`.
|
|
234
|
+
|
|
235
|
+
- **Steering**: typing while a run is active queues the message into the server-side run. It
|
|
236
|
+
floats above the prompt until the agent actually injects it (`QUEUED_MESSAGE_DELIVERED`),
|
|
237
|
+
then moves into the transcript as a user message.
|
|
238
|
+
- **Stop**: `Esc` or `/stop` aborts the stream and calls the stop endpoint.
|
|
239
|
+
- **Recovery**: if the stream drops, the CLI reconciles against the session detail
|
|
240
|
+
endpoint. Pending **read-only** tool calls are re-executed safely; pending **mutating**
|
|
241
|
+
calls are failed with an explanatory result instead of silently re-applied.
|
|
242
|
+
- **Usage**: live `RUN_USAGE` events update the sidebar; on resume, persisted
|
|
243
|
+
`usage_stats` seed the panel.
|
|
244
|
+
|
|
245
|
+
## Model selection
|
|
246
|
+
|
|
247
|
+
The model runs server-side and is shown on the prompt's bottom edge. The integration's Model
|
|
248
|
+
policy decides who picks it:
|
|
249
|
+
|
|
250
|
+
- **Client choice** (the default): the CLI fetches the selectable models from
|
|
251
|
+
`GET /v1/models` and `/model` offers them in a picker. The server default (the first entry)
|
|
252
|
+
is marked **default**; the model in use is marked **current** (current wins when it is also
|
|
253
|
+
the default). `--model <id>` preselects one for new sessions.
|
|
254
|
+
- **A forced model**: the endpoint returns exactly one entry, so there is nothing to choose.
|
|
255
|
+
|
|
256
|
+
A thread pins its model on its first run, so `/model` applies to the **next** new session
|
|
257
|
+
(`/new`); the running thread keeps its model. Once a session has run, `/thinking` offers the
|
|
258
|
+
levels reported for that session's model.
|
|
259
|
+
|
|
260
|
+
## Thinking level
|
|
261
|
+
|
|
262
|
+
The thinking level is selectable per request — the same behavior as the web UI's per-message
|
|
263
|
+
selector: `/thinking` offers the levels detected for the model plus **Default** (provider
|
|
264
|
+
decides). `--thinking <level>` sets it for a run.
|
|
265
|
+
|
|
266
|
+
## Credentials & config
|
|
267
|
+
|
|
268
|
+
Resolved in order — flags > env > config file:
|
|
269
|
+
|
|
270
|
+
- Flags: `--base-url`, `--integration-id`, `--api-key`
|
|
271
|
+
- Env: `MYAGENT_BASE_URL`, `MYAGENT_INTEGRATION_ID`, `MYAGENT_API_KEY`, `MYAGENT_APPROVAL`
|
|
272
|
+
- File: `$XDG_CONFIG_HOME/myagent/config.json` (0600), written by `myagent config`
|
|
273
|
+
|
|
274
|
+
`--base-url` / `MYAGENT_BASE_URL` also accept the combined **API Base URL** shown on the
|
|
275
|
+
Integrations page (`https://host/integrations/<id>`) — the integration id is derived from it.
|
|
276
|
+
|
|
277
|
+
Create an integration + API key on the MyAgent **Integrations** page. Recommended settings
|
|
278
|
+
for the CLI:
|
|
279
|
+
|
|
280
|
+
- **Tools**: leave empty (all coding tools are supplied by the CLI) or enable `web_fetch` for
|
|
281
|
+
server-side fetching.
|
|
282
|
+
- **Model**: any tool-capable model with a reasonable context window.
|
|
283
|
+
|
|
284
|
+
The CLI and the server deploy in lockstep — every documented integration-API feature is
|
|
285
|
+
assumed to exist (there is no capability handshake).
|
|
286
|
+
|
|
287
|
+
## Non-interactive use
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
myagent -p "summarise the failing tests" # text to stdout, tools to stderr
|
|
291
|
+
cat README.md | myagent run "explain this" # prompt from stdin
|
|
292
|
+
myagent -p --output-format json "..." # one JSON object: thread_id/text/usage
|
|
293
|
+
myagent -p --output-format stream-json "..." # JSONL events as they happen
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### Prompt mode (CI)
|
|
297
|
+
|
|
298
|
+
`--prompt` is the unattended one-shot entry point for CI and scripts:
|
|
299
|
+
|
|
300
|
+
```bash
|
|
301
|
+
myagent --prompt "fix the failing tests" # final answer to stdout
|
|
302
|
+
cat prompt.md | myagent --prompt - # prompt from stdin
|
|
303
|
+
myagent --prompt "..." --session <thread-id> # continue a previous session
|
|
304
|
+
myagent --prompt "..." --output-format json --timeout 600 # machine-readable, bounded
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
- **Non-TUI** — `--prompt` always runs headless, even on a TTY.
|
|
308
|
+
- **AUTO accept all** — every tool runs unattended, *including paths outside the
|
|
309
|
+
workspace* (no one is present to answer an approval prompt). `--ask` cannot be
|
|
310
|
+
honored here and is ignored — passing both prints a warning on stderr rather
|
|
311
|
+
than silently dropping the safer setting. `--plan` still works (read-only).
|
|
312
|
+
- **stdout is only the final assistant message** (tool progress goes to stderr).
|
|
313
|
+
`--output-format json` emits one JSON object — `thread_id`, `model`, `text`,
|
|
314
|
+
`usage`, `error`, `error_code` — and it is emitted for pre-run failures too, so
|
|
315
|
+
a parser never sees an empty stdout.
|
|
316
|
+
- **`--timeout <sec>`** aborts the run (and stops it server-side) after N seconds.
|
|
317
|
+
- **SIGINT/SIGTERM** stop the remote run gracefully (exit 130/143); a second
|
|
318
|
+
signal exits immediately.
|
|
319
|
+
- **Continuation**: the JSON `thread_id` is the session id — pass it back as
|
|
320
|
+
`--session <thread-id>` to continue the same context on the next run. `-c`
|
|
321
|
+
continues the most recent session for the working directory (local index).
|
|
322
|
+
|
|
323
|
+
Exit codes: `0` success · `1` run failed (including timeout) · `2` usage,
|
|
324
|
+
configuration, or connection error · `130`/`143` interrupted.
|
|
325
|
+
|
|
326
|
+
For CI, configure credentials via env (`MYAGENT_BASE_URL` — the combined
|
|
327
|
+
`https://host/integrations/<id>` URL works — `MYAGENT_INTEGRATION_ID`,
|
|
328
|
+
`MYAGENT_API_KEY`). `--prompt` sends its own final-answer instruction, so the
|
|
329
|
+
agent returns only the final answer without any integration-side configuration.
|
|
330
|
+
|
|
331
|
+
`-p`/`run` remain the general headless mode: they respect the configured
|
|
332
|
+
approval default (and `--ask` denies mutating tools), and stream all assistant
|
|
333
|
+
text. `--prompt` is the opinionated CI mode on top of the same runner.
|
|
334
|
+
|
|
335
|
+
## Local skills
|
|
336
|
+
|
|
337
|
+
A skill is a directory with `SKILL.md` (frontmatter `name`/`description` + instructions).
|
|
338
|
+
Search paths, earlier wins: `--skills-dir`, `<workspace>/.myagent/skills`,
|
|
339
|
+
`~/.config/myagent/skills`. The catalog is injected into the system block; the agent loads
|
|
340
|
+
one with `load_local_skill`.
|
|
341
|
+
|
|
342
|
+
## Development
|
|
343
|
+
|
|
344
|
+
```bash
|
|
345
|
+
bun install
|
|
346
|
+
bun run dev # run from source
|
|
347
|
+
bun test # unit + e2e (fake integration server) tests
|
|
348
|
+
bun run typecheck
|
|
349
|
+
bun run build # npm package → dist/ (tsc + fix-esm; prepack runs this)
|
|
350
|
+
bun run build:binary # standalone binary → dist/myagent
|
|
351
|
+
bun run build:all # cross-compile linux/macos/windows (x64 + arm64)
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
`bun run build` emits the publishable ESM package to `dist/` (`prepack` runs it
|
|
355
|
+
automatically on `npm publish`). The compiled binary has no runtime dependencies.
|
|
356
|
+
`@earendil-works/pi-tui` provides the rendering layer (the same library the pi
|
|
357
|
+
coding agent ships with).
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export interface ContextFile {
|
|
2
|
+
path: string;
|
|
3
|
+
content: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Discover context files: the global file first, then per-directory files
|
|
7
|
+
* from the trust boundary down to cwd (outermost first), so the most local
|
|
8
|
+
* instructions come last. One file per directory; AGENTS.override.md wins.
|
|
9
|
+
*
|
|
10
|
+
* The walk is bounded (see contextBoundary): an unbounded walk to `/` would
|
|
11
|
+
* let anyone who can write to a shared ancestor (e.g. /tmp/AGENTS.md) inject
|
|
12
|
+
* instructions into every turn's system prompt — and in auto-approval mode
|
|
13
|
+
* drive unsupervised tool calls.
|
|
14
|
+
*/
|
|
15
|
+
export declare function findContextFiles(cwd: string, globalContextPath?: string): Promise<ContextFile[]>;
|
|
16
|
+
export interface EnvironmentInfo {
|
|
17
|
+
workspace: string;
|
|
18
|
+
/** OS descriptor, e.g. "linux 6.8.0 (x64)". */
|
|
19
|
+
os: string;
|
|
20
|
+
}
|
|
21
|
+
/** Stable OS descriptor for the environment block (no volatile values). */
|
|
22
|
+
export declare function osDescription(): string;
|
|
23
|
+
export declare function buildEnvironmentBlock(info: EnvironmentInfo): string;
|
|
24
|
+
export interface SystemPromptInput {
|
|
25
|
+
workspace: string;
|
|
26
|
+
contextFiles: ContextFile[];
|
|
27
|
+
environment: EnvironmentInfo;
|
|
28
|
+
skillsCatalog?: string;
|
|
29
|
+
/** Additional instructions (e.g. per-session user config). */
|
|
30
|
+
extraInstructions?: string;
|
|
31
|
+
}
|
|
32
|
+
/** Compose the per-request system block. Must be byte-stable across turns. */
|
|
33
|
+
export declare function composeSystemPrompt(input: SystemPromptInput): string;
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Local context assembly for the remote agent.
|
|
3
|
+
//
|
|
4
|
+
// The agent's brain runs on MyAgent; the CLI is the only party that can see
|
|
5
|
+
// the user's repository. Every turn therefore ships a system block carrying:
|
|
6
|
+
// - context files (AGENTS.md / CLAUDE.md) discovered from cwd upward
|
|
7
|
+
// - environment facts (workspace, OS) — deliberately NOT date/git branch,
|
|
8
|
+
// which change during a session and would invalidate the provider prompt
|
|
9
|
+
// cache (the agent can query them with local_shell when needed)
|
|
10
|
+
// - the local skills catalog
|
|
11
|
+
// - every mode's instructions and the mode-announcement protocol
|
|
12
|
+
//
|
|
13
|
+
// The block is re-sent each turn because the integration API treats `system`
|
|
14
|
+
// messages as per-request instructions (replace semantics); it is therefore
|
|
15
|
+
// kept byte-stable across turns so the cached prefix survives. The current
|
|
16
|
+
// mode/todos never enter this block — the mode arrives as a `[mode: x]` marker
|
|
17
|
+
// in the user message, and todos live in the local_update_todo tool calls.
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
import { homedir, release } from "node:os";
|
|
20
|
+
import { readFile, stat } from "node:fs/promises";
|
|
21
|
+
import { dirname, join, resolve, sep } from "node:path";
|
|
22
|
+
import { DEFAULT_MODE, MODES } from "./modes.js";
|
|
23
|
+
const PER_DIR_CANDIDATES = [
|
|
24
|
+
"AGENTS.override.md",
|
|
25
|
+
"AGENTS.md",
|
|
26
|
+
"AGENTS.MD",
|
|
27
|
+
"CLAUDE.md",
|
|
28
|
+
"CLAUDE.MD",
|
|
29
|
+
".myagent/AGENTS.md",
|
|
30
|
+
];
|
|
31
|
+
async function readIfExists(path) {
|
|
32
|
+
try {
|
|
33
|
+
const info = await stat(path);
|
|
34
|
+
if (!info.isFile())
|
|
35
|
+
return null;
|
|
36
|
+
const text = await readFile(path, "utf-8");
|
|
37
|
+
return text.trim() ? text : null;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The outermost directory whose context files are trusted: the git repository
|
|
45
|
+
* root when cwd is inside one, else $HOME when cwd is under it, else cwd.
|
|
46
|
+
*/
|
|
47
|
+
async function contextBoundary(start) {
|
|
48
|
+
for (let dir = start;;) {
|
|
49
|
+
if (await pathExists(join(dir, ".git")))
|
|
50
|
+
return dir;
|
|
51
|
+
const parent = dirname(dir);
|
|
52
|
+
if (parent === dir)
|
|
53
|
+
break;
|
|
54
|
+
dir = parent;
|
|
55
|
+
}
|
|
56
|
+
const home = homedir();
|
|
57
|
+
return start === home || start.startsWith(home + sep) ? home : start;
|
|
58
|
+
}
|
|
59
|
+
async function pathExists(path) {
|
|
60
|
+
try {
|
|
61
|
+
await stat(path);
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Discover context files: the global file first, then per-directory files
|
|
70
|
+
* from the trust boundary down to cwd (outermost first), so the most local
|
|
71
|
+
* instructions come last. One file per directory; AGENTS.override.md wins.
|
|
72
|
+
*
|
|
73
|
+
* The walk is bounded (see contextBoundary): an unbounded walk to `/` would
|
|
74
|
+
* let anyone who can write to a shared ancestor (e.g. /tmp/AGENTS.md) inject
|
|
75
|
+
* instructions into every turn's system prompt — and in auto-approval mode
|
|
76
|
+
* drive unsupervised tool calls.
|
|
77
|
+
*/
|
|
78
|
+
export async function findContextFiles(cwd, globalContextPath) {
|
|
79
|
+
const files = [];
|
|
80
|
+
if (globalContextPath) {
|
|
81
|
+
const content = await readIfExists(globalContextPath);
|
|
82
|
+
if (content)
|
|
83
|
+
files.push({ path: globalContextPath, content });
|
|
84
|
+
}
|
|
85
|
+
const start = resolve(cwd);
|
|
86
|
+
const boundary = await contextBoundary(start);
|
|
87
|
+
const ancestors = [];
|
|
88
|
+
for (let dir = start;;) {
|
|
89
|
+
ancestors.push(dir);
|
|
90
|
+
if (dir === boundary)
|
|
91
|
+
break;
|
|
92
|
+
const parent = dirname(dir);
|
|
93
|
+
if (parent === dir)
|
|
94
|
+
break; // defensive: the boundary is always an ancestor
|
|
95
|
+
dir = parent;
|
|
96
|
+
}
|
|
97
|
+
// boundary → cwd
|
|
98
|
+
ancestors.reverse();
|
|
99
|
+
for (const dir of ancestors) {
|
|
100
|
+
for (const candidate of PER_DIR_CANDIDATES) {
|
|
101
|
+
const full = join(dir, candidate);
|
|
102
|
+
const content = await readIfExists(full);
|
|
103
|
+
if (content) {
|
|
104
|
+
files.push({ path: full, content });
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return files;
|
|
110
|
+
}
|
|
111
|
+
/** Stable OS descriptor for the environment block (no volatile values). */
|
|
112
|
+
export function osDescription() {
|
|
113
|
+
return `${process.platform} ${release()} (${process.arch})`;
|
|
114
|
+
}
|
|
115
|
+
export function buildEnvironmentBlock(info) {
|
|
116
|
+
return [
|
|
117
|
+
`<environment>`,
|
|
118
|
+
`workspace: ${info.workspace}`,
|
|
119
|
+
`os: ${info.os}`,
|
|
120
|
+
`</environment>`,
|
|
121
|
+
].join("\n");
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Static description of ALL modes plus the announcement protocol. Never
|
|
125
|
+
* changes with the active mode — the current mode is announced per user
|
|
126
|
+
* message as `[mode: plan]` / `[mode: coding]` / `[mode: cowork]`.
|
|
127
|
+
*/
|
|
128
|
+
function buildModesBlock() {
|
|
129
|
+
const modes = Object.keys(MODES);
|
|
130
|
+
const markers = modes.map((mode) => `[mode: ${mode}]`);
|
|
131
|
+
const markerList = `${markers.slice(0, -1).join(", ")} or ${markers.at(-1)}`;
|
|
132
|
+
const sections = modes.map((mode) => [`<${mode}_mode>`, MODES[mode].instructions, `</${mode}_mode>`].join("\n"));
|
|
133
|
+
return [
|
|
134
|
+
"<modes>",
|
|
135
|
+
"You operate in one of the modes below. Each user message may start with a mode marker —",
|
|
136
|
+
`${markerList} — announcing which mode you must follow; the`,
|
|
137
|
+
"latest marker wins. If no marker is present, keep following the last announced mode",
|
|
138
|
+
`(${DEFAULT_MODE} initially). All modes share the same tools: in plan mode the mutating tools refuse`,
|
|
139
|
+
"with an error instead of executing, so do not call them.",
|
|
140
|
+
"",
|
|
141
|
+
sections.join("\n\n"),
|
|
142
|
+
"</modes>",
|
|
143
|
+
].join("\n");
|
|
144
|
+
}
|
|
145
|
+
/** Compose the per-request system block. Must be byte-stable across turns. */
|
|
146
|
+
export function composeSystemPrompt(input) {
|
|
147
|
+
const parts = [];
|
|
148
|
+
parts.push([
|
|
149
|
+
"<cli_role>",
|
|
150
|
+
"You are MyAgent CLI, an assistant running on the user's machine.",
|
|
151
|
+
`All tools you are given execute locally inside the workspace: ${input.workspace}`,
|
|
152
|
+
"Always use those tools for file and shell work; there is no server-side copy of the files.",
|
|
153
|
+
"Paths are workspace-relative. The announced mode defines whether the task is software work",
|
|
154
|
+
"or general assistance — follow it.",
|
|
155
|
+
"</cli_role>",
|
|
156
|
+
].join("\n"));
|
|
157
|
+
parts.push(buildModesBlock());
|
|
158
|
+
if (input.contextFiles.length > 0) {
|
|
159
|
+
const blocks = input.contextFiles.map((file) => `--- ${file.path} ---\n${file.content.trimEnd()}`);
|
|
160
|
+
parts.push(`<project_context>\n${blocks.join("\n\n")}\n</project_context>`);
|
|
161
|
+
}
|
|
162
|
+
parts.push(buildEnvironmentBlock(input.environment));
|
|
163
|
+
if (input.skillsCatalog)
|
|
164
|
+
parts.push(input.skillsCatalog);
|
|
165
|
+
if (input.extraInstructions?.trim()) {
|
|
166
|
+
parts.push(`<user_instructions>\n${input.extraInstructions.trim()}\n</user_instructions>`);
|
|
167
|
+
}
|
|
168
|
+
return parts.join("\n\n");
|
|
169
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type AgentMode = "plan" | "coding" | "cowork";
|
|
2
|
+
/** The mode a fresh session starts in. */
|
|
3
|
+
export declare const DEFAULT_MODE: AgentMode;
|
|
4
|
+
export interface ModeSpec {
|
|
5
|
+
name: AgentMode;
|
|
6
|
+
/** Uppercase badge for the prompt frame / footer. */
|
|
7
|
+
label: string;
|
|
8
|
+
/** One-line UI blurb (mode picker rows). */
|
|
9
|
+
description: string;
|
|
10
|
+
/** Instructions for this mode, rendered into the static <modes> prompt block. */
|
|
11
|
+
instructions: string;
|
|
12
|
+
/** Mode accent color for badge + editor border. */
|
|
13
|
+
color: (s: string) => string;
|
|
14
|
+
}
|
|
15
|
+
export declare const MODES: Record<AgentMode, ModeSpec>;
|
|
16
|
+
/** `[mode: plan]` / `[mode: coding]` / `[mode: cowork]` marker at the start of a user message. */
|
|
17
|
+
export declare function modeMarker(mode: AgentMode): string;
|
|
18
|
+
export declare function stripModeMarker(text: string): string;
|
|
19
|
+
/** Tab / `/mode` cycle: plan → coding → cowork → plan. */
|
|
20
|
+
export declare function nextMode(mode: AgentMode): AgentMode;
|
|
21
|
+
export declare function isReadOnlyTool(name: string): boolean;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Plan / Coding / Cowork modes.
|
|
3
|
+
//
|
|
4
|
+
// IMPORTANT: the advertised tool set is mode-independent (a stable tool schema
|
|
5
|
+
// block keeps the provider prompt cache warm across mode switches). A mode is
|
|
6
|
+
// enforced at EXECUTION time: in plan mode mutating tools refuse with an error,
|
|
7
|
+
// and the mode is announced to the model per user message via a `[mode: x]`
|
|
8
|
+
// marker (see `modeMarker`). The system prompt statically defines all modes'
|
|
9
|
+
// behavior — it never changes with the mode.
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
import { CODING_COLOR, COWORK_COLOR, PLAN_COLOR } from "../ui/colors.js";
|
|
12
|
+
/** The mode a fresh session starts in. */
|
|
13
|
+
export const DEFAULT_MODE = "coding";
|
|
14
|
+
/** Tools whose execution never changes the user's machine. */
|
|
15
|
+
const READ_ONLY_TOOLS = ["local_read", "local_grep", "local_find", "local_ls", "load_local_skill"];
|
|
16
|
+
export const MODES = {
|
|
17
|
+
plan: {
|
|
18
|
+
name: "plan",
|
|
19
|
+
label: "PLAN",
|
|
20
|
+
color: PLAN_COLOR,
|
|
21
|
+
description: "read-only — investigate and propose",
|
|
22
|
+
instructions: [
|
|
23
|
+
"# Mode: PLAN (read-only)",
|
|
24
|
+
"You are in planning mode. Investigate and propose; do not change anything.",
|
|
25
|
+
"- Use the read-only tools to explore the codebase and gather evidence.",
|
|
26
|
+
"- Mutating tools (local_write, local_edit, local_shell) are advertised but REFUSE with an error in this mode — do not call them.",
|
|
27
|
+
"- local_update_todo is bookkeeping only and remains available if you want to lay out a plan checklist; batch it into the same turn as your other tool calls (parallel tool calling), never a turn of its own.",
|
|
28
|
+
"- Produce a concrete plan: files to change, the approach, risks, verification steps.",
|
|
29
|
+
"- Ask clarifying questions when the request is ambiguous instead of guessing.",
|
|
30
|
+
"- Keep the final plan concise and directly actionable — the user will switch to Coding mode to execute it.",
|
|
31
|
+
].join("\n"),
|
|
32
|
+
},
|
|
33
|
+
coding: {
|
|
34
|
+
name: "coding",
|
|
35
|
+
label: "CODING",
|
|
36
|
+
color: CODING_COLOR,
|
|
37
|
+
description: "software work, end to end",
|
|
38
|
+
instructions: [
|
|
39
|
+
"# Mode: CODING (software engineering)",
|
|
40
|
+
"You are in coding mode. Carry out the software task end-to-end.",
|
|
41
|
+
"- Read before you write; prefer editing existing files over creating new ones.",
|
|
42
|
+
"- Keep changes minimal and consistent with the existing code.",
|
|
43
|
+
"- Use local_update_todo to track multi-step work (send the entire list each time). Batch it into the same turn as the tool calls it describes (parallel tool calling) — a todo-only turn wastes a model round-trip.",
|
|
44
|
+
"- Verify your changes (run tests/typecheck/build with local_shell where available).",
|
|
45
|
+
"- Keep the user informed with brief progress notes, then finish with a short summary.",
|
|
46
|
+
].join("\n"),
|
|
47
|
+
},
|
|
48
|
+
cowork: {
|
|
49
|
+
name: "cowork",
|
|
50
|
+
label: "COWORK",
|
|
51
|
+
color: COWORK_COLOR,
|
|
52
|
+
description: "general tasks and everyday work",
|
|
53
|
+
instructions: [
|
|
54
|
+
"# Mode: COWORK (general tasks)",
|
|
55
|
+
"You are in cowork mode: a general-purpose assistant, not primarily a coding agent.",
|
|
56
|
+
"- Help with everyday work — answering questions, looking things up, summarizing, organizing information, drafting text, lightweight analysis.",
|
|
57
|
+
"- Do not assume the task involves a repository or code; ask when the goal is unclear.",
|
|
58
|
+
"- Answer directly and concisely first; expand only when it adds value.",
|
|
59
|
+
"- The full tool set is available (local_read, local_grep, local_find, local_ls, local_shell, local_write, local_edit, local_update_todo, local_transfer_files): use the tools when they genuinely help (inspect local files, run a command, verify a fact, move files to/from the myagent workspace). Do not refuse a tool just because the mode is cowork.",
|
|
60
|
+
"- local_update_todo remains available for multi-step work; batch it into the same turn as your other tool calls (parallel tool calling).",
|
|
61
|
+
"- Reply in the user's language.",
|
|
62
|
+
].join("\n"),
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
/** `[mode: plan]` / `[mode: coding]` / `[mode: cowork]` marker at the start of a user message. */
|
|
66
|
+
export function modeMarker(mode) {
|
|
67
|
+
return `[mode: ${mode}]`;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Remove a leading mode marker (used when rendering server-side history).
|
|
71
|
+
* `build`/`chat` are the pre-rename ids — old sessions still carry them.
|
|
72
|
+
*/
|
|
73
|
+
const MODE_MARKER_PATTERN = new RegExp(`^\\[mode: (?:${[...Object.keys(MODES), "build", "chat"].join("|")})\\]\\s*\\n*`);
|
|
74
|
+
export function stripModeMarker(text) {
|
|
75
|
+
return text.replace(MODE_MARKER_PATTERN, "");
|
|
76
|
+
}
|
|
77
|
+
/** Tab / `/mode` cycle: plan → coding → cowork → plan. */
|
|
78
|
+
export function nextMode(mode) {
|
|
79
|
+
const order = Object.keys(MODES);
|
|
80
|
+
return order[(order.indexOf(mode) + 1) % order.length];
|
|
81
|
+
}
|
|
82
|
+
export function isReadOnlyTool(name) {
|
|
83
|
+
return READ_ONLY_TOOLS.includes(name);
|
|
84
|
+
}
|