@datalabrotterdam/nova-ai-cli 1.2.0-alpha.2 → 1.2.0-alpha.3

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,17 @@
1
+ # [@datalabrotterdam/nova-ai-cli-v1.2.0-alpha.3](https://github.com/Datalab-Rotterdam/nova-ai-cli/compare/@datalabrotterdam/nova-ai-cli@1.2.0-alpha.2...@datalabrotterdam/nova-ai-cli@1.2.0-alpha.3) (2026-10-04)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **agent:** never overwrite an unreadable settings file when switching a skill ([ca989ef](https://github.com/Datalab-Rotterdam/nova-ai-cli/commit/ca989ef1810fc903cea069cdadf3041fe164e364))
7
+ * **cli:** install the command as nova-ai only ([eda8064](https://github.com/Datalab-Rotterdam/nova-ai-cli/commit/eda806411af1738ff6f6b1404d23d4e84a74ced8))
8
+
9
+
10
+ ### Features
11
+
12
+ * **agent,cli:** Nova skill folders, switching skills off, a budgeted skill list ([0891c79](https://github.com/Datalab-Rotterdam/nova-ai-cli/commit/0891c79248d3a645d638f12f46928a54652b0941))
13
+ * **cli:** update notice from npm for every channel, cached daily ([c3a0014](https://github.com/Datalab-Rotterdam/nova-ai-cli/commit/c3a001473336aac14d216cdeec0bedde4df34427))
14
+
1
15
  # [@datalabrotterdam/nova-ai-cli-v1.2.0-alpha.2](https://github.com/Datalab-Rotterdam/nova-ai-cli/compare/@datalabrotterdam/nova-ai-cli@1.2.0-alpha.1...@datalabrotterdam/nova-ai-cli@1.2.0-alpha.2) (2026-10-03)
2
16
 
3
17
 
package/README.md CHANGED
@@ -44,6 +44,11 @@ nova-ai # start the terminal UI in your project
44
44
  Or without installing: `npx @datalabrotterdam/nova-ai-cli login`, then
45
45
  `npx @datalabrotterdam/nova-ai-cli`.
46
46
 
47
+ When a newer version is on npm, the terminal UI says so, and so do `-p` and
48
+ `--version` (on stderr, only in a terminal). npm is asked at most once a day;
49
+ an alpha install also follows the `alpha` channel. `NOVA_NO_UPDATE_CHECK=1`
50
+ turns it off (it is off in CI).
51
+
47
52
  ```
48
53
  nova-ai [options] Interactive terminal UI
49
54
  nova-ai -p [options] [prompt] Run one request headless (see nova-ai -p --help)
@@ -131,7 +136,7 @@ always options, client file system and terminals, form questions, plans, usage
131
136
  and title updates, MCP servers (stdio, HTTP, SSE), image prompts, and three
132
137
  login methods (browser, `NOVA_API_KEY`, or `nova-ai login` in a terminal).
133
138
  Nova-specific features (prompt queue, rewind, compaction, context usage,
134
- background jobs) are `_nova/…` extension methods. The agent is also published
139
+ per-session settings, background jobs) are `_nova/…` extension methods. The agent is also published
135
140
  on its own as
136
141
  [`@datalabrotterdam/nova-ai-agent`](https://www.npmjs.com/package/@datalabrotterdam/nova-ai-agent)
137
142
  (command `nova-ai-agent`), without the terminal UI, for integrations that only
@@ -153,6 +158,9 @@ need ACP. Everything is documented in
153
158
  private `~/.nova-ai/projects/<key>/settings.json`, outside the repository.
154
159
  Compound commands (`a && b`, pipes) are checked part by part; substitutions
155
160
  and nested shells need an exact rule.
161
+ - **Web pages are fetched only with approval.** `fetch_url` asks every time
162
+ unless a rule allows that URL (a URL can carry data out), and a redirect to
163
+ another site needs its own approval.
156
164
  - **File access stays in the workspace** (symlinks resolved); commands run in
157
165
  their own process group with a timeout, and the whole tree is stopped on
158
166
  cancel.
@@ -172,8 +180,11 @@ VS Code extension: settings, per-project trust, sessions and memory.
172
180
  - **Instructions**: `NOVA.md` and `AGENTS.md` in the workspace.
173
181
  - **Memory**: a `MEMORY.md` index plus notes, globally and per project,
174
182
  through the `memory_read` and `memory_write` tools (writes need approval).
175
- - **Skills**: `SKILL.md` files under `.agents/skills`, `.claude/skills` or
176
- `.codex/skills`, in the workspace or your home folder.
183
+ - **Skills**: `SKILL.md` files under `.nova-ai/skills` in the workspace or
184
+ `~/.nova-ai/skills` for every project (`.agents`, `.claude` and `.codex`
185
+ skills folders are read too). `/skills` lists them; `/skills off <name>`
186
+ switches one off in this project (`--global`: everywhere). Only names and
187
+ short descriptions go into the prompt; Nova loads a skill when it fits.
177
188
  - **MCP servers**: `.mcp.json` or `mcpServers` in `.nova-ai/settings.json`.
178
189
 
179
190
  The complete layout and rule syntax: [docs/NOVA_HOME.md](https://github.com/Datalab-Rotterdam/nova-ai-cli/blob/main/docs/NOVA_HOME.md).
@@ -195,6 +206,7 @@ hand).
195
206
  | `NOVA_TOOL_PROTOCOL` | Force `native` or `text` tool calling |
196
207
  | `NOVA_NES_MODEL` | Separate model for next-edit suggestions (ACP) |
197
208
  | `NOVA_KITTY_KEYBOARD` | `1`/`0`: force the kitty keyboard protocol on or off |
209
+ | `NOVA_NO_UPDATE_CHECK` | Don't ask npm for a newer version (also `NO_UPDATE_NOTIFIER`, or `CI`) |
198
210
 
199
211
  ## Development
200
212
 
@@ -0,0 +1,147 @@
1
+ import {
2
+ CLI_PACKAGE_NAME,
3
+ cliVersion
4
+ } from "./chunk-FMHWW3E6.js";
5
+
6
+ // src/update-check.ts
7
+ import { mkdirSync, readFileSync, writeFileSync } from "fs";
8
+ import { dirname, join } from "path";
9
+ import { novaHomeRoot } from "@datalabrotterdam/nova-ai-agent/core/nova-home.js";
10
+ var DIST_TAGS_URL = `https://registry.npmjs.org/-/package/${CLI_PACKAGE_NAME.replace("/", "%2F")}/dist-tags`;
11
+ var DEFAULT_TIMEOUT_MS = 3e3;
12
+ var CACHE_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
13
+ function updateCachePath() {
14
+ return join(novaHomeRoot(), "update-check.json");
15
+ }
16
+ function updateChecksDisabled(env = process.env) {
17
+ return Boolean(env.NOVA_NO_UPDATE_CHECK || env.NO_UPDATE_NOTIFIER || env.CI);
18
+ }
19
+ async function checkForUpdate(options = {}) {
20
+ try {
21
+ if (updateChecksDisabled(options.env)) return null;
22
+ const currentVersion = options.currentVersion ?? cliVersion();
23
+ if (!currentVersion) return null;
24
+ const distTags = await readDistTags(options);
25
+ if (!distTags) return null;
26
+ return chooseUpdate(currentVersion, distTags);
27
+ } catch {
28
+ return null;
29
+ }
30
+ }
31
+ function chooseUpdate(currentVersion, distTags) {
32
+ const channel = prereleaseChannel(currentVersion);
33
+ const tags = channel && distTags[channel] ? [channel, "latest"] : ["latest"];
34
+ let best = null;
35
+ for (const tag of tags) {
36
+ const version = distTags[tag];
37
+ if (!version || !isNewerVersion(version, currentVersion)) continue;
38
+ if (!best || isNewerVersion(version, best.version)) best = { tag, version };
39
+ }
40
+ if (!best) return null;
41
+ return {
42
+ currentVersion,
43
+ latestVersion: best.version,
44
+ command: `npm install -g ${CLI_PACKAGE_NAME}@${best.tag}`
45
+ };
46
+ }
47
+ async function readDistTags(options) {
48
+ const now = (options.now ?? Date.now)();
49
+ const cachePath = options.cachePath === void 0 ? updateCachePath() : options.cachePath;
50
+ const cached = cachePath ? readCache(cachePath) : null;
51
+ if (cached && now - cached.checkedAt >= 0 && now - cached.checkedAt < CACHE_MAX_AGE_MS) {
52
+ return cached.distTags;
53
+ }
54
+ const response = await (options.fetchImpl ?? fetch)(DIST_TAGS_URL, {
55
+ headers: { accept: "application/json" },
56
+ signal: AbortSignal.any([
57
+ AbortSignal.timeout(options.timeoutMs ?? DEFAULT_TIMEOUT_MS),
58
+ ...options.signal ? [options.signal] : []
59
+ ])
60
+ });
61
+ if (!response.ok) return null;
62
+ const payload = await response.json();
63
+ if (!isDistTags(payload)) return null;
64
+ if (cachePath) writeCache(cachePath, { checkedAt: now, distTags: payload });
65
+ return payload;
66
+ }
67
+ function readCache(path) {
68
+ try {
69
+ const value = JSON.parse(readFileSync(path, "utf8"));
70
+ if (value && typeof value === "object" && typeof value.checkedAt === "number" && isDistTags(value.distTags)) {
71
+ return value;
72
+ }
73
+ } catch {
74
+ }
75
+ return null;
76
+ }
77
+ function writeCache(path, value) {
78
+ try {
79
+ mkdirSync(dirname(path), { recursive: true, mode: 448 });
80
+ writeFileSync(path, `${JSON.stringify(value)}
81
+ `, { mode: 384 });
82
+ } catch {
83
+ }
84
+ }
85
+ function isDistTags(value) {
86
+ return typeof value === "object" && value !== null && !Array.isArray(value) && Object.values(value).every((version) => typeof version === "string");
87
+ }
88
+ function prereleaseChannel(version) {
89
+ const parsed = parseVersion(version);
90
+ const first = parsed?.prerelease[0];
91
+ return first && !/^\d+$/.test(first) ? first : null;
92
+ }
93
+ function readInstalledVersion() {
94
+ return cliVersion();
95
+ }
96
+ function isNewerVersion(candidate, current) {
97
+ const next = parseVersion(candidate);
98
+ const installed = parseVersion(current);
99
+ if (!next || !installed) return false;
100
+ for (let index = 0; index < 3; index++) {
101
+ const difference = next.core[index] - installed.core[index];
102
+ if (difference !== 0) return difference > 0;
103
+ }
104
+ return comparePrerelease(next.prerelease, installed.prerelease) > 0;
105
+ }
106
+ function parseVersion(value) {
107
+ const match = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/.exec(value);
108
+ if (!match) return null;
109
+ return {
110
+ core: [Number(match[1]), Number(match[2]), Number(match[3])],
111
+ prerelease: match[4]?.split(".") ?? []
112
+ };
113
+ }
114
+ function comparePrerelease(left, right) {
115
+ if (left.length === 0 || right.length === 0) {
116
+ if (left.length === right.length) return 0;
117
+ return left.length === 0 ? 1 : -1;
118
+ }
119
+ const length = Math.max(left.length, right.length);
120
+ for (let index = 0; index < length; index++) {
121
+ const leftPart = left[index];
122
+ const rightPart = right[index];
123
+ if (leftPart === void 0 || rightPart === void 0) {
124
+ return leftPart === rightPart ? 0 : leftPart === void 0 ? -1 : 1;
125
+ }
126
+ if (leftPart === rightPart) continue;
127
+ const leftNumber = /^\d+$/.test(leftPart) ? Number(leftPart) : null;
128
+ const rightNumber = /^\d+$/.test(rightPart) ? Number(rightPart) : null;
129
+ if (leftNumber !== null && rightNumber !== null) return leftNumber - rightNumber;
130
+ if (leftNumber !== null || rightNumber !== null) return leftNumber !== null ? -1 : 1;
131
+ return leftPart < rightPart ? -1 : 1;
132
+ }
133
+ return 0;
134
+ }
135
+ function formatUpdateNotice(update) {
136
+ return `Update available: nova-ai ${update.currentVersion} \u2192 ${update.latestVersion}. Run: ${update.command}`;
137
+ }
138
+
139
+ export {
140
+ updateCachePath,
141
+ updateChecksDisabled,
142
+ checkForUpdate,
143
+ chooseUpdate,
144
+ readInstalledVersion,
145
+ isNewerVersion,
146
+ formatUpdateNotice
147
+ };
@@ -6,5 +6,6 @@ function cliVersion() {
6
6
  }
7
7
 
8
8
  export {
9
+ CLI_PACKAGE_NAME,
9
10
  cliVersion
10
11
  };
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  cliVersion
4
- } from "./chunk-SKAW2KXC.js";
4
+ } from "./chunk-FMHWW3E6.js";
5
5
 
6
6
  // src/cli.ts
7
7
  var SUBCOMMANDS = {
@@ -86,6 +86,28 @@ function parseTuiArgs(args) {
86
86
  }
87
87
 
88
88
  // src/index.ts
89
+ function startUpdateCheck() {
90
+ if (!process.stderr.isTTY) return null;
91
+ const abort = new AbortController();
92
+ const result = import("./update-check-OX6WAPNN.js").then(({ checkForUpdate }) => checkForUpdate({ signal: abort.signal })).catch(() => null);
93
+ return { result, cancel: () => abort.abort() };
94
+ }
95
+ async function noticeUpdate(check, waitMs) {
96
+ let timer;
97
+ const update = await Promise.race([
98
+ check.result,
99
+ new Promise((resolve) => {
100
+ timer = setTimeout(() => resolve(null), waitMs);
101
+ })
102
+ ]);
103
+ clearTimeout(timer);
104
+ check.cancel();
105
+ if (!update) return;
106
+ const { formatUpdateNotice } = await import("./update-check-OX6WAPNN.js");
107
+ process.stderr.write(`
108
+ ${formatUpdateNotice(update)}
109
+ `);
110
+ }
89
111
  async function main() {
90
112
  const command = parseCli(process.argv.slice(2));
91
113
  switch (command.mode) {
@@ -99,10 +121,13 @@ ${MAIN_HELP}
99
121
  process.stdout.write(`${MAIN_HELP}
100
122
  `);
101
123
  return 0;
102
- case "version":
124
+ case "version": {
125
+ const update = startUpdateCheck();
103
126
  process.stdout.write(`${versionText()}
104
127
  `);
128
+ if (update) await noticeUpdate(update, 800);
105
129
  return 0;
130
+ }
106
131
  case "acp": {
107
132
  if (command.args.length) {
108
133
  process.stderr.write(`--acp takes no other arguments (got ${command.args.join(" ")}).
@@ -114,8 +139,11 @@ ${MAIN_HELP}
114
139
  return 0;
115
140
  }
116
141
  case "print": {
142
+ const update = startUpdateCheck();
117
143
  const { default: runHeadless } = await import("./headless-OFJFVJ3O.js");
118
- return runHeadless(command.args);
144
+ const code = await runHeadless(command.args);
145
+ if (update) await noticeUpdate(update, 500);
146
+ return code;
119
147
  }
120
148
  case "login": {
121
149
  const { runLogin } = await import("@datalabrotterdam/nova-ai-agent/commands/login.js");
@@ -147,7 +175,7 @@ ${MAIN_HELP}
147
175
  );
148
176
  return 2;
149
177
  }
150
- const { default: runChat } = await import("./tui-NJWDD5X7.js");
178
+ const { default: runChat } = await import("./tui-LNFSHL4Y.js");
151
179
  return runChat(options);
152
180
  }
153
181
  }
@@ -1,6 +1,7 @@
1
1
  import {
2
- cliVersion
3
- } from "./chunk-SKAW2KXC.js";
2
+ checkForUpdate
3
+ } from "./chunk-37PMUZ34.js";
4
+ import "./chunk-FMHWW3E6.js";
4
5
  import {
5
6
  TuiAcpClient,
6
7
  createStore,
@@ -1012,82 +1013,6 @@ function fromAgentPermissionMode(mode) {
1012
1013
  return mode === "bypassPermissions" ? "bypassAll" : mode === "acceptEdits" ? "acceptEdits" : "ask";
1013
1014
  }
1014
1015
 
1015
- // src/tui/update-check.ts
1016
- var PACKAGE_NAME = "@datalabrotterdam/nova-ai-cli";
1017
- var REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME.replace("/", "%2F")}/latest`;
1018
- var DEFAULT_TIMEOUT_MS = 3e3;
1019
- var UPDATE_COMMAND = "npm install -g @datalabrotterdam/nova-ai-cli@latest";
1020
- async function checkForUpdate(options = {}) {
1021
- try {
1022
- const currentVersion = options.currentVersion ?? readInstalledVersion();
1023
- if (!currentVersion) return null;
1024
- const response = await (options.fetchImpl ?? fetch)(REGISTRY_URL, {
1025
- headers: { accept: "application/json" },
1026
- signal: AbortSignal.timeout(options.timeoutMs ?? DEFAULT_TIMEOUT_MS)
1027
- });
1028
- if (!response.ok) return null;
1029
- const payload = await response.json();
1030
- if (!isVersionPayload(payload)) return null;
1031
- if (!isNewerVersion(payload.version, currentVersion)) return null;
1032
- return {
1033
- currentVersion,
1034
- latestVersion: payload.version,
1035
- command: UPDATE_COMMAND
1036
- };
1037
- } catch {
1038
- return null;
1039
- }
1040
- }
1041
- function readInstalledVersion() {
1042
- return cliVersion();
1043
- }
1044
- function isNewerVersion(candidate, current) {
1045
- const next = parseVersion(candidate);
1046
- const installed = parseVersion(current);
1047
- if (!next || !installed) return false;
1048
- for (let index = 0; index < 3; index++) {
1049
- const difference = next.core[index] - installed.core[index];
1050
- if (difference !== 0) return difference > 0;
1051
- }
1052
- return comparePrerelease(next.prerelease, installed.prerelease) > 0;
1053
- }
1054
- function isVersionPayload(value) {
1055
- return typeof value === "object" && value !== null && "version" in value && typeof value.version === "string";
1056
- }
1057
- function parseVersion(value) {
1058
- const match = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/.exec(
1059
- value
1060
- );
1061
- if (!match) return null;
1062
- return {
1063
- core: [Number(match[1]), Number(match[2]), Number(match[3])],
1064
- prerelease: match[4]?.split(".") ?? []
1065
- };
1066
- }
1067
- function comparePrerelease(left, right) {
1068
- if (left.length === 0 || right.length === 0) {
1069
- if (left.length === right.length) return 0;
1070
- return left.length === 0 ? 1 : -1;
1071
- }
1072
- const length = Math.max(left.length, right.length);
1073
- for (let index = 0; index < length; index++) {
1074
- const leftPart = left[index];
1075
- const rightPart = right[index];
1076
- if (leftPart === void 0 || rightPart === void 0) {
1077
- return leftPart === rightPart ? 0 : leftPart === void 0 ? -1 : 1;
1078
- }
1079
- if (leftPart === rightPart) continue;
1080
- const leftNumber = /^\d+$/.test(leftPart) ? Number(leftPart) : null;
1081
- const rightNumber = /^\d+$/.test(rightPart) ? Number(rightPart) : null;
1082
- if (leftNumber !== null && rightNumber !== null)
1083
- return leftNumber - rightNumber;
1084
- if (leftNumber !== null || rightNumber !== null)
1085
- return leftNumber !== null ? -1 : 1;
1086
- return leftPart < rightPart ? -1 : 1;
1087
- }
1088
- return 0;
1089
- }
1090
-
1091
1016
  // src/tui/ink/terminal-keyboard.ts
1092
1017
  var KITTY_QUERY = "\x1B[?u";
1093
1018
  var DEVICE_ATTRIBUTES_QUERY = "\x1B[c";
@@ -1808,6 +1733,11 @@ function PlanView({
1808
1733
 
1809
1734
  // src/tui/ink/controller.ts
1810
1735
  import { setWorkspaceTrusted } from "@datalabrotterdam/nova-ai-agent/core/nova-home.js";
1736
+ import {
1737
+ disabledSkillNames,
1738
+ discoverSkills as discoverSkills2,
1739
+ setSkillEnabled
1740
+ } from "@datalabrotterdam/nova-ai-agent/core/skills.js";
1811
1741
  import { INTERACTION_MODES } from "@datalabrotterdam/nova-ai-agent/core/interaction-modes.js";
1812
1742
 
1813
1743
  // src/tui/commands/builtins.ts
@@ -1884,7 +1814,7 @@ var builtinCommands = [
1884
1814
  },
1885
1815
  {
1886
1816
  name: "exit",
1887
- description: "Exit nova-ai-cli",
1817
+ description: "Exit nova-ai",
1888
1818
  run(ctx) {
1889
1819
  ctx.exit();
1890
1820
  }
@@ -1957,9 +1887,20 @@ var builtinCommands = [
1957
1887
  },
1958
1888
  {
1959
1889
  name: "skills",
1960
- description: "Inspect discovered user and workspace skills",
1961
- run(ctx) {
1962
- ctx.openSkillInspector();
1890
+ description: "List skills, or switch one: /skills on|off <name> [--global]",
1891
+ run(ctx, args) {
1892
+ const words = args.trim().split(/\s+/).filter(Boolean);
1893
+ if (!words.length) {
1894
+ ctx.openSkillInspector();
1895
+ return;
1896
+ }
1897
+ const global = words.includes("--global");
1898
+ const [action, name] = words.filter((word) => word !== "--global");
1899
+ if (action !== "on" && action !== "off" || !name) {
1900
+ ctx.print("Usage: /skills, or /skills on|off <name> [--global]");
1901
+ return;
1902
+ }
1903
+ ctx.print(ctx.switchSkill(name, action === "on", global));
1963
1904
  }
1964
1905
  },
1965
1906
  {
@@ -4024,15 +3965,38 @@ status: failed - ${failure.message}`);
4024
3965
  rows.length ? rows.join("\n\n") : "No MCP servers configured for this workspace.\n\nAdd a project .mcp.json or mcpServers to .nova-ai/settings.json; ACP connects them when the session starts."
4025
3966
  );
4026
3967
  };
3968
+ /** Every discovered skill with its state; switched-off skills stay listed. */
4027
3969
  openSkillInspector = () => {
4028
- const skills = this.runner.skillSessionStatus();
3970
+ const cwd = this.runner.cwd;
3971
+ const skills = discoverSkills2(cwd);
3972
+ const disabled = disabledSkillNames(cwd);
3973
+ const on = skills.filter((skill) => !disabled.has(skill.name)).length;
4029
3974
  this.showText(
4030
- `Skills (${skills.length})`,
4031
- skills.length ? skills.map((skill) => `${skill.name} [${skill.source}]
3975
+ `Skills (${on} on, ${skills.length - on} off)`,
3976
+ skills.length ? [
3977
+ ...skills.map(
3978
+ (skill) => `${skill.name} [${skill.source}]${disabled.has(skill.name) ? " (off)" : ""}
4032
3979
  ${skill.description}
4033
- ${skill.path}`).join("\n\n") : "No skills discovered. Add SKILL.md under .agents/skills, .claude/skills, or .codex/skills in the workspace or user profile."
3980
+ ${skill.path}`
3981
+ ),
3982
+ "/skills off <name> switches one off in this project (--global: everywhere); /skills on <name> switches it back on."
3983
+ ].join("\n\n") : "No skills discovered. Add SKILL.md under .nova-ai/skills (or .agents, .claude, .codex skills) in the workspace, or ~/.nova-ai/skills for all projects."
4034
3984
  );
4035
3985
  };
3986
+ /** /skills on|off <name> [--global]: applies from the next message. */
3987
+ switchSkill = (name, enabled, global) => {
3988
+ const cwd = this.runner.cwd;
3989
+ if (!discoverSkills2(cwd).some((skill) => skill.name === name)) {
3990
+ return `No skill named "${name}". /skills lists them.`;
3991
+ }
3992
+ try {
3993
+ setSkillEnabled(cwd, global ? "global" : "project", name, enabled);
3994
+ } catch (error) {
3995
+ return errorMessage(error, "Could not change the skill.");
3996
+ }
3997
+ const stillOff = enabled && disabledSkillNames(cwd).has(name);
3998
+ return stillOff ? `Switched "${name}" on ${global ? "globally" : "in this project"}, but it is still off ${global ? "in this project" : "globally"} (use ${global ? "/skills on " + name : "/skills on " + name + " --global"}).` : `Skill "${name}" is ${enabled ? "on" : "off"} ${global ? "everywhere" : "in this project"} from the next message.`;
3999
+ };
4036
4000
  openUsageInspector = async () => {
4037
4001
  this.showText("Context usage", "Calculating context usage...");
4038
4002
  try {
@@ -4119,6 +4083,7 @@ ${skill.path}`).join("\n\n") : "No skills discovered. Add SKILL.md under .agents
4119
4083
  openToolInspector: this.openToolInspector,
4120
4084
  openMcpInspector: this.openMcpInspector,
4121
4085
  openSkillInspector: this.openSkillInspector,
4086
+ switchSkill: this.switchSkill,
4122
4087
  openUsageInspector: this.openUsageInspector,
4123
4088
  steerMessage: (message) => this.runner.steer(message),
4124
4089
  queuedMessages: () => this.runner.queuedMessages(),
@@ -0,0 +1,19 @@
1
+ import {
2
+ checkForUpdate,
3
+ chooseUpdate,
4
+ formatUpdateNotice,
5
+ isNewerVersion,
6
+ readInstalledVersion,
7
+ updateCachePath,
8
+ updateChecksDisabled
9
+ } from "./chunk-37PMUZ34.js";
10
+ import "./chunk-FMHWW3E6.js";
11
+ export {
12
+ checkForUpdate,
13
+ chooseUpdate,
14
+ formatUpdateNotice,
15
+ isNewerVersion,
16
+ readInstalledVersion,
17
+ updateCachePath,
18
+ updateChecksDisabled
19
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datalabrotterdam/nova-ai-cli",
3
- "version": "1.2.0-alpha.2",
3
+ "version": "1.2.0-alpha.3",
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",
@@ -21,7 +21,6 @@
21
21
  },
22
22
  "homepage": "https://github.com/Datalab-Rotterdam/nova-ai-cli#readme",
23
23
  "bin": {
24
- "nova-ai-cli": "dist/index.js",
25
24
  "nova-ai": "dist/index.js"
26
25
  },
27
26
  "files": [