coder-agent 2.0.0 → 2.1.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/README.md +7 -7
- package/dist/agent.js +2 -2
- package/dist/index.js +14 -7
- package/dist/memory.js +1 -0
- package/dist/tools.js +33 -18
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ A powerful, zero-dependency, cross-platform CLI coding agent powered by **Google
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
- **Apple-Inspired Design**: A clean, high-contrast, minimalist terminal UI styled in elegant monochrome greys and whites.
|
|
8
|
-
- **Global CLI Execution**: Install once and run anywhere via `coder` (or the `gemini-agent` / `groq-agent` compatibility aliases).
|
|
8
|
+
- **Global CLI Execution**: Install once and run anywhere via `coder-agent` (or the `coder` / `gemini-agent` / `groq-agent` compatibility aliases).
|
|
9
9
|
- **System Automation Tools**:
|
|
10
10
|
- `read_file` / `write_file` / `patch_file` — Segmented reading and patch-based editing.
|
|
11
11
|
- `list_directory` / `find_files` — Recursive structure traversal.
|
|
@@ -13,8 +13,8 @@ A powerful, zero-dependency, cross-platform CLI coding agent powered by **Google
|
|
|
13
13
|
- `web_search` — DuckDuckGo instant answers for documentation retrieval.
|
|
14
14
|
- **Context Persistence**: Local JSON-based persistent configuration stored in `~/.coder-config.json` for key management and default model settings.
|
|
15
15
|
- **Interactive REPL & Single-Shot Modes**:
|
|
16
|
-
- Start the interactive shell with `coder`.
|
|
17
|
-
- Execute a direct command: `coder "build a quicksort function in python"` (exits upon completion).
|
|
16
|
+
- Start the interactive shell with `coder-agent`.
|
|
17
|
+
- Execute a direct command: `coder-agent "build a quicksort function in python"` (exits upon completion).
|
|
18
18
|
|
|
19
19
|
---
|
|
20
20
|
|
|
@@ -39,13 +39,13 @@ npx coder-agent
|
|
|
39
39
|
The agent requires a **Gemini API Key**.
|
|
40
40
|
|
|
41
41
|
### 1. Interactive Bootstrapping
|
|
42
|
-
Simply run `coder`. If no key is configured, the CLI will interactively prompt you for the key and offer to save it globally.
|
|
42
|
+
Simply run `coder-agent`. If no key is configured, the CLI will interactively prompt you for the key and offer to save it globally.
|
|
43
43
|
|
|
44
44
|
### 2. Command Line Flags
|
|
45
45
|
You can save your key globally at any time using flags:
|
|
46
46
|
|
|
47
47
|
```bash
|
|
48
|
-
coder --set-key YOUR_GEMINI_API_KEY
|
|
48
|
+
coder-agent --set-key YOUR_GEMINI_API_KEY
|
|
49
49
|
```
|
|
50
50
|
|
|
51
51
|
### 3. Environment Variables
|
|
@@ -62,13 +62,13 @@ export GEMINI_API_KEY=AIzaSy...
|
|
|
62
62
|
### REPL Mode
|
|
63
63
|
Start an interactive pair programming session:
|
|
64
64
|
```bash
|
|
65
|
-
coder
|
|
65
|
+
coder-agent
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
### Single-Shot Prompt Mode
|
|
69
69
|
Run a prompt directly from the shell:
|
|
70
70
|
```bash
|
|
71
|
-
coder "write a binary search script in Python"
|
|
71
|
+
coder-agent "write a binary search script in Python"
|
|
72
72
|
```
|
|
73
73
|
|
|
74
74
|
### CLI Command Options
|
package/dist/agent.js
CHANGED
|
@@ -163,13 +163,13 @@ export class Agent {
|
|
|
163
163
|
// ── No tool calls → final answer ─────────────────────────────────────
|
|
164
164
|
if (toolCalls.length === 0) {
|
|
165
165
|
if (cleanContent) {
|
|
166
|
-
console.log("\n" + chalk.bold.white("coder ❯ ") + cleanContent);
|
|
166
|
+
console.log("\n" + chalk.bold.white("coder-agent ❯ ") + cleanContent);
|
|
167
167
|
}
|
|
168
168
|
break;
|
|
169
169
|
}
|
|
170
170
|
// If the model returned conversational text along with tool calls, print it cleanly
|
|
171
171
|
if (cleanContent) {
|
|
172
|
-
console.log("\n" + chalk.bold.white("coder ❯ ") + cleanContent);
|
|
172
|
+
console.log("\n" + chalk.bold.white("coder-agent ❯ ") + cleanContent);
|
|
173
173
|
}
|
|
174
174
|
// ── Execute tool calls ────────────────────────────────────────────────
|
|
175
175
|
for (const toolCall of toolCalls) {
|
package/dist/index.js
CHANGED
|
@@ -24,9 +24,9 @@ function printBanner(modelName) {
|
|
|
24
24
|
function printHelp() {
|
|
25
25
|
console.log(chalk.bold.white("\n Coder CLI v1.1\n"));
|
|
26
26
|
console.log(chalk.gray(" Usage:"));
|
|
27
|
-
console.log(chalk.white(" coder — Start the interactive REPL"));
|
|
28
|
-
console.log(chalk.white(" coder [options] — Run with config options"));
|
|
29
|
-
console.log(chalk.white(" coder \"your query here\" — Run in single-shot mode (exits after completion)"));
|
|
27
|
+
console.log(chalk.white(" coder-agent — Start the interactive REPL"));
|
|
28
|
+
console.log(chalk.white(" coder-agent [options] — Run with config options"));
|
|
29
|
+
console.log(chalk.white(" coder-agent \"your query here\" — Run in single-shot mode (exits after completion)"));
|
|
30
30
|
console.log("");
|
|
31
31
|
console.log(chalk.gray(" Options:"));
|
|
32
32
|
console.log(chalk.white(" -h, --help — Show this help screen"));
|
|
@@ -110,7 +110,7 @@ async function main() {
|
|
|
110
110
|
process.exit(0);
|
|
111
111
|
}
|
|
112
112
|
else if (args[i] === "-v" || args[i] === "--version") {
|
|
113
|
-
console.log("coder v1.1.0");
|
|
113
|
+
console.log("coder-agent v1.1.0");
|
|
114
114
|
process.exit(0);
|
|
115
115
|
}
|
|
116
116
|
else {
|
|
@@ -122,7 +122,14 @@ async function main() {
|
|
|
122
122
|
const envKey = process.env.GEMINI_API_KEY || process.env.GROQ_API_KEY;
|
|
123
123
|
let apiKey = envKey || storedKey;
|
|
124
124
|
const storedModel = await getStoredModel();
|
|
125
|
-
|
|
125
|
+
let defaultModel = "gemini-2.5-flash";
|
|
126
|
+
if (storedModel && VALID_MODELS.includes(storedModel)) {
|
|
127
|
+
defaultModel = storedModel;
|
|
128
|
+
}
|
|
129
|
+
if (tempModel && !VALID_MODELS.includes(tempModel)) {
|
|
130
|
+
console.log(chalk.red(`✗ Invalid model: ${tempModel}. Choose one of: ${VALID_MODELS.join(", ")}`));
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
126
133
|
const modelToUse = tempModel || defaultModel;
|
|
127
134
|
// Save model if specified without query
|
|
128
135
|
if (tempModel && queryArgs.length === 0) {
|
|
@@ -165,7 +172,7 @@ async function main() {
|
|
|
165
172
|
terminal: true,
|
|
166
173
|
});
|
|
167
174
|
const prompt = () => {
|
|
168
|
-
rl.question(chalk.gray("\ncoder ❯ "), async (input) => {
|
|
175
|
+
rl.question(chalk.gray("\ncoder-agent ❯ "), async (input) => {
|
|
169
176
|
const trimmed = input.trim();
|
|
170
177
|
if (!trimmed) {
|
|
171
178
|
prompt();
|
|
@@ -211,7 +218,7 @@ async function main() {
|
|
|
211
218
|
/exit — Exit Coder`));
|
|
212
219
|
console.log(chalk.bold.white("\n API Keys & Configuration:"));
|
|
213
220
|
console.log(chalk.gray(` • Stored at: ~/.coder-config.json
|
|
214
|
-
• To change key: Exit and run 'coder --set-key <key>'
|
|
221
|
+
• To change key: Exit and run 'coder-agent --set-key <key>'
|
|
215
222
|
• Env variable option: GEMINI_API_KEY`));
|
|
216
223
|
prompt();
|
|
217
224
|
return;
|
package/dist/memory.js
CHANGED
|
@@ -11,6 +11,7 @@ PRINCIPLES & SYSTEM PROTOCOLS FOR ERROR-FREE EXECUTION:
|
|
|
11
11
|
3. Precise Target Editing: Prefer patching files (using patch_file) over complete overwriting. Ensure targeted matches are unique and match exactly, leaving existing unrelated functions/comments intact.
|
|
12
12
|
4. Auto-Verification Loop: After any code or file edit, you MUST run the appropriate compiler, type-check, build script, or test tool (e.g. npm run build, npx tsc, pytest, cargo build, etc.) to verify your changes are syntactically and logically correct. If compilation fails, diagnose the error and patch it immediately.
|
|
13
13
|
5. Autonomous Troubleshooting: If a command fails or times out, inspect the codebase or script to see why it hangs or fails. Do not blindly edit package scripts or configs.
|
|
14
|
+
6. Automated Diagnostic Parsing: When the user pastes IDE problem diagnostics (e.g., JSON blocks containing "resource", "message", "startLineNumber"), stack traces, or compiler errors, parse the diagnostic payload autonomously. Extract the file path and line number, locate the file inside the workspace (resolving drive formats like '/c:/...' to standard local paths, or searching for the filename if needed), read the target lines, and formulate a fix. Do not ask the user for clarifying questions (such as "where is this error?") if the path and error message are already present in the diagnostic block.
|
|
14
15
|
|
|
15
16
|
Guidelines:
|
|
16
17
|
- Be concise in your explanations; let code and command output speak for itself.
|
package/dist/tools.js
CHANGED
|
@@ -3,7 +3,15 @@ import { promisify } from "util";
|
|
|
3
3
|
import * as fs from "fs/promises";
|
|
4
4
|
import * as path from "path";
|
|
5
5
|
const execAsync = promisify(exec);
|
|
6
|
-
//
|
|
6
|
+
// Helper to normalize file paths, especially handling leading slashes on Windows drive letters (e.g. /c:/... -> c:/...)
|
|
7
|
+
function normalizeFilePath(p) {
|
|
8
|
+
let normalized = p;
|
|
9
|
+
if (process.platform === "win32" && /^\/[a-zA-Z]:/.test(normalized)) {
|
|
10
|
+
normalized = normalized.slice(1);
|
|
11
|
+
}
|
|
12
|
+
return path.normalize(normalized);
|
|
13
|
+
}
|
|
14
|
+
// ─── Tool Definitions (sent to Gemini) ───────────────────────────────────────
|
|
7
15
|
export const TOOL_DEFINITIONS = [
|
|
8
16
|
{
|
|
9
17
|
type: "function",
|
|
@@ -90,7 +98,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
90
98
|
},
|
|
91
99
|
required: ["query"]
|
|
92
100
|
}
|
|
93
|
-
}
|
|
101
|
+
},
|
|
94
102
|
},
|
|
95
103
|
{
|
|
96
104
|
type: "function",
|
|
@@ -106,7 +114,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
106
114
|
},
|
|
107
115
|
required: ["file_path", "start_line", "end_line"]
|
|
108
116
|
}
|
|
109
|
-
}
|
|
117
|
+
},
|
|
110
118
|
},
|
|
111
119
|
{
|
|
112
120
|
type: "function",
|
|
@@ -121,7 +129,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
121
129
|
},
|
|
122
130
|
required: ["query"]
|
|
123
131
|
}
|
|
124
|
-
}
|
|
132
|
+
},
|
|
125
133
|
},
|
|
126
134
|
{
|
|
127
135
|
type: "function",
|
|
@@ -137,13 +145,14 @@ export const TOOL_DEFINITIONS = [
|
|
|
137
145
|
},
|
|
138
146
|
required: ["file_path", "target_code", "replacement_code"]
|
|
139
147
|
}
|
|
140
|
-
}
|
|
148
|
+
},
|
|
141
149
|
}
|
|
142
150
|
];
|
|
143
151
|
// ─── Tool Implementations ────────────────────────────────────────────────────
|
|
144
152
|
export async function read_file({ file_path }) {
|
|
145
153
|
try {
|
|
146
|
-
const
|
|
154
|
+
const targetPath = normalizeFilePath(file_path);
|
|
155
|
+
const content = await fs.readFile(targetPath, "utf-8");
|
|
147
156
|
return content;
|
|
148
157
|
}
|
|
149
158
|
catch (e) {
|
|
@@ -152,9 +161,10 @@ export async function read_file({ file_path }) {
|
|
|
152
161
|
}
|
|
153
162
|
export async function write_file({ file_path, content }) {
|
|
154
163
|
try {
|
|
155
|
-
|
|
156
|
-
await fs.
|
|
157
|
-
|
|
164
|
+
const targetPath = normalizeFilePath(file_path);
|
|
165
|
+
await fs.mkdir(path.dirname(targetPath), { recursive: true });
|
|
166
|
+
await fs.writeFile(targetPath, content, "utf-8");
|
|
167
|
+
return `✓ Written ${content.length} chars to ${targetPath}`;
|
|
158
168
|
}
|
|
159
169
|
catch (e) {
|
|
160
170
|
return `ERROR: ${e.message}`;
|
|
@@ -162,7 +172,8 @@ export async function write_file({ file_path, content }) {
|
|
|
162
172
|
}
|
|
163
173
|
export async function list_directory({ dir_path = "." }) {
|
|
164
174
|
try {
|
|
165
|
-
const
|
|
175
|
+
const targetPath = normalizeFilePath(dir_path);
|
|
176
|
+
const entries = await fs.readdir(targetPath, { withFileTypes: true });
|
|
166
177
|
const lines = entries.map((e) => `${e.isDirectory() ? "📁" : "📄"} ${e.name}`);
|
|
167
178
|
return lines.join("\n") || "(empty)";
|
|
168
179
|
}
|
|
@@ -174,12 +185,13 @@ export async function run_shell({ command, cwd }) {
|
|
|
174
185
|
try {
|
|
175
186
|
let targetCwd = process.cwd();
|
|
176
187
|
if (cwd) {
|
|
188
|
+
const targetCwdPath = normalizeFilePath(cwd);
|
|
177
189
|
try {
|
|
178
|
-
const stats = await fs.stat(
|
|
190
|
+
const stats = await fs.stat(targetCwdPath);
|
|
179
191
|
if (!stats.isDirectory()) {
|
|
180
192
|
return `ERROR: The specified working directory (cwd) "${cwd}" is a file, not a directory.`;
|
|
181
193
|
}
|
|
182
|
-
targetCwd = path.resolve(
|
|
194
|
+
targetCwd = path.resolve(targetCwdPath);
|
|
183
195
|
}
|
|
184
196
|
catch (err) {
|
|
185
197
|
return `ERROR: The specified working directory (cwd) "${cwd}" does not exist. Please specify a valid, existing directory path or omit 'cwd'.`;
|
|
@@ -225,7 +237,7 @@ async function walkDir(dir, fileList = []) {
|
|
|
225
237
|
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
226
238
|
for (const entry of entries) {
|
|
227
239
|
const fullPath = path.join(dir, entry.name);
|
|
228
|
-
if (["node_modules", ".git", "dist", "build", "__pycache__", ".next", "out"].includes(entry.name)) {
|
|
240
|
+
if (["node_modules", ".git", "dist", "build", "__pycache__", ".next", "out", ".coder", ".gemini-agent", ".groq-agent"].includes(entry.name)) {
|
|
229
241
|
continue;
|
|
230
242
|
}
|
|
231
243
|
if (entry.isDirectory()) {
|
|
@@ -241,10 +253,11 @@ async function walkDir(dir, fileList = []) {
|
|
|
241
253
|
}
|
|
242
254
|
export async function find_files({ query, dir_path = "." }) {
|
|
243
255
|
try {
|
|
244
|
-
const
|
|
256
|
+
const targetPath = normalizeFilePath(dir_path);
|
|
257
|
+
const allFiles = await walkDir(targetPath);
|
|
245
258
|
const lowercaseQuery = query.toLowerCase();
|
|
246
259
|
const matches = allFiles
|
|
247
|
-
.map(f => path.relative(
|
|
260
|
+
.map(f => path.relative(targetPath, f))
|
|
248
261
|
.filter(f => f.toLowerCase().includes(lowercaseQuery));
|
|
249
262
|
if (matches.length === 0) {
|
|
250
263
|
return `No files found matching: "${query}"`;
|
|
@@ -257,7 +270,8 @@ export async function find_files({ query, dir_path = "." }) {
|
|
|
257
270
|
}
|
|
258
271
|
export async function read_file_lines({ file_path, start_line, end_line }) {
|
|
259
272
|
try {
|
|
260
|
-
const
|
|
273
|
+
const targetPath = normalizeFilePath(file_path);
|
|
274
|
+
const content = await fs.readFile(targetPath, "utf-8");
|
|
261
275
|
const lines = content.split(/\r?\n/);
|
|
262
276
|
const totalLines = lines.length;
|
|
263
277
|
const start = Math.max(1, start_line);
|
|
@@ -309,7 +323,8 @@ export async function search_grep({ query, is_regex = false }) {
|
|
|
309
323
|
}
|
|
310
324
|
export async function patch_file({ file_path, target_code, replacement_code }) {
|
|
311
325
|
try {
|
|
312
|
-
const
|
|
326
|
+
const targetPath = normalizeFilePath(file_path);
|
|
327
|
+
const content = await fs.readFile(targetPath, "utf-8");
|
|
313
328
|
if (!content.includes(target_code)) {
|
|
314
329
|
return `ERROR: Target code not found in file ${file_path}. Please verify target content.`;
|
|
315
330
|
}
|
|
@@ -318,7 +333,7 @@ export async function patch_file({ file_path, target_code, replacement_code }) {
|
|
|
318
333
|
return `ERROR: Target code matches ${occurrences} times in file ${file_path}. Please provide more unique context to target the exact edit.`;
|
|
319
334
|
}
|
|
320
335
|
const newContent = content.replace(target_code, replacement_code);
|
|
321
|
-
await fs.writeFile(
|
|
336
|
+
await fs.writeFile(targetPath, newContent, "utf-8");
|
|
322
337
|
return `✓ Patched file ${file_path} successfully.`;
|
|
323
338
|
}
|
|
324
339
|
catch (e) {
|