oh-my-opencode 2.6.2 → 2.7.1

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.
@@ -54,6 +54,9 @@ export declare const HookNameSchema: z.ZodEnum<{
54
54
  "empty-message-sanitizer": "empty-message-sanitizer";
55
55
  "thinking-block-validator": "thinking-block-validator";
56
56
  }>;
57
+ export declare const BuiltinCommandNameSchema: z.ZodEnum<{
58
+ "init-deep": "init-deep";
59
+ }>;
57
60
  export declare const AgentOverrideConfigSchema: z.ZodObject<{
58
61
  model: z.ZodOptional<z.ZodString>;
59
62
  temperature: z.ZodOptional<z.ZodNumber>;
@@ -635,6 +638,9 @@ export declare const SisyphusAgentConfigSchema: z.ZodObject<{
635
638
  planner_enabled: z.ZodOptional<z.ZodBoolean>;
636
639
  replace_plan: z.ZodOptional<z.ZodBoolean>;
637
640
  }, z.core.$strip>;
641
+ export declare const CommentCheckerConfigSchema: z.ZodObject<{
642
+ custom_prompt: z.ZodOptional<z.ZodString>;
643
+ }, z.core.$strip>;
638
644
  export declare const DynamicContextPruningConfigSchema: z.ZodObject<{
639
645
  enabled: z.ZodDefault<z.ZodBoolean>;
640
646
  notification: z.ZodDefault<z.ZodEnum<{
@@ -693,7 +699,7 @@ export declare const ExperimentalConfigSchema: z.ZodObject<{
693
699
  }, z.core.$strip>>;
694
700
  }, z.core.$strip>>;
695
701
  }, z.core.$strip>>;
696
- dcp_on_compaction_failure: z.ZodOptional<z.ZodBoolean>;
702
+ dcp_for_compaction: z.ZodOptional<z.ZodBoolean>;
697
703
  }, z.core.$strip>;
698
704
  export declare const OhMyOpenCodeConfigSchema: z.ZodObject<{
699
705
  $schema: z.ZodOptional<z.ZodString>;
@@ -735,6 +741,9 @@ export declare const OhMyOpenCodeConfigSchema: z.ZodObject<{
735
741
  "empty-message-sanitizer": "empty-message-sanitizer";
736
742
  "thinking-block-validator": "thinking-block-validator";
737
743
  }>>>;
744
+ disabled_commands: z.ZodOptional<z.ZodArray<z.ZodEnum<{
745
+ "init-deep": "init-deep";
746
+ }>>>;
738
747
  agents: z.ZodOptional<z.ZodObject<{
739
748
  build: z.ZodOptional<z.ZodObject<{
740
749
  model: z.ZodOptional<z.ZodString>;
@@ -1270,6 +1279,9 @@ export declare const OhMyOpenCodeConfigSchema: z.ZodObject<{
1270
1279
  planner_enabled: z.ZodOptional<z.ZodBoolean>;
1271
1280
  replace_plan: z.ZodOptional<z.ZodBoolean>;
1272
1281
  }, z.core.$strip>>;
1282
+ comment_checker: z.ZodOptional<z.ZodObject<{
1283
+ custom_prompt: z.ZodOptional<z.ZodString>;
1284
+ }, z.core.$strip>>;
1273
1285
  experimental: z.ZodOptional<z.ZodObject<{
1274
1286
  aggressive_truncation: z.ZodOptional<z.ZodBoolean>;
1275
1287
  auto_resume: z.ZodOptional<z.ZodBoolean>;
@@ -1302,7 +1314,7 @@ export declare const OhMyOpenCodeConfigSchema: z.ZodObject<{
1302
1314
  }, z.core.$strip>>;
1303
1315
  }, z.core.$strip>>;
1304
1316
  }, z.core.$strip>>;
1305
- dcp_on_compaction_failure: z.ZodOptional<z.ZodBoolean>;
1317
+ dcp_for_compaction: z.ZodOptional<z.ZodBoolean>;
1306
1318
  }, z.core.$strip>>;
1307
1319
  auto_update: z.ZodOptional<z.ZodBoolean>;
1308
1320
  }, z.core.$strip>;
@@ -1311,7 +1323,9 @@ export type AgentOverrideConfig = z.infer<typeof AgentOverrideConfigSchema>;
1311
1323
  export type AgentOverrides = z.infer<typeof AgentOverridesSchema>;
1312
1324
  export type AgentName = z.infer<typeof AgentNameSchema>;
1313
1325
  export type HookName = z.infer<typeof HookNameSchema>;
1326
+ export type BuiltinCommandName = z.infer<typeof BuiltinCommandNameSchema>;
1314
1327
  export type SisyphusAgentConfig = z.infer<typeof SisyphusAgentConfigSchema>;
1328
+ export type CommentCheckerConfig = z.infer<typeof CommentCheckerConfigSchema>;
1315
1329
  export type ExperimentalConfig = z.infer<typeof ExperimentalConfigSchema>;
1316
1330
  export type DynamicContextPruningConfig = z.infer<typeof DynamicContextPruningConfigSchema>;
1317
1331
  export { McpNameSchema, type McpName } from "../mcp/types";
@@ -0,0 +1,2 @@
1
+ import type { BuiltinCommandName, BuiltinCommands } from "./types";
2
+ export declare function loadBuiltinCommands(disabledCommands?: BuiltinCommandName[]): BuiltinCommands;
@@ -0,0 +1,2 @@
1
+ export * from "./types";
2
+ export * from "./commands";
@@ -0,0 +1 @@
1
+ export declare const INIT_DEEP_TEMPLATE = "# Initialize Deep Knowledge Base\n\nGenerate comprehensive AGENTS.md files across project hierarchy. Combines root-level project knowledge (gen-knowledge) with complexity-based subdirectory documentation (gen-knowledge-deep).\n\n## Usage\n\n```\n/init-deep # Analyze and generate hierarchical AGENTS.md\n/init-deep --create-new # Force create from scratch (ignore existing)\n/init-deep --max-depth=2 # Limit to N directory levels (default: 3)\n```\n\n---\n\n## Core Principles\n\n- **Telegraphic Style**: Sacrifice grammar for concision (\"Project uses React\" \u2192 \"React 18\")\n- **Predict-then-Compare**: Predict standard \u2192 find actual \u2192 document ONLY deviations\n- **Hierarchy Aware**: Parent covers general, children cover specific\n- **No Redundancy**: Child AGENTS.md NEVER repeats parent content\n- **LSP-First**: Use LSP tools for accurate code intelligence when available (semantic > text search)\n\n---\n\n## Process\n\n<critical>\n**MANDATORY: TodoWrite for ALL phases. Mark in_progress \u2192 completed in real-time.**\n</critical>\n\n### Phase 0: Initialize\n\n```\nTodoWrite([\n { id: \"p1-analysis\", content: \"Parallel project structure & complexity analysis\", status: \"pending\", priority: \"high\" },\n { id: \"p2-scoring\", content: \"Score directories, determine AGENTS.md locations\", status: \"pending\", priority: \"high\" },\n { id: \"p3-root\", content: \"Generate root AGENTS.md with Predict-then-Compare\", status: \"pending\", priority: \"high\" },\n { id: \"p4-subdirs\", content: \"Generate subdirectory AGENTS.md files in parallel\", status: \"pending\", priority: \"high\" },\n { id: \"p5-review\", content: \"Review, deduplicate, validate all files\", status: \"pending\", priority: \"medium\" }\n])\n```\n\n---\n\n## Phase 1: Parallel Project Analysis\n\n**Mark \"p1-analysis\" as in_progress.**\n\nLaunch **ALL tasks simultaneously**:\n\n<parallel-tasks>\n\n### Structural Analysis (bash - run in parallel)\n```bash\n# Task A: Directory depth analysis\nfind . -type d -not -path '*/\\.*' -not -path '*/node_modules/*' -not -path '*/venv/*' -not -path '*/__pycache__/*' -not -path '*/dist/*' -not -path '*/build/*' | awk -F/ '{print NF-1}' | sort -n | uniq -c\n\n# Task B: File count per directory \nfind . -type f -not -path '*/\\.*' -not -path '*/node_modules/*' -not -path '*/venv/*' -not -path '*/__pycache__/*' | sed 's|/[^/]*$||' | sort | uniq -c | sort -rn | head -30\n\n# Task C: Code concentration\nfind . -type f \\( -name \"*.py\" -o -name \"*.ts\" -o -name \"*.tsx\" -o -name \"*.js\" -o -name \"*.jsx\" -o -name \"*.go\" -o -name \"*.rs\" -o -name \"*.java\" \\) -not -path '*/node_modules/*' -not -path '*/venv/*' | sed 's|/[^/]*$||' | sort | uniq -c | sort -rn | head -20\n\n# Task D: Existing knowledge files\nfind . -type f \\( -name \"AGENTS.md\" -o -name \"CLAUDE.md\" \\) -not -path '*/node_modules/*' 2>/dev/null\n```\n\n### Context Gathering (Explore agents - background_task in parallel)\n\n```\nbackground_task(agent=\"explore\", prompt=\"Project structure: PREDICT standard {lang} patterns \u2192 FIND package.json/pyproject.toml/go.mod \u2192 REPORT deviations only\")\n\nbackground_task(agent=\"explore\", prompt=\"Entry points: PREDICT typical (main.py, index.ts) \u2192 FIND actual \u2192 REPORT non-standard organization\")\n\nbackground_task(agent=\"explore\", prompt=\"Conventions: FIND .cursor/rules, .cursorrules, eslintrc, pyproject.toml \u2192 REPORT project-specific rules DIFFERENT from defaults\")\n\nbackground_task(agent=\"explore\", prompt=\"Anti-patterns: FIND comments with 'DO NOT', 'NEVER', 'ALWAYS', 'LEGACY', 'DEPRECATED' \u2192 REPORT forbidden patterns\")\n\nbackground_task(agent=\"explore\", prompt=\"Build/CI: FIND .github/workflows, Makefile, justfile \u2192 REPORT non-standard build/deploy patterns\")\n\nbackground_task(agent=\"explore\", prompt=\"Test patterns: FIND pytest.ini, jest.config, test structure \u2192 REPORT unique testing conventions\")\n```\n\n### Code Intelligence Analysis (LSP tools - run in parallel)\n\nLSP provides semantic understanding beyond text search. Use for accurate code mapping.\n\n```\n# Step 1: Check LSP availability\nlsp_servers() # Verify language server is available\n\n# Step 2: Analyze entry point files (run in parallel)\n# Find entry points first, then analyze each with lsp_document_symbols\nlsp_document_symbols(filePath=\"src/index.ts\") # Main entry\nlsp_document_symbols(filePath=\"src/main.py\") # Python entry\nlsp_document_symbols(filePath=\"cmd/main.go\") # Go entry\n\n# Step 3: Discover key symbols across workspace (run in parallel)\nlsp_workspace_symbols(filePath=\".\", query=\"class\") # All classes\nlsp_workspace_symbols(filePath=\".\", query=\"interface\") # All interfaces\nlsp_workspace_symbols(filePath=\".\", query=\"function\") # Top-level functions\nlsp_workspace_symbols(filePath=\".\", query=\"type\") # Type definitions\n\n# Step 4: Analyze symbol centrality (for top 5-10 key symbols)\n# High reference count = central/important concept\nlsp_find_references(filePath=\"src/index.ts\", line=X, character=Y) # Main export\n```\n\n#### LSP Analysis Output Format\n\n```\nCODE_INTELLIGENCE = {\n entry_points: [\n { file: \"src/index.ts\", exports: [\"Plugin\", \"createHook\"], symbol_count: 12 }\n ],\n key_symbols: [\n { name: \"Plugin\", type: \"class\", file: \"src/index.ts\", refs: 45, role: \"Central orchestrator\" },\n { name: \"createHook\", type: \"function\", file: \"src/utils.ts\", refs: 23, role: \"Hook factory\" }\n ],\n module_boundaries: [\n { dir: \"src/hooks\", exports: 21, imports_from: [\"shared/\"] },\n { dir: \"src/tools\", exports: 15, imports_from: [\"shared/\", \"hooks/\"] }\n ]\n}\n```\n\n<critical>\n**LSP Fallback**: If LSP unavailable (no server installed), skip this section and rely on explore agents + AST-grep patterns.\n</critical>\n\n</parallel-tasks>\n\n**Collect all results. Mark \"p1-analysis\" as completed.**\n\n---\n\n## Phase 2: Complexity Scoring & Location Decision\n\n**Mark \"p2-scoring\" as in_progress.**\n\n### Scoring Matrix\n\n| Factor | Weight | Threshold | Source |\n|--------|--------|-----------|--------|\n| File count | 3x | >20 files = high | bash |\n| Subdirectory count | 2x | >5 subdirs = high | bash |\n| Code file ratio | 2x | >70% code = high | bash |\n| Unique patterns | 1x | Has own config | explore |\n| Module boundary | 2x | Has __init__.py/index.ts | bash |\n| **Symbol density** | 2x | >30 symbols = high | LSP |\n| **Export count** | 2x | >10 exports = high | LSP |\n| **Reference centrality** | 3x | Symbols with >20 refs | LSP |\n\n<lsp-scoring>\n**LSP-Enhanced Scoring** (if available):\n\n```\nFor each directory in candidates:\n symbols = lsp_document_symbols(dir/index.ts or dir/__init__.py)\n \n symbol_score = len(symbols) > 30 ? 6 : len(symbols) > 15 ? 3 : 0\n export_score = count(exported symbols) > 10 ? 4 : 0\n \n # Check if this module is central (many things depend on it)\n for each exported symbol:\n refs = lsp_find_references(symbol)\n if refs > 20: centrality_score += 3\n \n total_score += symbol_score + export_score + centrality_score\n```\n</lsp-scoring>\n\n### Decision Rules\n\n| Score | Action |\n|-------|--------|\n| **Root (.)** | ALWAYS create AGENTS.md |\n| **High (>15)** | Create dedicated AGENTS.md |\n| **Medium (8-15)** | Create if distinct domain |\n| **Low (<8)** | Skip, parent sufficient |\n\n### Output Format\n\n```\nAGENTS_LOCATIONS = [\n { path: \".\", type: \"root\" },\n { path: \"src/api\", score: 18, reason: \"high complexity, 45 files\" },\n { path: \"src/hooks\", score: 12, reason: \"distinct domain, unique patterns\" },\n]\n```\n\n**Mark \"p2-scoring\" as completed.**\n\n---\n\n## Phase 3: Generate Root AGENTS.md\n\n**Mark \"p3-root\" as in_progress.**\n\nRoot AGENTS.md gets **full treatment** with Predict-then-Compare synthesis.\n\n### Required Sections\n\n```markdown\n# PROJECT KNOWLEDGE BASE\n\n**Generated:** {TIMESTAMP}\n**Commit:** {SHORT_SHA}\n**Branch:** {BRANCH}\n\n## OVERVIEW\n\n{1-2 sentences: what project does, core tech stack}\n\n## STRUCTURE\n\n\\`\\`\\`\n{project-root}/\n\u251C\u2500\u2500 {dir}/ # {non-obvious purpose only}\n\u2514\u2500\u2500 {entry} # entry point\n\\`\\`\\`\n\n## WHERE TO LOOK\n\n| Task | Location | Notes |\n|------|----------|-------|\n| Add feature X | \\`src/x/\\` | {pattern hint} |\n\n## CODE MAP\n\n{Generated from LSP analysis - shows key symbols and their relationships}\n\n| Symbol | Type | Location | Refs | Role |\n|--------|------|----------|------|------|\n| {MainClass} | Class | \\`src/index.ts\\` | {N} | {Central orchestrator} |\n| {createX} | Function | \\`src/utils.ts\\` | {N} | {Factory pattern} |\n| {Config} | Interface | \\`src/types.ts\\` | {N} | {Configuration contract} |\n\n### Module Dependencies\n\n\\`\\`\\`\n{entry} \u2500\u2500imports\u2500\u2500> {core/}\n \u2502 \u2502\n \u2514\u2500\u2500imports\u2500\u2500> {utils/} <\u2500\u2500imports\u2500\u2500 {features/}\n\\`\\`\\`\n\n<code-map-note>\n**Skip CODE MAP if**: LSP unavailable OR project too small (<10 files) OR no clear module boundaries.\n</code-map-note>\n\n## CONVENTIONS\n\n{ONLY deviations from standard - skip generic advice}\n\n- **{rule}**: {specific detail}\n\n## ANTI-PATTERNS (THIS PROJECT)\n\n{Things explicitly forbidden HERE}\n\n- **{pattern}**: {why} \u2192 {alternative}\n\n## UNIQUE STYLES\n\n{Project-specific coding styles}\n\n- **{style}**: {how different}\n\n## COMMANDS\n\n\\`\\`\\`bash\n{dev-command}\n{test-command}\n{build-command}\n\\`\\`\\`\n\n## NOTES\n\n{Gotchas, non-obvious info}\n```\n\n### Quality Gates\n\n- [ ] Size: 50-150 lines\n- [ ] No generic advice (\"write clean code\")\n- [ ] No obvious info (\"tests/ has tests\")\n- [ ] Every item is project-specific\n\n**Mark \"p3-root\" as completed.**\n\n---\n\n## Phase 4: Generate Subdirectory AGENTS.md\n\n**Mark \"p4-subdirs\" as in_progress.**\n\nFor each location in AGENTS_LOCATIONS (except root), launch **parallel document-writer agents**:\n\n```typescript\nfor (const loc of AGENTS_LOCATIONS.filter(l => l.path !== \".\")) {\n background_task({\n agent: \"document-writer\",\n prompt: \\`\n Generate AGENTS.md for: ${loc.path}\n \n CONTEXT:\n - Complexity reason: ${loc.reason}\n - Parent AGENTS.md: ./AGENTS.md (already covers project overview)\n \n CRITICAL RULES:\n 1. Focus ONLY on this directory's specific context\n 2. NEVER repeat parent AGENTS.md content\n 3. Shorter is better - 30-80 lines max\n 4. Telegraphic style - sacrifice grammar\n \n REQUIRED SECTIONS:\n - OVERVIEW (1 line: what this directory does)\n - STRUCTURE (only if >5 subdirs)\n - WHERE TO LOOK (directory-specific tasks)\n - CONVENTIONS (only if DIFFERENT from root)\n - ANTI-PATTERNS (directory-specific only)\n \n OUTPUT: Write to ${loc.path}/AGENTS.md\n \\`\n })\n}\n```\n\n**Wait for all agents. Mark \"p4-subdirs\" as completed.**\n\n---\n\n## Phase 5: Review & Deduplicate\n\n**Mark \"p5-review\" as in_progress.**\n\n### Validation Checklist\n\nFor EACH generated AGENTS.md:\n\n| Check | Action if Fail |\n|-------|----------------|\n| Contains generic advice | REMOVE the line |\n| Repeats parent content | REMOVE the line |\n| Missing required section | ADD it |\n| Over 150 lines (root) / 80 lines (subdir) | TRIM |\n| Verbose explanations | REWRITE telegraphic |\n\n### Cross-Reference Validation\n\n```\nFor each child AGENTS.md:\n For each line in child:\n If similar line exists in parent:\n REMOVE from child (parent already covers)\n```\n\n**Mark \"p5-review\" as completed.**\n\n---\n\n## Final Report\n\n```\n=== init-deep Complete ===\n\nFiles Generated:\n \u2713 ./AGENTS.md (root, {N} lines)\n \u2713 ./src/hooks/AGENTS.md ({N} lines)\n \u2713 ./src/tools/AGENTS.md ({N} lines)\n\nDirectories Analyzed: {N}\nAGENTS.md Created: {N}\nTotal Lines: {N}\n\nHierarchy:\n ./AGENTS.md\n \u251C\u2500\u2500 src/hooks/AGENTS.md\n \u2514\u2500\u2500 src/tools/AGENTS.md\n```\n\n---\n\n## Anti-Patterns for THIS Command\n\n- **Over-documenting**: Not every directory needs AGENTS.md\n- **Redundancy**: Child must NOT repeat parent\n- **Generic content**: Remove anything that applies to ALL projects\n- **Sequential execution**: MUST use parallel agents\n- **Deep nesting**: Rarely need AGENTS.md at depth 4+\n- **Verbose style**: \"This directory contains...\" \u2192 just list it\n- **Ignoring LSP**: If LSP available, USE IT - semantic analysis > text grep\n- **LSP without fallback**: Always have explore agent backup if LSP unavailable\n- **Over-referencing**: Don't trace refs for EVERY symbol - focus on exports only";
@@ -0,0 +1,6 @@
1
+ import type { CommandDefinition } from "../claude-code-command-loader";
2
+ export type BuiltinCommandName = "init-deep";
3
+ export interface BuiltinCommandConfig {
4
+ disabled_commands?: BuiltinCommandName[];
5
+ }
6
+ export type BuiltinCommands = Record<string, CommandDefinition>;
@@ -290,10 +290,66 @@ function startCallbackServer(timeoutMs = 5 * 60 * 1000) {
290
290
  };
291
291
  }
292
292
  // src/auth/antigravity/token.ts
293
+ class AntigravityTokenRefreshError extends Error {
294
+ code;
295
+ description;
296
+ status;
297
+ statusText;
298
+ responseBody;
299
+ constructor(options) {
300
+ super(options.message);
301
+ this.name = "AntigravityTokenRefreshError";
302
+ this.code = options.code;
303
+ this.description = options.description;
304
+ this.status = options.status;
305
+ this.statusText = options.statusText;
306
+ this.responseBody = options.responseBody;
307
+ }
308
+ get isInvalidGrant() {
309
+ return this.code === "invalid_grant";
310
+ }
311
+ get isNetworkError() {
312
+ return this.status === 0;
313
+ }
314
+ }
315
+ function parseOAuthErrorPayload(text) {
316
+ if (!text) {
317
+ return {};
318
+ }
319
+ try {
320
+ const payload = JSON.parse(text);
321
+ let code;
322
+ if (typeof payload.error === "string") {
323
+ code = payload.error;
324
+ } else if (payload.error && typeof payload.error === "object") {
325
+ code = payload.error.status ?? payload.error.code;
326
+ }
327
+ return {
328
+ code,
329
+ description: payload.error_description
330
+ };
331
+ } catch {
332
+ return { description: text };
333
+ }
334
+ }
293
335
  function isTokenExpired(tokens) {
294
336
  const expirationTime = tokens.timestamp + tokens.expires_in * 1000;
295
337
  return Date.now() >= expirationTime - ANTIGRAVITY_TOKEN_REFRESH_BUFFER_MS;
296
338
  }
339
+ var MAX_REFRESH_RETRIES = 3;
340
+ var INITIAL_RETRY_DELAY_MS = 1000;
341
+ function calculateRetryDelay(attempt) {
342
+ return Math.min(INITIAL_RETRY_DELAY_MS * Math.pow(2, attempt), 1e4);
343
+ }
344
+ function isRetryableError(status) {
345
+ if (status === 0)
346
+ return true;
347
+ if (status === 429)
348
+ return true;
349
+ if (status >= 500 && status < 600)
350
+ return true;
351
+ return false;
352
+ }
297
353
  async function refreshAccessToken(refreshToken, clientId = ANTIGRAVITY_CLIENT_ID, clientSecret = ANTIGRAVITY_CLIENT_SECRET) {
298
354
  const params = new URLSearchParams({
299
355
  grant_type: "refresh_token",
@@ -301,24 +357,67 @@ async function refreshAccessToken(refreshToken, clientId = ANTIGRAVITY_CLIENT_ID
301
357
  client_id: clientId,
302
358
  client_secret: clientSecret
303
359
  });
304
- const response = await fetch(GOOGLE_TOKEN_URL, {
305
- method: "POST",
306
- headers: {
307
- "Content-Type": "application/x-www-form-urlencoded"
308
- },
309
- body: params
310
- });
311
- if (!response.ok) {
312
- const errorText = await response.text().catch(() => "Unknown error");
313
- throw new Error(`Token refresh failed: ${response.status} ${response.statusText} - ${errorText}`);
360
+ let lastError;
361
+ for (let attempt = 0;attempt <= MAX_REFRESH_RETRIES; attempt++) {
362
+ try {
363
+ const response = await fetch(GOOGLE_TOKEN_URL, {
364
+ method: "POST",
365
+ headers: {
366
+ "Content-Type": "application/x-www-form-urlencoded"
367
+ },
368
+ body: params
369
+ });
370
+ if (response.ok) {
371
+ const data = await response.json();
372
+ return {
373
+ access_token: data.access_token,
374
+ refresh_token: data.refresh_token || refreshToken,
375
+ expires_in: data.expires_in,
376
+ token_type: data.token_type
377
+ };
378
+ }
379
+ const responseBody = await response.text().catch(() => {
380
+ return;
381
+ });
382
+ const parsed = parseOAuthErrorPayload(responseBody);
383
+ lastError = new AntigravityTokenRefreshError({
384
+ message: parsed.description || `Token refresh failed: ${response.status} ${response.statusText}`,
385
+ code: parsed.code,
386
+ description: parsed.description,
387
+ status: response.status,
388
+ statusText: response.statusText,
389
+ responseBody
390
+ });
391
+ if (parsed.code === "invalid_grant") {
392
+ throw lastError;
393
+ }
394
+ if (!isRetryableError(response.status)) {
395
+ throw lastError;
396
+ }
397
+ if (attempt < MAX_REFRESH_RETRIES) {
398
+ const delay = calculateRetryDelay(attempt);
399
+ await new Promise((resolve) => setTimeout(resolve, delay));
400
+ }
401
+ } catch (error) {
402
+ if (error instanceof AntigravityTokenRefreshError) {
403
+ throw error;
404
+ }
405
+ lastError = new AntigravityTokenRefreshError({
406
+ message: error instanceof Error ? error.message : "Network error during token refresh",
407
+ status: 0,
408
+ statusText: "Network Error"
409
+ });
410
+ if (attempt < MAX_REFRESH_RETRIES) {
411
+ const delay = calculateRetryDelay(attempt);
412
+ await new Promise((resolve) => setTimeout(resolve, delay));
413
+ }
414
+ }
314
415
  }
315
- const data = await response.json();
316
- return {
317
- access_token: data.access_token,
318
- refresh_token: data.refresh_token || refreshToken,
319
- expires_in: data.expires_in,
320
- token_type: data.token_type
321
- };
416
+ throw lastError || new AntigravityTokenRefreshError({
417
+ message: "Token refresh failed after all retries",
418
+ status: 0,
419
+ statusText: "Max Retries Exceeded"
420
+ });
322
421
  }
323
422
  function parseStoredToken(stored) {
324
423
  const parts = stored.split("|");
@@ -543,6 +642,10 @@ function clearProjectContextCache(accessToken) {
543
642
  projectContextCache.clear();
544
643
  }
545
644
  }
645
+ function invalidateProjectContextByRefreshToken(_refreshToken) {
646
+ projectContextCache.clear();
647
+ debugLog(`[invalidateProjectContextByRefreshToken] Cleared all project context cache due to refresh token invalidation`);
648
+ }
546
649
  // src/auth/antigravity/request.ts
547
650
  function buildRequestHeaders(accessToken) {
548
651
  return {
@@ -1257,7 +1360,7 @@ function debugLog4(message) {
1257
1360
  console.log(`[antigravity-fetch] ${message}`);
1258
1361
  }
1259
1362
  }
1260
- function isRetryableError(status) {
1363
+ function isRetryableError2(status) {
1261
1364
  if (status === 0)
1262
1365
  return true;
1263
1366
  if (status === 429)
@@ -1275,11 +1378,11 @@ var GCP_PERMISSION_ERROR_PATTERNS = [
1275
1378
  function isGcpPermissionError(text) {
1276
1379
  return GCP_PERMISSION_ERROR_PATTERNS.some((pattern) => text.includes(pattern));
1277
1380
  }
1278
- function calculateRetryDelay(attempt) {
1381
+ function calculateRetryDelay2(attempt) {
1279
1382
  return Math.min(200 * Math.pow(2, attempt), 2000);
1280
1383
  }
1281
1384
  async function isRetryableResponse(response) {
1282
- if (isRetryableError(response.status))
1385
+ if (isRetryableError2(response.status))
1283
1386
  return true;
1284
1387
  if (response.status === 403) {
1285
1388
  try {
@@ -1358,7 +1461,7 @@ async function attemptFetch(options) {
1358
1461
  const text = await response.clone().text();
1359
1462
  if (isGcpPermissionError(text)) {
1360
1463
  if (attempt < maxPermissionRetries) {
1361
- const delay = calculateRetryDelay(attempt);
1464
+ const delay = calculateRetryDelay2(attempt);
1362
1465
  debugLog4(`[RETRY] GCP permission error, retry ${attempt + 1}/${maxPermissionRetries} after ${delay}ms`);
1363
1466
  await new Promise((resolve) => setTimeout(resolve, delay));
1364
1467
  continue;
@@ -1479,6 +1582,14 @@ function createAntigravityFetch(getAuth, client, providerId, clientId, clientSec
1479
1582
  });
1480
1583
  debugLog4("Token refreshed successfully");
1481
1584
  } catch (error) {
1585
+ if (error instanceof AntigravityTokenRefreshError) {
1586
+ if (error.isInvalidGrant) {
1587
+ debugLog4(`[REFRESH] Token revoked (invalid_grant), clearing caches`);
1588
+ invalidateProjectContextByRefreshToken(refreshParts.refreshToken);
1589
+ clearProjectContextCache();
1590
+ }
1591
+ throw new Error(`Antigravity: Token refresh failed: ${error.description || error.message}${error.code ? ` (${error.code})` : ""}`);
1592
+ }
1482
1593
  throw new Error(`Antigravity: Token refresh failed: ${error instanceof Error ? error.message : "Unknown error"}`);
1483
1594
  }
1484
1595
  }
@@ -1560,10 +1671,29 @@ function createAntigravityFetch(getAuth, client, providerId, clientId, clientSec
1560
1671
  debugLog4("[401] Token refreshed, retrying request...");
1561
1672
  return executeWithEndpoints();
1562
1673
  } catch (refreshError) {
1674
+ if (refreshError instanceof AntigravityTokenRefreshError) {
1675
+ if (refreshError.isInvalidGrant) {
1676
+ debugLog4(`[401] Token revoked (invalid_grant), clearing caches`);
1677
+ invalidateProjectContextByRefreshToken(refreshParts.refreshToken);
1678
+ clearProjectContextCache();
1679
+ }
1680
+ debugLog4(`[401] Token refresh failed: ${refreshError.description || refreshError.message}`);
1681
+ return new Response(JSON.stringify({
1682
+ error: {
1683
+ message: refreshError.description || refreshError.message,
1684
+ type: refreshError.isInvalidGrant ? "token_revoked" : "unauthorized",
1685
+ code: refreshError.code || "token_refresh_failed"
1686
+ }
1687
+ }), {
1688
+ status: 401,
1689
+ statusText: "Unauthorized",
1690
+ headers: { "Content-Type": "application/json" }
1691
+ });
1692
+ }
1563
1693
  debugLog4(`[401] Token refresh failed: ${refreshError instanceof Error ? refreshError.message : "Unknown error"}`);
1564
1694
  return new Response(JSON.stringify({
1565
1695
  error: {
1566
- message: `Token refresh failed: ${refreshError instanceof Error ? refreshError.message : "Unknown error"}`,
1696
+ message: refreshError instanceof Error ? refreshError.message : "Unknown error",
1567
1697
  type: "unauthorized",
1568
1698
  code: "token_refresh_failed"
1569
1699
  }
@@ -40,8 +40,9 @@ export interface CheckResult {
40
40
  * Run comment-checker CLI with given input.
41
41
  * @param input Hook input to check
42
42
  * @param cliPath Optional explicit path to CLI binary
43
+ * @param customPrompt Optional custom prompt to replace default warning message
43
44
  */
44
- export declare function runCommentChecker(input: HookInput, cliPath?: string): Promise<CheckResult>;
45
+ export declare function runCommentChecker(input: HookInput, cliPath?: string, customPrompt?: string): Promise<CheckResult>;
45
46
  /**
46
47
  * Check if CLI is available (sync check, no download).
47
48
  */
@@ -1,4 +1,5 @@
1
- export declare function createCommentCheckerHooks(): {
1
+ import type { CommentCheckerConfig } from "../../config/schema";
2
+ export declare function createCommentCheckerHooks(config?: CommentCheckerConfig): {
2
3
  "tool.execute.before": (input: {
3
4
  tool: string;
4
5
  sessionID: string;
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { Plugin } from "@opencode-ai/plugin";
2
2
  declare const OhMyOpenCodePlugin: Plugin;
3
3
  export default OhMyOpenCodePlugin;
4
- export type { OhMyOpenCodeConfig, AgentName, AgentOverrideConfig, AgentOverrides, McpName, HookName, } from "./config";
4
+ export type { OhMyOpenCodeConfig, AgentName, AgentOverrideConfig, AgentOverrides, McpName, HookName, BuiltinCommandName, } from "./config";
5
5
  export type { ConfigLoadError } from "./shared/config-errors";