@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.
- package/CHANGELOG.md +16 -5316
- package/README.md +30 -26
- package/dist/cli.js +90 -62
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -1
- package/dist/config.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +18 -63
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/extensions/gitnexus/gateway.d.ts +2 -0
- package/dist/extensions/gitnexus/gateway.d.ts.map +1 -1
- package/dist/extensions/gitnexus/gateway.js +22 -4
- package/dist/extensions/gitnexus/gateway.js.map +1 -1
- package/dist/extensions/gitnexus/index.d.ts.map +1 -1
- package/dist/extensions/gitnexus/index.js +21 -3
- package/dist/extensions/gitnexus/index.js.map +1 -1
- package/dist/extensions/llama/index.d.ts.map +1 -1
- package/dist/extensions/llama/index.js +1 -1
- package/dist/extensions/llama/index.js.map +1 -1
- package/dist/extensions/web-access.d.ts +8 -1
- package/dist/extensions/web-access.d.ts.map +1 -1
- package/dist/extensions/web-access.js +48 -2
- package/dist/extensions/web-access.js.map +1 -1
- package/dist/index.js +93 -65
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +2 -2
- package/dist/migrations.js.map +1 -1
- package/dist/rpc-entry.js +90 -62
- package/dist/vendor/agent-core.js +6805 -0
- package/dist/vendor/ai.js +12407 -0
- package/dist/vendor/tui.js +11645 -0
- package/dist/vendor/web-access.js +93103 -0
- package/docs/compaction.md +14 -14
- package/docs/containerization.md +1 -1
- package/docs/custom-provider.md +17 -17
- package/docs/development.md +2 -2
- package/docs/environment-variables.md +1 -1
- package/docs/extensions.md +44 -44
- package/docs/index.md +5 -11
- package/docs/json.md +4 -4
- package/docs/llama-cpp.md +2 -2
- package/docs/models.md +3 -3
- package/docs/packages.md +6 -6
- package/docs/providers.md +13 -13
- package/docs/quickstart.md +19 -13
- package/docs/rpc.md +1 -1
- package/docs/sdk.md +28 -28
- package/docs/security.md +1 -1
- package/docs/session-format.md +6 -6
- package/docs/settings.md +1 -1
- package/docs/termux.md +1 -1
- package/docs/themes.md +2 -2
- package/docs/tui.md +20 -20
- package/docs/usage.md +5 -4
- package/examples/extensions/README.md +2 -2
- package/examples/extensions/auto-commit-on-exit.ts +1 -1
- package/examples/extensions/bash-spawn-hook.ts +2 -2
- package/examples/extensions/bookmark.ts +1 -1
- package/examples/extensions/border-status-editor.ts +3 -3
- package/examples/extensions/built-in-tool-renderer.ts +3 -3
- package/examples/extensions/claude-rules.ts +1 -1
- package/examples/extensions/commands.ts +1 -1
- package/examples/extensions/confirm-destructive.ts +1 -1
- package/examples/extensions/custom-compaction.ts +3 -3
- package/examples/extensions/custom-footer.ts +3 -3
- package/examples/extensions/custom-header.ts +2 -2
- package/examples/extensions/custom-provider-anthropic/index.ts +2 -2
- package/examples/extensions/custom-provider-gitlab-duo/index.ts +2 -2
- package/examples/extensions/custom-provider-gitlab-duo/test.ts +1 -1
- package/examples/extensions/dirty-repo-guard.ts +1 -1
- package/examples/extensions/doom-overlay/doom-component.ts +2 -2
- package/examples/extensions/doom-overlay/doom-keys.ts +1 -1
- package/examples/extensions/doom-overlay/index.ts +1 -1
- package/examples/extensions/dynamic-resources/dynamic.json +1 -1
- package/examples/extensions/dynamic-resources/index.ts +1 -1
- package/examples/extensions/dynamic-tools.ts +1 -1
- package/examples/extensions/entry-renderer.ts +2 -2
- package/examples/extensions/event-bus.ts +1 -1
- package/examples/extensions/file-trigger.ts +1 -1
- package/examples/extensions/git-checkpoint.ts +1 -1
- package/examples/extensions/git-merge-and-resolve.ts +1 -1
- package/examples/extensions/github-issue-autocomplete.ts +2 -2
- package/examples/extensions/gondolin/index.ts +2 -2
- package/examples/extensions/handoff.ts +4 -4
- package/examples/extensions/hello.ts +2 -2
- package/examples/extensions/hidden-thinking-label.ts +1 -1
- package/examples/extensions/inline-bash.ts +1 -1
- package/examples/extensions/input-transform-streaming.ts +1 -1
- package/examples/extensions/input-transform.ts +1 -1
- package/examples/extensions/interactive-shell.ts +1 -1
- package/examples/extensions/kimi-deferred-tools.ts +1 -1
- package/examples/extensions/mac-system-theme.ts +1 -1
- package/examples/extensions/message-renderer.ts +2 -2
- package/examples/extensions/minimal-mode.ts +3 -3
- package/examples/extensions/modal-editor.ts +2 -2
- package/examples/extensions/model-status.ts +1 -1
- package/examples/extensions/notify.ts +1 -1
- package/examples/extensions/overlay-qa-tests.ts +5 -5
- package/examples/extensions/overlay-test.ts +2 -2
- package/examples/extensions/permission-gate.ts +1 -1
- package/examples/extensions/pirate.ts +1 -1
- package/examples/extensions/plan-mode/index.ts +4 -4
- package/examples/extensions/preset.ts +4 -4
- package/examples/extensions/project-trust.ts +1 -1
- package/examples/extensions/prompt-customizer.ts +1 -1
- package/examples/extensions/protected-paths.ts +1 -1
- package/examples/extensions/provider-payload.ts +1 -1
- package/examples/extensions/qna.ts +3 -3
- package/examples/extensions/question.ts +2 -2
- package/examples/extensions/questionnaire.ts +2 -2
- package/examples/extensions/rainbow-editor.ts +1 -1
- package/examples/extensions/reload-runtime.ts +1 -1
- package/examples/extensions/rpc-demo.ts +1 -1
- package/examples/extensions/sandbox/index.ts +2 -2
- package/examples/extensions/send-user-message.ts +1 -1
- package/examples/extensions/session-name.ts +1 -1
- package/examples/extensions/shutdown-command.ts +1 -1
- package/examples/extensions/snake.ts +2 -2
- package/examples/extensions/space-invaders.ts +2 -2
- package/examples/extensions/ssh.ts +2 -2
- package/examples/extensions/status-line.ts +1 -1
- package/examples/extensions/structured-output.ts +2 -2
- package/examples/extensions/subagent/agents.ts +1 -1
- package/examples/extensions/subagent/index.ts +5 -5
- package/examples/extensions/summarize.ts +4 -4
- package/examples/extensions/system-prompt-header.ts +1 -1
- package/examples/extensions/tic-tac-toe.ts +3 -3
- package/examples/extensions/timed-confirm.ts +1 -1
- package/examples/extensions/titlebar-spinner.ts +1 -1
- package/examples/extensions/todo.ts +3 -3
- package/examples/extensions/tool-override.ts +2 -2
- package/examples/extensions/tools.ts +3 -3
- package/examples/extensions/trigger-compact.ts +1 -1
- package/examples/extensions/truncated-tool.ts +3 -3
- package/examples/extensions/widget-placement.ts +1 -1
- package/examples/extensions/with-deps/index.ts +1 -1
- package/examples/extensions/working-indicator.ts +1 -1
- package/examples/extensions/working-message-test.ts +1 -1
- package/examples/rpc-extension-ui.ts +1 -1
- package/examples/sdk/01-minimal.ts +1 -1
- package/examples/sdk/02-custom-model.ts +1 -1
- package/examples/sdk/03-custom-prompt.ts +1 -6
- package/examples/sdk/04-skills.ts +1 -1
- package/examples/sdk/05-tools.ts +1 -1
- package/examples/sdk/06-extensions.ts +2 -7
- package/examples/sdk/07-context-files.ts +1 -6
- package/examples/sdk/08-prompt-templates.ts +1 -1
- package/examples/sdk/09-api-keys-and-oauth.ts +1 -1
- package/examples/sdk/10-settings.ts +1 -1
- package/examples/sdk/11-sessions.ts +1 -1
- package/examples/sdk/12-full-control.ts +2 -2
- package/examples/sdk/13-session-runtime.ts +1 -1
- package/examples/sdk/README.md +2 -2
- package/npm-shrinkwrap.json +2 -26
- 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 @
|
|
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
|
-
|
|
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
|
-
|
|
49
|
+
Inside a session, `/login` signs in to GitNexus again. Configure the model provider separately:
|
|
53
50
|
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
/
|
|
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.
|
|
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 `/
|
|
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
|
|
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:**
|
|
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.
|
|
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-
|
|
414
|
-
"keywords": ["
|
|
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 "@
|
|
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
|
-
**
|
|
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
|
-
| `
|
|
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
|
-
-
|
|
675
|
-
-
|
|
676
|
-
-
|
|
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 || "@
|
|
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
|
-
//
|
|
64432
|
-
//
|
|
64433
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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/
|
|
67457
|
-
var EXTENSIONS_DOC_URL = "https://github.com/
|
|
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");
|
package/dist/config.d.ts.map
CHANGED
|
@@ -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":"AAcA;;;GAGG;AACH,eAAO,MAAM,WAAW,SACqF,CAAC;AAE9G,gEAAgE;AAChE,eAAO,MAAM,YAAY,SAAyB,CAAC;AAMnD,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,SAAS,CAAC;AAEvF,UAAU,qBAAqB;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iBAAkB,SAAQ,qBAAqB;IAC/D,KAAK,CAAC,EAAE,qBAAqB,EAAE,CAAC;CAChC;AAED,MAAM,MAAM,uBAAuB,GAAG,MAAM,GAAG;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAgC7F,wBAAgB,mBAAmB,IAAI,aAAa,CAqBnD;AA6ND,wBAAgB,oBAAoB,CACnC,WAAW,EAAE,MAAM,EACnB,UAAU,CAAC,EAAE,MAAM,EAAE,EACrB,mBAAmB,GAAE,uBAAqC,GACxD,iBAAiB,GAAG,SAAS,CAO/B;AAED,wBAAgB,mCAAmC,CAClD,WAAW,EAAE,MAAM,EACnB,UAAU,CAAC,EAAE,MAAM,EAAE,EACrB,mBAAmB,GAAE,uBAAqC,GACxD,MAAM,CAcR;AAED,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAOhE;AAMD;;;;;GAKG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAyBtC;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAQrC;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAO7C;AAED,+BAA+B;AAC/B,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED,4BAA4B;AAC5B,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,iCAAiC;AACjC,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAED,qCAAqC;AACrC,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAwBD,eAAO,MAAM,YAAY,EAAE,MAA2C,CAAC;AACvE,eAAO,MAAM,QAAQ,EAAE,MAAgC,CAAC;AACxD,eAAO,MAAM,SAAS,EAAE,MAA0C,CAAC;AACnE,eAAO,MAAM,eAAe,EAAE,MAA4C,CAAC;AAC3E,eAAO,MAAM,OAAO,EAAE,MAA+B,CAAC;AAGtD,eAAO,MAAM,aAAa,QAA+C,CAAC;AAC1E,eAAO,MAAM,eAAe,QAAuD,CAAC;AAgBpF,2DAA2D;AAC3D,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAIpD;AAED,kFAAkF;AAClF,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,GAAG,SAAS,CAOnG;AAED,kEAAkE;AAClE,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,IAAI,CAInG;AAED,qEAAqE;AACrE,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,IAAI,CAIvF;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAGxD;AAMD,0DAA0D;AAC1D,wBAAgB,WAAW,IAAI,MAAM,CAMpC;AAED,iDAAiD;AACjD,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED,8BAA8B;AAC9B,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,4BAA4B;AAC5B,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAED,gCAAgC;AAChC,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED,kCAAkC;AAClC,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAED,sDAAsD;AACtD,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED,6CAA6C;AAC7C,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,qCAAqC;AACrC,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,iCAAiC;AACjC,wBAAgB,eAAe,IAAI,MAAM,CAExC","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 || "@
|
|
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";
|