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,241 @@
1
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { isAbsolute, resolve } from 'node:path';
3
+ import { Cli, z } from 'incur';
4
+ import { MusicError, patternsToMusic, SFX_INDEX_MAX, setMusicSection, } from '../engine/audio/index.js';
5
+ import { Cart, CartParseError } from '../engine/cart/index.js';
6
+ /** Resolves a path argument to an absolute path (relative to cwd). */
7
+ function resolvePath(p) {
8
+ return isAbsolute(p) ? p : resolve(process.cwd(), p);
9
+ }
10
+ /**
11
+ * Loads + parses a cart, returning either the parsed cart or a structured reason
12
+ * the caller turns into an `error(...)`. Mirrors the sfx/gfx command loaders.
13
+ */
14
+ function loadCart(cartPath) {
15
+ if (!existsSync(cartPath)) {
16
+ return {
17
+ ok: false,
18
+ code: 'cart-not-found',
19
+ message: `no cart at ${cartPath}`,
20
+ };
21
+ }
22
+ const text = readFileSync(cartPath, 'utf8');
23
+ try {
24
+ return { ok: true, cart: Cart.parse(text) };
25
+ }
26
+ catch (e) {
27
+ if (e instanceof CartParseError) {
28
+ return {
29
+ ok: false,
30
+ code: 'cart-parse-failed',
31
+ message: `cart at ${cartPath} does not parse (${e.code}): ${e.message}`,
32
+ };
33
+ }
34
+ throw e;
35
+ }
36
+ }
37
+ /**
38
+ * Reads the pattern-list JSON into the codec's `Pattern[]` model (finding B.6).
39
+ *
40
+ * The serialization is JSON: an array of `{channels:[c0,c1,c2,c3], loopStart?,
41
+ * loopBack?, stop?}` (see the command help). This maps 1:1 to the finding's B.6
42
+ * model, so an author (human or agent) writes exactly that structure. An SFX
43
+ * reference is a number 0..63; an OFF channel is `null` (distinct from sfx 0, per
44
+ * finding B.7 #9). This parser is deliberately structural-only: it accepts the
45
+ * shape and hands it to the codec, which owns range/count validation.
46
+ */
47
+ function parsePatterns(text) {
48
+ let raw;
49
+ try {
50
+ raw = JSON.parse(text);
51
+ }
52
+ catch (e) {
53
+ return {
54
+ ok: false,
55
+ code: 'audio-music-parse-error',
56
+ message: `pattern list is not valid JSON: ${e instanceof Error ? e.message : String(e)}. Expected an array of patterns, e.g. [{"channels":[0,1,null,3],"loopStart":true}].`,
57
+ };
58
+ }
59
+ if (!Array.isArray(raw)) {
60
+ return {
61
+ ok: false,
62
+ code: 'audio-music-parse-error',
63
+ message: 'pattern list must be a JSON array of patterns, e.g. [{"channels":[0,1,null,3]}]. Each channel is an SFX index 0..63 or null (off).',
64
+ };
65
+ }
66
+ // Hand the array straight to the codec as Pattern[]; the codec validates
67
+ // channel count + sfx range structurally and refuses out-of-range values.
68
+ return { ok: true, patterns: raw };
69
+ }
70
+ /**
71
+ * Builds the `music` command group. Currently just `music from-patterns` (the
72
+ * structural pattern-list to `__music__` codec + cart merge).
73
+ */
74
+ function buildMusicGroup() {
75
+ const music = Cli.create('music', {
76
+ description: 'Assemble music STRUCTURALLY: an ordered list of up-to-4 SFX-channel references per pattern -> __music__. There is NO melodic content in __music__; melody lives in the referenced SFX (built by `sfx from-mml`). Pattern LENGTH is inherited from the referenced SFX (the left-most non-looping channel), NOT stored per pattern.',
77
+ });
78
+ music.command('from-patterns', {
79
+ description: 'Assemble a JSON pattern list into __music__ and merge it into the cart (only __music__ changes; every other section incl. __sfx__ stays byte-identical). A pattern is {"channels":[c0,c1,c2,c3], loopStart?, loopBack?, stop?}: each channel is an SFX index 0-63 OR null = OFF (silent this pattern; null is NOT sfx 0). Flags: loopStart (0x01), loopBack (0x02, jump back to loop-start), stop (0x04). Pattern order = song order. Pattern length is INHERITED from the referenced SFX (its speed + LEN via `sfx from-mml`), so no per-pattern length is stored. Out-of-range SFX index is refused; loopBack with no loopStart warns (PICO-8 falls back to pattern 0), not a crash.',
80
+ args: z.object({
81
+ patterns: z
82
+ .string()
83
+ .optional()
84
+ .describe('The pattern list as JSON: an array of {"channels":[c0,c1,c2,c3], loopStart?, loopBack?, stop?}. A channel is an SFX index 0-63 or null (off). Omit and pass --file to read from a file instead. Example: \'[{"channels":[0,1,null,3],"loopStart":true},{"channels":[2,2,2,2],"loopBack":true,"stop":true}]\'.'),
85
+ }),
86
+ options: z.object({
87
+ cart: z
88
+ .string()
89
+ .default('main.p8')
90
+ .describe('The .p8 cart to merge into. Defaults to main.p8 in the current folder. (An option, not a positional, so it never collides with an omitted `patterns` when using --file.)'),
91
+ file: z
92
+ .string()
93
+ .optional()
94
+ .describe('Read the JSON pattern list from this file instead of the `patterns` argument (for longer songs).'),
95
+ }),
96
+ output: z.object({
97
+ cart: z
98
+ .string()
99
+ .describe('The absolute path of the cart that was written.'),
100
+ patterns: z
101
+ .number()
102
+ .describe('The number of patterns written (= song length).'),
103
+ section: z
104
+ .string()
105
+ .describe('The __music__ section body that was merged.'),
106
+ warnings: z
107
+ .array(z.string())
108
+ .describe('Advisory warnings (e.g. loopBack with no loopStart -> PICO-8 loops to pattern 0).'),
109
+ }),
110
+ examples: [
111
+ {
112
+ description: 'A two-pattern loop (pattern 0 loops back to itself)',
113
+ args: {
114
+ patterns: '[{"channels":[0,1,2,3],"loopStart":true},{"channels":[0,1,2,3],"loopBack":true}]',
115
+ },
116
+ },
117
+ {
118
+ description: 'A pattern with channel 2 OFF (silent), not sfx 0',
119
+ args: { patterns: '[{"channels":[0,1,null,3]}]' },
120
+ options: { cart: 'game.p8' },
121
+ },
122
+ {
123
+ description: 'Read a longer song from a file',
124
+ args: {},
125
+ options: { file: 'song.json' },
126
+ },
127
+ ],
128
+ run({ args, options, error, ok }) {
129
+ // Resolve the pattern-list source: the `patterns` arg OR `--file` (exactly
130
+ // one). Pure text, shrinko-free + PICO-8-free (mirrors `sfx from-mml`).
131
+ let text;
132
+ if (options.file !== undefined) {
133
+ if (args.patterns !== undefined) {
134
+ return error({
135
+ code: 'ambiguous-input',
136
+ message: 'pass EITHER the patterns argument OR --file, not both (they are two ways to give the same source).',
137
+ exitCode: 1,
138
+ });
139
+ }
140
+ const filePath = resolvePath(options.file);
141
+ if (!existsSync(filePath)) {
142
+ return error({
143
+ code: 'patterns-file-not-found',
144
+ message: `no pattern-list file at ${filePath}`,
145
+ exitCode: 1,
146
+ });
147
+ }
148
+ text = readFileSync(filePath, 'utf8');
149
+ }
150
+ else if (args.patterns !== undefined) {
151
+ text = args.patterns;
152
+ }
153
+ else {
154
+ return error({
155
+ code: 'no-patterns',
156
+ message: 'no pattern list: pass it as the `patterns` argument (JSON) or via --file.',
157
+ exitCode: 1,
158
+ });
159
+ }
160
+ const parsed = parsePatterns(text);
161
+ if (!parsed.ok) {
162
+ return error({ code: parsed.code, message: parsed.message, exitCode: 1 });
163
+ }
164
+ // Transpile FIRST: a bad list is a caller error and must never touch the
165
+ // cart (no bytes change on a refusal), mirroring `sfx from-mml`.
166
+ let result;
167
+ try {
168
+ result = patternsToMusic(parsed.patterns);
169
+ }
170
+ catch (e) {
171
+ if (e instanceof MusicError) {
172
+ const cta = e.code === 'audio-music-sfx-out-of-range'
173
+ ? {
174
+ description: `SFX indices are a hard range (0..${SFX_INDEX_MAX}); to silence a channel use null, not an out-of-range index:`,
175
+ commands: [
176
+ {
177
+ command: 'sfx from-mml <slot> "<mml>"',
178
+ description: `Author the SFX you reference first (slots 0..${SFX_INDEX_MAX}), then arrange them here.`,
179
+ },
180
+ ],
181
+ }
182
+ : undefined;
183
+ return error({
184
+ code: e.code,
185
+ message: e.message,
186
+ exitCode: 1,
187
+ ...(cta ? { cta } : {}),
188
+ });
189
+ }
190
+ throw e;
191
+ }
192
+ const cartPath = resolvePath(options.cart);
193
+ const loaded = loadCart(cartPath);
194
+ if (!loaded.ok) {
195
+ return error({ code: loaded.code, message: loaded.message, exitCode: 1 });
196
+ }
197
+ // Replace __music__ ONLY, through the cart model, so every other section
198
+ // (and __sfx__) stays byte-identical.
199
+ setMusicSection(loaded.cart, result.section);
200
+ writeFileSync(cartPath, loaded.cart.serialize());
201
+ const warnings = result.warnings.map((w) => w.message);
202
+ // CTA toward the verify/hear loop: a merged song is next confirmed by
203
+ // verify (static integrity) and heard via `audio render`.
204
+ return ok({
205
+ cart: cartPath,
206
+ patterns: result.patterns,
207
+ section: result.section,
208
+ warnings,
209
+ }, {
210
+ cta: {
211
+ description: 'The song is merged. Confirm it, then hear it:',
212
+ commands: [
213
+ {
214
+ command: 'verify',
215
+ description: 'Static gate: the cart still parses + is within budget.',
216
+ },
217
+ {
218
+ command: 'audio render',
219
+ description: 'Record a WAV to hear the result (real-time capture, requires PICO-8).',
220
+ },
221
+ ],
222
+ },
223
+ });
224
+ },
225
+ });
226
+ return music;
227
+ }
228
+ /**
229
+ * Registers the `music` command group (`music from-patterns`) on the root CLI.
230
+ *
231
+ * `music` is a shrinko-FREE and PICO-8-FREE command group: it assembles a
232
+ * structural pattern list into `__music__` and merges it through the cart model
233
+ * entirely in TS, so it works with both absent and never spawns a child process.
234
+ * Music is STRUCTURAL (ADR-0005 + the audio spike finding A.5/B.6): a pattern
235
+ * list of SFX references, NOT a notation. "Off" (bit6) is distinct from "sfx 0",
236
+ * and pattern length is inherited from the referenced SFX (stored nowhere here).
237
+ */
238
+ export function registerMusic(cli) {
239
+ cli.command(buildMusicGroup());
240
+ }
241
+ //# sourceMappingURL=music.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"music.js","sourceRoot":"","sources":["../../src/commands/music.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import { type Cli } from 'incur';
2
+ import { type Pico8Adapter } from '../engine/pico8/index.js';
3
+ /**
4
+ * Injects the PICO-8 adapter `playtest` uses (defaults to the shell adapter over
5
+ * native `pico8 -x`). Mirrors `run`'s `Pico8AdapterFactory` seam: a test passes a
6
+ * stub to drive the drive-orchestration + the absent path WITHOUT the paid
7
+ * binary, and to isolate the child `env` (its `PICO8_PATH`/`PATH`).
8
+ */
9
+ export type Pico8AdapterFactory = (env: NodeJS.ProcessEnv) => Pico8Adapter;
10
+ /**
11
+ * Registers `picopilot playtest`, the driven-playtest command group (ADR-0011).
12
+ * The ROOT command (`playtest <cart>`) is the ONE-SHOT driver (US #1-5,7-11): it
13
+ * sends the whole scripted input up front and captures gameplay in one envelope.
14
+ * The SUBCOMMANDS are the RESUMABLE LIVE SESSION (US #6, the "A" model): `start`
15
+ * launches a persistent driven process the agent addresses BY ID, and `step` /
16
+ * `input` / `shot` / `stop` / `status` drive it across SEPARATE invocations while
17
+ * the game stays alive + paused between turns, over the same verified block+ACK
18
+ * transport. The cart-side drive-transform is reused UNCHANGED (see `drive.ts`).
19
+ *
20
+ * PICO-8 absent -> structured `pico8-not-found` + nonzero exit (mirrors `run`),
21
+ * never a crash or a hang. The CI-testable surface is the pure transform + the
22
+ * session handshake core (against a fake process); a live multi-turn session is
23
+ * the manual/opt-in tier.
24
+ *
25
+ * @param adapterFactory injects the adapter (defaults to the shell adapter);
26
+ * tests pass a stub to drive present/absent without the real binary.
27
+ */
28
+ export declare function registerPlaytest(cli: Cli.Cli, adapterFactory?: Pico8AdapterFactory): void;
29
+ //# sourceMappingURL=playtest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"playtest.d.ts","sourceRoot":"","sources":["../../src/commands/playtest.ts"],"names":[],"mappings":"AASA,OAAO,EAAC,KAAK,GAAG,EAAqB,MAAM,OAAO,CAAC;AACnD,OAAO,EAMN,KAAK,YAAY,EAajB,MAAM,0BAA0B,CAAC;AAElC;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,KAAK,YAAY,CAAC;AAiD3E;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAC/B,GAAG,EAAE,GAAG,CAAC,GAAG,EACZ,cAAc,GAAE,mBAA2C,GACzD,IAAI,CAEN"}