aegiscode 5.2.32 → 6.0.0

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.
@@ -0,0 +1,197 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * `aegiscode` — the AEGIS terminal host.
6
+ *
7
+ * Third host over the same two shared pieces the other two use: the thin
8
+ * transport (client/aegis.js) and the tool registry (mcp/tools.js). The MCP
9
+ * host answers a coding agent's tool calls; the desktop is a window; this is
10
+ * the shell. None of them owns a brain — routing, tiers, memory and billing all
11
+ * stay behind aegiscloud.org.
12
+ *
13
+ * This file is argument parsing and process lifecycle only. Everything
14
+ * testable lives in ../src/app.js.
15
+ */
16
+
17
+ const path = require('node:path');
18
+
19
+ const HELP = `aegiscode — AEGIS in your shell.
20
+
21
+ Usage:
22
+ aegiscode interactive session
23
+ aegiscode "question" one-shot, then exit
24
+ aegiscode -p "question" same, explicit
25
+ echo "q" | aegiscode -p - read the prompt from stdin
26
+
27
+ Options:
28
+ -m, --model <id> pin a model id (see /models; default: server choice)
29
+ --base <url> API base (default $AEGIS_API_BASE or aegiscloud.org)
30
+ --key <key> API key for this run (prefer $AEGIS_API_KEY)
31
+ --json with -p: emit JSON instead of text
32
+ --no-stream buffer the answer instead of streaming it
33
+ --max-tokens <n> output ceiling hint
34
+ --light light theme
35
+ --width <cols> force a render width (useful for piping/logs)
36
+ -h, --help this text
37
+ -v, --version print the version
38
+
39
+ In-session: type /help for commands, /quit to exit, esc/ctrl+c to interrupt a
40
+ running call. Plain text is a prompt (identical to /ask).
41
+ `;
42
+
43
+ function parseArgs(argv) {
44
+ const opts = {
45
+ model: null,
46
+ base: null,
47
+ key: null,
48
+ json: false,
49
+ stream: true,
50
+ maxTokens: undefined,
51
+ light: false,
52
+ width: null,
53
+ prompt: null,
54
+ help: false,
55
+ version: false,
56
+ };
57
+ const rest = [];
58
+
59
+ for (let i = 0; i < argv.length; i++) {
60
+ const a = argv[i];
61
+ const next = () => {
62
+ const v = argv[++i];
63
+ if (v === undefined) throw new Error(`${a} needs a value`);
64
+ return v;
65
+ };
66
+ switch (a) {
67
+ case '-h':
68
+ case '--help':
69
+ opts.help = true;
70
+ break;
71
+ case '-v':
72
+ case '--version':
73
+ opts.version = true;
74
+ break;
75
+ case '-p':
76
+ case '--print':
77
+ // `-p` takes an optional prompt; a bare `-p` means "read stdin".
78
+ opts.prompt = argv[i + 1] && !argv[i + 1].startsWith('-') ? next() : '-';
79
+ break;
80
+ case '-m':
81
+ case '--model':
82
+ opts.model = next();
83
+ break;
84
+ case '--base':
85
+ opts.base = next();
86
+ break;
87
+ case '--key':
88
+ opts.key = next();
89
+ break;
90
+ case '--json':
91
+ opts.json = true;
92
+ break;
93
+ case '--no-stream':
94
+ opts.stream = false;
95
+ break;
96
+ case '--stream':
97
+ opts.stream = true;
98
+ break;
99
+ case '--max-tokens':
100
+ opts.maxTokens = Number(next());
101
+ break;
102
+ case '--light':
103
+ opts.light = true;
104
+ break;
105
+ case '--width':
106
+ opts.width = Number(next());
107
+ break;
108
+ default:
109
+ if (a.startsWith('-') && a !== '-') throw new Error(`unknown option: ${a}`);
110
+ rest.push(a);
111
+ }
112
+ }
113
+ if (!opts.prompt && rest.length) opts.prompt = rest.join(' ');
114
+ return opts;
115
+ }
116
+
117
+ function readStdin() {
118
+ return new Promise((resolve) => {
119
+ let buf = '';
120
+ process.stdin.setEncoding('utf8');
121
+ process.stdin.on('data', (c) => {
122
+ buf += c;
123
+ });
124
+ process.stdin.on('end', () => resolve(buf.trim()));
125
+ });
126
+ }
127
+
128
+ async function main(argv = process.argv.slice(2)) {
129
+ let opts;
130
+ try {
131
+ opts = parseArgs(argv);
132
+ } catch (e) {
133
+ process.stderr.write(`aegiscode: ${e.message}\n\n${HELP}`);
134
+ return 2;
135
+ }
136
+
137
+ if (opts.help) {
138
+ process.stdout.write(HELP);
139
+ return 0;
140
+ }
141
+
142
+ const pkg = require(path.join(__dirname, '..', 'package.json'));
143
+ if (opts.version) {
144
+ process.stdout.write(pkg.version + '\n');
145
+ return 0;
146
+ }
147
+
148
+ // Apply the flags that the shared client reads from the environment, before
149
+ // anything constructs it.
150
+ if (opts.base) process.env.AEGIS_API_BASE = opts.base;
151
+ if (opts.key) process.env.AEGIS_API_KEY = opts.key;
152
+
153
+ const { createApp } = require('../src/app.js');
154
+
155
+ let prompt = opts.prompt;
156
+ if (prompt === '-') {
157
+ if (process.stdin.isTTY) {
158
+ process.stderr.write('aegiscode: -p - expects a prompt on stdin\n');
159
+ return 2;
160
+ }
161
+ prompt = await readStdin();
162
+ if (!prompt) {
163
+ process.stderr.write('aegiscode: empty stdin\n');
164
+ return 2;
165
+ }
166
+ }
167
+
168
+ const app = createApp({
169
+ model: opts.model,
170
+ stream: opts.stream,
171
+ light: opts.light,
172
+ maxTokens: opts.maxTokens,
173
+ width: opts.width ? () => opts.width : undefined,
174
+ interactive: !prompt && Boolean(process.stdin.isTTY),
175
+ });
176
+
177
+ if (prompt) return app.runOnce(prompt, { json: opts.json });
178
+
179
+ if (!process.stdin.isTTY) {
180
+ process.stderr.write(
181
+ 'aegiscode: no terminal and no prompt. Use `aegiscode -p "question"` or pipe a prompt in.\n'
182
+ );
183
+ return 2;
184
+ }
185
+ return app.runInteractive();
186
+ }
187
+
188
+ if (require.main === module) {
189
+ main()
190
+ .then((code) => process.exit(code || 0))
191
+ .catch((e) => {
192
+ process.stderr.write(`aegiscode: ${e && e.message ? e.message : e}\n`);
193
+ process.exit(1);
194
+ });
195
+ }
196
+
197
+ module.exports = { main, parseArgs, HELP };
package/package.json CHANGED
@@ -1,98 +1,45 @@
1
1
  {
2
2
  "name": "aegiscode",
3
- "version": "5.2.32",
4
- "description": "AEGIS CLI — AI-powered coding assistant",
5
- "type": "module",
6
- "bin": {
7
- "aegis": "bin/cli.js",
8
- "aegis-cli": "bin/cli.js",
9
- "aegiscli": "bin/cli.js",
10
- "aegiscode": "bin/cli.js"
11
- },
12
- "scripts": {
13
- "preinstall": "node scripts/ensure-node-version.mjs",
14
- "build": "node esbuild.mjs",
15
- "build:publish": "npm run build && node scripts/make-bin.mjs",
16
- "dev": "NODE_NO_WARNINGS=1 tsx src/main.tsx",
17
- "start": "node --no-deprecation dist/main.js",
18
- "typecheck": "tsc --noEmit",
19
- "test:prompts": "tsx src/prompts/test.ts",
20
- "test:tools": "tsx src/tools/test.ts",
21
- "test:pipeline": "tsx src/tools/execution/test.ts",
22
- "test:context": "tsx src/context/test.ts",
23
- "test:mcp": "tsx src/mcp/test.ts",
24
- "test:store": "tsx src/store/test.ts && tsx src/store/streaming-buffer.test.ts",
25
- "test:estimate": "tsx src/ui/components/layout/line-estimate.test.ts"
3
+ "productName": "AEGIS Code",
4
+ "version": "6.0.0",
5
+ "description": "aegiscode — the command-line version of AEGIS Desktop. The shared tool surface in your shell, over the same thin transport and tool registry as the MCP plugin and the desktop app. Ships transport + UI only; no brain.",
6
+ "author": {
7
+ "name": "AEGIS Code",
8
+ "email": "nborneklint@gmail.com"
26
9
  },
27
- "keywords": [
28
- "cli",
29
- "ai",
30
- "coding-agent",
31
- "llm",
32
- "openai",
33
- "gpt",
34
- "claude",
35
- "copilot",
36
- "assistant",
37
- "terminal",
38
- "developer-tools"
39
- ],
40
- "author": "Niklas Borneklint",
41
10
  "license": "MIT",
11
+ "homepage": "https://aegiscloud.org",
42
12
  "repository": {
43
13
  "type": "git",
44
- "url": "git+https://github.com/aegisinfo/aegiscode.git"
14
+ "url": "git+https://github.com/aegisinfo/aegiscode-plugin.git",
15
+ "directory": "cli"
45
16
  },
46
- "homepage": "https://github.com/aegisinfo/aegiscode#readme",
47
- "bugs": {
48
- "url": "https://github.com/aegisinfo/aegiscode/issues"
17
+ "bin": {
18
+ "aegiscode": "bin/aegiscode.js"
49
19
  },
50
20
  "files": [
51
- "bin/",
52
- "scripts/",
53
- "README.md",
54
- "LICENSE"
21
+ "bin",
22
+ "src",
23
+ "scripts",
24
+ "vendor",
25
+ "README.md"
55
26
  ],
56
27
  "engines": {
57
28
  "node": ">=18.0.0"
58
29
  },
59
- "dependencies": {
60
- "@huggingface/transformers": "^4.2.0",
61
- "@modelcontextprotocol/sdk": "^1.30.0",
62
- "chalk": "^6.0.0",
63
- "dotenv": "^17.4.2",
64
- "fuse.js": "^7.4.1",
65
- "glob": "^13.0.0",
66
- "ink": "^7.1.1",
67
- "ink-text-input": "^6.0.0",
68
- "js-tiktoken": "^1.0.21",
69
- "lowlight": "^3.3.0",
70
- "minimatch": "^10.2.6",
71
- "nanoid": "^6.0.0",
72
- "openai": "^7.3.0",
73
- "react": "^19.2.8",
74
- "react-dom": "^19.2.8",
75
- "sql.js": "^1.14.1",
76
- "string-width": "^8.2.2",
77
- "uuid": "^14.0.0",
78
- "yaml": "^2.8.2",
79
- "yargs": "^18.1.0",
80
- "zod": "^4.4.3",
81
- "zustand": "^5.0.14"
82
- },
83
- "devDependencies": {
84
- "@types/node": "^26.1.2",
85
- "@types/react": "^19.2.18",
86
- "@types/sql.js": "^1.4.11",
87
- "@types/yargs": "^17.0.35",
88
- "esbuild": "^0.28.0",
89
- "tsx": "^4.23.4",
90
- "typescript": "^7.0.2"
30
+ "scripts": {
31
+ "start": "node bin/aegiscode.js",
32
+ "predist": "node scripts/predist.mjs",
33
+ "prepublishOnly": "npm run predist",
34
+ "check": "node --check bin/aegiscode.js && node --check src/theme.js && node --check src/art.js && node --check src/screen.js && node --check src/format.js && node --check src/render.js && node --check src/commands.js && node --check src/deps.js && node --check src/app.js && node --check scripts/predist.mjs",
35
+ "test": "for f in ../../test/cli-*.test.mjs; do node \"$f\" || exit 1; done"
91
36
  },
92
- "overrides": {
93
- "string-width": "^8.2.2",
94
- "slice-ansi": "^7.1.2",
95
- "cli-truncate": "^4.0.0",
96
- "cli-boxes": "^3.0.0"
97
- }
37
+ "keywords": [
38
+ "aegis",
39
+ "aegiscode",
40
+ "aegiscloud",
41
+ "cli",
42
+ "terminal",
43
+ "llm"
44
+ ]
98
45
  }
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Renders a canned session to stdout — banner, a prompt, a streamed answer with
4
+ * markdown, the accounting line, and the status bar.
5
+ *
6
+ * No network and no key: this exists so the look can be reviewed (and pasted
7
+ * into docs) without a live account, and so a regression in the layout is
8
+ * visible to a human, not only to the width assertions in test/cli-render.
9
+ *
10
+ * node cli/scripts/demo.mjs [--light] [--width 84] [--plain]
11
+ */
12
+
13
+ import { createRequire } from 'node:module';
14
+ import path from 'node:path';
15
+ import { fileURLToPath } from 'node:url';
16
+
17
+ const require = createRequire(import.meta.url);
18
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
19
+ const src = path.join(__dirname, '..', 'src');
20
+
21
+ const render = require(path.join(src, 'render.js'));
22
+ const { stripAnsi } = require(path.join(src, 'screen.js'));
23
+ const art = require(path.join(src, 'art.js'));
24
+ // Read the version rather than hardcoding it — a demo that reports a stale
25
+ // version is the kind of drift nobody notices until it is in the docs.
26
+ const { version } = require(path.join(__dirname, '..', 'package.json'));
27
+
28
+ const argv = process.argv.slice(2);
29
+ const flag = (name, fallback) => {
30
+ const i = argv.indexOf(name);
31
+ return i === -1 ? fallback : argv[i + 1];
32
+ };
33
+ const width = Number(flag('--width', 0)) || Math.min(96, Math.max(48, process.stdout.columns || 84));
34
+ const ctx = { light: argv.includes('--light') };
35
+ const plain = argv.includes('--plain');
36
+
37
+ const out = [];
38
+ const push = (lines) => out.push(...(Array.isArray(lines) ? lines : [lines]));
39
+
40
+ push(
41
+ render.renderBanner(ctx, {
42
+ width,
43
+ version,
44
+ model: 'nexus-brain',
45
+ base: 'https://aegiscloud.org',
46
+ key: 'aegis_••••4f2a',
47
+ stream: true,
48
+ })
49
+ );
50
+ push('');
51
+ push(render.renderTurn(ctx, { role: 'user', text: 'summarise what changed in the token accounting' }, width));
52
+ push(
53
+ render.renderTurn(
54
+ ctx,
55
+ {
56
+ role: 'assistant',
57
+ text: [
58
+ 'Three things changed, and one of them was costing you money:',
59
+ '',
60
+ '- the pool **merges** worker usage instead of overwriting it',
61
+ '- cache reads and writes are billed, not ignored',
62
+ '- a zero-token call no longer refunds its reservation',
63
+ '',
64
+ '```',
65
+ 'merge_usage({input_tokens: 1250}, {output_tokens: 312})',
66
+ '=> 1562 total',
67
+ '```',
68
+ '',
69
+ 'Use `/balance` to see tokens beside € on every row.',
70
+ ].join('\n'),
71
+ meta: {
72
+ model: 'nexus-brain',
73
+ tokens: 1562,
74
+ usage: { input: 1250, output: 312 },
75
+ eur: 0.0007,
76
+ ms: 4200,
77
+ calls: 4,
78
+ },
79
+ },
80
+ width
81
+ )
82
+ );
83
+ push('');
84
+ push(render.renderWorking(ctx, { tick: 3, verb: 'Consulting', elapsedMs: 2100 }));
85
+ push(render.renderStatus(ctx, { model: 'nexus-brain', tokens: 1562, spend: 0.0007, mode: 'stream' }, width));
86
+
87
+ process.stdout.write((plain ? out.map(stripAnsi) : out).join('\n') + '\n\n');
88
+ if (!plain) {
89
+ process.stdout.write(
90
+ stripAnsi(`${art.WORDMARK} — ${out.length} lines rendered at ${width} cols${ctx.light ? ' (light)' : ''}\n`)
91
+ );
92
+ }
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Pre-publish staging for the `aegiscode` package.
4
+ *
5
+ * The CLI is a host, not a fork: it consumes the repo's shared modules
6
+ * (`client/aegis.js` transport, `mcp/tools.js` registry, the desktop's pure
7
+ * `usage.js` mapping) rather than copies of them. npm can only publish a
8
+ * package's own directory, so those files are staged into `cli/vendor/` here,
9
+ * at publish time, keeping the repo's relative shape:
10
+ *
11
+ * cli/ repo/
12
+ * vendor/mcp/tools.js ≡ mcp/tools.js
13
+ * vendor/client/*.js ≡ client/*.js
14
+ * vendor/desktop/... ≡ desktop/renderer/usage.js
15
+ *
16
+ * The shape matters: `mcp/tools.js` requires `../client/foreign-memory.js`, and
17
+ * because the staged tree mirrors the repo, that path resolves *inside* the
18
+ * vendor tree with no rewrite in either file.
19
+ *
20
+ * `src/deps.js` resolves in-repo paths first and falls back to `vendor/`, so a
21
+ * source checkout and an installed package run identical code.
22
+ */
23
+
24
+ import fs from 'node:fs';
25
+ import path from 'node:path';
26
+ import { fileURLToPath } from 'node:url';
27
+ import { createRequire } from 'node:module';
28
+
29
+ const require = createRequire(import.meta.url);
30
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
31
+
32
+ const CLI_DIR = path.join(__dirname, '..');
33
+ const REPO_DIR = path.join(CLI_DIR, '..');
34
+ const VENDOR = path.join(CLI_DIR, 'vendor');
35
+
36
+ /** repo-relative -> staged location (same relative shape, under vendor/) */
37
+ const FILES = [
38
+ 'client/aegis.js',
39
+ 'client/foreign-memory.js',
40
+ 'mcp/tools.js',
41
+ 'desktop/renderer/usage.js',
42
+ ];
43
+
44
+ function main() {
45
+ fs.rmSync(VENDOR, { recursive: true, force: true });
46
+ const staged = [];
47
+
48
+ for (const rel of FILES) {
49
+ const from = path.join(REPO_DIR, rel);
50
+ if (!fs.existsSync(from)) {
51
+ console.error(`predist: missing shared module: ${rel} (expected at ${from})`);
52
+ process.exit(1);
53
+ }
54
+ const to = path.join(VENDOR, rel);
55
+ fs.mkdirSync(path.dirname(to), { recursive: true });
56
+ fs.copyFileSync(from, to);
57
+ const same = fs.readFileSync(from).equals(fs.readFileSync(to));
58
+ if (!same) {
59
+ console.error(`predist: staged copy differs from source: ${rel}`);
60
+ process.exit(1);
61
+ }
62
+ staged.push(`${rel} -> vendor/${rel}`);
63
+ }
64
+
65
+ // Prove the staged tree stands on its own: the registry must load and
66
+ // resolve its own dependencies from inside vendor/, not from the repo.
67
+ const toolsPath = path.join(VENDOR, 'mcp', 'tools.js');
68
+ delete require.cache[require.resolve(toolsPath)];
69
+ const { createTools } = require(toolsPath);
70
+ const tools = createTools({
71
+ apiBase: 'http://127.0.0.1',
72
+ apiKey: 'predist-probe',
73
+ randomUUID: () => 'id',
74
+ });
75
+ const count = tools.toolList().length;
76
+ if (count === 0) {
77
+ console.error('predist: staged registry exposes no tools');
78
+ process.exit(1);
79
+ }
80
+
81
+ console.log(`predist: staged ${staged.length} shared modules into cli/vendor/`);
82
+ for (const s of staged) console.log(` ${s}`);
83
+ console.log(`predist: staged registry loads standalone (${count} tools)`);
84
+ }
85
+
86
+ main();