@8gi-foundation/8gent-code 0.20.12 → 0.20.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 CHANGED
@@ -9,6 +9,19 @@ versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
+ ## [0.20.14] - 2026-10-22
13
+
14
+ ### Changed
15
+ - Android Termux and proot install path, Node version commands, signal exit ([#4747](https://github.com/8gi-foundation/8gent-code/pull/4747))
16
+
17
+ ### Fixed
18
+ - tui: built-in slash commands run before the skill scan loads and after it fails ([#4745](https://github.com/8gi-foundation/8gent-code/pull/4745))
19
+
20
+ ## [0.20.13] - 2026-10-22
21
+
22
+ ### Fixed
23
+ - tui: the prompt stays live in every view unless a picker owns the keyboard, and Esc always returns (0.20.12 regression) ([#4740](https://github.com/8gi-foundation/8gent-code/pull/4740))
24
+
12
25
  ## [0.20.12] - 2026-10-22
13
26
 
14
27
  ### Added
package/README.md CHANGED
@@ -21,7 +21,7 @@
21
21
 
22
22
  <p align="center">
23
23
  <a href="https://www.apache.org/licenses/LICENSE-2.0"><img src="https://img.shields.io/badge/License-Apache_2.0-E8610A?style=for-the-badge&labelColor=1A1612" alt="Apache 2.0 License" /></a>
24
- <a href="https://8gent.dev"><img src="https://img.shields.io/badge/version-0.20.12-2D8A56?style=for-the-badge&labelColor=1A1612" alt="v0.20.12" /></a>
24
+ <a href="https://8gent.dev"><img src="https://img.shields.io/badge/version-0.20.14-2D8A56?style=for-the-badge&labelColor=1A1612" alt="v0.20.14" /></a>
25
25
  <a href="https://eight-vessel.fly.dev"><img src="https://img.shields.io/badge/daemon-Fly.io_Amsterdam-E8610A?style=for-the-badge&labelColor=1A1612" alt="Daemon" /></a>
26
26
  </p>
27
27
 
@@ -159,7 +159,7 @@ That's it. 8gent uses an adaptive provider router with 16 model providers (plus
159
159
 
160
160
  **No model yet?** On a machine with no local model and no key, the chat area shows a short card with both ways to connect one: install Ollama and run `ollama pull qwen3.5`, or run `8gent keys` to add `OPENROUTER_API_KEY` to `~/.8gent/keys.env` and then use `/provider openrouter` with `/model auto:free`. `/provider` lists every option. The card goes away once a model is reachable.
161
161
 
162
- **Pick your platform:** [macOS](docs/install/macos.md), [Windows](docs/install/windows.md), [Linux](docs/install/linux.md). Every provider, flag and environment variable is in [docs/install/providers.md](docs/install/providers.md). One-line binary installers (no Bun or Node needed) are in [docs/user/install.md](docs/user/install.md).
162
+ **Pick your platform:** [macOS](docs/install/macos.md), [Windows](docs/install/windows.md), [Linux](docs/install/linux.md), [Android (Termux, kali and arch proots)](docs/install/android-termux.md). Every provider, flag and environment variable is in [docs/install/providers.md](docs/install/providers.md). One-line binary installers (no Bun or Node needed) are in [docs/user/install.md](docs/user/install.md).
163
163
 
164
164
  **Ollama is optional.** To run on llama.cpp's `llama-server` instead, set `EIGHT_LOCAL_SERVER=llama-server` and start with `8gent --provider=llama-server`. 8gent then never contacts Ollama. See [docs/guides/llama-server.md](docs/guides/llama-server.md).
165
165
 
@@ -222,9 +222,9 @@ tar xzf 8gent-<version>-linux-amd64.tar.gz && sudo mv 8gent-*/bin/8gent /usr/loc
222
222
 
223
223
  > Binaries are code-signed only on releases where the maintainer's certificates are configured (see [SIGNING.md](SIGNING.md)). Unsigned downloads run fine but may prompt Gatekeeper (macOS) or SmartScreen (Windows) - allow them through, or build your own with `bun run build:binaries`.
224
224
 
225
- ### npm (old build, 0.17.0)
225
+ ### npm
226
226
 
227
- The npm package is behind GitHub. `npm install -g @8gi-foundation/8gent-code` installs 0.17.0 (published 11 May 2026), not the current release, and it needs [Bun](https://bun.sh). Use a one-line installer above until npm is republished.
227
+ The npm package tracks the GitHub release: `npm install -g @8gi-foundation/8gent-code` installs the current version. It runs on Bun, or on Node 22.5 or newer when Bun is not installed. On Android (Termux, kali or arch proot) follow [docs/install/android-termux.md](docs/install/android-termux.md).
228
228
 
229
229
  ### Build binaries from source
230
230
 
package/bin/8gent-run.js CHANGED
@@ -88,4 +88,11 @@ if (result.error && result.error.code === "ENOENT") {
88
88
  );
89
89
  }
90
90
 
91
+ // Killed by a signal (status null, e.g. SIGILL in a proot): say so and fail, never a silent 0.
92
+ if (result.status === null && result.signal) {
93
+ console.error(
94
+ `8gent: the runtime was killed by ${result.signal}.${wantNode ? "" : " Try EIGHT_RUNTIME=node 8gent to use the Node build."}`,
95
+ );
96
+ process.exit(1);
97
+ }
91
98
  process.exit(result.status ?? 0);
@@ -5,5 +5,10 @@ export function nodeTooOld(version) {
5
5
  }
6
6
 
7
7
  export function tooOldMessage(version) {
8
- return `8gent needs Node 22.5 or newer (found ${version}). Install Node 22 or Bun.`;
8
+ return [
9
+ `8gent needs Node 22.5 or newer (found ${version}). Install Node 22 or Bun.`,
10
+ " Debian / Kali: curl -fsSL https://deb.nodesource.com/setup_22.x -o /tmp/nodesource.sh && bash /tmp/nodesource.sh && apt-get install -y nodejs",
11
+ " Arch: pacman -Syu --noconfirm nodejs npm",
12
+ " Any Linux: install nvm (https://github.com/nvm-sh/nvm), then: nvm install 22",
13
+ ].join("\n");
9
14
  }
package/dist/cli.js CHANGED
@@ -597524,7 +597524,7 @@ function nodeExecPath(execPath = process.execPath, env = process.env, exists = e
597524
597524
 
597525
597525
  // bin/8gent.ts
597526
597526
  refuseIfNested();
597527
- var VERSION5 = "0.20.12";
597527
+ var VERSION5 = "0.20.14";
597528
597528
  var RELAUNCH_EXIT_CODE = 75;
597529
597529
  var MODULE_DIR = path158.dirname(fileURLToPath7(import.meta.url));
597530
597530
  var EXEC_DIR = path158.dirname(process.execPath);
package/dist/node/cli.js CHANGED
@@ -617700,7 +617700,7 @@ function nodeExecPath(execPath = process.execPath, env = process.env, exists = e
617700
617700
 
617701
617701
  // bin/8gent.ts
617702
617702
  refuseIfNested();
617703
- var VERSION5 = "0.20.12";
617703
+ var VERSION5 = "0.20.14";
617704
617704
  var RELAUNCH_EXIT_CODE = 75;
617705
617705
  var MODULE_DIR = path158.dirname(fileURLToPath8(import.meta.url));
617706
617706
  var EXEC_DIR = path158.dirname(process.execPath);
package/dist/node/tui.js CHANGED
@@ -33286,7 +33286,7 @@ var init_permissions = __esm(() => {
33286
33286
  var require_package = __commonJS(function(exports2, module2) {
33287
33287
  module2.exports = {
33288
33288
  name: "@8gi-foundation/8gent-code",
33289
- version: "0.20.12",
33289
+ version: "0.20.14",
33290
33290
  private: false,
33291
33291
  engines: {
33292
33292
  node: ">=22.5"
@@ -620694,6 +620694,9 @@ function safeLoadTemplates() {
620694
620694
  function getBuiltInSlashCommands() {
620695
620695
  return BUILT_IN_SLASH_COMMANDS;
620696
620696
  }
620697
+ function getBuiltInSlashRegistry() {
620698
+ return mergeSlashEntries(buildBuiltInEntries(), [], []);
620699
+ }
620697
620700
  async function getSlashRegistry() {
620698
620701
  return mergeSlashEntries(buildBuiltInEntries(), await buildSkillEntries(), buildPromptEntries());
620699
620702
  }
@@ -621149,8 +621152,9 @@ function CommandInput({
621149
621152
  const [historyIndex, setHistoryIndex] = import_react55.useState(-1);
621150
621153
  const draftRef = import_react55.useRef("");
621151
621154
  const [showSlashHelp, setShowSlashHelp] = import_react55.useState(false);
621152
- const [slashRegistryEntries, setSlashRegistryEntries] = import_react55.useState([]);
621153
- const [slashByToken, setSlashByToken] = import_react55.useState(new Map);
621155
+ const [initialRegistry] = import_react55.useState(getBuiltInSlashRegistry);
621156
+ const [slashRegistryEntries, setSlashRegistryEntries] = import_react55.useState(initialRegistry.entries);
621157
+ const [slashByToken, setSlashByToken] = import_react55.useState(initialRegistry.byToken);
621154
621158
  const builtInSlashGhosts = import_react55.useMemo(() => buildBuiltInSlashGhostSuggestions(), []);
621155
621159
  const extraSlashContext = import_react55.useMemo(() => [
621156
621160
  ...builtInSlashGhosts,
@@ -621183,8 +621187,8 @@ function CommandInput({
621183
621187
  }
621184
621188
  } catch {
621185
621189
  if (!cancelled) {
621186
- setSlashRegistryEntries([]);
621187
- setSlashByToken(new Map);
621190
+ setSlashRegistryEntries(initialRegistry.entries);
621191
+ setSlashByToken(initialRegistry.byToken);
621188
621192
  }
621189
621193
  }
621190
621194
  })();
@@ -630291,6 +630295,25 @@ function settleSetupTranscript(messages) {
630291
630295
  return out;
630292
630296
  }
630293
630297
 
630298
+ // apps/tui/src/lib/prompt-focus.ts
630299
+ var KEYBOARD_VIEWS = new Set([
630300
+ "model-select",
630301
+ "provider-select",
630302
+ "theme-select",
630303
+ "session-picker",
630304
+ "session-tree",
630305
+ "keys"
630306
+ ]);
630307
+ var ESC_RETURNS_LINE = "Esc returns to the prompt";
630308
+ function promptIsLive(s) {
630309
+ if (KEYBOARD_VIEWS.has(s.viewMode))
630310
+ return false;
630311
+ return !s.tabBodyShown && !s.isBubbleNavMode && !s.paletteOpen && !s.djKeys;
630312
+ }
630313
+ function mayAutoOpenProviderPicker(keyedHostedProviders) {
630314
+ return keyedHostedProviders.length !== 1;
630315
+ }
630316
+
630294
630317
  // apps/tui/src/lib/provider-health.ts
630295
630318
  init_text_tool_endpoint();
630296
630319
  init_local_model_server();
@@ -639459,13 +639482,7 @@ function speakOnboardingLine(rawText, preference) {
639459
639482
  })();
639460
639483
  }
639461
639484
  var PLAN_COLUMN_WIDTH = 24;
639462
- var PICKER_VIEWS = new Set([
639463
- "model-select",
639464
- "provider-select",
639465
- "theme-select",
639466
- "session-picker",
639467
- "session-tree"
639468
- ]);
639485
+ var PICKER_VIEWS = KEYBOARD_VIEWS;
639469
639486
  var SESSION_VIEWS = new Set(["session-picker", "session-tree"]);
639470
639487
  var PLAN_EMPTY_LINE = "No plan steps yet. They appear when the 8gent writes a PLAN: with numbered steps while working on a request.";
639471
639488
  var AVENUES_EMPTY_LINE = "No avenues in this session. Nothing produces them yet, so this stays empty rather than showing guesses.";
@@ -640164,6 +640181,7 @@ function App2({
640164
640181
  addSystemMessage(notice);
640165
640182
  setCurrentModel(next);
640166
640183
  }, [modelsLoading, availableModels, modelsOwner, currentProvider, currentModel]);
640184
+ const oneKeyNoModelNotedRef = import_react104.useRef(false);
640167
640185
  import_react104.useEffect(() => {
640168
640186
  if (modelsLoading)
640169
640187
  return;
@@ -640175,6 +640193,13 @@ function App2({
640175
640193
  return;
640176
640194
  if (availableModels.some((m) => !isLikelyEmbeddingModelId(m)))
640177
640195
  return;
640196
+ if (!mayAutoOpenProviderPicker(hostedKeyedProviders())) {
640197
+ if (viewMode === "chat" && !oneKeyNoModelNotedRef.current) {
640198
+ oneKeyNoModelNotedRef.current = true;
640199
+ addSystemMessage(`${currentProvider} listed no chat model. Check the key with /apikeys, or pick one with /provider.`);
640200
+ }
640201
+ return;
640202
+ }
640178
640203
  if (viewMode === "chat")
640179
640204
  setViewMode("provider-select");
640180
640205
  }, [modelsLoading, availableModels, currentModel, viewMode, modelsOwner, currentProvider]);
@@ -644786,8 +644811,14 @@ ${result.commands.map((c) => ` $ ${c}`).join(`
644786
644811
  }, undefined, false, undefined, this),
644787
644812
  PICKER_VIEWS.has(viewMode) && activeTabType === "chat" && /* @__PURE__ */ jsx_dev_runtime67.jsxDEV(Box_default, {
644788
644813
  flexShrink: 0,
644789
- children: renderMainContent()
644790
- }, undefined, false, undefined, this),
644814
+ flexDirection: "column",
644815
+ children: [
644816
+ renderMainContent(),
644817
+ /* @__PURE__ */ jsx_dev_runtime67.jsxDEV(MutedText, {
644818
+ children: ESC_RETURNS_LINE
644819
+ }, undefined, false, undefined, this)
644820
+ ]
644821
+ }, undefined, true, undefined, this),
644791
644822
  approvalPending && /* @__PURE__ */ jsx_dev_runtime67.jsxDEV(InlineApprovalPrompt, {
644792
644823
  target: approvalPending.target,
644793
644824
  reason: approvalPending.reason,
@@ -644803,7 +644834,13 @@ ${result.commands.map((c) => ` $ ${c}`).join(`
644803
644834
  onSubmit: handleSubmit,
644804
644835
  isProcessing,
644805
644836
  placeholder: showOnboarding ? onboardingPlaceholder ?? "Type your answer" : nameQuestionOpen ? "Your name" : undefined,
644806
- focused: (viewMode === "chat" && activeTabType === "chat" || viewMode === "onboarding") && !isBubbleNavMode && !paletteOpen && !djKeys,
644837
+ focused: promptIsLive({
644838
+ viewMode,
644839
+ tabBodyShown: tabBody != null,
644840
+ isBubbleNavMode,
644841
+ paletteOpen,
644842
+ djKeys
644843
+ }),
644807
644844
  typingPaused: djKeys,
644808
644845
  processingStage,
644809
644846
  showAnimations,
package/dist/tui.js CHANGED
@@ -29981,7 +29981,7 @@ var init_permissions = __esm(() => {
29981
29981
  var require_package = __commonJS(function(exports2, module2) {
29982
29982
  module2.exports = {
29983
29983
  name: "@8gi-foundation/8gent-code",
29984
- version: "0.20.12",
29984
+ version: "0.20.14",
29985
29985
  private: false,
29986
29986
  engines: {
29987
29987
  node: ">=22.5"
@@ -597659,6 +597659,9 @@ function safeLoadTemplates() {
597659
597659
  function getBuiltInSlashCommands() {
597660
597660
  return BUILT_IN_SLASH_COMMANDS;
597661
597661
  }
597662
+ function getBuiltInSlashRegistry() {
597663
+ return mergeSlashEntries(buildBuiltInEntries(), [], []);
597664
+ }
597662
597665
  async function getSlashRegistry() {
597663
597666
  return mergeSlashEntries(buildBuiltInEntries(), await buildSkillEntries(), buildPromptEntries());
597664
597667
  }
@@ -598114,8 +598117,9 @@ function CommandInput({
598114
598117
  const [historyIndex, setHistoryIndex] = import_react55.useState(-1);
598115
598118
  const draftRef = import_react55.useRef("");
598116
598119
  const [showSlashHelp, setShowSlashHelp] = import_react55.useState(false);
598117
- const [slashRegistryEntries, setSlashRegistryEntries] = import_react55.useState([]);
598118
- const [slashByToken, setSlashByToken] = import_react55.useState(new Map);
598120
+ const [initialRegistry] = import_react55.useState(getBuiltInSlashRegistry);
598121
+ const [slashRegistryEntries, setSlashRegistryEntries] = import_react55.useState(initialRegistry.entries);
598122
+ const [slashByToken, setSlashByToken] = import_react55.useState(initialRegistry.byToken);
598119
598123
  const builtInSlashGhosts = import_react55.useMemo(() => buildBuiltInSlashGhostSuggestions(), []);
598120
598124
  const extraSlashContext = import_react55.useMemo(() => [
598121
598125
  ...builtInSlashGhosts,
@@ -598148,8 +598152,8 @@ function CommandInput({
598148
598152
  }
598149
598153
  } catch {
598150
598154
  if (!cancelled) {
598151
- setSlashRegistryEntries([]);
598152
- setSlashByToken(new Map);
598155
+ setSlashRegistryEntries(initialRegistry.entries);
598156
+ setSlashByToken(initialRegistry.byToken);
598153
598157
  }
598154
598158
  }
598155
598159
  })();
@@ -607256,6 +607260,25 @@ function settleSetupTranscript(messages) {
607256
607260
  return out;
607257
607261
  }
607258
607262
 
607263
+ // apps/tui/src/lib/prompt-focus.ts
607264
+ var KEYBOARD_VIEWS = new Set([
607265
+ "model-select",
607266
+ "provider-select",
607267
+ "theme-select",
607268
+ "session-picker",
607269
+ "session-tree",
607270
+ "keys"
607271
+ ]);
607272
+ var ESC_RETURNS_LINE = "Esc returns to the prompt";
607273
+ function promptIsLive(s) {
607274
+ if (KEYBOARD_VIEWS.has(s.viewMode))
607275
+ return false;
607276
+ return !s.tabBodyShown && !s.isBubbleNavMode && !s.paletteOpen && !s.djKeys;
607277
+ }
607278
+ function mayAutoOpenProviderPicker(keyedHostedProviders) {
607279
+ return keyedHostedProviders.length !== 1;
607280
+ }
607281
+
607259
607282
  // apps/tui/src/lib/provider-health.ts
607260
607283
  init_text_tool_endpoint();
607261
607284
  init_local_model_server();
@@ -616424,13 +616447,7 @@ function speakOnboardingLine(rawText, preference) {
616424
616447
  })();
616425
616448
  }
616426
616449
  var PLAN_COLUMN_WIDTH = 24;
616427
- var PICKER_VIEWS = new Set([
616428
- "model-select",
616429
- "provider-select",
616430
- "theme-select",
616431
- "session-picker",
616432
- "session-tree"
616433
- ]);
616450
+ var PICKER_VIEWS = KEYBOARD_VIEWS;
616434
616451
  var SESSION_VIEWS = new Set(["session-picker", "session-tree"]);
616435
616452
  var PLAN_EMPTY_LINE = "No plan steps yet. They appear when the 8gent writes a PLAN: with numbered steps while working on a request.";
616436
616453
  var AVENUES_EMPTY_LINE = "No avenues in this session. Nothing produces them yet, so this stays empty rather than showing guesses.";
@@ -617129,6 +617146,7 @@ function App2({
617129
617146
  addSystemMessage(notice);
617130
617147
  setCurrentModel(next);
617131
617148
  }, [modelsLoading, availableModels, modelsOwner, currentProvider, currentModel]);
617149
+ const oneKeyNoModelNotedRef = import_react104.useRef(false);
617132
617150
  import_react104.useEffect(() => {
617133
617151
  if (modelsLoading)
617134
617152
  return;
@@ -617140,6 +617158,13 @@ function App2({
617140
617158
  return;
617141
617159
  if (availableModels.some((m) => !isLikelyEmbeddingModelId(m)))
617142
617160
  return;
617161
+ if (!mayAutoOpenProviderPicker(hostedKeyedProviders())) {
617162
+ if (viewMode === "chat" && !oneKeyNoModelNotedRef.current) {
617163
+ oneKeyNoModelNotedRef.current = true;
617164
+ addSystemMessage(`${currentProvider} listed no chat model. Check the key with /apikeys, or pick one with /provider.`);
617165
+ }
617166
+ return;
617167
+ }
617143
617168
  if (viewMode === "chat")
617144
617169
  setViewMode("provider-select");
617145
617170
  }, [modelsLoading, availableModels, currentModel, viewMode, modelsOwner, currentProvider]);
@@ -621751,8 +621776,14 @@ ${result.commands.map((c) => ` $ ${c}`).join(`
621751
621776
  }, undefined, false, undefined, this),
621752
621777
  PICKER_VIEWS.has(viewMode) && activeTabType === "chat" && /* @__PURE__ */ jsx_dev_runtime67.jsxDEV(Box_default, {
621753
621778
  flexShrink: 0,
621754
- children: renderMainContent()
621755
- }, undefined, false, undefined, this),
621779
+ flexDirection: "column",
621780
+ children: [
621781
+ renderMainContent(),
621782
+ /* @__PURE__ */ jsx_dev_runtime67.jsxDEV(MutedText, {
621783
+ children: ESC_RETURNS_LINE
621784
+ }, undefined, false, undefined, this)
621785
+ ]
621786
+ }, undefined, true, undefined, this),
621756
621787
  approvalPending && /* @__PURE__ */ jsx_dev_runtime67.jsxDEV(InlineApprovalPrompt, {
621757
621788
  target: approvalPending.target,
621758
621789
  reason: approvalPending.reason,
@@ -621768,7 +621799,13 @@ ${result.commands.map((c) => ` $ ${c}`).join(`
621768
621799
  onSubmit: handleSubmit,
621769
621800
  isProcessing,
621770
621801
  placeholder: showOnboarding ? onboardingPlaceholder ?? "Type your answer" : nameQuestionOpen ? "Your name" : undefined,
621771
- focused: (viewMode === "chat" && activeTabType === "chat" || viewMode === "onboarding") && !isBubbleNavMode && !paletteOpen && !djKeys,
621802
+ focused: promptIsLive({
621803
+ viewMode,
621804
+ tabBodyShown: tabBody != null,
621805
+ isBubbleNavMode,
621806
+ paletteOpen,
621807
+ djKeys
621808
+ }),
621772
621809
  typingPaused: djKeys,
621773
621810
  processingStage,
621774
621811
  showAnimations,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@8gi-foundation/8gent-code",
3
- "version": "0.20.12",
3
+ "version": "0.20.14",
4
4
  "private": false,
5
5
  "engines": {
6
6
  "node": ">=22.5"