@esso0428/pi-subagents 0.17.16 → 0.17.18

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 (359) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/ROADMAP.md +87 -0
  3. package/docs/post-0.17.6-agents-roster-viewer-spec.md +42 -0
  4. package/docs/post-0.17.6-durable-history-spec.md +41 -0
  5. package/docs/post-0.17.6-feature-specs.md +50 -0
  6. package/docs/post-0.17.6-nested-agents-spec.md +38 -0
  7. package/docs/post-0.17.6-recovery-shutdown-spec.md +46 -0
  8. package/docs/post-0.17.6-ui-latency-baseline-spec.md +46 -0
  9. package/docs/post-0.17.6-workflow-rpc-lifecycle-spec.md +54 -0
  10. package/package.json +1 -1
  11. package/src/agent-history.ts +2 -54
  12. package/src/agent-manager.ts +406 -1288
  13. package/src/agent-runner.ts +27 -251
  14. package/src/agent-types.ts +32 -188
  15. package/src/cross-extension-rpc.ts +20 -96
  16. package/src/custom-agents.ts +13 -170
  17. package/src/index.ts +480 -1920
  18. package/src/invocation-config.ts +3 -118
  19. package/src/model-resolver.ts +0 -18
  20. package/src/output-file.ts +6 -61
  21. package/src/prompts.ts +2 -45
  22. package/src/schedule.ts +14 -35
  23. package/src/settings.ts +2 -301
  24. package/src/status-note.ts +1 -66
  25. package/src/types.ts +10 -177
  26. package/src/ui/agent-widget.ts +57 -278
  27. package/src/ui/conversation-blocks.ts +0 -6
  28. package/src/ui/conversation-timeline.ts +25 -139
  29. package/src/ui/conversation-viewer.ts +48 -212
  30. package/src/ui/schedule-menu.ts +8 -9
  31. package/src/usage.ts +2 -109
  32. package/src/worktree.ts +55 -69
  33. package/dist/abortable.d.ts +0 -13
  34. package/dist/abortable.d.ts.map +0 -1
  35. package/dist/abortable.js +0 -43
  36. package/dist/abortable.js.map +0 -1
  37. package/dist/agent-color.d.ts +0 -36
  38. package/dist/agent-color.d.ts.map +0 -1
  39. package/dist/agent-color.js +0 -124
  40. package/dist/agent-color.js.map +0 -1
  41. package/dist/agent-file-toggle.d.ts +0 -126
  42. package/dist/agent-file-toggle.d.ts.map +0 -1
  43. package/dist/agent-file-toggle.js +0 -259
  44. package/dist/agent-file-toggle.js.map +0 -1
  45. package/dist/agent-history-list.d.ts +0 -19
  46. package/dist/agent-history-list.d.ts.map +0 -1
  47. package/dist/agent-history-list.js +0 -69
  48. package/dist/agent-history-list.js.map +0 -1
  49. package/dist/agent-history.d.ts +0 -35
  50. package/dist/agent-history.d.ts.map +0 -1
  51. package/dist/agent-history.js +0 -188
  52. package/dist/agent-history.js.map +0 -1
  53. package/dist/agent-manager.d.ts +0 -503
  54. package/dist/agent-manager.d.ts.map +0 -1
  55. package/dist/agent-manager.js +0 -1572
  56. package/dist/agent-manager.js.map +0 -1
  57. package/dist/agent-recovery.d.ts +0 -37
  58. package/dist/agent-recovery.d.ts.map +0 -1
  59. package/dist/agent-recovery.js +0 -171
  60. package/dist/agent-recovery.js.map +0 -1
  61. package/dist/agent-runner.d.ts +0 -303
  62. package/dist/agent-runner.d.ts.map +0 -1
  63. package/dist/agent-runner.js +0 -1003
  64. package/dist/agent-runner.js.map +0 -1
  65. package/dist/agent-types.d.ts +0 -120
  66. package/dist/agent-types.d.ts.map +0 -1
  67. package/dist/agent-types.js +0 -301
  68. package/dist/agent-types.js.map +0 -1
  69. package/dist/child-context.d.ts +0 -3
  70. package/dist/child-context.d.ts.map +0 -1
  71. package/dist/child-context.js +0 -13
  72. package/dist/child-context.js.map +0 -1
  73. package/dist/context.d.ts +0 -13
  74. package/dist/context.d.ts.map +0 -1
  75. package/dist/context.js +0 -57
  76. package/dist/context.js.map +0 -1
  77. package/dist/cross-extension-rpc.d.ts +0 -67
  78. package/dist/cross-extension-rpc.d.ts.map +0 -1
  79. package/dist/cross-extension-rpc.js +0 -139
  80. package/dist/cross-extension-rpc.js.map +0 -1
  81. package/dist/custom-agents.d.ts +0 -55
  82. package/dist/custom-agents.d.ts.map +0 -1
  83. package/dist/custom-agents.js +0 -309
  84. package/dist/custom-agents.js.map +0 -1
  85. package/dist/default-agents.d.ts +0 -8
  86. package/dist/default-agents.d.ts.map +0 -1
  87. package/dist/default-agents.js +0 -123
  88. package/dist/default-agents.js.map +0 -1
  89. package/dist/enabled-models.d.ts +0 -50
  90. package/dist/enabled-models.d.ts.map +0 -1
  91. package/dist/enabled-models.js +0 -146
  92. package/dist/enabled-models.js.map +0 -1
  93. package/dist/env.d.ts +0 -7
  94. package/dist/env.d.ts.map +0 -1
  95. package/dist/env.js +0 -29
  96. package/dist/env.js.map +0 -1
  97. package/dist/group-join.d.ts +0 -33
  98. package/dist/group-join.d.ts.map +0 -1
  99. package/dist/group-join.js +0 -117
  100. package/dist/group-join.js.map +0 -1
  101. package/dist/index.d.ts +0 -51
  102. package/dist/index.d.ts.map +0 -1
  103. package/dist/index.js +0 -3687
  104. package/dist/index.js.map +0 -1
  105. package/dist/invocation-config.d.ts +0 -108
  106. package/dist/invocation-config.d.ts.map +0 -1
  107. package/dist/invocation-config.js +0 -84
  108. package/dist/invocation-config.js.map +0 -1
  109. package/dist/memory.d.ts +0 -54
  110. package/dist/memory.d.ts.map +0 -1
  111. package/dist/memory.js +0 -166
  112. package/dist/memory.js.map +0 -1
  113. package/dist/mention-clone.d.ts +0 -88
  114. package/dist/mention-clone.d.ts.map +0 -1
  115. package/dist/mention-clone.js +0 -154
  116. package/dist/mention-clone.js.map +0 -1
  117. package/dist/mention.d.ts +0 -82
  118. package/dist/mention.d.ts.map +0 -1
  119. package/dist/mention.js +0 -132
  120. package/dist/mention.js.map +0 -1
  121. package/dist/model-resolver.d.ts +0 -37
  122. package/dist/model-resolver.d.ts.map +0 -1
  123. package/dist/model-resolver.js +0 -96
  124. package/dist/model-resolver.js.map +0 -1
  125. package/dist/model-scope.d.ts +0 -50
  126. package/dist/model-scope.d.ts.map +0 -1
  127. package/dist/model-scope.js +0 -49
  128. package/dist/model-scope.js.map +0 -1
  129. package/dist/nested-tools.d.ts +0 -57
  130. package/dist/nested-tools.d.ts.map +0 -1
  131. package/dist/nested-tools.js +0 -301
  132. package/dist/nested-tools.js.map +0 -1
  133. package/dist/nico-overrides.d.ts +0 -54
  134. package/dist/nico-overrides.d.ts.map +0 -1
  135. package/dist/nico-overrides.js +0 -170
  136. package/dist/nico-overrides.js.map +0 -1
  137. package/dist/output-file.d.ts +0 -44
  138. package/dist/output-file.d.ts.map +0 -1
  139. package/dist/output-file.js +0 -156
  140. package/dist/output-file.js.map +0 -1
  141. package/dist/prompts.d.ts +0 -56
  142. package/dist/prompts.d.ts.map +0 -1
  143. package/dist/prompts.js +0 -92
  144. package/dist/prompts.js.map +0 -1
  145. package/dist/schedule-store.d.ts +0 -39
  146. package/dist/schedule-store.d.ts.map +0 -1
  147. package/dist/schedule-store.js +0 -156
  148. package/dist/schedule-store.js.map +0 -1
  149. package/dist/schedule.d.ts +0 -110
  150. package/dist/schedule.d.ts.map +0 -1
  151. package/dist/schedule.js +0 -360
  152. package/dist/schedule.js.map +0 -1
  153. package/dist/settings.d.ts +0 -354
  154. package/dist/settings.d.ts.map +0 -1
  155. package/dist/settings.js +0 -247
  156. package/dist/settings.js.map +0 -1
  157. package/dist/skill-loader.d.ts +0 -25
  158. package/dist/skill-loader.d.ts.map +0 -1
  159. package/dist/skill-loader.js +0 -94
  160. package/dist/skill-loader.js.map +0 -1
  161. package/dist/status-note.d.ts +0 -62
  162. package/dist/status-note.d.ts.map +0 -1
  163. package/dist/status-note.js +0 -86
  164. package/dist/status-note.js.map +0 -1
  165. package/dist/structured-output.d.ts +0 -62
  166. package/dist/structured-output.d.ts.map +0 -1
  167. package/dist/structured-output.js +0 -113
  168. package/dist/structured-output.js.map +0 -1
  169. package/dist/types.d.ts +0 -372
  170. package/dist/types.d.ts.map +0 -1
  171. package/dist/types.js +0 -6
  172. package/dist/types.js.map +0 -1
  173. package/dist/ui/agent-mention.d.ts +0 -83
  174. package/dist/ui/agent-mention.d.ts.map +0 -1
  175. package/dist/ui/agent-mention.js +0 -188
  176. package/dist/ui/agent-mention.js.map +0 -1
  177. package/dist/ui/agent-widget.d.ts +0 -241
  178. package/dist/ui/agent-widget.d.ts.map +0 -1
  179. package/dist/ui/agent-widget.js +0 -992
  180. package/dist/ui/agent-widget.js.map +0 -1
  181. package/dist/ui/ccstyle/diff/ansi-utils.d.ts +0 -11
  182. package/dist/ui/ccstyle/diff/ansi-utils.d.ts.map +0 -1
  183. package/dist/ui/ccstyle/diff/ansi-utils.js +0 -145
  184. package/dist/ui/ccstyle/diff/ansi-utils.js.map +0 -1
  185. package/dist/ui/ccstyle/diff/diff-presentation.d.ts +0 -12
  186. package/dist/ui/ccstyle/diff/diff-presentation.d.ts.map +0 -1
  187. package/dist/ui/ccstyle/diff/diff-presentation.js +0 -48
  188. package/dist/ui/ccstyle/diff/diff-presentation.js.map +0 -1
  189. package/dist/ui/ccstyle/diff/diff-renderer.d.ts +0 -46
  190. package/dist/ui/ccstyle/diff/diff-renderer.d.ts.map +0 -1
  191. package/dist/ui/ccstyle/diff/diff-renderer.js +0 -2049
  192. package/dist/ui/ccstyle/diff/diff-renderer.js.map +0 -1
  193. package/dist/ui/ccstyle/diff/line-width-safety.d.ts +0 -12
  194. package/dist/ui/ccstyle/diff/line-width-safety.d.ts.map +0 -1
  195. package/dist/ui/ccstyle/diff/line-width-safety.js +0 -58
  196. package/dist/ui/ccstyle/diff/line-width-safety.js.map +0 -1
  197. package/dist/ui/ccstyle/diff/render-utils.d.ts +0 -6
  198. package/dist/ui/ccstyle/diff/render-utils.d.ts.map +0 -1
  199. package/dist/ui/ccstyle/diff/render-utils.js +0 -25
  200. package/dist/ui/ccstyle/diff/render-utils.js.map +0 -1
  201. package/dist/ui/ccstyle/diff/shiki-highlight.d.ts +0 -19
  202. package/dist/ui/ccstyle/diff/shiki-highlight.d.ts.map +0 -1
  203. package/dist/ui/ccstyle/diff/shiki-highlight.js +0 -85
  204. package/dist/ui/ccstyle/diff/shiki-highlight.js.map +0 -1
  205. package/dist/ui/ccstyle/diff/types.d.ts +0 -22
  206. package/dist/ui/ccstyle/diff/types.d.ts.map +0 -1
  207. package/dist/ui/ccstyle/diff/types.js +0 -10
  208. package/dist/ui/ccstyle/diff/types.js.map +0 -1
  209. package/dist/ui/ccstyle/diff/write-display-utils.d.ts +0 -2
  210. package/dist/ui/ccstyle/diff/write-display-utils.d.ts.map +0 -1
  211. package/dist/ui/ccstyle/diff/write-display-utils.js +0 -12
  212. package/dist/ui/ccstyle/diff/write-display-utils.js.map +0 -1
  213. package/dist/ui/ccstyle/tool-renderer.d.ts +0 -17
  214. package/dist/ui/ccstyle/tool-renderer.d.ts.map +0 -1
  215. package/dist/ui/ccstyle/tool-renderer.js +0 -74
  216. package/dist/ui/ccstyle/tool-renderer.js.map +0 -1
  217. package/dist/ui/ccstyle/tool-result.d.ts +0 -44
  218. package/dist/ui/ccstyle/tool-result.d.ts.map +0 -1
  219. package/dist/ui/ccstyle/tool-result.js +0 -423
  220. package/dist/ui/ccstyle/tool-result.js.map +0 -1
  221. package/dist/ui/conversation-blocks.d.ts +0 -46
  222. package/dist/ui/conversation-blocks.d.ts.map +0 -1
  223. package/dist/ui/conversation-blocks.js +0 -313
  224. package/dist/ui/conversation-blocks.js.map +0 -1
  225. package/dist/ui/conversation-nvim.d.ts +0 -8
  226. package/dist/ui/conversation-nvim.d.ts.map +0 -1
  227. package/dist/ui/conversation-nvim.js +0 -117
  228. package/dist/ui/conversation-nvim.js.map +0 -1
  229. package/dist/ui/conversation-role.d.ts +0 -13
  230. package/dist/ui/conversation-role.d.ts.map +0 -1
  231. package/dist/ui/conversation-role.js +0 -39
  232. package/dist/ui/conversation-role.js.map +0 -1
  233. package/dist/ui/conversation-search.d.ts +0 -39
  234. package/dist/ui/conversation-search.d.ts.map +0 -1
  235. package/dist/ui/conversation-search.js +0 -124
  236. package/dist/ui/conversation-search.js.map +0 -1
  237. package/dist/ui/conversation-timeline.d.ts +0 -102
  238. package/dist/ui/conversation-timeline.d.ts.map +0 -1
  239. package/dist/ui/conversation-timeline.js +0 -555
  240. package/dist/ui/conversation-timeline.js.map +0 -1
  241. package/dist/ui/conversation-viewer.d.ts +0 -138
  242. package/dist/ui/conversation-viewer.d.ts.map +0 -1
  243. package/dist/ui/conversation-viewer.js +0 -1175
  244. package/dist/ui/conversation-viewer.js.map +0 -1
  245. package/dist/ui/schedule-menu.d.ts +0 -17
  246. package/dist/ui/schedule-menu.d.ts.map +0 -1
  247. package/dist/ui/schedule-menu.js +0 -95
  248. package/dist/ui/schedule-menu.js.map +0 -1
  249. package/dist/ui/select-item.d.ts +0 -28
  250. package/dist/ui/select-item.d.ts.map +0 -1
  251. package/dist/ui/select-item.js +0 -35
  252. package/dist/ui/select-item.js.map +0 -1
  253. package/dist/ui/viewer-keys.d.ts +0 -21
  254. package/dist/ui/viewer-keys.d.ts.map +0 -1
  255. package/dist/ui/viewer-keys.js +0 -18
  256. package/dist/ui/viewer-keys.js.map +0 -1
  257. package/dist/ui/workflow-card.d.ts +0 -176
  258. package/dist/ui/workflow-card.d.ts.map +0 -1
  259. package/dist/ui/workflow-card.js +0 -333
  260. package/dist/ui/workflow-card.js.map +0 -1
  261. package/dist/ui/workflow-dialog.d.ts +0 -306
  262. package/dist/ui/workflow-dialog.d.ts.map +0 -1
  263. package/dist/ui/workflow-dialog.js +0 -844
  264. package/dist/ui/workflow-dialog.js.map +0 -1
  265. package/dist/ui/workflow-menu.d.ts +0 -42
  266. package/dist/ui/workflow-menu.d.ts.map +0 -1
  267. package/dist/ui/workflow-menu.js +0 -127
  268. package/dist/ui/workflow-menu.js.map +0 -1
  269. package/dist/usage.d.ts +0 -136
  270. package/dist/usage.d.ts.map +0 -1
  271. package/dist/usage.js +0 -121
  272. package/dist/usage.js.map +0 -1
  273. package/dist/workflow/collisions.d.ts +0 -96
  274. package/dist/workflow/collisions.d.ts.map +0 -1
  275. package/dist/workflow/collisions.js +0 -89
  276. package/dist/workflow/collisions.js.map +0 -1
  277. package/dist/workflow/entry.d.ts +0 -33
  278. package/dist/workflow/entry.d.ts.map +0 -1
  279. package/dist/workflow/entry.js +0 -30
  280. package/dist/workflow/entry.js.map +0 -1
  281. package/dist/workflow/host.d.ts +0 -63
  282. package/dist/workflow/host.d.ts.map +0 -1
  283. package/dist/workflow/host.js +0 -363
  284. package/dist/workflow/host.js.map +0 -1
  285. package/dist/workflow/journal.d.ts +0 -98
  286. package/dist/workflow/journal.d.ts.map +0 -1
  287. package/dist/workflow/journal.js +0 -121
  288. package/dist/workflow/journal.js.map +0 -1
  289. package/dist/workflow/json-schema.d.ts +0 -52
  290. package/dist/workflow/json-schema.d.ts.map +0 -1
  291. package/dist/workflow/json-schema.js +0 -112
  292. package/dist/workflow/json-schema.js.map +0 -1
  293. package/dist/workflow/meta.d.ts +0 -68
  294. package/dist/workflow/meta.d.ts.map +0 -1
  295. package/dist/workflow/meta.js +0 -318
  296. package/dist/workflow/meta.js.map +0 -1
  297. package/dist/workflow/progress.d.ts +0 -225
  298. package/dist/workflow/progress.d.ts.map +0 -1
  299. package/dist/workflow/progress.js +0 -362
  300. package/dist/workflow/progress.js.map +0 -1
  301. package/dist/workflow/runtime.d.ts +0 -335
  302. package/dist/workflow/runtime.d.ts.map +0 -1
  303. package/dist/workflow/runtime.js +0 -831
  304. package/dist/workflow/runtime.js.map +0 -1
  305. package/dist/workflow/saved.d.ts +0 -91
  306. package/dist/workflow/saved.d.ts.map +0 -1
  307. package/dist/workflow/saved.js +0 -204
  308. package/dist/workflow/saved.js.map +0 -1
  309. package/dist/workflow/task.d.ts +0 -137
  310. package/dist/workflow/task.d.ts.map +0 -1
  311. package/dist/workflow/task.js +0 -208
  312. package/dist/workflow/task.js.map +0 -1
  313. package/dist/workflow/tool-description.d.ts +0 -39
  314. package/dist/workflow/tool-description.d.ts.map +0 -1
  315. package/dist/workflow/tool-description.js +0 -200
  316. package/dist/workflow/tool-description.js.map +0 -1
  317. package/dist/workflow/worker-source.d.ts +0 -48
  318. package/dist/workflow/worker-source.d.ts.map +0 -1
  319. package/dist/workflow/worker-source.js +0 -779
  320. package/dist/workflow/worker-source.js.map +0 -1
  321. package/dist/worktree.d.ts +0 -53
  322. package/dist/worktree.d.ts.map +0 -1
  323. package/dist/worktree.js +0 -165
  324. package/dist/worktree.js.map +0 -1
  325. package/dist/write-execution.d.ts +0 -42
  326. package/dist/write-execution.d.ts.map +0 -1
  327. package/dist/write-execution.js +0 -138
  328. package/dist/write-execution.js.map +0 -1
  329. package/dist/xml.d.ts +0 -11
  330. package/dist/xml.d.ts.map +0 -1
  331. package/dist/xml.js +0 -13
  332. package/dist/xml.js.map +0 -1
  333. package/src/abortable.ts +0 -43
  334. package/src/agent-color.ts +0 -161
  335. package/src/agent-file-toggle.ts +0 -269
  336. package/src/child-context.ts +0 -15
  337. package/src/mention-clone.ts +0 -196
  338. package/src/mention.ts +0 -141
  339. package/src/model-scope.ts +0 -70
  340. package/src/nested-tools.ts +0 -424
  341. package/src/structured-output.ts +0 -130
  342. package/src/ui/agent-mention.ts +0 -216
  343. package/src/ui/select-item.ts +0 -45
  344. package/src/ui/workflow-card.ts +0 -470
  345. package/src/ui/workflow-dialog.ts +0 -1115
  346. package/src/ui/workflow-menu.ts +0 -166
  347. package/src/workflow/collisions.ts +0 -123
  348. package/src/workflow/entry.ts +0 -47
  349. package/src/workflow/host.ts +0 -403
  350. package/src/workflow/journal.ts +0 -164
  351. package/src/workflow/json-schema.ts +0 -128
  352. package/src/workflow/meta.ts +0 -325
  353. package/src/workflow/progress.ts +0 -550
  354. package/src/workflow/runtime.ts +0 -1219
  355. package/src/workflow/saved.ts +0 -217
  356. package/src/workflow/task.ts +0 -302
  357. package/src/workflow/tool-description.ts +0 -200
  358. package/src/workflow/worker-source.ts +0 -781
  359. package/src/xml.ts +0 -13
@@ -6,22 +6,7 @@ import { existsSync, readdirSync, readFileSync } from "node:fs";
6
6
  import { basename, join } from "node:path";
7
7
  import { getAgentDir, parseFrontmatter } from "@earendil-works/pi-coding-agent";
8
8
  import { BUILTIN_TOOL_NAMES } from "./agent-types.js";
9
- import type { AgentConfig, IsolationMode, MemoryScope, ThinkingLevel } from "./types.js";
10
-
11
- /**
12
- * The one thing a declared `name:` may not contain, matching Claude Code
13
- * exactly: it reserves `:` for plugin-scoped identifiers (`my-plugin:reviewer`)
14
- * and refuses to load a file whose name uses one.
15
- *
16
- * Nothing else is rejected. Claude Code's docs describe names as "lowercase
17
- * letters and hyphens", but that is guidance — the only stated load failure is
18
- * the colon, so `name: Code Reviewer` must work here too. (The stricter
19
- * letters/digits/underscore/hyphen regex in Claude Code applies to the Agent
20
- * tool's spawn-time `name` parameter, which is a different field.) Mixed case
21
- * has to be allowed regardless: the built-in types `Explore` and `Plan` use it,
22
- * and a file must be able to override one.
23
- */
24
- const RESERVED_IN_TYPE = ":";
9
+ import type { AgentConfig, MemoryScope, ThinkingLevel } from "./types.js";
25
10
 
26
11
  /**
27
12
  * Scan for custom agent .md files from multiple locations.
@@ -34,30 +19,21 @@ const RESERVED_IN_TYPE = ":";
34
19
  * between the two project locations, .pi/agents wins — .pi stays the project
35
20
  * authority; .agents/agents is an additional read location.
36
21
  * Any name is allowed — names matching defaults (e.g. "Explore") override them.
37
- *
38
- * An agent's type comes from its frontmatter `name:`, falling back to the
39
- * filename — Claude Code's rule, where "the filename doesn't have to match".
40
- * Because the type is now declared rather than derived from a unique path, two
41
- * files can claim the same one; the later load wins, as it always has for a
42
- * filename clash, and `warnSkippedOverride` reports the substitution.
43
22
  */
44
- export function loadCustomAgents(cwd: string, strict = false): Map<string, AgentConfig> {
23
+ export function loadCustomAgents(cwd: string): Map<string, AgentConfig> {
45
24
  const globalDir = join(getAgentDir(), "agents");
46
25
  const workspaceProjectDir = join(cwd, ".agents", "agents");
47
26
  const projectDir = join(cwd, ".pi", "agents");
48
27
 
49
28
  const agents = new Map<string, AgentConfig>();
50
- loadFromDir(globalDir, agents, "global", strict); // lowest priority
51
- loadFromDir(workspaceProjectDir, agents, "project", strict); // shared workspace
52
- loadFromDir(projectDir, agents, "project", strict); // highest priority (overwrites)
53
-
54
- warnedLastLoad = warnedThisLoad;
55
- warnedThisLoad = new Set();
29
+ loadFromDir(globalDir, agents, "global"); // lowest priority
30
+ loadFromDir(workspaceProjectDir, agents, "project"); // shared workspace
31
+ loadFromDir(projectDir, agents, "project"); // highest priority (overwrites)
56
32
  return agents;
57
33
  }
58
34
 
59
35
  /** Load agent configs from a directory into the map. */
60
- function loadFromDir(dir: string, agents: Map<string, AgentConfig>, source: "project" | "global", strict: boolean): void {
36
+ function loadFromDir(dir: string, agents: Map<string, AgentConfig>, source: "project" | "global"): void {
61
37
  if (!existsSync(dir)) return;
62
38
 
63
39
  let files: string[];
@@ -68,49 +44,22 @@ function loadFromDir(dir: string, agents: Map<string, AgentConfig>, source: "pro
68
44
  }
69
45
 
70
46
  for (const file of files) {
71
- const filenameType = basename(file, ".md");
47
+ const name = basename(file, ".md");
72
48
 
73
- const path = join(dir, file);
74
-
75
- const parsed = readAgentFile(path, strict);
76
- if (!parsed) {
77
- warnSkippedOverride(filenameType, agents);
49
+ let content: string;
50
+ try {
51
+ content = readFileSync(join(dir, file), "utf-8");
52
+ } catch {
78
53
  continue;
79
54
  }
80
- const { frontmatter: fm, body } = parsed;
81
55
 
82
- // Claude Code's rule: `name:` IS the agent type, and the filename need not
83
- // match. Absent, the filename stands in — Claude Code requires the field,
84
- // but most files here predate it and must keep loading.
85
- const declared = str(fm.name)?.trim();
86
- if (declared?.includes(RESERVED_IN_TYPE)) {
87
- // Refusing beats silently substituting: the file would otherwise load
88
- // under its filename, so `Agent({subagent_type})` would succeed against
89
- // an agent whose declared identity nothing honoured.
90
- warnIfNew(
91
- `Agent file ${path} declares name "${declared}", which contains "${RESERVED_IN_TYPE}" — reserved for `
92
- + "plugin-scoped identifiers. Rename it, or move the label to `display_name:`. Skipping.",
93
- );
94
- // No `warnSkippedOverride`: this file would have registered under its
95
- // *declared* name, which nothing else can hold (a colon keeps it out of
96
- // the registry), so it shadowed nothing. Passing the filename instead
97
- // would report a substitution of an unrelated agent that never happened.
98
- continue;
99
- }
100
- // `||`, not `??`: a quoted empty or all-whitespace `name:` would otherwise
101
- // register the agent under the empty type — unspawnable, and it takes the
102
- // filename-derived one down with it.
103
- const name = declared || filenameType;
56
+ const { frontmatter: fm, body } = parseFrontmatter<Record<string, unknown>>(content);
104
57
 
105
58
  const { builtinToolNames, extSelectors } = parseToolsField(fm.tools);
106
59
 
107
60
  agents.set(name, {
108
61
  name,
109
- // Only `display_name` now: `name` is the type, and `getConfig` already
110
- // falls back to the type when no label is set — so a Claude Code file
111
- // with `name: code-reviewer` still badges as "code-reviewer".
112
62
  displayName: str(fm.display_name),
113
- color: str(fm.color),
114
63
  description: str(fm.description) ?? name,
115
64
  builtinToolNames,
116
65
  extSelectors,
@@ -124,91 +73,19 @@ function loadFromDir(dir: string, agents: Map<string, AgentConfig>, source: "pro
124
73
  persistSession: fm.persist_session != null ? fm.persist_session === true : undefined,
125
74
  outputTranscript: fm.output_transcript != null ? fm.output_transcript !== false : undefined,
126
75
  sessionDir: str(fm.session_dir),
127
- allowedSubagents: parseAllowedSubagents(fm.allowed_subagents),
128
76
  systemPrompt: body.trim(),
129
77
  promptMode: fm.prompt_mode === "append" ? "append" : "replace",
130
78
  inheritContext: fm.inherit_context != null ? fm.inherit_context === true : undefined,
131
79
  runInBackground: fm.run_in_background != null ? fm.run_in_background === true : undefined,
132
80
  isolated: fm.isolated != null ? fm.isolated === true : undefined,
133
81
  memory: parseMemory(fm.memory),
134
- isolation: parseIsolation(fm.isolation),
82
+ isolation: fm.isolation === "worktree" ? "worktree" : undefined,
135
83
  enabled: fm.enabled !== false, // default true; explicitly false disables
136
84
  source,
137
- sourcePath: path,
138
85
  });
139
86
  }
140
87
  }
141
88
 
142
- /**
143
- * Read and parse one agent file, or warn and return undefined for the caller to
144
- * skip. One bad file must not take the whole extension down with it — an
145
- * unparseable `.md` used to abort activation, so pi exited before the TUI.
146
- *
147
- * The path is as much of the fix as the recovery: a bare YAML error ("line 2,
148
- * column 14") is unactionable when agents come from three directories at once,
149
- * and the only other symptom is `Unknown agent type`, which reads like a typo.
150
- *
151
- * Under `strict` the same failure rethrows, still naming the path, so callers
152
- * that opted into failing closed stop rather than run a substituted agent.
153
- */
154
- /**
155
- * Parse an agent file's frontmatter, tolerating a leading UTF-8 BOM.
156
- *
157
- * Editors across the Windows/CJK world write UTF-8 with a BOM by default, and
158
- * pi's parser did not look past one before 0.84.3: the fence never matched, so
159
- * the frontmatter came back empty and the *whole file* — YAML and all — became
160
- * the body. An agent authored that way silently lost every field. `tools: none`
161
- * going missing is the sharp edge: the agent registers with the default
162
- * toolset rather than none, which is a wider grant than its author wrote.
163
- *
164
- * Stripped here rather than detected per pi version, because this is the only
165
- * place agent files are read and the BOM is a file-encoding artifact, not
166
- * content — normalising it at the boundary keeps one behaviour across the whole
167
- * supported peer range instead of forking on what happens to be installed.
168
- */
169
- export function parseAgentFrontmatter<T extends Record<string, unknown>>(content: string): { frontmatter: T; body: string } {
170
- return parseFrontmatter<T>(content.startsWith("\uFEFF") ? content.slice(1) : content);
171
- }
172
-
173
- function readAgentFile(path: string, strict: boolean): { frontmatter: Record<string, unknown>; body: string } | undefined {
174
- try {
175
- return parseAgentFrontmatter<Record<string, unknown>>(readFileSync(path, "utf-8"));
176
- } catch (err) {
177
- const reason = err instanceof Error ? err.message : String(err);
178
- if (strict) throw new Error(`${path}: ${reason}`);
179
- warnIfNew(`Skipping agent file ${path}: ${reason}`);
180
- return undefined;
181
- }
182
- }
183
-
184
- /**
185
- * A skipped file that was overriding an already-loaded agent leaves the name
186
- * pointing at a *different* file — its own prompt, model and tools. Nothing
187
- * downstream can flag that: unlike an unknown type, the `Agent` call succeeds.
188
- */
189
- function warnSkippedOverride(name: string, agents: Map<string, AgentConfig>): void {
190
- const surviving = agents.get(name);
191
- // Nothing shadowed, or what it shadowed is disabled: dispatch refuses the type
192
- // either way (see resolveEnabledTypeIn), so there is no substitution to report.
193
- if (!surviving?.sourcePath || surviving.enabled === false) return;
194
- warnIfNew(`Agent "${name}" now loads from ${surviving.sourcePath} instead`);
195
- }
196
-
197
- let warnedLastLoad = new Set<string>();
198
- let warnedThisLoad = new Set<string>();
199
-
200
- /**
201
- * Agents reload on activation and again on every `Agent` call, so an unchanged
202
- * problem would re-warn all session — over a painted TUI, since pi does not
203
- * redirect console output. Compare against the previous load rather than every
204
- * load ever, so a file that is fixed and then broken again still reports.
205
- */
206
- function warnIfNew(message: string): void {
207
- warnedThisLoad.add(message);
208
- if (warnedLastLoad.has(message)) return;
209
- console.warn(`[pi-subagents] ${message}`);
210
- }
211
-
212
89
  // ---- Field parsers ----
213
90
  // All follow the same convention: omitted → default, "none"/empty → nothing, value → exact.
214
91
 
@@ -233,22 +110,6 @@ function parseCsvField(val: unknown): string[] | undefined {
233
110
  return items.length > 0 ? items : undefined;
234
111
  }
235
112
 
236
- /**
237
- * Parse the nested-delegation allowlist. Single field, default-off:
238
- * omitted/empty/"none"/`false` → undefined (no nested tools); "all"/"*"/`true`
239
- * → "all" (any enabled agent); csv → only the listed types.
240
- *
241
- * Booleans are accepted because `extensions:`/`skills:` take them and users
242
- * generalize: without this, YAML's `true` stringifies into an agent type
243
- * literally named "true", so the tools appear and every spawn is refused.
244
- */
245
- function parseAllowedSubagents(val: unknown): "all" | string[] | undefined {
246
- if (typeof val === "boolean") return val ? "all" : undefined;
247
- const items = parseCsvField(val);
248
- if (!items) return undefined;
249
- return items.some(i => i === "*" || i.toLowerCase() === "all") ? "all" : items;
250
- }
251
-
252
113
  /**
253
114
  * Parse a comma-separated list field with defaults.
254
115
  * omitted → defaults; "none"/empty → []; csv → listed items.
@@ -294,24 +155,6 @@ function parseMemory(val: unknown): MemoryScope | undefined {
294
155
  return undefined;
295
156
  }
296
157
 
297
- /**
298
- * Parse the `isolation` frontmatter field.
299
- *
300
- * `off` is kept as a value rather than folded into `undefined` because the two
301
- * do not mean the same thing here: agent config outranks tool-call params, so
302
- * `off` vetoes a caller's `worktree` while an absent field lets it through.
303
- *
304
- * pi's frontmatter parser is not YAML 1.1 — bare `off` and `no` arrive as
305
- * strings and only `false` becomes a boolean — so all three spellings are
306
- * accepted rather than leaving an author's intent silently dropped. Anything
307
- * else stays `undefined`, as before.
308
- */
309
- function parseIsolation(val: unknown): IsolationMode | undefined {
310
- if (val === "worktree") return "worktree";
311
- if (val === "off" || val === "none" || val === "no" || val === false) return "off";
312
- return undefined;
313
- }
314
-
315
158
  /**
316
159
  * Parse an inherit field (extensions, skills).
317
160
  * omitted/true → true (inherit all); false/"none"/empty → false; csv → listed names.