@hydra-acp/cli 0.1.46 → 0.1.48
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/cli.js +109 -40
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -4916,6 +4916,25 @@ function buildCombinedHistory(global, session) {
|
|
|
4916
4916
|
const filteredGlobal = global.filter((e) => !sessionSet.has(e));
|
|
4917
4917
|
return [...filteredGlobal, ...session];
|
|
4918
4918
|
}
|
|
4919
|
+
function mergeReplayedEntries(existing, replayed, cap = HISTORY_CAP) {
|
|
4920
|
+
if (replayed.length === 0) {
|
|
4921
|
+
return existing;
|
|
4922
|
+
}
|
|
4923
|
+
const seen = new Set(existing);
|
|
4924
|
+
let out = existing;
|
|
4925
|
+
for (const raw of replayed) {
|
|
4926
|
+
const trimmed = raw.replace(/\n+$/, "");
|
|
4927
|
+
if (trimmed.length === 0) {
|
|
4928
|
+
continue;
|
|
4929
|
+
}
|
|
4930
|
+
if (seen.has(trimmed)) {
|
|
4931
|
+
continue;
|
|
4932
|
+
}
|
|
4933
|
+
seen.add(trimmed);
|
|
4934
|
+
out = appendEntry(out, trimmed, cap);
|
|
4935
|
+
}
|
|
4936
|
+
return out;
|
|
4937
|
+
}
|
|
4919
4938
|
var HISTORY_CAP;
|
|
4920
4939
|
var init_history = __esm({
|
|
4921
4940
|
"src/tui/history.ts"() {
|
|
@@ -6388,7 +6407,7 @@ var init_input = __esm({
|
|
|
6388
6407
|
case "ctrl-r":
|
|
6389
6408
|
return this.startHistorySearch();
|
|
6390
6409
|
case "ctrl-s":
|
|
6391
|
-
return
|
|
6410
|
+
return this.amend();
|
|
6392
6411
|
case "ctrl-u":
|
|
6393
6412
|
this.killLine();
|
|
6394
6413
|
return [];
|
|
@@ -6906,22 +6925,30 @@ var init_input = __esm({
|
|
|
6906
6925
|
this.clearBuffer();
|
|
6907
6926
|
return [{ type: "send", text, planMode, attachments }];
|
|
6908
6927
|
}
|
|
6909
|
-
// Shift+Enter: amend the in-flight turn.
|
|
6910
|
-
//
|
|
6911
|
-
//
|
|
6912
|
-
//
|
|
6913
|
-
//
|
|
6914
|
-
//
|
|
6928
|
+
// Shift+Enter (also Ctrl+Enter / ^S): amend the in-flight turn.
|
|
6929
|
+
// While editing a queued slot, this is the "drop and amend" chord:
|
|
6930
|
+
// emit queue-remove for the slot AND amend with the loaded (possibly
|
|
6931
|
+
// edited) text, so the queued prompt becomes the amendment for the
|
|
6932
|
+
// running turn in a single keystroke. Empty buffer + no attachments
|
|
6933
|
+
// on a slot collapses to just queue-remove (matches empty-Enter).
|
|
6934
|
+
// Outside queue editing, an empty draft is a no-op. The app decides
|
|
6935
|
+
// whether to route the amend through amend_prompt or fall through to
|
|
6936
|
+
// a regular send when no turn is in flight.
|
|
6915
6937
|
amend() {
|
|
6916
6938
|
const text = this.bufferText();
|
|
6917
6939
|
if (this.queueIndex >= 0 && this.queueIndex < this.queue.length) {
|
|
6918
6940
|
const index = this.queueIndex;
|
|
6941
|
+
const planMode2 = this.planMode;
|
|
6919
6942
|
const attachments2 = [...this.attachments];
|
|
6943
|
+
const empty = text.trim().length === 0 && attachments2.length === 0;
|
|
6920
6944
|
this.clearBuffer();
|
|
6921
|
-
if (
|
|
6945
|
+
if (empty) {
|
|
6922
6946
|
return [{ type: "queue-remove", index }];
|
|
6923
6947
|
}
|
|
6924
|
-
return [
|
|
6948
|
+
return [
|
|
6949
|
+
{ type: "queue-remove", index },
|
|
6950
|
+
{ type: "amend", text, planMode: planMode2, attachments: attachments2 }
|
|
6951
|
+
];
|
|
6925
6952
|
}
|
|
6926
6953
|
if (text.trim().length === 0 && this.attachments.length === 0) {
|
|
6927
6954
|
return [];
|
|
@@ -9956,6 +9983,9 @@ uncaught: ${err.stack ?? err.message}
|
|
|
9956
9983
|
});
|
|
9957
9984
|
|
|
9958
9985
|
// src/tui/picker.ts
|
|
9986
|
+
function createPickerPrefs() {
|
|
9987
|
+
return { filters: { cwdOnly: false, hostFilter: "__local" } };
|
|
9988
|
+
}
|
|
9959
9989
|
async function pickSession(term, opts) {
|
|
9960
9990
|
process.stdout.write("\x1B[<u");
|
|
9961
9991
|
process.stdout.write("\x1B[?2004l");
|
|
@@ -9981,18 +10011,25 @@ async function pickSession(term, opts) {
|
|
|
9981
10011
|
return b.updatedAt.slice(0, 16).localeCompare(a.updatedAt.slice(0, 16));
|
|
9982
10012
|
});
|
|
9983
10013
|
};
|
|
9984
|
-
|
|
9985
|
-
|
|
9986
|
-
if (opts.currentSessionId !== void 0) {
|
|
10014
|
+
const prefs = opts.prefs ?? createPickerPrefs();
|
|
10015
|
+
if (opts.prefs === void 0 && opts.currentSessionId !== void 0) {
|
|
9987
10016
|
const current = opts.sessions.find(
|
|
9988
10017
|
(s) => s.sessionId === opts.currentSessionId
|
|
9989
10018
|
);
|
|
9990
10019
|
if (current?.importedFromMachine) {
|
|
9991
|
-
hostFilter = "__all";
|
|
10020
|
+
prefs.filters.hostFilter = "__all";
|
|
9992
10021
|
}
|
|
9993
10022
|
}
|
|
9994
10023
|
let allSessions = sortSessions(opts.sessions);
|
|
9995
|
-
|
|
10024
|
+
const applyPrefsFilters = (sessions) => {
|
|
10025
|
+
let base = sessions;
|
|
10026
|
+
if (prefs.filters.cwdOnly) {
|
|
10027
|
+
base = base.filter((s) => s.cwd === opts.cwd);
|
|
10028
|
+
}
|
|
10029
|
+
base = filterByHost(base, prefs.filters.hostFilter);
|
|
10030
|
+
return base;
|
|
10031
|
+
};
|
|
10032
|
+
let visible = applyPrefsFilters(allSessions);
|
|
9996
10033
|
let rows = visible.map((s) => toRow(s, Date.now()));
|
|
9997
10034
|
let widths = computeWidths(rows);
|
|
9998
10035
|
let total = 1 + visible.length;
|
|
@@ -10060,11 +10097,7 @@ async function pickSession(term, opts) {
|
|
|
10060
10097
|
computeLayout();
|
|
10061
10098
|
};
|
|
10062
10099
|
const applyFilter = () => {
|
|
10063
|
-
|
|
10064
|
-
if (cwdOnly) {
|
|
10065
|
-
base = base.filter((s) => s.cwd === opts.cwd);
|
|
10066
|
-
}
|
|
10067
|
-
base = filterByHost(base, hostFilter);
|
|
10100
|
+
const base = applyPrefsFilters(allSessions);
|
|
10068
10101
|
if (searchActive && searchTerm.length > 0) {
|
|
10069
10102
|
visible = base.filter((s) => matchesSearch(s, searchTerm));
|
|
10070
10103
|
} else {
|
|
@@ -10150,12 +10183,12 @@ async function pickSession(term, opts) {
|
|
|
10150
10183
|
const above = scrollOffset;
|
|
10151
10184
|
const below = Math.max(0, visible.length - scrollOffset - viewportSize);
|
|
10152
10185
|
const parts = [];
|
|
10153
|
-
if (cwdOnly) {
|
|
10186
|
+
if (prefs.filters.cwdOnly) {
|
|
10154
10187
|
parts.push("cwd-only");
|
|
10155
10188
|
}
|
|
10156
|
-
if (hostFilter !== "__all") {
|
|
10189
|
+
if (prefs.filters.hostFilter !== "__all") {
|
|
10157
10190
|
parts.push(
|
|
10158
|
-
hostFilter === "__local" ? "host: local" : `host: ${hostFilter}`
|
|
10191
|
+
prefs.filters.hostFilter === "__local" ? "host: local" : `host: ${prefs.filters.hostFilter}`
|
|
10159
10192
|
);
|
|
10160
10193
|
}
|
|
10161
10194
|
if (above > 0) {
|
|
@@ -10785,7 +10818,7 @@ ${cells}`;
|
|
|
10785
10818
|
}
|
|
10786
10819
|
if (name === "o" || name === "O") {
|
|
10787
10820
|
const keepId = selectedIdx > 0 ? visible[selectedIdx - 1]?.sessionId : void 0;
|
|
10788
|
-
cwdOnly = !cwdOnly;
|
|
10821
|
+
prefs.filters.cwdOnly = !prefs.filters.cwdOnly;
|
|
10789
10822
|
applyFilter();
|
|
10790
10823
|
if (keepId !== void 0) {
|
|
10791
10824
|
const idx = visible.findIndex((s) => s.sessionId === keepId);
|
|
@@ -10799,7 +10832,10 @@ ${cells}`;
|
|
|
10799
10832
|
}
|
|
10800
10833
|
if (name === "h" || name === "H") {
|
|
10801
10834
|
const keepId = selectedIdx > 0 ? visible[selectedIdx - 1]?.sessionId : void 0;
|
|
10802
|
-
hostFilter = nextHostFilter(
|
|
10835
|
+
prefs.filters.hostFilter = nextHostFilter(
|
|
10836
|
+
prefs.filters.hostFilter,
|
|
10837
|
+
allSessions
|
|
10838
|
+
);
|
|
10803
10839
|
applyFilter();
|
|
10804
10840
|
if (keepId !== void 0) {
|
|
10805
10841
|
const idx = visible.findIndex((s) => s.sessionId === keepId);
|
|
@@ -11198,7 +11234,7 @@ async function promptForImportCwd(term, session, opts = {}) {
|
|
|
11198
11234
|
const contentHeight = 9;
|
|
11199
11235
|
layout = drawBox(term, {
|
|
11200
11236
|
contentHeight,
|
|
11201
|
-
title: "
|
|
11237
|
+
title: "Fork locally \u2014 choose cwd"
|
|
11202
11238
|
});
|
|
11203
11239
|
const innerW = layout.contentW;
|
|
11204
11240
|
const headerRows = [
|
|
@@ -11437,7 +11473,10 @@ async function promptForImportAction(term, session) {
|
|
|
11437
11473
|
const shortId2 = stripHydraSessionPrefix(session.sessionId);
|
|
11438
11474
|
const fromMachine = session.importedFromMachine ?? "another machine";
|
|
11439
11475
|
const originalCwd = shortenHomePath(session.cwd);
|
|
11440
|
-
let selected =
|
|
11476
|
+
let selected = ACTION_CHOICES.findIndex((c) => c.key === "view");
|
|
11477
|
+
if (selected < 0) {
|
|
11478
|
+
selected = 0;
|
|
11479
|
+
}
|
|
11441
11480
|
const render = () => {
|
|
11442
11481
|
const choiceRows = ACTION_CHOICES.length * 2;
|
|
11443
11482
|
const contentHeight = 7 + choiceRows + 2;
|
|
@@ -11482,7 +11521,7 @@ async function promptForImportAction(term, session) {
|
|
|
11482
11521
|
}
|
|
11483
11522
|
row++;
|
|
11484
11523
|
term.moveTo(layout.contentX, layout.contentY + row);
|
|
11485
|
-
term.dim.noFormat(" \u2191/\u2193 navigate \xB7 Enter select \xB7
|
|
11524
|
+
term.dim.noFormat(" \u2191/\u2193 navigate \xB7 Enter select \xB7 f/v jump \xB7 Esc back");
|
|
11486
11525
|
return layout;
|
|
11487
11526
|
};
|
|
11488
11527
|
render();
|
|
@@ -11583,10 +11622,10 @@ var init_import_action_prompt = __esm({
|
|
|
11583
11622
|
init_prompt_utils();
|
|
11584
11623
|
ACTION_CHOICES = [
|
|
11585
11624
|
{
|
|
11586
|
-
key: "
|
|
11587
|
-
label: "
|
|
11588
|
-
hotkey: "
|
|
11589
|
-
description: "spawn
|
|
11625
|
+
key: "fork-local",
|
|
11626
|
+
label: "Fork locally",
|
|
11627
|
+
hotkey: "f",
|
|
11628
|
+
description: "spawn a local fork \u2014 original imported copy stays as-is"
|
|
11590
11629
|
},
|
|
11591
11630
|
{
|
|
11592
11631
|
key: "view",
|
|
@@ -12460,6 +12499,7 @@ async function runTuiApp(opts) {
|
|
|
12460
12499
|
const viewPrefs = {
|
|
12461
12500
|
showThoughts: config.tui.showThoughts
|
|
12462
12501
|
};
|
|
12502
|
+
const pickerPrefs = createPickerPrefs();
|
|
12463
12503
|
let altScreenEngaged = false;
|
|
12464
12504
|
const enterAltScreen = () => {
|
|
12465
12505
|
if (altScreenEngaged) {
|
|
@@ -12487,7 +12527,15 @@ async function runTuiApp(opts) {
|
|
|
12487
12527
|
let nextOpts = opts;
|
|
12488
12528
|
try {
|
|
12489
12529
|
while (nextOpts !== null) {
|
|
12490
|
-
nextOpts = await runSession(
|
|
12530
|
+
nextOpts = await runSession(
|
|
12531
|
+
term,
|
|
12532
|
+
config,
|
|
12533
|
+
target,
|
|
12534
|
+
nextOpts,
|
|
12535
|
+
exitHint,
|
|
12536
|
+
viewPrefs,
|
|
12537
|
+
pickerPrefs
|
|
12538
|
+
);
|
|
12491
12539
|
}
|
|
12492
12540
|
} finally {
|
|
12493
12541
|
leaveAltScreen();
|
|
@@ -12507,8 +12555,8 @@ async function runTuiApp(opts) {
|
|
|
12507
12555
|
);
|
|
12508
12556
|
}
|
|
12509
12557
|
}
|
|
12510
|
-
async function runSession(term, config, target, opts, exitHint, viewPrefs) {
|
|
12511
|
-
const ctx = await resolveSession(term, config, target, opts);
|
|
12558
|
+
async function runSession(term, config, target, opts, exitHint, viewPrefs, pickerPrefs) {
|
|
12559
|
+
const ctx = await resolveSession(term, config, target, opts, pickerPrefs);
|
|
12512
12560
|
if (!ctx) {
|
|
12513
12561
|
term.grabInput(false);
|
|
12514
12562
|
return null;
|
|
@@ -13017,6 +13065,7 @@ async function runSession(term, config, target, opts, exitHint, viewPrefs) {
|
|
|
13017
13065
|
history: buildCombinedHistory(globalHistory, history)
|
|
13018
13066
|
});
|
|
13019
13067
|
dispatcherRef = dispatcher;
|
|
13068
|
+
let livePeerHistoryRecording = false;
|
|
13020
13069
|
const recordHistoryEntry = (entry) => {
|
|
13021
13070
|
const trimmed = entry.replace(/\n+$/, "");
|
|
13022
13071
|
if (trimmed.length === 0) {
|
|
@@ -13355,10 +13404,10 @@ async function runSession(term, config, target, opts, exitHint, viewPrefs) {
|
|
|
13355
13404
|
const amendDesc = "amend the in-flight turn (cancel + replace)";
|
|
13356
13405
|
const head = config.tui.defaultEnterAction === "amend" ? [
|
|
13357
13406
|
["Enter", amendDesc],
|
|
13358
|
-
["Ctrl+Enter / Shift+Enter", enqueueDesc]
|
|
13407
|
+
["Ctrl+Enter / Shift+Enter / ^S", enqueueDesc]
|
|
13359
13408
|
] : [
|
|
13360
13409
|
["Enter", enqueueDesc],
|
|
13361
|
-
["Ctrl+Enter / Shift+Enter", amendDesc]
|
|
13410
|
+
["Ctrl+Enter / Shift+Enter / ^S", amendDesc]
|
|
13362
13411
|
];
|
|
13363
13412
|
return [...head, ...HELP_ENTRIES_TAIL];
|
|
13364
13413
|
};
|
|
@@ -13426,7 +13475,8 @@ async function runSession(term, config, target, opts, exitHint, viewPrefs) {
|
|
|
13426
13475
|
sessions,
|
|
13427
13476
|
config,
|
|
13428
13477
|
target,
|
|
13429
|
-
currentSessionId: resolvedSessionId
|
|
13478
|
+
currentSessionId: resolvedSessionId,
|
|
13479
|
+
prefs: pickerPrefs
|
|
13430
13480
|
});
|
|
13431
13481
|
if (choice2.kind === "abort") {
|
|
13432
13482
|
screen.start({ skipFullscreen: true });
|
|
@@ -14256,6 +14306,9 @@ async function runSession(term, config, target, opts, exitHint, viewPrefs) {
|
|
|
14256
14306
|
return;
|
|
14257
14307
|
}
|
|
14258
14308
|
if (event.kind === "user-text") {
|
|
14309
|
+
if (livePeerHistoryRecording) {
|
|
14310
|
+
recordHistoryEntry(event.text);
|
|
14311
|
+
}
|
|
14259
14312
|
closeAgentText();
|
|
14260
14313
|
if (toolsBlockStartedAt !== null) {
|
|
14261
14314
|
toolsBlockEndedAt = Date.now();
|
|
@@ -14388,6 +14441,12 @@ async function runSession(term, config, target, opts, exitHint, viewPrefs) {
|
|
|
14388
14441
|
};
|
|
14389
14442
|
const buffered = bufferedEvents;
|
|
14390
14443
|
bufferedEvents = [];
|
|
14444
|
+
const replayedPromptTexts = [];
|
|
14445
|
+
for (const event of buffered) {
|
|
14446
|
+
if (event.kind === "user-text" && typeof event.text === "string") {
|
|
14447
|
+
replayedPromptTexts.push(event.text);
|
|
14448
|
+
}
|
|
14449
|
+
}
|
|
14391
14450
|
screen.pauseRepaint();
|
|
14392
14451
|
try {
|
|
14393
14452
|
for (const event of buffered) {
|
|
@@ -14396,6 +14455,15 @@ async function runSession(term, config, target, opts, exitHint, viewPrefs) {
|
|
|
14396
14455
|
} finally {
|
|
14397
14456
|
screen.resumeRepaint();
|
|
14398
14457
|
}
|
|
14458
|
+
if (replayedPromptTexts.length > 0) {
|
|
14459
|
+
const merged = mergeReplayedEntries(history, replayedPromptTexts);
|
|
14460
|
+
if (merged !== history) {
|
|
14461
|
+
history = merged;
|
|
14462
|
+
dispatcher.setHistory(buildCombinedHistory(globalHistory, history));
|
|
14463
|
+
saveHistory(historyFile, history).catch(() => void 0);
|
|
14464
|
+
}
|
|
14465
|
+
}
|
|
14466
|
+
livePeerHistoryRecording = true;
|
|
14399
14467
|
if (initialTurnStartedAt !== void 0 && pendingTurns > 0) {
|
|
14400
14468
|
sessionBusySince = initialTurnStartedAt;
|
|
14401
14469
|
screen.setBanner({
|
|
@@ -14550,7 +14618,7 @@ connection lost: ${err.message}
|
|
|
14550
14618
|
}
|
|
14551
14619
|
return await sessionDone;
|
|
14552
14620
|
}
|
|
14553
|
-
async function resolveSession(term, config, target, opts) {
|
|
14621
|
+
async function resolveSession(term, config, target, opts, pickerPrefs) {
|
|
14554
14622
|
const cwd = opts.cwd ?? process.cwd();
|
|
14555
14623
|
if (opts.sessionId) {
|
|
14556
14624
|
const ctx = {
|
|
@@ -14586,7 +14654,8 @@ async function resolveSession(term, config, target, opts) {
|
|
|
14586
14654
|
cwd,
|
|
14587
14655
|
sessions,
|
|
14588
14656
|
config,
|
|
14589
|
-
target
|
|
14657
|
+
target,
|
|
14658
|
+
prefs: pickerPrefs
|
|
14590
14659
|
});
|
|
14591
14660
|
if (choice.kind === "abort") {
|
|
14592
14661
|
return null;
|
|
@@ -14834,7 +14903,7 @@ var init_app = __esm({
|
|
|
14834
14903
|
["^V", "paste image from clipboard"],
|
|
14835
14904
|
["^O", "expand / collapse tools block"],
|
|
14836
14905
|
null,
|
|
14837
|
-
["^R
|
|
14906
|
+
["^R", "history reverse search (^S walks forward once engaged)"],
|
|
14838
14907
|
["PgUp / PgDn", "scroll scrollback"],
|
|
14839
14908
|
["Mouse wheel", "scroll scrollback (when mouse capture is on)"],
|
|
14840
14909
|
["^X", "toggle mouse capture (wheel scroll vs. text selection)"],
|