@compilr-dev/cli 0.10.2 → 0.10.4

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 CHANGED
@@ -47,7 +47,7 @@ Use your preferred AI provider with three tiers per provider (Fast / Balanced /
47
47
  |----------|--------|---------|
48
48
  | **Claude** | Haiku, Sonnet, Opus | `ANTHROPIC_API_KEY` |
49
49
  | **OpenAI** | GPT-4o-mini, GPT-4o, o1 | `OPENAI_API_KEY` |
50
- | **Gemini** | Flash, Pro, Ultra | `GOOGLE_API_KEY` |
50
+ | **Gemini** | Flash, Pro, Ultra | `GOOGLE_AI_API_KEY` |
51
51
  | **Ollama** | Any local model | None (local) |
52
52
  | **Together AI** | Open-source models | `TOGETHER_API_KEY` |
53
53
  | **Groq** | Fast inference | `GROQ_API_KEY` |
@@ -211,7 +211,7 @@ Run `/keys` inside the CLI to securely store your API keys. Keys are encrypted a
211
211
  ```bash
212
212
  export ANTHROPIC_API_KEY="sk-ant-..." # Claude
213
213
  export OPENAI_API_KEY="sk-..." # OpenAI
214
- export GOOGLE_API_KEY="..." # Google Gemini
214
+ export GOOGLE_AI_API_KEY="..." # Google Gemini
215
215
  # Ollama: no key needed, just run `ollama serve`
216
216
  ```
217
217
 
Binary file
@@ -40,5 +40,15 @@ export declare function getAllModelsForProviderUI(provider: ProviderType): Model
40
40
  export declare function tierModelExamples(tier: ModelTier, providers?: readonly string[]): string;
41
41
  /** The current default model's display name for a tier and provider: "Sonnet 5.5". */
42
42
  export declare function tierModelName(provider: string, tier: ModelTier): string;
43
+ /**
44
+ * The model ID a tier resolves to, for the tutorials' screen mock-ups.
45
+ *
46
+ * ⚠️ IDs GO STALE DIFFERENTLY FROM NAMES. `tierModelName` covers prose ("Haiku 5.5"); a mock-up
47
+ * that shows what the app prints needs the actual id, and a hardcoded one reads as current
48
+ * forever — `claude-haiku-4-5-20251001` sat in three tutorials as the "Default" through four
49
+ * model generations. The guard test only checks that the CURRENT name is present, so a stale id
50
+ * beside it stays green.
51
+ */
52
+ export declare function tierModelId(provider: string, tier: ModelTier): string;
43
53
  /** "Claude, OpenAI, Gemini" — the providers whose current models accept images. */
44
54
  export declare function imageProviderNames(): string;
@@ -91,6 +91,18 @@ export function tierModelExamples(tier, providers = ['claude', 'openai', 'gemini
91
91
  export function tierModelName(provider, tier) {
92
92
  return (MODEL_REGISTRY.find((m) => m.provider === provider && m.defaultTier === tier)?.displayName ?? '');
93
93
  }
94
+ /**
95
+ * The model ID a tier resolves to, for the tutorials' screen mock-ups.
96
+ *
97
+ * ⚠️ IDs GO STALE DIFFERENTLY FROM NAMES. `tierModelName` covers prose ("Haiku 5.5"); a mock-up
98
+ * that shows what the app prints needs the actual id, and a hardcoded one reads as current
99
+ * forever — `claude-haiku-4-5-20251001` sat in three tutorials as the "Default" through four
100
+ * model generations. The guard test only checks that the CURRENT name is present, so a stale id
101
+ * beside it stays green.
102
+ */
103
+ export function tierModelId(provider, tier) {
104
+ return MODEL_REGISTRY.find((m) => m.provider === provider && m.defaultTier === tier)?.id ?? '';
105
+ }
94
106
  /** "Claude, OpenAI, Gemini" — the providers whose current models accept images. */
95
107
  export function imageProviderNames() {
96
108
  const seen = [];
@@ -5,7 +5,7 @@
5
5
  * likely to be silently wrong: WHICH prompt field an edit reads and writes, and WHAT
6
6
  * ends up in the patch.
7
7
  */
8
- import type { TeamAgentUpdate, MascotExpression, ToolProfile } from '@compilr-dev/sdk';
8
+ import type { TeamAgentUpdate, ToolProfile } from '@compilr-dev/sdk';
9
9
  import type { CustomAgentDefinition } from './custom-agents.js';
10
10
  import type { ModelTier } from '../models/index.js';
11
11
  /** The agent's values as they were when the editor opened. */
@@ -13,7 +13,7 @@ export interface AgentEditOriginals {
13
13
  displayName: string;
14
14
  specialty: string;
15
15
  instructions: string;
16
- mascot: MascotExpression;
16
+ mascot: string;
17
17
  modelTier: ModelTier;
18
18
  toolProfile: ToolProfile;
19
19
  /** Tone. Composed into the prompt at initialize() as "Your manner: …". */
@@ -26,7 +26,8 @@ export interface AgentEditedValues {
26
26
  displayName: string;
27
27
  specialty: string;
28
28
  instructions: string;
29
- mascot: MascotExpression;
29
+ /** See `AgentEditOriginals.mascot`. */
30
+ mascot: string;
30
31
  modelTier: ModelTier;
31
32
  toolProfile: ToolProfile;
32
33
  personality: string;
@@ -10,7 +10,7 @@ export { getActiveProject, setActiveProject } from './project-db.js';
10
10
  /**
11
11
  * All database tools combined (32 tools from SDK)
12
12
  */
13
- export declare const allDbTools: import("@compilr-dev/sdk").Tool<never>[];
13
+ export declare const allDbTools: import("@compilr-dev/agents").Tool<never>[];
14
14
  /**
15
15
  * All factory tools (5 tools from @compilr-dev/factory)
16
16
  */
@@ -14,7 +14,7 @@ import { existsSync, rmSync } from 'fs';
14
14
  import { join } from 'path';
15
15
  import { getCurrentProject, setCurrentProject } from './project-db.js';
16
16
  import { awardFirstProject, awardWorkItemCompletion } from '../games/coins.js';
17
- import { getActiveSharedContext } from '@compilr-dev/sdk';
17
+ import { getActiveSharedContext, resolveSelfAlias } from '@compilr-dev/sdk';
18
18
  import { setAnchorStore } from '../anchors/index.js';
19
19
  import { recordTeamActivity, getSessionsPath as getCheckpointerSessionsPath } from '../multi-agent/index.js';
20
20
  import { getGlobalEpisodeStore } from '../episodes/index.js';
@@ -159,13 +159,11 @@ const config = {
159
159
  awardWorkItemCompletion();
160
160
  },
161
161
  resolveOwner: () => {
162
- // The SDK only calls this with alias='self'
162
+ // The SDK only calls this with alias='self'. The "'default' is the coordinator slot, not
163
+ // an identity" rule comes from the SDK so Desktop and the CLI cannot drift on it — it was
164
+ // inline here and absent there, which cost Desktop per-agent user-edit notices entirely.
163
165
  const sharedContext = getActiveSharedContext();
164
- const activeAgent = sharedContext?.getTeam().activeAgent;
165
- if (activeAgent && activeAgent !== 'default') {
166
- return activeAgent;
167
- }
168
- return undefined;
166
+ return resolveSelfAlias(sharedContext?.getTeam().activeAgent);
169
167
  },
170
168
  },
171
169
  };
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Covers the /model overlay: provider tabs, tiers, API keys, model cycling.
5
5
  */
6
- import { tierModelExamples, tierModelName } from '../../../../../models/model-registry.js';
6
+ import { tierModelExamples, tierModelId, tierModelName } from '../../../../../models/model-registry.js';
7
7
  export const modelSelectionTutorial = (s) => ({
8
8
  id: 'model-selection',
9
9
  title: 'Model Selection',
@@ -97,8 +97,8 @@ export const modelSelectionTutorial = (s) => ({
97
97
  ` API Key: ${s.success('●')} Set`,
98
98
  '',
99
99
  ` ${s.muted('─── Model ID ───')}`,
100
- ` Default: ${s.muted('claude-haiku-4-5-20251001')}`,
101
- ` Current: Haiku 4.5 ${s.muted('[1/5]')}`,
100
+ ` Default: ${s.muted(tierModelId('claude', 'fast'))}`,
101
+ ` Current: ${tierModelName('claude', 'fast')} ${s.muted('[1/5]')}`,
102
102
  '',
103
103
  ` ${s.success('✓')} This is the active model`,
104
104
  '',
@@ -3,6 +3,7 @@
3
3
  *
4
4
  * Walkthrough of the /design command for comprehensive requirements gathering.
5
5
  */
6
+ import { tierModelId } from '../../../../../models/model-registry.js';
6
7
  export const designTutorial = (s) => ({
7
8
  id: 'design',
8
9
  title: 'Design Workflow',
@@ -40,10 +41,10 @@ export const designTutorial = (s) => ({
40
41
  ` /design works best with larger models for complex`,
41
42
  ` multi-question workflows.`,
42
43
  '',
43
- ` Current: ${s.muted('claude-haiku-4-5-20251001 (medium tier)')}`,
44
- ` Recommended: ${s.primary('claude-sonnet-4-6 (large tier)')}`,
44
+ ` Current: ${s.muted(`${tierModelId('claude', 'fast')} (medium tier)`)}`,
45
+ ` Recommended: ${s.primary(`${tierModelId('claude', 'balanced')} (large tier)`)}`,
45
46
  '',
46
- ` ${s.primary('>')} 1. Switch to claude-sonnet-4-6 and continue`,
47
+ ` ${s.primary('>')} 1. Switch to ${tierModelId('claude', 'balanced')} and continue`,
47
48
  ` 2. Use /sketch instead (simpler, works with any model)`,
48
49
  ` 3. Continue with current model anyway`,
49
50
  '',
@@ -3,6 +3,7 @@
3
3
  *
4
4
  * Walkthrough of the /scaffold command for creating project foundations.
5
5
  */
6
+ import { tierModelId } from '../../../../../models/model-registry.js';
6
7
  export const scaffoldTutorial = (s) => ({
7
8
  id: 'scaffold',
8
9
  title: 'Project Scaffolding',
@@ -68,7 +69,7 @@ export const scaffoldTutorial = (s) => ({
68
69
  ` ${s.warning('! Model Recommendation')}`,
69
70
  '',
70
71
  ` /scaffold works best with larger models.`,
71
- ` Current: ${s.muted('claude-haiku-4-5 (medium tier)')}`,
72
+ ` Current: ${s.muted(`${tierModelId('claude', 'fast')} (medium tier)`)}`,
72
73
  '',
73
74
  ` ${s.primary('>')} 1. Switch model and continue`,
74
75
  ` 2. Use /sketch instead`,
@@ -14,7 +14,7 @@
14
14
  */
15
15
  import { BaseOverlayV2 } from '../../base/index.js';
16
16
  import type { RenderContext, OverlayAction, KeyEvent } from '../types.js';
17
- import { type ToolProfile, type MascotExpression } from '@compilr-dev/sdk';
17
+ import { type ToolProfile } from '@compilr-dev/sdk';
18
18
  import { type ModelTier } from '../../../models/index.js';
19
19
  type FieldKey = 'displayName' | 'specialty' | 'personality' | 'instructions' | 'modelTier' | 'toolProfile' | 'mascot';
20
20
  interface EditableField {
@@ -38,7 +38,8 @@ interface AgentEditState {
38
38
  export interface AgentEditOptions {
39
39
  agentId: string;
40
40
  displayName: string;
41
- mascot: MascotExpression;
41
+ /** A face like `[⊡_⊡]`, not a `MascotExpression` — see multi-agent/agent-edit.ts. */
42
+ mascot: string;
42
43
  /** TeamAgent.description — the one-line "what this agent is for". */
43
44
  specialty: string;
44
45
  /** Tone only. Composed at initialize() as "Your manner: …" (SDK 0.23+). */
@@ -65,7 +66,8 @@ export interface AgentEditValues {
65
66
  instructions: string;
66
67
  modelTier: ModelTier;
67
68
  toolProfile: ToolProfile;
68
- mascot: MascotExpression;
69
+ /** A face like `[⊡_⊡]`, not a `MascotExpression` — see multi-agent/agent-edit.ts. */
70
+ mascot: string;
69
71
  }
70
72
  export interface AgentEditResult {
71
73
  saved: boolean;
@@ -11,7 +11,7 @@
11
11
  */
12
12
  import { BaseOverlayV2 } from '../../base/overlay-base-v2.js';
13
13
  import { renderBorder } from '../../base/index.js';
14
- import { hasApiKey, getMaskedKey, isKeyFromEnv } from '../../../utils/credentials.js';
14
+ import { hasApiKey, getMaskedKey, isKeyFromEnv, PROVIDER_ENV_VARS, } from '../../../utils/credentials.js';
15
15
  import { getSettings } from '../../../settings/index.js';
16
16
  import * as terminal from '../../terminal.js';
17
17
  // =============================================================================
@@ -38,10 +38,17 @@ export class InitSetupOverlayV2 extends BaseOverlayV2 {
38
38
  // API Keys Section
39
39
  lines.push(` ${s.secondary('API Keys')}`);
40
40
  lines.push('');
41
+ /*
42
+ ⚠️ The env var names come from PROVIDER_ENV_VARS, not from a list written out here. This
43
+ copy had drifted: it said `GOOGLE_API_KEY` while every reader in the CLI looks at
44
+ `GOOGLE_AI_API_KEY`, so a user who had set the key correctly was told
45
+ "✓ From env (GOOGLE_API_KEY)" — a variable they never set — and a user reading this screen
46
+ to learn the name set the wrong one.
47
+ */
41
48
  const providers = [
42
- { key: 'anthropic', name: 'Anthropic (Claude)', envVar: 'ANTHROPIC_API_KEY' },
43
- { key: 'openai', name: 'OpenAI', envVar: 'OPENAI_API_KEY' },
44
- { key: 'google', name: 'Google AI (Gemini)', envVar: 'GOOGLE_API_KEY' },
49
+ { key: 'anthropic', name: 'Anthropic (Claude)', envVar: PROVIDER_ENV_VARS.anthropic },
50
+ { key: 'openai', name: 'OpenAI', envVar: PROVIDER_ENV_VARS.openai },
51
+ { key: 'google', name: 'Google AI (Gemini)', envVar: PROVIDER_ENV_VARS.google },
45
52
  ];
46
53
  for (const provider of providers) {
47
54
  const has = hasApiKey(provider.key);
@@ -17,6 +17,17 @@ export interface StoredCredentials {
17
17
  perplexity?: string;
18
18
  openrouter?: string;
19
19
  }
20
+ /**
21
+ * The environment variable each provider's key is read from.
22
+ *
23
+ * ⚠️ ONE map, exported. It was written out three times — twice inline here and once as a
24
+ * display label in the `/init` wizard — and the wizard's copy had drifted: it told the user
25
+ * `GOOGLE_API_KEY` while every reader in the CLI looks at `GOOGLE_AI_API_KEY`. So a user who
26
+ * had set the key correctly was shown "✓ From env (GOOGLE_API_KEY)", naming a variable they
27
+ * never set, and a user reading the wizard to learn the name set the wrong one. The published
28
+ * docs repeat it too, inside a copy-paste block.
29
+ */
30
+ export declare const PROVIDER_ENV_VARS: Record<ProviderKey, string>;
20
31
  /**
21
32
  * Get API key for a provider
22
33
  * Priority: Environment variable > Stored credential
@@ -139,24 +139,34 @@ function saveCredentials(credentials) {
139
139
  // =============================================================================
140
140
  // Public API
141
141
  // =============================================================================
142
+ /**
143
+ * The environment variable each provider's key is read from.
144
+ *
145
+ * ⚠️ ONE map, exported. It was written out three times — twice inline here and once as a
146
+ * display label in the `/init` wizard — and the wizard's copy had drifted: it told the user
147
+ * `GOOGLE_API_KEY` while every reader in the CLI looks at `GOOGLE_AI_API_KEY`. So a user who
148
+ * had set the key correctly was shown "✓ From env (GOOGLE_API_KEY)", naming a variable they
149
+ * never set, and a user reading the wizard to learn the name set the wrong one. The published
150
+ * docs repeat it too, inside a copy-paste block.
151
+ */
152
+ export const PROVIDER_ENV_VARS = {
153
+ anthropic: 'ANTHROPIC_API_KEY',
154
+ openai: 'OPENAI_API_KEY',
155
+ google: 'GOOGLE_AI_API_KEY',
156
+ ollama: '', // No key needed
157
+ together: 'TOGETHER_API_KEY',
158
+ groq: 'GROQ_API_KEY',
159
+ fireworks: 'FIREWORKS_API_KEY',
160
+ perplexity: 'PERPLEXITY_API_KEY',
161
+ openrouter: 'OPENROUTER_API_KEY',
162
+ };
142
163
  /**
143
164
  * Get API key for a provider
144
165
  * Priority: Environment variable > Stored credential
145
166
  */
146
167
  export function getApiKey(provider) {
147
168
  // Check environment variable first
148
- const envVarMap = {
149
- anthropic: 'ANTHROPIC_API_KEY',
150
- openai: 'OPENAI_API_KEY',
151
- google: 'GOOGLE_AI_API_KEY',
152
- ollama: '', // No key needed
153
- together: 'TOGETHER_API_KEY',
154
- groq: 'GROQ_API_KEY',
155
- fireworks: 'FIREWORKS_API_KEY',
156
- perplexity: 'PERPLEXITY_API_KEY',
157
- openrouter: 'OPENROUTER_API_KEY',
158
- };
159
- const envVar = envVarMap[provider];
169
+ const envVar = PROVIDER_ENV_VARS[provider];
160
170
  if (envVar && process.env[envVar]) {
161
171
  return process.env[envVar] ?? null;
162
172
  }
@@ -236,18 +246,7 @@ export function getMaskedKey(provider) {
236
246
  * Check if key is from environment variable
237
247
  */
238
248
  export function isKeyFromEnv(provider) {
239
- const envVarMap = {
240
- anthropic: 'ANTHROPIC_API_KEY',
241
- openai: 'OPENAI_API_KEY',
242
- google: 'GOOGLE_AI_API_KEY',
243
- ollama: '',
244
- together: 'TOGETHER_API_KEY',
245
- groq: 'GROQ_API_KEY',
246
- fireworks: 'FIREWORKS_API_KEY',
247
- perplexity: 'PERPLEXITY_API_KEY',
248
- openrouter: 'OPENROUTER_API_KEY',
249
- };
250
- const envVar = envVarMap[provider];
249
+ const envVar = PROVIDER_ENV_VARS[provider];
251
250
  return !!(envVar && process.env[envVar]);
252
251
  }
253
252
  /**
@@ -28,6 +28,7 @@ const MODEL_TIERS = {
28
28
  'claude-fable-5': { tier: 'large', provider: 'anthropic' },
29
29
  'claude-opus-4-8': { tier: 'large', provider: 'anthropic' },
30
30
  'claude-sonnet-5': { tier: 'large', provider: 'anthropic' },
31
+ 'claude-haiku-5-5': { tier: 'medium', provider: 'anthropic', suggestedUpgrade: 'claude-sonnet-5-5' },
31
32
  'claude-haiku-4-5': { tier: 'medium', provider: 'anthropic', suggestedUpgrade: 'claude-sonnet-5-5' },
32
33
  // Claude legacy (for backward compatibility)
33
34
  'claude-opus-4-7': { tier: 'large', provider: 'anthropic' },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@compilr-dev/cli",
3
- "version": "0.10.2",
3
+ "version": "0.10.4",
4
4
  "description": "AI-powered coding assistant CLI using @compilr-dev/agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -12,8 +12,8 @@
12
12
  "start": "node dist/index.js",
13
13
  "dev": "tsc -b && node dist/index.js",
14
14
  "clean": "tsc -b --clean && rm -rf dist",
15
- "lint": "eslint src/",
16
- "lint:fix": "eslint src/ --fix",
15
+ "lint": "eslint src/ tests/",
16
+ "lint:fix": "eslint src/ tests/ --fix",
17
17
  "build:vsix": "cd vscode-extension && npx @vscode/vsce package --out ../dist/compilr-diff-companion.vsix",
18
18
  "prepublishOnly": "npm run lint && npm test && npm run build && npm run build:vsix",
19
19
  "test": "vitest run",
@@ -22,7 +22,9 @@
22
22
  "test:scroll": "npx tsx scripts/test-scroll-regions.ts",
23
23
  "test:scroll-input": "npx tsx scripts/test-scroll-input.ts",
24
24
  "test:repl-v2": "npx tsx scripts/test-repl-v2.ts",
25
- "test:ui": "npx tsx scripts/test-terminal-ui.ts"
25
+ "test:ui": "npx tsx scripts/test-terminal-ui.ts",
26
+ "typecheck": "tsc -b && npm run typecheck:tests",
27
+ "typecheck:tests": "tsc --noEmit -p tsconfig.test.json"
26
28
  },
27
29
  "keywords": [
28
30
  "cli",
@@ -55,12 +57,12 @@
55
57
  },
56
58
  "dependencies": {
57
59
  "@anthropic-ai/sdk": "^0.74.0",
58
- "@compilr-dev/agents": "^0.8.0",
60
+ "@compilr-dev/agents": "^0.9.1",
59
61
  "@compilr-dev/agents-coding": "^1.0.4",
60
62
  "@compilr-dev/editor-core": "^0.0.3",
61
63
  "@compilr-dev/factory": "^0.1.35",
62
64
  "@compilr-dev/logger": "^0.1.0",
63
- "@compilr-dev/sdk": "^0.35.0",
65
+ "@compilr-dev/sdk": "^0.42.0",
64
66
  "@compilr-dev/ui-core": "^0.0.1",
65
67
  "@modelcontextprotocol/sdk": "^1.23.0",
66
68
  "ansi-escapes": "^7.3.0",
@@ -88,10 +90,10 @@
88
90
  "@types/better-sqlite3": "^7.6.13",
89
91
  "@types/node": "^25.2.3",
90
92
  "@types/uuid": "^10.0.0",
91
- "@vscode/vsce": "^3.2.0",
93
+ "@vscode/vsce": "^4.0.0",
92
94
  "eslint": "^9.39.1",
93
95
  "typescript": "^5.3.0",
94
96
  "typescript-eslint": "^8.48.0",
95
- "vitest": "^4.0.18"
97
+ "vitest": "^4.1.11"
96
98
  }
97
99
  }