@hmharness/cli 0.4.3 → 0.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/dist/tui.d.ts +5 -0
- package/dist/tui.js +36 -8
- package/package.json +7 -7
package/dist/tui.d.ts
CHANGED
|
@@ -124,4 +124,9 @@ export declare class TuiRuntime {
|
|
|
124
124
|
private onKey;
|
|
125
125
|
render(): void;
|
|
126
126
|
}
|
|
127
|
+
/** First user line of a session file WITHOUT a full parse: peek the first
|
|
128
|
+
* 64KB, scan lines for the first user event. With hundreds of sessions a
|
|
129
|
+
* full loadTranscript per row would stall the picker for seconds; the user
|
|
130
|
+
* event is always near the head, so the peek is O(64KB) per session. */
|
|
131
|
+
export declare function firstUserLinePeek(file: string): Promise<string>;
|
|
127
132
|
export declare function tui(yes: boolean, noWeb?: boolean): Promise<void>;
|
package/dist/tui.js
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import { stdin, stdout } from 'node:process';
|
|
14
14
|
import { basename, join } from 'node:path';
|
|
15
15
|
import { createRequire } from 'node:module';
|
|
16
|
+
import { open as fopen } from 'node:fs/promises';
|
|
16
17
|
import { loadConfig, homeDir, resolveProvider, listProviders, setChatRoute, setLocale, PROVIDER_PRESETS, addProviders, detectLocalProviders } from '@hmharness/kernel';
|
|
17
18
|
import { listDrafts, listSkills, runBench, runEvolution } from '@hmharness/evolution';
|
|
18
19
|
import { buildRegistry, runAgentTask, strings } from '@hmharness/agent';
|
|
@@ -871,6 +872,34 @@ export class TuiRuntime {
|
|
|
871
872
|
}
|
|
872
873
|
}
|
|
873
874
|
/* ---------------- driver ---------------- */
|
|
875
|
+
/** First user line of a session file WITHOUT a full parse: peek the first
|
|
876
|
+
* 64KB, scan lines for the first user event. With hundreds of sessions a
|
|
877
|
+
* full loadTranscript per row would stall the picker for seconds; the user
|
|
878
|
+
* event is always near the head, so the peek is O(64KB) per session. */
|
|
879
|
+
export async function firstUserLinePeek(file) {
|
|
880
|
+
try {
|
|
881
|
+
const fh = await fopen(file, 'r');
|
|
882
|
+
try {
|
|
883
|
+
const buf = Buffer.alloc(65_536);
|
|
884
|
+
const { bytesRead } = await fh.read(buf, 0, 65_536, 0);
|
|
885
|
+
for (const line of buf.toString('utf8', 0, bytesRead).split('\n')) {
|
|
886
|
+
if (!line.includes('"user"'))
|
|
887
|
+
continue;
|
|
888
|
+
try {
|
|
889
|
+
const ev = JSON.parse(line);
|
|
890
|
+
if (ev.t === 'user' && typeof ev.text === 'string')
|
|
891
|
+
return ev.text;
|
|
892
|
+
}
|
|
893
|
+
catch { /* partial line at the buffer edge - no preview */ }
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
finally {
|
|
897
|
+
await fh.close();
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
catch { /* unreadable file - no preview */ }
|
|
901
|
+
return '';
|
|
902
|
+
}
|
|
874
903
|
export async function tui(yes, noWeb = false) {
|
|
875
904
|
let cfg = await loadConfig();
|
|
876
905
|
let autoApprove = yes || cfg.approval === 'auto';
|
|
@@ -958,22 +987,21 @@ export async function tui(yes, noWeb = false) {
|
|
|
958
987
|
const arg = line.slice(8).trim();
|
|
959
988
|
const { latestSession, loadTranscript } = await import('@hmharness/kernel');
|
|
960
989
|
const { readdir } = await import('node:fs/promises');
|
|
961
|
-
//
|
|
990
|
+
// ALL sessions, newest first - no arbitrary "recent 8" cap (user
|
|
991
|
+
// challenge: "不应该是所有历史会话吗"). Previews use the 64KB peek,
|
|
992
|
+
// never a full parse, so hundreds of rows still open instantly; the
|
|
993
|
+
// palette scrolls and head-prefix filtering narrows fast.
|
|
962
994
|
let files = [];
|
|
963
995
|
try {
|
|
964
996
|
files = (await readdir(join(home, 'sessions'))).filter((f) => f.endsWith('.jsonl'));
|
|
965
997
|
}
|
|
966
998
|
catch { /* none */ }
|
|
967
999
|
files.sort();
|
|
968
|
-
const recent = files.
|
|
1000
|
+
const recent = files.reverse();
|
|
969
1001
|
const rows = [];
|
|
970
1002
|
for (const f of recent) {
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
const firstUser = tr?.messages.find((m) => m.role === 'user')?.content ?? '';
|
|
974
|
-
rows.push({ name: f.slice(0, 18), desc: (firstUser || '(no user line)').replace(/\n/g, ' ').slice(0, 56) });
|
|
975
|
-
}
|
|
976
|
-
catch { /* skip unreadable */ }
|
|
1003
|
+
const firstUser = await firstUserLinePeek(join(home, 'sessions', f));
|
|
1004
|
+
rows.push({ name: f.slice(0, 18), desc: (firstUser || '(无预览)').replace(/\n/g, ' ').slice(0, 56) });
|
|
977
1005
|
}
|
|
978
1006
|
rt.setSessionChoices(rows);
|
|
979
1007
|
if (!arg) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hmharness/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
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.
|
|
47
|
-
"@hmharness/evolution": "0.
|
|
48
|
-
"@hmharness/domain-harmony": "0.
|
|
49
|
-
"@hmharness/domain-ops": "0.
|
|
50
|
-
"@hmharness/agent": "0.
|
|
51
|
-
"@hmharness/web": "0.
|
|
46
|
+
"@hmharness/kernel": "0.5.0",
|
|
47
|
+
"@hmharness/evolution": "0.5.0",
|
|
48
|
+
"@hmharness/domain-harmony": "0.5.0",
|
|
49
|
+
"@hmharness/domain-ops": "0.5.0",
|
|
50
|
+
"@hmharness/agent": "0.5.0",
|
|
51
|
+
"@hmharness/web": "0.5.0"
|
|
52
52
|
}
|
|
53
53
|
}
|