@maincode-ai/matilda-code 0.21.12 → 0.21.13-nightly.20261027.e00306cd3

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 (269) hide show
  1. package/chunks/agent-H5JE3RJQ.js +3 -0
  2. package/chunks/agent-headless-H2JZZ7UV.js +3 -0
  3. package/chunks/{askUserQuestion-2QI4NSFJ.js → askUserQuestion-L3WDL2T4.js} +3 -3
  4. package/chunks/ca-UBWUTZVZ.js +25 -0
  5. package/chunks/chunk-4CVGYUEL.js +3 -0
  6. package/chunks/{chunk-RNKDFE2S.js → chunk-5B2QERWR.js} +1 -1
  7. package/chunks/chunk-62GTGPTP.js +8 -0
  8. package/chunks/chunk-6365AZBW.js +56 -0
  9. package/chunks/chunk-6PFK4XTH.js +207 -0
  10. package/chunks/chunk-7ZODGQO6.js +4 -0
  11. package/chunks/chunk-DB5LMKXU.js +31 -0
  12. package/chunks/chunk-EYZPVR7N.js +13 -0
  13. package/chunks/chunk-FKHUEFMK.js +8 -0
  14. package/chunks/{chunk-6T2GIM76.js → chunk-GILXFDEG.js} +1 -1
  15. package/chunks/{chunk-VTGICC67.js → chunk-GKEUZTOB.js} +1 -1
  16. package/chunks/chunk-GSRXW64P.js +120 -0
  17. package/chunks/chunk-GT6MSEK4.js +3 -0
  18. package/chunks/chunk-HEJO7OBH.js +3 -0
  19. package/chunks/chunk-HPO2YWG4.js +78 -0
  20. package/chunks/chunk-I3CV4LOD.js +8 -0
  21. package/chunks/{chunk-DOOFGSRV.js → chunk-J3AMKDX6.js} +1 -1
  22. package/chunks/chunk-JU7QTSKS.js +14 -0
  23. package/chunks/chunk-LBQNFSZ2.js +9 -0
  24. package/chunks/chunk-LOB42ODE.js +9 -0
  25. package/chunks/chunk-LVDDFAK4.js +89 -0
  26. package/chunks/{chunk-27Q5KW3A.js → chunk-NK2QMGCC.js} +13 -13
  27. package/chunks/{chunk-YRQP3SLC.js → chunk-O7VPFJ3R.js} +1 -1
  28. package/chunks/chunk-OBE5S4GG.js +9 -0
  29. package/chunks/{chunk-CZGZ3KK7.js → chunk-ONANBLIE.js} +1 -1
  30. package/chunks/chunk-QXSBPCBA.js +8 -0
  31. package/chunks/chunk-TUKNB5CQ.js +33 -0
  32. package/chunks/chunk-UM6CLO7D.js +7 -0
  33. package/chunks/chunk-V4D3LER3.js +2615 -0
  34. package/chunks/{chunk-HYVP4472.js → chunk-V6UPUMP3.js} +1 -1
  35. package/chunks/chunk-VUJRLLGL.js +8 -0
  36. package/chunks/{chunk-MCQUPLRE.js → chunk-WTU6GSZH.js} +1 -1
  37. package/chunks/chunk-XBTBZHRC.js +8 -0
  38. package/chunks/{chunk-WV3IUPZS.js → chunk-YTB3OFTT.js} +1 -1
  39. package/chunks/chunk-ZQKABJBB.js +8 -0
  40. package/chunks/chunk-ZSGBDQXU.js +11 -0
  41. package/chunks/{computer-use-WKDNUTLD.js → computer-use-N6KGNST6.js} +1 -1
  42. package/chunks/{cron-create-F2LXM2OA.js → cron-create-4XBY5RVI.js} +1 -1
  43. package/chunks/cron-delete-BH4VP3LX.js +3 -0
  44. package/chunks/cron-list-XXRFKNGM.js +5 -0
  45. package/chunks/de-LEEHWWJ2.js +25 -0
  46. package/chunks/{devtools-5JRKK3E7.js → devtools-7YNCRYZV.js} +1 -1
  47. package/chunks/{chunk-OOEH6ZSV.js → dist-UWUWLWTO.js} +2 -2
  48. package/chunks/{edit-QQNJZ55P.js → edit-6ULTLTJQ.js} +1 -1
  49. package/chunks/{en-EJLDYIBM.js → en-LNOIHFSQ.js} +4 -4
  50. package/chunks/{enter-worktree-5X5SAMBJ.js → enter-worktree-V2VANZYJ.js} +1 -1
  51. package/chunks/enterPlanMode-75H54S3Z.js +18 -0
  52. package/chunks/{exit-worktree-SRKZBEYF.js → exit-worktree-KPB32K6P.js} +1 -1
  53. package/chunks/{exitPlanMode-WSPTGGVN.js → exitPlanMode-K7JRP22Y.js} +2 -2
  54. package/chunks/fr-4TA2AIU6.js +25 -0
  55. package/chunks/{getMachineId-bsd-ZYBNK2JG.js → getMachineId-bsd-ETKQTB6C.js} +1 -1
  56. package/chunks/{getMachineId-darwin-CM2MQGUI.js → getMachineId-darwin-AKNRWOWB.js} +1 -1
  57. package/chunks/{getMachineId-linux-SYE2KMCW.js → getMachineId-linux-2QSD7FTP.js} +1 -1
  58. package/chunks/{getMachineId-unsupported-6IHVG2XO.js → getMachineId-unsupported-UXSV7I24.js} +1 -1
  59. package/chunks/{getMachineId-win-S3J3JSUM.js → getMachineId-win-4MTFXGP3.js} +1 -1
  60. package/chunks/glob-ACAIHWST.js +22 -0
  61. package/chunks/{goal-tools-C6BSBHCF.js → goal-tools-VPFXBI6M.js} +3 -3
  62. package/chunks/{grep-MVCOBUYW.js → grep-R4K75RXS.js} +1 -1
  63. package/chunks/{ja-GGNJR7AR.js → ja-VXDMQKRW.js} +5 -5
  64. package/chunks/keychain-token-storage-7VYNARRB.js +3 -0
  65. package/chunks/ls-4ZEICYPK.js +15 -0
  66. package/chunks/{lsp-DYDK6XCS.js → lsp-4US2RIIV.js} +1 -1
  67. package/chunks/mcp-client-ZKQFYCFN.js +3 -0
  68. package/chunks/monitor-KSNFIY54.js +32 -0
  69. package/chunks/{multipart-parser-JI75T2O5.js → multipart-parser-FKHYAVGR.js} +1 -1
  70. package/chunks/notebook-edit-655ZNV6Q.js +17 -0
  71. package/chunks/openaiContentGenerator-BG3OM453.js +52 -0
  72. package/chunks/pt-WEIMCOKI.js +25 -0
  73. package/chunks/read-file-4VSUKSRB.js +3 -0
  74. package/chunks/ripGrep-PNALN2GG.js +3 -0
  75. package/chunks/ru-WFLK34ZL.js +25 -0
  76. package/chunks/scheduler-AVYCIBEK.js +8 -0
  77. package/chunks/send-message-ND2SRKX5.js +8 -0
  78. package/chunks/shell-GUHQNOMZ.js +3 -0
  79. package/chunks/{skill-HZPFEAPF.js → skill-AB2X2DTC.js} +1 -1
  80. package/chunks/src-LZEU4AYF.js +5 -0
  81. package/chunks/symbol-search-VICSUEZH.js +19 -0
  82. package/chunks/{syntheticOutput-432LXITX.js → syntheticOutput-Z7DQKMRI.js} +1 -1
  83. package/chunks/task-create-BQ73FRTH.js +3 -0
  84. package/chunks/{task-detach-XMSJ3HBM.js → task-detach-JCHSLUOR.js} +1 -1
  85. package/chunks/task-list-6ESRFO5F.js +9 -0
  86. package/chunks/{task-stop-NTS62FLG.js → task-stop-74H2CPAJ.js} +1 -1
  87. package/chunks/{task-update-AR26UFYZ.js → task-update-R3BN6KPQ.js} +1 -1
  88. package/chunks/{team-create-PYXFVIR5.js → team-create-UTKRPUPZ.js} +3 -3
  89. package/chunks/team-delete-PCCGTLGL.js +8 -0
  90. package/chunks/todoWrite-BZI75DFV.js +3 -0
  91. package/chunks/{tool-search-RKZDSRYS.js → tool-search-FJ5MAPQ6.js} +1 -1
  92. package/chunks/{web-fetch-VW3SVDQ6.js → web-fetch-DEUGZQZS.js} +17 -17
  93. package/chunks/{web-search-XQUIZARL.js → web-search-PUUIEFJO.js} +3 -3
  94. package/chunks/{workflow-YXNXDRWZ.js → workflow-CBADL3IM.js} +2 -2
  95. package/chunks/write-file-QGCL2AJL.js +15 -0
  96. package/cli.js +644 -679
  97. package/examples/starter/README.md +0 -3
  98. package/locales/ca.js +1 -37
  99. package/locales/de.js +1 -42
  100. package/locales/en.js +1 -43
  101. package/locales/fr.js +1 -39
  102. package/locales/ja.js +1 -42
  103. package/locales/pt.js +1 -42
  104. package/locales/ru.js +1 -42
  105. package/package.json +2 -2
  106. package/sandbox-macos-permissive-closed.sb +0 -1
  107. package/sandbox-macos-permissive-open.sb +0 -1
  108. package/sandbox-macos-permissive-proxied.sb +1 -2
  109. package/sandbox-macos-restrictive-closed.sb +14 -2
  110. package/sandbox-macos-restrictive-open.sb +14 -2
  111. package/sandbox-macos-restrictive-proxied.sb +15 -3
  112. package/bundled/mainy/SKILL.md +0 -157
  113. package/bundled/mainy/docs/_meta.ts +0 -32
  114. package/bundled/mainy/docs/common-workflow.md +0 -571
  115. package/bundled/mainy/docs/configuration/_meta.ts +0 -7
  116. package/bundled/mainy/docs/configuration/auth.md +0 -82
  117. package/bundled/mainy/docs/configuration/matilda-ignore.md +0 -55
  118. package/bundled/mainy/docs/configuration/model-providers.md +0 -598
  119. package/bundled/mainy/docs/configuration/settings.md +0 -790
  120. package/bundled/mainy/docs/configuration/themes.md +0 -199
  121. package/bundled/mainy/docs/configuration/trusted-folders.md +0 -67
  122. package/bundled/mainy/docs/extension/_meta.ts +0 -9
  123. package/bundled/mainy/docs/extension/extension-releasing.md +0 -204
  124. package/bundled/mainy/docs/extension/getting-started-extensions.md +0 -299
  125. package/bundled/mainy/docs/extension/introduction.md +0 -338
  126. package/bundled/mainy/docs/features/_meta.ts +0 -27
  127. package/bundled/mainy/docs/features/approval-mode.md +0 -388
  128. package/bundled/mainy/docs/features/arena.md +0 -219
  129. package/bundled/mainy/docs/features/auto-mode.md +0 -308
  130. package/bundled/mainy/docs/features/channels/_meta.ts +0 -8
  131. package/bundled/mainy/docs/features/channels/dingtalk.md +0 -134
  132. package/bundled/mainy/docs/features/channels/feishu.md +0 -186
  133. package/bundled/mainy/docs/features/channels/overview.md +0 -336
  134. package/bundled/mainy/docs/features/channels/plugins.md +0 -87
  135. package/bundled/mainy/docs/features/channels/telegram.md +0 -120
  136. package/bundled/mainy/docs/features/channels/weixin.md +0 -106
  137. package/bundled/mainy/docs/features/code-review.md +0 -291
  138. package/bundled/mainy/docs/features/commands.md +0 -528
  139. package/bundled/mainy/docs/features/dual-output.md +0 -627
  140. package/bundled/mainy/docs/features/followup-suggestions.md +0 -109
  141. package/bundled/mainy/docs/features/headless.md +0 -448
  142. package/bundled/mainy/docs/features/hooks.md +0 -1306
  143. package/bundled/mainy/docs/features/language.md +0 -171
  144. package/bundled/mainy/docs/features/lsp.md +0 -496
  145. package/bundled/mainy/docs/features/markdown-rendering.md +0 -183
  146. package/bundled/mainy/docs/features/mcp.md +0 -423
  147. package/bundled/mainy/docs/features/memory.md +0 -185
  148. package/bundled/mainy/docs/features/sandbox.md +0 -249
  149. package/bundled/mainy/docs/features/scheduled-tasks.md +0 -139
  150. package/bundled/mainy/docs/features/skills.md +0 -347
  151. package/bundled/mainy/docs/features/status-line.md +0 -397
  152. package/bundled/mainy/docs/features/structured-output.md +0 -309
  153. package/bundled/mainy/docs/features/sub-agents.md +0 -737
  154. package/bundled/mainy/docs/features/tips.md +0 -54
  155. package/bundled/mainy/docs/features/token-caching.md +0 -29
  156. package/bundled/mainy/docs/features/tool-use-summaries.md +0 -178
  157. package/bundled/mainy/docs/features/worktree.md +0 -345
  158. package/bundled/mainy/docs/ide-integration/_meta.ts +0 -4
  159. package/bundled/mainy/docs/ide-integration/ide-companion-spec.md +0 -182
  160. package/bundled/mainy/docs/ide-integration/ide-integration.md +0 -144
  161. package/bundled/mainy/docs/integration-github-action.md +0 -241
  162. package/bundled/mainy/docs/integration-jetbrains.md +0 -81
  163. package/bundled/mainy/docs/integration-vscode.md +0 -39
  164. package/bundled/mainy/docs/integration-zed.md +0 -72
  165. package/bundled/mainy/docs/matilda-serve-deploy-local.md +0 -221
  166. package/bundled/mainy/docs/matilda-serve.md +0 -661
  167. package/bundled/mainy/docs/overview.md +0 -64
  168. package/bundled/mainy/docs/quickstart.md +0 -234
  169. package/bundled/mainy/docs/reference/_meta.ts +0 -3
  170. package/bundled/mainy/docs/reference/keyboard-shortcuts.md +0 -91
  171. package/bundled/mainy/docs/support/Uninstall.md +0 -65
  172. package/bundled/mainy/docs/support/_meta.ts +0 -6
  173. package/bundled/mainy/docs/support/tos-privacy.md +0 -94
  174. package/bundled/mainy/docs/support/troubleshooting.md +0 -127
  175. package/chunks/agent-WM63A6NK.js +0 -3
  176. package/chunks/agent-headless-QIUBDKPW.js +0 -3
  177. package/chunks/anthropicContentGenerator-VKFTAIU3.js +0 -39
  178. package/chunks/ca-I2HEEIL7.js +0 -25
  179. package/chunks/chunk-2HTPVSGT.js +0 -3
  180. package/chunks/chunk-2MOGYHWM.js +0 -8
  181. package/chunks/chunk-3IRGBRSP.js +0 -113
  182. package/chunks/chunk-3YLXGE6T.js +0 -8
  183. package/chunks/chunk-47XR7IMP.js +0 -8
  184. package/chunks/chunk-4F4DA5UI.js +0 -8
  185. package/chunks/chunk-4J6DC24J.js +0 -59
  186. package/chunks/chunk-4Q2SNBUH.js +0 -18
  187. package/chunks/chunk-5C2RJ5RR.js +0 -37
  188. package/chunks/chunk-6E766NPX.js +0 -31
  189. package/chunks/chunk-6EQMZNIY.js +0 -8
  190. package/chunks/chunk-6GBNB5Z2.js +0 -3
  191. package/chunks/chunk-6Z2MV7ZY.js +0 -186
  192. package/chunks/chunk-BVOCFLC4.js +0 -56
  193. package/chunks/chunk-CSJTA7NE.js +0 -101
  194. package/chunks/chunk-DC7LAFZZ.js +0 -8
  195. package/chunks/chunk-DE3HKC2W.js +0 -9
  196. package/chunks/chunk-EQGA7LH7.js +0 -30
  197. package/chunks/chunk-FPO57O66.js +0 -39
  198. package/chunks/chunk-FWDMPRKU.js +0 -3
  199. package/chunks/chunk-G7R2XEEF.js +0 -7
  200. package/chunks/chunk-GOJCMVJJ.js +0 -8
  201. package/chunks/chunk-GTVCJCHR.js +0 -8
  202. package/chunks/chunk-JCE3JAVC.js +0 -25
  203. package/chunks/chunk-JRECUFB3.js +0 -2630
  204. package/chunks/chunk-KHHJYBR5.js +0 -9
  205. package/chunks/chunk-LJR4RZZ2.js +0 -3
  206. package/chunks/chunk-LUNDTQ3G.js +0 -8
  207. package/chunks/chunk-M53RP3YP.js +0 -31
  208. package/chunks/chunk-MO2HXJG5.js +0 -9
  209. package/chunks/chunk-MTMU7JYZ.js +0 -11
  210. package/chunks/chunk-O44XK65C.js +0 -8
  211. package/chunks/chunk-OA56BXPD.js +0 -3
  212. package/chunks/chunk-PA6532AY.js +0 -6
  213. package/chunks/chunk-PGXQZVLX.js +0 -8
  214. package/chunks/chunk-QKYGQLWC.js +0 -8
  215. package/chunks/chunk-QN6Y6NPA.js +0 -3
  216. package/chunks/chunk-QVCGLQCI.js +0 -32
  217. package/chunks/chunk-S3CWEVTS.js +0 -15
  218. package/chunks/chunk-SDTWBD62.js +0 -8
  219. package/chunks/chunk-T2T2UEPY.js +0 -4
  220. package/chunks/chunk-TISQNFB5.js +0 -3
  221. package/chunks/chunk-U347OO7S.js +0 -9
  222. package/chunks/chunk-UIRBHC5X.js +0 -3
  223. package/chunks/chunk-VI74YQQO.js +0 -8
  224. package/chunks/chunk-VYZOOSMP.js +0 -30
  225. package/chunks/chunk-WC2VTVCE.js +0 -27
  226. package/chunks/chunk-WVY2P6WH.js +0 -26
  227. package/chunks/chunk-WZ75OGYY.js +0 -14
  228. package/chunks/chunk-XMZIY7XX.js +0 -13
  229. package/chunks/chunk-YJAKOETP.js +0 -22
  230. package/chunks/chunk-YV3PUOBU.js +0 -65
  231. package/chunks/contextCommand-DAB3HOHY.js +0 -3
  232. package/chunks/cron-delete-DE6Q2S2L.js +0 -3
  233. package/chunks/cron-list-IAQEJVV7.js +0 -5
  234. package/chunks/de-AQXFJYZI.js +0 -25
  235. package/chunks/dist-7UYRLSLE.js +0 -3
  236. package/chunks/dist-ME6647DQ.js +0 -195
  237. package/chunks/dist-P6SLWL5V.js +0 -42
  238. package/chunks/dist-VG3ZDJ2A.js +0 -28
  239. package/chunks/dist-YTCHM57U.js +0 -23
  240. package/chunks/enterPlanMode-HP35SPLN.js +0 -18
  241. package/chunks/fileFromPath-7UCWPD4W.js +0 -3
  242. package/chunks/fr-6TDHK7F2.js +0 -25
  243. package/chunks/geminiContentGenerator-RJ73DQAP.js +0 -8
  244. package/chunks/glob-QQ6LGKDX.js +0 -22
  245. package/chunks/keychain-token-storage-KQK6MNOJ.js +0 -3
  246. package/chunks/ls-JYJXFSXN.js +0 -15
  247. package/chunks/matildaAgentContentGenerator-YUYS4336.js +0 -8
  248. package/chunks/matildaContentGenerator-O4Z2QPCP.js +0 -8
  249. package/chunks/matildaOAuth2-7COTQQR2.js +0 -3
  250. package/chunks/mcp-client-RXVQU5VI.js +0 -3
  251. package/chunks/monitor-77KXDXPR.js +0 -32
  252. package/chunks/notebook-edit-TKJD2EQZ.js +0 -17
  253. package/chunks/openaiContentGenerator-2EALAM3U.js +0 -3
  254. package/chunks/pt-D6L2QTDM.js +0 -25
  255. package/chunks/read-file-3KUAH6ND.js +0 -3
  256. package/chunks/ripGrep-U2PLKXMY.js +0 -3
  257. package/chunks/ru-BNJCHDYO.js +0 -25
  258. package/chunks/scheduler-J7VU2V5Y.js +0 -8
  259. package/chunks/send-message-P3SMWNEE.js +0 -8
  260. package/chunks/serve-FXH6EBQ6.js +0 -1015
  261. package/chunks/shell-PXR2AXDD.js +0 -3
  262. package/chunks/src-RFAEPLY7.js +0 -3
  263. package/chunks/src-WZTRMETQ.js +0 -5
  264. package/chunks/symbol-search-EAIZPWN7.js +0 -19
  265. package/chunks/task-create-Z3QRCMX4.js +0 -3
  266. package/chunks/task-list-GXAJW3JP.js +0 -9
  267. package/chunks/team-delete-OMYVUAQU.js +0 -8
  268. package/chunks/todoWrite-5NZ7CHTC.js +0 -3
  269. package/chunks/write-file-XDDLTGSG.js +0 -15
@@ -1,627 +0,0 @@
1
- # Dual Output
2
-
3
- Dual Output is a sidecar mode for the interactive TUI: while Matilda Code keeps
4
- rendering normally on `stdout`, it concurrently emits a structured JSON event
5
- stream to a separate channel so an external program — an IDE extension, a web
6
- frontend, a CI pipeline, an automation script — can observe and steer the
7
- session.
8
-
9
- It also provides a reverse channel: an external program can write JSONL
10
- commands into a file that the TUI watches, allowing it to submit prompts and
11
- respond to tool-permission requests as if a human were at the keyboard.
12
-
13
- Dual Output is fully optional. When the flags below are absent the TUI behaves
14
- exactly as before with no extra I/O and no behavioral changes.
15
-
16
- ## Use cases
17
-
18
- Dual Output is a low-level plumbing primitive. These are concrete integrations
19
- it unlocks:
20
-
21
- ### Terminal + Chat dual-mode real-time sync
22
-
23
- The flagship use case. A web or desktop ChatUI hosts the TUI inside a PTY
24
- and renders a parallel conversation view driven by the structured event
25
- stream:
26
-
27
- - User can type in either surface — the TUI (for terminal-native power-users)
28
- or the web UI (for richer UX, shareable links, mobile). Both views stay
29
- in sync because every message flows through the same JSON events.
30
- - Tool-approval prompts appear in both places; whoever approves first wins.
31
- - Session history is captured verbatim from `--json-file`, so the server
32
- side has a canonical machine-readable transcript without parsing ANSI.
33
-
34
- ### IDE extensions (VS Code / JetBrains / Cursor / Neovim)
35
-
36
- Embed Matilda Code inside the IDE. The TUI runs in the editor's integrated
37
- terminal panel for users who want it, while the extension consumes
38
- `--json-fd` / `--json-file` events to drive:
39
-
40
- - Inline diff overlays when the agent touches files.
41
- - A webview side panel with formatted markdown, syntax-highlighted tool
42
- calls, and clickable citations.
43
- - Status bar indicators (thinking / responding / awaiting approval).
44
- - Programmatic `confirmation_response` writes when the user clicks a
45
- native IDE approval button.
46
-
47
- ### Browser-based Chat frontends
48
-
49
- A Node/Bun server spawns the TUI in a PTY for its rendering semantics but
50
- exposes a WebSocket channel to the browser. Events on `--json-file` are
51
- forwarded to the client; user messages typed in the browser are injected
52
- via `--input-file`. No ANSI parsing on either side.
53
-
54
- ### CI / automation observers
55
-
56
- A CI job runs Matilda Code with a task prompt. The human sees the TUI in the
57
- job log; the CI system tails `--json-file` to:
58
-
59
- - Fail the job if a `result` event reports an error.
60
- - Push `token usage` / `duration_ms` / `tool_use` counts to metrics.
61
- - Archive the full transcript as a build artifact.
62
-
63
- ### Multi-agent orchestration
64
-
65
- A supervisor agent spawns multiple TUI workers, each with its own pair of
66
- event/input files. It watches progress, injects follow-up prompts, and
67
- enforces global budget / safety policies by approving or denying tool
68
- calls across all workers.
69
-
70
- ### Session recording, audit, and replay
71
-
72
- Tee every TUI session to a regular file with `--json-file`. Later:
73
-
74
- - Compliance audits can reconstruct exactly what was executed.
75
- - Automated regression tests can compare runs across model versions.
76
- - A replay tool can re-emit events through the same protocol to feed
77
- visualization dashboards.
78
-
79
- ### Observability dashboards
80
-
81
- Stream `--json-file` into Loki / OTEL / any pipeline that accepts JSONL.
82
- Extract `usage.input_tokens`, `tool_use.name`, `result.duration_api_ms`
83
- as first-class metrics in Grafana. No need for log-parsing regex.
84
-
85
- ### Testing and QA
86
-
87
- Integration tests spawn Matilda Code headlessly, drive it with `--input-file`
88
- scripts, and assert on `--json-file` events. Unlike parsing stdout ANSI,
89
- assertions are stable across UI refactors.
90
-
91
- ## Flags
92
-
93
- | Flag | Type | Purpose |
94
- | --------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
95
- | `--json-fd <n>` | number, `n >= 3` | Write structured JSON events to file descriptor `n`. The caller must provide this fd via spawn `stdio` configuration or shell redirection. |
96
- | `--json-file <path>` | path | Write structured JSON events to a file. The path can be a regular file, a FIFO (named pipe), or `/dev/fd/N`. |
97
- | `--input-file <path>` | path | Watch this file for JSONL commands written by an external program. |
98
-
99
- `--json-fd` and `--json-file` are mutually exclusive. fds 0, 1, and 2 are
100
- rejected to prevent corrupting the TUI's own output.
101
-
102
- ## Why two output flags? (`--json-fd` vs `--json-file`)
103
-
104
- At first glance `--json-fd` looks sufficient — the caller spawns Matilda Code
105
- with an extra file descriptor, the TUI writes events to it, done. In
106
- practice, fd passing breaks down under the most important embedding
107
- scenario: running the TUI inside a pseudo-terminal (PTY). That is why
108
- this feature also exposes a path-based alternative.
109
-
110
- ### When `--json-fd` works
111
-
112
- Pure `child_process.spawn` with a `stdio` array:
113
-
114
- ```ts
115
- const child = spawn('matilda', ['--json-fd', '3'], {
116
- stdio: ['inherit', 'inherit', 'inherit', eventsFd],
117
- });
118
- ```
119
-
120
- Node's spawn supports arbitrary `stdio` entries; fd 3 is inherited by the
121
- child, which can write to it directly. Zero-copy, zero-buffer, zero
122
- filesystem — the fastest path.
123
-
124
- ### Why `--json-fd` does **not** work under PTY
125
-
126
- PTY wrappers like [`node-pty`](https://github.com/microsoft/node-pty) and
127
- [`bun-pty`](https://github.com/oven-sh/bun) are how any serious embedder
128
- (IDE extensions, web terminals, tmux-like multiplexers) hosts an
129
- interactive TUI. They cannot forward extra fds to the child, for three
130
- reinforcing reasons:
131
-
132
- 1. **API surface.** `node-pty.spawn(file, args, options)` accepts `cwd`,
133
- `env`, `cols`, `rows`, `encoding`, etc. — but **no `stdio` array**. There
134
- is simply no place in the API to say "also attach this fd as fd 3 in
135
- the child". `bun-pty` exposes the same shape.
136
- 2. **`forkpty(3)` semantics.** Under the hood, PTY wrappers call
137
- `forkpty(3)` (or the equivalent `posix_openpt` + `login_tty` dance).
138
- That syscall allocates a master/slave pseudo-terminal pair and
139
- redirects the child's fds 0/1/2 to the slave side so the child thinks
140
- it is attached to a real terminal. Any fds above 2 in the parent are
141
- closed by `login_tty`, which calls `close(fd)` for `fd >= 3` before
142
- `exec`. Extra fds are actively wiped, not inherited.
143
- 3. **Controlling-terminal side effect.** Even if you hacked an extra fd
144
- through, it would not be a terminal, so the child's TUI renderer
145
- (which writes escape sequences assuming a TTY on fd 1) would still
146
- need the slave for its output. You would end up with two independent
147
- transports anyway.
148
-
149
- In short: the moment an embedder needs a real TTY for TUI rendering —
150
- which is every IDE extension, every web terminal, every desktop chat
151
- app — fd inheritance is off the table.
152
-
153
- ### `--json-file` fills the gap
154
-
155
- A file path is passed as an ordinary CLI argument, so it survives every
156
- spawn model:
157
-
158
- ```ts
159
- import { spawn } from 'node-pty';
160
-
161
- const pty = spawn(
162
- 'matilda',
163
- [
164
- '--json-file',
165
- '/tmp/matilda-events.jsonl',
166
- '--input-file',
167
- '/tmp/matilda-input.jsonl',
168
- ],
169
- { cols: 120, rows: 40 },
170
- );
171
- ```
172
-
173
- The child opens the file itself and writes events there; the embedder
174
- tails the same path with `fs.watch` + incremental reads. Three things to
175
- note:
176
-
177
- - **Regular file**, FIFO (named pipe), or `/dev/fd/N` all work. FIFO is
178
- the lowest-latency option when both sides are on the same host.
179
- - The bridge opens FIFOs with `O_NONBLOCK` and falls back to blocking
180
- mode on `ENXIO` (no reader yet), so PTY startup is never deadlocked
181
- waiting for a consumer.
182
- - For multi-session isolation, use per-session paths under
183
- `$XDG_RUNTIME_DIR` or a `mkdtemp`'d directory with mode `0700`.
184
-
185
- ### Which flag should I use?
186
-
187
- | Embedding style | Use |
188
- | ------------------------------------------------- | -------------------- |
189
- | `child_process.spawn` with plain stdio | `--json-fd` |
190
- | `node-pty` / `bun-pty` / any PTY host | `--json-file` |
191
- | Shell redirection / manual pipeline testing | either |
192
- | CI log collection (regular file, read after exit) | `--json-file` |
193
- | Lowest possible latency on same host | `--json-file` + FIFO |
194
-
195
- The general rule: **if you need the TUI to render correctly, you need a
196
- PTY, which means you need `--json-file`.** `--json-fd` is for simpler
197
- embedders that do not care about TUI fidelity — typically programmatic
198
- wrappers that throw away stdout anyway.
199
-
200
- ## Quick start
201
-
202
- Run Matilda Code with both channels enabled using regular files:
203
-
204
- ```bash
205
- touch /tmp/matilda-events.jsonl /tmp/matilda-input.jsonl
206
- matilda \
207
- --json-file /tmp/matilda-events.jsonl \
208
- --input-file /tmp/matilda-input.jsonl
209
- ```
210
-
211
- In a second terminal, tail the event stream:
212
-
213
- ```bash
214
- tail -f /tmp/matilda-events.jsonl
215
- ```
216
-
217
- In a third terminal, push a prompt into the running TUI:
218
-
219
- ```bash
220
- echo '{"type":"submit","text":"Explain this repo"}' >> /tmp/matilda-input.jsonl
221
- ```
222
-
223
- The prompt appears in the TUI exactly as if the user typed it, and the
224
- streaming response is mirrored on `/tmp/matilda-events.jsonl`.
225
-
226
- ### Using FIFOs (named pipes) for event output
227
-
228
- FIFOs deliver lower latency than regular files (no disk I/O) and work
229
- well when both sides are on the same host. The bridge opens FIFOs with
230
- `O_RDWR | O_NONBLOCK`, so it **does not block** even if no reader is
231
- connected yet — events are buffered in the kernel pipe buffer until a
232
- reader attaches.
233
-
234
- > **Note:** `--input-file` requires a regular file (not a FIFO) because
235
- > the watcher relies on `stat.size` to detect new data, which is always
236
- > 0 for FIFOs.
237
-
238
- ```bash
239
- mkfifo /tmp/matilda-events.jsonl
240
- touch /tmp/matilda-input.jsonl
241
- matilda \
242
- --json-file /tmp/matilda-events.jsonl \
243
- --input-file /tmp/matilda-input.jsonl
244
- # TUI starts immediately — no need to start a reader first.
245
-
246
- # In a second terminal, connect whenever ready:
247
- cat /tmp/matilda-events.jsonl
248
- ```
249
-
250
- If no reader ever connects, the bridge auto-disables once the internal
251
- buffer exceeds 1 MB. The TUI continues running normally.
252
-
253
- ## Output event schema
254
-
255
- Events are emitted as JSON Lines (one object per line). The schema is the same
256
- one used by the non-interactive `--output-format=stream-json` mode, with
257
- `includePartialMessages` always enabled.
258
-
259
- The first event on the channel is always `system` / `session_start`, emitted
260
- when the bridge is constructed. Use it to correlate the channel with a
261
- session id before any other event arrives.
262
-
263
- ```jsonc
264
- // Session lifecycle
265
- {
266
- "type": "system",
267
- "subtype": "session_start",
268
- "uuid": "...",
269
- "session_id": "...",
270
- "data": { "session_id": "...", "cwd": "/path/to/cwd" }
271
- }
272
-
273
- // Streaming events for an in-progress assistant turn
274
- { "type": "stream_event", "event": { "type": "message_start", "message": { ... } }, ... }
275
- { "type": "stream_event", "event": { "type": "content_block_start", "index": 0, "content_block": { "type": "text" } }, ... }
276
- { "type": "stream_event", "event": { "type": "content_block_delta", "index": 0, "delta": { "type": "text_delta", "text": "Hello" } }, ... }
277
- { "type": "stream_event", "event": { "type": "content_block_stop", "index": 0 }, ... }
278
- { "type": "stream_event", "event": { "type": "message_stop" }, ... }
279
-
280
- // Completed messages
281
- { "type": "user", "message": { "role": "user", "content": [...] }, ... }
282
- { "type": "assistant", "message": { "role": "assistant", "content": [...], "usage": { ... } }, ... }
283
- { "type": "user", "message": { "role": "user", "content": [{ "type": "tool_result", ... }] } }
284
-
285
- // Permission control plane (only when a tool needs approval)
286
- {
287
- "type": "control_request",
288
- "request_id": "...",
289
- "request": {
290
- "subtype": "can_use_tool",
291
- "tool_name": "run_shell_command",
292
- "tool_use_id": "...",
293
- "input": { "command": "rm -rf /tmp/x" },
294
- "permission_suggestions": null,
295
- "blocked_path": null
296
- }
297
- }
298
- {
299
- "type": "control_response",
300
- "response": {
301
- "subtype": "success",
302
- "request_id": "...",
303
- "response": { "allowed": true }
304
- }
305
- }
306
- ```
307
-
308
- `control_response` is emitted whether the decision was made in the TUI
309
- (native approval UI) or by an external `confirmation_response` (see below).
310
- Either way, all observers see the final outcome.
311
-
312
- ## Input command schema
313
-
314
- Two command shapes are accepted on `--input-file`:
315
-
316
- ```jsonc
317
- // Submit a user message into the prompt queue
318
- { "type": "submit", "text": "What does this function do?" }
319
-
320
- // Reply to a pending control_request
321
- { "type": "confirmation_response", "request_id": "...", "allowed": true }
322
- ```
323
-
324
- Behavior:
325
-
326
- - `submit` commands are queued. If the TUI is busy responding, they are
327
- retried automatically the next time the TUI returns to the idle state.
328
- - `confirmation_response` commands are dispatched immediately and never
329
- queued, because a tool call is blocking and the response must reach the
330
- underlying `onConfirm` handler without waiting for any earlier `submit`.
331
- - Whichever side approves a tool first wins; the other side's late response
332
- is harmlessly dropped.
333
- - Lines that fail to parse as JSON are logged and skipped — they do not
334
- stop the watcher.
335
-
336
- ## Latency notes
337
-
338
- The input file is observed with `fs.watchFile` at a 500 ms polling interval,
339
- so worst-case round-trip latency for a remote `submit` is about half a
340
- second. This is intentional: polling is portable across platforms and
341
- filesystems (including macOS / network mounts), and matches the typical
342
- human-in-the-loop pacing the feature targets. The output channel has no
343
- polling — events are written synchronously as the TUI emits them.
344
-
345
- ## Failure modes
346
-
347
- - **Bad fd.** If the fd passed to `--json-fd` is not open or is one of
348
- 0/1/2, the TUI prints a warning to `stderr` and continues without dual
349
- output enabled.
350
- - **Bad path.** If the file passed to `--json-file` cannot be opened, the
351
- TUI prints a warning and continues without dual output.
352
- - **Consumer disconnect.** If the reader on the other side of the channel
353
- goes away (`EPIPE`), the bridge silently disables itself and the TUI
354
- keeps running. No retry.
355
- - **FIFO buffer overflow.** When writing to a FIFO with no reader
356
- attached, events buffer in the kernel pipe (~64 KB on Linux) and the
357
- Node.js WriteStream. Once the pipe is full or the internal buffer
358
- exceeds 1 MB, the bridge disables itself and closes the fd. No
359
- `session_end` is emitted in this case — consumers should treat a
360
- closed stream without `session_end` as an abnormal termination. The
361
- TUI continues running normally.
362
- - **Adapter exception.** Any exception thrown while emitting an event is
363
- caught, logged, and disables the bridge. The TUI is never crashed by a
364
- dual-output failure.
365
-
366
- ## Spawn example
367
-
368
- A typical embedding parent process spawns Matilda Code with both channels:
369
-
370
- ```ts
371
- import { spawn } from 'node:child_process';
372
- import { openSync } from 'node:fs';
373
-
374
- const eventsFd = openSync('/tmp/matilda-events.jsonl', 'w');
375
- const child = spawn(
376
- 'matilda',
377
- ['--json-fd', '3', '--input-file', '/tmp/matilda-input.jsonl'],
378
- { stdio: ['inherit', 'inherit', 'inherit', eventsFd] },
379
- );
380
- ```
381
-
382
- The TUI still owns the user's terminal on stdio 0/1/2, while the embedder
383
- reads structured events on the file backing fd 3 and pushes commands by
384
- appending JSONL lines to `/tmp/matilda-input.jsonl`.
385
-
386
- ## Settings-based configuration
387
-
388
- For long-lived embedders it is often inconvenient to thread CLI flags
389
- through every launch. The same channels can be configured in
390
- `settings.json` under the top-level `dualOutput` key:
391
-
392
- ```jsonc
393
- // ~/.matilda/settings.json (user-level)
394
- // or <workspace>/.matilda/settings.json (workspace-level)
395
- {
396
- "dualOutput": {
397
- "jsonFile": "/tmp/matilda-events.jsonl",
398
- "inputFile": "/tmp/matilda-input.jsonl",
399
- },
400
- }
401
- ```
402
-
403
- Precedence rules:
404
-
405
- - CLI flag **wins** over settings. Passing `--json-file /foo` on the
406
- command line overrides `dualOutput.jsonFile` in settings.
407
- - `--json-fd` has no settings equivalent — fd passing is a spawn-time
408
- concern that cannot be statically declared.
409
- - If neither flag nor setting is present, dual output stays disabled
410
- (identical to today's default).
411
-
412
- The `requiresRestart: true` flag means changes only take effect on the
413
- next Matilda Code launch, since the bridge is constructed once during
414
- startup.
415
-
416
- ## Runnable demos
417
-
418
- Every script below is copy-paste ready. Start with POC&nbsp;1 to verify
419
- the build has dual output; POC&nbsp;4 is the closest analogue to a real
420
- IDE-extension integration.
421
-
422
- ### POC 1 — observe the event stream
423
-
424
- Watch every structured event the TUI emits while a human uses it
425
- normally:
426
-
427
- ```bash
428
- # Terminal A
429
- mkfifo /tmp/matilda-events.jsonl
430
- cat /tmp/matilda-events.jsonl | jq -c 'select(.type != "stream_event") | {type, subtype}'
431
-
432
- # Terminal B
433
- matilda --json-file /tmp/matilda-events.jsonl
434
- # ...then chat normally; terminal A shows session_start,
435
- # user/assistant/result/control_request lifecycle in real time.
436
- ```
437
-
438
- Expected first line in terminal A:
439
-
440
- ```json
441
- { "type": "system", "subtype": "session_start" }
442
- ```
443
-
444
- ### POC 2 — inject prompts from outside
445
-
446
- Drive the TUI from a second terminal without touching the keyboard of
447
- the first:
448
-
449
- ```bash
450
- # Terminal A
451
- touch /tmp/matilda-in.jsonl
452
- matilda --input-file /tmp/matilda-in.jsonl
453
-
454
- # Terminal B — the TUI responds as if you typed it
455
- echo '{"type":"submit","text":"list files in the current directory"}' \
456
- >> /tmp/matilda-in.jsonl
457
- ```
458
-
459
- ### POC 3 — remote tool-permission bridge
460
-
461
- Approve or deny tool calls from a separate process:
462
-
463
- ```bash
464
- # Terminal A — observe control_requests
465
- mkfifo /tmp/matilda-out.jsonl
466
- touch /tmp/matilda-in.jsonl
467
- (cat /tmp/matilda-out.jsonl \
468
- | jq -c 'select(.type == "control_request")') &
469
-
470
- # Terminal B
471
- matilda --json-file /tmp/matilda-out.jsonl --input-file /tmp/matilda-in.jsonl
472
- # Ask Matilda to do something that needs approval, e.g.
473
- # "run `ls -la /tmp`". A control_request will appear in terminal A.
474
- # Copy the request_id, then in a third terminal:
475
- echo '{"type":"confirmation_response","request_id":"<paste-id>","allowed":true}' \
476
- >> /tmp/matilda-in.jsonl
477
- # The TUI confirmation prompt dismisses and the tool executes.
478
- ```
479
-
480
- If you reply with an unknown `request_id`, the bridge emits a
481
- `control_response` with `subtype: "error"` on the output channel so your
482
- consumer can log it or retry:
483
-
484
- ```json
485
- {
486
- "type": "control_response",
487
- "response": {
488
- "subtype": "error",
489
- "request_id": "...",
490
- "error": "unknown request_id (already resolved, cancelled, or never issued)"
491
- }
492
- }
493
- ```
494
-
495
- ### POC 4 — Node embedder (IDE-like)
496
-
497
- The most realistic shape: a parent process spawns Matilda Code, tails
498
- events, and injects prompts on its own schedule.
499
-
500
- ```ts
501
- // demo-embedder.ts
502
- import { spawn } from 'node:child_process';
503
- import { appendFileSync, createReadStream, writeFileSync } from 'node:fs';
504
- import { createInterface } from 'node:readline';
505
- import { tmpdir } from 'node:os';
506
- import { join } from 'node:path';
507
-
508
- const events = join(tmpdir(), `matilda-events-${process.pid}.jsonl`);
509
- const input = join(tmpdir(), `matilda-input-${process.pid}.jsonl`);
510
- writeFileSync(events, '');
511
- writeFileSync(input, '');
512
-
513
- const child = spawn('matilda', ['--json-file', events, '--input-file', input], {
514
- stdio: 'inherit',
515
- });
516
-
517
- // Tail the output channel. In production you'd use a proper
518
- // byte-offset tail; this one re-streams from 0 for brevity.
519
- const rl = createInterface({
520
- input: createReadStream(events, { encoding: 'utf8' }),
521
- });
522
- rl.on('line', (line) => {
523
- if (!line.trim()) return;
524
- const ev = JSON.parse(line);
525
- if (ev.type === 'system' && ev.subtype === 'session_start') {
526
- console.log('[embedder] handshake:', {
527
- protocol_version: ev.data.protocol_version,
528
- version: ev.data.version,
529
- supported_events: ev.data.supported_events,
530
- });
531
- // Feature-detect before using a capability
532
- if (ev.data.supported_events.includes('control_request')) {
533
- console.log('[embedder] permission control-plane available');
534
- }
535
- }
536
- if (ev.type === 'assistant') {
537
- console.log(
538
- '[embedder] assistant turn ended, tokens =',
539
- ev.message.usage?.output_tokens,
540
- );
541
- }
542
- if (ev.type === 'system' && ev.subtype === 'session_end') {
543
- console.log('[embedder] session ended cleanly');
544
- }
545
- });
546
-
547
- // After 2s, inject a prompt as if the user typed it
548
- setTimeout(() => {
549
- appendFileSync(
550
- input,
551
- JSON.stringify({ type: 'submit', text: 'hello from embedder' }) + '\n',
552
- );
553
- }, 2000);
554
-
555
- child.on('exit', () => process.exit(0));
556
- ```
557
-
558
- Run with:
559
-
560
- ```bash
561
- npx tsx demo-embedder.ts
562
- # Matilda Code TUI opens in the current terminal; the embedder logs
563
- # handshake + turn-end + session_end events to the parent's stdout.
564
- ```
565
-
566
- ### POC 5 — capability handshake feature detection
567
-
568
- Older Matilda Code versions won't emit `protocol_version`. Treat the field
569
- as optional and feature-detect:
570
-
571
- ```ts
572
- rl.on('line', (line) => {
573
- const ev = JSON.parse(line);
574
- if (ev.type === 'system' && ev.subtype === 'session_start') {
575
- const v = ev.data?.protocol_version ?? 0;
576
- if (v < 1) {
577
- console.error(
578
- 'matilda-code dual output is present but protocol < 1; ' +
579
- 'falling back to best-effort behavior',
580
- );
581
- } else {
582
- console.log('matilda-code dual output protocol v' + v);
583
- }
584
- }
585
- });
586
- ```
587
-
588
- ### POC 6 — session_end as a clean termination signal
589
-
590
- ```ts
591
- rl.on('line', (line) => {
592
- const ev = JSON.parse(line);
593
- if (ev.type === 'system' && ev.subtype === 'session_end') {
594
- console.log('[embedder] clean shutdown, session', ev.data.session_id);
595
- // Flush metrics, close WebSockets, etc.
596
- }
597
- });
598
- ```
599
-
600
- If the TUI crashes before `session_end`, the output stream closes
601
- (`EPIPE` on next write); embedders should handle both paths.
602
-
603
- ### POC 7 — failure drills (prove the flags never break the TUI)
604
-
605
- ```bash
606
- matilda --json-fd 1
607
- # stderr: "Warning: dual output disabled — ..."
608
- # TUI still launches normally.
609
-
610
- matilda --json-fd 9999
611
- # stderr: "Warning: dual output disabled — fd 9999 not open"
612
- # TUI still launches normally.
613
-
614
- matilda --json-fd 3 --json-file /tmp/x.jsonl
615
- # yargs rejects: "--json-fd and --json-file are mutually exclusive."
616
- # Process exits before TUI starts.
617
-
618
- matilda --json-file /nonexistent/dir/x.jsonl
619
- # stderr warning; TUI still launches.
620
- ```
621
-
622
- ## Relation to Claude Code
623
-
624
- Claude Code exposes a similar stream-json event format under
625
- `--print --output-format stream-json`, but only in non-interactive mode
626
- — it has no equivalent of running the TUI and a structured sidecar
627
- channel at the same time. Dual Output fills that gap.