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,49 @@
1
+ /**
2
+ * The one line of code in the scaffolded cart. PICO-8's `#include` pulls the
3
+ * external Lua file in at load time; keeping it the ONLY thing in `__lua__` is
4
+ * the `#include` discipline the whole toolchain relies on.
5
+ */
6
+ export declare const LUA_INCLUDE_LINE = "#include main.lua";
7
+ /**
8
+ * Builds the scaffolded `main.p8` via the cart model. `__lua__` holds only the
9
+ * `#include`; the cart carries no binary sections yet (a fresh cart has no gfx /
10
+ * map / sfx / music); those are added later by the gfx/audio commands.
11
+ */
12
+ export declare function mainP8(): string;
13
+ /**
14
+ * The starter `main.lua`: a minimal but RUNNABLE cart (the `_init/_update/_draw`
15
+ * loop the API reference calls out as load-bearing) so a fresh folder boots
16
+ * instead of being inert. Intentionally tiny: it is a starting point the agent
17
+ * replaces, not a demo.
18
+ */
19
+ export declare function mainLua(): string;
20
+ /** The scaffolded `picopilot.json` (incur config: precedence argv > config > defaults). */
21
+ export declare function picopilotJson(): string;
22
+ /**
23
+ * The curated PICO-8 reference written to `AGENTS.md`.
24
+ *
25
+ * This is CONTEXT an agent loads every turn, so it is CURATED from
26
+ * `work/notes/findings/pico8-api-reference.md`, not a dump of the manual: the
27
+ * hard specs, the `_init/_update/_draw` loop, the fixed palette, the memory map
28
+ * + the gfx/map overlap WARNING, the grouped most-used API, and the token
29
+ * discipline, the things models get wrong without a reference.
30
+ */
31
+ export declare function agentsMd(): string;
32
+ /**
33
+ * One scaffolded file: its cart-relative name and its exact bytes. `init`
34
+ * iterates these, so adding a file is a one-line change here and the tests
35
+ * assert against this list (the "exactly these files" contract).
36
+ */
37
+ export interface ScaffoldFile {
38
+ readonly name: string;
39
+ readonly content: string;
40
+ }
41
+ /**
42
+ * The complete set of files `picopilot init` writes into a fresh cart folder,
43
+ * in a stable order: the cart, its editable Lua, the agent-context reference,
44
+ * and the config. Exactly FOUR files (the acceptance contract). `AGENTS.md` is
45
+ * the cross-agent context filename; Claude Code, Codex, and others read it, so
46
+ * one portable file beats duplicating identical content into `CLAUDE.md`.
47
+ */
48
+ export declare function scaffoldFiles(): ScaffoldFile[];
49
+ //# sourceMappingURL=scaffold.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../../src/engine/scaffold/scaffold.ts"],"names":[],"mappings":"AAiBA;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,sBAAsB,CAAC;AAEpD;;;;GAIG;AACH,wBAAgB,MAAM,IAAI,MAAM,CAM/B;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,IAAI,MAAM,CAehC;AAED,2FAA2F;AAC3F,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,IAAI,MAAM,CAuIjC;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,IAAI,YAAY,EAAE,CAO9C"}
@@ -0,0 +1,217 @@
1
+ import { Cart } from '../cart/index.js';
2
+ import { defaultConfigFile } from '../config/index.js';
3
+ /**
4
+ * The static content `picopilot init` writes into a fresh cart folder.
5
+ *
6
+ * The single scaffolding model (US #1): the `.p8`'s `__lua__` section is exactly
7
+ * `#include main.lua`, and the plain-Lua `main.lua` is the file the agent edits,
8
+ * so the agent never hand-writes the cart's binary hex sections. `main.p8` is
9
+ * built THROUGH the cart model (not a hand-written string) so init and every
10
+ * later cart-touching command share one serializer and the output is guaranteed
11
+ * to round-trip.
12
+ */
13
+ /** The PICO-8 cart format version init emits (the current stable text-format version). */
14
+ const CART_VERSION = 42;
15
+ /**
16
+ * The one line of code in the scaffolded cart. PICO-8's `#include` pulls the
17
+ * external Lua file in at load time; keeping it the ONLY thing in `__lua__` is
18
+ * the `#include` discipline the whole toolchain relies on.
19
+ */
20
+ export const LUA_INCLUDE_LINE = '#include main.lua';
21
+ /**
22
+ * Builds the scaffolded `main.p8` via the cart model. `__lua__` holds only the
23
+ * `#include`; the cart carries no binary sections yet (a fresh cart has no gfx /
24
+ * map / sfx / music); those are added later by the gfx/audio commands.
25
+ */
26
+ export function mainP8() {
27
+ const cart = Cart.parse(`pico-8 cartridge // http://www.pico-8.com\nversion ${CART_VERSION}\n__lua__\n`);
28
+ cart.setSection('lua', `${LUA_INCLUDE_LINE}\n`);
29
+ return cart.serialize();
30
+ }
31
+ /**
32
+ * The starter `main.lua`: a minimal but RUNNABLE cart (the `_init/_update/_draw`
33
+ * loop the API reference calls out as load-bearing) so a fresh folder boots
34
+ * instead of being inert. Intentionally tiny: it is a starting point the agent
35
+ * replaces, not a demo.
36
+ */
37
+ export function mainLua() {
38
+ return `-- main.lua, the plain-Lua source picopilot's main.p8 #includes.
39
+ -- Edit THIS file; never hand-write main.p8's binary sections.
40
+
41
+ function _init()
42
+ end
43
+
44
+ function _update()
45
+ end
46
+
47
+ function _draw()
48
+ cls()
49
+ print("hello picopilot", 36, 60, 7)
50
+ end
51
+ `;
52
+ }
53
+ /** The scaffolded `picopilot.json` (incur config: precedence argv > config > defaults). */
54
+ export function picopilotJson() {
55
+ return defaultConfigFile();
56
+ }
57
+ /**
58
+ * The curated PICO-8 reference written to `AGENTS.md`.
59
+ *
60
+ * This is CONTEXT an agent loads every turn, so it is CURATED from
61
+ * `work/notes/findings/pico8-api-reference.md`, not a dump of the manual: the
62
+ * hard specs, the `_init/_update/_draw` loop, the fixed palette, the memory map
63
+ * + the gfx/map overlap WARNING, the grouped most-used API, and the token
64
+ * discipline, the things models get wrong without a reference.
65
+ */
66
+ export function agentsMd() {
67
+ return `# PICO-8 cart context (generated by \`picopilot init\`)
68
+
69
+ You are editing a PICO-8 cart with picopilot. This file is your always-loaded
70
+ reference: the constraints, the loop, the palette, the memory map (+ the overlap
71
+ warning), the most-used API, and how to stay under the token budget. It is
72
+ curated from PICO-8's manual for the things models get wrong without a reminder.
73
+
74
+ ## The \`#include\` discipline (how this cart is laid out)
75
+
76
+ - \`main.p8\` is the cart. Its \`__lua__\` section is a single line: \`#include main.lua\`.
77
+ - \`main.lua\` is the plain Lua you EDIT. Never hand-write \`main.p8\`'s binary
78
+ sections (\`__gfx__\`, \`__map__\`, \`__sfx__\`, \`__music__\`), use picopilot's
79
+ \`gfx\` / audio commands, which edit them safely and round-trippably.
80
+
81
+ ## Hard specs (the constraints that shape everything)
82
+
83
+ - Display: **128x128**, a **fixed 16-colour palette** (indices 0..15).
84
+ - Code: **Lua**, max **8192 tokens** (the #1 thing to watch). Run \`picopilot tokens\`.
85
+ - Sprites: 256 8x8 sprites in a base bank (0..127) plus a shared bank (128..255).
86
+ - Map: 128x32 tiles + a shared half that ALIASES sprites 128..255 (see below).
87
+ - Sound: 4 channels, 64 SFX slots; music is patterns of up to 4 SFX references.
88
+ - Numbers: 16.16 fixed point, range \`-32768.0 .. 32767.99\`.
89
+
90
+ ## The execution loop (every cart)
91
+
92
+ - \`_init()\`: runs once at startup.
93
+ - \`_update()\`: runs every update tick at 30fps (\`_update60()\` for 60fps).
94
+ - \`_draw()\`: runs once per visible frame.
95
+
96
+ A cart with none of these is inert. Keep them defined.
97
+
98
+ ## The fixed 16-colour palette (index → RGB)
99
+
100
+ In a char grid, \`.\` = transparent and \`0-F\` = these indices.
101
+
102
+ | idx | hex | name | R | G | B |
103
+ |-----|-----|-------------|-----|-----|-----|
104
+ | 0 | 0 | black | 0 | 0 | 0 |
105
+ | 1 | 1 | dark-blue | 29 | 43 | 83 |
106
+ | 2 | 2 | dark-purple | 126 | 37 | 83 |
107
+ | 3 | 3 | dark-green | 0 | 135 | 81 |
108
+ | 4 | 4 | brown | 171 | 82 | 54 |
109
+ | 5 | 5 | dark-grey | 95 | 87 | 79 |
110
+ | 6 | 6 | light-grey | 194 | 195 | 199 |
111
+ | 7 | 7 | white | 255 | 241 | 232 |
112
+ | 8 | 8 | red | 255 | 0 | 77 |
113
+ | 9 | 9 | orange | 255 | 163 | 0 |
114
+ | 10 | a | yellow | 255 | 236 | 39 |
115
+ | 11 | b | green | 0 | 228 | 54 |
116
+ | 12 | c | blue | 41 | 173 | 255 |
117
+ | 13 | d | lavender | 131 | 118 | 156 |
118
+ | 14 | e | pink | 255 | 119 | 168 |
119
+ | 15 | f | peach | 255 | 204 | 170 |
120
+
121
+ ## Memory map + the gfx/map overlap warning
122
+
123
+ \`\`\`
124
+ 0x0000 gfx (spritesheet, sprites 0..127)
125
+ 0x1000 shared gfx / map2 <-- sprites 128..255 ALIAS the bottom half of the map
126
+ 0x2000 map
127
+ 0x3000 gfx flags
128
+ 0x3100 music
129
+ 0x3200 sfx
130
+ 0x4300 user data
131
+ 0x5e00 persistent cart data (256 bytes)
132
+ 0x5f00 draw state
133
+ 0x5f40 hardware state
134
+ 0x5f80 gpio pins
135
+ 0x6000 screen (8k)
136
+ \`\`\`
137
+
138
+ **Overlap warning:** sprites 128..255 (\`0x1000\`-\`0x1fff\`) occupy the SAME memory
139
+ as \`__map__\` rows 32..63. Writing a sprite in that range clobbers the map tiles
140
+ there. \`picopilot gfx set\` smart-refuses such a write when real map tiles are at
141
+ risk and nothing authorised the loss (\`--allow-map-overlap\` or \`allowMapOverlap\`
142
+ in \`picopilot.json\`), so you never silently destroy map data you cannot see.
143
+
144
+ ## Most-used API (grouped)
145
+
146
+ - **Graphics:** \`cls([col])\`, \`spr(n,x,y,[w,h],[fx],[fy])\`, \`sspr(...)\`,
147
+ \`map(cx,cy,sx,sy,cw,ch,[layer])\`, \`pset/pget\`, \`sget/sset\`,
148
+ \`print(str,[x,y,[col]])\`, \`rect/rectfill\`, \`circ/circfill\`, \`oval/ovalfill\`,
149
+ \`line\`, \`pal(c0,c1,[p])\`, \`palt(col,t)\`, \`camera([x,y])\`, \`clip([x,y,w,h])\`,
150
+ \`color(col)\`, \`fillp(mask)\`, \`tline(...)\`, \`flip()\`.
151
+ - **Map / flags:** \`mget(x,y)\`, \`mset(x,y,v)\`, \`fget(n,[f])\`, \`fset(n,[f],v)\`.
152
+ - **Input:** \`btn([i,[p]])\`, \`btnp([i,[p]])\` (buttons 0..5 = left,right,up,down,O/Z,X).
153
+ - **Audio:** \`sfx(n,[channel,[offset]])\`, \`music([n,[fade,[mask]]])\`.
154
+ - **Math:** \`abs\`, \`flr\` (\`-flr(-x)\` = ceil), \`min\`, \`max\`, \`mid\`, \`rnd(x)\`,
155
+ \`srand(x)\`, \`sgn\`, \`sqrt\`, \`sin(x)\`/\`cos(x)\` (range 0..1, sin inverted),
156
+ \`atan2(x,y)\`.
157
+ - **Tables:** \`add\`, \`del\`, \`deli\`, \`count\`, \`all(t)\`, \`foreach(t,f)\`, \`pairs\`, \`ipairs\`.
158
+ - **Memory:** \`peek\`, \`poke\`, \`memcpy\`, \`memset\`, \`cstore\`, \`reload\`.
159
+ - **Cart data:** \`cartdata(id)\`, \`dget(i)\`, \`dset(i,v)\`.
160
+ - **Debug:** \`printh(str)\` (to host stdout, \`picopilot run\` captures it), \`stat(n)\`.
161
+
162
+ ## Gotchas that silently break carts (these differ from normal Lua/math)
163
+
164
+ These compile and run but behave WRONG. They are the traps models hit most:
165
+
166
+ - \`sin(t)\`/\`cos(t)\` take TURNS (0..1), NOT radians, and \`sin\` is INVERTED for
167
+ screen space (+y is down). A full circle is \`t: 0->1\`. Do not multiply by
168
+ \`2*pi\`; do not re-negate \`sin\`.
169
+ - \`/\` does NOT truncate (\`5/2\`=\`2.5\`). Integer divide is \`\\\` (\`5\\2\`=\`2\`), which
170
+ floors toward negative infinity (\`-5\\2\`=\`-3\`).
171
+ - \`rnd(x)\` returns a FLOAT in [0,x). Use \`flr(rnd(x))\` for an integer;
172
+ \`rnd(table)\` returns a random element.
173
+ - Tables are 1-BASED. \`del(t,val)\` removes by VALUE, \`deli(t,i)\` by INDEX.
174
+ Deleting the current item inside \`foreach\` is safe; inside an ascending
175
+ \`for i=1,#t\` it ERRORS once the list shrinks (iterate descending, or foreach).
176
+ - Draw state PERSISTS across frames: \`camera\`, \`pal\`, \`palt\`, \`clip\`, \`color\`,
177
+ \`fillp\` stay set until reset (no-arg form: \`camera()\`, \`pal()\`). Reset before
178
+ the HUD or offsets/palette swaps leak.
179
+ - Numbers are 16.16 fixed point; overflow WRAPS at ~32767. Drawing coords are
180
+ floored to integer pixels (keep true position as a float, let draw floor it).
181
+ - Variables are GLOBAL unless \`local\`. The \`x = cond and a or b\` ternary returns
182
+ \`b\` when \`a\` is falsy (\`false\`/\`nil\`); use an explicit \`if\` then.
183
+
184
+ (The \`picopilot-code\` skill expands these, and carries per-genre code references:
185
+ platformer / puzzle-grid / twin-stick-arcade / top-down-adventure and more.)
186
+
187
+ ## Token discipline (stay under 8192)
188
+
189
+ Models blow the budget by writing verbose Lua. Prefer PICO-8 shorthands:
190
+
191
+ - \`?\` for \`print\`.
192
+ - inline if: \`if(c) x=1\`.
193
+ - compound assignment: \`+=\`, \`-=\`, \`*=\`, \`/=\`.
194
+ - \`\\\` for integer divide.
195
+ - reuse locals; avoid needless globals.
196
+
197
+ Run \`picopilot tokens\` each iteration to see the count and where it goes; when
198
+ over budget, \`picopilot minify\` (safe-only) reclaims tokens without changing
199
+ behaviour.
200
+ `;
201
+ }
202
+ /**
203
+ * The complete set of files `picopilot init` writes into a fresh cart folder,
204
+ * in a stable order: the cart, its editable Lua, the agent-context reference,
205
+ * and the config. Exactly FOUR files (the acceptance contract). `AGENTS.md` is
206
+ * the cross-agent context filename; Claude Code, Codex, and others read it, so
207
+ * one portable file beats duplicating identical content into `CLAUDE.md`.
208
+ */
209
+ export function scaffoldFiles() {
210
+ return [
211
+ { name: 'main.p8', content: mainP8() },
212
+ { name: 'main.lua', content: mainLua() },
213
+ { name: 'AGENTS.md', content: agentsMd() },
214
+ { name: 'picopilot.json', content: picopilotJson() },
215
+ ];
216
+ }
217
+ //# sourceMappingURL=scaffold.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../../../src/engine/scaffold/scaffold.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,IAAI,EAAC,MAAM,kBAAkB,CAAC;AACtC,OAAO,EAAC,iBAAiB,EAAC,MAAM,oBAAoB,CAAC;AAErD;;;;;;;;;GASG;AAEH,0FAA0F;AAC1F,MAAM,YAAY,GAAG,EAAE,CAAC;AAExB;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,mBAAmB,CAAC;AAEpD;;;;GAIG;AACH,MAAM,UAAU,MAAM;IACrB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CACtB,sDAAsD,YAAY,aAAa,CAC/E,CAAC;IACF,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,GAAG,gBAAgB,IAAI,CAAC,CAAC;IAChD,OAAO,IAAI,CAAC,SAAS,EAAE,CAAC;AACzB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,OAAO;IACtB,OAAO;;;;;;;;;;;;;CAaP,CAAC;AACF,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,aAAa;IAC5B,OAAO,iBAAiB,EAAE,CAAC;AAC5B,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,QAAQ;IACvB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqIP,CAAC;AACF,CAAC;AAYD;;;;;;GAMG;AACH,MAAM,UAAU,aAAa;IAC5B,OAAO;QACN,EAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,EAAC;QACpC,EAAC,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,EAAC;QACtC,EAAC,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAC;QACxC,EAAC,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,aAAa,EAAE,EAAC;KAClD,CAAC;AACH,CAAC"}
@@ -0,0 +1,167 @@
1
+ /**
2
+ * The shrinko adapter SEAM: a typed interface every shrinko-backed command
3
+ * (`tokens`, and later `lint`, `minify`, `gfx import/export`, `verify`) talks
4
+ * to, plus the discriminated result shape they all return.
5
+ *
6
+ * The seam exists so the command layer never knows HOW shrinko is reached. The
7
+ * v1 implementation shells out to a user-installed Python `shrinko8` (see
8
+ * `shell.ts` and ADR-0001); a future native-TS implementation can implement the
9
+ * SAME {@link ShrinkoAdapter} interface and drop in without touching a single
10
+ * command. The command-facing contract is entirely in this file.
11
+ *
12
+ * The two-tier capability contract (ADR-0002) lives in the RESULT type: every
13
+ * adapter method returns a {@link ShrinkoResult}, a discriminated union of a
14
+ * success value OR the structured {@link ShrinkoNotFound} failure. Absent
15
+ * shrinko is therefore NOT an exception a command must try/catch, it is a
16
+ * first-class `{ok:false, reason:'shrinko-not-found', ...}` value a command maps
17
+ * onto incur's error envelope with a nonzero exit. Never a crash, never a hollow
18
+ * success.
19
+ */
20
+ /** The PyPI package name in the remedy: `uv pip install shrinko` (NOT `shrinko8`). */
21
+ export declare const SHRINKO_REMEDY: "uv pip install shrinko";
22
+ /** What shrinko needs to run: Python 3.8+ (the module is `shrinko8`). */
23
+ export declare const SHRINKO_NEEDS: readonly ["python>=3.8"];
24
+ /**
25
+ * The structured "shrinko is not installed" failure (ADR-0002). Its shape is
26
+ * fixed by the prd's two-tier contract (US #17): `ok:false`, a machine-readable
27
+ * `reason`, the EXACT `remedy` string, and the `needs` list. A command turns
28
+ * this into an incur `error({ code: reason, ... })` with a nonzero exit.
29
+ */
30
+ export interface ShrinkoNotFound {
31
+ readonly ok: false;
32
+ readonly reason: 'shrinko-not-found';
33
+ /** The EXACT remedy: `uv pip install shrinko` (package is `shrinko`, module `shrinko8`). */
34
+ readonly remedy: typeof SHRINKO_REMEDY;
35
+ /** Prerequisites the remedy assumes are present. */
36
+ readonly needs: readonly string[];
37
+ }
38
+ /** A successful adapter call carrying the parsed value. */
39
+ export interface ShrinkoOk<T> {
40
+ readonly ok: true;
41
+ readonly value: T;
42
+ }
43
+ /**
44
+ * The result every {@link ShrinkoAdapter} method returns: either a parsed value
45
+ * or the structured {@link ShrinkoNotFound}. Absence is a value, not a throw, so
46
+ * the two-tier contract is total and type-checked at every call site.
47
+ */
48
+ export type ShrinkoResult<T> = ShrinkoOk<T> | ShrinkoNotFound;
49
+ /**
50
+ * The parsed `--count` report (US #3). `tokens`/`chars`/`compressed` are the
51
+ * raw counts shrinko prints; `pct` fields are shrinko's own reported
52
+ * percentages (its `--count` prints `tokens: 8053 98%`, i.e. against PICO-8's
53
+ * limits), preserved verbatim so a command can show them without recomputing.
54
+ * `compressed`/`compressedPct` are optional because shrinko omits the
55
+ * compressed line for input formats where it does not apply.
56
+ */
57
+ export interface CountReport {
58
+ readonly tokens: number;
59
+ readonly tokensPct: number;
60
+ readonly chars: number;
61
+ readonly charsPct: number;
62
+ readonly compressed: number | undefined;
63
+ readonly compressedPct: number | undefined;
64
+ }
65
+ /**
66
+ * The shrinko capability seam. One method per shrinko-backed operation; only
67
+ * `count` exists in this task, the rest (lint, minify, convert) land with their
68
+ * commands and extend THIS interface rather than forking a new one.
69
+ *
70
+ * Every method is async (the shell-out is async) and returns a
71
+ * {@link ShrinkoResult}, so absence is handled uniformly at each seam.
72
+ */
73
+ export interface ShrinkoAdapter {
74
+ /**
75
+ * Runs shrinko's `--count` on the cart at `cartPath` and parses the
76
+ * `tokens/chars/compressed` report. Returns {@link ShrinkoNotFound} when
77
+ * shrinko is not installed.
78
+ */
79
+ count(cartPath: string): Promise<ShrinkoResult<CountReport>>;
80
+ /**
81
+ * Runs shrinko's `--lint` on the cart at `cartPath` and parses the warnings
82
+ * (undefined globals, unused/duplicate locals, etc.) into structured
83
+ * {@link LintFinding}s. Lint warnings are DATA, not a failure: the report is
84
+ * returned with `findings` (possibly empty), not a throw. Returns
85
+ * {@link ShrinkoNotFound} when shrinko is not installed.
86
+ */
87
+ lint(cartPath: string): Promise<ShrinkoResult<LintReport>>;
88
+ /**
89
+ * Runs shrinko's SAFE minification on `cartPath` and writes the result to
90
+ * `outPath` (a SEPARATE artifact; the source is never touched, see ADR-0007).
91
+ * Returns the before/after token counts. Returns {@link ShrinkoNotFound} when
92
+ * shrinko is not installed.
93
+ */
94
+ minify(cartPath: string, outPath: string): Promise<ShrinkoResult<MinifyReport>>;
95
+ /**
96
+ * Exports the cart's spritesheet as a 128x128 PNG to `pngPath` (a raw
97
+ * round-trip image for external tools, distinct from `gfx render`'s upscaled
98
+ * JUDGE image). Returns {@link ShrinkoNotFound} when shrinko is not installed.
99
+ */
100
+ exportSpritesheet(cartPath: string, pngPath: string): Promise<ShrinkoResult<void>>;
101
+ /**
102
+ * Imports a 128x128 spritesheet PNG into `srcCartPath`'s `__gfx__`, MERGING
103
+ * (only the gfx section is replaced; code/map/sfx/music are preserved) and
104
+ * writing the result to `outCartPath`. Returns {@link ShrinkoNotFound} when
105
+ * shrinko is not installed.
106
+ */
107
+ importSpritesheet(srcCartPath: string, pngPath: string, outCartPath: string): Promise<ShrinkoResult<void>>;
108
+ }
109
+ /** One shrinko lint warning, parsed from a `file:line:col: message` line. */
110
+ export interface LintFinding {
111
+ readonly line: number;
112
+ readonly col: number;
113
+ readonly message: string;
114
+ }
115
+ /** The parsed `--lint` result: every warning shrinko reported (empty when clean). */
116
+ export interface LintReport {
117
+ readonly findings: readonly LintFinding[];
118
+ }
119
+ /** The parsed `minify` result: token counts before and after (ADR-0007's delta report). */
120
+ export interface MinifyReport {
121
+ /** Tokens in the source cart. */
122
+ readonly beforeTokens: number;
123
+ /** Tokens in the minified artifact. */
124
+ readonly afterTokens: number;
125
+ /** Absolute path of the written minified cart. */
126
+ readonly outPath: string;
127
+ }
128
+ /** The frozen {@link ShrinkoNotFound} value, so every call site returns the same shape. */
129
+ export declare function shrinkoNotFound(): ShrinkoNotFound;
130
+ /**
131
+ * Parses shrinko's `--count` output into a {@link CountReport}.
132
+ *
133
+ * shrinko prints lines like:
134
+ *
135
+ * ```
136
+ * tokens: 8053 98%
137
+ * chars: 30320 46%
138
+ * compressed: 12176 77%
139
+ * ```
140
+ *
141
+ * (the compressed line is absent for some input formats). Parsing is
142
+ * line-oriented and tolerant of surrounding output/whitespace: it scans for the
143
+ * `tokens:`/`chars:`/`compressed:` labels anywhere in the combined stdout+stderr
144
+ * and pulls the count and percentage off each. Throws if the required
145
+ * `tokens:`/`chars:` lines are missing, so a shrinko that ran but produced
146
+ * unparseable output surfaces as a real error rather than a silent zero.
147
+ */
148
+ export declare function parseCount(output: string): CountReport;
149
+ /**
150
+ * Parses shrinko's `--lint` output into {@link LintFinding}s. shrinko prints a
151
+ * `Lint warnings:` header then one `file:line:col: message` per warning; a clean
152
+ * cart prints nothing. Parsing scans the combined stdout+stderr for the
153
+ * `:line:col: message` shape (tolerant of the filename prefix, which we drop),
154
+ * so an empty result is a CLEAN cart, not an error. Unlike parseCount this never
155
+ * throws: no recognisable lines simply means zero findings.
156
+ */
157
+ export declare function parseLint(output: string): LintReport;
158
+ /**
159
+ * Thrown when shrinko RAN but its output could not be parsed (a shrinko
160
+ * version/format drift), distinct from shrinko being ABSENT (which is the
161
+ * structured {@link ShrinkoNotFound} value, not a throw). Commands surface this
162
+ * as a `shrinko-failed` error, separate from `shrinko-not-found`.
163
+ */
164
+ export declare class ShrinkoParseError extends Error {
165
+ readonly name = "ShrinkoParseError";
166
+ }
167
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../src/engine/shrinko/adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,sFAAsF;AACtF,eAAO,MAAM,cAAc,EAAG,wBAAiC,CAAC;AAEhE,yEAAyE;AACzE,eAAO,MAAM,aAAa,0BAA2B,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IACnB,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAC;IACrC,4FAA4F;IAC5F,QAAQ,CAAC,MAAM,EAAE,OAAO,cAAc,CAAC;IACvC,oDAAoD;IACpD,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC;AAED,2DAA2D;AAC3D,MAAM,WAAW,SAAS,CAAC,CAAC;IAC3B,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAClB,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;CAClB;AAED;;;;GAIG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,GAAG,eAAe,CAAC;AAE9D;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3C;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,cAAc;IAC9B;;;;OAIG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC;IAE7D;;;;;;OAMG;IACH,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC;IAE3D;;;;;OAKG;IACH,MAAM,CACL,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GACb,OAAO,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC,CAAC;IAExC;;;;OAIG;IACH,iBAAiB,CAChB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GACb,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC;IAEhC;;;;;OAKG;IACH,iBAAiB,CAChB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,GACjB,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC;CAChC;AAED,6EAA6E;AAC7E,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED,qFAAqF;AACrF,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAC;CAC1C;AAED,2FAA2F;AAC3F,MAAM,WAAW,YAAY;IAC5B,iCAAiC;IACjC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,uCAAuC;IACvC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,kDAAkD;IAClD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED,2FAA2F;AAC3F,wBAAgB,eAAe,IAAI,eAAe,CAOjD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CA2BtD;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAapD;AAED;;;;;GAKG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;IAC3C,SAAkB,IAAI,uBAAuB;CAC7C"}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * The shrinko adapter SEAM: a typed interface every shrinko-backed command
3
+ * (`tokens`, and later `lint`, `minify`, `gfx import/export`, `verify`) talks
4
+ * to, plus the discriminated result shape they all return.
5
+ *
6
+ * The seam exists so the command layer never knows HOW shrinko is reached. The
7
+ * v1 implementation shells out to a user-installed Python `shrinko8` (see
8
+ * `shell.ts` and ADR-0001); a future native-TS implementation can implement the
9
+ * SAME {@link ShrinkoAdapter} interface and drop in without touching a single
10
+ * command. The command-facing contract is entirely in this file.
11
+ *
12
+ * The two-tier capability contract (ADR-0002) lives in the RESULT type: every
13
+ * adapter method returns a {@link ShrinkoResult}, a discriminated union of a
14
+ * success value OR the structured {@link ShrinkoNotFound} failure. Absent
15
+ * shrinko is therefore NOT an exception a command must try/catch, it is a
16
+ * first-class `{ok:false, reason:'shrinko-not-found', ...}` value a command maps
17
+ * onto incur's error envelope with a nonzero exit. Never a crash, never a hollow
18
+ * success.
19
+ */
20
+ /** The PyPI package name in the remedy: `uv pip install shrinko` (NOT `shrinko8`). */
21
+ export const SHRINKO_REMEDY = 'uv pip install shrinko';
22
+ /** What shrinko needs to run: Python 3.8+ (the module is `shrinko8`). */
23
+ export const SHRINKO_NEEDS = ['python>=3.8'];
24
+ /** The frozen {@link ShrinkoNotFound} value, so every call site returns the same shape. */
25
+ export function shrinkoNotFound() {
26
+ return {
27
+ ok: false,
28
+ reason: 'shrinko-not-found',
29
+ remedy: SHRINKO_REMEDY,
30
+ needs: [...SHRINKO_NEEDS],
31
+ };
32
+ }
33
+ /**
34
+ * Parses shrinko's `--count` output into a {@link CountReport}.
35
+ *
36
+ * shrinko prints lines like:
37
+ *
38
+ * ```
39
+ * tokens: 8053 98%
40
+ * chars: 30320 46%
41
+ * compressed: 12176 77%
42
+ * ```
43
+ *
44
+ * (the compressed line is absent for some input formats). Parsing is
45
+ * line-oriented and tolerant of surrounding output/whitespace: it scans for the
46
+ * `tokens:`/`chars:`/`compressed:` labels anywhere in the combined stdout+stderr
47
+ * and pulls the count and percentage off each. Throws if the required
48
+ * `tokens:`/`chars:` lines are missing, so a shrinko that ran but produced
49
+ * unparseable output surfaces as a real error rather than a silent zero.
50
+ */
51
+ export function parseCount(output) {
52
+ const line = (label) => {
53
+ // `label: <count> <pct>%` (count and percent may be separated by any run
54
+ // of spaces; the `%` is optional-tolerant but shrinko always prints it).
55
+ const re = new RegExp(`^\\s*${label}:\\s*(\\d+)\\s+(\\d+)\\s*%`, 'm');
56
+ const m = re.exec(output);
57
+ if (m === null)
58
+ return undefined;
59
+ return { n: Number(m[1]), pct: Number(m[2]) };
60
+ };
61
+ const tokens = line('tokens');
62
+ const chars = line('chars');
63
+ if (tokens === undefined || chars === undefined) {
64
+ throw new ShrinkoParseError(`could not parse shrinko --count output (missing tokens/chars lines):\n${output}`);
65
+ }
66
+ const compressed = line('compressed');
67
+ return {
68
+ tokens: tokens.n,
69
+ tokensPct: tokens.pct,
70
+ chars: chars.n,
71
+ charsPct: chars.pct,
72
+ compressed: compressed?.n,
73
+ compressedPct: compressed?.pct,
74
+ };
75
+ }
76
+ /**
77
+ * Parses shrinko's `--lint` output into {@link LintFinding}s. shrinko prints a
78
+ * `Lint warnings:` header then one `file:line:col: message` per warning; a clean
79
+ * cart prints nothing. Parsing scans the combined stdout+stderr for the
80
+ * `:line:col: message` shape (tolerant of the filename prefix, which we drop),
81
+ * so an empty result is a CLEAN cart, not an error. Unlike parseCount this never
82
+ * throws: no recognisable lines simply means zero findings.
83
+ */
84
+ export function parseLint(output) {
85
+ const findings = [];
86
+ // Match `<anything>:<line>:<col>: <message>` (the filename may itself contain
87
+ // colons/paths, so anchor on the two trailing numeric groups before the msg).
88
+ const re = /:(\d+):(\d+):\s*(.+?)\s*$/gm;
89
+ for (const m of output.matchAll(re)) {
90
+ findings.push({
91
+ line: Number(m[1]),
92
+ col: Number(m[2]),
93
+ message: m[3] ?? '',
94
+ });
95
+ }
96
+ return { findings };
97
+ }
98
+ /**
99
+ * Thrown when shrinko RAN but its output could not be parsed (a shrinko
100
+ * version/format drift), distinct from shrinko being ABSENT (which is the
101
+ * structured {@link ShrinkoNotFound} value, not a throw). Commands surface this
102
+ * as a `shrinko-failed` error, separate from `shrinko-not-found`.
103
+ */
104
+ export class ShrinkoParseError extends Error {
105
+ name = 'ShrinkoParseError';
106
+ }
107
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../../src/engine/shrinko/adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,sFAAsF;AACtF,MAAM,CAAC,MAAM,cAAc,GAAG,wBAAiC,CAAC;AAEhE,yEAAyE;AACzE,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,aAAa,CAAU,CAAC;AAgItD,2FAA2F;AAC3F,MAAM,UAAU,eAAe;IAC9B,OAAO;QACN,EAAE,EAAE,KAAK;QACT,MAAM,EAAE,mBAAmB;QAC3B,MAAM,EAAE,cAAc;QACtB,KAAK,EAAE,CAAC,GAAG,aAAa,CAAC;KACzB,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,UAAU,CAAC,MAAc;IACxC,MAAM,IAAI,GAAG,CAAC,KAAa,EAAwC,EAAE;QACpE,yEAAyE;QACzE,yEAAyE;QACzE,MAAM,EAAE,GAAG,IAAI,MAAM,CAAC,QAAQ,KAAK,4BAA4B,EAAE,GAAG,CAAC,CAAC;QACtE,MAAM,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC1B,IAAI,CAAC,KAAK,IAAI;YAAE,OAAO,SAAS,CAAC;QACjC,OAAO,EAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAC,CAAC;IAC7C,CAAC,CAAC;IAEF,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC9B,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;IAC5B,IAAI,MAAM,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACjD,MAAM,IAAI,iBAAiB,CAC1B,yEAAyE,MAAM,EAAE,CACjF,CAAC;IACH,CAAC;IACD,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC;IAEtC,OAAO;QACN,MAAM,EAAE,MAAM,CAAC,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC,GAAG;QACrB,KAAK,EAAE,KAAK,CAAC,CAAC;QACd,QAAQ,EAAE,KAAK,CAAC,GAAG;QACnB,UAAU,EAAE,UAAU,EAAE,CAAC;QACzB,aAAa,EAAE,UAAU,EAAE,GAAG;KAC9B,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,SAAS,CAAC,MAAc;IACvC,MAAM,QAAQ,GAAkB,EAAE,CAAC;IACnC,8EAA8E;IAC9E,8EAA8E;IAC9E,MAAM,EAAE,GAAG,6BAA6B,CAAC;IACzC,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;QACrC,QAAQ,CAAC,IAAI,CAAC;YACb,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAClB,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACjB,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE;SACnB,CAAC,CAAC;IACJ,CAAC;IACD,OAAO,EAAC,QAAQ,EAAC,CAAC;AACnB,CAAC;AAED;;;;;GAKG;AACH,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IACzB,IAAI,GAAG,mBAAmB,CAAC;CAC7C"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The shrinko adapter seam: how every shrinko-backed command reaches shrinko8.
3
+ *
4
+ * `adapter.ts` is the command-facing CONTRACT (the {@link ShrinkoAdapter}
5
+ * interface, the {@link ShrinkoResult} discriminated union, the structured
6
+ * {@link ShrinkoNotFound} value, and the `--count` parser). `shell.ts` is the v1
7
+ * IMPLEMENTATION that shells out to a user-installed Python `shrinko8`
8
+ * (ADR-0001). A future native-TS implementation implements the same interface
9
+ * and drops in without touching the command layer.
10
+ */
11
+ export { type CountReport, type LintFinding, type LintReport, type MinifyReport, parseCount, parseLint, SHRINKO_NEEDS, SHRINKO_REMEDY, type ShrinkoAdapter, type ShrinkoNotFound, shrinkoNotFound, type ShrinkoOk, ShrinkoParseError, type ShrinkoResult, } from './adapter.js';
12
+ export { type ChildResult, type ChildRunner, execFileRunner, ShellShrinkoAdapter, type ShellShrinkoOptions, } from './shell.js';
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/engine/shrinko/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EACN,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,UAAU,EACV,SAAS,EACT,aAAa,EACb,cAAc,EACd,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,eAAe,EACf,KAAK,SAAS,EACd,iBAAiB,EACjB,KAAK,aAAa,GAClB,MAAM,cAAc,CAAC;AACtB,OAAO,EACN,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,cAAc,EACd,mBAAmB,EACnB,KAAK,mBAAmB,GACxB,MAAM,YAAY,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The shrinko adapter seam: how every shrinko-backed command reaches shrinko8.
3
+ *
4
+ * `adapter.ts` is the command-facing CONTRACT (the {@link ShrinkoAdapter}
5
+ * interface, the {@link ShrinkoResult} discriminated union, the structured
6
+ * {@link ShrinkoNotFound} value, and the `--count` parser). `shell.ts` is the v1
7
+ * IMPLEMENTATION that shells out to a user-installed Python `shrinko8`
8
+ * (ADR-0001). A future native-TS implementation implements the same interface
9
+ * and drops in without touching the command layer.
10
+ */
11
+ export { parseCount, parseLint, SHRINKO_NEEDS, SHRINKO_REMEDY, shrinkoNotFound, ShrinkoParseError, } from './adapter.js';
12
+ export { execFileRunner, ShellShrinkoAdapter, } from './shell.js';
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/engine/shrinko/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAKN,UAAU,EACV,SAAS,EACT,aAAa,EACb,cAAc,EAGd,eAAe,EAEf,iBAAiB,GAEjB,MAAM,cAAc,CAAC;AACtB,OAAO,EAGN,cAAc,EACd,mBAAmB,GAEnB,MAAM,YAAY,CAAC"}