papilot 0.7.0 → 0.8.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 (75) hide show
  1. package/README.md +53 -100
  2. package/README.zh-CN.md +52 -97
  3. package/bin/doctor.mjs +55 -6
  4. package/bin/installation.d.mts +2 -0
  5. package/bin/installation.mjs +7 -1
  6. package/dist/assets/{_virtual_papilot-licenses-BWIsfrQh.js → _virtual_papilot-licenses-CMfCtEYQ.js} +54 -3
  7. package/dist/assets/index-BlLyIqT9.js +265 -0
  8. package/dist/assets/index-DVZtyZvu.css +1 -0
  9. package/dist/assets/latex-BarqJFtQ.svg +1 -0
  10. package/dist/assets/{math-BsEl3u6P.js → math-Bu3xEyrC.js} +1 -1
  11. package/dist/assets/pdf-view-PbkCuTZ-.js +598 -0
  12. package/dist/index.html +2 -2
  13. package/examples/papilot-paper/main.tex +1 -1
  14. package/examples/papilot-paper/preview/main.pdf +0 -0
  15. package/examples/papilot-paper/preview/main.synctex.gz +0 -0
  16. package/examples/papilot-paper/sections/02-using.tex +3 -1
  17. package/examples/papilot-paper/sections/09-agents.tex +8 -5
  18. package/package.json +18 -8
  19. package/skills/ai-review.md +18 -0
  20. package/skills/latex-warnings.md +1 -1
  21. package/src/conversation.ts +6 -0
  22. package/src/server/acp.ts +387 -0
  23. package/src/server/agents.ts +220 -43
  24. package/src/server/ai-review-routes.ts +72 -0
  25. package/src/server/ai-review.ts +38 -0
  26. package/src/server/app.ts +56 -14
  27. package/src/server/build-problems.ts +329 -0
  28. package/src/server/changes.ts +25 -2
  29. package/src/server/codex.ts +2 -1
  30. package/src/server/cursor.ts +153 -0
  31. package/src/server/desktop-demo.ts +38 -0
  32. package/src/server/latex-installation.ts +43 -0
  33. package/src/server/manuscript.ts +183 -23
  34. package/src/server/paths.ts +1 -1
  35. package/src/server/reconcile.ts +20 -0
  36. package/src/server/references.ts +70 -0
  37. package/src/server/review.ts +6 -5
  38. package/src/server/skills.ts +5 -8
  39. package/src/server/start.ts +19 -4
  40. package/src/server/store.ts +20 -2
  41. package/src/server/update-install.ts +53 -2
  42. package/src/server/update-worker.ts +6 -6
  43. package/src/server/updates.ts +49 -14
  44. package/src/server/workspace.ts +236 -41
  45. package/src/shared.ts +88 -9
  46. package/src/skill-icons.ts +18 -0
  47. package/src/skill-metadata.ts +45 -0
  48. package/vendor/latex-review/dist/web/assets/index-CXdnUjct.js +19 -0
  49. package/vendor/latex-review/dist/web/assets/index-DEz-LsGl.css +1 -0
  50. package/vendor/latex-review/dist/web/index.html +2 -2
  51. package/vendor/latex-review/src/core/bib.ts +692 -0
  52. package/vendor/latex-review/src/core/changes.ts +39 -0
  53. package/vendor/latex-review/src/core/current-commit.ts +104 -0
  54. package/vendor/latex-review/src/core/delivery.ts +62 -16
  55. package/vendor/latex-review/src/core/git.ts +2 -1
  56. package/vendor/latex-review/src/core/publish.ts +7 -1
  57. package/vendor/latex-review/src/core/reconcile.ts +101 -0
  58. package/vendor/latex-review/src/core/review.ts +6 -2
  59. package/vendor/latex-review/src/core/store.ts +1 -0
  60. package/vendor/latex-review/src/core/tasks.ts +1 -1
  61. package/vendor/latex-review/src/core/verify.ts +784 -0
  62. package/vendor/latex-review/src/core/view.ts +204 -0
  63. package/vendor/latex-review/src/render/round.ts +11 -0
  64. package/vendor/latex-review/src/server.ts +77 -1
  65. package/vendor/latex-review/src/types.ts +130 -2
  66. package/vendor/latex-review/src/web/effect-preferences.ts +10 -0
  67. package/vendor/latex-review/src/web/references.tsx +499 -0
  68. package/vendor/latex-review/src/web/review.tsx +263 -133
  69. package/vendor/latex-review/src/web/style.css +285 -0
  70. package/vendor/latex-review/src/web/zh-CN.ts +79 -2
  71. package/dist/assets/index-CIJEYlP7.js +0 -252
  72. package/dist/assets/index-hOJEruFc.css +0 -1
  73. package/dist/assets/pdf-view-Cz9_vK5U.js +0 -598
  74. package/vendor/latex-review/dist/web/assets/index-Be9CtgcZ.css +0 -1
  75. package/vendor/latex-review/dist/web/assets/index-TvMWVSaw.js +0 -19
package/README.md CHANGED
@@ -1,147 +1,100 @@
1
1
  <div align="center">
2
2
 
3
- <picture>
4
- <source media="(prefers-color-scheme: dark)" srcset="docs/assets/logo-dark.svg">
5
- <img src="docs/assets/logo.svg" width="104" height="104" alt="Papilot logo">
6
- </picture>
3
+ <h1>
4
+ <img src="docs/assets/wordmark.svg#gh-light-mode-only" width="280" height="88" alt="Papilot">
5
+ <img src="docs/assets/wordmark-dark.svg#gh-dark-mode-only" width="280" height="88" alt="Papilot">
6
+ </h1>
7
7
 
8
- <h1>Papilot</h1>
8
+ <p><b>Revise LaTeX papers with AI, and review the changes on the PDF.</b></p>
9
9
 
10
- <p><b>A Cursor-like workspace for your papers:<br>annotate and review PDFs directly without ever touching LaTeX.</b></p>
10
+ <p><a href="https://papilot.ai">Website</a> · <a href="docs/guide.md">Documentation</a> · English · <a href="README.zh-CN.md">简体中文</a></p>
11
11
 
12
- <p>English · <a href="README.zh-CN.md">简体中文</a></p>
13
-
14
- [![CI](https://github.com/cu1ch3n/papilot/actions/workflows/ci.yml/badge.svg)](https://github.com/cu1ch3n/papilot/actions/workflows/ci.yml)
12
+ [![CI](https://github.com/PapilotAI/papilot/actions/workflows/ci.yml/badge.svg)](https://github.com/PapilotAI/papilot/actions/workflows/ci.yml)
15
13
  [![npm](https://img.shields.io/npm/v/papilot?color=2563eb&logo=npm)](https://www.npmjs.com/package/papilot)
16
14
  [![License: MIT](https://img.shields.io/badge/license-MIT-2563eb)](LICENSE)
17
- [![Node.js](https://img.shields.io/badge/node-%E2%89%A522.22-5FA04E?logo=nodedotjs&logoColor=white)](https://nodejs.org)
18
- [![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org)
19
- [![React](https://img.shields.io/badge/React-19-087EA4?logo=react&logoColor=white)](https://react.dev)
20
- [![LaTeX](https://img.shields.io/badge/LaTeX-008080?logo=latex&logoColor=white)](https://www.latex-project.org)
21
- [![PRs welcome](https://img.shields.io/badge/PRs-welcome-22c55e)](https://github.com/cu1ch3n/papilot/pulls)
22
-
23
- [![Codex](https://img.shields.io/badge/Codex-supported-10A37F?logo=openai&logoColor=white)](#-agents)
24
- [![Claude](https://img.shields.io/badge/Claude-supported-D97757?logo=claude&logoColor=white)](#-agents)
25
- [![Antigravity](https://img.shields.io/badge/Antigravity-supported-4285F4?logo=google&logoColor=white)](#-agents)
26
15
 
27
- <br>
28
-
29
- <img src="docs/assets/demo.gif" alt="Papilot: describe a revision, review the typeset diff, read the PDF, annotate it and commit" width="100%">
16
+ <p><img src="docs/assets/demo.gif" alt="Papilot: mark up a passage on the PDF, hand it to an agent, and review the typeset change" width="100%"></p>
30
17
 
31
18
  </div>
32
19
 
33
- <br>
34
-
35
- You read and mark up the PDF the way a supervisor marks up a draft. An AI agent makes the edits in LaTeX, and every change comes back to you as a before-and-after of the typeset page. Nothing touches your paper until you accept it.
20
+ Papilot is a local workspace for writing and revising LaTeX papers with AI agents. Mark up passages and equations on the PDF, let an agent edit a separate copy of the source, then review the typeset changes and choose which ones to keep.
36
21
 
37
- ## ✨ Features
22
+ ## Features
38
23
 
39
24
  <table>
40
25
  <tr>
41
26
  <td width="50%" valign="top">
42
- <img src="docs/assets/feature-note-appinn.jpeg" alt="A note on a selected passage of the PDF">
43
- <h3>📝 Mark up the PDF</h3>
44
- Select a sentence or circle an equation and say what should change. SyncTeX hands the agent the exact LaTeX lines.
27
+ <a href="docs/assets/readme-note.jpg"><img src="docs/assets/readme-note.jpg" alt="Color-grouped passage notes and a boxed equation note on the PDF"></a>
28
+ <h3>Annotate the PDF</h3>
29
+ Select a sentence or box an equation and say what should change. Group related notes by color to give several passages one instruction.
45
30
  </td>
46
31
  <td width="50%" valign="top">
47
- <img src="docs/assets/feature-review-appinn.jpeg" alt="A change shown before and after, with Accept and Reject">
48
- <h3>✅ Approve change by change</h3>
49
- Each edit comes back as a typeset diff. Accept, reject or comment on it, and undo decisions before saving the result.
50
-
51
- For new tasks, accepting an edit keeps it inside the task until you finish reviewing. Then choose <strong>Apply to working directory</strong> to keep the accepted changes uncommitted, or <strong>Commit to task branch</strong> to save them on a separate <code>papilot/…</code> branch, leaving your current branch and working directory unchanged. You can edit the commit message before saving. Further rounds use the same destination; delivered decisions remain available in History.
32
+ <a href="docs/assets/readme-review.jpg"><img src="docs/assets/readme-review.jpg" alt="Before-and-after typeset diff on the page with Accept, Reject, and Comment buttons"></a>
33
+ <h3>Review each change</h3>
34
+ Compare the typeset pages before and after, including changes elsewhere such as renumbered equations. Accept, reject, or comment on each edit, and undo decisions as you review.
52
35
  </td>
53
36
  </tr>
54
37
  <tr>
55
38
  <td width="50%" valign="top">
56
- <h3>📖 Read it like a real PDF</h3>
57
- Jump to any section from the contents, search every page and zoom in sharply. Selecting text picks exactly the printed words.
39
+ <a href="docs/assets/readme-deliver.jpg"><img src="docs/assets/readme-deliver.jpg" alt="Apply accepted edits to the manuscript"></a>
40
+ <h3>Choose where edits go</h3>
41
+ Each new task works on its own copy of the paper. Review the edits, then choose **Commit to manuscript** to save this task as one commit on your current branch, or **Apply to workspace** to leave the edits uncommitted. If the same passage changed meanwhile, AI resolves the conflict automatically and asks you only when the intended content is unclear. Review the merged PDF before saving. Push from the Git panel when you are ready.
58
42
  </td>
59
43
  <td width="50%" valign="top">
60
- <h3>🌿 Commit when you're happy</h3>
61
- Diff files and commit the ones you choose, with a commit message an agent writes. Pull and push, or merge a coauthor's changes and let an agent resolve the conflicts.
44
+ <a href="docs/assets/readme-git.jpg"><img src="docs/assets/readme-git.jpg" alt="Git panel with staged files, generated commit message, and commit graph"></a>
45
+ <h3>Work with Git</h3>
46
+ Stage and commit files, pull, and push from the workspace. An agent can draft commit messages and help resolve merge conflicts; the commit graph shows each task’s worktree.
62
47
  </td>
63
48
  </tr>
64
49
  </table>
65
50
 
66
- Also: ✨ **skills** such as Polish, Proofread and Check math, one `/` away, plus your own · 🤖 **Codex, Claude or Antigravity**, each with your own login · 🌳 **every task in its own Git worktree**, up to two at once · 🌗 **light and dark** · 📱 **runs on your computer**, and you can open it from your phone.
51
+ - **[Skills](docs/guide.md#skills) and [attachments](docs/guide.md#task-attachments):** Run `/` skills to polish prose, proofread, or check math, define custom skills in Settings, and attach reference files or images to tasks and messages.
52
+ - **Reading and workspace:** Navigate manuscripts with the PDF table of contents and full-text search, with light and dark themes and an English or Chinese interface.
53
+
54
+ ## Quick start
67
55
 
68
- ## 🚀 Quick start
56
+ For the macOS and Linux desktop apps and local build instructions, see [Papilot Desktop](desktop/README.md). Desktop CI builds Apple Silicon and Intel Mac installers, plus Linux x64 AppImage and deb packages.
69
57
 
70
- > [!NOTE]
71
- > You need **Node.js 22.22+**, **Git** and a full **TeX** distribution ([MacTeX](https://www.tug.org/mactex/) or [TeX Live](https://www.tug.org/texlive/)). Revising also needs one [signed-in agent](#-agents).
58
+ Papilot requires **Node.js 22.22+**, **Git**, and a full **TeX** distribution with `latexmk` and `synctex` ([MacTeX](https://www.tug.org/mactex/) or [TeX Live](https://www.tug.org/texlive/)). Revising a paper also requires at least one configured [agent](#agents).
72
59
 
73
60
  ```sh
74
61
  npm install -g papilot
75
62
  papilot demo
76
63
  ```
77
64
 
78
- Open **http://127.0.0.1:7274** and choose **PDF** in the sidebar. The demo adds an example paper to revise: a 26-page LaTeX paper about Papilot itself, full of equations. When you are ready, run `papilot` and choose **Add paper** to select a LaTeX folder or drop a ZIP. Papilot identifies the main file, prepares Git history and tries compilation, with help for any errors during import.
79
-
80
- ## 🤖 Agents
81
-
82
- | Agent | Sign in once | How Papilot runs it |
83
- | --- | --- | --- |
84
- | **Codex** | `papilot codex login` | Through the Codex app server, in its workspace sandbox. Papilot uses your `codex` if you have one, or installs it on first start. |
85
- | **Claude** | Start `papilot` with `ANTHROPIC_API_KEY` set | Through the Claude Agent SDK, downloaded the first time. The SDK does not accept a Pro or Max subscription login. |
86
- | **Antigravity** | Install Antigravity and sign in to `agy` | `agy -p`, one process per turn, editing files only. If your region needs it, set `PAPILOT_AGY_PROXY`. |
87
-
88
- Papilot never reads, stores or forwards your credentials. An agent that isn't installed or signed in shows as unavailable.
89
-
90
- Having trouble getting started? Run `papilot doctor` for environment checks and fixes, or `papilot doctor --repo /path/to/paper` to check a paper too. Add `--json` for a machine-readable report. Diagnostics do not install software, start the server or change your paper. See the [diagnostics guide](docs/guide.md#diagnostics) for what is checked.
91
-
92
- <details>
93
- <summary><b>⚙️ Options and remote access</b></summary>
94
-
95
- <br>
96
-
97
- ```sh
98
- papilot --port 4400 # another local port
99
- papilot --data ~/papilot-data # keep state somewhere else (default ~/.papilot)
100
- papilot service install # macOS: start after login, restart if it exits
101
- npm uninstall -g papilot # remove it; your state stays in ~/.papilot until you delete it
102
- ```
103
-
104
- When a release is available, **New version** appears next to **Settings**. Click it to install from the browser. Papilot waits for tasks and PDF builds to finish, restarts, and reconnects the page. Updates keep your data and preserve the previous installation for rollback. See [Updates](docs/guide.md#updates).
105
-
106
- Papilot listens only on `127.0.0.1`. To use it from another device, publish it on your tailnet with Tailscale Serve and set `PAPILOT_PUBLIC_ORIGIN` and `PAPILOT_TAILSCALE_LOGIN`. Papilot then checks the host, the browser origin and the Tailscale identity of every request. The [guide](docs/guide.md#remote-access-with-tailscale) has the steps.
107
-
108
- To invite an advisor or co-authors, give Papilot a public HTTPS address with `PAPILOT_ACCOUNT_ORIGIN` and open **People**: each person signs in with a passkey and sees only the papers you share. Invite only people you would trust with access to the computer Papilot runs on; see [Collaborators](docs/guide.md#collaborators).
109
-
110
- </details>
111
-
112
- <details>
113
- <summary><b>🛠️ Develop</b></summary>
114
-
115
- <br>
116
-
117
- ```sh
118
- git clone https://github.com/cu1ch3n/papilot && cd papilot
119
- npm ci && npm run setup:review
120
- npm run build && npm start
121
- ```
122
-
123
- Run `npm run typecheck`, `npm test` and `npm run test:ui` before sending a pull request.
65
+ Open [http://127.0.0.1:7274](http://127.0.0.1:7274) and select **Manuscript** in the sidebar to explore the bundled example paper.
124
66
 
125
- </details>
67
+ To work on your own manuscript, run `papilot` and choose **Add paper…** to open a local LaTeX folder or ZIP archive. Papilot looks for the main `.tex` file, prepares Git history when needed, and attempts compilation. If the main file is ambiguous or the build fails, the import dialog helps you choose a file or fix the problem.
126
68
 
127
- ## 📚 Documentation
69
+ Check your setup in **Settings → Runtime**, under **Agents** or **LaTeX**. Run `papilot doctor` for environment checks, or `papilot doctor --repo /path/to/paper` to check a manuscript too ([diagnostics](docs/guide.md#diagnostics)).
128
70
 
129
- The **[guide](docs/guide.md)** covers every part of the workspace, the configuration variables, recovery after restarts, and development. The example paper in [`examples/papilot-paper`](examples/papilot-paper) explains the design in depth, with proofs. What changed in each release is in the [changelog](CHANGELOG.md).
71
+ ## Agents
130
72
 
131
- ## 💬 Community
73
+ <!-- BEGIN AGENT ICONS -->
74
+ <p align="center">
75
+ <a href="docs/guide.md#agents">
76
+ <picture>
77
+ <source media="(prefers-color-scheme: dark) and (max-width: 600px)" srcset="docs/assets/readme-agents/compact-dark.svg">
78
+ <source media="(prefers-color-scheme: dark)" srcset="docs/assets/readme-agents/desktop-dark.svg">
79
+ <source media="(max-width: 600px)" srcset="docs/assets/readme-agents/compact.svg">
80
+ <img src="docs/assets/readme-agents/desktop.svg" alt="Codex, Claude, Cursor, Copilot, Grok, Antigravity, Kimi, GLM, DeepSeek, Qwen, OpenCode" width="816">
81
+ </picture>
82
+ </a>
83
+ </p>
84
+ <!-- END AGENT ICONS -->
132
85
 
133
- - 💡 **Questions and ideas**: [Discussions](https://github.com/cu1ch3n/papilot/discussions)
134
- - 🐛 **Something broke**: [open an issue](https://github.com/cu1ch3n/papilot/issues/new/choose)
135
- - 🤝 **Want to help**: pick a [good first issue](https://github.com/cu1ch3n/papilot/labels/good%20first%20issue) and read [CONTRIBUTING.md](CONTRIBUTING.md)
86
+ You can choose an agent and model for each task. Depending on the integration, Papilot runs agents using their local CLI sign-in or provider API credentials you supply, without storing your keys.
136
87
 
137
- If Papilot saves you time on a paper, a ⭐ helps other researchers find it.
88
+ Open **Settings → Runtime → Agents** to check status, models, and setup instructions (such as `papilot codex login`). Click **Reconnect** after installing or signing in to an agent while Papilot is running, or restart `papilot` after changing environment variables. See the [agent guide](docs/guide.md#agents) for per-agent requirements and capabilities.
138
89
 
139
- <a href="https://github.com/cu1ch3n/papilot/graphs/contributors"><img src="https://contrib.rocks/image?repo=cu1ch3n/papilot" alt="Papilot contributors"></a>
90
+ ## Documentation and community
140
91
 
141
- ## 🔗 Friends
92
+ - **Documentation:** Read the [User Guide](docs/guide.md) for workspace workflows, configuration, [in-app updates](docs/guide.md#updates), [remote access with Tailscale](docs/guide.md#remote-access-with-tailscale), and [passkey sharing for collaborators](docs/guide.md#collaborators).
93
+ - **Design and history:** Explore [`examples/papilot-paper`](examples/papilot-paper) for the formal design paper and proofs, and check the [Changelog](CHANGELOG.md) for release notes.
94
+ - **Community and support:** Ask questions in [Discussions](https://github.com/PapilotAI/papilot/discussions), report bugs in [Issues](https://github.com/PapilotAI/papilot/issues), or read [CONTRIBUTING.md](CONTRIBUTING.md) to contribute.
142
95
 
143
- [LINUX DO](https://linux.do/)
96
+ ---
144
97
 
145
- ## 📄 License
98
+ [MIT](LICENSE) © 2026 PapilotAI
146
99
 
147
- [MIT](LICENSE) © 2026 Cui Chen. The bundled review engine in [`vendor/latex-review`](vendor/latex-review) is also MIT-licensed.
100
+ Friends: [LINUX DO](https://linux.do/).
package/README.zh-CN.md CHANGED
@@ -1,145 +1,100 @@
1
1
  <div align="center">
2
2
 
3
- <picture>
4
- <source media="(prefers-color-scheme: dark)" srcset="docs/assets/logo-dark.svg">
5
- <img src="docs/assets/logo.svg" width="104" height="104" alt="Papilot 标志">
6
- </picture>
3
+ <h1>
4
+ <img src="docs/assets/wordmark.svg#gh-light-mode-only" width="280" height="88" alt="Papilot">
5
+ <img src="docs/assets/wordmark-dark.svg#gh-dark-mode-only" width="280" height="88" alt="Papilot">
6
+ </h1>
7
7
 
8
- <h1>Papilot</h1>
8
+ <p><b>在 PDF 上批注和审阅,让 AI 修改 LaTeX 论文。</b></p>
9
9
 
10
- <p><b>像 Cursor 一样改论文:<br>直接在 PDF 上批注和审阅,完全不用碰 LaTeX。</b></p>
10
+ <p><a href="https://papilot.ai">官网</a> · <a href="docs/guide.md">使用指南</a> · <a href="README.md">English</a> · 简体中文</p>
11
11
 
12
- <p><a href="README.md">English</a> · 简体中文</p>
13
-
14
- [![CI](https://github.com/cu1ch3n/papilot/actions/workflows/ci.yml/badge.svg)](https://github.com/cu1ch3n/papilot/actions/workflows/ci.yml)
12
+ [![CI](https://github.com/PapilotAI/papilot/actions/workflows/ci.yml/badge.svg)](https://github.com/PapilotAI/papilot/actions/workflows/ci.yml)
15
13
  [![npm](https://img.shields.io/npm/v/papilot?color=2563eb&logo=npm)](https://www.npmjs.com/package/papilot)
16
14
  [![License: MIT](https://img.shields.io/badge/license-MIT-2563eb)](LICENSE)
17
- [![Node.js](https://img.shields.io/badge/node-%E2%89%A522.22-5FA04E?logo=nodedotjs&logoColor=white)](https://nodejs.org)
18
- [![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org)
19
- [![React](https://img.shields.io/badge/React-19-087EA4?logo=react&logoColor=white)](https://react.dev)
20
- [![LaTeX](https://img.shields.io/badge/LaTeX-008080?logo=latex&logoColor=white)](https://www.latex-project.org)
21
- [![PRs welcome](https://img.shields.io/badge/PRs-welcome-22c55e)](https://github.com/cu1ch3n/papilot/pulls)
22
-
23
- [![Codex](https://img.shields.io/badge/Codex-supported-10A37F?logo=openai&logoColor=white)](#-智能体)
24
- [![Claude](https://img.shields.io/badge/Claude-supported-D97757?logo=claude&logoColor=white)](#-智能体)
25
- [![Antigravity](https://img.shields.io/badge/Antigravity-supported-4285F4?logo=google&logoColor=white)](#-智能体)
26
-
27
- <br>
28
15
 
29
- <img src="docs/assets/demo.gif" alt="Papilot:描述修改、审阅排版后的差异、阅读 PDF、在页面上批注并提交" width="100%">
16
+ <p><img src="docs/assets/demo.gif" alt="Papilot:在 PDF 上批注一段话,交给智能体修改,并审阅排版后的改动" width="100%"></p>
30
17
 
31
18
  </div>
32
19
 
33
- <br>
20
+ Papilot 是运行在本机的 LaTeX 论文工作台。你在 PDF 上批注,智能体在独立副本中修改源码;改好后先看排版对比,再决定接受哪些改动、保存到哪里。
34
21
 
35
- 你像导师批改学生的稿子一样,直接在 PDF 上阅读和批注;AI 智能体去改 LaTeX,每一处改动都以排版后修改前后的对比回到你面前。你点头之前,论文一个字都不会变。
22
+ macOS、Linux 桌面版与本地构建方法见 [Papilot Desktop](desktop/README.md)。桌面 CI 构建 Apple Silicon、Intel Mac 安装包,以及 Linux x64 的 AppImage、deb 安装包。
36
23
 
37
- ## ✨ 功能
24
+ ## 功能
38
25
 
39
26
  <table>
40
27
  <tr>
41
28
  <td width="50%" valign="top">
42
- <img src="docs/assets/feature-note-appinn.jpeg" alt="在 PDF 选中的段落上写批注">
43
- <h3>📝 在 PDF 上批注</h3>
44
- 选中一句话或框出一个公式,写下要怎么改。SyncTeX 会把对应的 LaTeX 行准确交给智能体。
29
+ <a href="docs/assets/readme-note.zh-CN.jpg"><img src="docs/assets/readme-note.zh-CN.jpg" alt="PDF 上的同色分组文字批注与公式框选批注"></a>
30
+ <h3>在 PDF 上批注</h3>
31
+ 选中句子或框出公式,写下修改要求。相关批注可以按颜色归成一组,一次说明多处修改。
45
32
  </td>
46
33
  <td width="50%" valign="top">
47
- <img src="docs/assets/feature-review-appinn.jpeg" alt="改动的修改前后对比,带接受和拒绝按钮">
48
- <h3>✅ 逐条点头</h3>
49
- 每处改动都以排版后的差异呈现。接受、拒绝或评论,任何决定都能撤销。
34
+ <a href="docs/assets/readme-review.zh-CN.jpg"><img src="docs/assets/readme-review.zh-CN.jpg" alt="排版页面上的修改前后对比,带接受、拒绝和评论按钮"></a>
35
+ <h3>逐处审阅改动</h3>
36
+ 对比修改前后的排版,也能看到公式重编号等连带变化。逐条接受、拒绝或评论,做过的决定可以撤销。
50
37
  </td>
51
38
  </tr>
52
39
  <tr>
53
40
  <td width="50%" valign="top">
54
- <h3>📖 像本地 PDF 一样阅读</h3>
55
- 用目录随意跳转,全文搜索,缩放清晰。选中的文字与印出的词完全对齐。
41
+ <a href="docs/assets/readme-deliver.zh-CN.jpg"><img src="docs/assets/readme-deliver.zh-CN.jpg" alt="将接受的改动应用到论文"></a>
42
+ <h3>选择保存方式</h3>
43
+ 每个新任务在独立副本里改稿。审阅完成后,可以选择 **提交到论文**,将这个任务的改动保存为当前分支上的一个 commit;也可以选择 **应用到工作区**,保留为未提交改动。如果同一段落期间有了其他修改,AI 会自动处理冲突,只有无法确定内容时才询问你。审核合并后的 PDF,再确认保存。需要推送时,在 Git 面板操作。
56
44
  </td>
57
45
  <td width="50%" valign="top">
58
- <h3>🌿 满意了再提交</h3>
59
- 新任务中,接受改动只会将其保存在任务内。审阅结束后,可以选择「应用到工作目录」,保留为未提交改动;也可以选择「提交到 task 分支」,编辑提交信息后保存到独立的 `papilot/…` 分支,当前分支和工作目录保持不变。同一任务的后续轮次沿用所选去向,已保存的审阅决定仍可在历史中查看。
60
-
61
- 查看差异,只提交你勾选的文件,提交信息可以让 agent 来写。可以 pull、push,也可以合并合作者的改动,冲突交给 agent 解决。
46
+ <a href="docs/assets/readme-git.zh-CN.jpg"><img src="docs/assets/readme-git.zh-CN.jpg" alt="Git 面板:已暂存文件、自动生成的提交说明与提交关系图"></a>
47
+ <h3>内置 Git</h3>
48
+ 在工作台里暂存、提交、拉取和推送,提交关系图也会显示各任务的工作树。提交说明和合并冲突,可以请智能体帮忙。
62
49
  </td>
63
50
  </tr>
64
51
  </table>
65
52
 
66
- 此外:✨ **技能(skill)**,如润色、校对、检查数学,输入 `/` 即可选用,也能添加你自己的 · 🤖 **Codex、Claude 或 Antigravity**,各用你自己的登录 · 🌳 **每个任务都有独立的 Git 工作树**,最多两个同时运行 · 🌗 **浅色与深色** · 📱 **运行在你的电脑上**,也能用手机打开。
53
+ - **[技能](docs/guide.md#skills)与[任务附件](docs/guide.md#task-attachments):** 输入 `/` 选择润色、校对、检查数学等技能,也能在设置里写自己的技能。任务和消息可以附带参考文件与图片。
54
+ - **阅读与界面:** 支持通过 PDF 目录跳转章节与全文搜索,提供浅色/深色主题及中英文界面。
67
55
 
68
- ## 🚀 快速开始
56
+ ## 快速开始
69
57
 
70
- > [!NOTE]
71
- > 需要 **Node.js 22.22+**、**Git** 和完整的 **TeX** 发行版([MacTeX](https://www.tug.org/mactex/) 或 [TeX Live](https://www.tug.org/texlive/))。修改论文还需要至少一个[已登录的智能体](#-智能体)。
58
+ 运行需要 **Node.js 22.22+**、**Git**,以及包含 `latexmk` 和 `synctex` 的完整 **TeX** 发行版([MacTeX](https://www.tug.org/mactex/) 或 [TeX Live](https://www.tug.org/texlive/))。修改论文还需要配置至少一个[智能体](#智能体)。
72
59
 
73
60
  ```sh
74
61
  npm install -g papilot
75
62
  papilot demo
76
63
  ```
77
64
 
78
- 打开 **http://127.0.0.1:7274**,在侧栏选择 **PDF**。demo 会添加一篇示例论文供你修改:一篇讲 Papilot 本身的 26 页 LaTeX 论文,满是公式。准备好之后,运行 `papilot`,选择 **添加论文**,选择 LaTeX 文件夹或拖入 ZIP。Papilot 会识别主文件、准备 Git 历史并尝试编译,遇到问题可在导入窗口中重试或请求助手修复。
79
-
80
- ## 🤖 智能体
81
-
82
- | 智能体 | 登录(一次即可) | Papilot 如何运行它 |
83
- | --- | --- | --- |
84
- | **Codex** | `papilot codex login` | 通过 Codex app server 在其工作区沙箱中运行;已装有 `codex` 就直接用,没有则在首次启动时自动安装。 |
85
- | **Claude** | 设置好 `ANTHROPIC_API_KEY` 再启动 `papilot` | 通过 Claude Agent SDK 运行,首次使用时下载;该 SDK 不接受 Pro 或 Max 订阅登录。 |
86
- | **Antigravity** | 安装 Antigravity 并登录 `agy` | 运行 `agy -p`,每轮一个进程,只编辑文件;所在地区需要代理时设置 `PAPILOT_AGY_PROXY`。 |
87
-
88
- Papilot 从不读取、保存或转发你的凭据。没有安装或没有登录的智能体会显示为不可用。
89
-
90
- 启动遇到问题?运行 `papilot doctor` 检查环境并查看修复建议,或用 `papilot doctor --repo /path/to/paper` 一并检查论文。加上 `--json` 可输出机器可读的报告。诊断不会安装软件、启动服务或修改论文。检查范围见[诊断指南](docs/guide.md#diagnostics)。
91
-
92
- <details>
93
- <summary><b>⚙️ 选项与远程访问</b></summary>
94
-
95
- <br>
96
-
97
- ```sh
98
- papilot --port 4400 # 使用其他本地端口
99
- papilot --data ~/papilot-data # 把状态放在别处(默认 ~/.papilot)
100
- papilot service install # macOS:登录后自动启动,退出后自动重启
101
- npm uninstall -g papilot # 卸载;状态仍保留在 ~/.papilot,可自行删除
102
- ```
103
-
104
- Papilot 只监听 `127.0.0.1`。若要从其他设备访问,用 Tailscale Serve 把它发布到你的 tailnet,并设置 `PAPILOT_PUBLIC_ORIGIN` 和 `PAPILOT_TAILSCALE_LOGIN`;Papilot 会校验每个请求的主机、浏览器来源和 Tailscale 身份。具体步骤见[指南](docs/guide.md#remote-access-with-tailscale)。
105
-
106
- 要邀请导师或合作者,用 `PAPILOT_ACCOUNT_ORIGIN` 给 Papilot 一个公开的 HTTPS 地址,然后打开 **成员**:每个人用通行密钥登录,只能看到你共享的论文。只邀请你愿意让其访问运行 Papilot 的这台电脑的人,详见[协作者](docs/guide.md#collaborators)。
107
-
108
- </details>
109
-
110
- <details>
111
- <summary><b>🛠️ 参与开发</b></summary>
112
-
113
- <br>
114
-
115
- ```sh
116
- git clone https://github.com/cu1ch3n/papilot && cd papilot
117
- npm ci && npm run setup:review
118
- npm run build && npm start
119
- ```
120
-
121
- 提交 pull request 前,请运行 `npm run typecheck`、`npm test` 和 `npm run test:ui`。
65
+ 打开 [http://127.0.0.1:7274](http://127.0.0.1:7274),在侧栏选择 **稿件** 即可体验内置的示例论文。
122
66
 
123
- </details>
67
+ 处理自己的论文时,运行 `papilot` 并在论文菜单中选择 **添加论文…**,打开本地 LaTeX 文件夹或拖入 ZIP 压缩包。Papilot 会自动识别主 `.tex` 文件、准备 Git 记录并尝试编译,遇到问题时可在导入界面中选择主文件或尝试修复。
124
68
 
125
- ## 📚 文档
69
+ 在 **设置 → 运行环境** 的 **智能体** 或 **LaTeX** 页面查看配置状态。运行 `papilot doctor` 检查环境,加上 `--repo /path/to/paper` 可一并检查论文([诊断说明](docs/guide.md#diagnostics))。
126
70
 
127
- **[指南](docs/guide.md)**(英文)介绍工作台的每个部分、配置变量、重启后的恢复以及开发流程。[`examples/papilot-paper`](examples/papilot-paper) 中的示例论文深入讲解了设计,并附有证明。每个版本的变化见[更新日志](CHANGELOG.md)。
71
+ ## 智能体
128
72
 
129
- ## 💬 社区
73
+ <!-- BEGIN AGENT ICONS -->
74
+ <p align="center">
75
+ <a href="docs/guide.md#agents">
76
+ <picture>
77
+ <source media="(prefers-color-scheme: dark) and (max-width: 600px)" srcset="docs/assets/readme-agents/compact-dark.svg">
78
+ <source media="(prefers-color-scheme: dark)" srcset="docs/assets/readme-agents/desktop-dark.svg">
79
+ <source media="(max-width: 600px)" srcset="docs/assets/readme-agents/compact.svg">
80
+ <img src="docs/assets/readme-agents/desktop.svg" alt="Codex, Claude, Cursor, Copilot, Grok, Antigravity, Kimi, GLM, DeepSeek, Qwen, OpenCode" width="816">
81
+ </picture>
82
+ </a>
83
+ </p>
84
+ <!-- END AGENT ICONS -->
130
85
 
131
- - 💡 **提问和想法**:[Discussions](https://github.com/cu1ch3n/papilot/discussions)
132
- - 🐛 **遇到问题**:[提交 issue](https://github.com/cu1ch3n/papilot/issues/new/choose)
133
- - 🤝 **想参与**:从 [good first issue](https://github.com/cu1ch3n/papilot/labels/good%20first%20issue) 开始,并阅读 [CONTRIBUTING.md](CONTRIBUTING.md)
86
+ 每个任务都可以选择智能体和模型。Papilot 使用智能体在本机的登录,或你为对应服务配置的 API 凭据,不另行保存密钥。
134
87
 
135
- 如果 Papilot 帮你省下了改论文的时间,点个 ⭐ 能让更多研究者发现它。
88
+ 打开 **设置 → 运行环境 → 智能体** 可查看状态、可用模型与配置说明(例如运行 `papilot codex login` 登录 Codex)。安装或登录智能体后,点击 **重新连接** 刷新状态;修改环境变量后则需重启 `papilot`。各智能体的具体要求与支持范围见[智能体指南](docs/guide.md#agents)。
136
89
 
137
- <a href="https://github.com/cu1ch3n/papilot/graphs/contributors"><img src="https://contrib.rocks/image?repo=cu1ch3n/papilot" alt="Papilot 贡献者"></a>
90
+ ## 文档与社区
138
91
 
139
- ## 🔗 友情链接
92
+ - **使用文档:** 阅读[使用指南](docs/guide.md)了解完整工作流、配置项、[应用内更新](docs/guide.md#updates)、[Tailscale 远程访问](docs/guide.md#remote-access-with-tailscale)以及[邀请协作者](docs/guide.md#collaborators)。
93
+ - **设计与版本:** 在 [`examples/papilot-paper`](examples/papilot-paper) 中查看阐述系统设计与证明的示例论文,在[更新日志](CHANGELOG.md)中查看各版本变更。
94
+ - **社区与支持:** 欢迎在 [Discussions](https://github.com/PapilotAI/papilot/discussions) 交流想法、在 [Issues](https://github.com/PapilotAI/papilot/issues) 反馈问题,或阅读 [CONTRIBUTING.md](CONTRIBUTING.md) 参与贡献。
140
95
 
141
- [LINUX DO](https://linux.do/) 是一个交流技术、分享开源项目的中文社区。Papilot 认同真诚、友善、团结、专业的社区理念,欢迎大家去发现有趣的项目、交流使用经验。
96
+ ---
142
97
 
143
- ## 📄 许可证
98
+ [MIT](LICENSE) © 2026 PapilotAI
144
99
 
145
- [MIT](LICENSE) © 2026 Cui Chen。内置的审阅引擎 [`vendor/latex-review`](vendor/latex-review) 同样采用 MIT 许可证。
100
+ 友情链接:[LINUX DO](https://linux.do/)。
package/bin/doctor.mjs CHANGED
@@ -34,6 +34,13 @@ function executable(command, env = process.env) {
34
34
  try { fs.accessSync(file, fs.constants.X_OK); return fs.statSync(file).isFile(); } catch { return false; }
35
35
  });
36
36
  }
37
+ // Agent CLIs are also looked for where they install themselves, as the server does.
38
+ function agentExecutable(command) {
39
+ if (command.includes('/') || command.includes('\\')) return executable(command);
40
+ const home = process.env.HOME || os.homedir();
41
+ const dirs = ['.local/bin', '.claude/local', '.kimi-code/bin', '.grok/bin', '.opencode/bin', '.bun/bin', '.npm-global/bin'].map(dir => path.join(home, dir)).concat('/opt/homebrew/bin', '/usr/local/bin');
42
+ return executable(command) || executable(command, { PATH: dirs.join(path.delimiter) });
43
+ }
37
44
  function command(file, args, options = {}) {
38
45
  return new Promise(resolve => {
39
46
  // Never return raw stderr: login commands can print credentials or account data.
@@ -141,14 +148,24 @@ export async function doctor(args) {
141
148
  return false;
142
149
  }
143
150
  async function claudeCheck() {
151
+ // With Claude Code installed, Claude runs claude -p on its own login; only the login state is read.
152
+ const cli = agentExecutable(process.env.PAPILOT_CLAUDE_BIN || 'claude');
153
+ if (cli) {
154
+ const result = await command(cli, ['auth', 'status', '--json']);
155
+ let login = null;
156
+ try { login = JSON.parse(result.stdout); } catch { /* Reported below. */ }
157
+ const ready = Boolean(login?.loggedIn);
158
+ check('agent.claude', ready, 'warn', `${cli}: ${ready ? `Claude Code signed in${login.subscriptionType ? ` (${login.subscriptionType} plan)` : ''}; generation was not tested` : login ? 'Claude Code is not signed in' : result.reason || 'login status unavailable'}.`, 'Run claude auth login, then select Reconnect in Papilot. Set PAPILOT_CLAUDE_BIN if needed.');
159
+ return ready;
160
+ }
144
161
  const sdk = installedModule('@anthropic-ai/claude-agent-sdk') || installedModule('@anthropic-ai/claude-agent-sdk', path.join(data, 'agents'));
145
162
  const configured = ['ANTHROPIC_API_KEY', 'ANTHROPIC_AUTH_TOKEN', 'CLAUDE_CODE_USE_BEDROCK', 'CLAUDE_CODE_USE_VERTEX', 'CLAUDE_CODE_USE_FOUNDRY'].some(key => Boolean(process.env[key]));
146
- check('agent.claude', Boolean(sdk && configured), 'warn', !configured ? 'Claude API credentials are not configured in this environment; SDK-managed credentials were not checked.' : sdk ? 'Claude SDK installed; API credentials configured (not validated).' : 'Claude API credentials configured; the Agent SDK is not installed yet.', !configured ? 'Start Papilot with ANTHROPIC_API_KEY or a supported cloud provider configured. A Claude subscription alone cannot be used by this adapter.' : 'Start papilot once to install the Claude Agent SDK.');
163
+ check('agent.claude', Boolean(sdk && configured), configured ? 'warn' : 'info', !configured ? 'Claude Code is not installed and no Claude API key is configured, so Papilot does not offer Claude.' : sdk ? 'Claude SDK installed; API credentials configured (not validated).' : 'Claude API credentials configured; the Agent SDK is not installed yet.', !configured ? 'Install Claude Code and sign in with claude auth login to use your Claude plan, or start Papilot with ANTHROPIC_API_KEY or a supported cloud provider configured.' : 'Start papilot once to install the Claude Agent SDK.');
147
164
  return Boolean(sdk && configured);
148
165
  }
149
166
  async function agyCheck() {
150
- const file = executable(process.env.PAPILOT_AGY_BIN || 'agy');
151
- if (!file) { check('agent.antigravity', false, 'warn', 'Antigravity CLI is not installed or PAPILOT_AGY_BIN is invalid.', 'Install Antigravity and sign in to agy, or choose another agent.'); return false; }
167
+ const file = agentExecutable(process.env.PAPILOT_AGY_BIN || 'agy');
168
+ if (!file) { check('agent.antigravity', false, 'info', missingAgent('agy', 'PAPILOT_AGY_BIN'), 'Install Antigravity and sign in to agy on this host. If already installed, correct PAPILOT_AGY_BIN or the server PATH, then restart Papilot.'); return false; }
152
169
  const proxy = process.env.PAPILOT_AGY_PROXY;
153
170
  const env = proxy ? { ...process.env, HTTPS_PROXY: proxy, HTTP_PROXY: proxy, https_proxy: proxy, http_proxy: proxy, NO_PROXY: '127.0.0.1,localhost' } : process.env;
154
171
  const result = await command(file, ['models'], { env });
@@ -156,7 +173,39 @@ export async function doctor(args) {
156
173
  check('agent.antigravity', ready, 'warn', `${file}: ${ready ? 'model listing succeeded (no generation tested)' : result.reason || 'no models returned'}.`, 'Sign in to agy and try agy models. If your region requires a proxy, set PAPILOT_AGY_PROXY.');
157
174
  return ready;
158
175
  }
159
- const agents = await Promise.all([codexCheck(), claudeCheck(), agyCheck()]);
176
+ // ACP agents: the CLI must offer its ACP mode. Login is not checked, so no credential is read.
177
+ function missingAgent(command, variable) {
178
+ return process.env[variable] ? `${variable} does not resolve to a runnable executable in Papilot's environment.`
179
+ : `${command} was not found on the PATH or in the standard agent folders available to Papilot. It may be installed for a different user or shell.`;
180
+ }
181
+ async function acpCheck(id, name, bin, help, install, update, supported = /\bACP\b|Agent Client Protocol/i) {
182
+ const file = agentExecutable(process.env[bin.env] || bin.name);
183
+ if (!file) { check(`agent.${id}`, false, 'info', missingAgent(bin.name, bin.env), `${install}. If already installed, correct ${bin.env} or the server PATH, then restart Papilot.`); return false; }
184
+ const result = await command(file, help);
185
+ const ready = result.ok && supported.test(result.stdout + '\n' + result.stderr);
186
+ check(`agent.${id}`, ready, 'warn', `${file}: ${ready ? 'ACP command available; login and generation were not tested' : result.reason || 'ACP command unavailable'}.`, `${update} Set ${bin.env} if needed.`);
187
+ return ready;
188
+ }
189
+ const kimiCheck = () => acpCheck('kimi', 'Kimi CLI', { env: 'PAPILOT_KIMI_BIN', name: 'kimi' }, ['acp', '--help'], 'Install Kimi Code CLI and run kimi login', 'Update Kimi Code CLI and run kimi login.');
190
+ const copilotCheck = () => acpCheck('copilot', 'GitHub Copilot CLI', { env: 'PAPILOT_COPILOT_BIN', name: 'copilot' }, ['--help'], 'Install GitHub Copilot CLI with npm install -g @github/copilot and run copilot login', 'Update GitHub Copilot CLI and run copilot login.');
191
+ const grokCheck = () => acpCheck('grok', 'Grok Build CLI', { env: 'PAPILOT_GROK_BIN', name: 'grok' }, ['--no-auto-update', 'agent', '--no-leader', 'stdio', '--help'], 'Install Grok Build CLI from https://x.ai/cli and run grok login or configure XAI_API_KEY', 'Update Grok Build CLI and run grok login or configure XAI_API_KEY.', /Usage:\s+grok agent stdio\b/i);
192
+ const qwenCheck = () => acpCheck('qwen', 'Qwen Code', { env: 'PAPILOT_QWEN_BIN', name: 'qwen' }, ['--help'], 'Install Qwen Code and sign in by running qwen once', 'Update Qwen Code and sign in by running qwen once.');
193
+ const deepseekCheck = () => acpCheck('deepseek', 'DeepSeek Harness (dsh)', { env: 'PAPILOT_DSH_BIN', name: 'dsh' }, ['acp', '--help'], 'Install DeepSeek Harness with npm install -g @deepseek-ai/dsh and set up a model in dsh', 'Update DeepSeek Harness and set up a model in dsh.');
194
+ const opencodeCheck = () => acpCheck('opencode', 'OpenCode', { env: 'PAPILOT_OPENCODE_BIN', name: 'opencode' }, ['acp', '--help'], 'Install OpenCode and connect a provider with opencode auth login', 'Update OpenCode and connect a provider with opencode auth login.');
195
+ // SDK agents: a key in the environment (or the SDK's own login) selects them; keys are not validated.
196
+ async function cursorCheck() {
197
+ const configured = Boolean(process.env.CURSOR_API_KEY) || fs.existsSync(path.join(os.homedir(), '.cursor/sdk/auth.json'));
198
+ const sdk = installedModule('@cursor/sdk') || installedModule('@cursor/sdk', path.join(data, 'agents'));
199
+ check('agent.cursor', Boolean(configured && sdk), configured ? 'warn' : 'info', !configured ? 'Neither CURSOR_API_KEY nor a saved Cursor SDK login was found for the user running Papilot.' : sdk ? 'Cursor SDK installed; SDK credentials detected (not validated).' : 'Cursor SDK credentials detected; the SDK is installed on first use.', !configured ? 'Start Papilot with CURSOR_API_KEY set to a key from the Cursor dashboard, or configure the Cursor SDK login. The Cursor app and CLI login are not used. Restart Papilot after changing its environment.' : 'Start papilot and choose Cursor once to install the SDK.');
200
+ return configured;
201
+ }
202
+ async function glmCheck() {
203
+ const configured = Boolean(process.env.ZHIPU_API_KEY || process.env.ZAI_API_KEY);
204
+ const sdk = installedModule('@anthropic-ai/claude-agent-sdk') || installedModule('@anthropic-ai/claude-agent-sdk', path.join(data, 'agents'));
205
+ check('agent.glm', Boolean(configured && sdk), configured ? 'warn' : 'info', !configured ? 'ZHIPU_API_KEY or ZAI_API_KEY is not set, so Papilot does not offer GLM.' : sdk ? `GLM key configured for ${process.env.ZHIPU_API_KEY ? 'open.bigmodel.cn' : 'api.z.ai'} (not validated).` : 'GLM key configured; the Claude Agent SDK that runs GLM is installed on first use.', !configured ? 'Start Papilot with ZHIPU_API_KEY (open.bigmodel.cn) or ZAI_API_KEY (api.z.ai) set to your GLM Coding Plan or API key.' : 'Start papilot and choose GLM once to install the Claude Agent SDK.');
206
+ return configured;
207
+ }
208
+ const agents = await Promise.all([codexCheck(), claudeCheck(), cursorCheck(), copilotCheck(), grokCheck(), agyCheck(), kimiCheck(), glmCheck(), deepseekCheck(), qwenCheck(), opencodeCheck()]);
160
209
  check('agents', agents.some(Boolean), 'warn', agents.some(Boolean) ? 'At least one agent passed its configuration check; live editing was not tested.' : 'No agent passed its configuration check. PDF reading can still be used with a working TeX installation.', 'Configure one agent before starting editing tasks.');
161
210
 
162
211
  if (repo) {
@@ -232,14 +281,14 @@ export async function doctor(args) {
232
281
  }
233
282
  }
234
283
  // Keep reports stable even though independent probes finish in any order.
235
- const order = ['node', 'dependencies', 'frontend', 'review-engine', 'data', 'git', 'latexmk', 'synctex', 'tex-engines', 'agent.codex', 'agent.claude', 'agent.antigravity', 'agents', 'project', 'project.git', 'project.head', 'project.main', 'project.engine', 'project.worktrees', 'remote', 'server', 'service', 'service.tools', 'service.data'];
284
+ const order = ['node', 'dependencies', 'frontend', 'review-engine', 'data', 'git', 'latexmk', 'synctex', 'tex-engines', 'agent.codex', 'agent.claude', 'agent.cursor', 'agent.copilot', 'agent.grok', 'agent.antigravity', 'agent.kimi', 'agent.glm', 'agent.deepseek', 'agent.qwen', 'agent.opencode', 'agents', 'project', 'project.git', 'project.head', 'project.main', 'project.engine', 'project.worktrees', 'remote', 'server', 'service', 'service.tools', 'service.data'];
236
285
  checks.sort((a, b) => order.indexOf(a.id) - order.indexOf(b.id));
237
286
  const report = { name: 'Papilot', version: pkg.version, ok: !checks.some(item => !item.ok && item.severity === 'error'), checks };
238
287
  if (json) console.log(JSON.stringify(report, null, 2));
239
288
  else {
240
289
  console.log(`Papilot ${pkg.version} doctor\n`);
241
290
  for (const item of checks) {
242
- console.log(`${item.ok ? '✓' : item.severity === 'error' ? '✗' : '!'} ${item.id}: ${item.detail}`);
291
+ console.log(`${item.ok ? '✓' : item.severity === 'error' ? '✗' : item.severity === 'info' ? '-' : '!'} ${item.id}: ${item.detail}`);
243
292
  if (item.fix) console.log(` Fix: ${item.fix}`);
244
293
  }
245
294
  const errors = checks.filter(item => !item.ok && item.severity === 'error').length;
@@ -1,4 +1,6 @@
1
1
  export function stableVersion(value: unknown): value is string;
2
+ export function devRevision(value: unknown): string | null;
3
+ export function releaseVersion(value: unknown): value is string;
2
4
  export function newerVersion(next: string, current: string): boolean;
3
5
  export function readJson(file: string): any;
4
6
  export function writeJson(file: string, value: unknown): void;
@@ -2,6 +2,8 @@ import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
 
4
4
  export const stableVersion = value => typeof value === 'string' && /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/.test(value) && value.length < 40;
5
+ export const devRevision = value => typeof value === 'string' && value.length < 100 && /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)-dev\.([a-f0-9]{40})$/.exec(value)?.[4] || null;
6
+ export const releaseVersion = value => stableVersion(value) || Boolean(devRevision(value));
5
7
  export function newerVersion(next, current) {
6
8
  if (!stableVersion(next)) return false;
7
9
  const match = /^(\d+)\.(\d+)\.(\d+)(.*)$/.exec(current);
@@ -22,7 +24,11 @@ export function writeJson(file, value) {
22
24
  export function activeInstallation(root, data) {
23
25
  const active = readJson(path.join(data, 'updates/active.json'));
24
26
  const version = readJson(path.join(root, 'package.json'))?.version;
25
- if (!active || !stableVersion(active.version) || !newerVersion(active.version, version)) return root;
27
+ if (!active || !releaseVersion(active.version)) return root;
28
+ // Explicit channel selections also cover dev builds and returning to stable.
29
+ // Older activation records retain their upgrade-only behavior.
30
+ if (!['stable', 'dev'].includes(active.channel) && !newerVersion(active.version, version)) return root;
31
+ if (active.channel && (active.channel === 'dev') !== Boolean(devRevision(active.version))) return root;
26
32
  if (typeof active.root !== 'string') return root;
27
33
  try {
28
34
  const releases = fs.realpathSync(path.join(data, 'updates/releases')) + path.sep;