lecodes-cli 0.19.0 → 0.20.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 (270) hide show
  1. package/README.md +1 -0
  2. package/dist/index.js +1069 -396
  3. package/package.json +14 -13
  4. package/runtime/scene-harness.json +1 -1
  5. package/runtime/sdk-types.json +1 -1
  6. package/runtime/web/assets/Roboto-Variable-DHm9jpd-.woff2 +0 -0
  7. package/runtime/web/assets/__vite-browser-external-BIHI7g3E.js +1 -0
  8. package/runtime/web/assets/basis-C64VHDVD.js +1 -0
  9. package/runtime/web/assets/basis_transcoder-VXdx5NbI.wasm +0 -0
  10. package/runtime/web/assets/createViewerLite-BvHobVhp.js +852 -0
  11. package/runtime/web/assets/draco-BiISTFcR.js +118 -0
  12. package/runtime/web/assets/draco_decoder-DsQ12WqX.wasm +0 -0
  13. package/runtime/web/assets/index-CfNCCBjN.js +2 -0
  14. package/runtime/web/assets/mapViewImpl-B2JcES8l.js +810 -0
  15. package/runtime/web/assets/maplibre-gl-worker-CJfwIrte.js +8 -0
  16. package/runtime/web/assets/neutral_ibl128-D-lzdkLN.ktx +0 -0
  17. package/runtime/web/assets/worker-Caf-yYEI.js +2 -0
  18. package/runtime/web/embed.html +32 -0
  19. package/runtime/web/embed.js +171 -0
  20. package/src/api.ts +302 -0
  21. package/src/browserAuth.ts +87 -0
  22. package/src/cmgenTool.ts +104 -0
  23. package/src/commands/app.ts +896 -0
  24. package/src/commands/appAndroid.ts +603 -0
  25. package/src/commands/appDesktop.ts +339 -0
  26. package/src/commands/appDesktopMac.ts +470 -0
  27. package/src/commands/appIcon.ts +187 -0
  28. package/src/commands/appShared.ts +542 -0
  29. package/src/commands/appTemplates.ts +907 -0
  30. package/src/commands/appTemplatesAndroid.ts +599 -0
  31. package/src/commands/appTemplatesGradlew.ts +9 -0
  32. package/src/commands/assets.ts +28 -0
  33. package/src/commands/clone.ts +60 -0
  34. package/src/commands/compile.ts +253 -0
  35. package/src/commands/create.ts +59 -0
  36. package/src/commands/design.ts +863 -0
  37. package/src/commands/designTemplates.ts +11 -0
  38. package/src/commands/desktop.ts +127 -0
  39. package/src/commands/dev.ts +236 -0
  40. package/src/commands/diff.ts +59 -0
  41. package/src/commands/init.ts +191 -0
  42. package/src/commands/install.ts +198 -0
  43. package/src/commands/lightmap.ts +290 -0
  44. package/src/commands/link.ts +147 -0
  45. package/src/commands/login.ts +24 -0
  46. package/src/commands/navmesh.ts +225 -0
  47. package/src/commands/pn.ts +305 -0
  48. package/src/commands/projectTemplates.ts +130 -0
  49. package/src/commands/pull.ts +109 -0
  50. package/src/commands/push.ts +173 -0
  51. package/src/commands/render.ts +416 -0
  52. package/src/commands/scene.ts +148 -0
  53. package/src/commands/shaders.ts +157 -0
  54. package/src/commands/status.ts +29 -0
  55. package/src/commands/test.ts +371 -0
  56. package/src/commands/thumbs.ts +178 -0
  57. package/src/commands/types.ts +87 -0
  58. package/src/commands/update.ts +190 -0
  59. package/src/compile/assetIcons.ts +214 -0
  60. package/src/compile/collect.ts +46 -0
  61. package/src/compile/collectLocal.ts +37 -0
  62. package/src/compile/designCompile.ts +104 -0
  63. package/src/compile/fonts.ts +156 -0
  64. package/src/compile/headlessBundle.ts +129 -0
  65. package/src/compile/nativeStack.ts +21 -0
  66. package/src/compile/projectCompile.ts +109 -0
  67. package/src/compile/sceneCompile.ts +113 -0
  68. package/src/compile/screenEntry.ts +127 -0
  69. package/src/compile/shaders.ts +245 -0
  70. package/src/config.ts +42 -0
  71. package/src/designMeta.ts +35 -0
  72. package/src/desktopRenderer.ts +545 -0
  73. package/src/desktopScript.ts +276 -0
  74. package/src/dev/clientTemplates.ts +160 -0
  75. package/src/dev/devServer.ts +333 -0
  76. package/src/dev/webRunner.ts +22 -0
  77. package/src/dev/wsServer.ts +144 -0
  78. package/src/distRoot.ts +20 -0
  79. package/src/ignore.ts +163 -0
  80. package/src/index.ts +494 -0
  81. package/src/lecodes-3d-editor.d.ts +41 -0
  82. package/src/lecodes-assets.d.ts +7 -0
  83. package/src/lecodes-design.d.ts +191 -0
  84. package/src/lecodes-renderer.d.ts +131 -0
  85. package/src/localFiles.ts +144 -0
  86. package/src/manifest.ts +46 -0
  87. package/src/matcTool.ts +137 -0
  88. package/src/peers.ts +40 -0
  89. package/src/project.ts +61 -0
  90. package/src/projectEnv.ts +94 -0
  91. package/src/qrcode-terminal.d.ts +9 -0
  92. package/src/releases.ts +125 -0
  93. package/src/serverDiff.ts +78 -0
  94. package/src/textDiff.ts +103 -0
  95. package/src/types.ts +0 -0
  96. package/src/util.ts +146 -0
  97. package/runtime/sdk/animate/animate.ts +0 -238
  98. package/runtime/sdk/animate/bezier.ts +0 -138
  99. package/runtime/sdk/animate/easings.ts +0 -126
  100. package/runtime/sdk/canvas/Canvas.ts +0 -305
  101. package/runtime/sdk/compile/aspectMacro.ts +0 -86
  102. package/runtime/sdk/compile/assetIconMacro.ts +0 -384
  103. package/runtime/sdk/compile/assetMacro.ts +0 -146
  104. package/runtime/sdk/compile/assetName.ts +0 -50
  105. package/runtime/sdk/compile/bundler.ts +0 -287
  106. package/runtime/sdk/compile/compileProject.ts +0 -129
  107. package/runtime/sdk/compile/detectEntry.ts +0 -128
  108. package/runtime/sdk/compile/fontMacro.ts +0 -459
  109. package/runtime/sdk/compile/fontRegistry.ts +0 -78
  110. package/runtime/sdk/compile/header.ts +0 -67
  111. package/runtime/sdk/compile/index.ts +0 -100
  112. package/runtime/sdk/compile/libraryImports.ts +0 -52
  113. package/runtime/sdk/compile/liteMaterial.ts +0 -247
  114. package/runtime/sdk/compile/sceneEditor.ts +0 -88
  115. package/runtime/sdk/compile/serverSplit.ts +0 -233
  116. package/runtime/sdk/compile/serverTypes.ts +0 -227
  117. package/runtime/sdk/compile/sfnt.ts +0 -98
  118. package/runtime/sdk/compile/shaderSchema.ts +0 -202
  119. package/runtime/sdk/compile/shaderTargets.ts +0 -81
  120. package/runtime/sdk/compile/sourcemap.ts +0 -25
  121. package/runtime/sdk/core/Aspect.ts +0 -568
  122. package/runtime/sdk/core/InspectorUI.ts +0 -212
  123. package/runtime/sdk/core/StateMachine.ts +0 -308
  124. package/runtime/sdk/core/color.ts +0 -66
  125. package/runtime/sdk/core/compWrite.ts +0 -42
  126. package/runtime/sdk/core/events.ts +0 -38
  127. package/runtime/sdk/core/fields.ts +0 -120
  128. package/runtime/sdk/core/registry.ts +0 -23
  129. package/runtime/sdk/core/signals.ts +0 -277
  130. package/runtime/sdk/core/time.ts +0 -84
  131. package/runtime/sdk/g2/Camera2D.ts +0 -40
  132. package/runtime/sdk/g2/CharacterController2D.ts +0 -276
  133. package/runtime/sdk/g2/Node2D.ts +0 -267
  134. package/runtime/sdk/g2/OneWay2D.ts +0 -66
  135. package/runtime/sdk/g2/Physics2D.ts +0 -346
  136. package/runtime/sdk/g2/Scene2D.ts +0 -209
  137. package/runtime/sdk/g2/Shape2D.ts +0 -259
  138. package/runtime/sdk/g2/Sprite.ts +0 -89
  139. package/runtime/sdk/g2/SpriteAnimation.ts +0 -171
  140. package/runtime/sdk/g2/SpriteSheet.ts +0 -166
  141. package/runtime/sdk/g2/Texture2D.ts +0 -47
  142. package/runtime/sdk/g2/Tilemap.ts +0 -41
  143. package/runtime/sdk/g2/Tileset.ts +0 -71
  144. package/runtime/sdk/g2/Trigger2D.ts +0 -77
  145. package/runtime/sdk/g2/autotile.ts +0 -433
  146. package/runtime/sdk/g2/cells.ts +0 -91
  147. package/runtime/sdk/g2/defineScene2d.ts +0 -381
  148. package/runtime/sdk/g2/groups2d.ts +0 -106
  149. package/runtime/sdk/g2/loop.ts +0 -50
  150. package/runtime/sdk/g2/scenarios2d.ts +0 -69
  151. package/runtime/sdk/g2/touch.ts +0 -83
  152. package/runtime/sdk/gl/Camera.ts +0 -160
  153. package/runtime/sdk/gl/CameraPlace.ts +0 -52
  154. package/runtime/sdk/gl/CharacterController.ts +0 -238
  155. package/runtime/sdk/gl/Gearbox.ts +0 -212
  156. package/runtime/sdk/gl/Geometry.ts +0 -279
  157. package/runtime/sdk/gl/IK.ts +0 -193
  158. package/runtime/sdk/gl/InstancedMesh.ts +0 -132
  159. package/runtime/sdk/gl/Light.ts +0 -107
  160. package/runtime/sdk/gl/Lightmap.ts +0 -256
  161. package/runtime/sdk/gl/Material.ts +0 -260
  162. package/runtime/sdk/gl/Mesh.ts +0 -83
  163. package/runtime/sdk/gl/Model.ts +0 -124
  164. package/runtime/sdk/gl/NavAgent.ts +0 -337
  165. package/runtime/sdk/gl/NavMesh.ts +0 -397
  166. package/runtime/sdk/gl/Node.ts +0 -350
  167. package/runtime/sdk/gl/Noise.ts +0 -30
  168. package/runtime/sdk/gl/Particles.ts +0 -676
  169. package/runtime/sdk/gl/Physics.ts +0 -222
  170. package/runtime/sdk/gl/Plane.ts +0 -53
  171. package/runtime/sdk/gl/Ragdoll.ts +0 -270
  172. package/runtime/sdk/gl/Ray.ts +0 -16
  173. package/runtime/sdk/gl/Scene.ts +0 -486
  174. package/runtime/sdk/gl/Shape.ts +0 -409
  175. package/runtime/sdk/gl/Terrain.ts +0 -1092
  176. package/runtime/sdk/gl/Texture.ts +0 -63
  177. package/runtime/sdk/gl/Trigger.ts +0 -45
  178. package/runtime/sdk/gl/Vehicle.ts +0 -473
  179. package/runtime/sdk/gl/Wheel.ts +0 -240
  180. package/runtime/sdk/gl/animation/AnimationClip.ts +0 -204
  181. package/runtime/sdk/gl/animation/Animator.ts +0 -120
  182. package/runtime/sdk/gl/animation/Layer.ts +0 -31
  183. package/runtime/sdk/gl/animation/Loop.ts +0 -30
  184. package/runtime/sdk/gl/animation/Playback.ts +0 -44
  185. package/runtime/sdk/gl/animation/core.ts +0 -336
  186. package/runtime/sdk/gl/controls.ts +0 -95
  187. package/runtime/sdk/gl/physicsEvents.ts +0 -20
  188. package/runtime/sdk/gl/scenarios.ts +0 -291
  189. package/runtime/sdk/gl/state.ts +0 -6
  190. package/runtime/sdk/gl/terrainMesh.ts +0 -219
  191. package/runtime/sdk/gl/touch.ts +0 -68
  192. package/runtime/sdk/inject.ts +0 -197
  193. package/runtime/sdk/math/Mathf.ts +0 -118
  194. package/runtime/sdk/math/mat4.ts +0 -278
  195. package/runtime/sdk/math/quat.ts +0 -232
  196. package/runtime/sdk/math/vec.ts +0 -255
  197. package/runtime/sdk/net/codec.ts +0 -119
  198. package/runtime/sdk/net/core.ts +0 -384
  199. package/runtime/sdk/net/index.ts +0 -181
  200. package/runtime/sdk/net/replication.ts +0 -622
  201. package/runtime/sdk/plugins/camera.ts +0 -81
  202. package/runtime/sdk/plugins/geolocation.ts +0 -123
  203. package/runtime/sdk/plugins/oauth.ts +0 -61
  204. package/runtime/sdk/plugins/permission.ts +0 -7
  205. package/runtime/sdk/plugins/push.ts +0 -132
  206. package/runtime/sdk/plugins/qr.ts +0 -73
  207. package/runtime/sdk/plugins/service.ts +0 -47
  208. package/runtime/sdk/runtime/app.ts +0 -101
  209. package/runtime/sdk/runtime/appEvents.ts +0 -54
  210. package/runtime/sdk/runtime/channel.ts +0 -61
  211. package/runtime/sdk/runtime/clipboard.ts +0 -20
  212. package/runtime/sdk/runtime/datetime.ts +0 -329
  213. package/runtime/sdk/runtime/device.ts +0 -293
  214. package/runtime/sdk/runtime/fetch.ts +0 -77
  215. package/runtime/sdk/runtime/files.ts +0 -23
  216. package/runtime/sdk/runtime/input.ts +0 -175
  217. package/runtime/sdk/runtime/media.ts +0 -111
  218. package/runtime/sdk/runtime/misc.ts +0 -16
  219. package/runtime/sdk/runtime/net.ts +0 -36
  220. package/runtime/sdk/runtime/rpc.ts +0 -218
  221. package/runtime/sdk/runtime/service.ts +0 -83
  222. package/runtime/sdk/runtime/share.ts +0 -9
  223. package/runtime/sdk/runtime/storage.ts +0 -13
  224. package/runtime/sdk/runtime/touch.ts +0 -76
  225. package/runtime/sdk/scene/defineScene.ts +0 -1353
  226. package/runtime/sdk/scene/editorPlugins.ts +0 -110
  227. package/runtime/sdk/scene/gizmos.ts +0 -148
  228. package/runtime/sdk/scene/grammar.ts +0 -120
  229. package/runtime/sdk/scene/material.ts +0 -188
  230. package/runtime/sdk/server/auth/appConfig.ts +0 -12
  231. package/runtime/sdk/server/auth/global.ts +0 -80
  232. package/runtime/sdk/server/auth/host.ts +0 -318
  233. package/runtime/sdk/server/auth/models.ts +0 -83
  234. package/runtime/sdk/server/auth/types.ts +0 -50
  235. package/runtime/sdk/server/channel.ts +0 -56
  236. package/runtime/sdk/server/context.ts +0 -36
  237. package/runtime/sdk/server/db/defineDb.ts +0 -237
  238. package/runtime/sdk/server/db/fields.ts +0 -132
  239. package/runtime/sdk/server/db/httpTransport.ts +0 -93
  240. package/runtime/sdk/server/db/index.ts +0 -7
  241. package/runtime/sdk/server/db/marci/query.ts +0 -412
  242. package/runtime/sdk/server/db/types.ts +0 -202
  243. package/runtime/sdk/server/errors.ts +0 -12
  244. package/runtime/sdk/server/host.ts +0 -74
  245. package/runtime/sdk/server/inject.ts +0 -13
  246. package/runtime/sdk/server/runtime.ts +0 -133
  247. package/runtime/sdk/server/validate.ts +0 -87
  248. package/runtime/sdk/ui/NativeView.ts +0 -142
  249. package/runtime/sdk/ui/UI.ts +0 -39
  250. package/runtime/sdk/ui/UIBottomSheet.ts +0 -139
  251. package/runtime/sdk/ui/UIButton.ts +0 -101
  252. package/runtime/sdk/ui/UIContainer.ts +0 -60
  253. package/runtime/sdk/ui/UIImage.ts +0 -83
  254. package/runtime/sdk/ui/UIInput.ts +0 -185
  255. package/runtime/sdk/ui/UIModal.ts +0 -139
  256. package/runtime/sdk/ui/UINode.ts +0 -830
  257. package/runtime/sdk/ui/UIPager.ts +0 -362
  258. package/runtime/sdk/ui/UIPopover.ts +0 -100
  259. package/runtime/sdk/ui/UIScreen.ts +0 -123
  260. package/runtime/sdk/ui/UIScrollable.ts +0 -87
  261. package/runtime/sdk/ui/UISpacer.ts +0 -14
  262. package/runtime/sdk/ui/UITabs.ts +0 -236
  263. package/runtime/sdk/ui/UIText.ts +0 -51
  264. package/runtime/sdk/ui/UIVideo.ts +0 -88
  265. package/runtime/sdk/ui/UIVirtualizedList.ts +0 -241
  266. package/runtime/sdk/ui/UIWidget.ts +0 -127
  267. package/runtime/sdk/ui/fonts.ts +0 -13
  268. package/runtime/sdk/ui/presentable.ts +0 -117
  269. package/runtime/sdk/ui/router.ts +0 -132
  270. package/runtime/sdk/ui/theme.ts +0 -84
package/src/ignore.ts ADDED
@@ -0,0 +1,163 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs"
2
+ import { join } from "node:path"
3
+
4
+ /*
5
+ * `.lecodesignore` support — keeps scratch files (temp probes, render screenshots, logs) out of
6
+ * `status` / `push` so they never land in a checkpoint. Syntax is a practical subset of .gitignore:
7
+ * - blank lines and `# comments` are skipped
8
+ * - `!pattern` re-includes something a previous pattern ignored
9
+ * - a trailing `/` matches directories only
10
+ * - a leading `/` (or any embedded `/`) anchors the pattern to the project root
11
+ * - `*` matches within a path segment, `**` across segments, `?` a single char
12
+ * - a slash-free pattern (e.g. `_*`, `*.png`) matches a basename at any depth
13
+ *
14
+ * IMPORTANT: ignores apply to UNTRACKED files only. A file already in the project (recorded in the
15
+ * manifest) is always tracked, even if it matches a pattern — so ignoring `*.png` can never make an
16
+ * existing image asset silently disappear from the project. Pruning happens in `scanLocal`.
17
+ */
18
+
19
+ export type IgnoreMatcher = (key: string, isDir: boolean) => boolean
20
+
21
+ type Rule = { re: RegExp, negate: boolean, dirOnly: boolean }
22
+
23
+ // Always applied, even with no .lecodesignore — only patterns that are never real project assets, so
24
+ // they're safe as invisible defaults. Everything asset-shaped (*.png, *.json, …) is opt-in via the
25
+ // file written at clone time (see DEFAULT_IGNORE_FILE). `.artifacts/` is `lecodes test`'s
26
+ // failure-frame dump dir (tests/.artifacts/) — regenerated scratch, never a project asset.
27
+ // `dist/` is `lecodes app build desktop`'s default output (<root>/dist/<slug>, appDesktop.ts) —
28
+ // same class, but it must be ignored for a second, load-bearing reason: that folder holds a COPY of
29
+ // the host's runtime assets (lit.filamat, uberarchive.bin, …). Collected as project assets they get
30
+ // staged next to the bundle, and onFetchLocal searches the bundle's dir BEFORE the host's assets dir
31
+ // (main.cpp) — so the copy shadows the host's own. Harmless while every host was OpenGL and the
32
+ // bytes matched; fatal once a build output can come from the Vulkan host, where the shadowing
33
+ // lit.filamat is SPIR-V and Filament aborts with "not built for any of the OpenGL backend's
34
+ // supported shader languages".
35
+ // `.env` / `.env.*`: the project's LECODES_* config (projectEnv.ts) — it holds tokens and keystore
36
+ // passwords, so it never leaves the machine whatever .lecodesignore says.
37
+ const BUILTIN_PATTERNS = ["node_modules/", ".git/", "_*", "*.log", ".DS_Store", ".artifacts/", "dist/", ".env", ".env.*"]
38
+
39
+ /** Template `.lecodesignore` written on clone — the explicit, editable "what we don't push" list. */
40
+ export const DEFAULT_IGNORE_FILE = `# Files matched here are NOT pushed to LeCodes.
41
+ # Only untracked files are affected — assets already in the project stay tracked
42
+ # even if they match a pattern. Built-in defaults (node_modules, _*, *.log, .artifacts)
43
+ # always apply.
44
+
45
+ # Render / preview outputs (the default \`lecodes render --png\` target)
46
+ screenshot.png
47
+ shots/
48
+
49
+ # Design-board comments live on the platform; design/comments/ is the local mirror of the
50
+ # open ones (\`lecodes design comments pull\`) — read by Claude, but pushing it back as
51
+ # project files would only duplicate the database.
52
+ design/comments/
53
+
54
+ # Image & other binary assets ARE pushed by default — they're real project resources.
55
+ # Uncomment a pattern below only if you want to keep some untracked ones out of pushes.
56
+ # *.png
57
+
58
+ # Local notes & dumps (uncomment to ignore)
59
+ # *.json
60
+ # notes.md
61
+ `
62
+
63
+ /** Translate a glob (one path segment's worth of `*`/`?`/`**`) into a regex fragment. */
64
+ const translate = (glob: string): string => {
65
+ let re = ""
66
+ for (let i = 0; i < glob.length; i++) {
67
+ const ch = glob[i]
68
+ if (ch === "*") {
69
+ if (glob[i + 1] === "*") { re += ".*"; i++; if (glob[i + 1] === "/") i++ } // `**/` spans dirs
70
+ else re += "[^/]*"
71
+ } else if (ch === "?") re += "[^/]"
72
+ else if (ch === "/") re += "/"
73
+ else if ("\\^$.|+()[]{}".includes(ch)) re += "\\" + ch
74
+ else re += ch
75
+ }
76
+ return re
77
+ }
78
+
79
+ const compile = (raw: string): Rule | null => {
80
+ let p = raw.trim()
81
+ if (!p || p.startsWith("#")) return null
82
+ let negate = false
83
+ if (p.startsWith("!")) { negate = true; p = p.slice(1) }
84
+ let dirOnly = false
85
+ if (p.endsWith("/")) { dirOnly = true; p = p.slice(0, -1) }
86
+ const anchored = p.startsWith("/") || p.includes("/")
87
+ if (p.startsWith("/")) p = p.slice(1)
88
+ if (!p) return null
89
+ const body = translate(p)
90
+ // `(/.*)?$` lets a directory pattern also match files nested under it, matching gitignore behavior
91
+ // even without relying on directory pruning.
92
+ const re = anchored ? new RegExp("^/" + body + "(/.*)?$") : new RegExp("(^|/)" + body + "(/.*)?$")
93
+ return { re, negate, dirOnly }
94
+ }
95
+
96
+ const buildMatcher = (rules: Rule[]): IgnoreMatcher => (key, isDir) => {
97
+ let ignored = false
98
+ for (const r of rules) {
99
+ if (r.dirOnly && !isDir) continue // a `foo/` rule only matches dirs directly (files via prune)
100
+ if (r.re.test(key)) ignored = !r.negate
101
+ }
102
+ return ignored
103
+ }
104
+
105
+ /** Compile a standalone glob list (same syntax as .lecodesignore lines, including `!` re-includes)
106
+ * into a file-path matcher — used for app.json's `resources.remote` patterns. */
107
+ export const buildGlobMatcher = (patterns: string[]): ((path: string) => boolean) => {
108
+ // Matching is over FILE paths — normalize `videos/` (dir-only, would match nothing here) to
109
+ // `videos`, whose `(/.*)?$` tail matches everything under the directory.
110
+ const rules = patterns.map((p) => compile(p.trim().replace(/\/+$/, ""))).filter((r): r is Rule => r !== null)
111
+ const match = buildMatcher(rules)
112
+ // Anchored rules compile to `^/…` — they expect the canonical leading-slash key form.
113
+ return (path) => match(path.startsWith("/") ? path : "/" + path, false)
114
+ }
115
+
116
+ /** Read `<root>/.lecodesignore` (if any) and combine with the built-in defaults into a matcher. */
117
+ export const loadIgnore = (root: string): IgnoreMatcher => {
118
+ const lines = [...BUILTIN_PATTERNS]
119
+ const file = join(root, ".lecodesignore")
120
+ if (existsSync(file)) {
121
+ try { lines.push(...readFileSync(file, "utf8").split(/\r?\n/)) } catch { /* unreadable → defaults only */ }
122
+ }
123
+ const rules = lines.map(compile).filter((r): r is Rule => r !== null)
124
+ return buildMatcher(rules)
125
+ }
126
+
127
+ /** Write the template `.lecodesignore` if the project doesn't have one yet (used on clone). */
128
+ export const writeDefaultIgnore = (root: string) => {
129
+ const file = join(root, ".lecodesignore")
130
+ if (!existsSync(file)) writeFileSync(file, DEFAULT_IGNORE_FILE)
131
+ }
132
+
133
+ /* `.gitignore` for the project's OWN git repo. `.lecodes/` is per-checkout state — regenerable IDE
134
+ * types (`lecodes update`) + `manifest.json`, this machine's push cursor (baseHeadId + per-file shas).
135
+ * Committing it makes collaborators fight over the cursor and ship stale types, so every scaffold
136
+ * path (init / clone / link) makes sure it's ignored. Render outputs mirror DEFAULT_IGNORE_FILE. */
137
+ export const DEFAULT_GITIGNORE = `# LeCodes per-checkout state: generated IDE types + this machine's sync cursor
138
+ # (.lecodes/manifest.json). Regenerate with \`lecodes update\` / re-attach with \`lecodes link\`.
139
+ .lecodes/
140
+
141
+ # Render / preview outputs and test failure frames
142
+ screenshot.png
143
+ shots/
144
+ tests/.artifacts/
145
+ tests/.shots/
146
+
147
+ node_modules/
148
+
149
+ dist/
150
+ `
151
+
152
+ /** Create `.gitignore` if missing; if it exists but doesn't ignore `.lecodes/`, append that rule.
153
+ * Returns what happened so callers can log it ("created" | "updated" | null). */
154
+ export const ensureGitignore = (root: string): "created" | "updated" | null => {
155
+ const file = join(root, ".gitignore")
156
+ if (!existsSync(file)) { writeFileSync(file, DEFAULT_GITIGNORE); return "created" }
157
+ const text = readFileSync(file, "utf8")
158
+ const covered = text.split(/\r?\n/).some(l => /^\/?\.lecodes\/?\s*$/.test(l.trim()))
159
+ if (covered) return null
160
+ const sep = text.length === 0 || text.endsWith("\n") ? "" : "\n"
161
+ writeFileSync(file, `${text}${sep}\n# LeCodes per-checkout state (generated types + sync cursor) — not source\n.lecodes/\n`)
162
+ return "updated"
163
+ }