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,151 @@
1
+ import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
2
+ import { isAbsolute, join, resolve } from 'node:path';
3
+ import { z } from 'incur';
4
+ import { scaffoldFiles } from '../engine/scaffold/index.js';
5
+ import { installSkills, } from '../engine/skills/index.js';
6
+ /**
7
+ * Registers `picopilot init`, the scaffolder that turns a fresh folder into an
8
+ * agent-ready PICO-8 cart (US #1, #2).
9
+ *
10
+ * INSTRUCT-not-mutate (US #2): by default `init` writes ONLY inside the target
11
+ * cart folder, the `main.p8` (`__lua__` = `#include main.lua`), the editable
12
+ * `main.lua`, the curated `AGENTS.md` reference, and `picopilot.json`. It does
13
+ * NOT run `git init` and does NOT write to shared agent skill dirs; it PRINTS a
14
+ * skills-discovery tip and a `git init` tip instead. The global skills install
15
+ * is the opt-in `init --install-skills`, a SEPARATE task, so the default is a
16
+ * good citizen of `~/.claude` / agent dirs and of the user's VCS choice.
17
+ *
18
+ * The opt-in `init --install-skills` (US #2, #20) IS the shared write: it runs
19
+ * incur's skills install (which defaults to a GLOBAL write into `~/.agents/skills`
20
+ * + detected agent dirs) so the discipline skills are auto-discovered. It is the
21
+ * only picopilot path that touches a shared location, so the installer is an
22
+ * injected seam and its target is fully redirectable for isolation-testing.
23
+ *
24
+ * @param installer injects the skills installer (defaults to the real global
25
+ * install); tests pass one that redirects the target to a temp dir.
26
+ */
27
+ export function registerInit(cli, installer = installSkills) {
28
+ cli.command('init', {
29
+ description: 'Scaffold an agent-ready PICO-8 cart (main.p8 + main.lua + AGENTS.md + picopilot.json).',
30
+ args: z.object({
31
+ dir: z
32
+ .string()
33
+ .default('.')
34
+ .describe('Target cart folder (created if missing). Defaults to the current directory.'),
35
+ }),
36
+ options: z.object({
37
+ installSkills: z
38
+ .boolean()
39
+ .default(false)
40
+ .describe("Opt in to INSTALLING picopilot's skills into your agent's skill dirs (a GLOBAL write). Default off: init only PRINTS how to discover them."),
41
+ global: z
42
+ .boolean()
43
+ .default(true)
44
+ .describe('With --install-skills, install GLOBALLY (default). Pass --no-global to install into THIS project (./.agents/skills) instead.'),
45
+ }),
46
+ output: z.object({
47
+ dir: z
48
+ .string()
49
+ .describe('The absolute path of the scaffolded cart folder.'),
50
+ files: z
51
+ .array(z.string())
52
+ .describe('The cart-relative names of the files written.'),
53
+ tips: z
54
+ .array(z.string())
55
+ .describe('One-line next-step tips (skills discovery + git init). NOT actions init took.'),
56
+ installedSkills: z
57
+ .array(z.string())
58
+ .optional()
59
+ .describe('Skills installed by --install-skills (absent unless the flag was passed).'),
60
+ wiredAgents: z
61
+ .array(z.string())
62
+ .optional()
63
+ .describe('Agents whose skill dirs --install-skills wired up (absent unless flagged).'),
64
+ }),
65
+ examples: [
66
+ { description: 'Scaffold in the current folder' },
67
+ { description: 'Scaffold into a new folder', args: { dir: 'my-game' } },
68
+ {
69
+ description: "Scaffold and install picopilot's skills globally",
70
+ options: { installSkills: true },
71
+ },
72
+ ],
73
+ async run({ args, options, error }) {
74
+ const targetDir = isAbsolute(args.dir)
75
+ ? args.dir
76
+ : resolve(process.cwd(), args.dir);
77
+ const files = scaffoldFiles();
78
+ // Refuse rather than clobber: `init` is for a fresh cart. Overwriting an
79
+ // existing main.p8/main.lua would silently destroy the agent's work, so a
80
+ // pre-existing scaffold file is a structured refusal, not a surprise write.
81
+ const clash = files.find((f) => existsSync(join(targetDir, f.name)));
82
+ if (clash !== undefined) {
83
+ return error({
84
+ code: 'already-initialised',
85
+ message: `refusing to overwrite existing ${clash.name} in ${targetDir}; init is for a fresh cart folder`,
86
+ exitCode: 1,
87
+ });
88
+ }
89
+ mkdirSync(targetDir, { recursive: true });
90
+ for (const f of files) {
91
+ writeFileSync(join(targetDir, f.name), f.content);
92
+ }
93
+ // INSTRUCT, do not mutate: these are printed tips, not actions init took.
94
+ const tips = [
95
+ "Skills: make your agent discover picopilot's skills with `npx picopilot skills add` (or symlink them yourself); run `picopilot init --install-skills` to install them globally.",
96
+ 'Version control: this folder is not a git repo. Run `git init` yourself if you want one. `init` deliberately does not touch VCS.',
97
+ ];
98
+ // Default init is INSTRUCT-not-mutate: it never touches a shared dir. Only
99
+ // the explicit --install-skills opt-in performs the shared write.
100
+ if (!options.installSkills) {
101
+ return {
102
+ dir: targetDir,
103
+ files: files.map((f) => f.name),
104
+ tips,
105
+ };
106
+ }
107
+ // The one shared write: install picopilot's skills. A project install
108
+ // (--no-global) lands under this cart folder; a global install lands in
109
+ // the real agent skill dirs. Failures surface structured, not as a crash,
110
+ // so the scaffold that already succeeded is not masked by an install slip.
111
+ let install;
112
+ try {
113
+ install = await installer({
114
+ cli,
115
+ global: options.global,
116
+ cwd: options.global ? undefined : targetDir,
117
+ });
118
+ }
119
+ catch (e) {
120
+ return error({
121
+ code: 'install-skills-failed',
122
+ message: `scaffold succeeded in ${targetDir}, but installing skills failed: ${e instanceof Error ? e.message : String(e)}. Discover them manually instead: run \`picopilot skills add\` or symlink the skills yourself.`,
123
+ exitCode: 1,
124
+ });
125
+ }
126
+ // Printed-instructions fallback (US #2): if no known non-universal agent
127
+ // was detected, the canonical `.agents/skills` install still happened (the
128
+ // universal-agent path), but a specific agent may not read it, so tell the
129
+ // user how to wire it manually rather than pretending every agent is set.
130
+ // Dedup: incur's install.agents has one entry per (skill, agent) symlink
131
+ // (see work/notes/observations/incur-install-agents-duplicated-per-skill.md),
132
+ // so N skills x M agents yields N*M entries. wiredAgents is the SET of
133
+ // agent names, so collapse duplicates here (order-preserving).
134
+ const wiredAgents = [...new Set(install.agents.map((a) => a.agent))];
135
+ const fallbackTip = wiredAgents.length === 0
136
+ ? `Installed to ${install.paths[0] ?? '.agents/skills'}. No known agent skill dir was detected to wire directly; if your agent does not auto-discover .agents/skills, symlink these skills into its skills dir.`
137
+ : `Wired agents: ${wiredAgents.join(', ')}.`;
138
+ return {
139
+ dir: targetDir,
140
+ files: files.map((f) => f.name),
141
+ tips: [
142
+ ...tips,
143
+ `Skills installed: ${install.skills.join(', ')}. ${fallbackTip}`,
144
+ ],
145
+ installedSkills: install.skills,
146
+ wiredAgents,
147
+ };
148
+ },
149
+ });
150
+ }
151
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAE,SAAS,EAAE,aAAa,EAAC,MAAM,SAAS,CAAC;AAC7D,OAAO,EAAC,UAAU,EAAE,IAAI,EAAE,OAAO,EAAC,MAAM,WAAW,CAAC;AACpD,OAAO,EAAW,CAAC,EAAC,MAAM,OAAO,CAAC;AAClC,OAAO,EAAC,aAAa,EAAC,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAGN,aAAa,GACb,MAAM,2BAA2B,CAAC;AAcnC;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,YAAY,CAC3B,GAAY,EACZ,YAA6B,aAAa;IAE1C,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE;QACnB,WAAW,EACV,wFAAwF;QACzF,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;YACd,GAAG,EAAE,CAAC;iBACJ,MAAM,EAAE;iBACR,OAAO,CAAC,GAAG,CAAC;iBACZ,QAAQ,CACR,6EAA6E,CAC7E;SACF,CAAC;QACF,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC;YACjB,aAAa,EAAE,CAAC;iBACd,OAAO,EAAE;iBACT,OAAO,CAAC,KAAK,CAAC;iBACd,QAAQ,CACR,4IAA4I,CAC5I;YACF,MAAM,EAAE,CAAC;iBACP,OAAO,EAAE;iBACT,OAAO,CAAC,IAAI,CAAC;iBACb,QAAQ,CACR,8HAA8H,CAC9H;SACF,CAAC;QACF,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;YAChB,GAAG,EAAE,CAAC;iBACJ,MAAM,EAAE;iBACR,QAAQ,CAAC,kDAAkD,CAAC;YAC9D,KAAK,EAAE,CAAC;iBACN,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;iBACjB,QAAQ,CAAC,+CAA+C,CAAC;YAC3D,IAAI,EAAE,CAAC;iBACL,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;iBACjB,QAAQ,CACR,+EAA+E,CAC/E;YACF,eAAe,EAAE,CAAC;iBAChB,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;iBACjB,QAAQ,EAAE;iBACV,QAAQ,CACR,2EAA2E,CAC3E;YACF,WAAW,EAAE,CAAC;iBACZ,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;iBACjB,QAAQ,EAAE;iBACV,QAAQ,CACR,4EAA4E,CAC5E;SACF,CAAC;QACF,QAAQ,EAAE;YACT,EAAC,WAAW,EAAE,gCAAgC,EAAC;YAC/C,EAAC,WAAW,EAAE,4BAA4B,EAAE,IAAI,EAAE,EAAC,GAAG,EAAE,SAAS,EAAC,EAAC;YACnE;gBACC,WAAW,EAAE,kDAAkD;gBAC/D,OAAO,EAAE,EAAC,aAAa,EAAE,IAAI,EAAC;aAC9B;SACD;QACD,KAAK,CAAC,GAAG,CAAC,EAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAC;YAC/B,MAAM,SAAS,GAAG,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC;gBACrC,CAAC,CAAC,IAAI,CAAC,GAAG;gBACV,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;YAEpC,MAAM,KAAK,GAAG,aAAa,EAAE,CAAC;YAE9B,yEAAyE;YACzE,0EAA0E;YAC1E,4EAA4E;YAC5E,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACrE,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACzB,OAAO,KAAK,CAAC;oBACZ,IAAI,EAAE,qBAAqB;oBAC3B,OAAO,EAAE,kCAAkC,KAAK,CAAC,IAAI,OAAO,SAAS,mCAAmC;oBACxG,QAAQ,EAAE,CAAC;iBACX,CAAC,CAAC;YACJ,CAAC;YAED,SAAS,CAAC,SAAS,EAAE,EAAC,SAAS,EAAE,IAAI,EAAC,CAAC,CAAC;YACxC,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;gBACvB,aAAa,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;YACnD,CAAC;YAED,0EAA0E;YAC1E,MAAM,IAAI,GAAG;gBACZ,iLAAiL;gBACjL,kIAAkI;aAClI,CAAC;YAEF,2EAA2E;YAC3E,kEAAkE;YAClE,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;gBAC5B,OAAO;oBACN,GAAG,EAAE,SAAS;oBACd,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;oBAC/B,IAAI;iBACJ,CAAC;YACH,CAAC;YAED,sEAAsE;YACtE,wEAAwE;YACxE,0EAA0E;YAC1E,2EAA2E;YAC3E,IAAI,OAA4B,CAAC;YACjC,IAAI,CAAC;gBACJ,OAAO,GAAG,MAAM,SAAS,CAAC;oBACzB,GAAG;oBACH,MAAM,EAAE,OAAO,CAAC,MAAM;oBACtB,GAAG,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;iBAC3C,CAAC,CAAC;YACJ,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACZ,OAAO,KAAK,CAAC;oBACZ,IAAI,EAAE,uBAAuB;oBAC7B,OAAO,EAAE,yBAAyB,SAAS,mCAC1C,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAC1C,gGAAgG;oBAChG,QAAQ,EAAE,CAAC;iBACX,CAAC,CAAC;YACJ,CAAC;YAED,yEAAyE;YACzE,2EAA2E;YAC3E,2EAA2E;YAC3E,0EAA0E;YAC1E,yEAAyE;YACzE,8EAA8E;YAC9E,uEAAuE;YACvE,+DAA+D;YAC/D,MAAM,WAAW,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACrE,MAAM,WAAW,GAChB,WAAW,CAAC,MAAM,KAAK,CAAC;gBACvB,CAAC,CAAC,gBAAgB,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,gBAAgB,0JAA0J;gBAChN,CAAC,CAAC,iBAAiB,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;YAE/C,OAAO;gBACN,GAAG,EAAE,SAAS;gBACd,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;gBAC/B,IAAI,EAAE;oBACL,GAAG,IAAI;oBACP,qBAAqB,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,WAAW,EAAE;iBAChE;gBACD,eAAe,EAAE,OAAO,CAAC,MAAM;gBAC/B,WAAW;aACX,CAAC;QACH,CAAC;KACD,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { type Cli } from 'incur';
2
+ import { type ShrinkoAdapter } from '../engine/shrinko/index.js';
3
+ /** See {@link import('./tokens.js').ShrinkoAdapterFactory}: lint reuses the same seam. */
4
+ export type ShrinkoAdapterFactory = (env: NodeJS.ProcessEnv) => ShrinkoAdapter;
5
+ /**
6
+ * Registers `picopilot lint` (US #4): surface PICO-8-specific issues (undefined
7
+ * globals, unused/duplicate locals, and, via the parsed findings, the classic
8
+ * gotchas) as STRUCTURED findings via shrinko8 `--lint`, so an agent catches
9
+ * them before running. Findings are DATA: a cart WITH warnings is a successful
10
+ * lint that reports them (exit 0), not a failure exit; only a broken cart or a
11
+ * missing dependency is an error.
12
+ *
13
+ * shrinko-backed: absent shrinko returns the structured `shrinko-not-found`
14
+ * error + nonzero exit (ADR-0002), mirroring `tokens`.
15
+ *
16
+ * @param adapterFactory injects the adapter (defaults to the shell adapter);
17
+ * tests pass a stub to drive present/absent without the real binary.
18
+ */
19
+ export declare function registerLint(cli: Cli.Cli, adapterFactory?: ShrinkoAdapterFactory): void;
20
+ //# sourceMappingURL=lint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lint.d.ts","sourceRoot":"","sources":["../../src/commands/lint.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,KAAK,GAAG,EAAI,MAAM,OAAO,CAAC;AAClC,OAAO,EACN,KAAK,cAAc,EAEnB,MAAM,4BAA4B,CAAC;AAEpC,0FAA0F;AAC1F,MAAM,MAAM,qBAAqB,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,KAAK,cAAc,CAAC;AAK/E;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAC3B,GAAG,EAAE,GAAG,CAAC,GAAG,EACZ,cAAc,GAAE,qBAA6C,GAC3D,IAAI,CAqFN"}
@@ -0,0 +1,90 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { isAbsolute, resolve } from 'node:path';
3
+ import { z } from 'incur';
4
+ import { ShellShrinkoAdapter, } from '../engine/shrinko/index.js';
5
+ const defaultAdapterFactory = (env) => new ShellShrinkoAdapter({ env });
6
+ /**
7
+ * Registers `picopilot lint` (US #4): surface PICO-8-specific issues (undefined
8
+ * globals, unused/duplicate locals, and, via the parsed findings, the classic
9
+ * gotchas) as STRUCTURED findings via shrinko8 `--lint`, so an agent catches
10
+ * them before running. Findings are DATA: a cart WITH warnings is a successful
11
+ * lint that reports them (exit 0), not a failure exit; only a broken cart or a
12
+ * missing dependency is an error.
13
+ *
14
+ * shrinko-backed: absent shrinko returns the structured `shrinko-not-found`
15
+ * error + nonzero exit (ADR-0002), mirroring `tokens`.
16
+ *
17
+ * @param adapterFactory injects the adapter (defaults to the shell adapter);
18
+ * tests pass a stub to drive present/absent without the real binary.
19
+ */
20
+ export function registerLint(cli, adapterFactory = defaultAdapterFactory) {
21
+ cli.command('lint', {
22
+ description: 'Surface PICO-8 lint findings (undefined globals, unused/duplicate locals) via shrinko8. Requires shrinko.',
23
+ args: z.object({
24
+ cart: z
25
+ .string()
26
+ .default('main.p8')
27
+ .describe('The .p8 cart to lint. Defaults to main.p8 in the current folder.'),
28
+ }),
29
+ env: z.object({
30
+ PATH: z
31
+ .string()
32
+ .optional()
33
+ .describe('Used to locate shrinko8 in the child process.'),
34
+ }),
35
+ output: z.object({
36
+ findings: z
37
+ .array(z.object({
38
+ line: z.number().describe('1-based line in the cart.'),
39
+ col: z.number().describe('1-based column.'),
40
+ message: z.string().describe('The lint warning text.'),
41
+ }))
42
+ .describe('Every lint warning shrinko reported (empty when the cart is clean).'),
43
+ clean: z.boolean().describe('True when there are no findings.'),
44
+ count: z.number().describe('Number of findings.'),
45
+ }),
46
+ examples: [
47
+ { description: 'Lint main.p8' },
48
+ { description: 'Lint a specific cart', args: { cart: 'game.p8' } },
49
+ ],
50
+ async run({ args, env, error, ok }) {
51
+ const cartPath = isAbsolute(args.cart)
52
+ ? args.cart
53
+ : resolve(process.cwd(), args.cart);
54
+ if (!existsSync(cartPath)) {
55
+ return error({
56
+ code: 'cart-not-found',
57
+ message: `no cart at ${cartPath}`,
58
+ exitCode: 1,
59
+ });
60
+ }
61
+ const adapter = adapterFactory(env);
62
+ const result = await adapter.lint(cartPath);
63
+ if (!result.ok) {
64
+ return error({
65
+ code: result.reason,
66
+ message: `shrinko is not installed. ${result.remedy} (needs: ${result.needs.join(', ')})`,
67
+ exitCode: 1,
68
+ });
69
+ }
70
+ const { findings } = result.value;
71
+ const clean = findings.length === 0;
72
+ // Findings are DATA, not a failure exit (an agent reads them and fixes the
73
+ // code). When there ARE findings, CTA toward the run/verify loop.
74
+ const cta = clean
75
+ ? undefined
76
+ : {
77
+ description: `${findings.length} lint finding(s). Fix them, then re-check:`,
78
+ commands: [
79
+ { command: 'lint', description: 'Re-lint after fixing.' },
80
+ {
81
+ command: 'verify',
82
+ description: 'Run the static gate before running.',
83
+ },
84
+ ],
85
+ };
86
+ return ok({ findings: [...findings], clean, count: findings.length }, cta === undefined ? undefined : { cta });
87
+ },
88
+ });
89
+ }
90
+ //# sourceMappingURL=lint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lint.js","sourceRoot":"","sources":["../../src/commands/lint.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import { type Cli } from 'incur';
2
+ import { type ShrinkoAdapter } from '../engine/shrinko/index.js';
3
+ /** See {@link import('./tokens.js').ShrinkoAdapterFactory}: minify reuses the same seam. */
4
+ export type ShrinkoAdapterFactory = (env: NodeJS.ProcessEnv) => ShrinkoAdapter;
5
+ /**
6
+ * Registers `picopilot minify` (US #5, ADR-0007): run shrinko8's SAFE
7
+ * minification and write the result to a SEPARATE artifact (`<name>.min.p8` by
8
+ * default), reporting the before/after token delta. It is COMPILER-STYLE: the
9
+ * user's authored source (`main.p8` / the `#include`d `main.lua`) is NEVER
10
+ * touched, and it refuses to clobber an existing output unless `--force`.
11
+ *
12
+ * Safe-only by default (never aggressive-minify silently). shrinko-backed:
13
+ * absent shrinko returns the structured `shrinko-not-found` error + nonzero
14
+ * exit, mirroring `tokens`/`lint`.
15
+ *
16
+ * @param adapterFactory injects the adapter (defaults to the shell adapter);
17
+ * tests pass a stub to drive present/absent without the real binary.
18
+ */
19
+ export declare function registerMinify(cli: Cli.Cli, adapterFactory?: ShrinkoAdapterFactory): void;
20
+ //# sourceMappingURL=minify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"minify.d.ts","sourceRoot":"","sources":["../../src/commands/minify.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,KAAK,GAAG,EAAI,MAAM,OAAO,CAAC;AAClC,OAAO,EACN,KAAK,cAAc,EAEnB,MAAM,4BAA4B,CAAC;AAEpC,4FAA4F;AAC5F,MAAM,MAAM,qBAAqB,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,KAAK,cAAc,CAAC;AAoB/E;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAC7B,GAAG,EAAE,GAAG,CAAC,GAAG,EACZ,cAAc,GAAE,qBAA6C,GAC3D,IAAI,CA6GN"}
@@ -0,0 +1,129 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { dirname, isAbsolute, join, resolve } from 'node:path';
3
+ import { z } from 'incur';
4
+ import { ShellShrinkoAdapter, } from '../engine/shrinko/index.js';
5
+ const defaultAdapterFactory = (env) => new ShellShrinkoAdapter({ env });
6
+ /**
7
+ * Derives the default output path `<name>.min.p8` next to the source cart:
8
+ * `main.p8` -> `main.min.p8`, `game.p8.png` -> `game.min.p8.png` is NOT the
9
+ * case (we only special-case the final `.p8`); a plain `<name>.p8` becomes
10
+ * `<name>.min.p8`. Anything without a `.p8` suffix just gets `.min.p8` appended.
11
+ */
12
+ function defaultOutPath(cartPath) {
13
+ const dir = dirname(cartPath);
14
+ const base = cartPath.slice(dir.length + 1);
15
+ const out = base.endsWith('.p8')
16
+ ? `${base.slice(0, -'.p8'.length)}.min.p8`
17
+ : `${base}.min.p8`;
18
+ return join(dir, out);
19
+ }
20
+ /**
21
+ * Registers `picopilot minify` (US #5, ADR-0007): run shrinko8's SAFE
22
+ * minification and write the result to a SEPARATE artifact (`<name>.min.p8` by
23
+ * default), reporting the before/after token delta. It is COMPILER-STYLE: the
24
+ * user's authored source (`main.p8` / the `#include`d `main.lua`) is NEVER
25
+ * touched, and it refuses to clobber an existing output unless `--force`.
26
+ *
27
+ * Safe-only by default (never aggressive-minify silently). shrinko-backed:
28
+ * absent shrinko returns the structured `shrinko-not-found` error + nonzero
29
+ * exit, mirroring `tokens`/`lint`.
30
+ *
31
+ * @param adapterFactory injects the adapter (defaults to the shell adapter);
32
+ * tests pass a stub to drive present/absent without the real binary.
33
+ */
34
+ export function registerMinify(cli, adapterFactory = defaultAdapterFactory) {
35
+ cli.command('minify', {
36
+ description: 'Safe-minify a cart to a SEPARATE artifact (<name>.min.p8), reporting the token delta. Source untouched. Requires shrinko.',
37
+ args: z.object({
38
+ cart: z
39
+ .string()
40
+ .default('main.p8')
41
+ .describe('The .p8 cart to minify. Defaults to main.p8 in the current folder.'),
42
+ }),
43
+ options: z.object({
44
+ out: z
45
+ .string()
46
+ .optional()
47
+ .describe('Output cart path. Defaults to <name>.min.p8 next to the source.'),
48
+ force: z
49
+ .boolean()
50
+ .default(false)
51
+ .describe('Overwrite an existing output cart. Default off (no-clobber).'),
52
+ }),
53
+ env: z.object({
54
+ PATH: z
55
+ .string()
56
+ .optional()
57
+ .describe('Used to locate shrinko8 in the child process.'),
58
+ }),
59
+ output: z.object({
60
+ outPath: z
61
+ .string()
62
+ .describe('Absolute path of the minified cart written.'),
63
+ beforeTokens: z.number().describe('Tokens in the source cart.'),
64
+ afterTokens: z.number().describe('Tokens in the minified cart.'),
65
+ saved: z.number().describe('Tokens reclaimed (before - after).'),
66
+ }),
67
+ examples: [
68
+ { description: 'Minify main.p8 to main.min.p8' },
69
+ {
70
+ description: 'Minify to a chosen path',
71
+ args: { cart: 'game.p8' },
72
+ options: { out: 'dist/game.p8' },
73
+ },
74
+ ],
75
+ async run({ args, options, env, error, ok }) {
76
+ const cartPath = isAbsolute(args.cart)
77
+ ? args.cart
78
+ : resolve(process.cwd(), args.cart);
79
+ if (!existsSync(cartPath)) {
80
+ return error({
81
+ code: 'cart-not-found',
82
+ message: `no cart at ${cartPath}`,
83
+ exitCode: 1,
84
+ });
85
+ }
86
+ const outPath = options.out !== undefined
87
+ ? isAbsolute(options.out)
88
+ ? options.out
89
+ : resolve(process.cwd(), options.out)
90
+ : defaultOutPath(cartPath);
91
+ // Guard the source FIRST: never let --out point AT the source cart (that
92
+ // would be in-place mutation, which ADR-0007 forbids). Checked before the
93
+ // no-clobber guard so it wins even though the source obviously exists.
94
+ if (outPath === cartPath) {
95
+ return error({
96
+ code: 'output-is-source',
97
+ message: `--out must differ from the source cart (${cartPath}); minify never mutates the source`,
98
+ exitCode: 1,
99
+ });
100
+ }
101
+ // No-clobber (ADR-0007, mirrors init): refuse to overwrite an existing
102
+ // artifact unless --force, so a minify never silently destroys a prior one.
103
+ if (existsSync(outPath) && !options.force) {
104
+ return error({
105
+ code: 'output-exists',
106
+ message: `refusing to overwrite existing ${outPath}; pass --force to overwrite`,
107
+ exitCode: 1,
108
+ });
109
+ }
110
+ const adapter = adapterFactory(env);
111
+ const result = await adapter.minify(cartPath, outPath);
112
+ if (!result.ok) {
113
+ return error({
114
+ code: result.reason,
115
+ message: `shrinko is not installed. ${result.remedy} (needs: ${result.needs.join(', ')})`,
116
+ exitCode: 1,
117
+ });
118
+ }
119
+ const { beforeTokens, afterTokens } = result.value;
120
+ return ok({
121
+ outPath,
122
+ beforeTokens,
123
+ afterTokens,
124
+ saved: beforeTokens - afterTokens,
125
+ });
126
+ },
127
+ });
128
+ }
129
+ //# sourceMappingURL=minify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"minify.js","sourceRoot":"","sources":["../../src/commands/minify.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { Cli } from 'incur';
2
+ /**
3
+ * Registers the `music` command group (`music from-patterns`) on the root CLI.
4
+ *
5
+ * `music` is a shrinko-FREE and PICO-8-FREE command group: it assembles a
6
+ * structural pattern list into `__music__` and merges it through the cart model
7
+ * entirely in TS, so it works with both absent and never spawns a child process.
8
+ * Music is STRUCTURAL (ADR-0005 + the audio spike finding A.5/B.6): a pattern
9
+ * list of SFX references, NOT a notation. "Off" (bit6) is distinct from "sfx 0",
10
+ * and pattern length is inherited from the referenced SFX (stored nowhere here).
11
+ */
12
+ export declare function registerMusic(cli: Cli.Cli): void;
13
+ //# sourceMappingURL=music.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"music.d.ts","sourceRoot":"","sources":["../../src/commands/music.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,GAAG,EAAI,MAAM,OAAO,CAAC;AAiR7B;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,GAAG,IAAI,CAEhD"}