@compilr-dev/cli 0.4.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 +110 -0
- package/dist/agent.d.ts +62 -0
- package/dist/agent.js +317 -0
- package/dist/agents/registry.d.ts +66 -0
- package/dist/agents/registry.js +238 -0
- package/dist/agents/types.d.ts +40 -0
- package/dist/agents/types.js +94 -0
- package/dist/commands/custom-registry.d.ts +69 -0
- package/dist/commands/custom-registry.js +246 -0
- package/dist/commands/index.d.ts +7 -0
- package/dist/commands/index.js +7 -0
- package/dist/commands/types.d.ts +31 -0
- package/dist/commands/types.js +26 -0
- package/dist/commands.d.ts +63 -0
- package/dist/commands.js +324 -0
- package/dist/db/index.d.ts +42 -0
- package/dist/db/index.js +146 -0
- package/dist/db/repositories/document-repository.d.ts +63 -0
- package/dist/db/repositories/document-repository.js +184 -0
- package/dist/db/repositories/index.d.ts +9 -0
- package/dist/db/repositories/index.js +6 -0
- package/dist/db/repositories/project-repository.d.ts +132 -0
- package/dist/db/repositories/project-repository.js +337 -0
- package/dist/db/repositories/work-item-repository.d.ts +115 -0
- package/dist/db/repositories/work-item-repository.js +389 -0
- package/dist/db/schema.d.ts +83 -0
- package/dist/db/schema.js +143 -0
- package/dist/debug.d.ts +8 -0
- package/dist/debug.js +48 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +348 -0
- package/dist/index.old.d.ts +7 -0
- package/dist/index.old.js +1014 -0
- package/dist/repl.d.ts +121 -0
- package/dist/repl.js +1878 -0
- package/dist/settings/index.d.ts +80 -0
- package/dist/settings/index.js +195 -0
- package/dist/shared-handlers.d.ts +63 -0
- package/dist/shared-handlers.js +57 -0
- package/dist/slash-autocomplete.d.ts +41 -0
- package/dist/slash-autocomplete.js +638 -0
- package/dist/state.d.ts +75 -0
- package/dist/state.js +130 -0
- package/dist/tabbed-menu.d.ts +11 -0
- package/dist/tabbed-menu.js +328 -0
- package/dist/templates/backlog-md.d.ts +7 -0
- package/dist/templates/backlog-md.js +94 -0
- package/dist/templates/claude-md.d.ts +7 -0
- package/dist/templates/claude-md.js +189 -0
- package/dist/templates/coding-standards.d.ts +7 -0
- package/dist/templates/coding-standards.js +299 -0
- package/dist/templates/compilr-md.d.ts +7 -0
- package/dist/templates/compilr-md.js +189 -0
- package/dist/templates/config-json.d.ts +38 -0
- package/dist/templates/config-json.js +39 -0
- package/dist/templates/gitignore.d.ts +7 -0
- package/dist/templates/gitignore.js +85 -0
- package/dist/templates/index.d.ts +19 -0
- package/dist/templates/index.js +302 -0
- package/dist/templates/package-json.d.ts +7 -0
- package/dist/templates/package-json.js +111 -0
- package/dist/templates/readme-md.d.ts +7 -0
- package/dist/templates/readme-md.js +161 -0
- package/dist/templates/tsconfig.d.ts +7 -0
- package/dist/templates/tsconfig.js +61 -0
- package/dist/templates/types.d.ts +33 -0
- package/dist/templates/types.js +24 -0
- package/dist/test-autocomplete.d.ts +7 -0
- package/dist/test-autocomplete.js +85 -0
- package/dist/test-tabbed-menu.d.ts +7 -0
- package/dist/test-tabbed-menu.js +25 -0
- package/dist/themes/colors.d.ts +49 -0
- package/dist/themes/colors.js +135 -0
- package/dist/themes/index.d.ts +23 -0
- package/dist/themes/index.js +24 -0
- package/dist/themes/registry.d.ts +60 -0
- package/dist/themes/registry.js +195 -0
- package/dist/themes/types.d.ts +82 -0
- package/dist/themes/types.js +7 -0
- package/dist/tool-selector.d.ts +71 -0
- package/dist/tool-selector.js +184 -0
- package/dist/tools/ask-user-simple.d.ts +19 -0
- package/dist/tools/ask-user-simple.js +86 -0
- package/dist/tools/ask-user.d.ts +32 -0
- package/dist/tools/ask-user.js +113 -0
- package/dist/tools/backlog.d.ts +53 -0
- package/dist/tools/backlog.js +709 -0
- package/dist/tools.d.ts +15 -0
- package/dist/tools.js +121 -0
- package/dist/ui/agents-overlay.d.ts +12 -0
- package/dist/ui/agents-overlay.js +501 -0
- package/dist/ui/arch-type-overlay.d.ts +20 -0
- package/dist/ui/arch-type-overlay.js +229 -0
- package/dist/ui/ask-user-overlay.d.ts +26 -0
- package/dist/ui/ask-user-overlay.js +647 -0
- package/dist/ui/ask-user-simple-overlay.d.ts +25 -0
- package/dist/ui/ask-user-simple-overlay.js +242 -0
- package/dist/ui/backlog-overlay.d.ts +17 -0
- package/dist/ui/backlog-overlay.js +786 -0
- package/dist/ui/commands-overlay.d.ts +11 -0
- package/dist/ui/commands-overlay.js +410 -0
- package/dist/ui/config-overlay.d.ts +34 -0
- package/dist/ui/config-overlay.js +977 -0
- package/dist/ui/conversation.d.ts +82 -0
- package/dist/ui/conversation.js +508 -0
- package/dist/ui/diff.d.ts +38 -0
- package/dist/ui/diff.js +182 -0
- package/dist/ui/ephemeral.d.ts +111 -0
- package/dist/ui/ephemeral.js +413 -0
- package/dist/ui/file-autocomplete.d.ts +45 -0
- package/dist/ui/file-autocomplete.js +237 -0
- package/dist/ui/footer.d.ts +153 -0
- package/dist/ui/footer.js +422 -0
- package/dist/ui/index.d.ts +12 -0
- package/dist/ui/index.js +15 -0
- package/dist/ui/init-overlay.d.ts +24 -0
- package/dist/ui/init-overlay.js +525 -0
- package/dist/ui/input-prompt-v2.d.ts +179 -0
- package/dist/ui/input-prompt-v2.js +991 -0
- package/dist/ui/input-prompt.d.ts +97 -0
- package/dist/ui/input-prompt.js +800 -0
- package/dist/ui/iteration-limit-overlay.d.ts +21 -0
- package/dist/ui/iteration-limit-overlay.js +150 -0
- package/dist/ui/keys-overlay.d.ts +14 -0
- package/dist/ui/keys-overlay.js +181 -0
- package/dist/ui/model-warning-overlay.d.ts +30 -0
- package/dist/ui/model-warning-overlay.js +171 -0
- package/dist/ui/overlay-controller.d.ts +25 -0
- package/dist/ui/overlay-controller.js +35 -0
- package/dist/ui/overlays.d.ts +47 -0
- package/dist/ui/overlays.js +627 -0
- package/dist/ui/permission-overlay.d.ts +16 -0
- package/dist/ui/permission-overlay.js +494 -0
- package/dist/ui/terminal.d.ts +117 -0
- package/dist/ui/terminal.js +237 -0
- package/dist/ui/todo-zone.d.ts +112 -0
- package/dist/ui/todo-zone.js +353 -0
- package/dist/ui/tools-overlay.d.ts +26 -0
- package/dist/ui/tools-overlay.js +278 -0
- package/dist/ui/tutorial-overlay.d.ts +10 -0
- package/dist/ui/tutorial-overlay.js +936 -0
- package/dist/ui/types.d.ts +103 -0
- package/dist/ui/types.js +33 -0
- package/dist/utils/credentials.d.ts +55 -0
- package/dist/utils/credentials.js +268 -0
- package/dist/utils/model-tiers.d.ts +37 -0
- package/dist/utils/model-tiers.js +118 -0
- package/dist/utils/project-memory.d.ts +47 -0
- package/dist/utils/project-memory.js +117 -0
- package/dist/utils/project-status.d.ts +56 -0
- package/dist/utils/project-status.js +237 -0
- package/package.json +66 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Force color output for all chalk-based libraries (cli-highlight, marked-terminal, etc.)
|
|
3
|
+
// This must be set BEFORE any imports that use chalk
|
|
4
|
+
process.env.FORCE_COLOR = '3';
|
|
5
|
+
/**
|
|
6
|
+
* @compilr-dev/cli
|
|
7
|
+
*
|
|
8
|
+
* AI-powered coding assistant CLI using @compilr-dev/agents.
|
|
9
|
+
* A terminal-based REPL for interacting with AI agents.
|
|
10
|
+
*/
|
|
11
|
+
import { createAgent } from './agent.js';
|
|
12
|
+
import { REPL } from './repl.js';
|
|
13
|
+
import * as diff from './ui/diff.js';
|
|
14
|
+
import * as terminal from './ui/terminal.js';
|
|
15
|
+
import { getStyles } from './themes/index.js';
|
|
16
|
+
import { getDefaultProvider, getDefaultModel as getDefaultModelSetting } from './settings/index.js';
|
|
17
|
+
import { showAskUserOverlay } from './ui/ask-user-overlay.js';
|
|
18
|
+
import { showAskUserSimpleOverlay } from './ui/ask-user-simple-overlay.js';
|
|
19
|
+
import { showPermissionOverlay } from './ui/permission-overlay.js';
|
|
20
|
+
import { showIterationLimitOverlay } from './ui/iteration-limit-overlay.js';
|
|
21
|
+
import { showKeysOverlay } from './ui/keys-overlay.js';
|
|
22
|
+
import { hasApiKey, settingsProviderToCredentialKey } from './utils/credentials.js';
|
|
23
|
+
import { printLogo } from './ui/conversation.js';
|
|
24
|
+
import { loadProjectMemory, formatBytes, getSizeCategory } from './utils/project-memory.js';
|
|
25
|
+
import { registerAskUserHandler, registerAskUserSimpleHandler, setOverlayActive, } from './shared-handlers.js';
|
|
26
|
+
import chalk from 'chalk';
|
|
27
|
+
const sharedState = { mode: 'normal', pendingSuggestion: null };
|
|
28
|
+
// =============================================================================
|
|
29
|
+
// Version
|
|
30
|
+
// =============================================================================
|
|
31
|
+
const VERSION = '0.0.1';
|
|
32
|
+
function parseArgs() {
|
|
33
|
+
const args = process.argv.slice(2);
|
|
34
|
+
const options = {};
|
|
35
|
+
for (let i = 0; i < args.length; i++) {
|
|
36
|
+
const arg = args[i];
|
|
37
|
+
if (arg === '--provider' || arg === '-p') {
|
|
38
|
+
options.provider = args[++i];
|
|
39
|
+
}
|
|
40
|
+
else if (arg === '--model' || arg === '-m') {
|
|
41
|
+
options.model = args[++i];
|
|
42
|
+
}
|
|
43
|
+
else if (arg === '--minimal') {
|
|
44
|
+
options.minimal = true;
|
|
45
|
+
}
|
|
46
|
+
else if (arg === '--show-filtering') {
|
|
47
|
+
options.showFiltering = true;
|
|
48
|
+
}
|
|
49
|
+
else if (arg === '--help' || arg === '-h') {
|
|
50
|
+
showHelp();
|
|
51
|
+
process.exit(0);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return options;
|
|
55
|
+
}
|
|
56
|
+
function showHelp() {
|
|
57
|
+
console.log(`
|
|
58
|
+
@compilr-dev/cli v${VERSION}
|
|
59
|
+
|
|
60
|
+
Usage: npm run dev [options]
|
|
61
|
+
|
|
62
|
+
Options:
|
|
63
|
+
--provider, -p <provider> LLM provider (claude, openai, gemini, ollama)
|
|
64
|
+
--model, -m <model> Model name (e.g., claude-3-5-haiku-20241022)
|
|
65
|
+
--minimal Use minimal tool set
|
|
66
|
+
--show-filtering Show tool filtering analysis
|
|
67
|
+
--help, -h Show this help
|
|
68
|
+
|
|
69
|
+
Environment Variables:
|
|
70
|
+
ANTHROPIC_API_KEY Required for Claude provider
|
|
71
|
+
OPENAI_API_KEY Required for OpenAI provider
|
|
72
|
+
GOOGLE_AI_API_KEY Required for Gemini provider
|
|
73
|
+
|
|
74
|
+
Examples:
|
|
75
|
+
npm run dev
|
|
76
|
+
npm run dev -- --model claude-3-5-haiku-20241022
|
|
77
|
+
npm run dev -- --provider gemini --model gemini-2.0-flash
|
|
78
|
+
npm run dev -- --minimal --show-filtering
|
|
79
|
+
`);
|
|
80
|
+
}
|
|
81
|
+
// =============================================================================
|
|
82
|
+
// Main Entry Point
|
|
83
|
+
// =============================================================================
|
|
84
|
+
async function main() {
|
|
85
|
+
const options = parseArgs();
|
|
86
|
+
// Show logo immediately on startup
|
|
87
|
+
printLogo(VERSION);
|
|
88
|
+
// Resolve provider: CLI arg > settings > auto-detect
|
|
89
|
+
const settingsProvider = getDefaultProvider();
|
|
90
|
+
const provider = options.provider
|
|
91
|
+
?? (settingsProvider !== 'auto' ? settingsProvider : null)
|
|
92
|
+
?? detectProvider();
|
|
93
|
+
// Resolve model: CLI arg > settings > provider default
|
|
94
|
+
const settingsModel = getDefaultModelSetting();
|
|
95
|
+
const model = options.model ?? settingsModel ?? getDefaultModel(provider);
|
|
96
|
+
// Check if API key is available for selected provider (skip for ollama)
|
|
97
|
+
if (provider !== 'ollama') {
|
|
98
|
+
const credentialKey = settingsProviderToCredentialKey(provider === 'claude' ? 'claude' : provider);
|
|
99
|
+
if (!hasApiKey(credentialKey)) {
|
|
100
|
+
const s = getStyles();
|
|
101
|
+
console.log(s.warning('⚠ No API key found for ' + provider));
|
|
102
|
+
console.log(s.muted(' Use --provider ollama for local models without an API key.'));
|
|
103
|
+
console.log('');
|
|
104
|
+
// Show keys overlay to let user set up their key
|
|
105
|
+
const result = await showKeysOverlay();
|
|
106
|
+
// Check again after overlay closes
|
|
107
|
+
if (!result.changed || !hasApiKey(credentialKey)) {
|
|
108
|
+
console.log('');
|
|
109
|
+
console.log(s.error('No API key configured. Cannot start.'));
|
|
110
|
+
console.log(s.muted(' Set a key with /keys or use: export ANTHROPIC_API_KEY=sk-ant-...'));
|
|
111
|
+
console.log('');
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
console.log(s.success('✓ API key configured'));
|
|
115
|
+
console.log('');
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Load project memory (COMPILR.md or CLAUDE.md)
|
|
119
|
+
const projectMemory = loadProjectMemory();
|
|
120
|
+
let projectContext;
|
|
121
|
+
if (projectMemory.found && projectMemory.filePath) {
|
|
122
|
+
const s = getStyles();
|
|
123
|
+
const filename = projectMemory.filePath.split('/').pop() ?? 'COMPILR.md';
|
|
124
|
+
const sizeCategory = getSizeCategory(projectMemory.originalSize);
|
|
125
|
+
if (projectMemory.truncated) {
|
|
126
|
+
// Large file - was truncated
|
|
127
|
+
console.log(s.warning(`⚠ Loaded ${filename} (${formatBytes(projectMemory.originalSize)} → truncated to 100KB)`));
|
|
128
|
+
}
|
|
129
|
+
else if (sizeCategory === 'medium') {
|
|
130
|
+
// Medium file - loaded with note
|
|
131
|
+
console.log(s.muted(` Loaded ${filename} (${formatBytes(projectMemory.originalSize)}, ~${projectMemory.estimatedTokens.toLocaleString()} tokens)`));
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
// Small file - loaded silently (just a subtle note)
|
|
135
|
+
console.log(s.muted(` Loaded ${filename}`));
|
|
136
|
+
}
|
|
137
|
+
projectContext = projectMemory.content;
|
|
138
|
+
}
|
|
139
|
+
// Create permission handler that respects mode state
|
|
140
|
+
const onPermissionRequest = async (request) => {
|
|
141
|
+
// Auto-accept mode: allow everything without prompting
|
|
142
|
+
if (sharedState.mode === 'auto-accept') {
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
// Normal mode: show permission prompt
|
|
146
|
+
// Pause footer to prevent interference with the overlay
|
|
147
|
+
sharedState.pauseFooter?.();
|
|
148
|
+
// Set overlay active flag so REPL buffers any text output
|
|
149
|
+
setOverlayActive(true);
|
|
150
|
+
// For edit operations, show the diff BEFORE the permission prompt
|
|
151
|
+
if (request.toolName === 'edit') {
|
|
152
|
+
const filePath = ((typeof request.input.filePath === 'string' ? request.input.filePath : null) ||
|
|
153
|
+
(typeof request.input.file_path === 'string' ? request.input.file_path : null) ||
|
|
154
|
+
(typeof request.input.path === 'string' ? request.input.path : null) || '');
|
|
155
|
+
const oldText = ((typeof request.input.oldString === 'string' ? request.input.oldString : null) ||
|
|
156
|
+
(typeof request.input.old_string === 'string' ? request.input.old_string : null) ||
|
|
157
|
+
(typeof request.input.old_text === 'string' ? request.input.old_text : null) || '');
|
|
158
|
+
const newText = ((typeof request.input.newString === 'string' ? request.input.newString : null) ||
|
|
159
|
+
(typeof request.input.new_string === 'string' ? request.input.new_string : null) ||
|
|
160
|
+
(typeof request.input.new_text === 'string' ? request.input.new_text : null) || '');
|
|
161
|
+
const replaceAll = Boolean(request.input.replaceAll);
|
|
162
|
+
if (filePath && oldText && newText) {
|
|
163
|
+
const editDiff = diff.generateEditDiff(filePath, oldText, newText, replaceAll);
|
|
164
|
+
const s = getStyles();
|
|
165
|
+
terminal.writeLine('');
|
|
166
|
+
if (editDiff) {
|
|
167
|
+
terminal.writeLine(s.warning('● ') + chalk.bold(`Edit(${filePath.split('/').pop() ?? 'unknown'})`));
|
|
168
|
+
terminal.writeLine(diff.formatDiffHeader(filePath, editDiff.additions, editDiff.removals));
|
|
169
|
+
for (const line of editDiff.lines) {
|
|
170
|
+
terminal.writeLine(line);
|
|
171
|
+
}
|
|
172
|
+
terminal.writeLine('');
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
// Fallback: show simple edit info
|
|
176
|
+
terminal.writeLine(s.warning('● ') + chalk.bold(`Edit(${filePath.split('/').pop() ?? 'unknown'})`));
|
|
177
|
+
terminal.writeLine(` ${s.muted('⎿')} Will replace text in ${s.primary(filePath.split('/').pop() ?? filePath)}`);
|
|
178
|
+
terminal.writeLine(` ${chalk.bgRed.white(' - ')} ${s.muted(oldText.slice(0, 60))}${oldText.length > 60 ? '...' : ''}`);
|
|
179
|
+
terminal.writeLine(` ${chalk.bgGreen.black(' + ')} ${s.muted(newText.slice(0, 60))}${newText.length > 60 ? '...' : ''}`);
|
|
180
|
+
terminal.writeLine('');
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
try {
|
|
185
|
+
const result = await showPermissionOverlay({
|
|
186
|
+
toolName: request.toolName,
|
|
187
|
+
args: request.input,
|
|
188
|
+
description: request.description,
|
|
189
|
+
});
|
|
190
|
+
// If user chose 'allow-always', grant session permission and switch to auto-accept mode
|
|
191
|
+
if (result === 'allow-always') {
|
|
192
|
+
sharedState.grantSession?.(request.toolName);
|
|
193
|
+
// Switch to auto-accept mode so user can see and toggle back if needed
|
|
194
|
+
sharedState.setMode?.('auto-accept');
|
|
195
|
+
}
|
|
196
|
+
// Convert result to boolean (allow/allow-always = true, deny = false)
|
|
197
|
+
return result !== 'deny';
|
|
198
|
+
}
|
|
199
|
+
finally {
|
|
200
|
+
// Clear overlay active flag and resume footer
|
|
201
|
+
setOverlayActive(false);
|
|
202
|
+
sharedState.resumeFooter?.();
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
// Register ask_user handler that coordinates with footer
|
|
206
|
+
registerAskUserHandler(async (input) => {
|
|
207
|
+
// Pause footer to prevent interference with the overlay
|
|
208
|
+
sharedState.pauseFooter?.();
|
|
209
|
+
try {
|
|
210
|
+
// Show the overlay and get answers
|
|
211
|
+
const result = await showAskUserOverlay({
|
|
212
|
+
questions: input.questions,
|
|
213
|
+
context: input.context,
|
|
214
|
+
});
|
|
215
|
+
return result;
|
|
216
|
+
}
|
|
217
|
+
finally {
|
|
218
|
+
// Resume footer after overlay closes
|
|
219
|
+
sharedState.resumeFooter?.();
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
// Register ask_user_simple handler for smaller models (flat schema)
|
|
223
|
+
registerAskUserSimpleHandler(async (input) => {
|
|
224
|
+
// Pause footer to prevent interference with the overlay
|
|
225
|
+
sharedState.pauseFooter?.();
|
|
226
|
+
try {
|
|
227
|
+
// Show the simple overlay and get answer
|
|
228
|
+
const result = await showAskUserSimpleOverlay({
|
|
229
|
+
question: input.question,
|
|
230
|
+
options: input.options,
|
|
231
|
+
allowCustom: input.allowCustom,
|
|
232
|
+
});
|
|
233
|
+
return result;
|
|
234
|
+
}
|
|
235
|
+
finally {
|
|
236
|
+
// Resume footer after overlay closes
|
|
237
|
+
sharedState.resumeFooter?.();
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
// Create iteration limit handler that shows overlay
|
|
241
|
+
const onIterationLimitReached = async (context) => {
|
|
242
|
+
// Pause footer to prevent interference with the overlay
|
|
243
|
+
sharedState.pauseFooter?.();
|
|
244
|
+
setOverlayActive(true);
|
|
245
|
+
try {
|
|
246
|
+
const result = await showIterationLimitOverlay({
|
|
247
|
+
iteration: context.iteration,
|
|
248
|
+
maxIterations: context.maxIterations,
|
|
249
|
+
toolCallCount: context.toolCallCount,
|
|
250
|
+
});
|
|
251
|
+
if (result.continue) {
|
|
252
|
+
return result.additionalIterations;
|
|
253
|
+
}
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
finally {
|
|
257
|
+
// Clear overlay active flag and resume footer
|
|
258
|
+
setOverlayActive(false);
|
|
259
|
+
sharedState.resumeFooter?.();
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
// Create agent with permission handler and suggestion callback
|
|
263
|
+
const agent = createAgent({
|
|
264
|
+
provider,
|
|
265
|
+
model,
|
|
266
|
+
minimal: options.minimal,
|
|
267
|
+
onPermissionRequest,
|
|
268
|
+
onIterationLimitReached,
|
|
269
|
+
onSuggest: (event) => {
|
|
270
|
+
// Store suggestion to be applied after agent finishes
|
|
271
|
+
sharedState.pendingSuggestion = event.action;
|
|
272
|
+
},
|
|
273
|
+
projectContext,
|
|
274
|
+
});
|
|
275
|
+
// Bind grantSession for the permission handler to use
|
|
276
|
+
sharedState.grantSession = (toolName) => {
|
|
277
|
+
agent.grantSessionPermission(toolName);
|
|
278
|
+
};
|
|
279
|
+
// Note: setMode will be bound after REPL is created (see below)
|
|
280
|
+
// Create and run REPL with mode state setter and footer callbacks
|
|
281
|
+
const repl = new REPL({
|
|
282
|
+
agent,
|
|
283
|
+
model,
|
|
284
|
+
provider,
|
|
285
|
+
version: VERSION,
|
|
286
|
+
showFiltering: options.showFiltering,
|
|
287
|
+
onModeChange: (mode) => {
|
|
288
|
+
sharedState.mode = mode;
|
|
289
|
+
},
|
|
290
|
+
onFooterReady: (pause, resume, setSuggestion) => {
|
|
291
|
+
sharedState.pauseFooter = pause;
|
|
292
|
+
sharedState.resumeFooter = resume;
|
|
293
|
+
sharedState.setSuggestion = setSuggestion;
|
|
294
|
+
},
|
|
295
|
+
onAgentFinish: () => {
|
|
296
|
+
// Apply any pending suggestion now that agent has finished
|
|
297
|
+
if (sharedState.pendingSuggestion) {
|
|
298
|
+
sharedState.setSuggestion?.(sharedState.pendingSuggestion);
|
|
299
|
+
sharedState.pendingSuggestion = null;
|
|
300
|
+
}
|
|
301
|
+
},
|
|
302
|
+
});
|
|
303
|
+
// Bind setMode for the permission handler to use
|
|
304
|
+
sharedState.setMode = (mode) => {
|
|
305
|
+
repl.setMode(mode);
|
|
306
|
+
};
|
|
307
|
+
// Handle Ctrl+C gracefully
|
|
308
|
+
process.on('SIGINT', () => {
|
|
309
|
+
repl.stop();
|
|
310
|
+
process.exit(0);
|
|
311
|
+
});
|
|
312
|
+
await repl.run();
|
|
313
|
+
process.exit(0);
|
|
314
|
+
}
|
|
315
|
+
// =============================================================================
|
|
316
|
+
// Provider Detection
|
|
317
|
+
// =============================================================================
|
|
318
|
+
function detectProvider() {
|
|
319
|
+
if (process.env.ANTHROPIC_API_KEY)
|
|
320
|
+
return 'claude';
|
|
321
|
+
if (process.env.OPENAI_API_KEY)
|
|
322
|
+
return 'openai';
|
|
323
|
+
if (process.env.GOOGLE_AI_API_KEY)
|
|
324
|
+
return 'gemini';
|
|
325
|
+
// Default to claude
|
|
326
|
+
return 'claude';
|
|
327
|
+
}
|
|
328
|
+
function getDefaultModel(provider) {
|
|
329
|
+
switch (provider) {
|
|
330
|
+
case 'claude':
|
|
331
|
+
return 'claude-sonnet-4-20250514';
|
|
332
|
+
case 'openai':
|
|
333
|
+
return 'gpt-4o';
|
|
334
|
+
case 'gemini':
|
|
335
|
+
return 'gemini-2.0-flash';
|
|
336
|
+
case 'ollama':
|
|
337
|
+
return 'llama3.2';
|
|
338
|
+
default:
|
|
339
|
+
return 'claude-sonnet-4-20250514';
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
// =============================================================================
|
|
343
|
+
// Run
|
|
344
|
+
// =============================================================================
|
|
345
|
+
main().catch((error) => {
|
|
346
|
+
console.error('Fatal error:', error);
|
|
347
|
+
process.exit(1);
|
|
348
|
+
});
|