@gavin-hu/my-pi-agent 0.18.0 → 0.19.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.
Files changed (249) hide show
  1. package/README.md +52 -44
  2. package/README.zh-CN.md +46 -41
  3. package/docs/extensions/README.md +34 -0
  4. package/docs/extensions/board.md +344 -0
  5. package/{extensions/bytefly/README.md → docs/extensions/bytefly.md} +446 -404
  6. package/{extensions/check/README.md → docs/extensions/check.md} +83 -68
  7. package/{extensions/context/README.md → docs/extensions/context.md} +40 -23
  8. package/docs/extensions/file-browse.md +554 -0
  9. package/{extensions/file-open/README.md → docs/extensions/file-open.md} +31 -23
  10. package/{extensions/file-read/README.md → docs/extensions/file-read.md} +47 -55
  11. package/{extensions/goal/README.md → docs/extensions/goal.md} +115 -131
  12. package/{extensions/job/README.md → docs/extensions/job.md} +61 -50
  13. package/{extensions/memory/README.md → docs/extensions/memory.md} +71 -60
  14. package/{extensions/plan/README.md → docs/extensions/plan.md} +390 -317
  15. package/{extensions/question/README.md → docs/extensions/question.md} +54 -31
  16. package/{extensions/rewind/README.md → docs/extensions/rewind.md} +115 -122
  17. package/docs/extensions/session-bus.md +285 -0
  18. package/{extensions/subagent/README.md → docs/extensions/subagent.md} +165 -118
  19. package/docs/extensions/team.md +226 -0
  20. package/{extensions/todo/README.md → docs/extensions/todo.md} +85 -88
  21. package/{extensions/watch/README.md → docs/extensions/watch.md} +54 -36
  22. package/{extensions/web-access/README.md → docs/extensions/web-access.md} +93 -50
  23. package/{extensions/wechat/README.md → docs/extensions/wechat.md} +258 -162
  24. package/{extensions/worktree/README.md → docs/extensions/worktree.md} +115 -72
  25. package/extensions/board/commands.ts +87 -0
  26. package/extensions/board/config.ts +36 -0
  27. package/extensions/board/format.ts +72 -0
  28. package/extensions/board/index.ts +40 -0
  29. package/extensions/board/input.ts +54 -0
  30. package/extensions/board/join.ts +42 -0
  31. package/extensions/board/page.ts +312 -0
  32. package/extensions/board/result.ts +99 -0
  33. package/extensions/board/rows.ts +48 -0
  34. package/extensions/board/schema.ts +75 -0
  35. package/extensions/board/seat.ts +29 -0
  36. package/extensions/board/snapshot.ts +41 -0
  37. package/extensions/board/tools.ts +279 -0
  38. package/extensions/board/transport/router.ts +172 -0
  39. package/extensions/board/transport/server.ts +149 -0
  40. package/extensions/board/types.ts +54 -0
  41. package/extensions/board/view/actions.ts +306 -0
  42. package/extensions/board/view/client.ts +41 -0
  43. package/extensions/board/view/keys.ts +121 -0
  44. package/extensions/board/view/live.ts +397 -0
  45. package/extensions/board/view/page.ts +158 -0
  46. package/extensions/board/view/render.ts +496 -0
  47. package/extensions/board/view/styles.ts +211 -0
  48. package/extensions/board/view/team.ts +305 -0
  49. package/extensions/board/writes.ts +99 -0
  50. package/extensions/bytefly/panel/commands.ts +40 -7
  51. package/extensions/bytefly/panel/settings-runtime.ts +0 -6
  52. package/extensions/bytefly/panel/settings.ts +3 -20
  53. package/extensions/bytefly/panel/tui/settings-view.ts +1 -1
  54. package/extensions/bytefly/picker/discovery.ts +0 -2
  55. package/extensions/bytefly/picker/types.ts +0 -2
  56. package/extensions/bytefly/update/check.ts +39 -0
  57. package/extensions/bytefly/update/format.ts +15 -0
  58. package/extensions/bytefly/update/register.ts +73 -34
  59. package/extensions/bytefly/update/state.ts +6 -3
  60. package/extensions/context/tui.ts +1 -1
  61. package/extensions/file-browse/{config.ts → config/config.ts} +1 -1
  62. package/extensions/file-browse/{files.ts → files/files.ts} +1 -14
  63. package/extensions/file-browse/{paths.ts → files/paths.ts} +1 -1
  64. package/extensions/file-browse/{git.ts → git/git.ts} +10 -3
  65. package/extensions/file-browse/index.ts +4 -4
  66. package/extensions/file-browse/{live.ts → live/live.ts} +20 -69
  67. package/extensions/file-browse/{router.ts → transport/router.ts} +21 -15
  68. package/extensions/file-browse/transport/server.ts +110 -0
  69. package/extensions/file-browse/{chrome.ts → view/chrome.ts} +4 -4
  70. package/extensions/file-browse/{client.ts → view/client.ts} +1 -1
  71. package/extensions/file-browse/{diff-html.ts → view/diff-html.ts} +3 -3
  72. package/extensions/file-browse/{html.ts → view/html.ts} +14 -12
  73. package/extensions/file-browse/{icons.ts → view/icons.ts} +1 -1
  74. package/extensions/file-browse/{markdown.ts → view/markdown.ts} +7 -7
  75. package/extensions/file-browse/{raw-html.ts → view/raw-html.ts} +1 -1
  76. package/extensions/file-browse/{styles.ts → view/styles.ts} +29 -5
  77. package/extensions/file-browse/{tree.ts → view/tree.ts} +1 -1
  78. package/extensions/file-browse/{urls.ts → view/urls.ts} +1 -1
  79. package/extensions/file-read/schema.ts +2 -1
  80. package/extensions/memory/store.ts +12 -5
  81. package/extensions/plan/blocks.ts +23 -2
  82. package/extensions/plan/commands.ts +21 -16
  83. package/extensions/plan/feedback.ts +147 -38
  84. package/extensions/plan/index.ts +6 -4
  85. package/extensions/plan/plan-index.ts +43 -9
  86. package/extensions/plan/plan-text.ts +33 -23
  87. package/extensions/plan/plans.ts +8 -11
  88. package/extensions/plan/policy.ts +20 -12
  89. package/extensions/plan/review.ts +3 -27
  90. package/extensions/plan/runtime.ts +21 -9
  91. package/extensions/plan/sections.ts +112 -0
  92. package/extensions/plan/steps.ts +3 -22
  93. package/extensions/plan/tools/exit.ts +11 -95
  94. package/extensions/plan/tui.ts +5 -34
  95. package/extensions/plan/types.ts +0 -8
  96. package/extensions/plan/web-client.ts +1 -1
  97. package/extensions/plan/web-page.ts +0 -7
  98. package/extensions/plan/web-router.ts +3 -57
  99. package/extensions/plan/web-share-session.ts +24 -30
  100. package/extensions/plan/web-streams.ts +14 -14
  101. package/extensions/plan/web-tokens.ts +2 -2
  102. package/extensions/plan/web.ts +66 -120
  103. package/extensions/rewind/{commands.ts → commands/commands.ts} +5 -5
  104. package/extensions/rewind/{review.ts → commands/review.ts} +13 -13
  105. package/extensions/rewind/{rewind.ts → commands/rewind.ts} +6 -6
  106. package/extensions/rewind/{config.ts → config/config.ts} +1 -1
  107. package/extensions/rewind/index.ts +3 -3
  108. package/extensions/rewind/{policy.ts → state/policy.ts} +4 -4
  109. package/extensions/rewind/{runtime.ts → state/runtime.ts} +11 -11
  110. package/extensions/rewind/{snapshot-run.ts → state/snapshot-run.ts} +1 -1
  111. package/extensions/rewind/{timeline.ts → state/timeline.ts} +2 -2
  112. package/extensions/rewind/{format.ts → text/format.ts} +3 -3
  113. package/extensions/rewind/{git.ts → transport/git.ts} +2 -2
  114. package/extensions/rewind/{restore.ts → transport/restore.ts} +2 -2
  115. package/extensions/rewind/{snapshot.ts → transport/snapshot.ts} +1 -1
  116. package/extensions/rewind/{store.ts → transport/store.ts} +1 -1
  117. package/extensions/rewind/{checklist.ts → view/checklist.ts} +6 -6
  118. package/extensions/rewind/{hunk-picker.ts → view/hunk-picker.ts} +8 -8
  119. package/extensions/rewind/{path-picker.ts → view/path-picker.ts} +7 -7
  120. package/extensions/rewind/{review-tui.ts → view/review-tui.ts} +11 -11
  121. package/extensions/rewind/{rewind-tui.ts → view/rewind-tui.ts} +17 -11
  122. package/extensions/rewind/{status.ts → view/status.ts} +4 -4
  123. package/extensions/session-bus/commands.ts +17 -69
  124. package/extensions/session-bus/format.ts +22 -45
  125. package/extensions/session-bus/index.ts +13 -7
  126. package/extensions/session-bus/join-request.ts +40 -0
  127. package/extensions/session-bus/mail-watch.ts +134 -0
  128. package/extensions/session-bus/notice.ts +32 -4
  129. package/{lib/claims → extensions/session-bus}/overlap.ts +11 -18
  130. package/extensions/session-bus/protocol.ts +19 -49
  131. package/extensions/session-bus/resolve.ts +67 -0
  132. package/extensions/session-bus/result.ts +16 -51
  133. package/extensions/session-bus/rows.ts +4 -2
  134. package/extensions/session-bus/runtime.ts +129 -238
  135. package/extensions/session-bus/store.ts +42 -6
  136. package/extensions/session-bus/tools.ts +31 -66
  137. package/extensions/session-bus/tui.ts +4 -4
  138. package/extensions/session-bus/types.ts +13 -13
  139. package/extensions/subagent/agents.ts +9 -8
  140. package/extensions/subagent/invocation.ts +4 -2
  141. package/extensions/team/commands.ts +112 -0
  142. package/extensions/team/dispatch.ts +133 -0
  143. package/extensions/team/format.ts +105 -0
  144. package/extensions/team/index.ts +33 -0
  145. package/extensions/team/schema.ts +29 -0
  146. package/extensions/team/tools.ts +246 -0
  147. package/extensions/team/types.ts +48 -0
  148. package/extensions/web-access/fetch/page.ts +4 -1
  149. package/extensions/web-access/fetch/structured.ts +447 -0
  150. package/extensions/web-access/fetch/tool.ts +2 -1
  151. package/extensions/web-access/search/tool.ts +2 -1
  152. package/extensions/wechat/bridge.ts +76 -92
  153. package/extensions/wechat/{commands.ts → commands/commands.ts} +26 -18
  154. package/extensions/wechat/{doctor.ts → commands/doctor.ts} +5 -5
  155. package/extensions/wechat/{history.ts → commands/history.ts} +1 -1
  156. package/extensions/wechat/{login.ts → commands/login.ts} +3 -3
  157. package/extensions/wechat/{slash.ts → commands/slash.ts} +38 -2
  158. package/extensions/wechat/dialog/channels.ts +136 -0
  159. package/extensions/wechat/{interaction.ts → dialog/interaction.ts} +37 -37
  160. package/extensions/wechat/{prompt.ts → dialog/prompt.ts} +2 -2
  161. package/extensions/wechat/{remote-ui.ts → dialog/remote-ui.ts} +1 -1
  162. package/extensions/wechat/index.ts +10 -10
  163. package/extensions/wechat/{admission.ts → peer/admission.ts} +2 -2
  164. package/extensions/wechat/{peer-session.ts → peer/peer-session.ts} +2 -2
  165. package/extensions/wechat/{peer-worker.ts → peer/peer-worker.ts} +100 -22
  166. package/extensions/wechat/{presence.ts → peer/presence.ts} +8 -7
  167. package/extensions/wechat/{reply-pipeline.ts → peer/reply-pipeline.ts} +1 -1
  168. package/extensions/wechat/{credentials.ts → state/credentials.ts} +2 -2
  169. package/extensions/wechat/{state.ts → state/state.ts} +14 -2
  170. package/extensions/wechat/{format.ts → text/format.ts} +3 -3
  171. package/extensions/wechat/{notices.ts → text/notices.ts} +8 -0
  172. package/extensions/wechat/tools.ts +29 -12
  173. package/extensions/wechat/{client.ts → transport/client.ts} +26 -2
  174. package/extensions/wechat/{deliver.ts → transport/deliver.ts} +72 -28
  175. package/extensions/wechat/{inbound.ts → transport/inbound.ts} +5 -5
  176. package/extensions/wechat/{media.ts → transport/media.ts} +11 -5
  177. package/extensions/wechat/transport/outbound.ts +142 -0
  178. package/extensions/wechat/{poll.ts → transport/poll.ts} +3 -3
  179. package/extensions/wechat/types.ts +10 -1
  180. package/extensions/wechat/{login-screen.ts → view/login-screen.ts} +4 -4
  181. package/extensions/wechat/{status-screen.ts → view/status-screen.ts} +31 -26
  182. package/extensions/worktree/guard.ts +3 -5
  183. package/lib/README.md +8 -6
  184. package/lib/board/card.ts +290 -0
  185. package/lib/board/links.ts +117 -0
  186. package/lib/board/page-pointer.ts +256 -0
  187. package/lib/board/round-text.ts +72 -0
  188. package/lib/board/round.ts +89 -0
  189. package/lib/board/rules.ts +304 -0
  190. package/lib/board/session.ts +105 -0
  191. package/lib/board/store.ts +310 -0
  192. package/lib/board/view.ts +150 -0
  193. package/lib/errors.ts +12 -4
  194. package/lib/format.ts +13 -0
  195. package/lib/http/guard.ts +112 -0
  196. package/lib/http/index.ts +16 -5
  197. package/lib/http/serve.ts +234 -0
  198. package/lib/http/server.ts +12 -10
  199. package/lib/http/sink.ts +110 -0
  200. package/lib/http/token.ts +38 -0
  201. package/lib/policy.ts +12 -2
  202. package/lib/requirements.ts +6 -7
  203. package/lib/session-label.ts +64 -0
  204. package/lib/session-roster.ts +130 -0
  205. package/lib/session-tool.ts +184 -0
  206. package/lib/tool-names.ts +25 -2
  207. package/lib/tui/glyphs.ts +2 -0
  208. package/lib/web/README.md +5 -2
  209. package/lib/web/components.ts +5 -0
  210. package/lib/web/runtime.ts +7 -1
  211. package/lib/web/tokens.ts +25 -2
  212. package/package.json +26 -14
  213. package/skills/next/SKILL.md +4 -3
  214. package/skills/project-docs/SKILL.md +133 -0
  215. package/skills/project-docs/references/audit.md +131 -0
  216. package/skills/project-docs/references/changelog.md +105 -0
  217. package/skills/project-docs/references/discovery.md +127 -0
  218. package/skills/project-docs/references/readme.md +80 -0
  219. package/skills/project-docs/references/release.md +84 -0
  220. package/skills/project-docs/references/roadmap.md +93 -0
  221. package/skills/project-docs/scripts/state.ts +316 -0
  222. package/skills/refactor/SKILL.md +100 -125
  223. package/skills/refactor/references/handoff.md +35 -0
  224. package/skills/refactor/references/signals.md +54 -0
  225. package/skills/refactor/scripts/inventory.ts +587 -0
  226. package/extensions/file-browse/README.md +0 -506
  227. package/extensions/file-browse/server.ts +0 -141
  228. package/extensions/plan/split.ts +0 -87
  229. package/extensions/session-bus/README.md +0 -247
  230. package/extensions/session-bus/team.ts +0 -102
  231. package/extensions/wechat/outbound.ts +0 -100
  232. package/lib/plans/sections.ts +0 -172
  233. package/lib/plans/setting.ts +0 -52
  234. package/lib/plans/split.ts +0 -215
  235. package/lib/plans/store.ts +0 -19
  236. package/lib/plans/team.ts +0 -151
  237. package/skills/changelog/SKILL.md +0 -148
  238. package/skills/parallel-dev/SKILL.md +0 -150
  239. /package/extensions/file-browse/{rev.ts → live/rev.ts} +0 -0
  240. /package/extensions/file-browse/{diff.ts → text/diff.ts} +0 -0
  241. /package/extensions/file-browse/{fuzzy.ts → text/fuzzy.ts} +0 -0
  242. /package/extensions/file-browse/{live-client.ts → view/live-client.ts} +0 -0
  243. /package/extensions/file-browse/{palette.ts → view/palette.ts} +0 -0
  244. /package/extensions/rewind/{patch.ts → text/patch.ts} +0 -0
  245. /package/extensions/rewind/{paths.ts → text/paths.ts} +0 -0
  246. /package/extensions/rewind/{queue.ts → transport/queue.ts} +0 -0
  247. /package/extensions/rewind/{temp-index.ts → transport/temp-index.ts} +0 -0
  248. /package/extensions/wechat/{lock.ts → state/lock.ts} +0 -0
  249. /package/extensions/wechat/{channel.ts → text/channel.ts} +0 -0
@@ -8,12 +8,29 @@
8
8
  the effective working directory with the same symlink-aware guard the built-in
9
9
  path tools use.
10
10
 
11
+ ## Capabilities
12
+
13
+ Status records what a reader can rely on: **Shipped** is in a tagged release, **Unreleased** is complete in the current tree but not yet tagged, and **Partial** has one released part and one unreleased part. Rows are ordered by user value, highest first.
14
+
15
+ | Capability | How | What you get | Status |
16
+ |---|---|---|---|
17
+ | Document extraction | `read_doc` | Plain text from a document or workbook whose bytes the built-in `read` cannot decode. | Shipped |
18
+ | Paged reading | `read_doc` (`startIndex`, `maxChars`) | A long file arrives in slices, and a truncated result names the offset to ask for next. | Shipped |
19
+ | Workbook text | `read_doc` on `.xlsx` | One `[Sheet]` heading per worksheet followed by tab-separated rows, so the shape of the file survives. | Shipped |
20
+ | Confined reads | `read_doc` | A path outside the effective working directory is refused, symlinks resolved, before any parser runs. | Shipped |
21
+ | Progress and cancellation | `startToolProgress`, the abort signal | A slow extraction shows `Reading... <elapsed>` and can be cancelled instead of looking dead. | Shipped |
22
+ | Bounded extraction | `file-read.maxFileBytes`, `file-read.extractTimeoutMs` | An over-size file or a parser past its deadline fails the read instead of parking the turn. | Shipped |
23
+ | Per-format switches | `file-read.formats` | Any of the six formats can be turned off, and a disabled read names the key that did it. | Shipped |
24
+ | Unsupported-format hint | `read_doc` on another extension | The refusal names the supported formats and the legacy `.xls` conversion hint instead of a generic read error. | Shipped |
25
+ | Lazy parsers | `read_doc` | Only the format you read loads a library, so registration costs nothing at startup. | Shipped |
26
+ | Code-point slicing | `read_doc` (`startIndex`, `maxChars`) | CJK and emoji stay well-formed across a page boundary. | Shipped |
27
+
11
28
  ## Quickstart
12
29
 
13
30
  ```bash
14
- pi --extension ./extensions/file-read # this extension only
15
- pi -e . # the package from this checkout
16
- pi install ./ # install the package for a user
31
+ pi --extension ./extensions/file-read # load just this extension
32
+ pi -e . # load the whole @gavin-hu/my-pi-agent package
33
+ pi install ./ # install the package
17
34
  ```
18
35
 
19
36
  The extraction libraries are ordinary dependencies, so a normal install brings
@@ -23,31 +40,15 @@ tool directly:
23
40
  ```jsonc
24
41
  { "path": "docs/spec.pdf" }
25
42
  { "path": "docs/spec.pdf", "startIndex": 40000, "maxChars": 40000 }
26
- { "path": "report.xlsx" }
27
43
  ```
28
44
 
29
- ## What it does
30
-
31
- - Extracts plain text from `.pdf`, `.docx`, `.doc`, `.odt`, `.rtf`, and `.xlsx`
32
- files for the model to read.
33
- - Pages long files: when output is truncated, call again with the `startIndex`
34
- named in the note.
35
- - Slices by code point, so CJK and emoji stay well-formed.
36
- - Reads only inside the effective working directory, resolving symlinks so a
37
- link cannot escape it.
38
- - Loads a format's parser only when that format is actually read, so registration
39
- costs nothing at startup.
40
-
41
45
  ## Tool
42
46
 
43
- `read_doc` — direct exposure, active by default, read-only, closed-world
44
- (no network).
45
-
46
- How the call and its result look in the transcript — the call line as a headline
47
- and the body indented under it:
48
-
49
47
  <img src="../../assets/tools/read_doc.png" alt="The read_doc tool's result: the call line and an indented body with the file path, format, size, and the extracted text preview" width="100%">
50
48
 
49
+ `read_doc` — direct exposure, active by default, read-only, closed-world (no
50
+ network). It takes one file and returns that file's text.
51
+
51
52
  | Parameter | Type | Meaning |
52
53
  |---|---|---|
53
54
  | `path` | string, required, 1–4096 chars | Path to a `.pdf`, `.docx`, `.doc`, `.odt`, `.rtf`, or `.xlsx`, relative to the working directory (or absolute inside it). |
@@ -74,14 +75,8 @@ the text says what the file was.
74
75
 
75
76
  Errors are thrown for: a missing or non-file path, a path outside the root, an
76
77
  unsupported extension (with a targeted hint for legacy `.xls`), a disabled
77
- format, an over-size file, and a parser that rejects the file. The extension
78
- installs nothing.
79
-
80
- ## Behaviour by mode
81
-
82
- The tool behaves identically in `tui`, RPC, JSON, and print modes; it has no
83
- widgets or status output. `renderCall` / `renderResult` affect only the
84
- interactive transcript (and HTML exports).
78
+ format, an over-size file, and a parser that rejects the file. No external
79
+ executable is installed.
85
80
 
86
81
  The call line carries the path (`~`-shortened, OSC-8 linked when the terminal
87
82
  supports it) and any paging options. The result line drops the path — it is
@@ -117,11 +112,18 @@ applied, so the theme's own ANSI codes stay intact. A host-rejected call (a
117
112
  permission block, a validation failure) renders its own message through the same
118
113
  sanitized error path.
119
114
 
115
+ ## Behaviour by mode
116
+
117
+ The tool behaves identically in `tui`, RPC, JSON, and print modes; it has no
118
+ widgets or status output. `renderCall` / `renderResult` affect only the
119
+ interactive transcript (and HTML exports).
120
+
120
121
  ## Configuration
121
122
 
122
- The `file-read` section of the shared `bytefly.json`, loaded from
123
- `~/.pi/agent/bytefly.json` then `<cwd>/.pi/bytefly.json` (project wins), each
124
- merged over the defaults and clamped:
123
+ The keys live in the `file-read` section of the shared `bytefly.json`; resolution
124
+ rules are in
125
+ [`../configuration.md`](../configuration.md#file-read--document-and-spreadsheet-extraction).
126
+ Each value is merged over the defaults and clamped:
125
127
 
126
128
  | Key | Default | Clamp | Meaning |
127
129
  |---|---|---|---|
@@ -163,16 +165,17 @@ reads only `file-read`, and `bun run config:migrate` renames an existing
163
165
 
164
166
  ## Extraction
165
167
 
166
- Each extractor builds a plain-text document from the format's bytes:
168
+ Each extractor builds a plain-text document from the format's bytes. Every
169
+ format is enabled by default; `file-read.formats` turns one off.
167
170
 
168
- | Format | Library | Output normalization |
169
- |---|---|---|
170
- | PDF | `unpdf` (`extractText`, `mergePages: true`) | Every page in reading order, pages separated by a blank line. No OCR: a scanned or image-only PDF yields little or no text. |
171
- | DOCX | `mammoth` (`extractRawText`) | The body story, paragraphs separated by blank lines. Headers, footers, footnotes, and comments are not included. |
172
- | DOC | `word-extractor` (OLE body) | The classic Word body story. |
173
- | ODT | `fflate` + `fast-xml-parser` | `content.xml` is unzipped and walked: paragraphs and headings are lines, table cells are tab-separated, and `text:s` / `text:tab` / `text:line-break` become their whitespace. |
174
- | RTF | `rtf-parser` | Paragraphs are flattened to their span text; formatting is dropped. |
175
- | XLSX | `exceljs` | One `[Sheet]` heading per non-empty worksheet, then tab-separated rows. Dates and numbers use the workbook's displayed text; formulas show their computed value. Cell formatting is dropped. |
171
+ | Format | Extensions | Library | Output normalization |
172
+ |---|---|---|---|
173
+ | PDF | `.pdf` | `unpdf` (`extractText`, `mergePages: true`) | Every page in reading order, pages separated by a blank line. No OCR: a scanned or image-only PDF yields little or no text. |
174
+ | DOCX | `.docx` | `mammoth` (`extractRawText`) | The body story, paragraphs separated by blank lines. Headers, footers, footnotes, and comments are not included. |
175
+ | DOC | `.doc` | `word-extractor` (OLE body) | The classic Word body story. |
176
+ | ODT | `.odt` | `fflate` + `fast-xml-parser` | `content.xml` is unzipped and walked: paragraphs and headings are lines, table cells are tab-separated, and `text:s` / `text:tab` / `text:line-break` become their whitespace. |
177
+ | RTF | `.rtf` | `rtf-parser` | Paragraphs are flattened to their span text; formatting is dropped. |
178
+ | XLSX | `.xlsx` | `exceljs` | One `[Sheet]` heading per non-empty worksheet, then tab-separated rows. Dates and numbers use the workbook's displayed text; formulas show their computed value. Cell formatting is dropped. |
176
179
 
177
180
  The tool is pageable across the whole extracted text, so a word-processing
178
181
  document or a workbook is read in the same `startIndex` / `maxChars` way as a PDF.
@@ -181,17 +184,6 @@ Each call is bounded by `extractTimeoutMs` (30 s by default). A parser that
181
184
  outlasts the deadline is abandoned — the read fails with the limit named, and a
182
185
  late result is discarded — so a pathological file cannot park the turn.
183
186
 
184
- ## Supported formats
185
-
186
- | Format | Extensions | Extractor | Enabled by default |
187
- |---|---|---|---|
188
- | PDF | `.pdf` | `unpdf` | yes |
189
- | DOCX | `.docx` | `mammoth` | yes |
190
- | DOC | `.doc` | `word-extractor` | yes |
191
- | ODT | `.odt` | `fflate` + `fast-xml-parser` | yes |
192
- | RTF | `.rtf` | `rtf-parser` | yes |
193
- | XLSX | `.xlsx` | `exceljs` | yes |
194
-
195
187
  ## Adding a format
196
188
 
197
189
  The format table in `formats.ts` is the single source of truth; the handler,
@@ -208,7 +200,7 @@ config defaults, and error listing all derive from it.
208
200
  3. Add a `ReadFormat` entry to `FORMATS` and widen the `id` union.
209
201
  4. Add the new extractor to `extract/extract.test.ts` with a real minimal
210
202
  fixture, and extend `tool.test.ts` for format selection and config.
211
- 5. Add a row to the tables above.
203
+ 5. Add a row to the format table above.
212
204
 
213
205
  `tool.ts`, `paging.ts`, and the config normalizer need no changes.
214
206
 
@@ -318,4 +310,4 @@ builds a genuine PDF, DOCX, and ODT; XLSX is written with `exceljs`; legacy `.do
318
310
  reads the committed `files/legacy.doc`), so the libraries are exercised, not
319
311
  stubbed. `tool.test.ts` injects fake extractors for behavioural cases and reads a
320
312
  real PDF and a real `.doc` end to end; `test/helpers/fixtures/file-read.ts`
321
- provides the scratch root and an escape symlink.
313
+ provides the scratch root and an escape symlink.
@@ -1,7 +1,27 @@
1
1
  # goal — a persistent session objective for Pi
2
2
 
3
3
  A single high-level objective for the session, kept visible and restated before
4
- every turn. The goal is the *what*; the [`todo`](../todo/) list is the *how*.
4
+ every turn. The goal is the *what*; the [`todo`](./todo.md) list is the *how*.
5
+
6
+ ## Capabilities
7
+
8
+ Status records what a reader can rely on: **Shipped** is in a tagged release, **Unreleased** is complete in the current tree but not yet tagged, and **Partial** has one released part and one unreleased part. Rows are ordered by user value, highest first.
9
+
10
+ | Capability | How | What you get | Status |
11
+ |---|---|---|---|
12
+ | Goal recording | `goal`, `/goal set`, `/goal raw`, `/goal clear` | The objective is replaced whole, cleared by an empty value, and recorded verbatim when you ask for it. | Shipped |
13
+ | Phase tracking | `goal`, `/goal done` | The goal is `planning`, `active`, or `achieved`, and only the workflow can open a draft. | Shipped |
14
+ | Plan-first question | `goal`, `/goal <prompt>`, `/goal raw` | One dialog decides whether a new goal enters plan mode before it is pursued. | Shipped |
15
+ | Plan-mode allowlist | `goal` in plan mode | The model can record or update the objective while plan mode keeps the rest of the session read-only. | Shipped |
16
+ | Plan-phase settlement | `planning` goal, `exit_plan_mode` | Leaving plan mode without approval makes the draft `active`; approving the plan writes its `## Goal` into it. | Shipped |
17
+ | Understanding loop | `/goal <prompt>`, `/goal set`, `/goal edit` | A typed goal is restated, questioned when it is unclear, and confirmed in the editor before it is recorded. | Shipped |
18
+ | Goal rail | the `Goal · active · …` widget, the `achieved` key | The objective stays one line above the editor while it is pursued. | Shipped |
19
+ | Per-turn restatement | the `[SESSION GOAL]` reminder | The model reads the pursued objective before each turn, without it entering the transcript. | Shipped |
20
+ | Open-list check | `goal`, `/goal done` | Completing the goal names the todo items the list still has open. | Shipped |
21
+ | Branch-following state | tool-result `details`, the `goal` session entry | The goal survives `/resume` and `/tree`, and abandoned branches never leak in. | Shipped |
22
+ | Command help | `/goal help`, `/goal -h`, `/goal --help` | The verb list and the usage of each form, without changing the goal. | Shipped |
23
+
24
+ ## Quickstart
5
25
 
6
26
  ```bash
7
27
  pi --extension ./extensions/goal # load just this extension
@@ -9,86 +29,29 @@ pi -e . # load the whole @gavin-hu/my-pi-agent packa
9
29
  pi install ./ # install the package
10
30
  ```
11
31
 
12
- ## Tool rendering
32
+ ```text
33
+ /goal # show the goal, or the usage when none is set
34
+ /goal Refactor the parser to support streaming input
35
+ ```
13
36
 
14
- How the `goal` call and its result look in the transcript — the call line as a headline and the body indented under it:
37
+ The model records the same objective with `goal`; the model-facing tool never
38
+ runs the understanding loop.
15
39
 
16
- <img src="../../assets/tools/goal.png" alt="The goal tool's result: the call line and an indented body with the Goal header and the objective body" width="100%">
40
+ ## Tool
17
41
 
18
- ## What it does
19
-
20
- - Registers one model-callable tool, `goal`, that records the session
21
- objective. Send the complete objective every call; an empty objective clears
22
- it.
23
- - Tracks the goal's **phase**, not a flag: `planning` while a plan is being
24
- designed, `active` once the objective is final, `achieved` when it is done.
25
- A model-issued call may only send `active` or `achieved`, so one call can never
26
- claim a plan that does not exist.
27
- - **Asks once whether to plan first** when a new goal is recorded: accepting
28
- enters plan mode and stores the objective as `planning`, declining stores it
29
- `active`. The `plan` extension owns the mode; the question and its consent
30
- sentence come from `lib/plan-entry.ts`, the same ones `enter_plan_mode` shows,
31
- so the user answers once. The question is skipped for an update to a goal that
32
- is already pursued, without a dialog UI, or when `plan` is not loaded.
33
- - **Settles a draft whose plan never arrived.** If plan mode is left without an
34
- approved plan — the toggle, the shortcut, a resumed branch — the `planning`
35
- goal becomes `active` with its own wording on the next observation, so the
36
- phase can never get stuck. Approval is different: `plan` sets the goal from the
37
- plan's own `## Goal` section (see the [`plan`](../plan/) docs).
38
- - Achieves a goal over an open list knowingly: completing it while the branch's
39
- todo list still has open items names the count in the result (`1 todo item is
40
- still open…`), so the model closes the *how* instead of declaring the *what*
41
- done over an unfinished list; `/goal done` appends the same sentence to its
42
- notice. The count is read from the branch through `lib/todo-state.ts`; the
43
- structured result is unchanged.
44
- - Shows a persistent one-line rail above the editor whenever a goal exists,
45
- label-first to match the [`todo`](../todo/) widget, pinned at the top of the
46
- `Goal / Todos` stack when both are loaded:
47
-
48
- ```
49
- Goal · active · Refactor the parser to support streaming input and ship it …
50
- ```
51
-
52
- Once achieved the goal is hidden from the widget by default, matching a
53
- completed todo list; the transcript keeps the record. Set `achieved: "show"`
54
- in the `goal` section of `bytefly.json` to keep drawing the dimmed line
55
- instead.
56
-
57
- - Restates a **pursued** goal to the model before each turn (an invisible
58
- `[SESSION GOAL]` message) — `planning` and `active` alike, so a plan is designed
59
- against the objective — and stops once it is achieved.
60
- - Stores tool-set goals in tool-result `details` and command-set goals as a
61
- `goal` session entry, so either way the goal follows the active session branch
62
- and survives `/resume` and `/tree` — a `planning` draft included, since it is
63
- written the same way. Abandoned branches never leak into the current goal.
64
- - Understands the goal before recording it: `/goal <prompt>` asks the session
65
- model to restate the goal as it understands it and, when it cannot yet, to ask
66
- up to three questions — which the command asks directly through host dialogs
67
- (`select` / `input`). The answers are carried into the editor under the
68
- restatement, below a `--- notes (not saved) ---` line, so accepting the
69
- restatement as-is records the goal alone; folding an answer into the sentence
70
- above the line keeps it. `/goal set
71
- <prompt>` reuses the loop but records without starting a turn, `/goal edit`
72
- changes the current goal in place, and `/goal raw <prompt>` records the typed
73
- words verbatim. The loop is best-effort and needs a dialog UI plus a model; the
74
- `goal` tool stays the silent setter.
42
+ <img src="../../assets/tools/goal.png" alt="The goal tool's result: the call line and an indented body with the Goal header and the objective body" width="100%">
75
43
 
76
- ## Tool
44
+ `goal` — `exposure: "direct"` (the default), `executionMode: "sequential"`,
45
+ read-write, closed-world, idempotent. It is a whole-goal replacement: the model
46
+ sends the complete objective and the previous goal is discarded.
77
47
 
78
- `goal` is a whole-goal replacement tool. It declares an `outputSchema` and
79
- returns matching `structuredContent` (`{ goal, action, error? }`, with
80
- `goal: null` after a clear), so codemode/scripts can read the goal as data.
48
+ | Parameter | Type | Meaning |
49
+ |---|---|---|
50
+ | `objective` | string, required | The complete objective, one line. Pass `""` to clear. |
51
+ | `status` | `active` \| `achieved` | `achieved` completes the goal; `active` records a final objective; omit it to let the workflow ask whether to plan first. `planning` is the workflow's to set; a call cannot. |
81
52
 
82
- | Field | Value |
83
- |---|---|
84
- | `name` | `goal` |
85
- | `objective` | The complete objective, one line. Pass `""` to clear. |
86
- | `status` | `achieved` completes the goal; `active` records a final objective; omit it to let the workflow ask whether to plan first. `planning` is the workflow's to set; a call cannot. |
87
- | `exposure` | `direct` (default). |
88
- | `executionMode` | `sequential` — calls share the in-memory goal. |
89
- | `annotations` | `idempotentHint: true` (a snapshot write), not read-only. |
90
- | `outputSchema` | `GoalResult` — `{ goal, action, error? }`, also returned as `structuredContent`. |
91
- | `promptGuidelines` | Carry the when-to-use rule and the mark-achieved-with-a-blank-objective rule; the replacement/clear semantics live in the description. |
53
+ Result (`details` / `structuredContent`): `{ goal, action, error? }`, with
54
+ `goal: null` after a clear.
92
55
 
93
56
  Validation (a violation returns an error result and leaves the goal unchanged):
94
57
  a known `status`, an objective of at most 2000 characters, and a non-blank
@@ -109,19 +72,22 @@ whether the todos were written by the model, by `/todos`, or by an approved
109
72
  plan. It is text only: `details`, `structuredContent`, and the transcript header
110
73
  are unaffected.
111
74
 
112
- `goal` is allowlisted in `plan-mode` (alongside `todo`), so the model can record
113
- or update the objective during read-only exploration without leaving plan mode.
114
- Plan mode also reads the goal: it restates a pursued one in its injected context,
115
- it promotes a `planning` goal when a plan is approved, and it offers to record the
116
- plan's `## Goal` when the session has none (`lib/goal-state.ts` is the reader). An
117
- `active` or `achieved` goal — a final objective — is never rewritten by an
118
- approval.
75
+ The transcript result is the status header alone, directly under the call line
76
+ that names the objective:
77
+
78
+ ```
79
+ goal → set: Refactor the parser to support streaming input
80
+ Goal · active
81
+ ```
82
+
83
+ The renderer reuses the slot's component (`context.lastComponent`) rather than
84
+ rebuilding it each render.
119
85
 
120
- ### `/goal` command
86
+ ## Commands
121
87
 
122
88
  The command has no tool result, so it persists each change as a `goal` custom
123
89
  entry; `reconstructGoal` replays both entry kinds in branch order through
124
- `lib/goal-state.ts`, so its readers agree.
90
+ `lib/goal-state.ts`, so its readers agree. `/goal` completes each verb.
125
91
 
126
92
  | Command | What it does |
127
93
  |---|---|
@@ -134,50 +100,28 @@ entry; `reconstructGoal` replays both entry kinds in branch order through
134
100
  | `/goal clear` | Clear the goal. |
135
101
  | `/goal help` (or `-h` / `--help`) | Show the command usage. |
136
102
 
137
- `/goal <prompt>` runs the loop only when a dialog UI and a session model are
138
- present (TUI or RPC). In print and JSON mode the typed text is set and run
139
- unchanged. A plain-text clarifier reply is treated as a restatement, so a model
140
- that ignores the JSON instruction still works; a failed, timed-out, or unusable
141
- comprehension notices the user and keeps the typed text; and a dismissed
142
- question or editor cancels the command, changing nothing. The questions are asked
143
- one at a time with `ctx.ui.select` (for a question with options) and
144
- `ctx.ui.input` (for a free-form one); the command never calls
145
- `ask_user_question`. A bare `/goal set` and `/goal edit` need a dialog UI —
146
- without one they show the usage instead. `/goal raw` never needs one for the
147
- words, but the plan question does need a dialog: both run variants ask it only
148
- where a dialog UI exists, and record `active` otherwise. The record-only paths
149
- never ask; a goal recorded while plan mode is already on is still a draft
150
- (`planning`), because that is what it is.
103
+ The questions and the confirm step are host dialogs, forwarded by RPC, so the
104
+ loop needs one; in print and JSON mode the typed text is set and run unchanged.
105
+ A dismissed question or editor cancels the command, changing nothing. The
106
+ [`plan`](./plan.md) extension owns plan mode, so the plan question is skipped
107
+ when it is not loaded, and a bare `/goal set` shows the usage instead of an
108
+ editor when there is no dialog UI.
151
109
 
152
110
  ## Behaviour by mode
153
111
 
154
112
  The tool works in every mode. The persistent widget requires interactive
155
113
  (`tui`) mode; the reminder and the `/goal` command work everywhere. The
156
- `/goal <prompt>` runs the loop: its model call uses a cancellable `tui` spinner
157
- in interactive mode and runs directly in RPC; the questions and the confirm step
158
- are host dialogs, forwarded by RPC. In print and JSON mode the loop is skipped
159
- and the typed text is sent. The plan question is a host dialog too, so it is
160
- asked only in TUI and RPC, and skipped when the `plan` extension is not loaded.
161
- The widget is always a single line; the transcript result is the status header
162
- alone, directly under the call line that names the objective:
163
-
164
- ```
165
- goal → set: Refactor the parser to support streaming input
166
- Goal · active
167
- ```
168
-
169
- The renderer reuses the slot's component (`context.lastComponent`) rather than
170
- rebuilding it each render. A `planning` goal shows the same rail with a warning
171
- status word and is restated; achieved goals are hidden from the widget by
172
- default (restore the dimmed line with `achieved: "show"`), and they are no
173
- longer restated to the model. While a dock screen is open (`/todos`, `/jobs`,
174
- `/rewind`, or the `question` questionnaire), the rail is hidden and
114
+ `/goal <prompt>` comprehension call uses a cancellable `tui` spinner in
115
+ interactive mode and runs directly in RPC. When a goal exists, the widget is
116
+ always a single line above the editor; while a dock screen is open (`/todos`,
117
+ `/jobs`, `/rewind`, or the `question` questionnaire), the rail is hidden and
175
118
  returns on close.
176
119
 
177
120
  ## Configuration
178
121
 
179
122
  The widget presentation is configurable from the `goal` section of the shared
180
- `bytefly.json` (global document, then project document, project wins):
123
+ `bytefly.json`; resolution rules are in
124
+ [`../configuration.md`](../configuration.md#goal--the-goal-widget).
181
125
 
182
126
  | Key | Default | Meaning |
183
127
  |---|---|---|
@@ -186,6 +130,53 @@ The widget presentation is configurable from the `goal` section of the shared
186
130
  Malformed documents and invalid values are ignored, and goal behavior never
187
131
  depends on config.
188
132
 
133
+ ## Plan entry
134
+
135
+ A new goal's phase is decided once, with one dialog. Accepting enters plan mode
136
+ and stores the objective as `planning`, so a plan can be designed against it;
137
+ declining stores it `active`, the final objective. The question is skipped for
138
+ an update to a goal that is already pursued, without a dialog UI, and when
139
+ `plan` is not loaded. The question and its consent sentence come from
140
+ `lib/plan-entry.ts`, the same ones `enter_plan_mode` shows, so the user answers
141
+ once.
142
+
143
+ A draft whose plan never arrived is settled rather than stranded. If plan mode is
144
+ left without an approved plan — the toggle, the shortcut, a resumed branch — the
145
+ `planning` goal becomes `active` with its own wording on the next observation, so
146
+ the phase can never get stuck. Approval is different: `plan` sets the goal from
147
+ the plan's own `## Goal` section (see the [`plan`](./plan.md) docs), and only a
148
+ `planning` goal is eligible, so a final objective is the user's.
149
+
150
+ `goal` is allowlisted in `plan-mode` (alongside `todo`), so the model can record
151
+ or update the objective during read-only exploration without leaving plan mode.
152
+ Plan mode also reads the goal: it restates a pursued one in its injected context,
153
+ it promotes a `planning` goal when a plan is approved, and it offers to record the
154
+ plan's `## Goal` when the session has none (`lib/goal-state.ts` is the reader). An
155
+ `active` or `achieved` goal — a final objective — is never rewritten by an
156
+ approval.
157
+
158
+ ## The understanding loop
159
+
160
+ `/goal <prompt>` asks the session model to restate the goal as it understands it
161
+ and, when it cannot yet, to ask up to three questions, which the command asks
162
+ directly through host dialogs (`select` / `input`). The answers are carried into
163
+ the editor under the restatement, below a `--- notes (not saved) ---` line, so
164
+ accepting the restatement as-is records the goal alone; folding an answer into
165
+ the sentence above the line keeps it. `/goal set <prompt>` reuses the loop but
166
+ records without starting a turn, and `/goal raw <prompt>` records the typed words
167
+ verbatim.
168
+
169
+ The loop is best-effort and needs a dialog UI plus a model. A plain-text clarifier
170
+ reply is treated as a restatement, so a model that ignores the JSON instruction
171
+ still works; a failed, timed-out, or unusable comprehension notices the user and
172
+ keeps the typed text. The questions are asked one at a time with `ctx.ui.select`
173
+ (for a question with options) and `ctx.ui.input` (for a free-form one); the
174
+ command never calls `ask_user_question`. `/goal raw` never needs a dialog for the
175
+ words, but the plan question does: both run variants ask it only where a dialog
176
+ UI exists, and record `active` otherwise. The record-only paths never ask, and a
177
+ goal recorded while plan mode is already on is still a draft (`planning`), because
178
+ that is what it is. The `goal` tool stays the silent setter.
179
+
189
180
  ## Non-goals
190
181
 
191
182
  - No multiple or nested goals — one objective per session.
@@ -246,19 +237,12 @@ depends on config.
246
237
  record and stops the reminder, which a single empty-clear cannot express.
247
238
  - **The command runs the understanding loop.** `/goal <prompt>` is the
248
239
  user-driven path: one comprehension call (`clarify-prompt.ts` + `clarify.ts`)
249
- returns `{ understood, goal, questions }`. An understood goal opens a prefilled
250
- `ctx.ui.editor`, then `pi.sendUserMessage` sets and runs it. A not-understood
251
- goal has its questions asked through host dialogs, and its answers ride into
252
- the editor as `Also:` notes below a sentinel line that is cut away on save:
253
- no second model call rewrites the goal, so the user is the one who folds an
254
- answer into the sentence, above the line, when they want it kept.
255
- `/goal set`
256
- reuses the loop but skips the send; a bare `/goal set` or `/goal edit` opens
257
- the editor prefilled with the current goal; and `/goal raw` skips the loop
258
- entirely. The loop is best-effort (a failure keeps the typed text) and a
259
- dismissed dialog cancels the command, so nothing is half-applied. No
260
- conversation history is sent: the model must not invent scope the user did not
261
- state. The `goal` tool remains the model's setter, with no comprehension loop.
240
+ returns `{ understood, goal, questions }`. No second model call rewrites the
241
+ goal, so the user is the one who folds an answer into the sentence, above the
242
+ sentinel line, when they want it kept; and no conversation history is sent, so
243
+ the model cannot invent scope the user did not state. The loop is best-effort
244
+ and a dismissed dialog cancels the command, so nothing is half-applied. The
245
+ `goal` tool remains the model's setter, with no comprehension loop.
262
246
  - **State in `details` and a custom entry.** A `goal` tool call stores the goal
263
247
  in its result's `details`; the `/goal` command appends a `goal` custom entry
264
248
  (`pi.appendEntry`). `reconstructGoal` reads the branch through
@@ -5,47 +5,51 @@ the background and manage them without blocking the turn. A job starts
5
5
  immediately and keeps running across tool calls; poll it, tail its output, wait
6
6
  for it, or kill it.
7
7
 
8
- <img src="../../assets/job.png" alt="The /jobs screen: a summary line, the job rows with status glyphs and elapsed times, and the focused job's command, cwd, and last output line" width="100%">
9
-
8
+ ## Capabilities
9
+
10
+ Status records what a reader can rely on: **Shipped** is in a tagged release, **Unreleased** is complete in the current tree but not yet tagged, and **Partial** has one released part and one unreleased part. Rows are ordered by user value, highest first.
11
+
12
+ | Capability | How | What you get | Status |
13
+ |---|---|---|---|
14
+ | Background execution | `job` (`start`) | A long build, test suite, server, or watcher keeps running while the turn moves on. | Shipped |
15
+ | Job listing | `job` (`list`), `/jobs` | This session's jobs with status and elapsed time, and a live peer session's tagged `peer`. | Shipped |
16
+ | Polling and waiting | `job` (`status`, `logs`, `wait`) | One call returns a job's status, a bounded output tail, or blocks until it finishes. | Shipped |
17
+ | Stopping jobs | `job` (`kill` with `signal`) | A running job receives `SIGTERM`, `SIGKILL`, or `SIGINT`, and a finished one can be cleared. | Shipped |
18
+ | Selective clear | `job` (`clear` with `id`, `all`) | Clear one finished record by id, or remove running jobs with `all` after a confirmation in the screen. | Shipped |
19
+ | Start deadline | `timeoutMs` on `start` | A job is killed after the requested wall-clock limit instead of running until the session ends. | Shipped |
20
+ | Kill escalation | `killGraceMs` | A kill sends `SIGTERM` first, then `SIGKILL` after the grace period, on a user kill and on shutdown. | Shipped |
21
+ | Stdin driving | `job` (`send` with `input`, `newline`, `eof`) | A line or a bare keystroke reaches a running job started with `stdin: true`, and `eof` closes its input. | Shipped |
22
+ | Rotating logs | `job` (`logs`), `job.maxLogBytes` | stdout and stderr stream to an append-only log that rotates at 8 MiB, so a chatty job cannot fill the disk. | Shipped |
23
+ | Status chips | `job.showStatus` | `▸ N` running and `✗ N` unreported-failure counts in the footer. | Shipped |
24
+ | Completion reporting | `job` (`wake`), `job.wakeOnFinish` | A finished job is reported at the next turn, or triggers one as soon as the agent is idle. | Shipped |
25
+ | Job screen | `/jobs` | Select a job, open its log, kill it, or clear finished ones, with a confirmation on the destructive verbs. | Shipped |
26
+ | Detached lifetime | `job` (`start`, `detached`) | A POSIX job outlives the session and is reattached next time; Windows refuses the detach instead of faking one. | Shipped |
27
+ | Finished-job pruning | `maxJobs` | The registry drops the oldest finished records once the configured count is exceeded. | Shipped |
28
+ | Structured result | tool-result `details` | Every call returns a `JobDetails` record of the job and the action's outcome. | Shipped |
29
+
30
+ ## Quickstart
31
+
32
+ ```bash
33
+ pi --extension ./extensions/job # load just this extension
34
+ pi -e . # load the whole @gavin-hu/my-pi-agent package
35
+ pi install ./ # install the package
10
36
  ```
11
- pi --extension ./extensions/job # load just this extension
12
- pi -e . # load the whole @gavin-hu/my-pi-agent package
13
- pi install ./ # install the package
37
+
38
+ ```text
39
+ /jobs # this session's jobs, and their logs
14
40
  ```
15
41
 
16
- ## Tool rendering
42
+ The model starts a job with `job start` and collects it with `job status`,
43
+ `job logs`, or `job wait`.
17
44
 
18
- How the `job` call and its result look in the transcript — the call line as a headline and the body indented under it:
45
+ ## Tool
19
46
 
20
47
  <img src="../../assets/tools/job.png" alt="The job tool's result: the call line and an indented body with a finished and a failed job row, each with its status glyph and elapsed time" width="100%">
21
48
 
22
- ## What it does
23
-
24
- - Registers one model-callable tool, `job`, with actions `start`, `list`,
25
- `status`, `logs`, `send`, `kill`, `wait`, and `clear`.
26
- - Shows this session's jobs, and — tagged `peer` — a live peer session's, so two
27
- Pi sessions on one repo can see each other's builds; the peer's rows are
28
- read-only on the screen.
29
- - Runs each command in its own process group, streaming stdout and stderr to an
30
- append-only log file under `~/.pi/agent/jobs/<project>/`. A segment rotates
31
- to `<id>.log.1` at `maxLogBytes` (8 MiB by default), so a chatty job cannot
32
- fill the disk, and `logs` reads across the boundary.
33
- - Feeds a running job input when it was started with `stdin: true`: `send`
34
- writes a line (or a bare keystroke) to its stdin, and `eof: true` closes it, so
35
- a REPL, a debugger prompt, or a `read` can be driven without a TTY.
36
- - Shows a `▸ N` running status chip and a separate `✗ N` unreported-failure
37
- chip for this session's jobs, plus a `/jobs` screen for selecting, tailing,
38
- killing, and clearing.
39
- - Reports finished jobs to the model at the start of the next turn, or — for
40
- jobs started with `wake: true` — by triggering one turn as soon as the agent is
41
- idle, including right after a running turn settles.
42
- - Kills session-owned jobs on shutdown so no process is left behind; on POSIX,
43
- start a job with `detached: true` to keep it running and reattach next session.
44
- A detached job points its own output straight at its log file, so it keeps
45
- logging after this process ends. Windows has neither a process group to leave
46
- nor a shell `EXIT` trap, so it refuses the detach instead of faking one.
47
-
48
- ## Tool
49
+ `job` — `executionMode: "sequential"`, `readOnlyHint: false`,
50
+ `destructiveHint: true`, `idempotentHint: false`, `openWorldHint: true`. One
51
+ tool with eight actions: `start` returns immediately with an id, and the rest
52
+ act on it.
49
53
 
50
54
  | Field | Value |
51
55
  |---|---|
@@ -147,20 +151,27 @@ job status j1
147
151
  ✓ j1 tests · exit 0 · 12.0s
148
152
  ```
149
153
 
150
- ### Command: `/jobs`
154
+ ## Commands
151
155
 
152
156
  `/jobs` opens an interactive list in the TUI, and prints a text summary
153
- elsewhere. The list shows the jobs this session started, a dead session's
154
- records adopted into it, and a live peer session's jobs tagged `peer`. On
155
- the screen: `↑`/`↓` or `j`/`k` select, `PgUp`/`PgDn` page, `Home`/`End` jump,
156
- `Enter`/`l` opens the log pane, `d` (or `K`) kills the selected running job,
157
- `x` clears finished jobs, `Esc`/`q` closes. `d` and `x` ask for a `y`/`N`
158
- confirmation before acting, so a stray key cannot kill a build; when `x` would
159
- drop an unreported result, the prompt says how many. A `d` on a finished job (or
160
- an `x` with nothing finished) says so instead of doing nothing. Another
161
- session's row opens its log but is otherwise left alone: `d` answers
162
- `j9 is another session's job; not killed.`, and `x` counts and clears only this
163
- session's finished jobs. In the log pane,
157
+ elsewhere.
158
+
159
+ <img src="../../assets/screens/job.png" alt="The /jobs screen: a summary line, the job rows with status glyphs and elapsed times, and the focused job's command, cwd, and last output line" width="100%">
160
+
161
+ | Command | What it does |
162
+ |---|---|
163
+ | `/jobs` | Show and manage background jobs: an interactive list in the TUI, a text summary elsewhere. |
164
+
165
+ The list shows the jobs this session started, a dead session's records adopted
166
+ into it, and a live peer session's jobs tagged `peer`. On the screen: `↑`/`↓` or
167
+ `j`/`k` select, `PgUp`/`PgDn` page, `Home`/`End` jump, `Enter`/`l` opens the log
168
+ pane, `d` (or `K`) kills the selected running job, `x` clears finished jobs,
169
+ `Esc`/`q` closes. `d` and `x` ask for a `y`/`N` confirmation before acting, so a
170
+ stray key cannot kill a build; when `x` would drop an unreported result, the
171
+ prompt says how many. A `d` on a finished job (or an `x` with nothing finished)
172
+ says so instead of doing nothing. Another session's row opens its log but is
173
+ otherwise left alone: `d` answers `j9 is another session's job; not killed.`,
174
+ and `x` counts and clears only this session's finished jobs. In the log pane,
164
175
  `↑`/`↓`, `PgUp`/`PgDn`, `Home`/`End`, and `g`/`G` scroll; the view follows the
165
176
  tail until you scroll up, and `Esc`/`Backspace`/`q` returns to the list. The
166
177
  pane's subheader names the job's log file, notes when only a bounded tail is
@@ -172,8 +183,7 @@ chips count only this session's. Running jobs come first, then recent finished
172
183
  ones, and a focused detail pane follows (command, cwd, outcome, the owner when
173
184
  it is another session, and the last output line). Selection is tracked by job
174
185
  id, so the live re-sort cannot move the cursor or switch the open log out from
175
- under the user.
176
- Any dock screen (`/todos`, `/jobs`, `/rewind`, or the
186
+ under the user. Any dock screen (`/todos`, `/jobs`, `/rewind`, or the
177
187
  `question` questionnaire) hides the above-editor goal/todo rails for its
178
188
  lifetime and restores them on close.
179
189
 
@@ -185,8 +195,9 @@ notifications instead.
185
195
 
186
196
  ## Configuration
187
197
 
188
- The `job` section of the shared `bytefly.json` (global document, then project
189
- document, project wins):
198
+ The keys live in the `job` section of the shared `bytefly.json`; resolution rules
199
+ are in
200
+ [`../configuration.md`](../configuration.md#job--background-jobs).
190
201
 
191
202
  | Key | Default | Meaning |
192
203
  |---|---|---|