@covibes/zeroshot 1.4.0 → 1.5.0
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 +43 -0
- package/cli/index.js +130 -49
- package/cli/message-formatters-normal.js +77 -38
- package/cluster-templates/base-templates/debug-workflow.json +11 -2
- package/cluster-templates/base-templates/full-workflow.json +20 -7
- package/cluster-templates/base-templates/single-worker.json +8 -1
- package/cluster-templates/base-templates/worker-validator.json +10 -2
- package/docker/zeroshot-cluster/Dockerfile +7 -0
- package/package.json +3 -1
- package/src/agent/agent-config.js +19 -6
- package/src/agent/agent-context-builder.js +9 -0
- package/src/agent/agent-task-executor.js +149 -65
- package/src/config-validator.js +13 -0
- package/src/isolation-manager.js +11 -7
- package/src/orchestrator.js +78 -1
- package/src/status-footer.js +59 -6
- package/src/template-resolver.js +23 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,46 @@
|
|
|
1
|
+
# [1.5.0](https://github.com/covibes/zeroshot/compare/v1.4.0...v1.5.0) (2025-12-28)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **agent:** stop polling after max failures and fix status matching ([7a0fbfe](https://github.com/covibes/zeroshot/commit/7a0fbfe5439f428bdf8e0bcadbd308542221b6f1))
|
|
7
|
+
* **ci:** skip pre-push hook in CI environment ([352b013](https://github.com/covibes/zeroshot/commit/352b013b71fcea7c2d484c8274fe7c42139c65ea))
|
|
8
|
+
* **cli:** prevent terminal garbling with status footer coordination ([2716ce5](https://github.com/covibes/zeroshot/commit/2716ce55eae9a08107788200ab798c3f76815820))
|
|
9
|
+
* **config:** add timeout: 0 default to agent configuration ([6ff66c0](https://github.com/covibes/zeroshot/commit/6ff66c093bf8dfd5048b468fbd250cbfc0d9dbc1))
|
|
10
|
+
* **deps:** regenerate package-lock.json for jscpd dependencies ([c46d84c](https://github.com/covibes/zeroshot/commit/c46d84c3fc1fbb3d00585922613ff36d829d917a))
|
|
11
|
+
* **infra:** improve container cleanup and npm install robustness ([6c04b46](https://github.com/covibes/zeroshot/commit/6c04b46374bd3041a8b5c185ca163009f2fb6635))
|
|
12
|
+
* **orchestrator:** prevent 0-message clusters on SIGINT during init ([33ed8f9](https://github.com/covibes/zeroshot/commit/33ed8f9b90d92da6bf9caf2a7b5e52eadbcecc9f))
|
|
13
|
+
* **template-resolver:** apply param defaults before resolving placeholders ([eafdd62](https://github.com/covibes/zeroshot/commit/eafdd62fce381a7b9a7cb9787cd06e13f421171b))
|
|
14
|
+
* **templates:** add timeout parameter to all base templates ([f853ed3](https://github.com/covibes/zeroshot/commit/f853ed39e0e566afaf31040ce94923a2dcc7bfb9))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Features
|
|
18
|
+
|
|
19
|
+
* **agents:** add git prohibition + minimal output instructions ([6f6496c](https://github.com/covibes/zeroshot/commit/6f6496c5db29073ebbeb6229ac128a5f62d7591f))
|
|
20
|
+
* mechanical enforcement of 7 antipatterns ([4286091](https://github.com/covibes/zeroshot/commit/428609163f9405a8d4b9e84adaee0edbc6bbb7d1)), closes [#1](https://github.com/covibes/zeroshot/issues/1) [#5](https://github.com/covibes/zeroshot/issues/5) [#2](https://github.com/covibes/zeroshot/issues/2) [#4](https://github.com/covibes/zeroshot/issues/4) [#3](https://github.com/covibes/zeroshot/issues/3) [#7](https://github.com/covibes/zeroshot/issues/7) [covibes/covibes#635](https://github.com/covibes/covibes/issues/635)
|
|
21
|
+
* **templates:** add timeout parameter to worker-validator agents ([ee8b17b](https://github.com/covibes/zeroshot/commit/ee8b17bc76aa29bb692965fddbc5a993749f11f9))
|
|
22
|
+
|
|
23
|
+
# [1.5.0](https://github.com/covibes/zeroshot/compare/v1.4.0...v1.5.0) (2025-12-28)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
### Bug Fixes
|
|
27
|
+
|
|
28
|
+
* **agent:** stop polling after max failures and fix status matching ([7a0fbfe](https://github.com/covibes/zeroshot/commit/7a0fbfe5439f428bdf8e0bcadbd308542221b6f1))
|
|
29
|
+
* **cli:** prevent terminal garbling with status footer coordination ([2716ce5](https://github.com/covibes/zeroshot/commit/2716ce55eae9a08107788200ab798c3f76815820))
|
|
30
|
+
* **config:** add timeout: 0 default to agent configuration ([6ff66c0](https://github.com/covibes/zeroshot/commit/6ff66c093bf8dfd5048b468fbd250cbfc0d9dbc1))
|
|
31
|
+
* **deps:** regenerate package-lock.json for jscpd dependencies ([c46d84c](https://github.com/covibes/zeroshot/commit/c46d84c3fc1fbb3d00585922613ff36d829d917a))
|
|
32
|
+
* **infra:** improve container cleanup and npm install robustness ([6c04b46](https://github.com/covibes/zeroshot/commit/6c04b46374bd3041a8b5c185ca163009f2fb6635))
|
|
33
|
+
* **orchestrator:** prevent 0-message clusters on SIGINT during init ([33ed8f9](https://github.com/covibes/zeroshot/commit/33ed8f9b90d92da6bf9caf2a7b5e52eadbcecc9f))
|
|
34
|
+
* **template-resolver:** apply param defaults before resolving placeholders ([eafdd62](https://github.com/covibes/zeroshot/commit/eafdd62fce381a7b9a7cb9787cd06e13f421171b))
|
|
35
|
+
* **templates:** add timeout parameter to all base templates ([f853ed3](https://github.com/covibes/zeroshot/commit/f853ed39e0e566afaf31040ce94923a2dcc7bfb9))
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
### Features
|
|
39
|
+
|
|
40
|
+
* **agents:** add git prohibition + minimal output instructions ([6f6496c](https://github.com/covibes/zeroshot/commit/6f6496c5db29073ebbeb6229ac128a5f62d7591f))
|
|
41
|
+
* mechanical enforcement of 7 antipatterns ([4286091](https://github.com/covibes/zeroshot/commit/428609163f9405a8d4b9e84adaee0edbc6bbb7d1)), closes [#1](https://github.com/covibes/zeroshot/issues/1) [#5](https://github.com/covibes/zeroshot/issues/5) [#2](https://github.com/covibes/zeroshot/issues/2) [#4](https://github.com/covibes/zeroshot/issues/4) [#3](https://github.com/covibes/zeroshot/issues/3) [#7](https://github.com/covibes/zeroshot/issues/7) [covibes/covibes#635](https://github.com/covibes/covibes/issues/635)
|
|
42
|
+
* **templates:** add timeout parameter to worker-validator agents ([ee8b17b](https://github.com/covibes/zeroshot/commit/ee8b17bc76aa29bb692965fddbc5a993749f11f9))
|
|
43
|
+
|
|
1
44
|
# [1.4.0](https://github.com/covibes/zeroshot/compare/v1.3.0...v1.4.0) (2025-12-28)
|
|
2
45
|
|
|
3
46
|
|
package/cli/index.js
CHANGED
|
@@ -60,6 +60,39 @@ let activeClusterId = null;
|
|
|
60
60
|
/** @type {import('../src/orchestrator') | null} */
|
|
61
61
|
let orchestratorInstance = null;
|
|
62
62
|
|
|
63
|
+
// Track active status footer for safe output routing
|
|
64
|
+
// When set, all output routes through statusFooter.print() to prevent garbling
|
|
65
|
+
/** @type {import('../src/status-footer').StatusFooter | null} */
|
|
66
|
+
let activeStatusFooter = null;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Safe print - routes through statusFooter when active to prevent garbling
|
|
70
|
+
* @param {...any} args - Arguments to print (like console.log)
|
|
71
|
+
*/
|
|
72
|
+
function safePrint(...args) {
|
|
73
|
+
const text = args.map(arg =>
|
|
74
|
+
typeof arg === 'string' ? arg : String(arg)
|
|
75
|
+
).join(' ');
|
|
76
|
+
|
|
77
|
+
if (activeStatusFooter) {
|
|
78
|
+
activeStatusFooter.print(text + '\n');
|
|
79
|
+
} else {
|
|
80
|
+
console.log(...args);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Safe write - routes through statusFooter when active
|
|
86
|
+
* @param {string} text - Text to write
|
|
87
|
+
*/
|
|
88
|
+
function safeWrite(text) {
|
|
89
|
+
if (activeStatusFooter) {
|
|
90
|
+
activeStatusFooter.print(text);
|
|
91
|
+
} else {
|
|
92
|
+
process.stdout.write(text);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
63
96
|
/**
|
|
64
97
|
* Handle fatal errors: log, cleanup cluster state, exit
|
|
65
98
|
* @param {string} type - 'uncaughtException' or 'unhandledRejection'
|
|
@@ -614,6 +647,8 @@ Input formats:
|
|
|
614
647
|
statusFooter.setCluster(clusterId);
|
|
615
648
|
statusFooter.setClusterState('running');
|
|
616
649
|
statusFooter.setMessageBus(cluster.messageBus);
|
|
650
|
+
// Set module-level reference so safePrint/safeWrite route through footer
|
|
651
|
+
activeStatusFooter = statusFooter;
|
|
617
652
|
|
|
618
653
|
// Subscribe to AGENT_LIFECYCLE to track agent states and PIDs
|
|
619
654
|
const lifecycleUnsubscribe = cluster.messageBus.subscribeTopic('AGENT_LIFECYCLE', (msg) => {
|
|
@@ -698,14 +733,16 @@ Input formats:
|
|
|
698
733
|
if (status.state !== 'running') {
|
|
699
734
|
clearInterval(checkInterval);
|
|
700
735
|
clearInterval(flushInterval);
|
|
701
|
-
// Stop status footer
|
|
702
|
-
statusFooter.stop();
|
|
703
736
|
lifecycleUnsubscribe();
|
|
704
|
-
// Final flush
|
|
737
|
+
// Final flush BEFORE stopping status footer
|
|
738
|
+
// (statusFooter.stop() sends ANSI codes that can clear terminal area)
|
|
705
739
|
for (const sender of sendersWithOutput) {
|
|
706
740
|
const prefix = getColorForSender(sender)(`${sender.padEnd(15)} |`);
|
|
707
741
|
flushLineBuffer(prefix, sender);
|
|
708
742
|
}
|
|
743
|
+
// Stop status footer AFTER output is done
|
|
744
|
+
statusFooter.stop();
|
|
745
|
+
activeStatusFooter = null;
|
|
709
746
|
unsubscribe();
|
|
710
747
|
resolve();
|
|
711
748
|
}
|
|
@@ -714,6 +751,7 @@ Input formats:
|
|
|
714
751
|
clearInterval(checkInterval);
|
|
715
752
|
clearInterval(flushInterval);
|
|
716
753
|
statusFooter.stop();
|
|
754
|
+
activeStatusFooter = null;
|
|
717
755
|
lifecycleUnsubscribe();
|
|
718
756
|
unsubscribe();
|
|
719
757
|
resolve();
|
|
@@ -726,6 +764,7 @@ Input formats:
|
|
|
726
764
|
process.on('SIGINT', async () => {
|
|
727
765
|
// Stop status footer first to restore terminal
|
|
728
766
|
statusFooter.stop();
|
|
767
|
+
activeStatusFooter = null;
|
|
729
768
|
lifecycleUnsubscribe();
|
|
730
769
|
|
|
731
770
|
console.log(chalk.dim('\n\n--- Interrupted ---'));
|
|
@@ -750,6 +789,22 @@ Input formats:
|
|
|
750
789
|
}
|
|
751
790
|
|
|
752
791
|
// Daemon mode: cluster runs in background, stay alive via orchestrator's setInterval
|
|
792
|
+
// Add cleanup handlers for daemon mode to ensure container cleanup on process exit
|
|
793
|
+
// CRITICAL: Without this, containers become orphaned when daemon process dies
|
|
794
|
+
if (process.env.CREW_DAEMON) {
|
|
795
|
+
const cleanup = async (signal) => {
|
|
796
|
+
console.log(`\n[DAEMON] Received ${signal}, cleaning up cluster ${clusterId}...`);
|
|
797
|
+
try {
|
|
798
|
+
await orchestrator.stop(clusterId);
|
|
799
|
+
console.log(`[DAEMON] Cluster ${clusterId} stopped.`);
|
|
800
|
+
} catch (e) {
|
|
801
|
+
console.error(`[DAEMON] Cleanup error: ${e.message}`);
|
|
802
|
+
}
|
|
803
|
+
process.exit(0);
|
|
804
|
+
};
|
|
805
|
+
process.on('SIGTERM', () => cleanup('SIGTERM'));
|
|
806
|
+
process.on('SIGINT', () => cleanup('SIGINT'));
|
|
807
|
+
}
|
|
753
808
|
} catch (error) {
|
|
754
809
|
console.error('Error:', error.message);
|
|
755
810
|
process.exit(1);
|
|
@@ -3570,32 +3625,40 @@ function accumulateText(prefix, sender, text) {
|
|
|
3570
3625
|
buf.textBuffer = buf.textBuffer.slice(newlineIdx + 1);
|
|
3571
3626
|
|
|
3572
3627
|
// Word wrap and print the complete line
|
|
3628
|
+
// CRITICAL: Batch all output into single safeWrite() to prevent interleaving with render()
|
|
3573
3629
|
const wrappedLines = wordWrap(completeLine, contentWidth);
|
|
3630
|
+
let outputBuffer = '';
|
|
3631
|
+
|
|
3574
3632
|
for (let i = 0; i < wrappedLines.length; i++) {
|
|
3575
3633
|
const wrappedLine = wrappedLines[i];
|
|
3576
3634
|
|
|
3577
3635
|
// Print prefix (real or continuation)
|
|
3578
3636
|
if (buf.needsPrefix) {
|
|
3579
|
-
|
|
3637
|
+
outputBuffer += `${prefix} `;
|
|
3580
3638
|
buf.needsPrefix = false;
|
|
3581
3639
|
} else if (i > 0) {
|
|
3582
|
-
|
|
3640
|
+
outputBuffer += `${continuationPrefix}`;
|
|
3583
3641
|
}
|
|
3584
3642
|
|
|
3585
3643
|
if (wrappedLine.trim()) {
|
|
3586
|
-
|
|
3644
|
+
outputBuffer += formatInlineMarkdown(wrappedLine);
|
|
3587
3645
|
}
|
|
3588
3646
|
|
|
3589
3647
|
// Newline after each wrapped segment
|
|
3590
3648
|
if (i < wrappedLines.length - 1) {
|
|
3591
|
-
|
|
3649
|
+
outputBuffer += '\n';
|
|
3592
3650
|
}
|
|
3593
3651
|
}
|
|
3594
3652
|
|
|
3595
3653
|
// Complete the line
|
|
3596
|
-
|
|
3654
|
+
outputBuffer += '\n';
|
|
3597
3655
|
buf.needsPrefix = true;
|
|
3598
3656
|
buf.pendingNewline = false;
|
|
3657
|
+
|
|
3658
|
+
// Single atomic write prevents interleaving
|
|
3659
|
+
if (outputBuffer) {
|
|
3660
|
+
safeWrite(outputBuffer);
|
|
3661
|
+
}
|
|
3599
3662
|
}
|
|
3600
3663
|
|
|
3601
3664
|
// Mark that we have pending text (no newline yet)
|
|
@@ -3620,35 +3683,45 @@ function accumulateThinking(prefix, sender, text) {
|
|
|
3620
3683
|
const newlineIdx = remaining.indexOf('\n');
|
|
3621
3684
|
const rawLine = newlineIdx === -1 ? remaining : remaining.slice(0, newlineIdx);
|
|
3622
3685
|
|
|
3686
|
+
// CRITICAL: Batch all output into single safeWrite() to prevent interleaving with render()
|
|
3623
3687
|
const wrappedLines = wordWrap(rawLine, contentWidth);
|
|
3688
|
+
let outputBuffer = '';
|
|
3624
3689
|
|
|
3625
3690
|
for (let i = 0; i < wrappedLines.length; i++) {
|
|
3626
3691
|
const wrappedLine = wrappedLines[i];
|
|
3627
3692
|
|
|
3628
3693
|
if (buf.thinkingNeedsPrefix) {
|
|
3629
|
-
|
|
3694
|
+
outputBuffer += `${prefix} ${chalk.dim.italic('💭 ')}`;
|
|
3630
3695
|
buf.thinkingNeedsPrefix = false;
|
|
3631
3696
|
} else if (i > 0) {
|
|
3632
|
-
|
|
3697
|
+
outputBuffer += `${continuationPrefix}`;
|
|
3633
3698
|
}
|
|
3634
3699
|
|
|
3635
3700
|
if (wrappedLine.trim()) {
|
|
3636
|
-
|
|
3701
|
+
outputBuffer += chalk.dim.italic(wrappedLine);
|
|
3637
3702
|
}
|
|
3638
3703
|
|
|
3639
3704
|
if (i < wrappedLines.length - 1) {
|
|
3640
|
-
|
|
3705
|
+
outputBuffer += '\n';
|
|
3641
3706
|
}
|
|
3642
3707
|
}
|
|
3643
3708
|
|
|
3644
3709
|
if (newlineIdx === -1) {
|
|
3645
3710
|
buf.thinkingPendingNewline = true;
|
|
3711
|
+
// Single atomic write
|
|
3712
|
+
if (outputBuffer) {
|
|
3713
|
+
safeWrite(outputBuffer);
|
|
3714
|
+
}
|
|
3646
3715
|
break;
|
|
3647
3716
|
} else {
|
|
3648
|
-
|
|
3717
|
+
outputBuffer += '\n';
|
|
3649
3718
|
buf.thinkingNeedsPrefix = true;
|
|
3650
3719
|
buf.thinkingPendingNewline = false;
|
|
3651
3720
|
remaining = remaining.slice(newlineIdx + 1);
|
|
3721
|
+
// Single atomic write
|
|
3722
|
+
if (outputBuffer) {
|
|
3723
|
+
safeWrite(outputBuffer);
|
|
3724
|
+
}
|
|
3652
3725
|
}
|
|
3653
3726
|
}
|
|
3654
3727
|
}
|
|
@@ -3658,7 +3731,10 @@ function flushLineBuffer(prefix, sender) {
|
|
|
3658
3731
|
const buf = lineBuffers.get(sender);
|
|
3659
3732
|
if (!buf) return;
|
|
3660
3733
|
|
|
3661
|
-
// CRITICAL:
|
|
3734
|
+
// CRITICAL: Batch all output into single safeWrite() to prevent interleaving with render()
|
|
3735
|
+
let outputBuffer = '';
|
|
3736
|
+
|
|
3737
|
+
// Flush any remaining text in textBuffer (text without trailing newline)
|
|
3662
3738
|
if (buf.textBuffer && buf.textBuffer.length > 0) {
|
|
3663
3739
|
// Calculate widths for word wrapping (same as accumulateText)
|
|
3664
3740
|
const prefixLen = chalk.reset(prefix).replace(/\\x1b\[[0-9;]*m/g, '').length + 1;
|
|
@@ -3671,18 +3747,18 @@ function flushLineBuffer(prefix, sender) {
|
|
|
3671
3747
|
const wrappedLine = wrappedLines[i];
|
|
3672
3748
|
|
|
3673
3749
|
if (buf.needsPrefix) {
|
|
3674
|
-
|
|
3750
|
+
outputBuffer += `${prefix} `;
|
|
3675
3751
|
buf.needsPrefix = false;
|
|
3676
3752
|
} else if (i > 0) {
|
|
3677
|
-
|
|
3753
|
+
outputBuffer += `${continuationPrefix}`;
|
|
3678
3754
|
}
|
|
3679
3755
|
|
|
3680
3756
|
if (wrappedLine.trim()) {
|
|
3681
|
-
|
|
3757
|
+
outputBuffer += formatInlineMarkdown(wrappedLine);
|
|
3682
3758
|
}
|
|
3683
3759
|
|
|
3684
3760
|
if (i < wrappedLines.length - 1) {
|
|
3685
|
-
|
|
3761
|
+
outputBuffer += '\n';
|
|
3686
3762
|
}
|
|
3687
3763
|
}
|
|
3688
3764
|
|
|
@@ -3692,15 +3768,20 @@ function flushLineBuffer(prefix, sender) {
|
|
|
3692
3768
|
}
|
|
3693
3769
|
|
|
3694
3770
|
if (buf.pendingNewline) {
|
|
3695
|
-
|
|
3771
|
+
outputBuffer += '\n';
|
|
3696
3772
|
buf.needsPrefix = true;
|
|
3697
3773
|
buf.pendingNewline = false;
|
|
3698
3774
|
}
|
|
3699
3775
|
if (buf.thinkingPendingNewline) {
|
|
3700
|
-
|
|
3776
|
+
outputBuffer += '\n';
|
|
3701
3777
|
buf.thinkingNeedsPrefix = true;
|
|
3702
3778
|
buf.thinkingPendingNewline = false;
|
|
3703
3779
|
}
|
|
3780
|
+
|
|
3781
|
+
// Single atomic write prevents interleaving
|
|
3782
|
+
if (outputBuffer) {
|
|
3783
|
+
safeWrite(outputBuffer);
|
|
3784
|
+
}
|
|
3704
3785
|
}
|
|
3705
3786
|
|
|
3706
3787
|
// Lines to filter out (noise, metadata, errors)
|
|
@@ -3783,17 +3864,17 @@ function printMessage(msg, showClusterId = false, watchMode = false, isActive =
|
|
|
3783
3864
|
}
|
|
3784
3865
|
|
|
3785
3866
|
if (msg.topic === 'PR_CREATED') {
|
|
3786
|
-
formatPrCreated(msg, prefix, timestamp);
|
|
3867
|
+
formatPrCreated(msg, prefix, timestamp, safePrint);
|
|
3787
3868
|
return;
|
|
3788
3869
|
}
|
|
3789
3870
|
|
|
3790
3871
|
if (msg.topic === 'CLUSTER_COMPLETE') {
|
|
3791
|
-
formatClusterComplete(msg, prefix, timestamp);
|
|
3872
|
+
formatClusterComplete(msg, prefix, timestamp, safePrint);
|
|
3792
3873
|
return;
|
|
3793
3874
|
}
|
|
3794
3875
|
|
|
3795
3876
|
if (msg.topic === 'CLUSTER_FAILED') {
|
|
3796
|
-
formatClusterFailed(msg, prefix, timestamp);
|
|
3877
|
+
formatClusterFailed(msg, prefix, timestamp, safePrint);
|
|
3797
3878
|
return;
|
|
3798
3879
|
}
|
|
3799
3880
|
|
|
@@ -3819,7 +3900,7 @@ function printMessage(msg, showClusterId = false, watchMode = false, isActive =
|
|
|
3819
3900
|
if (event.text) {
|
|
3820
3901
|
accumulateThinking(prefix, msg.sender, event.text);
|
|
3821
3902
|
} else if (event.type === 'thinking_start') {
|
|
3822
|
-
|
|
3903
|
+
safePrint(`${prefix} ${chalk.dim.italic('💭 thinking...')}`);
|
|
3823
3904
|
}
|
|
3824
3905
|
break;
|
|
3825
3906
|
|
|
@@ -3833,7 +3914,7 @@ function printMessage(msg, showClusterId = false, watchMode = false, isActive =
|
|
|
3833
3914
|
flushLineBuffer(prefix, msg.sender);
|
|
3834
3915
|
const icon = getToolIcon(event.toolName);
|
|
3835
3916
|
const toolDesc = formatToolCall(event.toolName, event.input);
|
|
3836
|
-
|
|
3917
|
+
safePrint(`${prefix} ${icon} ${chalk.cyan(event.toolName)} ${chalk.dim(toolDesc)}`);
|
|
3837
3918
|
// Store tool call info for matching with result
|
|
3838
3919
|
currentToolCall.set(msg.sender, {
|
|
3839
3920
|
toolName: event.toolName,
|
|
@@ -3855,7 +3936,7 @@ function printMessage(msg, showClusterId = false, watchMode = false, isActive =
|
|
|
3855
3936
|
toolCall?.toolName,
|
|
3856
3937
|
toolCall?.input
|
|
3857
3938
|
);
|
|
3858
|
-
|
|
3939
|
+
safePrint(`${prefix} ${status} ${resultDesc}`);
|
|
3859
3940
|
// Clear stored tool call after result
|
|
3860
3941
|
currentToolCall.delete(msg.sender);
|
|
3861
3942
|
break;
|
|
@@ -3865,7 +3946,7 @@ function printMessage(msg, showClusterId = false, watchMode = false, isActive =
|
|
|
3865
3946
|
flushLineBuffer(prefix, msg.sender);
|
|
3866
3947
|
// Final result - only show errors (success text already streamed)
|
|
3867
3948
|
if (!event.success) {
|
|
3868
|
-
|
|
3949
|
+
safePrint(`${prefix} ${chalk.bold.red('✗ Error:')} ${event.error || 'Task failed'}`);
|
|
3869
3950
|
}
|
|
3870
3951
|
break;
|
|
3871
3952
|
|
|
@@ -3906,7 +3987,7 @@ function printMessage(msg, showClusterId = false, watchMode = false, isActive =
|
|
|
3906
3987
|
// Skip duplicate content
|
|
3907
3988
|
if (isDuplicate(trimmed)) continue;
|
|
3908
3989
|
|
|
3909
|
-
|
|
3990
|
+
safePrint(`${prefix} ${line}`);
|
|
3910
3991
|
}
|
|
3911
3992
|
}
|
|
3912
3993
|
return;
|
|
@@ -3914,22 +3995,22 @@ function printMessage(msg, showClusterId = false, watchMode = false, isActive =
|
|
|
3914
3995
|
|
|
3915
3996
|
// AGENT_ERROR: Show errors with visual prominence
|
|
3916
3997
|
if (msg.topic === 'AGENT_ERROR') {
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3998
|
+
safePrint(''); // Blank line before error
|
|
3999
|
+
safePrint(chalk.bold.red(`${'─'.repeat(60)}`));
|
|
4000
|
+
safePrint(`${prefix} ${chalk.gray(timestamp)} ${chalk.bold.red('🔴 AGENT ERROR')}`);
|
|
3920
4001
|
if (msg.content?.text) {
|
|
3921
|
-
|
|
4002
|
+
safePrint(`${prefix} ${chalk.red(msg.content.text)}`);
|
|
3922
4003
|
}
|
|
3923
4004
|
if (msg.content?.data?.stack) {
|
|
3924
4005
|
// Show first 5 lines of stack trace
|
|
3925
4006
|
const stackLines = msg.content.data.stack.split('\n').slice(0, 5);
|
|
3926
4007
|
for (const line of stackLines) {
|
|
3927
4008
|
if (line.trim()) {
|
|
3928
|
-
|
|
4009
|
+
safePrint(`${prefix} ${chalk.dim(line)}`);
|
|
3929
4010
|
}
|
|
3930
4011
|
}
|
|
3931
4012
|
}
|
|
3932
|
-
|
|
4013
|
+
safePrint(chalk.bold.red(`${'─'.repeat(60)}`));
|
|
3933
4014
|
return;
|
|
3934
4015
|
}
|
|
3935
4016
|
|
|
@@ -3941,29 +4022,29 @@ function printMessage(msg, showClusterId = false, watchMode = false, isActive =
|
|
|
3941
4022
|
}
|
|
3942
4023
|
shownNewTaskForCluster.add(msg.cluster_id);
|
|
3943
4024
|
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
4025
|
+
safePrint(''); // Blank line before new task
|
|
4026
|
+
safePrint(chalk.bold.blue(`${'─'.repeat(60)}`));
|
|
4027
|
+
safePrint(`${prefix} ${chalk.gray(timestamp)} ${chalk.bold.blue('📋 NEW TASK')}`);
|
|
3947
4028
|
if (msg.content?.text) {
|
|
3948
4029
|
// Show task description (first 3 lines max)
|
|
3949
4030
|
const lines = msg.content.text.split('\n').slice(0, 3);
|
|
3950
4031
|
for (const line of lines) {
|
|
3951
4032
|
if (line.trim() && line.trim() !== '# Manual Input') {
|
|
3952
|
-
|
|
4033
|
+
safePrint(`${prefix} ${chalk.white(line)}`);
|
|
3953
4034
|
}
|
|
3954
4035
|
}
|
|
3955
4036
|
}
|
|
3956
|
-
|
|
4037
|
+
safePrint(chalk.bold.blue(`${'─'.repeat(60)}`));
|
|
3957
4038
|
return;
|
|
3958
4039
|
}
|
|
3959
4040
|
|
|
3960
4041
|
// IMPLEMENTATION_READY: milestone marker
|
|
3961
4042
|
if (msg.topic === 'IMPLEMENTATION_READY') {
|
|
3962
|
-
|
|
4043
|
+
safePrint(
|
|
3963
4044
|
`${prefix} ${chalk.gray(timestamp)} ${chalk.bold.yellow('✅ IMPLEMENTATION READY')}`
|
|
3964
4045
|
);
|
|
3965
4046
|
if (msg.content?.data?.commit) {
|
|
3966
|
-
|
|
4047
|
+
safePrint(
|
|
3967
4048
|
`${prefix} ${chalk.gray('Commit:')} ${chalk.cyan(msg.content.data.commit.substring(0, 8))}`
|
|
3968
4049
|
);
|
|
3969
4050
|
}
|
|
@@ -3976,33 +4057,33 @@ function printMessage(msg, showClusterId = false, watchMode = false, isActive =
|
|
|
3976
4057
|
const approved = data.approved === true || data.approved === 'true';
|
|
3977
4058
|
const status = approved ? chalk.bold.green('✓ APPROVED') : chalk.bold.red('✗ REJECTED');
|
|
3978
4059
|
|
|
3979
|
-
|
|
4060
|
+
safePrint(`${prefix} ${chalk.gray(timestamp)} ${status}`);
|
|
3980
4061
|
|
|
3981
4062
|
// Show summary if present and not a template variable
|
|
3982
4063
|
if (msg.content?.text && !msg.content.text.includes('{{')) {
|
|
3983
|
-
|
|
4064
|
+
safePrint(`${prefix} ${msg.content.text.substring(0, 100)}`);
|
|
3984
4065
|
}
|
|
3985
4066
|
|
|
3986
4067
|
// Show full JSON data structure
|
|
3987
|
-
|
|
4068
|
+
safePrint(
|
|
3988
4069
|
`${prefix} ${chalk.dim(JSON.stringify(data, null, 2).split('\n').join(`\n${prefix} `))}`
|
|
3989
4070
|
);
|
|
3990
4071
|
|
|
3991
4072
|
// Show errors/issues if any
|
|
3992
4073
|
if (data.errors && Array.isArray(data.errors) && data.errors.length > 0) {
|
|
3993
|
-
|
|
4074
|
+
safePrint(`${prefix} ${chalk.red('Errors:')}`);
|
|
3994
4075
|
data.errors.forEach((err) => {
|
|
3995
4076
|
if (err && typeof err === 'string') {
|
|
3996
|
-
|
|
4077
|
+
safePrint(`${prefix} - ${err}`);
|
|
3997
4078
|
}
|
|
3998
4079
|
});
|
|
3999
4080
|
}
|
|
4000
4081
|
|
|
4001
4082
|
if (data.issues && Array.isArray(data.issues) && data.issues.length > 0) {
|
|
4002
|
-
|
|
4083
|
+
safePrint(`${prefix} ${chalk.yellow('Issues:')}`);
|
|
4003
4084
|
data.issues.forEach((issue) => {
|
|
4004
4085
|
if (issue && typeof issue === 'string') {
|
|
4005
|
-
|
|
4086
|
+
safePrint(`${prefix} - ${issue}`);
|
|
4006
4087
|
}
|
|
4007
4088
|
});
|
|
4008
4089
|
}
|
|
@@ -4010,7 +4091,7 @@ function printMessage(msg, showClusterId = false, watchMode = false, isActive =
|
|
|
4010
4091
|
}
|
|
4011
4092
|
|
|
4012
4093
|
// Fallback: generic message display for unknown topics
|
|
4013
|
-
formatGenericMessage(msg, prefix, timestamp);
|
|
4094
|
+
formatGenericMessage(msg, prefix, timestamp, safePrint);
|
|
4014
4095
|
}
|
|
4015
4096
|
|
|
4016
4097
|
// Default command handling: if first arg doesn't match a known command, treat it as 'run'
|