codebot-ai 1.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.
- package/LICENSE +21 -0
- package/README.md +247 -0
- package/bin/codebot +5 -0
- package/dist/agent.d.ts +31 -0
- package/dist/agent.js +256 -0
- package/dist/banner.d.ts +19 -0
- package/dist/banner.js +148 -0
- package/dist/browser/cdp.d.ts +29 -0
- package/dist/browser/cdp.js +292 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +518 -0
- package/dist/context/manager.d.ts +27 -0
- package/dist/context/manager.js +139 -0
- package/dist/context/repo-map.d.ts +5 -0
- package/dist/context/repo-map.js +100 -0
- package/dist/history.d.ts +27 -0
- package/dist/history.js +146 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +42 -0
- package/dist/memory.d.ts +39 -0
- package/dist/memory.js +168 -0
- package/dist/parser.d.ts +8 -0
- package/dist/parser.js +79 -0
- package/dist/providers/anthropic.d.ts +9 -0
- package/dist/providers/anthropic.js +288 -0
- package/dist/providers/index.d.ts +5 -0
- package/dist/providers/index.js +13 -0
- package/dist/providers/openai.d.ts +11 -0
- package/dist/providers/openai.js +173 -0
- package/dist/providers/registry.d.ts +15 -0
- package/dist/providers/registry.js +115 -0
- package/dist/setup.d.ts +17 -0
- package/dist/setup.js +243 -0
- package/dist/tools/browser.d.ts +43 -0
- package/dist/tools/browser.js +329 -0
- package/dist/tools/edit.d.ts +26 -0
- package/dist/tools/edit.js +73 -0
- package/dist/tools/execute.d.ts +26 -0
- package/dist/tools/execute.js +52 -0
- package/dist/tools/glob.d.ts +24 -0
- package/dist/tools/glob.js +102 -0
- package/dist/tools/grep.d.ts +29 -0
- package/dist/tools/grep.js +125 -0
- package/dist/tools/index.d.ts +10 -0
- package/dist/tools/index.js +49 -0
- package/dist/tools/memory.d.ts +36 -0
- package/dist/tools/memory.js +114 -0
- package/dist/tools/read.d.ts +26 -0
- package/dist/tools/read.js +75 -0
- package/dist/tools/think.d.ts +18 -0
- package/dist/tools/think.js +20 -0
- package/dist/tools/web-fetch.d.ts +36 -0
- package/dist/tools/web-fetch.js +83 -0
- package/dist/tools/write.d.ts +22 -0
- package/dist/tools/write.js +65 -0
- package/dist/types.d.ts +82 -0
- package/dist/types.js +3 -0
- package/package.json +57 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Ascendral Software Development & Innovation
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# CodeBot AI
|
|
2
|
+
|
|
3
|
+
Local-first AI coding assistant. Zero runtime dependencies. Works with Ollama, LM Studio, vLLM, Claude, GPT, Gemini, DeepSeek, Groq, Mistral, and Grok.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Install globally
|
|
9
|
+
npm install -g codebot-ai
|
|
10
|
+
|
|
11
|
+
# Run — setup wizard launches automatically on first use
|
|
12
|
+
codebot
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Or run without installing:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npx codebot-ai
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Or from source:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
git clone https://github.com/AscendralSoftware/codebot-ai.git
|
|
25
|
+
cd codebot-ai
|
|
26
|
+
npm install && npm run build
|
|
27
|
+
./bin/codebot
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Setup
|
|
31
|
+
|
|
32
|
+
On first run, CodeBot detects your environment and walks you through configuration:
|
|
33
|
+
|
|
34
|
+
- Scans for local LLM servers (Ollama, LM Studio, vLLM)
|
|
35
|
+
- Detects API keys from environment variables
|
|
36
|
+
- Lets you pick a provider and model
|
|
37
|
+
- Saves config to `~/.codebot/config.json`
|
|
38
|
+
|
|
39
|
+
To reconfigure anytime: `codebot --setup`
|
|
40
|
+
|
|
41
|
+
### Environment Variables
|
|
42
|
+
|
|
43
|
+
Set an API key for your preferred cloud provider:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
export ANTHROPIC_API_KEY="sk-ant-..." # Claude
|
|
47
|
+
export OPENAI_API_KEY="sk-..." # GPT
|
|
48
|
+
export GEMINI_API_KEY="..." # Gemini
|
|
49
|
+
export DEEPSEEK_API_KEY="sk-..." # DeepSeek
|
|
50
|
+
export GROQ_API_KEY="gsk_..." # Groq
|
|
51
|
+
export MISTRAL_API_KEY="..." # Mistral
|
|
52
|
+
export XAI_API_KEY="xai-..." # Grok
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
For local models, just have Ollama/LM Studio/vLLM running — CodeBot auto-detects them.
|
|
56
|
+
|
|
57
|
+
## Usage
|
|
58
|
+
|
|
59
|
+
### Interactive Mode
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
codebot
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Single Message
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
codebot "fix the bug in app.ts"
|
|
69
|
+
codebot --model claude-sonnet-4-6 "explain this codebase"
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Pipe Mode
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
echo "write a function that sorts by date" | codebot
|
|
76
|
+
cat error.log | codebot "what's causing this?"
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Autonomous Mode
|
|
80
|
+
|
|
81
|
+
Skip all permission prompts — full auto:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
codebot --autonomous "refactor the auth module and run tests"
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Session Resume
|
|
88
|
+
|
|
89
|
+
CodeBot auto-saves every conversation. Resume anytime:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
codebot --continue # Resume last session
|
|
93
|
+
codebot --resume <session-id> # Resume specific session
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## CLI Options
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
--setup Run the setup wizard
|
|
100
|
+
--model <name> Model to use (default: qwen2.5-coder:32b)
|
|
101
|
+
--provider <name> Provider: openai, anthropic, gemini, deepseek, groq, mistral, xai
|
|
102
|
+
--base-url <url> LLM API base URL
|
|
103
|
+
--api-key <key> API key (or use env vars)
|
|
104
|
+
--autonomous Skip all permission prompts
|
|
105
|
+
--resume <id> Resume a session by ID
|
|
106
|
+
--continue, -c Resume the most recent session
|
|
107
|
+
--max-iterations <n> Max agent loop iterations (default: 50)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Interactive Commands
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
/help Show commands
|
|
114
|
+
/model Show or change model
|
|
115
|
+
/models List all supported models
|
|
116
|
+
/sessions List saved sessions
|
|
117
|
+
/auto Toggle autonomous mode
|
|
118
|
+
/clear Clear conversation
|
|
119
|
+
/compact Force context compaction
|
|
120
|
+
/config Show configuration
|
|
121
|
+
/quit Exit
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Tools
|
|
125
|
+
|
|
126
|
+
CodeBot has 10 built-in tools:
|
|
127
|
+
|
|
128
|
+
| Tool | Description | Permission |
|
|
129
|
+
|------|-------------|-----------|
|
|
130
|
+
| `read_file` | Read files with line numbers | auto |
|
|
131
|
+
| `write_file` | Create or overwrite files | prompt |
|
|
132
|
+
| `edit_file` | Find-and-replace edits | prompt |
|
|
133
|
+
| `execute` | Run shell commands | always-ask |
|
|
134
|
+
| `glob` | Find files by pattern | auto |
|
|
135
|
+
| `grep` | Search file contents with regex | auto |
|
|
136
|
+
| `think` | Internal reasoning scratchpad | auto |
|
|
137
|
+
| `memory` | Persistent memory across sessions | auto |
|
|
138
|
+
| `web_fetch` | HTTP requests and API calls | prompt |
|
|
139
|
+
| `browser` | Chrome automation via CDP | prompt |
|
|
140
|
+
|
|
141
|
+
### Permission Levels
|
|
142
|
+
|
|
143
|
+
- **auto** — Runs without asking
|
|
144
|
+
- **prompt** — Asks for approval (skipped in `--autonomous` mode)
|
|
145
|
+
- **always-ask** — Always asks, even in autonomous mode
|
|
146
|
+
|
|
147
|
+
### Browser Tool
|
|
148
|
+
|
|
149
|
+
Controls Chrome via the Chrome DevTools Protocol. Actions:
|
|
150
|
+
|
|
151
|
+
- `navigate` — Go to a URL
|
|
152
|
+
- `content` — Read page text
|
|
153
|
+
- `screenshot` — Capture the page
|
|
154
|
+
- `click` — Click an element by CSS selector
|
|
155
|
+
- `type` — Type into an input field
|
|
156
|
+
- `evaluate` — Run JavaScript on the page
|
|
157
|
+
- `tabs` — List open tabs
|
|
158
|
+
- `close` — Close browser connection
|
|
159
|
+
|
|
160
|
+
Chrome is auto-launched with `--remote-debugging-port` if not already running.
|
|
161
|
+
|
|
162
|
+
### Memory
|
|
163
|
+
|
|
164
|
+
CodeBot has persistent memory that survives across sessions:
|
|
165
|
+
|
|
166
|
+
- **Global memory** (`~/.codebot/memory/`) — preferences, patterns
|
|
167
|
+
- **Project memory** (`.codebot/memory/`) — project-specific context
|
|
168
|
+
- Memory is automatically injected into the system prompt
|
|
169
|
+
- The agent can read/write its own memory using the `memory` tool
|
|
170
|
+
|
|
171
|
+
## Supported Models
|
|
172
|
+
|
|
173
|
+
### Local (Ollama / LM Studio / vLLM)
|
|
174
|
+
|
|
175
|
+
qwen2.5-coder (3b/7b/14b/32b), qwen3, deepseek-coder, codellama, llama3.x, mistral, mixtral, phi-3/4, starcoder2, granite-code, gemma2, command-r
|
|
176
|
+
|
|
177
|
+
### Cloud
|
|
178
|
+
|
|
179
|
+
- **Anthropic**: claude-opus-4-6, claude-sonnet-4-6, claude-haiku-4-5
|
|
180
|
+
- **OpenAI**: gpt-4o, gpt-4.1, o1, o3, o4-mini
|
|
181
|
+
- **Google**: gemini-2.5-pro, gemini-2.5-flash, gemini-2.0-flash
|
|
182
|
+
- **DeepSeek**: deepseek-chat, deepseek-reasoner
|
|
183
|
+
- **Groq**: llama-3.3-70b, mixtral-8x7b (fast inference)
|
|
184
|
+
- **Mistral**: mistral-large, codestral
|
|
185
|
+
- **xAI**: grok-3, grok-3-mini
|
|
186
|
+
|
|
187
|
+
## Architecture
|
|
188
|
+
|
|
189
|
+
```
|
|
190
|
+
src/
|
|
191
|
+
agent.ts Agent loop with streaming, tool execution, permissions
|
|
192
|
+
cli.ts CLI interface, REPL, slash commands
|
|
193
|
+
types.ts TypeScript interfaces
|
|
194
|
+
parser.ts XML/JSON tool call parser (for models without native tool support)
|
|
195
|
+
history.ts Session persistence (JSONL)
|
|
196
|
+
memory.ts Persistent memory system
|
|
197
|
+
setup.ts Interactive setup wizard
|
|
198
|
+
context/
|
|
199
|
+
manager.ts Context window management, LLM-powered compaction
|
|
200
|
+
repo-map.ts Project structure scanner
|
|
201
|
+
providers/
|
|
202
|
+
openai.ts OpenAI-compatible provider (covers most cloud APIs)
|
|
203
|
+
anthropic.ts Native Anthropic Messages API provider
|
|
204
|
+
registry.ts Model registry, provider detection
|
|
205
|
+
browser/
|
|
206
|
+
cdp.ts Chrome DevTools Protocol client (zero-dep WebSocket)
|
|
207
|
+
tools/
|
|
208
|
+
read.ts, write.ts, edit.ts, execute.ts
|
|
209
|
+
glob.ts, grep.ts, think.ts
|
|
210
|
+
memory.ts, web-fetch.ts, browser.ts
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## Programmatic API
|
|
214
|
+
|
|
215
|
+
CodeBot can be used as a library:
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
import { Agent, OpenAIProvider, AnthropicProvider } from 'codebot-ai';
|
|
219
|
+
|
|
220
|
+
const provider = new AnthropicProvider({
|
|
221
|
+
baseUrl: 'https://api.anthropic.com',
|
|
222
|
+
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
223
|
+
model: 'claude-sonnet-4-6',
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
const agent = new Agent({
|
|
227
|
+
provider,
|
|
228
|
+
model: 'claude-sonnet-4-6',
|
|
229
|
+
autoApprove: true,
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
for await (const event of agent.run('list all TypeScript files')) {
|
|
233
|
+
if (event.type === 'text') process.stdout.write(event.text || '');
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Configuration
|
|
238
|
+
|
|
239
|
+
Config is loaded in this order (later values win):
|
|
240
|
+
|
|
241
|
+
1. `~/.codebot/config.json` (saved by setup wizard)
|
|
242
|
+
2. Environment variables (`CODEBOT_MODEL`, `CODEBOT_PROVIDER`, etc.)
|
|
243
|
+
3. CLI flags (`--model`, `--provider`, etc.)
|
|
244
|
+
|
|
245
|
+
## License
|
|
246
|
+
|
|
247
|
+
MIT - Ascendral Software Development & Innovation
|
package/bin/codebot
ADDED
package/dist/agent.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Message, AgentEvent, LLMProvider } from './types';
|
|
2
|
+
export declare class Agent {
|
|
3
|
+
private provider;
|
|
4
|
+
private tools;
|
|
5
|
+
private context;
|
|
6
|
+
private messages;
|
|
7
|
+
private maxIterations;
|
|
8
|
+
private autoApprove;
|
|
9
|
+
private model;
|
|
10
|
+
private askPermission;
|
|
11
|
+
private onMessage?;
|
|
12
|
+
constructor(opts: {
|
|
13
|
+
provider: LLMProvider;
|
|
14
|
+
model: string;
|
|
15
|
+
maxIterations?: number;
|
|
16
|
+
autoApprove?: boolean;
|
|
17
|
+
askPermission?: (tool: string, args: Record<string, unknown>) => Promise<boolean>;
|
|
18
|
+
onMessage?: (message: Message) => void;
|
|
19
|
+
});
|
|
20
|
+
/** Load messages from a previous session for resume */
|
|
21
|
+
loadMessages(messages: Message[]): void;
|
|
22
|
+
run(userMessage: string): AsyncGenerator<AgentEvent>;
|
|
23
|
+
clearHistory(): void;
|
|
24
|
+
forceCompact(): {
|
|
25
|
+
before: number;
|
|
26
|
+
after: number;
|
|
27
|
+
};
|
|
28
|
+
getMessages(): Message[];
|
|
29
|
+
private buildSystemPrompt;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=agent.d.ts.map
|
package/dist/agent.js
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.Agent = void 0;
|
|
37
|
+
const readline = __importStar(require("readline"));
|
|
38
|
+
const tools_1 = require("./tools");
|
|
39
|
+
const parser_1 = require("./parser");
|
|
40
|
+
const manager_1 = require("./context/manager");
|
|
41
|
+
const repo_map_1 = require("./context/repo-map");
|
|
42
|
+
const memory_1 = require("./memory");
|
|
43
|
+
const registry_1 = require("./providers/registry");
|
|
44
|
+
class Agent {
|
|
45
|
+
provider;
|
|
46
|
+
tools;
|
|
47
|
+
context;
|
|
48
|
+
messages = [];
|
|
49
|
+
maxIterations;
|
|
50
|
+
autoApprove;
|
|
51
|
+
model;
|
|
52
|
+
askPermission;
|
|
53
|
+
onMessage;
|
|
54
|
+
constructor(opts) {
|
|
55
|
+
this.provider = opts.provider;
|
|
56
|
+
this.model = opts.model;
|
|
57
|
+
this.tools = new tools_1.ToolRegistry(process.cwd());
|
|
58
|
+
this.context = new manager_1.ContextManager(opts.model, opts.provider);
|
|
59
|
+
this.maxIterations = opts.maxIterations || 25;
|
|
60
|
+
this.autoApprove = opts.autoApprove || false;
|
|
61
|
+
this.askPermission = opts.askPermission || defaultAskPermission;
|
|
62
|
+
this.onMessage = opts.onMessage;
|
|
63
|
+
const supportsTools = (0, registry_1.getModelInfo)(opts.model).supportsToolCalling;
|
|
64
|
+
this.messages.push({
|
|
65
|
+
role: 'system',
|
|
66
|
+
content: this.buildSystemPrompt(supportsTools),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
/** Load messages from a previous session for resume */
|
|
70
|
+
loadMessages(messages) {
|
|
71
|
+
this.messages = messages;
|
|
72
|
+
}
|
|
73
|
+
async *run(userMessage) {
|
|
74
|
+
const userMsg = { role: 'user', content: userMessage };
|
|
75
|
+
this.messages.push(userMsg);
|
|
76
|
+
this.onMessage?.(userMsg);
|
|
77
|
+
if (!this.context.fitsInBudget(this.messages)) {
|
|
78
|
+
const result = await this.context.compactWithSummary(this.messages);
|
|
79
|
+
this.messages = result.messages;
|
|
80
|
+
yield { type: 'compaction', text: result.summary || 'Context compacted to fit budget.' };
|
|
81
|
+
}
|
|
82
|
+
for (let i = 0; i < this.maxIterations; i++) {
|
|
83
|
+
const supportsTools = (0, registry_1.getModelInfo)(this.model).supportsToolCalling;
|
|
84
|
+
const toolSchemas = supportsTools ? this.tools.getSchemas() : undefined;
|
|
85
|
+
let fullText = '';
|
|
86
|
+
let toolCalls = [];
|
|
87
|
+
// Stream LLM response
|
|
88
|
+
for await (const event of this.provider.chat(this.messages, toolSchemas)) {
|
|
89
|
+
switch (event.type) {
|
|
90
|
+
case 'text':
|
|
91
|
+
fullText += event.text || '';
|
|
92
|
+
yield { type: 'text', text: event.text };
|
|
93
|
+
break;
|
|
94
|
+
case 'thinking':
|
|
95
|
+
yield { type: 'thinking', text: event.text };
|
|
96
|
+
break;
|
|
97
|
+
case 'tool_call_end':
|
|
98
|
+
if (event.toolCall) {
|
|
99
|
+
toolCalls.push(event.toolCall);
|
|
100
|
+
}
|
|
101
|
+
break;
|
|
102
|
+
case 'usage':
|
|
103
|
+
yield { type: 'usage', usage: event.usage };
|
|
104
|
+
break;
|
|
105
|
+
case 'error':
|
|
106
|
+
yield { type: 'error', error: event.error };
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// If no native tool calls, try parsing from text
|
|
111
|
+
if (toolCalls.length === 0 && fullText) {
|
|
112
|
+
toolCalls = (0, parser_1.parseToolCalls)(fullText);
|
|
113
|
+
}
|
|
114
|
+
// Save assistant message
|
|
115
|
+
const assistantMsg = { role: 'assistant', content: fullText };
|
|
116
|
+
if (toolCalls.length > 0) {
|
|
117
|
+
assistantMsg.tool_calls = toolCalls;
|
|
118
|
+
}
|
|
119
|
+
this.messages.push(assistantMsg);
|
|
120
|
+
this.onMessage?.(assistantMsg);
|
|
121
|
+
// No tool calls = conversation turn done
|
|
122
|
+
if (toolCalls.length === 0) {
|
|
123
|
+
yield { type: 'done' };
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
// Execute each tool call
|
|
127
|
+
for (const tc of toolCalls) {
|
|
128
|
+
const toolName = tc.function.name;
|
|
129
|
+
const tool = this.tools.get(toolName);
|
|
130
|
+
if (!tool) {
|
|
131
|
+
const errResult = `Error: Unknown tool "${toolName}"`;
|
|
132
|
+
const toolMsg = { role: 'tool', content: errResult, tool_call_id: tc.id };
|
|
133
|
+
this.messages.push(toolMsg);
|
|
134
|
+
this.onMessage?.(toolMsg);
|
|
135
|
+
yield { type: 'tool_result', toolResult: { name: toolName, result: errResult, is_error: true } };
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
let args;
|
|
139
|
+
try {
|
|
140
|
+
args = JSON.parse(tc.function.arguments);
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
const errResult = `Error: Invalid JSON arguments for ${toolName}`;
|
|
144
|
+
const toolMsg = { role: 'tool', content: errResult, tool_call_id: tc.id };
|
|
145
|
+
this.messages.push(toolMsg);
|
|
146
|
+
this.onMessage?.(toolMsg);
|
|
147
|
+
yield { type: 'tool_result', toolResult: { name: toolName, result: errResult, is_error: true } };
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
yield { type: 'tool_call', toolCall: { name: toolName, args } };
|
|
151
|
+
// Permission check
|
|
152
|
+
const needsPermission = tool.permission === 'always-ask' ||
|
|
153
|
+
(tool.permission === 'prompt' && !this.autoApprove);
|
|
154
|
+
if (needsPermission) {
|
|
155
|
+
const approved = await this.askPermission(toolName, args);
|
|
156
|
+
if (!approved) {
|
|
157
|
+
const toolMsg = { role: 'tool', content: 'Permission denied by user.', tool_call_id: tc.id };
|
|
158
|
+
this.messages.push(toolMsg);
|
|
159
|
+
this.onMessage?.(toolMsg);
|
|
160
|
+
yield { type: 'tool_result', toolResult: { name: toolName, result: 'Permission denied.' } };
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// Execute
|
|
165
|
+
try {
|
|
166
|
+
const output = await tool.execute(args);
|
|
167
|
+
const toolMsg = { role: 'tool', content: output, tool_call_id: tc.id };
|
|
168
|
+
this.messages.push(toolMsg);
|
|
169
|
+
this.onMessage?.(toolMsg);
|
|
170
|
+
yield { type: 'tool_result', toolResult: { name: toolName, result: output } };
|
|
171
|
+
}
|
|
172
|
+
catch (err) {
|
|
173
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
174
|
+
const toolMsg = { role: 'tool', content: `Error: ${errMsg}`, tool_call_id: tc.id };
|
|
175
|
+
this.messages.push(toolMsg);
|
|
176
|
+
this.onMessage?.(toolMsg);
|
|
177
|
+
yield { type: 'tool_result', toolResult: { name: toolName, result: errMsg, is_error: true } };
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// Compact after tool results if needed
|
|
181
|
+
if (!this.context.fitsInBudget(this.messages)) {
|
|
182
|
+
const result = await this.context.compactWithSummary(this.messages);
|
|
183
|
+
this.messages = result.messages;
|
|
184
|
+
yield { type: 'compaction', text: result.summary || 'Context compacted.' };
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
yield { type: 'error', error: `Max iterations (${this.maxIterations}) reached.` };
|
|
188
|
+
}
|
|
189
|
+
clearHistory() {
|
|
190
|
+
const system = this.messages[0];
|
|
191
|
+
this.messages = system?.role === 'system' ? [system] : [];
|
|
192
|
+
}
|
|
193
|
+
forceCompact() {
|
|
194
|
+
const before = this.messages.length;
|
|
195
|
+
this.messages = this.context.compact(this.messages, true);
|
|
196
|
+
return { before, after: this.messages.length };
|
|
197
|
+
}
|
|
198
|
+
getMessages() {
|
|
199
|
+
return [...this.messages];
|
|
200
|
+
}
|
|
201
|
+
buildSystemPrompt(supportsTools) {
|
|
202
|
+
let repoMap = '';
|
|
203
|
+
try {
|
|
204
|
+
repoMap = (0, repo_map_1.buildRepoMap)(process.cwd());
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
repoMap = 'Project structure: (unable to scan)';
|
|
208
|
+
}
|
|
209
|
+
// Load persistent memory
|
|
210
|
+
let memoryBlock = '';
|
|
211
|
+
try {
|
|
212
|
+
const memory = new memory_1.MemoryManager(process.cwd());
|
|
213
|
+
memoryBlock = memory.getContextBlock();
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
// memory unavailable
|
|
217
|
+
}
|
|
218
|
+
let prompt = `You are CodeBot, an AI coding assistant. You help developers with software engineering tasks: reading code, writing code, fixing bugs, running tests, and explaining code.
|
|
219
|
+
|
|
220
|
+
Rules:
|
|
221
|
+
- Always read files before editing them.
|
|
222
|
+
- Prefer editing over rewriting entire files.
|
|
223
|
+
- Be concise and direct.
|
|
224
|
+
- Explain what you're doing and why.
|
|
225
|
+
- Use the memory tool to save important context, user preferences, and patterns you learn. Memory persists across sessions.
|
|
226
|
+
|
|
227
|
+
${repoMap}${memoryBlock}`;
|
|
228
|
+
if (!supportsTools) {
|
|
229
|
+
prompt += `
|
|
230
|
+
|
|
231
|
+
To use tools, wrap calls in XML tags:
|
|
232
|
+
<tool_call>{"name": "tool_name", "arguments": {"arg1": "value1"}}</tool_call>
|
|
233
|
+
|
|
234
|
+
Available tools:
|
|
235
|
+
${this.tools.all().map(t => `- ${t.name}: ${t.description}`).join('\n')}`;
|
|
236
|
+
}
|
|
237
|
+
return prompt;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
exports.Agent = Agent;
|
|
241
|
+
async function defaultAskPermission(tool, args) {
|
|
242
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
243
|
+
const summary = Object.entries(args)
|
|
244
|
+
.map(([k, v]) => {
|
|
245
|
+
const val = typeof v === 'string' ? (v.length > 80 ? v.substring(0, 80) + '...' : v) : JSON.stringify(v);
|
|
246
|
+
return ` ${k}: ${val}`;
|
|
247
|
+
})
|
|
248
|
+
.join('\n');
|
|
249
|
+
return new Promise(resolve => {
|
|
250
|
+
rl.question(`\n⚡ ${tool}\n${summary}\nAllow? [y/N] `, answer => {
|
|
251
|
+
rl.close();
|
|
252
|
+
resolve(answer.toLowerCase().startsWith('y'));
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
//# sourceMappingURL=agent.js.map
|
package/dist/banner.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CodeBot AI mascot and CLI banner.
|
|
3
|
+
*
|
|
4
|
+
* Mascot name: Codi
|
|
5
|
+
* Three designs: Pixel Bot, Monitor Bot, Visor Helmet
|
|
6
|
+
*/
|
|
7
|
+
export declare const MASCOT_1 = "\n \u2588\u2588\n \u2584\u2584\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2584\u2584\n \u2588 \u2588\n \u2588 \u2584\u2588\u2588\u2584 \u2584\u2588\u2588\u2584 \u2588\n \u2588 \u2580\u2588\u2588\u2580 \u2580\u2588\u2588\u2580 \u2588\n \u2588 \u2588\n \u2588 \u2580\u2588\u2588\u2588\u2588\u2588\u2588\u2580 \u2588\n \u2580\u2580\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2580\u2580\n";
|
|
8
|
+
export declare const BANNER_1: (version: string, model: string, provider: string, session: string, autonomous: boolean) => string;
|
|
9
|
+
export declare const MASCOT_2 = "\n \u2554\u2557 \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557 \u2554\u2557\n \u2551\u2551 \u2551 \u2551 \u2551\u2551\n \u2551\u2551 \u2551 \u25CF \u25CF \u2551 \u2551\u2551\n \u2551\u2551 \u2551 \u2551 \u2551\u2551\n \u2551\u2551 \u2551 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2551 \u2551\u2551\n \u2551\u2551 \u2551 \u2551 \u2551\u2551\n \u255A\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u255D\n";
|
|
10
|
+
export declare const BANNER_2: (version: string, model: string, provider: string, session: string, autonomous: boolean) => string;
|
|
11
|
+
export declare const MASCOT_3 = "\n \u2584\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2584\n \u2588\u2580 \u2580\u2588\n \u2588 \u2591\u2591\u2588\u2588\u2588\u2588\u2588\u2588\u2591\u2591 \u2588\n \u2588 \u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591 \u2588\n \u2588\u2584 \u2584\u2588\n \u2580\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2580\n";
|
|
12
|
+
export declare const BANNER_3: (version: string, model: string, provider: string, session: string, autonomous: boolean) => string;
|
|
13
|
+
export declare const banner: (version: string, model: string, provider: string, session: string, autonomous: boolean) => string;
|
|
14
|
+
export declare function randomGreeting(): string;
|
|
15
|
+
/**
|
|
16
|
+
* Compact single-line startup for non-TTY / piped usage.
|
|
17
|
+
*/
|
|
18
|
+
export declare function compactBanner(version: string, model: string): string;
|
|
19
|
+
//# sourceMappingURL=banner.d.ts.map
|