@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
@@ -0,0 +1,45 @@
1
+ /**
2
+ * File Path Autocomplete
3
+ *
4
+ * Uses ripgrep to provide fast file/folder suggestions when user types @path
5
+ */
6
+ export interface FileMatch {
7
+ /** Display name (filename or dirname/) */
8
+ name: string;
9
+ /** Full path from cwd */
10
+ path: string;
11
+ /** Is this a directory? */
12
+ isDirectory: boolean;
13
+ }
14
+ /**
15
+ * Get file/folder matches for a partial path
16
+ *
17
+ * @param partial - The partial path after @ (e.g., "src/comp" or "package")
18
+ * @param cwd - Current working directory
19
+ * @returns Array of matching files/folders
20
+ */
21
+ export declare function getFileMatches(partial: string, cwd?: string): FileMatch[];
22
+ /**
23
+ * Extract the @ mention from input text
24
+ *
25
+ * @param input - Full input text
26
+ * @param cursorPos - Current cursor position
27
+ * @returns The partial path after @ or null if not in @ context
28
+ */
29
+ export declare function extractAtMention(input: string, cursorPos: number): string | null;
30
+ /**
31
+ * Replace the @ mention with completed path
32
+ *
33
+ * @param input - Full input text
34
+ * @param cursorPos - Current cursor position
35
+ * @param completedPath - The completed path to insert
36
+ * @returns New input text and new cursor position
37
+ */
38
+ export declare function replaceAtMention(input: string, cursorPos: number, completedPath: string): {
39
+ input: string;
40
+ cursorPos: number;
41
+ };
42
+ /**
43
+ * Clear the file cache (call when cwd changes)
44
+ */
45
+ export declare function clearFileCache(): void;
@@ -0,0 +1,237 @@
1
+ /**
2
+ * File Path Autocomplete
3
+ *
4
+ * Uses ripgrep to provide fast file/folder suggestions when user types @path
5
+ */
6
+ import { execSync } from 'child_process';
7
+ import * as path from 'path';
8
+ // =============================================================================
9
+ // Constants
10
+ // =============================================================================
11
+ /** Max results to return */
12
+ const MAX_RESULTS = 15;
13
+ /** Cache for file list (refreshed periodically) */
14
+ let fileCache = [];
15
+ let lastCacheTime = 0;
16
+ const CACHE_TTL_MS = 5000; // 5 seconds
17
+ // =============================================================================
18
+ // Ripgrep File Listing
19
+ // =============================================================================
20
+ /**
21
+ * Get all files using ripgrep
22
+ * Results are cached for performance
23
+ */
24
+ function getAllFiles(cwd) {
25
+ const now = Date.now();
26
+ // Return cached results if fresh
27
+ if (fileCache.length > 0 && now - lastCacheTime < CACHE_TTL_MS) {
28
+ return fileCache;
29
+ }
30
+ try {
31
+ // Use ripgrep to list all files
32
+ // --files: list files instead of searching
33
+ // Respects .gitignore by default
34
+ const result = execSync('rg --files 2>/dev/null', {
35
+ cwd,
36
+ encoding: 'utf-8',
37
+ maxBuffer: 10 * 1024 * 1024, // 10MB buffer
38
+ timeout: 3000, // 3 second timeout
39
+ });
40
+ fileCache = result
41
+ .split('\n')
42
+ .filter((line) => line.trim())
43
+ .map((line) => line.replace(/\\/g, '/')); // Normalize paths
44
+ lastCacheTime = now;
45
+ return fileCache;
46
+ }
47
+ catch {
48
+ // Ripgrep failed or not available, return empty
49
+ return fileCache; // Return stale cache if available
50
+ }
51
+ }
52
+ /**
53
+ * Extract unique directories from file paths
54
+ */
55
+ function extractDirectories(files) {
56
+ const dirs = new Set();
57
+ for (const file of files) {
58
+ const parts = file.split('/');
59
+ let current = '';
60
+ // Add all parent directories
61
+ for (let i = 0; i < parts.length - 1; i++) {
62
+ current = current ? `${current}/${parts[i]}` : parts[i];
63
+ dirs.add(current);
64
+ }
65
+ }
66
+ return dirs;
67
+ }
68
+ // =============================================================================
69
+ // File Matching
70
+ // =============================================================================
71
+ /**
72
+ * Get file/folder matches for a partial path
73
+ *
74
+ * @param partial - The partial path after @ (e.g., "src/comp" or "package")
75
+ * @param cwd - Current working directory
76
+ * @returns Array of matching files/folders
77
+ */
78
+ export function getFileMatches(partial, cwd = process.cwd()) {
79
+ const files = getAllFiles(cwd);
80
+ const dirs = extractDirectories(files);
81
+ const matches = [];
82
+ const seen = new Set();
83
+ const normalizedPartial = partial.toLowerCase().replace(/\\/g, '/');
84
+ // If partial has a directory component, filter by that
85
+ const lastSlash = normalizedPartial.lastIndexOf('/');
86
+ const dirPrefix = lastSlash >= 0 ? normalizedPartial.slice(0, lastSlash + 1) : '';
87
+ const namePrefix = lastSlash >= 0 ? normalizedPartial.slice(lastSlash + 1) : normalizedPartial;
88
+ // Match directories first
89
+ for (const dir of dirs) {
90
+ const dirLower = dir.toLowerCase();
91
+ const dirWithSlash = `${dir}/`;
92
+ // Check if this directory matches
93
+ if (dirPrefix) {
94
+ // Has directory prefix - match directories under that path
95
+ if (dirLower.startsWith(dirPrefix.slice(0, -1))) {
96
+ const remaining = dir.slice(dirPrefix.length - 1);
97
+ if (remaining && !remaining.includes('/')) {
98
+ // Direct child directory
99
+ if (!namePrefix || remaining.toLowerCase().startsWith(namePrefix)) {
100
+ if (!seen.has(dirWithSlash)) {
101
+ seen.add(dirWithSlash);
102
+ matches.push({
103
+ name: `${remaining}/`,
104
+ path: dirWithSlash,
105
+ isDirectory: true,
106
+ });
107
+ }
108
+ }
109
+ }
110
+ }
111
+ }
112
+ else {
113
+ // No directory prefix - match top-level directories
114
+ if (!dir.includes('/')) {
115
+ if (!namePrefix || dirLower.startsWith(namePrefix)) {
116
+ if (!seen.has(dirWithSlash)) {
117
+ seen.add(dirWithSlash);
118
+ matches.push({
119
+ name: `${dir}/`,
120
+ path: dirWithSlash,
121
+ isDirectory: true,
122
+ });
123
+ }
124
+ }
125
+ }
126
+ }
127
+ if (matches.length >= MAX_RESULTS)
128
+ break;
129
+ }
130
+ // Then match files
131
+ for (const file of files) {
132
+ if (matches.length >= MAX_RESULTS)
133
+ break;
134
+ const fileLower = file.toLowerCase();
135
+ if (dirPrefix) {
136
+ // Has directory prefix - match files under that path
137
+ if (fileLower.startsWith(dirPrefix)) {
138
+ const remaining = file.slice(dirPrefix.length);
139
+ if (!remaining.includes('/')) {
140
+ // Direct child file
141
+ if (!namePrefix || remaining.toLowerCase().startsWith(namePrefix)) {
142
+ if (!seen.has(file)) {
143
+ seen.add(file);
144
+ matches.push({
145
+ name: remaining,
146
+ path: file,
147
+ isDirectory: false,
148
+ });
149
+ }
150
+ }
151
+ }
152
+ }
153
+ }
154
+ else {
155
+ // No directory prefix - match top-level files or by filename anywhere
156
+ const fileName = path.basename(file);
157
+ if (!namePrefix || fileName.toLowerCase().startsWith(namePrefix) || fileLower.startsWith(namePrefix)) {
158
+ if (!seen.has(file)) {
159
+ seen.add(file);
160
+ matches.push({
161
+ name: file.includes('/') ? file : fileName,
162
+ path: file,
163
+ isDirectory: false,
164
+ });
165
+ }
166
+ }
167
+ }
168
+ }
169
+ // Sort: directories first, then alphabetically
170
+ matches.sort((a, b) => {
171
+ if (a.isDirectory && !b.isDirectory)
172
+ return -1;
173
+ if (!a.isDirectory && b.isDirectory)
174
+ return 1;
175
+ return a.name.localeCompare(b.name);
176
+ });
177
+ return matches.slice(0, MAX_RESULTS);
178
+ }
179
+ /**
180
+ * Extract the @ mention from input text
181
+ *
182
+ * @param input - Full input text
183
+ * @param cursorPos - Current cursor position
184
+ * @returns The partial path after @ or null if not in @ context
185
+ */
186
+ export function extractAtMention(input, cursorPos) {
187
+ // Look backwards from cursor to find @
188
+ let start = cursorPos - 1;
189
+ while (start >= 0) {
190
+ const char = input[start];
191
+ // Found @
192
+ if (char === '@') {
193
+ // Make sure it's at start or after whitespace
194
+ if (start === 0 || /\s/.test(input[start - 1])) {
195
+ return input.slice(start + 1, cursorPos);
196
+ }
197
+ return null;
198
+ }
199
+ // Hit whitespace without finding @ - not in @ context
200
+ if (/\s/.test(char)) {
201
+ return null;
202
+ }
203
+ start--;
204
+ }
205
+ return null;
206
+ }
207
+ /**
208
+ * Replace the @ mention with completed path
209
+ *
210
+ * @param input - Full input text
211
+ * @param cursorPos - Current cursor position
212
+ * @param completedPath - The completed path to insert
213
+ * @returns New input text and new cursor position
214
+ */
215
+ export function replaceAtMention(input, cursorPos, completedPath) {
216
+ // Find the @ position
217
+ let atPos = cursorPos - 1;
218
+ while (atPos >= 0 && input[atPos] !== '@') {
219
+ atPos--;
220
+ }
221
+ if (atPos < 0) {
222
+ return { input, cursorPos };
223
+ }
224
+ // Replace from @ to cursor with the completed path
225
+ const before = input.slice(0, atPos);
226
+ const after = input.slice(cursorPos);
227
+ const newInput = `${before}@${completedPath}${after}`;
228
+ const newCursorPos = atPos + 1 + completedPath.length;
229
+ return { input: newInput, cursorPos: newCursorPos };
230
+ }
231
+ /**
232
+ * Clear the file cache (call when cwd changes)
233
+ */
234
+ export function clearFileCache() {
235
+ fileCache = [];
236
+ lastCacheTime = 0;
237
+ }
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Footer Orchestrator
3
+ *
4
+ * Manages all persistent UI elements below the scrolling zone:
5
+ * - TodoZone (spinner + todo list)
6
+ * - Queued input display
7
+ * - InputPrompt
8
+ *
9
+ * Handles:
10
+ * - Coordinated rendering via single render loop
11
+ * - Event forwarding from InputPrompt
12
+ * - Clearing for scrolling output
13
+ * - State management for agent running/idle modes
14
+ */
15
+ import { EventEmitter } from 'events';
16
+ import type { TodoItem, AgentMode } from './types.js';
17
+ export interface FooterEvents {
18
+ submit: (input: string) => void;
19
+ command: (command: string, args: string) => void;
20
+ cancel: () => void;
21
+ escape: () => void;
22
+ modeChange: () => void;
23
+ }
24
+ export interface FooterOptions {
25
+ prompt?: string;
26
+ showSeparators?: boolean;
27
+ renderInterval?: number;
28
+ initialMode?: AgentMode;
29
+ }
30
+ export declare class Footer extends EventEmitter {
31
+ private readonly todoZone;
32
+ private readonly inputPrompt;
33
+ private agentRunning;
34
+ private mode;
35
+ private lastRenderHeight;
36
+ private isRunning;
37
+ private isPaused;
38
+ private cursorLineFromBottom;
39
+ private readonly renderInterval;
40
+ private renderTimer;
41
+ private needsRender;
42
+ constructor(options?: FooterOptions);
43
+ /**
44
+ * Start the footer (begins render loop and input capture)
45
+ */
46
+ start(): void;
47
+ /**
48
+ * Stop the footer
49
+ */
50
+ stop(): void;
51
+ /**
52
+ * Set whether agent is running
53
+ * Enables/disables queue mode and spinner
54
+ */
55
+ setAgentRunning(running: boolean): void;
56
+ /**
57
+ * Check if agent is running
58
+ */
59
+ isAgentRunning(): boolean;
60
+ /**
61
+ * Set the current mode
62
+ */
63
+ setMode(mode: AgentMode): void;
64
+ /**
65
+ * Get the current mode
66
+ */
67
+ getMode(): AgentMode;
68
+ /**
69
+ * Update the todo list
70
+ */
71
+ setTodos(todos: TodoItem[]): void;
72
+ /**
73
+ * Set the current tool being executed
74
+ */
75
+ setCurrentTool(tool: string | null): void;
76
+ /**
77
+ * Set a suggestion for the next action (ghost text in input prompt)
78
+ */
79
+ setSuggestion(action: string | null): void;
80
+ /**
81
+ * Set custom spinner text (overrides todo activeForm and random thinking word)
82
+ * Pass null to clear and use default behavior.
83
+ */
84
+ setSpinnerText(text: string | null): void;
85
+ /**
86
+ * Add tokens to the spinner counter
87
+ */
88
+ addTokens(count: number): void;
89
+ /**
90
+ * Get queued inputs
91
+ */
92
+ getQueuedInputs(): string[];
93
+ /**
94
+ * Pop the first queued input
95
+ */
96
+ popQueuedInput(): string | null;
97
+ /**
98
+ * Check if there are queued inputs
99
+ */
100
+ hasQueuedInput(): boolean;
101
+ /**
102
+ * Clear the queue
103
+ */
104
+ clearQueue(): void;
105
+ /**
106
+ * Clear footer before scrolling output
107
+ * Call this before writing to the scrolling zone
108
+ */
109
+ clearForOutput(): void;
110
+ /**
111
+ * Force an immediate render
112
+ */
113
+ forceRender(): void;
114
+ /**
115
+ * Pause footer completely (for overlays)
116
+ * Stops render loop, input capture, and animation
117
+ */
118
+ pauseAnimation(): void;
119
+ /**
120
+ * Resume footer after pause
121
+ * Restarts render loop, input capture, and animation
122
+ */
123
+ resumeAnimation(): void;
124
+ /**
125
+ * Get the height of last render
126
+ */
127
+ getLastRenderHeight(): number;
128
+ /**
129
+ * Refresh the prompt with current theme colors
130
+ * Call after theme changes to apply new colors immediately
131
+ */
132
+ refreshPrompt(): void;
133
+ /**
134
+ * Clear the footer area
135
+ */
136
+ private clear;
137
+ /**
138
+ * Render the entire footer
139
+ */
140
+ private render;
141
+ /**
142
+ * Render mode indicator line
143
+ */
144
+ private renderModeIndicator;
145
+ /**
146
+ * Calculate how many terminal rows a line takes (accounting for wrapping)
147
+ */
148
+ private getTerminalRowsForLine;
149
+ /**
150
+ * Position cursor correctly within the input prompt
151
+ */
152
+ private positionCursor;
153
+ }