Alvin 6 mesiacov pred
rodič
commit
e8c6b64117

+ 2 - 8
README.md

@@ -63,7 +63,7 @@ https://raw.githubusercontent.com/alvinunreal/oh-my-opencode-slim/refs/heads/mas
   </tr>
   <tr>
     <td colspan="2">
-      <b>Recommended Models:</b> <code>cliproxy/gemini-claude-opus-4-5-thinking</code> <code>openai/gpt-5.2-codex</code>
+      <b>Recommended Models:</b> <code>kimi-for-coding/k2p5</code> <code>openai/gpt-5.2-codex</code>
     </td>
   </tr>
 </table>
@@ -125,7 +125,7 @@ https://raw.githubusercontent.com/alvinunreal/oh-my-opencode-slim/refs/heads/mas
   </tr>
   <tr>
     <td colspan="2">
-      <b>Recommended Models:</b> <code>openai/gpt-5.2-codex</code> <code>cliproxy/gemini-3-pro-high</code>
+      <b>Recommended Models:</b> <code>openai/gpt-5.2-codex</code> <code>kimi-for-coding/k2p5</code>
     </td>
   </tr>
 </table>
@@ -232,12 +232,6 @@ https://raw.githubusercontent.com/alvinunreal/oh-my-opencode-slim/refs/heads/mas
 
 ---
 
-## 🙏 Credits
-
-This is a slimmed-down fork of [oh-my-opencode](https://github.com/code-yeongyu/oh-my-opencode) by [@code-yeongyu](https://github.com/code-yeongyu).
-
----
-
 ## 📄 License
 
 MIT

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "oh-my-opencode-slim",
-  "version": "0.6.2",
+  "version": "0.6.3",
   "description": "Lightweight agent orchestration plugin for OpenCode - a slimmed-down fork of oh-my-opencode",
   "main": "dist/index.js",
   "types": "dist/index.d.ts",

+ 2 - 1
src/background/background-manager.ts

@@ -159,7 +159,8 @@ export class BackgroundTaskManager {
       this.activeStarts < this.maxConcurrentStarts &&
       this.startQueue.length > 0
     ) {
-      const task = this.startQueue.shift()!;
+      const task = this.startQueue.shift();
+      if (!task) break;
       this.startTask(task);
     }
   }

+ 7 - 25
src/cli/config-io.test.ts

@@ -12,7 +12,6 @@ import { tmpdir } from 'node:os';
 import { join } from 'node:path';
 import {
   addPluginToOpenCodeConfig,
-  addProviderConfig,
   detectCurrentConfig,
   disableDefaultAgents,
   parseConfig,
@@ -120,40 +119,23 @@ describe('config-io', () => {
     expect(saved.plugin.length).toBe(2);
   });
 
-  // Removed: addAuthPlugins test - auth plugin no longer used with cliproxy
-
-  test('addProviderConfig adds cliproxy provider config', () => {
-    const configPath = join(tmpDir, 'opencode', 'opencode.json');
-    paths.ensureConfigDir();
-    writeFileSync(configPath, JSON.stringify({}));
-
-    const result = addProviderConfig({
-      hasAntigravity: true,
-      hasOpenAI: false,
-      hasOpencodeZen: false,
-      hasTmux: false,
-    });
-    expect(result.success).toBe(true);
-
-    const saved = JSON.parse(readFileSync(configPath, 'utf-8'));
-    expect(saved.provider.cliproxy).toBeDefined();
-  });
-
   test('writeLiteConfig writes lite config', () => {
     const litePath = join(tmpDir, 'opencode', 'oh-my-opencode-slim.json');
     paths.ensureConfigDir();
 
     const result = writeLiteConfig({
-      hasAntigravity: true,
+      hasKimi: true,
       hasOpenAI: false,
       hasOpencodeZen: false,
       hasTmux: true,
+      installSkills: false,
+      installCustomSkills: false,
     });
     expect(result.success).toBe(true);
 
     const saved = JSON.parse(readFileSync(litePath, 'utf-8'));
-    expect(saved.preset).toBe('cliproxy');
-    expect(saved.presets.cliproxy).toBeDefined();
+    expect(saved.preset).toBe('kimi');
+    expect(saved.presets.kimi).toBeDefined();
     expect(saved.tmux.enabled).toBe(true);
   });
 
@@ -180,7 +162,7 @@ describe('config-io', () => {
       JSON.stringify({
         plugin: ['oh-my-opencode-slim'],
         provider: {
-          cliproxy: {
+          kimi: {
             npm: '@ai-sdk/openai-compatible',
           },
         },
@@ -201,7 +183,7 @@ describe('config-io', () => {
 
     const detected = detectCurrentConfig();
     expect(detected.isInstalled).toBe(true);
-    expect(detected.hasAntigravity).toBe(true);
+    expect(detected.hasKimi).toBe(true);
     expect(detected.hasOpenAI).toBe(true);
     expect(detected.hasTmux).toBe(true);
   });

+ 5 - 38
src/cli/config-io.ts

@@ -12,7 +12,7 @@ import {
   getExistingConfigPath,
   getLiteConfig,
 } from './paths';
-import { CLIPROXY_PROVIDER_CONFIG, generateLiteConfig } from './providers';
+import { generateLiteConfig } from './providers';
 import type {
   ConfigMergeResult,
   DetectedConfig,
@@ -138,40 +138,7 @@ export async function addPluginToOpenCodeConfig(): Promise<ConfigMergeResult> {
 }
 
 // Removed: addAuthPlugins - no longer needed with cliproxy
-
-export function addProviderConfig(
-  installConfig: InstallConfig,
-): ConfigMergeResult {
-  const configPath = getExistingConfigPath();
-
-  try {
-    ensureConfigDir();
-    const { config: parsedConfig, error } = parseConfig(configPath);
-    if (error) {
-      return {
-        success: false,
-        configPath,
-        error: `Failed to parse config: ${error}`,
-      };
-    }
-    const config = parsedConfig ?? {};
-
-    if (installConfig.hasAntigravity) {
-      const providers = (config.provider ?? {}) as Record<string, unknown>;
-      providers.cliproxy = CLIPROXY_PROVIDER_CONFIG.cliproxy;
-      config.provider = providers;
-    }
-
-    writeConfig(configPath, config);
-    return { success: true, configPath };
-  } catch (err) {
-    return {
-      success: false,
-      configPath,
-      error: `Failed to add provider config: ${err}`,
-    };
-  }
-}
+// Removed: addProviderConfig - default opencode now has kimi provider config
 
 export function writeLiteConfig(
   installConfig: InstallConfig,
@@ -239,7 +206,7 @@ export function disableDefaultAgents(): ConfigMergeResult {
 export function detectCurrentConfig(): DetectedConfig {
   const result: DetectedConfig = {
     isInstalled: false,
-    hasAntigravity: false,
+    hasKimi: false,
     hasOpenAI: false,
     hasOpencodeZen: false,
     hasTmux: false,
@@ -251,9 +218,9 @@ export function detectCurrentConfig(): DetectedConfig {
   const plugins = config.plugin ?? [];
   result.isInstalled = plugins.some((p) => p.startsWith(PACKAGE_NAME));
 
-  // Check for cliproxy provider instead of auth plugin
+  // Check for kimi provider
   const providers = config.provider as Record<string, unknown> | undefined;
-  result.hasAntigravity = !!providers?.cliproxy;
+  result.hasKimi = !!providers?.kimi;
 
   // Try to detect from lite config
   const { config: liteConfig } = parseConfig(getLiteConfig());

+ 4 - 4
src/cli/index.ts

@@ -10,8 +10,8 @@ function parseArgs(args: string[]): InstallArgs {
   for (const arg of args) {
     if (arg === '--no-tui') {
       result.tui = false;
-    } else if (arg.startsWith('--antigravity=')) {
-      result.antigravity = arg.split('=')[1] as BooleanArg;
+    } else if (arg.startsWith('--kimi=')) {
+      result.kimi = arg.split('=')[1] as BooleanArg;
     } else if (arg.startsWith('--openai=')) {
       result.openai = arg.split('=')[1] as BooleanArg;
     } else if (arg.startsWith('--tmux=')) {
@@ -32,7 +32,7 @@ oh-my-opencode-slim installer
 Usage: bunx oh-my-opencode-slim install [OPTIONS]
 
 Options:
-  --antigravity=yes|no   Antigravity subscription (yes/no)
+  --kimi=yes|no          Kimi API access (yes/no)
   --openai=yes|no        OpenAI API access (yes/no)
   --tmux=yes|no          Enable tmux integration (yes/no)
   --no-tui               Non-interactive mode (requires all flags)
@@ -40,7 +40,7 @@ Options:
 
 Examples:
   bunx oh-my-opencode-slim install
-  bunx oh-my-opencode-slim install --no-tui --antigravity=yes --openai=yes --tmux=no
+  bunx oh-my-opencode-slim install --no-tui --kimi=yes --openai=yes --tmux=no
 `);
 }
 

+ 14 - 39
src/cli/install.ts

@@ -1,7 +1,6 @@
 import * as readline from 'node:readline/promises';
 import {
   addPluginToOpenCodeConfig,
-  addProviderConfig,
   detectCurrentConfig,
   disableDefaultAgents,
   generateLiteConfig,
@@ -107,9 +106,7 @@ function formatConfigSummary(config: InstallConfig): string {
   lines.push(`${BOLD}Configuration Summary${RESET}`);
   lines.push('');
   lines.push(`  ${BOLD}Preset:${RESET} ${BLUE}${preset}${RESET}`);
-  lines.push(
-    `  ${config.hasAntigravity ? SYMBOLS.check : `${DIM}○${RESET}`} Antigravity`,
-  );
+  lines.push(`  ${config.hasKimi ? SYMBOLS.check : `${DIM}○${RESET}`} Kimi`);
   lines.push(
     `  ${config.hasOpenAI ? SYMBOLS.check : `${DIM}○${RESET}`} OpenAI`,
   );
@@ -153,7 +150,7 @@ function printAgentModels(config: InstallConfig): void {
 
 function argsToConfig(args: InstallArgs): InstallConfig {
   return {
-    hasAntigravity: args.antigravity === 'yes',
+    hasKimi: args.kimi === 'yes',
     hasOpenAI: args.openai === 'yes',
     hasOpencodeZen: true, // Always enabled - free models available to all users
     hasTmux: args.tmux === 'yes',
@@ -192,10 +189,10 @@ async function runInteractiveMode(
 
   try {
     console.log(`${BOLD}Question 1/${totalQuestions}:${RESET}`);
-    const antigravity = await askYesNo(
+    const kimi = await askYesNo(
       rl,
-      'Do you have an Antigravity subscription (via cliproxy)?',
-      'yes',
+      'Do you want to use Kimi For Coding?',
+      detected.hasKimi ? 'yes' : 'no',
     );
     console.log();
 
@@ -240,7 +237,7 @@ async function runInteractiveMode(
     console.log();
 
     return {
-      hasAntigravity: antigravity === 'yes',
+      hasKimi: kimi === 'yes',
       hasOpenAI: openai === 'yes',
       hasOpencodeZen: true,
       hasTmux: false,
@@ -260,7 +257,6 @@ async function runInstall(config: InstallConfig): Promise<number> {
 
   // Calculate total steps dynamically
   let totalSteps = 4; // Base: check opencode, add plugin, disable default agents, write lite config
-  if (config.hasAntigravity) totalSteps += 1; // provider config only (no auth plugin needed)
   if (config.installSkills) totalSteps += 1; // skills installation
   if (config.installCustomSkills) totalSteps += 1; // custom skills installation
 
@@ -278,13 +274,6 @@ async function runInstall(config: InstallConfig): Promise<number> {
   const agentResult = disableDefaultAgents();
   if (!handleStepResult(agentResult, 'Default agents disabled')) return 1;
 
-  if (config.hasAntigravity) {
-    printStep(step++, totalSteps, 'Adding cliproxy provider configuration...');
-    const providerResult = addProviderConfig(config);
-    if (!handleStepResult(providerResult, 'Cliproxy provider configured'))
-      return 1;
-  }
-
   printStep(step++, totalSteps, 'Writing oh-my-opencode-slim configuration...');
   const liteResult = writeLiteConfig(config);
   if (!handleStepResult(liteResult, 'Config written')) return 1;
@@ -332,7 +321,7 @@ async function runInstall(config: InstallConfig): Promise<number> {
 
   printAgentModels(config);
 
-  if (!config.hasAntigravity && !config.hasOpenAI) {
+  if (!config.hasKimi && !config.hasOpenAI) {
     printWarning(
       'No providers configured. Zen Big Pickle models will be used as fallback.',
     );
@@ -347,27 +336,13 @@ async function runInstall(config: InstallConfig): Promise<number> {
 
   let nextStep = 1;
 
-  if (config.hasAntigravity) {
-    console.log(`  ${nextStep++}. Install cliproxy:`);
-    console.log(`     ${DIM}macOS:${RESET}`);
-    console.log(`       ${BLUE}$ brew install cliproxyapi${RESET}`);
-    console.log(`       ${BLUE}$ brew services start cliproxyapi${RESET}`);
-    console.log(`     ${DIM}Linux:${RESET}`);
-    console.log(
-      `       ${BLUE}$ curl -fsSL https://raw.githubusercontent.com/brokechubb/cliproxyapi-installer/refs/heads/master/cliproxyapi-installer | bash${RESET}`,
-    );
-    console.log();
-    console.log(`  ${nextStep++}. Authenticate with Antigravity via OAuth:`);
-    console.log(`     ${BLUE}$ ./cli-proxy-api --antigravity-login${RESET}`);
-    console.log(
-      `     ${DIM}(Add --no-browser to print login URL instead of opening browser)${RESET}`,
-    );
-    console.log();
-  }
-
-  if (config.hasOpenAI || !config.hasAntigravity) {
+  if (config.hasKimi || config.hasOpenAI) {
     console.log(`  ${nextStep++}. Authenticate with your providers:`);
     console.log(`     ${BLUE}$ opencode auth login${RESET}`);
+    if (config.hasKimi) {
+      console.log();
+      console.log(`     Then select ${BOLD}Kimi For Coding${RESET} provider.`);
+    }
     console.log();
   }
 
@@ -388,7 +363,7 @@ async function runInstall(config: InstallConfig): Promise<number> {
 export async function install(args: InstallArgs): Promise<number> {
   // Non-interactive mode: all args must be provided
   if (!args.tui) {
-    const requiredArgs = ['antigravity', 'openai', 'tmux'] as const;
+    const requiredArgs = ['kimi', 'openai', 'tmux'] as const;
     const errors = requiredArgs.filter((key) => {
       const value = args[key];
       return value === undefined || !['yes', 'no'].includes(value);
@@ -402,7 +377,7 @@ export async function install(args: InstallArgs): Promise<number> {
       }
       console.log();
       printInfo(
-        'Usage: bunx oh-my-opencode-slim install --no-tui --antigravity=<yes|no> --openai=<yes|no> --tmux=<yes|no>',
+        'Usage: bunx oh-my-opencode-slim install --no-tui --kimi=<yes|no> --openai=<yes|no> --tmux=<yes|no>',
       );
       console.log();
       return 1;

+ 32 - 26
src/cli/providers.test.ts

@@ -4,45 +4,44 @@ import { describe, expect, test } from 'bun:test';
 import { generateLiteConfig, MODEL_MAPPINGS } from './providers';
 
 describe('providers', () => {
-  test('generateLiteConfig generates antigravity config when only antigravity selected', () => {
+  test('generateLiteConfig generates kimi config when only kimi selected', () => {
     const config = generateLiteConfig({
-      hasAntigravity: true,
+      hasKimi: true,
       hasOpenAI: false,
       hasOpencodeZen: false,
       hasTmux: false,
       installSkills: false,
+      installCustomSkills: false,
     });
 
-    expect(config.preset).toBe('cliproxy');
-    const agents = (config.presets as any).cliproxy;
+    expect(config.preset).toBe('kimi');
+    const agents = (config.presets as any).kimi;
     expect(agents).toBeDefined();
-    expect(agents.orchestrator.model).toBe(
-      'cliproxy/gemini-claude-opus-4-5-thinking',
-    );
+    expect(agents.orchestrator.model).toBe('kimi-for-coding/k2p5');
     expect(agents.orchestrator.variant).toBeUndefined();
-    expect(agents.fixer.model).toBe('cliproxy/gemini-3-flash-preview');
+    expect(agents.fixer.model).toBe('kimi-for-coding/k2p5');
     expect(agents.fixer.variant).toBe('low');
     // Should NOT include other presets
     expect((config.presets as any).openai).toBeUndefined();
     expect((config.presets as any)['zen-free']).toBeUndefined();
   });
 
-  test('generateLiteConfig generates antigravity-openai preset when both selected', () => {
+  test('generateLiteConfig generates kimi-openai preset when both selected', () => {
     const config = generateLiteConfig({
-      hasAntigravity: true,
+      hasKimi: true,
       hasOpenAI: true,
       hasOpencodeZen: false,
       hasTmux: false,
       installSkills: false,
+      installCustomSkills: false,
     });
 
-    expect(config.preset).toBe('cliproxy');
-    const agents = (config.presets as any).cliproxy;
+    expect(config.preset).toBe('kimi');
+    const agents = (config.presets as any).kimi;
     expect(agents).toBeDefined();
-    expect(agents.orchestrator.model).toBe(
-      'cliproxy/gemini-claude-opus-4-5-thinking',
-    );
+    expect(agents.orchestrator.model).toBe('kimi-for-coding/k2p5');
     expect(agents.orchestrator.variant).toBeUndefined();
+    // Oracle uses OpenAI when both kimi and openai are enabled
     expect(agents.oracle.model).toBe('openai/gpt-5.2-codex');
     expect(agents.oracle.variant).toBe('high');
     // Should NOT include other presets
@@ -52,11 +51,12 @@ describe('providers', () => {
 
   test('generateLiteConfig generates openai preset when only openai selected', () => {
     const config = generateLiteConfig({
-      hasAntigravity: false,
+      hasKimi: false,
       hasOpenAI: true,
       hasOpencodeZen: false,
       hasTmux: false,
       installSkills: false,
+      installCustomSkills: false,
     });
 
     expect(config.preset).toBe('openai');
@@ -67,17 +67,18 @@ describe('providers', () => {
     );
     expect(agents.orchestrator.variant).toBeUndefined();
     // Should NOT include other presets
-    expect((config.presets as any).cliproxy).toBeUndefined();
+    expect((config.presets as any).kimi).toBeUndefined();
     expect((config.presets as any)['zen-free']).toBeUndefined();
   });
 
   test('generateLiteConfig generates zen-free preset when no providers selected', () => {
     const config = generateLiteConfig({
-      hasAntigravity: false,
+      hasKimi: false,
       hasOpenAI: false,
       hasOpencodeZen: false,
       hasTmux: false,
       installSkills: false,
+      installCustomSkills: false,
     });
 
     expect(config.preset).toBe('zen-free');
@@ -86,17 +87,18 @@ describe('providers', () => {
     expect(agents.orchestrator.model).toBe('opencode/big-pickle');
     expect(agents.orchestrator.variant).toBeUndefined();
     // Should NOT include other presets
-    expect((config.presets as any).cliproxy).toBeUndefined();
+    expect((config.presets as any).kimi).toBeUndefined();
     expect((config.presets as any).openai).toBeUndefined();
   });
 
   test('generateLiteConfig uses zen-free big-pickle models', () => {
     const config = generateLiteConfig({
-      hasAntigravity: false,
+      hasKimi: false,
       hasOpenAI: false,
       hasOpencodeZen: true,
       hasTmux: false,
       installSkills: false,
+      installCustomSkills: false,
     });
 
     expect(config.preset).toBe('zen-free');
@@ -110,11 +112,12 @@ describe('providers', () => {
 
   test('generateLiteConfig enables tmux when requested', () => {
     const config = generateLiteConfig({
-      hasAntigravity: false,
+      hasKimi: false,
       hasOpenAI: false,
       hasOpencodeZen: false,
       hasTmux: true,
       installSkills: false,
+      installCustomSkills: false,
     });
 
     expect(config.tmux).toBeDefined();
@@ -123,14 +126,15 @@ describe('providers', () => {
 
   test('generateLiteConfig includes default skills', () => {
     const config = generateLiteConfig({
-      hasAntigravity: true,
+      hasKimi: true,
       hasOpenAI: false,
       hasOpencodeZen: false,
       hasTmux: false,
       installSkills: true,
+      installCustomSkills: false,
     });
 
-    const agents = (config.presets as any).cliproxy;
+    const agents = (config.presets as any).kimi;
     // Orchestrator should always have '*'
     expect(agents.orchestrator.skills).toEqual(['*']);
 
@@ -143,14 +147,15 @@ describe('providers', () => {
 
   test('generateLiteConfig includes mcps field', () => {
     const config = generateLiteConfig({
-      hasAntigravity: true,
+      hasKimi: true,
       hasOpenAI: false,
       hasOpencodeZen: false,
       hasTmux: false,
       installSkills: false,
+      installCustomSkills: false,
     });
 
-    const agents = (config.presets as any).cliproxy;
+    const agents = (config.presets as any).kimi;
     expect(agents.orchestrator.mcps).toBeDefined();
     expect(Array.isArray(agents.orchestrator.mcps)).toBe(true);
     expect(agents.librarian.mcps).toBeDefined();
@@ -159,11 +164,12 @@ describe('providers', () => {
 
   test('generateLiteConfig zen-free includes correct mcps', () => {
     const config = generateLiteConfig({
-      hasAntigravity: false,
+      hasKimi: false,
       hasOpenAI: false,
       hasOpencodeZen: false,
       hasTmux: false,
       installSkills: false,
+      installCustomSkills: false,
     });
 
     const agents = (config.presets as any)['zen-free'];

+ 13 - 55
src/cli/providers.ts

@@ -2,56 +2,15 @@ import { DEFAULT_AGENT_MCPS } from '../config/agent-mcps';
 import { RECOMMENDED_SKILLS } from './skills';
 import type { InstallConfig } from './types';
 
-/**
- * Provider configurations for Cliproxy (Antigravity via cliproxy)
- */
-export const CLIPROXY_PROVIDER_CONFIG = {
-  cliproxy: {
-    npm: '@ai-sdk/openai-compatible',
-    name: 'CliProxy',
-    options: {
-      baseURL: 'http://127.0.0.1:8317/v1',
-      apiKey: 'your-api-key-1',
-    },
-    models: {
-      'gemini-3-pro-high': {
-        name: 'Gemini 3 Pro High',
-        thinking: true,
-        attachment: true,
-        limit: { context: 1048576, output: 65535 },
-        modalities: { input: ['text', 'image', 'pdf'], output: ['text'] },
-      },
-      'gemini-3-flash-preview': {
-        name: 'Gemini 3 Flash',
-        attachment: true,
-        limit: { context: 1048576, output: 65536 },
-        modalities: { input: ['text', 'image', 'pdf'], output: ['text'] },
-      },
-      'gemini-claude-opus-4-5-thinking': {
-        name: 'Claude Opus 4.5 Thinking',
-        attachment: true,
-        limit: { context: 200000, output: 32000 },
-        modalities: { input: ['text', 'image', 'pdf'], output: ['text'] },
-      },
-      'gemini-claude-sonnet-4-5-thinking': {
-        name: 'Claude Sonnet 4.5 Thinking',
-        attachment: true,
-        limit: { context: 200000, output: 32000 },
-        modalities: { input: ['text', 'image', 'pdf'], output: ['text'] },
-      },
-    },
-  },
-};
-
 // Model mappings by provider priority
 export const MODEL_MAPPINGS = {
-  antigravity: {
-    orchestrator: { model: 'cliproxy/gemini-claude-opus-4-5-thinking' },
-    oracle: { model: 'cliproxy/gemini-3-pro-preview', variant: 'high' },
-    librarian: { model: 'cliproxy/gemini-3-flash-preview', variant: 'low' },
-    explorer: { model: 'cliproxy/gemini-3-flash-preview', variant: 'low' },
-    designer: { model: 'cliproxy/gemini-3-flash-preview', variant: 'medium' },
-    fixer: { model: 'cliproxy/gemini-3-flash-preview', variant: 'low' },
+  kimi: {
+    orchestrator: { model: 'kimi-for-coding/k2p5' },
+    oracle: { model: 'kimi-for-coding/k2p5', variant: 'high' },
+    librarian: { model: 'kimi-for-coding/k2p5', variant: 'low' },
+    explorer: { model: 'kimi-for-coding/k2p5', variant: 'low' },
+    designer: { model: 'kimi-for-coding/k2p5', variant: 'medium' },
+    fixer: { model: 'kimi-for-coding/k2p5', variant: 'low' },
   },
   openai: {
     orchestrator: { model: 'openai/gpt-5.2-codex' },
@@ -80,8 +39,8 @@ export function generateLiteConfig(
   };
 
   // Determine active preset name
-  let activePreset: 'cliproxy' | 'openai' | 'zen-free' = 'zen-free';
-  if (installConfig.hasAntigravity) activePreset = 'cliproxy';
+  let activePreset: 'kimi' | 'openai' | 'zen-free' = 'zen-free';
+  if (installConfig.hasKimi) activePreset = 'kimi';
   else if (installConfig.hasOpenAI) activePreset = 'openai';
 
   config.preset = activePreset;
@@ -121,9 +80,9 @@ export function generateLiteConfig(
       Object.entries(mapping).map(([agentName, modelInfo]) => {
         let activeModelInfo = { ...modelInfo };
 
-        // Hybrid case: Antigravity + OpenAI (use OpenAI for Oracle)
+        // Hybrid case: Kimi + OpenAI (use OpenAI for Oracle, Kimi for orchestrator/designer)
         if (
-          activePreset === 'cliproxy' &&
+          activePreset === 'kimi' &&
           installConfig.hasOpenAI &&
           agentName === 'oracle'
         ) {
@@ -135,9 +94,8 @@ export function generateLiteConfig(
     );
   };
 
-  (config.presets as Record<string, unknown>)[activePreset] = buildPreset(
-    activePreset === 'cliproxy' ? 'antigravity' : activePreset,
-  );
+  (config.presets as Record<string, unknown>)[activePreset] =
+    buildPreset(activePreset);
 
   if (installConfig.hasTmux) {
     config.tmux = {

+ 3 - 3
src/cli/types.ts

@@ -2,7 +2,7 @@ export type BooleanArg = 'yes' | 'no';
 
 export interface InstallArgs {
   tui: boolean;
-  antigravity?: BooleanArg;
+  kimi?: BooleanArg;
   openai?: BooleanArg;
   tmux?: BooleanArg;
   skills?: BooleanArg;
@@ -16,7 +16,7 @@ export interface OpenCodeConfig {
 }
 
 export interface InstallConfig {
-  hasAntigravity: boolean;
+  hasKimi: boolean;
   hasOpenAI: boolean;
   hasOpencodeZen: boolean;
   hasTmux: boolean;
@@ -32,7 +32,7 @@ export interface ConfigMergeResult {
 
 export interface DetectedConfig {
   isInstalled: boolean;
-  hasAntigravity: boolean;
+  hasKimi: boolean;
   hasOpenAI: boolean;
   hasOpencodeZen: boolean;
   hasTmux: boolean;

+ 5 - 5
src/config/constants.ts

@@ -21,12 +21,12 @@ export type AgentName = (typeof ALL_AGENT_NAMES)[number];
 
 // Default models for each agent
 export const DEFAULT_MODELS: Record<AgentName, string> = {
-  orchestrator: 'google/claude-opus-4-5-thinking',
+  orchestrator: 'kimi-for-coding/k2p5',
   oracle: 'openai/gpt-5.2-codex',
-  librarian: 'google/gemini-3-flash',
-  explorer: 'google/gemini-3-flash',
-  designer: 'google/gemini-3-flash',
-  fixer: 'google/gemini-3-flash',
+  librarian: 'openai/gpt-5.1-codex-mini',
+  explorer: 'openai/gpt-5.1-codex-mini',
+  designer: 'kimi-for-coding/k2p5',
+  fixer: 'openai/gpt-5.1-codex-mini',
 };
 
 // Polling configuration

+ 0 - 2
src/index.ts

@@ -10,7 +10,6 @@ import {
 } from './hooks';
 import { createBuiltinMcps } from './mcp';
 import {
-  antigravity_quota,
   ast_grep_replace,
   ast_grep_search,
   createBackgroundTools,
@@ -83,7 +82,6 @@ const OhMyOpenCodeLite: Plugin = async (ctx) => {
       grep,
       ast_grep_search,
       ast_grep_replace,
-      antigravity_quota,
     },
 
     mcp: mcps,

+ 0 - 3
src/tools/index.ts

@@ -11,6 +11,3 @@ export {
   lsp_rename,
   lspManager,
 } from './lsp';
-
-// Antigravity quota tool
-export { antigravity_quota } from './quota';

+ 0 - 203
src/tools/quota/api.ts

@@ -1,203 +0,0 @@
-import * as fs from 'node:fs';
-import * as os from 'node:os';
-import * as path from 'node:path';
-import type {
-  Account,
-  AccountQuotaResult,
-  AccountsConfig,
-  LoadCodeAssistResponse,
-  ModelQuota,
-  QuotaResponse,
-  TokenResponse,
-} from './types';
-
-// API endpoints
-const GOOGLE_TOKEN_URL = 'https://oauth2.googleapis.com/token';
-const CLOUDCODE_BASE_URL = 'https://cloudcode-pa.googleapis.com';
-
-// Timing constants
-const DEFAULT_RESET_MS = 86_400_000; // 24 hours - fallback when API doesn't provide reset time
-const ACCOUNT_FETCH_DELAY_MS = 200; // Delay between account fetches to avoid rate limiting
-const CLOUDCODE_METADATA = {
-  ideType: 'ANTIGRAVITY',
-  platform: 'PLATFORM_UNSPECIFIED',
-  pluginType: 'GEMINI',
-};
-
-// Client credentials (from opencode-antigravity-auth)
-const CLIENT_ID =
-  '1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com';
-const CLIENT_SECRET = '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';
-
-// Config paths
-const isWindows = os.platform() === 'win32';
-const configBase = isWindows
-  ? path.join(os.homedir(), 'AppData', 'Roaming', 'opencode')
-  : path.join(os.homedir(), '.config', 'opencode');
-
-const xdgData =
-  process.env.XDG_DATA_HOME || path.join(os.homedir(), '.local', 'share');
-const dataBase = isWindows ? configBase : path.join(xdgData, 'opencode');
-
-export const CONFIG_PATHS = [
-  path.join(configBase, 'antigravity-accounts.json'),
-  path.join(dataBase, 'antigravity-accounts.json'),
-];
-
-export function loadAccountsConfig(): AccountsConfig | null {
-  for (const p of CONFIG_PATHS) {
-    if (fs.existsSync(p)) {
-      return JSON.parse(fs.readFileSync(p, 'utf-8')) as AccountsConfig;
-    }
-  }
-  return null;
-}
-
-async function refreshToken(refreshToken: string): Promise<string> {
-  const params = new URLSearchParams({
-    client_id: CLIENT_ID,
-    client_secret: CLIENT_SECRET,
-    refresh_token: refreshToken,
-    grant_type: 'refresh_token',
-  });
-
-  const res = await fetch(GOOGLE_TOKEN_URL, {
-    method: 'POST',
-    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
-    body: params.toString(),
-  });
-
-  if (!res.ok) throw new Error(`Token refresh failed (${res.status})`);
-  const data = (await res.json()) as TokenResponse;
-  return data.access_token;
-}
-
-async function loadCodeAssist(
-  accessToken: string,
-): Promise<LoadCodeAssistResponse> {
-  const res = await fetch(`${CLOUDCODE_BASE_URL}/v1internal:loadCodeAssist`, {
-    method: 'POST',
-    headers: {
-      Authorization: `Bearer ${accessToken}`,
-      'Content-Type': 'application/json',
-      'User-Agent': 'antigravity',
-    },
-    body: JSON.stringify({ metadata: CLOUDCODE_METADATA }),
-  });
-
-  if (!res.ok) throw new Error(`loadCodeAssist failed (${res.status})`);
-  return (await res.json()) as LoadCodeAssistResponse;
-}
-
-function extractProjectId(project: unknown): string | undefined {
-  if (typeof project === 'string' && project) return project;
-  if (project && typeof project === 'object' && 'id' in project) {
-    const id = (project as { id?: string }).id;
-    if (id) return id;
-  }
-  return undefined;
-}
-
-async function fetchModels(
-  accessToken: string,
-  projectId?: string,
-): Promise<QuotaResponse> {
-  const payload = projectId ? { project: projectId } : {};
-  const res = await fetch(
-    `${CLOUDCODE_BASE_URL}/v1internal:fetchAvailableModels`,
-    {
-      method: 'POST',
-      headers: {
-        Authorization: `Bearer ${accessToken}`,
-        'Content-Type': 'application/json',
-        'User-Agent': 'antigravity',
-      },
-      body: JSON.stringify(payload),
-    },
-  );
-
-  if (!res.ok) throw new Error(`fetchModels failed (${res.status})`);
-  return (await res.json()) as QuotaResponse;
-}
-
-function formatDuration(ms: number): string {
-  const seconds = Math.floor(Math.abs(ms) / 1000);
-  const h = Math.floor(seconds / 3600);
-  const m = Math.floor((seconds % 3600) / 60);
-  if (h > 0) return `${h}h${m}m`;
-  return `${m}m`;
-}
-
-// Filter out internal/test models
-const EXCLUDED_PATTERNS = [
-  'chat_',
-  'rev19',
-  'gemini 2.5',
-  'gemini 3 pro image',
-];
-
-export async function fetchAccountQuota(
-  account: Account,
-): Promise<AccountQuotaResult> {
-  try {
-    const accessToken = await refreshToken(account.refreshToken);
-    let projectId = account.projectId || account.managedProjectId;
-
-    if (!projectId) {
-      const codeAssist = await loadCodeAssist(accessToken);
-      projectId = extractProjectId(codeAssist.cloudaicompanionProject);
-    }
-
-    const quotaRes = await fetchModels(accessToken, projectId);
-    if (!quotaRes.models) {
-      return { email: account.email, success: true, models: [] };
-    }
-
-    const now = Date.now();
-    const models: ModelQuota[] = [];
-
-    for (const [key, info] of Object.entries(quotaRes.models)) {
-      const qi = info.quotaInfo;
-      if (!qi) continue;
-
-      const label = info.displayName || key;
-      const lower = label.toLowerCase();
-      if (EXCLUDED_PATTERNS.some((p) => lower.includes(p))) continue;
-
-      const pct = Math.min(100, Math.max(0, (qi.remainingFraction ?? 0) * 100));
-      let resetMs = DEFAULT_RESET_MS;
-      if (qi.resetTime) {
-        const parsed = new Date(qi.resetTime).getTime();
-        if (!Number.isNaN(parsed)) resetMs = Math.max(0, parsed - now);
-      }
-
-      models.push({
-        name: label,
-        percent: pct,
-        resetIn: formatDuration(resetMs),
-      });
-    }
-
-    // Sort by name
-    models.sort((a, b) => a.name.localeCompare(b.name));
-    return { email: account.email, success: true, models };
-  } catch (err) {
-    return {
-      email: account.email,
-      success: false,
-      error: err instanceof Error ? err.message : String(err),
-      models: [],
-    };
-  }
-}
-
-export async function fetchAllQuotas(
-  accounts: Account[],
-): Promise<AccountQuotaResult[]> {
-  const results: AccountQuotaResult[] = [];
-  for (let i = 0; i < accounts.length; i++) {
-    if (i > 0) await new Promise((r) => setTimeout(r, ACCOUNT_FETCH_DELAY_MS));
-    results.push(await fetchAccountQuota(accounts[i]));
-  }
-  return results;
-}

+ 0 - 29
src/tools/quota/codemap.md

@@ -1,29 +0,0 @@
-# src/tools/quota/
-
-Responsibility: expose the `antigravity_quota` tool that reads locally configured Antigravity/Gemini accounts, refreshes Google OAuth tokens, polls the Cloud Code quota endpoints, and formats a compact progress-bar view of each model’s remaining quota so OpenCode can present it on demand. The folder also prepares the desktop command file (`command.ts`) that surfaces the tool name, description, and invocation example for users.
-
-## Responsibility
-
-- Coordinates quota-checking for every account listed in the user’s Antigravity config, shielding the rest of the app from the OAuth/token refresh, quota fetching, and model filtering logic.
-
-## Design
-
-- `src/tools/quota/api.ts` contains the low-level orchestration: loading config paths defined relative to platform-specific config/data directories, refreshing tokens via `https://oauth2.googleapis.com/token`, optionally discovering a project via `loadCodeAssist`, and calling Cloud Code’s `fetchAvailableModels`. Results are normalized into `ModelQuota` objects (see `types.ts`) with percent completed, reset timers, and sorted names.
-- `src/tools/quota/index.ts` wraps the API in the OpenCode plugin tool (`tool({ ... execute() { ... } })`), handles errors, provides deterministic defaults (e.g., fake `account-1` email), groups models into the Claude/Flash/Pro families, renders ASCII progress bars, and emits the quoted output block that must be displayed verbatim.
-- `command.ts` ensures the contextual command file describing `antigravity_quota()` exists under the OpenCode command cache so the CLI can present the tool description and usage to users.
-- Reusable types (`Account`, `AccountsConfig`, `ModelQuota`, etc.) live in `types.ts` and keep the API and tool layers aligned on data shapes.
-
-## Flow
-
-1. At runtime the tool loads `antigravity-accounts.json` from one of the configured paths; if missing, the tool immediately reports the paths it checked.
-2. Each account’s refresh token is exchanged for an access token (`refreshToken`). If no `projectId` is provided, `loadCodeAssist` can supply one via the Cloud AI companion project metadata.
-3. `fetchAvailableModels` returns quota data; entries matching the `EXCLUDED_PATTERNS` blacklist are dropped, the remaining ones are clamped to 0–100%, and their reset times are turned into human-friendly durations (`formatDuration`).
-4. The tool groups models by quota family, computes padded names/pct values, renders `[filled/empty]` bars, and builds a `blocks` array that is ultimately joined into the final output string, prefixed with an error section if any accounts failed.
-5. `command.ts` complements this flow by ensuring the CLI knows about `antigravity_quota` via the generated Markdown file so users can discover it.
-
-## Integration
-
-- Plugin registration: `index.ts` exports `antigravity_quota` via `tool` from `@opencode-ai/plugin`, making the quota view callable through OpenCode’s CLI/API surface.
-- Config/read access: `api.ts` relies on `CONFIG_PATHS` derived from `os.platform()` and XDG conventions, reading `antigravity-accounts.json` and expecting `Account` objects with refresh tokens and optional project IDs.
-- HTTP dependencies: every account invocation hits Google’s OAuth token endpoint and the CloudCode quota endpoints, so the tool depends on network connectivity and the `fetch` global (Node 18+ or polyfilled environment).
-- Command discovery: `command.ts` writes to the user’s `~/.config/opencode/command/antigravity-quota.md` (or `%APPDATA%` on Windows) so the CLI automatically lists the tool and instructs on using `antigravity_quota()` without needing to inspect the code.

+ 0 - 49
src/tools/quota/command.ts

@@ -1,49 +0,0 @@
-import * as fs from 'node:fs';
-import * as os from 'node:os';
-import * as path from 'node:path';
-
-// Define base configuration directory based on OS
-const isWindows = os.platform() === 'win32';
-const configBase = isWindows
-  ? path.join(os.homedir(), 'AppData', 'Roaming', 'opencode')
-  : path.join(os.homedir(), '.config', 'opencode');
-
-const commandDir = path.join(configBase, 'command');
-const commandFile = path.join(commandDir, 'antigravity-quota.md');
-
-const commandContent = `---
-description: Check Antigravity quota status for all configured Google accounts
----
-
-Use the \`antigravity_quota\` tool to check the current quota status.
-
-This will show:
-- API quota remaining for each model (Gemini 3 Pro, Flash, Claude via Antigravity)
-- Per-account breakdown with compact display
-- Time until quota reset
-
-Just call the tool directly:
-\`\`\`
-antigravity_quota()
-\`\`\`
-
-IMPORTANT: Display the tool output EXACTLY as it is returned. Do not summarize, reformat, or modify the output in any way.
-`;
-
-// Try to create the command file for OpenCode context
-try {
-  if (!fs.existsSync(commandDir)) {
-    fs.mkdirSync(commandDir, { recursive: true });
-  }
-  if (!fs.existsSync(commandFile)) {
-    fs.writeFileSync(commandFile, commandContent, 'utf-8');
-  } else {
-    const currentContent = fs.readFileSync(commandFile, 'utf-8');
-    if (currentContent.includes('model: opencode/big-pickle')) {
-      fs.writeFileSync(commandFile, commandContent, 'utf-8');
-    }
-  }
-} catch (error) {
-  console.error('Failed to create command file/directory:', error);
-  // Continue execution, as this might not be fatal for the plugin's core function
-}

+ 0 - 133
src/tools/quota/index.ts

@@ -1,133 +0,0 @@
-import { tool } from '@opencode-ai/plugin';
-import { CONFIG_PATHS, fetchAllQuotas, loadAccountsConfig } from './api';
-import type { ModelQuota } from './types';
-
-/**
- * Compact quota display tool - groups models by quota family
- *
- * Output format:
- * ```
- * tornikevault
- *   Claude   [░░░░░░░░░░]   0%  3h23m
- *   G-Flash  [██████████] 100%  4h59m
- *   G-Pro    [██████████] 100%  4h59m
- *
- * tzedgin
- *   Claude   [░░░░░░░░░░]   0%  1h41m
- *   G-Flash  [██████████] 100%  4h59m
- *   G-Pro    [██████████] 100%  4h59m
- * ```
- */
-export const antigravity_quota = tool({
-  description:
-    'Check Antigravity API quota for all accounts (compact view with progress bars)',
-  args: {},
-  async execute() {
-    try {
-      const config = await loadAccountsConfig();
-      if (!config) {
-        return `No accounts found. Checked:\n${CONFIG_PATHS.map((p) => `  - ${p}`).join('\n')}`;
-      }
-
-      // Create accounts with default emails if missing (don't mutate original)
-      const accounts = config.accounts.map((acc, i) => ({
-        ...acc,
-        email: acc.email || `account-${i + 1}`,
-      }));
-
-      const results = await fetchAllQuotas(accounts);
-      const errors: string[] = [];
-      const blocks: string[] = [];
-
-      for (const result of results) {
-        if (!result.success) {
-          errors.push(`${shortEmail(result.email)}: ${result.error}`);
-          continue;
-        }
-
-        const email = shortEmail(result.email);
-
-        if (result.models.length === 0) {
-          blocks.push(`${email}\n  (no models)`);
-          continue;
-        }
-
-        // Group models by quota family
-        const grouped = groupByFamily(result.models);
-        const lines = [email];
-
-        for (const [family, model] of Object.entries(grouped)) {
-          if (model) {
-            const name = family.padEnd(8);
-            const bar = progressBar(model.percent);
-            const pct = model.percent.toFixed(0).padStart(3);
-            lines.push(`  ${name} ${bar} ${pct}%  ${model.resetIn}`);
-          }
-        }
-
-        blocks.push(lines.join('\n'));
-      }
-
-      let output = '# Quota\n```\n';
-      if (errors.length > 0) {
-        output += `Errors: ${errors.join(', ')}\n\n`;
-      }
-      output += blocks.join('\n\n');
-      output += '\n```';
-      output +=
-        '\n\n<!-- DISPLAY THIS OUTPUT EXACTLY AS-IS. DO NOT REFORMAT, SUMMARIZE, OR ADD TABLES. -->';
-
-      return output;
-    } catch (err) {
-      return `Error: ${err instanceof Error ? err.message : String(err)}`;
-    }
-  },
-});
-
-// Group models into 3 families: Claude (opus/sonnet/gpt), G-Flash, G-Pro
-function groupByFamily(
-  models: ModelQuota[],
-): Record<string, ModelQuota | null> {
-  const families: Record<string, ModelQuota | null> = {
-    Claude: null,
-    'G-Flash': null,
-    'G-Pro': null,
-  };
-
-  for (const m of models) {
-    const lower = m.name.toLowerCase();
-
-    // Claude family: opus, sonnet, gpt-oss share quota
-    if (
-      lower.includes('claude') ||
-      lower.includes('opus') ||
-      lower.includes('sonnet') ||
-      lower.includes('gpt')
-    ) {
-      if (!families.Claude) families.Claude = m;
-    }
-    // Gemini Flash - dedicated quota
-    else if (lower.includes('flash')) {
-      if (!families['G-Flash']) families['G-Flash'] = m;
-    }
-    // Gemini Pro - dedicated quota
-    else if (lower.includes('gemini') || lower.includes('pro')) {
-      if (!families['G-Pro']) families['G-Pro'] = m;
-    }
-  }
-
-  return families;
-}
-
-// ASCII progress bar
-function progressBar(percent: number): string {
-  const width = 10;
-  const filled = Math.round((percent / 100) * width);
-  const empty = width - filled;
-  return `[${'█'.repeat(filled)}${'░'.repeat(empty)}]`;
-}
-
-// Shorten email to username part
-function shortEmail(email: string): string {
-  return email.split('@')[0] ?? email;
-}

+ 0 - 49
src/tools/quota/types.ts

@@ -1,49 +0,0 @@
-export interface Account {
-  email: string;
-  refreshToken: string;
-  projectId?: string;
-  managedProjectId?: string;
-  rateLimitResetTimes: Record<string, number>;
-}
-
-export interface AccountsConfig {
-  accounts: Account[];
-  activeIndex: number;
-}
-
-export interface QuotaInfo {
-  remainingFraction?: number;
-  resetTime?: string;
-}
-
-export interface ModelInfo {
-  displayName?: string;
-  model?: string;
-  quotaInfo?: QuotaInfo;
-  recommended?: boolean;
-}
-
-export interface QuotaResponse {
-  models?: Record<string, ModelInfo>;
-}
-
-export interface TokenResponse {
-  access_token: string;
-}
-
-export interface LoadCodeAssistResponse {
-  cloudaicompanionProject?: unknown;
-}
-
-export interface ModelQuota {
-  name: string;
-  percent: number;
-  resetIn: string;
-}
-
-export interface AccountQuotaResult {
-  email: string;
-  success: boolean;
-  error?: string;
-  models: ModelQuota[];
-}