@akon-labs/nexus-cli 0.1.2 → 0.1.4

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.
Files changed (155) hide show
  1. package/CHANGELOG.md +16 -5316
  2. package/README.md +30 -26
  3. package/dist/cli.js +90 -62
  4. package/dist/config.d.ts.map +1 -1
  5. package/dist/config.js +1 -1
  6. package/dist/config.js.map +1 -1
  7. package/dist/core/extensions/loader.d.ts.map +1 -1
  8. package/dist/core/extensions/loader.js +18 -63
  9. package/dist/core/extensions/loader.js.map +1 -1
  10. package/dist/extensions/gitnexus/gateway.d.ts +2 -0
  11. package/dist/extensions/gitnexus/gateway.d.ts.map +1 -1
  12. package/dist/extensions/gitnexus/gateway.js +22 -4
  13. package/dist/extensions/gitnexus/gateway.js.map +1 -1
  14. package/dist/extensions/gitnexus/index.d.ts.map +1 -1
  15. package/dist/extensions/gitnexus/index.js +21 -3
  16. package/dist/extensions/gitnexus/index.js.map +1 -1
  17. package/dist/extensions/llama/index.d.ts.map +1 -1
  18. package/dist/extensions/llama/index.js +1 -1
  19. package/dist/extensions/llama/index.js.map +1 -1
  20. package/dist/extensions/web-access.d.ts +8 -1
  21. package/dist/extensions/web-access.d.ts.map +1 -1
  22. package/dist/extensions/web-access.js +48 -2
  23. package/dist/extensions/web-access.js.map +1 -1
  24. package/dist/index.js +93 -65
  25. package/dist/migrations.d.ts.map +1 -1
  26. package/dist/migrations.js +2 -2
  27. package/dist/migrations.js.map +1 -1
  28. package/dist/rpc-entry.js +90 -62
  29. package/dist/vendor/agent-core.js +6805 -0
  30. package/dist/vendor/ai.js +12407 -0
  31. package/dist/vendor/tui.js +11645 -0
  32. package/dist/vendor/web-access.js +93103 -0
  33. package/docs/compaction.md +14 -14
  34. package/docs/containerization.md +1 -1
  35. package/docs/custom-provider.md +17 -17
  36. package/docs/development.md +2 -2
  37. package/docs/environment-variables.md +1 -1
  38. package/docs/extensions.md +44 -44
  39. package/docs/index.md +5 -11
  40. package/docs/json.md +4 -4
  41. package/docs/llama-cpp.md +2 -2
  42. package/docs/models.md +3 -3
  43. package/docs/packages.md +6 -6
  44. package/docs/providers.md +13 -13
  45. package/docs/quickstart.md +19 -13
  46. package/docs/rpc.md +1 -1
  47. package/docs/sdk.md +28 -28
  48. package/docs/security.md +1 -1
  49. package/docs/session-format.md +6 -6
  50. package/docs/settings.md +1 -1
  51. package/docs/termux.md +1 -1
  52. package/docs/themes.md +2 -2
  53. package/docs/tui.md +20 -20
  54. package/docs/usage.md +5 -4
  55. package/examples/extensions/README.md +2 -2
  56. package/examples/extensions/auto-commit-on-exit.ts +1 -1
  57. package/examples/extensions/bash-spawn-hook.ts +2 -2
  58. package/examples/extensions/bookmark.ts +1 -1
  59. package/examples/extensions/border-status-editor.ts +3 -3
  60. package/examples/extensions/built-in-tool-renderer.ts +3 -3
  61. package/examples/extensions/claude-rules.ts +1 -1
  62. package/examples/extensions/commands.ts +1 -1
  63. package/examples/extensions/confirm-destructive.ts +1 -1
  64. package/examples/extensions/custom-compaction.ts +3 -3
  65. package/examples/extensions/custom-footer.ts +3 -3
  66. package/examples/extensions/custom-header.ts +2 -2
  67. package/examples/extensions/custom-provider-anthropic/index.ts +2 -2
  68. package/examples/extensions/custom-provider-gitlab-duo/index.ts +2 -2
  69. package/examples/extensions/custom-provider-gitlab-duo/test.ts +1 -1
  70. package/examples/extensions/dirty-repo-guard.ts +1 -1
  71. package/examples/extensions/doom-overlay/doom-component.ts +2 -2
  72. package/examples/extensions/doom-overlay/doom-keys.ts +1 -1
  73. package/examples/extensions/doom-overlay/index.ts +1 -1
  74. package/examples/extensions/dynamic-resources/dynamic.json +1 -1
  75. package/examples/extensions/dynamic-resources/index.ts +1 -1
  76. package/examples/extensions/dynamic-tools.ts +1 -1
  77. package/examples/extensions/entry-renderer.ts +2 -2
  78. package/examples/extensions/event-bus.ts +1 -1
  79. package/examples/extensions/file-trigger.ts +1 -1
  80. package/examples/extensions/git-checkpoint.ts +1 -1
  81. package/examples/extensions/git-merge-and-resolve.ts +1 -1
  82. package/examples/extensions/github-issue-autocomplete.ts +2 -2
  83. package/examples/extensions/gondolin/index.ts +2 -2
  84. package/examples/extensions/handoff.ts +4 -4
  85. package/examples/extensions/hello.ts +2 -2
  86. package/examples/extensions/hidden-thinking-label.ts +1 -1
  87. package/examples/extensions/inline-bash.ts +1 -1
  88. package/examples/extensions/input-transform-streaming.ts +1 -1
  89. package/examples/extensions/input-transform.ts +1 -1
  90. package/examples/extensions/interactive-shell.ts +1 -1
  91. package/examples/extensions/kimi-deferred-tools.ts +1 -1
  92. package/examples/extensions/mac-system-theme.ts +1 -1
  93. package/examples/extensions/message-renderer.ts +2 -2
  94. package/examples/extensions/minimal-mode.ts +3 -3
  95. package/examples/extensions/modal-editor.ts +2 -2
  96. package/examples/extensions/model-status.ts +1 -1
  97. package/examples/extensions/notify.ts +1 -1
  98. package/examples/extensions/overlay-qa-tests.ts +5 -5
  99. package/examples/extensions/overlay-test.ts +2 -2
  100. package/examples/extensions/permission-gate.ts +1 -1
  101. package/examples/extensions/pirate.ts +1 -1
  102. package/examples/extensions/plan-mode/index.ts +4 -4
  103. package/examples/extensions/preset.ts +4 -4
  104. package/examples/extensions/project-trust.ts +1 -1
  105. package/examples/extensions/prompt-customizer.ts +1 -1
  106. package/examples/extensions/protected-paths.ts +1 -1
  107. package/examples/extensions/provider-payload.ts +1 -1
  108. package/examples/extensions/qna.ts +3 -3
  109. package/examples/extensions/question.ts +2 -2
  110. package/examples/extensions/questionnaire.ts +2 -2
  111. package/examples/extensions/rainbow-editor.ts +1 -1
  112. package/examples/extensions/reload-runtime.ts +1 -1
  113. package/examples/extensions/rpc-demo.ts +1 -1
  114. package/examples/extensions/sandbox/index.ts +2 -2
  115. package/examples/extensions/send-user-message.ts +1 -1
  116. package/examples/extensions/session-name.ts +1 -1
  117. package/examples/extensions/shutdown-command.ts +1 -1
  118. package/examples/extensions/snake.ts +2 -2
  119. package/examples/extensions/space-invaders.ts +2 -2
  120. package/examples/extensions/ssh.ts +2 -2
  121. package/examples/extensions/status-line.ts +1 -1
  122. package/examples/extensions/structured-output.ts +2 -2
  123. package/examples/extensions/subagent/agents.ts +1 -1
  124. package/examples/extensions/subagent/index.ts +5 -5
  125. package/examples/extensions/summarize.ts +4 -4
  126. package/examples/extensions/system-prompt-header.ts +1 -1
  127. package/examples/extensions/tic-tac-toe.ts +3 -3
  128. package/examples/extensions/timed-confirm.ts +1 -1
  129. package/examples/extensions/titlebar-spinner.ts +1 -1
  130. package/examples/extensions/todo.ts +3 -3
  131. package/examples/extensions/tool-override.ts +2 -2
  132. package/examples/extensions/tools.ts +3 -3
  133. package/examples/extensions/trigger-compact.ts +1 -1
  134. package/examples/extensions/truncated-tool.ts +3 -3
  135. package/examples/extensions/widget-placement.ts +1 -1
  136. package/examples/extensions/with-deps/index.ts +1 -1
  137. package/examples/extensions/working-indicator.ts +1 -1
  138. package/examples/extensions/working-message-test.ts +1 -1
  139. package/examples/rpc-extension-ui.ts +1 -1
  140. package/examples/sdk/01-minimal.ts +1 -1
  141. package/examples/sdk/02-custom-model.ts +1 -1
  142. package/examples/sdk/03-custom-prompt.ts +1 -6
  143. package/examples/sdk/04-skills.ts +1 -1
  144. package/examples/sdk/05-tools.ts +1 -1
  145. package/examples/sdk/06-extensions.ts +2 -7
  146. package/examples/sdk/07-context-files.ts +1 -6
  147. package/examples/sdk/08-prompt-templates.ts +1 -1
  148. package/examples/sdk/09-api-keys-and-oauth.ts +1 -1
  149. package/examples/sdk/10-settings.ts +1 -1
  150. package/examples/sdk/11-sessions.ts +1 -1
  151. package/examples/sdk/12-full-control.ts +2 -2
  152. package/examples/sdk/13-session-runtime.ts +1 -1
  153. package/examples/sdk/README.md +2 -2
  154. package/npm-shrinkwrap.json +2 -26
  155. package/package.json +10 -25
package/README.md CHANGED
@@ -6,8 +6,6 @@ Nexus is a minimal terminal coding harness that ships connected to your GitNexus
6
6
 
7
7
  Nexus runs in four modes: interactive, print or JSON, RPC for process integration, and an SDK for embedding in your own apps.
8
8
 
9
- Built on a vendored fork of [pi](https://pi.dev) (`earendil-works/pi`, MIT). Packages published for pi work here unchanged.
10
-
11
9
  ## Table of Contents
12
10
 
13
11
  - [Quick Start](#quick-start)
@@ -37,25 +35,26 @@ Built on a vendored fork of [pi](https://pi.dev) (`earendil-works/pi`, MIT). Pac
37
35
  ## Quick Start
38
36
 
39
37
  ```bash
40
- npm install -g --ignore-scripts @akonlabs/nexus-cli
38
+ npm install -g --ignore-scripts @akon-labs/nexus-cli
41
39
  ```
42
40
 
43
41
  `--ignore-scripts` disables dependency lifecycle scripts during install. Nexus does not require install scripts for normal npm installs.
44
42
 
45
- Authenticate with an API key:
43
+ Start Nexus. The first interactive launch opens the GitNexus browser sign-in flow:
46
44
 
47
45
  ```bash
48
- export ANTHROPIC_API_KEY=sk-ant-...
49
46
  nexus
50
47
  ```
51
48
 
52
- Or use your existing subscription:
49
+ Inside a session, `/login` signs in to GitNexus again. Configure the model provider separately:
53
50
 
54
- ```bash
55
- nexus
56
- /login # Then select provider
51
+ ```text
52
+ /byok
53
+ /model
57
54
  ```
58
55
 
56
+ For headless GitNexus authentication, set `GITNEXUS_TOKEN`. Model API keys can still be set through provider environment variables such as `ANTHROPIC_API_KEY`.
57
+
59
58
  Then just talk to nexus. By default, nexus gives the model four tools: `read`, `write`, `edit`, and `bash`. The model uses these to fulfill your requests. Add capabilities via [skills](#skills), [prompt templates](#prompt-templates), [extensions](#extensions), or [nexus packages](#nexus-packages).
60
59
 
61
60
  **Platform notes:** [Windows](docs/windows.md) | [Termux (Android)](docs/termux.md) | [tmux](docs/tmux.md) | [Terminal setup](docs/terminal-setup.md) | [Shell aliases](docs/shell-aliases.md)
@@ -64,7 +63,7 @@ Then just talk to nexus. By default, nexus gives the model four tools: `read`, `
64
63
 
65
64
  ## Providers & Models
66
65
 
67
- For each built-in provider, nexus maintains a list of tool-capable models. Configured provider catalogs refresh automatically; run `nexus update --models` to force an immediate refresh. Authenticate via subscription (`/login`) or API key, then select any model from that provider via `/model` (or Ctrl+L).
66
+ For each built-in provider, nexus maintains a list of tool-capable models. Configured provider catalogs refresh automatically; run `nexus update --models` to force an immediate refresh. Use `/byok` to configure a subscription, API key, or local Ollama model, then select a model via `/model` (or Ctrl+L).
68
67
 
69
68
  **Subscriptions:**
70
69
  - Anthropic Claude Pro/Max
@@ -104,7 +103,7 @@ For each built-in provider, nexus maintains a list of tool-capable models. Confi
104
103
  - Xiaomi MiMo Token Plan (Amsterdam)
105
104
  - Xiaomi MiMo Token Plan (Singapore)
106
105
 
107
- Nexus also supports the llama.cpp router server. Configure it with `/login llama.cpp`, manage downloads and loaded models with `/llama`, then select a loaded model with `/model`. See [docs/llama-cpp.md](docs/llama-cpp.md) for setup and usage.
106
+ Nexus also supports the llama.cpp router server. Configure it with `/byok llama.cpp`, manage downloads and loaded models with `/llama`, then select a loaded model with `/model`. See [docs/llama-cpp.md](docs/llama-cpp.md) for setup and usage.
108
107
 
109
108
  See [docs/providers.md](docs/providers.md) for other provider setup instructions.
110
109
 
@@ -144,7 +143,10 @@ Type `/` in the editor to trigger commands. [Extensions](#extensions) can regist
144
143
 
145
144
  | Command | Description |
146
145
  |---------|-------------|
147
- | `/login`, `/logout` | Manage provider credentials |
146
+ | `/login` | Sign in to GitNexus |
147
+ | `/byok [provider\|ollama]` | Configure a cloud provider, API key, or local Ollama model |
148
+ | `/logout` | Remove stored model-provider credentials |
149
+ | `/gitnexus-status` | Show the GitNexus connection status |
148
150
  | [`/llama`](docs/llama-cpp.md) | Download, load, and unload llama.cpp router models |
149
151
  | `/model` | Switch models |
150
152
  | `/scoped-models` | Enable/disable models for Ctrl+P cycling |
@@ -164,7 +166,6 @@ Type `/` in the editor to trigger commands. [Extensions](#extensions) can regist
164
166
  | `/share` | Upload as private GitHub gist with shareable HTML link |
165
167
  | `/reload` | Reload keybindings, extensions, skills, prompts, themes, and context files |
166
168
  | `/hotkeys` | Show all keyboard shortcuts |
167
- | `/changelog` | Display version history |
168
169
  | `/quit` | Quit nexus |
169
170
 
170
171
  ### Keyboard Shortcuts
@@ -278,7 +279,7 @@ Use `/trust` in interactive mode to save a project trust decision for future ses
278
279
 
279
280
  Nexus has two separate startup features:
280
281
 
281
- - **Update check:** fetches `https://pi.dev/api/latest-version` to check whether a newer version exists. Disable it with `NEXUS_SKIP_VERSION_CHECK=1`. This still points at the upstream endpoint and is pending replacement.
282
+ - **Update check:** disabled until a Nexus release feed is configured with `NEXUS_LATEST_VERSION_URL`. Disable a configured feed with `NEXUS_SKIP_VERSION_CHECK=1`.
282
283
  - **Provider attribution headers:** optional headers sent with OpenRouter, Cloudflare, and direct NVIDIA NIM requests. Controlled by `enableInstallTelemetry` in `settings.json` or `NEXUS_TELEMETRY=0`.
283
284
 
284
285
  Nexus does not send install or update pings.
@@ -376,7 +377,7 @@ Place in `~/.nexus/agent/themes/`, `.nexus/themes/`, or a [nexus package](#nexus
376
377
 
377
378
  ### Nexus Packages
378
379
 
379
- Bundle and share extensions, skills, prompts, and themes via npm or git. Find packages on [npmjs.com](https://www.npmjs.com/search?q=keywords%3Api-package).
380
+ Bundle and share extensions, skills, prompts, and themes via npm or git.
380
381
 
381
382
  > **Security:** Nexus packages run with full system access. Extensions execute arbitrary code, and skills can instruct the model to perform any action including running executables. Review source code before installing third-party packages.
382
383
 
@@ -410,8 +411,8 @@ Create a package by adding a `nexus` key to `package.json`:
410
411
 
411
412
  ```json
412
413
  {
413
- "name": "my-pi-package",
414
- "keywords": ["pi-package"],
414
+ "name": "my-nexus-package",
415
+ "keywords": ["nexus-package"],
415
416
  "nexus": {
416
417
  "extensions": ["./extensions"],
417
418
  "skills": ["./skills"],
@@ -432,7 +433,7 @@ See [docs/packages.md](docs/packages.md).
432
433
  ### SDK
433
434
 
434
435
  ```typescript
435
- import { createAgentSession, ModelRuntime, SessionManager } from "@earendil-works/pi-coding-agent";
436
+ import { createAgentSession, ModelRuntime, SessionManager } from "@akon-labs/nexus-cli";
436
437
 
437
438
  const modelRuntime = await ModelRuntime.create();
438
439
  const { session } = await createAgentSession({
@@ -465,7 +466,7 @@ See [docs/rpc.md](docs/rpc.md) for the protocol.
465
466
 
466
467
  Nexus is aggressively extensible so it doesn't have to dictate your workflow. Features that other tools bake in can be built with [extensions](#extensions), [skills](#skills), or installed from third-party [nexus packages](#nexus-packages). This keeps the core minimal while letting you shape nexus to fit how you work.
467
468
 
468
- **No MCP.** Build CLI tools with READMEs (see [Skills](#skills)), or build an extension that adds MCP support. [Why?](https://mariozechner.at/posts/2025-11-02-what-if-you-dont-need-mcp/)
469
+ **MCP is built in.** Nexus loads configured MCP servers through its built-in adapter while keeping extensions and skills available for workflows that do not need MCP.
469
470
 
470
471
  **No sub-agents.** There's many ways to do this. Spawn nexus instances via tmux, or build your own with [extensions](#extensions), or install a package that does it your way.
471
472
 
@@ -477,8 +478,6 @@ Nexus is aggressively extensible so it doesn't have to dictate your workflow. Fe
477
478
 
478
479
  **No background bash.** Use tmux. Full observability, direct interaction.
479
480
 
480
- Read the [blog post](https://mariozechner.at/posts/2025-11-30-pi-coding-agent/) for the full rationale.
481
-
482
481
  ---
483
482
 
484
483
  ## CLI Reference
@@ -642,8 +641,14 @@ nexus --thinking high "Solve this complex problem"
642
641
  | `NEXUS_CODING_AGENT_SESSION_DIR` | Override session storage directory (overridden by `--session-dir`) |
643
642
  | `NEXUS_PACKAGE_DIR` | Override package directory (useful for Nix/Guix where store paths tokenize poorly) |
644
643
  | `NEXUS_OFFLINE` | Disable startup network operations, including update checks, package update checks, and install/update telemetry |
645
- | `NEXUS_SKIP_VERSION_CHECK` | Skip the Nexus version update check at startup. This prevents the `pi.dev` latest-version request |
644
+ | `NEXUS_LATEST_VERSION_URL` | Configure the Nexus JSON release-feed URL; no release feed is used by default |
645
+ | `NEXUS_SKIP_VERSION_CHECK` | Skip the Nexus version update check at startup |
646
+ | `NEXUS_SKIP_LOGIN` | Disable the GitNexus startup sign-in gate for CI and automated tests |
647
+ | `NEXUS_RAIL` | Set to `0` to disable the GitNexus side rail in fullscreen mode |
648
+ | `NEXUS_TRACE` | Set to `1` to enable GitNexus and text-mode tool tracing |
646
649
  | `NEXUS_TELEMETRY` | Override install/update telemetry and provider attribution headers. Use `1`/`true`/`yes` to enable or `0`/`false`/`no` to disable. This does not disable update checks |
650
+ | `GITNEXUS_TOKEN` | GitNexus Hub token for headless or automated sign-in |
651
+ | `GITNEXUS_HUB_URL` | Override the GitNexus Hub MCP endpoint |
647
652
  | `NEXUS_CACHE_RETENTION` | Set to `long` for extended prompt cache (Anthropic: 1h, OpenAI: 24h) |
648
653
  | `VISUAL`, `EDITOR` | Fallback external editor for Ctrl+G when `externalEditor` is unset; defaults to Notepad on Windows and `nano` elsewhere |
649
654
 
@@ -671,7 +676,6 @@ MIT
671
676
 
672
677
  ## See Also
673
678
 
674
- - [@earendil-works/pi-ai](https://www.npmjs.com/package/@earendil-works/pi-ai): Core LLM toolkit
675
- - [@earendil-works/pi-agent-core](https://www.npmjs.com/package/@earendil-works/pi-agent-core): Agent framework
676
- - [@earendil-works/pi-tui](https://www.npmjs.com/package/@earendil-works/pi-tui): Terminal UI components
677
-
679
+ - `@akon-labs/nexus-cli/ai`: Core LLM toolkit
680
+ - `@akon-labs/nexus-cli/agent-core`: Agent framework
681
+ - `@akon-labs/nexus-cli/tui`: Terminal UI components
package/dist/cli.js CHANGED
@@ -9132,7 +9132,7 @@ try {
9132
9132
  throw e;
9133
9133
  }
9134
9134
  var piConfigName = pkg.piConfig?.name;
9135
- var PACKAGE_NAME = pkg.name || "@akonlabs/nexus-cli";
9135
+ var PACKAGE_NAME = pkg.name || "@akon-labs/nexus-cli";
9136
9136
  var APP_NAME = piConfigName || "nexus";
9137
9137
  var APP_TITLE = piConfigName ? APP_NAME : "Nexus";
9138
9138
  var CONFIG_DIR_NAME = pkg.piConfig?.configDir || ".nexus";
@@ -37451,7 +37451,6 @@ function createEventBus() {
37451
37451
 
37452
37452
  // dist/core/extensions/loader.js
37453
37453
  import * as fs6 from "node:fs";
37454
- import { createRequire as createRequire5 } from "node:module";
37455
37454
  import * as path10 from "node:path";
37456
37455
  import { fileURLToPath as fileURLToPath6 } from "node:url";
37457
37456
 
@@ -64136,6 +64135,14 @@ var VIRTUAL_MODULES = {
64136
64135
  "@earendil-works/pi-ai/oauth": oauth_exports,
64137
64136
  "@earendil-works/pi-ai/providers/all": all_exports,
64138
64137
  "@earendil-works/pi-coding-agent": index_exports,
64138
+ // The fork's own SDK names: without these mappings, an extension importing
64139
+ // them would resolve the installed package from disk and load a second copy
64140
+ // instead of sharing the host's instances. These are also the only names
64141
+ // the public docs/examples use for the pi ecosystem packages.
64142
+ "@akon-labs/nexus-cli": index_exports,
64143
+ "@akon-labs/nexus-cli/agent-core": src_exports2,
64144
+ "@akon-labs/nexus-cli/tui": src_exports,
64145
+ "@akon-labs/nexus-cli/ai": compat_exports,
64139
64146
  "@mariozechner/pi-agent-core": src_exports2,
64140
64147
  "@mariozechner/pi-tui": src_exports,
64141
64148
  "@mariozechner/pi-ai": compat_exports,
@@ -64144,55 +64151,7 @@ var VIRTUAL_MODULES = {
64144
64151
  "@mariozechner/pi-ai/providers/all": all_exports,
64145
64152
  "@mariozechner/pi-coding-agent": index_exports
64146
64153
  };
64147
- var require3 = createRequire5(import.meta.url);
64148
64154
  var isTypeScriptSourceRuntime = !isBunBinary && path10.extname(fileURLToPath6(import.meta.url)) === ".ts";
64149
- var _aliases = null;
64150
- function getAliases() {
64151
- if (_aliases)
64152
- return _aliases;
64153
- const __dirname2 = path10.dirname(fileURLToPath6(import.meta.url));
64154
- const packageIndex = path10.resolve(__dirname2, "../..", "index.js");
64155
- const typeboxEntry = require3.resolve("typebox");
64156
- const typeboxCompileEntry = require3.resolve("typebox/compile");
64157
- const typeboxValueEntry = require3.resolve("typebox/value");
64158
- const packagesRoot = path10.resolve(__dirname2, "../../../../");
64159
- const resolveWorkspaceOrImport = (workspaceRelativePath, specifier) => {
64160
- const workspacePath = path10.join(packagesRoot, workspaceRelativePath);
64161
- if (fs6.existsSync(workspacePath)) {
64162
- return workspacePath;
64163
- }
64164
- return fileURLToPath6(import.meta.resolve(specifier));
64165
- };
64166
- const piCodingAgentEntry = packageIndex;
64167
- const piAgentCoreEntry = resolveWorkspaceOrImport("agent/dist/index.js", "@earendil-works/pi-agent-core");
64168
- const piTuiEntry = resolveWorkspaceOrImport("tui/dist/index.js", "@earendil-works/pi-tui");
64169
- const piAiCompatEntry = resolveWorkspaceOrImport("ai/dist/compat.js", "@earendil-works/pi-ai/compat");
64170
- const piAiOauthEntry = resolveWorkspaceOrImport("ai/dist/oauth.js", "@earendil-works/pi-ai/oauth");
64171
- const piAiProvidersEntry = resolveWorkspaceOrImport("ai/dist/providers/all.js", "@earendil-works/pi-ai/providers/all");
64172
- _aliases = {
64173
- "@earendil-works/pi-coding-agent": piCodingAgentEntry,
64174
- "@earendil-works/pi-agent-core": piAgentCoreEntry,
64175
- "@earendil-works/pi-tui": piTuiEntry,
64176
- "@earendil-works/pi-ai/providers/all": piAiProvidersEntry,
64177
- "@earendil-works/pi-ai/compat": piAiCompatEntry,
64178
- "@earendil-works/pi-ai/oauth": piAiOauthEntry,
64179
- "@earendil-works/pi-ai": piAiCompatEntry,
64180
- "@mariozechner/pi-coding-agent": piCodingAgentEntry,
64181
- "@mariozechner/pi-agent-core": piAgentCoreEntry,
64182
- "@mariozechner/pi-tui": piTuiEntry,
64183
- "@mariozechner/pi-ai/providers/all": piAiProvidersEntry,
64184
- "@mariozechner/pi-ai/compat": piAiCompatEntry,
64185
- "@mariozechner/pi-ai/oauth": piAiOauthEntry,
64186
- "@mariozechner/pi-ai": piAiCompatEntry,
64187
- typebox: typeboxEntry,
64188
- "typebox/compile": typeboxCompileEntry,
64189
- "typebox/value": typeboxValueEntry,
64190
- "@sinclair/typebox": typeboxEntry,
64191
- "@sinclair/typebox/compile": typeboxCompileEntry,
64192
- "@sinclair/typebox/value": typeboxValueEntry
64193
- };
64194
- return _aliases;
64195
- }
64196
64155
  var extensionCacheCwd;
64197
64156
  var extensionCacheGeneration = 0;
64198
64157
  var extensionCache = /* @__PURE__ */ new Map();
@@ -64428,9 +64387,14 @@ async function loadExtensionModule(extensionPath, cacheToken) {
64428
64387
  }
64429
64388
  const jiti = createJiti(import.meta.url, {
64430
64389
  moduleCache: false,
64431
- // Bun uses modules embedded in the executable. Source TypeScript reuses the
64432
- // host-resolved modules and root tsconfig paths. Built Node uses dist aliases.
64433
- ...isBunBinary ? { virtualModules: VIRTUAL_MODULES, tryNative: false } : isTypeScriptSourceRuntime ? { virtualModules: VIRTUAL_MODULES, tsconfigPaths: true } : { alias: getAliases() }
64390
+ // All modes serve extensions the statically imported module instances via
64391
+ // virtualModules. This is required for Bun (modules are embedded in the
64392
+ // executable) and for the published npm build (the vendored workspace
64393
+ // packages are inlined into dist/cli.js and exist nowhere on disk), and it
64394
+ // guarantees extensions share the host's module instances rather than
64395
+ // loading a second copy. Source TypeScript additionally resolves through
64396
+ // the root tsconfig paths.
64397
+ ...isTypeScriptSourceRuntime && !isBunBinary ? { virtualModules: VIRTUAL_MODULES, tsconfigPaths: true } : { virtualModules: VIRTUAL_MODULES, tryNative: false }
64434
64398
  });
64435
64399
  const module = await jiti.import(extensionPath, { default: true });
64436
64400
  const factory = module;
@@ -65838,6 +65802,24 @@ import { existsSync as existsSync24, mkdirSync as mkdirSync12, readFileSync as r
65838
65802
  import { dirname as dirname22, join as join37 } from "node:path";
65839
65803
  var AUTO_PROVIDER_ID = "gitnexus";
65840
65804
  var AUTO_MODEL_ID = "auto";
65805
+ function parseUsage2(value) {
65806
+ if (!value || typeof value !== "object")
65807
+ return void 0;
65808
+ const usage = value;
65809
+ if (typeof usage.used !== "number" || typeof usage.limit !== "number")
65810
+ return void 0;
65811
+ return {
65812
+ used: usage.used,
65813
+ limit: usage.limit,
65814
+ ...typeof usage.remaining === "number" ? { remaining: usage.remaining } : {}
65815
+ };
65816
+ }
65817
+ function formatCreditBalance(policy) {
65818
+ if (!policy?.usage)
65819
+ return void 0;
65820
+ const remaining = policy.usage.remaining ?? Math.max(0, policy.usage.limit - policy.usage.used);
65821
+ return `Nexus Credits: ${remaining.toLocaleString()} / ${policy.usage.limit.toLocaleString()}`;
65822
+ }
65841
65823
  function gatewayCachePath() {
65842
65824
  return join37(dirname22(gitNexusConfigPath()), "gateway.json");
65843
65825
  }
@@ -65850,7 +65832,7 @@ function readGatewayCache(path11 = gatewayCachePath()) {
65850
65832
  configured: parsed.configured === true,
65851
65833
  contextWindow: parsed.contextWindow,
65852
65834
  maxTokens: parsed.maxTokens,
65853
- usage: parsed.usage
65835
+ usage: parseUsage2(parsed.usage)
65854
65836
  };
65855
65837
  } catch {
65856
65838
  return void 0;
@@ -65887,7 +65869,7 @@ async function refreshGatewayPolicy(config, options = {}) {
65887
65869
  configured: body.configured === true,
65888
65870
  contextWindow: body.contextWindow,
65889
65871
  maxTokens: body.maxTokens,
65890
- usage: body.usage
65872
+ usage: parseUsage2(body.usage)
65891
65873
  };
65892
65874
  if (!existsSync24(dirname22(cachePath)))
65893
65875
  mkdirSync12(dirname22(cachePath), { recursive: true, mode: 448 });
@@ -65920,8 +65902,8 @@ function registerAutoProvider(pi, config, policy) {
65920
65902
  // so none are offered.
65921
65903
  reasoning: false,
65922
65904
  input: ["text", "image"],
65923
- // Spend is metered hub-side against a monthly allowance; there is no
65924
- // per-token price to show.
65905
+ // Spend is metered hub-side against Nexus Credits; there is no
65906
+ // per-token or dollar price to show.
65925
65907
  cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
65926
65908
  contextWindow: policy.contextWindow,
65927
65909
  maxTokens: policy.maxTokens
@@ -66735,7 +66717,11 @@ function gitnexusExtension(pi) {
66735
66717
  let startupNotice;
66736
66718
  let loginRequired = false;
66737
66719
  const cwd = process.cwd();
66738
- registerAutoProvider(pi, resolveGitNexusConfig(), readGatewayCache());
66720
+ let gatewayPolicy = readGatewayCache();
66721
+ registerAutoProvider(pi, resolveGitNexusConfig(), gatewayPolicy);
66722
+ function updateCreditStatus(ctx) {
66723
+ ctx.ui.setStatus("nexus-credits", formatCreditBalance(gatewayPolicy));
66724
+ }
66739
66725
  registerAugmentedGrep(pi, cwd, () => activeClient, () => current?.name);
66740
66726
  registerChangeTracking(pi, cwd);
66741
66727
  async function connect2() {
@@ -66788,9 +66774,18 @@ function gitnexusExtension(pi) {
66788
66774
  }
66789
66775
  pi.on("session_start", async (_event, ctx) => {
66790
66776
  await connect2();
66777
+ updateCreditStatus(ctx);
66791
66778
  if (startupNotice)
66792
66779
  ctx.ui.notify(startupNotice, inventory ? "info" : "warning");
66793
66780
  });
66781
+ pi.on("agent_settled", async (_event, ctx) => {
66782
+ if (!gatewayPolicy || ctx.model?.provider !== "gitnexus")
66783
+ return;
66784
+ const config = resolveGitNexusConfig();
66785
+ if (config)
66786
+ gatewayPolicy = await refreshGatewayPolicy(config);
66787
+ updateCreditStatus(ctx);
66788
+ });
66794
66789
  pi.on("resources_discover", (event) => {
66795
66790
  const hasOwnGitnexusSkills = existsSync25(join38(event.cwd, ".agents", "skills", "gitnexus")) || existsSync25(join38(homedir12(), ".agents", "skills", "gitnexus-debugging"));
66796
66791
  if (hasOwnGitnexusSkills)
@@ -66846,13 +66841,18 @@ ${buildGitNexusPrompt(inventory, current)}` };
66846
66841
  pi.registerCommand("gitnexus-status", {
66847
66842
  description: "Show GitNexus connection status",
66848
66843
  handler: async (_args, ctx) => {
66844
+ const config = resolveGitNexusConfig();
66845
+ if (config)
66846
+ gatewayPolicy = await refreshGatewayPolicy(config);
66847
+ updateCreditStatus(ctx);
66849
66848
  if (!inventory) {
66850
66849
  const message = loginRequired ? "GitNexus session expired. Run /login to sign in again." : startupNotice ?? "GitNexus: not signed in. Run /login.";
66851
66850
  ctx.ui.notify(message, "warning");
66852
66851
  return;
66853
66852
  }
66854
66853
  const repoLine = current ? `current repo: ${current.name}` : "current repo: not indexed";
66855
- ctx.ui.notify(`GitNexus: connected \u2014 ${inventory.repos.length} repos, ${repoLine}`, "info");
66854
+ const creditLine = formatCreditBalance(gatewayPolicy);
66855
+ ctx.ui.notify(`GitNexus: connected \u2014 ${inventory.repos.length} repos, ${repoLine}${creditLine ? `, ${creditLine}` : ""}`, "info");
66856
66856
  }
66857
66857
  });
66858
66858
  }
@@ -67252,7 +67252,26 @@ function mcpExtension(pi) {
67252
67252
  // dist/extensions/web-access.js
67253
67253
  import { existsSync as existsSync26, mkdirSync as mkdirSync13, readFileSync as readFileSync21, writeFileSync as writeFileSync13 } from "node:fs";
67254
67254
  import { join as join40 } from "node:path";
67255
- var WEB_ACCESS_EXTENSION_SOURCE = "npm:pi-web-access@0.22.0";
67255
+ var WEB_ACCESS_NPM_FALLBACK_SOURCE = "npm:pi-web-access@0.22.0";
67256
+ function resolveWebAccessExtensionSource() {
67257
+ const packageDir = getPackageDir();
67258
+ const sourceCandidates = [
67259
+ join40(packageDir, "node_modules", "pi-web-access", "index.ts"),
67260
+ join40(packageDir, "..", "..", "node_modules", "pi-web-access", "index.ts")
67261
+ ];
67262
+ const bundledCandidates = [
67263
+ join40(packageDir, "dist", "vendor", "web-access.js"),
67264
+ join40(packageDir, "vendor", "web-access.js")
67265
+ ];
67266
+ const isTypeScriptSourceRuntime2 = import.meta.url.endsWith(".ts");
67267
+ const candidates = isTypeScriptSourceRuntime2 ? [...sourceCandidates, ...bundledCandidates] : [...bundledCandidates, ...sourceCandidates];
67268
+ for (const candidate of candidates) {
67269
+ if (existsSync26(candidate))
67270
+ return candidate;
67271
+ }
67272
+ return WEB_ACCESS_NPM_FALLBACK_SOURCE;
67273
+ }
67274
+ var WEB_ACCESS_EXTENSION_SOURCE = resolveWebAccessExtensionSource();
67256
67275
  var WEB_ACCESS_CONFIG_FILENAME = "web-search.json";
67257
67276
  var DEFAULT_WORKFLOW = "none";
67258
67277
  var DEFAULT_WEB_SEARCH_TOOL_NAME = "websearch";
@@ -67314,6 +67333,15 @@ function prepareWebAccessConfig(agentDir) {
67314
67333
  config.searchRouting = DEFAULT_SEARCH_ROUTING;
67315
67334
  changed = true;
67316
67335
  }
67336
+ if (config.commands === void 0) {
67337
+ config.commands = {
67338
+ websearch: { enabled: false },
67339
+ curator: { enabled: false },
67340
+ search: { enabled: false },
67341
+ "google-account": { enabled: false }
67342
+ };
67343
+ changed = true;
67344
+ }
67317
67345
  if (!changed) {
67318
67346
  return { changed: false };
67319
67347
  }
@@ -67453,8 +67481,8 @@ var builtInExtensions = [
67453
67481
  import chalk10 from "chalk";
67454
67482
  import { existsSync as existsSync27, mkdirSync as mkdirSync14, readdirSync as readdirSync10, readFileSync as readFileSync22, renameSync as renameSync2, rmSync as rmSync5, writeFileSync as writeFileSync14 } from "fs";
67455
67483
  import { dirname as dirname23, join as join41 } from "path";
67456
- var MIGRATION_GUIDE_URL = "https://github.com/earendil-works/pi-mono/blob/main/packages/coding-agent/CHANGELOG.md#extensions-migration";
67457
- var EXTENSIONS_DOC_URL = "https://github.com/earendil-works/pi-mono/blob/main/packages/coding-agent/docs/extensions.md";
67484
+ var MIGRATION_GUIDE_URL = "https://github.com/Akon-Labs/nexus-cli/blob/main/packages/coding-agent/CHANGELOG.upstream.md#extensions-migration";
67485
+ var EXTENSIONS_DOC_URL = "https://github.com/Akon-Labs/nexus-cli/blob/main/packages/coding-agent/docs/extensions.md";
67458
67486
  function migrateAuthToAuthJson() {
67459
67487
  const agentDir = getAgentDir();
67460
67488
  const authPath = join41(agentDir, "auth.json");
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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","sourcesContent":["import { accessSync, constants, existsSync, readFileSync, realpathSync } from \"fs\";\nimport { homedir } from \"os\";\nimport { basename, dirname, join, resolve, sep, win32 } from \"path\";\nimport { fileURLToPath } from \"url\";\nimport { spawnProcessSync } from \"./utils/child-process.ts\";\nimport { normalizePath } from \"./utils/paths.ts\";\n\n// =============================================================================\n// Package Detection\n// =============================================================================\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n/**\n * Detect if we're running as a Bun compiled binary.\n * Bun binaries have import.meta.url containing \"$bunfs\", \"~BUN\", or \"%7EBUN\" (Bun's virtual filesystem path)\n */\nexport const isBunBinary =\n\timport.meta.url.includes(\"$bunfs\") || import.meta.url.includes(\"~BUN\") || import.meta.url.includes(\"%7EBUN\");\n\n/** Detect if Bun is the runtime (compiled binary or bun run) */\nexport const isBunRuntime = !!process.versions.bun;\n\n// =============================================================================\n// Install Method Detection\n// =============================================================================\n\nexport type InstallMethod = \"bun-binary\" | \"npm\" | \"pnpm\" | \"yarn\" | \"bun\" | \"unknown\";\n\ninterface SelfUpdateCommandStep {\n\tcommand: string;\n\targs: string[];\n\tdisplay: string;\n}\n\nexport interface SelfUpdateCommand extends SelfUpdateCommandStep {\n\tsteps?: SelfUpdateCommandStep[];\n}\n\nexport type SelfUpdatePackageTarget = string | { packageName: string; installSpec?: string };\n\nfunction normalizeSelfUpdatePackageTarget(target: SelfUpdatePackageTarget): {\n\tpackageName: string;\n\tinstallSpec: string;\n} {\n\tif (typeof target === \"string\") {\n\t\treturn { packageName: target, installSpec: target };\n\t}\n\treturn { packageName: target.packageName, installSpec: target.installSpec ?? target.packageName };\n}\n\nfunction makeSelfUpdateCommand(\n\tinstallStep: SelfUpdateCommandStep,\n\tuninstallStep?: SelfUpdateCommandStep,\n): SelfUpdateCommand {\n\tif (!uninstallStep) return installStep;\n\treturn {\n\t\t...installStep,\n\t\tdisplay: `${uninstallStep.display} && ${installStep.display}`,\n\t\tsteps: [uninstallStep, installStep],\n\t};\n}\n\nfunction makeSelfUpdateCommandStep(command: string, args: string[]): SelfUpdateCommandStep {\n\treturn {\n\t\tcommand,\n\t\targs,\n\t\tdisplay: [command, ...args].map((arg) => (/\\s/.test(arg) ? `\"${arg}\"` : arg)).join(\" \"),\n\t};\n}\n\nexport function detectInstallMethod(): InstallMethod {\n\tif (isBunBinary) {\n\t\treturn \"bun-binary\";\n\t}\n\n\tconst resolvedPath = `${__dirname}\\0${process.execPath || \"\"}`.toLowerCase().replace(/\\\\/g, \"/\");\n\n\tif (resolvedPath.includes(\"/pnpm/\") || resolvedPath.includes(\"/.pnpm/\")) {\n\t\treturn \"pnpm\";\n\t}\n\tif (resolvedPath.includes(\"/yarn/\") || resolvedPath.includes(\"/.yarn/\")) {\n\t\treturn \"yarn\";\n\t}\n\tif (isBunRuntime || resolvedPath.includes(\"/install/global/node_modules/\")) {\n\t\treturn \"bun\";\n\t}\n\tif (resolvedPath.includes(\"/npm/\") || resolvedPath.includes(\"/node_modules/\")) {\n\t\treturn \"npm\";\n\t}\n\n\treturn \"unknown\";\n}\n\nfunction getInferredNpmInstall(): { root: string; prefix: string } | undefined {\n\tconst packageDir = getPackageDir();\n\tconst path = process.platform === \"win32\" || packageDir.includes(\"\\\\\") ? win32 : { basename, dirname };\n\tconst parent = path.dirname(packageDir);\n\tlet root: string | undefined;\n\tif (path.basename(parent).startsWith(\"@\") && path.basename(path.dirname(parent)) === \"node_modules\") {\n\t\troot = path.dirname(parent);\n\t} else if (path.basename(parent) === \"node_modules\") {\n\t\troot = parent;\n\t}\n\tif (!root) return undefined;\n\tconst rootParent = path.dirname(root);\n\tif (path.basename(rootParent) === \"lib\") return { root, prefix: path.dirname(rootParent) };\n\t// Windows global npm prefixes use `<prefix>\\\\node_modules`, which is\n\t// indistinguishable from local project installs by path shape alone. Do not\n\t// infer unsupported Windows custom prefixes without `npm root -g` evidence.\n\treturn undefined;\n}\n\nfunction getSelfUpdateCommandForMethod(\n\tmethod: InstallMethod,\n\tinstalledPackageName: string,\n\tupdatePackageTarget: SelfUpdatePackageTarget = installedPackageName,\n\tnpmCommand?: string[],\n): SelfUpdateCommand | undefined {\n\tconst target = normalizeSelfUpdatePackageTarget(updatePackageTarget);\n\tswitch (method) {\n\t\tcase \"bun-binary\":\n\t\t\treturn undefined;\n\t\tcase \"pnpm\": {\n\t\t\tconst match = readCommandOutput(\"pnpm\", [\"root\", \"-g\"])\n\t\t\t\t? undefined\n\t\t\t\t: /^(.*[\\\\/]global[\\\\/][^\\\\/]+)[\\\\/]\\.pnpm[\\\\/]/.exec(getPackageDir());\n\t\t\tconst binDirArgs = match\n\t\t\t\t? [`--config.global-bin-dir=${process.env.PNPM_HOME || dirname(dirname(match[1]))}`]\n\t\t\t\t: [];\n\t\t\treturn makeSelfUpdateCommand(\n\t\t\t\tmakeSelfUpdateCommandStep(\"pnpm\", [\n\t\t\t\t\t\"install\",\n\t\t\t\t\t\"-g\",\n\t\t\t\t\t\"--ignore-scripts\",\n\t\t\t\t\t\"--config.minimumReleaseAge=0\",\n\t\t\t\t\t...binDirArgs,\n\t\t\t\t\ttarget.installSpec,\n\t\t\t\t]),\n\t\t\t\ttarget.packageName === installedPackageName\n\t\t\t\t\t? undefined\n\t\t\t\t\t: makeSelfUpdateCommandStep(\"pnpm\", [\"remove\", \"-g\", ...binDirArgs, installedPackageName]),\n\t\t\t);\n\t\t}\n\t\tcase \"yarn\":\n\t\t\treturn makeSelfUpdateCommand(\n\t\t\t\tmakeSelfUpdateCommandStep(\"yarn\", [\"global\", \"add\", \"--ignore-scripts\", target.installSpec]),\n\t\t\t\ttarget.packageName === installedPackageName\n\t\t\t\t\t? undefined\n\t\t\t\t\t: makeSelfUpdateCommandStep(\"yarn\", [\"global\", \"remove\", installedPackageName]),\n\t\t\t);\n\t\tcase \"bun\":\n\t\t\treturn makeSelfUpdateCommand(\n\t\t\t\tmakeSelfUpdateCommandStep(\"bun\", [\n\t\t\t\t\t\"install\",\n\t\t\t\t\t\"-g\",\n\t\t\t\t\t\"--ignore-scripts\",\n\t\t\t\t\t\"--minimum-release-age=0\",\n\t\t\t\t\ttarget.installSpec,\n\t\t\t\t]),\n\t\t\t\ttarget.packageName === installedPackageName\n\t\t\t\t\t? undefined\n\t\t\t\t\t: makeSelfUpdateCommandStep(\"bun\", [\"uninstall\", \"-g\", installedPackageName]),\n\t\t\t);\n\t\tcase \"npm\": {\n\t\t\tconst [command = \"npm\", ...npmArgs] = npmCommand ?? [];\n\t\t\tconst inferred = npmCommand?.length ? undefined : getInferredNpmInstall();\n\t\t\tconst prefixArgs = [...npmArgs, ...(inferred ? [\"--prefix\", inferred.prefix] : [])];\n\t\t\tconst installStep = makeSelfUpdateCommandStep(command, [\n\t\t\t\t...prefixArgs,\n\t\t\t\t\"install\",\n\t\t\t\t\"-g\",\n\t\t\t\t\"--ignore-scripts\",\n\t\t\t\t\"--min-release-age=0\",\n\t\t\t\ttarget.installSpec,\n\t\t\t]);\n\t\t\tconst uninstallStep =\n\t\t\t\ttarget.packageName === installedPackageName\n\t\t\t\t\t? undefined\n\t\t\t\t\t: makeSelfUpdateCommandStep(command, [...prefixArgs, \"uninstall\", \"-g\", installedPackageName]);\n\t\t\treturn makeSelfUpdateCommand(installStep, uninstallStep);\n\t\t}\n\t\tcase \"unknown\":\n\t\t\treturn undefined;\n\t}\n}\n\nfunction readCommandOutput(\n\tcommand: string,\n\targs: string[],\n\toptions: { requireSuccess?: boolean } = {},\n): string | undefined {\n\tconst result = spawnProcessSync(command, args, {\n\t\tencoding: \"utf-8\",\n\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t});\n\tif (result.status === 0) return result.stdout.trim() || undefined;\n\tif (options.requireSuccess) {\n\t\tconst reason = result.error?.message || result.stderr.trim() || `exit code ${result.status ?? \"unknown\"}`;\n\t\tthrow new Error(`Failed to run ${[command, ...args].join(\" \")}: ${reason}`);\n\t}\n\treturn undefined;\n}\n\nfunction getGlobalPackageRoots(method: InstallMethod, _packageName: string, npmCommand?: string[]): string[] {\n\tswitch (method) {\n\t\tcase \"npm\": {\n\t\t\tconst configured = !!npmCommand?.length;\n\t\t\tconst [command = \"npm\", ...npmArgs] = npmCommand ?? [];\n\t\t\tif (configured && command === \"bun\") {\n\t\t\t\tconst bunBin = readCommandOutput(command, [...npmArgs, \"pm\", \"bin\", \"-g\"], {\n\t\t\t\t\trequireSuccess: true,\n\t\t\t\t});\n\t\t\t\tconst roots = [join(homedir(), \".bun\", \"install\", \"global\", \"node_modules\")];\n\t\t\t\tif (bunBin) {\n\t\t\t\t\troots.push(join(dirname(bunBin), \"install\", \"global\", \"node_modules\"));\n\t\t\t\t}\n\t\t\t\treturn roots;\n\t\t\t}\n\t\t\tconst root = readCommandOutput(command, [...npmArgs, \"root\", \"-g\"], {\n\t\t\t\trequireSuccess: configured,\n\t\t\t});\n\t\t\tconst inferred = configured ? undefined : getInferredNpmInstall();\n\t\t\treturn [root, inferred?.root].filter((x): x is string => !!x);\n\t\t}\n\t\tcase \"pnpm\": {\n\t\t\tconst root = readCommandOutput(\"pnpm\", [\"root\", \"-g\"]);\n\t\t\tif (root) return [root, dirname(root)];\n\t\t\tconst match = /^(.*[\\\\/]global[\\\\/][^\\\\/]+)[\\\\/]\\.pnpm[\\\\/]/.exec(getPackageDir());\n\t\t\treturn match ? [match[1]] : [];\n\t\t}\n\t\tcase \"yarn\": {\n\t\t\tconst dir = readCommandOutput(\"yarn\", [\"global\", \"dir\"]);\n\t\t\treturn dir ? [dir, join(dir, \"node_modules\")] : [];\n\t\t}\n\t\tcase \"bun\": {\n\t\t\tconst bunBin = readCommandOutput(\"bun\", [\"pm\", \"bin\", \"-g\"]);\n\t\t\tconst roots = [join(homedir(), \".bun\", \"install\", \"global\", \"node_modules\")];\n\t\t\tif (bunBin) {\n\t\t\t\troots.push(join(dirname(bunBin), \"install\", \"global\", \"node_modules\"));\n\t\t\t}\n\t\t\treturn roots;\n\t\t}\n\t\tcase \"bun-binary\":\n\t\tcase \"unknown\":\n\t\t\treturn [];\n\t}\n}\n\nfunction normalizeExistingPathForComparison(path: string, resolveSymlinks: boolean): string | undefined {\n\tconst resolvedPath = resolve(path);\n\tif (!existsSync(resolvedPath)) {\n\t\treturn undefined;\n\t}\n\tlet normalizedPath = resolvedPath;\n\tif (resolveSymlinks) {\n\t\ttry {\n\t\t\tnormalizedPath = realpathSync(resolvedPath);\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\tif (process.platform === \"win32\") {\n\t\tnormalizedPath = normalizedPath.toLowerCase();\n\t}\n\treturn normalizedPath;\n}\n\nfunction getPathComparisonCandidates(path: string): string[] {\n\treturn Array.from(\n\t\tnew Set(\n\t\t\t[normalizeExistingPathForComparison(path, false), normalizeExistingPathForComparison(path, true)].filter(\n\t\t\t\t(candidate): candidate is string => !!candidate,\n\t\t\t),\n\t\t),\n\t);\n}\n\nfunction getEntrypointPackageDir(): string | undefined {\n\tconst entrypoint = process.argv[1];\n\tif (!entrypoint) return undefined;\n\tlet dir = dirname(entrypoint);\n\twhile (dir !== dirname(dir)) {\n\t\tif (existsSync(join(dir, \"package.json\"))) {\n\t\t\treturn dir;\n\t\t}\n\t\tdir = dirname(dir);\n\t}\n\treturn undefined;\n}\n\nfunction isSelfUpdatePathWritable(): boolean {\n\tconst packageDir = getPackageDir();\n\ttry {\n\t\taccessSync(packageDir, constants.W_OK);\n\t\taccessSync(dirname(packageDir), constants.W_OK);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction isManagedByGlobalPackageManager(method: InstallMethod, packageName: string, npmCommand?: string[]): boolean {\n\tconst packageDirs = [getPackageDir(), getEntrypointPackageDir()].filter((dir): dir is string => !!dir);\n\tconst packageDirCandidates = packageDirs.flatMap((dir) => getPathComparisonCandidates(dir));\n\treturn getGlobalPackageRoots(method, packageName, npmCommand).some((root) => {\n\t\treturn getPathComparisonCandidates(root).some((normalizedRoot) => {\n\t\t\tconst rootPrefix = normalizedRoot.endsWith(sep) ? normalizedRoot : `${normalizedRoot}${sep}`;\n\t\t\treturn packageDirCandidates.some((packageDir) => packageDir.startsWith(rootPrefix));\n\t\t});\n\t});\n}\n\nexport function getSelfUpdateCommand(\n\tpackageName: string,\n\tnpmCommand?: string[],\n\tupdatePackageTarget: SelfUpdatePackageTarget = packageName,\n): SelfUpdateCommand | undefined {\n\tconst method = detectInstallMethod();\n\tconst command = getSelfUpdateCommandForMethod(method, packageName, updatePackageTarget, npmCommand);\n\tif (!command || !isManagedByGlobalPackageManager(method, packageName, npmCommand) || !isSelfUpdatePathWritable()) {\n\t\treturn undefined;\n\t}\n\treturn command;\n}\n\nexport function getSelfUpdateUnavailableInstruction(\n\tpackageName: string,\n\tnpmCommand?: string[],\n\tupdatePackageTarget: SelfUpdatePackageTarget = packageName,\n): string {\n\tconst method = detectInstallMethod();\n\tconst target = normalizeSelfUpdatePackageTarget(updatePackageTarget);\n\tif (method === \"bun-binary\") {\n\t\treturn `Download from: https://github.com/Akon-Labs/nexus-cli/releases/latest`;\n\t}\n\tconst command = getSelfUpdateCommandForMethod(method, packageName, target, npmCommand);\n\tif (command) {\n\t\tif (isManagedByGlobalPackageManager(method, packageName, npmCommand) && !isSelfUpdatePathWritable()) {\n\t\t\treturn `This installation is managed by a global ${method} install, but the install path is not writable. Update it yourself with: ${command.display}`;\n\t\t}\n\t\treturn `This installation is not managed by a global ${method} install. Update it with the package manager, wrapper, or source checkout that provides it.`;\n\t}\n\treturn `Update ${target.installSpec} using the package manager, wrapper, or source checkout that provides this installation.`;\n}\n\nexport function getUpdateInstruction(packageName: string): string {\n\tconst method = detectInstallMethod();\n\tconst command = getSelfUpdateCommandForMethod(method, packageName);\n\tif (command) {\n\t\treturn `Run: ${command.display}`;\n\t}\n\treturn getSelfUpdateUnavailableInstruction(packageName);\n}\n\n// =============================================================================\n// Package Asset Paths (shipped with executable)\n// =============================================================================\n\n/**\n * Get the base directory for resolving package assets (themes, package.json, README.md, CHANGELOG.md).\n * - For Bun binary: returns the directory containing the executable\n * - For Node.js (dist/): returns __dirname (the dist/ directory)\n * - For tsx (src/): returns parent directory (the package root)\n */\nexport function getPackageDir(): string {\n\t// Allow override via environment variable (useful for Nix/Guix where store paths tokenize poorly)\n\t//\n\t// This one cannot go through readAppEnv: locating the package dir is what\n\t// lets us read package.json, which is where APP_NAME (and therefore the\n\t// current env prefix) comes from. Both names are checked literally instead.\n\tconst envDir = process.env.NEXUS_PACKAGE_DIR ?? process.env.PI_PACKAGE_DIR;\n\tif (envDir) {\n\t\treturn normalizePath(envDir);\n\t}\n\n\tif (isBunBinary) {\n\t\t// Bun binary: process.execPath points to the compiled executable\n\t\treturn dirname(process.execPath);\n\t}\n\t// Node.js: walk up from __dirname until we find package.json\n\tlet dir = __dirname;\n\twhile (dir !== dirname(dir)) {\n\t\tif (existsSync(join(dir, \"package.json\"))) {\n\t\t\treturn dir;\n\t\t}\n\t\tdir = dirname(dir);\n\t}\n\t// Fallback (shouldn't happen)\n\treturn __dirname;\n}\n\n/**\n * Get path to built-in themes directory (shipped with package)\n * - For Bun binary: theme/ next to executable\n * - For Node.js (dist/): dist/modes/interactive/theme/\n * - For tsx (src/): src/modes/interactive/theme/\n */\nexport function getThemesDir(): string {\n\tif (isBunBinary) {\n\t\treturn join(getPackageDir(), \"theme\");\n\t}\n\t// Theme is in modes/interactive/theme/ relative to src/ or dist/\n\tconst packageDir = getPackageDir();\n\tconst srcOrDist = existsSync(join(packageDir, \"src\")) ? \"src\" : \"dist\";\n\treturn join(packageDir, srcOrDist, \"modes\", \"interactive\", \"theme\");\n}\n\n/**\n * Get path to HTML export template directory (shipped with package)\n * - For Bun binary: export-html/ next to executable\n * - For Node.js (dist/): dist/core/export-html/\n * - For tsx (src/): src/core/export-html/\n */\nexport function getExportTemplateDir(): string {\n\tif (isBunBinary) {\n\t\treturn join(getPackageDir(), \"export-html\");\n\t}\n\tconst packageDir = getPackageDir();\n\tconst srcOrDist = existsSync(join(packageDir, \"src\")) ? \"src\" : \"dist\";\n\treturn join(packageDir, srcOrDist, \"core\", \"export-html\");\n}\n\n/** Get path to package.json */\nexport function getPackageJsonPath(): string {\n\treturn join(getPackageDir(), \"package.json\");\n}\n\n/** Get path to README.md */\nexport function getReadmePath(): string {\n\treturn resolve(join(getPackageDir(), \"README.md\"));\n}\n\n/** Get path to docs directory */\nexport function getDocsPath(): string {\n\treturn resolve(join(getPackageDir(), \"docs\"));\n}\n\n/** Get path to examples directory */\nexport function getExamplesPath(): string {\n\treturn resolve(join(getPackageDir(), \"examples\"));\n}\n\n// =============================================================================\n// App Config (from package.json piConfig)\n// =============================================================================\n\ninterface PackageJson {\n\tname?: string;\n\tversion?: string;\n\tpiConfig?: {\n\t\tname?: string;\n\t\tconfigDir?: string;\n\t};\n}\n\nlet pkg: PackageJson = {};\ntry {\n\tpkg = JSON.parse(readFileSync(getPackageJsonPath(), \"utf-8\")) as PackageJson;\n} catch (e: unknown) {\n\tconst err = e as NodeJS.ErrnoException;\n\tif (err.code !== \"ENOENT\") throw e;\n}\n\nconst piConfigName: string | undefined = pkg.piConfig?.name;\nexport const PACKAGE_NAME: string = pkg.name || \"@akonlabs/nexus-cli\";\nexport const APP_NAME: string = piConfigName || \"nexus\";\nexport const APP_TITLE: string = piConfigName ? APP_NAME : \"Nexus\";\nexport const CONFIG_DIR_NAME: string = pkg.piConfig?.configDir || \".nexus\";\nexport const VERSION: string = pkg.version || \"0.0.0\";\n\n// e.g., PI_CODING_AGENT_DIR or TAU_CODING_AGENT_DIR\nexport const ENV_AGENT_DIR = `${APP_NAME.toUpperCase()}_CODING_AGENT_DIR`;\nexport const ENV_SESSION_DIR = `${APP_NAME.toUpperCase()}_CODING_AGENT_SESSION_DIR`;\n\n// =============================================================================\n// Environment variable compatibility\n// =============================================================================\n//\n// The published extension ecosystem was written against pi and reads `PI_*`\n// names directly (pi-subagents reads PI_CODING_AGENT_DIR and PI_OFFLINE, for\n// example). We therefore publish every app variable under both the current\n// `<APP>_` prefix and the legacy `PI_` prefix, and accept either when reading.\n// Dropping the legacy name would silently change behaviour inside third-party\n// extensions rather than fail loudly, so both are kept indefinitely.\n\nconst ENV_PREFIX = `${APP_NAME.toUpperCase()}_`;\nconst LEGACY_ENV_PREFIX = \"PI_\";\n\n/** Both env var names for a suffix, current name first. */\nexport function appEnvNames(suffix: string): string[] {\n\tconst current = `${ENV_PREFIX}${suffix}`;\n\tconst legacy = `${LEGACY_ENV_PREFIX}${suffix}`;\n\treturn current === legacy ? [current] : [current, legacy];\n}\n\n/** Read an app env var, preferring the current name over the legacy `PI_` one. */\nexport function readAppEnv(suffix: string, env: NodeJS.ProcessEnv = process.env): string | undefined {\n\tfor (const name of appEnvNames(suffix)) {\n\t\tconst value = env[name];\n\t\t// An empty value counts as unset, so `NEXUS_X=` does not shadow `PI_X`.\n\t\tif (value !== undefined && value !== \"\") return value;\n\t}\n\treturn undefined;\n}\n\n/** Set an app env var under both the current and legacy names. */\nexport function setAppEnv(suffix: string, value: string, env: NodeJS.ProcessEnv = process.env): void {\n\tfor (const name of appEnvNames(suffix)) {\n\t\tenv[name] = value;\n\t}\n}\n\n/** Remove an app env var under both the current and legacy names. */\nexport function deleteAppEnv(suffix: string, env: NodeJS.ProcessEnv = process.env): void {\n\tfor (const name of appEnvNames(suffix)) {\n\t\tdelete env[name];\n\t}\n}\n\nexport function expandTildePath(path: string): string {\n\treturn normalizePath(path);\n}\n\n/**\n * Where a shared session can be viewed.\n *\n * Upstream pointed this at their own viewer. We have none, so the gist itself\n * is the destination — it renders the exported HTML and is the thing the user\n * actually owns. Set NEXUS_SHARE_VIEWER_URL to front it with a viewer.\n */\nexport function getShareViewerUrl(gistId: string): string {\n\tconst baseUrl = readAppEnv(\"SHARE_VIEWER_URL\");\n\treturn baseUrl ? `${baseUrl}#${gistId}` : `https://gist.github.com/${gistId}`;\n}\n\n// =============================================================================\n// User Config Paths (~/.pi/agent/*)\n// =============================================================================\n\n/** Get the agent config directory (e.g., ~/.pi/agent/) */\nexport function getAgentDir(): string {\n\tconst envDir = readAppEnv(\"CODING_AGENT_DIR\");\n\tif (envDir) {\n\t\treturn expandTildePath(envDir);\n\t}\n\treturn join(homedir(), CONFIG_DIR_NAME, \"agent\");\n}\n\n/** Get path to user's custom themes directory */\nexport function getCustomThemesDir(): string {\n\treturn join(getAgentDir(), \"themes\");\n}\n\n/** Get path to models.json */\nexport function getModelsPath(): string {\n\treturn join(getAgentDir(), \"models.json\");\n}\n\n/** Get path to auth.json */\nexport function getAuthPath(): string {\n\treturn join(getAgentDir(), \"auth.json\");\n}\n\n/** Get path to settings.json */\nexport function getSettingsPath(): string {\n\treturn join(getAgentDir(), \"settings.json\");\n}\n\n/** Get path to tools directory */\nexport function getToolsDir(): string {\n\treturn join(getAgentDir(), \"tools\");\n}\n\n/** Get path to managed binaries directory (fd, rg) */\nexport function getBinDir(): string {\n\treturn join(getAgentDir(), \"bin\");\n}\n\n/** Get path to prompt templates directory */\nexport function getPromptsDir(): string {\n\treturn join(getAgentDir(), \"prompts\");\n}\n\n/** Get path to sessions directory */\nexport function getSessionsDir(): string {\n\treturn join(getAgentDir(), \"sessions\");\n}\n\n/** Get path to debug log file */\nexport function getDebugLogPath(): string {\n\treturn join(getAgentDir(), `${APP_NAME}-debug.log`);\n}\n"]}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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","sourcesContent":["import { accessSync, constants, existsSync, readFileSync, realpathSync } from \"fs\";\nimport { homedir } from \"os\";\nimport { basename, dirname, join, resolve, sep, win32 } from \"path\";\nimport { fileURLToPath } from \"url\";\nimport { spawnProcessSync } from \"./utils/child-process.ts\";\nimport { normalizePath } from \"./utils/paths.ts\";\n\n// =============================================================================\n// Package Detection\n// =============================================================================\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n/**\n * Detect if we're running as a Bun compiled binary.\n * Bun binaries have import.meta.url containing \"$bunfs\", \"~BUN\", or \"%7EBUN\" (Bun's virtual filesystem path)\n */\nexport const isBunBinary =\n\timport.meta.url.includes(\"$bunfs\") || import.meta.url.includes(\"~BUN\") || import.meta.url.includes(\"%7EBUN\");\n\n/** Detect if Bun is the runtime (compiled binary or bun run) */\nexport const isBunRuntime = !!process.versions.bun;\n\n// =============================================================================\n// Install Method Detection\n// =============================================================================\n\nexport type InstallMethod = \"bun-binary\" | \"npm\" | \"pnpm\" | \"yarn\" | \"bun\" | \"unknown\";\n\ninterface SelfUpdateCommandStep {\n\tcommand: string;\n\targs: string[];\n\tdisplay: string;\n}\n\nexport interface SelfUpdateCommand extends SelfUpdateCommandStep {\n\tsteps?: SelfUpdateCommandStep[];\n}\n\nexport type SelfUpdatePackageTarget = string | { packageName: string; installSpec?: string };\n\nfunction normalizeSelfUpdatePackageTarget(target: SelfUpdatePackageTarget): {\n\tpackageName: string;\n\tinstallSpec: string;\n} {\n\tif (typeof target === \"string\") {\n\t\treturn { packageName: target, installSpec: target };\n\t}\n\treturn { packageName: target.packageName, installSpec: target.installSpec ?? target.packageName };\n}\n\nfunction makeSelfUpdateCommand(\n\tinstallStep: SelfUpdateCommandStep,\n\tuninstallStep?: SelfUpdateCommandStep,\n): SelfUpdateCommand {\n\tif (!uninstallStep) return installStep;\n\treturn {\n\t\t...installStep,\n\t\tdisplay: `${uninstallStep.display} && ${installStep.display}`,\n\t\tsteps: [uninstallStep, installStep],\n\t};\n}\n\nfunction makeSelfUpdateCommandStep(command: string, args: string[]): SelfUpdateCommandStep {\n\treturn {\n\t\tcommand,\n\t\targs,\n\t\tdisplay: [command, ...args].map((arg) => (/\\s/.test(arg) ? `\"${arg}\"` : arg)).join(\" \"),\n\t};\n}\n\nexport function detectInstallMethod(): InstallMethod {\n\tif (isBunBinary) {\n\t\treturn \"bun-binary\";\n\t}\n\n\tconst resolvedPath = `${__dirname}\\0${process.execPath || \"\"}`.toLowerCase().replace(/\\\\/g, \"/\");\n\n\tif (resolvedPath.includes(\"/pnpm/\") || resolvedPath.includes(\"/.pnpm/\")) {\n\t\treturn \"pnpm\";\n\t}\n\tif (resolvedPath.includes(\"/yarn/\") || resolvedPath.includes(\"/.yarn/\")) {\n\t\treturn \"yarn\";\n\t}\n\tif (isBunRuntime || resolvedPath.includes(\"/install/global/node_modules/\")) {\n\t\treturn \"bun\";\n\t}\n\tif (resolvedPath.includes(\"/npm/\") || resolvedPath.includes(\"/node_modules/\")) {\n\t\treturn \"npm\";\n\t}\n\n\treturn \"unknown\";\n}\n\nfunction getInferredNpmInstall(): { root: string; prefix: string } | undefined {\n\tconst packageDir = getPackageDir();\n\tconst path = process.platform === \"win32\" || packageDir.includes(\"\\\\\") ? win32 : { basename, dirname };\n\tconst parent = path.dirname(packageDir);\n\tlet root: string | undefined;\n\tif (path.basename(parent).startsWith(\"@\") && path.basename(path.dirname(parent)) === \"node_modules\") {\n\t\troot = path.dirname(parent);\n\t} else if (path.basename(parent) === \"node_modules\") {\n\t\troot = parent;\n\t}\n\tif (!root) return undefined;\n\tconst rootParent = path.dirname(root);\n\tif (path.basename(rootParent) === \"lib\") return { root, prefix: path.dirname(rootParent) };\n\t// Windows global npm prefixes use `<prefix>\\\\node_modules`, which is\n\t// indistinguishable from local project installs by path shape alone. Do not\n\t// infer unsupported Windows custom prefixes without `npm root -g` evidence.\n\treturn undefined;\n}\n\nfunction getSelfUpdateCommandForMethod(\n\tmethod: InstallMethod,\n\tinstalledPackageName: string,\n\tupdatePackageTarget: SelfUpdatePackageTarget = installedPackageName,\n\tnpmCommand?: string[],\n): SelfUpdateCommand | undefined {\n\tconst target = normalizeSelfUpdatePackageTarget(updatePackageTarget);\n\tswitch (method) {\n\t\tcase \"bun-binary\":\n\t\t\treturn undefined;\n\t\tcase \"pnpm\": {\n\t\t\tconst match = readCommandOutput(\"pnpm\", [\"root\", \"-g\"])\n\t\t\t\t? undefined\n\t\t\t\t: /^(.*[\\\\/]global[\\\\/][^\\\\/]+)[\\\\/]\\.pnpm[\\\\/]/.exec(getPackageDir());\n\t\t\tconst binDirArgs = match\n\t\t\t\t? [`--config.global-bin-dir=${process.env.PNPM_HOME || dirname(dirname(match[1]))}`]\n\t\t\t\t: [];\n\t\t\treturn makeSelfUpdateCommand(\n\t\t\t\tmakeSelfUpdateCommandStep(\"pnpm\", [\n\t\t\t\t\t\"install\",\n\t\t\t\t\t\"-g\",\n\t\t\t\t\t\"--ignore-scripts\",\n\t\t\t\t\t\"--config.minimumReleaseAge=0\",\n\t\t\t\t\t...binDirArgs,\n\t\t\t\t\ttarget.installSpec,\n\t\t\t\t]),\n\t\t\t\ttarget.packageName === installedPackageName\n\t\t\t\t\t? undefined\n\t\t\t\t\t: makeSelfUpdateCommandStep(\"pnpm\", [\"remove\", \"-g\", ...binDirArgs, installedPackageName]),\n\t\t\t);\n\t\t}\n\t\tcase \"yarn\":\n\t\t\treturn makeSelfUpdateCommand(\n\t\t\t\tmakeSelfUpdateCommandStep(\"yarn\", [\"global\", \"add\", \"--ignore-scripts\", target.installSpec]),\n\t\t\t\ttarget.packageName === installedPackageName\n\t\t\t\t\t? undefined\n\t\t\t\t\t: makeSelfUpdateCommandStep(\"yarn\", [\"global\", \"remove\", installedPackageName]),\n\t\t\t);\n\t\tcase \"bun\":\n\t\t\treturn makeSelfUpdateCommand(\n\t\t\t\tmakeSelfUpdateCommandStep(\"bun\", [\n\t\t\t\t\t\"install\",\n\t\t\t\t\t\"-g\",\n\t\t\t\t\t\"--ignore-scripts\",\n\t\t\t\t\t\"--minimum-release-age=0\",\n\t\t\t\t\ttarget.installSpec,\n\t\t\t\t]),\n\t\t\t\ttarget.packageName === installedPackageName\n\t\t\t\t\t? undefined\n\t\t\t\t\t: makeSelfUpdateCommandStep(\"bun\", [\"uninstall\", \"-g\", installedPackageName]),\n\t\t\t);\n\t\tcase \"npm\": {\n\t\t\tconst [command = \"npm\", ...npmArgs] = npmCommand ?? [];\n\t\t\tconst inferred = npmCommand?.length ? undefined : getInferredNpmInstall();\n\t\t\tconst prefixArgs = [...npmArgs, ...(inferred ? [\"--prefix\", inferred.prefix] : [])];\n\t\t\tconst installStep = makeSelfUpdateCommandStep(command, [\n\t\t\t\t...prefixArgs,\n\t\t\t\t\"install\",\n\t\t\t\t\"-g\",\n\t\t\t\t\"--ignore-scripts\",\n\t\t\t\t\"--min-release-age=0\",\n\t\t\t\ttarget.installSpec,\n\t\t\t]);\n\t\t\tconst uninstallStep =\n\t\t\t\ttarget.packageName === installedPackageName\n\t\t\t\t\t? undefined\n\t\t\t\t\t: makeSelfUpdateCommandStep(command, [...prefixArgs, \"uninstall\", \"-g\", installedPackageName]);\n\t\t\treturn makeSelfUpdateCommand(installStep, uninstallStep);\n\t\t}\n\t\tcase \"unknown\":\n\t\t\treturn undefined;\n\t}\n}\n\nfunction readCommandOutput(\n\tcommand: string,\n\targs: string[],\n\toptions: { requireSuccess?: boolean } = {},\n): string | undefined {\n\tconst result = spawnProcessSync(command, args, {\n\t\tencoding: \"utf-8\",\n\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t});\n\tif (result.status === 0) return result.stdout.trim() || undefined;\n\tif (options.requireSuccess) {\n\t\tconst reason = result.error?.message || result.stderr.trim() || `exit code ${result.status ?? \"unknown\"}`;\n\t\tthrow new Error(`Failed to run ${[command, ...args].join(\" \")}: ${reason}`);\n\t}\n\treturn undefined;\n}\n\nfunction getGlobalPackageRoots(method: InstallMethod, _packageName: string, npmCommand?: string[]): string[] {\n\tswitch (method) {\n\t\tcase \"npm\": {\n\t\t\tconst configured = !!npmCommand?.length;\n\t\t\tconst [command = \"npm\", ...npmArgs] = npmCommand ?? [];\n\t\t\tif (configured && command === \"bun\") {\n\t\t\t\tconst bunBin = readCommandOutput(command, [...npmArgs, \"pm\", \"bin\", \"-g\"], {\n\t\t\t\t\trequireSuccess: true,\n\t\t\t\t});\n\t\t\t\tconst roots = [join(homedir(), \".bun\", \"install\", \"global\", \"node_modules\")];\n\t\t\t\tif (bunBin) {\n\t\t\t\t\troots.push(join(dirname(bunBin), \"install\", \"global\", \"node_modules\"));\n\t\t\t\t}\n\t\t\t\treturn roots;\n\t\t\t}\n\t\t\tconst root = readCommandOutput(command, [...npmArgs, \"root\", \"-g\"], {\n\t\t\t\trequireSuccess: configured,\n\t\t\t});\n\t\t\tconst inferred = configured ? undefined : getInferredNpmInstall();\n\t\t\treturn [root, inferred?.root].filter((x): x is string => !!x);\n\t\t}\n\t\tcase \"pnpm\": {\n\t\t\tconst root = readCommandOutput(\"pnpm\", [\"root\", \"-g\"]);\n\t\t\tif (root) return [root, dirname(root)];\n\t\t\tconst match = /^(.*[\\\\/]global[\\\\/][^\\\\/]+)[\\\\/]\\.pnpm[\\\\/]/.exec(getPackageDir());\n\t\t\treturn match ? [match[1]] : [];\n\t\t}\n\t\tcase \"yarn\": {\n\t\t\tconst dir = readCommandOutput(\"yarn\", [\"global\", \"dir\"]);\n\t\t\treturn dir ? [dir, join(dir, \"node_modules\")] : [];\n\t\t}\n\t\tcase \"bun\": {\n\t\t\tconst bunBin = readCommandOutput(\"bun\", [\"pm\", \"bin\", \"-g\"]);\n\t\t\tconst roots = [join(homedir(), \".bun\", \"install\", \"global\", \"node_modules\")];\n\t\t\tif (bunBin) {\n\t\t\t\troots.push(join(dirname(bunBin), \"install\", \"global\", \"node_modules\"));\n\t\t\t}\n\t\t\treturn roots;\n\t\t}\n\t\tcase \"bun-binary\":\n\t\tcase \"unknown\":\n\t\t\treturn [];\n\t}\n}\n\nfunction normalizeExistingPathForComparison(path: string, resolveSymlinks: boolean): string | undefined {\n\tconst resolvedPath = resolve(path);\n\tif (!existsSync(resolvedPath)) {\n\t\treturn undefined;\n\t}\n\tlet normalizedPath = resolvedPath;\n\tif (resolveSymlinks) {\n\t\ttry {\n\t\t\tnormalizedPath = realpathSync(resolvedPath);\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\tif (process.platform === \"win32\") {\n\t\tnormalizedPath = normalizedPath.toLowerCase();\n\t}\n\treturn normalizedPath;\n}\n\nfunction getPathComparisonCandidates(path: string): string[] {\n\treturn Array.from(\n\t\tnew Set(\n\t\t\t[normalizeExistingPathForComparison(path, false), normalizeExistingPathForComparison(path, true)].filter(\n\t\t\t\t(candidate): candidate is string => !!candidate,\n\t\t\t),\n\t\t),\n\t);\n}\n\nfunction getEntrypointPackageDir(): string | undefined {\n\tconst entrypoint = process.argv[1];\n\tif (!entrypoint) return undefined;\n\tlet dir = dirname(entrypoint);\n\twhile (dir !== dirname(dir)) {\n\t\tif (existsSync(join(dir, \"package.json\"))) {\n\t\t\treturn dir;\n\t\t}\n\t\tdir = dirname(dir);\n\t}\n\treturn undefined;\n}\n\nfunction isSelfUpdatePathWritable(): boolean {\n\tconst packageDir = getPackageDir();\n\ttry {\n\t\taccessSync(packageDir, constants.W_OK);\n\t\taccessSync(dirname(packageDir), constants.W_OK);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction isManagedByGlobalPackageManager(method: InstallMethod, packageName: string, npmCommand?: string[]): boolean {\n\tconst packageDirs = [getPackageDir(), getEntrypointPackageDir()].filter((dir): dir is string => !!dir);\n\tconst packageDirCandidates = packageDirs.flatMap((dir) => getPathComparisonCandidates(dir));\n\treturn getGlobalPackageRoots(method, packageName, npmCommand).some((root) => {\n\t\treturn getPathComparisonCandidates(root).some((normalizedRoot) => {\n\t\t\tconst rootPrefix = normalizedRoot.endsWith(sep) ? normalizedRoot : `${normalizedRoot}${sep}`;\n\t\t\treturn packageDirCandidates.some((packageDir) => packageDir.startsWith(rootPrefix));\n\t\t});\n\t});\n}\n\nexport function getSelfUpdateCommand(\n\tpackageName: string,\n\tnpmCommand?: string[],\n\tupdatePackageTarget: SelfUpdatePackageTarget = packageName,\n): SelfUpdateCommand | undefined {\n\tconst method = detectInstallMethod();\n\tconst command = getSelfUpdateCommandForMethod(method, packageName, updatePackageTarget, npmCommand);\n\tif (!command || !isManagedByGlobalPackageManager(method, packageName, npmCommand) || !isSelfUpdatePathWritable()) {\n\t\treturn undefined;\n\t}\n\treturn command;\n}\n\nexport function getSelfUpdateUnavailableInstruction(\n\tpackageName: string,\n\tnpmCommand?: string[],\n\tupdatePackageTarget: SelfUpdatePackageTarget = packageName,\n): string {\n\tconst method = detectInstallMethod();\n\tconst target = normalizeSelfUpdatePackageTarget(updatePackageTarget);\n\tif (method === \"bun-binary\") {\n\t\treturn `Download from: https://github.com/Akon-Labs/nexus-cli/releases/latest`;\n\t}\n\tconst command = getSelfUpdateCommandForMethod(method, packageName, target, npmCommand);\n\tif (command) {\n\t\tif (isManagedByGlobalPackageManager(method, packageName, npmCommand) && !isSelfUpdatePathWritable()) {\n\t\t\treturn `This installation is managed by a global ${method} install, but the install path is not writable. Update it yourself with: ${command.display}`;\n\t\t}\n\t\treturn `This installation is not managed by a global ${method} install. Update it with the package manager, wrapper, or source checkout that provides it.`;\n\t}\n\treturn `Update ${target.installSpec} using the package manager, wrapper, or source checkout that provides this installation.`;\n}\n\nexport function getUpdateInstruction(packageName: string): string {\n\tconst method = detectInstallMethod();\n\tconst command = getSelfUpdateCommandForMethod(method, packageName);\n\tif (command) {\n\t\treturn `Run: ${command.display}`;\n\t}\n\treturn getSelfUpdateUnavailableInstruction(packageName);\n}\n\n// =============================================================================\n// Package Asset Paths (shipped with executable)\n// =============================================================================\n\n/**\n * Get the base directory for resolving package assets (themes, package.json, README.md, CHANGELOG.md).\n * - For Bun binary: returns the directory containing the executable\n * - For Node.js (dist/): returns __dirname (the dist/ directory)\n * - For tsx (src/): returns parent directory (the package root)\n */\nexport function getPackageDir(): string {\n\t// Allow override via environment variable (useful for Nix/Guix where store paths tokenize poorly)\n\t//\n\t// This one cannot go through readAppEnv: locating the package dir is what\n\t// lets us read package.json, which is where APP_NAME (and therefore the\n\t// current env prefix) comes from. Both names are checked literally instead.\n\tconst envDir = process.env.NEXUS_PACKAGE_DIR ?? process.env.PI_PACKAGE_DIR;\n\tif (envDir) {\n\t\treturn normalizePath(envDir);\n\t}\n\n\tif (isBunBinary) {\n\t\t// Bun binary: process.execPath points to the compiled executable\n\t\treturn dirname(process.execPath);\n\t}\n\t// Node.js: walk up from __dirname until we find package.json\n\tlet dir = __dirname;\n\twhile (dir !== dirname(dir)) {\n\t\tif (existsSync(join(dir, \"package.json\"))) {\n\t\t\treturn dir;\n\t\t}\n\t\tdir = dirname(dir);\n\t}\n\t// Fallback (shouldn't happen)\n\treturn __dirname;\n}\n\n/**\n * Get path to built-in themes directory (shipped with package)\n * - For Bun binary: theme/ next to executable\n * - For Node.js (dist/): dist/modes/interactive/theme/\n * - For tsx (src/): src/modes/interactive/theme/\n */\nexport function getThemesDir(): string {\n\tif (isBunBinary) {\n\t\treturn join(getPackageDir(), \"theme\");\n\t}\n\t// Theme is in modes/interactive/theme/ relative to src/ or dist/\n\tconst packageDir = getPackageDir();\n\tconst srcOrDist = existsSync(join(packageDir, \"src\")) ? \"src\" : \"dist\";\n\treturn join(packageDir, srcOrDist, \"modes\", \"interactive\", \"theme\");\n}\n\n/**\n * Get path to HTML export template directory (shipped with package)\n * - For Bun binary: export-html/ next to executable\n * - For Node.js (dist/): dist/core/export-html/\n * - For tsx (src/): src/core/export-html/\n */\nexport function getExportTemplateDir(): string {\n\tif (isBunBinary) {\n\t\treturn join(getPackageDir(), \"export-html\");\n\t}\n\tconst packageDir = getPackageDir();\n\tconst srcOrDist = existsSync(join(packageDir, \"src\")) ? \"src\" : \"dist\";\n\treturn join(packageDir, srcOrDist, \"core\", \"export-html\");\n}\n\n/** Get path to package.json */\nexport function getPackageJsonPath(): string {\n\treturn join(getPackageDir(), \"package.json\");\n}\n\n/** Get path to README.md */\nexport function getReadmePath(): string {\n\treturn resolve(join(getPackageDir(), \"README.md\"));\n}\n\n/** Get path to docs directory */\nexport function getDocsPath(): string {\n\treturn resolve(join(getPackageDir(), \"docs\"));\n}\n\n/** Get path to examples directory */\nexport function getExamplesPath(): string {\n\treturn resolve(join(getPackageDir(), \"examples\"));\n}\n\n// =============================================================================\n// App Config (from package.json piConfig)\n// =============================================================================\n\ninterface PackageJson {\n\tname?: string;\n\tversion?: string;\n\tpiConfig?: {\n\t\tname?: string;\n\t\tconfigDir?: string;\n\t};\n}\n\nlet pkg: PackageJson = {};\ntry {\n\tpkg = JSON.parse(readFileSync(getPackageJsonPath(), \"utf-8\")) as PackageJson;\n} catch (e: unknown) {\n\tconst err = e as NodeJS.ErrnoException;\n\tif (err.code !== \"ENOENT\") throw e;\n}\n\nconst piConfigName: string | undefined = pkg.piConfig?.name;\nexport const PACKAGE_NAME: string = pkg.name || \"@akon-labs/nexus-cli\";\nexport const APP_NAME: string = piConfigName || \"nexus\";\nexport const APP_TITLE: string = piConfigName ? APP_NAME : \"Nexus\";\nexport const CONFIG_DIR_NAME: string = pkg.piConfig?.configDir || \".nexus\";\nexport const VERSION: string = pkg.version || \"0.0.0\";\n\n// e.g., PI_CODING_AGENT_DIR or TAU_CODING_AGENT_DIR\nexport const ENV_AGENT_DIR = `${APP_NAME.toUpperCase()}_CODING_AGENT_DIR`;\nexport const ENV_SESSION_DIR = `${APP_NAME.toUpperCase()}_CODING_AGENT_SESSION_DIR`;\n\n// =============================================================================\n// Environment variable compatibility\n// =============================================================================\n//\n// The published extension ecosystem was written against pi and reads `PI_*`\n// names directly (pi-subagents reads PI_CODING_AGENT_DIR and PI_OFFLINE, for\n// example). We therefore publish every app variable under both the current\n// `<APP>_` prefix and the legacy `PI_` prefix, and accept either when reading.\n// Dropping the legacy name would silently change behaviour inside third-party\n// extensions rather than fail loudly, so both are kept indefinitely.\n\nconst ENV_PREFIX = `${APP_NAME.toUpperCase()}_`;\nconst LEGACY_ENV_PREFIX = \"PI_\";\n\n/** Both env var names for a suffix, current name first. */\nexport function appEnvNames(suffix: string): string[] {\n\tconst current = `${ENV_PREFIX}${suffix}`;\n\tconst legacy = `${LEGACY_ENV_PREFIX}${suffix}`;\n\treturn current === legacy ? [current] : [current, legacy];\n}\n\n/** Read an app env var, preferring the current name over the legacy `PI_` one. */\nexport function readAppEnv(suffix: string, env: NodeJS.ProcessEnv = process.env): string | undefined {\n\tfor (const name of appEnvNames(suffix)) {\n\t\tconst value = env[name];\n\t\t// An empty value counts as unset, so `NEXUS_X=` does not shadow `PI_X`.\n\t\tif (value !== undefined && value !== \"\") return value;\n\t}\n\treturn undefined;\n}\n\n/** Set an app env var under both the current and legacy names. */\nexport function setAppEnv(suffix: string, value: string, env: NodeJS.ProcessEnv = process.env): void {\n\tfor (const name of appEnvNames(suffix)) {\n\t\tenv[name] = value;\n\t}\n}\n\n/** Remove an app env var under both the current and legacy names. */\nexport function deleteAppEnv(suffix: string, env: NodeJS.ProcessEnv = process.env): void {\n\tfor (const name of appEnvNames(suffix)) {\n\t\tdelete env[name];\n\t}\n}\n\nexport function expandTildePath(path: string): string {\n\treturn normalizePath(path);\n}\n\n/**\n * Where a shared session can be viewed.\n *\n * Upstream pointed this at their own viewer. We have none, so the gist itself\n * is the destination — it renders the exported HTML and is the thing the user\n * actually owns. Set NEXUS_SHARE_VIEWER_URL to front it with a viewer.\n */\nexport function getShareViewerUrl(gistId: string): string {\n\tconst baseUrl = readAppEnv(\"SHARE_VIEWER_URL\");\n\treturn baseUrl ? `${baseUrl}#${gistId}` : `https://gist.github.com/${gistId}`;\n}\n\n// =============================================================================\n// User Config Paths (~/.pi/agent/*)\n// =============================================================================\n\n/** Get the agent config directory (e.g., ~/.pi/agent/) */\nexport function getAgentDir(): string {\n\tconst envDir = readAppEnv(\"CODING_AGENT_DIR\");\n\tif (envDir) {\n\t\treturn expandTildePath(envDir);\n\t}\n\treturn join(homedir(), CONFIG_DIR_NAME, \"agent\");\n}\n\n/** Get path to user's custom themes directory */\nexport function getCustomThemesDir(): string {\n\treturn join(getAgentDir(), \"themes\");\n}\n\n/** Get path to models.json */\nexport function getModelsPath(): string {\n\treturn join(getAgentDir(), \"models.json\");\n}\n\n/** Get path to auth.json */\nexport function getAuthPath(): string {\n\treturn join(getAgentDir(), \"auth.json\");\n}\n\n/** Get path to settings.json */\nexport function getSettingsPath(): string {\n\treturn join(getAgentDir(), \"settings.json\");\n}\n\n/** Get path to tools directory */\nexport function getToolsDir(): string {\n\treturn join(getAgentDir(), \"tools\");\n}\n\n/** Get path to managed binaries directory (fd, rg) */\nexport function getBinDir(): string {\n\treturn join(getAgentDir(), \"bin\");\n}\n\n/** Get path to prompt templates directory */\nexport function getPromptsDir(): string {\n\treturn join(getAgentDir(), \"prompts\");\n}\n\n/** Get path to sessions directory */\nexport function getSessionsDir(): string {\n\treturn join(getAgentDir(), \"sessions\");\n}\n\n/** Get path to debug log file */\nexport function getDebugLogPath(): string {\n\treturn join(getAgentDir(), `${APP_NAME}-debug.log`);\n}\n"]}
package/dist/config.js CHANGED
@@ -370,7 +370,7 @@ catch (e) {
370
370
  throw e;
371
371
  }
372
372
  const piConfigName = pkg.piConfig?.name;
373
- export const PACKAGE_NAME = pkg.name || "@akonlabs/nexus-cli";
373
+ export const PACKAGE_NAME = pkg.name || "@akon-labs/nexus-cli";
374
374
  export const APP_NAME = piConfigName || "nexus";
375
375
  export const APP_TITLE = piConfigName ? APP_NAME : "Nexus";
376
376
  export const CONFIG_DIR_NAME = pkg.piConfig?.configDir || ".nexus";