upfynai-code 2.3.0 → 2.4.1

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 (162) hide show
  1. package/client/dist/assets/AppContent-DTZ2FbvM.js +513 -0
  2. package/client/dist/assets/CanvasPanel-DlTW6Jh6.js +6 -0
  3. package/client/dist/assets/LoginModal-CWoFm0au.js +19 -0
  4. package/client/dist/assets/MarkdownPreview-CYdvwJaV.js +1 -0
  5. package/client/dist/assets/{Onboarding-Coxo6mFA.js → Onboarding-CtIoXiTp.js} +1 -1
  6. package/client/dist/assets/{SetupForm-BzYOsbji.js → SetupForm-B4p8im5O.js} +1 -1
  7. package/client/dist/assets/{ar-SA-G6X2FPQ2-Bmw2-hDt.js → ar-SA-G6X2FPQ2-2gfmdvHk.js} +1 -1
  8. package/client/dist/assets/{arc-BMqY7_Ci.js → arc-DCZSHhoJ.js} +1 -1
  9. package/client/dist/assets/{az-AZ-76LH7QW2-Dh1le_qs.js → az-AZ-76LH7QW2-CDdeucRZ.js} +1 -1
  10. package/client/dist/assets/{bg-BG-XCXSNQG7-Cbav8Z9z.js → bg-BG-XCXSNQG7-D6__XtOK.js} +1 -1
  11. package/client/dist/assets/{blockDiagram-38ab4fdb-ChHJxsXw.js → blockDiagram-38ab4fdb-Cfbaeyp6.js} +3 -3
  12. package/client/dist/assets/{bn-BD-2XOGV67Q-DCNjOaWz.js → bn-BD-2XOGV67Q-DHNJw3OG.js} +1 -1
  13. package/client/dist/assets/{c4Diagram-3d4e48cf-b8Xue4Z6.js → c4Diagram-3d4e48cf-BBCnjOTy.js} +1 -1
  14. package/client/dist/assets/{ca-ES-6MX7JW3Y-Dl_vM7NS.js → ca-ES-6MX7JW3Y-r5g4o3zQ.js} +1 -1
  15. package/client/dist/assets/channel-O3ovC0x9.js +1 -0
  16. package/client/dist/assets/{classDiagram-70f12bd4-BheP7Ggo.js → classDiagram-70f12bd4-D0lhAcxU.js} +1 -1
  17. package/client/dist/assets/classDiagram-v2-f2320105-BuwUsF3F.js +2 -0
  18. package/client/dist/assets/clone-BG9u7vLi.js +1 -0
  19. package/client/dist/assets/{createText-2e5e7dd3-_n4jI_fO.js → createText-2e5e7dd3-B8jCDmF_.js} +1 -1
  20. package/client/dist/assets/{cs-CZ-2BRQDIVT-ftsKDdz4.js → cs-CZ-2BRQDIVT-p08jRLRC.js} +1 -1
  21. package/client/dist/assets/{da-DK-5WZEPLOC-DAjdwGRO.js → da-DK-5WZEPLOC-CnhOImFf.js} +1 -1
  22. package/client/dist/assets/{de-DE-XR44H4JA-BJXczHGT.js → de-DE-XR44H4JA-BunSXZ-Y.js} +1 -1
  23. package/client/dist/assets/{edges-e0da2a9e-CfPZr4YM.js → edges-e0da2a9e-CGBBhG8k.js} +2 -2
  24. package/client/dist/assets/{el-GR-BZB4AONW-DW2p_uy7.js → el-GR-BZB4AONW-D4wv1oIz.js} +1 -1
  25. package/client/dist/assets/{erDiagram-9861fffd-CF33V-Of.js → erDiagram-9861fffd-CYaF3q1I.js} +1 -1
  26. package/client/dist/assets/{es-ES-U4NZUMDT-DLOIGnrl.js → es-ES-U4NZUMDT-CGeTKXgd.js} +1 -1
  27. package/client/dist/assets/{eu-ES-A7QVB2H4-LJXbf89m.js → eu-ES-A7QVB2H4-Cayx1TxR.js} +1 -1
  28. package/client/dist/assets/{fa-IR-HGAKTJCU-Dvx65fgW.js → fa-IR-HGAKTJCU-CmUg8pmw.js} +1 -1
  29. package/client/dist/assets/{fi-FI-Z5N7JZ37-EoL65BQh.js → fi-FI-Z5N7JZ37-xvHcPhsU.js} +1 -1
  30. package/client/dist/assets/{flowDb-956e92f1-HgoXVy2H.js → flowDb-956e92f1-C-_LFz70.js} +3 -3
  31. package/client/dist/assets/flowDiagram-66a62f08-C1sHdSjn.js +4 -0
  32. package/client/dist/assets/flowDiagram-v2-96b9c2cf-Cd0Iascd.js +1 -0
  33. package/client/dist/assets/{flowchart-elk-definition-4a651766-DJbI2dpv.js → flowchart-elk-definition-4a651766-CNGfpudb.js} +7 -7
  34. package/client/dist/assets/{fr-FR-RHASNOE6-DNk_jdDs.js → fr-FR-RHASNOE6-DBoHEcNj.js} +1 -1
  35. package/client/dist/assets/{ganttDiagram-c361ad54-2XX670FU.js → ganttDiagram-c361ad54-B8HJQqjt.js} +1 -1
  36. package/client/dist/assets/{gitGraphDiagram-72cf32ee-CcUfruAo.js → gitGraphDiagram-72cf32ee-DojCDvlS.js} +1 -1
  37. package/client/dist/assets/{gl-ES-HMX3MZ6V-dxzFjZlG.js → gl-ES-HMX3MZ6V-p6hrn2cN.js} +1 -1
  38. package/client/dist/assets/{graph-BSbiMSBC.js → graph-DXM7lcy1.js} +1 -1
  39. package/client/dist/assets/{he-IL-6SHJWFNN-Cogsfdt1.js → he-IL-6SHJWFNN-y2jEX6-0.js} +1 -1
  40. package/client/dist/assets/{hi-IN-IWLTKZ5I-L6wbgi4F.js → hi-IN-IWLTKZ5I-99pNfyWr.js} +1 -1
  41. package/client/dist/assets/{hu-HU-A5ZG7DT2-DSA6ZDsH.js → hu-HU-A5ZG7DT2-hygceGMS.js} +1 -1
  42. package/client/dist/assets/{id-ID-SAP4L64H-BK_vGGS6.js → id-ID-SAP4L64H-CyIqi1hv.js} +1 -1
  43. package/client/dist/assets/{image-blob-reduce.esm-BLtmMM_J.js → image-blob-reduce.esm-D6s-rqMO.js} +6 -1
  44. package/client/dist/assets/{index-3862675e-Bv32HUgT.js → index-3862675e-4idOQN2N.js} +1 -1
  45. package/client/dist/assets/{index-BPwf8Fw3.js → index-BGmwbRlb.js} +6 -6
  46. package/client/dist/assets/index-BHZfFT_V.js +97 -0
  47. package/client/dist/assets/{infoDiagram-f8f76790-w4mR4pxn.js → infoDiagram-f8f76790-CFLrHqtc.js} +1 -1
  48. package/client/dist/assets/{it-IT-JPQ66NNP-BLdHYMhn.js → it-IT-JPQ66NNP-DzVvVdQI.js} +1 -1
  49. package/client/dist/assets/{ja-JP-DBVTYXUO-B_vmexl_.js → ja-JP-DBVTYXUO-BI4fPexV.js} +1 -1
  50. package/client/dist/assets/{journeyDiagram-49397b02-D9nmO17e.js → journeyDiagram-49397b02-C3CFDo8z.js} +1 -1
  51. package/client/dist/assets/{kaa-6HZHGXH3-5s-3jl6F.js → kaa-6HZHGXH3-fwOleoQB.js} +1 -1
  52. package/client/dist/assets/{kab-KAB-ZGHBKWFO-2QaVDuSf.js → kab-KAB-ZGHBKWFO-DBI_ri48.js} +1 -1
  53. package/client/dist/assets/{kk-KZ-P5N5QNE5-CTC52Vbi.js → kk-KZ-P5N5QNE5-zpl7uvyF.js} +1 -1
  54. package/client/dist/assets/{km-KH-HSX4SM5Z-DxawH8UZ.js → km-KH-HSX4SM5Z-DOMFSres.js} +1 -1
  55. package/client/dist/assets/{ko-KR-MTYHY66A-CmosEM8_.js → ko-KR-MTYHY66A-tb08hXzd.js} +1 -1
  56. package/client/dist/assets/{ku-TR-6OUDTVRD-DbiLen4y.js → ku-TR-6OUDTVRD-DlIQCCY4.js} +1 -1
  57. package/client/dist/assets/{layout-jmt3H9tA.js → layout-B_11mCXA.js} +1 -1
  58. package/client/dist/assets/{line-JTlRayUJ.js → line-B-qmK_vI.js} +1 -1
  59. package/client/dist/assets/{linear-DJeB5p7x.js → linear-Ph6uuYcX.js} +1 -1
  60. package/client/dist/assets/{lt-LT-XHIRWOB4-CH15wrjA.js → lt-LT-XHIRWOB4--qWy24_Z.js} +1 -1
  61. package/client/dist/assets/{lv-LV-5QDEKY6T-dhgfPuCQ.js → lv-LV-5QDEKY6T-Bnd_1GDb.js} +1 -1
  62. package/client/dist/assets/mindmap-definition-fc14e90a-Do79tIc0.js +425 -0
  63. package/client/dist/assets/{mr-IN-CRQNXWMA-3Gi6iq7A.js → mr-IN-CRQNXWMA-BsV6HaD9.js} +1 -1
  64. package/client/dist/assets/{my-MM-5M5IBNSE-CpH4rdJj.js → my-MM-5M5IBNSE-kZQURVIi.js} +1 -1
  65. package/client/dist/assets/{nb-NO-T6EIAALU-Du6iiGql.js → nb-NO-T6EIAALU-Cvf9FdSF.js} +1 -1
  66. package/client/dist/assets/{nl-NL-IS3SIHDZ-BGvsd1MT.js → nl-NL-IS3SIHDZ-DA1yqpXw.js} +1 -1
  67. package/client/dist/assets/{nn-NO-6E72VCQL-B-odvJZW.js → nn-NO-6E72VCQL-89lm3vku.js} +1 -1
  68. package/client/dist/assets/{oc-FR-POXYY2M6-COC8xNjo.js → oc-FR-POXYY2M6-BsrjTJQh.js} +1 -1
  69. package/client/dist/assets/{pa-IN-N4M65BXN-CE21PUQH.js → pa-IN-N4M65BXN-CczefYaj.js} +1 -1
  70. package/client/dist/assets/pdf-CE_K4jFx.js +12 -0
  71. package/client/dist/assets/percentages-BXMCSKIN-Be6p9phi.js +207 -0
  72. package/client/dist/assets/pica-CQIY57Tf.js +7 -0
  73. package/client/dist/assets/{pieDiagram-8a3498a8-Cvfh7Qr5.js → pieDiagram-8a3498a8-CfblQHdm.js} +2 -2
  74. package/client/dist/assets/{pl-PL-T2D74RX3-D4xFVSoT.js → pl-PL-T2D74RX3-DdhH-zcK.js} +1 -1
  75. package/client/dist/assets/{pt-BR-5N22H2LF-CCq257gA.js → pt-BR-5N22H2LF-gpwlheL6.js} +1 -1
  76. package/client/dist/assets/{pt-PT-UZXXM6DQ-1l8gt5vA.js → pt-PT-UZXXM6DQ-Cs87vICi.js} +1 -1
  77. package/client/dist/assets/{quadrantDiagram-120e2f19-BA0js1aD.js → quadrantDiagram-120e2f19-CRMSamSP.js} +1 -1
  78. package/client/dist/assets/{requirementDiagram-deff3bca-B0QNFfIn.js → requirementDiagram-deff3bca-D3LBN016.js} +1 -1
  79. package/client/dist/assets/{ro-RO-JPDTUUEW-yosBW01E.js → ro-RO-JPDTUUEW-CWTSJ1Dt.js} +1 -1
  80. package/client/dist/assets/roundRect-0PYZxl1G.js +1 -0
  81. package/client/dist/assets/{ru-RU-B4JR7IUQ-8LkEJUix.js → ru-RU-B4JR7IUQ-Bq7aN2ep.js} +1 -1
  82. package/client/dist/assets/{sankeyDiagram-04a897e0-D4T9eCXn.js → sankeyDiagram-04a897e0-CsFqOQZN.js} +3 -3
  83. package/client/dist/assets/{sequenceDiagram-704730f1-CfBUTCrO.js → sequenceDiagram-704730f1-BRYXVDGX.js} +1 -1
  84. package/client/dist/assets/{si-LK-N5RQ5JYF-D8rjbqtd.js → si-LK-N5RQ5JYF-BBjcNYQh.js} +1 -1
  85. package/client/dist/assets/{sk-SK-C5VTKIMK-Bg14sAzN.js → sk-SK-C5VTKIMK-ByjKQzUb.js} +1 -1
  86. package/client/dist/assets/{sl-SI-NN7IZMDC-CMTib6Zs.js → sl-SI-NN7IZMDC-B8WCyMBU.js} +1 -1
  87. package/client/dist/assets/{stateDiagram-587899a1-BGgvmVSZ.js → stateDiagram-587899a1-BHoy9LtD.js} +1 -1
  88. package/client/dist/assets/{stateDiagram-v2-d93cdb3a-Qn3DpYuO.js → stateDiagram-v2-d93cdb3a-BvMUA6bS.js} +1 -1
  89. package/client/dist/assets/{styles-6aaf32cf-IdVZLPrD.js → styles-6aaf32cf-Dr-lfIOW.js} +2 -2
  90. package/client/dist/assets/{styles-9a916d00-BAC3L45X.js → styles-9a916d00-DS4wRpL7.js} +1 -1
  91. package/client/dist/assets/{styles-c10674c1-COhXxX8c.js → styles-c10674c1-nKRF6NrH.js} +1 -1
  92. package/client/dist/assets/{subset-shared.chunk-BWHnFai4.js → subset-shared.chunk-KT79s7KG.js} +64 -2
  93. package/client/dist/assets/subset-worker.chunk-BMx1eyv3.js +1 -0
  94. package/client/dist/assets/{sv-SE-XGPEYMSR-C1425rOF.js → sv-SE-XGPEYMSR-BiIPUVbv.js} +1 -1
  95. package/client/dist/assets/{svgDrawCommon-08f97a94-Cfk-fgnN.js → svgDrawCommon-08f97a94-C3uP9PYr.js} +1 -1
  96. package/client/dist/assets/{ta-IN-2NMHFXQM-BHHo1zpF.js → ta-IN-2NMHFXQM-Cidadso2.js} +1 -1
  97. package/client/dist/assets/{th-TH-HPSO5L25-CZVzm_WT.js → th-TH-HPSO5L25-CFNnJwSv.js} +1 -1
  98. package/client/dist/assets/{timeline-definition-85554ec2-VAvuJith.js → timeline-definition-85554ec2-BSsLsIgF.js} +1 -1
  99. package/client/dist/assets/{tr-TR-DEFEU3FU-DE1lclCq.js → tr-TR-DEFEU3FU-DaFcI-KL.js} +1 -1
  100. package/client/dist/assets/{uk-UA-QMV73CPH-D4lJZ85O.js → uk-UA-QMV73CPH-DkBW36St.js} +1 -1
  101. package/client/dist/assets/vendor-codemirror-langs-BH1ZcKHY.js +20 -0
  102. package/client/dist/assets/vendor-codemirror-rix45NST.js +16 -0
  103. package/client/dist/assets/vendor-i18n-DCFGyhQR.js +1 -0
  104. package/client/dist/assets/vendor-icons-Dh9m_Ydt.js +596 -0
  105. package/client/dist/assets/{vendor-markdown-CIVH08vJ.js → vendor-markdown-BXEi_H3G.js} +3 -3
  106. package/client/dist/assets/vendor-react-9mUTKBHH.js +67 -0
  107. package/client/dist/assets/{vendor-syntax-Djb62v3a.js → vendor-syntax-DnmwQQJF.js} +14 -7
  108. package/client/dist/assets/vendor-xterm-CZq1hqo1.js +66 -0
  109. package/client/dist/assets/vendor-xterm-qxJ8_QYu.css +32 -0
  110. package/client/dist/assets/{vi-VN-M7AON7JQ-Dgc_SShk.js → vi-VN-M7AON7JQ-KrtfxOzl.js} +1 -1
  111. package/client/dist/assets/{xychartDiagram-e933f94c-BeyVBJhb.js → xychartDiagram-e933f94c-CgNgZ4pp.js} +1 -1
  112. package/client/dist/assets/{zh-CN-LNUGB5OW-MH4Yh8in.js → zh-CN-LNUGB5OW-BQu12RoD.js} +1 -1
  113. package/client/dist/assets/{zh-HK-E62DVLB3-D4XHehjx.js → zh-HK-E62DVLB3-zx9CvERq.js} +1 -1
  114. package/client/dist/assets/{zh-TW-RAJ6MFWO--efj3evj.js → zh-TW-RAJ6MFWO-ffJWgVxn.js} +1 -1
  115. package/client/dist/index.html +128 -66
  116. package/client/dist/manifest.json +61 -15
  117. package/client/dist/sw.js +19 -55
  118. package/commands/upfynai-connect.md +31 -18
  119. package/commands/upfynai.md +45 -26
  120. package/package.json +1 -1
  121. package/server/cli-ui.js +320 -169
  122. package/server/cli.js +255 -23
  123. package/server/constants/config.js +29 -3
  124. package/server/database/auth.db +0 -0
  125. package/server/index.js +380 -121
  126. package/server/mcp-server.js +2 -1
  127. package/server/middleware/auth.js +18 -8
  128. package/server/openrouter.js +137 -0
  129. package/server/relay-client.js +262 -18
  130. package/server/routes/agent.js +54 -19
  131. package/server/routes/auth.js +23 -12
  132. package/server/routes/commands.js +1 -1
  133. package/server/routes/settings.js +91 -0
  134. package/shared/modelConstants.js +29 -0
  135. package/client/dist/assets/AppContent-CTSHQdyq.js +0 -513
  136. package/client/dist/assets/CanvasPanel-Cig0Mo9s.js +0 -6
  137. package/client/dist/assets/LoginModal-silya-zP.js +0 -11
  138. package/client/dist/assets/MarkdownPreview-B3c7OEj6.js +0 -1
  139. package/client/dist/assets/channel-CSnvHe_M.js +0 -1
  140. package/client/dist/assets/classDiagram-v2-f2320105-xtym7GEZ.js +0 -2
  141. package/client/dist/assets/clone-B75abXxS.js +0 -1
  142. package/client/dist/assets/flowDiagram-66a62f08-tffoET0H.js +0 -4
  143. package/client/dist/assets/flowDiagram-v2-96b9c2cf-Byc3JCHh.js +0 -1
  144. package/client/dist/assets/index-BnXuHrpJ.js +0 -523
  145. package/client/dist/assets/index-BwxNox94.css +0 -1
  146. package/client/dist/assets/index-D1urGMYu.js +0 -95
  147. package/client/dist/assets/mindmap-definition-fc14e90a-BOOrexmz.js +0 -415
  148. package/client/dist/assets/pdf-TYrZqVzP.js +0 -12
  149. package/client/dist/assets/percentages-BXMCSKIN-C9GT0OD3.js +0 -199
  150. package/client/dist/assets/pica-VkdyTzi8.js +0 -2
  151. package/client/dist/assets/roundRect-mAH3dD0p.js +0 -1
  152. package/client/dist/assets/subset-worker.chunk-C8QUSruZ.js +0 -1
  153. package/client/dist/assets/vendor-codemirror-BARtJV1V.js +0 -16
  154. package/client/dist/assets/vendor-codemirror-langs-52_y1wip.js +0 -20
  155. package/client/dist/assets/vendor-i18n-ByAl-gdx.js +0 -1
  156. package/client/dist/assets/vendor-icons-D33IkSIf.js +0 -1
  157. package/client/dist/assets/vendor-react-CHoMc7ka.js +0 -8
  158. package/client/dist/assets/vendor-xterm-DBb3RXlu.js +0 -66
  159. package/client/dist/assets/vendor-xterm-DrlLKa8f.css +0 -1
  160. package/client/dist/llms.txt +0 -40
  161. package/client/dist/robots.txt +0 -11
  162. package/client/dist/sitemap.xml +0 -45
package/server/cli-ui.js CHANGED
@@ -1,15 +1,16 @@
1
1
  /**
2
2
  * Upfyn-Code CLI — Terminal UI & Animations
3
3
  *
4
- * Beautiful blue-themed terminal experience inspired by Claude Code
5
- * with rocket launch animation, ASCII branding, and styled panels.
4
+ * Beautiful blue-themed terminal experience inspired by Claude Code.
5
+ * Uses terminal-safe characters that render correctly on Windows CMD,
6
+ * PowerShell, Windows Terminal, Git Bash, macOS Terminal, and Linux.
6
7
  */
7
8
 
8
- // ─── ANSI escape codes ───
9
+ // --- ANSI escape codes ---
9
10
  const ESC = '\x1b';
10
11
  const CSI = `${ESC}[`;
11
12
 
12
- // Blue theme palette
13
+ // Blue theme palette (24-bit RGB — works in all modern terminals)
13
14
  const theme = {
14
15
  // Core blues
15
16
  blue: `${CSI}38;2;59;130;246m`, // #3B82F6
@@ -47,6 +48,62 @@ const theme = {
47
48
  const t = theme;
48
49
  const r = t.reset;
49
50
 
51
+ // --- Terminal capability detection ---
52
+ function detectTerminalSupport() {
53
+ // Windows Terminal, VS Code, modern terminals support full Unicode
54
+ if (process.env.WT_SESSION) return 'unicode';
55
+ if (process.env.TERM_PROGRAM === 'vscode') return 'unicode';
56
+ if (process.env.TERM_PROGRAM === 'iTerm.app') return 'unicode';
57
+ if (process.env.TERM_PROGRAM === 'Hyper') return 'unicode';
58
+ if (process.env.TERM === 'xterm-256color') return 'unicode';
59
+ if (process.env.TERM === 'xterm-kitty') return 'unicode';
60
+ if (process.env.COLORTERM === 'truecolor') return 'unicode';
61
+
62
+ // Windows without WT_SESSION — use ASCII
63
+ if (process.platform === 'win32') return 'ascii';
64
+
65
+ // Most modern Linux/Mac terminals support basic box-drawing
66
+ return 'unicode';
67
+ }
68
+
69
+ // Box-drawing character sets
70
+ const CHARS_UNICODE = {
71
+ h: '\u2500', // ─
72
+ v: '\u2502', // │
73
+ tl: '\u250C', // ┌
74
+ tr: '\u2510', // ┐
75
+ bl: '\u2514', // └
76
+ br: '\u2518', // ┘
77
+ cross: '\u253C', // ┼
78
+ ltee: '\u251C', // ├
79
+ rtee: '\u2524', // ┤
80
+ ttee: '\u252C', // ┬
81
+ btee: '\u2534', // ┴
82
+ dot: '\u00B7', // ·
83
+ bullet: '\u2022', // •
84
+ star: '*',
85
+ };
86
+
87
+ const CHARS_ASCII = {
88
+ h: '-',
89
+ v: '|',
90
+ tl: '+',
91
+ tr: '+',
92
+ bl: '+',
93
+ br: '+',
94
+ cross: '+',
95
+ ltee: '+',
96
+ rtee: '+',
97
+ ttee: '+',
98
+ btee: '+',
99
+ dot: '.',
100
+ bullet: '*',
101
+ star: '*',
102
+ };
103
+
104
+ const termLevel = detectTerminalSupport();
105
+ const ch = termLevel === 'unicode' ? CHARS_UNICODE : CHARS_ASCII;
106
+
50
107
  // Helper — colorize
51
108
  const c = {
52
109
  blue: (s) => `${t.blue}${s}${r}`,
@@ -71,7 +128,7 @@ const c = {
71
128
  bCyan: (s) => `${t.bold}${t.cyan}${s}${r}`,
72
129
  };
73
130
 
74
- // ─── ASCII Art Logo ───
131
+ // --- ASCII Art Logo ---
75
132
  const LOGO = [
76
133
  `${t.brightBlue} __ __ ____ ${t.skyBlue} ______ __ ${r}`,
77
134
  `${t.brightBlue} / / / /___ / __/_ ______ ${t.skyBlue}/ ____/___ ____/ /__ ${r}`,
@@ -81,127 +138,150 @@ const LOGO = [
81
138
  `${t.brightBlue} /_/ /____/ ${r}`,
82
139
  ];
83
140
 
84
- // ─── Rocket ASCII Art (16 frames for launch animation) ───
141
+ // --- Rocket ASCII Art (safe characters only) ---
85
142
  const ROCKET_FRAMES = [
86
- // Frame 0: Pre-launch — rocket on pad
143
+ // Frame 0: Pre-launch
87
144
  [
88
- ` ${t.dimGray}│${r}`,
89
- ` ${t.white}╱${t.brightBlue}▲${t.white}╲${r}`,
90
- ` ${t.white}│${t.blue}║║${t.white}│${r}`,
91
- ` ${t.white}│${t.blue}║║${t.white}│${r}`,
92
- ` ${t.white}│${t.brightBlue}║║${t.white}│${r}`,
93
- ` ${t.white}╱${t.blue}║║║${t.white}╲${r}`,
94
- ` ${t.white}╱${t.blue}═════${t.white}╲${r}`,
95
- ` ${t.dimGray}▔▔▔▔▔▔▔▔▔${r}`,
145
+ ` ${t.dimGray}|${r}`,
146
+ ` ${t.white}/${t.brightBlue}^${t.white}\\${r}`,
147
+ ` ${t.white}|${t.blue}||${t.white}|${r}`,
148
+ ` ${t.white}|${t.blue}||${t.white}|${r}`,
149
+ ` ${t.white}|${t.brightBlue}||${t.white}|${r}`,
150
+ ` ${t.white}/${t.blue}|||${t.white}\\${r}`,
151
+ ` ${t.white}/${t.blue}=====${t.white}\\${r}`,
152
+ ` ${t.dimGray}==========${r}`,
96
153
  ],
97
154
  // Frame 1: Ignition
98
155
  [
99
- ` ${t.dimGray}│${r}`,
100
- ` ${t.white}╱${t.brightBlue}▲${t.white}╲${r}`,
101
- ` ${t.white}│${t.blue}║║${t.white}│${r}`,
102
- ` ${t.white}│${t.blue}║║${t.white}│${r}`,
103
- ` ${t.white}│${t.brightBlue}║║${t.white}│${r}`,
104
- ` ${t.white}╱${t.blue}║║║${t.white}╲${r}`,
105
- ` ${t.white}╱${t.blue}═════${t.white}╲${r}`,
106
- ` ${t.orange}▓${t.yellow}░${t.orange}▓${t.yellow}░${t.orange}▓${r}`,
156
+ ` ${t.dimGray}|${r}`,
157
+ ` ${t.white}/${t.brightBlue}^${t.white}\\${r}`,
158
+ ` ${t.white}|${t.blue}||${t.white}|${r}`,
159
+ ` ${t.white}|${t.blue}||${t.white}|${r}`,
160
+ ` ${t.white}|${t.brightBlue}||${t.white}|${r}`,
161
+ ` ${t.white}/${t.blue}|||${t.white}\\${r}`,
162
+ ` ${t.white}/${t.blue}=====${t.white}\\${r}`,
163
+ ` ${t.orange}#${t.yellow}*${t.orange}#${t.yellow}*${t.orange}#${r}`,
107
164
  ],
108
165
  // Frame 2: Lift-off
109
166
  [
110
- ` ${t.white}╱${t.brightBlue}▲${t.white}╲${r}`,
111
- ` ${t.white}│${t.blue}║║${t.white}│${r}`,
112
- ` ${t.white}│${t.blue}║║${t.white}│${r}`,
113
- ` ${t.white}│${t.brightBlue}║║${t.white}│${r}`,
114
- ` ${t.white}╱${t.blue}║║║${t.white}╲${r}`,
115
- ` ${t.white}╱${t.blue}═════${t.white}╲${r}`,
116
- ` ${t.yellow}▓${t.orange}█${t.yellow}▓${t.orange}█${t.yellow}▓${r}`,
117
- ` ${t.orange}░${t.yellow}▒${t.orange}░${r}`,
167
+ ` ${t.white}/${t.brightBlue}^${t.white}\\${r}`,
168
+ ` ${t.white}|${t.blue}||${t.white}|${r}`,
169
+ ` ${t.white}|${t.blue}||${t.white}|${r}`,
170
+ ` ${t.white}|${t.brightBlue}||${t.white}|${r}`,
171
+ ` ${t.white}/${t.blue}|||${t.white}\\${r}`,
172
+ ` ${t.white}/${t.blue}=====${t.white}\\${r}`,
173
+ ` ${t.yellow}#${t.orange}@${t.yellow}#${t.orange}@${t.yellow}#${r}`,
174
+ ` ${t.orange}*${t.yellow}o${t.orange}*${r}`,
118
175
  ],
119
176
  // Frame 3: Ascending
120
177
  [
121
- ` ${t.white}╱${t.brightBlue}▲${t.white}╲${r}`,
122
- ` ${t.white}│${t.blue}║║${t.white}│${r}`,
123
- ` ${t.white}│${t.brightBlue}║║${t.white}│${r}`,
124
- ` ${t.white}╱${t.blue}║║║${t.white}╲${r}`,
125
- ` ${t.white}╱${t.blue}═════${t.white}╲${r}`,
126
- ` ${t.yellow}█${t.orange}▓${t.yellow}█${t.orange}▓${t.yellow}█${r}`,
127
- ` ${t.orange}▓${t.yellow}█${t.orange}▓${r}`,
128
- ` ${t.yellow}░${t.orange}░${r}`,
178
+ ` ${t.white}/${t.brightBlue}^${t.white}\\${r}`,
179
+ ` ${t.white}|${t.blue}||${t.white}|${r}`,
180
+ ` ${t.white}|${t.brightBlue}||${t.white}|${r}`,
181
+ ` ${t.white}/${t.blue}|||${t.white}\\${r}`,
182
+ ` ${t.white}/${t.blue}=====${t.white}\\${r}`,
183
+ ` ${t.yellow}@${t.orange}#${t.yellow}@${t.orange}#${t.yellow}@${r}`,
184
+ ` ${t.orange}#${t.yellow}@${t.orange}#${r}`,
185
+ ` ${t.yellow}*${t.orange}*${r}`,
129
186
  ],
130
187
  // Frame 4: Full thrust
131
188
  [
132
- ` ${t.white}╱${t.brightBlue}▲${t.white}╲${r}`,
133
- ` ${t.white}│${t.blue}║║${t.white}│${r}`,
134
- ` ${t.white}│${t.brightBlue}║║${t.white}│${r}`,
135
- ` ${t.white}╱${t.blue}║║║${t.white}╲${r}`,
136
- ` ${t.white}╱${t.blue}═════${t.white}╲${r}`,
137
- ` ${t.yellow}█${t.orange}▓${t.yellow}█${t.orange}▓${t.yellow}█${t.orange}▓${r}`,
138
- ` ${t.orange}▓${t.yellow}█${t.orange}▓${t.yellow}█${r}`,
139
- ` ${t.yellow}▒${t.orange}░${t.yellow}▒${r}`,
140
- ` ${t.orange}░${t.yellow}░${r}`,
189
+ ` ${t.white}/${t.brightBlue}^${t.white}\\${r}`,
190
+ ` ${t.white}|${t.blue}||${t.white}|${r}`,
191
+ ` ${t.white}|${t.brightBlue}||${t.white}|${r}`,
192
+ ` ${t.white}/${t.blue}|||${t.white}\\${r}`,
193
+ ` ${t.white}/${t.blue}=====${t.white}\\${r}`,
194
+ ` ${t.yellow}@${t.orange}#${t.yellow}@${t.orange}#${t.yellow}@${t.orange}#${r}`,
195
+ ` ${t.orange}#${t.yellow}@${t.orange}#${t.yellow}@${r}`,
196
+ ` ${t.yellow}o${t.orange}*${t.yellow}o${r}`,
197
+ ` ${t.orange}*${t.yellow}*${r}`,
141
198
  ],
142
- // Frame 5: Going up (smaller)
199
+ // Frame 5: Going up
143
200
  [
144
201
  ``,
145
- ` ${t.white}╱${t.brightBlue}▲${t.white}╲${r}`,
146
- ` ${t.white}│${t.blue}║║${t.white}│${r}`,
147
- ` ${t.white}╱${t.blue}║║║${t.white}╲${r}`,
148
- ` ${t.white}╱${t.blue}═════${t.white}╲${r}`,
149
- ` ${t.yellow}█${t.orange}▓${t.yellow}█${t.orange}▓${t.yellow}█${t.orange}▓${r}`,
150
- ` ${t.orange}█${t.yellow}▓${t.orange}█${t.yellow}▓${r}`,
151
- ` ${t.yellow}▒${t.orange}▒${t.yellow}▒${r}`,
152
- ` ${t.orange}░${r} ${t.dimGray}·${r}`,
202
+ ` ${t.white}/${t.brightBlue}^${t.white}\\${r}`,
203
+ ` ${t.white}|${t.blue}||${t.white}|${r}`,
204
+ ` ${t.white}/${t.blue}|||${t.white}\\${r}`,
205
+ ` ${t.white}/${t.blue}=====${t.white}\\${r}`,
206
+ ` ${t.yellow}@${t.orange}#${t.yellow}@${t.orange}#${t.yellow}@${t.orange}#${r}`,
207
+ ` ${t.orange}@${t.yellow}#${t.orange}@${t.yellow}#${r}`,
208
+ ` ${t.yellow}o${t.orange}o${t.yellow}o${r}`,
209
+ ` ${t.orange}*${r} ${t.dimGray}.${r}`,
153
210
  ],
154
211
  // Frame 6: Higher
155
212
  [
156
213
  ``,
157
214
  ``,
158
- ` ${t.skyBlue}╱${t.brightBlue}▲${t.skyBlue}╲${r}`,
159
- ` ${t.skyBlue}│${t.blue}║║${t.skyBlue}│${r}`,
160
- ` ${t.skyBlue}╱${t.blue}══${t.skyBlue}╲${r}`,
161
- ` ${t.yellow}▓${t.orange}█${t.yellow}▓${t.orange}▓${r}`,
162
- ` ${t.orange}▒${t.yellow}▒${r}`,
163
- ` ${t.yellow}░${r}`,
164
- ` ${t.dimGray}·${r} ${t.dimGray}·${r}`,
215
+ ` ${t.skyBlue}/${t.brightBlue}^${t.skyBlue}\\${r}`,
216
+ ` ${t.skyBlue}|${t.blue}||${t.skyBlue}|${r}`,
217
+ ` ${t.skyBlue}/${t.blue}==${t.skyBlue}\\${r}`,
218
+ ` ${t.yellow}#${t.orange}@${t.yellow}#${t.orange}#${r}`,
219
+ ` ${t.orange}o${t.yellow}o${r}`,
220
+ ` ${t.yellow}*${r}`,
221
+ ` ${t.dimGray}.${r} ${t.dimGray}.${r}`,
165
222
  ],
166
- // Frame 7: Almost gone — tiny dot
223
+ // Frame 7: Almost gone
167
224
  [
168
225
  ``,
169
226
  ``,
170
227
  ``,
171
- ` ${t.skyBlue}╱${t.brightBlue}▲${t.skyBlue}╲${r}`,
172
- ` ${t.blue}══${r}`,
173
- ` ${t.orange}▒${t.yellow}▒${r}`,
174
- ` ${t.yellow}░${r}`,
228
+ ` ${t.skyBlue}/${t.brightBlue}^${t.skyBlue}\\${r}`,
229
+ ` ${t.blue}==${r}`,
230
+ ` ${t.orange}o${t.yellow}o${r}`,
231
+ ` ${t.yellow}*${r}`,
175
232
  ``,
176
- ` ${t.dimGray}·${r} ${t.dimGray}·${r} ${t.dimGray}·${r}`,
233
+ ` ${t.dimGray}.${r} ${t.dimGray}.${r} ${t.dimGray}.${r}`,
177
234
  ],
178
235
  // Frame 8: Gone — just stars
179
236
  [
180
237
  ``,
181
238
  ``,
182
239
  ``,
183
- ` ${t.brightBlue}✦${r}`,
240
+ ` ${t.brightBlue}*${r}`,
184
241
  ``,
185
- ` ${t.dimGray}·${r}`,
242
+ ` ${t.dimGray}.${r}`,
186
243
  ``,
187
- ` ${t.dimGray}·${r} ${t.dimGray}·${r}`,
188
- ` ${t.dimGray}·${r} ${t.dimGray}·${r} ${t.dimGray}·${r}`,
244
+ ` ${t.dimGray}.${r} ${t.dimGray}.${r}`,
245
+ ` ${t.dimGray}.${r} ${t.dimGray}.${r} ${t.dimGray}.${r}`,
189
246
  ],
190
247
  ];
191
248
 
192
- // ─── Sleep utility ───
249
+ // --- Sleep utility ---
193
250
  const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
194
251
 
195
- // ─── Clear screen ───
252
+ // --- Clear screen ---
196
253
  const clearScreen = () => process.stdout.write(`${CSI}2J${CSI}H`);
197
254
 
198
- // ─── Move cursor ───
255
+ // --- Move cursor ---
199
256
  const moveTo = (row, col) => process.stdout.write(`${CSI}${row};${col}H`);
200
257
 
201
- // ─── Hide/show cursor ───
258
+ // --- Hide/show cursor ---
202
259
  const hideCursor = () => process.stdout.write(`${CSI}?25l`);
203
260
  const showCursor = () => process.stdout.write(`${CSI}?25h`);
204
261
 
262
+ /**
263
+ * Strip ANSI codes for length calculation
264
+ */
265
+ function stripAnsi(str) {
266
+ return str.replace(/\x1b\[[0-9;]*m/g, '');
267
+ }
268
+
269
+ /**
270
+ * Pad a string (with ANSI codes) to a visual width
271
+ */
272
+ function padAnsi(str, width) {
273
+ const visible = stripAnsi(str).length;
274
+ const pad = Math.max(0, width - visible);
275
+ return str + ' '.repeat(pad);
276
+ }
277
+
278
+ /**
279
+ * Draw a horizontal line
280
+ */
281
+ function hLine(width = 60) {
282
+ return c.dark(ch.h.repeat(width));
283
+ }
284
+
205
285
  /**
206
286
  * Play the rocket launch animation
207
287
  */
@@ -242,7 +322,7 @@ async function playRocketAnimation() {
242
322
  else if (f <= 4) process.stdout.write(c.bright('Ascending...'));
243
323
  else if (f <= 6) process.stdout.write(c.sky('Reaching orbit...'));
244
324
  else if (f === 7) process.stdout.write(c.cyan('Entering space...'));
245
- else process.stdout.write(c.bBright('Ready to code! '));
325
+ else process.stdout.write(c.bBright('Ready to code! *'));
246
326
 
247
327
  await sleep(delays[f] || 200);
248
328
  }
@@ -252,82 +332,72 @@ async function playRocketAnimation() {
252
332
  }
253
333
 
254
334
  /**
255
- * Draw a horizontal line
256
- */
257
- function hLine(char = '─', width = 60) {
258
- return c.dark(char.repeat(width));
259
- }
260
-
261
- /**
262
- * Draw a box with title
335
+ * Draw a box with title (terminal-safe characters)
263
336
  */
264
337
  function box(title, content, width = 58) {
265
338
  const lines = [];
266
339
  const inner = width - 4;
267
340
 
268
- lines.push(` ${t.dimGray}╭─${r} ${c.bBright(title)} ${t.dimGray}${'─'.repeat(Math.max(0, inner - title.length - 1))}╮${r}`);
341
+ lines.push(` ${t.dimGray}${ch.tl}${ch.h}${r} ${c.bBright(title)} ${t.dimGray}${ch.h.repeat(Math.max(0, inner - title.length - 1))}${ch.tr}${r}`);
269
342
 
270
343
  for (const line of content) {
271
- // Pad line — we can't easily measure ANSI length, so just append
272
- lines.push(` ${t.dimGray}│${r} ${line}`);
344
+ lines.push(` ${t.dimGray}${ch.v}${r} ${line}`);
273
345
  }
274
346
 
275
- lines.push(` ${t.dimGray}╰${'─'.repeat(width - 2)}╯${r}`);
347
+ lines.push(` ${t.dimGray}${ch.bl}${ch.h.repeat(width - 2)}${ch.br}${r}`);
276
348
  return lines;
277
349
  }
278
350
 
279
351
  /**
280
- * Show the main welcome screen (like Claude Code's startup)
352
+ * Show the main welcome screen (Claude Code-style layout)
281
353
  */
282
354
  function showWelcomeScreen(version, options = {}) {
283
355
  const cols = process.stdout.columns || 80;
284
356
  const { serverUrl, username, cwd, isConnected } = options;
357
+ const W = Math.min(cols - 4, 72); // content width
358
+ const leftW = 32;
359
+ const rightW = W - leftW - 3; // 3 for separator + padding
285
360
 
286
361
  clearScreen();
287
362
 
288
363
  const lines = [];
289
364
 
290
- // ─── Top border with version ───
365
+ // --- Top border with version ---
291
366
  const verStr = `Upfyn-Code v${version}`;
292
- const topLine = `${t.dimGray}── ${r}${c.bBright(verStr)}${t.dimGray} ${'─'.repeat(Math.max(0, 56 - verStr.length))}${r}`;
367
+ const topPad = Math.max(0, W - verStr.length - 4);
293
368
  lines.push('');
294
- lines.push(` ${topLine}`);
369
+ lines.push(` ${t.dimGray}${ch.h}${ch.h} ${r}${c.bBright(verStr)}${t.dimGray} ${ch.h.repeat(topPad)}${r}`);
295
370
  lines.push('');
296
371
 
297
- // ─── Left panel: Welcome + Mascot ───
298
- // We use a side-by-side layout like Claude Code
372
+ // --- Build left and right panels ---
299
373
  const leftPanel = [];
300
374
  const rightPanel = [];
301
375
 
302
- // Welcome message
376
+ // Left: Welcome + Rocket mascot
303
377
  leftPanel.push(c.bWhite(' Welcome back!'));
304
378
  leftPanel.push('');
305
-
306
- // Rocket mascot (small static version)
307
- leftPanel.push(` ${t.dimGray}│${r}`);
308
- leftPanel.push(` ${t.white}╱${t.brightBlue}▲${t.white}╲${r}`);
309
- leftPanel.push(` ${t.white}│${t.blue}║${t.brightBlue}◈${t.blue}║${t.white}│${r}`);
310
- leftPanel.push(` ${t.white}│${t.blue}║║${t.white}│${r}`);
311
- leftPanel.push(` ${t.white}╱${t.blue}═══${t.white}╲${r}`);
312
- leftPanel.push(` ${t.cyan}▔▔▔▔▔${r}`);
379
+ leftPanel.push(` ${t.dimGray}|${r}`);
380
+ leftPanel.push(` ${t.white}/${t.brightBlue}^${t.white}\\${r}`);
381
+ leftPanel.push(` ${t.white}|${t.blue}|${t.brightBlue}o${t.blue}|${t.white}|${r}`);
382
+ leftPanel.push(` ${t.white}|${t.blue}||${t.white}|${r}`);
383
+ leftPanel.push(` ${t.white}/${t.blue}===${t.white}\\${r}`);
384
+ leftPanel.push(` ${t.cyan}~~~~~${r}`);
313
385
  leftPanel.push('');
314
-
315
- // Info
316
386
  if (username) {
317
387
  leftPanel.push(` ${c.gray('User:')} ${c.bright(username)}`);
318
388
  }
319
389
  leftPanel.push(` ${c.gray('Path:')} ${c.dim(cwd || process.cwd())}`);
320
390
 
321
- // Right panel: Tips + Recent Activity
391
+ // Right: Tips + Recent Activity
322
392
  rightPanel.push(` ${c.bCyan('Tips for getting started')}`);
323
- rightPanel.push(` ${c.gray('Run')} ${c.bright('uc connect')} ${c.gray('to bridge to the web UI')}`);
324
- rightPanel.push(` ${c.gray('Run')} ${c.bright('uc start')} ${c.gray('to launch the local server')}`);
393
+ rightPanel.push(` ${c.gray('Run')} ${c.bright('uc connect')} ${c.gray('to bridge to web UI')}`);
394
+ rightPanel.push(` ${c.gray('Run')} ${c.bright('uc start')} ${c.gray('to launch local server')}`);
325
395
  rightPanel.push(` ${c.gray('Run')} ${c.bright('uc status')} ${c.gray('to see configuration')}`);
326
396
  rightPanel.push(` ${c.gray('Run')} ${c.bright('uc help')} ${c.gray('for all commands')}`);
327
397
  rightPanel.push('');
328
398
  rightPanel.push(` ${c.bCyan('Recent activity')}`);
329
399
  if (isConnected) {
330
- rightPanel.push(` ${c.green('')} Connected to ${c.bright(serverUrl || 'server')}`);
400
+ rightPanel.push(` ${c.green('*')} Connected to ${c.bright(serverUrl || 'server')}`);
331
401
  } else {
332
402
  rightPanel.push(` ${c.dim('No active connection')}`);
333
403
  }
@@ -337,40 +407,38 @@ function showWelcomeScreen(version, options = {}) {
337
407
  }
338
408
 
339
409
  // Merge panels side by side
340
- const leftWidth = 30;
341
410
  const maxRows = Math.max(leftPanel.length, rightPanel.length);
342
411
 
343
412
  for (let i = 0; i < maxRows; i++) {
344
- const left = leftPanel[i] || '';
413
+ const left = padAnsi(leftPanel[i] || '', leftW);
345
414
  const right = rightPanel[i] || '';
346
-
347
- // Separator between panels
348
- if (i === 0) {
349
- lines.push(`${left}${''.padEnd(Math.max(0, leftWidth - stripAnsi(left).length))}${t.dimGray}│${r} ${right}`);
350
- } else {
351
- lines.push(`${left}${''.padEnd(Math.max(0, leftWidth - stripAnsi(left).length))}${t.dimGray}│${r} ${right}`);
352
- }
415
+ lines.push(`${left}${t.dimGray}${ch.v}${r} ${right}`);
353
416
  }
354
417
 
355
418
  lines.push('');
356
419
 
357
- // ─── Bottom border ───
358
- lines.push(` ${t.dimGray}${'─'.repeat(58)}${r}`);
420
+ // --- Bottom border ---
421
+ lines.push(` ${t.dimGray}${ch.h.repeat(W)}${r}`);
359
422
  lines.push('');
360
423
 
361
- // ─── Status bar ───
424
+ // --- Status bar ---
362
425
  const statusItems = [];
363
426
  if (isConnected) {
364
- statusItems.push(`${t.green} Connected${r}`);
427
+ statusItems.push(`${t.green}* Connected${r}`);
365
428
  } else {
366
- statusItems.push(`${t.dimGray} Not connected${r}`);
429
+ statusItems.push(`${t.dimGray}o Not connected${r}`);
367
430
  }
368
431
  statusItems.push(`${t.gray}v${version}${r}`);
369
432
 
370
- lines.push(` ${statusItems.join(` ${t.dimGray}·${r} `)}`);
433
+ lines.push(` ${statusItems.join(` ${t.dimGray}${ch.dot}${r} `)}`);
434
+ lines.push('');
435
+
436
+ // --- Input prompt area (between two lines like Claude Code) ---
437
+ lines.push(` ${t.dimGray}${ch.h.repeat(W)}${r}`);
438
+ lines.push(` ${t.brightBlue}>${r} ${c.dim('Type a command or use /upfynai-connect ...')}`);
439
+ lines.push(` ${t.dimGray}${ch.h.repeat(W)}${r}`);
371
440
  lines.push('');
372
441
 
373
- // Print all at once
374
442
  process.stdout.write(lines.join('\n') + '\n');
375
443
  }
376
444
 
@@ -378,17 +446,19 @@ function showWelcomeScreen(version, options = {}) {
378
446
  * Show connection success banner
379
447
  */
380
448
  function showConnectionBanner(username, serverUrl) {
449
+ const W = 50;
381
450
  const lines = [];
451
+ const bLine = (content) => ` ${t.green}${ch.v}${r}${padAnsi(content, W)}${t.green}${ch.v}${r}`;
382
452
  lines.push('');
383
- lines.push(` ${t.green}╭──────────────────────────────────────────────╮${r}`);
384
- lines.push(` ${t.green}│${r} ${c.green('')} ${c.bWhite('Connected successfully!')} ${t.green}│${r}`);
385
- lines.push(` ${t.green}│${r} ${t.green}│${r}`);
386
- lines.push(` ${t.green}│${r} ${c.gray('User:')} ${c.bright(username || 'Unknown')}${' '.repeat(Math.max(0, 30 - (username || 'Unknown').length))}${t.green}│${r}`);
387
- lines.push(` ${t.green}│${r} ${c.gray('Server:')} ${c.cyan(serverUrl || 'Unknown')}${' '.repeat(Math.max(0, 28 - (serverUrl || 'Unknown').length))}${t.green}│${r}`);
388
- lines.push(` ${t.green}│${r} ${t.green}│${r}`);
389
- lines.push(` ${t.green}│${r} ${c.dim('Your machine is bridged to the web UI.')} ${t.green}│${r}`);
390
- lines.push(` ${t.green}│${r} ${c.dim('Press Ctrl+C to disconnect.')} ${t.green}│${r}`);
391
- lines.push(` ${t.green}╰──────────────────────────────────────────────╯${r}`);
453
+ lines.push(` ${t.green}${ch.tl}${ch.h.repeat(W)}${ch.tr}${r}`);
454
+ lines.push(bLine(` ${c.green('OK')} ${c.bWhite('Connected successfully!')}`));
455
+ lines.push(bLine(''));
456
+ lines.push(bLine(` ${c.gray('User:')} ${c.bright(username || 'Unknown')}`));
457
+ lines.push(bLine(` ${c.gray('Server:')} ${c.cyan(serverUrl || 'Unknown')}`));
458
+ lines.push(bLine(''));
459
+ lines.push(bLine(` ${c.dim('Your machine is bridged to the web UI.')}`));
460
+ lines.push(bLine(` ${c.dim('Press Ctrl+C to disconnect.')}`));
461
+ lines.push(` ${t.green}${ch.bl}${ch.h.repeat(W)}${ch.br}${r}`);
392
462
  lines.push('');
393
463
 
394
464
  process.stdout.write(lines.join('\n') + '\n');
@@ -398,7 +468,7 @@ function showConnectionBanner(username, serverUrl) {
398
468
  * Show a spinner with message
399
469
  */
400
470
  function createSpinner(message) {
401
- const frames = ['', '', '', '', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
471
+ const frames = ['/', '-', '\\', '|'];
402
472
  let i = 0;
403
473
  let interval;
404
474
 
@@ -409,16 +479,16 @@ function createSpinner(message) {
409
479
  const frame = frames[i % frames.length];
410
480
  process.stdout.write(`\r ${t.brightBlue}${frame}${r} ${c.gray(message)}`);
411
481
  i++;
412
- }, 80);
482
+ }, 100);
413
483
  },
414
484
  stop(finalMessage) {
415
485
  clearInterval(interval);
416
- process.stdout.write(`\r ${c.green('')} ${c.white(finalMessage || message)}${''.padEnd(20)}\n`);
486
+ process.stdout.write(`\r ${c.green('OK')} ${c.white(finalMessage || message)}${''.padEnd(20)}\n`);
417
487
  showCursor();
418
488
  },
419
489
  fail(finalMessage) {
420
490
  clearInterval(interval);
421
- process.stdout.write(`\r ${c.red('')} ${c.red(finalMessage || message)}${''.padEnd(20)}\n`);
491
+ process.stdout.write(`\r ${c.red('FAIL')} ${c.red(finalMessage || message)}${''.padEnd(20)}\n`);
422
492
  showCursor();
423
493
  }
424
494
  };
@@ -428,14 +498,17 @@ function createSpinner(message) {
428
498
  * Show styled help
429
499
  */
430
500
  function showStyledHelp(version) {
501
+ const W = 60;
431
502
  const lines = [];
432
503
 
433
504
  lines.push('');
434
- // Header
435
- lines.push(` ${t.brightBlue}╔══════════════════════════════════════════════════════════╗${r}`);
436
- lines.push(` ${t.brightBlue}║${r} ${c.bBright('Upfyn-Code')} ${c.dim('—')} ${c.gray('by Thinqmesh Technologies')} ${t.brightBlue}║${r}`);
437
- lines.push(` ${t.brightBlue}║${r} ${c.dim('Visual AI Coding Interface with Upfyn-Canvas')} ${t.brightBlue}║${r}`);
438
- lines.push(` ${t.brightBlue}╚══════════════════════════════════════════════════════════╝${r}`);
505
+ // Header box — build each line to exactly W visible chars inside the borders
506
+ const hdrLine1 = ` ${c.bBright('Upfyn-Code')} ${c.dim('--')} ${c.gray('by Thinqmesh Technologies')}`;
507
+ const hdrLine2 = ` ${c.dim('Visual AI Coding Interface with Upfyn-Canvas')}`;
508
+ lines.push(` ${t.brightBlue}${ch.tl}${ch.h.repeat(W)}${ch.tr}${r}`);
509
+ lines.push(` ${t.brightBlue}${ch.v}${r}${padAnsi(hdrLine1, W)}${t.brightBlue}${ch.v}${r}`);
510
+ lines.push(` ${t.brightBlue}${ch.v}${r}${padAnsi(hdrLine2, W)}${t.brightBlue}${ch.v}${r}`);
511
+ lines.push(` ${t.brightBlue}${ch.bl}${ch.h.repeat(W)}${ch.br}${r}`);
439
512
  lines.push('');
440
513
 
441
514
  // Usage
@@ -446,8 +519,10 @@ function showStyledHelp(version) {
446
519
  // Commands
447
520
  lines.push(` ${c.bBright('Commands:')}`);
448
521
  const cmds = [
449
- ['start', 'Start the local server (default)'],
522
+ ['(default)', 'Launch Claude Code with Upfyn branding'],
523
+ ['start', 'Start the local server only'],
450
524
  ['connect', 'Connect local machine to hosted server'],
525
+ ['config', 'View/set configuration (API key, etc.)'],
451
526
  ['status', 'Show configuration and data locations'],
452
527
  ['install-commands', 'Install /upfynai-* slash commands'],
453
528
  ['uninstall-commands', 'Remove /upfynai-* slash commands'],
@@ -461,7 +536,7 @@ function showStyledHelp(version) {
461
536
  lines.push('');
462
537
 
463
538
  // Slash Commands
464
- lines.push(` ${c.bBright('Slash Commands')} ${c.dim('(inside your AI CLI):')}`);
539
+ lines.push(` ${c.bBright('Slash Commands')} ${c.dim('(inside Claude Code CLI):')}`);
465
540
  const slashCmds = [
466
541
  ['/upfynai', 'Start the web UI server'],
467
542
  ['/upfynai-connect', 'Connect CLI session to web UI'],
@@ -480,6 +555,7 @@ function showStyledHelp(version) {
480
555
  ['-p, --port <port>', 'Set server port (default: 3001)'],
481
556
  ['--server <url>', 'Server URL for connect'],
482
557
  ['--key <token>', 'Relay token for connect'],
558
+ ['--api-key <key>', 'Set Anthropic API key'],
483
559
  ['--database-path <path>', 'Custom database location'],
484
560
  ['-h, --help', 'Show help'],
485
561
  ['-v, --version', 'Show version'],
@@ -491,9 +567,10 @@ function showStyledHelp(version) {
491
567
 
492
568
  // Examples
493
569
  lines.push(` ${c.bBright('Examples:')}`);
494
- lines.push(` ${c.dim('$')} ${c.bright('uc')} ${c.dim('# Start with defaults')}`);
495
- lines.push(` ${c.dim('$')} ${c.bright('uc --port 8080')} ${c.dim('# Start on port 8080')}`);
570
+ lines.push(` ${c.dim('$')} ${c.bright('uc')} ${c.dim('# Launch Claude Code')}`);
496
571
  lines.push(` ${c.dim('$')} ${c.bright('uc connect --key upfyn_xxx')} ${c.dim('# Bridge to hosted server')}`);
572
+ lines.push(` ${c.dim('$')} ${c.bright('uc config --api-key sk-ant-xxx')} ${c.dim('# Save Anthropic API key')}`);
573
+ lines.push(` ${c.dim('$')} ${c.bright('uc start --port 8080')} ${c.dim('# Start on port 8080')}`);
497
574
  lines.push(` ${c.dim('$')} ${c.bright('uc status')} ${c.dim('# Show configuration')}`);
498
575
  lines.push('');
499
576
 
@@ -512,8 +589,8 @@ function showStyledStatus(info) {
512
589
  const lines = [];
513
590
 
514
591
  lines.push('');
515
- lines.push(` ${c.bBright('Upfyn-Code')} ${c.dim(' Status')}`);
516
- lines.push(` ${c.dark(''.repeat(50))}`);
592
+ lines.push(` ${c.bBright('Upfyn-Code')} ${c.dim('-- Status')}`);
593
+ lines.push(` ${c.dark('='.repeat(50))}`);
517
594
  lines.push('');
518
595
 
519
596
  // Version
@@ -524,9 +601,9 @@ function showStyledStatus(info) {
524
601
  lines.push(` ${c.bright('Install')} ${c.dim(info.installDir)}`);
525
602
  lines.push(` ${c.bright('Database')} ${c.dim(info.dbPath)}`);
526
603
  if (info.dbExists) {
527
- lines.push(` ${c.green('')} ${c.gray(`Exists (${info.dbSize})`)}`);
604
+ lines.push(` ${c.green('OK')} ${c.gray(`Exists (${info.dbSize})`)}`);
528
605
  } else {
529
- lines.push(` ${c.yellow('')} ${c.gray('Not created yet')}`);
606
+ lines.push(` ${c.yellow('--')} ${c.gray('Not created yet')}`);
530
607
  }
531
608
  lines.push('');
532
609
 
@@ -535,15 +612,18 @@ function showStyledStatus(info) {
535
612
  lines.push(` ${c.gray('PORT')} ${c.cyan(info.port)} ${info.portDefault ? c.dim('(default)') : ''}`);
536
613
  lines.push(` ${c.gray('DATABASE')} ${c.dim(info.dbPath)}`);
537
614
  lines.push(` ${c.gray('CLI')} ${c.dim(info.claudeCli || 'claude (default)')}`);
615
+ if (info.apiKey) {
616
+ lines.push(` ${c.gray('API KEY')} ${c.green('Set')} ${c.dim('(sk-ant-...' + info.apiKey.slice(-4) + ')')}`);
617
+ }
538
618
  lines.push('');
539
619
 
540
620
  // Bottom
541
- lines.push(` ${c.dark(''.repeat(50))}`);
621
+ lines.push(` ${c.dark('='.repeat(50))}`);
542
622
  lines.push('');
543
623
  lines.push(` ${c.bCyan('Tips:')}`);
544
- lines.push(` ${c.dim('>')} Use ${c.bright('uc --port 8080')} to run on a custom port`);
624
+ lines.push(` ${c.dim('>')} Use ${c.bright('uc')} to launch Claude Code with Upfyn branding`);
545
625
  lines.push(` ${c.dim('>')} Use ${c.bright('uc connect --key upfyn_xxx')} to bridge to web UI`);
546
- lines.push(` ${c.dim('>')} Access the UI at ${c.cyan(`http://localhost:${info.port}`)}`);
626
+ lines.push(` ${c.dim('>')} Use ${c.bright('uc config --api-key sk-ant-xxx')} to save your API key`);
547
627
  lines.push('');
548
628
 
549
629
  process.stdout.write(lines.join('\n') + '\n');
@@ -558,11 +638,11 @@ function showServerBanner(port, version) {
558
638
  lines.push('');
559
639
  for (const line of LOGO) lines.push(` ${line}`);
560
640
  lines.push('');
561
- lines.push(` ${c.dim('v' + version)} ${c.dim('')} ${c.gray('by Thinqmesh Technologies')}`);
641
+ lines.push(` ${c.dim('v' + version)} ${c.dim('--')} ${c.gray('by Thinqmesh Technologies')}`);
562
642
  lines.push('');
563
- lines.push(` ${c.dark('─'.repeat(52))}`);
643
+ lines.push(` ${c.dark(ch.h.repeat(52))}`);
564
644
  lines.push('');
565
- lines.push(` ${c.green('')} ${c.bWhite('Server running')} ${c.gray('on')} ${c.bCyan(`http://localhost:${port}`)}`);
645
+ lines.push(` ${c.green('*')} ${c.bWhite('Server running')} ${c.gray('on')} ${c.bCyan(`http://localhost:${port}`)}`);
566
646
  lines.push('');
567
647
  lines.push(` ${c.dim('Press Ctrl+C to stop.')}`);
568
648
  lines.push('');
@@ -583,9 +663,9 @@ async function showConnectStartup(serverUrl, machine, user, version) {
583
663
  lines.push('');
584
664
  for (const line of LOGO) lines.push(` ${line}`);
585
665
  lines.push('');
586
- lines.push(` ${c.dim('v' + version)} ${c.dim('')} ${c.gray('Relay Client')}`);
666
+ lines.push(` ${c.dim('v' + version)} ${c.dim('--')} ${c.gray('Relay Client')}`);
587
667
  lines.push('');
588
- lines.push(` ${c.dark('─'.repeat(52))}`);
668
+ lines.push(` ${c.dark(ch.h.repeat(52))}`);
589
669
  lines.push('');
590
670
  lines.push(` ${c.gray('Server:')} ${c.cyan(serverUrl)}`);
591
671
  lines.push(` ${c.gray('Machine:')} ${c.dim(machine)}`);
@@ -595,6 +675,79 @@ async function showConnectStartup(serverUrl, machine, user, version) {
595
675
  process.stdout.write(lines.join('\n') + '\n');
596
676
  }
597
677
 
678
+ /**
679
+ * Show interactive launch screen (before spawning Claude Code)
680
+ */
681
+ function showLaunchScreen(version, claudeBin, options = {}) {
682
+ const cols = process.stdout.columns || 80;
683
+ const W = Math.min(cols - 4, 72);
684
+
685
+ const lines = [];
686
+
687
+ lines.push('');
688
+ for (const line of LOGO) lines.push(` ${line}`);
689
+ lines.push('');
690
+ lines.push(` ${c.dim('v' + version)} ${c.dim('--')} ${c.gray('by Thinqmesh Technologies')}`);
691
+ lines.push('');
692
+ lines.push(` ${c.dark(ch.h.repeat(W))}`);
693
+ lines.push('');
694
+
695
+ if (claudeBin) {
696
+ lines.push(` ${c.green('*')} ${c.bWhite('Claude Code detected:')} ${c.dim(claudeBin)}`);
697
+ }
698
+ if (options.relayStatus) {
699
+ lines.push(` ${c.green('*')} ${c.bWhite('Relay:')} ${c.dim(options.relayStatus)}`);
700
+ }
701
+ if (options.apiKey) {
702
+ lines.push(` ${c.green('*')} ${c.bWhite('API Key:')} ${c.dim('Configured')}`);
703
+ }
704
+ lines.push('');
705
+
706
+ if (claudeBin) {
707
+ lines.push(` ${c.dim('Launching Claude Code...')}`);
708
+ lines.push(` ${c.dim('Slash commands available: /upfynai-connect, /upfynai-status')}`);
709
+ } else {
710
+ lines.push(` ${c.yellow('!')} ${c.white('Claude Code CLI not found on PATH')}`);
711
+ lines.push('');
712
+ lines.push(` ${c.gray('Install Claude Code:')}`);
713
+ lines.push(` ${c.bright('npm install -g @anthropic-ai/claude-code')}`);
714
+ lines.push('');
715
+ lines.push(` ${c.gray('Or start the server instead:')}`);
716
+ lines.push(` ${c.bright('uc start')}`);
717
+ }
718
+ lines.push('');
719
+ lines.push(` ${c.dark(ch.h.repeat(W))}`);
720
+ lines.push('');
721
+
722
+ process.stdout.write(lines.join('\n') + '\n');
723
+ }
724
+
725
+ /**
726
+ * Show config display
727
+ */
728
+ function showConfig(config) {
729
+ const lines = [];
730
+ lines.push('');
731
+ lines.push(` ${c.bBright('Upfyn-Code')} ${c.dim('-- Configuration')}`);
732
+ lines.push(` ${c.dark('='.repeat(50))}`);
733
+ lines.push('');
734
+
735
+ lines.push(` ${c.gray('Server')} ${config.server ? c.cyan(config.server) : c.dim('(not set)')}`);
736
+ lines.push(` ${c.gray('Relay Key')} ${config.relayKey ? c.green('Set') + ' ' + c.dim('(' + config.relayKey.slice(0, 10) + '...)') : c.dim('(not set)')}`);
737
+ lines.push(` ${c.gray('API Key')} ${config.anthropicApiKey ? c.green('Set') + ' ' + c.dim('(sk-ant-...' + config.anthropicApiKey.slice(-4) + ')') : c.dim('(not set)')}`);
738
+ lines.push('');
739
+ lines.push(` ${c.gray('Config file:')} ${c.dim(config._path || '~/.upfynai/config.json')}`);
740
+ lines.push('');
741
+
742
+ lines.push(` ${c.bCyan('Set values:')}`);
743
+ lines.push(` ${c.dim('$')} ${c.bright('uc config --api-key sk-ant-xxx')} ${c.dim('# Save Anthropic API key')}`);
744
+ lines.push(` ${c.dim('$')} ${c.bright('uc config --clear-api-key')} ${c.dim('# Remove API key')}`);
745
+ lines.push(` ${c.dim('$')} ${c.bright('uc connect --server <url> --key <token>')}`);
746
+ lines.push('');
747
+
748
+ process.stdout.write(lines.join('\n') + '\n');
749
+ }
750
+
598
751
  /**
599
752
  * Log a relay event with icon
600
753
  */
@@ -604,17 +757,12 @@ function logRelayEvent(icon, message, color = 'gray') {
604
757
  console.log(` ${c.dim(timestamp)} ${icon} ${colorFn(message)}`);
605
758
  }
606
759
 
607
- /**
608
- * Strip ANSI codes for length calculation
609
- */
610
- function stripAnsi(str) {
611
- return str.replace(/\x1b\[[0-9;]*m/g, '');
612
- }
613
-
614
760
  export {
615
761
  theme,
616
762
  c,
763
+ ch,
617
764
  LOGO,
765
+ detectTerminalSupport,
618
766
  playRocketAnimation,
619
767
  showWelcomeScreen,
620
768
  showConnectionBanner,
@@ -622,6 +770,8 @@ export {
622
770
  showStyledStatus,
623
771
  showServerBanner,
624
772
  showConnectStartup,
773
+ showLaunchScreen,
774
+ showConfig,
625
775
  logRelayEvent,
626
776
  createSpinner,
627
777
  hideCursor,
@@ -631,4 +781,5 @@ export {
631
781
  hLine,
632
782
  box,
633
783
  stripAnsi,
784
+ padAnsi,
634
785
  };