@bitefoundry/retrieviewer 0.0.0-stage → 0.0.1

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 (292) hide show
  1. package/ASSET-PROVENANCE.md +43 -0
  2. package/ASSETS-LICENSE.md +58 -0
  3. package/ATTRIBUTION.md +54 -0
  4. package/EMBEDDING.md +96 -0
  5. package/INSTALLING.md +107 -0
  6. package/LICENSE +16 -0
  7. package/LICENSE-CODE +21 -0
  8. package/PLUGIN.md +110 -0
  9. package/PUPPY-MEMORY.md +57 -0
  10. package/PUPPY-Q5-COMPARISON.md +73 -0
  11. package/README.md +61 -2
  12. package/RELEASING.md +61 -0
  13. package/THIRD-PARTY-NOTICES.md +17 -0
  14. package/TRADEMARKS.md +21 -0
  15. package/braille.mjs +45 -0
  16. package/examples/bridge.py +62 -0
  17. package/examples/embed.mjs +15 -0
  18. package/examples/live.mjs +40 -0
  19. package/examples/plugin.mjs +21 -0
  20. package/integrations/opencode/runtime.mjs +195 -0
  21. package/integrations/opencode/tui.tsx +59 -0
  22. package/locomotion-core.mjs +21 -0
  23. package/locomotion-puppy.animal +0 -0
  24. package/package.json +125 -4
  25. package/pet-q5-manifest.json +118 -0
  26. package/puppy-clean.mjs +51 -0
  27. package/puppy-companion-interactions-v3-q5.bin.gz +0 -0
  28. package/puppy-companion-interactions-v3.json +1 -0
  29. package/puppy-companion-life.mjs +182 -0
  30. package/puppy-companion-objects.mjs +30 -0
  31. package/puppy-companion-sprites.mjs +14 -0
  32. package/puppy-continuous.mjs +57 -0
  33. package/puppy-face.mjs +46 -0
  34. package/puppy-frame-predictive.mjs +147 -0
  35. package/puppy-frame-store.mjs +108 -0
  36. package/puppy-mouth-anchor.json +1 -0
  37. package/puppy-mouth-anchor.mjs +33 -0
  38. package/puppy-paper-actions-v9-q5.bin.gz +0 -0
  39. package/puppy-paper-actions-v9.json +1 -0
  40. package/puppy-paper-actions.mjs +48 -0
  41. package/puppy-paper-approach-q5.bin.gz +0 -0
  42. package/puppy-paper-approach.json +1 -0
  43. package/puppy-paper-area.mjs +15 -0
  44. package/puppy-paper-arrival-seat-q5.bin.gz +0 -0
  45. package/puppy-paper-arrival-seat.json +1 -0
  46. package/puppy-paper-behaviors-q5.bin.gz +0 -0
  47. package/puppy-paper-behaviors.json +1 -0
  48. package/puppy-paper-behaviors.mjs +8 -0
  49. package/puppy-paper-contact.mjs +103 -0
  50. package/puppy-paper-director.mjs +21 -0
  51. package/puppy-paper-paw-transport.mjs +76 -0
  52. package/puppy-paper-rear-gallop-q5.bin.gz +0 -0
  53. package/puppy-paper-rear-gallop.json +1 -0
  54. package/puppy-paper-seat-q5.bin.gz +0 -0
  55. package/puppy-paper-seat.json +14 -0
  56. package/puppy-paper-seat.mjs +13 -0
  57. package/puppy-paper-trick-timing.mjs +58 -0
  58. package/puppy-paper-world.mjs +108 -0
  59. package/puppy-paper.mjs +271 -0
  60. package/puppy-stable-meta.json +1606 -0
  61. package/puppy-stable.mjs +72 -0
  62. package/puppy-stride-meta.json +343 -0
  63. package/puppy-stride-tweens-q5.bin.gz +0 -0
  64. package/puppy-zoomies.mjs +27 -0
  65. package/puppy-zoomies30.mjs +12 -0
  66. package/retrieviewer-cli.mjs +81 -0
  67. package/retrieviewer-config.mjs +49 -0
  68. package/retrieviewer-install.mjs +157 -0
  69. package/retrieviewer-plugin.d.mts +44 -0
  70. package/retrieviewer-plugin.mjs +97 -0
  71. package/retrieviewer-stdio.mjs +38 -0
  72. package/retrieviewer.d.mts +85 -0
  73. package/retrieviewer.mjs +142 -0
  74. package/vendor/procedural-animals/LICENSE +21 -0
  75. package/vendor/procedural-animals/package.json +40 -0
  76. package/vendor/procedural-animals/src/animal.js +146 -0
  77. package/vendor/procedural-animals/src/bake.js +51 -0
  78. package/vendor/procedural-animals/src/build.js +81 -0
  79. package/vendor/procedural-animals/src/core/build/coatKit.js +121 -0
  80. package/vendor/procedural-animals/src/core/build/featherCards.js +189 -0
  81. package/vendor/procedural-animals/src/core/build/finEdges.js +85 -0
  82. package/vendor/procedural-animals/src/core/build/furSlope.js +115 -0
  83. package/vendor/procedural-animals/src/core/build/mesh.js +67 -0
  84. package/vendor/procedural-animals/src/core/build/pipeline.js +272 -0
  85. package/vendor/procedural-animals/src/core/build/seams.js +183 -0
  86. package/vendor/procedural-animals/src/core/build/warp.js +112 -0
  87. package/vendor/procedural-animals/src/core/build/weights.js +393 -0
  88. package/vendor/procedural-animals/src/core/math/vec.js +81 -0
  89. package/vendor/procedural-animals/src/core/motion/actions.js +800 -0
  90. package/vendor/procedural-animals/src/core/motion/bird.js +3063 -0
  91. package/vendor/procedural-animals/src/core/motion/birdActions.js +1059 -0
  92. package/vendor/procedural-animals/src/core/motion/common.js +158 -0
  93. package/vendor/procedural-animals/src/core/motion/index.js +22 -0
  94. package/vendor/procedural-animals/src/core/motion/quadruped.js +2875 -0
  95. package/vendor/procedural-animals/src/core/motion/snake.js +1594 -0
  96. package/vendor/procedural-animals/src/core/motion/spider.js +1810 -0
  97. package/vendor/procedural-animals/src/core/motion/swimmer.js +1047 -0
  98. package/vendor/procedural-animals/src/core/motion/swimmerActions.js +613 -0
  99. package/vendor/procedural-animals/src/core/render/animalObject.js +262 -0
  100. package/vendor/procedural-animals/src/core/render/coatMaterial.js +1301 -0
  101. package/vendor/procedural-animals/src/core/render/dqs.js +159 -0
  102. package/vendor/procedural-animals/src/core/render/eyes.js +676 -0
  103. package/vendor/procedural-animals/src/core/render/membranes.js +77 -0
  104. package/vendor/procedural-animals/src/core/render/skeleton.js +41 -0
  105. package/vendor/procedural-animals/src/core/rig/bird.js +327 -0
  106. package/vendor/procedural-animals/src/core/rig/quadruped.js +76 -0
  107. package/vendor/procedural-animals/src/core/rig/rig.js +79 -0
  108. package/vendor/procedural-animals/src/core/rig/snake.js +76 -0
  109. package/vendor/procedural-animals/src/core/rig/spider.js +110 -0
  110. package/vendor/procedural-animals/src/core/rig/swimmer.js +181 -0
  111. package/vendor/procedural-animals/src/core/sdf/eyeSocket.js +53 -0
  112. package/vendor/procedural-animals/src/core/sdf/mesher.js +238 -0
  113. package/vendor/procedural-animals/src/core/sdf/sdf.js +263 -0
  114. package/vendor/procedural-animals/src/index.d.ts +98 -0
  115. package/vendor/procedural-animals/src/index.js +29 -0
  116. package/vendor/procedural-animals/src/registry.js +48 -0
  117. package/vendor/procedural-animals/src/species/bear/behaviour.js +463 -0
  118. package/vendor/procedural-animals/src/species/bear/claws.js +147 -0
  119. package/vendor/procedural-animals/src/species/bear/coat.js +726 -0
  120. package/vendor/procedural-animals/src/species/bear/index.js +139 -0
  121. package/vendor/procedural-animals/src/species/bear/motion.js +112 -0
  122. package/vendor/procedural-animals/src/species/bear/regions.js +78 -0
  123. package/vendor/procedural-animals/src/species/bear/rig.js +112 -0
  124. package/vendor/procedural-animals/src/species/bear/sculpt.js +272 -0
  125. package/vendor/procedural-animals/src/species/boar/behaviour.js +335 -0
  126. package/vendor/procedural-animals/src/species/boar/coat.js +372 -0
  127. package/vendor/procedural-animals/src/species/boar/index.js +110 -0
  128. package/vendor/procedural-animals/src/species/boar/motion.js +85 -0
  129. package/vendor/procedural-animals/src/species/boar/regions.js +88 -0
  130. package/vendor/procedural-animals/src/species/boar/rig.js +111 -0
  131. package/vendor/procedural-animals/src/species/boar/sculpt.js +285 -0
  132. package/vendor/procedural-animals/src/species/cat/behaviour.js +559 -0
  133. package/vendor/procedural-animals/src/species/cat/coat.js +841 -0
  134. package/vendor/procedural-animals/src/species/cat/index.js +211 -0
  135. package/vendor/procedural-animals/src/species/cat/motion.js +101 -0
  136. package/vendor/procedural-animals/src/species/cat/regions.js +82 -0
  137. package/vendor/procedural-animals/src/species/cat/rig.js +100 -0
  138. package/vendor/procedural-animals/src/species/cat/sculpt.js +266 -0
  139. package/vendor/procedural-animals/src/species/cat/whiskers.js +110 -0
  140. package/vendor/procedural-animals/src/species/cheetah/coat.js +427 -0
  141. package/vendor/procedural-animals/src/species/cheetah/index.js +51 -0
  142. package/vendor/procedural-animals/src/species/cheetah/joints.js +3 -0
  143. package/vendor/procedural-animals/src/species/cheetah/motion.js +77 -0
  144. package/vendor/procedural-animals/src/species/cheetah/regions.js +95 -0
  145. package/vendor/procedural-animals/src/species/cheetah/rig.js +66 -0
  146. package/vendor/procedural-animals/src/species/cheetah/sculpt.js +158 -0
  147. package/vendor/procedural-animals/src/species/chicken/coat.js +313 -0
  148. package/vendor/procedural-animals/src/species/chicken/coverts.js +320 -0
  149. package/vendor/procedural-animals/src/species/chicken/index.js +110 -0
  150. package/vendor/procedural-animals/src/species/chicken/motion.js +177 -0
  151. package/vendor/procedural-animals/src/species/chicken/regions.js +66 -0
  152. package/vendor/procedural-animals/src/species/chicken/rig.js +144 -0
  153. package/vendor/procedural-animals/src/species/chicken/sculpt.js +389 -0
  154. package/vendor/procedural-animals/src/species/chicken/wingLook.js +66 -0
  155. package/vendor/procedural-animals/src/species/chicken/wingStack.js +124 -0
  156. package/vendor/procedural-animals/src/species/cow/behaviour.js +521 -0
  157. package/vendor/procedural-animals/src/species/cow/coat.js +465 -0
  158. package/vendor/procedural-animals/src/species/cow/index.js +118 -0
  159. package/vendor/procedural-animals/src/species/cow/motion.js +81 -0
  160. package/vendor/procedural-animals/src/species/cow/regions.js +98 -0
  161. package/vendor/procedural-animals/src/species/cow/rig.js +116 -0
  162. package/vendor/procedural-animals/src/species/cow/sculpt.js +339 -0
  163. package/vendor/procedural-animals/src/species/crow/coat.js +145 -0
  164. package/vendor/procedural-animals/src/species/crow/index.js +70 -0
  165. package/vendor/procedural-animals/src/species/crow/joints.js +3 -0
  166. package/vendor/procedural-animals/src/species/crow/motion.js +101 -0
  167. package/vendor/procedural-animals/src/species/crow/regions.js +50 -0
  168. package/vendor/procedural-animals/src/species/crow/rig.js +83 -0
  169. package/vendor/procedural-animals/src/species/crow/sculpt.js +118 -0
  170. package/vendor/procedural-animals/src/species/deer/behaviour.js +513 -0
  171. package/vendor/procedural-animals/src/species/deer/coat.js +391 -0
  172. package/vendor/procedural-animals/src/species/deer/index.js +106 -0
  173. package/vendor/procedural-animals/src/species/deer/motion.js +97 -0
  174. package/vendor/procedural-animals/src/species/deer/regions.js +60 -0
  175. package/vendor/procedural-animals/src/species/deer/rig.js +124 -0
  176. package/vendor/procedural-animals/src/species/deer/sculpt.js +297 -0
  177. package/vendor/procedural-animals/src/species/dog/behaviour.js +521 -0
  178. package/vendor/procedural-animals/src/species/dog/coat.js +495 -0
  179. package/vendor/procedural-animals/src/species/dog/index.js +144 -0
  180. package/vendor/procedural-animals/src/species/dog/motion.js +118 -0
  181. package/vendor/procedural-animals/src/species/dog/regions.js +61 -0
  182. package/vendor/procedural-animals/src/species/dog/rig.js +129 -0
  183. package/vendor/procedural-animals/src/species/dog/sculpt.js +246 -0
  184. package/vendor/procedural-animals/src/species/eagle/coat.js +250 -0
  185. package/vendor/procedural-animals/src/species/eagle/index.js +76 -0
  186. package/vendor/procedural-animals/src/species/eagle/joints.js +3 -0
  187. package/vendor/procedural-animals/src/species/eagle/motion.js +137 -0
  188. package/vendor/procedural-animals/src/species/eagle/regions.js +57 -0
  189. package/vendor/procedural-animals/src/species/eagle/rig.js +107 -0
  190. package/vendor/procedural-animals/src/species/eagle/sculpt.js +137 -0
  191. package/vendor/procedural-animals/src/species/fish/coat.js +271 -0
  192. package/vendor/procedural-animals/src/species/fish/index.js +88 -0
  193. package/vendor/procedural-animals/src/species/fish/motion.js +92 -0
  194. package/vendor/procedural-animals/src/species/fish/regions.js +27 -0
  195. package/vendor/procedural-animals/src/species/fish/rig.js +143 -0
  196. package/vendor/procedural-animals/src/species/fish/sculpt.js +214 -0
  197. package/vendor/procedural-animals/src/species/fish/variants.js +126 -0
  198. package/vendor/procedural-animals/src/species/fox/behaviour.js +314 -0
  199. package/vendor/procedural-animals/src/species/fox/coat.js +444 -0
  200. package/vendor/procedural-animals/src/species/fox/index.js +120 -0
  201. package/vendor/procedural-animals/src/species/fox/motion.js +81 -0
  202. package/vendor/procedural-animals/src/species/fox/regions.js +59 -0
  203. package/vendor/procedural-animals/src/species/fox/rig.js +91 -0
  204. package/vendor/procedural-animals/src/species/fox/sculpt.js +202 -0
  205. package/vendor/procedural-animals/src/species/frog/behaviour.js +590 -0
  206. package/vendor/procedural-animals/src/species/frog/coat.js +305 -0
  207. package/vendor/procedural-animals/src/species/frog/index.js +97 -0
  208. package/vendor/procedural-animals/src/species/frog/motion.js +69 -0
  209. package/vendor/procedural-animals/src/species/frog/regions.js +46 -0
  210. package/vendor/procedural-animals/src/species/frog/rig.js +156 -0
  211. package/vendor/procedural-animals/src/species/frog/sculpt.js +240 -0
  212. package/vendor/procedural-animals/src/species/goat/behaviour.js +509 -0
  213. package/vendor/procedural-animals/src/species/goat/coat.js +590 -0
  214. package/vendor/procedural-animals/src/species/goat/index.js +149 -0
  215. package/vendor/procedural-animals/src/species/goat/motion.js +95 -0
  216. package/vendor/procedural-animals/src/species/goat/regions.js +98 -0
  217. package/vendor/procedural-animals/src/species/goat/rig.js +181 -0
  218. package/vendor/procedural-animals/src/species/goat/sculpt.js +547 -0
  219. package/vendor/procedural-animals/src/species/horse/coat.js +361 -0
  220. package/vendor/procedural-animals/src/species/horse/coatSeam.js +122 -0
  221. package/vendor/procedural-animals/src/species/horse/index.js +80 -0
  222. package/vendor/procedural-animals/src/species/horse/motion.js +245 -0
  223. package/vendor/procedural-animals/src/species/horse/regions.js +60 -0
  224. package/vendor/procedural-animals/src/species/horse/rig.js +117 -0
  225. package/vendor/procedural-animals/src/species/horse/sculpt.js +316 -0
  226. package/vendor/procedural-animals/src/species/horse/seamWeights.js +272 -0
  227. package/vendor/procedural-animals/src/species/lion/behaviour.js +509 -0
  228. package/vendor/procedural-animals/src/species/lion/coat.js +1016 -0
  229. package/vendor/procedural-animals/src/species/lion/index.js +111 -0
  230. package/vendor/procedural-animals/src/species/lion/motion.js +87 -0
  231. package/vendor/procedural-animals/src/species/lion/regions.js +106 -0
  232. package/vendor/procedural-animals/src/species/lion/rig.js +110 -0
  233. package/vendor/procedural-animals/src/species/lion/sculpt.js +384 -0
  234. package/vendor/procedural-animals/src/species/lion/whiskers.js +132 -0
  235. package/vendor/procedural-animals/src/species/pig/behaviour.js +575 -0
  236. package/vendor/procedural-animals/src/species/pig/coat.js +730 -0
  237. package/vendor/procedural-animals/src/species/pig/ears.js +242 -0
  238. package/vendor/procedural-animals/src/species/pig/index.js +161 -0
  239. package/vendor/procedural-animals/src/species/pig/motion.js +105 -0
  240. package/vendor/procedural-animals/src/species/pig/regions.js +100 -0
  241. package/vendor/procedural-animals/src/species/pig/rig.js +152 -0
  242. package/vendor/procedural-animals/src/species/pig/sculpt.js +382 -0
  243. package/vendor/procedural-animals/src/species/rabbit/behaviour.js +605 -0
  244. package/vendor/procedural-animals/src/species/rabbit/coat.js +285 -0
  245. package/vendor/procedural-animals/src/species/rabbit/index.js +85 -0
  246. package/vendor/procedural-animals/src/species/rabbit/motion.js +85 -0
  247. package/vendor/procedural-animals/src/species/rabbit/regions.js +48 -0
  248. package/vendor/procedural-animals/src/species/rabbit/rig.js +99 -0
  249. package/vendor/procedural-animals/src/species/rabbit/sculpt.js +148 -0
  250. package/vendor/procedural-animals/src/species/rat/behaviour.js +539 -0
  251. package/vendor/procedural-animals/src/species/rat/coat.js +341 -0
  252. package/vendor/procedural-animals/src/species/rat/index.js +111 -0
  253. package/vendor/procedural-animals/src/species/rat/motion.js +90 -0
  254. package/vendor/procedural-animals/src/species/rat/regions.js +92 -0
  255. package/vendor/procedural-animals/src/species/rat/rig.js +112 -0
  256. package/vendor/procedural-animals/src/species/rat/sculpt.js +163 -0
  257. package/vendor/procedural-animals/src/species/rat/whiskers.js +119 -0
  258. package/vendor/procedural-animals/src/species/shark/coat.js +383 -0
  259. package/vendor/procedural-animals/src/species/shark/index.js +104 -0
  260. package/vendor/procedural-animals/src/species/shark/motion.js +69 -0
  261. package/vendor/procedural-animals/src/species/shark/regions.js +34 -0
  262. package/vendor/procedural-animals/src/species/shark/rig.js +194 -0
  263. package/vendor/procedural-animals/src/species/shark/sculpt.js +343 -0
  264. package/vendor/procedural-animals/src/species/shark/variants.js +105 -0
  265. package/vendor/procedural-animals/src/species/sheep/behaviour.js +522 -0
  266. package/vendor/procedural-animals/src/species/sheep/coat.js +529 -0
  267. package/vendor/procedural-animals/src/species/sheep/index.js +152 -0
  268. package/vendor/procedural-animals/src/species/sheep/motion.js +86 -0
  269. package/vendor/procedural-animals/src/species/sheep/neck.js +41 -0
  270. package/vendor/procedural-animals/src/species/sheep/regions.js +62 -0
  271. package/vendor/procedural-animals/src/species/sheep/rig.js +163 -0
  272. package/vendor/procedural-animals/src/species/sheep/sculpt.js +542 -0
  273. package/vendor/procedural-animals/src/species/snake/coat.js +315 -0
  274. package/vendor/procedural-animals/src/species/snake/index.js +73 -0
  275. package/vendor/procedural-animals/src/species/snake/motion.js +51 -0
  276. package/vendor/procedural-animals/src/species/snake/regions.js +51 -0
  277. package/vendor/procedural-animals/src/species/snake/rig.js +108 -0
  278. package/vendor/procedural-animals/src/species/snake/sculpt.js +122 -0
  279. package/vendor/procedural-animals/src/species/spider/coat.js +279 -0
  280. package/vendor/procedural-animals/src/species/spider/index.js +78 -0
  281. package/vendor/procedural-animals/src/species/spider/motion.js +72 -0
  282. package/vendor/procedural-animals/src/species/spider/regions.js +87 -0
  283. package/vendor/procedural-animals/src/species/spider/rig.js +118 -0
  284. package/vendor/procedural-animals/src/species/spider/sculpt.js +182 -0
  285. package/vendor/procedural-animals/src/species/wolf/behaviour.js +160 -0
  286. package/vendor/procedural-animals/src/species/wolf/coat.js +749 -0
  287. package/vendor/procedural-animals/src/species/wolf/index.js +107 -0
  288. package/vendor/procedural-animals/src/species/wolf/motion.js +80 -0
  289. package/vendor/procedural-animals/src/species/wolf/regions.js +57 -0
  290. package/vendor/procedural-animals/src/species/wolf/rig.js +87 -0
  291. package/vendor/procedural-animals/src/species/wolf/sculpt.js +339 -0
  292. package/vendor/procedural-animals/src/worker.js +29 -0
package/README.md CHANGED
@@ -1,3 +1,62 @@
1
- # Temporary Holding Version
1
+ # Retrieviewer
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ An autonomous, fluffy Braille retriever that lives in a pane of your terminal UI.
4
+ Give it a ball or a treat; otherwise it wanders and plays on its own. By BiteFoundry.
5
+
6
+ **Version 0.0.1.** Source code is MIT; puppy artwork is CC BY-SA 4.0, including
7
+ commercial use and adaptations under its attribution/ShareAlike conditions.
8
+ See [licensing](./LICENSE), [artwork terms](./ASSETS-LICENSE.md),
9
+ [credits](./ATTRIBUTION.md) and [brand notice](./TRADEMARKS.md).
10
+ The complete package is not MIT-only.
11
+
12
+ ## Install
13
+
14
+ Node.js >=20 and a terminal font supporting Unicode Braille are required.
15
+
16
+ ```sh
17
+ npm install -g @bitefoundry/retrieviewer@0.0.1
18
+ retrieviewer enable --agent opencode --dry-run
19
+ retrieviewer enable --agent opencode --yes
20
+ opencode
21
+ ```
22
+
23
+ Without `--agent` / `--yes`, interactive `retrieviewer enable` offers agent
24
+ selection and a preview before changing configuration. The native adapter is
25
+ currently verified only with OpenCode 2.1.2 on Windows. Codex, Claude Code, Cursor
26
+ Agent, Grok and Antigravity native panes are not implemented. MCP connectivity
27
+ alone does not create a pet inside their screens.
28
+
29
+ Inside OpenCode use `/retrieviewer ball`, `/retrieviewer treat`, or
30
+ `/retrieviewer disable`. See [installation and removal](./INSTALLING.md).
31
+
32
+ ## Embed in your own TUI
33
+
34
+ Install `@bitefoundry/retrieviewer` in your app, without `-g`, then use the host callbacks:
35
+
36
+ ```js
37
+ import {createPetPlugin} from '@bitefoundry/retrieviewer/plugin';
38
+
39
+ const puppy = await createPetPlugin({host: {
40
+ getSize: () => ({columns: pane.width, rows: pane.height}),
41
+ render: ({text}) => pane.setContent(text),
42
+ onError: error => logToYourApp(error),
43
+ }});
44
+ puppy.start();
45
+ // Optional interaction: puppy.interact('ball') / puppy.interact('treat')
46
+ // On unmount: puppy.dispose()
47
+ ```
48
+
49
+ `pane` and `logToYourApp` are supplied by your app. The pet does not take over
50
+ stdin, stdout or the screen. Q5 (32-level) artwork is bundled; no model API,
51
+ account or telemetry is needed by the runtime. npm dependencies require an
52
+ initial download or a populated cache. This is not a tiny icon: the complete
53
+ tarball is about 21 MB and a previous Windows sample used about 172 MB RSS.
54
+
55
+ - [Plugin lifecycle](./PLUGIN.md) — host callbacks, clocks and embedding.
56
+ - [Core and JSONL bridge](./EMBEDDING.md) — JS and non-JS TUI integration.
57
+ - [Release process](./RELEASING.md) — checks and support boundaries.
58
+ - [Third-party notices](./THIRD-PARTY-NOTICES.md).
59
+
60
+ The runtime has no install scripts and never edits agent settings automatically.
61
+ Only an explicit `retrieviewer enable` changes settings, after a preview/approval
62
+ or an explicit `--yes`. Unsupported hosts are refused.
package/RELEASING.md ADDED
@@ -0,0 +1,61 @@
1
+ # Retrieviewer 0.0.1 release process
2
+
3
+ Target: `@bitefoundry/retrieviewer@0.0.1`, public npm registry, `latest` tag.
4
+ The npm organization `bitefoundry` uses the free public-packages plan; account
5
+ `shinick` owns the organization.
6
+
7
+ ## Approval and licensing
8
+
9
+ The project owner explicitly authorized first publication on 2026-10-23 and
10
+ selected MIT code + CC BY-SA 4.0 artwork. The earlier unpublished restrictive
11
+ artwork draft is superseded. ATTRIBUTION.md retains the photographic references
12
+ and original license links. ASSET-PROVENANCE.md records the bounded review and
13
+ remaining limits; this is not comprehensive legal clearance.
14
+
15
+ `release-policy.json` has four independent approval gates and the CC artwork
16
+ license identifier. The guard still refuses false/missing approvals, private
17
+ packages, missing attribution/notices, install scripts and known leaked paths.
18
+ Passing tests does not itself provide a new publication authorization.
19
+
20
+ ## Checks before publication
21
+
22
+ ```sh
23
+ npm test
24
+ npm run test:package
25
+ npm run release:check
26
+ npm publish --dry-run --access public --registry=https://registry.npmjs.org/
27
+ ```
28
+
29
+ The package test packs the exact candidate, installs it in a fresh offline
30
+ consumer, byte-compares all installed files and checks exports, frames,
31
+ interactions, lifecycle, examples, JSONL and isolated installer settings.
32
+ Its report is `dist/package-check.json`; it does not publish or edit live
33
+ agent configuration. Keep the final tarball hash and file inventory.
34
+
35
+ The product workspace is not a Git repository. No public source repository,
36
+ Actions pipeline or npm trusted publisher is implied by this npm release.
37
+ Do not upload this experimental workspace wholesale: original photos, source
38
+ image sheets, workers, QA receipts, credentials and local settings stay local.
39
+
40
+ ## First publish
41
+
42
+ Confirm `npm whoami` is the intended account. Publish only the tested candidate
43
+ with explicit public access; complete any npm browser/2FA verification through
44
+ the user's own verification flow. Never put credentials in files or chat.
45
+ After publication, verify registry name/version/license/integrity and exercise
46
+ a fresh registry installation. A prepared or uploaded candidate is not the same
47
+ as a verified live package.
48
+
49
+ For later CI releases, consider GitHub Actions OIDC trusted publishing after an
50
+ audited source repository and exact workflow exist:
51
+ https://docs.npmjs.com/trusted-publishers/
52
+
53
+ ## Support boundaries
54
+
55
+ The default Q5 art and runtime are unchanged by this release. The OpenCode
56
+ 2.1.2 Windows directory-based adapter has local host proof; npm `./tui`
57
+ discovery alone is not live-host certification. Other agent-native renderers
58
+ remain unimplemented. MCP alone cannot inject a pet into arbitrary agent UIs.
59
+ See INSTALLING.md for support and removal.
60
+
61
+ Historical research notes remain local and are not current support promises.
@@ -0,0 +1,17 @@
1
+ # Third-party notices
2
+
3
+ - **procedural-animals 0.1.0** — MIT, Copyright (c) 2026 Majid Manzarpour and
4
+ procedural-animals contributors. Source is vendored under
5
+ `vendor/procedural-animals/src`; the full notice is retained at
6
+ `vendor/procedural-animals/LICENSE`.
7
+ Upstream: https://github.com/majidmanzarpour/threejs-procedural-animals
8
+ - **three 0.186.1** — MIT; installed as a pinned npm dependency. Its distribution
9
+ includes its own `LICENSE`. https://github.com/mrdoob/three.js
10
+ - **jsonc-parser 3.3.1** — MIT; installed as a pinned npm dependency. Its
11
+ distribution includes `LICENSE.md`. https://github.com/microsoft/node-jsonc-parser
12
+
13
+ The generated puppy artwork is separately CC BY-SA 4.0, not MIT. Its reference
14
+ photographers (Scott, CC BY 2.0; Stefan Bauer, CC BY-SA 2.5) and adaptation history
15
+ are credited in ATTRIBUTION.md. Original photographs are not bundled. See LICENSE,
16
+ LICENSE-CODE, ASSETS-LICENSE.md and the bounded ASSET-PROVENANCE.md review. No
17
+ comprehensive legal-clearance or exclusive AI-output ownership claim is made.
package/TRADEMARKS.md ADDED
@@ -0,0 +1,21 @@
1
+ # Names and origin
2
+
3
+ BiteFoundry and Retrieviewer are the project's brand and product names. No claim
4
+ is made here that either is a registered trademark or has passed legal clearance.
5
+ Owning a GitHub/npm namespace is not equivalent to obtaining trademark rights.
6
+
7
+ Neither the MIT code license nor the CC artwork license grants a general
8
+ right to market a different product as an official BiteFoundry/Retrieviewer
9
+ release. Accurate descriptive references and required attribution are allowed.
10
+ Clearly identify modified distributions and unaffiliated integrations; do not
11
+ falsely imply endorsement. Do not use the registered trademark symbol (R) unless
12
+ and until the relevant registration and scope justify it.
13
+
14
+ This notice does not limit lawful referential uses, statutory exceptions or
15
+ the MIT permissions for code or the CC permissions for artwork. It does not
16
+ require separate permission for lawful commercial reuse of the CC artwork, and
17
+ does not claim exclusive rights over generic
18
+ retriever imagery, dog behavior, the Braille rendering technique or ideas.
19
+
20
+ Domestic word-mark research is being conducted separately. No application,
21
+ registration, paid service engagement or fee payment is authorized by this file.
package/braille.mjs ADDED
@@ -0,0 +1,45 @@
1
+ // Unicode 2x4 cells, the same dot numbering used by AuraDeck's Braille canvas.
2
+ export const BRAILLE_BITS=[[1,8],[2,16],[4,32],[64,128]];
3
+ const BAYER=[[0,8,2,10],[12,4,14,6],[3,11,1,9],[15,7,13,5]];
4
+ const clamp=v=>Math.max(0,Math.min(1,v));
5
+ export function rgbaToBraille(rgba,width,height,{brightness=1,contrast=1,method='floyd'}={}){
6
+ if(!Number.isInteger(width)||!Number.isInteger(height)||width<1||height<1||width*height>1_000_000||rgba.length!==width*height*4)throw Error('Invalid RGBA image');
7
+ if(!Number.isFinite(brightness)||brightness<=0||brightness>4||!Number.isFinite(contrast)||contrast<=0||contrast>4||!['floyd','bayer','stable'].includes(method))throw Error('Invalid dithering options');
8
+ const values=new Float32Array(width*height),bits=new Uint8Array(width*height);
9
+ for(let i=0;i<values.length;i++){
10
+ const p=i*4,luma=(.2126*rgba[p]+.7152*rgba[p+1]+.0722*rgba[p+2])/255*(rgba[p+3]/255);
11
+ values[i]=luma===0?0:clamp((Math.pow(luma,.9)*brightness-.5)*contrast+.5);
12
+ }
13
+ if(method==='stable'){
14
+ // Fixed per-pixel threshold, not fresh random noise on every animation tick.
15
+ // Unlike the 4x4 Bayer matrix this avoids a repeating cross-hatch on fur.
16
+ for(let y=0;y<height;y++)for(let x=0;x<width;x++){
17
+ let n=Math.imul(x+1,0x1f123bb5)^Math.imul(y+1,0x5f356495);
18
+ n=Math.imul(n^(n>>>16),0x45d9f3b);n=Math.imul(n^(n>>>16),0x45d9f3b);n=(n^(n>>>16))>>>0;
19
+ bits[y*width+x]=values[y*width+x]>(n+.5)/4294967296?1:0;
20
+ }
21
+ }else if(method==='bayer'){
22
+ for(let y=0;y<height;y++)for(let x=0;x<width;x++)bits[y*width+x]=values[y*width+x]>(BAYER[y%4][x%4]+.5)/16?1:0;
23
+ }else{
24
+ const add=(x,y,error,weight)=>{if(x>=0&&x<width&&y<height)values[y*width+x]+=error*weight/16;};
25
+ for(let y=0;y<height;y++){
26
+ const direction=y%2?-1:1;
27
+ for(let k=0;k<width;k++){
28
+ const x=direction===1?k:width-1-k,i=y*width+x,old=values[i],value=old>=.5?1:0,error=old-value;
29
+ bits[i]=value;add(x+direction,y,error,7);add(x-direction,y+1,error,3);add(x,y+1,error,5);add(x+direction,y+1,error,1);
30
+ }
31
+ }
32
+ }
33
+ const columns=Math.ceil(width/2),rows=Math.ceil(height/4),lines=[];
34
+ for(let cy=0;cy<rows;cy++){
35
+ let line='';
36
+ for(let cx=0;cx<columns;cx++){
37
+ let mask=0;for(let dy=0;dy<4;dy++)for(let dx=0;dx<2;dx++){
38
+ const x=cx*2+dx,y=cy*4+dy;if(x<width&&y<height&&bits[y*width+x])mask|=BRAILLE_BITS[dy][dx];
39
+ }
40
+ line+=String.fromCodePoint(0x2800+mask);
41
+ }
42
+ lines.push(line);
43
+ }
44
+ return {text:lines.join('\n'),columns,rows,dots:bits.reduce((a,b)=>a+b,0)};
45
+ }
@@ -0,0 +1,62 @@
1
+ """Minimal non-JS host: one plain frame, no terminal/input ownership.
2
+
3
+ python examples/bridge.py [--bridge /installed/path/retrieviewer-stdio.mjs]
4
+ Use request(op='tick', dt=elapsed_seconds, columns=..., rows=...) in your TUI loop.
5
+ Node >=20 is required. Only Python's standard library is used.
6
+ """
7
+ import argparse
8
+ import json
9
+ from pathlib import Path
10
+ import subprocess
11
+ import sys
12
+
13
+
14
+ class PetBridge:
15
+ def __init__(self, bridge):
16
+ self.process = subprocess.Popen(
17
+ ["node", str(bridge)], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
18
+ text=True, encoding="utf-8", bufsize=1,
19
+ )
20
+ self.serial = 0
21
+ try:
22
+ ready = json.loads(self.process.stdout.readline())
23
+ if ready != {"event": "ready", "protocol": 1}:
24
+ raise RuntimeError("Unsupported pet protocol")
25
+ except BaseException:
26
+ self.close()
27
+ raise
28
+
29
+ def request(self, **request):
30
+ self.serial += 1
31
+ request["id"] = self.serial
32
+ self.process.stdin.write(json.dumps(request) + "\n")
33
+ self.process.stdin.flush()
34
+ response = json.loads(self.process.stdout.readline())
35
+ if response.get("id") != self.serial or not response.get("ok"):
36
+ raise RuntimeError(response.get("error", "Pet protocol mismatch"))
37
+ return response
38
+
39
+ def close(self):
40
+ if self.process.stdin and not self.process.stdin.closed:
41
+ self.process.stdin.close() # EOF releases pet even after host errors.
42
+ try:
43
+ self.process.wait(timeout=5)
44
+ except subprocess.TimeoutExpired:
45
+ self.process.terminate()
46
+ self.process.wait(timeout=5)
47
+ finally:
48
+ self.process.stdout.close()
49
+
50
+
51
+ if __name__ == "__main__":
52
+ parser = argparse.ArgumentParser(description=__doc__)
53
+ parser.add_argument("--bridge", type=Path,
54
+ default=Path(__file__).resolve().parents[1] / "retrieviewer-stdio.mjs")
55
+ args = parser.parse_args()
56
+ pet = PetBridge(args.bridge)
57
+ try:
58
+ frame = pet.request(op="tick", dt=1 / 30, columns=48, rows=14)
59
+ sys.stdout.reconfigure(encoding="utf-8")
60
+ sys.stdout.write(frame["text"] + "\n")
61
+ finally:
62
+ pet.close()
@@ -0,0 +1,15 @@
1
+ // Minimal embed: print one plain Braille frame and exit. No fullscreen, no ANSI.
2
+ // Run from anywhere: node examples/embed.mjs [columns] [rows]
3
+ import {createPet} from '../retrieviewer.mjs';
4
+
5
+ const columns=Number(process.argv[2]??48),rows=Number(process.argv[3]??14);
6
+ const pet=await createPet();
7
+ try{
8
+ // A real TUI calls step(elapsedSeconds) from ITS OWN loop (timer, render tick,
9
+ // input poll), then frame() with the current pane size. Here we fake 3 s of life.
10
+ for(let i=0;i<90;i++)pet.step(1/30);
11
+ const {text}=pet.frame({columns,rows});
12
+ process.stdout.write(text+'\n');
13
+ }finally{
14
+ pet.dispose(); // release caches/renderer when the pane closes
15
+ }
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env node
2
+ // Opt-in terminal host. The imported pet itself never touches terminal I/O.
3
+ import {createPet} from '../retrieviewer.mjs';
4
+ import {once} from 'node:events';
5
+
6
+ const pet=await createPet({artProfile:process.argv.includes('--original')?'original':'q5'}),out=process.stdout,input=process.stdin;
7
+ if(!out.isTTY||!input.isTTY){
8
+ try{out.write(pet.frame({columns:80,rows:24}).text+'\n');}finally{pet.dispose();}
9
+ }else{
10
+ let stopped=false,paused=false,last=performance.now(),previous=[],key='';
11
+ const wasRaw=!!input.isRaw;
12
+ const stop=()=>{stopped=true;};
13
+ const data=bytes=>{for(const c of bytes.toString()){
14
+ if(c==='q'||c==='\x03')stop();
15
+ if(c===' ')paused=!paused;
16
+ if(c==='b'||c==='t'){if(pet.interact(c==='b'?'ball':'treat'))paused=false;}
17
+ }};
18
+ process.on('SIGINT',stop);process.on('SIGTERM',stop);
19
+ input.setRawMode(true);input.resume();input.on('data',data);
20
+ out.write('\x1b[?1049h\x1b[?25l\x1b[2J');
21
+ try{
22
+ while(!stopped){
23
+ const start=performance.now();if(!paused)pet.step((start-last)/1000);last=start;
24
+ const columns=Math.max(1,(out.columns??80)-1),available=Math.max(1,(out.rows??24)-2);
25
+ const rows=Math.min(available,Math.max(1,Math.floor(125000/columns)));
26
+ const nextKey=`${columns}/${rows}`;
27
+ if(key!==nextKey){key=nextKey;previous=[];out.write('\x1b[2J');}
28
+ const frame=pet.frame({columns,rows}),lines=frame.text.split('\n');let batch='';
29
+ for(let y=0;y<rows;y++)if(lines[y]!==previous[y])batch+=`\x1b[${y+1};1H${lines[y]}`;
30
+ previous=lines;
31
+ const status=`${paused?'Paused':frame.report.intent} | B ball T treat Space pause Q quit`.slice(0,columns);
32
+ batch+=`\x1b[${rows+1};1H\x1b[2K${status}`;
33
+ if(!out.write(batch))await once(out,'drain');
34
+ await new Promise(resolve=>setTimeout(resolve,Math.max(1,1000/30-(performance.now()-start))));
35
+ }
36
+ }finally{
37
+ process.off('SIGINT',stop);process.off('SIGTERM',stop);input.off('data',data);
38
+ input.setRawMode(wasRaw);input.pause();out.write('\x1b[0m\x1b[?25h\x1b[?1049l');pet.dispose();
39
+ }
40
+ }
@@ -0,0 +1,21 @@
1
+ // Plugin embed with a host-owned loop: the host supplies the pane size, receives
2
+ // frames and drives time with tick(). No timers, raw mode or escape codes here.
3
+ // Run from an installed package: node examples/plugin.mjs [columns] [rows]
4
+ import {createPetPlugin} from '@bitefoundry/retrieviewer/plugin';
5
+
6
+ const columns=Number(process.argv[2]??48),rows=Number(process.argv[3]??14);
7
+ let latest='';
8
+ const plugin=await createPetPlugin({host:{
9
+ getSize:()=>({columns,rows}), // return null while the pane is hidden
10
+ render:frame=>{latest=frame.text;}, // copy what you keep; pixels are borrowed
11
+ clear:()=>{latest='';},
12
+ }});
13
+ try{
14
+ // A real TUI calls tick(elapsedSeconds) from ITS OWN render loop. Here: 3 s of life.
15
+ for(let i=0;i<90;i++)plugin.tick(1/30);
16
+ const lines=latest.split('\n');
17
+ if(lines.length!==rows||lines.some(l=>[...l].length!==columns||!/^[⠀-⣿]*$/u.test(l)))throw Error('frame is not exact plain Braille');
18
+ process.stdout.write(latest+'\n');
19
+ }finally{
20
+ plugin.dispose(); // stops the pet and calls clear() once
21
+ }
@@ -0,0 +1,195 @@
1
+ // Host-neutral controller behind the OpenCode TUI widget. One pet core is shared by
2
+ // every mounted slot view; the most recently attached view receives frames. Import
3
+ // and creation start no timer and load no art or settings: the first attach() starts
4
+ // a <=1 Hz settings poll, and only an enabled, visible view loads the pet. It never
5
+ // touches stdin/stdout, the alternate screen, processes, windows or the network.
6
+
7
+ export const AGENT='opencode';
8
+ export const DEFAULT_FPS=30;
9
+ export const POLL_MS=1000;
10
+ export const HEARTBEAT_MS=5000;
11
+ // Bounded so the host prompt stays usable: at most 64x12 cells, hidden when the
12
+ // terminal is too small for a legible pet.
13
+ export const LIMITS=Object.freeze({maxColumns:64,maxRows:12,minColumns:20,minRows:5,sideMargin:4,reservedRows:14});
14
+ export const COMMANDS=Object.freeze(['enable','disable','ball','treat','status']);
15
+
16
+ const fn=(value,name)=>{if(typeof value!=='function')throw TypeError(`${name} must be a function`);};
17
+ const defaultScheduler=()=>({now:()=>performance.now(),schedule:(callback,delayMs)=>setTimeout(callback,delayMs),cancel:handle=>clearTimeout(handle)});
18
+ const defaultLoadPlugin=async()=>(await import('../../retrieviewer-plugin.mjs')).createPetPlugin;
19
+ const defaultLoadSettings=()=>import('../../retrieviewer-config.mjs');
20
+
21
+ // Terminal viewport (cells) -> pet pane, or null when hidden/too small.
22
+ export function petViewport(viewport){
23
+ if(!viewport||typeof viewport!=='object')return null;
24
+ const {width,height}=viewport;
25
+ if(!Number.isFinite(width)||!Number.isFinite(height))return null;
26
+ const columns=Math.min(LIMITS.maxColumns,Math.floor(width)-LIMITS.sideMargin);
27
+ const rows=Math.min(LIMITS.maxRows,Math.floor((Math.floor(height)-LIMITS.reservedRows)/2));
28
+ return columns<LIMITS.minColumns||rows<LIMITS.minRows?null:{columns,rows};
29
+ }
30
+
31
+ export function createOpenCodePetController({hostVersion='unknown',scheduler,fps=DEFAULT_FPS,petOptions,loadPlugin=defaultLoadPlugin,loadSettings=defaultLoadSettings,onError}={}){
32
+ if(scheduler!==undefined){fn(scheduler?.now,'scheduler.now');fn(scheduler.schedule,'scheduler.schedule');fn(scheduler.cancel,'scheduler.cancel');}
33
+ fn(loadPlugin,'loadPlugin');fn(loadSettings,'loadSettings');
34
+ if(onError!==undefined)fn(onError,'onError');
35
+ const clock=scheduler??defaultScheduler();
36
+ const views=new Set();
37
+ let active=null,shown=null,enabled=false,disposed=false;
38
+ let plugin=null,loading=false,gen=0;
39
+ let settings=null,pollHandle=null,polling=false,epoch=0,hiddenCheck=null,pollError=null;
40
+ let receipt=null,writes=Promise.resolve();
41
+ const report=error=>{try{onError?.(error);}catch{}};
42
+ const settingsModule=()=>settings??=Promise.resolve().then(loadSettings);
43
+
44
+ // Receipts carry our own state only, serialized so phases never reorder.
45
+ function writeReceipt(phase){
46
+ if(!receipt)return;
47
+ const body={hostVersion:String(hostVersion),phase,firstFrameAt:receipt.firstFrameAt,columns:receipt.columns,rows:receipt.rows,frames:receipt.frames};
48
+ writes=writes.then(async()=>{const m=await settingsModule();await m.writeRuntimeReceipt(AGENT,body);}).catch(report);
49
+ }
50
+ function show(view,text){if(!view)return;view.render(text);shown=text?view:(shown===view?null:shown);}
51
+ function clearShown(){if(shown){const v=shown;shown=null;v.render('');}}
52
+
53
+ const host={
54
+ getSize(){
55
+ const box=active?petViewport(active.getViewport()):null;
56
+ if(!box&&!hiddenCheck)hiddenCheck=clock.schedule(()=>{hiddenCheck=null;reconcile();},0);
57
+ return box;
58
+ },
59
+ render(frame){
60
+ if(!active)return;
61
+ if(shown&&shown!==active)clearShown();
62
+ show(active,frame.text);
63
+ const now=clock.now();
64
+ if(!receipt){receipt={firstFrameAt:new Date().toISOString(),frames:1,beat:now,columns:frame.columns,rows:frame.rows};writeReceipt('rendering');return;}
65
+ receipt.frames++;receipt.columns=frame.columns;receipt.rows=frame.rows;
66
+ if(now-receipt.beat>=HEARTBEAT_MS){receipt.beat=now;writeReceipt('rendering');}
67
+ },
68
+ clear:clearShown,
69
+ onError(error){stopPet();report(error);},
70
+ };
71
+
72
+ function stopPet(){
73
+ gen++;loading=false;
74
+ if(hiddenCheck){clock.cancel(hiddenCheck);hiddenCheck=null;}
75
+ const p=plugin;plugin=null;
76
+ try{p?.dispose();}catch(error){report(error);}
77
+ clearShown();
78
+ if(receipt){writeReceipt('stopped');receipt=null;}
79
+ }
80
+ const wanted=()=>!disposed&&enabled&&views.size>0;
81
+
82
+ async function load(){
83
+ const mine=++gen;loading=true;
84
+ try{
85
+ const create=await loadPlugin();
86
+ if(mine!==gen||!wanted())return;
87
+ const p=await create({host,fps,petOptions,scheduler:clock});
88
+ // Disabled, unmounted or reloaded while the art loaded: release it unseen.
89
+ if(mine!==gen||!wanted()){p.dispose();return;}
90
+ plugin=p;
91
+ }catch(error){if(mine===gen)report(error);return;}
92
+ finally{if(mine===gen)loading=false;}
93
+ reconcile();
94
+ }
95
+
96
+ function reconcile(redraw=false){
97
+ if(!wanted()){if(plugin||loading||receipt)stopPet();return;}
98
+ if(!plugin){if(!loading)load();return;}
99
+ const box=active?petViewport(active.getViewport()):null;
100
+ // Hidden: no timer and no simulated time until a refresh reveals the pane.
101
+ if(!box){plugin.pause();clearShown();return;}
102
+ try{if(!plugin.running)plugin.start();else if(redraw)plugin.render();}
103
+ catch(error){stopPet();report(error);}
104
+ }
105
+
106
+ function schedulePoll(){
107
+ if(disposed||views.size===0||pollHandle!==null)return;
108
+ pollHandle=clock.schedule(()=>{pollHandle=null;poll();},POLL_MS);
109
+ }
110
+ async function poll(){
111
+ if(disposed||views.size===0||polling)return;
112
+ polling=true;const seen=epoch;
113
+ try{
114
+ const m=await settingsModule();
115
+ const s=await m.readAgentSettings(AGENT);
116
+ pollError=null;
117
+ if(seen===epoch&&!disposed){enabled=s?.enabled===true;reconcile();}
118
+ }catch(error){
119
+ // A persistent failure (e.g. missing settings module) is reported once, not per poll.
120
+ const key=String(error?.message??error);
121
+ if(key!==pollError){pollError=key;report(error);}
122
+ }
123
+ finally{polling=false;schedulePoll();}
124
+ }
125
+ function stopPolling(){if(pollHandle!==null){clock.cancel(pollHandle);pollHandle=null;}}
126
+
127
+ async function setEnabled(value){
128
+ if(typeof value!=='boolean')throw TypeError('enabled must be boolean');
129
+ const m=await settingsModule();
130
+ await m.setAgentEnabled(AGENT,value);
131
+ epoch++;
132
+ if(disposed)return;
133
+ enabled=value;reconcile();
134
+ }
135
+ function interact(kind){
136
+ if(kind!=='ball'&&kind!=='treat')throw TypeError("kind must be 'ball' or 'treat'");
137
+ return plugin?plugin.interact(kind):false;
138
+ }
139
+ function status(){
140
+ return {enabled,mounted:views.size,loading,running:!!plugin?.running,frames:receipt?.frames??0,columns:receipt?.columns??null,rows:receipt?.rows??null,firstFrameAt:receipt?.firstFrameAt??null};
141
+ }
142
+
143
+ return {
144
+ // view: {getViewport(): {width,height}|null, render(text: string): void}. Text is
145
+ // plain Braille (rows lines); '' means show nothing.
146
+ attach(view){
147
+ if(disposed)throw Error('OpenCode pet controller is disposed');
148
+ if(!view||typeof view!=='object')throw TypeError('view must be an object');
149
+ fn(view.getViewport,'view.getViewport');fn(view.render,'view.render');
150
+ const record={getViewport:view.getViewport,render:view.render};
151
+ views.add(record);active=record;
152
+ if(views.size===1)poll();
153
+ reconcile(true);
154
+ let attached=true;
155
+ return {
156
+ // Call on resize or visibility change; re-derives the pane from the viewport.
157
+ refresh(){if(attached&&!disposed)reconcile(true);},
158
+ detach(){
159
+ if(!attached)return;attached=false;
160
+ views.delete(record);
161
+ if(shown===record)clearShown();
162
+ if(active===record)active=[...views].at(-1)??null;
163
+ if(views.size===0)stopPolling();
164
+ if(!disposed)reconcile(true);
165
+ },
166
+ };
167
+ },
168
+ setEnabled,
169
+ interact,
170
+ status,
171
+ // Slash handler: '/retrieviewer <enable|disable|ball|treat|status>'.
172
+ async command(argument){
173
+ const word=String(argument??'').trim().toLowerCase()||'status';
174
+ if(word==='enable'||word==='disable'){await setEnabled(word==='enable');return word==='enable'?'Retrieviewer enabled':'Retrieviewer disabled';}
175
+ if(word==='ball'||word==='treat'){
176
+ if(!plugin)return enabled?'Retrieviewer is still loading':'Retrieviewer is disabled; run /retrieviewer enable';
177
+ return interact(word)?`Retrieviewer: ${word} accepted`:`Retrieviewer is busy; ${word} not accepted`;
178
+ }
179
+ if(word==='status'){
180
+ const s=status();
181
+ return `Retrieviewer ${s.enabled?'enabled':'disabled'}, ${s.running?`rendering ${s.columns}x${s.rows}, ${s.frames} frames`:s.loading?'loading':'idle'}, ${s.mounted} view(s)`;
182
+ }
183
+ return `Usage: /retrieviewer ${COMMANDS.join('|')}`;
184
+ },
185
+ // Resolves once queued receipt writes have finished (for clean shutdown/tests).
186
+ settled(){return writes;},
187
+ get disposed(){return disposed;},
188
+ dispose(){
189
+ if(disposed)return writes;
190
+ disposed=true;stopPolling();stopPet();
191
+ views.clear();active=null;
192
+ return writes;
193
+ },
194
+ };
195
+ }
@@ -0,0 +1,59 @@
1
+ /** @jsxImportSource @opentui/solid */
2
+ // OpenCode v2 TUI plugin: the Retrieviewer pet drawn inside OpenCode's own UI (home
3
+ // footer and session composer slots). The host resolves @opencode/plugin/tui,
4
+ // @opentui/solid and solid-js and compiles this file; no production dependency here.
5
+ // All lifecycle lives in ./runtime.mjs, which loads nothing until a view mounts.
6
+ import {Plugin} from '@opencode/plugin/tui';
7
+ import {useTerminalDimensions} from '@opentui/solid';
8
+ import {createEffect, createSignal, onCleanup, Show} from 'solid-js';
9
+ import {createOpenCodePetController} from './runtime.mjs';
10
+
11
+ export default Plugin.define({
12
+ id:'retrieviewer.pet',
13
+ setup(context){
14
+ const toast=(message,variant='info')=>context.ui.toast.show({title:'Retrieviewer',message,variant});
15
+ const pet=createOpenCodePetController({
16
+ hostVersion:context.app?.version??'unknown',
17
+ onError:error=>toast(String(error?.message??error),'error'),
18
+ });
19
+
20
+ // One view per mounted slot; all share the single pet core in the controller.
21
+ function PetView(){
22
+ const dimensions=useTerminalDimensions();
23
+ const [text,setText]=createSignal('');
24
+ const view=pet.attach({
25
+ getViewport:()=>{const {width,height}=dimensions();return {width,height};},
26
+ render:value=>setText(value),
27
+ });
28
+ createEffect(()=>{dimensions();view.refresh();});
29
+ onCleanup(()=>view.detach());
30
+ return (
31
+ <Show when={text()}>
32
+ <box flexShrink={0} paddingLeft={2}>
33
+ <text wrapMode="none" fg={context.theme.text.base}>{text()}</text>
34
+ </box>
35
+ </Show>
36
+ );
37
+ }
38
+
39
+ context.ui.slot({append:'home.footer',render:()=><PetView/>});
40
+ context.ui.slot({append:'session.composer.top',render:()=><PetView/>});
41
+ context.keymap.layer(()=>({
42
+ mode:'global',
43
+ commands:[{
44
+ id:'retrieviewer.pet',
45
+ title:'Retrieviewer pet',
46
+ description:'enable | disable | ball | treat | status',
47
+ group:'Retrieviewer',
48
+ palette:true,
49
+ slash:{name:'retrieviewer',arguments:true},
50
+ async run(argument){
51
+ try{toast(await pet.command(argument));}
52
+ catch(error){toast(String(error?.message??error),'error');}
53
+ },
54
+ }],
55
+ }));
56
+
57
+ return ()=>pet.dispose();
58
+ },
59
+ });
@@ -0,0 +1,21 @@
1
+ import * as THREE from 'three';
2
+ import {createAnimal} from './vendor/procedural-animals/src/index.js';
3
+ export const LAB_OPTIONS={variant:'retriever',age:'juvenile',seed:2,quality:'low',worker:false,overrides:{colour:'yellow',stanceSoftMargin:.005}};
4
+ export async function makeLabAnimal(extra={}){
5
+ return createAnimal('dog',{...LAB_OPTIONS,...extra,overrides:{...LAB_OPTIONS.overrides,...extra.overrides}});
6
+ }
7
+ // Never translate the mesh externally. The gait engine owns root motion + IK.
8
+ export function makeLabController(animal){
9
+ let target=null,speed=animal.gears.walk;
10
+ return {get target(){return target;},get speed(){return speed;},setSpeed(kind){
11
+ if(!['walk','trot','gallop'].includes(kind))throw Error('Unknown gait');
12
+ speed=animal.gears[kind];if(target)animal.moveTo(target,{speed});
13
+ },go(x,z){
14
+ if(![x,z].every(Number.isFinite))throw Error('Invalid ground target');
15
+ target=new THREE.Vector3(Math.max(-.8,Math.min(.8,x)),0,Math.max(-1.7,Math.min(.3,z)));
16
+ animal.stopAction();animal.moveTo(target,{speed});
17
+ },stop(){target=null;animal.stop();},update(dt){
18
+ if(!Number.isFinite(dt)||dt<0||dt>.1)throw Error('Invalid lab step');animal.update(dt);
19
+ if(target&&animal.position.distanceTo(target)<.025&&animal.speed<.03){target=null;animal.stop();}
20
+ }};
21
+ }
Binary file