@pmndrs/pine 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +180 -2
- package/bin/pine.js +27 -0
- package/docs/SKILL.md +189 -0
- package/docs/references/agents.md +368 -0
- package/docs/references/coding.md +126 -0
- package/docs/references/configuration.md +229 -0
- package/docs/references/events.md +263 -0
- package/docs/references/mcp.md +257 -0
- package/docs/references/operations.md +280 -0
- package/docs/references/overview.md +186 -0
- package/docs/references/security.md +489 -0
- package/docs/references/setup.md +181 -0
- package/docs/references/sources.md +768 -0
- package/docs/references/stack.md +105 -0
- package/docs/references/tui.md +348 -0
- package/examples/home/.env.example +5 -0
- package/examples/home/USER.md +4 -0
- package/examples/home/agents/main/agent.json +4 -0
- package/examples/home/agents/main/workspace/AGENTS.md +14 -0
- package/examples/home/agents/main/workspace/MEMORY.md +4 -0
- package/examples/home/agents/main/workspace/SOUL.md +4 -0
- package/examples/home/models.json +62 -0
- package/examples/home/pine.json +30 -0
- package/examples/home/skills/README.txt +7 -0
- package/examples/launchd/com.pine.daemon.plist +32 -0
- package/mcp/browser.d.ts +34 -0
- package/mcp/browser.js +228 -0
- package/mcp/commands.d.ts +20 -0
- package/mcp/commands.js +88 -0
- package/mcp/lib.d.ts +13 -0
- package/mcp/lib.js +50 -0
- package/mcp/web.d.ts +6 -0
- package/mcp/web.js +140 -0
- package/package.json +32 -4
- package/src/agent-admin.d.ts +24 -0
- package/src/agent-admin.js +55 -0
- package/src/agents.d.ts +343 -0
- package/src/agents.js +1587 -0
- package/src/cli.d.ts +2 -0
- package/src/cli.js +526 -0
- package/src/config.d.ts +351 -0
- package/src/config.js +514 -0
- package/src/dev/claude-model.d.ts +28 -0
- package/src/dev/claude-model.js +174 -0
- package/src/env-file.d.ts +4 -0
- package/src/env-file.js +40 -0
- package/src/http.d.ts +46 -0
- package/src/http.js +407 -0
- package/src/limiter.d.ts +32 -0
- package/src/limiter.js +84 -0
- package/src/login.d.ts +21 -0
- package/src/login.js +126 -0
- package/src/manual.d.ts +61 -0
- package/src/manual.js +425 -0
- package/src/mcp-config.d.ts +40 -0
- package/src/mcp-config.js +132 -0
- package/src/net.d.ts +36 -0
- package/src/net.js +122 -0
- package/src/pine.d.ts +32 -0
- package/src/pine.js +142 -0
- package/src/router.d.ts +56 -0
- package/src/router.js +164 -0
- package/src/sandbox-fs.d.ts +27 -0
- package/src/sandbox-fs.js +172 -0
- package/src/security.d.ts +121 -0
- package/src/security.js +363 -0
- package/src/skills.d.ts +67 -0
- package/src/skills.js +163 -0
- package/src/source-types.d.ts +61 -0
- package/src/source-types.js +1 -0
- package/src/sources/command.d.ts +4 -0
- package/src/sources/command.js +77 -0
- package/src/sources/folder.d.ts +6 -0
- package/src/sources/folder.js +116 -0
- package/src/sources/ftp.d.ts +4 -0
- package/src/sources/ftp.js +307 -0
- package/src/sources/http.d.ts +4 -0
- package/src/sources/http.js +201 -0
- package/src/sources/mqtt.d.ts +18 -0
- package/src/sources/mqtt.js +97 -0
- package/src/sources/telegram.d.ts +22 -0
- package/src/sources/telegram.js +269 -0
- package/src/sources/webhook.d.ts +4 -0
- package/src/sources/webhook.js +52 -0
- package/src/sources.d.ts +339 -0
- package/src/sources.js +955 -0
- package/src/store.d.ts +89 -0
- package/src/store.js +251 -0
- package/src/timers.d.ts +83 -0
- package/src/timers.js +318 -0
- package/src/tools.d.ts +6 -0
- package/src/tools.js +777 -0
- package/src/tui/app.d.ts +209 -0
- package/src/tui/app.js +2223 -0
- package/src/tui/client.d.ts +178 -0
- package/src/tui/client.js +116 -0
- package/src/tui/code-pane.d.ts +51 -0
- package/src/tui/code-pane.js +398 -0
- package/src/tui/highlight.d.ts +8 -0
- package/src/tui/highlight.js +86 -0
- package/src/tui/picture.d.ts +141 -0
- package/src/tui/picture.js +571 -0
- package/src/tui/run.d.ts +8 -0
- package/src/tui/run.js +97 -0
- package/src/tui/theme.d.ts +32 -0
- package/src/tui/theme.js +128 -0
- package/src/types.d.ts +124 -0
- package/src/types.js +1 -0
- package/src/update.d.ts +25 -0
- package/src/update.js +110 -0
- package/src/util.d.ts +32 -0
- package/src/util.js +116 -0
- package/src/watching.d.ts +20 -0
- package/src/watching.js +68 -0
- package/src/workspace-view.d.ts +38 -0
- 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 `` 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
|
+

|
|
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
|
+

|
|
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 |
|