pi-ui-extend 1.0.41 → 1.0.45

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 (236) hide show
  1. package/README.md +50 -13
  2. package/dist/app/app.d.ts +13 -0
  3. package/dist/app/app.js +158 -8
  4. package/dist/app/cli/install.js +2 -2
  5. package/dist/app/commands/command-controller.d.ts +4 -0
  6. package/dist/app/commands/command-controller.js +11 -0
  7. package/dist/app/commands/command-git-actions.d.ts +25 -0
  8. package/dist/app/commands/command-git-actions.js +381 -0
  9. package/dist/app/commands/command-host.d.ts +4 -0
  10. package/dist/app/commands/command-host.js +5 -2
  11. package/dist/app/commands/command-model-actions.d.ts +1 -0
  12. package/dist/app/commands/command-model-actions.js +57 -35
  13. package/dist/app/commands/command-navigation-actions.d.ts +1 -0
  14. package/dist/app/commands/command-navigation-actions.js +16 -3
  15. package/dist/app/commands/command-registry.d.ts +2 -0
  16. package/dist/app/commands/command-registry.js +15 -1
  17. package/dist/app/commands/command-session-actions.js +9 -3
  18. package/dist/app/commands/reload-context-inventory.d.ts +11 -0
  19. package/dist/app/commands/reload-context-inventory.js +70 -0
  20. package/dist/app/extensions/extension-actions-controller.d.ts +1 -0
  21. package/dist/app/extensions/extension-actions-controller.js +6 -0
  22. package/dist/app/extensions/subagent-catalog-state.d.ts +9 -0
  23. package/dist/app/extensions/subagent-catalog-state.js +23 -0
  24. package/dist/app/input/autocomplete-controller.js +61 -34
  25. package/dist/app/input/input-action-controller.d.ts +2 -0
  26. package/dist/app/input/input-action-controller.js +8 -1
  27. package/dist/app/input/input-controller.d.ts +2 -1
  28. package/dist/app/input/input-controller.js +7 -2
  29. package/dist/app/input/prompt-enhancer-controller.js +53 -35
  30. package/dist/app/input/voice-controller.d.ts +41 -45
  31. package/dist/app/input/voice-controller.js +351 -419
  32. package/dist/app/model/model-usage-controller.js +17 -7
  33. package/dist/app/model/model-usage-status.d.ts +4 -1
  34. package/dist/app/model/model-usage-status.js +270 -46
  35. package/dist/app/popup/menu-items-controller.d.ts +13 -3
  36. package/dist/app/popup/menu-items-controller.js +37 -21
  37. package/dist/app/popup/popup-action-controller.d.ts +12 -2
  38. package/dist/app/popup/popup-action-controller.js +77 -24
  39. package/dist/app/popup/popup-menu-controller.d.ts +36 -14
  40. package/dist/app/popup/popup-menu-controller.js +239 -69
  41. package/dist/app/rendering/dcp-stats.d.ts +9 -4
  42. package/dist/app/rendering/dcp-stats.js +40 -425
  43. package/dist/app/rendering/editor-panels.js +10 -4
  44. package/dist/app/rendering/popup-menu-renderer.d.ts +3 -5
  45. package/dist/app/rendering/popup-menu-renderer.js +42 -37
  46. package/dist/app/rendering/render-controller.js +23 -2
  47. package/dist/app/rendering/status-line-renderer.d.ts +5 -0
  48. package/dist/app/rendering/status-line-renderer.js +41 -5
  49. package/dist/app/rendering/tab-line-renderer.js +26 -20
  50. package/dist/app/runtime.d.ts +12 -1
  51. package/dist/app/runtime.js +120 -12
  52. package/dist/app/screen/mouse-controller.d.ts +2 -0
  53. package/dist/app/screen/mouse-controller.js +17 -7
  54. package/dist/app/screen/status-controller.d.ts +4 -0
  55. package/dist/app/screen/status-controller.js +5 -0
  56. package/dist/app/session/lazy-session-manager.js +34 -0
  57. package/dist/app/session/session-event-controller.d.ts +1 -0
  58. package/dist/app/session/session-event-controller.js +10 -1
  59. package/dist/app/session/session-history.d.ts +1 -0
  60. package/dist/app/session/session-history.js +12 -1
  61. package/dist/app/session/session-lifecycle-controller.d.ts +7 -1
  62. package/dist/app/session/session-lifecycle-controller.js +16 -1
  63. package/dist/app/session/tabs-controller.d.ts +26 -1
  64. package/dist/app/session/tabs-controller.js +454 -147
  65. package/dist/app/subagents/subagents-files.js +60 -1
  66. package/dist/app/subagents/subagents-model.d.ts +1 -0
  67. package/dist/app/subagents/subagents-model.js +18 -2
  68. package/dist/app/subagents/subagents-widget-controller.d.ts +1 -0
  69. package/dist/app/subagents/subagents-widget-controller.js +6 -0
  70. package/dist/app/types.d.ts +16 -1
  71. package/dist/app/workspace/workspace-actions-controller.js +10 -2
  72. package/dist/app/workspace/workspace-undo.d.ts +1 -0
  73. package/dist/app/workspace/workspace-undo.js +1 -0
  74. package/dist/bundled-extensions/question/index.js +9 -1
  75. package/dist/bundled-extensions/question/remote.d.ts +4 -0
  76. package/dist/bundled-extensions/question/remote.js +33 -0
  77. package/dist/bundled-extensions/telegram-connector/bot.d.ts +43 -0
  78. package/dist/bundled-extensions/telegram-connector/bot.js +166 -0
  79. package/dist/bundled-extensions/telegram-connector/config.d.ts +8 -0
  80. package/dist/bundled-extensions/telegram-connector/config.js +87 -0
  81. package/dist/bundled-extensions/telegram-connector/coordinator.d.ts +66 -0
  82. package/dist/bundled-extensions/telegram-connector/coordinator.js +413 -0
  83. package/dist/bundled-extensions/telegram-connector/index.d.ts +3 -0
  84. package/dist/bundled-extensions/telegram-connector/index.js +195 -0
  85. package/dist/bundled-extensions/terminal-bell/index.d.ts +0 -8
  86. package/dist/bundled-extensions/terminal-bell/index.js +0 -76
  87. package/dist/bundled-extensions/workspace-undo/index.d.ts +26 -0
  88. package/dist/bundled-extensions/workspace-undo/index.js +191 -0
  89. package/dist/config.d.ts +21 -2
  90. package/dist/config.js +159 -32
  91. package/dist/default-pix-config.js +25 -5
  92. package/dist/schemas/index.d.ts +1 -0
  93. package/dist/schemas/index.js +1 -0
  94. package/dist/schemas/pi-tools-suite-schema.d.ts +88 -62
  95. package/dist/schemas/pi-tools-suite-schema.js +54 -83
  96. package/dist/schemas/pix-schema.d.ts +19 -2
  97. package/dist/schemas/pix-schema.js +47 -5
  98. package/dist/schemas/tasks-schema.d.ts +18 -0
  99. package/dist/schemas/tasks-schema.js +41 -0
  100. package/docs/concurrency.md +9 -1
  101. package/docs/desktop-mvp.md +22 -6
  102. package/docs/desktop-task-manager.md +148 -84
  103. package/docs/release.md +30 -6
  104. package/external/pi-tools-suite/README.md +197 -84
  105. package/external/pi-tools-suite/docs/evals.md +1 -1
  106. package/external/pi-tools-suite/docs/session-recovery.md +47 -14
  107. package/external/pi-tools-suite/docs/subagent-model-pools.md +48 -30
  108. package/external/pi-tools-suite/docs/ui-qa-subagent.md +440 -0
  109. package/external/pi-tools-suite/package.json +6 -1
  110. package/external/pi-tools-suite/src/antigravity-auth/auth-store.ts +2 -1
  111. package/external/pi-tools-suite/src/antigravity-auth/constants.ts +9 -3
  112. package/external/pi-tools-suite/src/antigravity-auth/headers.ts +41 -3
  113. package/external/pi-tools-suite/src/antigravity-auth/models.ts +94 -21
  114. package/external/pi-tools-suite/src/antigravity-auth/oauth.ts +5 -17
  115. package/external/pi-tools-suite/src/antigravity-auth/payload.ts +72 -7
  116. package/external/pi-tools-suite/src/antigravity-auth/stream.ts +13 -1
  117. package/external/pi-tools-suite/src/async-subagents/agents/frontier-review.md +23 -0
  118. package/external/pi-tools-suite/src/async-subagents/agents/implement.md +1 -1
  119. package/external/pi-tools-suite/src/async-subagents/agents/presets.jsonc +16 -0
  120. package/external/pi-tools-suite/src/async-subagents/agents/research.md +5 -3
  121. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/backends/browser.mjs +346 -0
  122. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/backends/desktop.mjs +1038 -0
  123. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/backends/tui.mjs +759 -0
  124. package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/scripts/browser-qa-runner.mjs +32 -23
  125. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/chrome-devtools/chrome-devtools-provider.mjs +895 -0
  126. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/linux/linux-atspi.py +494 -0
  127. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/macos/macos-accessibility.swift +1085 -0
  128. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/native-terminal/bridge-client.mjs +50 -0
  129. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/native-terminal/native-terminal-host.mjs +501 -0
  130. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/windows/windows-uia.ps1 +454 -0
  131. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser-auth.md +114 -0
  132. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser-chrome-devtools.md +91 -0
  133. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser-playwright.md +145 -0
  134. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser.md +82 -0
  135. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop-linux.md +26 -0
  136. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop-macos.md +29 -0
  137. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop-windows.md +23 -0
  138. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop.md +54 -0
  139. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/tui-native-terminal.md +59 -0
  140. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/tui-pty.md +37 -0
  141. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/tui.md +60 -0
  142. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/scripts/ui-qa-runner.mjs +535 -0
  143. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa.md +75 -0
  144. package/external/pi-tools-suite/src/async-subagents/commands.ts +15 -71
  145. package/external/pi-tools-suite/src/async-subagents/core/activity.ts +33 -0
  146. package/external/pi-tools-suite/src/async-subagents/core/agent-catalog.ts +5 -4
  147. package/external/pi-tools-suite/src/async-subagents/core/agent-strategy.ts +1 -1
  148. package/external/pi-tools-suite/src/async-subagents/core/agents-dir.ts +8 -5
  149. package/external/pi-tools-suite/src/async-subagents/core/browser-qa.ts +16 -2
  150. package/external/pi-tools-suite/src/async-subagents/core/config.ts +160 -327
  151. package/external/pi-tools-suite/src/async-subagents/core/model-selection.ts +2 -1
  152. package/external/pi-tools-suite/src/async-subagents/core/registry.ts +158 -33
  153. package/external/pi-tools-suite/src/async-subagents/core/routing.ts +21 -5
  154. package/external/pi-tools-suite/src/async-subagents/core/spawn.ts +40 -26
  155. package/external/pi-tools-suite/src/async-subagents/core/state.ts +40 -0
  156. package/external/pi-tools-suite/src/async-subagents/core/types.ts +7 -0
  157. package/external/pi-tools-suite/src/async-subagents/core/ultrawork-auto.ts +51 -45
  158. package/external/pi-tools-suite/src/async-subagents/index.ts +69 -5
  159. package/external/pi-tools-suite/src/async-subagents/lib.ts +16 -9
  160. package/external/pi-tools-suite/src/async-subagents/tools/spawn.ts +8 -5
  161. package/external/pi-tools-suite/src/async-subagents/tools/subagents.ts +5 -4
  162. package/external/pi-tools-suite/src/async-subagents/types.ts +1 -0
  163. package/external/pi-tools-suite/src/coding-discipline/index.ts +90 -59
  164. package/external/pi-tools-suite/src/config.ts +55 -1
  165. package/external/pi-tools-suite/src/context-gateway/accounting-log.ts +282 -0
  166. package/external/pi-tools-suite/src/context-gateway/config.ts +77 -3
  167. package/external/pi-tools-suite/src/context-gateway/efficiency.ts +414 -0
  168. package/external/pi-tools-suite/src/context-gateway/enforcement.ts +222 -0
  169. package/external/pi-tools-suite/src/context-gateway/index.ts +206 -19
  170. package/external/pi-tools-suite/src/context-gateway/storeless-capabilities.ts +7 -7
  171. package/external/pi-tools-suite/src/context-gateway/telemetry.ts +101 -28
  172. package/external/pi-tools-suite/src/context-gateway/types.ts +16 -3
  173. package/external/pi-tools-suite/src/context-inventory.ts +99 -0
  174. package/external/pi-tools-suite/src/dcp/auto-compress-budget.ts +39 -4
  175. package/external/pi-tools-suite/src/dcp/auto-compress.ts +128 -49
  176. package/external/pi-tools-suite/src/dcp/commands.ts +32 -156
  177. package/external/pi-tools-suite/src/dcp/compress-tool.ts +177 -46
  178. package/external/pi-tools-suite/src/dcp/compression-blocks.ts +6 -58
  179. package/external/pi-tools-suite/src/dcp/compression-preview.ts +9 -0
  180. package/external/pi-tools-suite/src/dcp/compression-progress.ts +22 -0
  181. package/external/pi-tools-suite/src/dcp/config.ts +114 -17
  182. package/external/pi-tools-suite/src/dcp/conversation-index.ts +36 -9
  183. package/external/pi-tools-suite/src/dcp/diagnostics.ts +41 -0
  184. package/external/pi-tools-suite/src/dcp/fresh-tool-results.ts +38 -0
  185. package/external/pi-tools-suite/src/dcp/index.ts +338 -66
  186. package/external/pi-tools-suite/src/dcp/journal.ts +126 -4
  187. package/external/pi-tools-suite/src/dcp/progress-controller.ts +2 -1
  188. package/external/pi-tools-suite/src/dcp/prompts.ts +83 -192
  189. package/external/pi-tools-suite/src/dcp/protected-continuity.ts +175 -0
  190. package/external/pi-tools-suite/src/dcp/pruner-candidates.ts +93 -3
  191. package/external/pi-tools-suite/src/dcp/pruner-message-ids.ts +6 -10
  192. package/external/pi-tools-suite/src/dcp/pruner-nudge.ts +36 -33
  193. package/external/pi-tools-suite/src/dcp/pruner-tools.ts +6 -3
  194. package/external/pi-tools-suite/src/dcp/pruner.ts +1 -0
  195. package/external/pi-tools-suite/src/dcp/routine-pressure.ts +86 -0
  196. package/external/pi-tools-suite/src/dcp/state.ts +9 -0
  197. package/external/pi-tools-suite/src/dcp/statistics.d.ts +8 -0
  198. package/external/pi-tools-suite/src/dcp/statistics.js +156 -0
  199. package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +78 -22
  200. package/external/pi-tools-suite/src/index.ts +7 -1
  201. package/external/pi-tools-suite/src/lib/project.ts +36 -1
  202. package/external/pi-tools-suite/src/model-tools/index.ts +10 -7
  203. package/external/pi-tools-suite/src/repo-discovery/index.ts +304 -4
  204. package/external/pi-tools-suite/src/resource-registry/index.ts +2551 -0
  205. package/external/pi-tools-suite/src/session-recovery/index.ts +17 -0
  206. package/external/pi-tools-suite/src/shell-command-policy.ts +219 -0
  207. package/external/pi-tools-suite/src/todo/index.ts +21 -0
  208. package/external/pi-tools-suite/src/todo/todo.ts +19 -1
  209. package/external/pi-tools-suite/src/tool-descriptions.ts +47 -19
  210. package/package.json +10 -9
  211. package/schemas/pi-tools-suite.json +466 -287
  212. package/schemas/pix.json +129 -11
  213. package/schemas/tasks.json +131 -0
  214. package/skills/simplify/SKILL.md +33 -5
  215. package/docs/desktop-markdown-media.md +0 -77
  216. package/external/pi-tools-suite/docs/browser-qa-subagent.md +0 -177
  217. package/external/pi-tools-suite/docs/dcp-emergency-current-turn.md +0 -102
  218. package/external/pi-tools-suite/src/async-subagents/agents/browser-qa.md +0 -598
  219. package/external/pi-tools-suite/src/async-subagents/async-subagents.sample.jsonc +0 -54
  220. package/external/pi-tools-suite/src/skill-installer/index.ts +0 -333
  221. package/skills/playwright-cli/SKILL.md +0 -420
  222. package/skills/playwright-cli/references/element-attributes.md +0 -23
  223. package/skills/playwright-cli/references/playwright-tests.md +0 -50
  224. package/skills/playwright-cli/references/request-mocking.md +0 -87
  225. package/skills/playwright-cli/references/running-code.md +0 -241
  226. package/skills/playwright-cli/references/session-management.md +0 -273
  227. package/skills/playwright-cli/references/spec-driven-testing.md +0 -311
  228. package/skills/playwright-cli/references/storage-state.md +0 -290
  229. package/skills/playwright-cli/references/test-generation.md +0 -142
  230. package/skills/playwright-cli/references/tracing.md +0 -154
  231. package/skills/playwright-cli/references/video-recording.md +0 -147
  232. package/skills/spec-lite/SKILL.md +0 -140
  233. /package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/examples/qa-auth.example.jsonc +0 -0
  234. /package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/examples/qa-flow.example.jsonc +0 -0
  235. /package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/vendor/fflate.LICENSE +0 -0
  236. /package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/vendor/fflate.mjs +0 -0
@@ -201,6 +201,14 @@ function toolResultFrom(entry: EntryLike): UnknownRecord | undefined {
201
201
  return message?.role === "toolResult" ? message : undefined;
202
202
  }
203
203
 
204
+ function recoverableGatewayDetails(message: UnknownRecord): UnknownRecord | undefined {
205
+ const toolName = typeof message.toolName === "string" ? message.toolName.toLowerCase() : "";
206
+ if (toolName !== "web_search" && toolName !== "web_fetch") return undefined;
207
+ if (!isRecord(message.details) || !isRecord(message.details.contextGateway)) return undefined;
208
+ if (message.details.contextGateway.representation !== "web-recoverable-compact") return undefined;
209
+ return message.details;
210
+ }
211
+
204
212
  function summaryFrom(entry: EntryLike): string {
205
213
  if ((entry.type === "compaction" || entry.type === "branch_summary") && typeof entry.summary === "string") {
206
214
  return entry.summary;
@@ -218,6 +226,11 @@ function entryText(entry: EntryLike): string {
218
226
  for (const call of toolCallsFrom(entry)) {
219
227
  chunks.push(call.name, serializeJson(call.arguments));
220
228
  }
229
+ if (message.role === "toolResult") {
230
+ const toolName = typeof message.toolName === "string" ? message.toolName : "unknown";
231
+ const callId = typeof message.toolCallId === "string" ? message.toolCallId : "unknown";
232
+ chunks.push(`tool_result ${toolName}#${callId}`);
233
+ }
221
234
  } else if (entry.type === "custom_message") {
222
235
  const text = textFromContent(entry.content);
223
236
  if (text) chunks.push(text);
@@ -416,6 +429,10 @@ function renderEntryFull(entry: EntryLike): string {
416
429
  const toolName = typeof message.toolName === "string" ? message.toolName : "unknown";
417
430
  const callId = typeof message.toolCallId === "string" ? message.toolCallId : "unknown";
418
431
  lines.push(`tool_result ${toolName}#${callId}${message.isError === true ? " error" : ""}`);
432
+ const recoverableDetails = recoverableGatewayDetails(message);
433
+ if (recoverableDetails) {
434
+ lines.push("recoverable_raw_details:", serializeJson(recoverableDetails));
435
+ }
419
436
  }
420
437
  } else if (entry.type === "compaction") {
421
438
  lines.push(summaryFrom(entry));
@@ -0,0 +1,219 @@
1
+ export type ShellCommandScope = "simple" | "compound" | "unknown";
2
+ export type ShellCommandKind = "inspection" | "test-build" | "mutation" | "unknown";
3
+
4
+ export interface ShellCommandClassification {
5
+ scope: ShellCommandScope;
6
+ kind: ShellCommandKind;
7
+ }
8
+
9
+ const ENV_ASSIGNMENT_RE = /^[A-Za-z_][A-Za-z0-9_]*=.*$/;
10
+ const SAFE_EPHEMERAL_WRITE_RE = /^\/(?:private\/)?tmp\/[^\0\r\n]+$/;
11
+ const COMMAND_SEPARATORS = new Set([";", "&&", "||", "|"]);
12
+ const REDIRECT_OPERATORS = new Set([">", ">>", "<"]);
13
+
14
+ interface ShellScan {
15
+ scope: ShellCommandScope;
16
+ commands: string[][];
17
+ unsafeWrite: boolean;
18
+ unsupported: boolean;
19
+ }
20
+
21
+ function safeEphemeralWriteTarget(target: string): boolean {
22
+ return target === "/dev/null" || SAFE_EPHEMERAL_WRITE_RE.test(target);
23
+ }
24
+
25
+ /**
26
+ * Minimal conservative shell lexer. It understands quoting only well enough to
27
+ * keep metacharacters inside quotes literal and to prove a small allowlisted
28
+ * inspection pipeline safe. Unsupported shell syntax remains unknown.
29
+ */
30
+ function scanShell(command: string): ShellScan {
31
+ const tokens: string[] = [];
32
+ let current = "";
33
+ let quote: "'" | '"' | undefined;
34
+ let unsupported = false;
35
+ const flush = () => {
36
+ if (current.length > 0) tokens.push(current);
37
+ current = "";
38
+ };
39
+
40
+ for (let index = 0; index < command.length; index++) {
41
+ const char = command[index]!;
42
+ const next = command[index + 1];
43
+ if (quote) {
44
+ if (char === quote) {
45
+ quote = undefined;
46
+ continue;
47
+ }
48
+ if (quote === '"' && char === "\\" && next !== undefined) {
49
+ current += next;
50
+ index++;
51
+ continue;
52
+ }
53
+ if (quote === '"' && (char === "`" || (char === "$" && next === "("))) unsupported = true;
54
+ current += char;
55
+ continue;
56
+ }
57
+
58
+ if (char === "'" || char === '"') {
59
+ quote = char;
60
+ continue;
61
+ }
62
+ if (char === "\\" && next !== undefined) {
63
+ current += next;
64
+ index++;
65
+ continue;
66
+ }
67
+ if (char === "`" || (char === "$" && next === "(") || ((char === "<" || char === ">") && next === "(")) {
68
+ unsupported = true;
69
+ }
70
+ if (char === "\r" || char === "\n" || char === "&" && next !== "&" || char === "(" || char === ")") {
71
+ unsupported = true;
72
+ }
73
+ if (/\s/.test(char)) {
74
+ flush();
75
+ continue;
76
+ }
77
+ if (char === ";" || char === "|" || char === ">" || char === "<" || (char === "&" && next === "&")) {
78
+ flush();
79
+ let operator = char;
80
+ if ((char === "&" && next === "&") || (char === "|" && next === "|") || (char === ">" && next === ">") || (char === "<" && next === "<")) {
81
+ operator += next;
82
+ index++;
83
+ }
84
+ if (operator === "<<") unsupported = true;
85
+ tokens.push(operator);
86
+ continue;
87
+ }
88
+ current += char;
89
+ }
90
+ flush();
91
+ if (quote) unsupported = true;
92
+
93
+ const commands: string[][] = [];
94
+ let currentCommand: string[] = [];
95
+ let unsafeWrite = false;
96
+ let compound = false;
97
+ for (let index = 0; index < tokens.length; index++) {
98
+ const token = tokens[index]!;
99
+ if (COMMAND_SEPARATORS.has(token)) {
100
+ compound = true;
101
+ if (currentCommand.length === 0) unsupported = true;
102
+ else commands.push(currentCommand);
103
+ currentCommand = [];
104
+ continue;
105
+ }
106
+ if (REDIRECT_OPERATORS.has(token)) {
107
+ compound = true;
108
+ const target = tokens[++index];
109
+ if (!target || COMMAND_SEPARATORS.has(target) || REDIRECT_OPERATORS.has(target)) {
110
+ unsupported = true;
111
+ continue;
112
+ }
113
+ if ((token === ">" || token === ">>") && !safeEphemeralWriteTarget(target)) unsafeWrite = true;
114
+ continue;
115
+ }
116
+ currentCommand.push(token);
117
+ }
118
+ if (currentCommand.length > 0) commands.push(currentCommand);
119
+ else if (tokens.length > 0 && COMMAND_SEPARATORS.has(tokens.at(-1)!)) unsupported = true;
120
+
121
+ for (const words of commands) {
122
+ while (words.length > 0 && (ENV_ASSIGNMENT_RE.test(words[0]!) || /^\d+$/.test(words[0]!))) words.shift();
123
+ if (words.length === 0) unsupported = true;
124
+ }
125
+ return {
126
+ scope: unsupported ? "unknown" : compound ? "compound" : "simple",
127
+ commands,
128
+ unsafeWrite,
129
+ unsupported,
130
+ };
131
+ }
132
+
133
+ function baseName(value: string): string {
134
+ return value.replace(/\\/g, "/").split("/").at(-1)?.toLowerCase() ?? "";
135
+ }
136
+
137
+ function isKnownInspection(words: readonly string[]): boolean {
138
+ const exe = baseName(words[0]!);
139
+ if (["pwd", "ls", "grep", "cat", "head", "tail", "wc", "stat", "du", "which", "sort", "uniq", "cut", "tr", "printf", "echo", "basename", "dirname"].includes(exe)) {
140
+ return true;
141
+ }
142
+ if (exe === "find") {
143
+ return !words.some((word) => ["-delete", "-exec", "-execdir", "-ok", "-okdir"].includes(word));
144
+ }
145
+ if (exe === "fd") {
146
+ return !words.some((word) => ["-x", "--exec", "-X", "--exec-batch"].includes(word));
147
+ }
148
+ if (exe === "rg") {
149
+ return !words.some((word) => word === "--pre" || word.startsWith("--pre="));
150
+ }
151
+ if (exe === "command" && words[1] === "-v") return true;
152
+ if (exe === "sed") return words.includes("-n") && !words.some((word) => /^-.*i/.test(word));
153
+ if (exe === "jq") return !words.some((word) => word === "-i" || word === "--in-place");
154
+ if (exe !== "git") return false;
155
+ const sub = words[1]?.toLowerCase();
156
+ if (["status", "diff", "log", "show", "grep", "rev-parse", "ls-files", "ls-tree"].includes(sub ?? "")) return true;
157
+ if (sub === "branch") return words.includes("--show-current");
158
+ if (sub === "remote") {
159
+ const action = words[2]?.toLowerCase();
160
+ return action === undefined || action === "-v" || action === "--verbose" || action === "get-url";
161
+ }
162
+ return false;
163
+ }
164
+
165
+ function isKnownTestBuild(words: readonly string[]): boolean {
166
+ const exe = baseName(words[0]!);
167
+ const first = words[1]?.toLowerCase();
168
+ if (["pytest", "vitest", "jest", "tsc", "eslint", "stylelint"].includes(exe)) return true;
169
+ if (exe === "biome") return first === "check" || first === "lint";
170
+ if (exe === "cargo") return ["test", "check", "build", "clippy", "fmt"].includes(first ?? "");
171
+ if (exe === "go") return first === "test" || first === "vet";
172
+ if (exe === "bun") {
173
+ if (first === "test") return true;
174
+ if (first === "run") return /^(test|check|lint|typecheck|build)(:|$)/.test(words[2]?.toLowerCase() ?? "");
175
+ }
176
+ if (["npm", "pnpm", "yarn"].includes(exe)) {
177
+ if (first === "test") return true;
178
+ if (first === "run") return /^(test|check|lint|typecheck|build)(:|$)/.test(words[2]?.toLowerCase() ?? "");
179
+ }
180
+ return false;
181
+ }
182
+
183
+ function isKnownMutation(words: readonly string[]): boolean {
184
+ const exe = baseName(words[0]!);
185
+ if (["rm", "mv", "cp", "mkdir", "rmdir", "touch", "chmod", "chown", "ln", "install"].includes(exe)) return true;
186
+ if (exe === "sed" && words.some((word) => /^-.*i/.test(word))) return true;
187
+ if (exe === "git") {
188
+ return [
189
+ "add", "commit", "reset", "checkout", "switch", "restore", "clean", "merge", "rebase",
190
+ "cherry-pick", "revert", "tag", "push", "pull", "fetch", "stash", "worktree",
191
+ ].includes(words[1]?.toLowerCase() ?? "");
192
+ }
193
+ if (["npm", "pnpm", "yarn", "bun"].includes(exe)) {
194
+ return ["install", "add", "remove", "uninstall", "update", "upgrade", "link", "publish"].includes(words[1]?.toLowerCase() ?? "");
195
+ }
196
+ return false;
197
+ }
198
+
199
+ export function shellCommandText(input: unknown): string | undefined {
200
+ if (!input || typeof input !== "object" || Array.isArray(input)) return undefined;
201
+ const command = (input as Record<string, unknown>).command;
202
+ return typeof command === "string" && command.trim().length > 0 ? command.trim() : undefined;
203
+ }
204
+
205
+ /**
206
+ * Conservative, non-executing shell classifier. A command is considered
207
+ * inspection/test-build only when both its structure and executable/subcommand
208
+ * are allowlisted. Everything ambiguous fails closed to unknown.
209
+ */
210
+ export function classifyShellCommand(input: unknown): ShellCommandClassification {
211
+ const command = shellCommandText(input);
212
+ if (!command) return { scope: "unknown", kind: "unknown" };
213
+ const scan = scanShell(command);
214
+ if (scan.unsupported || scan.commands.length === 0) return { scope: "unknown", kind: "unknown" };
215
+ if (scan.unsafeWrite || scan.commands.some(isKnownMutation)) return { scope: scan.scope, kind: "mutation" };
216
+ if (scan.commands.length === 1 && isKnownTestBuild(scan.commands[0]!)) return { scope: scan.scope, kind: "test-build" };
217
+ if (scan.commands.every(isKnownInspection)) return { scope: scan.scope, kind: "inspection" };
218
+ return { scope: scan.scope, kind: "unknown" };
219
+ }
@@ -1,6 +1,7 @@
1
1
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
2
2
  import { loadPiToolsSuiteConfig, type TodoThinkingLevel as ConfigTodoThinkingLevel } from "../config.js";
3
3
  import { isAgentBusyRaceError } from "../context-usage.js";
4
+ import { hasAvailableIndexedProjectRoot } from "../lib/project.js";
4
5
  import { autoClearCompletedTodos } from "./state/auto-clear.js";
5
6
  import { loadPersistedPlan, syncPersistedPlan } from "./state/persistence.js";
6
7
  import { replayFromBranch } from "./state/replay.js";
@@ -32,6 +33,25 @@ const TODO_NUDGE_IDLE_RETRY_DELAY_MS = 100;
32
33
  const TODO_NUDGE_MAX_IDLE_ATTEMPTS = 40;
33
34
  const ASK_USER_TOOL_NAMES = new Set(["ask_user", "ask_user_question", "question"]);
34
35
  const TODO_THINKING_RESTORE_METADATA_KEY = "__piTodoRestoreThinking";
36
+ const REPO_KNOWLEDGE_FINALIZATION_REMINDER =
37
+ "📚 Before completing the final todo: if behavior/contracts changed, reconcile affected specs and repo knowledge; skip for mechanical changes.";
38
+
39
+ function completesTodo(info: { action: string; params: TaskMutationParams }): boolean {
40
+ if (info.action === "update") return info.params.status === "completed";
41
+ if (info.action !== "batch_update") return false;
42
+ return (info.params.items ?? []).some((item) => item.status === "completed");
43
+ }
44
+
45
+ function repoKnowledgeFinalizationReminder(
46
+ state: ReturnType<typeof getState>,
47
+ ctx: ExtensionContext,
48
+ info: { action: string; params: TaskMutationParams },
49
+ ): string | undefined {
50
+ if (!hasAvailableIndexedProjectRoot(ctx.cwd)) return undefined;
51
+ if (!completesTodo(info)) return undefined;
52
+ const remainingActive = selectVisibleTasks(state).filter((task) => ACTIVE_STATUSES.has(task.status));
53
+ return remainingActive.length === 1 ? REPO_KNOWLEDGE_FINALIZATION_REMINDER : undefined;
54
+ }
35
55
 
36
56
  function isStaleExtensionContextError(error: unknown): boolean {
37
57
  return error instanceof Error && /ctx is stale|stale ctx|stale after session replacement|stale after.*reload/i.test(error.message);
@@ -241,6 +261,7 @@ export default function (pi: ExtensionAPI) {
241
261
  registerTodoTool(pi, {
242
262
  ...thinkingPrompt,
243
263
  ...(availableThinkingLevels ? { parameters: todoParamsSchemaForThinkingLevels(availableThinkingLevels) } : {}),
264
+ resultReminder: repoKnowledgeFinalizationReminder,
244
265
  prepareMutation: (state, ctx, info) => {
245
266
  if (!todoThinkingEnabled) return info.params;
246
267
  if (info.action === "create" || info.action === "update") return prepareTodoThinkingMutation(state, ctx, info.params);
@@ -84,6 +84,11 @@ interface TodoToolHooks {
84
84
  ctx: ExtensionContext,
85
85
  info: { action: TaskAction; params: TaskMutationParams },
86
86
  ) => TaskMutationParams | Promise<TaskMutationParams>;
87
+ resultReminder?: (
88
+ state: ReturnType<typeof getState>,
89
+ ctx: ExtensionContext,
90
+ info: { action: TaskAction; params: TaskMutationParams },
91
+ ) => string | undefined | Promise<string | undefined>;
87
92
  afterCommit?: (
88
93
  state: ReturnType<typeof getState>,
89
94
  ctx: ExtensionContext,
@@ -448,7 +453,20 @@ export function registerTodoTool(pi: ExtensionAPI, hooks: TodoToolRegistrationOp
448
453
  params: preparedParams,
449
454
  committedState: autoClear.state,
450
455
  });
451
- const toolResult = buildToolResult(params.action, preparedParams, autoClear.state, result.op);
456
+ let toolResult = buildToolResult(params.action, preparedParams, autoClear.state, result.op);
457
+ const resultReminder = await hooks.resultReminder?.(result.state, _ctx as ExtensionContext, {
458
+ action: params.action,
459
+ params: preparedParams,
460
+ });
461
+ if (resultReminder?.trim()) {
462
+ toolResult = {
463
+ ...toolResult,
464
+ content: [{
465
+ type: "text" as const,
466
+ text: `${toolResult.content[0]?.text ?? ""}\n\n${resultReminder.trim()}`.trim(),
467
+ }],
468
+ };
469
+ }
452
470
  if (!autoClear.cleared) return toolResult;
453
471
  return {
454
472
  ...toolResult,
@@ -17,6 +17,8 @@ export type RepoDiscoveryToolDescription = ToolDescription & Required<Pick<ToolD
17
17
  targetDescription?: string;
18
18
  };
19
19
 
20
+ export type RepoKnowledgeToolDescription = ToolDescription & Required<Pick<ToolDescription, "promptSnippet" | "promptGuidelines">>;
21
+
20
22
  export type ToolDescriptionSetOptions = {
21
23
  repoDiscovery?: boolean;
22
24
  };
@@ -29,10 +31,9 @@ export const COMPRESS_TOOL_DESCRIPTION: ToolDescription = {
29
31
  name: "compress",
30
32
  label: "Compress Context",
31
33
  description: COMPRESS_RANGE_DESCRIPTION,
32
- promptSnippet: "Use compress for context-pressure housekeeping: summarize closed, high-yield stale slices when context is meaningfully high or a DCP reminder gives concrete candidates; do not compress just because low-context work produced a small closed slice.",
34
+ promptSnippet: "Compress only closed, high-yield stale context when pressure/reminders justify it; keep active or still-needed raw context. Omit summary for DCP summarizer.",
33
35
  promptGuidelines: [
34
- "Compression candidates: completed implementation, verification, config/doc edits, answered exploration, dead ends, and log inspection when large/stale or context pressure is meaningful; Low context usage by itself does not require compression.",
35
- "Summarize large stale shell/read/repo/web outputs, diffs, and passing logs once exact text is no longer useful; keep active or still-needed context raw.",
36
+ "Prefer completed work and understood large tool/log output; low context alone is not a trigger.",
36
37
  ],
37
38
  };
38
39
 
@@ -70,11 +71,11 @@ export function asyncSubagentToolDescriptions(options: ToolDescriptionSetOptions
70
71
  name: "subagents",
71
72
  label: "Subagents",
72
73
  description: [
73
- "For every real-browser QA request, immediately spawn subagentType='browser-qa' before checking files, URLs, servers, or other prerequisites; the QA sub-agent owns feasibility checks and blocked reports, so the parent must not attempt browser QA itself.",
74
- "If browser-qa reports that credentials are required, it must identify the generated project-local template and explicitly ask the user to fill it; the parent relays that request without reading or editing the credential file.",
75
- "After browser testing, browser-qa must return clickable links for every available screenshot, video, and trace; the parent must preserve those links in its user-facing report.",
74
+ "For every real user-interface QA request (browser, terminal/TUI, or desktop GUI), immediately spawn subagentType='ui-qa' before checking files, URLs, servers, launch commands, or other prerequisites; the QA sub-agent owns feasibility checks and blocked reports, so the parent must not substitute static checks for requested UI QA.",
75
+ "If ui-qa browser testing reports that credentials are required, it must identify the generated project-local template and explicitly ask the user to fill it; the parent relays that request without reading or editing the credential file.",
76
+ "After UI testing, ui-qa must return clickable links for every available screenshot, terminal capture, video, trace, or other retained evidence; the parent must preserve those links in its user-facing report.",
76
77
  SUBAGENT_DELEGATION_GUIDANCE,
77
- "Presets declare available models; each agent's ordered models selects the first usable model in that pool. AGENTS_PRESET or /subagent-preset session <name> selects the current session pool; /subagent-preset init creates a sample config. Do not override the model merely to choose a role.",
78
+ "Presets declare available models; each agent's ordered models selects the first usable model in that pool. AGENTS_PRESET or /subagent-preset session <name> selects the current session pool. Bundled pools live with agent files, and projects can override/add pools in .pi/agents/presets.jsonc. Do not override the model merely to choose a role.",
78
79
  SUBAGENT_TYPE_SELECTION_GUIDANCE,
79
80
  repoDiscovery
80
81
  ? "Use for broad independent tracks, review axes, or hypotheses even though repo_* tools are available."
@@ -83,18 +84,18 @@ export function asyncSubagentToolDescriptions(options: ToolDescriptionSetOptions
83
84
  "Results are compact with artifact links. Agents run isolated pi processes with extensions disabled to prevent recursive spawning; spawn/task timeoutSeconds can shorten the default 30m watchdog, project concurrency queues excess agents, and retry backoff/fallback models/Antigravity account rotation are config-driven.",
84
85
  ].join(" "),
85
86
  promptSnippet:
86
- "For every browser-based visual QA, UI bug reproduction, or real-browser fix-verification request, immediately spawn subagentType='browser-qa' even for a single track and before inspecting files or checking prerequisites. The browser-qa sub-agent must discover the target and report missing prerequisites; do not preflight, perform, or substitute browser QA in the parent agent. " +
87
- "Give browser-qa a concise acceptance brief: the known target URL/app, user-visible flow, expected observable result, and required artifacts. Do not prescribe repository files, searches, commands, server setup, or mock/synthetic substitutes; unknown setup belongs to the QA sub-agent's discovery. " +
87
+ "For every real UI QA, UI bug reproduction, or user-facing fix-verification request across browser, terminal/TUI, or desktop GUI, immediately spawn subagentType='ui-qa' even for a single track and before inspecting files or checking prerequisites. The ui-qa sub-agent must discover the actual target/control path and report missing prerequisites; do not preflight, perform, or substitute requested UI QA in the parent agent. " +
88
+ "Give ui-qa a concise acceptance brief: the known target URL/app/command, user-visible flow, expected observable result, and required artifacts. Do not prescribe repository files, searches, commands, server setup, or mock/synthetic substitutes; unknown setup belongs to the QA sub-agent's discovery. " +
88
89
  "For other work, use subagents action='spawn' for economical execution or context isolation, including one bounded sequential task or explicit delegate/parallelize/split work requests. " +
89
90
  SUBAGENT_TYPE_SELECTION_GUIDANCE + " Avoid trivial reads/edits and do not call status/wait immediately after spawn just for progress. " +
90
91
  (repoDiscovery
91
92
  ? "For one semantic code-discovery question, use repo_search; for independent tracks/hypotheses/review axes, delegate even when repo_* tools exist. Read result only after completion when findings are needed."
92
93
  : "For one focused code-discovery question, use direct read/grep. Without repo_* tools, delegate bounded research tracks for broad discovery rather than flooding parent context. Read result only after completion when findings are needed."),
93
94
  promptGuidelines: [
94
- "Treat every real-browser QA request as a mandatory delegation trigger and an explicit exception to the large/parallel threshold: immediately spawn with `subagentType: \"browser-qa\"` before checking prerequisites. The QA sub-agent owns target discovery, feasibility checks, browser automation, evidence, and blocked reports; the parent must not inspect the project first or substitute non-browser checks.",
95
- "Keep the browser-qa task payload at the user-visible acceptance level: known target URL/app, actions to perform, expected observable outcome, and requested evidence. Do not turn it into a repository investigation plan, name internal files or commands, dictate server setup, or invent a mock/synthetic target. Leave unknown prerequisites to the QA sub-agent.",
96
- "When browser-qa reports missing credentials, relay its explicit request and generated `.pi/qa_auth.jsonc` template path; never inspect, populate, or edit that credential file in the parent.",
97
- "After browser-qa completes a test, preserve its clickable screenshot, video, and trace links in the final user-facing response whenever those artifacts exist.",
95
+ "Treat every real UI QA request as a mandatory delegation trigger and an explicit exception to the large/parallel threshold: immediately spawn with `subagentType: \"ui-qa\"` before checking prerequisites. The QA sub-agent owns target discovery, feasibility checks, UI automation, evidence, and blocked reports; the parent must not inspect the project first or substitute non-UI checks.",
96
+ "Keep the ui-qa task payload at the user-visible acceptance level: known target URL/app/command, actions to perform, expected observable outcome, and requested evidence. Do not turn it into a repository investigation plan, name internal files or commands, dictate setup, or invent a mock/synthetic target. Leave unknown prerequisites to the QA sub-agent.",
97
+ "When ui-qa browser testing reports missing credentials, relay its explicit request and generated `.pi/qa_auth.jsonc` template path; never inspect, populate, or edit that credential file in the parent.",
98
+ "After ui-qa completes a test, preserve its clickable screenshot, terminal capture, video, trace, and other evidence links in the final user-facing response whenever those artifacts exist.",
98
99
  SUBAGENT_DELEGATION_GUIDANCE,
99
100
  repoDiscovery
100
101
  ? "For one discovery question, use repo_search; spawn for independent tracks/hypotheses/review axes, and do not let repo_* availability suppress delegation."
@@ -222,7 +223,24 @@ export const REPO_DISCOVERY_TOOLS: RepoDiscoveryToolDescription[] = [
222
223
  },
223
224
  ];
224
225
 
225
- export const REPO_DISCOVERY_TOOL_NAMES = REPO_DISCOVERY_TOOLS.map((tool) => tool.name);
226
+ export const REPO_KNOWLEDGE_TOOL_DESCRIPTION: RepoKnowledgeToolDescription = {
227
+ name: "repo_knowledge",
228
+ label: "Repo Knowledge",
229
+ description:
230
+ "Project behavioral knowledge/spec maintenance backed by idx. Query contracts with context/search/show/status, review task-scoped impact after material behavior changes, discover new/moved docs, and explicitly record/relate/verify metadata only after semantic evidence review. This tool is exposed only when idx is available and the project is indexed.",
231
+ promptSnippet:
232
+ "Use repo_knowledge for project behavior/contracts and their maintenance. For a behavior/contract question that also needs implementation/tests/freshness, prefer action=context; use action=search when only the relevant specs/contracts are needed. Before a material behavior change, find the current primary contract; after implementation, run action=impact on this task's changed paths. Update the existing spec, or create a focused new spec with Edit/Write/apply_patch when no suitable contract exists, then record/relate and verify only after checking code/tests. Skip this lifecycle for mechanical/non-behavioral edits.",
233
+ promptGuidelines: [
234
+ "Primary specs/contracts are authoritative; catalog/search summaries, embeddings, graph proximity and semantic candidates are routing evidence only. record is classification, not verification; non-fresh status requires review before presenting behavior as current.",
235
+ "For material externally visible or project-contract behavior changes: query existing knowledge before/while implementing, keep the authoritative spec aligned in the same task, create a focused primary spec when no suitable contract exists, then run task-scoped impact and reconcile uncovered/new/moved docs before finishing.",
236
+ "Never persist a relation from similarity alone and never add one just to make coverage non-empty. A reviewed no-impact outcome is valid. verify only after reading the primary source and checking relevant implementation/tests; changed code never auto-rewrites spec semantics.",
237
+ ],
238
+ };
239
+
240
+ export const REPO_DISCOVERY_TOOL_NAMES = [
241
+ ...REPO_DISCOVERY_TOOLS.map((tool) => tool.name),
242
+ REPO_KNOWLEDGE_TOOL_DESCRIPTION.name,
243
+ ];
226
244
 
227
245
  export const TODO_TOOL_DESCRIPTION: ToolDescription = {
228
246
  name: "todo",
@@ -333,12 +351,16 @@ export function claudeAliasToolDescriptions(options: ToolDescriptionSetOptions |
333
351
  Edit: {
334
352
  name: "Edit",
335
353
  label: "Edit",
336
- description: "Replace exact text in an existing file. Use for surgical edits; use Write only for intentional whole-file replacement.",
354
+ description: repoDiscovery
355
+ ? "Replace exact text in an existing file. Use for surgical edits; use Write only for whole-file replacement. After a material behavior change, do not finish until repo_knowledge impact reviews this task's changed paths and the authoritative spec is updated or created and verified; skip that lifecycle for mechanical/non-behavioral edits."
356
+ : "Replace exact text in an existing file. Use for surgical edits; use Write only for intentional whole-file replacement.",
337
357
  },
338
358
  Write: {
339
359
  name: "Write",
340
360
  label: "Write",
341
- description: "Create or overwrite a file with complete contents. Use only when replacing the whole file is intended.",
361
+ description: repoDiscovery
362
+ ? "Create or overwrite a file with complete contents. Use only for intentional whole-file writes. When creating/changing material project behavior, keep or create the primary spec in the same task and finish with task-scoped repo_knowledge impact + semantic verification; mechanical/non-behavioral writes do not require it."
363
+ : "Create or overwrite a file with complete contents. Use only when replacing the whole file is intended.",
342
364
  },
343
365
  Bash: {
344
366
  name: "Bash",
@@ -365,7 +387,9 @@ export function claudeAliasToolDescriptions(options: ToolDescriptionSetOptions |
365
387
  export const CLAUDE_ALIAS_TOOL_DESCRIPTIONS = claudeAliasToolDescriptions(false);
366
388
  export const CLAUDE_ALIAS_TOOL_DESCRIPTIONS_WITH_REPO = claudeAliasToolDescriptions(true);
367
389
 
368
- export const CODEX_ALIAS_TOOL_DESCRIPTIONS = {
390
+ export function codexAliasToolDescriptions(options: ToolDescriptionSetOptions | boolean = false) {
391
+ const repoDiscovery = hasRepoDiscovery(options);
392
+ return {
369
393
  shellCommand: {
370
394
  name: "shell",
371
395
  label: "shell",
@@ -374,7 +398,7 @@ export const CODEX_ALIAS_TOOL_DESCRIPTIONS = {
374
398
  applyPatch: {
375
399
  name: "apply_patch",
376
400
  label: "apply_patch",
377
- description: `Apply file edits with a relative-path patch or standard unified diff. Use for creating, updating, moving, or deleting files; keep each patch focused.
401
+ description: `Apply file edits with a relative-path patch or standard unified diff. Use for creating, updating, moving, or deleting files; keep each patch focused.${repoDiscovery ? " After a material behavior change, keep/create the authoritative primary spec in the same task and finish with task-scoped repo_knowledge impact plus semantic verification; skip this for mechanical/non-behavioral edits." : ""}
378
402
 
379
403
  Begin-patch format:
380
404
  *** Begin Patch
@@ -388,4 +412,8 @@ Sections: *** Add File (new lines start with +), *** Update File (optionally ***
388
412
 
389
413
  Unified diff with ---/+++ headers is also supported. Paths must be workspace-relative, never absolute. Provide the complete patch in input.`,
390
414
  },
391
- } satisfies Record<string, ToolDescription>;
415
+ } satisfies Record<string, ToolDescription>;
416
+ }
417
+
418
+ export const CODEX_ALIAS_TOOL_DESCRIPTIONS = codexAliasToolDescriptions(false);
419
+ export const CODEX_ALIAS_TOOL_DESCRIPTIONS_WITH_REPO = codexAliasToolDescriptions(true);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-ui-extend",
3
- "version": "1.0.41",
3
+ "version": "1.0.45",
4
4
  "description": "Pix: a workspace-first terminal UI for Pi with tabs, readable tool activity, voice input, and bundled agent tools.",
5
5
  "private": false,
6
6
  "repository": {
@@ -26,7 +26,8 @@
26
26
  "import": "./dist/sdk.js"
27
27
  },
28
28
  "./schemas/pix.json": "./schemas/pix.json",
29
- "./schemas/pi-tools-suite.json": "./schemas/pi-tools-suite.json"
29
+ "./schemas/pi-tools-suite.json": "./schemas/pi-tools-suite.json",
30
+ "./schemas/tasks.json": "./schemas/tasks.json"
30
31
  },
31
32
  "files": [
32
33
  "assets/screenshots",
@@ -54,6 +55,7 @@
54
55
  "prelink:pix": "npm run build:pix --silent",
55
56
  "link:pix": "mise exec node@24.16.0 -- npm link --silent",
56
57
  "watch:pix": "npm run link:pix --silent && mise exec node@24.16.0 -- node scripts/watch-pix.mjs",
58
+ "watch:all": "mise exec node@24.16.0 -- node scripts/watch-all.mjs",
57
59
  "rebuild:all": "npm run build:desktop",
58
60
  "check": "mise exec node@24.16.0 -- npm run check:inner",
59
61
  "precheck:inner": "npm run generate-schemas --silent",
@@ -61,6 +63,8 @@
61
63
  "test": "mise exec node@24.16.0 -- npm run test:inner",
62
64
  "test:inner": "node --import tsx --test \"tests/**/*.test.ts\"",
63
65
  "test:tools-suite": "npm --prefix external/pi-tools-suite test",
66
+ "test:dcp-session-sim": "npm --prefix external/pi-tools-suite run test:dcp-session-sim",
67
+ "test:dcp-reminder-e2e": "npm --prefix external/pi-tools-suite run test:dcp-reminder-e2e",
64
68
  "build:acp": "npm --prefix acp run build",
65
69
  "check:acp": "npm --prefix acp run check",
66
70
  "dev:desktop": "npm --prefix desktop run dev",
@@ -76,6 +80,7 @@
76
80
  "prepack": "npm run generate-schemas --silent",
77
81
  "pack:dry-run": "npm pack --dry-run",
78
82
  "smoke-test": "mise exec node@24.16.0 -- bash scripts/smoke-test-package.sh",
83
+ "smoke-test:cli": "mise exec node@24.16.0 -- node scripts/smoke-test-package-cli.mjs",
79
84
  "release:check": "npm run check && npm run build:pix && npm run pack:dry-run",
80
85
  "publish-npm": "bash scripts/publish.sh",
81
86
  "generate-schemas": "tsx scripts/generate-schemas.ts",
@@ -84,10 +89,13 @@
84
89
  "prepublishOnly": "npm run check && npm run build:pix && npm run generate-schemas"
85
90
  },
86
91
  "dependencies": {
92
+ "@cortexkit/antigravity-auth-core": "2.2.1",
87
93
  "@earendil-works/pi-ai": "0.85.1",
88
94
  "@earendil-works/pi-coding-agent": "0.85.1",
89
95
  "@earendil-works/pi-tui": "0.85.1",
96
+ "@lydell/node-pty": "1.1.0",
90
97
  "@mariozechner/clipboard": "^0.3.9",
98
+ "@xterm/headless": "^6.0.0",
91
99
  "grok-mermaid": "0.2.2",
92
100
  "jsonc-parser": "3.3.1",
93
101
  "typebox": "1.1.38",
@@ -103,13 +111,6 @@
103
111
  "engines": {
104
112
  "node": ">=22.19.0 <25"
105
113
  },
106
- "overrides": {
107
- "get-uv-event-loop-napi-h": "1.0.6",
108
- "node-addon-api": "8.8.0"
109
- },
110
- "optionalDependencies": {
111
- "vosk": "0.3.39"
112
- },
113
114
  "keywords": [
114
115
  "pi",
115
116
  "pix",