codemaxxing 0.2.1 β 0.3.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 +72 -6
- package/dist/agent.d.ts +27 -0
- package/dist/agent.js +108 -2
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +6 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.js +9 -0
- package/dist/exec.d.ts +7 -0
- package/dist/exec.js +154 -0
- package/dist/index.js +83 -4
- package/dist/utils/context.d.ts +9 -1
- package/dist/utils/context.js +31 -11
- package/dist/utils/lint.d.ts +13 -0
- package/dist/utils/lint.js +108 -0
- package/package.json +1 -1
- package/src/agent.ts +124 -2
- package/src/cli.ts +5 -1
- package/src/config.ts +11 -0
- package/src/exec.ts +171 -0
- package/src/index.tsx +81 -3
- package/src/utils/context.ts +34 -12
- package/src/utils/lint.ts +116 -0
package/README.md
CHANGED
|
@@ -121,11 +121,56 @@ codemaxxing --provider openrouter
|
|
|
121
121
|
### π₯ Streaming Tokens
|
|
122
122
|
Real-time token display. See the model think, not just the final answer.
|
|
123
123
|
|
|
124
|
-
### β οΈ Tool Approval
|
|
125
|
-
Dangerous operations
|
|
124
|
+
### β οΈ Tool Approval + Diff Preview
|
|
125
|
+
Dangerous operations require your approval. File writes show a **unified diff** of what will change before you say yes. Press `y` to allow, `n` to deny, `a` to always allow.
|
|
126
|
+
|
|
127
|
+
### ποΈ Architect Mode
|
|
128
|
+
Dual-model planning. A "planner" model reasons through the approach, then your editor model executes the changes.
|
|
129
|
+
- `/architect` β toggle on/off
|
|
130
|
+
- `/architect claude-3-5-sonnet` β set the planner model
|
|
131
|
+
- Great for pairing expensive reasoning models with fast editors
|
|
132
|
+
|
|
133
|
+
### π§ Skills System (21 Built-In)
|
|
134
|
+
Downloadable skill packs that teach the agent domain expertise. Ships with 21 built-in skills:
|
|
135
|
+
|
|
136
|
+
**Frontend:** react-expert, nextjs-app, tailwind-ui, svelte-kit
|
|
137
|
+
**Mobile:** react-native, swift-ios, flutter
|
|
138
|
+
**Backend:** python-pro, node-backend, go-backend, rust-systems
|
|
139
|
+
**Data:** sql-master, supabase
|
|
140
|
+
**Practices:** typescript-strict, api-designer, test-engineer, doc-writer, security-audit, devops-toolkit, git-workflow
|
|
141
|
+
**Game Dev:** unity-csharp
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
/skills # Browse & install from registry
|
|
145
|
+
/skills install X # Quick install
|
|
146
|
+
/skills on/off X # Toggle per session
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Project-level config: add `.codemaxxing/skills.json` to scope skills per project.
|
|
150
|
+
|
|
151
|
+
### π CODEMAXXING.md β Project Rules
|
|
152
|
+
Drop a `CODEMAXXING.md` in your project root for project-specific instructions. Auto-loaded every session. Also supports `.cursorrules` for Cursor migrants.
|
|
153
|
+
|
|
154
|
+
### π§ Auto-Lint
|
|
155
|
+
Automatically runs your linter after every file edit and feeds errors back to the model for auto-fix. Detects eslint, biome, ruff, clippy, golangci-lint, and more.
|
|
156
|
+
- `/lint on` / `/lint off` β toggle (ON by default)
|
|
126
157
|
|
|
127
158
|
### π Smart Context (Repo Map)
|
|
128
|
-
|
|
159
|
+
Scans your codebase and builds a map of functions, classes, and types. The model knows what exists where without reading every file.
|
|
160
|
+
|
|
161
|
+
### π¦ Context Compression
|
|
162
|
+
When conversation history exceeds 80k tokens, older messages are automatically summarized to free up context. Configurable via `contextCompressionThreshold`.
|
|
163
|
+
|
|
164
|
+
### π° Cost Tracking
|
|
165
|
+
Per-session token usage and estimated cost in the status bar. Pricing for 20+ common models. Saved to session history.
|
|
166
|
+
|
|
167
|
+
### π₯οΈ Headless/CI Mode
|
|
168
|
+
Run codemaxxing in scripts and pipelines without the TUI:
|
|
169
|
+
```bash
|
|
170
|
+
codemaxxing exec "add error handling to api.ts"
|
|
171
|
+
codemaxxing exec --auto-approve "fix all lint errors"
|
|
172
|
+
echo "add tests" | codemaxxing exec
|
|
173
|
+
```
|
|
129
174
|
|
|
130
175
|
### π Git Integration
|
|
131
176
|
Opt-in git commands built in:
|
|
@@ -138,18 +183,23 @@ Opt-in git commands built in:
|
|
|
138
183
|
### πΎ Session Persistence
|
|
139
184
|
Conversations auto-save to SQLite. Pick up where you left off:
|
|
140
185
|
- `/sessions` β list past sessions
|
|
186
|
+
- `/session delete` β remove a session
|
|
141
187
|
- `/resume` β interactive session picker
|
|
142
188
|
|
|
143
189
|
### π Multi-Provider
|
|
144
190
|
Switch models mid-session without restarting:
|
|
145
191
|
- `/model gpt-4o` β switch to a different model
|
|
146
192
|
- `/models` β list available models from your provider
|
|
193
|
+
- Native Anthropic API support (not just OpenAI-compatible)
|
|
194
|
+
|
|
195
|
+
### π¨ 14 Themes
|
|
196
|
+
`/theme` to browse: cyberpunk-neon, dracula, gruvbox, nord, catppuccin, tokyo-night, one-dark, rosΓ©-pine, synthwave, blood-moon, mono, solarized, hacker, acid
|
|
147
197
|
|
|
148
198
|
### π Authentication
|
|
149
|
-
One command to connect any LLM provider. OpenRouter OAuth
|
|
199
|
+
One command to connect any LLM provider. OpenRouter OAuth, Anthropic subscription linking, Codex/Qwen CLI import, GitHub Copilot device flow, or manual API keys.
|
|
150
200
|
|
|
151
201
|
### π Smart Paste
|
|
152
|
-
|
|
202
|
+
Multi-line pastes collapse into `[Pasted text #1 +N lines]` badges.
|
|
153
203
|
|
|
154
204
|
### β¨οΈ Slash Commands
|
|
155
205
|
Type `/` for autocomplete suggestions. Arrow keys to navigate, Tab or Enter to select.
|
|
@@ -159,11 +209,17 @@ Type `/` for autocomplete suggestions. Arrow keys to navigate, Tab or Enter to s
|
|
|
159
209
|
| Command | Description |
|
|
160
210
|
|---------|-------------|
|
|
161
211
|
| `/help` | Show all commands |
|
|
212
|
+
| `/connect` | Retry LLM connection |
|
|
162
213
|
| `/login` | Interactive auth setup |
|
|
214
|
+
| `/architect` | Toggle architect mode / set model |
|
|
215
|
+
| `/skills` | Browse, install, manage skills |
|
|
216
|
+
| `/lint on/off` | Toggle auto-linting |
|
|
163
217
|
| `/model <name>` | Switch model mid-session |
|
|
164
218
|
| `/models` | List available models |
|
|
219
|
+
| `/theme` | Switch color theme |
|
|
165
220
|
| `/map` | Show repository map |
|
|
166
221
|
| `/sessions` | List past sessions |
|
|
222
|
+
| `/session delete` | Delete a session |
|
|
167
223
|
| `/resume` | Resume a past session |
|
|
168
224
|
| `/reset` | Clear conversation |
|
|
169
225
|
| `/context` | Show message count + tokens |
|
|
@@ -174,7 +230,17 @@ Type `/` for autocomplete suggestions. Arrow keys to navigate, Tab or Enter to s
|
|
|
174
230
|
| `/git on/off` | Toggle auto-commits |
|
|
175
231
|
| `/quit` | Exit |
|
|
176
232
|
|
|
177
|
-
## CLI
|
|
233
|
+
## CLI
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
codemaxxing # Start TUI
|
|
237
|
+
codemaxxing login # Auth setup
|
|
238
|
+
codemaxxing auth list # Show saved credentials
|
|
239
|
+
codemaxxing exec "prompt" # Headless mode (no TUI)
|
|
240
|
+
codemaxxing exec --auto-approve "x" # Skip approval prompts
|
|
241
|
+
codemaxxing exec --json "x" # JSON output for scripts
|
|
242
|
+
echo "fix tests" | codemaxxing exec # Pipe from stdin
|
|
243
|
+
```
|
|
178
244
|
|
|
179
245
|
```
|
|
180
246
|
-m, --model <model> Model name to use
|
package/dist/agent.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ export interface AgentOptions {
|
|
|
11
11
|
onToolApproval?: (name: string, args: Record<string, unknown>, diff?: string) => Promise<"yes" | "no" | "always">;
|
|
12
12
|
onGitCommit?: (message: string) => void;
|
|
13
13
|
onContextCompressed?: (oldTokens: number, newTokens: number) => void;
|
|
14
|
+
onArchitectPlan?: (plan: string) => void;
|
|
15
|
+
onLintResult?: (file: string, errors: string) => void;
|
|
14
16
|
contextCompressionThreshold?: number;
|
|
15
17
|
}
|
|
16
18
|
export declare class CodingAgent {
|
|
@@ -35,6 +37,10 @@ export declare class CodingAgent {
|
|
|
35
37
|
private systemPrompt;
|
|
36
38
|
private compressionThreshold;
|
|
37
39
|
private sessionDisabledSkills;
|
|
40
|
+
private projectRulesSource;
|
|
41
|
+
private architectModel;
|
|
42
|
+
private autoLintEnabled;
|
|
43
|
+
private detectedLinter;
|
|
38
44
|
constructor(options: AgentOptions);
|
|
39
45
|
/**
|
|
40
46
|
* Initialize the agent β call this after constructor to build async context
|
|
@@ -53,6 +59,10 @@ export declare class CodingAgent {
|
|
|
53
59
|
* Rebuild the repo map (useful after file changes)
|
|
54
60
|
*/
|
|
55
61
|
refreshRepoMap(): Promise<string>;
|
|
62
|
+
/**
|
|
63
|
+
* Send a message, routing through architect model if enabled
|
|
64
|
+
*/
|
|
65
|
+
send(userMessage: string): Promise<string>;
|
|
56
66
|
/**
|
|
57
67
|
* Stream a response from the model.
|
|
58
68
|
* Assembles tool call chunks, emits tokens in real-time,
|
|
@@ -97,5 +107,22 @@ export declare class CodingAgent {
|
|
|
97
107
|
getSessionDisabledSkills(): Set<string>;
|
|
98
108
|
getActiveSkillCount(): number;
|
|
99
109
|
getCwd(): string;
|
|
110
|
+
getProjectRulesSource(): string | null;
|
|
111
|
+
setArchitectModel(model: string | null): void;
|
|
112
|
+
getArchitectModel(): string | null;
|
|
113
|
+
setAutoLint(enabled: boolean): void;
|
|
114
|
+
isAutoLintEnabled(): boolean;
|
|
115
|
+
getDetectedLinter(): {
|
|
116
|
+
command: string;
|
|
117
|
+
name: string;
|
|
118
|
+
} | null;
|
|
119
|
+
setDetectedLinter(linter: {
|
|
120
|
+
command: string;
|
|
121
|
+
name: string;
|
|
122
|
+
} | null): void;
|
|
123
|
+
/**
|
|
124
|
+
* Run the architect model to generate a plan, then feed to editor model
|
|
125
|
+
*/
|
|
126
|
+
private architectChat;
|
|
100
127
|
reset(): void;
|
|
101
128
|
}
|
package/dist/agent.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import OpenAI from "openai";
|
|
2
2
|
import Anthropic from "@anthropic-ai/sdk";
|
|
3
3
|
import { FILE_TOOLS, executeTool, generateDiff, getExistingContent } from "./tools/files.js";
|
|
4
|
-
import {
|
|
4
|
+
import { detectLinter, runLinter } from "./utils/lint.js";
|
|
5
|
+
import { buildProjectContext, getSystemPrompt, loadProjectRules } from "./utils/context.js";
|
|
5
6
|
import { isGitRepo, autoCommit } from "./utils/git.js";
|
|
6
7
|
import { buildSkillPrompts, getActiveSkillCount } from "./utils/skills.js";
|
|
7
8
|
import { createSession, saveMessage, updateTokenEstimate, updateSessionCost, loadMessages } from "./utils/sessions.js";
|
|
@@ -73,6 +74,10 @@ export class CodingAgent {
|
|
|
73
74
|
systemPrompt = "";
|
|
74
75
|
compressionThreshold;
|
|
75
76
|
sessionDisabledSkills = new Set();
|
|
77
|
+
projectRulesSource = null;
|
|
78
|
+
architectModel = null;
|
|
79
|
+
autoLintEnabled = true;
|
|
80
|
+
detectedLinter = null;
|
|
76
81
|
constructor(options) {
|
|
77
82
|
this.options = options;
|
|
78
83
|
this.providerType = options.provider.type || "openai";
|
|
@@ -102,7 +107,12 @@ export class CodingAgent {
|
|
|
102
107
|
async init() {
|
|
103
108
|
const context = await buildProjectContext(this.cwd);
|
|
104
109
|
const skillPrompts = buildSkillPrompts(this.cwd, this.sessionDisabledSkills);
|
|
105
|
-
|
|
110
|
+
const rules = loadProjectRules(this.cwd);
|
|
111
|
+
if (rules)
|
|
112
|
+
this.projectRulesSource = rules.source;
|
|
113
|
+
this.systemPrompt = await getSystemPrompt(context, skillPrompts, rules?.content ?? "");
|
|
114
|
+
// Detect project linter
|
|
115
|
+
this.detectedLinter = detectLinter(this.cwd);
|
|
106
116
|
this.messages = [
|
|
107
117
|
{ role: "system", content: this.systemPrompt },
|
|
108
118
|
];
|
|
@@ -138,6 +148,15 @@ export class CodingAgent {
|
|
|
138
148
|
this.repoMap = await buildRepoMap(this.cwd);
|
|
139
149
|
return this.repoMap;
|
|
140
150
|
}
|
|
151
|
+
/**
|
|
152
|
+
* Send a message, routing through architect model if enabled
|
|
153
|
+
*/
|
|
154
|
+
async send(userMessage) {
|
|
155
|
+
if (this.architectModel) {
|
|
156
|
+
return this.architectChat(userMessage);
|
|
157
|
+
}
|
|
158
|
+
return this.chat(userMessage);
|
|
159
|
+
}
|
|
141
160
|
/**
|
|
142
161
|
* Stream a response from the model.
|
|
143
162
|
* Assembles tool call chunks, emits tokens in real-time,
|
|
@@ -296,6 +315,22 @@ export class CodingAgent {
|
|
|
296
315
|
this.options.onGitCommit?.(`write ${path}`);
|
|
297
316
|
}
|
|
298
317
|
}
|
|
318
|
+
// Auto-lint after successful write_file
|
|
319
|
+
if (this.autoLintEnabled && this.detectedLinter && toolCall.name === "write_file" && result.startsWith("β
")) {
|
|
320
|
+
const filePath = String(args.path ?? "");
|
|
321
|
+
const lintErrors = runLinter(this.detectedLinter, filePath, this.cwd);
|
|
322
|
+
if (lintErrors) {
|
|
323
|
+
this.options.onLintResult?.(filePath, lintErrors);
|
|
324
|
+
const lintMsg = {
|
|
325
|
+
role: "tool",
|
|
326
|
+
tool_call_id: toolCall.id,
|
|
327
|
+
content: result + `\n\nLint errors detected in ${filePath}:\n${lintErrors}\nPlease fix these issues.`,
|
|
328
|
+
};
|
|
329
|
+
this.messages.push(lintMsg);
|
|
330
|
+
saveMessage(this.sessionId, lintMsg);
|
|
331
|
+
continue; // skip the normal tool message push
|
|
332
|
+
}
|
|
333
|
+
}
|
|
299
334
|
const toolMsg = {
|
|
300
335
|
role: "tool",
|
|
301
336
|
tool_call_id: toolCall.id,
|
|
@@ -477,6 +512,22 @@ export class CodingAgent {
|
|
|
477
512
|
this.options.onGitCommit?.(`write ${path}`);
|
|
478
513
|
}
|
|
479
514
|
}
|
|
515
|
+
// Auto-lint after successful write_file
|
|
516
|
+
if (this.autoLintEnabled && this.detectedLinter && toolCall.name === "write_file" && result.startsWith("β
")) {
|
|
517
|
+
const filePath = String(args.path ?? "");
|
|
518
|
+
const lintErrors = runLinter(this.detectedLinter, filePath, this.cwd);
|
|
519
|
+
if (lintErrors) {
|
|
520
|
+
this.options.onLintResult?.(filePath, lintErrors);
|
|
521
|
+
const lintMsg = {
|
|
522
|
+
role: "tool",
|
|
523
|
+
tool_call_id: toolCall.id,
|
|
524
|
+
content: result + `\n\nLint errors detected in ${filePath}:\n${lintErrors}\nPlease fix these issues.`,
|
|
525
|
+
};
|
|
526
|
+
this.messages.push(lintMsg);
|
|
527
|
+
saveMessage(this.sessionId, lintMsg);
|
|
528
|
+
continue;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
480
531
|
const toolMsg = {
|
|
481
532
|
role: "tool",
|
|
482
533
|
tool_call_id: toolCall.id,
|
|
@@ -632,6 +683,61 @@ export class CodingAgent {
|
|
|
632
683
|
getCwd() {
|
|
633
684
|
return this.cwd;
|
|
634
685
|
}
|
|
686
|
+
getProjectRulesSource() {
|
|
687
|
+
return this.projectRulesSource;
|
|
688
|
+
}
|
|
689
|
+
setArchitectModel(model) {
|
|
690
|
+
this.architectModel = model;
|
|
691
|
+
}
|
|
692
|
+
getArchitectModel() {
|
|
693
|
+
return this.architectModel;
|
|
694
|
+
}
|
|
695
|
+
setAutoLint(enabled) {
|
|
696
|
+
this.autoLintEnabled = enabled;
|
|
697
|
+
}
|
|
698
|
+
isAutoLintEnabled() {
|
|
699
|
+
return this.autoLintEnabled;
|
|
700
|
+
}
|
|
701
|
+
getDetectedLinter() {
|
|
702
|
+
return this.detectedLinter;
|
|
703
|
+
}
|
|
704
|
+
setDetectedLinter(linter) {
|
|
705
|
+
this.detectedLinter = linter;
|
|
706
|
+
}
|
|
707
|
+
/**
|
|
708
|
+
* Run the architect model to generate a plan, then feed to editor model
|
|
709
|
+
*/
|
|
710
|
+
async architectChat(userMessage) {
|
|
711
|
+
const architectSystemPrompt = "You are a senior software architect. Analyze the request and create a detailed implementation plan. List exactly which files to modify, what changes to make, and in what order. Do NOT write code β just plan.";
|
|
712
|
+
let plan = "";
|
|
713
|
+
if (this.providerType === "anthropic" && this.anthropicClient) {
|
|
714
|
+
const response = await this.anthropicClient.messages.create({
|
|
715
|
+
model: this.architectModel,
|
|
716
|
+
max_tokens: this.maxTokens,
|
|
717
|
+
system: architectSystemPrompt,
|
|
718
|
+
messages: [{ role: "user", content: userMessage }],
|
|
719
|
+
});
|
|
720
|
+
plan = response.content
|
|
721
|
+
.filter((b) => b.type === "text")
|
|
722
|
+
.map((b) => b.text)
|
|
723
|
+
.join("");
|
|
724
|
+
}
|
|
725
|
+
else {
|
|
726
|
+
const response = await this.client.chat.completions.create({
|
|
727
|
+
model: this.architectModel,
|
|
728
|
+
max_tokens: this.maxTokens,
|
|
729
|
+
messages: [
|
|
730
|
+
{ role: "system", content: architectSystemPrompt },
|
|
731
|
+
{ role: "user", content: userMessage },
|
|
732
|
+
],
|
|
733
|
+
});
|
|
734
|
+
plan = response.choices[0]?.message?.content ?? "(no plan generated)";
|
|
735
|
+
}
|
|
736
|
+
this.options.onArchitectPlan?.(plan);
|
|
737
|
+
// Feed plan + original request to the editor model
|
|
738
|
+
const editorPrompt = `## Architect Plan\n${plan}\n\n## Original Request\n${userMessage}\n\nExecute the plan above. Follow it step by step.`;
|
|
739
|
+
return this.chat(editorPrompt);
|
|
740
|
+
}
|
|
635
741
|
reset() {
|
|
636
742
|
const systemMsg = this.messages[0];
|
|
637
743
|
this.messages = [systemMsg];
|
package/dist/cli.d.ts
CHANGED
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
3
|
* Codemaxxing CLI entry point
|
|
4
|
-
* Routes subcommands (login, auth) to
|
|
4
|
+
* Routes subcommands (login, auth, exec) to handlers, everything else to the TUI
|
|
5
5
|
*/
|
|
6
6
|
import { spawn } from "node:child_process";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
@@ -21,6 +21,11 @@ if (subcmd === "login" || subcmd === "auth") {
|
|
|
21
21
|
});
|
|
22
22
|
child.on("exit", (code) => process.exit(code ?? 0));
|
|
23
23
|
}
|
|
24
|
+
else if (subcmd === "exec") {
|
|
25
|
+
// Headless/CI mode β no TUI
|
|
26
|
+
const { runExec } = await import("./exec.js");
|
|
27
|
+
await runExec(process.argv.slice(3));
|
|
28
|
+
}
|
|
24
29
|
else {
|
|
25
30
|
// TUI mode β import directly (not spawn) to preserve raw stdin
|
|
26
31
|
await import("./index.js");
|
package/dist/config.d.ts
CHANGED
package/dist/config.js
CHANGED
|
@@ -55,6 +55,7 @@ codemaxxing β your code. your model. no excuses.
|
|
|
55
55
|
|
|
56
56
|
Usage:
|
|
57
57
|
codemaxxing [options]
|
|
58
|
+
codemaxxing exec "prompt" [exec-options]
|
|
58
59
|
|
|
59
60
|
Options:
|
|
60
61
|
-m, --model <model> Model name to use
|
|
@@ -63,11 +64,19 @@ Options:
|
|
|
63
64
|
-u, --base-url <url> Base URL for the provider API
|
|
64
65
|
-h, --help Show this help
|
|
65
66
|
|
|
67
|
+
Exec options (headless/CI mode):
|
|
68
|
+
--auto-approve Skip tool approval prompts
|
|
69
|
+
--json Output JSON instead of streaming text
|
|
70
|
+
-m, --model <model> Model to use
|
|
71
|
+
-p, --provider <name> Provider profile
|
|
72
|
+
|
|
66
73
|
Examples:
|
|
67
74
|
codemaxxing # Auto-detect local LLM
|
|
68
75
|
codemaxxing -m gpt-4o -u https://api.openai.com/v1 -k sk-...
|
|
69
76
|
codemaxxing -p openrouter # Use saved provider profile
|
|
70
77
|
codemaxxing -m qwen3.5-35b # Override model only
|
|
78
|
+
codemaxxing exec "fix the failing tests" # Headless mode
|
|
79
|
+
echo "explain this code" | codemaxxing exec # Pipe input
|
|
71
80
|
|
|
72
81
|
Config: ~/.codemaxxing/settings.json
|
|
73
82
|
`);
|
package/dist/exec.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Headless/CI execution mode β runs agent without TUI
|
|
3
|
+
* Usage: codemaxxing exec "your prompt here"
|
|
4
|
+
* Flags: --auto-approve, --json, --model <model>, --provider <name>
|
|
5
|
+
* Supports stdin pipe: echo "fix the tests" | codemaxxing exec
|
|
6
|
+
*/
|
|
7
|
+
export declare function runExec(argv: string[]): Promise<void>;
|
package/dist/exec.js
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Headless/CI execution mode β runs agent without TUI
|
|
3
|
+
* Usage: codemaxxing exec "your prompt here"
|
|
4
|
+
* Flags: --auto-approve, --json, --model <model>, --provider <name>
|
|
5
|
+
* Supports stdin pipe: echo "fix the tests" | codemaxxing exec
|
|
6
|
+
*/
|
|
7
|
+
import { CodingAgent } from "./agent.js";
|
|
8
|
+
import { loadConfig, applyOverrides, detectLocalProvider } from "./config.js";
|
|
9
|
+
function parseExecArgs(argv) {
|
|
10
|
+
const args = {
|
|
11
|
+
prompt: "",
|
|
12
|
+
autoApprove: false,
|
|
13
|
+
json: false,
|
|
14
|
+
};
|
|
15
|
+
const positional = [];
|
|
16
|
+
for (let i = 0; i < argv.length; i++) {
|
|
17
|
+
const arg = argv[i];
|
|
18
|
+
const next = argv[i + 1];
|
|
19
|
+
if (arg === "--auto-approve") {
|
|
20
|
+
args.autoApprove = true;
|
|
21
|
+
}
|
|
22
|
+
else if (arg === "--json") {
|
|
23
|
+
args.json = true;
|
|
24
|
+
}
|
|
25
|
+
else if ((arg === "--model" || arg === "-m") && next) {
|
|
26
|
+
args.model = next;
|
|
27
|
+
i++;
|
|
28
|
+
}
|
|
29
|
+
else if ((arg === "--provider" || arg === "-p") && next) {
|
|
30
|
+
args.provider = next;
|
|
31
|
+
i++;
|
|
32
|
+
}
|
|
33
|
+
else if (!arg.startsWith("-")) {
|
|
34
|
+
positional.push(arg);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
args.prompt = positional.join(" ");
|
|
38
|
+
return args;
|
|
39
|
+
}
|
|
40
|
+
async function readStdin() {
|
|
41
|
+
// Check if stdin has data (piped input)
|
|
42
|
+
if (process.stdin.isTTY)
|
|
43
|
+
return "";
|
|
44
|
+
return new Promise((resolve) => {
|
|
45
|
+
let data = "";
|
|
46
|
+
process.stdin.setEncoding("utf-8");
|
|
47
|
+
process.stdin.on("data", (chunk) => { data += chunk; });
|
|
48
|
+
process.stdin.on("end", () => resolve(data.trim()));
|
|
49
|
+
// Timeout after 1s if no data arrives
|
|
50
|
+
setTimeout(() => resolve(data.trim()), 1000);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
export async function runExec(argv) {
|
|
54
|
+
const args = parseExecArgs(argv);
|
|
55
|
+
// Read from stdin if no prompt provided
|
|
56
|
+
if (!args.prompt) {
|
|
57
|
+
args.prompt = await readStdin();
|
|
58
|
+
}
|
|
59
|
+
if (!args.prompt) {
|
|
60
|
+
process.stderr.write("Error: No prompt provided.\n");
|
|
61
|
+
process.stderr.write("Usage: codemaxxing exec \"your prompt here\"\n");
|
|
62
|
+
process.stderr.write(" echo \"fix tests\" | codemaxxing exec\n");
|
|
63
|
+
process.stderr.write("\nFlags:\n");
|
|
64
|
+
process.stderr.write(" --auto-approve Skip approval prompts\n");
|
|
65
|
+
process.stderr.write(" --json JSON output\n");
|
|
66
|
+
process.stderr.write(" -m, --model Model to use\n");
|
|
67
|
+
process.stderr.write(" -p, --provider Provider profile\n");
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
// Resolve provider config
|
|
71
|
+
const rawConfig = loadConfig();
|
|
72
|
+
const cliArgs = {
|
|
73
|
+
model: args.model,
|
|
74
|
+
provider: args.provider,
|
|
75
|
+
};
|
|
76
|
+
const config = applyOverrides(rawConfig, cliArgs);
|
|
77
|
+
let provider = config.provider;
|
|
78
|
+
// Auto-detect local provider if needed
|
|
79
|
+
if (provider.model === "auto" || (provider.baseUrl === "http://localhost:1234/v1" && !args.provider)) {
|
|
80
|
+
const detected = await detectLocalProvider();
|
|
81
|
+
if (detected) {
|
|
82
|
+
if (args.model)
|
|
83
|
+
detected.model = args.model;
|
|
84
|
+
provider = detected;
|
|
85
|
+
}
|
|
86
|
+
else if (!args.provider) {
|
|
87
|
+
process.stderr.write("Error: No LLM provider found. Start a local server or use --provider.\n");
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
process.stderr.write(`Provider: ${provider.baseUrl}\n`);
|
|
92
|
+
process.stderr.write(`Model: ${provider.model}\n`);
|
|
93
|
+
process.stderr.write(`Prompt: ${args.prompt.slice(0, 100)}${args.prompt.length > 100 ? "..." : ""}\n`);
|
|
94
|
+
process.stderr.write("---\n");
|
|
95
|
+
const cwd = process.cwd();
|
|
96
|
+
let hasChanges = false;
|
|
97
|
+
let fullResponse = "";
|
|
98
|
+
const toolResults = [];
|
|
99
|
+
const agent = new CodingAgent({
|
|
100
|
+
provider,
|
|
101
|
+
cwd,
|
|
102
|
+
maxTokens: config.defaults.maxTokens,
|
|
103
|
+
autoApprove: args.autoApprove,
|
|
104
|
+
onToken: (token) => {
|
|
105
|
+
if (!args.json) {
|
|
106
|
+
process.stdout.write(token);
|
|
107
|
+
}
|
|
108
|
+
fullResponse += token;
|
|
109
|
+
},
|
|
110
|
+
onToolCall: (name, toolArgs) => {
|
|
111
|
+
process.stderr.write(`Tool: ${name}(${Object.values(toolArgs).map(v => String(v).slice(0, 60)).join(", ")})\n`);
|
|
112
|
+
if (name === "write_file")
|
|
113
|
+
hasChanges = true;
|
|
114
|
+
},
|
|
115
|
+
onToolResult: (name, result) => {
|
|
116
|
+
const lines = result.split("\n").length;
|
|
117
|
+
process.stderr.write(` β ${lines} lines\n`);
|
|
118
|
+
toolResults.push({ tool: name, args: {}, result });
|
|
119
|
+
},
|
|
120
|
+
onToolApproval: async (name, toolArgs, diff) => {
|
|
121
|
+
if (args.autoApprove)
|
|
122
|
+
return "yes";
|
|
123
|
+
// In non-interactive mode without auto-approve, deny dangerous tools
|
|
124
|
+
process.stderr.write(`β Denied ${name} (use --auto-approve to allow)\n`);
|
|
125
|
+
return "no";
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
try {
|
|
129
|
+
await agent.init();
|
|
130
|
+
await agent.send(args.prompt);
|
|
131
|
+
if (!args.json) {
|
|
132
|
+
// Ensure newline at end of output
|
|
133
|
+
process.stdout.write("\n");
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
// JSON output mode
|
|
137
|
+
const output = {
|
|
138
|
+
response: fullResponse,
|
|
139
|
+
model: provider.model,
|
|
140
|
+
tools_used: toolResults.length,
|
|
141
|
+
has_changes: hasChanges,
|
|
142
|
+
};
|
|
143
|
+
process.stdout.write(JSON.stringify(output, null, 2) + "\n");
|
|
144
|
+
}
|
|
145
|
+
process.exit(hasChanges ? 0 : 2);
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
process.stderr.write(`Error: ${err.message}\n`);
|
|
149
|
+
if (args.json) {
|
|
150
|
+
process.stdout.write(JSON.stringify({ error: err.message }, null, 2) + "\n");
|
|
151
|
+
}
|
|
152
|
+
process.exit(1);
|
|
153
|
+
}
|
|
154
|
+
}
|