@typecad/cuttlefish 1.0.0-alpha.14 → 1.0.0-alpha.16

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 (315) hide show
  1. package/README.md +82 -79
  2. package/assets/editor-extensions/typecad-debug/README.md +25 -26
  3. package/dist/api/config.d.ts +22 -26
  4. package/dist/api/schema/types.d.ts +20 -3
  5. package/dist/api/shared/async-runtime-static.d.ts +1 -1
  6. package/dist/api/shared/async-runtime-static.js +5 -5
  7. package/dist/api/shared/async-types.d.ts +3 -2
  8. package/dist/api/shared/board-catalog.d.ts +2 -0
  9. package/dist/api/shared/board-catalog.js +6 -0
  10. package/dist/api/shared/board-resolver.d.ts +4 -7
  11. package/dist/api/shared/board-resolver.js +2 -1
  12. package/dist/api/shared/coop-scheduler.d.ts +1 -1
  13. package/dist/api/shared/coop-scheduler.js +1 -1
  14. package/dist/api/shared/cpp-type-ir.d.ts +1 -1
  15. package/dist/api/shared/cpp-type-ir.js +1 -1
  16. package/dist/api/shared/display-profile.d.ts +1 -1
  17. package/dist/api/shared/display-profile.js +3 -5
  18. package/dist/api/shared/framework-manifest-registry.d.ts +1 -1
  19. package/dist/api/shared/framework-manifest-registry.js +0 -1
  20. package/dist/api/shared/framework-manifest.d.ts +7 -6
  21. package/dist/api/shared/framework-manifest.js +6 -11
  22. package/dist/api/shared/hal-op-ir.d.ts +294 -685
  23. package/dist/api/shared/hal-op-ir.js +31 -65
  24. package/dist/api/shared/index.d.ts +0 -4
  25. package/dist/api/shared/index.js +1 -7
  26. package/dist/api/shared/ir-core.d.ts +6 -0
  27. package/dist/api/shared/ir-declarations.d.ts +4 -0
  28. package/dist/api/shared/ir.d.ts +1 -3
  29. package/dist/api/shared/platform-strategy.d.ts +14 -37
  30. package/dist/api/shared/polyfill-helper-registry.js +0 -5
  31. package/dist/api/shared/polyfill-types.js +0 -27
  32. package/dist/api/shared/promise-runtime.js +3 -3
  33. package/dist/api/shared/spdx-licenses.d.ts +12 -10
  34. package/dist/api/shared/spdx-licenses.js +1 -1
  35. package/dist/api/shared/toolchain-types.d.ts +1 -1
  36. package/dist/api/shared/toolchain-types.js +3 -3
  37. package/dist/api/shared/types.d.ts +0 -3
  38. package/dist/api/shared/validate-framework-manifest.js +13 -33
  39. package/dist/board-catalog/dts-reader.d.ts +220 -0
  40. package/dist/board-catalog/dts-reader.js +1052 -0
  41. package/dist/board-catalog/index.d.ts +11 -0
  42. package/dist/board-catalog/index.js +12 -0
  43. package/dist/board-catalog/pinconfig.d.ts +48 -0
  44. package/dist/board-catalog/pinconfig.js +263 -0
  45. package/dist/board-catalog/sdk.d.ts +82 -0
  46. package/dist/board-catalog/sdk.js +180 -0
  47. package/dist/board-catalog/store.d.ts +196 -0
  48. package/dist/board-catalog/store.js +363 -0
  49. package/dist/board-catalog/types.d.ts +233 -0
  50. package/dist/board-catalog/types.js +7 -0
  51. package/dist/board-catalog/walker.d.ts +112 -0
  52. package/dist/board-catalog/walker.js +1101 -0
  53. package/dist/cli-utils.d.ts +2 -2
  54. package/dist/cli-utils.js +5 -5
  55. package/dist/cli.js +219 -242
  56. package/dist/config-loader.d.ts +14 -19
  57. package/dist/config-loader.js +240 -78
  58. package/dist/config-schema.d.ts +3 -5
  59. package/dist/config-schema.js +8 -8
  60. package/dist/contract/board-generator.d.ts +4 -4
  61. package/dist/contract/board-generator.js +16 -5
  62. package/dist/contract/contract-parser.d.ts +27 -0
  63. package/dist/contract/contract-parser.js +76 -0
  64. package/dist/contract/index.d.ts +1 -1
  65. package/dist/contract/index.js +70 -21
  66. package/dist/create/active-board-catalog.d.ts +9 -0
  67. package/dist/create/active-board-catalog.js +94 -0
  68. package/dist/create/board-catalog.generated.d.ts +96 -0
  69. package/dist/create/board-catalog.generated.js +128737 -0
  70. package/dist/create/board-search.d.ts +23 -0
  71. package/dist/create/board-search.js +88 -0
  72. package/dist/create/editor-integration.d.ts +4 -2
  73. package/dist/create/editor-integration.js +41 -2
  74. package/dist/create/framework-catalog.d.ts +57 -9
  75. package/dist/create/framework-catalog.js +76 -57
  76. package/dist/create/index.d.ts +5 -11
  77. package/dist/create/index.js +3 -8
  78. package/dist/create/mcu-target.d.ts +39 -0
  79. package/dist/create/mcu-target.js +80 -0
  80. package/dist/create/pack-targets.d.ts +19 -0
  81. package/dist/create/pack-targets.js +56 -0
  82. package/dist/create/scaffold.d.ts +4 -7
  83. package/dist/create/scaffold.js +19 -109
  84. package/dist/create/templates.d.ts +32 -5
  85. package/dist/create/templates.js +130 -116
  86. package/dist/create/wizard.d.ts +12 -1
  87. package/dist/create/wizard.js +168 -38
  88. package/dist/debug/preprocessor.d.ts +5 -0
  89. package/dist/debug/preprocessor.js +31 -7
  90. package/dist/diagnostics/diagnostics-report.d.ts +1 -1
  91. package/dist/diagnostics/diagnostics-report.js +3 -11
  92. package/dist/diagnostics/json-schema.d.ts +1 -1
  93. package/dist/diagnostics/md-writer.js +2 -2
  94. package/dist/diagnostics/mermaid-builder.d.ts +1 -1
  95. package/dist/diagnostics/mermaid-builder.js +1 -1
  96. package/dist/emit/cpp-emitter.js +4 -1
  97. package/dist/emit/emitters/class-emitter.js +32 -4
  98. package/dist/emit/emitters/emitter-context.d.ts +11 -0
  99. package/dist/emit/emitters/entrypoint-synthesizer.js +1 -1
  100. package/dist/emit/emitters/function-emitter-impl.js +33 -17
  101. package/dist/emit/emitters/output-finalizer.d.ts +7 -0
  102. package/dist/emit/emitters/output-finalizer.js +31 -23
  103. package/dist/emit/emitters/setup.js +125 -63
  104. package/dist/emit/emitters/top-level-prep.js +1 -1
  105. package/dist/emit/emitters/type-decl-emitter.js +13 -8
  106. package/dist/emit/emitters/ui-emitter.js +1 -1
  107. package/dist/emit/expression-renderer.d.ts +3 -3
  108. package/dist/emit/expression-renderer.js +17 -7
  109. package/dist/emit/route-hal-op.js +17 -48
  110. package/dist/emit/snprintf-helpers.d.ts +1 -3
  111. package/dist/emit/snprintf-helpers.js +1 -39
  112. package/dist/emit/statement-renderer.d.ts +0 -4
  113. package/dist/emit/statement-renderer.js +54 -45
  114. package/dist/emit/utils/async-state-machine.d.ts +16 -9
  115. package/dist/emit/utils/async-state-machine.js +56 -54
  116. package/dist/emit/utils/comment-helpers.d.ts +0 -8
  117. package/dist/emit/utils/comment-helpers.js +1 -1
  118. package/dist/emit/utils/cpp-helpers.d.ts +2 -2
  119. package/dist/emit/utils/cpp-helpers.js +2 -2
  120. package/dist/emit/utils/hal-op-cpp-type.js +4 -6
  121. package/dist/emit/utils/index.d.ts +3 -3
  122. package/dist/emit/utils/index.js +3 -3
  123. package/dist/emit/utils/type-inference.d.ts +1 -29
  124. package/dist/emit/utils/type-inference.js +0 -75
  125. package/dist/framework-package.d.ts +3 -0
  126. package/dist/framework-package.js +23 -6
  127. package/dist/framework-registry.d.ts +5 -5
  128. package/dist/frameworks/native/graphics/terminal-preview.d.ts +5 -0
  129. package/dist/frameworks/native/graphics/terminal-preview.js +33 -0
  130. package/dist/frameworks/native/index.d.ts +3 -0
  131. package/dist/frameworks/native/index.js +15 -0
  132. package/dist/frameworks/native/native-compile.d.ts +17 -0
  133. package/dist/frameworks/native/native-compile.js +170 -0
  134. package/dist/frameworks/native/native-config.d.ts +41 -0
  135. package/dist/frameworks/native/native-config.js +7 -0
  136. package/dist/frameworks/native/strategy.d.ts +97 -0
  137. package/dist/frameworks/native/strategy.js +664 -0
  138. package/dist/ir/board-resolver.d.ts +18 -23
  139. package/dist/ir/board-resolver.js +43 -401
  140. package/dist/ir/build-ir-state.d.ts +3 -0
  141. package/dist/ir/build-ir-state.js +42 -0
  142. package/dist/ir/build-ir.d.ts +1 -1
  143. package/dist/ir/build-ir.js +40 -42
  144. package/dist/ir/declaration-builders.js +1 -0
  145. package/dist/ir/expression-to-ir.js +17 -10
  146. package/dist/ir/hal/hal-emitter.d.ts +0 -2
  147. package/dist/ir/hal/hal-emitter.js +92 -41
  148. package/dist/ir/hal/hal-parser.d.ts +5 -5
  149. package/dist/ir/hal/hal-parser.js +128 -36
  150. package/dist/ir/hal/hal-plugins.d.ts +0 -1
  151. package/dist/ir/hal/hal-plugins.js +425 -716
  152. package/dist/ir/heap-analysis.js +2 -41
  153. package/dist/ir/identifier-collector.js +32 -20
  154. package/dist/ir/network-validation.js +7 -11
  155. package/dist/ir/ownership-analysis.js +75 -13
  156. package/dist/ir/peripheral-usage.d.ts +8 -2
  157. package/dist/ir/peripheral-usage.js +78 -149
  158. package/dist/ir/peripheral-validation.js +23 -3
  159. package/dist/ir/pin-capability-validation.d.ts +12 -0
  160. package/dist/ir/pin-capability-validation.js +61 -97
  161. package/dist/ir/pin-mode-validation.js +5 -5
  162. package/dist/ir/program-analysis.d.ts +8 -15
  163. package/dist/ir/program-analysis.js +140 -323
  164. package/dist/ir/pulldown-validation.js +11 -4
  165. package/dist/ir/resource-analysis.js +15 -3
  166. package/dist/ir/statement-to-ir.js +0 -6
  167. package/dist/ir/timing-validation.js +1 -1
  168. package/dist/ir/transformers/array-methods.js +11 -5
  169. package/dist/ir/transformers/call-statement.js +20 -87
  170. package/dist/ir/transformers/callback-context-registry.d.ts +1 -1
  171. package/dist/ir/transformers/callback-context-registry.js +8 -16
  172. package/dist/ir/transformers/control-flow.js +0 -13
  173. package/dist/ir/transformers/expressions.js +10 -9
  174. package/dist/ir/transformers/hal-call-resolver.d.ts +2 -2
  175. package/dist/ir/transformers/hal-call-resolver.js +24 -10
  176. package/dist/ir/transformers/hal-emit-helpers.d.ts +2 -2
  177. package/dist/ir/transformers/hal-emit-helpers.js +2 -2
  178. package/dist/ir/transformers/namespace-methods.js +1 -1
  179. package/dist/ir/transformers/ui-callback-lowering.d.ts +0 -2
  180. package/dist/ir/transformers/ui-callback-lowering.js +0 -24
  181. package/dist/ir/transformers/variables.js +338 -42
  182. package/dist/ir/try-catch-validation.js +4 -3
  183. package/dist/ir/type-resolution.js +15 -4
  184. package/dist/ir/validation-orchestrator.js +0 -9
  185. package/dist/libdef/cpp-to-decl.d.ts +0 -9
  186. package/dist/libdef/cpp-to-decl.js +0 -72
  187. package/dist/libdef/registry.d.ts +0 -1
  188. package/dist/libdef/registry.js +1 -24
  189. package/dist/library/init.d.ts +1 -1
  190. package/dist/library/init.js +137 -137
  191. package/dist/library-packages.d.ts +1 -1
  192. package/dist/library-packages.js +1 -1
  193. package/dist/mapping/peripheral-names.js +9 -4
  194. package/dist/mapping/source-map.d.ts +3 -4
  195. package/dist/mapping/source-map.js +12 -18
  196. package/dist/orchestrator/graph-builder.d.ts +3 -3
  197. package/dist/orchestrator/graph-builder.js +13 -12
  198. package/dist/orchestrator/type-checker.d.ts +3 -3
  199. package/dist/orchestrator/type-checker.js +3 -3
  200. package/dist/platform/async-runtime.js +1 -1
  201. package/dist/platform/coop-scheduler-runtime.js +2 -2
  202. package/dist/platform/generic-debug-codegen.d.ts +36 -0
  203. package/dist/platform/generic-debug-codegen.js +185 -0
  204. package/dist/platform/generic-strategy.d.ts +3 -4
  205. package/dist/platform/generic-strategy.js +5 -80
  206. package/dist/platform/index.js +1 -1
  207. package/dist/platform/registry.d.ts +2 -2
  208. package/dist/platform/registry.js +2 -2
  209. package/dist/safety/asil-decorators.d.ts +32 -0
  210. package/dist/safety/asil-decorators.js +32 -0
  211. package/dist/safety/authoring.d.ts +85 -0
  212. package/dist/safety/authoring.js +61 -0
  213. package/dist/safety/engine.d.ts +3 -0
  214. package/dist/safety/engine.js +83 -0
  215. package/dist/safety/hal/ops.d.ts +48 -0
  216. package/dist/safety/hal/ops.js +46 -0
  217. package/dist/safety/iso26262/analyze.d.ts +13 -0
  218. package/dist/safety/iso26262/analyze.js +29 -0
  219. package/dist/safety/iso26262/asil.d.ts +10 -0
  220. package/dist/safety/iso26262/asil.js +22 -0
  221. package/dist/safety/iso26262/collect.d.ts +14 -0
  222. package/dist/safety/iso26262/collect.js +137 -0
  223. package/dist/safety/iso26262/goto-checker.d.ts +10 -0
  224. package/dist/safety/iso26262/goto-checker.js +72 -0
  225. package/dist/safety/iso26262/heap-checker.d.ts +7 -0
  226. package/dist/safety/iso26262/heap-checker.js +57 -0
  227. package/dist/safety/iso26262/init-checker.d.ts +13 -0
  228. package/dist/safety/iso26262/init-checker.js +127 -0
  229. package/dist/safety/iso26262/loop-checker.d.ts +7 -0
  230. package/dist/safety/iso26262/loop-checker.js +86 -0
  231. package/dist/safety/iso26262/recursion-checker.d.ts +9 -0
  232. package/dist/safety/iso26262/recursion-checker.js +134 -0
  233. package/dist/safety/iso26262/sidecar-writer.d.ts +25 -0
  234. package/dist/safety/iso26262/sidecar-writer.js +13 -0
  235. package/dist/safety/passes/pinMode-intercept.d.ts +3 -0
  236. package/dist/safety/passes/pinMode-intercept.js +60 -0
  237. package/dist/safety/runtime/mode-table.d.ts +5 -0
  238. package/dist/safety/runtime/mode-table.js +55 -0
  239. package/dist/safety/runtime/polyfills.d.ts +6 -0
  240. package/dist/safety/runtime/polyfills.js +12 -0
  241. package/dist/safety/runtime/result-struct.d.ts +11 -0
  242. package/dist/safety/runtime/result-struct.js +98 -0
  243. package/dist/safety/runtime/safe-int.d.ts +33 -0
  244. package/dist/safety/runtime/safe-int.js +170 -0
  245. package/dist/safety/runtime/safe-traits.d.ts +4 -0
  246. package/dist/safety/runtime/safe-traits.js +95 -0
  247. package/dist/safety/runtime/safe-variable.d.ts +39 -0
  248. package/dist/safety/runtime/safe-variable.js +226 -0
  249. package/dist/safety/runtime/vote.d.ts +8 -0
  250. package/dist/safety/runtime/vote.js +92 -0
  251. package/dist/safety/runtime/write-verify.d.ts +8 -0
  252. package/dist/safety/runtime/write-verify.js +71 -0
  253. package/dist/safety/safe-int-types.d.ts +45 -0
  254. package/dist/safety/safe-int-types.js +25 -0
  255. package/dist/safety/safe-variable-types.d.ts +29 -0
  256. package/dist/safety/safe-variable-types.js +19 -0
  257. package/dist/safety/safety-bridge.d.ts +5 -6
  258. package/dist/safety/safety-bridge.js +15 -28
  259. package/dist/safety/sidecar-bridge.d.ts +1 -3
  260. package/dist/safety/sidecar-bridge.js +3 -20
  261. package/dist/safety/specifiers.d.ts +6 -0
  262. package/dist/safety/specifiers.js +14 -0
  263. package/dist/safety-hook.d.ts +8 -7
  264. package/dist/safety-hook.js +12 -13
  265. package/dist/testing.d.ts +4 -5
  266. package/dist/testing.js +5 -5
  267. package/dist/transpile/resolution.d.ts +1 -1
  268. package/dist/transpile/resolution.js +18 -9
  269. package/dist/transpile/test-pins.d.ts +18 -0
  270. package/dist/transpile/test-pins.js +142 -0
  271. package/dist/transpile.d.ts +1 -2
  272. package/dist/transpile.js +36 -30
  273. package/dist/types.d.ts +26 -26
  274. package/dist/ui-hook.d.ts +1 -1
  275. package/dist/utils/cli.d.ts +2 -2
  276. package/dist/utils/cli.js +92 -154
  277. package/dist/utils/fs.d.ts +3 -4
  278. package/dist/utils/fs.js +4 -5
  279. package/dist/utils/strings.d.ts +0 -1
  280. package/dist/utils/strings.js +0 -8
  281. package/dist/utils/ui.d.ts +5 -2
  282. package/dist/utils/ui.js +13 -3
  283. package/package.json +158 -157
  284. package/dist/add-preset.d.ts +0 -4
  285. package/dist/add-preset.js +0 -74
  286. package/dist/api/shared/async-symbol-detector.d.ts +0 -11
  287. package/dist/api/shared/async-symbol-detector.js +0 -140
  288. package/dist/api/shared/native-display-op-resolver.d.ts +0 -10
  289. package/dist/api/shared/native-display-op-resolver.js +0 -64
  290. package/dist/create/board-checklist.d.ts +0 -2
  291. package/dist/create/board-checklist.js +0 -52
  292. package/dist/create/board-codegen.d.ts +0 -9
  293. package/dist/create/board-codegen.js +0 -238
  294. package/dist/create/board-generators.d.ts +0 -12
  295. package/dist/create/board-generators.js +0 -651
  296. package/dist/create/board-spec.d.ts +0 -181
  297. package/dist/create/board-spec.js +0 -214
  298. package/dist/install/framework-catalog.d.ts +0 -53
  299. package/dist/install/framework-catalog.js +0 -107
  300. package/dist/install/handle-install.d.ts +0 -35
  301. package/dist/install/handle-install.js +0 -177
  302. package/dist/install/index.d.ts +0 -4
  303. package/dist/install/index.js +0 -3
  304. package/dist/ir/pin-state-tracking.d.ts +0 -58
  305. package/dist/ir/pin-state-tracking.js +0 -182
  306. package/dist/ir/pwm-timer-sharing.d.ts +0 -4
  307. package/dist/ir/pwm-timer-sharing.js +0 -94
  308. package/dist/ir/timer0-pwm-timing-conflict.d.ts +0 -4
  309. package/dist/ir/timer0-pwm-timing-conflict.js +0 -72
  310. package/dist/libdef/component-decls.d.ts +0 -2
  311. package/dist/libdef/component-decls.js +0 -6
  312. package/dist/libdef/component-discovery.d.ts +0 -43
  313. package/dist/libdef/component-discovery.js +0 -83
  314. package/dist/theme-tokens.d.ts +0 -22
  315. package/dist/theme-tokens.js +0 -172
package/dist/cli.js CHANGED
@@ -3,18 +3,19 @@ import path from "node:path";
3
3
  import fs from "node:fs";
4
4
  import { parseCommandLine, printHelp } from "./utils/cli.js";
5
5
  import { runLibraryCommand } from "./library/cli.js";
6
- import { scaffoldProject, printCreateNextSteps, KNOWN_TARGETS, frameworksForTarget, frameworkCatalogEntry, frameworkCompatibleWithTarget, FRAMEWORK_CATALOG, frameworkTargetProfile } from "./create/index.js";
6
+ import { scaffoldProject, printCreateNextSteps, KNOWN_TARGETS, frameworksForTarget, frameworkCatalogEntry, FRAMEWORK_CATALOG, frameworkTargetProfile, probeMethodsForBoard, probeRunnerQuirks } from "./create/index.js";
7
+ import { findPackBoard, packBoardAsTarget } from "./create/pack-targets.js";
8
+ import { activeBoardCatalog, assertZephyrSdkForCreate, formatZephyrSdkFound, ensureFreshBoardCatalog, resetBoardCatalogOverlayCache, sdkFingerprint, PINNED_ZEPHYR_MANIFEST_REV } from "./board-catalog/index.js";
7
9
  import { generateFrameworkDebugArtifacts } from "./create/debug-artifacts.js";
8
10
  import { runCreateWizard } from "./create/index.js";
9
11
  import { installProjectDependencies } from "./create/install-deps.js";
10
- import { generateLibraryDefinitions, transpileFile } from "./transpile.js";
11
- import { generateDecl, generateDeclsForDirectory, generateComponentDeclsForProject } from "./libdef/cpp-to-decl.js";
12
- import { mapCppLocationToTs, readSourceMap, resolveMapPath } from "./mapping/source-map.js";
12
+ import { transpileFile } from "./transpile.js";
13
+ import { generateDecl, generateDeclsForDirectory } from "./libdef/cpp-to-decl.js";
13
14
  import { compileSource, uploadFirmware, monitorDevice } from "./platform/toolchain.js";
14
15
  import { resolveStrategy } from "./platform/registry.js";
15
16
  import { loadFrameworkPackage } from "./framework-package.js";
16
17
  import { getLoadedFramework, hasLoadedFramework } from "./framework-registry.js";
17
- import { loadCuttlefishConfig, generateVirtualTypeDeclaration } from "./config-loader.js";
18
+ import { loadCuttlefishConfig, generateVirtualTypeDeclaration, regenBoardModule } from "./config-loader.js";
18
19
  import { generateContractBoard } from "./contract/index.js";
19
20
  import { requireUIHook, hasUIHook } from "./ui-hook.js";
20
21
  import { loadUIEngine } from "./ui/ui-bridge.js";
@@ -36,36 +37,64 @@ function displayConfigForTranspile(config) {
36
37
  }
37
38
  return display;
38
39
  }
39
- /**
40
- * Apply the PSRAM config to the Arduino build: append the `PSRAM={opi|quad}`
41
- * menu option to the FQBN (so the Arduino core's psramFound()/ps_malloc work
42
- * at runtime — they're gated on the board menu option, not just the define)
43
- * and add `-DBOARD_HAS_PSRAM` to the defines (so the framework's PSRAM canvas
44
- * allocator is compiled in). No-op when psram is unset or the target isn't an
45
- * Arduino FQBN (the PSRAM= option is Arduino-core-specific).
46
- */
47
- function applyPsramToArduinoBuild(buildTarget, defines, psram) {
48
- if (!psram || !buildTarget || !buildTarget.includes(':')) {
49
- return { buildTarget, defines: defines ?? {} };
50
- }
51
- // Arduino FQBN config options follow the board:option as `key=value` pairs.
52
- // Append PSRAM= if not already present (don't clobber an explicit override).
53
- const psramOpt = `PSRAM=${psram}`;
54
- const adjustedTarget = buildTarget.includes('PSRAM=') ? buildTarget : `${buildTarget}:${psramOpt}`;
55
- const adjustedDefines = { ...(defines ?? {}) };
56
- if (!('BOARD_HAS_PSRAM' in adjustedDefines))
57
- adjustedDefines.BOARD_HAS_PSRAM = '';
58
- return { buildTarget: adjustedTarget, defines: adjustedDefines };
59
- }
60
40
  async function handleCreate(options) {
61
41
  const targetId = options.target ?? options.board;
62
42
  const hasTarget = !!targetId;
63
- if (hasTarget) {
64
- const target = KNOWN_TARGETS.find(t => t.id === targetId);
43
+ // SDK gate: the installed Zephyr SDK is the source of truth — boards, pin
44
+ // data, everything derives from it. No SDK (or a wrong-version one) means
45
+ // the project cannot work; fail with the fix instead of scaffolding junk.
46
+ // Native-desktop projects are the one exception (no Zephyr involved).
47
+ const wantsNative = options.framework === "native" || (!options.framework && targetId === "native");
48
+ if (!wantsNative) {
49
+ // The gate doubles as the lookup — its ok-return carries everything the
50
+ // printout needs (no second discovery pass).
51
+ const sdk = assertZephyrSdkForCreate();
52
+ if (sdk) {
53
+ console.log(chalk.gray(' Zephyr SDK:'));
54
+ for (const line of formatZephyrSdkFound(sdk))
55
+ console.log(chalk.gray(line));
56
+ }
57
+ // Build/refresh the board registry from the installed SDK now — the
58
+ // wizard's board list and --board resolution below read it.
59
+ ensureFreshBoardCatalog();
60
+ resetBoardCatalogOverlayCache();
61
+ }
62
+ // Preseeded targets run non-interactively ONLY when stdin is not a TTY
63
+ // (scripts/CI pipe nothing and expect zero prompts). A human typing
64
+ // `cuttlefish create x --board <target>` still gets the wizard — it
65
+ // prints the preseeded answers and asks only what was not specified
66
+ // (probe method, serial port, baud, starter).
67
+ const interactive = process.stdin.isTTY === true;
68
+ if (hasTarget && !interactive) {
69
+ // Target resolution: --target/--board names the native target or any
70
+ // board from the catalog (a qualified target like
71
+ // 'nucleo_f411re/stm32f411xe', or a bare board id).
72
+ let target = KNOWN_TARGETS.find(t => t.id === targetId)
73
+ ?? (() => {
74
+ const pack = findPackBoard(targetId);
75
+ return pack ? packBoardAsTarget(pack) : undefined;
76
+ })();
65
77
  if (!target) {
66
- const available = KNOWN_TARGETS.map(t => ` - ${t.id} (${t.displayName})`).join("\n");
67
- throw new Error(`Unknown target '${targetId}'. Available targets:\n${available}`);
78
+ // No catalog on this machine (or a typo). A QUALIFIED target passes
79
+ // through raw: the first build syncs the catalog from the Zephyr tree
80
+ // and validates it there, where the real error is actionable.
81
+ if (targetId.includes("/")) {
82
+ target = packBoardAsTarget({
83
+ identifier: targetId,
84
+ name: targetId,
85
+ soc: targetId.split("/")[1] ?? "",
86
+ });
87
+ }
88
+ else {
89
+ const known = KNOWN_TARGETS.map(t => ` - ${t.id} (${t.displayName})`);
90
+ throw new Error(`Unknown target '${targetId}'. Built-in targets:\n${known.join("\n")}\n` +
91
+ `Or pass any board from the catalog (e.g. --board nucleo_f411re/stm32f411xe) —\n` +
92
+ `the catalog comes from your Zephyr tree; run \`cuttlefish board sync\` (or the\n` +
93
+ `installer) to generate it.`);
94
+ }
68
95
  }
96
+ const compatibleFrameworks = frameworksForTarget(target)
97
+ .filter(f => f.installable);
69
98
  // Resolve the framework. --framework wins (validated against the board's
70
99
  // compatible set); otherwise narrow via the catalog for the chosen board
71
100
  // and auto-pick when exactly one is compatible.
@@ -77,8 +106,8 @@ async function handleCreate(options) {
77
106
  const available = FRAMEWORK_CATALOG.filter(f => f.installable).map(f => f.id).join(", ");
78
107
  throw new Error(`Unknown framework '${options.framework}'. Available: ${available}`);
79
108
  }
80
- if (!frameworkCompatibleWithTarget(target, requested.id)) {
81
- const list = frameworksForTarget(target).filter(f => f.installable).map(f => f.id).join(", ");
109
+ if (!compatibleFrameworks.some(f => f.id === requested.id)) {
110
+ const list = compatibleFrameworks.map(f => f.id).join(", ");
82
111
  throw new Error(`Framework '${requested.id}' is not compatible with target '${target.id}' (${target.displayName}). ` +
83
112
  `Compatible frameworks: ${list}`);
84
113
  }
@@ -86,35 +115,58 @@ async function handleCreate(options) {
86
115
  frameworkPackage = requested.packageName;
87
116
  }
88
117
  else {
89
- const compatible = frameworksForTarget(target).filter(f => f.installable);
90
- if (compatible.length === 1) {
91
- frameworkId = compatible[0].id;
92
- frameworkPackage = compatible[0].packageName;
118
+ if (compatibleFrameworks.length === 1) {
119
+ frameworkId = compatibleFrameworks[0].id;
120
+ frameworkPackage = compatibleFrameworks[0].packageName;
93
121
  }
94
122
  else {
95
- const list = compatible.map(f => f.id).join(", ");
123
+ const list = compatibleFrameworks.map(f => f.id).join(", ");
96
124
  throw new Error(`Target '${target.id}' is compatible with multiple frameworks (${list}). ` +
97
125
  `Pass --framework <id> to choose one.`);
98
126
  }
99
127
  }
128
+ // Probe methods (Zephyr boards whose packages ship a table). An explicit
129
+ // --probe id is validated against the catalog so a typo fails at create
130
+ // time, not at the first upload.
131
+ const probeMethods = frameworkId === 'zephyr' ? probeMethodsForBoard(target.id) : [];
132
+ if (options.probe && probeMethods.length > 0 && !probeMethods.some(m => m.id === options.probe)) {
133
+ const list = probeMethods.map(m => `${m.id} (${m.description})`).join('; ');
134
+ throw new Error(`Unknown probe method '${options.probe}' for ${target.displayName}. Supported: ${list}.`);
135
+ }
100
136
  const projectName = options.projectName || 'my-project';
101
- // Framework-specific build target + toolchain (Zephyr board id + 'west' vs
102
- // the Arduino FQBN + 'arduino-cli').
137
+ // Board-catalog quirk for the chosen probe method (e.g. an srst-based
138
+ // openocd.cfg behind a debug header with no NRST) — baked into the
139
+ // scaffolded config's runnerArgs so the first flash works.
140
+ const probeRunnerArgs = options.probe && frameworkId === 'zephyr' ? probeRunnerQuirks(target.id, options.probe) : [];
141
+ // Framework-specific build target + toolchain (Zephyr board id + 'west').
103
142
  const profile = frameworkTargetProfile(target, frameworkId);
143
+ const buildTarget = profile.buildTarget ?? target.buildTarget;
104
144
  const result = scaffoldProject({
145
+ probeMethod: options.probe,
146
+ probeMethods,
147
+ ...(probeRunnerArgs.length > 0 ? { probeRunnerArgs } : {}),
105
148
  projectName,
106
149
  targetId: target.id,
107
150
  targetDisplayName: target.displayName,
108
151
  isNative: target.isNative,
109
152
  architecture: target.architecture,
110
- boardPackage: target.boardPackage,
153
+ board: target.board,
154
+ // Pack fact: does the board's devicetree declare an LED? Drives the
155
+ // starter between LED-blink and console-heartbeat (384 of 1,248 pack
156
+ // boards ship no gpio-leds node).
157
+ ...(target.board ? { hasLed: Boolean(activeBoardCatalog()[target.board]?.led) } : {}),
111
158
  frameworkPackage,
112
159
  framework: frameworkId,
113
- buildTarget: profile.buildTarget ?? target.buildTarget,
160
+ buildTarget,
114
161
  ...(profile.toolchainType ? { toolchainType: profile.toolchainType } : {}),
115
- mcu: target.mcu,
116
- baudRate: target.isNative ? undefined : (options.baud ?? 9600),
117
- includeSketch: !options.noSketch,
162
+ // soc rides the config only for bare-silicon/contract projects — a
163
+ // board target's soc derives from the identifier at boardgen time.
164
+ ...(target.board ? {} : { soc: target.soc }),
165
+ // Zephyr consoles default 115200; the 9600 default is the Arduino-class
166
+ // convention.
167
+ baudRate: target.isNative ? undefined : (options.baud ?? (frameworkId === 'zephyr' ? 115200 : 9600)),
168
+ includeStarter: !options.noStarter,
169
+ ...(options.port ? { port: options.port } : {}),
118
170
  ...(target.frameworkData
119
171
  ? { frameworkData: target.frameworkData }
120
172
  : {}),
@@ -124,10 +176,12 @@ async function handleCreate(options) {
124
176
  else {
125
177
  const wizardResult = await runCreateWizard({
126
178
  projectName: options.projectName,
127
- board: options.board,
179
+ board: options.board ?? options.target,
128
180
  framework: options.framework,
129
181
  baud: options.baud,
130
- noSketch: options.noSketch,
182
+ noStarter: options.noStarter,
183
+ probe: options.probe,
184
+ port: options.port,
131
185
  });
132
186
  if (!wizardResult) {
133
187
  console.log(chalk.yellow("Project setup cancelled."));
@@ -179,22 +233,6 @@ function finalizeCreate(result, options) {
179
233
  }
180
234
  printCreateNextSteps(result.options, result.outDir, { installed, debugProfile: debugArtifacts.length > 0 });
181
235
  }
182
- async function handleBoardAdd(options) {
183
- const { scaffoldBoardPackages, parseBoardSpec, generateFrameworkChecklist } = await import("./create/index.js");
184
- if (!fs.existsSync(options.specPath)) {
185
- throw new Error(`Spec file not found: ${options.specPath}`);
186
- }
187
- console.log(`Reading spec: ${options.specPath}`);
188
- const specText = fs.readFileSync(options.specPath, "utf8");
189
- const spec = parseBoardSpec(specText);
190
- console.log(`Generating board packages for ${spec.architecture} (${spec.boardName})...`);
191
- const result = scaffoldBoardPackages(spec, { force: options.force });
192
- console.log(`\nCreated ${result.createdFiles.length} files:`);
193
- for (const f of result.createdFiles) {
194
- console.log(` ${path.relative(process.cwd(), f)}`);
195
- }
196
- console.log(generateFrameworkChecklist(spec));
197
- }
198
236
  async function main() {
199
237
  try {
200
238
  const options = parseCommandLine(process.argv);
@@ -206,10 +244,6 @@ async function main() {
206
244
  await handleCreate(options);
207
245
  return;
208
246
  }
209
- if (options.command === "board-add") {
210
- await handleBoardAdd(options);
211
- return;
212
- }
213
247
  if (options.command === "library") {
214
248
  await runLibraryCommand(options);
215
249
  return;
@@ -221,31 +255,6 @@ async function main() {
221
255
  });
222
256
  return;
223
257
  }
224
- if (options.command === "map-error") {
225
- if (!options.mapFile || !options.cppLine || !options.cppColumn) {
226
- throw new Error("map-error requires map file and C++ line/column options.");
227
- }
228
- const mapPath = resolveMapPath(options.mapFile);
229
- const sourceMap = readSourceMap(mapPath);
230
- const mapped = mapCppLocationToTs(sourceMap, options.cppLine, options.cppColumn, options.message, options.cppFile);
231
- if (!mapped.mappedTsSpan) {
232
- console.log("No TypeScript mapping found for the provided C++ location.");
233
- }
234
- else {
235
- console.log([
236
- `Mapped TS location: ${mapped.mappedTsSpan.filePath}`,
237
- `(${mapped.mappedTsSpan.startLine},${mapped.mappedTsSpan.startColumn})`,
238
- `node=${mapped.nodeKind ?? "unknown"}`,
239
- mapped.symbolName ? `symbol=${mapped.symbolName}` : undefined,
240
- ]
241
- .filter(Boolean)
242
- .join(" "));
243
- }
244
- if (mapped.message) {
245
- console.log(`Compiler message: ${mapped.message}`);
246
- }
247
- return;
248
- }
249
258
  if (options.command === "doctor" || options.command === "licenses") {
250
259
  // These commands run standalone (often before a build), so the framework
251
260
  // isn't loaded yet. Load it from the config's framework field so the
@@ -278,6 +287,74 @@ async function main() {
278
287
  fw.licenses(options.strict ?? false, options.all ?? false);
279
288
  return;
280
289
  }
290
+ // ── Handle board catalog sync / board module regen ────────────────────
291
+ if (options.command === "board") {
292
+ const config = loadCuttlefishConfig(process.cwd());
293
+ if (!config) {
294
+ throw new Error("No cuttlefish.config.ts found in current directory.\n" +
295
+ "Run 'cuttlefish create' to create one.");
296
+ }
297
+ // The framework owns the board catalog + module generation; load it
298
+ // the same way the doctor command does.
299
+ if (!hasLoadedFramework() && config.framework) {
300
+ try {
301
+ loadFrameworkPackage(config.framework, process.cwd());
302
+ }
303
+ catch {
304
+ // Framework package not resolvable — the hook error below names
305
+ // the problem better than a loader crash would.
306
+ }
307
+ }
308
+ const strategy = hasLoadedFramework()
309
+ ? getLoadedFramework().strategy
310
+ : undefined;
311
+ if (options.subcommand === "sync") {
312
+ if (typeof strategy?.syncBoardCatalog !== "function") {
313
+ throw new Error(`Framework '${config.framework ?? "(none)"}' provides no board catalog sync — ` +
314
+ `'cuttlefish board sync' applies to Zephyr-framework projects.`);
315
+ }
316
+ const report = strategy.syncBoardCatalog(options.zephyrBase);
317
+ const version = report.provenance.version || "unknown version";
318
+ const head = report.provenance.gitHead ? ` @ ${report.provenance.gitHead.slice(0, 12)}` : "";
319
+ const fp = sdkFingerprint(report.zephyrBase);
320
+ ui.printSuccess(`Board catalog synced from Zephyr ${version}${head}`);
321
+ console.log(chalk.gray(` tree: ${report.zephyrBase}`));
322
+ if (fp)
323
+ console.log(chalk.gray(` fingerprint: ${fp} (pin ${PINNED_ZEPHYR_MANIFEST_REV})`));
324
+ console.log(chalk.gray(` catalog: ${report.overlayPath}`));
325
+ console.log(` ${report.stats.variants} board variants (${report.stats.withFacts} with pin facts` +
326
+ `${report.stats.failures > 0 ? `, ${report.stats.failures} reader failures` : ""}` +
327
+ `${report.stats.droppedYamls > 0 ? `, ${report.stats.droppedYamls} yamls with no resolvable .dts` : ""})`);
328
+ const { added, changed, removed } = report;
329
+ const list = (ids, verb) => ids.length === 0
330
+ ? chalk.gray(` 0 ${verb}`)
331
+ : ` ${ids.length} ${verb}: ${ids.slice(0, 8).join(", ")}${ids.length > 8 ? ", …" : ""}`;
332
+ console.log(chalk.gray(" vs the previous catalog (what changed in your tree):"));
333
+ console.log(list(added, "added"));
334
+ console.log(list(changed, "changed"));
335
+ console.log(list(removed, "removed"));
336
+ // Refresh the project's own board module so the sync lands in this
337
+ // project immediately (not just on the next first-build).
338
+ if (config.board && !config.contract) {
339
+ const dir = regenBoardModule(config);
340
+ ui.printSuccess(`Regenerated the board module for '${config.board}' in ${path.join(dir, "board.ts")}`);
341
+ }
342
+ return;
343
+ }
344
+ // regen: refresh a stale catalog overlay first, so a regen after
345
+ // `west update` picks up the tree's boards automatically. Best-effort
346
+ // — a missing tree or a failed walk must not block regenerating from
347
+ // whatever catalog is already present.
348
+ try {
349
+ strategy?.ensureFreshBoardCatalog?.();
350
+ }
351
+ catch {
352
+ // Non-fatal by design (see above).
353
+ }
354
+ const dir = regenBoardModule(config);
355
+ ui.printSuccess(`Regenerated the board module for '${config.board}' in ${path.join(dir, "board.ts")}`);
356
+ return;
357
+ }
281
358
  // ── Handle build command — entry point comes from config ──────────
282
359
  if (options.command === "build") {
283
360
  const buildConfig = loadCuttlefishConfig(process.cwd());
@@ -287,7 +364,7 @@ async function main() {
287
364
  }
288
365
  if (!buildConfig.entry) {
289
366
  throw new Error("cuttlefish.config.ts has no 'entry' field.\n" +
290
- "Add: entry: './src/sketch.ts'");
367
+ "Add: entry: './src/main.ts'");
291
368
  }
292
369
  const entryFile = path.resolve(path.dirname(buildConfig.configPath), buildConfig.entry);
293
370
  if (!fs.existsSync(entryFile)) {
@@ -308,37 +385,6 @@ async function main() {
308
385
  // it intentionally has no inputFile (it scans scanDir instead), so the
309
386
  // generic "Missing input file path" check would otherwise block it.
310
387
  if (options.command === "gen-decls") {
311
- // Check for --components flag (ESP-IDF components: managed + local).
312
- // Runs before scanDir/single-file branches; components mode has no
313
- // inputFile by design (it scans managed_components/ + components/).
314
- const componentsDir = options.componentsDir;
315
- if (componentsDir) {
316
- ui.printHeader();
317
- ui.printStep(`Generating component declarations for ${componentsDir}...`);
318
- const config = loadCuttlefishConfig(componentsDir);
319
- const frameworkConfig = (config?.frameworkConfig ?? {});
320
- const componentsNode = frameworkConfig?.components ?? {};
321
- const managedSpecs = Object.keys(componentsNode.managed ?? {});
322
- // idf.py stores managed deps as <namespace>__<name> (slashes → __).
323
- const managedNames = managedSpecs.map((spec) => spec.replace("/", "__"));
324
- const localPaths = (componentsNode.local ?? []).map((p) => path.isAbsolute(p) ? p : path.resolve(componentsDir, p));
325
- const builtinNames = componentsNode.builtin ?? [];
326
- const created = generateComponentDeclsForProject(componentsDir, {
327
- managed: managedNames,
328
- local: localPaths,
329
- builtin: [],
330
- idfRoot: undefined,
331
- });
332
- if (created.length === 0) {
333
- ui.printInfo("No component declaration files created.");
334
- }
335
- else {
336
- ui.printSuccess(`Created ${created.length} declaration file(s):`);
337
- for (const f of created)
338
- ui.printFileCreated(f);
339
- }
340
- return;
341
- }
342
388
  // Check for --all flag (scan directory)
343
389
  const scanDir = options.scanDir;
344
390
  if (scanDir) {
@@ -379,9 +425,9 @@ async function main() {
379
425
  if (options.expect) {
380
426
  const config = loadCuttlefishConfig(process.cwd());
381
427
  const exitCode = runExpectTests({
382
- port: options.port ?? config?.console?.port,
428
+ port: options.port,
383
429
  buildTarget: config?.buildTarget,
384
- baud: config?.console?.baudRate ?? options.baud,
430
+ baud: options.baud,
385
431
  expectFile: options.expectFile,
386
432
  });
387
433
  process.exitCode = exitCode;
@@ -390,23 +436,6 @@ async function main() {
390
436
  throw new Error("Missing input file path.");
391
437
  }
392
438
  assertTypeScriptInput(options.inputFile);
393
- if (options.command === "gen-libdefs") {
394
- const outDir = path.dirname(options.inputFile);
395
- const created = generateLibraryDefinitions({
396
- inputFile: options.inputFile,
397
- outDir,
398
- });
399
- if (created.length === 0) {
400
- console.log("No new library definition files created.");
401
- }
402
- else {
403
- console.log("Created library definition files:");
404
- for (const filePath of created) {
405
- console.log(`- ${filePath}`);
406
- }
407
- }
408
- return;
409
- }
410
439
  // ── Load cuttlefish.config.ts (config wins over CLI flags) ──────────
411
440
  const inputDir = path.dirname(path.resolve(options.inputFile));
412
441
  const config = loadCuttlefishConfig(inputDir);
@@ -419,42 +448,6 @@ async function main() {
419
448
  if (config?.contract) {
420
449
  await generateContractBoard(config);
421
450
  }
422
- // ── Pre-transpile: generate component .d.ts stubs ───────────────────
423
- // The transpile type-check needs the .d.ts imports in main.ts to resolve.
424
- // For ESP-IDF builtin/managed/local components, those .d.ts files are
425
- // generated by gen-decls — so we must run it BEFORE type-checking, not
426
- // only as part of --compile. The cache is written under the transpile
427
- // output dir (the same path --compile uses), so user imports resolve
428
- // identically in both flows.
429
- if (config?.frameworkConfig) {
430
- const fc = config.frameworkConfig;
431
- const componentsNode = fc?.components ?? {};
432
- const managedSpecs = Object.keys(componentsNode.managed ?? {});
433
- const localPaths = (componentsNode.local ?? []).map((p) => path.isAbsolute(p) ? p : path.resolve(inputDir, p));
434
- const builtinNames = componentsNode.builtin ?? [];
435
- if (managedSpecs.length > 0 || localPaths.length > 0 || builtinNames.length > 0) {
436
- // Resolve the transpile output dir — the cache lives under it so the
437
- // framework's compile path and this pre-transpile pass agree on location.
438
- // output.outDir in config is relative to the project root (inputDir);
439
- // if absent, the transpile output goes alongside the entry file.
440
- const outBase = config.outputOutDir
441
- ? path.resolve(inputDir, config.outputOutDir)
442
- : inputDir;
443
- try {
444
- generateComponentDeclsForProject(outBase, {
445
- managed: managedSpecs.map((s) => s.replace('/', '__')),
446
- local: localPaths,
447
- builtin: [],
448
- idfRoot: undefined,
449
- });
450
- }
451
- catch {
452
- // Non-fatal: if gen-decls fails (e.g. component not yet fetched),
453
- // the type-checker will surface the missing-import errors with
454
- // clearer context than crashing here.
455
- }
456
- }
457
- }
458
451
  // Load the UI engine (if @typecad/ui is installed) before any UI work.
459
452
  await loadUIEngine();
460
453
  // Load the safety engine (if @typecad/safety is installed) before any
@@ -463,9 +456,8 @@ async function main() {
463
456
  let effectivePlatformContext = options.platformContext;
464
457
  let effectiveTarget = options.target;
465
458
  let effectiveOutDir = options.outDir;
466
- let effectiveBoardPackage = options.boardPackage;
459
+ let effectiveBoardTarget = options.boardTarget;
467
460
  let effectiveFrameworkPackage = options.frameworkPackage;
468
- let effectiveMcuPackage;
469
461
  let effectivePort = options.port;
470
462
  // CUTTLEFISH_PORT env var sits between the CLI flag and the config file
471
463
  // (flag > env > config), so cross-platform uploads don't need a
@@ -474,15 +466,8 @@ async function main() {
474
466
  effectivePort = process.env.CUTTLEFISH_PORT;
475
467
  }
476
468
  if (config) {
477
- if (config.mcu) {
478
- effectiveMcuPackage = config.mcu;
479
- }
480
- // Config port is the default; CLI --port flag overrides it.
481
- if (!effectivePort && config.console?.port) {
482
- effectivePort = config.console.port;
483
- }
484
- if (config.board || config.mcu) {
485
- effectiveBoardPackage = config.board || config.mcu;
469
+ if (config.board) {
470
+ effectiveBoardTarget = config.board;
486
471
  }
487
472
  let configBuildTarget = config.buildTarget;
488
473
  if (configBuildTarget) {
@@ -493,7 +478,7 @@ async function main() {
493
478
  frameworkData: {
494
479
  buildTarget: configBuildTarget,
495
480
  // Thread the PSRAM type through to the framework so it can emit the
496
- // PSRAM-enabling Kconfig (Zephyr) / define + FQBN option (Arduino).
481
+ // PSRAM-enabling Kconfig + BOARD_HAS_PSRAM compile definition.
497
482
  ...(config.psram ? { psram: config.psram } : {}),
498
483
  },
499
484
  };
@@ -504,24 +489,6 @@ async function main() {
504
489
  if (config.framework) {
505
490
  effectiveFrameworkPackage = config.framework;
506
491
  }
507
- if (config.target) {
508
- if (effectiveFrameworkPackage) {
509
- try {
510
- loadFrameworkPackage(effectiveFrameworkPackage, inputDir);
511
- if (hasLoadedFramework()) {
512
- effectiveTarget = getLoadedFramework().strategy.id;
513
- }
514
- }
515
- catch {
516
- // Framework not loadable — keep the CLI-provided target
517
- }
518
- }
519
- }
520
- // Pass console baud rate to platform context
521
- if (config.console?.baudRate) {
522
- effectivePlatformContext = effectivePlatformContext || {};
523
- effectivePlatformContext.console = { baudRate: config.console.baudRate };
524
- }
525
492
  // Keep cuttlefish-env.d.ts in sync so the TS language server can resolve
526
493
  // '@typecad/board' imports in editor without a linter error.
527
494
  // Platform-specific declarations come from the strategy if available.
@@ -547,8 +514,7 @@ async function main() {
547
514
  ui.printHeader();
548
515
  ui.printBuildInfo({
549
516
  framework: effectiveFrameworkPackage,
550
- mcu: effectiveMcuPackage,
551
- board: effectiveBoardPackage,
517
+ board: effectiveBoardTarget,
552
518
  buildTarget: effectivePlatformContext?.frameworkData?.buildTarget,
553
519
  });
554
520
  // ── Watch mode ────────────────────────────────────────────────────────
@@ -566,7 +532,7 @@ async function main() {
566
532
  emitMaps: options.emitMaps,
567
533
  platformContext: effectivePlatformContext,
568
534
  treeShaking: options.treeShaking,
569
- boardPackage: effectiveBoardPackage,
535
+ boardTarget: effectiveBoardTarget,
570
536
  frameworkPackage: effectiveFrameworkPackage,
571
537
  debug: options.debug,
572
538
  force: options.force,
@@ -588,28 +554,29 @@ async function main() {
588
554
  // Initial compile + upload if flags are set
589
555
  if (options.compile) {
590
556
  const baseBuildTarget = effectivePlatformContext?.frameworkData?.buildTarget ?? options.platformContext?.frameworkData?.buildTarget;
591
- const { buildTarget, defines: psramDefines } = applyPsramToArduinoBuild(baseBuildTarget, config?.outputDefines, config?.psram);
592
557
  const watchOpts = {
593
558
  outputDir: path.dirname(result.sourcePath),
594
559
  sourcePath: result.sourcePath,
595
- buildTarget,
560
+ buildTarget: baseBuildTarget,
596
561
  port: effectivePort,
597
- baud: options.baud ?? config?.console?.baudRate,
562
+ baud: options.baud,
598
563
  extraFlags: config?.outputExtraFlags,
599
- defines: psramDefines,
564
+ defines: config?.outputDefines ?? {},
600
565
  psram: config?.psram,
601
566
  frameworkConfig: config?.frameworkConfig,
602
567
  zephyrConfig: config?.zephyrConfig,
603
568
  display: displayConfigForTranspile(config),
604
569
  debug: options.debug,
605
570
  };
606
- ui.printCompiling(buildTarget ?? "native");
571
+ ui.printCompiling(baseBuildTarget ?? "native");
607
572
  const compileResult = compileSource(watchOpts);
608
573
  printMappedCompileErrors(compileResult, result.sourceMapPath, result.sourcePath, path.dirname(result.sourcePath));
609
574
  if (compileResult.success) {
610
575
  if (compileResult.memoryUsage)
611
576
  ui.printMemoryUsage(compileResult.memoryUsage);
612
- if (options.upload && effectivePort) {
577
+ if (options.upload) {
578
+ // No port gate here — probe/USB flashing needs none, and the
579
+ // framework reports a friendly failure when the runner does.
613
580
  ui.printUploading(effectivePort);
614
581
  const uploadResult = uploadFirmware(watchOpts);
615
582
  if (uploadResult.output)
@@ -644,7 +611,7 @@ async function main() {
644
611
  process.stdout.write("\x1Bc");
645
612
  const timestamp = new Date().toLocaleTimeString();
646
613
  const relativePath = path.relative(process.cwd(), changedFile);
647
- console.log(chalk.cyan(`⤳ Cuttlefish`) + chalk.gray(` v0.1.0`));
614
+ console.log(chalk.cyan(`⤳ Cuttlefish`) + chalk.gray(` v${ui.VERSION}`));
648
615
  console.log();
649
616
  ui.printInfo(`[${timestamp}] Change detected: ${relativePath}`);
650
617
  console.log();
@@ -665,7 +632,7 @@ async function main() {
665
632
  emitMaps: options.emitMaps,
666
633
  platformContext: effectivePlatformContext,
667
634
  treeShaking: options.treeShaking,
668
- boardPackage: effectiveBoardPackage,
635
+ boardTarget: effectiveBoardTarget,
669
636
  frameworkPackage: effectiveFrameworkPackage,
670
637
  debug: options.debug,
671
638
  force: true, // Always force in watch mode to bypass stale cache
@@ -688,28 +655,30 @@ async function main() {
688
655
  ui.printSuccess();
689
656
  if (options.compile) {
690
657
  const baseBuildTarget = effectivePlatformContext?.frameworkData?.buildTarget ?? options.platformContext?.frameworkData?.buildTarget;
691
- const { buildTarget, defines: psramDefines } = applyPsramToArduinoBuild(baseBuildTarget, config?.outputDefines, config?.psram);
692
658
  const rebuildOpts = {
693
659
  outputDir: path.dirname(rebuildResult.sourcePath),
694
660
  sourcePath: rebuildResult.sourcePath,
695
- buildTarget,
661
+ buildTarget: baseBuildTarget,
696
662
  port: effectivePort,
697
- baud: options.baud ?? config?.console?.baudRate,
663
+ baud: options.baud,
698
664
  extraFlags: config?.outputExtraFlags,
699
- defines: psramDefines,
665
+ defines: config?.outputDefines ?? {},
700
666
  psram: config?.psram,
701
667
  frameworkConfig: config?.frameworkConfig,
702
668
  zephyrConfig: config?.zephyrConfig,
703
669
  display: displayConfigForTranspile(config),
704
670
  debug: options.debug,
705
671
  };
706
- ui.printCompiling(buildTarget ?? "native");
672
+ ui.printCompiling(baseBuildTarget ?? "native");
707
673
  const compileResult = compileSource(rebuildOpts);
708
674
  printMappedCompileErrors(compileResult, rebuildResult.sourceMapPath, rebuildResult.sourcePath, path.dirname(rebuildResult.sourcePath));
709
675
  if (compileResult.success) {
710
676
  if (compileResult.memoryUsage)
711
677
  ui.printMemoryUsage(compileResult.memoryUsage);
712
- if (options.upload && effectivePort) {
678
+ if (options.upload) {
679
+ // No port gate here — probe/USB flashing needs none, and
680
+ // the framework reports a friendly failure when the
681
+ // runner does.
713
682
  ui.printUploading(effectivePort);
714
683
  const uploadResult = uploadFirmware(rebuildOpts);
715
684
  if (uploadResult.output)
@@ -758,7 +727,7 @@ async function main() {
758
727
  emitMaps: options.emitMaps,
759
728
  platformContext: effectivePlatformContext,
760
729
  treeShaking: options.treeShaking,
761
- boardPackage: effectiveBoardPackage,
730
+ boardTarget: effectiveBoardTarget,
762
731
  frameworkPackage: effectiveFrameworkPackage,
763
732
  debug: options.debug,
764
733
  force: options.force,
@@ -786,7 +755,7 @@ async function main() {
786
755
  const exitCode = runExpectTests({
787
756
  port: effectivePort,
788
757
  buildTarget: effectivePlatformContext?.frameworkData?.buildTarget ?? options.platformContext?.frameworkData?.buildTarget,
789
- baud: config?.console?.baudRate ?? options.baud,
758
+ baud: options.baud,
790
759
  expectFile: options.expectFile,
791
760
  });
792
761
  process.exitCode = exitCode;
@@ -795,22 +764,24 @@ async function main() {
795
764
  }
796
765
  // --compile (delegates to the active framework's toolchain)
797
766
  const baseBuildTarget = effectivePlatformContext?.frameworkData?.buildTarget ?? options.platformContext?.frameworkData?.buildTarget;
798
- const { buildTarget, defines: psramDefines } = applyPsramToArduinoBuild(baseBuildTarget, config?.outputDefines, config?.psram);
799
767
  const toolchainOpts = {
800
768
  outputDir: path.dirname(result.sourcePath),
801
769
  sourcePath: result.sourcePath,
802
- buildTarget,
770
+ buildTarget: baseBuildTarget,
803
771
  port: effectivePort,
804
- baud: options.baud ?? config?.console?.baudRate,
772
+ baud: options.baud,
805
773
  extraFlags: config?.outputExtraFlags,
806
- defines: psramDefines,
774
+ defines: config?.outputDefines ?? {},
807
775
  psram: config?.psram,
808
776
  frameworkConfig: config?.frameworkConfig,
809
- zephyrConfig: config?.zephyrConfig,
777
+ // --probe <method> overrides zephyr.probe from the config for this run.
778
+ zephyrConfig: options.probe
779
+ ? { ...(config?.zephyrConfig ?? {}), probe: options.probe }
780
+ : config?.zephyrConfig,
810
781
  display: displayConfigForTranspile(config),
811
782
  debug: options.debug,
812
783
  };
813
- ui.printCompiling(buildTarget ?? "native");
784
+ ui.printCompiling(baseBuildTarget ?? "native");
814
785
  let compileResult;
815
786
  try {
816
787
  compileResult = compileSource(toolchainOpts);
@@ -840,10 +811,11 @@ async function main() {
840
811
  ui.printSuccess();
841
812
  return;
842
813
  }
843
- // --upload (requires a port from --port flag or config.console.port)
844
- if (!effectivePort) {
845
- throw new Error("--upload requires a port. Set --port <port> on the command line or console.port in cuttlefish.config.ts.");
846
- }
814
+ // --upload. Whether a port is required is the active framework's call —
815
+ // probe-based flashing (openocd/jlink) and USB bootloaders (dfu-util,
816
+ // uf2) need no serial port, so a missing --port only surfaces as the
817
+ // framework's own upload failure for serial-port runners (esptool,
818
+ // bossac).
847
819
  const port = effectivePort;
848
820
  ui.printUploading(port);
849
821
  const uploadResult = uploadFirmware(toolchainOpts);
@@ -859,9 +831,14 @@ async function main() {
859
831
  return;
860
832
  }
861
833
  if (options.monitor) {
862
- // --monitor (blocks until Ctrl+C). Show the resolved baud (config ?? --baud)
863
- // in the bannertoolchainOpts.baud is built the same way, so what the
864
- // user sees is what the framework monitor opens the port at.
834
+ // --monitor (blocks until Ctrl+C). Unlike flashing, monitoring always
835
+ // needs a serial port there is no probe/USB flavor of it.
836
+ if (!port) {
837
+ throw new Error("--monitor requires a port. Set --port <port> on the command line (or the CUTTLEFISH_PORT env var).");
838
+ }
839
+ // Show the resolved --baud in the banner — toolchainOpts.baud is built
840
+ // the same way, so what the user sees is what the framework monitor
841
+ // opens the port at.
865
842
  ui.printMonitoring(port, toolchainOpts.baud ?? 115200);
866
843
  monitorDevice(toolchainOpts);
867
844
  return;
@@ -871,8 +848,8 @@ async function main() {
871
848
  ui.printSuccess();
872
849
  const exitCode = runExpectTests({
873
850
  port: effectivePort,
874
- buildTarget,
875
- baud: config?.console?.baudRate ?? options.baud,
851
+ buildTarget: baseBuildTarget,
852
+ baud: options.baud,
876
853
  expectFile: options.expectFile,
877
854
  });
878
855
  process.exitCode = exitCode;