@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.
Files changed (152) hide show
  1. package/README.md +110 -0
  2. package/dist/agent.d.ts +62 -0
  3. package/dist/agent.js +317 -0
  4. package/dist/agents/registry.d.ts +66 -0
  5. package/dist/agents/registry.js +238 -0
  6. package/dist/agents/types.d.ts +40 -0
  7. package/dist/agents/types.js +94 -0
  8. package/dist/commands/custom-registry.d.ts +69 -0
  9. package/dist/commands/custom-registry.js +246 -0
  10. package/dist/commands/index.d.ts +7 -0
  11. package/dist/commands/index.js +7 -0
  12. package/dist/commands/types.d.ts +31 -0
  13. package/dist/commands/types.js +26 -0
  14. package/dist/commands.d.ts +63 -0
  15. package/dist/commands.js +324 -0
  16. package/dist/db/index.d.ts +42 -0
  17. package/dist/db/index.js +146 -0
  18. package/dist/db/repositories/document-repository.d.ts +63 -0
  19. package/dist/db/repositories/document-repository.js +184 -0
  20. package/dist/db/repositories/index.d.ts +9 -0
  21. package/dist/db/repositories/index.js +6 -0
  22. package/dist/db/repositories/project-repository.d.ts +132 -0
  23. package/dist/db/repositories/project-repository.js +337 -0
  24. package/dist/db/repositories/work-item-repository.d.ts +115 -0
  25. package/dist/db/repositories/work-item-repository.js +389 -0
  26. package/dist/db/schema.d.ts +83 -0
  27. package/dist/db/schema.js +143 -0
  28. package/dist/debug.d.ts +8 -0
  29. package/dist/debug.js +48 -0
  30. package/dist/index.d.ts +2 -0
  31. package/dist/index.js +348 -0
  32. package/dist/index.old.d.ts +7 -0
  33. package/dist/index.old.js +1014 -0
  34. package/dist/repl.d.ts +121 -0
  35. package/dist/repl.js +1878 -0
  36. package/dist/settings/index.d.ts +80 -0
  37. package/dist/settings/index.js +195 -0
  38. package/dist/shared-handlers.d.ts +63 -0
  39. package/dist/shared-handlers.js +57 -0
  40. package/dist/slash-autocomplete.d.ts +41 -0
  41. package/dist/slash-autocomplete.js +638 -0
  42. package/dist/state.d.ts +75 -0
  43. package/dist/state.js +130 -0
  44. package/dist/tabbed-menu.d.ts +11 -0
  45. package/dist/tabbed-menu.js +328 -0
  46. package/dist/templates/backlog-md.d.ts +7 -0
  47. package/dist/templates/backlog-md.js +94 -0
  48. package/dist/templates/claude-md.d.ts +7 -0
  49. package/dist/templates/claude-md.js +189 -0
  50. package/dist/templates/coding-standards.d.ts +7 -0
  51. package/dist/templates/coding-standards.js +299 -0
  52. package/dist/templates/compilr-md.d.ts +7 -0
  53. package/dist/templates/compilr-md.js +189 -0
  54. package/dist/templates/config-json.d.ts +38 -0
  55. package/dist/templates/config-json.js +39 -0
  56. package/dist/templates/gitignore.d.ts +7 -0
  57. package/dist/templates/gitignore.js +85 -0
  58. package/dist/templates/index.d.ts +19 -0
  59. package/dist/templates/index.js +302 -0
  60. package/dist/templates/package-json.d.ts +7 -0
  61. package/dist/templates/package-json.js +111 -0
  62. package/dist/templates/readme-md.d.ts +7 -0
  63. package/dist/templates/readme-md.js +161 -0
  64. package/dist/templates/tsconfig.d.ts +7 -0
  65. package/dist/templates/tsconfig.js +61 -0
  66. package/dist/templates/types.d.ts +33 -0
  67. package/dist/templates/types.js +24 -0
  68. package/dist/test-autocomplete.d.ts +7 -0
  69. package/dist/test-autocomplete.js +85 -0
  70. package/dist/test-tabbed-menu.d.ts +7 -0
  71. package/dist/test-tabbed-menu.js +25 -0
  72. package/dist/themes/colors.d.ts +49 -0
  73. package/dist/themes/colors.js +135 -0
  74. package/dist/themes/index.d.ts +23 -0
  75. package/dist/themes/index.js +24 -0
  76. package/dist/themes/registry.d.ts +60 -0
  77. package/dist/themes/registry.js +195 -0
  78. package/dist/themes/types.d.ts +82 -0
  79. package/dist/themes/types.js +7 -0
  80. package/dist/tool-selector.d.ts +71 -0
  81. package/dist/tool-selector.js +184 -0
  82. package/dist/tools/ask-user-simple.d.ts +19 -0
  83. package/dist/tools/ask-user-simple.js +86 -0
  84. package/dist/tools/ask-user.d.ts +32 -0
  85. package/dist/tools/ask-user.js +113 -0
  86. package/dist/tools/backlog.d.ts +53 -0
  87. package/dist/tools/backlog.js +709 -0
  88. package/dist/tools.d.ts +15 -0
  89. package/dist/tools.js +121 -0
  90. package/dist/ui/agents-overlay.d.ts +12 -0
  91. package/dist/ui/agents-overlay.js +501 -0
  92. package/dist/ui/arch-type-overlay.d.ts +20 -0
  93. package/dist/ui/arch-type-overlay.js +229 -0
  94. package/dist/ui/ask-user-overlay.d.ts +26 -0
  95. package/dist/ui/ask-user-overlay.js +647 -0
  96. package/dist/ui/ask-user-simple-overlay.d.ts +25 -0
  97. package/dist/ui/ask-user-simple-overlay.js +242 -0
  98. package/dist/ui/backlog-overlay.d.ts +17 -0
  99. package/dist/ui/backlog-overlay.js +786 -0
  100. package/dist/ui/commands-overlay.d.ts +11 -0
  101. package/dist/ui/commands-overlay.js +410 -0
  102. package/dist/ui/config-overlay.d.ts +34 -0
  103. package/dist/ui/config-overlay.js +977 -0
  104. package/dist/ui/conversation.d.ts +82 -0
  105. package/dist/ui/conversation.js +508 -0
  106. package/dist/ui/diff.d.ts +38 -0
  107. package/dist/ui/diff.js +182 -0
  108. package/dist/ui/ephemeral.d.ts +111 -0
  109. package/dist/ui/ephemeral.js +413 -0
  110. package/dist/ui/file-autocomplete.d.ts +45 -0
  111. package/dist/ui/file-autocomplete.js +237 -0
  112. package/dist/ui/footer.d.ts +153 -0
  113. package/dist/ui/footer.js +422 -0
  114. package/dist/ui/index.d.ts +12 -0
  115. package/dist/ui/index.js +15 -0
  116. package/dist/ui/init-overlay.d.ts +24 -0
  117. package/dist/ui/init-overlay.js +525 -0
  118. package/dist/ui/input-prompt-v2.d.ts +179 -0
  119. package/dist/ui/input-prompt-v2.js +991 -0
  120. package/dist/ui/input-prompt.d.ts +97 -0
  121. package/dist/ui/input-prompt.js +800 -0
  122. package/dist/ui/iteration-limit-overlay.d.ts +21 -0
  123. package/dist/ui/iteration-limit-overlay.js +150 -0
  124. package/dist/ui/keys-overlay.d.ts +14 -0
  125. package/dist/ui/keys-overlay.js +181 -0
  126. package/dist/ui/model-warning-overlay.d.ts +30 -0
  127. package/dist/ui/model-warning-overlay.js +171 -0
  128. package/dist/ui/overlay-controller.d.ts +25 -0
  129. package/dist/ui/overlay-controller.js +35 -0
  130. package/dist/ui/overlays.d.ts +47 -0
  131. package/dist/ui/overlays.js +627 -0
  132. package/dist/ui/permission-overlay.d.ts +16 -0
  133. package/dist/ui/permission-overlay.js +494 -0
  134. package/dist/ui/terminal.d.ts +117 -0
  135. package/dist/ui/terminal.js +237 -0
  136. package/dist/ui/todo-zone.d.ts +112 -0
  137. package/dist/ui/todo-zone.js +353 -0
  138. package/dist/ui/tools-overlay.d.ts +26 -0
  139. package/dist/ui/tools-overlay.js +278 -0
  140. package/dist/ui/tutorial-overlay.d.ts +10 -0
  141. package/dist/ui/tutorial-overlay.js +936 -0
  142. package/dist/ui/types.d.ts +103 -0
  143. package/dist/ui/types.js +33 -0
  144. package/dist/utils/credentials.d.ts +55 -0
  145. package/dist/utils/credentials.js +268 -0
  146. package/dist/utils/model-tiers.d.ts +37 -0
  147. package/dist/utils/model-tiers.js +118 -0
  148. package/dist/utils/project-memory.d.ts +47 -0
  149. package/dist/utils/project-memory.js +117 -0
  150. package/dist/utils/project-status.d.ts +56 -0
  151. package/dist/utils/project-status.js +237 -0
  152. 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
+ });
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Agent Demo - CLI Interface
4
+ *
5
+ * Simple readline-based REPL for testing the agent.
6
+ */
7
+ export {};