@parallel-cli/parallel 0.4.6 → 0.4.7
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.
- package/CHANGELOG.md +28 -0
- package/README.md +58 -11
- package/dist/agents/agent.js +98 -14
- package/dist/agents/tools.js +218 -20
- package/dist/commands.js +52 -0
- package/dist/controller.js +23 -13
- package/dist/coordination/blackboard.js +37 -1
- package/dist/i18n.js +69 -13
- package/dist/index.js +1 -0
- package/dist/ui/AgentPanel.js +87 -25
- package/dist/ui/App.js +11 -6
- package/dist/ui/AttachApp.js +99 -14
- package/dist/ui/CommandInput.js +12 -2
- package/dist/ui/Timeline.js +5 -0
- package/dist/ui/events.js +20 -17
- package/dist/ui/tokens.js +2 -2
- package/dist/ui/views.js +17 -5
- package/dist/version.js +1 -1
- package/package.json +2 -2
package/dist/i18n.js
CHANGED
|
@@ -104,6 +104,11 @@ const en = {
|
|
|
104
104
|
'board.none': '(no agents)',
|
|
105
105
|
'board.activity': 'Who works where (file activity, no locks):',
|
|
106
106
|
'board.workMap': 'Work map warnings:',
|
|
107
|
+
'board.workMapOk': 'Work map: no overlapping claims.',
|
|
108
|
+
'board.warningMeta': '{agents} · {paths} · {time}',
|
|
109
|
+
'board.warningSuggestion': 'Suggestion: /focus agent or /diff before editing the same area.',
|
|
110
|
+
'board.secondsAgo': '{n}s ago',
|
|
111
|
+
'board.minutesAgo': '{n}m ago',
|
|
107
112
|
'board.noActivity': '(no edits yet)',
|
|
108
113
|
'board.notes': 'Latest notes:',
|
|
109
114
|
'notes.title': '✉ INTER-AGENT NOTES (last 30)',
|
|
@@ -130,6 +135,7 @@ const en = {
|
|
|
130
135
|
// commands (descriptions)
|
|
131
136
|
'cmd.ask': 'Ask-only agent: answers and advises without editing',
|
|
132
137
|
'cmd.task': 'Task agent: executes, edits, validates, summarizes',
|
|
138
|
+
'cmd.review': 'Lightweight reviewer: verdict, risks, tests, files to inspect',
|
|
133
139
|
'cmd.agents': 'Agents panel view (real time)',
|
|
134
140
|
'cmd.board': 'Coordination view: who works where, notes',
|
|
135
141
|
'cmd.notes': 'Inter-agent notes history',
|
|
@@ -154,6 +160,14 @@ const en = {
|
|
|
154
160
|
'timeline.section.coordinate': 'Coordinating',
|
|
155
161
|
'timeline.section.result': 'Result',
|
|
156
162
|
'timeline.section.other': 'Activity',
|
|
163
|
+
'timeline.narration.inspectAfterValidate': 'That signal is not enough, so I inspect the project again to confirm the real state.',
|
|
164
|
+
'timeline.narration.inspect': 'I inspect the project state and relevant files before deciding.',
|
|
165
|
+
'timeline.narration.change': 'I change the targeted files while keeping the edit focused.',
|
|
166
|
+
'timeline.narration.validate': 'I run local validation to check the changes technically hold.',
|
|
167
|
+
'timeline.narration.publish': 'I prepare Git synchronization after checking the local state.',
|
|
168
|
+
'timeline.narration.coordinate': 'I handle coordination signals, notes, approvals, and questions.',
|
|
169
|
+
'timeline.narration.result': 'I check the final result and any important errors.',
|
|
170
|
+
'timeline.narration.other': 'I continue the current activity.',
|
|
157
171
|
'cmd.send': 'Instruction to an agent (same as: @Agent message)',
|
|
158
172
|
'cmd.pause': 'Pause',
|
|
159
173
|
'cmd.resume': 'Resume',
|
|
@@ -458,7 +472,7 @@ const fr = {
|
|
|
458
472
|
'main.ready1': '⚡ Prêt — dossier : {folder}',
|
|
459
473
|
'main.ready2': "Tape une tâche + Entrée pour lancer ton premier agent. /help pour l'aide.",
|
|
460
474
|
'main.empty': "Aucun agent pour le moment. Tape une tâche + Entrée pour lancer ton premier agent — puis relances-en d'autres à tout moment, même pendant qu'ils travaillent.",
|
|
461
|
-
'main.prompt': 'Exemple : Fais
|
|
475
|
+
'main.prompt': 'Exemple : Fais-moi une refonte UI',
|
|
462
476
|
'main.emptyCard.tagline': 'Code multi-agent depuis un terminal.',
|
|
463
477
|
'main.emptyCard.cta': 'Décris le travail ci-dessous pour lancer le premier agent.',
|
|
464
478
|
'main.emptyCard.hints': '/ pour les commandes · @agent pour piloter · /help pour les raccourcis',
|
|
@@ -483,6 +497,11 @@ const fr = {
|
|
|
483
497
|
'board.none': '(aucun agent)',
|
|
484
498
|
'board.activity': 'Qui travaille où (activité fichiers, sans verrou) :',
|
|
485
499
|
'board.workMap': 'Alertes work map :',
|
|
500
|
+
'board.workMapOk': 'Work map : aucune zone déclarée en chevauchement.',
|
|
501
|
+
'board.warningMeta': '{agents} · {paths} · {time}',
|
|
502
|
+
'board.warningSuggestion': 'Suggestion : /focus agent ou /diff avant de modifier la même zone.',
|
|
503
|
+
'board.secondsAgo': 'il y a {n}s',
|
|
504
|
+
'board.minutesAgo': 'il y a {n}min',
|
|
486
505
|
'board.noActivity': '(aucune modification pour le moment)',
|
|
487
506
|
'board.notes': 'Dernières notes :',
|
|
488
507
|
'notes.title': '✉ NOTES INTER-AGENTS (30 dernières)',
|
|
@@ -508,6 +527,7 @@ const fr = {
|
|
|
508
527
|
'help.keys': 'Esc : revenir à la vue agents · Tab : complétion · PgUp/PgDn : scroll hub/focus · ↑/↓ : suggestions, historique ou scroll selon la vue · Ctrl+V : coller une image',
|
|
509
528
|
'cmd.ask': 'Agent ask : répond et conseille sans modifier',
|
|
510
529
|
'cmd.task': 'Agent task : exécute, modifie, valide, résume',
|
|
530
|
+
'cmd.review': 'Reviewer léger : verdict, risques, tests, fichiers à inspecter',
|
|
511
531
|
'cmd.agents': 'Vue panneaux agents (temps réel)',
|
|
512
532
|
'cmd.board': 'Vue coordination : qui travaille où, notes',
|
|
513
533
|
'cmd.notes': 'Historique des notes inter-agents',
|
|
@@ -518,7 +538,7 @@ const fr = {
|
|
|
518
538
|
'timeline.activity': 'Activité',
|
|
519
539
|
'timeline.raw': 'Activité brute',
|
|
520
540
|
'timeline.empty': 'Aucune activité pour le moment.',
|
|
521
|
-
'timeline.ran': '
|
|
541
|
+
'timeline.ran': 'Exécuté',
|
|
522
542
|
'timeline.readFiles': '{count} fichier(s) lu(s)',
|
|
523
543
|
'timeline.editedFiles': '{count} fichier(s) modifié(s)',
|
|
524
544
|
'timeline.wroteFiles': '{count} fichier(s) écrit(s)',
|
|
@@ -532,6 +552,14 @@ const fr = {
|
|
|
532
552
|
'timeline.section.coordinate': 'Coordination',
|
|
533
553
|
'timeline.section.result': 'Résultat',
|
|
534
554
|
'timeline.section.other': 'Activité',
|
|
555
|
+
'timeline.narration.inspectAfterValidate': 'Ce signal ne suffit pas, donc je réinspecte le projet pour confirmer l’état réel.',
|
|
556
|
+
'timeline.narration.inspect': 'J’inspecte l’état du projet et les fichiers concernés avant de décider.',
|
|
557
|
+
'timeline.narration.change': 'Je modifie les fichiers ciblés en gardant le changement focalisé.',
|
|
558
|
+
'timeline.narration.validate': 'Je lance les validations locales pour vérifier que les changements tiennent techniquement.',
|
|
559
|
+
'timeline.narration.publish': 'Je prépare la synchronisation Git après avoir vérifié l’état local.',
|
|
560
|
+
'timeline.narration.coordinate': 'Je traite les signaux de coordination, notes, approbations et questions.',
|
|
561
|
+
'timeline.narration.result': 'Je vérifie le résultat final et les erreurs importantes.',
|
|
562
|
+
'timeline.narration.other': 'Je poursuis l’activité en cours.',
|
|
535
563
|
'cmd.send': 'Instruction à un agent (équivalent : @Agent message)',
|
|
536
564
|
'cmd.pause': 'Mettre en pause',
|
|
537
565
|
'cmd.resume': 'Reprendre',
|
|
@@ -649,7 +677,7 @@ const fr = {
|
|
|
649
677
|
'm.missingModel': 'Le provider {name} n’a pas de modèle par défaut. Utilise /settings → Modèles du provider.',
|
|
650
678
|
'm.doctorOk': '✓ Configuration prête : {pm}. Changements : /settings · modèle temporaire : /model [provider:]modèle',
|
|
651
679
|
'm.doctorReport': 'Doctor\n{lines}',
|
|
652
|
-
'm.doctorNoProvider': '✗ Aucun provider configuré. Ouvre /settings →
|
|
680
|
+
'm.doctorNoProvider': '✗ Aucun provider configuré. Ouvre /settings → Ajouter un fournisseur.',
|
|
653
681
|
'm.doctorProvider': '• Provider : {provider}:{model}',
|
|
654
682
|
'm.doctorKeyMissing': '✗ Clef API manquante. Ouvre /settings → Providers → Clef API.',
|
|
655
683
|
'm.doctorKeyOk': '✓ Clef API présente.',
|
|
@@ -704,7 +732,7 @@ const fr = {
|
|
|
704
732
|
'cost.unknown': '(prix inconnu — règle-le dans /settings → Prix des modèles)',
|
|
705
733
|
'cost.total': 'Total session :',
|
|
706
734
|
'cost.partial': '(partiel — certains modèles n’ont pas de prix connu)',
|
|
707
|
-
'cost.hint': 'Prix en USD.
|
|
735
|
+
'cost.hint': 'Prix en USD. Surcharge par modèle : /settings → Prix des modèles. Les sessions restaurées gardent leur historique de coût.',
|
|
708
736
|
'skills.title': '🧩 SKILLS — instructions réutilisables que les agents peuvent charger',
|
|
709
737
|
'skills.empty': 'Aucun skill. Crée-en un : /skill new <nom> [global], puis édite le fichier .md.',
|
|
710
738
|
'skills.hint1': 'Fichiers : ~/.parallel/skills/*.md (global) · <projet>/.parallel/skills/*.md (projet).',
|
|
@@ -853,6 +881,11 @@ const es = {
|
|
|
853
881
|
'board.none': '(sin agentes)',
|
|
854
882
|
'board.activity': 'Quién trabaja dónde (actividad de archivos, sin bloqueos):',
|
|
855
883
|
'board.workMap': 'Avisos del mapa de trabajo:',
|
|
884
|
+
'board.workMapOk': 'Mapa de trabajo: no hay reclamos superpuestos.',
|
|
885
|
+
'board.warningMeta': '{agents} · {paths} · {time}',
|
|
886
|
+
'board.warningSuggestion': 'Sugerencia: /focus agente o /diff antes de editar la misma zona.',
|
|
887
|
+
'board.secondsAgo': 'hace {n}s',
|
|
888
|
+
'board.minutesAgo': 'hace {n}min',
|
|
856
889
|
'board.noActivity': '(sin modificaciones por ahora)',
|
|
857
890
|
'board.notes': 'Últimas notas:',
|
|
858
891
|
'notes.title': '✉ NOTAS ENTRE AGENTES (últimas 30)',
|
|
@@ -878,6 +911,7 @@ const es = {
|
|
|
878
911
|
'help.keys': 'Esc: volver a la vista de agentes · Tab: autocompletar · PgUp/PgDn: scroll hub/focus · ↑/↓: sugerencias, historial o scroll de vista · Ctrl+V: pegar imagen',
|
|
879
912
|
'cmd.ask': 'Agente ask: responde y aconseja sin editar',
|
|
880
913
|
'cmd.task': 'Agente task: ejecuta, edita, valida y resume',
|
|
914
|
+
'cmd.review': 'Revisor ligero: veredicto, riesgos, pruebas y archivos a inspeccionar',
|
|
881
915
|
'cmd.agents': 'Vista de paneles de agentes (tiempo real)',
|
|
882
916
|
'cmd.board': 'Vista de coordinación: quién trabaja dónde, notas',
|
|
883
917
|
'cmd.notes': 'Historial de notas entre agentes',
|
|
@@ -888,7 +922,7 @@ const es = {
|
|
|
888
922
|
'timeline.activity': 'Actividad',
|
|
889
923
|
'timeline.raw': 'Actividad sin procesar',
|
|
890
924
|
'timeline.empty': 'Aún no hay actividad.',
|
|
891
|
-
'timeline.ran': '
|
|
925
|
+
'timeline.ran': 'Ejecutó',
|
|
892
926
|
'timeline.readFiles': '{count} archivo(s) leído(s)',
|
|
893
927
|
'timeline.editedFiles': '{count} archivo(s) editado(s)',
|
|
894
928
|
'timeline.wroteFiles': '{count} archivo(s) escrito(s)',
|
|
@@ -902,6 +936,14 @@ const es = {
|
|
|
902
936
|
'timeline.section.coordinate': 'Coordinando',
|
|
903
937
|
'timeline.section.result': 'Resultado',
|
|
904
938
|
'timeline.section.other': 'Actividad',
|
|
939
|
+
'timeline.narration.inspectAfterValidate': 'Esa señal no basta, así que vuelvo a inspeccionar el proyecto para confirmar el estado real.',
|
|
940
|
+
'timeline.narration.inspect': 'Inspecciono el estado del proyecto y los archivos relevantes antes de decidir.',
|
|
941
|
+
'timeline.narration.change': 'Modifico los archivos objetivo manteniendo el cambio enfocado.',
|
|
942
|
+
'timeline.narration.validate': 'Ejecuto validaciones locales para comprobar que los cambios se sostienen técnicamente.',
|
|
943
|
+
'timeline.narration.publish': 'Preparo la sincronización Git después de revisar el estado local.',
|
|
944
|
+
'timeline.narration.coordinate': 'Gestiono señales de coordinación, notas, aprobaciones y preguntas.',
|
|
945
|
+
'timeline.narration.result': 'Reviso el resultado final y los errores importantes.',
|
|
946
|
+
'timeline.narration.other': 'Continúo la actividad en curso.',
|
|
905
947
|
'cmd.send': 'Instrucción a un agente (igual que: @Agente mensaje)',
|
|
906
948
|
'cmd.pause': 'Pausar',
|
|
907
949
|
'cmd.resume': 'Reanudar',
|
|
@@ -953,9 +995,9 @@ const es = {
|
|
|
953
995
|
'm.saved': '💾 Sesión guardada.',
|
|
954
996
|
'm.savedAs': '💾 Sesión guardada como "{name}".',
|
|
955
997
|
'm.usageFocus': 'Uso: /focus <agente|off>',
|
|
956
|
-
'm.focusOn': '🎯
|
|
957
|
-
'm.focusOff': '🎯
|
|
958
|
-
'm.focusHint': '🎯
|
|
998
|
+
'm.focusOn': '🎯 Enfoque en {name} — el texto simple se le envía. Esc o /focus off para salir.',
|
|
999
|
+
'm.focusOff': '🎯 Enfoque desactivado.',
|
|
1000
|
+
'm.focusHint': '🎯 enfoque {name} · escribe para hablarle · PgUp/PgDn desplazamiento · Esc para salir',
|
|
959
1001
|
'm.usageRestore': 'Uso: /restore <agente> (tras cargar una sesión con /session)',
|
|
960
1002
|
'm.restored': '⏪ {name} relanzado con su conversación anterior.',
|
|
961
1003
|
'm.noRestoredAgent': 'No hay ningún agente llamado "{name}" en la sesión restaurada.',
|
|
@@ -1019,7 +1061,7 @@ const es = {
|
|
|
1019
1061
|
'm.missingModel': 'El proveedor {name} no tiene modelo por defecto. Usa /settings → Modelos del proveedor.',
|
|
1020
1062
|
'm.doctorOk': '✓ Configuración lista: {pm}. Cambios: /settings · modelo temporal: /model [proveedor:]modelo',
|
|
1021
1063
|
'm.doctorReport': 'Doctor\n{lines}',
|
|
1022
|
-
'm.doctorNoProvider': '✗ No hay proveedor configurado. Abre /settings →
|
|
1064
|
+
'm.doctorNoProvider': '✗ No hay proveedor configurado. Abre /settings → Añadir un proveedor.',
|
|
1023
1065
|
'm.doctorProvider': '• Proveedor: {provider}:{model}',
|
|
1024
1066
|
'm.doctorKeyMissing': '✗ Falta la clave API. Abre /settings → Providers → Clave API.',
|
|
1025
1067
|
'm.doctorKeyOk': '✓ Clave API presente.',
|
|
@@ -1074,7 +1116,7 @@ const es = {
|
|
|
1074
1116
|
'cost.unknown': '(precio desconocido — defínelo en /settings → Precios de modelos)',
|
|
1075
1117
|
'cost.total': 'Total de la sesión:',
|
|
1076
1118
|
'cost.partial': '(parcial — algunos modelos no tienen precio conocido)',
|
|
1077
|
-
'cost.hint': 'Precios en USD.
|
|
1119
|
+
'cost.hint': 'Precios en USD. Sobrescritura por modelo: /settings → Precios de modelos. Las sesiones restauradas conservan su historial de costes.',
|
|
1078
1120
|
'skills.title': '🧩 SKILLS — instrucciones reutilizables que los agentes pueden cargar',
|
|
1079
1121
|
'skills.empty': 'Sin skills. Crea uno: /skill new <nombre> [global], luego edita el archivo .md.',
|
|
1080
1122
|
'skills.hint1': 'Archivos: ~/.parallel/skills/*.md (global) · <proyecto>/.parallel/skills/*.md (proyecto).',
|
|
@@ -1223,6 +1265,11 @@ const zh = {
|
|
|
1223
1265
|
'board.none': '(无智能体)',
|
|
1224
1266
|
'board.activity': '谁在哪里工作(文件活动,无锁定):',
|
|
1225
1267
|
'board.workMap': '工作地图提醒:',
|
|
1268
|
+
'board.workMapOk': '工作地图:没有重叠的声明区域。',
|
|
1269
|
+
'board.warningMeta': '{agents} · {paths} · {time}',
|
|
1270
|
+
'board.warningSuggestion': '建议:编辑同一区域前先用 /focus agent 或 /diff。',
|
|
1271
|
+
'board.secondsAgo': '{n} 秒前',
|
|
1272
|
+
'board.minutesAgo': '{n} 分钟前',
|
|
1226
1273
|
'board.noActivity': '(暂无修改)',
|
|
1227
1274
|
'board.notes': '最新便签:',
|
|
1228
1275
|
'notes.title': '✉ 智能体间便签(最近 30 条)',
|
|
@@ -1248,6 +1295,7 @@ const zh = {
|
|
|
1248
1295
|
'help.keys': 'Esc:返回智能体视图 · Tab:补全 · PgUp/PgDn:hub/focus 滚动 · ↑/↓:建议、历史或视图滚动 · Ctrl+V:粘贴图片',
|
|
1249
1296
|
'cmd.ask': 'Ask 智能体:只回答和建议,不编辑',
|
|
1250
1297
|
'cmd.task': 'Task 智能体:执行、编辑、验证并总结',
|
|
1298
|
+
'cmd.review': '轻量审核智能体:结论、风险、测试和需检查文件',
|
|
1251
1299
|
'cmd.agents': '智能体面板视图(实时)',
|
|
1252
1300
|
'cmd.board': '协调视图:谁在哪里工作、便签',
|
|
1253
1301
|
'cmd.notes': '智能体间便签历史',
|
|
@@ -1258,7 +1306,7 @@ const zh = {
|
|
|
1258
1306
|
'timeline.activity': '活动',
|
|
1259
1307
|
'timeline.raw': '原始活动',
|
|
1260
1308
|
'timeline.empty': '暂无活动。',
|
|
1261
|
-
'timeline.ran': '
|
|
1309
|
+
'timeline.ran': '已执行',
|
|
1262
1310
|
'timeline.readFiles': '读取 {count} 个文件',
|
|
1263
1311
|
'timeline.editedFiles': '编辑 {count} 个文件',
|
|
1264
1312
|
'timeline.wroteFiles': '写入 {count} 个文件',
|
|
@@ -1272,6 +1320,14 @@ const zh = {
|
|
|
1272
1320
|
'timeline.section.coordinate': '协调',
|
|
1273
1321
|
'timeline.section.result': '结果',
|
|
1274
1322
|
'timeline.section.other': '活动',
|
|
1323
|
+
'timeline.narration.inspectAfterValidate': '这个信号还不够,所以我会重新检查项目以确认真实状态。',
|
|
1324
|
+
'timeline.narration.inspect': '我先检查项目状态和相关文件,再做决定。',
|
|
1325
|
+
'timeline.narration.change': '我正在修改目标文件,并保持改动聚焦。',
|
|
1326
|
+
'timeline.narration.validate': '我运行本地验证,确认改动在技术上成立。',
|
|
1327
|
+
'timeline.narration.publish': '我会在确认本地状态后准备 Git 同步。',
|
|
1328
|
+
'timeline.narration.coordinate': '我处理协调信号、便签、审批和问题。',
|
|
1329
|
+
'timeline.narration.result': '我检查最终结果和重要错误。',
|
|
1330
|
+
'timeline.narration.other': '我继续当前活动。',
|
|
1275
1331
|
'cmd.send': '向某个智能体发指令(等同于:@智能体 消息)',
|
|
1276
1332
|
'cmd.pause': '暂停',
|
|
1277
1333
|
'cmd.resume': '恢复',
|
|
@@ -1389,7 +1445,7 @@ const zh = {
|
|
|
1389
1445
|
'm.missingModel': '提供商 {name} 没有默认模型。使用 /settings → 提供商模型。',
|
|
1390
1446
|
'm.doctorOk': '✓ 配置就绪:{pm}。修改:/settings · 临时模型:/model [提供商:]模型',
|
|
1391
1447
|
'm.doctorReport': 'Doctor\n{lines}',
|
|
1392
|
-
'm.doctorNoProvider': '✗ 未配置提供商。打开 /settings →
|
|
1448
|
+
'm.doctorNoProvider': '✗ 未配置提供商。打开 /settings → 添加提供商。',
|
|
1393
1449
|
'm.doctorProvider': '• 提供商:{provider}:{model}',
|
|
1394
1450
|
'm.doctorKeyMissing': '✗ 缺少 API 密钥。打开 /settings → Providers → API 密钥。',
|
|
1395
1451
|
'm.doctorKeyOk': '✓ API 密钥已设置。',
|
|
@@ -1479,7 +1535,7 @@ const zh = {
|
|
|
1479
1535
|
// provider-pick section headers
|
|
1480
1536
|
'wiz.provider.section.configured': '已配置的提供商',
|
|
1481
1537
|
'wiz.provider.section.western': '西方',
|
|
1482
|
-
'wiz.provider.section.chinese': '
|
|
1538
|
+
'wiz.provider.section.chinese': '国产',
|
|
1483
1539
|
'wiz.provider.section.gateways': '网关',
|
|
1484
1540
|
'wiz.provider.section.inference': '推理',
|
|
1485
1541
|
'wiz.provider.section.local': '本地模型',
|
package/dist/index.js
CHANGED
|
@@ -59,6 +59,7 @@ Environment variables:
|
|
|
59
59
|
Inside the TUI:
|
|
60
60
|
<task> + Enter Launch agent N+1 — even while the others are working
|
|
61
61
|
@a1 <message> Real-time instruction to an agent (@all for everyone)
|
|
62
|
+
/review [agent|all] Ask-mode reviewer: verdict, risks, tests, files
|
|
62
63
|
/project [folder] Change project folder or reopen the folder picker
|
|
63
64
|
/wizard Relaunch the setup wizard
|
|
64
65
|
/help All commands
|
package/dist/ui/AgentPanel.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useRef, useState, useEffect } from 'react';
|
|
3
3
|
import { Box, Text } from 'ink';
|
|
4
4
|
import { fmtCost } from '../pricing.js';
|
|
@@ -28,24 +28,68 @@ export function cleanHubSummary(text) {
|
|
|
28
28
|
}
|
|
29
29
|
export function formatAgentTelemetry(agent) {
|
|
30
30
|
const ctx = agent.ctxPct !== undefined ? ` · ${agent.ctxPct}% ctx` : '';
|
|
31
|
-
|
|
31
|
+
const perf = agent.perf ? ` · ${agent.perf.modelTurns}t/${agent.perf.toolCalls} tools` : '';
|
|
32
|
+
return `${elapsed(agent.startedAt)}${ctx}${perf} · ${agent.cost === null ? '$-' : fmtCost(agent.cost)}`;
|
|
32
33
|
}
|
|
33
|
-
function
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
34
|
+
function firstSectionLine(text, labels) {
|
|
35
|
+
const lines = text.replace(/\r/g, '').split('\n');
|
|
36
|
+
for (let i = 0; i < lines.length; i++) {
|
|
37
|
+
const clean = lines[i].replace(/^#{1,6}\s+/, '').replace(/\*\*/g, '').trim();
|
|
38
|
+
const match = clean.match(/^([^:]+):?\s*(.*)$/);
|
|
39
|
+
if (!match)
|
|
40
|
+
continue;
|
|
41
|
+
const label = match[1].toLowerCase();
|
|
42
|
+
if (!labels.some((l) => label.includes(l)))
|
|
43
|
+
continue;
|
|
44
|
+
const inline = cleanHubSummary(match[2] ?? '');
|
|
45
|
+
if (inline)
|
|
46
|
+
return inline;
|
|
47
|
+
const next = cleanHubSummary(lines.slice(i + 1).find((l) => l.trim()) ?? '');
|
|
48
|
+
if (next)
|
|
49
|
+
return next;
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
function fileSummary(text) {
|
|
54
|
+
const paths = [...text.matchAll(/\b(?:src|test|tests|bin|scripts|docs|\.parallel|\.cursor)\/[A-Za-z0-9._/-]+|\b[A-Za-z0-9._-]+\.(?:ts|tsx|js|mjs|json|md|sh)\b/g)]
|
|
55
|
+
.map((m) => m[0])
|
|
56
|
+
.filter((p, i, arr) => arr.indexOf(p) === i)
|
|
57
|
+
.slice(0, 3);
|
|
58
|
+
if (paths.length === 0)
|
|
59
|
+
return null;
|
|
60
|
+
return paths.join(', ');
|
|
61
|
+
}
|
|
62
|
+
export function hubSummaryLines(text, maxLines = 4, maxWidth = 100) {
|
|
63
|
+
const cleanLines = text
|
|
64
|
+
.replace(/\r/g, '')
|
|
65
|
+
.split('\n')
|
|
66
|
+
.map(cleanHubSummary)
|
|
67
|
+
.filter(Boolean);
|
|
68
|
+
const outcome = firstSectionLine(text, ['ce que j', 'résultat', 'outcome', 'what i did', 'réponse courte', 'mode task']) ?? cleanLines[0] ?? 'Task complete.';
|
|
69
|
+
const validation = firstSectionLine(text, ['validation', 'vérifié', 'verified', 'tests']);
|
|
70
|
+
const files = firstSectionLine(text, ['fichiers', 'files']) ?? fileSummary(text);
|
|
71
|
+
const risks = firstSectionLine(text, ['risque', 'risk', 'caveat', 'problème', 'remaining']);
|
|
72
|
+
const candidates = [
|
|
73
|
+
outcome,
|
|
74
|
+
validation ? `Validation: ${validation}` : null,
|
|
75
|
+
files ? `Files: ${files}` : null,
|
|
76
|
+
risks ? `Risks: ${risks}` : null,
|
|
77
|
+
].filter((line) => Boolean(line));
|
|
78
|
+
const out = [];
|
|
79
|
+
for (const line of candidates) {
|
|
80
|
+
const normalized = truncate(line.replace(/^[•\-✓]\s*/, ''), maxWidth);
|
|
81
|
+
if (!out.includes(normalized))
|
|
82
|
+
out.push(normalized);
|
|
83
|
+
if (out.length >= maxLines)
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
return out;
|
|
43
87
|
}
|
|
44
88
|
function ResultBlock({ agent, compact = false }) {
|
|
45
89
|
if (!agent.lastResult)
|
|
46
90
|
return null;
|
|
47
91
|
if (compact) {
|
|
48
|
-
return (_jsxs(Text, { wrap: "truncate-end", children: [
|
|
92
|
+
return (_jsxs(Text, { wrap: "truncate-end", children: [_jsx(Text, { color: COLOR.cream, children: "\u2022 " }), _jsx(Text, { children: truncate(agent.lastResult, 110) })] }));
|
|
49
93
|
}
|
|
50
94
|
return (_jsxs(Box, { borderStyle: "single", borderColor: "gray", flexDirection: "column", paddingX: 1, marginTop: 1, children: [_jsx(Text, { color: UI.ok, bold: true, children: "Result" }), _jsx(Md, { text: agent.lastResult })] }));
|
|
51
95
|
}
|
|
@@ -55,16 +99,27 @@ function spinnerColor(state) {
|
|
|
55
99
|
return COLOR.cream;
|
|
56
100
|
return 'yellow'; // thinking, listening, waiting
|
|
57
101
|
}
|
|
58
|
-
function
|
|
102
|
+
export function modeBadge(mode) {
|
|
59
103
|
if (mode === 'ask')
|
|
60
|
-
return {
|
|
104
|
+
return { label: 'ASK', color: MODE.ask };
|
|
61
105
|
if (mode === 'plan')
|
|
62
|
-
return {
|
|
63
|
-
return
|
|
106
|
+
return { label: 'PLAN', color: MODE.plan };
|
|
107
|
+
return { label: 'TASK', color: MODE.task };
|
|
64
108
|
}
|
|
65
109
|
function agentDisplayName(agent) {
|
|
66
110
|
return agent.alias && agent.alias !== agent.name ? `${agent.alias} ${agent.name}` : agent.alias || agent.name;
|
|
67
111
|
}
|
|
112
|
+
export function ProgressSteps({ agent, max = 4, cols = 100 }) {
|
|
113
|
+
const steps = agent.progressSteps?.slice(0, max) ?? [];
|
|
114
|
+
if (steps.length === 0)
|
|
115
|
+
return null;
|
|
116
|
+
const textMax = Math.max(20, cols - 8);
|
|
117
|
+
return (_jsx(Box, { flexDirection: "column", children: steps.map((step, i) => {
|
|
118
|
+
const active = step.status === 'active';
|
|
119
|
+
const done = step.status === 'done';
|
|
120
|
+
return (_jsxs(Text, { color: done ? UI.ok : active ? COLOR.cream : UI.muted, wrap: "truncate-end", children: [_jsxs(Text, { color: done ? UI.ok : active ? COLOR.cream : UI.muted, children: [done ? MARK.done : active ? MARK.active : MARK.idle, " "] }), truncate(step.text, textMax)] }, `${i}-${step.text}`));
|
|
121
|
+
}) }));
|
|
122
|
+
}
|
|
68
123
|
export function AgentRow({ agent, logs, cols, }) {
|
|
69
124
|
const meta = STATE_META[agent.state];
|
|
70
125
|
// ── State transition pulse (Phase 5) ──
|
|
@@ -81,24 +136,31 @@ export function AgentRow({ agent, logs, cols, }) {
|
|
|
81
136
|
// Pulse bumps the mark/spinner color to whiteBright for 400ms
|
|
82
137
|
const pulseColor = pulse ? 'whiteBright' : null;
|
|
83
138
|
const name = agentDisplayName(agent);
|
|
84
|
-
const mode =
|
|
85
|
-
const
|
|
139
|
+
const mode = modeBadge(agent.mode);
|
|
140
|
+
const quickActions = `full /focus ${agent.alias || agent.name} · term /attach ${agent.alias || agent.name}`;
|
|
141
|
+
const actionBudget = Math.min(44, quickActions.length + 2);
|
|
142
|
+
const taskMax = Math.max(10, cols - 18 - actionBudget);
|
|
86
143
|
const line2Max = Math.max(10, cols - 2);
|
|
87
144
|
const telemetry = formatAgentTelemetry(agent);
|
|
88
145
|
const signal = latestSignal(agent, toUIEvents(logs));
|
|
89
146
|
const specialist = agent.specialist ? ` #${agent.specialist}` : '';
|
|
147
|
+
const claims = agent.claims && agent.claims.length > 0 ? `⚑ ${truncate(agent.claims.join(', '), Math.max(12, Math.floor(line2Max * 0.35)))}` : '';
|
|
148
|
+
const summary = agent.lastResult ? hubSummaryLines(agent.lastResult, 4, Math.max(20, line2Max - 4)) : [];
|
|
90
149
|
let line2 = null;
|
|
91
|
-
if (agent.lastResult) {
|
|
92
|
-
|
|
150
|
+
if (!agent.lastResult && signal && signal !== agent.task) {
|
|
151
|
+
const detail = claims ? `${truncate(signal, Math.max(10, line2Max - claims.length - 4))} · ${claims}` : signal;
|
|
152
|
+
line2 = { text: `▸ ${truncate(detail, line2Max)}`, color: UI.accent };
|
|
93
153
|
}
|
|
94
|
-
else if (
|
|
95
|
-
line2 = { text:
|
|
154
|
+
else if (claims) {
|
|
155
|
+
line2 = { text: claims, color: UI.warn };
|
|
96
156
|
}
|
|
97
|
-
return (_jsxs(Box, { flexDirection: "column", marginBottom: 0, paddingLeft: 1, children: [_jsxs(Text, { wrap: "truncate-end", children: [SPINNER_STATES.has(agent.state) ? (_jsx(Spinner, { color: pulseColor ?? spinnerColor(agent.state) })) : (_jsx(Text, { color: pulseColor ?? meta.color, bold: true, children: meta.mark })), _jsx(Text, { children: " " }), _jsx(Text, { color: agent.color, bold: true, children: name }),
|
|
157
|
+
return (_jsxs(Box, { flexDirection: "column", marginBottom: 0, paddingLeft: 1, children: [_jsxs(Box, { flexDirection: "row", justifyContent: "space-between", children: [_jsxs(Text, { wrap: "truncate-end", children: [SPINNER_STATES.has(agent.state) ? (_jsx(Spinner, { color: pulseColor ?? spinnerColor(agent.state) })) : (_jsx(Text, { color: pulseColor ?? meta.color, bold: true, children: meta.mark })), _jsx(Text, { children: " " }), _jsx(Text, { color: agent.color, bold: true, children: name }), _jsxs(Text, { color: mode.color, children: [" [", mode.label, "]"] }), specialist ? _jsx(Text, { color: UI.note, children: specialist }) : null, _jsxs(Text, { color: UI.text, children: [" ", truncate(agent.task, taskMax)] })] }), _jsx(Text, { color: UI.muted, wrap: "truncate-end", children: truncate(quickActions, actionBudget) })] }), summary.length > 0 ? (_jsx(Box, { flexDirection: "column", children: summary.map((line, i) => (_jsxs(Box, { flexDirection: "row", justifyContent: i === 0 ? 'space-between' : undefined, children: [_jsxs(Text, { color: COLOR.cream, wrap: "truncate-end", children: [_jsx(Text, { color: COLOR.cream, children: "\u2022 " }), line] }), i === 0 ? _jsx(Text, { color: UI.muted, children: telemetry }) : null] }, `${i}-${line}`))) })) : line2 ? (_jsxs(Box, { flexDirection: "row", justifyContent: "space-between", children: [_jsx(Text, { color: line2.color, wrap: "truncate-end", children: line2.text }), _jsx(Text, { color: UI.muted, children: telemetry })] })) : null, !agent.lastResult ? _jsx(ProgressSteps, { agent: agent, max: 3, cols: line2Max }) : null] }));
|
|
98
158
|
}
|
|
99
159
|
export function AgentTranscript({ agent, logs, raw = false, scrolled = 0, cols = 100, }) {
|
|
100
160
|
const meta = STATE_META[agent.state];
|
|
101
|
-
|
|
161
|
+
const mode = modeBadge(agent.mode);
|
|
162
|
+
const shell = agent.perf && agent.perf.shellCommands > 0 ? ` · shell ${agent.perf.shellCommands}/${Math.round(agent.perf.shellMs / 1000)}s` : '';
|
|
163
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { justifyContent: "space-between", children: [_jsxs(Text, { children: [_jsx(Text, { color: agent.color, bold: true, children: agent.name }), agent.alias && agent.alias !== agent.name ? _jsxs(Text, { color: UI.muted, children: [" @", agent.alias] }) : null, _jsxs(Text, { color: mode.color, children: [" [", mode.label, "]"] }), _jsx(Text, { color: UI.muted, children: " " }), _jsxs(Text, { color: meta.color, bold: true, children: [meta.mark, " ", meta.label] })] }), _jsxs(Text, { color: UI.muted, wrap: "truncate-end", children: [agent.model, " \u00B7 ", formatAgentTelemetry(agent), shell] })] }), _jsxs(Text, { color: UI.muted, wrap: "wrap", children: ["Task ", _jsx(Text, { color: UI.text, children: agent.task })] }), agent.claims && agent.claims.length > 0 ? (_jsxs(Text, { color: UI.warn, wrap: "truncate-end", children: ["Claims ", agent.claims.join(' ')] })) : null, agent.currentAction ? (_jsxs(Text, { color: UI.accent, wrap: "truncate-end", children: ["Current ", truncate(agent.currentAction, 140)] })) : null, _jsx(ProgressSteps, { agent: agent, max: 6, cols: cols }), agent.state === 'done' || agent.lastResult ? _jsx(ResultBlock, { agent: agent }) : null, _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { color: UI.muted, bold: true, children: ["Activity", raw ? ' raw' : ''] }), _jsx(Timeline, { logs: logs, raw: raw, cols: cols })] }), _jsxs(Text, { color: UI.muted, wrap: "truncate-end", children: ["PgUp/PgDn scroll \u00B7 /raw toggles detail \u00B7 Esc returns", scrolled > 0 ? ` · ${scrolled} older` : ''] })] }));
|
|
102
164
|
}
|
|
103
165
|
export function AgentPanel({ agent, logs, width, expanded = false, }) {
|
|
104
166
|
return (_jsx(Box, { width: width, flexDirection: "column", children: expanded ? _jsx(AgentTranscript, { agent: agent, logs: logs }) : _jsx(AgentRow, { agent: agent, logs: logs, cols: 100 }) }));
|
package/dist/ui/App.js
CHANGED
|
@@ -539,7 +539,7 @@ function MainScreen({ ctl, folder, view, focus, rawLogs, systemLines, agentNames
|
|
|
539
539
|
: systemLines).reduce((sum, l) => sum + l.text.split('\n').length, 0)
|
|
540
540
|
: 0;
|
|
541
541
|
const inputLines = inputRows;
|
|
542
|
-
const spacerLines =
|
|
542
|
+
const spacerLines = 2;
|
|
543
543
|
const approvalHeight = approval ? 6 : 0;
|
|
544
544
|
const questionHeight = question ? 7 : 0;
|
|
545
545
|
const bodyHeight = Math.max(1, rows - headerLines - footerLines - systemMsgLines - inputLines - spacerLines - approvalHeight - questionHeight);
|
|
@@ -627,6 +627,8 @@ function MainScreen({ ctl, folder, view, focus, rawLogs, systemLines, agentNames
|
|
|
627
627
|
const provider = ctl.sessionProvider();
|
|
628
628
|
const providerModel = provider ? `${provider.name}:${ctl.session.model}` : 'no model';
|
|
629
629
|
const headerColor = view === 'agents' && agents.length === 0 ? BRAND.muted : CHROME.muted;
|
|
630
|
+
const workMapAlerts = ctl.board.workMapWarnings.filter((w) => w.level !== 'info');
|
|
631
|
+
const conflictAlerts = workMapAlerts.filter((w) => w.level === 'conflict');
|
|
630
632
|
// View breadcrumb: when not in agents view, show the view name instead of "control room".
|
|
631
633
|
const VIEW_LABEL = {
|
|
632
634
|
agents: 'control room',
|
|
@@ -642,7 +644,7 @@ function MainScreen({ ctl, folder, view, focus, rawLogs, systemLines, agentNames
|
|
|
642
644
|
specialists: 'specialists',
|
|
643
645
|
};
|
|
644
646
|
const viewLabel = VIEW_LABEL[view] ?? 'control room';
|
|
645
|
-
return (_jsxs(Box, { flexDirection: "column", height: rows, children: [_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: headerColor, children: ["\u256D", '─'.repeat(cols - 2), "\u256E"] }), _jsxs(Box, { flexDirection: "row", width: cols, children: [_jsx(Text, { color: headerColor, children: "\u2502 " }), _jsxs(Box, { flexDirection: "row", width: cols - 4, justifyContent: "space-between", children: [_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { bold: true, color: BRAND.primary, children: "PARALLEL" }), _jsx(Text, { color: globalDotColor, children: " \u25CF" }), _jsxs(Text, { color: view === 'agents' ? CHROME.muted : BRAND.muted, children: [" ", agents.length === 0 ? 'ready' : viewLabel] }), rawLogs && focused ? _jsx(Text, { color: UI.warn, children: " [RAW]" }) : null] }), _jsx(Text, { color: CHROME.muted, children: middleTruncate(folder, folderMax) })] }), _jsx(Text, { color: headerColor, children: " \u2502" })] }), _jsxs(Box, { flexDirection: "row", width: cols, children: [_jsx(Text, { color: headerColor, children: "\u2502 " }), _jsxs(Box, { flexDirection: "row", width: cols - 4, justifyContent: "space-between", children: [agents.length > 0 ? (_jsx(Box, { flexDirection: "row", children: _jsxs(Text, { children: [_jsxs(Text, { color: CHROME.muted, children: ["\u25C7 ", idleCount, " idle"] }), ' · ', _jsxs(Text, { color: workingCount > 0 ? STATE.working : CHROME.muted, children: ["\u25CF ", workingCount, " active"] }), ' · ', _jsxs(Text, { color: doneCount > 0 ? STATE.done : CHROME.muted, children: ["\u2713 ", doneCount, " done"] }), ' · ', _jsxs(Text, { color: errorCount > 0 ? STATE.error : CHROME.muted, children: ["\u2717 ", errorCount, " err"] })] }) })) : (_jsx(Text, { color: CHROME.muted, children: providerModel })), _jsxs(Text, { color: CHROME.muted, children: ["v", VERSION] })] }), _jsx(Text, { color: headerColor, children: " \u2502" })] }), _jsxs(Text, { color: headerColor, children: ["\u2570", '─'.repeat(cols - 2), "\u256F"] })] }), _jsx(Box, { height: bodyHeight, overflow: "hidden", flexDirection: "column", children: view === 'settings' ? (_jsx(SettingsPanel, { ctl: ctl, scope: "global", height: bodyHeight, onClose: onEscape })) : view === 'settings-session' ? (_jsx(SettingsPanel, { ctl: ctl, scope: "session", height: bodyHeight, onClose: onEscape })) : view === 'board' ? (_jsx(BoardView, { board: ctl.board, bodyHeight: bodyHeight })) : view === 'notes' ? (_jsx(NotesView, { board: ctl.board, bodyHeight: bodyHeight })) : view === 'sessions' ? (_jsx(SessionsView, { projectRoot: ctl.projectRoot, bodyHeight: bodyHeight })) : view === 'diff' ? (_jsx(DiffView, { board: ctl.board, bodyHeight: bodyHeight })) : view === 'cost' ? (_jsx(CostView, { board: ctl.board, bodyHeight: bodyHeight })) : view === 'skills' ? (_jsx(SkillsView, { skills: ctl.getSkills(), bodyHeight: bodyHeight })) : view === 'specialists' ? (_jsx(SpecialistsView, { specialists: ctl.getSpecialists(), bodyHeight: bodyHeight })) : view === 'help' ? (_jsx(HelpView, { bodyHeight: bodyHeight, onSelect: (cmd) => onInput(cmd) })) : agents.length === 0 ? (_jsx(EmptyHub, { bodyHeight: bodyHeight })) : focused ? (_jsxs(Box, { flexDirection: "column", children: [_jsx(AgentTranscript, { agent: focused, logs: visibleLogs, raw: rawLogs, scrolled: clampedScroll, cols: cols }), !focusFollowTail ? _jsx(Text, { color: UI.warn, children: "Viewing older \u00B7 PgDn to latest" }) : null] })) : (_jsx(AgentHub, { agents: agents, ctl: ctl, cols: cols, scroll: clampedHub, visibleRows: hubRows })) }), systemLines.length > 0 && !settingsOpen && (_jsxs(Box, { flexDirection: "column", children: [agents.length > 0 ? _jsx(Text, { color: UI.muted, bold: true, children: "Session" }) : null, (agents.length > 0
|
|
647
|
+
return (_jsxs(Box, { flexDirection: "column", height: rows, children: [_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: headerColor, children: ["\u256D", '─'.repeat(cols - 2), "\u256E"] }), _jsxs(Box, { flexDirection: "row", width: cols, children: [_jsx(Text, { color: headerColor, children: "\u2502 " }), _jsxs(Box, { flexDirection: "row", width: cols - 4, justifyContent: "space-between", children: [_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { bold: true, color: BRAND.primary, children: "PARALLEL" }), _jsx(Text, { color: globalDotColor, children: " \u25CF" }), _jsxs(Text, { color: view === 'agents' ? CHROME.muted : BRAND.muted, children: [" ", agents.length === 0 ? 'ready' : viewLabel] }), rawLogs && focused ? _jsx(Text, { color: UI.warn, children: " [RAW]" }) : null] }), _jsx(Text, { color: CHROME.muted, children: middleTruncate(folder, folderMax) })] }), _jsx(Text, { color: headerColor, children: " \u2502" })] }), _jsxs(Box, { flexDirection: "row", width: cols, children: [_jsx(Text, { color: headerColor, children: "\u2502 " }), _jsxs(Box, { flexDirection: "row", width: cols - 4, justifyContent: "space-between", children: [agents.length > 0 ? (_jsx(Box, { flexDirection: "row", children: _jsxs(Text, { children: [_jsxs(Text, { color: CHROME.muted, children: ["\u25C7 ", idleCount, " idle"] }), ' · ', _jsxs(Text, { color: workingCount > 0 ? STATE.working : CHROME.muted, children: ["\u25CF ", workingCount, " active"] }), ' · ', _jsxs(Text, { color: doneCount > 0 ? STATE.done : CHROME.muted, children: ["\u2713 ", doneCount, " done"] }), ' · ', _jsxs(Text, { color: errorCount > 0 ? STATE.error : CHROME.muted, children: ["\u2717 ", errorCount, " err"] }), workMapAlerts.length > 0 ? (_jsxs(Text, { color: conflictAlerts.length > 0 ? UI.danger : UI.warn, children: [" \u00B7 \u26A0 work-map ", workMapAlerts.length] })) : null] }) })) : (_jsx(Text, { color: CHROME.muted, children: providerModel })), _jsxs(Text, { color: CHROME.muted, children: ["v", VERSION] })] }), _jsx(Text, { color: headerColor, children: " \u2502" })] }), _jsxs(Text, { color: headerColor, children: ["\u2570", '─'.repeat(cols - 2), "\u256F"] })] }), _jsx(Box, { height: bodyHeight, overflow: "hidden", flexDirection: "column", children: view === 'settings' ? (_jsx(SettingsPanel, { ctl: ctl, scope: "global", height: bodyHeight, onClose: onEscape })) : view === 'settings-session' ? (_jsx(SettingsPanel, { ctl: ctl, scope: "session", height: bodyHeight, onClose: onEscape })) : view === 'board' ? (_jsx(BoardView, { board: ctl.board, bodyHeight: bodyHeight })) : view === 'notes' ? (_jsx(NotesView, { board: ctl.board, bodyHeight: bodyHeight })) : view === 'sessions' ? (_jsx(SessionsView, { projectRoot: ctl.projectRoot, bodyHeight: bodyHeight })) : view === 'diff' ? (_jsx(DiffView, { board: ctl.board, bodyHeight: bodyHeight })) : view === 'cost' ? (_jsx(CostView, { board: ctl.board, bodyHeight: bodyHeight })) : view === 'skills' ? (_jsx(SkillsView, { skills: ctl.getSkills(), bodyHeight: bodyHeight })) : view === 'specialists' ? (_jsx(SpecialistsView, { specialists: ctl.getSpecialists(), bodyHeight: bodyHeight })) : view === 'help' ? (_jsx(HelpView, { bodyHeight: bodyHeight, onSelect: (cmd) => onInput(cmd) })) : agents.length === 0 ? (_jsx(EmptyHub, { bodyHeight: bodyHeight })) : focused ? (_jsxs(Box, { flexDirection: "column", children: [_jsx(AgentTranscript, { agent: focused, logs: visibleLogs, raw: rawLogs, scrolled: clampedScroll, cols: cols }), !focusFollowTail ? _jsx(Text, { color: UI.warn, children: "Viewing older \u00B7 PgDn to latest" }) : null] })) : (_jsx(AgentHub, { agents: agents, ctl: ctl, cols: cols, scroll: clampedHub, visibleRows: hubRows })) }), systemLines.length > 0 && !settingsOpen && (_jsxs(Box, { flexDirection: "column", children: [agents.length > 0 ? _jsx(Text, { color: UI.muted, bold: true, children: "Session" }) : null, (agents.length > 0
|
|
646
648
|
? systemLines
|
|
647
649
|
.filter((l) => !/^Ready|^Type a task|^⚡ Ready|^Default \/task|^Agent .* launched/.test(l.text))
|
|
648
650
|
.slice(-2)
|
|
@@ -654,9 +656,9 @@ function MainScreen({ ctl, folder, view, focus, rawLogs, systemLines, agentNames
|
|
|
654
656
|
// Split on \n so multiline i18n messages render correctly (Ink <Text> doesn't interpret \n).
|
|
655
657
|
const lines = l.text.split('\n');
|
|
656
658
|
return lines.map((line, j) => (_jsx(Text, { color: levelColor, wrap: "truncate-end", children: line }, `${i}-${j}`)));
|
|
657
|
-
})] })), approval && (_jsx(ApprovalPrompt, { request: approval, pendingCount: ctl.approvals.length, onAnswer: (id, ok, always) => ctl.answerApproval(id, ok, always) })), question && (_jsx(QuestionPrompt, { question: question, pendingCount: ctl.questions.length, onAnswer: (id, answer, auto) => ctl.answerQuestion(id, answer, auto) }, question.id)), _jsx(CommandInput, { active: inputActive, placeholder: focus ? `Message ${focus} or /command` : t('main.prompt'), context: focus ? 'focus' : 'hub', targetAgent: focused?.name, modelLabel: ctl.sessionProvider() ? `${ctl.sessionProvider()?.name}:${ctl.session.model}` : undefined, agentNames: agentNames, agents: agents, width: cols, onHeightChange: setInputRows, onSubmit: onInput, onEscape: onEscape, notify: notify }), _jsx(Box, { flexDirection: "column", children: _jsxs(Text, { children: [_jsx(Text, { color: CHROME.muted, children: "/ for commands" }), _jsx(Text, { color: CHROME.muted, children: " \u00B7 Shell " }), _jsx(Text, { color: ctl.session.approvalMode === 'ask' ? UI.warn :
|
|
659
|
+
})] })), approval && (_jsx(ApprovalPrompt, { request: approval, pendingCount: ctl.approvals.length, onAnswer: (id, ok, always) => ctl.answerApproval(id, ok, always) })), question && (_jsx(QuestionPrompt, { question: question, pendingCount: ctl.questions.length, onAnswer: (id, answer, auto) => ctl.answerQuestion(id, answer, auto) }, question.id)), _jsx(Text, { children: " " }), _jsx(CommandInput, { active: inputActive, placeholder: focus ? `Message ${focus} or /command` : t('main.prompt'), context: focus ? 'focus' : 'hub', targetAgent: focused?.name, modelLabel: ctl.sessionProvider() ? `${ctl.sessionProvider()?.name}:${ctl.session.model}` : undefined, agentNames: agentNames, agents: agents, width: cols, onHeightChange: setInputRows, onSubmit: onInput, onEscape: onEscape, notify: notify }), _jsx(Text, { children: " " }), _jsx(Box, { flexDirection: "column", children: _jsxs(Text, { children: [_jsx(Text, { color: CHROME.muted, children: "/ for commands" }), _jsx(Text, { color: CHROME.muted, children: " \u00B7 Shell " }), _jsx(Text, { color: ctl.session.approvalMode === 'ask' ? UI.warn :
|
|
658
660
|
ctl.session.approvalMode === 'yolo' ? UI.danger :
|
|
659
|
-
UI.ok, children: ctl.session.approvalMode === 'auto-safe' ? 'auto' : ctl.session.approvalMode }), agents.length > 0 ? _jsxs(Text, { color: CHROME.muted, children: [" \u00B7 Sessions ", Controller.listSessions(ctl.projectRoot).length] }) : null, ctl.questions.length > 0 ? (_jsxs(Text, { color: UI.warn, children: [" \u00B7 \u2753", ctl.questions.length] })) : null, ctl.approvals.length > 0 ? (_jsxs(Text, { color: UI.warn, children: [" \u00B7 \u23F3", ctl.approvals.length] })) : null, focused ? (_jsxs(Text, { color: BRAND.muted, children: [" \u00B7 \uD83C\uDFAF ", focused.name] })) : null] }) })] }));
|
|
661
|
+
UI.ok, children: ctl.session.approvalMode === 'auto-safe' ? 'auto' : ctl.session.approvalMode }), agents.length > 0 ? _jsxs(Text, { color: CHROME.muted, children: [" \u00B7 Sessions ", Controller.listSessions(ctl.projectRoot).length] }) : null, ctl.questions.length > 0 ? (_jsxs(Text, { color: UI.warn, children: [" \u00B7 \u2753", ctl.questions.length] })) : null, ctl.approvals.length > 0 ? (_jsxs(Text, { color: UI.warn, children: [" \u00B7 \u23F3", ctl.approvals.length] })) : null, workMapAlerts.length > 0 ? (_jsx(Text, { color: conflictAlerts.length > 0 ? UI.danger : UI.warn, children: " \u00B7 \u26A0 /board" })) : null, conflictAlerts.length > 0 ? (_jsx(Text, { color: UI.danger, children: " \u00B7 run /review all" })) : null, focused ? (_jsxs(Text, { color: BRAND.muted, children: [" \u00B7 \uD83C\uDFAF ", focused.name] })) : null] }) })] }));
|
|
660
662
|
}
|
|
661
663
|
function groupAgents(agents) {
|
|
662
664
|
const needs = agents.filter((a) => ['waiting', 'paused'].includes(a.state));
|
|
@@ -684,7 +686,10 @@ function AgentHub({ agents, ctl, cols, scroll, visibleRows, }) {
|
|
|
684
686
|
continue;
|
|
685
687
|
}
|
|
686
688
|
const needsSeparator = rows.length > 0;
|
|
687
|
-
const
|
|
689
|
+
const summaryLines = agent.lastResult ? Math.min(4, Math.max(1, agent.lastResult.split('\n').filter((l) => l.trim()).length)) : 0;
|
|
690
|
+
const stepLines = !agent.lastResult && agent.progressSteps && agent.progressSteps.length > 0 ? Math.min(3, agent.progressSteps.length) : 0;
|
|
691
|
+
const agentLines = 1 + Math.max(summaryLines, agent.currentAction || agent.claims?.length ? 1 : 0) + stepLines;
|
|
692
|
+
const neededLines = agentLines + (needsSeparator ? 1 : 0);
|
|
688
693
|
if (renderedLines + neededLines > visibleRows) {
|
|
689
694
|
full = true;
|
|
690
695
|
break;
|
|
@@ -694,7 +699,7 @@ function AgentHub({ agents, ctl, cols, scroll, visibleRows, }) {
|
|
|
694
699
|
renderedLines++;
|
|
695
700
|
}
|
|
696
701
|
renderedAgents++;
|
|
697
|
-
renderedLines +=
|
|
702
|
+
renderedLines += agentLines;
|
|
698
703
|
rows.push(_jsx(AgentRow, { agent: agent, logs: ctl.board.logsFor(agent.id, 8), cols: cols }, agent.id));
|
|
699
704
|
}
|
|
700
705
|
if (full)
|