@datalabrotterdam/nova-ai-cli 1.2.0-alpha.12 → 1.2.0-alpha.14
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 +14 -0
- package/README.md +9 -2
- package/dist/index.js +1 -1
- package/dist/{tui-RR2LCTL7.js → tui-NBVDJQIA.js} +67 -2
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# [@datalabrotterdam/nova-ai-cli-v1.2.0-alpha.14](https://github.com/Datalab-Rotterdam/nova-ai-cli/compare/@datalabrotterdam/nova-ai-cli@1.2.0-alpha.13...@datalabrotterdam/nova-ai-cli@1.2.0-alpha.14) (2026-10-24)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* GitHub Action and GitLab template for nova-ai in CI ([2352e97](https://github.com/Datalab-Rotterdam/nova-ai-cli/commit/2352e97327ae3e3c65be238bb2d3134db13abeb3))
|
|
7
|
+
|
|
8
|
+
# [@datalabrotterdam/nova-ai-cli-v1.2.0-alpha.13](https://github.com/Datalab-Rotterdam/nova-ai-cli/compare/@datalabrotterdam/nova-ai-cli@1.2.0-alpha.12...@datalabrotterdam/nova-ai-cli@1.2.0-alpha.13) (2026-10-24)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **cli:** Ctrl+S select mode and /copy ([bcb7e02](https://github.com/Datalab-Rotterdam/nova-ai-cli/commit/bcb7e02f05739caae7a6155d2c37b7d2e54d01fa))
|
|
14
|
+
|
|
1
15
|
# [@datalabrotterdam/nova-ai-cli-v1.2.0-alpha.12](https://github.com/Datalab-Rotterdam/nova-ai-cli/compare/@datalabrotterdam/nova-ai-cli@1.2.0-alpha.11...@datalabrotterdam/nova-ai-cli@1.2.0-alpha.12) (2026-10-24)
|
|
2
16
|
|
|
3
17
|
|
package/README.md
CHANGED
|
@@ -75,7 +75,9 @@ The UI runs full screen (the terminal's alternate screen, like `less` or
|
|
|
75
75
|
`vim`) and gives your shell its screen back on exit. The mouse wheel or
|
|
76
76
|
PgUp/PgDn scroll the transcript, Esc jumps back to the newest output, and
|
|
77
77
|
while you read an answer the prompt that asked for it stays pinned on the top
|
|
78
|
-
row. To select text, hold Shift (Option in some macOS terminals) while dragging
|
|
78
|
+
row. To select text, hold Shift (Option in some macOS terminals) while dragging,
|
|
79
|
+
or press Ctrl+S to turn the mouse over to your terminal (select mode; Ctrl+S
|
|
80
|
+
again gives the wheel back). `/copy` copies the last answer.
|
|
79
81
|
|
|
80
82
|
- **Modes** (Shift+Tab): `agent` does the work; `plan` reads the code, asks
|
|
81
83
|
questions and builds a task list but changes nothing; `ask` only answers;
|
|
@@ -102,10 +104,11 @@ row. To select text, hold Shift (Option in some macOS terminals) while dragging.
|
|
|
102
104
|
| Shift+Tab | Cycle agent / ask / plan / autopilot and your own agents |
|
|
103
105
|
| Ctrl+R / Ctrl+P / Ctrl+K | Sessions / models / permission mode |
|
|
104
106
|
| Ctrl+O / Ctrl+T / Ctrl+B | Thinking and verbose tool output / tool inspector / background jobs |
|
|
107
|
+
| Ctrl+S | Select mode: the mouse selects text instead of scrolling (again to switch back) |
|
|
105
108
|
| Alt+V | Paste a clipboard image |
|
|
106
109
|
|
|
107
110
|
Slash commands: `/help`, `/model`, `/effort` (thinking models), `/mode`, `/permission`, `/session`,
|
|
108
|
-
`/resume`, `/clear`, `/compact`, `/rewind [n]`, `/queue`, `/steer`, `/usage` (context, and the session's tokens and estimated cost; the status line shows `≈ €0.0123` once a priced model answered),
|
|
111
|
+
`/resume`, `/clear`, `/compact`, `/copy`, `/rewind [n]`, `/queue`, `/steer`, `/usage` (context, and the session's tokens and estimated cost; the status line shows `≈ €0.0123` once a priced model answered),
|
|
109
112
|
`/tools`, `/mcp`, `/skills`, `/shell` and `/agent` (background jobs), `/trust`
|
|
110
113
|
and `/exit`. Your own commands are markdown files in `~/.nova-ai/commands/` or
|
|
111
114
|
the repository's `.nova-ai/commands/` (Claude Code's format, so
|
|
@@ -136,6 +139,10 @@ nova-ai -p -c "and now add a changelog entry"
|
|
|
136
139
|
Exit codes: `0` success, `1` configuration or runtime error, `2` cancelled,
|
|
137
140
|
`3` tool-turn safety limit, `130` interrupted.
|
|
138
141
|
|
|
142
|
+
In CI: a GitHub Action (`uses: Datalab-Rotterdam/nova-ai-cli@main`) and a GitLab
|
|
143
|
+
template review pull requests and post the answer as a comment; see
|
|
144
|
+
[docs/CI.md](https://github.com/Datalab-Rotterdam/nova-ai-cli/blob/main/docs/CI.md).
|
|
145
|
+
|
|
139
146
|
## Editors (ACP)
|
|
140
147
|
|
|
141
148
|
Configure `nova-ai --acp` as a custom agent. For Zed:
|
package/dist/index.js
CHANGED
|
@@ -1554,6 +1554,10 @@ function StatusLine({
|
|
|
1554
1554
|
ui.permissionMode,
|
|
1555
1555
|
separator,
|
|
1556
1556
|
ui.busy ? /* @__PURE__ */ jsx2(Text2, { color: palette.warning, children: "working" }) : /* @__PURE__ */ jsx2(Text2, { color: palette.success, children: "ready" }),
|
|
1557
|
+
ui.selectMode ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
1558
|
+
separator,
|
|
1559
|
+
/* @__PURE__ */ jsx2(Text2, { color: palette.warning, children: "select \xB7 Ctrl+S" })
|
|
1560
|
+
] }) : null,
|
|
1557
1561
|
usage ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
1558
1562
|
separator,
|
|
1559
1563
|
usage
|
|
@@ -2092,6 +2096,13 @@ var builtinCommands = [
|
|
|
2092
2096
|
ctx.print(await ctx.switchSkill(name, action === "on", global));
|
|
2093
2097
|
}
|
|
2094
2098
|
},
|
|
2099
|
+
{
|
|
2100
|
+
name: "copy",
|
|
2101
|
+
description: "Copy the last answer to the clipboard (Ctrl+S selects text with the mouse)",
|
|
2102
|
+
async run(ctx) {
|
|
2103
|
+
ctx.print(await ctx.copyLastAnswer());
|
|
2104
|
+
}
|
|
2105
|
+
},
|
|
2095
2106
|
{
|
|
2096
2107
|
name: "usage",
|
|
2097
2108
|
description: "Inspect estimated context usage by category",
|
|
@@ -2386,6 +2397,35 @@ const png = bitmap.representationUsingTypeProperties($.NSBitmapImageFileTypePNG,
|
|
|
2386
2397
|
console.log(ObjC.unwrap(png.base64EncodedStringWithOptions(0)));
|
|
2387
2398
|
`;
|
|
2388
2399
|
|
|
2400
|
+
// src/tui/files/clipboard-text.ts
|
|
2401
|
+
import { spawn } from "child_process";
|
|
2402
|
+
async function copyText(text, write, tools = systemTools()) {
|
|
2403
|
+
write(`\x1B]52;c;${Buffer.from(text, "utf8").toString("base64")}\x07`);
|
|
2404
|
+
for (const [command, args] of tools) {
|
|
2405
|
+
if (await pipeTo(command, args, text)) return command;
|
|
2406
|
+
}
|
|
2407
|
+
return null;
|
|
2408
|
+
}
|
|
2409
|
+
function systemTools() {
|
|
2410
|
+
if (process.platform === "darwin") return [["pbcopy", []]];
|
|
2411
|
+
if (process.platform === "win32") return [["clip", []]];
|
|
2412
|
+
return [["wl-copy", []], ["xclip", ["-selection", "clipboard"]]];
|
|
2413
|
+
}
|
|
2414
|
+
function pipeTo(command, args, text) {
|
|
2415
|
+
return new Promise((resolve2) => {
|
|
2416
|
+
let child;
|
|
2417
|
+
try {
|
|
2418
|
+
child = spawn(command, args, { stdio: ["pipe", "ignore", "ignore"] });
|
|
2419
|
+
} catch {
|
|
2420
|
+
resolve2(false);
|
|
2421
|
+
return;
|
|
2422
|
+
}
|
|
2423
|
+
child.on("error", () => resolve2(false));
|
|
2424
|
+
child.on("close", (code) => resolve2(code === 0));
|
|
2425
|
+
child.stdin.end(text);
|
|
2426
|
+
});
|
|
2427
|
+
}
|
|
2428
|
+
|
|
2389
2429
|
// src/tui/files/find-fd.ts
|
|
2390
2430
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
2391
2431
|
var FD_CANDIDATES = ["fd", "fdfind"];
|
|
@@ -2454,7 +2494,7 @@ var DraftImageAttachments = class {
|
|
|
2454
2494
|
};
|
|
2455
2495
|
|
|
2456
2496
|
// src/tui/files/workspace-autocomplete.ts
|
|
2457
|
-
import { spawn } from "child_process";
|
|
2497
|
+
import { spawn as spawn2 } from "child_process";
|
|
2458
2498
|
import { readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
2459
2499
|
import { homedir } from "os";
|
|
2460
2500
|
import { basename, dirname as dirname2, join as join3 } from "path";
|
|
@@ -2665,7 +2705,7 @@ function walkWithFd(cwd, fdPath, query, signal) {
|
|
|
2665
2705
|
resolve2([]);
|
|
2666
2706
|
return;
|
|
2667
2707
|
}
|
|
2668
|
-
const child =
|
|
2708
|
+
const child = spawn2(fdPath, args, { stdio: ["ignore", "pipe", "ignore"] });
|
|
2669
2709
|
let stdout = "";
|
|
2670
2710
|
let settled = false;
|
|
2671
2711
|
const finish = (entries) => {
|
|
@@ -4051,6 +4091,9 @@ var TuiController = class {
|
|
|
4051
4091
|
case "o":
|
|
4052
4092
|
this.toggleToolDetails();
|
|
4053
4093
|
return true;
|
|
4094
|
+
case "s":
|
|
4095
|
+
this.toggleSelectMode();
|
|
4096
|
+
return true;
|
|
4054
4097
|
case "t":
|
|
4055
4098
|
this.openToolInspector();
|
|
4056
4099
|
return true;
|
|
@@ -4325,6 +4368,27 @@ ${skill.path}`
|
|
|
4325
4368
|
this.scroll = next;
|
|
4326
4369
|
this.refresh();
|
|
4327
4370
|
}
|
|
4371
|
+
/**
|
|
4372
|
+
* Select mode: mouse reporting off, so the terminal selects and copies text
|
|
4373
|
+
* natively (the wheel no longer scrolls the transcript; PgUp/PgDn still do).
|
|
4374
|
+
* For terminals where Shift/Option + drag does not get past mouse reporting.
|
|
4375
|
+
*/
|
|
4376
|
+
toggleSelectMode() {
|
|
4377
|
+
const selectMode = !this.store.getState().selectMode;
|
|
4378
|
+
this.io.write(selectMode ? MOUSE_REPORTING_OFF : MOUSE_REPORTING_ON);
|
|
4379
|
+
this.store.setState({
|
|
4380
|
+
selectMode,
|
|
4381
|
+
statusLine: selectMode ? "Select mode: drag to select text, your terminal copies it. PgUp/PgDn scroll; Ctrl+S turns the mouse wheel back on." : "Mouse wheel scrolls the transcript again. Ctrl+S to select text."
|
|
4382
|
+
});
|
|
4383
|
+
}
|
|
4384
|
+
/** /copy: the last answer (not its thinking) to the clipboard. */
|
|
4385
|
+
async copyLastAnswer() {
|
|
4386
|
+
const answer = [...this.store.getState().messages].reverse().find((message) => message.role === "assistant" && !message.thinking && message.text.trim());
|
|
4387
|
+
if (answer?.role !== "assistant") return "There is no answer to copy yet.";
|
|
4388
|
+
const tool = await copyText(answer.text.trim(), (data) => this.io.write(data));
|
|
4389
|
+
const size = `${answer.text.trim().length} characters`;
|
|
4390
|
+
return tool ? `Copied the last answer (${size}).` : `Sent the last answer (${size}) to the terminal's clipboard; if nothing was copied, use Ctrl+S to select it.`;
|
|
4391
|
+
}
|
|
4328
4392
|
toggleToolDetails() {
|
|
4329
4393
|
this.expandedTools = !this.expandedTools;
|
|
4330
4394
|
this.store.setState({
|
|
@@ -4397,6 +4461,7 @@ ${skill.path}`
|
|
|
4397
4461
|
setModel: (model) => this.setModel(model),
|
|
4398
4462
|
getInteractionMode: () => this.runner.interactionMode,
|
|
4399
4463
|
getInteractionModes: () => this.runner.modeIds,
|
|
4464
|
+
copyLastAnswer: () => this.copyLastAnswer(),
|
|
4400
4465
|
agentCommands: () => this.agentCommands(),
|
|
4401
4466
|
setInteractionMode: (mode) => this.setInteractionMode(mode),
|
|
4402
4467
|
getPermissionMode: () => this.store.getState().permissionMode,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datalabrotterdam/nova-ai-cli",
|
|
3
|
-
"version": "1.2.0-alpha.
|
|
3
|
+
"version": "1.2.0-alpha.14",
|
|
4
4
|
"description": "Agentic coding with DataLab Rotterdam's Nova AI in your terminal: interactive TUI, headless mode, and an ACP agent for editors",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "DataLab Rotterdam",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@agentclientprotocol/sdk": "^0.29.0",
|
|
42
|
-
"@datalabrotterdam/nova-ai-agent": "^1.0.0-alpha.
|
|
42
|
+
"@datalabrotterdam/nova-ai-agent": "^1.0.0-alpha.10",
|
|
43
43
|
"@datalabrotterdam/nova-ai-browser-host": "^1.0.0",
|
|
44
44
|
"chalk": "^5.3.0",
|
|
45
45
|
"cli-highlight": "^2.1.11",
|