@herbertgao/pi-extensions 2026.8.14 → 2026.8.15

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 (231) hide show
  1. package/README.md +16 -13
  2. package/THIRD_PARTY_NOTICES.md +0 -1
  3. package/node_modules/@czottmann/pi-automode/CHANGELOG.md +8 -1
  4. package/node_modules/@czottmann/pi-automode/README.md +4 -0
  5. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/classifier.ts +71 -9
  6. package/node_modules/@czottmann/pi-automode/package.json +1 -1
  7. package/node_modules/@herbertgao/pi-bark/LICENSE +21 -0
  8. package/node_modules/@herbertgao/pi-bark/README.md +44 -0
  9. package/node_modules/@herbertgao/pi-bark/assets/pi-icon.png +0 -0
  10. package/node_modules/@herbertgao/pi-bark/package.json +53 -0
  11. package/node_modules/@herbertgao/pi-bark/src/index.ts +178 -0
  12. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
  13. package/node_modules/@narumitw/pi-btw/README.md +125 -88
  14. package/node_modules/@narumitw/pi-btw/dist/index.ts +140 -45
  15. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +2 -2
  16. package/node_modules/@narumitw/pi-btw/package.json +5 -8
  17. package/node_modules/@narumitw/pi-btw/src/btw.ts +31 -26
  18. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +106 -17
  19. package/node_modules/@narumitw/pi-btw/src/menu.ts +34 -2
  20. package/node_modules/@narumitw/pi-btw/src/settings.ts +16 -0
  21. package/node_modules/@tifan/pi-handoff/README.md +36 -13
  22. package/node_modules/@tifan/pi-handoff/package.json +2 -2
  23. package/node_modules/@tifan/pi-handoff/src/index.ts +160 -4
  24. package/node_modules/@tifan/pi-recap/README.md +1 -1
  25. package/node_modules/@tifan/pi-recap/package.json +2 -2
  26. package/node_modules/@tifan/pi-recap/src/index.ts +15 -0
  27. package/node_modules/@tifan/pi-rename/README.md +42 -21
  28. package/node_modules/@tifan/pi-rename/package.json +1 -1
  29. package/node_modules/@tifan/pi-rename/src/index.ts +62 -11
  30. package/node_modules/@tifan/pi-rename/src/language.ts +18 -0
  31. package/node_modules/@tifan/pi-rename/src/models.ts +69 -13
  32. package/node_modules/@tifan/pi-rename/src/naming.ts +36 -7
  33. package/node_modules/@tifan/pi-rename/src/sanitize.ts +22 -14
  34. package/node_modules/pi-lens/CHANGELOG.md +320 -0
  35. package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +79 -0
  36. package/node_modules/pi-lens/dist/clients/actionable-warnings-logger.js +16 -1
  37. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +13 -6
  38. package/node_modules/pi-lens/dist/clients/availability-probe-flight.js +12 -0
  39. package/node_modules/pi-lens/dist/clients/biome-client.js +7 -0
  40. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +20 -0
  41. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +56 -19
  42. package/node_modules/pi-lens/dist/clients/build-identity.js +95 -0
  43. package/node_modules/pi-lens/dist/clients/bus-publish.js +3 -0
  44. package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +2 -1
  45. package/node_modules/pi-lens/dist/clients/cache-observability.js +2 -1
  46. package/node_modules/pi-lens/dist/clients/cascade-logger.js +15 -1
  47. package/node_modules/pi-lens/dist/clients/dead-code-client.js +14 -11
  48. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +56 -4
  49. package/node_modules/pi-lens/dist/clients/demoted-finding-render.js +14 -0
  50. package/node_modules/pi-lens/dist/clients/dependency-checker.js +20 -3
  51. package/node_modules/pi-lens/dist/clients/deps/minimatch.js +1 -1
  52. package/node_modules/pi-lens/dist/clients/dispatch/collect-later-tier.js +39 -0
  53. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +150 -9
  54. package/node_modules/pi-lens/dist/clients/dispatch/fact-store.js +231 -9
  55. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +205 -100
  56. package/node_modules/pi-lens/dist/clients/dispatch/pending-runner-findings.js +87 -0
  57. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +21 -5
  58. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +174 -55
  59. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +5 -1
  60. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +16 -3
  61. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +54 -9
  62. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +1 -3
  63. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +37 -1
  64. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +4 -1
  65. package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +11 -4
  66. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +4 -0
  67. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +8 -3
  68. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +76 -25
  69. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +4 -0
  70. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +19 -7
  71. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +7 -13
  72. package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +51 -17
  73. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +66 -1
  74. package/node_modules/pi-lens/dist/clients/file-utils.js +157 -7
  75. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +5 -0
  76. package/node_modules/pi-lens/dist/clients/formatters.js +28 -3
  77. package/node_modules/pi-lens/dist/clients/git-guard.js +197 -47
  78. package/node_modules/pi-lens/dist/clients/git-tracked-ignore.js +32 -1
  79. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +5 -1
  80. package/node_modules/pi-lens/dist/clients/gzip-stage-write.js +7 -0
  81. package/node_modules/pi-lens/dist/clients/installer/index.js +131 -34
  82. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +3 -1
  83. package/node_modules/pi-lens/dist/clients/instance-reaper.js +4 -13
  84. package/node_modules/pi-lens/dist/clients/instance-registry-lock.js +145 -0
  85. package/node_modules/pi-lens/dist/clients/instance-registry.js +432 -122
  86. package/node_modules/pi-lens/dist/clients/jscpd-client.js +6 -1
  87. package/node_modules/pi-lens/dist/clients/language-profile.js +4 -2
  88. package/node_modules/pi-lens/dist/clients/latency-logger.js +19 -1
  89. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +8 -0
  90. package/node_modules/pi-lens/dist/clients/lens-map.js +3 -1
  91. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +4 -0
  92. package/node_modules/pi-lens/dist/clients/lsp/client.js +516 -31
  93. package/node_modules/pi-lens/dist/clients/lsp/config.js +25 -2
  94. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +9 -4
  95. package/node_modules/pi-lens/dist/clients/lsp/index.js +509 -182
  96. package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +5 -0
  97. package/node_modules/pi-lens/dist/clients/lsp/launch.js +36 -8
  98. package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +116 -4
  99. package/node_modules/pi-lens/dist/clients/lsp/server.js +142 -13
  100. package/node_modules/pi-lens/dist/clients/lsp/session-roots.js +99 -0
  101. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +16 -6
  102. package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +32 -23
  103. package/node_modules/pi-lens/dist/clients/lsp-mutation.js +16 -2
  104. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +12 -6
  105. package/node_modules/pi-lens/dist/clients/mcp/session.js +14 -1
  106. package/node_modules/pi-lens/dist/clients/memory-sampler.js +23 -1
  107. package/node_modules/pi-lens/dist/clients/message-end-attribution.js +30 -0
  108. package/node_modules/pi-lens/dist/clients/metrics-history.js +21 -4
  109. package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +232 -18
  110. package/node_modules/pi-lens/dist/clients/package-manager.js +6 -18
  111. package/node_modules/pi-lens/dist/clients/path-keyed-map.js +14 -1
  112. package/node_modules/pi-lens/dist/clients/path-utils.js +32 -0
  113. package/node_modules/pi-lens/dist/clients/pipeline.js +0 -8
  114. package/node_modules/pi-lens/dist/clients/process-singletons.js +210 -0
  115. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/madge.js +2 -1
  116. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +10 -4
  117. package/node_modules/pi-lens/dist/clients/project-snapshot.js +33 -11
  118. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +24 -2
  119. package/node_modules/pi-lens/dist/clients/read-guard.js +74 -7
  120. package/node_modules/pi-lens/dist/clients/recent-touches.js +17 -5
  121. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +639 -62
  122. package/node_modules/pi-lens/dist/clients/review-graph/tsconfig-paths.js +5 -1
  123. package/node_modules/pi-lens/dist/clients/review-graph-logger.js +21 -1
  124. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +68 -0
  125. package/node_modules/pi-lens/dist/clients/runtime-session.js +30 -12
  126. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +15 -0
  127. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +106 -7
  128. package/node_modules/pi-lens/dist/clients/runtime-turn.js +242 -27
  129. package/node_modules/pi-lens/dist/clients/safe-spawn.js +158 -18
  130. package/node_modules/pi-lens/dist/clients/security-scan-client.js +48 -17
  131. package/node_modules/pi-lens/dist/clients/session-lifecycle.js +238 -42
  132. package/node_modules/pi-lens/dist/clients/sg-runner.js +27 -15
  133. package/node_modules/pi-lens/dist/clients/shared-checkout-guard.js +338 -0
  134. package/node_modules/pi-lens/dist/clients/spawn-output-cap.js +37 -0
  135. package/node_modules/pi-lens/dist/clients/spawn-timeout-cooldown.js +15 -1
  136. package/node_modules/pi-lens/dist/clients/startup-scan.js +6 -7
  137. package/node_modules/pi-lens/dist/clients/startup-timing.js +69 -4
  138. package/node_modules/pi-lens/dist/clients/string-utils.js +20 -0
  139. package/node_modules/pi-lens/dist/clients/test-runner-client.js +419 -88
  140. package/node_modules/pi-lens/dist/clients/tool-policy.js +1 -1
  141. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +11 -5
  142. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +17 -1
  143. package/node_modules/pi-lens/dist/clients/warm-attach.js +8 -10
  144. package/node_modules/pi-lens/dist/clients/word-index-logger.js +13 -1
  145. package/node_modules/pi-lens/dist/clients/word-index-store.js +448 -0
  146. package/node_modules/pi-lens/dist/clients/word-index.js +582 -104
  147. package/node_modules/pi-lens/dist/clients/workspace-topology.js +17 -0
  148. package/node_modules/pi-lens/dist/clients/zizmor-config.js +26 -6
  149. package/node_modules/pi-lens/dist/index.js +11243 -6651
  150. package/node_modules/pi-lens/dist/mcp/build-staleness.js +8 -7
  151. package/node_modules/pi-lens/dist/mcp/cli.js +3 -1
  152. package/node_modules/pi-lens/dist/mcp/server.js +6 -1
  153. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +4 -0
  154. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +14 -5
  155. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +3 -1
  156. package/node_modules/pi-lens/docs/agent-guide.md +4 -2
  157. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +41 -10
  158. package/node_modules/pi-lens/docs/astplayground.md +26 -17
  159. package/node_modules/pi-lens/package.json +5 -1
  160. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/c-no-malloc-free-test.yml +20 -0
  161. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/cpp-no-malloc-free-test.yml +25 -0
  162. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/cpp-no-printf-test.yml +19 -0
  163. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/go-no-fmt-println-test.yml +19 -0
  164. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/go-no-panic-in-lib-test.yml +17 -0
  165. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/go-no-underscore-func-name-test.yml +18 -0
  166. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/go-prefer-errors-is-test.yml +15 -0
  167. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/go-prefer-string-builder-test.yml +21 -0
  168. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/hyphenated-svg-attribute-test.yml +11 -0
  169. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-lateinit-test.yml +17 -0
  170. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-nullable-boolean-test.yml +15 -0
  171. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-println-test.yml +19 -0
  172. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-unnecessary-let-test.yml +7 -0
  173. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-unsafe-cast-test.yml +7 -0
  174. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-var-test.yml +7 -0
  175. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-prefer-data-class-test.yml +25 -0
  176. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-prefer-is-empty-test.yml +11 -0
  177. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-important-test.yml +21 -0
  178. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-raw-types-test.yml +30 -0
  179. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-string-concat-in-loop-test.yml +100 -0
  180. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-system-out-println-test.yml +7 -0
  181. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/prefer-string-is-empty-test.yml +7 -0
  182. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/python-optional-type-test.yml +9 -0
  183. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/reducible-list-comprehension-test.yml +11 -0
  184. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/ruby-symbol-to-proc-candidate-test.yml +10 -0
  185. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/rust-no-chars-enumerate-test.yml +7 -0
  186. package/node_modules/pi-lens/rules/ast-grep-rules/rules/c-no-malloc-free.yml +39 -0
  187. package/node_modules/pi-lens/rules/ast-grep-rules/rules/cpp-no-malloc-free.yml +45 -0
  188. package/node_modules/pi-lens/rules/ast-grep-rules/rules/cpp-no-printf.yml +41 -0
  189. package/node_modules/pi-lens/rules/ast-grep-rules/rules/go-no-fmt-println.yml +43 -0
  190. package/node_modules/pi-lens/rules/ast-grep-rules/rules/go-no-panic-in-lib.yml +41 -0
  191. package/node_modules/pi-lens/rules/ast-grep-rules/rules/go-no-underscore-func-name.yml +35 -0
  192. package/node_modules/pi-lens/rules/ast-grep-rules/rules/go-prefer-errors-is.yml +40 -0
  193. package/node_modules/pi-lens/rules/ast-grep-rules/rules/go-prefer-string-builder.yml +43 -0
  194. package/node_modules/pi-lens/rules/ast-grep-rules/rules/hyphenated-svg-attribute.yml +32 -0
  195. package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-lateinit.yml +37 -0
  196. package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-nullable-boolean.yml +33 -0
  197. package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-println.yml +42 -0
  198. package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-unnecessary-let.yml +28 -0
  199. package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-unsafe-cast.yml +28 -0
  200. package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-var.yml +35 -0
  201. package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-prefer-data-class.yml +66 -0
  202. package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-prefer-is-empty.yml +35 -0
  203. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-important.yml +21 -0
  204. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-types.yml +33 -0
  205. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-string-concat-in-loop.yml +90 -0
  206. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-system-out-println.yml +16 -0
  207. package/node_modules/pi-lens/rules/ast-grep-rules/rules/prefer-string-is-empty.yml +11 -0
  208. package/node_modules/pi-lens/rules/ast-grep-rules/rules/python-optional-type.yml +22 -0
  209. package/node_modules/pi-lens/rules/ast-grep-rules/rules/reducible-list-comprehension.yml +29 -0
  210. package/node_modules/pi-lens/rules/ast-grep-rules/rules/ruby-symbol-to-proc-candidate.yml +25 -0
  211. package/node_modules/pi-lens/rules/ast-grep-rules/rules/rust-no-chars-enumerate.yml +19 -0
  212. package/node_modules/pi-mcp-adapter/CHANGELOG.md +15 -0
  213. package/node_modules/pi-mcp-adapter/README.md +2 -2
  214. package/node_modules/pi-mcp-adapter/commands.ts +1 -1
  215. package/node_modules/pi-mcp-adapter/dist/types.d.ts +1 -1
  216. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +105 -32
  217. package/node_modules/pi-mcp-adapter/package.json +1 -1
  218. package/node_modules/pi-mcp-adapter/proxy-modes.ts +22 -12
  219. package/node_modules/pi-mcp-adapter/server-manager.ts +6 -0
  220. package/node_modules/pi-mcp-adapter/types.ts +1 -1
  221. package/node_modules/pi-web-access/CHANGELOG.md +20 -0
  222. package/node_modules/pi-web-access/README.md +15 -2
  223. package/node_modules/pi-web-access/extract.ts +57 -12
  224. package/node_modules/pi-web-access/github-extract.ts +47 -3
  225. package/node_modules/pi-web-access/index.ts +1 -1
  226. package/node_modules/pi-web-access/package.json +1 -1
  227. package/node_modules/pi-web-access/page-query.ts +61 -8
  228. package/package.json +16 -16
  229. package/node_modules/@tifan/pi-titlebar-spinner/README.md +0 -24
  230. package/node_modules/@tifan/pi-titlebar-spinner/package.json +0 -39
  231. package/node_modules/@tifan/pi-titlebar-spinner/src/index.ts +0 -49
@@ -1,4 +1,4 @@
1
- import { closeSync, existsSync, lstatSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, realpathSync, rmSync, statSync, unlinkSync } from "node:fs";
1
+ import { chmodSync, closeSync, existsSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, readSync, readdirSync, realpathSync, rmSync, statSync, unlinkSync } from "node:fs";
2
2
  import { execFile, spawn, type ChildProcess } from "node:child_process";
3
3
  import { createHash } from "node:crypto";
4
4
  import { basename, dirname, extname, join, resolve as resolvePath, sep as pathSep } from "node:path";
@@ -59,6 +59,7 @@ interface GitHubCloneConfig {
59
59
  const cloneCache = new Map<string, CachedClone>();
60
60
 
61
61
  let cachedConfig: GitHubCloneConfig | null = null;
62
+ let cloneRuntime: { parentPath: string; rootPath: string } | null = null;
62
63
 
63
64
  function normalizeEnabled(value: unknown, fallback: boolean): boolean {
64
65
  return typeof value === "boolean" ? value : fallback;
@@ -189,10 +190,48 @@ function cacheKey(owner: string, repo: string, ref?: string): string {
189
190
  return ref ? `${owner}/${repo}@${ref}` : `${owner}/${repo}`;
190
191
  }
191
192
 
193
+ function removeCloneRuntime(parentPath: string, runtimePath: string): void {
194
+ const normalizedParentPath = resolvePath(parentPath);
195
+ const normalizedRuntimePath = resolvePath(runtimePath);
196
+ if (dirname(normalizedRuntimePath) !== normalizedParentPath || !basename(normalizedRuntimePath).startsWith("runtime-")) return;
197
+
198
+ try {
199
+ const entry = lstatSync(normalizedRuntimePath);
200
+ if (entry.isSymbolicLink()) unlinkSync(normalizedRuntimePath);
201
+ else rmSync(normalizedRuntimePath, { recursive: true, force: true });
202
+ } catch {
203
+ // The runtime directory may already have been removed externally.
204
+ }
205
+ }
206
+
207
+ function getCloneRuntimeRoot(config: GitHubCloneConfig): string | null {
208
+ if (cloneRuntime) return cloneRuntime.rootPath;
209
+
210
+ let parentPath: string | null = null;
211
+ let runtimePath: string | null = null;
212
+ try {
213
+ const configuredPath = resolvePath(config.clonePath);
214
+ mkdirSync(configuredPath, { recursive: true });
215
+ parentPath = realpathSync(configuredPath);
216
+ runtimePath = mkdtempSync(join(parentPath, "runtime-"));
217
+ chmodSync(runtimePath, 0o700);
218
+ const rootPath = realpathSync(runtimePath);
219
+ if (dirname(rootPath) !== parentPath) {
220
+ removeCloneRuntime(parentPath, runtimePath);
221
+ return null;
222
+ }
223
+ cloneRuntime = { parentPath, rootPath };
224
+ return rootPath;
225
+ } catch {
226
+ if (parentPath && runtimePath) removeCloneRuntime(parentPath, runtimePath);
227
+ return null;
228
+ }
229
+ }
230
+
192
231
  function cloneDestination(config: GitHubCloneConfig, owner: string, repo: string, ref?: string): CloneDestination | null {
193
232
  try {
194
- mkdirSync(resolvePath(config.clonePath), { recursive: true });
195
- const rootPath = realpathSync(resolvePath(config.clonePath));
233
+ const rootPath = getCloneRuntimeRoot(config);
234
+ if (!rootPath) return null;
196
235
  const digest = createHash("sha256").update(JSON.stringify([owner, repo, ref ?? null])).digest("hex");
197
236
  const localPath = resolvePath(rootPath, digest);
198
237
  if (dirname(localPath) !== rootPath) return null;
@@ -742,5 +781,10 @@ export function clearCloneCache(): void {
742
781
  removeCloneDestination(entry.destination);
743
782
  }
744
783
  cloneCache.clear();
784
+
785
+ if (cloneRuntime) {
786
+ removeCloneRuntime(cloneRuntime.parentPath, cloneRuntime.rootPath);
787
+ }
788
+ cloneRuntime = null;
745
789
  cachedConfig = null;
746
790
  }
@@ -2443,7 +2443,7 @@ export default function (pi: ExtensionAPI) {
2443
2443
  description: "Fetch mode: readable (default extraction), raw (exact textual HTTP body), or answer (answer prompt using only fetched content).",
2444
2444
  })),
2445
2445
  answerModel: Type.Optional(Type.String({
2446
- description: "Optional provider/model-id override for mode answer. Defaults to the current Pi model.",
2446
+ description: "Optional provider/model-id override for mode answer. Defaults to fetch.answerProvider + fetch.answerModel when configured, otherwise the current Pi model.",
2447
2447
  })),
2448
2448
  timestamp: Type.Optional(Type.String({
2449
2449
  description: "Extract video frame(s) at a timestamp or time range. Single: '1:23:45', '23:45', or '85' (seconds). Range: '23:41-25:00' extracts evenly-spaced frames across that span (default 6). Use frames with ranges to control density; single+frames uses a fixed 5s interval. YouTube requires yt-dlp + ffmpeg; local videos require ffmpeg. Use a range when you know the approximate area but not the exact moment — you'll get a contact sheet to visually identify the right frame.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-web-access",
3
- "version": "0.26.0",
3
+ "version": "0.27.0",
4
4
  "description": "Web search, URL fetching, GitHub repo cloning, PDF extraction, YouTube video understanding, and local video analysis for Pi coding agent. Supports OpenAI, Brave, Parallel, TinyFish, Search1API, Searchinfinity, Querit, Tavily, Firecrawl, Jina, SERPdive, Kagi, Ollama, AnySearch, Bright Data SERP, SerpBase, SearXNG, Exa, Perplexity, Gemini, and Kimi.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -1,6 +1,8 @@
1
1
  import { complete, type Api, type Message, type Model } from "@earendil-works/pi-ai/compat";
2
2
  import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
3
- import { loadEnabledModelPatterns, modelMatchesEnabledPatterns } from "./summary-model-scope.ts";
3
+ import { existsSync, readFileSync } from "node:fs";
4
+ import { findModelWithProviderRouting, loadEnabledModelPatterns, modelMatchesEnabledPatterns } from "./summary-model-scope.ts";
5
+ import { getWebSearchConfigPath } from "./utils.ts";
4
6
 
5
7
  const OUTPUT_TOKENS = 2_000;
6
8
  const INPUT_CONTEXT_FRACTION = 0.6;
@@ -16,7 +18,48 @@ export interface PageAnswer {
16
18
  truncated: boolean;
17
19
  }
18
20
 
19
- function parseModelSelector(value: string): { provider: string; id: string } {
21
+ interface AnswerModelSelector {
22
+ provider: string;
23
+ id: string;
24
+ }
25
+
26
+ function loadConfiguredAnswerModel(): AnswerModelSelector | undefined {
27
+ const configPath = getWebSearchConfigPath();
28
+ if (!existsSync(configPath)) return undefined;
29
+
30
+ let raw: unknown;
31
+ try {
32
+ raw = JSON.parse(readFileSync(configPath, "utf8"));
33
+ } catch (err) {
34
+ const message = err instanceof Error ? err.message : String(err);
35
+ throw new Error(`Failed to parse ${configPath}: ${message}`);
36
+ }
37
+
38
+ const root = raw && typeof raw === "object" && !Array.isArray(raw)
39
+ ? raw as Record<string, unknown>
40
+ : undefined;
41
+ const fetchConfig = root?.fetch;
42
+ if (!fetchConfig || typeof fetchConfig !== "object" || Array.isArray(fetchConfig)) return undefined;
43
+ const values = fetchConfig as Record<string, unknown>;
44
+ const hasProvider = Object.hasOwn(values, "answerProvider");
45
+ const hasModel = Object.hasOwn(values, "answerModel");
46
+ if (!hasProvider && !hasModel) return undefined;
47
+
48
+ const provider = values.answerProvider;
49
+ const model = values.answerModel;
50
+ if (hasProvider && (typeof provider !== "string" || provider.trim().length === 0)) {
51
+ throw new Error(`fetch.answerProvider in ${configPath} must be a non-empty string`);
52
+ }
53
+ if (hasModel && (typeof model !== "string" || model.trim().length === 0)) {
54
+ throw new Error(`fetch.answerModel in ${configPath} must be a non-empty string`);
55
+ }
56
+ if (!hasProvider || !hasModel) {
57
+ throw new Error(`fetch.answerProvider and fetch.answerModel must be configured together in ${configPath}`);
58
+ }
59
+ return { provider: (provider as string).trim(), id: (model as string).trim() };
60
+ }
61
+
62
+ function parseModelSelector(value: string): AnswerModelSelector {
20
63
  const separator = value.indexOf("/");
21
64
  if (separator <= 0 || separator === value.length - 1) {
22
65
  throw new Error(`Invalid answerModel: ${value}. Use provider/model-id.`);
@@ -24,14 +67,20 @@ function parseModelSelector(value: string): { provider: string; id: string } {
24
67
  return { provider: value.slice(0, separator), id: value.slice(separator + 1) };
25
68
  }
26
69
 
27
- function resolveModel(ctx: ExtensionContext, override?: string): Model<Api> {
28
- const model = override
70
+ function resolveModel(ctx: ExtensionContext, override?: string, configured?: AnswerModelSelector): Model<Api> {
71
+ const selector = override ? parseModelSelector(override) : configured;
72
+ const model = selector
29
73
  ? (() => {
30
- const selector = parseModelSelector(override);
31
- return ctx.modelRegistry.find(selector.provider, selector.id);
74
+ return findModelWithProviderRouting(ctx.modelRegistry, selector.provider, selector.id);
32
75
  })()
33
76
  : ctx.model;
34
- if (!model) throw new Error(override ? `Answer model not found: ${override}` : "No current model available for page answering");
77
+ if (!model) {
78
+ if (override) throw new Error(`Answer model not found: ${override}`);
79
+ if (configured) {
80
+ throw new Error(`Answer model not found: ${configured.provider}/${configured.id} (from fetch.answerProvider/fetch.answerModel in ${getWebSearchConfigPath()})`);
81
+ }
82
+ throw new Error("No current model available for page answering");
83
+ }
35
84
  if (!model.input.includes("text")) throw new Error(`Answer model does not support text input: ${model.provider}/${model.id}`);
36
85
  if (!modelMatchesEnabledPatterns(model, loadEnabledModelPatterns(ctx))) {
37
86
  throw new Error(`Answer model is not enabled: ${model.provider}/${model.id}`);
@@ -53,7 +102,11 @@ export async function answerFromPage(
53
102
  ctx: ExtensionContext,
54
103
  signal?: AbortSignal,
55
104
  ): Promise<PageAnswer> {
56
- const model = resolveModel(ctx, input.model);
105
+ // Resolve an explicit per-call model first. In particular, do not read a
106
+ // malformed or partial config when the caller has supplied an override.
107
+ const model = input.model
108
+ ? resolveModel(ctx, input.model)
109
+ : resolveModel(ctx, undefined, loadConfiguredAnswerModel());
57
110
  const auth = await ctx.modelRegistry.getApiKeyAndHeaders(model);
58
111
  if (!auth.ok || !auth.apiKey) throw new Error(`No API key available for answer model ${model.provider}/${model.id}`);
59
112
  const registry = ctx.modelRegistry as typeof ctx.modelRegistry & { complete?: typeof complete };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@herbertgao/pi-extensions",
3
- "version": "2026.8.14",
3
+ "version": "2026.8.15",
4
4
  "description": "Aggregate installer for HerbertGao-maintained Pi extensions.",
5
5
  "keywords": [
6
6
  "extensions",
@@ -37,17 +37,18 @@
37
37
  "dependencies": {
38
38
  "@ast-grep/cli": "^0.45.0",
39
39
  "@ast-grep/napi": "^0.45.0",
40
- "@czottmann/pi-automode": "1.14.0",
40
+ "@czottmann/pi-automode": "1.15.0",
41
41
  "@dietrichgebert/ponytail": "4.9.0",
42
- "@earendil-works/pi-coding-agent": "^0.84.2",
43
- "@earendil-works/pi-tui": "^0.84.2",
42
+ "@earendil-works/pi-coding-agent": "^0.84.4",
43
+ "@earendil-works/pi-tui": "^0.84.4",
44
44
  "@effect/platform-node": "4.0.0-beta.103",
45
45
  "@effect/platform-node-shared": "4.0.0-beta.103",
46
+ "@herbertgao/pi-bark": "0.1.0",
46
47
  "@herbertgao/pi-cc-extensions": "0.8.60",
47
48
  "@herbertgao/pi-subagents": "0.17.1",
48
49
  "@herbertgao/resume-from": "0.2.0",
49
- "@juicesharp/rpiv-ask-user-question": "2.7.1",
50
- "@juicesharp/rpiv-config": "^2.7.1",
50
+ "@juicesharp/rpiv-ask-user-question": "2.8.0",
51
+ "@juicesharp/rpiv-config": "^2.8.0",
51
52
  "@luxusai/pi-hindsight": "0.12.0",
52
53
  "@modelcontextprotocol/client": "2.0.0",
53
54
  "@modelcontextprotocol/core": "2.0.0",
@@ -55,20 +56,19 @@
55
56
  "@modelcontextprotocol/sdk": "^1.29.0",
56
57
  "@mozilla/readability": "^0.6.0",
57
58
  "@napi-rs/keyring": "^1.3.0",
58
- "@narumitw/pi-btw": "0.55.4",
59
+ "@narumitw/pi-btw": "0.56.0",
59
60
  "@narumitw/pi-tui-kit": "^0.59.0",
60
61
  "@noble/ed25519": "^3.1.0",
61
62
  "@pi-plugins/fast-mode": "0.1.10",
62
63
  "@shikijs/cli": "^4.0.2",
63
64
  "@sinclair/typebox": "^0.34.49",
64
65
  "@tifan/pi-copy-response": "0.2.6",
65
- "@tifan/pi-handoff": "2.0.1",
66
+ "@tifan/pi-handoff": "2.2.0",
66
67
  "@tifan/pi-inline-skills": "1.0.5",
67
68
  "@tifan/pi-mermaid-open": "0.2.0",
68
69
  "@tifan/pi-preferred-thinking": "1.0.1",
69
- "@tifan/pi-recap": "0.4.5",
70
- "@tifan/pi-rename": "0.5.1",
71
- "@tifan/pi-titlebar-spinner": "0.1.3",
70
+ "@tifan/pi-recap": "0.4.6",
71
+ "@tifan/pi-rename": "0.6.0",
72
72
  "@vectorize-io/hindsight-client": "^0.9.0",
73
73
  "ajv": "^8.17.1",
74
74
  "ajv-formats": "^3.0.1",
@@ -88,9 +88,9 @@
88
88
  "open": "^10.2.0",
89
89
  "p-limit": "^6.1.0",
90
90
  "pi-footer": "0.5.1",
91
- "pi-lens": "4.1.2",
92
- "pi-mcp-adapter": "2.30.0",
93
- "pi-web-access": "0.26.0",
91
+ "pi-lens": "4.1.3",
92
+ "pi-mcp-adapter": "2.31.0",
93
+ "pi-web-access": "0.27.0",
94
94
  "pidusage": "^4.0.1",
95
95
  "promise.try": "^2.0.1",
96
96
  "qrcode-terminal": "^0.12.0",
@@ -113,6 +113,7 @@
113
113
  "bundledDependencies": [
114
114
  "@czottmann/pi-automode",
115
115
  "@dietrichgebert/ponytail",
116
+ "@herbertgao/pi-bark",
116
117
  "@herbertgao/pi-cc-extensions",
117
118
  "@herbertgao/pi-subagents",
118
119
  "@herbertgao/resume-from",
@@ -127,7 +128,6 @@
127
128
  "@tifan/pi-preferred-thinking",
128
129
  "@tifan/pi-recap",
129
130
  "@tifan/pi-rename",
130
- "@tifan/pi-titlebar-spinner",
131
131
  "pi-footer",
132
132
  "pi-lens",
133
133
  "pi-mcp-adapter",
@@ -151,8 +151,8 @@
151
151
  "./node_modules/@tifan/pi-recap/src/index.ts",
152
152
  "./node_modules/@tifan/pi-rename/src/index.ts",
153
153
  "./node_modules/@herbertgao/pi-subagents/src/index.ts",
154
- "./node_modules/@tifan/pi-titlebar-spinner/src/index.ts",
155
154
  "./node_modules/@juicesharp/rpiv-ask-user-question/index.ts",
155
+ "./node_modules/@herbertgao/pi-bark/src/index.ts",
156
156
  "./node_modules/@luxusai/pi-hindsight/extensions/index.ts",
157
157
  "./node_modules/@narumitw/pi-btw/dist/index.ts",
158
158
  "./node_modules/@pi-plugins/fast-mode/dist/index.mjs",
@@ -1,24 +0,0 @@
1
- # @tifan/pi-titlebar-spinner
2
-
3
- Show a braille spinner in the pi titlebar while the agent runs.
4
-
5
- The spinner starts on `agent_start` and stops when the agent settles or the session shuts down. When it stops, the extension restores the default `π - [session - ]cwd` title.
6
-
7
- [add image: titlebar spinner while agent runs]
8
-
9
- - The titlebar while the spinner is active.
10
- - The restored titlebar after the agent finishes.
11
-
12
- ## Install
13
-
14
- ```bash
15
- pi install npm:@tifan/pi-titlebar-spinner
16
- ```
17
-
18
- ## Release notes
19
-
20
- See [CHANGELOG.md](CHANGELOG.md)
21
-
22
- ## License
23
-
24
- [MIT](LICENSE)
@@ -1,39 +0,0 @@
1
- {
2
- "name": "@tifan/pi-titlebar-spinner",
3
- "version": "0.1.3",
4
- "description": "Show a spinner in the pi titlebar while the agent runs.",
5
- "keywords": [
6
- "pi",
7
- "pi-extension",
8
- "pi-package",
9
- "spinner",
10
- "titlebar"
11
- ],
12
- "homepage": "https://github.com/tifandotme/pi-extensions/tree/master/packages/pi-titlebar-spinner#readme",
13
- "bugs": "https://github.com/tifandotme/pi-extensions/issues",
14
- "license": "MIT",
15
- "author": "Tifan Dwi Avianto <hi@tifan.me>",
16
- "repository": {
17
- "type": "git",
18
- "url": "git+https://github.com/tifandotme/pi-extensions.git",
19
- "directory": "packages/pi-titlebar-spinner"
20
- },
21
- "files": [
22
- "src/**/*.ts",
23
- "README.md",
24
- "LICENSE"
25
- ],
26
- "type": "module",
27
- "publishConfig": {
28
- "access": "public",
29
- "provenance": true
30
- },
31
- "engines": {
32
- "node": ">=20"
33
- },
34
- "pi": {
35
- "extensions": [
36
- "./src/index.ts"
37
- ]
38
- }
39
- }
@@ -1,49 +0,0 @@
1
- import path from "node:path"
2
- import type {
3
- ExtensionAPI,
4
- ExtensionContext,
5
- } from "@earendil-works/pi-coding-agent"
6
-
7
- const FRAMES = ["⡿", "⣟", "⣯", "⣷", "⣾", "⣽", "⣻", "⢿"]
8
- const INTERVAL_MS = 70
9
-
10
- function baseTitle(pi: ExtensionAPI, cwdPath: string): string {
11
- const cwd = path.basename(cwdPath)
12
- const session = pi.getSessionName()
13
- return session ? `π - ${session} - ${cwd}` : `π - ${cwd}`
14
- }
15
-
16
- export default function (pi: ExtensionAPI): void {
17
- let timer: ReturnType<typeof setInterval> | undefined
18
- let frameIndex = 0
19
-
20
- function stop(ctx: ExtensionContext): void {
21
- if (timer) {
22
- clearInterval(timer)
23
- timer = undefined
24
- }
25
- frameIndex = 0
26
- ctx.ui.setTitle(baseTitle(pi, ctx.cwd))
27
- }
28
-
29
- function start(ctx: ExtensionContext): void {
30
- stop(ctx)
31
- timer = setInterval(() => {
32
- const frame = FRAMES[frameIndex % FRAMES.length]
33
- ctx.ui.setTitle(`${frame} ${baseTitle(pi, ctx.cwd)}`)
34
- frameIndex++
35
- }, INTERVAL_MS)
36
- }
37
-
38
- pi.on("agent_start", async (_event, ctx) => {
39
- start(ctx)
40
- })
41
-
42
- pi.on("agent_settled", async (_event, ctx) => {
43
- stop(ctx)
44
- })
45
-
46
- pi.on("session_shutdown", async (_event, ctx) => {
47
- stop(ctx)
48
- })
49
- }