cli-jaw 1.5.0 → 1.6.2

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 (230) hide show
  1. package/README.ko.md +26 -19
  2. package/README.md +50 -22
  3. package/README.zh-CN.md +26 -19
  4. package/dist/bin/cli-jaw.js +5 -1
  5. package/dist/bin/cli-jaw.js.map +1 -1
  6. package/dist/bin/commands/dispatch.js +57 -0
  7. package/dist/bin/commands/dispatch.js.map +1 -0
  8. package/dist/lib/mcp-sync.js +1 -1
  9. package/dist/lib/mcp-sync.js.map +1 -1
  10. package/dist/lib/upload.js +14 -1
  11. package/dist/lib/upload.js.map +1 -1
  12. package/dist/server.js +57 -42
  13. package/dist/server.js.map +1 -1
  14. package/dist/src/agent/events.js +72 -13
  15. package/dist/src/agent/events.js.map +1 -1
  16. package/dist/src/agent/spawn.js +71 -29
  17. package/dist/src/agent/spawn.js.map +1 -1
  18. package/dist/src/cli/acp-client.js +4 -2
  19. package/dist/src/cli/acp-client.js.map +1 -1
  20. package/dist/src/cli/commands.js +6 -6
  21. package/dist/src/cli/commands.js.map +1 -1
  22. package/dist/src/cli/handlers.js +11 -9
  23. package/dist/src/cli/handlers.js.map +1 -1
  24. package/dist/src/cli/registry.js +7 -1
  25. package/dist/src/cli/registry.js.map +1 -1
  26. package/dist/src/core/config.js +15 -8
  27. package/dist/src/core/config.js.map +1 -1
  28. package/dist/src/core/db.js +18 -3
  29. package/dist/src/core/db.js.map +1 -1
  30. package/dist/src/core/employees.js +34 -0
  31. package/dist/src/core/employees.js.map +1 -0
  32. package/dist/src/discord/bot.js +72 -9
  33. package/dist/src/discord/bot.js.map +1 -1
  34. package/dist/src/discord/commands.js +12 -8
  35. package/dist/src/discord/commands.js.map +1 -1
  36. package/dist/src/orchestrator/distribute.js +57 -35
  37. package/dist/src/orchestrator/distribute.js.map +1 -1
  38. package/dist/src/orchestrator/gateway.js +3 -1
  39. package/dist/src/orchestrator/gateway.js.map +1 -1
  40. package/dist/src/orchestrator/pipeline.js +120 -27
  41. package/dist/src/orchestrator/pipeline.js.map +1 -1
  42. package/dist/src/orchestrator/research.js +5 -5
  43. package/dist/src/orchestrator/research.js.map +1 -1
  44. package/dist/src/orchestrator/scope.js +55 -0
  45. package/dist/src/orchestrator/scope.js.map +1 -0
  46. package/dist/src/orchestrator/state-machine.js +23 -21
  47. package/dist/src/orchestrator/state-machine.js.map +1 -1
  48. package/dist/src/orchestrator/worker-registry.js +9 -2
  49. package/dist/src/orchestrator/worker-registry.js.map +1 -1
  50. package/dist/src/prompt/builder.js +76 -37
  51. package/dist/src/prompt/builder.js.map +1 -1
  52. package/dist/src/prompt/templates/a1-system.md +40 -0
  53. package/dist/src/prompt/templates/employee.md +12 -4
  54. package/dist/src/prompt/templates/orchestration.md +17 -1
  55. package/dist/src/telegram/bot.js +7 -1
  56. package/dist/src/telegram/bot.js.map +1 -1
  57. package/package.json +4 -2
  58. package/public/assets/fonts/GeistVF.woff2 +0 -0
  59. package/public/assets/fonts/JetBrainsMono-Variable.woff2 +0 -0
  60. package/public/assets/providers/claude-color.svg +1 -0
  61. package/public/assets/providers/claude.svg +1 -0
  62. package/public/assets/providers/copilot-color.svg +1 -0
  63. package/public/assets/providers/copilot.svg +1 -0
  64. package/public/assets/providers/discord.svg +1 -0
  65. package/public/assets/providers/gemini-color.svg +1 -0
  66. package/public/assets/providers/gemini.svg +1 -0
  67. package/public/assets/providers/openai.svg +1 -0
  68. package/public/assets/providers/opencode.svg +1 -0
  69. package/public/assets/providers/telegram.svg +1 -0
  70. package/public/css/chat.css +79 -51
  71. package/public/css/diagram.css +348 -0
  72. package/public/css/layout.css +38 -11
  73. package/public/css/markdown.css +64 -18
  74. package/public/css/modals.css +3 -3
  75. package/public/css/orc-state.css +9 -9
  76. package/public/css/sidebar.css +37 -3
  77. package/public/css/tool-ui.css +46 -11
  78. package/public/css/variables.css +63 -63
  79. package/public/dist/assets/api-Bbmmo0o1.js +1 -0
  80. package/public/dist/assets/architecture-YZFGNWBL-BxiHqtOH.js +1 -0
  81. package/public/dist/assets/architectureDiagram-Q4EWVU46-CJTGDw5p.js +1 -0
  82. package/public/dist/assets/blockDiagram-DXYQGD6D-Btl5Y-Er.js +1 -0
  83. package/public/dist/assets/c4Diagram-AHTNJAMY-sDfjW4ZF.js +1 -0
  84. package/public/dist/assets/classDiagram-6PBFFD2Q-ByCgggL2.js +1 -0
  85. package/public/dist/assets/classDiagram-v2-HSJHXN6E-t1amaXkU.js +1 -0
  86. package/public/dist/assets/constants-C8_0OtK2.js +1 -0
  87. package/public/dist/assets/cose-bilkent-S5V4N54A-eOSFGdaE.js +1 -0
  88. package/public/dist/assets/dagre-KV5264BT-OCB5j8Q6.js +1 -0
  89. package/public/dist/assets/diagram-5BDNPKRD-C-4fgK5P.js +1 -0
  90. package/public/dist/assets/diagram-G4DWMVQ6-C6vmHKDV.js +1 -0
  91. package/public/dist/assets/diagram-MMDJMWI5-BMCo_wmt.js +1 -0
  92. package/public/dist/assets/diagram-TYMM5635-DaCLdttJ.js +1 -0
  93. package/public/dist/assets/employees-D5n7mX5v.js +39 -0
  94. package/public/dist/assets/erDiagram-SMLLAGMA-BiTRdm5s.js +1 -0
  95. package/public/dist/assets/flowDiagram-DWJPFMVM-sv6jVi0d.js +1 -0
  96. package/public/dist/assets/ganttDiagram-T4ZO3ILL-jCP3OW23.js +1 -0
  97. package/public/dist/assets/gitGraph-7Q5UKJZL-BIeKLxpm.js +1 -0
  98. package/public/dist/assets/gitGraphDiagram-UUTBAWPF-CokylnbW.js +1 -0
  99. package/public/dist/assets/index-CLd0BsAu.js +49 -0
  100. package/public/dist/assets/index-D6ci1wCN.css +1 -0
  101. package/public/dist/assets/info-OMHHGYJF-CIEl5dWF.js +1 -0
  102. package/public/dist/assets/infoDiagram-42DDH7IO-BBnK1Bh2.js +1 -0
  103. package/public/dist/assets/ishikawaDiagram-UXIWVN3A-qXIz0VhS.js +1 -0
  104. package/public/dist/assets/journeyDiagram-VCZTEJTY-BdrOLof3.js +1 -0
  105. package/public/dist/assets/kanban-definition-6JOO6SKY-CcX7CE04.js +1 -0
  106. package/public/dist/assets/mermaid.core-BoxIvw7E.js +1 -0
  107. package/public/dist/assets/mindmap-definition-QFDTVHPH-CdXARskX.js +1 -0
  108. package/public/dist/assets/packet-4T2RLAQJ-Bz4ZwYZ3.js +1 -0
  109. package/public/dist/assets/pie-ZZUOXDRM-AtGL3wZ2.js +1 -0
  110. package/public/dist/assets/pieDiagram-DEJITSTG-B5SOq9Yr.js +1 -0
  111. package/public/dist/assets/quadrantDiagram-34T5L4WZ-D0uNWgpI.js +1 -0
  112. package/public/dist/assets/radar-PYXPWWZC-AbJSfeqB.js +1 -0
  113. package/public/dist/assets/render-C8N0rp4L.js +25 -0
  114. package/public/dist/assets/requirementDiagram-MS252O5E-62td6IQ-.js +1 -0
  115. package/public/dist/assets/sankeyDiagram-XADWPNL6-Crg_02iC.js +1 -0
  116. package/public/dist/assets/sequenceDiagram-FGHM5R23-fbCocZHj.js +1 -0
  117. package/public/dist/assets/settings-BJR-IGM5.js +41 -0
  118. package/public/dist/assets/settings-Dm6OnPmY.js +1 -0
  119. package/public/dist/assets/skills-D6jv9AIs.js +1 -0
  120. package/public/dist/assets/skills-uywskdFh.js +12 -0
  121. package/public/dist/assets/slash-commands-CCDonT40.js +1 -0
  122. package/public/dist/assets/{slash-commands-DMsE88bu.js → slash-commands-DWvL-VDU.js} +1 -1
  123. package/public/dist/assets/stateDiagram-FHFEXIEX-Gy3DhXxQ.js +1 -0
  124. package/public/dist/assets/stateDiagram-v2-QKLJ7IA2-DJc-FW9O.js +1 -0
  125. package/public/dist/assets/timeline-definition-GMOUNBTQ-B3cA9DgY.js +1 -0
  126. package/public/dist/assets/treeView-SZITEDCU-Cn58DIbB.js +1 -0
  127. package/public/dist/assets/treemap-W4RFUUIX-DPcYjDzH.js +1 -0
  128. package/public/dist/assets/ui-C1daR00l.js +1 -0
  129. package/public/dist/assets/ui-D-oFkXed.js +131 -0
  130. package/public/dist/assets/vendor-icons-1Ec7ZWcT.js +1 -0
  131. package/public/dist/assets/{vendor-mermaid-COidH9HB.js → vendor-mermaid-lvHqQdfg.js} +4 -4
  132. package/public/dist/assets/vennDiagram-DHZGUBPP-DkBfxilo.js +1 -0
  133. package/public/dist/assets/wardley-RL74JXVD-6Dg0PJ4H.js +1 -0
  134. package/public/dist/assets/wardleyDiagram-NUSXRM2D-Dsntl_vy.js +1 -0
  135. package/public/dist/assets/ws-Dnn8HG8B.js +2 -0
  136. package/public/dist/assets/xychartDiagram-5P7HB3ND-B_McB5GE.js +1 -0
  137. package/public/dist/index.html +63 -55
  138. package/public/index.html +62 -53
  139. package/public/js/constants.ts +8 -2
  140. package/public/js/diagram/iframe-renderer.ts +559 -0
  141. package/public/js/diagram/types.ts +129 -0
  142. package/public/js/diagram/widget-validator.ts +82 -0
  143. package/public/js/features/chat.ts +24 -12
  144. package/public/js/features/employees.ts +3 -2
  145. package/public/js/features/heartbeat.ts +4 -3
  146. package/public/js/features/memory.ts +4 -3
  147. package/public/js/features/process-block.ts +12 -11
  148. package/public/js/features/settings-cli-status.ts +10 -7
  149. package/public/js/features/settings-core.ts +13 -3
  150. package/public/js/features/settings-discord.ts +9 -0
  151. package/public/js/features/settings-mcp.ts +8 -7
  152. package/public/js/features/settings-stt.ts +4 -4
  153. package/public/js/features/settings-templates.ts +10 -8
  154. package/public/js/features/settings-types.ts +1 -1
  155. package/public/js/features/settings.ts +1 -1
  156. package/public/js/features/sidebar.ts +37 -7
  157. package/public/js/features/skills.ts +4 -3
  158. package/public/js/features/theme.ts +4 -0
  159. package/public/js/features/tool-ui.ts +6 -5
  160. package/public/js/features/voice-recorder.ts +2 -1
  161. package/public/js/icons.ts +257 -0
  162. package/public/js/main.ts +9 -3
  163. package/public/js/provider-icons.ts +88 -0
  164. package/public/js/render.ts +493 -30
  165. package/public/js/streaming-render.ts +3 -3
  166. package/public/js/ui.ts +38 -18
  167. package/public/js/ws.ts +17 -10
  168. package/public/locales/en.json +89 -88
  169. package/public/locales/ko.json +89 -88
  170. package/scripts/release-1.6.0.sh +69 -0
  171. package/scripts/release-preview.sh +1 -1
  172. package/dist/lib/token-keepalive.js +0 -34
  173. package/dist/lib/token-keepalive.js.map +0 -1
  174. package/public/dist/assets/api-BlPw3bUI.js +0 -1
  175. package/public/dist/assets/architecture-YZFGNWBL-BkS7SZQi.js +0 -1
  176. package/public/dist/assets/architectureDiagram-Q4EWVU46-Dl3iBKeR.js +0 -1
  177. package/public/dist/assets/blockDiagram-DXYQGD6D-DPr4D17p.js +0 -1
  178. package/public/dist/assets/c4Diagram-AHTNJAMY-CfoxJtDk.js +0 -1
  179. package/public/dist/assets/classDiagram-6PBFFD2Q-BOAdHnnB.js +0 -1
  180. package/public/dist/assets/classDiagram-v2-HSJHXN6E-B2QCJXWC.js +0 -1
  181. package/public/dist/assets/constants-DshMUJbo.js +0 -1
  182. package/public/dist/assets/cose-bilkent-S5V4N54A-CA14jk7w.js +0 -1
  183. package/public/dist/assets/dagre-KV5264BT-BVwNaSwC.js +0 -1
  184. package/public/dist/assets/diagram-5BDNPKRD-CHqIAvdc.js +0 -1
  185. package/public/dist/assets/diagram-G4DWMVQ6-DxvsCvTP.js +0 -1
  186. package/public/dist/assets/diagram-MMDJMWI5-DqOPO7dl.js +0 -1
  187. package/public/dist/assets/diagram-TYMM5635-C9xMWPQn.js +0 -1
  188. package/public/dist/assets/employees-CFRlsbHm.js +0 -39
  189. package/public/dist/assets/erDiagram-SMLLAGMA-BTmpfRkm.js +0 -1
  190. package/public/dist/assets/flowDiagram-DWJPFMVM-DZBSQAKA.js +0 -1
  191. package/public/dist/assets/ganttDiagram-T4ZO3ILL-TAEMCRbT.js +0 -1
  192. package/public/dist/assets/gitGraph-7Q5UKJZL-bbxndRNA.js +0 -1
  193. package/public/dist/assets/gitGraphDiagram-UUTBAWPF-CBxtx0MB.js +0 -1
  194. package/public/dist/assets/index-1Gg-6jeC.css +0 -1
  195. package/public/dist/assets/index-CQHqXjrK.js +0 -49
  196. package/public/dist/assets/info-OMHHGYJF-DRXq_Ywr.js +0 -1
  197. package/public/dist/assets/infoDiagram-42DDH7IO-C7FFwX4m.js +0 -1
  198. package/public/dist/assets/ishikawaDiagram-UXIWVN3A-DEZJE79j.js +0 -1
  199. package/public/dist/assets/journeyDiagram-VCZTEJTY-ZHLiY6GV.js +0 -1
  200. package/public/dist/assets/kanban-definition-6JOO6SKY-NP7pY7ML.js +0 -1
  201. package/public/dist/assets/mermaid.core-CHuluSlD.js +0 -1
  202. package/public/dist/assets/mindmap-definition-QFDTVHPH-Bd1OgfN_.js +0 -1
  203. package/public/dist/assets/packet-4T2RLAQJ-CGu8ST97.js +0 -1
  204. package/public/dist/assets/pie-ZZUOXDRM-CDG65mhS.js +0 -1
  205. package/public/dist/assets/pieDiagram-DEJITSTG-BbMBHLDM.js +0 -1
  206. package/public/dist/assets/quadrantDiagram-34T5L4WZ-CDCDgI1e.js +0 -1
  207. package/public/dist/assets/radar-PYXPWWZC-1gS-6ylu.js +0 -1
  208. package/public/dist/assets/render-YHvL5VM_.js +0 -6
  209. package/public/dist/assets/requirementDiagram-MS252O5E-DkmvOtYz.js +0 -1
  210. package/public/dist/assets/sankeyDiagram-XADWPNL6-GNcXIYsL.js +0 -1
  211. package/public/dist/assets/sequenceDiagram-FGHM5R23-BFrw2n6x.js +0 -1
  212. package/public/dist/assets/settings-BQF_u5px.js +0 -37
  213. package/public/dist/assets/settings-C5Q9IPjJ.js +0 -1
  214. package/public/dist/assets/skills-9Pd2rOw-.js +0 -1
  215. package/public/dist/assets/skills-BI7sQAdk.js +0 -12
  216. package/public/dist/assets/slash-commands-FMpJzlDB.js +0 -1
  217. package/public/dist/assets/stateDiagram-FHFEXIEX-BLgKnllT.js +0 -1
  218. package/public/dist/assets/stateDiagram-v2-QKLJ7IA2-CbzuWsSa.js +0 -1
  219. package/public/dist/assets/timeline-definition-GMOUNBTQ-CcZTJ3gL.js +0 -1
  220. package/public/dist/assets/treeView-SZITEDCU-BDqBsaH1.js +0 -1
  221. package/public/dist/assets/treemap-W4RFUUIX-BaWHA3Di.js +0 -1
  222. package/public/dist/assets/ui-BukgLHuh.js +0 -29
  223. package/public/dist/assets/ui-Bvz1JfTE.js +0 -1
  224. package/public/dist/assets/vennDiagram-DHZGUBPP-BWV_j1bh.js +0 -1
  225. package/public/dist/assets/wardley-RL74JXVD-C7gKA3d7.js +0 -1
  226. package/public/dist/assets/wardleyDiagram-NUSXRM2D-BUoq_wug.js +0 -1
  227. package/public/dist/assets/ws-S_AZgx7L.js +0 -2
  228. package/public/dist/assets/xychartDiagram-5P7HB3ND-PvT5Ec82.js +0 -1
  229. /package/public/dist/assets/{api-B8XKQ4OT.js → api-Ci-lgwRp.js} +0 -0
  230. /package/public/dist/assets/{locale-BjoAcbis.js → locale-DIXc-_34.js} +0 -0
package/public/js/ui.ts CHANGED
@@ -7,7 +7,10 @@ import { api } from './api.js';
7
7
  import { cacheMessages, getCachedMessages, appendCachedMessage } from './features/idb-cache.js';
8
8
  import { getVirtualScroll, VS_THRESHOLD } from './virtual-scroll.js';
9
9
  import { createStreamRenderer, appendChunk, finalizeStream, type StreamState } from './streaming-render.js';
10
+ import { activateWidgets } from './diagram/iframe-renderer.js';
10
11
  import { renderLiveToolActivity, cleanupToolElements, bindToolItemInteractions, type ToolLogEntry } from './features/tool-ui.js';
12
+ import { ICONS, emojiToIcon, emojiToStatus, isCompletionEmoji } from './icons.js';
13
+ import { providerIcon } from './provider-icons.js';
11
14
  import {
12
15
  createProcessBlock,
13
16
  addStep,
@@ -18,7 +21,7 @@ import {
18
21
  bindProcessBlockInteractions,
19
22
  type ProcessStep,
20
23
  } from './features/process-block.js';
21
- interface MessageItem { role: string; content: string; tool_log?: string | null; }
24
+ interface MessageItem { role: string; content: string; tool_log?: string | null; cli?: string | null; }
22
25
 
23
26
  function parseToolLog(toolLog?: string | null): ToolLogEntry[] {
24
27
  if (!toolLog) return [];
@@ -30,13 +33,19 @@ function parseToolLog(toolLog?: string | null): ToolLogEntry[] {
30
33
  }
31
34
  }
32
35
 
36
+ function getAgentIcon(_cli?: string | null): string {
37
+ // Chat mascot is always the shark — provider icons are for header/sidebar only
38
+ return ICONS.shark;
39
+ }
40
+
33
41
  function toProcessSteps(tools: ToolLogEntry[]): ProcessStep[] {
34
42
  return tools.map((tool: any) => ({
35
43
  id: crypto.randomUUID(),
36
- icon: tool.icon || '🔧',
44
+ icon: tool.icon ? emojiToIcon(tool.icon) : ICONS.tool,
37
45
  label: tool.label || tool.name || 'tool',
38
46
  type: tool.toolType || 'tool',
39
47
  detail: tool.detail || '',
48
+ stepRef: tool.stepRef || '',
40
49
  status: tool.status || 'done',
41
50
  startTime: Date.now(),
42
51
  }));
@@ -49,11 +58,11 @@ export function setStatus(s: string): void {
49
58
  document.getElementById('typingIndicator')?.classList.toggle('active', state.agentBusy);
50
59
  if (s === 'running') {
51
60
  if (badge) { badge.className = 'status-badge status-running'; badge.textContent = 'running'; }
52
- if (btn) { btn.textContent = '■'; btn.title = t('btn.stop'); btn.classList.add('stop-mode'); }
61
+ if (btn) { btn.innerHTML = ICONS.stop; btn.title = t('btn.stop'); btn.classList.add('stop-mode'); }
53
62
  showSkeleton();
54
63
  } else {
55
64
  if (badge) { badge.className = 'status-badge status-idle'; badge.textContent = 'idle'; }
56
- if (btn) { btn.textContent = '➤'; btn.title = 'Send'; btn.classList.remove('stop-mode'); }
65
+ if (btn) { btn.innerHTML = ICONS.send; btn.title = 'Send'; btn.classList.remove('stop-mode'); }
57
66
  removeSkeleton();
58
67
  updateQueueBadge(0);
59
68
  }
@@ -110,7 +119,7 @@ export function addSystemMsg(text: string, extraClass?: string, type?: string):
110
119
  const div = document.createElement('div');
111
120
  const typeClass = type ? ` msg-type-${type}` : '';
112
121
  div.className = 'msg msg-system' + typeClass + (extraClass ? ' ' + extraClass : '');
113
- div.textContent = text;
122
+ div.innerHTML = text;
114
123
  container.appendChild(div);
115
124
  container.scrollTop = container.scrollHeight;
116
125
  }
@@ -143,9 +152,11 @@ export function showProcessStep(step: ProcessStep): void {
143
152
  }
144
153
  }
145
154
  if (state.currentProcessBlock) {
146
- // Completion icons (✅/❌) update the last matching running step
147
- if (step.icon === '✅' || step.icon === '') {
148
- const status = step.icon === '✅' ? 'done' : 'error';
155
+ // Completion detection: prefer semantic status field, fall back to emoji check
156
+ const resolvedStatus = (step.status && step.status !== 'running')
157
+ ? step.status
158
+ : emojiToStatus(step.icon);
159
+ if (resolvedStatus === 'done' || resolvedStatus === 'error') {
149
160
  // Prefer matching by stepRef (stable correlation), fall back to label
150
161
  const ref = step.stepRef;
151
162
  const match = ref
@@ -154,7 +165,7 @@ export function showProcessStep(step: ProcessStep): void {
154
165
  : [...state.currentProcessBlock.steps].reverse()
155
166
  .find(s => s.status === 'running' && s.label === step.label);
156
167
  if (match) {
157
- updateStepStatus(state.currentProcessBlock, match.id, status);
168
+ updateStepStatus(state.currentProcessBlock, match.id, resolvedStatus);
158
169
  scrollToBottom();
159
170
  return;
160
171
  }
@@ -162,7 +173,7 @@ export function showProcessStep(step: ProcessStep): void {
162
173
  const anyRunning = [...state.currentProcessBlock.steps].reverse()
163
174
  .find(s => s.status === 'running');
164
175
  if (anyRunning) {
165
- updateStepStatus(state.currentProcessBlock, anyRunning.id, status);
176
+ updateStepStatus(state.currentProcessBlock, anyRunning.id, resolvedStatus);
166
177
  scrollToBottom();
167
178
  return;
168
179
  }
@@ -180,6 +191,8 @@ export function showProcessStep(step: ProcessStep): void {
180
191
  return;
181
192
  }
182
193
  }
194
+ // Convert emoji icon to SVG before adding step
195
+ step.icon = emojiToIcon(step.icon);
183
196
  addStep(state.currentProcessBlock, step);
184
197
  }
185
198
  scrollToBottom();
@@ -222,13 +235,15 @@ export function finalizeAgent(text: string, toolLog?: ToolLogEntry[]): void {
222
235
  state.currentAgentDiv = addMessage('agent', '');
223
236
  }
224
237
  const content = (state.currentAgentDiv as HTMLElement)?.querySelector('.msg-content');
225
- // Finalize rAF stream if active, otherwise use raw text
226
- const finalText = currentStream ? finalizeStream(currentStream) || text : text;
238
+ // Live stream is preview-only; agent_done text stays authoritative.
239
+ const streamedText = currentStream ? finalizeStream(currentStream) : '';
240
+ const finalText = text || streamedText;
227
241
  currentStream = null;
228
242
  // Skip static tool HTML when process block already shows tool summary
229
243
  const toolHtml = hasTools && !hadProcessBlock ? buildProcessBlockHtml(toProcessSteps(toolLog!), true) : '';
230
244
  if (content) content.innerHTML = toolHtml + renderMarkdown(finalText);
231
245
  if (content) content.setAttribute('data-raw', stripOrchestration(finalText));
246
+ if (content) activateWidgets(content as HTMLElement);
232
247
  }
233
248
  currentStream = null;
234
249
  state.currentAgentDiv = null;
@@ -239,7 +254,7 @@ export function finalizeAgent(text: string, toolLog?: ToolLogEntry[]): void {
239
254
  if (text) appendCachedMessage('assistant', text).catch(() => {});
240
255
  }
241
256
 
242
- export function addMessage(role: string, text: string): HTMLDivElement {
257
+ export function addMessage(role: string, text: string, cli?: string | null): HTMLDivElement {
243
258
  const container = document.getElementById('chatMessages');
244
259
  const vs = getVirtualScroll();
245
260
  if (vs.active) vs.flushToDOM();
@@ -252,7 +267,7 @@ export function addMessage(role: string, text: string): HTMLDivElement {
252
267
  const div = document.createElement('div');
253
268
  if (role === 'agent') {
254
269
  div.className = 'msg msg-agent';
255
- div.innerHTML = `<div class="agent-icon" aria-hidden="true">🦈</div><div class="agent-body"><div class="msg-content">${rendered}</div><button class="msg-copy" title="Copy" aria-label="Copy message"></button></div>`;
270
+ div.innerHTML = `<div class="agent-icon" aria-hidden="true">${getAgentIcon(cli)}</div><div class="agent-body"><div class="msg-content">${rendered}</div><button class="msg-copy" title="Copy" aria-label="Copy message"></button></div>`;
256
271
  } else {
257
272
  div.className = `msg msg-${role}`;
258
273
  div.innerHTML = `<div class="msg-label">${label}</div><div class="msg-content">${rendered}</div><button class="msg-copy" title="Copy" aria-label="Copy message"></button>`;
@@ -260,6 +275,7 @@ export function addMessage(role: string, text: string): HTMLDivElement {
260
275
  const contentEl = div.querySelector('.msg-content');
261
276
  if (contentEl) contentEl.setAttribute('data-raw', stripOrchestration(text));
262
277
  container?.appendChild(div);
278
+ activateWidgets(div);
263
279
  scrollToBottom();
264
280
  return div;
265
281
  }
@@ -328,14 +344,18 @@ export async function loadMessages(): Promise<void> {
328
344
  const tools = m.role === 'assistant' ? parseToolLog(m.tool_log) : [];
329
345
  const toolHtml = tools.length > 0 ? buildProcessBlockHtml(toProcessSteps(tools), true) : '';
330
346
  const html = role === 'agent'
331
- ? `<div class="msg msg-agent"><div class="agent-icon" aria-hidden="true">🦈</div><div class="agent-body">${toolHtml}<div class="msg-content" data-raw="${escapeHtml(stripOrchestration(m.content))}">${rendered}</div><button class="msg-copy" title="Copy" aria-label="Copy message"></button></div></div>`
347
+ ? `<div class="msg msg-agent"><div class="agent-icon" aria-hidden="true">${getAgentIcon(m.cli)}</div><div class="agent-body">${toolHtml}<div class="msg-content" data-raw="${escapeHtml(stripOrchestration(m.content))}">${rendered}</div><button class="msg-copy" title="Copy" aria-label="Copy message"></button></div></div>`
332
348
  : `<div class="msg msg-${role}"><div class="msg-label">${label}</div><div class="msg-content" data-raw="${escapeHtml(stripOrchestration(m.content))}">${rendered}</div><button class="msg-copy" title="Copy" aria-label="Copy message"></button></div>`;
333
349
  vs.addItem(crypto.randomUUID(), html);
334
350
  }
335
351
  vs.scrollToBottom();
352
+ requestAnimationFrame(() => {
353
+ const chatElRef = document.getElementById('chatMessages');
354
+ if (chatElRef) activateWidgets(chatElRef);
355
+ });
336
356
  } else {
337
357
  msgs.forEach(m => {
338
- const div = addMessage(m.role === 'assistant' ? 'agent' : m.role, m.content);
358
+ const div = addMessage(m.role === 'assistant' ? 'agent' : m.role, m.content, m.cli);
339
359
  if (m.role === 'assistant') {
340
360
  const tools = parseToolLog(m.tool_log);
341
361
  if (tools.length > 0) {
@@ -366,7 +386,7 @@ export async function loadMessages(): Promise<void> {
366
386
  const cached = await getCachedMessages();
367
387
  if (cached.length > 0) {
368
388
  cached.forEach(m => addMessage(m.role === 'assistant' ? 'agent' : m.role, m.content));
369
- addMessage('system', '📴 오프라인 모드 — 캐시된 메시지 표시 중');
389
+ addSystemMsg(`${ICONS.warning} 오프라인 모드 — 캐시된 메시지 표시 중`);
370
390
  }
371
391
  showEmptyState();
372
392
  }
@@ -406,7 +426,7 @@ export function initMsgCopy(): void {
406
426
  const text = content.getAttribute('data-raw') || content.innerText || content.textContent || '';
407
427
  navigator.clipboard.writeText(text).then(() => {
408
428
  btn.classList.add('copied');
409
- btn.textContent = '✓';
429
+ btn.innerHTML = ICONS.checkSimple;
410
430
  setTimeout(() => {
411
431
  btn.classList.remove('copied');
412
432
  btn.textContent = '';
package/public/js/ws.ts CHANGED
@@ -3,6 +3,8 @@ import { state } from './state.js';
3
3
  import { setStatus, updateQueueBadge, addSystemMsg, appendAgentText, finalizeAgent, addMessage, showProcessStep, cleanupToolActivity } from './ui.js';
4
4
  import { t, getLang } from './features/i18n.js';
5
5
  import { getVirtualScroll } from './virtual-scroll.js';
6
+ import { ICONS, emojiToIcon } from './icons.js';
7
+ import { escapeHtml } from './render.js';
6
8
  import type { OrcStateName } from './state.js';
7
9
 
8
10
  const ROADMAP_PHASES = ['P', 'A', 'B', 'C'] as const;
@@ -36,6 +38,7 @@ function positionShark(roadmap: HTMLElement, shark: HTMLElement, phase: string):
36
38
 
37
39
  interface WsMessage {
38
40
  type: string;
41
+ scope?: string;
39
42
  running?: boolean;
40
43
  status?: string;
41
44
  agentId?: string;
@@ -68,6 +71,8 @@ interface WsMessage {
68
71
  // Agent phase state (populated by agent_status events from orchestrator)
69
72
  const agentPhaseState: Record<string, { phase: string; phaseLabel: string }> = {};
70
73
 
74
+ let currentOrcScope = '';
75
+
71
76
  /** Hydrate agent phase cache from snapshot (used after reconnect) */
72
77
  export function hydrateAgentPhases(workers: Array<{
73
78
  agentId: string;
@@ -214,10 +219,10 @@ export function connect(): void {
214
219
  } else if (msg.type === 'queue_update') {
215
220
  updateQueueBadge(msg.pending || 0);
216
221
  } else if (msg.type === 'worklog_created') {
217
- addSystemMsg(`📋 Worklog: ${msg.path || ''}`);
222
+ addSystemMsg(`${ICONS.clipboard} Worklog: ${escapeHtml(msg.path || '')}`);
218
223
  } else if (msg.type === 'round_start') {
219
224
  const agents = (msg.agentPhases || msg.subtasks || []);
220
- const names = agents.map(a => a.agent || a.name || '').join(', ');
225
+ const names = agents.map(a => escapeHtml(a.agent || a.name || '')).join(', ');
221
226
  addSystemMsg(t('ws.roundStart', { round: msg.round || 0, count: agents.length, names }));
222
227
  } else if (msg.type === 'round_done') {
223
228
  if (msg.action === 'complete') {
@@ -233,21 +238,21 @@ export function connect(): void {
233
238
  showProcessStep({
234
239
  id: `step-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,
235
240
  type: stepType,
236
- icon: msg.icon || '🔧',
241
+ icon: msg.icon || ICONS.tool,
237
242
  label: msg.label || '',
238
243
  detail: msg.detail || '',
239
244
  stepRef: msg.stepRef || '',
240
- status: 'running',
245
+ status: (msg.status as 'running' | 'done' | 'error') || 'running',
241
246
  startTime: Date.now(),
242
247
  });
243
248
  } else if (msg.type === 'agent_output') {
244
249
  appendAgentText(msg.text || '');
245
250
  } else if (msg.type === 'agent_retry') {
246
- addSystemMsg(t('ws.retry', { cli: msg.cli || '', delay: msg.delay || 10 }), 'tool-activity');
251
+ addSystemMsg(t('ws.retry', { cli: escapeHtml(msg.cli || ''), delay: msg.delay || 10 }), 'tool-activity');
247
252
  } else if (msg.type === 'agent_fallback') {
248
- addSystemMsg(t('ws.fallback', { from: msg.from || '', to: msg.to || '' }), 'tool-activity');
253
+ addSystemMsg(t('ws.fallback', { from: escapeHtml(msg.from || ''), to: escapeHtml(msg.to || '') }), 'tool-activity');
249
254
  } else if (msg.type === 'agent_smoke') {
250
- addSystemMsg(`⚠️ ${msg.cli || 'agent'}: smoke response detected — auto-continuing`, 'tool-activity');
255
+ addSystemMsg(`${ICONS.warning} ${escapeHtml(msg.cli || 'agent')}: smoke response detected — auto-continuing`, 'tool-activity');
251
256
  } else if (msg.type === 'agent_done') {
252
257
  finalizeAgent(msg.text || '', msg.toolLog);
253
258
  } else if (msg.type === 'orchestrate_done') {
@@ -258,13 +263,14 @@ export function connect(): void {
258
263
  const el = document.getElementById('chatMessages');
259
264
  if (el) el.innerHTML = '';
260
265
  } else if (msg.type === 'session_reset') {
261
- addSystemMsg('🔄 Session reset — history preserved', 'tool-activity');
266
+ addSystemMsg(`${ICONS.refresh} Session reset — history preserved`, 'tool-activity');
262
267
  } else if (msg.type === 'agent_added' || msg.type === 'agent_updated' || msg.type === 'agent_deleted') {
263
268
  import('./features/employees.js').then(m => m.loadEmployees());
264
269
  } else if (msg.type === 'orc_state') {
270
+ if (msg.scope && currentOrcScope && msg.scope !== currentOrcScope) return;
265
271
  applyOrcState(typeof msg.state === 'string' ? msg.state : 'IDLE', msg.title);
266
272
  } else if (msg.type === 'new_message' && (msg.source === 'telegram' || msg.source === 'discord')) {
267
- addMessage(msg.role === 'assistant' ? 'agent' : (msg.role || 'user'), msg.content || '');
273
+ addMessage(msg.role === 'assistant' ? 'agent' : (msg.role || 'user'), msg.content || '', msg.cli);
268
274
  }
269
275
  };
270
276
  state.ws.onopen = () => {
@@ -281,6 +287,7 @@ export function connect(): void {
281
287
  fetch('/api/orchestrate/snapshot')
282
288
  .then(r => r.json())
283
289
  .then((snap: any) => {
290
+ currentOrcScope = String(snap.orc.scope || '');
284
291
  applyOrcState(snap.orc.state);
285
292
  hydrateAgentPhases(snap.workers);
286
293
  updateQueueBadge(snap.runtime.queuePending);
@@ -295,7 +302,7 @@ export function connect(): void {
295
302
  console.log('[ws] disconnected, reconnecting in 2s...');
296
303
  import('./ui.js').then(m => m.cleanupToolActivity());
297
304
  setStatus('idle');
298
- addSystemMsg('⚡ 연결 끊김 — 재연결 중...', 'tool-activity');
305
+ addSystemMsg(`${ICONS.exec} 연결 끊김 — 재연결 중...`, 'tool-activity');
299
306
  setTimeout(connect, 2000);
300
307
  };
301
308
  }
@@ -34,42 +34,42 @@
34
34
  "cmd.steer.tg_desc": "Redirect agent to new task",
35
35
  "cmd.steer.noPrompt": "Usage: /steer <new prompt>",
36
36
  "cmd.steer.noAgent": "No agent is currently running.",
37
- "cmd.steer.started": "🔄 Redirecting...",
37
+ "cmd.steer.started": "Redirecting...",
38
38
  "cmd.unknown": "Unknown command: /{name}\nUse /help to see available commands.",
39
- "cmd.unsupported": "/{name} is not available on {iface}.",
40
- "cmd.error": "/{name} execution error: {msg}",
39
+ "cmd.unsupported": "/{name} is not available on {iface}.",
40
+ "cmd.error": "/{name} execution error: {msg}",
41
41
  "cmd.helpTitle": "Available commands",
42
42
  "cmd.helpDetail": "Detailed help: /help <command>",
43
- "cmd.settingsLoadFail": "Failed to load settings.",
43
+ "cmd.settingsLoadFail": "Failed to load settings.",
44
44
  "cmd.model.current": "Current model ({cli}): {model}",
45
- "cmd.model.invalid": "Invalid model name.",
46
- "cmd.model.changed": "Model changed: {model}\nApplied from next message.",
45
+ "cmd.model.invalid": "Invalid model name.",
46
+ "cmd.model.changed": "Model changed: {model}\nApplied from next message.",
47
47
  "cmd.cli.current": "Current CLI: {cli}\nAvailable: {available}",
48
- "cmd.cli.unknown": "Unknown CLI: {cli}\nAvailable: {available}",
48
+ "cmd.cli.unknown": "Unknown CLI: {cli}\nAvailable: {available}",
49
49
  "cmd.cli.already": "{cli} is already active.",
50
- "cmd.cli.changed": "CLI changed: {from} → {to}",
51
- "cmd.skill.loadFail": "Failed to load skill info.",
52
- "cmd.skill.resetUnavailable": "/skill reset is not available in this environment.",
53
- "cmd.skill.resetDone": "Skill reset completed.",
54
- "cmd.employee.resetUnavailable": "/employee reset is not available in this environment.",
55
- "cmd.employee.resetDone": "Employees reset to defaults ({count} agents)",
56
- "cmd.clear.telegram": "ℹ️ /clear only shows a notice on Telegram (no screen clearing).",
57
- "cmd.clear.discord": "ℹ️ /clear only shows a notice on Discord (no screen clearing).",
58
- "cmd.clear.done": "Screen cleared. (Chat history preserved)",
59
- "cmd.reset.confirm": "⚠️ Full reset: MCP, skills, employees, and session will be reset to defaults.\nType /reset confirm to proceed.",
60
- "cmd.reset.unavailable": "Reset is not supported in this environment.",
61
- "cmd.reset.done": "Reset complete: {items}",
50
+ "cmd.cli.changed": "CLI changed: {from} → {to}",
51
+ "cmd.skill.loadFail": "Failed to load skill info.",
52
+ "cmd.skill.resetUnavailable": "/skill reset is not available in this environment.",
53
+ "cmd.skill.resetDone": "Skill reset completed.",
54
+ "cmd.employee.resetUnavailable": "/employee reset is not available in this environment.",
55
+ "cmd.employee.resetDone": "Employees reset to defaults ({count} agents)",
56
+ "cmd.clear.telegram": "/clear only shows a notice on Telegram (no screen clearing).",
57
+ "cmd.clear.discord": "/clear only shows a notice on Discord (no screen clearing).",
58
+ "cmd.clear.done": "Screen cleared. (Chat history preserved)",
59
+ "cmd.reset.confirm": "Full reset: MCP, skills, employees, and session will be reset to defaults.\nType /reset confirm to proceed.",
60
+ "cmd.reset.unavailable": "Reset is not supported in this environment.",
61
+ "cmd.reset.done": "Reset complete: {items}",
62
62
  "cmd.reset.skills": "Skills",
63
63
  "cmd.reset.employees": "Employees",
64
64
  "cmd.reset.sessions": "Session",
65
- "cmd.mcp.syncDone": "MCP sync complete ({count} targets)",
66
- "cmd.mcp.installDone": "MCP install complete ({count} servers)",
67
- "cmd.memory.empty": "🧠 No memory files.",
68
- "cmd.browser.noTabs": "🌐 No open tabs.",
69
- "cmd.fallback.inactive": "Fallback: disabled\nAvailable: {available}",
70
- "cmd.fallback.off": "Fallback disabled",
71
- "cmd.fallback.invalidCli": "No valid CLI specified\nAvailable: {available}",
72
- "cmd.fallback.set": "Fallback set: {order}",
65
+ "cmd.mcp.syncDone": "MCP sync complete ({count} targets)",
66
+ "cmd.mcp.installDone": "MCP install complete ({count} servers)",
67
+ "cmd.memory.empty": "No memory files.",
68
+ "cmd.browser.noTabs": "No open tabs.",
69
+ "cmd.fallback.inactive": "Fallback: disabled\nAvailable: {available}",
70
+ "cmd.fallback.off": "Fallback disabled",
71
+ "cmd.fallback.invalidCli": "No valid CLI specified\nAvailable: {available}",
72
+ "cmd.fallback.set": "Fallback set: {order}",
73
73
  "cmd.arg.skillList": "Skill list",
74
74
  "cmd.arg.skillReset": "Reset skills",
75
75
  "cmd.arg.employeeReset": "Reset to 4 defaults",
@@ -77,93 +77,93 @@
77
77
  "cmd.arg.browserTabs": "Open tabs",
78
78
  "cmd.arg.fallbackOff": "Disable",
79
79
  "cmd.arg.flushOff": "Disable (use active CLI)",
80
- "cmd.flush.current": "🔄 Flush model ({cli}): {model}",
81
- "cmd.flush.changed": "Flush model changed: {cli} / {model}",
82
- "cmd.flush.reset": "Flush model reset (using active CLI/model)",
83
- "cmd.flush.cliUnavailable": "{cli} CLI not found (model: {model}). Please install and retry.",
80
+ "cmd.flush.current": "Flush model ({cli}): {model}",
81
+ "cmd.flush.changed": "Flush model changed: {cli} / {model}",
82
+ "cmd.flush.reset": "Flush model reset (using active CLI/model)",
83
+ "cmd.flush.cliUnavailable": "{cli} CLI not found (model: {model}). Please install and retry.",
84
84
  "cmd.arg.argSelect": "Select argument",
85
85
  "api.notCommand": "Not a slash command.",
86
86
  "api.serverError": "Server error: {msg}",
87
- "ws.agentBusy": "⚠️ Agent is currently running. Please try again after it completes.",
88
- "ws.roundStart": "🔄 Round {round} — {count} tasks [{names}]",
89
- "ws.roundDone": "🏁 Round {round} complete",
90
- "ws.roundNext": "➡️ Round {round} → next round",
91
- "ws.roundRetry": "↩️ Round {round} → retry",
92
- "ws.fallback": "{from} failed → retrying with {to}",
93
- "ws.retry": "{cli} 429 → retrying in {delay}s",
94
- "tg.settingsUnsupported": "Settings changes are not supported on Telegram.",
95
- "dc.settingsUnsupported": "Settings changes are not supported on Discord.",
87
+ "ws.agentBusy": "Agent is currently running. Please try again after it completes.",
88
+ "ws.roundStart": "Round {round} — {count} tasks [{names}]",
89
+ "ws.roundDone": "Round {round} complete",
90
+ "ws.roundNext": "Round {round} → next round",
91
+ "ws.roundRetry": "Round {round} → retry",
92
+ "ws.fallback": "{from} failed → retrying with {to}",
93
+ "ws.retry": "{cli} 429 → retrying in {delay}s",
94
+ "tg.settingsUnsupported": "Settings changes are not supported on Telegram.",
95
+ "dc.settingsUnsupported": "Settings changes are not supported on Discord.",
96
96
  "tg.promptUnsupported": "(Not supported on Telegram)",
97
- "tg.queued": "📥 Added to queue (#{count})",
97
+ "tg.queued": "Added to queue (#{count})",
98
98
  "tg.imageCaption": "[📷 Image] {caption}",
99
- "tg.imageFail": "Image processing failed: {msg}",
100
- "tg.fileFail": "File processing failed: {msg}",
101
- "tg.voiceFail": "Voice transcription failed: {msg}",
102
- "tg.voiceEmpty": "🤔 Couldn't understand the voice message. Please try again.",
99
+ "tg.imageFail": "Image processing failed: {msg}",
100
+ "tg.fileFail": "File processing failed: {msg}",
101
+ "tg.voiceFail": "Voice transcription failed: {msg}",
102
+ "tg.voiceEmpty": "Couldn't understand the voice message. Please try again.",
103
103
  "voice.start": "Record voice",
104
104
  "voice.stop": "Stop recording",
105
- "voice.micDenied": "🎤 Microphone access denied. Please allow microphone in browser settings.",
106
- "voice.micNotFound": "🎤 No microphone found. Please connect a microphone.",
107
- "voice.micBusy": "🎤 Microphone is in use by another app.",
108
- "voice.httpsRequired": "🎤 Voice recording requires HTTPS or localhost.",
109
- "voice.unsupported": "🎤 Voice recording is not supported in this browser.",
110
- "voice.interrupted": "⚠️ Recording was interrupted.",
111
- "voice.tooLarge": "⚠️ Recording too large (max 20MB).",
112
- "voice.tooShort": "⚠️ Recording too short.",
113
- "voice.sttFail": "Voice recognition failed: {msg}",
114
- "copilot.keychain": "🔑 Keychain",
105
+ "voice.micDenied": "Microphone access denied. Please allow microphone in browser settings.",
106
+ "voice.micNotFound": "No microphone found. Please connect a microphone.",
107
+ "voice.micBusy": "Microphone is in use by another app.",
108
+ "voice.httpsRequired": "Voice recording requires HTTPS or localhost.",
109
+ "voice.unsupported": "Voice recording is not supported in this browser.",
110
+ "voice.interrupted": "Recording was interrupted.",
111
+ "voice.tooLarge": "Recording too large (max 20MB).",
112
+ "voice.tooShort": "Recording too short.",
113
+ "voice.sttFail": "Voice recognition failed: {msg}",
114
+ "copilot.keychain": "Keychain",
115
115
  "copilot.keychainHint": "Re-read Copilot token from macOS Keychain",
116
- "stt.title": "🎙️ Voice Recognition (STT)",
116
+ "stt.title": "Voice Recognition (STT)",
117
117
  "stt.engine": "Engine",
118
118
  "stt.geminiKey": "Gemini API Key",
119
119
  "stt.geminiKeyHint": "Used for cloud STT via Gemini",
120
120
  "stt.geminiModel": "Gemini Model",
121
121
  "stt.whisperModel": "Whisper Model",
122
- "stt.save": "💾 Save STT Settings",
123
- "stt.saved": "Saved",
124
- "stt.shortcutHint": "⌨️ Ctrl+Shift+Space (⌘+Shift+Space on Mac)",
122
+ "stt.save": "Save STT Settings",
123
+ "stt.saved": "Saved",
124
+ "stt.shortcutHint": "Ctrl+Shift+Space (⌘+Shift+Space on Mac)",
125
125
  "stt.openaiBaseUrl": "Base URL",
126
126
  "stt.openaiKey": "API Key",
127
127
  "stt.openaiModel": "Model",
128
128
  "stt.vertexConfig": "Vertex AI Config (JSON)",
129
- "tg.timeout": "Timeout (20 min no response)",
129
+ "tg.timeout": "Timeout (20 min no response)",
130
130
  "tg.noResponse": "No response",
131
- "tg.connected": "🦈 Jaw Agent connected! Send a message and the AI agent will respond.",
132
- "tg.resetDone": "Reset complete.",
131
+ "tg.connected": "Jaw Agent connected! Send a message and the AI agent will respond.",
132
+ "tg.resetDone": "Reset complete.",
133
133
  "cli.fileNotFound": "File not found: {path}",
134
134
  "cli.fileSentPrompt": "[User sent a file: {path}]\nPlease read and analyze this file.",
135
135
  "cli.fileSentWithMsg": "\n\nUser message: {text}",
136
136
  "cli.fallbackRetry": "{from} failed → retrying with {to}",
137
137
  "cli.noReadyCli": "No authenticated CLI available — install and authenticate at least one CLI above to get started.",
138
- "chat.cmd.fail": "Command execution failed: {msg}",
138
+ "chat.cmd.fail": "Command execution failed: {msg}",
139
139
  "chat.file.sent": "[User sent a file: {path}]\nPlease read and analyze this file.",
140
140
  "chat.file.sentWithMsg": "\n\nUser message: {text}",
141
- "chat.file.uploadFail": "File upload failed: {msg}",
141
+ "chat.file.uploadFail": "File upload failed: {msg}",
142
142
  "chat.requestFail": "Request failed ({status})",
143
- "chat.continue": "↪️ Continuing from previous worklog.",
143
+ "chat.continue": "Continuing from previous worklog.",
144
144
  "skill.loadFail": "Failed to load skills",
145
145
  "skill.count": "{active} active / {total} total",
146
146
  "skill.filter.all": "All",
147
- "skill.filter.installed": "📦 Installed",
148
- "skill.filter.productivity": "📝 Productivity",
149
- "skill.filter.communication": "📧 Comms",
150
- "skill.filter.devtools": "🔧 Dev",
151
- "skill.filter.utility": "🌐 Utility",
152
- "skill.filter.smarthome": "🏠 Home",
153
- "skill.filter.other": "📂 Other",
154
- "skill.filter.ai": "🤖 AI",
147
+ "skill.filter.installed": "Installed",
148
+ "skill.filter.productivity": "Productivity",
149
+ "skill.filter.communication": "Comms",
150
+ "skill.filter.devtools": "Dev",
151
+ "skill.filter.utility": "Utility",
152
+ "skill.filter.smarthome": "Home",
153
+ "skill.filter.other": "Other",
154
+ "skill.filter.ai": "AI",
155
155
  "lang.ko": "한국어",
156
156
  "lang.en": "English",
157
157
  "emp.addPrompt": "Add an agent",
158
158
  "emp.delete": "Delete",
159
159
  "emp.customRole": "Custom role...",
160
- "emp.customModel": "✏️ Enter manually...",
161
- "role.label.frontend": "🎨 Frontend",
162
- "role.label.backend": "⚙️ Backend",
163
- "role.label.research": "🔎 Research",
164
- "role.label.data": "📊 Data",
165
- "role.label.docs": "📝 Docs",
166
- "role.label.custom": "✏️ Custom...",
160
+ "emp.customModel": "Enter manually...",
161
+ "role.label.frontend": "Frontend",
162
+ "role.label.backend": "Backend",
163
+ "role.label.research": "Research",
164
+ "role.label.data": "Data",
165
+ "role.label.docs": "Docs",
166
+ "role.label.custom": "Custom...",
167
167
  "hb.empty": "No heartbeats",
168
168
  "hb.name": "Name",
169
169
  "hb.prompt": "Prompt...",
@@ -184,15 +184,15 @@
184
184
  "mem.noFiles": "No memory files yet",
185
185
  "mem.empty": "No memory yet",
186
186
  "model.promptInput": "Enter model ID:",
187
- "model.customOption": "✏️ Enter manually...",
187
+ "model.customOption": "Enter manually...",
188
188
  "model.placeholder": "Enter model ID",
189
189
  "mcp.syncing": "Syncing...",
190
- "mcp.installing": "📦 Installing via npm... (up to 2 min)",
190
+ "mcp.installing": "Installing via npm... (up to 2 min)",
191
191
  "settings.none": "(none)",
192
192
  "cli.gemini.auth": "Browser auth on first run",
193
- "cli.authRequired": "⚠️ Install / auth required",
194
- "cli.notAuthenticated": "🔑 Auth required — run command below",
195
- "drag.drop": "📎 Drop files here",
193
+ "cli.authRequired": "Install / auth required",
194
+ "cli.notAuthenticated": "Auth required — run command below",
195
+ "drag.drop": "Drop files here",
196
196
  "status.responding": "Responding",
197
197
  "aria.cmdList": "Command list",
198
198
  "btn.attach": "Attach file",
@@ -244,8 +244,8 @@
244
244
  "label.flushEvery": "Flush Every",
245
245
  "emp.empty": "No employees yet",
246
246
  "modal.promptTitle": "System Prompt (A-2)",
247
- "modal.heartbeatTitle": "💓 Heartbeat Jobs",
248
- "modal.memoryTitle": "🧠 Memory",
247
+ "modal.heartbeatTitle": "Heartbeat Jobs",
248
+ "modal.memoryTitle": "Memory",
249
249
  "code.copy": "Copy",
250
250
  "code.copied": "Copied ✓",
251
251
  "mcp.noServers": "(no servers configured)",
@@ -254,5 +254,6 @@
254
254
  "cmd.ide.desc": "IDE diff view",
255
255
  "cmd.ide.toggled": "IDE diff mode toggled",
256
256
  "cmd.ide.popToggled": "IDE popup toggled",
257
- "cmd.ide.usage": "Usage: /ide [pop|on|off]"
257
+ "cmd.ide.usage": "Usage: /ide [pop|on|off]",
258
+ "label.mentionOnly": "Mention Only"
258
259
  }