picopilot 0.0.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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 +202 -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 +217 -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 +460 -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 +238 -0
  243. package/src/commands/run.ts +259 -0
  244. package/src/commands/serve.test.ts +218 -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 +236 -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,303 @@
1
+ /**
2
+ * The PICO-8 adapter SEAM: the typed interface `picopilot run` talks to, plus
3
+ * the structured result shapes. Mirrors the `engine/shrinko` seam (ADR-0002):
4
+ * the command layer never knows HOW PICO-8 is launched; it talks to
5
+ * {@link Pico8Adapter}, and PICO-8 being ABSENT is a first-class value
6
+ * (`{ok:false, reason:'pico8-not-found', ...}`), never a throw or a hollow
7
+ * success. The v1 implementation shells out to the user's native `pico8 -x`
8
+ * (see `shell.ts` and ADR-0006, native path first); a future web-export /
9
+ * headless-browser implementation can satisfy the SAME interface.
10
+ *
11
+ * PICO-8 is a licensed, paid binary with no pip/npm install path, so absence is
12
+ * a normal, well-signposted boundary (US #14, #19), mirroring shrinko-not-found.
13
+ */
14
+
15
+ /** The remedy string in a pico8-not-found result: how the user makes `run` work. */
16
+ export const PICO8_REMEDY = 'set PICO8_PATH or install PICO-8' as const;
17
+
18
+ /** What `run` needs: the PICO-8 binary (a paid, licensed download; no package manager). */
19
+ export const PICO8_NEEDS = ['pico8'] as const;
20
+
21
+ /** The default done-sentinel: the cart prints this via `printh` to signal it is finished. */
22
+ export const DONE_SENTINEL = '__PICOPILOT_DONE__' as const;
23
+
24
+ /**
25
+ * The structured "PICO-8 is not installed" failure (US #19, ADR-0006). Shape
26
+ * mirrors {@link import('../shrinko/adapter.js').ShrinkoNotFound}: `ok:false`, a
27
+ * machine-readable `reason`, the EXACT `remedy`, and the `needs` list. A command
28
+ * maps it onto incur's `error({ code: reason, ... })` with a nonzero exit.
29
+ */
30
+ export interface Pico8NotFound {
31
+ readonly ok: false;
32
+ readonly reason: 'pico8-not-found';
33
+ readonly remedy: typeof PICO8_REMEDY;
34
+ readonly needs: readonly string[];
35
+ }
36
+
37
+ /** Why a run ended: the cart signalled done, the backstop fired, or PICO-8 exited on its own. */
38
+ export type ExitReason = 'sentinel' | 'timeout' | 'exit';
39
+
40
+ /**
41
+ * A completed run's collected results. `screenshots` are the PNG paths PICO-8
42
+ * wrote into the run-controlled `-desktop` dir (via the cart's `extcmd("screen")`);
43
+ * `printh` is the captured stdout (the cart's `printh` output plus PICO-8's own
44
+ * lines); `exitReason` says how it ended.
45
+ */
46
+ export interface RunReport {
47
+ readonly screenshots: readonly string[];
48
+ readonly printh: string;
49
+ readonly exitReason: ExitReason;
50
+ }
51
+
52
+ /** A successful adapter call carrying the run report. */
53
+ export interface Pico8Ok {
54
+ readonly ok: true;
55
+ readonly value: RunReport;
56
+ }
57
+
58
+ /**
59
+ * The result {@link Pico8Adapter.run} returns: either a {@link RunReport} or the
60
+ * structured {@link Pico8NotFound}. Absence is a value, not a throw, so the
61
+ * two-tier capability contract is total and type-checked at the call site.
62
+ */
63
+ export type Pico8Result = Pico8Ok | Pico8NotFound;
64
+
65
+ /**
66
+ * A completed audio-RECORDING run's results (ADR-0009). `wavPath` is the WAV the
67
+ * cart wrote via `extcmd("audio_end", 1)` into the run-controlled folder (the
68
+ * `audio_end(1)`-to-current-folder quirk, isolated to a temp `wavDir`), or
69
+ * `undefined` if none was produced (e.g. a headless `-x` capture, which yields a
70
+ * silent 0-frame file we treat as "no usable WAV", or a run that never recorded).
71
+ * `printh` + `exitReason` mirror {@link RunReport}, so a recording is a run with a
72
+ * WAV alongside the same run state.
73
+ */
74
+ export interface RecordReport {
75
+ readonly wavPath: string | undefined;
76
+ readonly printh: string;
77
+ readonly exitReason: ExitReason;
78
+ }
79
+
80
+ /** A successful audio-record call carrying the {@link RecordReport}. */
81
+ export interface Pico8RecordOk {
82
+ readonly ok: true;
83
+ readonly value: RecordReport;
84
+ }
85
+
86
+ /**
87
+ * A completed DRIVE run's results (ADR-0011, `playtest`). Same shape family as
88
+ * {@link RunReport}: `screenshots` are the PNGs the driven cart wrote at the SHOT
89
+ * points (named `<basename>0.png`, ...), `printh` is the captured stdout (the
90
+ * cart's ACKs + the done-sentinel + any cart printh), `exitReason` says how it
91
+ * ended. A driven run is a run whose input + frame loop the harness owned.
92
+ */
93
+ export interface DriveReport {
94
+ readonly screenshots: readonly string[];
95
+ readonly printh: string;
96
+ readonly exitReason: ExitReason;
97
+ }
98
+
99
+ /** A successful drive call carrying the {@link DriveReport}. */
100
+ export interface Pico8DriveOk {
101
+ readonly ok: true;
102
+ readonly value: DriveReport;
103
+ }
104
+
105
+ /**
106
+ * The result {@link Pico8Adapter.drive} returns: a {@link DriveReport} or the
107
+ * structured {@link Pico8NotFound} (PICO-8 absent), mirroring {@link Pico8Result}.
108
+ */
109
+ export type Pico8DriveResult = Pico8DriveOk | Pico8NotFound;
110
+
111
+ /**
112
+ * Options for a single {@link Pico8Adapter.drive} (ADR-0011). The cart is the
113
+ * THROWAWAY driven cart the drive-transform produced; `blocks` are the encoded
114
+ * FIXED-SIZE command blocks piped to its live `-x` stdin (the load-bearing
115
+ * transport: small unpadded writes coalesce/drop, so the whole block stream is
116
+ * written up front for the one-shot). Screenshots land in `shotDir` (`-desktop`).
117
+ */
118
+ export interface DriveOptions {
119
+ /** Absolute path to the throwaway driven `.p8` cart to run. */
120
+ readonly cartPath: string;
121
+ /** The run-controlled dir PICO-8 writes screenshots into (`-desktop <dir>`). */
122
+ readonly shotDir: string;
123
+ /** The encoded fixed-size command blocks to pipe to the cart's stdin. */
124
+ readonly blocks: Uint8Array;
125
+ /** The stdout line that ends the run when matched (defaults to {@link DONE_SENTINEL}). */
126
+ readonly sentinel?: string;
127
+ /** The hard backstop in ms: kill PICO-8 if it neither signals nor exits by then. */
128
+ readonly backstopMs: number;
129
+ }
130
+
131
+ /**
132
+ * The result {@link Pico8Adapter.record} returns: a {@link RecordReport} or the
133
+ * structured {@link Pico8NotFound} (PICO-8 absent), mirroring {@link Pico8Result}.
134
+ */
135
+ export type Pico8RecordResult = Pico8RecordOk | Pico8NotFound;
136
+
137
+ /**
138
+ * Options for a single {@link Pico8Adapter.record} (ADR-0009). The recorded cart
139
+ * is expected to COOPERATE: at start it does `extcmd("set_filename", <base>)` +
140
+ * `extcmd("audio_rec")`, and when finished `extcmd("audio_end", 1)` (save to the
141
+ * current folder) then `printh` the sentinel. For `audio record <cart>` the
142
+ * caller injects that harness around the user's cart; for `audio render` the
143
+ * whole harness cart is built by {@link import('./harness.js')}.
144
+ */
145
+ export interface RecordOptions {
146
+ /** Absolute path to the `.p8` cart to run and record. */
147
+ readonly cartPath: string;
148
+ /**
149
+ * The run-controlled folder PICO-8 records the WAV into. Because
150
+ * `extcmd("audio_end", 1)` saves to PICO-8's CURRENT folder (NOT `-desktop`),
151
+ * the adapter points PICO-8's `root_path` here so the WAV lands in an isolated
152
+ * temp dir, never `~/Desktop` or the carts root (the shared-write discipline).
153
+ */
154
+ readonly wavDir: string;
155
+ /**
156
+ * The base filename (no extension) the cart passed to `extcmd("set_filename")`,
157
+ * so the adapter knows which `<base>.wav` to collect. Defaults to
158
+ * {@link RECORD_WAV_BASENAME} when omitted.
159
+ */
160
+ readonly wavBasename?: string;
161
+ /** The stdout line that ends the run when matched (defaults to {@link DONE_SENTINEL}). */
162
+ readonly sentinel?: string;
163
+ /** The hard backstop in ms: kill PICO-8 if it neither signals nor exits by then. */
164
+ readonly backstopMs: number;
165
+ }
166
+
167
+ /**
168
+ * A completed EXPORT run's results (the PICO-8 HTML export, `pico8 <cart>
169
+ * -export <name>.html -x`). PICO-8 writes a pair into the output dir: the shell
170
+ * page (`<name>.html`, which references its sibling by name) and the
171
+ * Emscripten runtime + baked-in cart payload (`<name>.js`). `htmlPath` is the
172
+ * shell page (undefined for a payload-only export that keeps only the runtime);
173
+ * `jsPath` is the runtime payload; `files` lists every produced file (absolute).
174
+ * `labelWarning` surfaces PICO-8's "please capture a label first" notice: a
175
+ * labelless cart exports with an ugly/blank splash (a soft warning, not a
176
+ * failure).
177
+ */
178
+ export interface ExportReport {
179
+ readonly htmlPath: string | undefined;
180
+ readonly jsPath: string | undefined;
181
+ readonly files: readonly string[];
182
+ readonly labelWarning: boolean;
183
+ }
184
+
185
+ /** A successful export call carrying the {@link ExportReport}. */
186
+ export interface Pico8ExportOk {
187
+ readonly ok: true;
188
+ readonly value: ExportReport;
189
+ }
190
+
191
+ /**
192
+ * The result {@link Pico8Adapter.export} returns: an {@link ExportReport} or the
193
+ * structured {@link Pico8NotFound} (PICO-8 absent), mirroring {@link Pico8Result}.
194
+ */
195
+ export type Pico8ExportResult = Pico8ExportOk | Pico8NotFound;
196
+
197
+ /**
198
+ * Options for a single {@link Pico8Adapter.export}. PICO-8 derives the produced
199
+ * file names from `htmlName` (`game.html` -> `game.html` + `game.js`), so the
200
+ * command passes `index.html` to get a directly-serveable `index.html` +
201
+ * `index.js` pair in `outDir`. `payloadOnly` keeps only the `.js` runtime
202
+ * payload (dropping the shell page) for a website that provides its OWN player
203
+ * shell (a Svelte component), never the PICO-8 binary's concern: the split is a
204
+ * post-export file operation the command owns.
205
+ */
206
+ export interface ExportOptions {
207
+ /** Absolute path to the `.p8` cart to export. */
208
+ readonly cartPath: string;
209
+ /** The run-controlled dir PICO-8 writes the export pair into. */
210
+ readonly outDir: string;
211
+ /**
212
+ * The `.html` name handed to `-export` (e.g. `index.html`); PICO-8 derives the
213
+ * sibling `.js` from it. Defaults to {@link EXPORT_HTML_NAME}.
214
+ */
215
+ readonly htmlName?: string;
216
+ /** The hard backstop in ms: kill PICO-8 if the export neither completes nor exits. */
217
+ readonly backstopMs: number;
218
+ }
219
+
220
+ /** The default `.html` name `export` hands to `-export`: yields a serveable `index.html` + `index.js`. */
221
+ export const EXPORT_HTML_NAME = 'index.html' as const;
222
+
223
+ /** Options for a single {@link Pico8Adapter.run}. */
224
+ export interface RunOptions {
225
+ /** Absolute path to the `.p8` cart to run. */
226
+ readonly cartPath: string;
227
+ /** The run-controlled dir PICO-8 writes screenshots into (`-desktop <dir>`). */
228
+ readonly shotDir: string;
229
+ /** The stdout line that ends the run when matched (defaults to {@link DONE_SENTINEL}). */
230
+ readonly sentinel?: string;
231
+ /** The hard backstop in ms: kill PICO-8 if it neither signals nor exits by then. */
232
+ readonly backstopMs: number;
233
+ /**
234
+ * Optional one-shot scripted input passed as `-p <input>`; the cart reads it
235
+ * via `stat(6)` and replays it (the canned-playtest channel). The cart must
236
+ * cooperate by decoding `stat(6)` (taught in the `picopilot-debug` skill).
237
+ * Absent = no input passed.
238
+ */
239
+ readonly input?: string;
240
+ }
241
+
242
+ /**
243
+ * The PICO-8 capability seam. One method for now: {@link run}. Later PICO-8-gated
244
+ * operations (`audio render`, `export`) extend THIS interface rather than forking.
245
+ */
246
+ export interface Pico8Adapter {
247
+ /**
248
+ * Launches PICO-8 on `cartPath`, streams stdout, ends the run on the sentinel
249
+ * (with the backstop as the safety net), and collects screenshots + printh +
250
+ * exit reason. Returns {@link Pico8NotFound} when PICO-8 is not installed.
251
+ */
252
+ run(options: RunOptions): Promise<Pico8Result>;
253
+
254
+ /**
255
+ * Launches PICO-8 on `cartPath` in a REAL audio+video session (NOT headless
256
+ * `-x`, which mixes no audio and yields an empty WAV, ADR-0009), streams
257
+ * stdout, ends on the sentinel (backstop as the net), and collects the WAV the
258
+ * cooperating cart wrote via `audio_rec`/`audio_end(1)`. Returns
259
+ * {@link Pico8NotFound} when PICO-8 is not installed. Live capture is a
260
+ * manual/opt-in tier; CI drives a fake runner + the absent boundary.
261
+ */
262
+ record(options: RecordOptions): Promise<Pico8RecordResult>;
263
+
264
+ /**
265
+ * Launches PICO-8 on the THROWAWAY driven cart (`-desktop <shotDir> -x`),
266
+ * pipes the encoded FIXED-SIZE command blocks to its live stdin (the input +
267
+ * frame-loop transport, ADR-0011), ends on the sentinel (backstop as the net),
268
+ * and collects the SHOT screenshots + printh + exit reason. Returns
269
+ * {@link Pico8NotFound} when PICO-8 is absent. Live drive-and-capture is a
270
+ * manual/opt-in tier; CI drives a fake runner + the absent boundary.
271
+ */
272
+ drive(options: DriveOptions): Promise<Pico8DriveResult>;
273
+
274
+ /**
275
+ * Exports `cartPath` as a PICO-8 HTML bundle (`pico8 <cart> -export
276
+ * <outDir>/<htmlName> -x`, the HEADLESS `-x` path, no display needed) and
277
+ * collects the produced `.html` + `.js` pair from `outDir`. Returns
278
+ * {@link Pico8NotFound} when PICO-8 is absent (the CI-testable boundary). Live
279
+ * export is a manual/opt-in tier: it hard-requires the paid binary, and a
280
+ * labelless cart exports with a `labelWarning` set (a soft warning, not a
281
+ * failure). The command owns any post-export payload-only file split.
282
+ */
283
+ export(options: ExportOptions): Promise<Pico8ExportResult>;
284
+ }
285
+
286
+ /** The default WAV basename a recorded cart passes to `extcmd("set_filename")`. */
287
+ export const RECORD_WAV_BASENAME = 'picopilot-audio' as const;
288
+
289
+ /** The 60 audio ticks PICO-8 advances per second: the record-duration unit (finding). */
290
+ export const PICO8_TICKS_PER_SECOND = 120;
291
+
292
+ /** The video frames PICO-8 runs per second (`_update` at 30fps; `t` counts frames). */
293
+ export const PICO8_FRAMES_PER_SECOND = 30;
294
+
295
+ /** The frozen {@link Pico8NotFound} value, so every call site returns the same shape. */
296
+ export function pico8NotFound(): Pico8NotFound {
297
+ return {
298
+ ok: false,
299
+ reason: 'pico8-not-found',
300
+ remedy: PICO8_REMEDY,
301
+ needs: [...PICO8_NEEDS],
302
+ };
303
+ }