paimon 0.2.6__tar.gz → 0.2.8__tar.gz

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 (65) hide show
  1. paimon-0.2.8/PKG-INFO +230 -0
  2. paimon-0.2.8/README.md +212 -0
  3. paimon-0.2.8/README.zh-CN.md +210 -0
  4. paimon-0.2.8/paimon/_grep_worker.py +16 -0
  5. {paimon-0.2.6 → paimon-0.2.8}/paimon/agent.py +585 -136
  6. paimon-0.2.8/paimon/agentpane.py +136 -0
  7. paimon-0.2.8/paimon/antigravity.py +422 -0
  8. {paimon-0.2.6 → paimon-0.2.8}/paimon/app.py +162 -196
  9. {paimon-0.2.6 → paimon-0.2.8}/paimon/app.tcss +46 -19
  10. paimon-0.2.8/paimon/chatgpt.py +208 -0
  11. {paimon-0.2.6 → paimon-0.2.8}/paimon/cli.py +59 -28
  12. {paimon-0.2.6 → paimon-0.2.8}/paimon/commandpane.py +36 -32
  13. {paimon-0.2.6 → paimon-0.2.8}/paimon/commands.py +125 -42
  14. {paimon-0.2.6 → paimon-0.2.8}/paimon/compaction.py +29 -63
  15. {paimon-0.2.6 → paimon-0.2.8}/paimon/config.py +126 -123
  16. {paimon-0.2.6 → paimon-0.2.8}/paimon/diff.py +1 -1
  17. {paimon-0.2.6 → paimon-0.2.8}/paimon/errors.py +1 -1
  18. {paimon-0.2.6 → paimon-0.2.8}/paimon/headless.py +48 -20
  19. paimon-0.2.8/paimon/herdr.py +145 -0
  20. paimon-0.2.8/paimon/llm.py +250 -0
  21. {paimon-0.2.6 → paimon-0.2.8}/paimon/lockfile.py +39 -3
  22. {paimon-0.2.6 → paimon-0.2.8}/paimon/login.py +98 -41
  23. paimon-0.2.8/paimon/oauth.py +234 -0
  24. {paimon-0.2.6 → paimon-0.2.8}/paimon/pane.py +199 -412
  25. {paimon-0.2.6 → paimon-0.2.8}/paimon/prompt.py +56 -29
  26. paimon-0.2.8/paimon/review.py +170 -0
  27. {paimon-0.2.6 → paimon-0.2.8}/paimon/session.py +19 -31
  28. {paimon-0.2.6 → paimon-0.2.8}/paimon/skill/SKILL.md +14 -11
  29. {paimon-0.2.6 → paimon-0.2.8}/paimon/tabs.py +17 -22
  30. {paimon-0.2.6 → paimon-0.2.8}/paimon/telemetry.py +1 -1
  31. paimon-0.2.8/paimon/textual_debug.py +230 -0
  32. {paimon-0.2.6 → paimon-0.2.8}/paimon/tools.py +612 -606
  33. paimon-0.2.8/paimon/transcript.py +287 -0
  34. paimon-0.2.8/paimon/turns.py +198 -0
  35. {paimon-0.2.6 → paimon-0.2.8}/paimon/ui.py +337 -12
  36. paimon-0.2.8/paimon/websearch.py +126 -0
  37. paimon-0.2.8/paimon.egg-info/PKG-INFO +230 -0
  38. {paimon-0.2.6 → paimon-0.2.8}/paimon.egg-info/SOURCES.txt +11 -5
  39. paimon-0.2.8/paimon.egg-info/requires.txt +9 -0
  40. {paimon-0.2.6 → paimon-0.2.8}/paimon.egg-info/scm_file_list.json +39 -11
  41. paimon-0.2.8/paimon.egg-info/scm_version.json +8 -0
  42. {paimon-0.2.6 → paimon-0.2.8}/pyproject.toml +5 -1
  43. paimon-0.2.6/PKG-INFO +0 -218
  44. paimon-0.2.6/README.md +0 -204
  45. paimon-0.2.6/README.zh-CN.md +0 -204
  46. paimon-0.2.6/paimon/agents.py +0 -259
  47. paimon-0.2.6/paimon/aside.py +0 -165
  48. paimon-0.2.6/paimon/jobs.py +0 -454
  49. paimon-0.2.6/paimon/llm.py +0 -108
  50. paimon-0.2.6/paimon/model_windows.py +0 -2852
  51. paimon-0.2.6/paimon/supervisor.py +0 -425
  52. paimon-0.2.6/paimon.egg-info/PKG-INFO +0 -218
  53. paimon-0.2.6/paimon.egg-info/requires.txt +0 -5
  54. paimon-0.2.6/paimon.egg-info/scm_version.json +0 -8
  55. {paimon-0.2.6 → paimon-0.2.8}/LICENSE +0 -0
  56. {paimon-0.2.6 → paimon-0.2.8}/MANIFEST.in +0 -0
  57. {paimon-0.2.6 → paimon-0.2.8}/paimon/__init__.py +0 -0
  58. {paimon-0.2.6 → paimon-0.2.8}/paimon/__main__.py +0 -0
  59. {paimon-0.2.6 → paimon-0.2.8}/paimon/mentions.py +0 -0
  60. {paimon-0.2.6 → paimon-0.2.8}/paimon/retry.py +0 -0
  61. {paimon-0.2.6 → paimon-0.2.8}/paimon/skills.py +0 -0
  62. {paimon-0.2.6 → paimon-0.2.8}/paimon.egg-info/dependency_links.txt +0 -0
  63. {paimon-0.2.6 → paimon-0.2.8}/paimon.egg-info/entry_points.txt +0 -0
  64. {paimon-0.2.6 → paimon-0.2.8}/paimon.egg-info/top_level.txt +0 -0
  65. {paimon-0.2.6 → paimon-0.2.8}/setup.cfg +0 -0
paimon-0.2.8/PKG-INFO ADDED
@@ -0,0 +1,230 @@
1
+ Metadata-Version: 2.4
2
+ Name: paimon
3
+ Version: 0.2.8
4
+ Summary: A minimal code agent built on pydantic-ai + textual
5
+ Requires-Python: >=3.10
6
+ Description-Content-Type: text/markdown
7
+ License-File: LICENSE
8
+ Requires-Dist: httpx>=0.27
9
+ Requires-Dist: genai-prices>=0.1.6
10
+ Requires-Dist: pydantic-ai-slim[anthropic,google,openai]>=2.54.0
11
+ Requires-Dist: textual>=8.2.7
12
+ Requires-Dist: textual-serve>=1.1.3
13
+ Requires-Dist: pyyaml>=6
14
+ Requires-Dist: pydantic>=2.10
15
+ Requires-Dist: typing-extensions>=4.12
16
+ Requires-Dist: ddgs>=9
17
+ Dynamic: license-file
18
+
19
+ # Paimon
20
+
21
+ ![Paimon](https://automaton-media.com/wp-content/uploads/2020/10/20201019-140524-header.jpg)
22
+
23
+ English | [简体中文](README.zh-CN.md)
24
+
25
+ Paimon is a coding agent that lives in your terminal. It reads and edits files in the current directory and runs commands. It also runs headless and imports as a library, so a stronger agent or a program of your own can drive it.
26
+
27
+ ## Install
28
+
29
+ ```bash
30
+ uv tool install paimon # or: pip install paimon
31
+ ```
32
+
33
+ ## Getting started
34
+
35
+ ```bash
36
+ paimon
37
+ ```
38
+
39
+ Or run it without installing anything:
40
+
41
+ ```bash
42
+ uvx paimon
43
+ ```
44
+
45
+ The first launch asks for a provider, its API base and key, then a model. Then just type what you want done. Write `@path/to/file` in a prompt to hand a file to the agent.
46
+
47
+ While it runs: `Shift+Tab` switches how much the agent may do on its own (**read** only reads, **auto** edits inside the working directory and has a second model call approve everything else, **yolo** checks nothing and is the default), `Esc` interrupts the current turn, `Ctrl+P` opens the command palette, `Ctrl+C` quits. A line starting with `!` runs in a shell instead of being sent, and Paimon sees what it printed. `!!` keeps it to yourself.
48
+
49
+ `Ctrl+T` opens another session in a pane of its own, `Ctrl+W` closes one, `Ctrl+PageUp` and `Ctrl+PageDown` move between them, and `Ctrl+G` jumps to a pane waiting for permission. Paimon can work in parallel: ask for two independent things and it starts a second agent in a tab of its own, where you can watch it work and answer what it asks, and whose answer comes back into the conversation when it is done. Closing the tab stops it. It can also leave a command running in a tab, a dev server or a watcher, instead of holding up a turn.
50
+
51
+ ## Web search
52
+
53
+ Paimon can search the web without an API key. It asks in no permission mode, because a search changes nothing on your machine. Searches go through [ddgs](https://github.com/deedy5/ddgs), a metasearch library, so a query may be sent to any of several public search engines. `--no-web-search` takes the tool away for a run.
54
+
55
+ ## Skills
56
+
57
+ Paimon loads [Agent Skills](https://agentskills.io) from `~/.config/paimon/skills`, `~/.agents/skills` and every `.agents/skills` from the working directory up to the repository root. Only each skill's name and description go into the system prompt; the model reads the `SKILL.md` when a task matches, and `/skill:name args` sends it explicitly (the `/` command palette lists them). More locations go in `config.json` as `"skills": ["~/.claude/skills"]` or on the command line with `--skill PATH`; `--no-skills` skips the default locations. When two skills share a name, explicit paths beat the project's, which beat the global ones.
58
+
59
+ ## Using Paimon as a subagent
60
+
61
+ Frontier models are good at planning and reviewing; the steps in between are often mechanical. Point Paimon at a cheaper model and let Claude Code or Codex write the plan and check the result. Log in to that model's provider once, then name the model on each run:
62
+
63
+ ```bash
64
+ paimon login zai --api-key-env ZAI_API_KEY
65
+ paimon --model zai:glm-4.7 -p "apply the plan in PLAN.md" --mode auto --output-format result
66
+ ```
67
+
68
+ The bundled skill teaches the calling agent this workflow:
69
+
70
+ ```bash
71
+ paimon install-skill # into Claude Code (~/.claude/skills/paimon)
72
+ paimon install-skill --target codex # into Codex; --dest DIR for anywhere else
73
+ npx skills add aisk/paimon # the same skill, via skills.sh
74
+ ```
75
+
76
+ ## Using Paimon as a library
77
+
78
+ The agent loop is importable, so a Python program can drive it without going through the CLI. `Agent.open()` starts or resumes a session and `agent.run()` yields typed events, one per text chunk, tool call and turn end, which the caller renders or filters however it likes:
79
+
80
+ ```python
81
+ import asyncio
82
+
83
+ from paimon.agent import Agent, TextDelta
84
+
85
+ async def main():
86
+ agent = Agent.open(mode="auto")
87
+ async for event in agent.run("summarize the tests in this directory"):
88
+ if isinstance(event, TextDelta):
89
+ print(event.text, end="", flush=True)
90
+
91
+ asyncio.run(main())
92
+ ```
93
+
94
+ `Agent.open()` also takes a working directory, an async `confirm` callback for the permission prompts that are left, and a `toolset` to hand the model fewer tools or tools of your own. An agent holds its session until it goes away; to give it back at a definite moment, call `close()` or use the agent as a context manager. It writes the same session files as the CLI, so a run started in code can be resumed later with `paimon -r`.
95
+
96
+ ## Sessions
97
+
98
+ Every conversation is saved, and long ones are summarized in place near the context limit. Paimon prints the command that brings a session back when you leave:
99
+
100
+ ```bash
101
+ paimon -r # choose a session started in this directory
102
+ paimon -r a1b2c3 # resume one by id
103
+ paimon -c # resume the most recent one
104
+ paimon sessions # list them (--json for machines)
105
+ paimon log a1b2c3 # what a session did, one line per event
106
+ ```
107
+
108
+ ## Other ways to run it
109
+
110
+ ```bash
111
+ paimon --mode read # start in a more cautious permission mode (yolo is the default)
112
+ paimon --strict # hold every command, even read-only ones
113
+ paimon --no-web-search # take the web search tool away for this run
114
+ paimon --web # the same UI in a browser (--port, default 8000)
115
+ paimon -p "what does cli.py do?" # one answer on stdout, no UI
116
+ cat log.txt | paimon -p "summarize this"
117
+ paimon --model zai:glm-4.7 # this model for this run only
118
+ ```
119
+
120
+ `-p` never stops to ask, so with the default `yolo` mode it can already write files and run commands. Add `--output-format result` for a single JSON object with the outcome, which is what a calling program should read. `paimon --help` lists the rest.
121
+
122
+ Inside a [Herdr](https://herdr.dev) pane the UI reports its state and resume command to Herdr on its own, with nothing to install.
123
+
124
+ ### Debugging the Textual UI
125
+
126
+ Start with `paimon --textual-debug` (also works with `--web`) when Paimon needs to diagnose its own layout or styling. It is off by default. When enabled, the model receives five tools scoped to the live UI process:
127
+
128
+ - `textual_inspect` queries the DOM by CSS selector and reports geometry, pseudo-classes, focus and computed CSS;
129
+ - `textual_eval` / `textual_exec` run Python in the UI process, including private APIs, persistent variables and top-level `await`;
130
+ - `textual_apply_css` layers on CSS and relayouts immediately;
131
+ - `textual_screenshot` exports the current render to SVG.
132
+
133
+ You can ask it, for example, to “find why `#prompt` is narrower than expected, try a live fix, and take a screenshot.” `textual_exec` deliberately provides unrestricted in-process Python and can change any Textual or Paimon state. Use this development option only with trusted prompts. Live changes last only until exit; after confirming a fix, ask Paimon to edit the source.
134
+
135
+ ## Configuration
136
+
137
+ Model settings are kept in `~/.config/paimon/config.json`, written by the first launch or by `paimon login`. Each provider keeps its own credentials, and `paimon model provider:name` or Switch model in the command palette picks the model to run on. A ChatGPT plan works in place of an API key: `paimon login chatgpt` signs in through the browser. `paimon login antigravity` does the same for an Antigravity plan, which Google does not support outside Antigravity: Paimon signs in as the Antigravity client and calls an internal endpoint, so the account may be limited or closed, and the risk is yours. Sessions live in `~/.local/share/paimon/sessions/`.
138
+
139
+ Read and auto modes run a small set of clearly read-only commands (`ls`, `cat`, `git status`, …) on their own; `--strict` turns that off, and on Windows, where cmd.exe runs the commands, none are recognized. Read mode refuses everything else. Auto mode asks a reviewer model, which sees your messages and the agent's tool calls but not its reasoning or any tool output, and answers allow or block. It asks you instead when the reviewer cannot be reached or has blocked three calls in a row, and under `-p` those are refusals. Current models review with their provider's fast tier by default (GPT with Luna, Claude with Haiku, GLM with Flash) and the rest review with themselves; set `"review_model": "provider:name"` in the config to choose. **All of this is a guardrail against agent mistakes, not a security boundary.** For real isolation, run Paimon inside a container or VM.
140
+
141
+ ## Architecture
142
+
143
+ `Agent.run` is a UI-agnostic stream of events; the TUI, `--web` and headless mode are three renderers over that one stream. An agent holds the subagents and background commands it starts. A subagent is another `Agent` running as a task in the same process, and its answer is delivered to its parent as a message. A UI that wants to show a job hooks `Agent.open_job`, which is how the TUI gives each one a pane.
144
+
145
+ ```mermaid
146
+ flowchart TD
147
+ subgraph entry["Entry points"]
148
+ CLI["cli.py"]
149
+ Commands["commands.py<br/>status / login / model / sessions"]
150
+ Headless["headless.py<br/>-p, one-shot"]
151
+ App["app.py<br/>Textual TUI / --web"]
152
+ end
153
+
154
+ subgraph tui["TUI widgets"]
155
+ Pane["pane.py<br/>SessionPane"]
156
+ AgentPane["agentpane.py<br/>subagent pane"]
157
+ CommandPane["commandpane.py<br/>background command pane"]
158
+ Transcript["transcript.py<br/>conversation log, event renderer"]
159
+ Tabs["tabs.py<br/>pane strip"]
160
+ Login["login.py<br/>provider / key"]
161
+ UIWidgets["ui.py<br/>prompt input, confirmations"]
162
+ Diff["diff.py<br/>side-by-side diff rendering"]
163
+ end
164
+
165
+ subgraph core["Agent loop"]
166
+ AgentLoop["agent.py<br/>Agent.run()"]
167
+ LLM["llm.py<br/>build_model()"]
168
+ PromptMod["prompt.py<br/>system prompt"]
169
+ ToolsMod["tools.py<br/>tool REGISTRY"]
170
+ SessionMod["session.py<br/>JSONL persistence"]
171
+ Compaction["compaction.py"]
172
+ Retry["retry.py"]
173
+ Mentions["mentions.py<br/>@path expansion"]
174
+ Jobs["turns.py<br/>turn driver"]
175
+ end
176
+
177
+ subgraph support["Config & skills"]
178
+ Config["config.py<br/>settings, credentials"]
179
+ Skills["skills.py<br/>Agent Skills discovery"]
180
+ end
181
+
182
+ CLI --> Commands
183
+ CLI --> Headless
184
+ CLI --> App
185
+ CLI --> Config
186
+
187
+ Headless --> AgentLoop
188
+ Headless --> Mentions
189
+
190
+ App --> Pane
191
+ App --> AgentPane
192
+ App --> CommandPane
193
+ App --> Tabs
194
+ App --> Login
195
+ App --> Config
196
+
197
+ Pane --> Jobs
198
+ Pane --> AgentLoop
199
+ Pane --> Diff
200
+ Pane --> Transcript
201
+ Pane --> UIWidgets
202
+ Pane --> LLM
203
+ AgentPane --> Pane
204
+ AgentPane --> Transcript
205
+ CommandPane --> Pane
206
+ Transcript --> UIWidgets
207
+ UIWidgets --> Diff
208
+
209
+ AgentLoop --> LLM
210
+ AgentLoop --> PromptMod
211
+ AgentLoop --> ToolsMod
212
+ AgentLoop --> SessionMod
213
+ AgentLoop --> Compaction
214
+ AgentLoop --> Retry
215
+ AgentLoop --> Mentions
216
+ AgentLoop -. "spawn_agent" .-> AgentLoop
217
+ AgentLoop -. "spawn_agent" .-> AgentPane
218
+ AgentLoop -. "run_background" .-> CommandPane
219
+
220
+ PromptMod --> Skills
221
+
222
+ Jobs --> AgentLoop
223
+
224
+ Skills --> Config
225
+ Config --> LLM
226
+ ```
227
+
228
+ ## Telemetry
229
+
230
+ Each launch sends one anonymous event to Google Analytics: a random install id, the launch mode, the version, the OS name, the language from `LC_ALL`/`LANG`, whether this is the first launch, and the configured provider and model name. Nothing from your sessions, prompts, files or credentials is included. Set `PAIMON_NO_TELEMETRY=1` or `DO_NOT_TRACK=1` to turn it off.
paimon-0.2.8/README.md ADDED
@@ -0,0 +1,212 @@
1
+ # Paimon
2
+
3
+ ![Paimon](https://automaton-media.com/wp-content/uploads/2020/10/20201019-140524-header.jpg)
4
+
5
+ English | [简体中文](README.zh-CN.md)
6
+
7
+ Paimon is a coding agent that lives in your terminal. It reads and edits files in the current directory and runs commands. It also runs headless and imports as a library, so a stronger agent or a program of your own can drive it.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ uv tool install paimon # or: pip install paimon
13
+ ```
14
+
15
+ ## Getting started
16
+
17
+ ```bash
18
+ paimon
19
+ ```
20
+
21
+ Or run it without installing anything:
22
+
23
+ ```bash
24
+ uvx paimon
25
+ ```
26
+
27
+ The first launch asks for a provider, its API base and key, then a model. Then just type what you want done. Write `@path/to/file` in a prompt to hand a file to the agent.
28
+
29
+ While it runs: `Shift+Tab` switches how much the agent may do on its own (**read** only reads, **auto** edits inside the working directory and has a second model call approve everything else, **yolo** checks nothing and is the default), `Esc` interrupts the current turn, `Ctrl+P` opens the command palette, `Ctrl+C` quits. A line starting with `!` runs in a shell instead of being sent, and Paimon sees what it printed. `!!` keeps it to yourself.
30
+
31
+ `Ctrl+T` opens another session in a pane of its own, `Ctrl+W` closes one, `Ctrl+PageUp` and `Ctrl+PageDown` move between them, and `Ctrl+G` jumps to a pane waiting for permission. Paimon can work in parallel: ask for two independent things and it starts a second agent in a tab of its own, where you can watch it work and answer what it asks, and whose answer comes back into the conversation when it is done. Closing the tab stops it. It can also leave a command running in a tab, a dev server or a watcher, instead of holding up a turn.
32
+
33
+ ## Web search
34
+
35
+ Paimon can search the web without an API key. It asks in no permission mode, because a search changes nothing on your machine. Searches go through [ddgs](https://github.com/deedy5/ddgs), a metasearch library, so a query may be sent to any of several public search engines. `--no-web-search` takes the tool away for a run.
36
+
37
+ ## Skills
38
+
39
+ Paimon loads [Agent Skills](https://agentskills.io) from `~/.config/paimon/skills`, `~/.agents/skills` and every `.agents/skills` from the working directory up to the repository root. Only each skill's name and description go into the system prompt; the model reads the `SKILL.md` when a task matches, and `/skill:name args` sends it explicitly (the `/` command palette lists them). More locations go in `config.json` as `"skills": ["~/.claude/skills"]` or on the command line with `--skill PATH`; `--no-skills` skips the default locations. When two skills share a name, explicit paths beat the project's, which beat the global ones.
40
+
41
+ ## Using Paimon as a subagent
42
+
43
+ Frontier models are good at planning and reviewing; the steps in between are often mechanical. Point Paimon at a cheaper model and let Claude Code or Codex write the plan and check the result. Log in to that model's provider once, then name the model on each run:
44
+
45
+ ```bash
46
+ paimon login zai --api-key-env ZAI_API_KEY
47
+ paimon --model zai:glm-4.7 -p "apply the plan in PLAN.md" --mode auto --output-format result
48
+ ```
49
+
50
+ The bundled skill teaches the calling agent this workflow:
51
+
52
+ ```bash
53
+ paimon install-skill # into Claude Code (~/.claude/skills/paimon)
54
+ paimon install-skill --target codex # into Codex; --dest DIR for anywhere else
55
+ npx skills add aisk/paimon # the same skill, via skills.sh
56
+ ```
57
+
58
+ ## Using Paimon as a library
59
+
60
+ The agent loop is importable, so a Python program can drive it without going through the CLI. `Agent.open()` starts or resumes a session and `agent.run()` yields typed events, one per text chunk, tool call and turn end, which the caller renders or filters however it likes:
61
+
62
+ ```python
63
+ import asyncio
64
+
65
+ from paimon.agent import Agent, TextDelta
66
+
67
+ async def main():
68
+ agent = Agent.open(mode="auto")
69
+ async for event in agent.run("summarize the tests in this directory"):
70
+ if isinstance(event, TextDelta):
71
+ print(event.text, end="", flush=True)
72
+
73
+ asyncio.run(main())
74
+ ```
75
+
76
+ `Agent.open()` also takes a working directory, an async `confirm` callback for the permission prompts that are left, and a `toolset` to hand the model fewer tools or tools of your own. An agent holds its session until it goes away; to give it back at a definite moment, call `close()` or use the agent as a context manager. It writes the same session files as the CLI, so a run started in code can be resumed later with `paimon -r`.
77
+
78
+ ## Sessions
79
+
80
+ Every conversation is saved, and long ones are summarized in place near the context limit. Paimon prints the command that brings a session back when you leave:
81
+
82
+ ```bash
83
+ paimon -r # choose a session started in this directory
84
+ paimon -r a1b2c3 # resume one by id
85
+ paimon -c # resume the most recent one
86
+ paimon sessions # list them (--json for machines)
87
+ paimon log a1b2c3 # what a session did, one line per event
88
+ ```
89
+
90
+ ## Other ways to run it
91
+
92
+ ```bash
93
+ paimon --mode read # start in a more cautious permission mode (yolo is the default)
94
+ paimon --strict # hold every command, even read-only ones
95
+ paimon --no-web-search # take the web search tool away for this run
96
+ paimon --web # the same UI in a browser (--port, default 8000)
97
+ paimon -p "what does cli.py do?" # one answer on stdout, no UI
98
+ cat log.txt | paimon -p "summarize this"
99
+ paimon --model zai:glm-4.7 # this model for this run only
100
+ ```
101
+
102
+ `-p` never stops to ask, so with the default `yolo` mode it can already write files and run commands. Add `--output-format result` for a single JSON object with the outcome, which is what a calling program should read. `paimon --help` lists the rest.
103
+
104
+ Inside a [Herdr](https://herdr.dev) pane the UI reports its state and resume command to Herdr on its own, with nothing to install.
105
+
106
+ ### Debugging the Textual UI
107
+
108
+ Start with `paimon --textual-debug` (also works with `--web`) when Paimon needs to diagnose its own layout or styling. It is off by default. When enabled, the model receives five tools scoped to the live UI process:
109
+
110
+ - `textual_inspect` queries the DOM by CSS selector and reports geometry, pseudo-classes, focus and computed CSS;
111
+ - `textual_eval` / `textual_exec` run Python in the UI process, including private APIs, persistent variables and top-level `await`;
112
+ - `textual_apply_css` layers on CSS and relayouts immediately;
113
+ - `textual_screenshot` exports the current render to SVG.
114
+
115
+ You can ask it, for example, to “find why `#prompt` is narrower than expected, try a live fix, and take a screenshot.” `textual_exec` deliberately provides unrestricted in-process Python and can change any Textual or Paimon state. Use this development option only with trusted prompts. Live changes last only until exit; after confirming a fix, ask Paimon to edit the source.
116
+
117
+ ## Configuration
118
+
119
+ Model settings are kept in `~/.config/paimon/config.json`, written by the first launch or by `paimon login`. Each provider keeps its own credentials, and `paimon model provider:name` or Switch model in the command palette picks the model to run on. A ChatGPT plan works in place of an API key: `paimon login chatgpt` signs in through the browser. `paimon login antigravity` does the same for an Antigravity plan, which Google does not support outside Antigravity: Paimon signs in as the Antigravity client and calls an internal endpoint, so the account may be limited or closed, and the risk is yours. Sessions live in `~/.local/share/paimon/sessions/`.
120
+
121
+ Read and auto modes run a small set of clearly read-only commands (`ls`, `cat`, `git status`, …) on their own; `--strict` turns that off, and on Windows, where cmd.exe runs the commands, none are recognized. Read mode refuses everything else. Auto mode asks a reviewer model, which sees your messages and the agent's tool calls but not its reasoning or any tool output, and answers allow or block. It asks you instead when the reviewer cannot be reached or has blocked three calls in a row, and under `-p` those are refusals. Current models review with their provider's fast tier by default (GPT with Luna, Claude with Haiku, GLM with Flash) and the rest review with themselves; set `"review_model": "provider:name"` in the config to choose. **All of this is a guardrail against agent mistakes, not a security boundary.** For real isolation, run Paimon inside a container or VM.
122
+
123
+ ## Architecture
124
+
125
+ `Agent.run` is a UI-agnostic stream of events; the TUI, `--web` and headless mode are three renderers over that one stream. An agent holds the subagents and background commands it starts. A subagent is another `Agent` running as a task in the same process, and its answer is delivered to its parent as a message. A UI that wants to show a job hooks `Agent.open_job`, which is how the TUI gives each one a pane.
126
+
127
+ ```mermaid
128
+ flowchart TD
129
+ subgraph entry["Entry points"]
130
+ CLI["cli.py"]
131
+ Commands["commands.py<br/>status / login / model / sessions"]
132
+ Headless["headless.py<br/>-p, one-shot"]
133
+ App["app.py<br/>Textual TUI / --web"]
134
+ end
135
+
136
+ subgraph tui["TUI widgets"]
137
+ Pane["pane.py<br/>SessionPane"]
138
+ AgentPane["agentpane.py<br/>subagent pane"]
139
+ CommandPane["commandpane.py<br/>background command pane"]
140
+ Transcript["transcript.py<br/>conversation log, event renderer"]
141
+ Tabs["tabs.py<br/>pane strip"]
142
+ Login["login.py<br/>provider / key"]
143
+ UIWidgets["ui.py<br/>prompt input, confirmations"]
144
+ Diff["diff.py<br/>side-by-side diff rendering"]
145
+ end
146
+
147
+ subgraph core["Agent loop"]
148
+ AgentLoop["agent.py<br/>Agent.run()"]
149
+ LLM["llm.py<br/>build_model()"]
150
+ PromptMod["prompt.py<br/>system prompt"]
151
+ ToolsMod["tools.py<br/>tool REGISTRY"]
152
+ SessionMod["session.py<br/>JSONL persistence"]
153
+ Compaction["compaction.py"]
154
+ Retry["retry.py"]
155
+ Mentions["mentions.py<br/>@path expansion"]
156
+ Jobs["turns.py<br/>turn driver"]
157
+ end
158
+
159
+ subgraph support["Config & skills"]
160
+ Config["config.py<br/>settings, credentials"]
161
+ Skills["skills.py<br/>Agent Skills discovery"]
162
+ end
163
+
164
+ CLI --> Commands
165
+ CLI --> Headless
166
+ CLI --> App
167
+ CLI --> Config
168
+
169
+ Headless --> AgentLoop
170
+ Headless --> Mentions
171
+
172
+ App --> Pane
173
+ App --> AgentPane
174
+ App --> CommandPane
175
+ App --> Tabs
176
+ App --> Login
177
+ App --> Config
178
+
179
+ Pane --> Jobs
180
+ Pane --> AgentLoop
181
+ Pane --> Diff
182
+ Pane --> Transcript
183
+ Pane --> UIWidgets
184
+ Pane --> LLM
185
+ AgentPane --> Pane
186
+ AgentPane --> Transcript
187
+ CommandPane --> Pane
188
+ Transcript --> UIWidgets
189
+ UIWidgets --> Diff
190
+
191
+ AgentLoop --> LLM
192
+ AgentLoop --> PromptMod
193
+ AgentLoop --> ToolsMod
194
+ AgentLoop --> SessionMod
195
+ AgentLoop --> Compaction
196
+ AgentLoop --> Retry
197
+ AgentLoop --> Mentions
198
+ AgentLoop -. "spawn_agent" .-> AgentLoop
199
+ AgentLoop -. "spawn_agent" .-> AgentPane
200
+ AgentLoop -. "run_background" .-> CommandPane
201
+
202
+ PromptMod --> Skills
203
+
204
+ Jobs --> AgentLoop
205
+
206
+ Skills --> Config
207
+ Config --> LLM
208
+ ```
209
+
210
+ ## Telemetry
211
+
212
+ Each launch sends one anonymous event to Google Analytics: a random install id, the launch mode, the version, the OS name, the language from `LC_ALL`/`LANG`, whether this is the first launch, and the configured provider and model name. Nothing from your sessions, prompts, files or credentials is included. Set `PAIMON_NO_TELEMETRY=1` or `DO_NOT_TRACK=1` to turn it off.
@@ -0,0 +1,210 @@
1
+ # Paimon
2
+
3
+ ![Paimon](https://automaton-media.com/wp-content/uploads/2020/10/20201019-140524-header.jpg)
4
+
5
+ [English](README.md) | 简体中文
6
+
7
+ Paimon 是一个终端里的 coding agent。它读写当前目录下的文件,执行命令。它也支持无头运行,还可以作为库导入,由更强的 agent 或者你自己的程序来驱动。
8
+
9
+ ## 安装
10
+
11
+ ```bash
12
+ uv tool install paimon # 或者:pip install paimon
13
+ ```
14
+
15
+ ## 快速开始
16
+
17
+ ```bash
18
+ paimon
19
+ ```
20
+
21
+ 或者不安装直接运行:
22
+
23
+ ```bash
24
+ uvx paimon
25
+ ```
26
+
27
+ 首次启动会询问 provider 及其 API base 和 key,然后选择模型。之后输入要完成的任务即可。在提示中写 `@path/to/file` 可以把文件提供给 agent。
28
+
29
+ 运行时:`Shift+Tab` 切换 agent 的自主程度(**read** 只读,**auto** 放行工作目录内的编辑,其余操作交给另一次模型调用审批,**yolo** 不做任何检查,也是默认值),`Esc` 打断当前回合,`Ctrl+P` 打开命令面板,`Ctrl+C` 退出。以 `!` 开头的一行不发给模型,而是直接在 shell 里执行,输出 Paimon 也能看到。`!!` 则不告诉它。
30
+
31
+ `Ctrl+T` 在新 pane 里打开另一个会话,`Ctrl+W` 关闭当前 pane,`Ctrl+PageUp` 和 `Ctrl+PageDown` 在 pane 之间切换,`Ctrl+G` 跳到正在等待授权的 pane。Paimon 也能并行干活:让它同时做两件互不相干的事,它会在单独的 tab 里起第二个 agent,你可以在那里看它干活、回答它的授权请求,做完后结果会回到当前会话里,关掉 tab 就是停掉它。它也能把一条命令留在 tab 里跑,比如开发服务器或者文件监视,不占着当前回合。
32
+
33
+ ## 网页搜索
34
+
35
+ Paimon 可以搜索网页,不需要 API key。任何权限模式下都不会询问,因为搜索不会改动本机的任何东西。搜索通过元搜索库 [ddgs](https://github.com/deedy5/ddgs) 进行,所以一条 query 可能被发往多个公共搜索引擎中的任意一个。`--no-web-search` 可以在本次运行中去掉这个工具。
36
+
37
+ ## Skills
38
+
39
+ Paimon 会从 `~/.config/paimon/skills`、`~/.agents/skills` 以及工作目录到仓库根之间每一层的 `.agents/skills` 加载 [Agent Skills](https://agentskills.io)。system prompt 里只放每个 skill 的名字和描述,任务匹配时模型自己去读 `SKILL.md`,也可以用 `/skill:name 参数` 显式发送(`/` 命令面板里列出了它们)。其他位置可以写进 `config.json` 的 `"skills": ["~/.claude/skills"]`,或者用命令行参数 `--skill PATH`;`--no-skills` 跳过默认位置。同名时显式指定的优先于项目的,项目的优先于全局的。
40
+
41
+ ## 当作 subagent 使用
42
+
43
+ 前沿模型擅长制定计划和验收结果,中间的执行步骤往往比较机械。让 Paimon 使用成本较低的模型执行,由 Claude Code 或 Codex 制定计划并检查结果。先登录该模型的 provider,之后每次运行时指定模型:
44
+
45
+ ```bash
46
+ paimon login zai --api-key-env ZAI_API_KEY
47
+ paimon --model zai:glm-4.7 -p "apply the plan in PLAN.md" --mode auto --output-format result
48
+ ```
49
+
50
+ 自带的 skill 会向调用方 agent 说明这套流程:
51
+
52
+ ```bash
53
+ paimon install-skill # 安装到 Claude Code(~/.claude/skills/paimon)
54
+ paimon install-skill --target codex # 安装到 Codex;--dest DIR 安装到任意目录
55
+ npx skills add aisk/paimon # 通过 skills.sh 安装同一个 skill
56
+ ```
57
+
58
+ ## 当作库使用
59
+
60
+ agent 循环本身是可以导入的,Python 程序不必经过 CLI 就能驱动它。`Agent.open()` 新建或恢复一个会话,`agent.run()` 产出类型化的事件,每段文本、每次工具调用、每个回合结束各一个,调用方自己决定怎么渲染或过滤:
61
+
62
+ ```python
63
+ import asyncio
64
+
65
+ from paimon.agent import Agent, TextDelta
66
+
67
+ async def main():
68
+ agent = Agent.open(mode="auto")
69
+ async for event in agent.run("summarize the tests in this directory"):
70
+ if isinstance(event, TextDelta):
71
+ print(event.text, end="", flush=True)
72
+
73
+ asyncio.run(main())
74
+ ```
75
+
76
+ `Agent.open()` 还接受工作目录、用于剩余权限确认的异步 `confirm` 回调,以及 `toolset`,可以只给模型一部分工具,或者换成你自己的工具。agent 被回收时会交还会话,想在确定的时刻交还就调 `close()`,或者把它当上下文管理器用。它写的会话文件和 CLI 一样,所以代码里跑出来的会话之后可以用 `paimon -r` 恢复。
77
+
78
+ ## 会话
79
+
80
+ 每次对话都会保存,很长的对话在接近上下文上限时会被原地总结。退出时 Paimon 会打印恢复该会话的命令:
81
+
82
+ ```bash
83
+ paimon -r # 从当前目录的会话中选择
84
+ paimon -r a1b2c3 # 按 id 恢复
85
+ paimon -c # 恢复最近一个会话
86
+ paimon sessions # 列出会话(--json 输出机器可读格式)
87
+ paimon log a1b2c3 # 查看会话做了什么,每个事件一行
88
+ ```
89
+
90
+ ## 其他运行方式
91
+
92
+ ```bash
93
+ paimon --mode read # 以更谨慎的权限模式启动(默认为 yolo)
94
+ paimon --strict # 不再放行只读命令
95
+ paimon --no-web-search # 本次运行不提供网页搜索工具
96
+ paimon --web # 在浏览器中使用同一套 UI(--port,默认 8000)
97
+ paimon -p "what does cli.py do?" # 直接在 stdout 输出回答,不启动 UI
98
+ cat log.txt | paimon -p "summarize this"
99
+ paimon --model zai:glm-4.7 # 仅本次运行使用该模型
100
+ ```
101
+
102
+ `-p` 不会停下来询问,配合默认的 `yolo` 模式,它已经可以修改文件和执行命令。加 `--output-format result` 会输出一个包含结果的 JSON 对象,调用方程序读这个就够了。其余选项见 `paimon --help`。
103
+
104
+ ### 调试 Textual UI
105
+
106
+ 需要让 Paimon 排查自己的布局或样式时,用 `paimon --textual-debug` 启动(也可与 `--web` 一起使用)。默认不启用;开启后模型会多出五个只作用于当前 UI 进程的工具:
107
+
108
+ - `textual_inspect`:按 CSS selector 查看 DOM、geometry、pseudo-classes、焦点和 computed CSS;
109
+ - `textual_eval` / `textual_exec`:在 UI 进程里运行 Python,支持 private API、持久变量和 top-level `await`;
110
+ - `textual_apply_css`:动态叠加 CSS 并立即重新布局;
111
+ - `textual_screenshot`:把当前渲染结果导出成 SVG。
112
+
113
+ 例如可以直接让它“检查 `#prompt` 为什么比预期窄,动态试一个修复并截图”。`textual_exec` 有意提供完整的进程内 Python 能力,能修改任意 Textual/Paimon 状态;这个选项只适合开发调试,不应对不受信任的 prompt 开启。动态修改只保留到进程退出,确定修复后仍需让 Paimon编辑源码。
114
+
115
+ ## 配置
116
+
117
+ 模型设置保存在 `~/.config/paimon/config.json`,由首次启动或 `paimon login` 写入。每个 provider 各自保存凭据,用 `paimon model provider:name` 或命令面板里的 Switch model 选择要用的模型。也可以用 ChatGPT 订阅代替 API key,`paimon login chatgpt` 会通过浏览器登录。会话存放在 `~/.local/share/paimon/sessions/`。
118
+
119
+ read 和 auto 模式会直接执行一小组明确只读的命令(`ls`、`cat`、`git status` 等),`--strict` 可以关掉,Windows 上命令由 cmd.exe 执行,一条也不识别。read 模式拒绝其余所有操作。auto 模式把它们交给审批模型,它能看到你的消息和 agent 的工具调用,看不到 agent 的推理和工具输出,只回答放行或拦截。审批模型不可用或者连续拦截三次时改为询问你,`-p` 下则直接拒绝。当前的模型默认用同一家里快速档的那个来审批(GPT 用 Luna,Claude 用 Haiku,GLM 用 Flash),其余模型自己审自己,在配置里写 `"review_model": "provider:name"` 可以自行指定。**这些都是防止 agent 失误的护栏,不是安全边界。** 需要真正的隔离时,请在容器或虚拟机中运行 Paimon。
120
+
121
+ ## 架构
122
+
123
+ `Agent.run` 产出一串与 UI 无关的事件流,TUI、`--web` 和无头模式都是这条事件流的渲染器。agent 自己持有它启动的子 agent 和后台命令。子 agent 是同一进程里作为任务运行的另一个 `Agent`,它的结果会作为一条消息送回父 agent。想把 job 显示出来的 UI 挂上 `Agent.open_job` 即可,TUI 就是这样给每个 job 开 pane 的。
124
+
125
+ ```mermaid
126
+ flowchart TD
127
+ subgraph entry["入口"]
128
+ CLI["cli.py"]
129
+ Commands["commands.py<br/>status / login / model / sessions"]
130
+ Headless["headless.py<br/>-p,单次运行"]
131
+ App["app.py<br/>Textual TUI / --web"]
132
+ end
133
+
134
+ subgraph tui["TUI 组件"]
135
+ Pane["pane.py<br/>SessionPane"]
136
+ AgentPane["agentpane.py<br/>子 agent pane"]
137
+ CommandPane["commandpane.py<br/>后台命令 pane"]
138
+ Transcript["transcript.py<br/>对话记录与事件渲染"]
139
+ Tabs["tabs.py<br/>pane 标签栏"]
140
+ Login["login.py<br/>provider / key"]
141
+ UIWidgets["ui.py<br/>输入框、确认面板"]
142
+ Diff["diff.py<br/>并排 diff 渲染"]
143
+ end
144
+
145
+ subgraph core["Agent 主循环"]
146
+ AgentLoop["agent.py<br/>Agent.run()"]
147
+ LLM["llm.py<br/>build_model()"]
148
+ PromptMod["prompt.py<br/>system prompt"]
149
+ ToolsMod["tools.py<br/>工具注册表"]
150
+ SessionMod["session.py<br/>JSONL 持久化"]
151
+ Compaction["compaction.py"]
152
+ Retry["retry.py"]
153
+ Mentions["mentions.py<br/>@path 展开"]
154
+ Jobs["turns.py<br/>回合驱动"]
155
+ end
156
+
157
+ subgraph support["配置与 skills"]
158
+ Config["config.py<br/>设置、凭证"]
159
+ Skills["skills.py<br/>Agent Skills 发现"]
160
+ end
161
+
162
+ CLI --> Commands
163
+ CLI --> Headless
164
+ CLI --> App
165
+ CLI --> Config
166
+
167
+ Headless --> AgentLoop
168
+ Headless --> Mentions
169
+
170
+ App --> Pane
171
+ App --> AgentPane
172
+ App --> CommandPane
173
+ App --> Tabs
174
+ App --> Login
175
+ App --> Config
176
+
177
+ Pane --> Jobs
178
+ Pane --> AgentLoop
179
+ Pane --> Diff
180
+ Pane --> Transcript
181
+ Pane --> UIWidgets
182
+ Pane --> LLM
183
+ AgentPane --> Pane
184
+ AgentPane --> Transcript
185
+ CommandPane --> Pane
186
+ Transcript --> UIWidgets
187
+ UIWidgets --> Diff
188
+
189
+ AgentLoop --> LLM
190
+ AgentLoop --> PromptMod
191
+ AgentLoop --> ToolsMod
192
+ AgentLoop --> SessionMod
193
+ AgentLoop --> Compaction
194
+ AgentLoop --> Retry
195
+ AgentLoop --> Mentions
196
+ AgentLoop -. "spawn_agent" .-> AgentLoop
197
+ AgentLoop -. "spawn_agent" .-> AgentPane
198
+ AgentLoop -. "run_background" .-> CommandPane
199
+
200
+ PromptMod --> Skills
201
+
202
+ Jobs --> AgentLoop
203
+
204
+ Skills --> Config
205
+ Config --> LLM
206
+ ```
207
+
208
+ ## 遥测
209
+
210
+ 每次启动会向 Google Analytics 发送一条匿名事件:随机生成的安装 id、启动方式、版本号、操作系统名称、取自 `LC_ALL`/`LANG` 的语言、是否首次启动,以及配置的 provider 和模型名。会话、提示词、文件和密钥一概不上报。设置 `PAIMON_NO_TELEMETRY=1` 或 `DO_NOT_TRACK=1` 可以关闭。
@@ -0,0 +1,16 @@
1
+ """Disposable process for bounded, cancellable code searches."""
2
+
3
+ import json
4
+ import sys
5
+ from pathlib import Path
6
+
7
+ from .tools import _grep
8
+
9
+
10
+ def main() -> None:
11
+ args, cwd, sandboxed = json.load(sys.stdin)
12
+ sys.stdout.write(json.dumps(_grep(args, Path(cwd), sandboxed=sandboxed)))
13
+
14
+
15
+ if __name__ == "__main__":
16
+ main()