@hmharness/cli 0.6.5 → 0.6.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/dist/tui.d.ts +4 -0
- package/dist/tui.js +30 -30
- package/package.json +7 -7
package/dist/tui.d.ts
CHANGED
|
@@ -32,6 +32,9 @@ export declare class TuiRuntime {
|
|
|
32
32
|
private approval;
|
|
33
33
|
private approvalResolve;
|
|
34
34
|
private running;
|
|
35
|
+
/** queued task count: the busy hints line shows it live (queue at a glance,
|
|
36
|
+
* no /queue query needed) */
|
|
37
|
+
private queued;
|
|
35
38
|
private renderTimer?;
|
|
36
39
|
private exitResolve;
|
|
37
40
|
private driver;
|
|
@@ -116,6 +119,7 @@ export declare class TuiRuntime {
|
|
|
116
119
|
foldThinking(): void;
|
|
117
120
|
setBusy(busy: boolean, label?: string): void;
|
|
118
121
|
setStatus(s: string): void;
|
|
122
|
+
setQueued(n: number): void;
|
|
119
123
|
requestApproval(name: string, args: Record<string, unknown>): Promise<boolean>;
|
|
120
124
|
consumeInput(): string;
|
|
121
125
|
onSubmit(fn: () => void): void;
|
package/dist/tui.js
CHANGED
|
@@ -154,6 +154,9 @@ export class TuiRuntime {
|
|
|
154
154
|
approval = null;
|
|
155
155
|
approvalResolve = null;
|
|
156
156
|
running = true;
|
|
157
|
+
/** queued task count: the busy hints line shows it live (queue at a glance,
|
|
158
|
+
* no /queue query needed) */
|
|
159
|
+
queued = 0;
|
|
157
160
|
renderTimer;
|
|
158
161
|
exitResolve = null;
|
|
159
162
|
driver = null;
|
|
@@ -418,6 +421,10 @@ export class TuiRuntime {
|
|
|
418
421
|
this.status = s;
|
|
419
422
|
this.dirty = true;
|
|
420
423
|
}
|
|
424
|
+
setQueued(n) {
|
|
425
|
+
this.queued = Math.max(0, n);
|
|
426
|
+
this.dirty = true;
|
|
427
|
+
}
|
|
421
428
|
requestApproval(name, args) {
|
|
422
429
|
this.approval = { name, args };
|
|
423
430
|
this.dirty = true;
|
|
@@ -855,9 +862,11 @@ export class TuiRuntime {
|
|
|
855
862
|
}
|
|
856
863
|
}
|
|
857
864
|
frame.push(DIM('└' + '─'.repeat(iw + 2) + '┘'));
|
|
858
|
-
const hints = this.
|
|
859
|
-
? `${DIM(this.t.
|
|
860
|
-
:
|
|
865
|
+
const hints = this.busy
|
|
866
|
+
? `${DIM(this.t.tuiBusyHints(this.queued))}`
|
|
867
|
+
: this.scrollFromBottom > 0
|
|
868
|
+
? `${DIM(this.t.tuiScrolled)}`
|
|
869
|
+
: `${DIM(this.t.tuiHints)}`;
|
|
861
870
|
const stat = this.status && !this.busy ? DIM(this.status) : '';
|
|
862
871
|
frame.push(truncateTo(hints + ' '.repeat(Math.max(1, W - strWidth(stripAnsi(hints)) - strWidth(stat))) + stat, W - 1));
|
|
863
872
|
// Absolute per-row addressing: CUP resets the column and cancels the
|
|
@@ -935,34 +944,31 @@ export async function tui(yes, noWeb = false) {
|
|
|
935
944
|
// Task queue: new submissions during a running task are queued (not
|
|
936
945
|
// rejected, not run concurrently — sequential execution preserves history
|
|
937
946
|
// integrity). Slash commands still run immediately (they're quick).
|
|
938
|
-
//
|
|
947
|
+
// Codex-style interaction: ONE key does both jobs — Enter sends when there
|
|
948
|
+
// is text and STOPS the running task when the line is empty (the TUI
|
|
949
|
+
// equivalent of the send button that becomes a stop button). No `!` prefix,
|
|
950
|
+
// no /queue skip: the queue is visible in the busy hints line.
|
|
939
951
|
const taskQueue = [];
|
|
940
952
|
let taskRunning = false;
|
|
941
953
|
let currentAbort = null;
|
|
942
954
|
rt.onSubmit(() => {
|
|
943
955
|
const line = rt.consumeInput().trim();
|
|
944
|
-
if (!line)
|
|
956
|
+
if (!line) {
|
|
957
|
+
// empty Enter while running = stop (interrupts the current task;
|
|
958
|
+
// queued tasks still run — clear them first with /queue clear if not)
|
|
959
|
+
if (taskRunning) {
|
|
960
|
+
currentAbort?.abort();
|
|
961
|
+
rt.addText('⏹ interrupting current task (in-flight tool calls finish first; queued tasks still run)', 'dim');
|
|
962
|
+
}
|
|
945
963
|
return;
|
|
964
|
+
}
|
|
946
965
|
if (line.startsWith('/')) {
|
|
947
966
|
void handleLine(line);
|
|
948
967
|
return;
|
|
949
968
|
}
|
|
950
|
-
if (line.startsWith('!')) {
|
|
951
|
-
// urgent: strip the ! and insert at front of queue (or run immediately)
|
|
952
|
-
const urgent = line.slice(1).trim();
|
|
953
|
-
if (!urgent)
|
|
954
|
-
return;
|
|
955
|
-
if (taskRunning) {
|
|
956
|
-
taskQueue.unshift(urgent);
|
|
957
|
-
rt.addText(`⚡ inserted at front: "${urgent.slice(0, 60)}${urgent.length > 60 ? '…' : ''}" (runs next)`, 'dim');
|
|
958
|
-
currentAbort?.abort(); // interrupt current to run the urgent task
|
|
959
|
-
return;
|
|
960
|
-
}
|
|
961
|
-
void executeTaskQueue(urgent);
|
|
962
|
-
return;
|
|
963
|
-
}
|
|
964
969
|
if (taskRunning) {
|
|
965
970
|
taskQueue.push(line);
|
|
971
|
+
rt.setQueued(taskQueue.length);
|
|
966
972
|
rt.addText(`📋 queued: "${line.slice(0, 60)}${line.length > 60 ? '…' : ''}" (${taskQueue.length} waiting)`, 'dim');
|
|
967
973
|
return;
|
|
968
974
|
}
|
|
@@ -974,6 +980,7 @@ export async function tui(yes, noWeb = false) {
|
|
|
974
980
|
while (task) {
|
|
975
981
|
await runSingleTask(task);
|
|
976
982
|
task = taskQueue.shift();
|
|
983
|
+
rt.setQueued(taskQueue.length);
|
|
977
984
|
if (task)
|
|
978
985
|
rt.addText(`▶ next queued: "${task.slice(0, 60)}${task.length > 60 ? '…' : ''}"`, 'dim');
|
|
979
986
|
}
|
|
@@ -1047,24 +1054,17 @@ export async function tui(yes, noWeb = false) {
|
|
|
1047
1054
|
if (sub === 'clear') {
|
|
1048
1055
|
const n = taskQueue.length;
|
|
1049
1056
|
taskQueue.length = 0;
|
|
1057
|
+
rt.setQueued(0);
|
|
1050
1058
|
rt.addText(n > 0 ? 'cleared ' + n + ' queued task(s)' : 'queue was already empty', 'dim');
|
|
1051
1059
|
return;
|
|
1052
1060
|
}
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
rt.addText('no task is running', 'dim');
|
|
1056
|
-
return;
|
|
1057
|
-
}
|
|
1058
|
-
currentAbort?.abort();
|
|
1059
|
-
rt.addText('interrupting current task (finishes in-flight tool calls)...', 'dim');
|
|
1060
|
-
return;
|
|
1061
|
-
}
|
|
1062
|
-
// bare /queue: show status
|
|
1061
|
+
// bare /queue: show status. Operations are key-based, not command-based:
|
|
1062
|
+
// empty Enter stops the current task, typed input queues, this only inspects.
|
|
1063
1063
|
const status = taskRunning ? 'running' : 'idle';
|
|
1064
1064
|
const queueList = taskQueue.length > 0
|
|
1065
1065
|
? taskQueue.map((task, i) => ' ' + (i + 1) + '. ' + task.slice(0, 70)).join('\n')
|
|
1066
1066
|
: ' (empty)';
|
|
1067
|
-
rt.addText('queue: ' + status + ' | ' + taskQueue.length + ' waiting\n' + queueList + '\n\
|
|
1067
|
+
rt.addText('queue: ' + status + ' | ' + taskQueue.length + ' waiting\n' + queueList + '\n\nempty Enter = stop current · typed Enter = queue · /queue clear = drop all', 'dim');
|
|
1068
1068
|
return;
|
|
1069
1069
|
}
|
|
1070
1070
|
if (line === '?' || line === '/help') {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hmharness/cli",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.7",
|
|
4
4
|
"description": "hmharness command line: one-shot tasks, an interactive REPL, a fullscreen TUI, the web frontend, and direct tool invocation.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/main.js",
|
|
@@ -43,11 +43,11 @@
|
|
|
43
43
|
"build": "tsc -p tsconfig.build.json"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@hmharness/kernel": "0.6.
|
|
47
|
-
"@hmharness/evolution": "0.6.
|
|
48
|
-
"@hmharness/domain-harmony": "0.6.
|
|
49
|
-
"@hmharness/domain-ops": "0.6.
|
|
50
|
-
"@hmharness/agent": "0.6.
|
|
51
|
-
"@hmharness/web": "0.6.
|
|
46
|
+
"@hmharness/kernel": "0.6.7",
|
|
47
|
+
"@hmharness/evolution": "0.6.7",
|
|
48
|
+
"@hmharness/domain-harmony": "0.6.7",
|
|
49
|
+
"@hmharness/domain-ops": "0.6.7",
|
|
50
|
+
"@hmharness/agent": "0.6.7",
|
|
51
|
+
"@hmharness/web": "0.6.7"
|
|
52
52
|
}
|
|
53
53
|
}
|