@pmndrs/pine 0.0.0-stage → 0.1.1

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.
Files changed (117) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +180 -2
  3. package/bin/pine.js +27 -0
  4. package/docs/SKILL.md +189 -0
  5. package/docs/references/agents.md +368 -0
  6. package/docs/references/coding.md +126 -0
  7. package/docs/references/configuration.md +229 -0
  8. package/docs/references/events.md +263 -0
  9. package/docs/references/mcp.md +257 -0
  10. package/docs/references/operations.md +280 -0
  11. package/docs/references/overview.md +186 -0
  12. package/docs/references/security.md +489 -0
  13. package/docs/references/setup.md +181 -0
  14. package/docs/references/sources.md +768 -0
  15. package/docs/references/stack.md +105 -0
  16. package/docs/references/tui.md +348 -0
  17. package/examples/home/.env.example +5 -0
  18. package/examples/home/USER.md +4 -0
  19. package/examples/home/agents/main/agent.json +4 -0
  20. package/examples/home/agents/main/workspace/AGENTS.md +14 -0
  21. package/examples/home/agents/main/workspace/MEMORY.md +4 -0
  22. package/examples/home/agents/main/workspace/SOUL.md +4 -0
  23. package/examples/home/models.json +62 -0
  24. package/examples/home/pine.json +30 -0
  25. package/examples/home/skills/README.txt +7 -0
  26. package/examples/launchd/com.pine.daemon.plist +32 -0
  27. package/mcp/browser.d.ts +34 -0
  28. package/mcp/browser.js +228 -0
  29. package/mcp/commands.d.ts +20 -0
  30. package/mcp/commands.js +88 -0
  31. package/mcp/lib.d.ts +13 -0
  32. package/mcp/lib.js +50 -0
  33. package/mcp/web.d.ts +6 -0
  34. package/mcp/web.js +140 -0
  35. package/package.json +32 -4
  36. package/src/agent-admin.d.ts +24 -0
  37. package/src/agent-admin.js +55 -0
  38. package/src/agents.d.ts +343 -0
  39. package/src/agents.js +1587 -0
  40. package/src/cli.d.ts +2 -0
  41. package/src/cli.js +526 -0
  42. package/src/config.d.ts +351 -0
  43. package/src/config.js +514 -0
  44. package/src/dev/claude-model.d.ts +28 -0
  45. package/src/dev/claude-model.js +174 -0
  46. package/src/env-file.d.ts +4 -0
  47. package/src/env-file.js +40 -0
  48. package/src/http.d.ts +46 -0
  49. package/src/http.js +407 -0
  50. package/src/limiter.d.ts +32 -0
  51. package/src/limiter.js +84 -0
  52. package/src/login.d.ts +21 -0
  53. package/src/login.js +126 -0
  54. package/src/manual.d.ts +61 -0
  55. package/src/manual.js +425 -0
  56. package/src/mcp-config.d.ts +40 -0
  57. package/src/mcp-config.js +132 -0
  58. package/src/net.d.ts +36 -0
  59. package/src/net.js +122 -0
  60. package/src/pine.d.ts +32 -0
  61. package/src/pine.js +142 -0
  62. package/src/router.d.ts +56 -0
  63. package/src/router.js +164 -0
  64. package/src/sandbox-fs.d.ts +27 -0
  65. package/src/sandbox-fs.js +172 -0
  66. package/src/security.d.ts +121 -0
  67. package/src/security.js +363 -0
  68. package/src/skills.d.ts +67 -0
  69. package/src/skills.js +163 -0
  70. package/src/source-types.d.ts +61 -0
  71. package/src/source-types.js +1 -0
  72. package/src/sources/command.d.ts +4 -0
  73. package/src/sources/command.js +77 -0
  74. package/src/sources/folder.d.ts +6 -0
  75. package/src/sources/folder.js +116 -0
  76. package/src/sources/ftp.d.ts +4 -0
  77. package/src/sources/ftp.js +307 -0
  78. package/src/sources/http.d.ts +4 -0
  79. package/src/sources/http.js +201 -0
  80. package/src/sources/mqtt.d.ts +18 -0
  81. package/src/sources/mqtt.js +97 -0
  82. package/src/sources/telegram.d.ts +22 -0
  83. package/src/sources/telegram.js +269 -0
  84. package/src/sources/webhook.d.ts +4 -0
  85. package/src/sources/webhook.js +52 -0
  86. package/src/sources.d.ts +339 -0
  87. package/src/sources.js +955 -0
  88. package/src/store.d.ts +89 -0
  89. package/src/store.js +251 -0
  90. package/src/timers.d.ts +83 -0
  91. package/src/timers.js +318 -0
  92. package/src/tools.d.ts +6 -0
  93. package/src/tools.js +777 -0
  94. package/src/tui/app.d.ts +209 -0
  95. package/src/tui/app.js +2223 -0
  96. package/src/tui/client.d.ts +178 -0
  97. package/src/tui/client.js +116 -0
  98. package/src/tui/code-pane.d.ts +51 -0
  99. package/src/tui/code-pane.js +398 -0
  100. package/src/tui/highlight.d.ts +8 -0
  101. package/src/tui/highlight.js +86 -0
  102. package/src/tui/picture.d.ts +141 -0
  103. package/src/tui/picture.js +571 -0
  104. package/src/tui/run.d.ts +8 -0
  105. package/src/tui/run.js +97 -0
  106. package/src/tui/theme.d.ts +32 -0
  107. package/src/tui/theme.js +128 -0
  108. package/src/types.d.ts +124 -0
  109. package/src/types.js +1 -0
  110. package/src/update.d.ts +25 -0
  111. package/src/update.js +110 -0
  112. package/src/util.d.ts +32 -0
  113. package/src/util.js +116 -0
  114. package/src/watching.d.ts +20 -0
  115. package/src/watching.js +68 -0
  116. package/src/workspace-view.d.ts +38 -0
  117. package/src/workspace-view.js +160 -0
@@ -0,0 +1,368 @@
1
+ # Agents
2
+
3
+ An agent is a folder under `agents/` in pine's home. The folder name is its id (lowercase
4
+ letters, digits, `-`, `_`, starting with a letter or digit; `create_agent` wants a letter first
5
+ and 2 to 31 characters).
6
+
7
+ ```
8
+ agents/gonzo/
9
+ agent.json settings (see configuration.md); {} is enough
10
+ workspace/ where it works, and the files it keeps itself:
11
+ AGENTS.md its purpose and how it works
12
+ SOUL.md who it is: character and tone
13
+ MEMORY.md what should outlive a conversation
14
+ skills/ its own skills, one folder each; the agent adds them with add_skill
15
+ conversations/ what was set up for one conversation alone (its skills); made when needed
16
+ extensions/ optional: its own pi extensions
17
+ AGENTS.md optional: rules of yours that the agent cannot change
18
+ ```
19
+
20
+ ## Creating an agent
21
+
22
+ Ask the main agent ("create an agent that keeps my books"): it calls `create_agent`, you confirm
23
+ what it proposes, and the folder above is made with `agent.json` and a workspace that holds its
24
+ instructions (`AGENTS.md`) and stubs of `SOUL.md` and `MEMORY.md`. The agent exists at once,
25
+ without a restart. Only the main agent has this tool; the
26
+ others tell you to ask it. See [setup.md](setup.md).
27
+
28
+ By hand: make the folder with an `agent.json` (`{}` is enough) and a `workspace/AGENTS.md`,
29
+ run `pine check`, and restart.
30
+
31
+ ## The files an agent keeps itself
32
+
33
+ An agent's instructions are files in its workspace, and the agent can write them. Together
34
+ with one file about you they are read into its prompt with every conversation:
35
+
36
+ | File | Holds | Who writes it |
37
+ |---|---|---|
38
+ | `workspace/AGENTS.md` | Its purpose and how it works. You shape it by telling the agent ("your purpose is ...", "from now on answer in tables") | The agent, and you |
39
+ | `workspace/SOUL.md` | Who it is: character and tone | The agent, and you |
40
+ | `workspace/MEMORY.md` | What matters beyond a conversation: decisions, facts, preferences, open ends | The agent |
41
+ | `USER.md` in pine's home | What every agent knows about you. One file for all agents | The main agent, and you |
42
+ | `agents/<id>/AGENTS.md` | Optional. Rules of yours that the agent reads and cannot change | You |
43
+
44
+ Every agent is told what these files are for: to write into `AGENTS.md` what you say it is for
45
+ or how you want things done, to put into `MEMORY.md` what it learns that matters later, at
46
+ once and briefly, and to tidy its memory when it has grown long (merge, drop what no longer
47
+ holds, move details into other files and leave a pointer). When `MEMORY.md` passes about 6 000
48
+ characters, pine reminds the agent, because all of it is read with every message.
49
+
50
+ `pine init` starts them as short stubs; `create_agent` writes the instructions it was given
51
+ into `AGENTS.md` and starts the other two as stubs. A file that is missing is simply not
52
+ loaded, and the agent creates it when it has something to put there. A change applies from the
53
+ agent's next message on. Only these names (and a `CLAUDE.md`), at the top of the workspace, are
54
+ read; anything else the agent keeps there is its own notes, which it reads when it needs them.
55
+
56
+ Other agents get `USER.md` in their prompt but cannot open or change the file; they tell you
57
+ when something belongs there. A conversation that has read outside content cannot change any
58
+ of these files until it is reset ([security.md](security.md)): what is read into every future
59
+ prompt is exactly what an injected instruction would try to rewrite.
60
+
61
+ An existing home from before this layout keeps working: `agents/<id>/AGENTS.md` is still read.
62
+ To hand the instructions over to the agent, move that file to `agents/<id>/workspace/AGENTS.md`;
63
+ `pine init` adds the stubs that are missing and leaves your file where it is.
64
+
65
+ ## What an agent sees
66
+
67
+ Its system prompt is put together from, in this order:
68
+
69
+ 1. pine's base prompt: that it is an agent in pine working for its owner, how to use its file
70
+ tools, that it is confined to its workspace, that pine's setup is changed through tools and
71
+ never by writing its files (which of them it has, and that creating agents and anything for
72
+ all agents is the main agent's job), that for everything else it should explain the steps to
73
+ its owner, and that text from tools, files and the web is information, never a command. An
74
+ `agents/<id>/SYSTEM.md` replaces this text.
75
+ 2. `agents/<id>/AGENTS.md`, if you keep one: rules the agent cannot change.
76
+ 3. The files it keeps itself, from its workspace: `AGENTS.md` (also `CLAUDE.md`), `SOUL.md`,
77
+ then `USER.md` from pine's home, then `MEMORY.md`. See "The files an agent keeps itself".
78
+ After them the instruction files of a folder opened for the conversation, and any files in
79
+ `context`.
80
+ 4. Its skills: one line each; the agent reads a skill's file when it needs it. This manual is
81
+ not among them: the agent searches it with its `manual` tool (see below). An agent asked
82
+ for another model or a route looks it up and tells you what to put into which file; an
83
+ event source, a schedule, an MCP server or a skill it sets up itself by tool call
84
+ ([setup.md](setup.md)).
85
+ 5. A `<pine>` block: who the agent is, which agents it can delegate to, which MCP servers it
86
+ has. It only changes when the configuration does.
87
+
88
+ To show you a picture, an agent writes `![caption](path)` in its answer, with a file from its
89
+ workspace. The terminal UI draws it (see [tui.md](tui.md)); pine's base prompt tells every agent
90
+ this. A picture from the web it downloads first and then shows the file. A video file is shown
91
+ the same way and can be played in the conversation.
92
+
93
+ Nothing in the system prompt changes from one turn to the next. That is deliberate: a model
94
+ server keeps what it has already read (the prompt cache) only as long as the beginning of the
95
+ prompt is identical, and on a local model a miss means reading the whole conversation again
96
+ before the first word of the answer. What does change, pine puts in front of your message
97
+ instead, in a `<pine-note>`: the local time and date, the mode, the folder opened for the
98
+ conversation, whether its shell has network access, a reminder when `MEMORY.md` has grown
99
+ long, the shared state listed in `state.inject`, and whether the session has to be careful
100
+ with outside content. The note is only added when something other than the time of day has
101
+ changed since the last one. Text that arrives from elsewhere cannot fake a note; the tag is
102
+ rewritten on the way in.
103
+
104
+ The bottom bar of the terminal UI shows `cache N%` after an answer: the share of the input the
105
+ model server took from its cache. In a running conversation it should be above 90%.
106
+
107
+ The details of how pine works are not in the prompt; they are in this manual, which the agent
108
+ searches when a question calls for it. That keeps the prompt short for small models.
109
+
110
+ ### The manual tool
111
+
112
+ Every agent has a `manual` tool (not in a run that an outside event started). It holds the
113
+ whole of this manual, cut into small parts: one
114
+ for each heading, and one for each row of a table that describes a named thing (a
115
+ configuration key, a tool, a command, an option).
116
+
117
+ | Call | Answers with |
118
+ |---|---|
119
+ | `manual` `search: "<a few words>"` | The parts that fit best, each with its id and the sentence that matched. Several queries can be given at once, one thing each. |
120
+ | `manual` `read: "<id of a part>"` | That part in full; several ids at once. A long part comes in pages (the id with `@2`, `@3`). |
121
+ | `manual` with neither | The list of the manual's parts. |
122
+
123
+ A search is by words, not by exact wording. Words an agent or you would use and the manual
124
+ does not ("cron job", "reminder", "permission", "watcher") find the manual's own words through
125
+ a list of synonyms. A source type that was registered in this installation is found as well,
126
+ with its options. When nothing matches, the answer says so and suggests other words; it never
127
+ says that pine cannot do something.
128
+
129
+ `pine manual <words>` is the same search in a shell (without the source types registered in
130
+ an installation), and `pine manual --read <id>` reads a part: a quick way to see what an agent
131
+ would find. One call takes up to 6 queries and reads up to 8 parts.
132
+
133
+ Nothing from your own home folder leaks in: pi would normally also pick up `~/.agents/skills`
134
+ and instruction files in the folders above the workspace; pine filters those out. Edits to the
135
+ instruction files take effect on the agent's next task.
136
+
137
+ ## Workspaces
138
+
139
+ Point `workspace` at the folder the agent should work in, typically an existing repository:
140
+
141
+ ```json
142
+ { "description": "The house: contracts, utilities, solar", "workspace": "~/dev/house" }
143
+ ```
144
+
145
+ The agent's shell and file tools can read and write there and nowhere else in your home. Each
146
+ agent also has a private home folder (`sandbox/<id>/`), which is what `~` means inside its
147
+ shell. Agents cannot read each other's workspaces; they talk through `delegate`.
148
+
149
+ Two agents may share a workspace if you give them the same path. They can then overwrite each
150
+ other's files.
151
+
152
+ A single conversation can work somewhere else without any of this: `pine <folder>`, or the
153
+ folder in the bottom bar of the terminal UI, opens a folder of yours for that one conversation.
154
+ Its tools then start there and may change what is in it, and the folder's `AGENTS.md` (or
155
+ `CLAUDE.md`) applies next to the agent's own. See [coding.md](coding.md).
156
+
157
+ ## Sessions
158
+
159
+ A session is one conversation with history.
160
+
161
+ | You pass | What happens |
162
+ |---|---|
163
+ | nothing | The agent's `main` session: one long conversation, compacted by pi when it fills up |
164
+ | a name, for example `tg-123456` or `case-okapi` | A separate named conversation |
165
+ | `new` | A fresh conversation for that one task; it is not continued |
166
+
167
+ Routes pick the session with `"session"`, the CLI with `--session`. What you write on Telegram
168
+ arrives in the conversation that subscribed to the Telegram source. Runs started by untrusted
169
+ events use a twin of the session with `~u` appended,
170
+ so they never see or extend your own conversation.
171
+
172
+ Tasks for one session queue up. With `"whenBusy": "steer"` on a route, a new message is instead
173
+ shown to the agent in the middle of what it is doing.
174
+
175
+ `pine reset <agent> [session]` (or `/reset` in the terminal UI) starts a session
176
+ over. The old history is kept on disk next to it with a `.reset-<time>` suffix for
177
+ `limits.eventRetentionDays`. What the conversation was subscribed to on its own ends with the
178
+ reset.
179
+
180
+ A named conversation does not stay for ever. One that is not an agent's `main`, not pinned, not
181
+ open in a tab of the terminal UI, not subscribed to an event source and without a schedule of
182
+ its own is removed with its history after `limits.conversationDays` (7) without a message. That holds for every named
183
+ conversation: one you opened in the terminal UI, a delegation thread's, one a route names. Pin the ones to keep (`/pin`, or the list of
184
+ conversations in the terminal UI; see [tui.md](tui.md)), or set `limits.conversationDays` to
185
+ `0` to keep them all. Everything that was set up for one conversation alone goes with it: its
186
+ event sources and subscriptions, schedules, MCP servers and skills ([setup.md](setup.md)).
187
+
188
+ ## Handing work to another agent
189
+
190
+ The `delegate` tool exists for an agent when its `delegates` list is not empty. The main agent
191
+ (`"main"` in `pine.json`) may delegate to everyone by default.
192
+
193
+ ```json
194
+ { "delegates": ["gonzo", "scout"] }
195
+ ```
196
+
197
+ What the model sees:
198
+
199
+ ```
200
+ delegate(agent, task, wait?, thread?)
201
+ ```
202
+
203
+ - The other agent gets only the task text, prefixed with who sent it. It runs in its own
204
+ workspace with its own tools and instructions.
205
+ - By default the caller waits and gets the answer as the tool's result.
206
+ - `wait: false` returns at once; the answer arrives later as a new message in the caller's
207
+ session.
208
+ - Each delegation starts the other agent fresh. `thread: "okapi"` instead continues a named
209
+ conversation with it, so it remembers earlier tasks of that thread.
210
+ - An agent already waiting further up the chain cannot be called again, and chains stop at
211
+ `limits.maxDelegationDepth`. The model gets an error it can act on.
212
+
213
+ Write `description` for the delegating model: it is the only thing it knows about the other
214
+ agent. "Contract law; knows the case files in its workspace" works;
215
+ "Gonzo" does not.
216
+
217
+ ## Sub-agents
218
+
219
+ `subagent(tasks: string[])` runs up to `subagents.maxParallel` copies of the same agent at once
220
+ (4 unless the agent's `agent.json` says otherwise).
221
+ Each copy has the agent's workspace, instructions and tools but an empty conversation, cannot
222
+ delegate or spawn further, and returns only its final answer. Use it for searches and checks
223
+ that would otherwise fill the context:
224
+
225
+ > Find every clause about notice periods in `contracts/`. Use sub-agents, one per file.
226
+
227
+ A sub-agent works under the permissions of the conversation that started it: access you granted
228
+ there and your "always" answers apply to it, and what it is granted applies to the conversation.
229
+ It has the agent's MCP servers (unless `subagents.mcp` is `false`) and all its tools, or the
230
+ ones listed in `subagents.tools`.
231
+
232
+ Sub-agents count against the model slots. With fewer slots than sub-agents they queue; nothing
233
+ deadlocks, because the waiting parent lends out its own slot.
234
+
235
+ A sub-agent is a task like any other, with its own number and its own session, and its work can
236
+ be looked at while it runs and afterwards:
237
+
238
+ - In the terminal UI, the conversation shows a line when one starts and when it answers. A click
239
+ on either opens a tab with what it was given, what it thought and wrote, and every tool call
240
+ with the start of its result. `/task <id>` opens the same tab; the agent list (click the agent
241
+ name in the bottom bar) lists the ones this conversation started.
242
+ - `pine tasks` lists them, `pine task <id>` shows the task and its result, and
243
+ `GET /tasks/<id>/transcript` on the control API returns the steps.
244
+ - The session file itself stays in `agents/<id>/sessions/_tasks/<task id>/`.
245
+
246
+ The same works for a task that was delegated to another agent.
247
+
248
+ ## Shared state
249
+
250
+ Small facts that several agents and routes need: who is home, the mode of the house, the state
251
+ of the printer.
252
+
253
+ - Routes write it: `"set": { "house/mode": "{{payload.value}}" }`.
254
+ - Agents read it with `state_get` and write it with `state_set`, within `state.read` and
255
+ `state.write`.
256
+ - `state.inject` shows keys to an agent in every turn, always current, without growing the
257
+ conversation:
258
+
259
+ ```json
260
+ { "state": { "inject": ["house/#"], "write": ["house/#"] } }
261
+ ```
262
+
263
+ State values are shown to agents as facts, not as instructions, and are cut to 200 characters.
264
+ Do not route untrusted text into a key that agents see.
265
+
266
+ ## What an agent's answer does
267
+
268
+ - The final text of a task is its result. If the task has a reply target, it is sent there.
269
+ - `NO_REPLY` (or `SILENT`) as the whole answer sends nothing.
270
+ - A failed task (model error, timeout) sends "`<agent>` could not finish: ..." to the reply
271
+ target, so you are not left waiting. A cancelled task sends nothing.
272
+
273
+ ## pi extensions
274
+
275
+ Any pi extension works in pine sessions. One that adds a line to every system prompt:
276
+
277
+ ```ts
278
+ // ~/.pine/agents/gonzo/extensions/citations.ts
279
+ export default function (pi) {
280
+ pi.on("before_agent_start", async (event) => ({ systemPrompt: `${event.systemPrompt}\n\nCite the paragraph for every legal claim.` }));
281
+ }
282
+ ```
283
+
284
+ Files in `agents/<id>/extensions/` are found automatically; list others under `extensions`.
285
+ pi's extension guide is in `node_modules/@earendil-works/pi-coding-agent/docs/extensions.md`.
286
+ Things that need a terminal UI (dialogs, widgets) have no effect in the daemon.
287
+
288
+ `.pi/extensions/` in a workspace is deliberately not loaded; see `trustWorkspace` in
289
+ [configuration.md](configuration.md).
290
+
291
+ ## Skills
292
+
293
+ A skill is a folder with a `SKILL.md` that starts with a name and a description:
294
+
295
+ ```markdown
296
+ ---
297
+ name: inverter
298
+ description: Read and interpret the inverter's daily export. Use for questions about solar yield.
299
+ ---
300
+
301
+ 1. The exports are in `data/inverter/YYYY-MM.csv` ...
302
+ ```
303
+
304
+ The agent sees the description in its system prompt and reads the file when the task calls for
305
+ it. Files the instructions refer to (a script, a template) lie beside the `SKILL.md`.
306
+
307
+ ### Ask the agent
308
+
309
+ > Remember how to do this: make a skill for filing invoices.
310
+
311
+ The agent calls `add_skill` with a name, the description and the instructions, and optionally a
312
+ folder of its workspace whose files go along. pine writes the skill; the agent has it from its
313
+ next message on. Asking again under the same name replaces it, and `remove_skill` deletes it.
314
+
315
+ Like an event source or an MCP server, a skill is for the conversation it was asked for in,
316
+ for the agent, or for all agents ([setup.md](setup.md)):
317
+
318
+ | Said | Who gets it | You are asked |
319
+ |---|---|---|
320
+ | "make a skill for this" in a side conversation | That conversation; it goes when the conversation is removed | No |
321
+ | the same in the agent's main conversation, or "for you as an agent" | Every conversation of that agent | Only from a side conversation |
322
+ | "for all agents" | Every agent. Only the main agent can do this | Yes |
323
+
324
+ A name is used once among everything that would stand beside the skill, so no conversation
325
+ ever has two skills of one name.
326
+
327
+ A skill is part of what an agent is told, like its `AGENTS.md`. It is therefore written by
328
+ pine, in pine's home where the agent reads but does not write, and never from a conversation
329
+ that has read outside content: there `add_skill` refuses. What is a fact and not a way of
330
+ doing something belongs into the agent's `MEMORY.md`.
331
+
332
+ ### Put it there yourself
333
+
334
+ A folder with a `SKILL.md` in one of these places is a skill, with no entry anywhere:
335
+
336
+ | Folder | For |
337
+ |---|---|
338
+ | `~/.pine/skills/<name>/` | all agents |
339
+ | `~/.pine/agents/<id>/skills/<name>/` | that agent |
340
+
341
+ What lies in these two folders an agent can also replace (`add_skill` under the same name) and
342
+ remove (`remove_skill`), whoever put it there.
343
+
344
+ A skill that lies somewhere else is named under `skills` in `pine.json` (for all) or in an
345
+ `agent.json` (for that agent). Agents can use those and cannot replace or remove them.
346
+
347
+ A skill's name is lowercase letters, digits and single hyphens, at most 64 characters; `pine`
348
+ is taken. The description may have 1024 characters, the instructions 40 000. Files that go
349
+ along: at most 200 and 4 MB together, real files only (a link is refused).
350
+
351
+ `pine skills` lists every skill, whom it is for and where it lies.
352
+
353
+ ## Checking on agents
354
+
355
+ ```bash
356
+ bin/pine status
357
+ ```
358
+
359
+ ```bash
360
+ bin/pine tasks --agent gonzo
361
+ ```
362
+
363
+ ```bash
364
+ bin/pine events --topic "agent/#" -f
365
+ ```
366
+
367
+ `pine task <id>` shows one task in full: prompt, result, error, trust, and statistics (turns,
368
+ tokens, which tools it called, how long it took).
@@ -0,0 +1,126 @@
1
+ # Working on code
2
+
3
+ Two things turn a conversation into a place to work on a project: a **folder** that is opened
4
+ for it, and the **code pane** beside it that shows what git says changed there, the files, and
5
+ what the agent's tool calls printed.
6
+
7
+ ```bash
8
+ cd ~/dev/shop
9
+ pine .
10
+ ```
11
+
12
+ ![The code pane beside a conversation, showing the diff of a file the agent changed](../images/code.png)
13
+
14
+ ## A folder for a conversation
15
+
16
+ An agent works in its own workspace. A conversation can be given a folder of yours on top of
17
+ that: a repository, a project. From its next message on
18
+
19
+ - the agent's shell and file tools start in that folder (a relative path means a path there),
20
+ - it may read and change what is in it, like its own workspace,
21
+ - the folder's instruction files (`AGENTS.md`, `CLAUDE.md`) apply, next to the agent's own,
22
+ - the agent is told which folder it works in and that its own workspace is still there.
23
+
24
+ Everything else stays as it is: the rest of your home is closed, the network is closed unless
25
+ you opened it, and the agent's mode decides what it asks before doing.
26
+
27
+ The folder belongs to that one conversation. Another conversation of the same agent does not
28
+ have it, and neither has another agent it delegates to; sub-agents it starts do. It stays until
29
+ you take it away or remove the conversation, also across restarts. A renamed conversation
30
+ keeps it.
31
+
32
+ There are four ways to open one:
33
+
34
+ | | |
35
+ |---|---|
36
+ | `pine .` or `pine <folder>` | Starts the terminal UI with the tabs of last time and one more on that folder: a conversation of the main agent, named after the folder. If a conversation already works there, that one is opened again |
37
+ | The folder in the bottom bar | Click it (`no folder`, or the folder's path) for a list: no folder, this conversation's folder, the folder pine was started in (unless that is your home folder), folders other conversations of this agent use, and "Pick a folder …", which opens the system's folder chooser on macOS |
38
+ | `/folder <path>`, `/folder none` | The same by command (`off` and `-` mean `none` too). `/folder` alone opens the list |
39
+ | Drag a folder onto the terminal | It becomes the folder of the conversation in front. (A file dropped there is attached to your next message, as always) |
40
+
41
+ ![The folder menu over a conversation that works in a project folder](../images/folder.png)
42
+
43
+ Only you can do this. There is no tool for it: an agent cannot open a folder for itself, and
44
+ nothing an agent reads can.
45
+
46
+ A folder is refused when it is, contains or lies in pine's home (your home folder contains it,
47
+ if pine's home is `~/.pine`), pine's own program, or another agent's workspace. It cannot be
48
+ changed while the conversation is working.
49
+
50
+ For a project an agent should always work in, without opening anything, set `workspace` in its
51
+ `agent.json` instead ([agents.md](agents.md)).
52
+
53
+ ## The code pane
54
+
55
+ `▤ code` at the right end of the tab bar, `ctrl+g` or `/code` opens a pane beside the
56
+ conversation; the same closes it. It shows the conversation's folder, or the agent's workspace
57
+ when no folder is open. In a terminal of 100 columns or more it takes half the width (at most
58
+ 96 columns); in a narrower one it takes the place of the conversation until it is closed.
59
+
60
+ ```
61
+ pine 1 main × 2 main·shop × + ▤ code
62
+ ───────────────────────────────────────────────────────────────────────────────────────────
63
+ make the total include shipping │ Changes Files Output ↻ ×
64
+ │ main · 2 changed · ~/dev/shop
65
+ ✓ edit src/cart.ts 12 ms │─────────────────────────────────────────────────
66
+ ✓ bash npm test 2.1 s │ M src/cart.ts +4 −1
67
+ │ + src/shipping.ts +12
68
+ The total now includes shipping ... │
69
+ ```
70
+
71
+ | View | Shows | A click |
72
+ |---|---|---|
73
+ | **Changes** | What differs from the last commit: changed (`M`), added (`A`), new (`+`), deleted (`D`) and renamed (`R`) files with the lines added and removed, and the branch | on a file shows its diff, added lines green and removed ones red; `‹ back` returns to the list |
74
+ | **Files** | The folder as a tree | on a folder opens or closes it; on a file shows its text with line numbers and colours, or a picture as a picture |
75
+ | **Output** | Every tool call of the conversation's last 8 messages, newest first: the tool, what it was called with, how long it took | on a call shows everything it printed, not only the first lines the conversation shows |
76
+
77
+ The pane follows the agent: when a tool call finishes, what is shown is read again, so a diff
78
+ grows while the agent edits. `↻` reads it again by hand, `×` closes the pane. The wheel scrolls
79
+ whichever side the pointer is over. Whether the pane was open is remembered for the next start.
80
+
81
+ `/code changes`, `/code files` and `/code output` open it on that view; `/code off` closes it.
82
+
83
+ The pane only shows. It changes no file, runs no command of the project and sends nothing to a
84
+ model. Long lines wrap. A file is shown up to 256 KB, a diff up to 200 000 characters, a tool's
85
+ output up to 200 000 characters and 4000 lines; binary files are named, not shown. Changes
86
+ lists the first 300 files, and Files shows up to 500 entries of a folder.
87
+
88
+ While the pane is open, pictures and videos in the conversation are drawn with coloured half
89
+ blocks also on terminals that can show real pictures. A terminal draws a real picture only at
90
+ the left edge of a full line, where it would lie across the pane.
91
+
92
+ ## What it is safe against
93
+
94
+ - **Looking is not running.** A repository can name programs for git to start: a diff driver, a
95
+ text converter, hooks, a file system monitor. The pane runs git in a sandbox that can write
96
+ nothing and has no network, and with those features switched off, so a folder you only opened
97
+ to look at cannot make anything run.
98
+ - **Nothing outside the folder.** Paths are resolved with their links and must stay inside the
99
+ folder; a link that leads out of it is neither listed nor opened.
100
+ - **A folder is for your conversation, not for what arrives in it.** A run that an outside
101
+ event started, not you, does not get the folder: it works in the agent's own workspace. A
102
+ conversation of yours that has read outside content (a web page) keeps the folder, with the
103
+ two limits below. See [security.md](security.md).
104
+ - **Instruction files.** `AGENTS.md` and `CLAUDE.md` of an opened folder are instructions to
105
+ the agent, like those of its workspace. Open folders you trust as much as you trust their
106
+ authors with your agent. Once a conversation has read outside content, it can no longer
107
+ change these files.
108
+ - **Git hooks and configuration.** What lies in `.git/hooks` and `.git/config` runs with your
109
+ rights the next time you use git in that folder. A conversation that has read outside content
110
+ can still commit and branch there but cannot change those. One that has read none can, like
111
+ the rest of the folder: it acts on what you told it.
112
+
113
+ ## From a script
114
+
115
+ The control API ([operations.md](operations.md)) has the same things:
116
+
117
+ | Request | Purpose |
118
+ |---|---|
119
+ | `GET /agents/<id>/folder?session=` | The folder of a conversation, and the agent's workspace |
120
+ | `GET /agents/<id>/folders` | Every conversation of the agent that has a folder |
121
+ | `POST /agents/<id>/folder` `{ session, folder }` | Open a folder for a conversation; `folder: null` closes it |
122
+ | `GET /agents/<id>/view/changes?session=` | What git says changed in the conversation's folder |
123
+ | `GET /agents/<id>/view/diff?session=&path=` | The diff of one file (path from the top of the repository) |
124
+ | `GET /agents/<id>/view/tree?session=&path=` | What is in one folder (path from the conversation's folder) |
125
+ | `GET /agents/<id>/view/file?session=&path=` | A file's text |
126
+ | `GET /tasks/<id>/transcript?full=1` | A task's steps with the whole output of each tool call |