@moxxy/cli 1.2.6 → 1.2.8
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
CHANGED
|
@@ -3,29 +3,59 @@ import { Box, Text } from 'ink';
|
|
|
3
3
|
import { THEME } from '../../theme.js';
|
|
4
4
|
|
|
5
5
|
export function SkillMessage({ msg, showDetails = false }) {
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
const isRunning = msg.status === 'running';
|
|
7
|
+
const isError = msg.status === 'error';
|
|
8
|
+
const steps = msg.steps || [];
|
|
8
9
|
|
|
10
|
+
// Header icon and color
|
|
11
|
+
let headerIcon = '⊞';
|
|
12
|
+
let headerColor = THEME.primary;
|
|
9
13
|
if (msg.status === 'completed') {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
} else if (
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
headerIcon = '✓';
|
|
15
|
+
headerColor = THEME.success;
|
|
16
|
+
} else if (isError) {
|
|
17
|
+
headerIcon = '✗';
|
|
18
|
+
headerColor = THEME.error;
|
|
15
19
|
}
|
|
16
20
|
|
|
21
|
+
// Header text
|
|
22
|
+
const headerText = isRunning
|
|
23
|
+
? `Invoking ${msg.name}...`
|
|
24
|
+
: msg.name;
|
|
25
|
+
|
|
17
26
|
return (
|
|
18
27
|
<Box flexDirection="column">
|
|
19
28
|
<Text>
|
|
20
|
-
<Text color={
|
|
21
|
-
<Text bold color={
|
|
22
|
-
<Text color={THEME.
|
|
23
|
-
{msg.status === 'running' && <Text color={THEME.dim}> …</Text>}
|
|
24
|
-
{msg.error ? <Text color={THEME.error}> - {msg.error}</Text> : null}
|
|
29
|
+
<Text color={headerColor}>{headerIcon}</Text>
|
|
30
|
+
<Text bold color={headerColor}> {headerText}</Text>
|
|
31
|
+
{isError && msg.error ? <Text color={THEME.error}> - {msg.error}</Text> : null}
|
|
25
32
|
</Text>
|
|
26
|
-
{
|
|
27
|
-
|
|
28
|
-
|
|
33
|
+
{steps.map((step, i) => {
|
|
34
|
+
let stepIcon = '◷';
|
|
35
|
+
let stepColor = THEME.dim;
|
|
36
|
+
if (step.status === 'completed') {
|
|
37
|
+
stepIcon = '◷';
|
|
38
|
+
stepColor = THEME.dim;
|
|
39
|
+
} else if (step.status === 'error') {
|
|
40
|
+
stepIcon = '✗';
|
|
41
|
+
stepColor = THEME.error;
|
|
42
|
+
} else {
|
|
43
|
+
stepIcon = '⏳';
|
|
44
|
+
stepColor = THEME.warning;
|
|
45
|
+
}
|
|
46
|
+
const resultText = step.error
|
|
47
|
+
? step.error
|
|
48
|
+
: step.result
|
|
49
|
+
? (step.result.length > 60 ? step.result.slice(0, 60) + '…' : step.result)
|
|
50
|
+
: null;
|
|
51
|
+
return (
|
|
52
|
+
<Text key={i}>
|
|
53
|
+
<Text color={stepColor}>{stepIcon}</Text>
|
|
54
|
+
<Text bold> {step.name}</Text>
|
|
55
|
+
{resultText ? <Text color={THEME.dim}> → {resultText}</Text> : null}
|
|
56
|
+
</Text>
|
|
57
|
+
);
|
|
58
|
+
})}
|
|
29
59
|
</Box>
|
|
30
60
|
);
|
|
31
61
|
}
|
|
@@ -24,13 +24,17 @@ export function ToolGroup({ messages, expanded = false }) {
|
|
|
24
24
|
|
|
25
25
|
const icon = errors > 0 ? '✗' : '✓';
|
|
26
26
|
const color = errors > 0 ? THEME.error : THEME.success;
|
|
27
|
-
const
|
|
27
|
+
const skillMsgs = messages.filter(m => m.type === 'skill');
|
|
28
28
|
const tools = messages.filter(m => m.type === 'tool').length;
|
|
29
|
+
// Count total steps across all skills
|
|
30
|
+
const skillSteps = skillMsgs.reduce((sum, m) => sum + (m.steps ? m.steps.length : 0), 0);
|
|
29
31
|
const parts = [];
|
|
30
|
-
|
|
31
|
-
if (
|
|
32
|
+
const totalTools = tools + skillSteps;
|
|
33
|
+
if (totalTools > 0) parts.push(`${totalTools} tool${totalTools > 1 ? 's' : ''}`);
|
|
34
|
+
if (skillMsgs.length > 0) parts.push(`${skillMsgs.length} skill${skillMsgs.length > 1 ? 's' : ''}`);
|
|
32
35
|
const label = parts.join(', ');
|
|
33
|
-
const
|
|
36
|
+
const totalDone = completed + skillSteps;
|
|
37
|
+
const detail = errors > 0 ? `${totalDone} done, ${errors} failed` : `${totalDone} done`;
|
|
34
38
|
|
|
35
39
|
if (!expanded) {
|
|
36
40
|
return (
|
|
@@ -8,9 +8,10 @@ const ERROR_EVENTS = new Set([
|
|
|
8
8
|
]);
|
|
9
9
|
|
|
10
10
|
// Error events that should be shown as user-friendly system messages
|
|
11
|
-
// instead of raw event brackets (brackets only in debug mode)
|
|
11
|
+
// instead of raw event brackets (brackets only in debug mode).
|
|
12
|
+
// NOTE: primitive.failed is handled separately (with skill awareness) before this check.
|
|
12
13
|
const FRIENDLY_ERROR_EVENTS = new Set([
|
|
13
|
-
'run.failed',
|
|
14
|
+
'run.failed',
|
|
14
15
|
]);
|
|
15
16
|
|
|
16
17
|
// Tool activity events always shown in chat as compact messages
|
|
@@ -104,6 +105,7 @@ export class EventsHandler {
|
|
|
104
105
|
this._thinkingTimer = null;
|
|
105
106
|
this.pendingAsk = null; // { questionId, question } = set when agent asks user
|
|
106
107
|
this._subAgents = new Map(); // agentId -> { name, task, buffer, status }
|
|
108
|
+
this._activeSkillIdx = null; // index into this.messages for the running skill
|
|
107
109
|
this._hiveStatus = null; // aggregated hive status tracker
|
|
108
110
|
this._version = 0;
|
|
109
111
|
this.messageVersion = 0;
|
|
@@ -597,6 +599,14 @@ export class EventsHandler {
|
|
|
597
599
|
clearTimeout(this._deltaTimer);
|
|
598
600
|
this._deltaTimer = null;
|
|
599
601
|
}
|
|
602
|
+
// Close active skill session on final message
|
|
603
|
+
if (this._activeSkillIdx != null) {
|
|
604
|
+
const skill = this.messages[this._activeSkillIdx];
|
|
605
|
+
if (skill && skill.status === 'running') {
|
|
606
|
+
skill.status = 'completed';
|
|
607
|
+
}
|
|
608
|
+
this._activeSkillIdx = null;
|
|
609
|
+
}
|
|
600
610
|
const finalContent = payload.content || payload.text || this._assistantBuffer;
|
|
601
611
|
this._assistantBuffer = '';
|
|
602
612
|
const last = this.messages[this.messages.length - 1];
|
|
@@ -614,6 +624,14 @@ export class EventsHandler {
|
|
|
614
624
|
// Stop thinking on run completion or errors
|
|
615
625
|
if (type === 'run.completed' || type === 'run.failed') {
|
|
616
626
|
if (this.thinking) this._stopThinking();
|
|
627
|
+
// Close active skill session on run end
|
|
628
|
+
if (this._activeSkillIdx != null) {
|
|
629
|
+
const skill = this.messages[this._activeSkillIdx];
|
|
630
|
+
if (skill) {
|
|
631
|
+
skill.status = type === 'run.failed' ? 'error' : 'completed';
|
|
632
|
+
}
|
|
633
|
+
this._activeSkillIdx = null;
|
|
634
|
+
}
|
|
617
635
|
}
|
|
618
636
|
|
|
619
637
|
// Silence errors for hidden tool prefixes (e.g. agent.*)
|
|
@@ -621,6 +639,58 @@ export class EventsHandler {
|
|
|
621
639
|
return;
|
|
622
640
|
}
|
|
623
641
|
|
|
642
|
+
// Handle primitive.failed with skill awareness BEFORE friendly error fallback
|
|
643
|
+
if (type === 'primitive.failed') {
|
|
644
|
+
const toolName = payload.name || 'unknown';
|
|
645
|
+
const errorMsg = payload.error || 'unknown error';
|
|
646
|
+
|
|
647
|
+
// skill.execute itself failed → mark the skill as error and close session
|
|
648
|
+
if (toolName === 'skill.execute' && this._activeSkillIdx != null) {
|
|
649
|
+
const skill = this.messages[this._activeSkillIdx];
|
|
650
|
+
if (skill) {
|
|
651
|
+
skill.status = 'error';
|
|
652
|
+
skill.error = errorMsg;
|
|
653
|
+
}
|
|
654
|
+
this._activeSkillIdx = null;
|
|
655
|
+
this.messageVersion++;
|
|
656
|
+
this._notify();
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
// A tool within an active skill failed → mark the step as error
|
|
661
|
+
if (this._activeSkillIdx != null) {
|
|
662
|
+
const skill = this.messages[this._activeSkillIdx];
|
|
663
|
+
if (skill && skill.status === 'running') {
|
|
664
|
+
const step = [...skill.steps].reverse().find(s => s.name === toolName && s.status === 'running');
|
|
665
|
+
if (step) {
|
|
666
|
+
step.status = 'error';
|
|
667
|
+
step.error = errorMsg;
|
|
668
|
+
this.messageVersion++;
|
|
669
|
+
this._notify();
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
// Update the last tool message for the same primitive if it was just invoked
|
|
676
|
+
const last = this.messages[this.messages.length - 1];
|
|
677
|
+
if (last && last.type === 'tool' && last.name === toolName && last.status === 'invoked') {
|
|
678
|
+
last.status = 'error';
|
|
679
|
+
last.error = errorMsg;
|
|
680
|
+
this.messageVersion++;
|
|
681
|
+
this._notify();
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// Fallback: show as friendly system message (non-debug mode)
|
|
686
|
+
if (!this.debug) {
|
|
687
|
+
this.messages.push({ type: 'system', content: `Tool error: ${errorMsg}`, ts: event.ts });
|
|
688
|
+
this.messageVersion++;
|
|
689
|
+
this._notify();
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
624
694
|
// For user-facing error events, show as a friendly system message
|
|
625
695
|
// (raw event format only in debug mode)
|
|
626
696
|
if (!this.debug && FRIENDLY_ERROR_EVENTS.has(type)) {
|
|
@@ -636,8 +706,48 @@ export class EventsHandler {
|
|
|
636
706
|
if (TOOL_ACTIVITY_EVENTS.has(type)) {
|
|
637
707
|
if (type === 'primitive.invoked') {
|
|
638
708
|
if (isHiddenTool(payload.name || '')) return;
|
|
709
|
+
const toolName = payload.name || 'unknown';
|
|
710
|
+
|
|
711
|
+
// Detect skill.execute → start a skill session
|
|
712
|
+
if (toolName === 'skill.execute') {
|
|
713
|
+
// Parse arguments — may be an object or a JSON string
|
|
714
|
+
let args = payload.arguments;
|
|
715
|
+
if (typeof args === 'string') {
|
|
716
|
+
try { args = JSON.parse(args); } catch { args = {}; }
|
|
717
|
+
}
|
|
718
|
+
const skillName = (args && args.name) || 'unknown';
|
|
719
|
+
|
|
720
|
+
// Close any previous active skill
|
|
721
|
+
if (this._activeSkillIdx != null) {
|
|
722
|
+
const prev = this.messages[this._activeSkillIdx];
|
|
723
|
+
if (prev && prev.status === 'running') {
|
|
724
|
+
prev.status = 'completed';
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
this.messages.push({
|
|
729
|
+
type: 'skill', name: skillName, status: 'running',
|
|
730
|
+
steps: [], ts: event.ts,
|
|
731
|
+
});
|
|
732
|
+
this._activeSkillIdx = this.messages.length - 1;
|
|
733
|
+
this.messageVersion++;
|
|
734
|
+
this._notify();
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// If a skill is active, nest this tool as a step
|
|
739
|
+
if (this._activeSkillIdx != null) {
|
|
740
|
+
const skill = this.messages[this._activeSkillIdx];
|
|
741
|
+
if (skill && skill.status === 'running') {
|
|
742
|
+
skill.steps.push({ name: toolName, status: 'running', result: null });
|
|
743
|
+
this.messageVersion++;
|
|
744
|
+
this._notify();
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
|
|
639
749
|
this.messages.push({
|
|
640
|
-
type: 'tool', name:
|
|
750
|
+
type: 'tool', name: toolName, status: 'invoked',
|
|
641
751
|
arguments: formatParams(payload.arguments),
|
|
642
752
|
rawArguments: payload.arguments,
|
|
643
753
|
ts: event.ts,
|
|
@@ -648,9 +758,41 @@ export class EventsHandler {
|
|
|
648
758
|
}
|
|
649
759
|
if (type === 'primitive.completed') {
|
|
650
760
|
if (isHiddenTool(payload.name || '')) return;
|
|
761
|
+
const toolName = payload.name || 'unknown';
|
|
762
|
+
|
|
763
|
+
// skill.execute completed → just absorb (skill stays running for subsequent tools)
|
|
764
|
+
if (toolName === 'skill.execute') {
|
|
765
|
+
// Update skill name from result if available
|
|
766
|
+
if (this._activeSkillIdx != null) {
|
|
767
|
+
const skill = this.messages[this._activeSkillIdx];
|
|
768
|
+
const resultName = payload.result && typeof payload.result === 'object' && payload.result.name;
|
|
769
|
+
if (skill && resultName) {
|
|
770
|
+
skill.name = resultName;
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
this.messageVersion++;
|
|
774
|
+
this._notify();
|
|
775
|
+
return;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// If a skill is active, update the step
|
|
779
|
+
if (this._activeSkillIdx != null) {
|
|
780
|
+
const skill = this.messages[this._activeSkillIdx];
|
|
781
|
+
if (skill && skill.status === 'running') {
|
|
782
|
+
const step = [...skill.steps].reverse().find(s => s.name === toolName && s.status === 'running');
|
|
783
|
+
if (step) {
|
|
784
|
+
step.status = 'completed';
|
|
785
|
+
step.result = formatParams(payload.result);
|
|
786
|
+
}
|
|
787
|
+
this.messageVersion++;
|
|
788
|
+
this._notify();
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
|
|
651
793
|
// Update the last tool message for the same primitive if it was just invoked
|
|
652
794
|
const last = this.messages[this.messages.length - 1];
|
|
653
|
-
if (last && last.type === 'tool' && last.name ===
|
|
795
|
+
if (last && last.type === 'tool' && last.name === toolName && last.status === 'invoked') {
|
|
654
796
|
last.status = 'completed';
|
|
655
797
|
last.result = formatParams(payload.result);
|
|
656
798
|
last.rawResult = payload.result;
|
|
@@ -663,10 +805,18 @@ export class EventsHandler {
|
|
|
663
805
|
|
|
664
806
|
// Show skill activity events as compact bordered messages
|
|
665
807
|
if (type === 'skill.invoked') {
|
|
808
|
+
// Close any previous active skill
|
|
809
|
+
if (this._activeSkillIdx != null) {
|
|
810
|
+
const prev = this.messages[this._activeSkillIdx];
|
|
811
|
+
if (prev && prev.status === 'running') {
|
|
812
|
+
prev.status = 'completed';
|
|
813
|
+
}
|
|
814
|
+
}
|
|
666
815
|
this.messages.push({
|
|
667
816
|
type: 'skill', name: payload.name || 'unknown', status: 'running',
|
|
668
|
-
|
|
817
|
+
steps: [], ts: event.ts,
|
|
669
818
|
});
|
|
819
|
+
this._activeSkillIdx = this.messages.length - 1;
|
|
670
820
|
this.messageVersion++;
|
|
671
821
|
this._notify();
|
|
672
822
|
return;
|
|
@@ -680,6 +830,7 @@ export class EventsHandler {
|
|
|
680
830
|
break;
|
|
681
831
|
}
|
|
682
832
|
}
|
|
833
|
+
this._activeSkillIdx = null;
|
|
683
834
|
this.messageVersion++;
|
|
684
835
|
this._notify();
|
|
685
836
|
return;
|
|
@@ -699,27 +850,15 @@ export class EventsHandler {
|
|
|
699
850
|
if (!found) {
|
|
700
851
|
this.messages.push({
|
|
701
852
|
type: 'skill', name: payload.name || 'unknown', status: 'error',
|
|
702
|
-
error: payload.error || 'unknown error', ts: event.ts,
|
|
853
|
+
error: payload.error || 'unknown error', steps: [], ts: event.ts,
|
|
703
854
|
});
|
|
704
855
|
}
|
|
856
|
+
this._activeSkillIdx = null;
|
|
705
857
|
this.messageVersion++;
|
|
706
858
|
this._notify();
|
|
707
859
|
return;
|
|
708
860
|
}
|
|
709
861
|
|
|
710
|
-
// Show tool error events
|
|
711
|
-
if (type === 'primitive.failed') {
|
|
712
|
-
// Update the last tool message for the same primitive if it was just invoked
|
|
713
|
-
const last = this.messages[this.messages.length - 1];
|
|
714
|
-
if (last && last.type === 'tool' && last.name === (payload.name || 'unknown') && last.status === 'invoked') {
|
|
715
|
-
last.status = 'error';
|
|
716
|
-
last.error = payload.error || 'unknown error';
|
|
717
|
-
this.messageVersion++;
|
|
718
|
-
this._notify();
|
|
719
|
-
return;
|
|
720
|
-
}
|
|
721
|
-
}
|
|
722
|
-
|
|
723
862
|
// Show error events always; show all events in debug mode
|
|
724
863
|
if (ERROR_EVENTS.has(type) || this.debug) {
|
|
725
864
|
this.messages.push({ type: 'event', eventType: type, payload, ts: event.ts });
|