picopilot 0.0.0 → 0.1.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 (334) hide show
  1. package/LICENSE +661 -0
  2. package/dist/bin.d.ts +3 -0
  3. package/dist/bin.d.ts.map +1 -0
  4. package/dist/bin.js +4 -0
  5. package/dist/bin.js.map +1 -0
  6. package/dist/cli.d.ts +15 -0
  7. package/dist/cli.d.ts.map +1 -0
  8. package/dist/cli.js +59 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/commands/audio.d.ts +17 -0
  11. package/dist/commands/audio.d.ts.map +1 -0
  12. package/dist/commands/audio.js +315 -0
  13. package/dist/commands/audio.js.map +1 -0
  14. package/dist/commands/export.d.ts +36 -0
  15. package/dist/commands/export.d.ts.map +1 -0
  16. package/dist/commands/export.js +257 -0
  17. package/dist/commands/export.js.map +1 -0
  18. package/dist/commands/gfx.d.ts +28 -0
  19. package/dist/commands/gfx.d.ts.map +1 -0
  20. package/dist/commands/gfx.js +521 -0
  21. package/dist/commands/gfx.js.map +1 -0
  22. package/dist/commands/init.d.ts +34 -0
  23. package/dist/commands/init.d.ts.map +1 -0
  24. package/dist/commands/init.js +151 -0
  25. package/dist/commands/init.js.map +1 -0
  26. package/dist/commands/lint.d.ts +20 -0
  27. package/dist/commands/lint.d.ts.map +1 -0
  28. package/dist/commands/lint.js +90 -0
  29. package/dist/commands/lint.js.map +1 -0
  30. package/dist/commands/minify.d.ts +20 -0
  31. package/dist/commands/minify.d.ts.map +1 -0
  32. package/dist/commands/minify.js +129 -0
  33. package/dist/commands/minify.js.map +1 -0
  34. package/dist/commands/music.d.ts +13 -0
  35. package/dist/commands/music.d.ts.map +1 -0
  36. package/dist/commands/music.js +241 -0
  37. package/dist/commands/music.js.map +1 -0
  38. package/dist/commands/playtest.d.ts +29 -0
  39. package/dist/commands/playtest.d.ts.map +1 -0
  40. package/dist/commands/playtest.js +631 -0
  41. package/dist/commands/playtest.js.map +1 -0
  42. package/dist/commands/run.d.ts +30 -0
  43. package/dist/commands/run.d.ts.map +1 -0
  44. package/dist/commands/run.js +224 -0
  45. package/dist/commands/run.js.map +1 -0
  46. package/dist/commands/serve.d.ts +50 -0
  47. package/dist/commands/serve.d.ts.map +1 -0
  48. package/dist/commands/serve.js +213 -0
  49. package/dist/commands/serve.js.map +1 -0
  50. package/dist/commands/sfx.d.ts +13 -0
  51. package/dist/commands/sfx.d.ts.map +1 -0
  52. package/dist/commands/sfx.js +254 -0
  53. package/dist/commands/sfx.js.map +1 -0
  54. package/dist/commands/tokens.d.ts +36 -0
  55. package/dist/commands/tokens.d.ts.map +1 -0
  56. package/dist/commands/tokens.js +129 -0
  57. package/dist/commands/tokens.js.map +1 -0
  58. package/dist/commands/verify.d.ts +57 -0
  59. package/dist/commands/verify.d.ts.map +1 -0
  60. package/dist/commands/verify.js +213 -0
  61. package/dist/commands/verify.js.map +1 -0
  62. package/dist/commands/version.d.ts +12 -0
  63. package/dist/commands/version.d.ts.map +1 -0
  64. package/dist/commands/version.js +24 -0
  65. package/dist/commands/version.js.map +1 -0
  66. package/dist/engine/audio/index.d.ts +14 -0
  67. package/dist/engine/audio/index.d.ts.map +1 -0
  68. package/dist/engine/audio/index.js +14 -0
  69. package/dist/engine/audio/index.js.map +1 -0
  70. package/dist/engine/audio/music.d.ts +116 -0
  71. package/dist/engine/audio/music.d.ts.map +1 -0
  72. package/dist/engine/audio/music.js +138 -0
  73. package/dist/engine/audio/music.js.map +1 -0
  74. package/dist/engine/audio/section.d.ts +34 -0
  75. package/dist/engine/audio/section.d.ts.map +1 -0
  76. package/dist/engine/audio/section.js +67 -0
  77. package/dist/engine/audio/section.js.map +1 -0
  78. package/dist/engine/audio/sfx.d.ts +141 -0
  79. package/dist/engine/audio/sfx.d.ts.map +1 -0
  80. package/dist/engine/audio/sfx.js +506 -0
  81. package/dist/engine/audio/sfx.js.map +1 -0
  82. package/dist/engine/cart/cart.d.ts +75 -0
  83. package/dist/engine/cart/cart.d.ts.map +1 -0
  84. package/dist/engine/cart/cart.js +153 -0
  85. package/dist/engine/cart/cart.js.map +1 -0
  86. package/dist/engine/cart/errors.d.ts +29 -0
  87. package/dist/engine/cart/errors.d.ts.map +1 -0
  88. package/dist/engine/cart/errors.js +26 -0
  89. package/dist/engine/cart/errors.js.map +1 -0
  90. package/dist/engine/cart/gfx.d.ts +79 -0
  91. package/dist/engine/cart/gfx.d.ts.map +1 -0
  92. package/dist/engine/cart/gfx.js +171 -0
  93. package/dist/engine/cart/gfx.js.map +1 -0
  94. package/dist/engine/cart/index.d.ts +15 -0
  95. package/dist/engine/cart/index.d.ts.map +1 -0
  96. package/dist/engine/cart/index.js +15 -0
  97. package/dist/engine/cart/index.js.map +1 -0
  98. package/dist/engine/cart/map.d.ts +46 -0
  99. package/dist/engine/cart/map.d.ts.map +1 -0
  100. package/dist/engine/cart/map.js +118 -0
  101. package/dist/engine/cart/map.js.map +1 -0
  102. package/dist/engine/config/config.d.ts +74 -0
  103. package/dist/engine/config/config.d.ts.map +1 -0
  104. package/dist/engine/config/config.js +103 -0
  105. package/dist/engine/config/config.js.map +1 -0
  106. package/dist/engine/config/index.d.ts +7 -0
  107. package/dist/engine/config/index.d.ts.map +1 -0
  108. package/dist/engine/config/index.js +7 -0
  109. package/dist/engine/config/index.js.map +1 -0
  110. package/dist/engine/gfx/grid.d.ts +51 -0
  111. package/dist/engine/gfx/grid.d.ts.map +1 -0
  112. package/dist/engine/gfx/grid.js +116 -0
  113. package/dist/engine/gfx/grid.js.map +1 -0
  114. package/dist/engine/gfx/index.d.ts +17 -0
  115. package/dist/engine/gfx/index.d.ts.map +1 -0
  116. package/dist/engine/gfx/index.js +17 -0
  117. package/dist/engine/gfx/index.js.map +1 -0
  118. package/dist/engine/gfx/label.d.ts +34 -0
  119. package/dist/engine/gfx/label.d.ts.map +1 -0
  120. package/dist/engine/gfx/label.js +190 -0
  121. package/dist/engine/gfx/label.js.map +1 -0
  122. package/dist/engine/gfx/overlap.d.ts +42 -0
  123. package/dist/engine/gfx/overlap.d.ts.map +1 -0
  124. package/dist/engine/gfx/overlap.js +57 -0
  125. package/dist/engine/gfx/overlap.js.map +1 -0
  126. package/dist/engine/gfx/palette.d.ts +29 -0
  127. package/dist/engine/gfx/palette.d.ts.map +1 -0
  128. package/dist/engine/gfx/palette.js +46 -0
  129. package/dist/engine/gfx/palette.js.map +1 -0
  130. package/dist/engine/gfx/png.d.ts +31 -0
  131. package/dist/engine/gfx/png.d.ts.map +1 -0
  132. package/dist/engine/gfx/png.js +165 -0
  133. package/dist/engine/gfx/png.js.map +1 -0
  134. package/dist/engine/gfx/render.d.ts +42 -0
  135. package/dist/engine/gfx/render.d.ts.map +1 -0
  136. package/dist/engine/gfx/render.js +72 -0
  137. package/dist/engine/gfx/render.js.map +1 -0
  138. package/dist/engine/index.d.ts +22 -0
  139. package/dist/engine/index.d.ts.map +1 -0
  140. package/dist/engine/index.js +22 -0
  141. package/dist/engine/index.js.map +1 -0
  142. package/dist/engine/pico8/adapter.d.ts +267 -0
  143. package/dist/engine/pico8/adapter.d.ts.map +1 -0
  144. package/dist/engine/pico8/adapter.js +37 -0
  145. package/dist/engine/pico8/adapter.js.map +1 -0
  146. package/dist/engine/pico8/drive.d.ts +308 -0
  147. package/dist/engine/pico8/drive.d.ts.map +1 -0
  148. package/dist/engine/pico8/drive.js +581 -0
  149. package/dist/engine/pico8/drive.js.map +1 -0
  150. package/dist/engine/pico8/harness.d.ts +146 -0
  151. package/dist/engine/pico8/harness.d.ts.map +1 -0
  152. package/dist/engine/pico8/harness.js +251 -0
  153. package/dist/engine/pico8/harness.js.map +1 -0
  154. package/dist/engine/pico8/index.d.ts +21 -0
  155. package/dist/engine/pico8/index.d.ts.map +1 -0
  156. package/dist/engine/pico8/index.js +21 -0
  157. package/dist/engine/pico8/index.js.map +1 -0
  158. package/dist/engine/pico8/sentinel.d.ts +63 -0
  159. package/dist/engine/pico8/sentinel.d.ts.map +1 -0
  160. package/dist/engine/pico8/sentinel.js +86 -0
  161. package/dist/engine/pico8/sentinel.js.map +1 -0
  162. package/dist/engine/pico8/session-daemon-entry.d.ts +10 -0
  163. package/dist/engine/pico8/session-daemon-entry.d.ts.map +1 -0
  164. package/dist/engine/pico8/session-daemon-entry.js +11 -0
  165. package/dist/engine/pico8/session-daemon-entry.js.map +1 -0
  166. package/dist/engine/pico8/session-daemon-main.d.ts +44 -0
  167. package/dist/engine/pico8/session-daemon-main.d.ts.map +1 -0
  168. package/dist/engine/pico8/session-daemon-main.js +71 -0
  169. package/dist/engine/pico8/session-daemon-main.js.map +1 -0
  170. package/dist/engine/pico8/session-daemon.d.ts +68 -0
  171. package/dist/engine/pico8/session-daemon.d.ts.map +1 -0
  172. package/dist/engine/pico8/session-daemon.js +264 -0
  173. package/dist/engine/pico8/session-daemon.js.map +1 -0
  174. package/dist/engine/pico8/session.d.ts +140 -0
  175. package/dist/engine/pico8/session.d.ts.map +1 -0
  176. package/dist/engine/pico8/session.js +250 -0
  177. package/dist/engine/pico8/session.js.map +1 -0
  178. package/dist/engine/pico8/shell.d.ts +103 -0
  179. package/dist/engine/pico8/shell.d.ts.map +1 -0
  180. package/dist/engine/pico8/shell.js +324 -0
  181. package/dist/engine/pico8/shell.js.map +1 -0
  182. package/dist/engine/pico8/supervisor.d.ts +137 -0
  183. package/dist/engine/pico8/supervisor.d.ts.map +1 -0
  184. package/dist/engine/pico8/supervisor.js +140 -0
  185. package/dist/engine/pico8/supervisor.js.map +1 -0
  186. package/dist/engine/scaffold/index.d.ts +8 -0
  187. package/dist/engine/scaffold/index.d.ts.map +1 -0
  188. package/dist/engine/scaffold/index.js +8 -0
  189. package/dist/engine/scaffold/index.js.map +1 -0
  190. package/dist/engine/scaffold/scaffold.d.ts +49 -0
  191. package/dist/engine/scaffold/scaffold.d.ts.map +1 -0
  192. package/dist/engine/scaffold/scaffold.js +259 -0
  193. package/dist/engine/scaffold/scaffold.js.map +1 -0
  194. package/dist/engine/shrinko/adapter.d.ts +167 -0
  195. package/dist/engine/shrinko/adapter.d.ts.map +1 -0
  196. package/dist/engine/shrinko/adapter.js +107 -0
  197. package/dist/engine/shrinko/adapter.js.map +1 -0
  198. package/dist/engine/shrinko/index.d.ts +13 -0
  199. package/dist/engine/shrinko/index.d.ts.map +1 -0
  200. package/dist/engine/shrinko/index.js +13 -0
  201. package/dist/engine/shrinko/index.js.map +1 -0
  202. package/dist/engine/shrinko/shell.d.ts +69 -0
  203. package/dist/engine/shrinko/shell.d.ts.map +1 -0
  204. package/dist/engine/shrinko/shell.js +156 -0
  205. package/dist/engine/shrinko/shell.js.map +1 -0
  206. package/dist/engine/skills/index.d.ts +8 -0
  207. package/dist/engine/skills/index.d.ts.map +1 -0
  208. package/dist/engine/skills/index.js +8 -0
  209. package/dist/engine/skills/index.js.map +1 -0
  210. package/dist/engine/skills/skills.d.ts +104 -0
  211. package/dist/engine/skills/skills.d.ts.map +1 -0
  212. package/dist/engine/skills/skills.js +149 -0
  213. package/dist/engine/skills/skills.js.map +1 -0
  214. package/dist/index.d.ts +10 -0
  215. package/dist/index.d.ts.map +1 -0
  216. package/dist/index.js +10 -0
  217. package/dist/index.js.map +1 -0
  218. package/dist/version.d.ts +2 -0
  219. package/dist/version.d.ts.map +1 -0
  220. package/dist/version.js +19 -0
  221. package/dist/version.js.map +1 -0
  222. package/package.json +48 -2
  223. package/src/bin.ts +4 -0
  224. package/src/cli.ts +61 -0
  225. package/src/commands/audio.test.ts +280 -0
  226. package/src/commands/audio.ts +410 -0
  227. package/src/commands/export.test.ts +310 -0
  228. package/src/commands/export.ts +316 -0
  229. package/src/commands/gfx-io.test.ts +178 -0
  230. package/src/commands/gfx.test.ts +470 -0
  231. package/src/commands/gfx.ts +645 -0
  232. package/src/commands/init.test.ts +482 -0
  233. package/src/commands/init.ts +193 -0
  234. package/src/commands/lint.test.ts +137 -0
  235. package/src/commands/lint.ts +117 -0
  236. package/src/commands/minify.test.ts +166 -0
  237. package/src/commands/minify.ts +156 -0
  238. package/src/commands/music.test.ts +272 -0
  239. package/src/commands/music.ts +288 -0
  240. package/src/commands/playtest.test.ts +317 -0
  241. package/src/commands/playtest.ts +773 -0
  242. package/src/commands/run.test.ts +264 -0
  243. package/src/commands/run.ts +285 -0
  244. package/src/commands/serve.test.ts +227 -0
  245. package/src/commands/serve.ts +272 -0
  246. package/src/commands/sfx.test.ts +349 -0
  247. package/src/commands/sfx.ts +300 -0
  248. package/src/commands/tokens.test.ts +212 -0
  249. package/src/commands/tokens.ts +168 -0
  250. package/src/commands/verify.test.ts +238 -0
  251. package/src/commands/verify.ts +256 -0
  252. package/src/commands/version.test.ts +70 -0
  253. package/src/commands/version.ts +24 -0
  254. package/src/engine/audio/index.ts +47 -0
  255. package/src/engine/audio/music.test.ts +188 -0
  256. package/src/engine/audio/music.ts +218 -0
  257. package/src/engine/audio/section.test.ts +124 -0
  258. package/src/engine/audio/section.ts +79 -0
  259. package/src/engine/audio/sfx.test.ts +410 -0
  260. package/src/engine/audio/sfx.ts +694 -0
  261. package/src/engine/cart/cart.test.ts +304 -0
  262. package/src/engine/cart/cart.ts +211 -0
  263. package/src/engine/cart/errors.ts +39 -0
  264. package/src/engine/cart/fixtures/crlf.p8 +6 -0
  265. package/src/engine/cart/fixtures/full.p8 +174 -0
  266. package/src/engine/cart/fixtures/header-only.p8 +2 -0
  267. package/src/engine/cart/fixtures/malformed-no-header.p8 +2 -0
  268. package/src/engine/cart/fixtures/minimal.p8 +10 -0
  269. package/src/engine/cart/fixtures/no-trailing-newline.p8 +6 -0
  270. package/src/engine/cart/gfx.ts +195 -0
  271. package/src/engine/cart/index.ts +27 -0
  272. package/src/engine/cart/map.ts +134 -0
  273. package/src/engine/config/config.ts +115 -0
  274. package/src/engine/config/index.ts +12 -0
  275. package/src/engine/gfx/grid.test.ts +128 -0
  276. package/src/engine/gfx/grid.ts +136 -0
  277. package/src/engine/gfx/index.ts +34 -0
  278. package/src/engine/gfx/label.test.ts +105 -0
  279. package/src/engine/gfx/label.ts +229 -0
  280. package/src/engine/gfx/overlap.test.ts +71 -0
  281. package/src/engine/gfx/overlap.ts +66 -0
  282. package/src/engine/gfx/palette.test.ts +35 -0
  283. package/src/engine/gfx/palette.ts +54 -0
  284. package/src/engine/gfx/png.test.ts +111 -0
  285. package/src/engine/gfx/png.ts +196 -0
  286. package/src/engine/gfx/render.test.ts +180 -0
  287. package/src/engine/gfx/render.ts +106 -0
  288. package/src/engine/index.ts +21 -0
  289. package/src/engine/pico8/adapter.ts +303 -0
  290. package/src/engine/pico8/drive.test.ts +415 -0
  291. package/src/engine/pico8/drive.ts +729 -0
  292. package/src/engine/pico8/harness.test.ts +181 -0
  293. package/src/engine/pico8/harness.ts +354 -0
  294. package/src/engine/pico8/index.ts +20 -0
  295. package/src/engine/pico8/sentinel.test.ts +80 -0
  296. package/src/engine/pico8/sentinel.ts +93 -0
  297. package/src/engine/pico8/session-daemon-entry.ts +12 -0
  298. package/src/engine/pico8/session-daemon-main.ts +98 -0
  299. package/src/engine/pico8/session-daemon.test.ts +212 -0
  300. package/src/engine/pico8/session-daemon.ts +306 -0
  301. package/src/engine/pico8/session.test.ts +239 -0
  302. package/src/engine/pico8/session.ts +321 -0
  303. package/src/engine/pico8/shell.test.ts +583 -0
  304. package/src/engine/pico8/shell.ts +412 -0
  305. package/src/engine/pico8/supervisor.test.ts +105 -0
  306. package/src/engine/pico8/supervisor.ts +197 -0
  307. package/src/engine/scaffold/index.ts +15 -0
  308. package/src/engine/scaffold/scaffold.ts +278 -0
  309. package/src/engine/shrinko/adapter.ts +241 -0
  310. package/src/engine/shrinko/fixtures/count-no-compressed.txt +2 -0
  311. package/src/engine/shrinko/fixtures/count-over-budget.txt +3 -0
  312. package/src/engine/shrinko/fixtures/count-under-budget.txt +3 -0
  313. package/src/engine/shrinko/index.ts +33 -0
  314. package/src/engine/shrinko/shell.ts +224 -0
  315. package/src/engine/shrinko/shrinko.test.ts +182 -0
  316. package/src/engine/skills/index.ts +14 -0
  317. package/src/engine/skills/skills.test.ts +376 -0
  318. package/src/engine/skills/skills.ts +205 -0
  319. package/src/index.ts +13 -0
  320. package/src/skills/game-design-reference/SKILL.md +70 -0
  321. package/src/skills/game-jam/SKILL.md +45 -0
  322. package/src/skills/picopilot-art/SKILL.md +62 -0
  323. package/src/skills/picopilot-audio/SKILL.md +194 -0
  324. package/src/skills/picopilot-code/SKILL.md +97 -0
  325. package/src/skills/picopilot-code/reference/README.md +42 -0
  326. package/src/skills/picopilot-code/reference/mode7-racing.md +95 -0
  327. package/src/skills/picopilot-code/reference/platformer.md +119 -0
  328. package/src/skills/picopilot-code/reference/puzzle-grid.md +106 -0
  329. package/src/skills/picopilot-code/reference/rpg-menus-dialog.md +122 -0
  330. package/src/skills/picopilot-code/reference/top-down-adventure.md +117 -0
  331. package/src/skills/picopilot-code/reference/twin-stick-arcade.md +128 -0
  332. package/src/skills/picopilot-debug/SKILL.md +195 -0
  333. package/src/skills/picopilot-overview/SKILL.md +52 -0
  334. package/src/version.ts +22 -0
@@ -0,0 +1,195 @@
1
+ ---
2
+ name: picopilot-debug
3
+ description: Close the run-test loop for a PICO-8 cart with picopilot. Run the cart headless and read printh output, use the static verify gate honestly (well-formed is not proven-to-run), and understand the structured pico8-not-found / shrinko-not-found boundaries. Use when a cart misbehaves or you need to confirm it boots.
4
+ ---
5
+
6
+ # picopilot debug
7
+
8
+ Static checks tell you a cart is well-formed; only running it tells you it works.
9
+ picopilot gives you both a static gate and a headless run so you can drive a
10
+ closed loop. (See `picopilot-overview` for the `#include` discipline and
11
+ `picopilot-code` for the token loop.)
12
+
13
+ ## Static first: `picopilot verify`
14
+
15
+ `picopilot verify` runs tokens + integrity and returns ONE structured pass/fail
16
+ envelope. It is STATIC and NEVER runs the cart, so:
17
+
18
+ - A green verify means "well-formed", NOT "it runs". The passing result points
19
+ you at `picopilot run` to confirm the cart boots. Do not stop at green.
20
+ - A fail lists WHICH check flunked (over budget, or a malformed cart), so you fix
21
+ the named thing.
22
+ - With shrinko absent, verify is `gate-incapable` (a distinct nonzero outcome,
23
+ NEVER green): it cannot check token bloat, so it refuses to report pass.
24
+
25
+ ## Then run it: `picopilot run`
26
+
27
+ `picopilot run <cart>` launches PICO-8 headless (`pico8 -x`), streams stdout,
28
+ ENDS the run the moment the cart prints a done-sentinel, and collects one
29
+ structured result: the screenshot PNG paths, the captured `printh` stdout, and
30
+ the exit reason (`sentinel` / `timeout` / `exit`). Read the screenshots to SEE
31
+ the running game; read printh for text traces. On a backstop `timeout` it CTAs
32
+ you to `verify`/`tokens` to catch a code fault.
33
+
34
+ `run` requires PICO-8 (a licensed, paid binary with no pip/npm path). If PICO-8
35
+ is absent, `run` returns a structured `pico8-not-found` result with a remedy
36
+ (`set PICO8_PATH or install PICO-8`) and a nonzero exit, not a crash or a hang.
37
+ Use the static `verify` loop while PICO-8 is unavailable; it never needs it.
38
+
39
+ ### NEVER run `pico8 --help` / `--version` / bare `pico8` (they BLOCK your tool call)
40
+
41
+ PICO-8 is a GUI app with NO headless diagnostic mode. `pico8 --help`,
42
+ `pico8 --version`, and bare `pico8` do NOT print text and exit like a normal
43
+ CLI, they LAUNCH THE INTERACTIVE APP and hang forever (verified: `timeout 5
44
+ pico8 --help` exits 124 with zero output). Running one from an agent/automation
45
+ context STALLS your own tool call indefinitely (it has already bitten this
46
+ project's agents more than once, hanging a whole build). Do NOT invoke `pico8`
47
+ to "see its flags" or check it works. The only safe, non-blocking calls are the
48
+ automation ones, ALWAYS with `</dev/null` + a `timeout` backstop:
49
+ `pico8 -x <cart>`, `pico8 -run <cart>`, `pico8 <cart> -export foo.p8.png`. And
50
+ prefer to let picopilot (`run`, `audio render`, etc.) shell out for you rather
51
+ than calling `pico8` by hand. To learn a CLI flag, read the PICO-8 manual, never
52
+ ask the binary.
53
+
54
+ ### The cart must COOPERATE (the recipe `run` orchestrates)
55
+
56
+ There is no "screenshot an arbitrary running cart" hook and a cart CANNOT quit
57
+ the PICO-8 app itself (`extcmd("shutdown")` works only in EXPORTED binaries).
58
+ So the cart takes its own screenshots and signals when it is done, and `run`
59
+ does the watch + kill + collect. Add these debug lines to the cart, run, then
60
+ remove them:
61
+
62
+ ```lua
63
+ -- screenshot on a frame timer (the verb is "screen", NOT "screenshot")
64
+ if t==20 then extcmd("set_filename","frame_0") extcmd("screen") end
65
+ if t==40 then extcmd("set_filename","frame_1") extcmd("screen") end
66
+ -- signal done so run kills PICO-8 promptly (it can't self-quit)
67
+ if t==45 then printh("__PICOPILOT_DONE__") end
68
+ ```
69
+
70
+ Load-bearing details (each an easy wrong guess, all tested):
71
+
72
+ - The screenshot verb is **`extcmd("screen")`**. `extcmd("screenshot")` ERRORS
73
+ ("unknown extcmd"). `extcmd("set_filename","frame_0")` before it names the next
74
+ shot deterministically (so you can read `frame_0.png`, `frame_1.png`, ... in
75
+ order and PERCEIVE MOTION by comparing frames).
76
+ - The done-sentinel is the default `__PICOPILOT_DONE__` on its OWN line via
77
+ `printh`. `run` kills PICO-8 the instant it matches, so the run lasts exactly
78
+ as long as the cart's logic needs, no blind wait. `--sentinel` changes it.
79
+ - A cart that never prints the sentinel is killed by the backstop
80
+ (`--backstop-ms`, default 15000) and reported as `exitReason: timeout`.
81
+ - `-x` IS genuinely headless (runs with no display); the external kill is needed
82
+ only because a cart cannot self-quit, NOT because of a window.
83
+ - Screenshots go to a run-controlled dir (`--shot-dir`, default an isolated temp
84
+ dir), never `~/Desktop`.
85
+
86
+ To SEE the game actually being PLAYED (drive input + capture gameplay), use
87
+ `picopilot playtest`, next. (`run` boots the cart and captures what it does on
88
+ its own; `playtest` drives it.) There is also a legacy `run --input` that passes
89
+ a one-shot `-p`/`stat(6)` string, but it only works if the cart hand-decodes
90
+ `stat(6)`; for driving an arbitrary game, `playtest` is the tool, not `run
91
+ --input`.
92
+
93
+ ## Actually PLAY it: `picopilot playtest`
94
+
95
+ `picopilot playtest` drives ANY normally-written cart (one that reads real
96
+ `btn`/`btnp`) with NO change to it: on a THROWAWAY copy it redefines `btn`/`btnp`
97
+ to read a harness-piped held-buttons byte (reconstructing `btnp` edges) and OWNS the frame
98
+ loop, so it advances an exact number of frames, PAUSES (freezing all state,
99
+ including logic in `_draw`) for a clean screenshot, and captures gameplay (not
100
+ the title). Your `main.p8` is never mutated. `playtest` requires PICO-8 and
101
+ returns the same structured `pico8-not-found` boundary as `run` when it is
102
+ absent. (Mechanism: ADR-0011.)
103
+
104
+ There are three ways to drive a cart; pick by how much continuity you need.
105
+
106
+ ### One-shot: `picopilot playtest run <cart>`
107
+
108
+ The whole scripted input is known up front, so the cart replays it to completion
109
+ and exits, returning ONE envelope (screenshot paths + captured printh + the
110
+ steps run). Great for a scripted playtest or benchmark judging.
111
+
112
+ ```
113
+ picopilot playtest run main.p8 # generic driver (press-to-start + gentle presses)
114
+ picopilot playtest run game.p8 --input "z" # just press O (a bare button = a press at frame 0)
115
+ picopilot playtest run game.p8 --input "3:o, 18-22:o, 20:right" # a timeline: press/hold at specific frames
116
+ picopilot playtest run game.p8 --seed 1 --input "o" # opt-in srand for deterministic replay
117
+ ```
118
+
119
+ `--input` accepts EITHER a bare list of buttons (`"z"`, `"o right"`, pressed at
120
+ frame 0, the same "just press these" form as the session `input` verb) OR a
121
+ timeline of `frame:button` (a single press, one clean `btnp` edge) /
122
+ `from-to:button` (a hold). A button is a NAME (`o x left right up down`, aliases
123
+ `l r u d`, PICO-8 keys `z`/`x`) or a bit `0..5`. Omit `--input` for the generic
124
+ one-button/runner/flappy driver. `--seed n` injects `srand(n)` at cart start
125
+ (never silent) so a random cart replays identically. Screenshots go to
126
+ `--shot-dir` (default an isolated temp dir, NEVER `~/Desktop`).
127
+
128
+ ### (A) Resumable LIVE session: `start` -> `step`/`input`/`shot` -> `stop`
129
+
130
+ When you want to PLAY across turns (look at the last frame, decide, inject input,
131
+ step, look again), keep the game ALIVE and PAUSED between your turns. `start`
132
+ launches a persistent driven PICO-8 and returns a SESSION ID; every other verb
133
+ addresses that id in a SEPARATE invocation, and the game stays alive + paused in
134
+ between. Stepping is deterministic via a stdout ACK handshake (the host waits for
135
+ the cart's ack before returning), so you always act on a settled, known frame, no
136
+ wall-clock guessing.
137
+
138
+ ```
139
+ ID=$(picopilot playtest start main.p8 --format json | jq -r .id) # launch, get the id
140
+ picopilot playtest input $ID "right o" # hold buttons for the next steps (names: left/right/up/down/o/x)
141
+ picopilot playtest step $ID --frames 30 # advance EXACTLY 30 frames, then pause
142
+ picopilot playtest shot $ID # screenshot the current frozen frame -> a PNG path
143
+ picopilot playtest status $ID # is it alive? how many frames in?
144
+ picopilot playtest stop $ID # tear it down (quit PICO-8, reap the dir)
145
+ ```
146
+
147
+ Lifecycle notes (each verified/handled, not a hope):
148
+
149
+ - Each verb WRITES a fixed-size command block and WAITS for the cart's ACK, so a
150
+ `step N` advances exactly N frames and a `shot` between steps captures the
151
+ frozen intended frame. Between verbs the game is frozen (stable framebuffer).
152
+ - ONE session per id; MULTIPLE concurrent sessions are allowed (distinct ids get
153
+ distinct daemons/dirs). Omit `--id` for a fresh random id; pass `--id name` for
154
+ a stable one.
155
+ - ORPHAN reaping: a session you never `stop` self-reaps after an idle window
156
+ (`--idle-timeout-ms`, default 10 min), so a walked-away session cannot leak a
157
+ live PICO-8 forever. `stop` is idempotent (safe on an already-dead session).
158
+ - PICO-8 dying mid-session surfaces as a STRUCTURED error on the next verb
159
+ (`playtest-session-process-dead`), never a hang; a command with no ACK by the
160
+ deadline is `playtest-session-ack-timeout`. `start` with PICO-8 absent is
161
+ `pico8-not-found`, same as `run`.
162
+ - Screenshots + the throwaway driven cart live under a controlled temp session
163
+ dir, never `~/Desktop` or your carts root.
164
+
165
+ ### (C) Stateless REPLAY (the free fallback, no daemon)
166
+
167
+ You do NOT always need a live session. Because the one-shot is deterministic
168
+ under `--seed`, you can "resume" by RE-INVOKING `playtest run` with an
169
+ ACCUMULATING script each turn: re-run from frame 0 with the same seed plus the
170
+ new presses appended. It is cheap, reproducible, and daemon-free.
171
+
172
+ ```
173
+ picopilot playtest run game.p8 --seed 1 --input "3:4" # turn 1
174
+ picopilot playtest run game.p8 --seed 1 --input "3:4, 40:1" # turn 2 (replays turn 1, then the new press)
175
+ picopilot playtest run game.p8 --seed 1 --input "3:4, 40:1, 55:5" # turn 3 ...
176
+ ```
177
+
178
+ Prefer (C) for short, replay-safe games (it needs no live process). Use the live
179
+ session (A) for true continuity: long games, or games that reseed from a
180
+ NON-deterministic source (wall-clock/entropy) mid-run and so cannot be replayed
181
+ from frame 0. Both drive the SAME cart-side transform; only the host lifecycle
182
+ differs.
183
+
184
+ ## The two structured boundaries
185
+
186
+ Both dependency boundaries are soft and well-signposted, so read the result, do
187
+ not guess:
188
+
189
+ - `shrinko-not-found` (remedy `uv pip install shrinko`): gates the token/lint
190
+ commands and therefore `verify`.
191
+ - `pico8-not-found` (remedy `set PICO8_PATH or install PICO-8`): gates `run`,
192
+ `playtest`, `audio render`, and binary/PNG exports.
193
+
194
+ Each is a structured `{ ok: false, reason, remedy }` envelope with a nonzero
195
+ exit, telling you exactly which capability is gated and how to enable it.
@@ -0,0 +1,52 @@
1
+ ---
2
+ name: picopilot-overview
3
+ description: Start here for any PICO-8 cart built with picopilot. The #include discipline (edit main.lua, never the .p8 binary sections), the per-iteration verify loop, and which deeper skill to load for code, art, audio, or debugging. Use when you open a picopilot cart folder or run picopilot init.
4
+ ---
5
+
6
+ # picopilot overview
7
+
8
+ picopilot is the transpile-and-verify layer between what you are good at (text)
9
+ and PICO-8's reality (binary cart sections). It gives you eyes (render sprites to
10
+ a viewable PNG), token-bloat detection, safe cart editing, and one static
11
+ acceptance gate, so you can build a PICO-8 game and self-correct.
12
+
13
+ Load this skill first. Then load the group skill for whatever you are doing:
14
+ `picopilot-code`, `picopilot-art`, `picopilot-audio`, or `picopilot-debug`.
15
+
16
+ ## The #include discipline (how every picopilot cart is laid out)
17
+
18
+ - `main.p8` is the cart. Its `__lua__` section is a single line: `#include main.lua`.
19
+ - `main.lua` is the plain Lua you EDIT. You work in ordinary Lua and never
20
+ hand-write the cart's binary hex sections (`__gfx__`, `__map__`, `__sfx__`,
21
+ `__music__`).
22
+ - To touch a binary section, use picopilot's commands (`gfx set`, the audio
23
+ transpiles). They edit those sections safely and round-trippably. Editing hex
24
+ by hand is how art and audio silently break.
25
+
26
+ `picopilot init` scaffolds this layout: `main.p8`, `main.lua`, an `AGENTS.md`
27
+ PICO-8 reference you should read, and a `picopilot.json` config.
28
+
29
+ ## The iteration loop
30
+
31
+ Every change follows the same shape:
32
+
33
+ 1. Edit `main.lua` (or a cart section via a picopilot command).
34
+ 2. `picopilot tokens` to watch the 8,192-token budget (see `picopilot-code`).
35
+ 3. `picopilot verify` as the single static acceptance gate.
36
+
37
+ `picopilot verify` is STATIC: it runs tokens + integrity and NEVER runs the cart.
38
+ A green verify means "well-formed", not "it works", so a passing verify points
39
+ you at `picopilot run` to confirm the cart actually boots. Never treat a green
40
+ static gate as done.
41
+
42
+ If shrinko is not installed, token-backed commands (and therefore `verify`) fail
43
+ with a structured `shrinko-not-found` result carrying the exact remedy
44
+ `uv pip install shrinko`. `verify` in that state returns `gate-incapable`, never
45
+ green, so the gate can never pass by skipping its most important check.
46
+
47
+ ## Making your agent discover picopilot
48
+
49
+ `picopilot init` PRINTS how to make your agent discover these skills; it does not
50
+ silently write into your shared skill dirs. To actually install them, run
51
+ `picopilot init --install-skills` (or `picopilot skills add`). That is the one
52
+ deliberate, opt-in write to a shared location.
package/src/version.ts ADDED
@@ -0,0 +1,22 @@
1
+ import {readFileSync} from 'node:fs';
2
+ import {dirname, join} from 'node:path';
3
+ import {fileURLToPath} from 'node:url';
4
+
5
+ /**
6
+ * The installed picopilot version, read from the package manifest at load time.
7
+ *
8
+ * Single source of truth: the version lives only in `package.json`. The manifest
9
+ * sits one level above the compiled module (`dist/version.js` → `package.json`)
10
+ * and one level above the source module (`src/version.ts` → `package.json`), so
11
+ * the same relative lookup works for both the built binary and `tsx` dev runs.
12
+ */
13
+ function readVersion(): string {
14
+ const here = dirname(fileURLToPath(import.meta.url));
15
+ const manifestPath = join(here, '..', 'package.json');
16
+ const manifest = JSON.parse(readFileSync(manifestPath, 'utf8')) as {
17
+ version?: string;
18
+ };
19
+ return manifest.version ?? '0.0.0';
20
+ }
21
+
22
+ export const VERSION = readVersion();