@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,103 @@
1
+ /**
2
+ * UI Types and Interfaces
3
+ *
4
+ * Shared types used across all UI modules.
5
+ */
6
+ export interface UserMessage {
7
+ type: 'user';
8
+ content: string;
9
+ timestamp: Date;
10
+ }
11
+ export interface AssistantMessage {
12
+ type: 'assistant';
13
+ content: string;
14
+ timestamp: Date;
15
+ }
16
+ export interface ToolMessage {
17
+ type: 'tool';
18
+ name: string;
19
+ args?: string;
20
+ result?: string;
21
+ timestamp: Date;
22
+ }
23
+ export type ConversationMessage = UserMessage | AssistantMessage | ToolMessage;
24
+ export interface TodoItem {
25
+ content: string;
26
+ status: 'pending' | 'in_progress' | 'completed';
27
+ activeForm?: string;
28
+ }
29
+ export interface SpinnerState {
30
+ text: string;
31
+ tool?: string;
32
+ tokens: number;
33
+ startTime: number;
34
+ }
35
+ export interface CommandOption {
36
+ command: string;
37
+ description: string;
38
+ }
39
+ export interface AutocompleteState {
40
+ options: CommandOption[];
41
+ selectedIndex: number;
42
+ inputPrefix: string;
43
+ }
44
+ export type InputResult = {
45
+ action: 'submit';
46
+ value: string;
47
+ } | {
48
+ action: 'command';
49
+ command: string;
50
+ args: string;
51
+ } | {
52
+ action: 'cancel';
53
+ } | {
54
+ action: 'continue';
55
+ };
56
+ export interface PhysicalLayout {
57
+ lines: string[];
58
+ cursorRow: number;
59
+ cursorCol: number;
60
+ totalRows: number;
61
+ }
62
+ export interface ContextStats {
63
+ tokens: number;
64
+ maxTokens: number;
65
+ messages: number;
66
+ turns: number;
67
+ utilization: number;
68
+ }
69
+ export type PermissionResult = 'allow' | 'deny' | 'allow-always';
70
+ /**
71
+ * Agent execution modes (cycle with Shift+Tab)
72
+ *
73
+ * - normal: Ask permission for dangerous operations (default)
74
+ * - auto-accept: Execute all operations without asking
75
+ * - plan: Generate plan only, don't execute (placeholder for now)
76
+ */
77
+ export type AgentMode = 'normal' | 'auto-accept' | 'plan';
78
+ /**
79
+ * Mode display info
80
+ */
81
+ export interface ModeInfo {
82
+ mode: AgentMode;
83
+ label: string;
84
+ description: string;
85
+ }
86
+ /**
87
+ * Get display info for a mode
88
+ */
89
+ export declare const MODE_INFO: Record<AgentMode, ModeInfo>;
90
+ /**
91
+ * Get next mode in cycle
92
+ */
93
+ export declare function getNextMode(current: AgentMode): AgentMode;
94
+ export interface REPLState {
95
+ conversationHistory: ConversationMessage[];
96
+ spinner: SpinnerState | null;
97
+ todos: TodoItem[];
98
+ pendingMessages: string[];
99
+ isAgentRunning: boolean;
100
+ currentModel: string;
101
+ inputHistory: string[];
102
+ allowedTools: Set<string>;
103
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * UI Types and Interfaces
3
+ *
4
+ * Shared types used across all UI modules.
5
+ */
6
+ /**
7
+ * Get display info for a mode
8
+ */
9
+ export const MODE_INFO = {
10
+ 'normal': {
11
+ mode: 'normal',
12
+ label: 'Normal',
13
+ description: 'Ask permission for dangerous operations',
14
+ },
15
+ 'auto-accept': {
16
+ mode: 'auto-accept',
17
+ label: 'Auto-accept',
18
+ description: 'Execute all operations without asking',
19
+ },
20
+ 'plan': {
21
+ mode: 'plan',
22
+ label: 'Plan',
23
+ description: 'Generate plan only (coming soon)',
24
+ },
25
+ };
26
+ /**
27
+ * Get next mode in cycle
28
+ */
29
+ export function getNextMode(current) {
30
+ const modes = ['normal', 'auto-accept', 'plan'];
31
+ const currentIndex = modes.indexOf(current);
32
+ return modes[(currentIndex + 1) % modes.length];
33
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Credentials Management
3
+ *
4
+ * Securely stores API keys using AES-256-GCM encryption.
5
+ * Encryption key is derived from:
6
+ * - Machine-specific identifiers (hostname, username)
7
+ * - A random "mk" value stored in settings.json
8
+ */
9
+ export type ProviderKey = 'anthropic' | 'openai' | 'google' | 'ollama';
10
+ export interface StoredCredentials {
11
+ anthropic?: string;
12
+ openai?: string;
13
+ google?: string;
14
+ }
15
+ /**
16
+ * Get API key for a provider
17
+ * Priority: Environment variable > Stored credential
18
+ */
19
+ export declare function getApiKey(provider: ProviderKey): string | null;
20
+ /**
21
+ * Set API key for a provider (stores encrypted)
22
+ */
23
+ export declare function setApiKey(provider: ProviderKey, key: string): void;
24
+ /**
25
+ * Delete API key for a provider
26
+ */
27
+ export declare function deleteApiKey(provider: ProviderKey): void;
28
+ /**
29
+ * Check if API key exists for a provider
30
+ */
31
+ export declare function hasApiKey(provider: ProviderKey): boolean;
32
+ /**
33
+ * Get masked version of API key for display
34
+ * Shows first 7 and last 4 characters
35
+ */
36
+ export declare function getMaskedKey(provider: ProviderKey): string | null;
37
+ /**
38
+ * Check if key is from environment variable
39
+ */
40
+ export declare function isKeyFromEnv(provider: ProviderKey): boolean;
41
+ /**
42
+ * Get all provider statuses
43
+ */
44
+ export declare function getAllProviderStatuses(): Array<{
45
+ provider: ProviderKey;
46
+ name: string;
47
+ hasKey: boolean;
48
+ masked: string | null;
49
+ fromEnv: boolean;
50
+ keyUrl: string;
51
+ }>;
52
+ /**
53
+ * Map provider type from settings to credential provider key
54
+ */
55
+ export declare function settingsProviderToCredentialKey(provider: 'auto' | 'claude' | 'openai' | 'gemini' | 'ollama'): ProviderKey;
@@ -0,0 +1,268 @@
1
+ /**
2
+ * Credentials Management
3
+ *
4
+ * Securely stores API keys using AES-256-GCM encryption.
5
+ * Encryption key is derived from:
6
+ * - Machine-specific identifiers (hostname, username)
7
+ * - A random "mk" value stored in settings.json
8
+ */
9
+ import * as crypto from 'crypto';
10
+ import * as fs from 'fs';
11
+ import * as path from 'path';
12
+ import * as os from 'os';
13
+ // =============================================================================
14
+ // Constants
15
+ // =============================================================================
16
+ const CONFIG_DIR = path.join(os.homedir(), '.compilr-dev');
17
+ const SETTINGS_FILE = path.join(CONFIG_DIR, 'settings.json');
18
+ const CREDENTIALS_FILE = path.join(CONFIG_DIR, 'credentials.enc');
19
+ const ALGORITHM = 'aes-256-gcm';
20
+ const SALT = 'compilr-dev-v1';
21
+ // =============================================================================
22
+ // Master Key Management
23
+ // =============================================================================
24
+ /**
25
+ * Get or create the master key ("mk") from settings.json
26
+ */
27
+ function getMasterKey() {
28
+ try {
29
+ // Ensure config directory exists
30
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
31
+ // Load settings
32
+ let settings = {};
33
+ if (fs.existsSync(SETTINGS_FILE)) {
34
+ const parsed = JSON.parse(fs.readFileSync(SETTINGS_FILE, 'utf-8'));
35
+ if (parsed && typeof parsed === 'object') {
36
+ settings = parsed;
37
+ }
38
+ }
39
+ // Check if mk exists
40
+ if (settings.mk && typeof settings.mk === 'string' && settings.mk.length === 64) {
41
+ return settings.mk;
42
+ }
43
+ // Generate new mk (32 bytes = 64 hex chars)
44
+ const mk = crypto.randomBytes(32).toString('hex');
45
+ settings.mk = mk;
46
+ // Save back to settings
47
+ fs.writeFileSync(SETTINGS_FILE, JSON.stringify(settings, null, 2), 'utf-8');
48
+ return mk;
49
+ }
50
+ catch (error) {
51
+ throw new Error(`Failed to manage master key: ${error instanceof Error ? error.message : String(error)}`);
52
+ }
53
+ }
54
+ /**
55
+ * Derive encryption key from master key + machine identifiers
56
+ */
57
+ function deriveKey() {
58
+ const mk = getMasterKey();
59
+ const machineId = `${os.hostname()}:${os.userInfo().username}`;
60
+ // Use scrypt for key derivation (secure, slow by design)
61
+ return crypto.scryptSync(`${mk}:${machineId}`, SALT, 32);
62
+ }
63
+ // =============================================================================
64
+ // Encryption/Decryption
65
+ // =============================================================================
66
+ /**
67
+ * Encrypt data using AES-256-GCM
68
+ */
69
+ function encrypt(data) {
70
+ const key = deriveKey();
71
+ const iv = crypto.randomBytes(16);
72
+ const cipher = crypto.createCipheriv(ALGORITHM, key, iv);
73
+ let encrypted = cipher.update(data, 'utf8', 'hex');
74
+ encrypted += cipher.final('hex');
75
+ const authTag = cipher.getAuthTag();
76
+ // Format: iv:authTag:encrypted
77
+ return `${iv.toString('hex')}:${authTag.toString('hex')}:${encrypted}`;
78
+ }
79
+ /**
80
+ * Decrypt data using AES-256-GCM
81
+ */
82
+ function decrypt(encryptedData) {
83
+ const key = deriveKey();
84
+ const parts = encryptedData.split(':');
85
+ if (parts.length !== 3) {
86
+ throw new Error('Invalid encrypted data format');
87
+ }
88
+ const iv = Buffer.from(parts[0], 'hex');
89
+ const authTag = Buffer.from(parts[1], 'hex');
90
+ const encrypted = parts[2];
91
+ const decipher = crypto.createDecipheriv(ALGORITHM, key, iv);
92
+ decipher.setAuthTag(authTag);
93
+ let decrypted = decipher.update(encrypted, 'hex', 'utf8');
94
+ decrypted += decipher.final('utf8');
95
+ return decrypted;
96
+ }
97
+ // =============================================================================
98
+ // Credentials Storage
99
+ // =============================================================================
100
+ /**
101
+ * Load all stored credentials
102
+ */
103
+ function loadCredentials() {
104
+ try {
105
+ if (!fs.existsSync(CREDENTIALS_FILE)) {
106
+ return {};
107
+ }
108
+ const encryptedData = fs.readFileSync(CREDENTIALS_FILE, 'utf-8');
109
+ const decrypted = decrypt(encryptedData);
110
+ return JSON.parse(decrypted);
111
+ }
112
+ catch {
113
+ // If decryption fails (e.g., key changed), return empty
114
+ return {};
115
+ }
116
+ }
117
+ /**
118
+ * Save all credentials
119
+ */
120
+ function saveCredentials(credentials) {
121
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
122
+ const encrypted = encrypt(JSON.stringify(credentials));
123
+ fs.writeFileSync(CREDENTIALS_FILE, encrypted, 'utf-8');
124
+ }
125
+ // =============================================================================
126
+ // Public API
127
+ // =============================================================================
128
+ /**
129
+ * Get API key for a provider
130
+ * Priority: Environment variable > Stored credential
131
+ */
132
+ export function getApiKey(provider) {
133
+ // Check environment variable first
134
+ const envVarMap = {
135
+ anthropic: 'ANTHROPIC_API_KEY',
136
+ openai: 'OPENAI_API_KEY',
137
+ google: 'GOOGLE_API_KEY',
138
+ ollama: '', // No key needed
139
+ };
140
+ const envVar = envVarMap[provider];
141
+ if (envVar && process.env[envVar]) {
142
+ return process.env[envVar] ?? null;
143
+ }
144
+ // Check stored credentials
145
+ if (provider === 'ollama') {
146
+ return 'local'; // Ollama doesn't need a key
147
+ }
148
+ const credentials = loadCredentials();
149
+ return credentials[provider] ?? null;
150
+ }
151
+ /**
152
+ * Set API key for a provider (stores encrypted)
153
+ */
154
+ export function setApiKey(provider, key) {
155
+ if (provider === 'ollama') {
156
+ return; // Ollama doesn't need a key
157
+ }
158
+ const credentials = loadCredentials();
159
+ credentials[provider] = key;
160
+ saveCredentials(credentials);
161
+ }
162
+ /**
163
+ * Delete API key for a provider
164
+ */
165
+ export function deleteApiKey(provider) {
166
+ const credentials = loadCredentials();
167
+ // Remove the key by setting to undefined and then filtering
168
+ if (provider === 'anthropic') {
169
+ delete credentials.anthropic;
170
+ }
171
+ else if (provider === 'openai') {
172
+ delete credentials.openai;
173
+ }
174
+ else if (provider === 'google') {
175
+ delete credentials.google;
176
+ }
177
+ saveCredentials(credentials);
178
+ }
179
+ /**
180
+ * Check if API key exists for a provider
181
+ */
182
+ export function hasApiKey(provider) {
183
+ return getApiKey(provider) !== null;
184
+ }
185
+ /**
186
+ * Get masked version of API key for display
187
+ * Shows first 7 and last 4 characters
188
+ */
189
+ export function getMaskedKey(provider) {
190
+ const key = getApiKey(provider);
191
+ if (!key || key === 'local') {
192
+ return key;
193
+ }
194
+ if (key.length <= 15) {
195
+ return '***';
196
+ }
197
+ return `${key.substring(0, 7)}...${key.substring(key.length - 4)}`;
198
+ }
199
+ /**
200
+ * Check if key is from environment variable
201
+ */
202
+ export function isKeyFromEnv(provider) {
203
+ const envVarMap = {
204
+ anthropic: 'ANTHROPIC_API_KEY',
205
+ openai: 'OPENAI_API_KEY',
206
+ google: 'GOOGLE_API_KEY',
207
+ ollama: '',
208
+ };
209
+ const envVar = envVarMap[provider];
210
+ return !!(envVar && process.env[envVar]);
211
+ }
212
+ /**
213
+ * Get all provider statuses
214
+ */
215
+ export function getAllProviderStatuses() {
216
+ return [
217
+ {
218
+ provider: 'anthropic',
219
+ name: 'Anthropic (Claude)',
220
+ hasKey: hasApiKey('anthropic'),
221
+ masked: getMaskedKey('anthropic'),
222
+ fromEnv: isKeyFromEnv('anthropic'),
223
+ keyUrl: 'https://console.anthropic.com/settings/keys',
224
+ },
225
+ {
226
+ provider: 'openai',
227
+ name: 'OpenAI',
228
+ hasKey: hasApiKey('openai'),
229
+ masked: getMaskedKey('openai'),
230
+ fromEnv: isKeyFromEnv('openai'),
231
+ keyUrl: 'https://platform.openai.com/api-keys',
232
+ },
233
+ {
234
+ provider: 'google',
235
+ name: 'Google AI (Gemini)',
236
+ hasKey: hasApiKey('google'),
237
+ masked: getMaskedKey('google'),
238
+ fromEnv: isKeyFromEnv('google'),
239
+ keyUrl: 'https://aistudio.google.com/apikey',
240
+ },
241
+ {
242
+ provider: 'ollama',
243
+ name: 'Ollama (Local)',
244
+ hasKey: true, // Always available
245
+ masked: 'local',
246
+ fromEnv: false,
247
+ keyUrl: 'https://ollama.ai',
248
+ },
249
+ ];
250
+ }
251
+ /**
252
+ * Map provider type from settings to credential provider key
253
+ */
254
+ export function settingsProviderToCredentialKey(provider) {
255
+ switch (provider) {
256
+ case 'claude':
257
+ return 'anthropic';
258
+ case 'gemini':
259
+ return 'google';
260
+ case 'openai':
261
+ return 'openai';
262
+ case 'ollama':
263
+ return 'ollama';
264
+ case 'auto':
265
+ default:
266
+ return 'anthropic'; // Default to anthropic for auto
267
+ }
268
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Model Tier Detection
3
+ *
4
+ * Categorizes LLM models by capability tier (small/medium/large)
5
+ * and provides upgrade suggestions within the same provider.
6
+ *
7
+ * Used by commands like /design to warn users about model limitations
8
+ * and suggest alternatives.
9
+ */
10
+ export type TierLevel = 'small' | 'medium' | 'large';
11
+ export interface ModelTier {
12
+ tier: TierLevel;
13
+ provider: string;
14
+ suggestedUpgrade?: string;
15
+ }
16
+ /**
17
+ * Get the tier information for a model.
18
+ * Handles versioned model names (e.g., claude-3-5-sonnet-20241022)
19
+ */
20
+ export declare function getModelTier(modelId: string): ModelTier;
21
+ /**
22
+ * Get the minimum tier required for a command.
23
+ */
24
+ export declare function getCommandMinTier(command: string): TierLevel;
25
+ /**
26
+ * Compare two tier levels.
27
+ * Returns: -1 if a < b, 0 if a == b, 1 if a > b
28
+ */
29
+ export declare function compareTiers(a: TierLevel, b: TierLevel): number;
30
+ /**
31
+ * Check if a model meets the minimum tier requirement for a command.
32
+ */
33
+ export declare function modelMeetsTier(modelId: string, command: string): boolean;
34
+ /**
35
+ * Get a friendly display name for a tier.
36
+ */
37
+ export declare function getTierDisplayName(tier: TierLevel): string;
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Model Tier Detection
3
+ *
4
+ * Categorizes LLM models by capability tier (small/medium/large)
5
+ * and provides upgrade suggestions within the same provider.
6
+ *
7
+ * Used by commands like /design to warn users about model limitations
8
+ * and suggest alternatives.
9
+ */
10
+ // =============================================================================
11
+ // Model Tier Mappings
12
+ // =============================================================================
13
+ const MODEL_TIERS = {
14
+ // Claude (Anthropic)
15
+ 'claude-3-opus': { tier: 'large', provider: 'anthropic' },
16
+ 'claude-opus-4': { tier: 'large', provider: 'anthropic' },
17
+ 'claude-sonnet-4': { tier: 'large', provider: 'anthropic' },
18
+ 'claude-3-5-sonnet': { tier: 'large', provider: 'anthropic' },
19
+ 'claude-3-sonnet': { tier: 'large', provider: 'anthropic' },
20
+ 'claude-3-5-haiku': { tier: 'medium', provider: 'anthropic', suggestedUpgrade: 'claude-sonnet-4-20250514' },
21
+ 'claude-3-haiku': { tier: 'medium', provider: 'anthropic', suggestedUpgrade: 'claude-sonnet-4-20250514' },
22
+ // OpenAI
23
+ 'gpt-4o': { tier: 'large', provider: 'openai' },
24
+ 'gpt-4-turbo': { tier: 'large', provider: 'openai' },
25
+ 'gpt-4': { tier: 'large', provider: 'openai' },
26
+ 'gpt-4o-mini': { tier: 'medium', provider: 'openai', suggestedUpgrade: 'gpt-4o' },
27
+ 'gpt-3.5-turbo': { tier: 'small', provider: 'openai', suggestedUpgrade: 'gpt-4o-mini' },
28
+ 'o1': { tier: 'large', provider: 'openai' },
29
+ 'o1-mini': { tier: 'medium', provider: 'openai', suggestedUpgrade: 'o1' },
30
+ 'o1-preview': { tier: 'large', provider: 'openai' },
31
+ // Google (Gemini)
32
+ 'gemini-1.5-pro': { tier: 'large', provider: 'google' },
33
+ 'gemini-2.0-flash': { tier: 'large', provider: 'google' },
34
+ 'gemini-2.5-pro': { tier: 'large', provider: 'google' },
35
+ 'gemini-2.5-flash': { tier: 'medium', provider: 'google', suggestedUpgrade: 'gemini-2.5-pro' },
36
+ 'gemini-1.5-flash': { tier: 'medium', provider: 'google', suggestedUpgrade: 'gemini-1.5-pro' },
37
+ 'gemini-2.0-flash-lite': { tier: 'small', provider: 'google', suggestedUpgrade: 'gemini-2.0-flash' },
38
+ // Ollama / Local models (common ones)
39
+ 'llama3.2': { tier: 'small', provider: 'ollama', suggestedUpgrade: 'llama3.1:70b' },
40
+ 'llama3.1': { tier: 'medium', provider: 'ollama', suggestedUpgrade: 'llama3.1:70b' },
41
+ 'llama3.1:70b': { tier: 'large', provider: 'ollama' },
42
+ 'mistral': { tier: 'small', provider: 'ollama' },
43
+ 'mixtral': { tier: 'medium', provider: 'ollama' },
44
+ 'codellama': { tier: 'medium', provider: 'ollama' },
45
+ 'deepseek-coder': { tier: 'medium', provider: 'ollama' },
46
+ 'qwen2.5-coder': { tier: 'medium', provider: 'ollama' },
47
+ };
48
+ // =============================================================================
49
+ // Functions
50
+ // =============================================================================
51
+ /**
52
+ * Get the tier information for a model.
53
+ * Handles versioned model names (e.g., claude-3-5-sonnet-20241022)
54
+ */
55
+ export function getModelTier(modelId) {
56
+ // Check exact match first
57
+ if (modelId in MODEL_TIERS) {
58
+ return MODEL_TIERS[modelId];
59
+ }
60
+ // Check partial match (for versioned models like claude-3-5-sonnet-20241022)
61
+ for (const [key, value] of Object.entries(MODEL_TIERS)) {
62
+ if (modelId.startsWith(key)) {
63
+ return value;
64
+ }
65
+ }
66
+ // Ollama/local models - assume small unless known
67
+ if (modelId.includes('ollama') || modelId.includes('localhost')) {
68
+ return { tier: 'small', provider: 'ollama' };
69
+ }
70
+ // Unknown - assume medium (safer than assuming small)
71
+ return { tier: 'medium', provider: 'unknown' };
72
+ }
73
+ /**
74
+ * Get the minimum tier required for a command.
75
+ */
76
+ export function getCommandMinTier(command) {
77
+ const requirements = {
78
+ '/design': 'large',
79
+ '/refine': 'large',
80
+ '/arch': 'large',
81
+ '/build': 'medium',
82
+ '/prd': 'medium',
83
+ '/sketch': 'small', // Simplified version - works with any model
84
+ '/init': 'small',
85
+ '/backlog': 'small',
86
+ '/note': 'small',
87
+ };
88
+ return requirements[command] ?? 'small';
89
+ }
90
+ /**
91
+ * Compare two tier levels.
92
+ * Returns: -1 if a < b, 0 if a == b, 1 if a > b
93
+ */
94
+ export function compareTiers(a, b) {
95
+ const order = { small: 0, medium: 1, large: 2 };
96
+ return order[a] - order[b];
97
+ }
98
+ /**
99
+ * Check if a model meets the minimum tier requirement for a command.
100
+ */
101
+ export function modelMeetsTier(modelId, command) {
102
+ const modelTier = getModelTier(modelId);
103
+ const minTier = getCommandMinTier(command);
104
+ return compareTiers(modelTier.tier, minTier) >= 0;
105
+ }
106
+ /**
107
+ * Get a friendly display name for a tier.
108
+ */
109
+ export function getTierDisplayName(tier) {
110
+ switch (tier) {
111
+ case 'small':
112
+ return 'Small';
113
+ case 'medium':
114
+ return 'Medium';
115
+ case 'large':
116
+ return 'Large';
117
+ }
118
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Project Memory Loader
3
+ *
4
+ * Loads project-level instructions from COMPILR.md or CLAUDE.md files.
5
+ * Provides size guards and warnings for large files.
6
+ */
7
+ export interface ProjectMemoryResult {
8
+ /** Whether a memory file was found */
9
+ found: boolean;
10
+ /** The loaded content (may be truncated) */
11
+ content: string;
12
+ /** Path to the file that was loaded */
13
+ filePath: string | null;
14
+ /** Original file size in bytes */
15
+ originalSize: number;
16
+ /** Whether the content was truncated */
17
+ truncated: boolean;
18
+ /** Estimated token count (chars / 4) */
19
+ estimatedTokens: number;
20
+ }
21
+ export interface ProjectMemoryOptions {
22
+ /** Maximum content size in bytes before truncation (default: 100KB) */
23
+ maxSize?: number;
24
+ /** Size threshold for warning (default: 30KB) */
25
+ warnSize?: number;
26
+ /** Custom search directory (default: process.cwd()) */
27
+ cwd?: string;
28
+ }
29
+ /**
30
+ * Load project memory from COMPILR.md or CLAUDE.md
31
+ *
32
+ * @param options - Configuration options
33
+ * @returns Result with content and metadata
34
+ */
35
+ export declare function loadProjectMemory(options?: ProjectMemoryOptions): ProjectMemoryResult;
36
+ /**
37
+ * Check if a project memory file exists without loading it
38
+ */
39
+ export declare function hasProjectMemory(cwd?: string): boolean;
40
+ /**
41
+ * Get the size category for display purposes
42
+ */
43
+ export declare function getSizeCategory(bytes: number): 'small' | 'medium' | 'large';
44
+ /**
45
+ * Format bytes as human-readable string
46
+ */
47
+ export declare function formatBytes(bytes: number): string;