0agent 1.0.41 → 1.0.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +226 -89
  2. package/bin/0agent.js +46 -20
  3. package/bin/chat.js +172 -10
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -2,141 +2,216 @@
2
2
 
3
3
  **A persistent, learning AI agent that runs on your machine.**
4
4
 
5
+ > Runs a local daemon. Learns from every task. Remembers everything. Gets better over time.
6
+
5
7
  ```bash
6
8
  npx 0agent@latest
7
9
  ```
8
10
 
9
- That's it. 0agent installs, walks you through a 4-step setup, and starts a daemon that gets smarter with every task you run.
11
+ [![npm](https://img.shields.io/npm/v/0agent?color=black&label=npm)](https://www.npmjs.com/package/0agent)
12
+ [![license](https://img.shields.io/badge/license-Apache%202.0-black)](LICENSE)
13
+ [![node](https://img.shields.io/badge/node-%E2%89%A520-black)](https://nodejs.org)
10
14
 
11
15
  ---
12
16
 
13
- ## What it does
17
+ ## What is this?
14
18
 
15
- ```bash
16
- # Sprint workflow
17
- 0agent /office-hours "I want to build a Slack bot"
18
- 0agent /plan-ceo-review
19
- 0agent /plan-eng-review
20
- 0agent /build
21
- 0agent /review
22
- 0agent /qa --url https://staging.myapp.com
23
- 0agent /ship
24
- 0agent /retro
25
-
26
- # One-off tasks
27
- 0agent /research "Acme Corp Series B funding"
28
- 0agent /debug "TypeError at auth.ts:47"
29
- 0agent /test-writer src/payments/
30
- 0agent /refactor src/api/routes.ts
31
-
32
- # Plain language
33
- 0agent run "fix the auth bug Marcus reported"
34
- 0agent run "research Acme Corp and draft a follow-up email to Sarah"
35
-
36
- # Entity-scoped (learns who you are)
37
- 0agent run "pull auth metrics" --entity sarah_chen
38
- ```
19
+ 0agent is a CLI agent that runs as a background daemon on your machine. It executes real tasks — shell commands, file operations, web search, browser automation — using your API key, and learns from every outcome via a weighted knowledge graph.
39
20
 
40
- ---
21
+ Unlike chat-based AI tools, 0agent:
41
22
 
42
- ## How it learns
23
+ - **Persists** runs in the background, remembers past sessions
24
+ - **Learns** — every task outcome updates edge weights in a graph; plan selection improves over time
25
+ - **Executes** — actually runs commands, writes files, searches the web, opens browsers
26
+ - **Syncs** — optionally backs up the knowledge graph to a private GitHub repo
43
27
 
44
- Every time you run a task, 0agent records which strategy it chose and whether it worked. After 50 interactions, it converges to your optimal workflow — measurably, provably, via a weighted knowledge graph.
28
+ ---
45
29
 
46
- - Edge weights start at 0.5 (neutral)
47
- - Positive outcomes push them toward 1.0
48
- - Negative outcomes push them toward 0.0
49
- - After 100 traces, plan selection is noticeably better
30
+ ## Quick start
50
31
 
51
- ---
32
+ ```bash
33
+ npx 0agent@latest
34
+ ```
52
35
 
53
- ## Requirements
36
+ The wizard asks for:
37
+ 1. LLM provider + API key (Anthropic, OpenAI, xAI, Gemini, or local Ollama)
38
+ 2. GitHub repo for memory backup (optional, uses `gh` CLI if installed)
39
+ 3. Workspace folder (where the agent creates files — default: `~/0agent-workspace`)
40
+ 4. Embedding provider (for semantic memory search)
54
41
 
55
- - **Node.js** 20
56
- - **API key** for Anthropic, OpenAI, or a local Ollama instance
57
- - **Docker** (optional but recommended — enables sandboxed subagents)
42
+ After setup, the chat TUI opens automatically. No manual steps.
58
43
 
59
44
  ---
60
45
 
61
- ## Install
46
+ ## Usage
47
+
48
+ ### Interactive chat
62
49
 
63
50
  ```bash
64
- # One-liner
51
+ 0agent # open chat (starts daemon if needed)
65
52
  npx 0agent@latest
53
+ ```
66
54
 
67
- # Global install
68
- npm install -g 0agent
69
- 0agent init
55
+ ```
56
+ 0agent anthropic/claude-sonnet-4-6
57
+ Type a task, or /help for commands.
70
58
 
71
- # Or via brew (coming soon)
72
- brew install 0agent
59
+ make a website for my coffee shop and deploy it locally
60
+ build a REST API in Go with auth
61
+ › research my competitor's pricing and draft a response strategy
73
62
  ```
74
63
 
75
- ---
64
+ Type while the agent works — messages queue automatically and run one after another.
76
65
 
77
- ## Local development
66
+ ### Slash skills
78
67
 
79
68
  ```bash
80
- git clone https://github.com/0agent-oss/0agent
81
- cd 0agent
82
- pnpm install
83
- pnpm build
84
-
85
- # Run the wizard
86
- node bin/0agent.js init
69
+ # Software engineering
70
+ 0agent /review # code review current branch
71
+ 0agent /build # run build, fix errors
72
+ 0agent /qa # generate and run tests
73
+ 0agent /debug # debug a failing test or error
74
+ 0agent /refactor # refactor a file or module
75
+ 0agent /test-writer # write unit tests
76
+ 0agent /doc # generate documentation
77
+
78
+ # Planning & strategy
79
+ 0agent /office-hours "I want to build a payments feature"
80
+ 0agent /plan-eng-review
81
+ 0agent /plan-ceo-review
82
+ 0agent /retro # weekly retrospective
83
+ 0agent /ship # pre-release checklist
87
84
 
88
- # Start daemon
89
- node bin/0agent.js start
85
+ # Research
86
+ 0agent /research "Acme Corp Series B"
87
+ 0agent /security-audit
88
+ ```
90
89
 
91
- # Check status
92
- node bin/0agent.js status
90
+ ### Scheduled tasks
93
91
 
94
- # Open dashboard
95
- open http://localhost:4200
96
92
  ```
93
+ › /schedule add "run /retro" every Friday at 5pm
94
+ › /schedule add "check the build" every day at 9am
95
+ › /schedule list
96
+ ```
97
+
98
+ ### Commands
99
+
100
+ | Command | Description |
101
+ |---|---|
102
+ | `/model` | Show or switch model |
103
+ | `/model add anthropic sk-ant-...` | Add a provider API key |
104
+ | `/key anthropic sk-ant-...` | Update a stored key |
105
+ | `/status` | Daemon health + graph stats |
106
+ | `/skills` | List available skills |
107
+ | `/schedule` | Manage scheduled jobs |
108
+ | `/update` | Update to latest version |
109
+ | `/graph` | Open 3D knowledge graph |
110
+ | `/clear` | Clear screen |
111
+ | `Ctrl+C` | Cancel current task |
97
112
 
98
113
  ---
99
114
 
100
- ## Architecture
115
+ ## How it learns
116
+
117
+ Every task updates a weighted knowledge graph stored in `~/.0agent/graph.db`.
101
118
 
102
119
  ```
103
- You 0agent CLI Daemon (port 4200) → Knowledge Graph
104
- → Subagents (sandboxed)
105
- → MCP Tools (filesystem, browser, shell)
106
- Learning Engine (weight propagation)
120
+ Edge weights: 0.0 ──── 0.5 ──── 1.0
121
+ bad neutral good
122
+
123
+ After each task:
124
+ success → weight += 0.1 × learning_rate
125
+ failure → weight -= 0.1 × learning_rate
126
+ decay → weight → 0.5 over time (forgetting)
107
127
  ```
108
128
 
109
- - **Knowledge graph** — weighted, multimodal. SQLite + HNSW. Persists to `~/.0agent/graph.db`
110
- - **Subagents** sandboxed (Docker/Podman/process). Zero-trust capability tokens. Never write to the graph.
111
- - **MCP** connects to any MCP server. Built-in: filesystem, shell, browser, memory.
112
- - **Skills** 15 built-in YAML-defined skills. Add your own in `~/.0agent/skills/custom/`
113
- - **Self-improvement** — weekly analysis of skill gaps, workflow optimization, prompt refinement.
129
+ After ~50 interactions, plan selection measurably improves. The graph also stores:
130
+ - Discovered facts: URLs, ports, file paths, API endpoints (via `memory_write` tool)
131
+ - Conversation history (last 8 exchanges injected as context)
132
+ - Identity + personality per entity
114
133
 
115
134
  ---
116
135
 
117
- ## Entity nesting
136
+ ## Memory sync
118
137
 
119
- 0agent can learn individual personalities within an organization:
138
+ 0agent can back up its knowledge graph to a private GitHub repo:
120
139
 
121
- ```yaml
122
- # One-time setup in config
123
- entity_nesting:
140
+ ```bash
141
+ # Set up during init, or add manually to ~/.0agent/config.yaml:
142
+ github_memory:
124
143
  enabled: true
125
- visibility_policy:
126
- allow_work_context: true # company sees projects/tasks
127
- allow_personality_profile: false # company can't see communication style
144
+ token: ghp_...
145
+ owner: your-username
146
+ repo: 0agent-memory
128
147
  ```
129
148
 
130
- After 3+ interactions with Sarah, responses automatically match her style:
131
- - Terse? Leads with numbers, no preamble.
132
- - Bullet-point user? Bullets.
133
- - Exploratory? More context and options.
149
+ - **Pulls** on daemon start
150
+ - **Pushes** every 30 minutes if there are changes
151
+ - **Final push** on daemon shutdown
152
+ - The same repo doubles as a GitHub Codespace template for browser sessions
153
+
154
+ ---
155
+
156
+ ## What can the agent actually do?
134
157
 
135
- The company graph sees `[from member] Sarah used /build` — not the raw conversations.
158
+ | Capability | How |
159
+ |---|---|
160
+ | Run shell commands | `shell_exec` — bash, any CLI tool |
161
+ | Read / write files | `file_op` — read, write, list, mkdir |
162
+ | Search the web | `web_search` — DuckDuckGo, no API key needed |
163
+ | Scrape pages | `scrape_url` — full page text, tables, links |
164
+ | Open browser | `browser_open` — system Chrome or default OS browser |
165
+ | Remember facts | `memory_write` — persists to knowledge graph |
166
+ | Schedule tasks | Natural language cron via `/schedule` |
167
+ | Self-heal | Detects runtime errors, proposes + applies patches |
136
168
 
137
169
  ---
138
170
 
139
- ## Config
171
+ ## Architecture
172
+
173
+ ```
174
+ npx 0agent@latest
175
+
176
+
177
+ ┌─────────────────────────────────────────────────────────┐
178
+ │ CLI (bin/0agent.js + bin/chat.js) │
179
+ │ • Init wizard • Chat TUI • Slash commands │
180
+ └───────────────────────┬─────────────────────────────────┘
181
+ │ HTTP + WebSocket
182
+
183
+ ┌─────────────────────────────────────────────────────────┐
184
+ │ Daemon (dist/daemon.mjs) — port 4200 │
185
+ │ │
186
+ │ SessionManager ── AgentExecutor ── LLMExecutor │
187
+ │ │ │ │ │
188
+ │ │ CapabilityRegistry │ │
189
+ │ │ • shell_exec │ │
190
+ │ │ • file_op │ │
191
+ │ │ • web_search │ │
192
+ │ │ • scrape_url │ │
193
+ │ │ • browser_open │ │
194
+ │ │ • memory_write │ │
195
+ │ │ │ │
196
+ │ KnowledgeGraph ◄────── outcome feedback ┘ │
197
+ │ (SQLite + HNSW) │
198
+ │ │ │
199
+ │ GitHubMemorySync ── SchedulerManager ── SelfHealLoop │
200
+ └─────────────────────────────────────────────────────────┘
201
+ ```
202
+
203
+ **Key packages:**
204
+
205
+ | Package | Description |
206
+ |---|---|
207
+ | `packages/core` | Knowledge graph, inference engine, storage adapters |
208
+ | `packages/daemon` | HTTP server, session manager, agent executor, capabilities |
209
+ | `bin/chat.js` | Claude Code-style TUI with message queue, WS events, spinner |
210
+ | `bin/0agent.js` | CLI entry point, init wizard, daemon lifecycle |
211
+
212
+ ---
213
+
214
+ ## Configuration
140
215
 
141
216
  `~/.0agent/config.yaml` — created by `0agent init`, edit anytime:
142
217
 
@@ -144,21 +219,83 @@ The company graph sees `[from member] Sarah used /build` — not the raw convers
144
219
  llm_providers:
145
220
  - provider: anthropic
146
221
  model: claude-sonnet-4-6
147
- api_key: sk-ant-...
222
+ api_key: sk-ant-... # never committed to git
148
223
  is_default: true
149
224
 
225
+ workspace:
226
+ path: /Users/you/0agent-workspace # agent creates files here
227
+
150
228
  sandbox:
151
229
  backend: docker # docker | podman | process | firecracker
152
230
 
153
- entity_nesting:
231
+ github_memory:
154
232
  enabled: true
233
+ token: ghp_...
234
+ owner: your-username
235
+ repo: 0agent-memory
236
+
237
+ embedding:
238
+ provider: nomic-ollama # nomic-ollama | openai | none
239
+ model: nomic-embed-text
240
+ dimensions: 768
241
+ ```
155
242
 
156
- self_improvement:
157
- schedule: weekly
243
+ ---
244
+
245
+ ## Local development
246
+
247
+ ```bash
248
+ git clone https://github.com/cadetmaze/0agentv1
249
+ cd 0agentv1
250
+ pnpm install
251
+ pnpm build
252
+
253
+ # Run init wizard
254
+ node bin/0agent.js init
255
+
256
+ # Or start daemon directly
257
+ node bin/0agent.js start
258
+ node bin/chat.js
259
+
260
+ # Bundle daemon into single file
261
+ node scripts/bundle.mjs
262
+
263
+ # Check status
264
+ node bin/0agent.js status
265
+ open http://localhost:4200 # 3D knowledge graph dashboard
158
266
  ```
159
267
 
268
+ **Requirements:**
269
+ - Node.js ≥ 20
270
+ - pnpm (`npm install -g pnpm`)
271
+ - API key for Anthropic, OpenAI, xAI, Gemini, or a local Ollama instance
272
+ - Docker (optional — enables sandboxed execution)
273
+
274
+ ---
275
+
276
+ ## Roadmap
277
+
278
+ - [ ] Telegram bot interface
279
+ - [ ] MCP server support (connect to external tools)
280
+ - [ ] Team collaboration (shared graph, sync via GitHub)
281
+ - [ ] Mobile companion app
282
+ - [ ] Plugin SDK for custom capabilities
283
+
284
+ ---
285
+
286
+ ## Contributing
287
+
288
+ Issues and PRs welcome. This is early-stage software — things break, APIs change.
289
+
290
+ 1. Fork the repo
291
+ 2. `pnpm install && pnpm build`
292
+ 3. Make changes to `packages/daemon/src/` or `bin/`
293
+ 4. `node scripts/bundle.mjs` to rebuild the bundle
294
+ 5. Test with `node bin/0agent.js init`
295
+ 6. Submit a PR
296
+
160
297
  ---
161
298
 
162
299
  ## License
163
300
 
164
- Apache 2.0
301
+ [Apache 2.0](LICENSE) — use it, fork it, build on it.
package/bin/0agent.js CHANGED
@@ -369,11 +369,20 @@ ${ghToken && ghOwner ? `\ngithub_memory:\n enabled: true\n token: "${ghToken}"
369
369
  console.log(' ✓ Built-in skills installed');
370
370
  }
371
371
 
372
- // Always kill any existing daemon so the new config (with API key) is loaded
373
- try { stopDaemon(); await sleep(800); } catch {}
372
+ // Force-kill any running daemon so new config is loaded fresh
373
+ await forceStopDaemon();
374
374
 
375
- console.log('\n Starting daemon...\n');
375
+ // Start fresh daemon with new config
376
+ console.log('\n Starting...');
376
377
  await startDaemon();
378
+
379
+ // Open chat TUI immediately — no manual steps needed
380
+ const chatSc = resolve(dirname(new URL(import.meta.url).pathname), 'chat.js');
381
+ if (existsSync(chatSc)) {
382
+ const { spawn: sp } = await import('node:child_process');
383
+ const p = sp(process.execPath, [chatSc], { stdio: 'inherit' });
384
+ await new Promise(r => p.on('close', r));
385
+ }
377
386
  }
378
387
 
379
388
  function detectSandboxes() {
@@ -391,9 +400,17 @@ function detectSandboxes() {
391
400
  // ─── Daemon lifecycle ─────────────────────────────────────────────────────
392
401
 
393
402
  async function startDaemon() {
403
+ // If still running (e.g. stop was called just before), wait a bit
394
404
  if (await isDaemonRunning()) {
395
- console.log(' Daemon already running on port 4200. Run `0agent status`.');
396
- return;
405
+ // Give it up to 3s to die before giving up
406
+ for (let i = 0; i < 6; i++) {
407
+ await sleep(500);
408
+ if (!await isDaemonRunning()) break;
409
+ }
410
+ if (await isDaemonRunning()) {
411
+ console.log(' Daemon already running on port 4200. Run `0agent status`.');
412
+ return;
413
+ }
397
414
  }
398
415
 
399
416
  if (!existsSync(CONFIG_PATH)) {
@@ -423,33 +440,42 @@ async function startDaemon() {
423
440
  child.unref();
424
441
 
425
442
  // Wait for daemon to be ready (poll /api/health)
426
- process.stdout.write(' Starting');
427
443
  for (let i = 0; i < 30; i++) {
428
444
  await sleep(500);
429
- process.stdout.write('.');
430
445
  if (await isDaemonRunning()) {
431
- console.log(' ✓\n');
432
- console.log(` Daemon running on http://localhost:4200`);
433
- console.log(` Dashboard: http://localhost:4200`);
434
- console.log(` Run: 0agent run "your task"\n`);
446
+ process.stdout.write(` \x1b[32m✓\x1b[0m Daemon ready\n`);
435
447
  return;
436
448
  }
437
449
  }
438
- console.log('\n Daemon did not start. Check logs: 0agent logs');
450
+ console.log(' Daemon did not start in time. Check: 0agent logs');
439
451
  }
440
452
 
441
453
  function stopDaemon() {
442
- if (!existsSync(PID_PATH)) {
454
+ // Try PID file first
455
+ if (existsSync(PID_PATH)) {
456
+ const pid = parseInt(readFileSync(PID_PATH, 'utf8').trim(), 10);
457
+ try { process.kill(pid, 'SIGTERM'); } catch {}
458
+ } else {
443
459
  console.log(' No daemon PID file found. Is it running?');
444
- return;
445
460
  }
446
- const pid = parseInt(readFileSync(PID_PATH, 'utf8').trim(), 10);
447
- try {
448
- process.kill(pid, 'SIGTERM');
449
- console.log(` Sent SIGTERM to daemon (pid ${pid}). Shutting down...`);
450
- } catch (e) {
451
- console.log(` Could not stop daemon: ${e instanceof Error ? e.message : e}`);
461
+ // Also kill by process name as a fallback
462
+ try { execSync('pkill -f "daemon.mjs" 2>/dev/null; true', { stdio: 'ignore' }); } catch {}
463
+ }
464
+
465
+ // Forcefully kill any running daemon regardless of PID file state.
466
+ // Used after init to ensure fresh config is picked up.
467
+ async function forceStopDaemon() {
468
+ // Kill by PID file if present
469
+ if (existsSync(PID_PATH)) {
470
+ const pid = parseInt(readFileSync(PID_PATH, 'utf8').trim(), 10);
471
+ try { process.kill(pid, 'SIGTERM'); } catch {}
452
472
  }
473
+ // Kill by process name (catches daemons started by chat.js or other means)
474
+ try { execSync('pkill -f "daemon.mjs" 2>/dev/null; true', { stdio: 'ignore' }); } catch {}
475
+ // Kill by port 4200 (last resort)
476
+ try { execSync('lsof -ti:4200 | xargs kill -9 2>/dev/null; true', { stdio: 'ignore' }); } catch {}
477
+ // Wait for port to be free
478
+ await sleep(1200);
453
479
  }
454
480
 
455
481
  async function showStatus() {
package/bin/chat.js CHANGED
@@ -7,12 +7,41 @@
7
7
  * /model to switch. /key to add provider keys. Never forgets previous keys.
8
8
  */
9
9
 
10
- import { createInterface } from 'node:readline';
10
+ import { createInterface, emitKeypressEvents, moveCursor, clearLine } from 'node:readline';
11
11
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
12
12
  import { resolve } from 'node:path';
13
13
  import { homedir } from 'node:os';
14
14
  import YAML from 'yaml';
15
15
 
16
+ // ─── Slash command registry (used for live menu + tab completion) ─────────────
17
+ const SLASH_COMMANDS = [
18
+ // Skills
19
+ { cmd: '/review', desc: 'Code review — bugs, style, security issues' },
20
+ { cmd: '/build', desc: 'Build project and fix compilation errors' },
21
+ { cmd: '/qa', desc: 'Generate and run tests' },
22
+ { cmd: '/debug', desc: 'Debug a failing test or runtime error' },
23
+ { cmd: '/refactor', desc: 'Refactor a file or module' },
24
+ { cmd: '/test-writer', desc: 'Write unit tests for your code' },
25
+ { cmd: '/doc', desc: 'Generate documentation' },
26
+ { cmd: '/research', desc: 'Research a topic, person, or company' },
27
+ { cmd: '/retro', desc: 'Weekly retrospective — what went well / badly' },
28
+ { cmd: '/ship', desc: 'Pre-release checklist — ready to deploy?' },
29
+ { cmd: '/office-hours', desc: 'Plan a new feature or project from scratch' },
30
+ { cmd: '/plan-eng-review',desc:'Engineering planning review' },
31
+ { cmd: '/security-audit',desc: 'Security audit — find vulnerabilities' },
32
+ { cmd: '/design-review', desc: 'Design review — architecture and patterns' },
33
+ // Built-ins
34
+ { cmd: '/model', desc: 'Show or switch the LLM model' },
35
+ { cmd: '/key', desc: 'Update a stored API key' },
36
+ { cmd: '/status', desc: 'Daemon health, graph stats, active sessions' },
37
+ { cmd: '/skills', desc: 'List all available skills' },
38
+ { cmd: '/schedule', desc: 'Manage scheduled / recurring tasks' },
39
+ { cmd: '/update', desc: 'Update 0agent to the latest version' },
40
+ { cmd: '/graph', desc: 'Open 3D knowledge graph in browser' },
41
+ { cmd: '/clear', desc: 'Clear the screen' },
42
+ { cmd: '/help', desc: 'Show this help' },
43
+ ];
44
+
16
45
  const AGENT_DIR = resolve(homedir(), '.0agent');
17
46
  const CONFIG_PATH = resolve(AGENT_DIR, 'config.yaml');
18
47
  const BASE_URL = process.env['ZEROAGENT_URL'] ?? 'http://localhost:4200';
@@ -183,7 +212,7 @@ function printHeader() {
183
212
  const modelStr = provider ? `${provider.provider}/${provider.model}` : 'no model';
184
213
  console.log();
185
214
  console.log(fmt(C.bold, ' 0agent') + fmt(C.dim, ` — ${modelStr}`));
186
- console.log(fmt(C.dim, ' Type a task, or /help for commands. Ctrl+C to exit.\n'));
215
+ console.log(fmt(C.dim, ' Type a task or /command press Tab to browse, / to see all.\n'));
187
216
  }
188
217
 
189
218
  function printInsights() {
@@ -328,6 +357,14 @@ function handleWsEvent(event) {
328
357
  if (r.files_written?.length) console.log(`\n ${fmt(C.green, '✓')} ${r.files_written.join(', ')}`);
329
358
  if (r.tokens_used) process.stdout.write(fmt(C.dim, `\n ${r.tokens_used} tokens · ${r.model ?? ''}\n`));
330
359
 
360
+ // Contextual next-step suggestions
361
+ const suggestions = _suggestNext(lineBuffer, r);
362
+ if (suggestions.length > 0 && messageQueue.length === 0) {
363
+ process.stdout.write(
364
+ ` ${fmt(C.dim, '→')} ${suggestions.map(s => fmt(C.cyan, s)).join(fmt(C.dim, ' · '))}\n`
365
+ );
366
+ }
367
+
331
368
  // Confirm server if port mentioned
332
369
  confirmServer(r, lineBuffer);
333
370
  lineBuffer = '';
@@ -381,6 +418,26 @@ function handleWsEvent(event) {
381
418
  }
382
419
  }
383
420
 
421
+ function _suggestNext(output, result) {
422
+ const text = (output + ' ' + (result?.output ?? '') + ' ' + JSON.stringify(result?.files_written ?? [])).toLowerCase();
423
+ if (text.includes('written') || text.includes('creat') || text.includes('built') || (result?.files_written?.length > 0)) {
424
+ return ['/review', '/qa'];
425
+ }
426
+ if (text.includes('test') || text.includes('spec') || text.includes('pass')) {
427
+ return ['/ship'];
428
+ }
429
+ if (text.includes('research') || text.includes('found') || text.includes('results')) {
430
+ return ['/office-hours', '/doc'];
431
+ }
432
+ if (text.includes('bug') || text.includes('fix') || text.includes('error') || text.includes('debug')) {
433
+ return ['/qa', '/review'];
434
+ }
435
+ if (text.includes('deploy') || text.includes('server') || text.includes('running')) {
436
+ return ['/qa', '/ship'];
437
+ }
438
+ return [];
439
+ }
440
+
384
441
  async function confirmServer(result, output) {
385
442
  const allText = [...(result.commands_run ?? []), output].join(' ');
386
443
  const portMatch = allText.match(/(?:localhost:|port\s*[=:]?\s*)(\d{4,5})/i);
@@ -751,23 +808,116 @@ async function handleCommand(input) {
751
808
  }
752
809
  }
753
810
 
811
+ // ─── Live slash-command menu ──────────────────────────────────────────────────
812
+ // Drawn below the prompt as the user types. Uses moveCursor to avoid cursor
813
+ // save/restore conflicts with readline.
814
+ let _menuLines = 0; // how many lines the current menu occupies below the cursor
815
+
816
+ function _drawMenu(filter) {
817
+ if (pendingResolve) { _clearMenu(); return; } // don't show while session running
818
+
819
+ const items = filter === null ? [] :
820
+ SLASH_COMMANDS.filter(c =>
821
+ !filter || c.cmd.slice(1).toLowerCase().startsWith(filter.toLowerCase())
822
+ ).slice(0, 10);
823
+
824
+ // If nothing changed (same count), skip redraw to avoid flicker
825
+ if (items.length === 0) { _clearMenu(); return; }
826
+
827
+ const needed = items.length + 1; // +1 for blank line
828
+
829
+ // Move down past existing menu lines (or 0), then clear downward
830
+ const existingLines = _menuLines;
831
+ if (existingLines > 0) {
832
+ moveCursor(process.stdout, 0, existingLines);
833
+ for (let i = 0; i < existingLines; i++) {
834
+ clearLine(process.stdout, 0);
835
+ if (i < existingLines - 1) moveCursor(process.stdout, 0, -1);
836
+ }
837
+ moveCursor(process.stdout, 0, -(existingLines - 1));
838
+ }
839
+
840
+ // Print blank separator + menu items, tracking column 0
841
+ process.stdout.write('\n');
842
+ for (const m of items) {
843
+ process.stdout.write(
844
+ ` ${fmt(C.cyan, m.cmd.padEnd(20))} ${fmt(C.dim, m.desc)}\x1b[K\n`
845
+ );
846
+ }
847
+
848
+ // Move back up to the prompt line and restore cursor after the typed text
849
+ moveCursor(process.stdout, 0, -(needed));
850
+ // Jump to end of current line (readline already put cursor there)
851
+ moveCursor(process.stdout, 999, 0);
852
+
853
+ _menuLines = needed;
854
+ }
855
+
856
+ function _clearMenu() {
857
+ if (_menuLines === 0) return;
858
+ const n = _menuLines;
859
+ _menuLines = 0;
860
+ moveCursor(process.stdout, 0, n);
861
+ for (let i = 0; i < n; i++) {
862
+ clearLine(process.stdout, 0);
863
+ moveCursor(process.stdout, 0, -1);
864
+ }
865
+ moveCursor(process.stdout, 0, 1); // back to prompt line
866
+ }
867
+
754
868
  // ─── Main REPL ────────────────────────────────────────────────────────────────
755
869
  const rl = createInterface({
756
870
  input: process.stdin,
757
871
  output: process.stdout,
758
872
  prompt: `\n ${fmt(C.cyan, '›')} `,
759
873
  historySize: 100,
760
- completer: (line) => {
761
- const commands = ['/model', '/key', '/status', '/skills', '/graph', '/clear', '/help',
762
- '/schedule', '/schedule list', '/schedule add',
763
- '/review', '/build', '/debug', '/qa', '/research', '/refactor', '/test-writer', '/retro'];
764
- const hits = commands.filter(c => c.startsWith(line));
765
- return [hits.length ? hits : commands, line];
874
+ completer: (line, callback) => {
875
+ if (!line.startsWith('/')) return callback(null, [[], line]);
876
+
877
+ const filter = line.slice(1).toLowerCase();
878
+ const matches = SLASH_COMMANDS.filter(c =>
879
+ !filter || c.cmd.slice(1).startsWith(filter)
880
+ );
881
+
882
+ if (matches.length === 0) return callback(null, [[], line]);
883
+
884
+ // Single match — let readline silently auto-complete
885
+ if (matches.length === 1) {
886
+ _clearMenu();
887
+ return callback(null, [[matches[0].cmd + ' '], line]);
888
+ }
889
+
890
+ // Multiple matches — print formatted menu, suppress readline's plain list
891
+ _clearMenu();
892
+ process.stdout.write('\n\n');
893
+ for (const m of matches.slice(0, 12)) {
894
+ process.stdout.write(` ${fmt(C.cyan, m.cmd.padEnd(22))} ${fmt(C.dim, m.desc)}\n`);
895
+ }
896
+ if (matches.length > 12) {
897
+ process.stdout.write(` ${fmt(C.dim, `…and ${matches.length - 12} more`)}\n`);
898
+ }
899
+ process.stdout.write('\n');
900
+ setImmediate(() => rl.prompt(true));
901
+ return callback(null, [[], line]);
766
902
  },
767
903
  });
768
904
 
769
- // Restore history from conversations if possible
770
- rl.on('history', () => {});
905
+ // Live menu on keypress draws below the prompt as user types
906
+ emitKeypressEvents(process.stdin, rl);
907
+ process.stdin.on('keypress', (_char, key) => {
908
+ if (key?.name === 'return' || key?.name === 'enter') {
909
+ _clearMenu(); // clear before readline processes the line
910
+ return;
911
+ }
912
+ setImmediate(() => {
913
+ const line = rl.line ?? '';
914
+ if (line.startsWith('/') && !pendingResolve) {
915
+ _drawMenu(line.slice(1));
916
+ } else {
917
+ _clearMenu();
918
+ }
919
+ });
920
+ });
771
921
 
772
922
  printHeader();
773
923
  printInsights();
@@ -1075,9 +1225,21 @@ async function drainQueue() {
1075
1225
  }
1076
1226
 
1077
1227
  rl.on('line', async (input) => {
1228
+ _clearMenu(); // always clear menu when a line is submitted
1078
1229
  const line = input.trim();
1079
1230
  if (!line) { rl.prompt(); return; }
1080
1231
 
1232
+ // Bare `/` → show full command palette
1233
+ if (line === '/') {
1234
+ console.log('');
1235
+ for (const m of SLASH_COMMANDS) {
1236
+ console.log(` ${fmt(C.cyan, m.cmd.padEnd(22))} ${fmt(C.dim, m.desc)}`);
1237
+ }
1238
+ console.log('');
1239
+ rl.prompt();
1240
+ return;
1241
+ }
1242
+
1081
1243
  // If a session is already running, queue the message.
1082
1244
  // pauseFor() stops the spinner briefly so the user can see the confirmation,
1083
1245
  // then resumes — prevents spinner from overwriting their typed text.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "0agent",
3
- "version": "1.0.41",
3
+ "version": "1.0.43",
4
4
  "description": "A persistent, learning AI agent that runs on your machine. An agent that learns.",
5
5
  "private": false,
6
6
  "license": "Apache-2.0",