@vitos-pizza/vitos-pizza 0.1.0 → 0.2.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 (259) hide show
  1. package/AGENTS.md +158 -158
  2. package/CHANGELOG.md +42 -24
  3. package/LICENSE +21 -21
  4. package/README.md +237 -234
  5. package/assets/logo.svg +28 -28
  6. package/node_modules/@vitos-pizza/agent-mode/extensions/index.ts +138 -117
  7. package/node_modules/@vitos-pizza/agent-mode/package.json +3 -3
  8. package/node_modules/@vitos-pizza/agent-mode/src/apply-mode.ts +60 -60
  9. package/node_modules/@vitos-pizza/agent-mode/src/mode-events.ts +102 -102
  10. package/node_modules/@vitos-pizza/agent-mode/src/modes.ts +43 -43
  11. package/node_modules/@vitos-pizza/agent-mode/src/plan-instructions.ts +85 -44
  12. package/node_modules/@vitos-pizza/agent-mode/tests/apply-mode.test.ts +36 -36
  13. package/node_modules/@vitos-pizza/agent-mode/tests/mode-events.test.ts +22 -22
  14. package/node_modules/@vitos-pizza/agent-mode/tests/modes.test.ts +38 -38
  15. package/node_modules/@vitos-pizza/agent-mode/tests/plan-instructions.test.ts +68 -45
  16. package/node_modules/@vitos-pizza/agent-mode/tsconfig.json +13 -13
  17. package/node_modules/@vitos-pizza/agent-mode/vitest.config.ts +8 -8
  18. package/node_modules/@vitos-pizza/keybindings/extensions/index.ts +104 -104
  19. package/node_modules/@vitos-pizza/keybindings/package.json +1 -1
  20. package/node_modules/@vitos-pizza/keybindings/presets/shortcuts.json +3 -3
  21. package/node_modules/@vitos-pizza/keybindings/src/bind.ts +57 -57
  22. package/node_modules/@vitos-pizza/keybindings/src/config.ts +84 -84
  23. package/node_modules/@vitos-pizza/keybindings/src/registry.ts +43 -43
  24. package/node_modules/@vitos-pizza/keybindings/src/types.ts +54 -54
  25. package/node_modules/@vitos-pizza/keybindings/tests/config.test.ts +83 -83
  26. package/node_modules/@vitos-pizza/keybindings/tests/registry.test.ts +142 -142
  27. package/node_modules/@vitos-pizza/keybindings/tsconfig.json +13 -13
  28. package/node_modules/@vitos-pizza/keybindings/vitest.config.ts +8 -8
  29. package/node_modules/@vitos-pizza/permission-system/extensions/index.ts +162 -162
  30. package/node_modules/@vitos-pizza/permission-system/package.json +2 -2
  31. package/node_modules/@vitos-pizza/permission-system/src/bash/matcher.ts +54 -51
  32. package/node_modules/@vitos-pizza/permission-system/src/bash/parser.ts +40 -40
  33. package/node_modules/@vitos-pizza/permission-system/src/config-paths.ts +23 -23
  34. package/node_modules/@vitos-pizza/permission-system/src/detect-preset-mode.ts +61 -61
  35. package/node_modules/@vitos-pizza/permission-system/src/expand-home.ts +12 -12
  36. package/node_modules/@vitos-pizza/permission-system/src/forwarding/forwarder.ts +224 -224
  37. package/node_modules/@vitos-pizza/permission-system/src/forwarding/subagent-registry.ts +61 -61
  38. package/node_modules/@vitos-pizza/permission-system/src/gates/path-utils.ts +66 -66
  39. package/node_modules/@vitos-pizza/permission-system/src/gates/pipeline.ts +151 -155
  40. package/node_modules/@vitos-pizza/permission-system/src/handlers/before-agent-start.ts +86 -65
  41. package/node_modules/@vitos-pizza/permission-system/src/handlers/skill-input.ts +1 -1
  42. package/node_modules/@vitos-pizza/permission-system/src/handlers/tool-call-gate.ts +204 -199
  43. package/node_modules/@vitos-pizza/permission-system/src/mode-api.ts +44 -44
  44. package/node_modules/@vitos-pizza/permission-system/src/permission-evaluator.ts +233 -216
  45. package/node_modules/@vitos-pizza/permission-system/src/permission-session.ts +62 -62
  46. package/node_modules/@vitos-pizza/permission-system/src/policy-loader.ts +211 -211
  47. package/node_modules/@vitos-pizza/permission-system/src/sanitizers/skill-prompt.ts +99 -99
  48. package/node_modules/@vitos-pizza/permission-system/src/sanitizers/system-prompt.ts +221 -221
  49. package/node_modules/@vitos-pizza/permission-system/src/service.ts +66 -66
  50. package/node_modules/@vitos-pizza/permission-system/src/session-approvals.ts +4 -1
  51. package/node_modules/@vitos-pizza/permission-system/src/types.ts +80 -80
  52. package/node_modules/@vitos-pizza/permission-system/src/ui/approval-summary.ts +43 -43
  53. package/node_modules/@vitos-pizza/permission-system/src/ui/config-modal.ts +36 -36
  54. package/node_modules/@vitos-pizza/permission-system/src/ui/permission-dialog.ts +52 -52
  55. package/node_modules/@vitos-pizza/permission-system/src/wildcard-matcher.ts +81 -81
  56. package/node_modules/@vitos-pizza/permission-system/src/yaml-frontmatter.ts +84 -84
  57. package/node_modules/@vitos-pizza/permission-system/src/yolo/runtime-api.ts +49 -49
  58. package/node_modules/@vitos-pizza/permission-system/tests/bash-parser.test.ts +32 -32
  59. package/node_modules/@vitos-pizza/permission-system/tests/before-agent-start.test.ts +61 -0
  60. package/node_modules/@vitos-pizza/permission-system/tests/detect-preset-mode.test.ts +68 -68
  61. package/node_modules/@vitos-pizza/permission-system/tests/forwarder.test.ts +55 -55
  62. package/node_modules/@vitos-pizza/permission-system/tests/gates.test.ts +170 -61
  63. package/node_modules/@vitos-pizza/permission-system/tests/path-utils.test.ts +68 -68
  64. package/node_modules/@vitos-pizza/permission-system/tests/permission-evaluator.test.ts +82 -55
  65. package/node_modules/@vitos-pizza/permission-system/tests/policy-loader.test.ts +25 -25
  66. package/node_modules/@vitos-pizza/permission-system/tests/system-prompt-sanitizer.test.ts +22 -22
  67. package/node_modules/@vitos-pizza/permission-system/tests/wildcard-matcher.test.ts +27 -27
  68. package/node_modules/@vitos-pizza/permission-system/tsconfig.json +13 -13
  69. package/node_modules/@vitos-pizza/permission-system/vitest.config.ts +8 -8
  70. package/node_modules/@vitos-pizza/question/package.json +1 -1
  71. package/node_modules/@vitos-pizza/question/src/question-ui.ts +5 -1
  72. package/node_modules/@vitos-pizza/question/src/register-tool.ts +1 -4
  73. package/node_modules/@vitos-pizza/question/tests/question-ui-width-cache.test.ts +45 -0
  74. package/node_modules/@vitos-pizza/question/tests/register-tool-render.test.ts +48 -45
  75. package/node_modules/@vitos-pizza/session-title/extensions/index.ts +137 -137
  76. package/node_modules/@vitos-pizza/session-title/package.json +3 -3
  77. package/node_modules/@vitos-pizza/session-title/src/build-title-prompt.ts +16 -16
  78. package/node_modules/@vitos-pizza/session-title/src/extract-title-context.ts +55 -55
  79. package/node_modules/@vitos-pizza/session-title/src/fast-rules.ts +48 -48
  80. package/node_modules/@vitos-pizza/session-title/src/load-settings.ts +70 -70
  81. package/node_modules/@vitos-pizza/session-title/src/normalize-title.ts +20 -20
  82. package/node_modules/@vitos-pizza/session-title/src/parse-title-response.ts +6 -6
  83. package/node_modules/@vitos-pizza/session-title/src/resolve-title-model.ts +33 -33
  84. package/node_modules/@vitos-pizza/session-title/src/schedule-title-job.ts +53 -53
  85. package/node_modules/@vitos-pizza/session-title/src/should-schedule.ts +30 -30
  86. package/node_modules/@vitos-pizza/session-title/src/title-agent.ts +60 -60
  87. package/node_modules/@vitos-pizza/session-title/src/truncate-input.ts +10 -10
  88. package/node_modules/@vitos-pizza/session-title/src/types.ts +29 -29
  89. package/node_modules/@vitos-pizza/session-title/tests/build-title-prompt.test.ts +34 -34
  90. package/node_modules/@vitos-pizza/session-title/tests/extension.test.ts +198 -198
  91. package/node_modules/@vitos-pizza/session-title/tests/extract-title-context.test.ts +48 -48
  92. package/node_modules/@vitos-pizza/session-title/tests/fast-rules.test.ts +43 -43
  93. package/node_modules/@vitos-pizza/session-title/tests/parse-title-response.test.ts +34 -34
  94. package/node_modules/@vitos-pizza/session-title/tests/should-schedule.test.ts +30 -30
  95. package/node_modules/@vitos-pizza/session-title/tests/title-agent.test.ts +154 -154
  96. package/node_modules/@vitos-pizza/session-title/tests/truncate-input.test.ts +15 -15
  97. package/node_modules/@vitos-pizza/session-title/tsconfig.json +13 -13
  98. package/node_modules/@vitos-pizza/session-title/vitest.config.ts +8 -8
  99. package/node_modules/@vitos-pizza/subagents/agents/planner.md +46 -43
  100. package/node_modules/@vitos-pizza/subagents/agents/scout.md +40 -40
  101. package/node_modules/@vitos-pizza/subagents/agents/title.md +13 -13
  102. package/node_modules/@vitos-pizza/subagents/agents/worker.md +36 -36
  103. package/node_modules/@vitos-pizza/subagents/extensions/index.ts +164 -164
  104. package/node_modules/@vitos-pizza/subagents/package.json +1 -1
  105. package/node_modules/@vitos-pizza/subagents/skills/subagents/SKILL.md +86 -80
  106. package/node_modules/@vitos-pizza/subagents/src/agents.ts +166 -166
  107. package/node_modules/@vitos-pizza/subagents/src/async/job-tracker.ts +71 -71
  108. package/node_modules/@vitos-pizza/subagents/src/async/spawn-async.ts +220 -220
  109. package/node_modules/@vitos-pizza/subagents/src/async/storage.ts +57 -57
  110. package/node_modules/@vitos-pizza/subagents/src/env.ts +4 -4
  111. package/node_modules/@vitos-pizza/subagents/src/execute-subagent.ts +285 -285
  112. package/node_modules/@vitos-pizza/subagents/src/progress.ts +187 -187
  113. package/node_modules/@vitos-pizza/subagents/src/render-animation.ts +40 -40
  114. package/node_modules/@vitos-pizza/subagents/src/render.ts +428 -428
  115. package/node_modules/@vitos-pizza/subagents/src/rpc/channels.ts +21 -21
  116. package/node_modules/@vitos-pizza/subagents/src/rpc/client.ts +144 -144
  117. package/node_modules/@vitos-pizza/subagents/src/rpc/server.ts +11 -11
  118. package/node_modules/@vitos-pizza/subagents/src/run-agent.ts +313 -313
  119. package/node_modules/@vitos-pizza/subagents/src/runtime.ts +102 -102
  120. package/node_modules/@vitos-pizza/subagents/src/schema.ts +62 -62
  121. package/node_modules/@vitos-pizza/subagents/src/spawn.ts +104 -104
  122. package/node_modules/@vitos-pizza/subagents/src/types.ts +92 -92
  123. package/node_modules/@vitos-pizza/subagents/src/utils.ts +100 -100
  124. package/node_modules/@vitos-pizza/subagents/src/wait.ts +159 -159
  125. package/node_modules/@vitos-pizza/subagents/tests/agents.test.ts +19 -19
  126. package/node_modules/@vitos-pizza/subagents/tests/execute-subagent.test.ts +96 -96
  127. package/node_modules/@vitos-pizza/subagents/tests/extension.test.ts +42 -42
  128. package/node_modules/@vitos-pizza/subagents/tests/progress.test.ts +206 -206
  129. package/node_modules/@vitos-pizza/subagents/tests/rpc.test.ts +74 -74
  130. package/node_modules/@vitos-pizza/subagents/tsconfig.json +13 -13
  131. package/node_modules/@vitos-pizza/subagents/vitest.config.ts +8 -8
  132. package/node_modules/@vitos-pizza/ui-enhancements/package.json +1 -1
  133. package/node_modules/@vitos-pizza/ui-enhancements/src/chrome/agent-mode-badge.ts +60 -60
  134. package/node_modules/@vitos-pizza/ui-enhancements/src/chrome/border-status.ts +14 -1
  135. package/node_modules/@vitos-pizza/ui-enhancements/src/chrome/resize-recovery.ts +63 -0
  136. package/node_modules/@vitos-pizza/ui-enhancements/tests/agent-mode-badge.test.ts +35 -35
  137. package/node_modules/@vitos-pizza/ui-enhancements/tests/resize-recovery.test.ts +135 -0
  138. package/node_modules/@vitos-pizza/websearch/package.json +1 -1
  139. package/node_modules/node-addon-api/tools/conversion.js +0 -0
  140. package/node_modules/node-gyp-build/bin.js +0 -0
  141. package/node_modules/node-gyp-build/build-test.js +0 -0
  142. package/node_modules/node-gyp-build/optional.js +0 -0
  143. package/package.json +11 -9
  144. package/packages/agent-mode/extensions/index.ts +138 -117
  145. package/packages/agent-mode/package.json +3 -3
  146. package/packages/agent-mode/src/apply-mode.ts +60 -60
  147. package/packages/agent-mode/src/mode-events.ts +102 -102
  148. package/packages/agent-mode/src/modes.ts +43 -43
  149. package/packages/agent-mode/src/plan-instructions.ts +85 -44
  150. package/packages/agent-mode/tsconfig.json +13 -13
  151. package/packages/agent-mode/vitest.config.ts +8 -8
  152. package/packages/keybindings/extensions/index.ts +104 -104
  153. package/packages/keybindings/package.json +1 -1
  154. package/packages/keybindings/presets/shortcuts.json +3 -3
  155. package/packages/keybindings/src/bind.ts +57 -57
  156. package/packages/keybindings/src/config.ts +84 -84
  157. package/packages/keybindings/src/registry.ts +43 -43
  158. package/packages/keybindings/src/types.ts +54 -54
  159. package/packages/keybindings/tsconfig.json +13 -13
  160. package/packages/keybindings/vitest.config.ts +8 -8
  161. package/packages/permission-system/extensions/index.ts +162 -162
  162. package/packages/permission-system/node_modules/web-tree-sitter/debug/tree-sitter.wasm +0 -0
  163. package/packages/permission-system/node_modules/web-tree-sitter/tree-sitter.wasm +0 -0
  164. package/packages/permission-system/package.json +2 -2
  165. package/packages/permission-system/src/bash/matcher.ts +54 -51
  166. package/packages/permission-system/src/bash/parser.ts +40 -40
  167. package/packages/permission-system/src/config-paths.ts +23 -23
  168. package/packages/permission-system/src/detect-preset-mode.ts +61 -61
  169. package/packages/permission-system/src/expand-home.ts +12 -12
  170. package/packages/permission-system/src/forwarding/forwarder.ts +224 -224
  171. package/packages/permission-system/src/forwarding/subagent-registry.ts +61 -61
  172. package/packages/permission-system/src/gates/path-utils.ts +66 -66
  173. package/packages/permission-system/src/gates/pipeline.ts +151 -155
  174. package/packages/permission-system/src/handlers/before-agent-start.ts +86 -65
  175. package/packages/permission-system/src/handlers/skill-input.ts +1 -1
  176. package/packages/permission-system/src/handlers/tool-call-gate.ts +204 -199
  177. package/packages/permission-system/src/mode-api.ts +44 -44
  178. package/packages/permission-system/src/permission-evaluator.ts +233 -216
  179. package/packages/permission-system/src/permission-session.ts +62 -62
  180. package/packages/permission-system/src/policy-loader.ts +211 -211
  181. package/packages/permission-system/src/sanitizers/skill-prompt.ts +99 -99
  182. package/packages/permission-system/src/sanitizers/system-prompt.ts +221 -221
  183. package/packages/permission-system/src/service.ts +66 -66
  184. package/packages/permission-system/src/session-approvals.ts +4 -1
  185. package/packages/permission-system/src/types.ts +80 -80
  186. package/packages/permission-system/src/ui/approval-summary.ts +43 -43
  187. package/packages/permission-system/src/ui/config-modal.ts +36 -36
  188. package/packages/permission-system/src/ui/permission-dialog.ts +52 -52
  189. package/packages/permission-system/src/wildcard-matcher.ts +81 -81
  190. package/packages/permission-system/src/yaml-frontmatter.ts +84 -84
  191. package/packages/permission-system/src/yolo/runtime-api.ts +49 -49
  192. package/packages/permission-system/tsconfig.json +13 -13
  193. package/packages/permission-system/vitest.config.ts +8 -8
  194. package/packages/question/package.json +1 -1
  195. package/packages/question/src/question-ui.ts +5 -1
  196. package/packages/question/src/register-tool.ts +1 -4
  197. package/packages/session-title/extensions/index.ts +137 -137
  198. package/packages/session-title/package.json +3 -3
  199. package/packages/session-title/src/build-title-prompt.ts +16 -16
  200. package/packages/session-title/src/extract-title-context.ts +55 -55
  201. package/packages/session-title/src/fast-rules.ts +48 -48
  202. package/packages/session-title/src/load-settings.ts +70 -70
  203. package/packages/session-title/src/normalize-title.ts +20 -20
  204. package/packages/session-title/src/parse-title-response.ts +6 -6
  205. package/packages/session-title/src/resolve-title-model.ts +33 -33
  206. package/packages/session-title/src/schedule-title-job.ts +53 -53
  207. package/packages/session-title/src/should-schedule.ts +30 -30
  208. package/packages/session-title/src/title-agent.ts +60 -60
  209. package/packages/session-title/src/truncate-input.ts +10 -10
  210. package/packages/session-title/src/types.ts +29 -29
  211. package/packages/session-title/tsconfig.json +13 -13
  212. package/packages/session-title/vitest.config.ts +8 -8
  213. package/packages/subagents/agents/planner.md +46 -43
  214. package/packages/subagents/agents/scout.md +40 -40
  215. package/packages/subagents/agents/title.md +13 -13
  216. package/packages/subagents/agents/worker.md +36 -36
  217. package/packages/subagents/extensions/index.ts +164 -164
  218. package/packages/subagents/package.json +1 -1
  219. package/packages/subagents/skills/subagents/SKILL.md +86 -80
  220. package/packages/subagents/src/agents.ts +166 -166
  221. package/packages/subagents/src/async/job-tracker.ts +71 -71
  222. package/packages/subagents/src/async/spawn-async.ts +220 -220
  223. package/packages/subagents/src/async/storage.ts +57 -57
  224. package/packages/subagents/src/env.ts +4 -4
  225. package/packages/subagents/src/execute-subagent.ts +285 -285
  226. package/packages/subagents/src/progress.ts +187 -187
  227. package/packages/subagents/src/render-animation.ts +40 -40
  228. package/packages/subagents/src/render.ts +428 -428
  229. package/packages/subagents/src/rpc/channels.ts +21 -21
  230. package/packages/subagents/src/rpc/client.ts +144 -144
  231. package/packages/subagents/src/rpc/server.ts +11 -11
  232. package/packages/subagents/src/run-agent.ts +313 -313
  233. package/packages/subagents/src/runtime.ts +102 -102
  234. package/packages/subagents/src/schema.ts +62 -62
  235. package/packages/subagents/src/spawn.ts +104 -104
  236. package/packages/subagents/src/types.ts +92 -92
  237. package/packages/subagents/src/utils.ts +100 -100
  238. package/packages/subagents/src/wait.ts +159 -159
  239. package/packages/subagents/tsconfig.json +13 -13
  240. package/packages/subagents/vitest.config.ts +8 -8
  241. package/packages/todoist/extensions/index.ts +163 -0
  242. package/packages/todoist/package.json +34 -0
  243. package/packages/todoist/src/events.ts +7 -0
  244. package/packages/todoist/src/silent-render.ts +8 -0
  245. package/packages/todoist/src/state.ts +147 -0
  246. package/packages/todoist/src/tools.ts +175 -0
  247. package/packages/todoist/src/types.ts +34 -0
  248. package/packages/todoist/src/widget.ts +109 -0
  249. package/packages/todoist/tsconfig.json +13 -0
  250. package/packages/todoist/vitest.config.ts +8 -0
  251. package/packages/ui-enhancements/package.json +1 -1
  252. package/packages/ui-enhancements/src/chrome/agent-mode-badge.ts +60 -60
  253. package/packages/ui-enhancements/src/chrome/border-status.ts +14 -1
  254. package/packages/ui-enhancements/src/chrome/resize-recovery.ts +63 -0
  255. package/packages/websearch/package.json +1 -1
  256. package/scripts/sync-pi-manifest.mjs +1 -1
  257. package/packages/permission-system/node_modules/web-tree-sitter/lib/tree-sitter.cjs +0 -2988
  258. package/packages/permission-system/node_modules/web-tree-sitter/lib/tree-sitter.wasm +0 -0
  259. package/packages/permission-system/node_modules/web-tree-sitter/lib/tree-sitter.wasm.map +0 -1
@@ -1,159 +1,159 @@
1
- import * as fs from "node:fs";
2
- import type { AsyncJobTracker, SubagentEventBus } from "./async/job-tracker.ts";
3
- import { summarizeAsyncRun } from "./async/spawn-async.ts";
4
- import {
5
- getAsyncRootFromSessionFile,
6
- getRunDir,
7
- isTerminalState,
8
- } from "./async/storage.ts";
9
- import { SUBAGENT_ASYNC_COMPLETE } from "./rpc/channels.ts";
10
- import type { AsyncRunSummary, WaitInput, WaitResult } from "./types.ts";
11
-
12
- const DEFAULT_TIMEOUT_MS = 30 * 60 * 1000;
13
- const DEFAULT_POLL_INTERVAL_MS = 500;
14
-
15
- export interface WaitDeps {
16
- tracker: AsyncJobTracker;
17
- sessionFile?: string | null;
18
- events?: SubagentEventBus;
19
- now?: () => number;
20
- sleep?: (ms: number) => Promise<void>;
21
- }
22
-
23
- function listRunDirs(asyncRoot: string): string[] {
24
- if (!fs.existsSync(asyncRoot)) return [];
25
- try {
26
- return fs
27
- .readdirSync(asyncRoot, { withFileTypes: true })
28
- .filter((entry) => entry.isDirectory())
29
- .map((entry) => getRunDir(asyncRoot, entry.name));
30
- } catch {
31
- return [];
32
- }
33
- }
34
-
35
- function listActiveRuns(
36
- tracker: AsyncJobTracker,
37
- sessionFile: string | null | undefined,
38
- ): AsyncRunSummary[] {
39
- const asyncRoot = getAsyncRootFromSessionFile(sessionFile ?? null);
40
- const summaries: AsyncRunSummary[] = [];
41
- const seen = new Set<string>();
42
-
43
- for (const status of tracker.list()) {
44
- if (isTerminalState(status.state)) continue;
45
- seen.add(status.id);
46
- summaries.push({
47
- id: status.id,
48
- state: status.state,
49
- agents: status.agents,
50
- exitCode: status.exitCode,
51
- });
52
- }
53
-
54
- for (const runDir of listRunDirs(asyncRoot)) {
55
- const summary = summarizeAsyncRun(runDir);
56
- if (!summary || isTerminalState(summary.state) || seen.has(summary.id)) continue;
57
- summaries.push(summary);
58
- }
59
-
60
- return summaries;
61
- }
62
-
63
- function collectCompleted(
64
- tracker: AsyncJobTracker,
65
- sessionFile: string | null | undefined,
66
- idPrefix?: string,
67
- ): AsyncRunSummary[] {
68
- const asyncRoot = getAsyncRootFromSessionFile(sessionFile ?? null);
69
- const completed: AsyncRunSummary[] = [];
70
- const seen = new Set<string>();
71
-
72
- for (const status of tracker.list()) {
73
- if (!isTerminalState(status.state)) continue;
74
- if (idPrefix && !status.id.startsWith(idPrefix)) continue;
75
- seen.add(status.id);
76
- const runDir = getRunDir(asyncRoot, status.id);
77
- const summary = summarizeAsyncRun(runDir);
78
- completed.push(
79
- summary ?? {
80
- id: status.id,
81
- state: status.state,
82
- agents: status.agents,
83
- exitCode: status.exitCode,
84
- },
85
- );
86
- }
87
-
88
- return completed;
89
- }
90
-
91
- export async function waitForSubagents(
92
- input: WaitInput,
93
- deps: WaitDeps,
94
- ): Promise<WaitResult> {
95
- const timeoutMs = input.timeoutMs ?? DEFAULT_TIMEOUT_MS;
96
- const now = deps.now ?? (() => Date.now());
97
- const sleep =
98
- deps.sleep ?? ((ms: number) => new Promise((resolve) => setTimeout(resolve, ms)));
99
- const deadline = now() + timeoutMs;
100
- const waitAll = input.all === true;
101
- const idPrefix = input.id;
102
-
103
- let wake = false;
104
- const unsub = deps.events?.on(SUBAGENT_ASYNC_COMPLETE, () => {
105
- wake = true;
106
- });
107
-
108
- try {
109
- while (now() < deadline) {
110
- const active = listActiveRuns(deps.tracker, deps.sessionFile).filter((run) =>
111
- idPrefix ? run.id.startsWith(idPrefix) : true,
112
- );
113
-
114
- if (active.length === 0) {
115
- return {
116
- completed: collectCompleted(deps.tracker, deps.sessionFile, idPrefix),
117
- timedOut: false,
118
- activeRemaining: 0,
119
- };
120
- }
121
-
122
- if (!waitAll) {
123
- const completed = collectCompleted(deps.tracker, deps.sessionFile, idPrefix);
124
- if (completed.length > 0) {
125
- return {
126
- completed,
127
- timedOut: false,
128
- activeRemaining: active.length,
129
- };
130
- }
131
- } else {
132
- const stillActive = active.filter((run) => !isTerminalState(run.state));
133
- if (stillActive.length === 0) {
134
- return {
135
- completed: collectCompleted(deps.tracker, deps.sessionFile, idPrefix),
136
- timedOut: false,
137
- activeRemaining: 0,
138
- };
139
- }
140
- }
141
-
142
- wake = false;
143
- await sleep(DEFAULT_POLL_INTERVAL_MS);
144
- if (!wake) continue;
145
- }
146
-
147
- const activeRemaining = listActiveRuns(deps.tracker, deps.sessionFile).filter((run) =>
148
- idPrefix ? run.id.startsWith(idPrefix) : true,
149
- ).length;
150
-
151
- return {
152
- completed: collectCompleted(deps.tracker, deps.sessionFile, idPrefix),
153
- timedOut: true,
154
- activeRemaining,
155
- };
156
- } finally {
157
- unsub?.();
158
- }
159
- }
1
+ import * as fs from "node:fs";
2
+ import type { AsyncJobTracker, SubagentEventBus } from "./async/job-tracker.ts";
3
+ import { summarizeAsyncRun } from "./async/spawn-async.ts";
4
+ import {
5
+ getAsyncRootFromSessionFile,
6
+ getRunDir,
7
+ isTerminalState,
8
+ } from "./async/storage.ts";
9
+ import { SUBAGENT_ASYNC_COMPLETE } from "./rpc/channels.ts";
10
+ import type { AsyncRunSummary, WaitInput, WaitResult } from "./types.ts";
11
+
12
+ const DEFAULT_TIMEOUT_MS = 30 * 60 * 1000;
13
+ const DEFAULT_POLL_INTERVAL_MS = 500;
14
+
15
+ export interface WaitDeps {
16
+ tracker: AsyncJobTracker;
17
+ sessionFile?: string | null;
18
+ events?: SubagentEventBus;
19
+ now?: () => number;
20
+ sleep?: (ms: number) => Promise<void>;
21
+ }
22
+
23
+ function listRunDirs(asyncRoot: string): string[] {
24
+ if (!fs.existsSync(asyncRoot)) return [];
25
+ try {
26
+ return fs
27
+ .readdirSync(asyncRoot, { withFileTypes: true })
28
+ .filter((entry) => entry.isDirectory())
29
+ .map((entry) => getRunDir(asyncRoot, entry.name));
30
+ } catch {
31
+ return [];
32
+ }
33
+ }
34
+
35
+ function listActiveRuns(
36
+ tracker: AsyncJobTracker,
37
+ sessionFile: string | null | undefined,
38
+ ): AsyncRunSummary[] {
39
+ const asyncRoot = getAsyncRootFromSessionFile(sessionFile ?? null);
40
+ const summaries: AsyncRunSummary[] = [];
41
+ const seen = new Set<string>();
42
+
43
+ for (const status of tracker.list()) {
44
+ if (isTerminalState(status.state)) continue;
45
+ seen.add(status.id);
46
+ summaries.push({
47
+ id: status.id,
48
+ state: status.state,
49
+ agents: status.agents,
50
+ exitCode: status.exitCode,
51
+ });
52
+ }
53
+
54
+ for (const runDir of listRunDirs(asyncRoot)) {
55
+ const summary = summarizeAsyncRun(runDir);
56
+ if (!summary || isTerminalState(summary.state) || seen.has(summary.id)) continue;
57
+ summaries.push(summary);
58
+ }
59
+
60
+ return summaries;
61
+ }
62
+
63
+ function collectCompleted(
64
+ tracker: AsyncJobTracker,
65
+ sessionFile: string | null | undefined,
66
+ idPrefix?: string,
67
+ ): AsyncRunSummary[] {
68
+ const asyncRoot = getAsyncRootFromSessionFile(sessionFile ?? null);
69
+ const completed: AsyncRunSummary[] = [];
70
+ const seen = new Set<string>();
71
+
72
+ for (const status of tracker.list()) {
73
+ if (!isTerminalState(status.state)) continue;
74
+ if (idPrefix && !status.id.startsWith(idPrefix)) continue;
75
+ seen.add(status.id);
76
+ const runDir = getRunDir(asyncRoot, status.id);
77
+ const summary = summarizeAsyncRun(runDir);
78
+ completed.push(
79
+ summary ?? {
80
+ id: status.id,
81
+ state: status.state,
82
+ agents: status.agents,
83
+ exitCode: status.exitCode,
84
+ },
85
+ );
86
+ }
87
+
88
+ return completed;
89
+ }
90
+
91
+ export async function waitForSubagents(
92
+ input: WaitInput,
93
+ deps: WaitDeps,
94
+ ): Promise<WaitResult> {
95
+ const timeoutMs = input.timeoutMs ?? DEFAULT_TIMEOUT_MS;
96
+ const now = deps.now ?? (() => Date.now());
97
+ const sleep =
98
+ deps.sleep ?? ((ms: number) => new Promise((resolve) => setTimeout(resolve, ms)));
99
+ const deadline = now() + timeoutMs;
100
+ const waitAll = input.all === true;
101
+ const idPrefix = input.id;
102
+
103
+ let wake = false;
104
+ const unsub = deps.events?.on(SUBAGENT_ASYNC_COMPLETE, () => {
105
+ wake = true;
106
+ });
107
+
108
+ try {
109
+ while (now() < deadline) {
110
+ const active = listActiveRuns(deps.tracker, deps.sessionFile).filter((run) =>
111
+ idPrefix ? run.id.startsWith(idPrefix) : true,
112
+ );
113
+
114
+ if (active.length === 0) {
115
+ return {
116
+ completed: collectCompleted(deps.tracker, deps.sessionFile, idPrefix),
117
+ timedOut: false,
118
+ activeRemaining: 0,
119
+ };
120
+ }
121
+
122
+ if (!waitAll) {
123
+ const completed = collectCompleted(deps.tracker, deps.sessionFile, idPrefix);
124
+ if (completed.length > 0) {
125
+ return {
126
+ completed,
127
+ timedOut: false,
128
+ activeRemaining: active.length,
129
+ };
130
+ }
131
+ } else {
132
+ const stillActive = active.filter((run) => !isTerminalState(run.state));
133
+ if (stillActive.length === 0) {
134
+ return {
135
+ completed: collectCompleted(deps.tracker, deps.sessionFile, idPrefix),
136
+ timedOut: false,
137
+ activeRemaining: 0,
138
+ };
139
+ }
140
+ }
141
+
142
+ wake = false;
143
+ await sleep(DEFAULT_POLL_INTERVAL_MS);
144
+ if (!wake) continue;
145
+ }
146
+
147
+ const activeRemaining = listActiveRuns(deps.tracker, deps.sessionFile).filter((run) =>
148
+ idPrefix ? run.id.startsWith(idPrefix) : true,
149
+ ).length;
150
+
151
+ return {
152
+ completed: collectCompleted(deps.tracker, deps.sessionFile, idPrefix),
153
+ timedOut: true,
154
+ activeRemaining,
155
+ };
156
+ } finally {
157
+ unsub?.();
158
+ }
159
+ }
@@ -1,13 +1,13 @@
1
- {
2
- "extends": "../../tsconfig.base.json",
3
- "compilerOptions": {
4
- "allowImportingTsExtensions": true
5
- },
6
- "include": [
7
- "extensions/**/*.ts",
8
- "src/**/*.ts",
9
- "tests/**/*.ts",
10
- "vitest.config.ts"
11
- ],
12
- "exclude": ["node_modules"]
13
- }
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "allowImportingTsExtensions": true
5
+ },
6
+ "include": [
7
+ "extensions/**/*.ts",
8
+ "src/**/*.ts",
9
+ "tests/**/*.ts",
10
+ "vitest.config.ts"
11
+ ],
12
+ "exclude": ["node_modules"]
13
+ }
@@ -1,8 +1,8 @@
1
- import { defineConfig } from "vitest/config";
2
-
3
- export default defineConfig({
4
- test: {
5
- environment: "node",
6
- include: ["tests/**/*.test.ts"],
7
- },
8
- });
1
+ import { defineConfig } from "vitest/config";
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ environment: "node",
6
+ include: ["tests/**/*.test.ts"],
7
+ },
8
+ });
@@ -0,0 +1,163 @@
1
+ /**
2
+ * @vitos-pizza/todoist — In-memory task management with TUI widget.
3
+ */
4
+
5
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
6
+ import { TODOIST_STATUS_KEY, TODOIST_WIDGET_KEY } from "../src/events.ts";
7
+ import { TodoStore } from "../src/state.ts";
8
+ import { registerTodoistTools } from "../src/tools.ts";
9
+ import { buildTodoWidgetView, type WidgetTheme } from "../src/widget.ts";
10
+
11
+ function renderTodoWidget(
12
+ setWidget: (key: string, content: string[] | undefined) => void,
13
+ setStatus: (key: string, content: string | undefined) => void,
14
+ theme: WidgetTheme,
15
+ store: TodoStore,
16
+ ): void {
17
+ const view = buildTodoWidgetView(store.getSnapshot(), theme);
18
+ setWidget(TODOIST_WIDGET_KEY, view.lines);
19
+ setStatus(TODOIST_STATUS_KEY, view.status);
20
+ }
21
+
22
+ export default function (pi: ExtensionAPI): void {
23
+ const store = new TodoStore();
24
+
25
+ // Track current mode for plan-mode injection
26
+ let currentMode = "agent";
27
+
28
+ // Listen for agent mode changes
29
+ pi.events.on("agent_mode_changed", (payload: unknown) => {
30
+ const p = payload as { mode?: string };
31
+ if (p?.mode) currentMode = p.mode;
32
+ });
33
+
34
+ // Register 4 LLM tools
35
+ registerTodoistTools(pi, store);
36
+
37
+ // /todo command — show current todo list
38
+ pi.registerCommand("todo", {
39
+ description: "Show current todo list",
40
+ handler: async (_args, ctx) => {
41
+ const todos = store.list({ status: "all" });
42
+ if (todos.length === 0) {
43
+ ctx.ui.notify("📭 No tasks. Use `todo_add` to create one.", "info");
44
+ return;
45
+ }
46
+
47
+ const pending = todos.filter((t) => t.status === "pending");
48
+ const inProgress = todos.filter((t) => t.status === "in_progress");
49
+ const done = todos.filter((t) => t.status === "done");
50
+
51
+ const lines: string[] = [];
52
+ if (inProgress.length > 0) {
53
+ lines.push("In Progress:");
54
+ lines.push(...inProgress.map((t) => ` ✳ #${t.id} ${t.text}`));
55
+ }
56
+ if (pending.length > 0) {
57
+ lines.push("Pending:");
58
+ lines.push(...pending.map((t) => ` ☐ #${t.id} ${t.text}`));
59
+ }
60
+ if (done.length > 0) {
61
+ lines.push("Done:");
62
+ lines.push(...done.map((t) => ` ☑ #${t.id} ${t.text}`));
63
+ }
64
+ lines.push(`\n📊 ${todos.length} total (${done.length} done)`);
65
+
66
+ ctx.ui.notify(lines.join("\n"), "info");
67
+ },
68
+ });
69
+
70
+ // /todoist command — show connection info (stub, MCP removed)
71
+ pi.registerCommand("todoist", {
72
+ description: "Todoist — local in-memory task list",
73
+ handler: async (_args, ctx) => {
74
+ const todos = store.list({ status: "all" });
75
+ const lines = [
76
+ "📋 Local in-memory task list (no external sync)",
77
+ `Tasks: ${todos.length} (${todos.filter((t) => t.status === "done").length} done)`,
78
+ ];
79
+ ctx.ui.notify(lines.join("\n"), "info");
80
+ },
81
+ });
82
+
83
+ // Session lifecycle
84
+ pi.on("session_start", async (_event, ctx) => {
85
+ // Try to restore state from conversation history
86
+ try {
87
+ const entries = ctx.sessionManager?.getEntries?.() ?? [];
88
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
89
+ const toolEntries: any[] = [];
90
+ for (const entry of entries) {
91
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
92
+ const e = entry as any;
93
+ if (e.type === "tool_call" && e.toolName) {
94
+ toolEntries.push({
95
+ toolName: e.toolName,
96
+ input: e.input ?? {},
97
+ result: e.result,
98
+ });
99
+ }
100
+ }
101
+ store.restoreFromHistory(toolEntries);
102
+ } catch {
103
+ // Silently fall back to empty state
104
+ }
105
+
106
+ // Render initial widget if there are any tasks (including all-done)
107
+ const snap = store.getSnapshot();
108
+ if (snap.todos.length > 0 && ctx.hasUI) {
109
+ const theme = ctx.ui.theme as unknown as WidgetTheme;
110
+ renderTodoWidget(
111
+ (k, v) => ctx.ui.setWidget(k, v),
112
+ (k, v) => ctx.ui.setStatus(k, v),
113
+ theme,
114
+ store,
115
+ );
116
+ }
117
+
118
+ // Set up store change listener
119
+ store.setOnChange(() => {
120
+ if (ctx.hasUI) {
121
+ const theme = ctx.ui.theme as unknown as WidgetTheme;
122
+ renderTodoWidget(
123
+ (k, v) => ctx.ui.setWidget(k, v),
124
+ (k, v) => ctx.ui.setStatus(k, v),
125
+ theme,
126
+ store,
127
+ );
128
+ }
129
+ });
130
+ });
131
+
132
+ // Plan mode injection — add pending tasks to system prompt
133
+ pi.on("before_agent_start", async (event, _ctx) => {
134
+ if (currentMode !== "plan") return {};
135
+
136
+ const activeTodos = store.list({ status: "active" });
137
+ if (activeTodos.length === 0) return {};
138
+
139
+ const lines = activeTodos.map(
140
+ (t) =>
141
+ ` • #${t.id}: ${t.text} (p${t.priority})${t.project ? ` [${t.project}]` : ""}`,
142
+ );
143
+
144
+ const todoBlock = [
145
+ "",
146
+ "## Current Tasks",
147
+ ...lines,
148
+ "",
149
+ `📊 ${store.list({ status: "all" }).length} total (${store.list({ status: "done" }).length} done)`,
150
+ "",
151
+ ].join("\n");
152
+
153
+ const eventTyped = event as { systemPrompt?: string };
154
+ return {
155
+ systemPrompt: `${eventTyped.systemPrompt ?? ""}\n${todoBlock}`,
156
+ };
157
+ });
158
+
159
+ pi.on("session_shutdown", async (_event, ctx) => {
160
+ ctx.ui.setWidget(TODOIST_WIDGET_KEY, undefined);
161
+ ctx.ui.setStatus(TODOIST_STATUS_KEY, undefined);
162
+ });
163
+ }
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@vitos-pizza/todoist",
3
+ "version": "0.2.0",
4
+ "description": "In-memory task management with TUI widget and optional Todoist MCP sync for Vito's Pizzeria Pi distribution",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "private": true,
8
+ "pi": {
9
+ "requires": [
10
+ "@vitos-pizza/agent-mode"
11
+ ]
12
+ },
13
+ "scripts": {
14
+ "test": "vitest run",
15
+ "test:watch": "vitest",
16
+ "typecheck": "tsc --noEmit",
17
+ "lint": "biome check .",
18
+ "lint:fix": "biome check --write ."
19
+ },
20
+ "peerDependencies": {
21
+ "@earendil-works/pi-agent-core": "*",
22
+ "@earendil-works/pi-ai": "*",
23
+ "@earendil-works/pi-coding-agent": "*",
24
+ "@earendil-works/pi-tui": "*",
25
+ "typebox": "*"
26
+ },
27
+ "devDependencies": {
28
+ "@earendil-works/pi-ai": "*",
29
+ "@earendil-works/pi-coding-agent": "*",
30
+ "@earendil-works/pi-tui": "*",
31
+ "typebox": "*",
32
+ "vitest": "^3.2.4"
33
+ }
34
+ }
@@ -0,0 +1,7 @@
1
+ export const TODOIST_COUNT_CHANGED = "todoist:count-changed";
2
+ export const TODOIST_TASK_ADDED = "todoist:task-added";
3
+ export const TODOIST_TASK_UPDATED = "todoist:task-updated";
4
+ export const TODOIST_TASK_REMOVED = "todoist:task-removed";
5
+
6
+ export const TODOIST_WIDGET_KEY = "vitos-todoist";
7
+ export const TODOIST_STATUS_KEY = "vitos-todoist";
@@ -0,0 +1,8 @@
1
+ import { Text } from "@earendil-works/pi-tui";
2
+
3
+ /** Hide tool rows in the transcript; state is shown via the widget instead. */
4
+ export const silentRender = {
5
+ renderShell: "self" as const,
6
+ renderCall: () => new Text("", 0, 0),
7
+ renderResult: () => new Text("", 0, 0),
8
+ };