zyn-ai 1.3.5 → 1.3.6
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/package.json +1 -1
- package/src/cli/print.js +3 -3
- package/src/core/prompts.js +25 -6
- package/src/tui/app.mjs +79 -18
package/package.json
CHANGED
package/src/cli/print.js
CHANGED
|
@@ -112,11 +112,11 @@ function pushAction(state, kind, title, detail = '') {
|
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
const EVENT_SYMBOLS = {
|
|
115
|
-
info: { sym: '
|
|
115
|
+
info: { sym: 'ⓘ', color: C.gray },
|
|
116
116
|
think: { sym: '○', color: C.gray },
|
|
117
|
-
tool: { sym: '
|
|
117
|
+
tool: { sym: '⚒', color: C.purple },
|
|
118
118
|
ok: { sym: '✓', color: C.green },
|
|
119
|
-
warn: { sym: '
|
|
119
|
+
warn: { sym: '⚠', color: C.yellow },
|
|
120
120
|
error: { sym: '✗', color: C.red },
|
|
121
121
|
};
|
|
122
122
|
|
package/src/core/prompts.js
CHANGED
|
@@ -44,6 +44,23 @@ function getPlatformInfo() {
|
|
|
44
44
|
return osName;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
|
|
48
|
+
const TOOL_ALIASES = {
|
|
49
|
+
bash: 'run_command',
|
|
50
|
+
shell: 'run_command',
|
|
51
|
+
terminal: 'run_command',
|
|
52
|
+
execute_command: 'run_command',
|
|
53
|
+
command: 'run_command',
|
|
54
|
+
run_terminal_command: 'run_command',
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
function normalizeToolName(name) {
|
|
58
|
+
const raw = String(name || '').trim();
|
|
59
|
+
if (!raw) return raw;
|
|
60
|
+
const lower = raw.toLowerCase();
|
|
61
|
+
return TOOL_ALIASES[lower] || lower;
|
|
62
|
+
}
|
|
63
|
+
|
|
47
64
|
const KNOWN_TOOLS = new Set([
|
|
48
65
|
...TOOL_DEFINITIONS.map(tool => tool.name),
|
|
49
66
|
'task_create', 'task_list', 'task_update', 'task_complete', 'task_delete', 'task_clear',
|
|
@@ -238,7 +255,7 @@ function extractXmlTool(text) {
|
|
|
238
255
|
);
|
|
239
256
|
if (!invokeMatch) return null;
|
|
240
257
|
|
|
241
|
-
const tool = invokeMatch[1];
|
|
258
|
+
const tool = normalizeToolName(invokeMatch[1]);
|
|
242
259
|
if (!KNOWN_TOOLS.has(tool)) return null;
|
|
243
260
|
|
|
244
261
|
const rawArgs = invokeMatch[2].trim();
|
|
@@ -257,13 +274,15 @@ function extractXmlTool(text) {
|
|
|
257
274
|
|
|
258
275
|
function classifyParsed(parsed) {
|
|
259
276
|
if (parsed?.type === 'tool' && parsed.tool) {
|
|
260
|
-
|
|
277
|
+
const normalized = normalizeToolName(parsed.tool);
|
|
278
|
+
if (KNOWN_TOOLS.has(normalized)) return { type: 'tool', tool: normalized, args: parsed.args ?? {} };
|
|
261
279
|
}
|
|
262
280
|
if (parsed?.type === 'final') {
|
|
263
281
|
return { type: 'final', content: typeof parsed.content === 'string' ? parsed.content : '' };
|
|
264
282
|
}
|
|
265
|
-
if (parsed?.tool
|
|
266
|
-
|
|
283
|
+
if (parsed?.tool) {
|
|
284
|
+
const normalized = normalizeToolName(parsed.tool);
|
|
285
|
+
if (KNOWN_TOOLS.has(normalized)) return { type: 'tool', tool: normalized, args: parsed.args ?? {} };
|
|
267
286
|
}
|
|
268
287
|
return null;
|
|
269
288
|
}
|
|
@@ -335,7 +354,7 @@ function fuzzyExtractTool(text) {
|
|
|
335
354
|
const toolMatch = text.match(/(?:"|')?tool(?:"|')?\s*:\s*"(\w+)"/i);
|
|
336
355
|
if (!toolMatch) return null;
|
|
337
356
|
|
|
338
|
-
const tool = toolMatch[1];
|
|
357
|
+
const tool = normalizeToolName(toolMatch[1]);
|
|
339
358
|
if (!KNOWN_TOOLS.has(tool)) return null;
|
|
340
359
|
|
|
341
360
|
const longArg = LONG_VALUE_ARG[tool];
|
|
@@ -529,7 +548,7 @@ function buildToolResultMessage(parsed, result) {
|
|
|
529
548
|
|
|
530
549
|
function buildToolErrorMessage(parsed, errorMessage) {
|
|
531
550
|
return [
|
|
532
|
-
`La herramienta "${parsed.tool}"
|
|
551
|
+
`La herramienta "${parsed.tool}" no se pudo ejecutar (nombre inválido o no disponible en este modo).`,
|
|
533
552
|
`Error: ${errorMessage}`,
|
|
534
553
|
`Las unicas herramientas disponibles son: ${TOOL_DEFINITIONS.map(t => t.name).join(', ')}.`,
|
|
535
554
|
'Elige UNA de esas herramientas. Usa el formato exacto del nombre. No inventes herramientas.',
|
package/src/tui/app.mjs
CHANGED
|
@@ -28,13 +28,12 @@ function uiText(en, es) {
|
|
|
28
28
|
return getTuiLang() === 'es' ? es : en;
|
|
29
29
|
}
|
|
30
30
|
const MAX_THINKING_LINES = 20;
|
|
31
|
-
const MAX_PASTE_PREVIEW = 200;
|
|
32
31
|
const SPIN_MS = 80;
|
|
33
32
|
|
|
34
33
|
const SPIN_FRAMES = ['\u280b', '\u2819', '\u2839', '\u2838', '\u283c', '\u2834', '\u2826', '\u2827', '\u2807', '\u280f'];
|
|
35
34
|
|
|
36
35
|
const T = {
|
|
37
|
-
bg: '#
|
|
36
|
+
bg: '#212823',
|
|
38
37
|
surface: '#1a1a1a',
|
|
39
38
|
surfaceHi: '#222222',
|
|
40
39
|
text: '#ffffff',
|
|
@@ -78,6 +77,8 @@ class UIStore extends EventEmitter {
|
|
|
78
77
|
this.messageQueue = [];
|
|
79
78
|
this.pendingExit = false;
|
|
80
79
|
this.lastEscapeAt = 0;
|
|
80
|
+
this.submittedHistory = [];
|
|
81
|
+
this.submittedRedo = [];
|
|
81
82
|
this._idCounter = 0;
|
|
82
83
|
this._scheduled = false;
|
|
83
84
|
}
|
|
@@ -159,6 +160,34 @@ class UIStore extends EventEmitter {
|
|
|
159
160
|
this._emit();
|
|
160
161
|
}
|
|
161
162
|
|
|
163
|
+
|
|
164
|
+
pushSubmittedMessage(text) {
|
|
165
|
+
const value = String(text || '');
|
|
166
|
+
if (!value) return;
|
|
167
|
+
this.submittedHistory.unshift(value);
|
|
168
|
+
if (this.submittedHistory.length > 200) this.submittedHistory.pop();
|
|
169
|
+
this.submittedRedo = [];
|
|
170
|
+
this._emit();
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
undoSubmittedMessage() {
|
|
174
|
+
if (this.submittedHistory.length === 0) return null;
|
|
175
|
+
const value = this.submittedHistory.shift();
|
|
176
|
+
this.submittedRedo.unshift(value);
|
|
177
|
+
this.setInputDraft(value);
|
|
178
|
+
this.addEvent('info', uiText('message restored', 'mensaje restaurado'), shortTextPreview(value, 120));
|
|
179
|
+
return value;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
redoSubmittedMessage() {
|
|
183
|
+
if (this.submittedRedo.length === 0) return null;
|
|
184
|
+
const value = this.submittedRedo.shift();
|
|
185
|
+
this.submittedHistory.unshift(value);
|
|
186
|
+
this.setInputDraft(value);
|
|
187
|
+
this.addEvent('info', uiText('message reapplied', 'mensaje reaplicado'), shortTextPreview(value, 120));
|
|
188
|
+
return value;
|
|
189
|
+
}
|
|
190
|
+
|
|
162
191
|
_emit() {
|
|
163
192
|
if (this._scheduled) return;
|
|
164
193
|
this._scheduled = true;
|
|
@@ -466,15 +495,19 @@ function EventLine({ kind, title, detail }) {
|
|
|
466
495
|
};
|
|
467
496
|
const { sym, color } = cfg[kind] || cfg.info;
|
|
468
497
|
|
|
469
|
-
const
|
|
470
|
-
const
|
|
498
|
+
const cleanTitle = String(title || '').trim();
|
|
499
|
+
const detailLines = String(detail || '').split('\n').map(line => line.replace(/\t/g, ' ').replace(/\s+$/g, '')).filter(Boolean);
|
|
471
500
|
|
|
472
501
|
return h(Box, { paddingLeft: 3, flexDirection: 'column' },
|
|
473
502
|
h(Box, { gap: 1 },
|
|
474
503
|
h(Text, { color }, sym),
|
|
475
|
-
h(Text, { color: kind === 'tool' ? T.textDim : T.textMuted, wrap: 'wrap' },
|
|
504
|
+
h(Text, { color: kind === 'tool' ? T.textDim : T.textMuted, wrap: 'wrap' }, cleanTitle),
|
|
476
505
|
),
|
|
477
|
-
|
|
506
|
+
detailLines.length
|
|
507
|
+
? h(Box, { paddingLeft: 2, flexDirection: 'column' },
|
|
508
|
+
...detailLines.map((line, idx) => h(Text, { key: String(idx), color: T.textGhost, wrap: 'wrap' }, line)),
|
|
509
|
+
)
|
|
510
|
+
: null,
|
|
478
511
|
);
|
|
479
512
|
}
|
|
480
513
|
|
|
@@ -675,8 +708,12 @@ function InputBar({ onSubmit, processing, width = 100, draft = '', onDraftChange
|
|
|
675
708
|
}, [onDraftChange]);
|
|
676
709
|
|
|
677
710
|
const showSuggestions = value.startsWith('/') && !value.includes(' ') && value.length > 0;
|
|
711
|
+
const localSlash = [
|
|
712
|
+
{ name: 'undo', desc: 'restore previous message', descEs: 'restaurar mensaje anterior' },
|
|
713
|
+
{ name: 'redo', desc: 'reapply restored message', descEs: 'reaplicar mensaje restaurado' },
|
|
714
|
+
];
|
|
678
715
|
const suggestions = showSuggestions
|
|
679
|
-
? SLASH_COMMANDS.filter(c => ('/' + c.name).startsWith(value.toLowerCase()))
|
|
716
|
+
? [...SLASH_COMMANDS, ...localSlash].filter(c => ('/' + c.name).startsWith(value.toLowerCase()))
|
|
680
717
|
: [];
|
|
681
718
|
|
|
682
719
|
useInput((input, key) => {
|
|
@@ -766,6 +803,16 @@ function InputBar({ onSubmit, processing, width = 100, draft = '', onDraftChange
|
|
|
766
803
|
return;
|
|
767
804
|
}
|
|
768
805
|
|
|
806
|
+
if (key.ctrl && input === 'z') {
|
|
807
|
+
onSubmit('/undo');
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
if (key.ctrl && input === 'y') {
|
|
812
|
+
onSubmit('/redo');
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
815
|
+
|
|
769
816
|
if (key.ctrl && input === 'w') {
|
|
770
817
|
const before = value.slice(0, cursor);
|
|
771
818
|
const after = value.slice(cursor);
|
|
@@ -775,7 +822,7 @@ function InputBar({ onSubmit, processing, width = 100, draft = '', onDraftChange
|
|
|
775
822
|
return;
|
|
776
823
|
}
|
|
777
824
|
|
|
778
|
-
if (key.backspace
|
|
825
|
+
if (key.backspace) {
|
|
779
826
|
if (cursor === 0) return;
|
|
780
827
|
updateValue(value.slice(0, cursor - 1) + value.slice(cursor));
|
|
781
828
|
setCursor(c => Math.max(0, c - 1));
|
|
@@ -783,8 +830,18 @@ function InputBar({ onSubmit, processing, width = 100, draft = '', onDraftChange
|
|
|
783
830
|
return;
|
|
784
831
|
}
|
|
785
832
|
|
|
833
|
+
if (key.delete) {
|
|
834
|
+
if (cursor >= value.length) return;
|
|
835
|
+
updateValue(value.slice(0, cursor) + value.slice(cursor + 1));
|
|
836
|
+
setSuggestIdx(0);
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
839
|
+
|
|
786
840
|
if (input && !key.ctrl && !key.meta) {
|
|
787
|
-
const normalizedInput = input
|
|
841
|
+
const normalizedInput = input
|
|
842
|
+
.replace(/\u001b\[200~/g, '')
|
|
843
|
+
.replace(/\u001b\[201~/g, '')
|
|
844
|
+
.replace(/\r\n/g, '\n');
|
|
788
845
|
const safeInput = normalizedInput.includes('\n') ? normalizedInput.replace(/\n/g, ' ') : normalizedInput;
|
|
789
846
|
updateValue(value.slice(0, cursor) + safeInput + value.slice(cursor));
|
|
790
847
|
setCursor(c => c + safeInput.length);
|
|
@@ -1009,6 +1066,16 @@ export async function startTUI(options = {}) {
|
|
|
1009
1066
|
return;
|
|
1010
1067
|
}
|
|
1011
1068
|
|
|
1069
|
+
if (input === '/undo') {
|
|
1070
|
+
store.undoSubmittedMessage();
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
if (input === '/redo') {
|
|
1075
|
+
store.redoSubmittedMessage();
|
|
1076
|
+
return;
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1012
1079
|
if (input.startsWith('/')) {
|
|
1013
1080
|
const commandName = input.split(' ')[0].slice(1).toLowerCase();
|
|
1014
1081
|
const lines = [];
|
|
@@ -1085,15 +1152,6 @@ export async function startTUI(options = {}) {
|
|
|
1085
1152
|
let appInstance = null;
|
|
1086
1153
|
|
|
1087
1154
|
const handleSubmit = async (input, meta = null) => {
|
|
1088
|
-
const pasteLen = Number(meta?.length || 0);
|
|
1089
|
-
if (pasteLen > 0 || (typeof input === 'string' && input.length > MAX_PASTE_PREVIEW)) {
|
|
1090
|
-
const shown = input.length;
|
|
1091
|
-
store.addEvent(
|
|
1092
|
-
'warn',
|
|
1093
|
-
`[ Pasted Text of ${shown} Characters ]`,
|
|
1094
|
-
input.slice(0, MAX_PASTE_PREVIEW) + '...'
|
|
1095
|
-
);
|
|
1096
|
-
}
|
|
1097
1155
|
if (input.startsWith('/') && store.processing) {
|
|
1098
1156
|
await processInput(input);
|
|
1099
1157
|
return;
|
|
@@ -1104,6 +1162,9 @@ export async function startTUI(options = {}) {
|
|
|
1104
1162
|
return;
|
|
1105
1163
|
}
|
|
1106
1164
|
|
|
1165
|
+
store.pushSubmittedMessage(input);
|
|
1166
|
+
store.setInputDraft('');
|
|
1167
|
+
|
|
1107
1168
|
store.processing = true;
|
|
1108
1169
|
store.turnCount += 1;
|
|
1109
1170
|
store._emit();
|