0agent 1.0.42 → 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.
- package/README.md +226 -89
- package/bin/chat.js +172 -10
- 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
|
-
|
|
11
|
+
[](https://www.npmjs.com/package/0agent)
|
|
12
|
+
[](LICENSE)
|
|
13
|
+
[](https://nodejs.org)
|
|
10
14
|
|
|
11
15
|
---
|
|
12
16
|
|
|
13
|
-
## What
|
|
17
|
+
## What is this?
|
|
14
18
|
|
|
15
|
-
|
|
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
|
-
|
|
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
|
-
|
|
28
|
+
---
|
|
45
29
|
|
|
46
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
46
|
+
## Usage
|
|
47
|
+
|
|
48
|
+
### Interactive chat
|
|
62
49
|
|
|
63
50
|
```bash
|
|
64
|
-
#
|
|
51
|
+
0agent # open chat (starts daemon if needed)
|
|
65
52
|
npx 0agent@latest
|
|
53
|
+
```
|
|
66
54
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
55
|
+
```
|
|
56
|
+
0agent — anthropic/claude-sonnet-4-6
|
|
57
|
+
Type a task, or /help for commands.
|
|
70
58
|
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
66
|
+
### Slash skills
|
|
78
67
|
|
|
79
68
|
```bash
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
#
|
|
86
|
-
|
|
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
|
-
#
|
|
89
|
-
|
|
85
|
+
# Research
|
|
86
|
+
0agent /research "Acme Corp Series B"
|
|
87
|
+
0agent /security-audit
|
|
88
|
+
```
|
|
90
89
|
|
|
91
|
-
|
|
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
|
-
##
|
|
115
|
+
## How it learns
|
|
116
|
+
|
|
117
|
+
Every task updates a weighted knowledge graph stored in `~/.0agent/graph.db`.
|
|
101
118
|
|
|
102
119
|
```
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
110
|
-
-
|
|
111
|
-
-
|
|
112
|
-
-
|
|
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
|
-
##
|
|
136
|
+
## Memory sync
|
|
118
137
|
|
|
119
|
-
0agent can
|
|
138
|
+
0agent can back up its knowledge graph to a private GitHub repo:
|
|
120
139
|
|
|
121
|
-
```
|
|
122
|
-
#
|
|
123
|
-
|
|
140
|
+
```bash
|
|
141
|
+
# Set up during init, or add manually to ~/.0agent/config.yaml:
|
|
142
|
+
github_memory:
|
|
124
143
|
enabled: true
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
144
|
+
token: ghp_...
|
|
145
|
+
owner: your-username
|
|
146
|
+
repo: 0agent-memory
|
|
128
147
|
```
|
|
129
148
|
|
|
130
|
-
|
|
131
|
-
-
|
|
132
|
-
-
|
|
133
|
-
-
|
|
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
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
157
|
-
|
|
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/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
|
|
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
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
const
|
|
765
|
-
|
|
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
|
-
//
|
|
770
|
-
|
|
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.
|