@typecad/cuttlefish 0.1.0-alpha.1 → 1.0.0-alpha.10

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 (225) hide show
  1. package/README.md +10 -10
  2. package/dist/api/board-types.d.ts +1 -1
  3. package/dist/api/config.d.ts +32 -0
  4. package/dist/api/index.d.ts +1 -1
  5. package/dist/api/schema/types.d.ts +15 -0
  6. package/dist/api/shared/async-runtime-static.d.ts +12 -1
  7. package/dist/api/shared/async-runtime-static.js +69 -24
  8. package/dist/api/shared/async-symbol-detector.d.ts +11 -0
  9. package/dist/api/shared/async-symbol-detector.js +140 -0
  10. package/dist/api/shared/async-types.d.ts +24 -0
  11. package/dist/api/shared/coop-scheduler.d.ts +60 -0
  12. package/dist/api/shared/coop-scheduler.js +149 -0
  13. package/dist/api/shared/display-adapter.d.ts +4 -3
  14. package/dist/api/shared/display-adapter.js +15 -118
  15. package/dist/api/shared/display-adapters/sdl.js +38 -31
  16. package/dist/api/shared/display-profile.d.ts +47 -25
  17. package/dist/api/shared/display-profile.js +31 -164
  18. package/dist/api/shared/framework-manifest-registry.d.ts +9 -0
  19. package/dist/api/shared/framework-manifest-registry.js +27 -0
  20. package/dist/api/shared/framework-manifest.d.ts +595 -0
  21. package/dist/api/shared/framework-manifest.js +193 -0
  22. package/dist/api/shared/glcdfont.d.ts +12 -0
  23. package/dist/api/shared/glcdfont.js +124 -0
  24. package/dist/api/shared/graphics-strategy.d.ts +37 -0
  25. package/dist/api/shared/hal-op-ir.d.ts +764 -1
  26. package/dist/api/shared/hal-op-ir.js +129 -1
  27. package/dist/api/shared/index.d.ts +20 -2
  28. package/dist/api/shared/index.js +25 -1
  29. package/dist/api/shared/ir-core.d.ts +4 -0
  30. package/dist/api/shared/ir-declarations.d.ts +6 -0
  31. package/dist/api/shared/native-display-op-resolver.d.ts +10 -0
  32. package/dist/api/shared/native-display-op-resolver.js +64 -0
  33. package/dist/api/shared/platform-strategy.d.ts +119 -2
  34. package/dist/api/shared/polyfill-helper-registry.js +18 -1
  35. package/dist/api/shared/promise-runtime.d.ts +1 -1
  36. package/dist/api/shared/promise-runtime.js +95 -13
  37. package/dist/api/shared/toolchain-types.d.ts +17 -0
  38. package/dist/api/shared/types.d.ts +8 -0
  39. package/dist/api/shared/validate-framework-manifest.d.ts +28 -0
  40. package/dist/api/shared/validate-framework-manifest.js +494 -0
  41. package/dist/api/shared/worker-runtime-polyfill.d.ts +22 -0
  42. package/dist/api/shared/worker-runtime-polyfill.js +34 -0
  43. package/dist/api/shared/worker-runtime.d.ts +69 -0
  44. package/dist/api/shared/worker-runtime.js +163 -0
  45. package/dist/cli-utils.d.ts +1 -0
  46. package/dist/cli-utils.js +3 -1
  47. package/dist/cli.js +260 -35
  48. package/dist/config-loader.d.ts +7 -2
  49. package/dist/config-loader.js +23 -3
  50. package/dist/config-schema.d.ts +112 -70
  51. package/dist/config-schema.js +14 -0
  52. package/dist/contract/board-generator.d.ts +17 -0
  53. package/dist/contract/board-generator.js +57 -0
  54. package/dist/contract/contract-parser.d.ts +217 -0
  55. package/dist/contract/contract-parser.js +224 -0
  56. package/dist/contract/index.d.ts +22 -0
  57. package/dist/contract/index.js +68 -0
  58. package/dist/create/board-codegen.js +4 -4
  59. package/dist/create/board-generators.js +4 -5
  60. package/dist/create/board-spec.d.ts +72 -75
  61. package/dist/create/board-spec.js +0 -1
  62. package/dist/create/framework-catalog.d.ts +64 -0
  63. package/dist/create/framework-catalog.js +136 -0
  64. package/dist/create/index.d.ts +5 -1
  65. package/dist/create/index.js +3 -1
  66. package/dist/create/init-scaffold.d.ts +12 -3
  67. package/dist/create/init-scaffold.js +49 -14
  68. package/dist/create/init-templates.d.ts +4 -0
  69. package/dist/create/init-templates.js +263 -17
  70. package/dist/create/init-wizard.js +41 -27
  71. package/dist/create/install-deps.d.ts +32 -0
  72. package/dist/create/install-deps.js +46 -0
  73. package/dist/debug/preprocessor.js +178 -39
  74. package/dist/debug/types.d.ts +23 -0
  75. package/dist/diagnostics/mermaid-builder.d.ts +1 -1
  76. package/dist/diagnostics/mermaid-builder.js +34 -24
  77. package/dist/emit/compliance/arxml-writer.d.ts +11 -0
  78. package/dist/emit/compliance/arxml-writer.js +34 -0
  79. package/dist/emit/compliance/compliance-context.d.ts +57 -0
  80. package/dist/emit/compliance/compliance-context.js +113 -0
  81. package/dist/emit/compliance/deviation-ledger.d.ts +27 -0
  82. package/dist/emit/compliance/deviation-ledger.js +47 -0
  83. package/dist/emit/compliance/deviation-writer.d.ts +30 -0
  84. package/dist/emit/compliance/deviation-writer.js +37 -0
  85. package/dist/emit/compliance/index.d.ts +7 -0
  86. package/dist/emit/compliance/index.js +6 -0
  87. package/dist/emit/compliance/rule-engine.d.ts +13 -0
  88. package/dist/emit/compliance/rule-engine.js +101 -0
  89. package/dist/emit/compliance/rules.d.ts +16 -0
  90. package/dist/emit/compliance/rules.js +191 -0
  91. package/dist/emit/compliance/types.d.ts +59 -0
  92. package/dist/emit/compliance/types.js +8 -0
  93. package/dist/emit/cpp-emitter.js +4 -3
  94. package/dist/emit/emitters/class-emitter.js +6 -1
  95. package/dist/emit/emitters/emitter-context.d.ts +28 -1
  96. package/dist/emit/emitters/function-emitter-impl.js +153 -53
  97. package/dist/emit/emitters/line-appender.js +19 -0
  98. package/dist/emit/emitters/line-marker.d.ts +38 -0
  99. package/dist/emit/emitters/line-marker.js +39 -0
  100. package/dist/emit/emitters/output-finalizer.d.ts +6 -0
  101. package/dist/emit/emitters/output-finalizer.js +101 -12
  102. package/dist/emit/emitters/setup.d.ts +17 -0
  103. package/dist/emit/emitters/setup.js +372 -30
  104. package/dist/emit/emitters/top-level-prep.js +8 -0
  105. package/dist/emit/emitters/type-decl-emitter.js +28 -3
  106. package/dist/emit/emitters/ui-emitter.js +33 -9
  107. package/dist/emit/expression-renderer.d.ts +1 -1
  108. package/dist/emit/expression-renderer.js +91 -6
  109. package/dist/emit/route-hal-op.js +18 -5
  110. package/dist/emit/snprintf-helpers.js +15 -4
  111. package/dist/emit/statement-renderer.d.ts +10 -0
  112. package/dist/emit/statement-renderer.js +34 -5
  113. package/dist/emit/utils/async-state-machine.js +221 -125
  114. package/dist/emit/utils/hal-op-cpp-type.d.ts +6 -0
  115. package/dist/emit/utils/hal-op-cpp-type.js +40 -0
  116. package/dist/framework-package.js +2 -0
  117. package/dist/framework-registry.d.ts +17 -0
  118. package/dist/install/framework-catalog.d.ts +53 -0
  119. package/dist/install/framework-catalog.js +107 -0
  120. package/dist/install/handle-install.d.ts +35 -0
  121. package/dist/install/handle-install.js +177 -0
  122. package/dist/install/index.d.ts +4 -0
  123. package/dist/install/index.js +3 -0
  124. package/dist/ir/adc-range-validation.js +40 -26
  125. package/dist/ir/build-ir-state.d.ts +1 -0
  126. package/dist/ir/build-ir-state.js +14 -0
  127. package/dist/ir/build-ir.js +9 -5
  128. package/dist/ir/call-graph.js +16 -0
  129. package/dist/ir/expression-to-ir.js +207 -6
  130. package/dist/ir/feature-registry.js +7 -25
  131. package/dist/ir/function-builder.js +22 -0
  132. package/dist/ir/hal/hal-emitter.d.ts +5 -2
  133. package/dist/ir/hal/hal-emitter.js +50 -18
  134. package/dist/ir/hal/hal-parser.d.ts +6 -0
  135. package/dist/ir/hal/hal-parser.js +81 -0
  136. package/dist/ir/hal/hal-plugins.js +733 -1
  137. package/dist/ir/identifier-collector.js +35 -0
  138. package/dist/ir/interrupt-analysis.d.ts +5 -1
  139. package/dist/ir/interrupt-analysis.js +16 -19
  140. package/dist/ir/memory-budget-validation.js +1 -0
  141. package/dist/ir/network-validation.d.ts +4 -0
  142. package/dist/ir/network-validation.js +184 -0
  143. package/dist/ir/ownership-analysis.js +39 -1
  144. package/dist/ir/peripheral-ownership.js +5 -0
  145. package/dist/ir/peripheral-validation.d.ts +1 -1
  146. package/dist/ir/peripheral-validation.js +6 -3
  147. package/dist/ir/pin-alias-conflict.d.ts +1 -1
  148. package/dist/ir/pin-alias-conflict.js +2 -1
  149. package/dist/ir/pin-capability-validation.js +71 -32
  150. package/dist/ir/pin-mode-validation.d.ts +2 -2
  151. package/dist/ir/pin-mode-validation.js +54 -18
  152. package/dist/ir/pin-safety.d.ts +1 -1
  153. package/dist/ir/pin-safety.js +2 -1
  154. package/dist/ir/program-analysis.d.ts +81 -0
  155. package/dist/ir/program-analysis.js +429 -0
  156. package/dist/ir/pulldown-validation.d.ts +1 -1
  157. package/dist/ir/pulldown-validation.js +2 -1
  158. package/dist/ir/pwm-timer-sharing.d.ts +1 -1
  159. package/dist/ir/pwm-timer-sharing.js +2 -1
  160. package/dist/ir/render-expr.js +11 -0
  161. package/dist/ir/resource-analysis.js +2 -0
  162. package/dist/ir/timer0-pwm-timing-conflict.d.ts +1 -1
  163. package/dist/ir/timer0-pwm-timing-conflict.js +2 -1
  164. package/dist/ir/timing-validation.d.ts +6 -1
  165. package/dist/ir/timing-validation.js +61 -13
  166. package/dist/ir/transformers/call-statement.js +105 -0
  167. package/dist/ir/transformers/expressions.js +62 -0
  168. package/dist/ir/transformers/hal-call-resolver.js +21 -0
  169. package/dist/ir/transformers/hal-emit-helpers.js +1 -1
  170. package/dist/ir/transformers/namespace-methods.js +17 -12
  171. package/dist/ir/transformers/ui-reactive.js +2 -2
  172. package/dist/ir/transformers/variables.js +137 -19
  173. package/dist/ir/try-catch-validation.js +2 -0
  174. package/dist/ir/type-resolution.js +20 -2
  175. package/dist/ir/ui-element-auto-wire.js +7 -5
  176. package/dist/ir/unit-suspicion-validation.js +9 -7
  177. package/dist/ir/utils/map-statements.d.ts +4 -0
  178. package/dist/ir/utils/map-statements.js +79 -0
  179. package/dist/ir/validation-orchestrator.js +15 -8
  180. package/dist/ir/worker-analysis.d.ts +10 -0
  181. package/dist/ir/worker-analysis.js +261 -0
  182. package/dist/libdef/c-to-decl.d.ts +27 -0
  183. package/dist/libdef/c-to-decl.js +397 -0
  184. package/dist/libdef/component-decls.d.ts +2 -0
  185. package/dist/libdef/component-decls.js +6 -0
  186. package/dist/libdef/component-discovery.d.ts +43 -0
  187. package/dist/libdef/component-discovery.js +83 -0
  188. package/dist/libdef/cpp-to-decl.d.ts +9 -0
  189. package/dist/libdef/cpp-to-decl.js +72 -0
  190. package/dist/libdef/registry.js +5 -2
  191. package/dist/lint-cache.d.ts +59 -0
  192. package/dist/lint-cache.js +257 -0
  193. package/dist/orchestrator/graph-builder.js +14 -9
  194. package/dist/orchestrator/type-checker.js +4 -2
  195. package/dist/platform/coop-scheduler-runtime.d.ts +19 -0
  196. package/dist/platform/coop-scheduler-runtime.js +41 -0
  197. package/dist/platform/generic-strategy.d.ts +15 -3
  198. package/dist/platform/generic-strategy.js +49 -4
  199. package/dist/safety/safety-bridge.d.ts +11 -0
  200. package/dist/safety/safety-bridge.js +48 -0
  201. package/dist/safety/sidecar-bridge.d.ts +5 -0
  202. package/dist/safety/sidecar-bridge.js +22 -0
  203. package/dist/safety-hook.d.ts +79 -0
  204. package/dist/safety-hook.js +35 -0
  205. package/dist/testing.d.ts +9 -2
  206. package/dist/testing.js +11 -2
  207. package/dist/transpile.d.ts +3 -0
  208. package/dist/transpile.js +202 -54
  209. package/dist/types.d.ts +22 -3
  210. package/dist/ui/ui-bridge.d.ts +12 -0
  211. package/dist/ui/ui-bridge.js +18 -0
  212. package/dist/ui-hook.d.ts +17 -3
  213. package/dist/utils/cli.js +122 -2
  214. package/dist/utils/fs.d.ts +13 -0
  215. package/dist/utils/fs.js +50 -0
  216. package/dist/utils/ui.js +3 -1
  217. package/package.json +147 -127
  218. package/dist/api/shared/display-adapters/eink-mono.d.ts +0 -2
  219. package/dist/api/shared/display-adapters/eink-mono.js +0 -53
  220. package/dist/api/shared/display-adapters/ssd1309.d.ts +0 -2
  221. package/dist/api/shared/display-adapters/ssd1309.js +0 -136
  222. package/dist/api/shared/display-adapters/st7796.d.ts +0 -2
  223. package/dist/api/shared/display-adapters/st7796.js +0 -132
  224. package/dist/ir/heap-array-validation.d.ts +0 -24
  225. package/dist/ir/heap-array-validation.js +0 -29
@@ -0,0 +1,163 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Shared worker-offload runtime C++ code generator
3
+ //
4
+ // Generates the generic worker pool + the __tc_worker_submit / __tc_worker_done
5
+ // contract that generalizes the one-off ESP32 HTTP offload pattern
6
+ // (framework-esp32/src/lowering/http.ts: the __tc_http_send_task /
7
+ // __tc_http_send_start / __tc_http_done trio). The pool and contract live here;
8
+ // the per-framework spawn/signal/poll PRIMITIVES are injected via the strategy
9
+ // hooks workerSpawnLines / workerSignalDoneExpr / workerIsDoneExpr.
10
+ //
11
+ // Design (the safety property, per the approved plan):
12
+ // - The worker runtime is request-in / poll-out: the worker function receives
13
+ // its inputs by value and exposes outputs only through the opaque handle.
14
+ // - No mutex/semaphore is added to the IR. The worker-isolation analyzer
15
+ // (worker-analysis.ts) makes shared mutable state a transpile-time error.
16
+ // - STL-free: fixed-size static slot table, function-pointer + void*. Links
17
+ // on minimal-libc targets (Zephyr).
18
+ //
19
+ // The memory-barrier contract is load-bearing on dual-core targets: the signal
20
+ // primitive (workerSignalDoneExpr) MUST issue a full barrier so the consumer
21
+ // observes the worker's preceding output writes once it observes completion.
22
+ // ESP32 xTaskNotifyGive and Zephyr k_sem_give both satisfy this; bare volatile
23
+ // does NOT across cores.
24
+ // ---------------------------------------------------------------------------
25
+ /**
26
+ * Generate the worker-offload runtime C++ for a framework that supplies a
27
+ * backing. Returns null if `backing` is null (unsupported on this framework).
28
+ *
29
+ * The emitted code defines, for each slot i in [0, poolSize):
30
+ * - a worker trampoline __tc_worker_fn_i(void* arg) that runs the recorded
31
+ * fn and then signals completion via signalDoneExpr,
32
+ * - __tc_worker_submit(i, fn, arg) — records the waiter, clears done, spawns,
33
+ * - __tc_worker_done(i) — poll predicate.
34
+ *
35
+ * AUTOSAR C++14: fixed-width types, static_cast, final structs, no STL, no heap.
36
+ */
37
+ export function generateWorkerRuntime(backing, options = {}) {
38
+ if (!backing)
39
+ return null;
40
+ const poolSize = options.poolSize ?? 4;
41
+ const stackSize = options.stackSize ?? 8192;
42
+ const lines = [];
43
+ lines.push(`// ── Worker offload runtime (generalized request-in / poll-out) ──────────────`);
44
+ lines.push(`// Slot table + __tc_worker_submit/done contract. Per-framework primitives`);
45
+ lines.push(`// (spawn/signal/poll) are injected by the framework strategy. STL-free so`);
46
+ lines.push(`// this links on minimal-libc targets. See worker-runtime.ts.`);
47
+ lines.push(`namespace typecad_worker {`);
48
+ lines.push(` using WorkerFn = void (*)(void*);`);
49
+ lines.push(``);
50
+ lines.push(` struct WorkerSlot final {`);
51
+ lines.push(` volatile bool done;`);
52
+ lines.push(` void* waiter; // framework-specific waiter handle (opaque)`);
53
+ lines.push(` WorkerFn fn;`);
54
+ lines.push(` void* arg;`);
55
+ lines.push(` };`);
56
+ lines.push(``);
57
+ // Per-slot static instances.
58
+ for (let i = 0; i < poolSize; i++) {
59
+ lines.push(` static WorkerSlot __slot_${i};`);
60
+ }
61
+ lines.push(``);
62
+ // Forward-declare the per-slot trampolines so backings that need to bind a
63
+ // named handler to one (e.g. Zephyr K_WORK_DEFINE(_work_i, handler_i)) can
64
+ // reference it before the trampoline definition appears below.
65
+ for (let i = 0; i < poolSize; i++) {
66
+ lines.push(` static void __tc_worker_fn_${i}(void* arg);`);
67
+ }
68
+ lines.push(``);
69
+ // Per-slot backing declarations (e.g. Zephyr K_SEM_DEFINE + K_WORK_DEFINE +
70
+ // a work handler that forwards to the trampoline). Emitted BEFORE the
71
+ // trampoline definitions so the trampoline bodies can reference per-slot
72
+ // objects declared here (e.g. the completion semaphore used by the signal
73
+ // expression). Backings must only forward-declare, not define, the
74
+ // trampoline itself.
75
+ if (backing.slotDeclarations) {
76
+ for (let i = 0; i < poolSize; i++) {
77
+ const trampolineName = `__tc_worker_fn_${i}`;
78
+ for (const d of backing.slotDeclarations(i, trampolineName)) {
79
+ lines.push(` ${d}`);
80
+ }
81
+ }
82
+ lines.push(``);
83
+ }
84
+ // Per-slot worker trampolines: run fn(arg), then signal completion.
85
+ for (let i = 0; i < poolSize; i++) {
86
+ const signal = backing.signalDoneExpr(i);
87
+ lines.push(` // Worker trampoline for slot ${i}.`);
88
+ lines.push(` static void __tc_worker_fn_${i}(void* arg) {`);
89
+ lines.push(` // The shared runtime recorded fn/waiter before spawn; run it.`);
90
+ lines.push(` if (__slot_${i}.fn != nullptr) { __slot_${i}.fn(arg); }`);
91
+ lines.push(` __slot_${i}.done = true;`);
92
+ lines.push(` ${signal}; // barrier: makes the fn's output writes visible`);
93
+ lines.push(` }`);
94
+ lines.push(``);
95
+ }
96
+ // submit / done per slot.
97
+ for (let i = 0; i < poolSize; i++) {
98
+ const trampolineName = `__tc_worker_fn_${i}`;
99
+ const spawn = backing.spawnLines(i, trampolineName, `__slot_${i}.waiter`);
100
+ lines.push(` static inline void __tc_worker_submit_${i}(WorkerFn fn, void* arg) {`);
101
+ lines.push(` __slot_${i}.done = false;`);
102
+ lines.push(` __slot_${i}.fn = fn;`);
103
+ lines.push(` __slot_${i}.arg = arg;`);
104
+ lines.push(` // waiter is set by the framework spawn primitive below.`);
105
+ for (const s of spawn)
106
+ lines.push(` ${s}`);
107
+ lines.push(` }`);
108
+ lines.push(``);
109
+ const poll = backing.isDoneExpr(i);
110
+ lines.push(` static inline bool __tc_worker_done_${i}(void) {`);
111
+ lines.push(` if (__slot_${i}.done) { return true; } // fast path`);
112
+ lines.push(` if (${poll}) { __slot_${i}.done = true; return true; }`);
113
+ lines.push(` return false;`);
114
+ lines.push(` }`);
115
+ lines.push(``);
116
+ }
117
+ lines.push(`} // namespace typecad_worker`);
118
+ lines.push(``);
119
+ // Global dispatch aliases so HAL emit (worker.submit / worker.done) resolves.
120
+ lines.push(`// Global dispatch: select the per-slot submit/done by handle id.`);
121
+ lines.push(`inline void __tc_worker_submit(int32_t handle, typecad_worker::WorkerFn fn, void* arg) {`);
122
+ lines.push(` switch (handle) {`);
123
+ for (let i = 0; i < poolSize; i++) {
124
+ lines.push(` case ${i}: typecad_worker::__tc_worker_submit_${i}(fn, arg); return;`);
125
+ }
126
+ lines.push(` default: break; // out-of-range handle: no-op`);
127
+ lines.push(` }`);
128
+ lines.push(`}`);
129
+ lines.push(`inline bool __tc_worker_done(int32_t handle) {`);
130
+ lines.push(` switch (handle) {`);
131
+ for (let i = 0; i < poolSize; i++) {
132
+ lines.push(` case ${i}: return typecad_worker::__tc_worker_done_${i}();`);
133
+ }
134
+ lines.push(` default: return true; // out-of-range handle: treat as already done`);
135
+ lines.push(` }`);
136
+ lines.push(`}`);
137
+ return lines.join("\n");
138
+ }
139
+ /**
140
+ * Resolve a HAL worker.* op to C++ against the shared contract.
141
+ * Returns undefined for ops the caller did not lower (unsupported).
142
+ *
143
+ * Note: the worker function itself (fnRef) and its argument (argRef) come from
144
+ * the IR as flat-scalar strings (per the hal-op-ir flat-fields contract). The
145
+ * lowering emits a straight call into __tc_worker_submit / __tc_worker_done.
146
+ */
147
+ export function lowerWorkerOp(op) {
148
+ const o = op;
149
+ switch (op.operation) {
150
+ case 'worker.submit': {
151
+ const handle = Number(o.handleId);
152
+ const fn = String(o.fnRef);
153
+ const arg = o.argRef != null ? String(o.argRef) : 'nullptr';
154
+ return { code: `__tc_worker_submit(static_cast<int32_t>(${handle}), ${fn}, ${arg});` };
155
+ }
156
+ case 'worker.done': {
157
+ const handle = Number(o.handleId);
158
+ return { expression: `__tc_worker_done(static_cast<int32_t>(${handle}))` };
159
+ }
160
+ default:
161
+ return undefined;
162
+ }
163
+ }
@@ -13,6 +13,7 @@ export declare function printDiagnostics(diagnostics: Array<Diagnostic | {
13
13
  line?: number;
14
14
  column?: number;
15
15
  code?: string;
16
+ filePath?: string;
16
17
  sourceLine?: string;
17
18
  }>): void;
18
19
  /**
package/dist/cli-utils.js CHANGED
@@ -50,6 +50,7 @@ export function printDiagnostics(diagnostics) {
50
50
  for (const diagnostic of diagnostics) {
51
51
  const position = diagnostic.line && diagnostic.column ? chalk.gray(`(${diagnostic.line},${diagnostic.column})`) : "";
52
52
  const code = diagnostic.code ? chalk.gray(` [${diagnostic.code}]`) : "";
53
+ const file = 'filePath' in diagnostic && diagnostic.filePath ? chalk.cyan(diagnostic.filePath) : "";
53
54
  let severityLabel;
54
55
  let hintColor;
55
56
  if (diagnostic.severity === "error") {
@@ -65,7 +66,8 @@ export function printDiagnostics(diagnostics) {
65
66
  hintColor = chalk.cyan;
66
67
  }
67
68
  const location = position ? ` ${position}` : "";
68
- const header = `${severityLabel}${code}${location}: ${chalk.white(diagnostic.message)}`;
69
+ const filePrefix = file ? `${file} ` : "";
70
+ const header = `${filePrefix}${severityLabel}${code}${location}: ${chalk.white(diagnostic.message)}`;
69
71
  if (diagnostic.severity === "error") {
70
72
  console.error(header);
71
73
  }
package/dist/cli.js CHANGED
@@ -2,18 +2,21 @@
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 } from "./create/index.js";
5
+ import { scaffoldProject, printInitNextSteps, KNOWN_TARGETS, frameworksForTarget, frameworkCatalogEntry, FRAMEWORK_CATALOG, frameworkTargetProfile } from "./create/index.js";
6
6
  import { runInitWizard } from "./create/index.js";
7
+ import { installProjectDependencies } from "./create/install-deps.js";
7
8
  import { generateLibraryDefinitions, transpileFile } from "./transpile.js";
8
- import { generateDecl, generateDeclsForDirectory } from "./libdef/cpp-to-decl.js";
9
+ import { generateDecl, generateDeclsForDirectory, generateComponentDeclsForProject } from "./libdef/cpp-to-decl.js";
9
10
  import { mapCppLocationToTs, readSourceMap, resolveMapPath } from "./mapping/source-map.js";
10
11
  import { compileSource, uploadFirmware, monitorDevice } from "./platform/toolchain.js";
11
12
  import { resolveStrategy } from "./platform/registry.js";
12
13
  import { loadFrameworkPackage } from "./framework-package.js";
13
14
  import { getLoadedFramework, hasLoadedFramework } from "./framework-registry.js";
14
15
  import { loadCuttlefishConfig, generateVirtualTypeDeclaration } from "./config-loader.js";
15
- import { requireUIHook } from "./ui-hook.js";
16
+ import { generateContractBoard } from "./contract/index.js";
17
+ import { requireUIHook, hasUIHook } from "./ui-hook.js";
16
18
  import { loadUIEngine } from "./ui/ui-bridge.js";
19
+ import { loadSafetyEngine } from "./safety/safety-bridge.js";
17
20
  import { runWatch, discoverWatchDirs } from "./watch.js";
18
21
  import { runExpectTests, assertTypeScriptInput, printDiagnostics, printMappedCompileErrors } from "./cli-utils.js";
19
22
  import { runPreviewServer } from "./preview/server.js";
@@ -31,6 +34,27 @@ function displayConfigForTranspile(config) {
31
34
  }
32
35
  return display;
33
36
  }
37
+ /**
38
+ * Apply the PSRAM config to the Arduino build: append the `PSRAM={opi|quad}`
39
+ * menu option to the FQBN (so the Arduino core's psramFound()/ps_malloc work
40
+ * at runtime — they're gated on the board menu option, not just the define)
41
+ * and add `-DBOARD_HAS_PSRAM` to the defines (so the framework's PSRAM canvas
42
+ * allocator is compiled in). No-op when psram is unset or the target isn't an
43
+ * Arduino FQBN (the PSRAM= option is Arduino-core-specific).
44
+ */
45
+ function applyPsramToArduinoBuild(buildTarget, defines, psram) {
46
+ if (!psram || !buildTarget || !buildTarget.includes(':')) {
47
+ return { buildTarget, defines: defines ?? {} };
48
+ }
49
+ // Arduino FQBN config options follow the board:option as `key=value` pairs.
50
+ // Append PSRAM= if not already present (don't clobber an explicit override).
51
+ const psramOpt = `PSRAM=${psram}`;
52
+ const adjustedTarget = buildTarget.includes('PSRAM=') ? buildTarget : `${buildTarget}:${psramOpt}`;
53
+ const adjustedDefines = { ...(defines ?? {}) };
54
+ if (!('BOARD_HAS_PSRAM' in adjustedDefines))
55
+ adjustedDefines.BOARD_HAS_PSRAM = '';
56
+ return { buildTarget: adjustedTarget, defines: adjustedDefines };
57
+ }
34
58
  async function handleCreate(options) {
35
59
  const targetId = options.target ?? options.board;
36
60
  const hasTarget = !!targetId;
@@ -40,15 +64,35 @@ async function handleCreate(options) {
40
64
  const available = KNOWN_TARGETS.map(t => ` - ${t.id} (${t.displayName})`).join("\n");
41
65
  throw new Error(`Unknown target '${targetId}'. Available targets:\n${available}`);
42
66
  }
43
- const framework = options.framework ?? target.framework;
44
- const frameworkPackage = target.isNative
45
- ? target.frameworkPackage
46
- : framework === 'avr'
47
- ? '@typecad/framework-avr'
48
- : framework === 'arduino'
49
- ? '@typecad/framework-arduino'
50
- : `@typecad/framework-${framework}`;
67
+ // Resolve the framework. --framework wins; otherwise narrow via the catalog
68
+ // for the chosen board and auto-pick when exactly one is compatible.
69
+ let frameworkId;
70
+ let frameworkPackage;
71
+ if (options.framework) {
72
+ const requested = frameworkCatalogEntry(options.framework);
73
+ if (!requested) {
74
+ const available = FRAMEWORK_CATALOG.filter(f => f.installable).map(f => f.id).join(", ");
75
+ throw new Error(`Unknown framework '${options.framework}'. Available: ${available}`);
76
+ }
77
+ frameworkId = requested.id;
78
+ frameworkPackage = requested.packageName;
79
+ }
80
+ else {
81
+ const compatible = frameworksForTarget(target).filter(f => f.installable);
82
+ if (compatible.length === 1) {
83
+ frameworkId = compatible[0].id;
84
+ frameworkPackage = compatible[0].packageName;
85
+ }
86
+ else {
87
+ const list = compatible.map(f => f.id).join(", ");
88
+ throw new Error(`Target '${target.id}' is compatible with multiple frameworks (${list}). ` +
89
+ `Pass --framework <id> to choose one.`);
90
+ }
91
+ }
51
92
  const projectName = options.projectName || 'my-project';
93
+ // Framework-specific build target + toolchain (Zephyr board id + 'west' vs
94
+ // the Arduino FQBN + 'arduino-cli').
95
+ const profile = frameworkTargetProfile(target, frameworkId);
52
96
  const result = scaffoldProject({
53
97
  projectName,
54
98
  targetId: target.id,
@@ -57,18 +101,17 @@ async function handleCreate(options) {
57
101
  architecture: target.architecture,
58
102
  boardPackage: target.boardPackage,
59
103
  frameworkPackage,
60
- framework,
61
- buildTarget: target.buildTarget,
104
+ framework: frameworkId,
105
+ buildTarget: profile.buildTarget ?? target.buildTarget,
106
+ ...(profile.toolchainType ? { toolchainType: profile.toolchainType } : {}),
62
107
  mcu: target.mcu,
63
108
  baudRate: target.isNative ? undefined : (options.baud ?? 9600),
64
109
  includeSketch: !options.noSketch,
110
+ ...(target.frameworkData
111
+ ? { frameworkData: target.frameworkData }
112
+ : {}),
65
113
  }, options.outDir);
66
- console.log(`\n${chalk.green("✓")} Created project files:`);
67
- for (const file of result.createdFiles) {
68
- const relative = path.relative(process.cwd(), file);
69
- console.log(` ${chalk.dim(relative || file)}`);
70
- }
71
- printInitNextSteps(result.options, result.outDir);
114
+ finalizeCreate(result, options);
72
115
  }
73
116
  else {
74
117
  const wizardResult = await runInitWizard({
@@ -83,13 +126,37 @@ async function handleCreate(options) {
83
126
  return;
84
127
  }
85
128
  const result = scaffoldProject(wizardResult, options.outDir);
86
- console.log(`\n${chalk.green("✓")} Created project files:`);
87
- for (const file of result.createdFiles) {
88
- const relative = path.relative(process.cwd(), file);
89
- console.log(` ${chalk.dim(relative || file)}`);
129
+ finalizeCreate(result, options);
130
+ }
131
+ }
132
+ /**
133
+ * Shared tail of `cuttlefish create`: list the created files, install the new
134
+ * project's dependencies (so it's ready to build with no extra step — skipped
135
+ * via --no-install), and print the next-steps. A failed install is non-fatal:
136
+ * the scaffold itself is valid, so we warn and point at the manual command
137
+ * rather than undoing anything.
138
+ */
139
+ function finalizeCreate(result, options) {
140
+ console.log(`\n${chalk.green("✓")} Created project files:`);
141
+ for (const file of result.createdFiles) {
142
+ const relative = path.relative(process.cwd(), file);
143
+ console.log(` ${chalk.dim(relative || file)}`);
144
+ }
145
+ let installed = false;
146
+ if (!options.noInstall) {
147
+ try {
148
+ const { pm } = installProjectDependencies({ projectDir: result.outDir });
149
+ installed = true;
150
+ console.log(`\n${chalk.green("✓")} Installed dependencies via ${pm}.`);
151
+ }
152
+ catch (err) {
153
+ const message = err instanceof Error ? err.message : String(err);
154
+ const relativeDir = path.relative(process.cwd(), result.outDir) || '.';
155
+ console.log(`\n${chalk.yellow("!")} Could not install dependencies automatically: ${message}`);
156
+ console.log(chalk.dim(` Run '${chalk.white("npm install")}' manually in ${relativeDir} when ready.`));
90
157
  }
91
- printInitNextSteps(result.options, result.outDir);
92
158
  }
159
+ printInitNextSteps(result.options, result.outDir, { installed });
93
160
  }
94
161
  async function handleBoardAdd(options) {
95
162
  const { scaffoldBoardPackages, parseBoardSpec, generateFrameworkChecklist } = await import("./create/index.js");
@@ -154,6 +221,38 @@ async function main() {
154
221
  }
155
222
  return;
156
223
  }
224
+ if (options.command === "doctor" || options.command === "licenses") {
225
+ // These commands run standalone (often before a build), so the framework
226
+ // isn't loaded yet. Load it from the config's framework field so the
227
+ // framework-supplied doctor/licenses handlers are available.
228
+ if (!hasLoadedFramework()) {
229
+ const config = loadCuttlefishConfig(process.cwd());
230
+ if (config?.framework) {
231
+ try {
232
+ loadFrameworkPackage(config.framework, process.cwd());
233
+ }
234
+ catch {
235
+ // Framework package not resolvable — fall through to the
236
+ // no-support message below rather than crashing the command.
237
+ }
238
+ }
239
+ }
240
+ const fw = hasLoadedFramework() ? getLoadedFramework() : undefined;
241
+ if (options.command === "doctor") {
242
+ if (!fw?.doctor) {
243
+ ui.printInfo("This framework provides no doctor support.");
244
+ return;
245
+ }
246
+ fw.doctor();
247
+ return;
248
+ }
249
+ if (!fw?.licenses) {
250
+ ui.printInfo("This framework provides no licenses support.");
251
+ return;
252
+ }
253
+ fw.licenses(options.strict ?? false, options.all ?? false);
254
+ return;
255
+ }
157
256
  // ── Handle build command — entry point comes from config ──────────
158
257
  if (options.command === "build") {
159
258
  const buildConfig = loadCuttlefishConfig(process.cwd());
@@ -184,6 +283,37 @@ async function main() {
184
283
  // it intentionally has no inputFile (it scans scanDir instead), so the
185
284
  // generic "Missing input file path" check would otherwise block it.
186
285
  if (options.command === "gen-decls") {
286
+ // Check for --components flag (ESP-IDF components: managed + local).
287
+ // Runs before scanDir/single-file branches; components mode has no
288
+ // inputFile by design (it scans managed_components/ + components/).
289
+ const componentsDir = options.componentsDir;
290
+ if (componentsDir) {
291
+ ui.printHeader();
292
+ ui.printStep(`Generating component declarations for ${componentsDir}...`);
293
+ const config = loadCuttlefishConfig(componentsDir);
294
+ const frameworkConfig = (config?.frameworkConfig ?? {});
295
+ const componentsNode = frameworkConfig?.components ?? {};
296
+ const managedSpecs = Object.keys(componentsNode.managed ?? {});
297
+ // idf.py stores managed deps as <namespace>__<name> (slashes → __).
298
+ const managedNames = managedSpecs.map((spec) => spec.replace("/", "__"));
299
+ const localPaths = (componentsNode.local ?? []).map((p) => path.isAbsolute(p) ? p : path.resolve(componentsDir, p));
300
+ const builtinNames = componentsNode.builtin ?? [];
301
+ const created = generateComponentDeclsForProject(componentsDir, {
302
+ managed: managedNames,
303
+ local: localPaths,
304
+ builtin: [],
305
+ idfRoot: undefined,
306
+ });
307
+ if (created.length === 0) {
308
+ ui.printInfo("No component declaration files created.");
309
+ }
310
+ else {
311
+ ui.printSuccess(`Created ${created.length} declaration file(s):`);
312
+ for (const f of created)
313
+ ui.printFileCreated(f);
314
+ }
315
+ return;
316
+ }
187
317
  // Check for --all flag (scan directory)
188
318
  const scanDir = options.scanDir;
189
319
  if (scanDir) {
@@ -255,8 +385,56 @@ async function main() {
255
385
  // ── Load cuttlefish.config.ts (config wins over CLI flags) ──────────
256
386
  const inputDir = path.dirname(path.resolve(options.inputFile));
257
387
  const config = loadCuttlefishConfig(inputDir);
388
+ // ── Contract-based board narrowing ──────────────────────────────────
389
+ // If the config names a TypeCAD contract (*.contract.json from typecad.net),
390
+ // generate the narrowed `.cuttlefish/board.ts` BEFORE env.d.ts is written
391
+ // (it emits `export * from './board.js'` for this case) and before the
392
+ // board package is resolved downstream. The generated board re-exports
393
+ // only the pins/peripherals the PCB actually wires.
394
+ if (config?.contract) {
395
+ await generateContractBoard(config);
396
+ }
397
+ // ── Pre-transpile: generate component .d.ts stubs ───────────────────
398
+ // The transpile type-check needs the .d.ts imports in main.ts to resolve.
399
+ // For ESP-IDF builtin/managed/local components, those .d.ts files are
400
+ // generated by gen-decls — so we must run it BEFORE type-checking, not
401
+ // only as part of --compile. The cache is written under the transpile
402
+ // output dir (the same path --compile uses), so user imports resolve
403
+ // identically in both flows.
404
+ if (config?.frameworkConfig) {
405
+ const fc = config.frameworkConfig;
406
+ const componentsNode = fc?.components ?? {};
407
+ const managedSpecs = Object.keys(componentsNode.managed ?? {});
408
+ const localPaths = (componentsNode.local ?? []).map((p) => path.isAbsolute(p) ? p : path.resolve(inputDir, p));
409
+ const builtinNames = componentsNode.builtin ?? [];
410
+ if (managedSpecs.length > 0 || localPaths.length > 0 || builtinNames.length > 0) {
411
+ // Resolve the transpile output dir — the cache lives under it so the
412
+ // framework's compile path and this pre-transpile pass agree on location.
413
+ // output.outDir in config is relative to the project root (inputDir);
414
+ // if absent, the transpile output goes alongside the entry file.
415
+ const outBase = config.outputOutDir
416
+ ? path.resolve(inputDir, config.outputOutDir)
417
+ : inputDir;
418
+ try {
419
+ generateComponentDeclsForProject(outBase, {
420
+ managed: managedSpecs.map((s) => s.replace('/', '__')),
421
+ local: localPaths,
422
+ builtin: [],
423
+ idfRoot: undefined,
424
+ });
425
+ }
426
+ catch {
427
+ // Non-fatal: if gen-decls fails (e.g. component not yet fetched),
428
+ // the type-checker will surface the missing-import errors with
429
+ // clearer context than crashing here.
430
+ }
431
+ }
432
+ }
258
433
  // Load the UI engine (if @typecad/ui is installed) before any UI work.
259
434
  await loadUIEngine();
435
+ // Load the safety engine (if @typecad/safety is installed) before any
436
+ // safety work.
437
+ await loadSafetyEngine();
260
438
  let effectivePlatformContext = options.platformContext;
261
439
  let effectiveTarget = options.target;
262
440
  let effectiveOutDir = options.outDir;
@@ -277,9 +455,16 @@ async function main() {
277
455
  }
278
456
  let configBuildTarget = config.buildTarget;
279
457
  if (configBuildTarget) {
458
+ // Reconstruct frameworkData from buildTarget so the toolchain sees the
459
+ // full frameworkData, not just buildTarget.
280
460
  effectivePlatformContext = {
281
461
  architecture: configBuildTarget.split(":")?.[1]?.toLowerCase(),
282
- frameworkData: { buildTarget: configBuildTarget },
462
+ frameworkData: {
463
+ buildTarget: configBuildTarget,
464
+ // Thread the PSRAM type through to the framework so it can emit the
465
+ // PSRAM-enabling Kconfig (Zephyr) / define + FQBN option (Arduino).
466
+ ...(config.psram ? { psram: config.psram } : {}),
467
+ },
283
468
  };
284
469
  }
285
470
  if (config.outputOutDir && !options.outDir) {
@@ -321,7 +506,11 @@ async function main() {
321
506
  // Strategy not yet loaded — env.d.ts will have generic declarations only
322
507
  }
323
508
  generateVirtualTypeDeclaration(config, platformDeclarations);
324
- requireUIHook().generateProjectUITypeDeclarations(path.dirname(config.configPath));
509
+ // @typecad/ui is optional — skip UI type-decl generation when the engine
510
+ // is not loaded (no UI modules exist to declare).
511
+ if (hasUIHook()) {
512
+ requireUIHook().generateProjectUITypeDeclarations(path.dirname(config.configPath));
513
+ }
325
514
  }
326
515
  // Print branded header and build info
327
516
  ui.printHeader();
@@ -354,15 +543,21 @@ async function main() {
354
543
  diagnostics: options.diagnostics,
355
544
  display: displayConfigForTranspile(config),
356
545
  projectRoot: options.projectRoot,
546
+ autosar: options.autosar,
547
+ autosarArxml: options.autosarArxml,
357
548
  });
358
549
  printDiagnostics(result.diagnostics);
359
550
  ui.printTasks(result.asyncTaskNames ?? [], result.usesTimers ?? false);
551
+ if (result.diagnosticsReportPath) {
552
+ ui.printInfo(`Diagnostics report: ${result.diagnosticsReportPath}`);
553
+ }
360
554
  if (result.diagnostics.length === 0) {
361
555
  ui.printSuccess();
362
556
  initialBuildOk = true;
363
557
  // Initial compile + upload if flags are set
364
558
  if (options.compile) {
365
- const buildTarget = effectivePlatformContext?.frameworkData?.buildTarget ?? options.platformContext?.frameworkData?.buildTarget;
559
+ const baseBuildTarget = effectivePlatformContext?.frameworkData?.buildTarget ?? options.platformContext?.frameworkData?.buildTarget;
560
+ const { buildTarget, defines: psramDefines } = applyPsramToArduinoBuild(baseBuildTarget, config?.outputDefines, config?.psram);
366
561
  const watchOpts = {
367
562
  outputDir: path.dirname(result.sourcePath),
368
563
  sourcePath: result.sourcePath,
@@ -371,8 +566,12 @@ async function main() {
371
566
  baud: options.baud ?? config?.console?.baudRate,
372
567
  optimize: config?.outputOptimize,
373
568
  extraFlags: config?.outputExtraFlags,
374
- defines: config?.outputDefines,
569
+ defines: psramDefines,
570
+ psram: config?.psram,
375
571
  frameworkConfig: config?.frameworkConfig,
572
+ zephyrConfig: config?.zephyrConfig,
573
+ display: displayConfigForTranspile(config),
574
+ debug: options.debug,
376
575
  };
377
576
  ui.printCompiling(buildTarget ?? "native");
378
577
  const compileResult = compileSource(watchOpts);
@@ -421,7 +620,11 @@ async function main() {
421
620
  console.log();
422
621
  try {
423
622
  if (config) {
424
- requireUIHook().generateProjectUITypeDeclarations(path.dirname(config.configPath));
623
+ // @typecad/ui is optional — skip UI type-decl generation when the
624
+ // engine is not loaded (no UI modules exist to declare).
625
+ if (hasUIHook()) {
626
+ requireUIHook().generateProjectUITypeDeclarations(path.dirname(config.configPath));
627
+ }
425
628
  }
426
629
  ui.printTranspiling();
427
630
  const rebuildResult = await transpileFile({
@@ -440,16 +643,22 @@ async function main() {
440
643
  diagnostics: options.diagnostics,
441
644
  display: displayConfigForTranspile(config),
442
645
  projectRoot: options.projectRoot,
646
+ autosar: options.autosar,
647
+ autosarArxml: options.autosarArxml,
443
648
  });
444
649
  printDiagnostics(rebuildResult.diagnostics);
445
650
  ui.printTasks(rebuildResult.asyncTaskNames ?? [], rebuildResult.usesTimers ?? false);
651
+ if (rebuildResult.diagnosticsReportPath) {
652
+ ui.printInfo(`Diagnostics report: ${rebuildResult.diagnosticsReportPath}`);
653
+ }
446
654
  if (rebuildResult.diagnostics.length > 0) {
447
655
  // Errors shown via printDiagnostics — skip compile/upload
448
656
  }
449
657
  else {
450
658
  ui.printSuccess();
451
659
  if (options.compile) {
452
- const buildTarget = effectivePlatformContext?.frameworkData?.buildTarget ?? options.platformContext?.frameworkData?.buildTarget;
660
+ const baseBuildTarget = effectivePlatformContext?.frameworkData?.buildTarget ?? options.platformContext?.frameworkData?.buildTarget;
661
+ const { buildTarget, defines: psramDefines } = applyPsramToArduinoBuild(baseBuildTarget, config?.outputDefines, config?.psram);
453
662
  const rebuildOpts = {
454
663
  outputDir: path.dirname(rebuildResult.sourcePath),
455
664
  sourcePath: rebuildResult.sourcePath,
@@ -458,8 +667,12 @@ async function main() {
458
667
  baud: options.baud ?? config?.console?.baudRate,
459
668
  optimize: config?.outputOptimize,
460
669
  extraFlags: config?.outputExtraFlags,
461
- defines: config?.outputDefines,
670
+ defines: psramDefines,
671
+ psram: config?.psram,
462
672
  frameworkConfig: config?.frameworkConfig,
673
+ zephyrConfig: config?.zephyrConfig,
674
+ display: displayConfigForTranspile(config),
675
+ debug: options.debug,
463
676
  };
464
677
  ui.printCompiling(buildTarget ?? "native");
465
678
  const compileResult = compileSource(rebuildOpts);
@@ -524,9 +737,14 @@ async function main() {
524
737
  diagnostics: options.diagnostics,
525
738
  display: displayConfigForTranspile(config) ?? options.display,
526
739
  projectRoot: options.projectRoot,
740
+ autosar: options.autosar,
741
+ autosarArxml: options.autosarArxml,
527
742
  });
528
743
  printDiagnostics(result.diagnostics);
529
744
  ui.printTasks(result.asyncTaskNames ?? [], result.usesTimers ?? false);
745
+ if (result.diagnosticsReportPath) {
746
+ ui.printInfo(`Diagnostics report: ${result.diagnosticsReportPath}`);
747
+ }
530
748
  }
531
749
  if (hasFatalDiagnostics(result)) {
532
750
  ui.printError("Transpilation failed. Fix the transpiler diagnostics above before compiling.");
@@ -547,7 +765,8 @@ async function main() {
547
765
  return;
548
766
  }
549
767
  // --compile (delegates to the active framework's toolchain)
550
- const buildTarget = effectivePlatformContext?.frameworkData?.buildTarget ?? options.platformContext?.frameworkData?.buildTarget;
768
+ const baseBuildTarget = effectivePlatformContext?.frameworkData?.buildTarget ?? options.platformContext?.frameworkData?.buildTarget;
769
+ const { buildTarget, defines: psramDefines } = applyPsramToArduinoBuild(baseBuildTarget, config?.outputDefines, config?.psram);
551
770
  const toolchainOpts = {
552
771
  outputDir: path.dirname(result.sourcePath),
553
772
  sourcePath: result.sourcePath,
@@ -556,8 +775,12 @@ async function main() {
556
775
  baud: options.baud ?? config?.console?.baudRate,
557
776
  optimize: config?.outputOptimize,
558
777
  extraFlags: config?.outputExtraFlags,
559
- defines: config?.outputDefines,
778
+ defines: psramDefines,
779
+ psram: config?.psram,
560
780
  frameworkConfig: config?.frameworkConfig,
781
+ zephyrConfig: config?.zephyrConfig,
782
+ display: displayConfigForTranspile(config),
783
+ debug: options.debug,
561
784
  };
562
785
  ui.printCompiling(buildTarget ?? "native");
563
786
  let compileResult;
@@ -608,8 +831,10 @@ async function main() {
608
831
  return;
609
832
  }
610
833
  if (options.monitor) {
611
- // --monitor (blocks until Ctrl+C)
612
- ui.printMonitoring(port, options.baud);
834
+ // --monitor (blocks until Ctrl+C). Show the resolved baud (config ?? --baud)
835
+ // in the banner — toolchainOpts.baud is built the same way, so what the
836
+ // user sees is what the framework monitor opens the port at.
837
+ ui.printMonitoring(port, toolchainOpts.baud ?? 115200);
613
838
  monitorDevice(toolchainOpts);
614
839
  return;
615
840
  }