@datalabrotterdam/nova-ai-cli 1.2.0-alpha.16 → 1.2.0-alpha.17

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 CHANGED
@@ -1,3 +1,10 @@
1
+ # [@datalabrotterdam/nova-ai-cli-v1.2.0-alpha.17](https://github.com/Datalab-Rotterdam/nova-ai-cli/compare/@datalabrotterdam/nova-ai-cli@1.2.0-alpha.16...@datalabrotterdam/nova-ai-cli@1.2.0-alpha.17) (2026-10-25)
2
+
3
+
4
+ ### Features
5
+
6
+ * **cli:** /tasks to monitor and stop background tasks; drop Ctrl+B ([aaa3cb9](https://github.com/Datalab-Rotterdam/nova-ai-cli/commit/aaa3cb9dc058ab9a726964414a959084c9237669))
7
+
1
8
  # [@datalabrotterdam/nova-ai-cli-v1.2.0-alpha.16](https://github.com/Datalab-Rotterdam/nova-ai-cli/compare/@datalabrotterdam/nova-ai-cli@1.2.0-alpha.15...@datalabrotterdam/nova-ai-cli@1.2.0-alpha.16) (2026-10-25)
2
9
 
3
10
 
package/README.md CHANGED
@@ -75,9 +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,
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.
78
+ row. Drag over text to select it, then click (or press Enter) to copy it;
79
+ Shift+arrows extend the selection and Esc cancels. `/copy` copies the last
80
+ answer.
81
81
 
82
82
  - **Modes** (Shift+Tab): `agent` does the work; `plan` reads the code, asks
83
83
  questions and builds a task list but changes nothing; `ask` only answers;
@@ -99,17 +99,16 @@ again gives the wheel back). `/copy` copies the last answer.
99
99
  | Key | Action |
100
100
  |---|---|
101
101
  | Enter / Shift+Enter | Send / new line (Shift+Enter where the terminal supports the kitty keyboard protocol: VS Code, kitty, WezTerm, Ghostty, foot, Alacritty, recent iTerm2; Alt+Enter, Ctrl+J or a trailing `\` work everywhere) |
102
- | Esc | Cancel the running turn |
103
- | Ctrl+C twice | Exit; prints `nova-ai --resume <id>` |
102
+ | Esc | Interrupt the running turn (asks whether running background tasks keep going) |
103
+ | Ctrl+C | Clear the prompt; on an empty prompt, twice to exit (prints `nova-ai --resume <id>`) |
104
104
  | Shift+Tab | Cycle agent / ask / plan / autopilot and your own agents |
105
105
  | Ctrl+R / Ctrl+P / Ctrl+K | Sessions / models / permission mode |
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) |
106
+ | Ctrl+O / Ctrl+T | Thinking and verbose tool output / tool inspector |
108
107
  | Alt+V | Paste a clipboard image |
109
108
 
110
109
  Slash commands: `/help`, `/model`, `/effort` (thinking models), `/mode`, `/permission`, `/session`,
111
110
  `/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),
112
- `/tools`, `/mcp`, `/skills`, `/shell` and `/agent` (background jobs), `/trust`
111
+ `/tools`, `/tasks` (watch or stop background shells and agents), `/mcp`, `/skills`, `/shell` and `/agent` (background jobs), `/trust`
113
112
  and `/exit`. Your own commands are markdown files in `~/.nova-ai/commands/` or
114
113
  the repository's `.nova-ai/commands/` (Claude Code's format, so
115
114
  `.claude/commands` works too): the file is the prompt, `$ARGUMENTS` what you
package/dist/index.js CHANGED
@@ -185,7 +185,7 @@ ${MAIN_HELP}
185
185
  );
186
186
  return 2;
187
187
  }
188
- const { default: runChat } = await import("./tui-UHITDRFM.js");
188
+ const { default: runChat } = await import("./tui-VP2V5DOF.js");
189
189
  return runChat(options);
190
190
  }
191
191
  }
@@ -2175,6 +2175,13 @@ var builtinCommands = [
2175
2175
  ctx.openToolInspector();
2176
2176
  }
2177
2177
  },
2178
+ {
2179
+ name: "tasks",
2180
+ description: "Monitor background shells and agents, and stop one",
2181
+ run(ctx) {
2182
+ return ctx.openTasks();
2183
+ }
2184
+ },
2178
2185
  {
2179
2186
  name: "mcp",
2180
2187
  description: "Inspect MCP servers, or sign in to one: /mcp login|logout <server>",
@@ -4212,9 +4219,6 @@ var TuiController = class {
4212
4219
  case "k":
4213
4220
  this.openPermissionModes();
4214
4221
  return true;
4215
- case "b":
4216
- void this.openBackgroundTasks();
4217
- return true;
4218
4222
  case "o":
4219
4223
  this.toggleToolDetails();
4220
4224
  return true;
@@ -4400,36 +4404,65 @@ var TuiController = class {
4400
4404
  }
4401
4405
  );
4402
4406
  }
4403
- openBackgroundTasks = async () => {
4407
+ /** /tasks: background shells and agents; pick one to read its output or stop it. */
4408
+ openTasks = async () => {
4409
+ let jobs;
4404
4410
  try {
4405
- const jobs = await this.runner.listBackgroundJobs();
4406
- this.showSelection(
4407
- "Background shells and agents",
4408
- jobs.map((job) => ({
4409
- value: job.jobId,
4410
- label: `${job.kind}: ${job.title}`,
4411
- description: job.status
4412
- })),
4413
- "background-tasks",
4414
- (jobId) => {
4415
- void this.runner.backgroundOutput(jobId).then(
4416
- (result) => this.showText(
4417
- `${result.job.kind}: ${result.job.title}`,
4418
- `jobId: ${jobId}
4419
- status: ${result.job.status}
4411
+ jobs = await this.runner.listBackgroundJobs();
4412
+ } catch (error) {
4413
+ this.showText("Background tasks", errorMessage(error, "Failed to list background tasks."));
4414
+ return;
4415
+ }
4416
+ if (jobs.length === 0) {
4417
+ this.showText("Background tasks", "No background shells or agents in this session.");
4418
+ return;
4419
+ }
4420
+ this.showSelection(
4421
+ "Background tasks",
4422
+ jobs.map((job) => ({ value: job.jobId, label: taskLabel(job), description: job.status })),
4423
+ "background-tasks",
4424
+ (jobId) => {
4425
+ const job = jobs.find((candidate) => candidate.jobId === jobId);
4426
+ if (job.status !== "running") {
4427
+ void this.showTaskOutput(jobId);
4428
+ return;
4429
+ }
4430
+ this.showSelection(
4431
+ taskLabel(job),
4432
+ [
4433
+ { value: "output", label: "View output", description: "What it printed so far" },
4434
+ { value: "stop", label: "Stop", description: "Kill this task" }
4435
+ ],
4436
+ "background-tasks",
4437
+ (action) => void (action === "stop" ? this.stopTask(job) : this.showTaskOutput(jobId))
4438
+ );
4439
+ }
4440
+ );
4441
+ };
4442
+ async showTaskOutput(jobId) {
4443
+ try {
4444
+ const result = await this.runner.backgroundOutput(jobId);
4445
+ this.showText(
4446
+ taskLabel(result.job),
4447
+ `status: ${result.job.status}
4420
4448
  outputPath: ${result.outputPath ?? result.job.outputPath ?? ""}
4421
4449
 
4422
4450
  ${result.output}${result.truncated ? "\n\n[output truncated]" : ""}`
4423
- )
4424
- ).catch(
4425
- (error) => this.showText("Background job", errorMessage(error, "Failed to read output."))
4426
- );
4427
- }
4428
4451
  );
4429
4452
  } catch (error) {
4430
- this.showText("Background jobs", errorMessage(error, "Failed to list background jobs."));
4453
+ this.showText("Background task", errorMessage(error, "Failed to read output."));
4431
4454
  }
4432
- };
4455
+ }
4456
+ /** Stops one task, then shows the list again. */
4457
+ async stopTask(job) {
4458
+ try {
4459
+ await this.runner.killBackgroundJob(job.jobId);
4460
+ this.store.setState({ statusLine: `Stopped ${taskLabel(job)}.` });
4461
+ } catch (error) {
4462
+ this.store.setState({ statusLine: errorMessage(error, `Failed to stop ${taskLabel(job)}.`) });
4463
+ }
4464
+ await this.openTasks();
4465
+ }
4433
4466
  openToolInspector = () => {
4434
4467
  if (this.toolCalls().length === 0) {
4435
4468
  this.showText("Tool calls", "No tool calls yet.");
@@ -4685,6 +4718,7 @@ ${skill.path}`
4685
4718
  return "Workspace trusted: its allow rules apply now; MCP servers it declares start with the next session (/clear).";
4686
4719
  },
4687
4720
  openToolInspector: this.openToolInspector,
4721
+ openTasks: this.openTasks,
4688
4722
  openMcpInspector: this.openMcpInspector,
4689
4723
  openSkillInspector: this.openSkillInspector,
4690
4724
  switchSkill: this.switchSkill,
@@ -4892,6 +4926,9 @@ function mcpCounts(runner) {
4892
4926
  function errorMessage(error, fallback) {
4893
4927
  return error instanceof Error ? error.message : fallback;
4894
4928
  }
4929
+ function taskLabel(job) {
4930
+ return `${job.kind === "terminal" ? "shell" : "agent"}: ${job.title}`;
4931
+ }
4895
4932
 
4896
4933
  // src/tui/ink/dialogs/views.tsx
4897
4934
  import { Box as Box5, Text as Text5, useInput as useInput2, usePaste as usePaste2 } from "ink";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datalabrotterdam/nova-ai-cli",
3
- "version": "1.2.0-alpha.16",
3
+ "version": "1.2.0-alpha.17",
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",