@typecad/cuttlefish 1.0.0-alpha.13 → 1.0.0-alpha.15

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