Compare commits

..

1 Commits

Author SHA1 Message Date
gnanam1990
9906b33fa0 fix(typecheck): make bun run typecheck actionable on main (#473)
Issue #473 reported that `bun run typecheck` fails on main with ~4400
errors due to repo-foundation drift, masking branch-specific
regressions. Per kevincodex1's guidance ("lets narrow the typecheck
scope for now and then we expand step by step") this PR addresses the
foundational root causes and brings the error count down 60% so the
gate is actionable for branch reviews.

Changes:

- tsconfig.json: bump target to ES2023 + add lib ["ES2023", "DOM"]
  so Array.findLast / findLastIndex resolve (kills 41 TS2550 errors).
  Add `noEmit: true` for typecheck-only mode and
  `allowImportingTsExtensions: true` (kills 40 TS5097 errors). Set
  `noImplicitAny: false` because cleaning up TSX-component implicit
  any is explicitly out of scope per the issue.

- src/global.d.ts: ambient declaration for the build-time MACRO
  global injected by scripts/build.ts via Bun's `define` option
  (kills 9 TS2304 'Cannot find name MACRO' errors).

- src/types/{message,utils,tools}.ts: stubs for the highest-impact
  missing modules from the partial source snapshot (~21 importers
  for message alone). Document the snapshot caveat at the top of each
  stub and reference issue #473 so future readers know they're
  placeholders.

- src/entrypoints/sdk/controlTypes.ts and src/constants/querySource.ts:
  similar one-file stubs unblocking 18 + 19 importers respectively.

- src/entrypoints/agentSdkTypes.ts: append `any`-typed aliases for
  ~70 SDK names that callers expect on the public surface but that
  live in stubbed sub-files (PermissionMode, SDKCompactBoundaryMessage,
  HookEvent, ModelUsage, ModelInfo, etc. — exactly the list from
  auriti's bug-report enumeration).

Verified locally on Linux:
- baseline `bunx tsc --noEmit` on stashed main: 4434 errors
- with PR applied:                              1782 errors (60% drop)
- `bun run build`:                              passes (v0.7.0)
- `bun test`:                                   1632 pass; the 4
   remaining failures (StartupScreen, thinking) reproduce on main
   and are unrelated.
- TS2550 (lib): 41 → 0
- TS5097 (.ts imports): 40 → 0
- TS2304 'MACRO': 9 → 0
- TS2307 missing modules: 587 → 325

Remaining errors are localized to specific stubbed modules and can
be addressed in smaller follow-up issues, matching the issue's
"Definition of done" criterion.
2026-04-28 12:44:12 +05:30
13 changed files with 194 additions and 110 deletions

View File

@@ -28,7 +28,6 @@
"@opentelemetry/sdk-trace-base": "2.6.1",
"@opentelemetry/sdk-trace-node": "2.6.1",
"@opentelemetry/semantic-conventions": "1.40.0",
"@vscode/ripgrep": "^1.17.1",
"ajv": "8.18.0",
"auto-bind": "5.0.1",
"axios": "1.15.0",
@@ -462,8 +461,6 @@
"@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="],
"@vscode/ripgrep": ["@vscode/ripgrep@1.17.1", "", { "dependencies": { "https-proxy-agent": "^7.0.2", "proxy-from-env": "^1.1.0", "yauzl": "^2.9.2" } }, "sha512-xTs7DGyAO3IsJYOCTBP8LnTvPiYVKEuyv8s0xyJDBXfs8rhBfqnZPvb6xDT+RnwWzcXqW27xLS/aGrkjX7lNWw=="],
"accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
"agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
@@ -494,8 +491,6 @@
"bowser": ["bowser@2.14.1", "", {}, "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg=="],
"buffer-crc32": ["buffer-crc32@0.2.13", "", {}, "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ=="],
"buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="],
"bun-types": ["bun-types@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="],
@@ -614,8 +609,6 @@
"fast-xml-parser": ["fast-xml-parser@5.5.8", "", { "dependencies": { "fast-xml-builder": "^1.1.4", "path-expression-matcher": "^1.2.0", "strnum": "^2.2.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ=="],
"fd-slicer": ["fd-slicer@1.1.0", "", { "dependencies": { "pend": "~1.2.0" } }, "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g=="],
"fflate": ["fflate@0.8.2", "", {}, "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="],
"figures": ["figures@6.1.0", "", { "dependencies": { "is-unicode-supported": "^2.0.0" } }, "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg=="],
@@ -794,8 +787,6 @@
"path-to-regexp": ["path-to-regexp@8.4.1", "", {}, "sha512-fvU78fIjZ+SBM9YwCknCvKOUKkLVqtWDVctl0s7xIqfmfb38t2TT4ZU2gHm+Z8xGwgW+QWEU3oQSAzIbo89Ggw=="],
"pend": ["pend@1.2.0", "", {}, "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="],
"picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="],
"pkce-challenge": ["pkce-challenge@5.0.1", "", {}, "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ=="],
@@ -810,7 +801,7 @@
"proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="],
"proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="],
"proxy-from-env": ["proxy-from-env@2.1.0", "", {}, "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA=="],
"qrcode": ["qrcode@1.5.4", "", { "dependencies": { "dijkstrajs": "^1.0.1", "pngjs": "^5.0.0", "yargs": "^15.3.1" }, "bin": { "qrcode": "bin/qrcode" } }, "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg=="],
@@ -962,8 +953,6 @@
"yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
"yauzl": ["yauzl@2.10.0", "", { "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" } }, "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g=="],
"yoctocolors": ["yoctocolors@2.1.2", "", {}, "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug=="],
"zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
@@ -1380,8 +1369,6 @@
"@smithy/uuid/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"axios/proxy-from-env": ["proxy-from-env@2.1.0", "", {}, "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA=="],
"cli-highlight/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
"cli-highlight/yargs": ["yargs@16.2.0", "", { "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.0", "y18n": "^5.0.5", "yargs-parser": "^20.2.2" } }, "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw=="],
@@ -1442,8 +1429,6 @@
"@aws-sdk/nested-clients/@smithy/util-base64/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.2", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q=="],
"@mendable/firecrawl-js/axios/proxy-from-env": ["proxy-from-env@2.1.0", "", {}, "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA=="],
"@opentelemetry/exporter-trace-otlp-grpc/@opentelemetry/core/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.28.0", "", {}, "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="],
"@opentelemetry/exporter-trace-otlp-grpc/@opentelemetry/otlp-transformer/@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.57.2", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-uIX52NnTM0iBh84MShlpouI7UKqkZ7MrUszTmaypHBu4r7NofznSnQRfJ+uUeDtQDj6w8eFGg5KBLDAwAPz1+A=="],
@@ -1524,8 +1509,6 @@
"cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
"firecrawl/axios/proxy-from-env": ["proxy-from-env@2.1.0", "", {}, "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA=="],
"form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
"qrcode/yargs/cliui": ["cliui@6.0.0", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^6.2.0" } }, "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ=="],

View File

@@ -74,7 +74,6 @@
"@opentelemetry/sdk-trace-base": "2.6.1",
"@opentelemetry/sdk-trace-node": "2.6.1",
"@opentelemetry/semantic-conventions": "1.40.0",
"@vscode/ripgrep": "^1.17.1",
"ajv": "8.18.0",
"auto-bind": "5.0.1",
"axios": "1.15.0",

View File

@@ -472,11 +472,6 @@ ${exports}
'@aws-sdk/credential-providers',
'@azure/identity',
'google-auth-library',
// @vscode/ripgrep ships a platform-specific binary alongside its
// index.js and resolves the path via __dirname at runtime. Bundling
// would freeze the build host's absolute path into dist/cli.mjs, so we
// keep it external and rely on the npm package being installed.
'@vscode/ripgrep',
],
})

View File

@@ -0,0 +1,7 @@
/**
* Stub — query source enum not included in source snapshot. See
* src/types/message.ts for the same scoping caveat (issue #473).
*/
/* eslint-disable @typescript-eslint/no-explicit-any */
export type QuerySource = any

View File

@@ -442,7 +442,84 @@ export async function connectRemoteControl(
throw new Error('not implemented')
}
// add exit reason types for removing the error within gracefulShutdown file
// add exit reason types for removing the error within gracefulShutdown file
export type ExitReason = {
}
}
// ============================================================================
// Stub re-exports — types not included in source snapshot.
//
// The upstream Anthropic SDK defines these in sub-files (sdk/coreTypes,
// sdk/runtimeTypes, sdk/controlTypes, sdk/toolTypes) that are stubbed
// in this open repo. Until the real definitions are restored, alias the
// names to `any` so callers can resolve their imports and `tsc` becomes
// actionable. See issue #473 for the typecheck-foundation effort.
// ============================================================================
/* eslint-disable @typescript-eslint/no-explicit-any */
export type AnyZodRawShape = any
export type ApiKeySource = any
export type AsyncHookJSONOutput = any
export type ConfigChangeHookInput = any
export type CwdChangedHookInput = any
export type ElicitationHookInput = any
export type ElicitationResultHookInput = any
export type FileChangedHookInput = any
export type ForkSessionOptions = any
export type ForkSessionResult = any
export type GetSessionInfoOptions = any
export type GetSessionMessagesOptions = any
export type HookEvent = any
export type HookInput = any
export type HookJSONOutput = any
export type InferShape<_T> = any
export type InstructionsLoadedHookInput = any
export type InternalOptions = any
export type InternalQuery = any
export type ListSessionsOptions = any
export type McpSdkServerConfigWithInstance = any
export type McpServerConfigForProcessTransport = any
export type McpServerStatus = any
export type ModelInfo = any
export type ModelUsage = any
export type NotificationHookInput = any
export type Options = any
export type PermissionDeniedHookInput = any
export type PermissionMode = any
export type PermissionRequestHookInput = any
export type PermissionResult = any
export type PermissionUpdate = any
export type PostCompactHookInput = any
export type PostToolUseFailureHookInput = any
export type PostToolUseHookInput = any
export type PreCompactHookInput = any
export type PreToolUseHookInput = any
export type Query = any
export type RewindFilesResult = any
export type SDKAssistantMessage = any
export type SDKAssistantMessageError = any
export type SDKCompactBoundaryMessage = any
export type SdkMcpToolDefinition = any
export type SDKPartialAssistantMessage = any
export type SDKPermissionDenial = any
export type SDKRateLimitInfo = any
export type SDKStatus = any
export type SDKStatusMessage = any
export type SDKSystemMessage = any
export type SDKToolProgressMessage = any
export type SDKUserMessageReplay = any
export type SessionEndHookInput = any
export type SessionMessage = any
export type SessionMutationOptions = any
export type SessionStartHookInput = any
export type SetupHookInput = any
export type StopFailureHookInput = any
export type StopHookInput = any
export type SubagentStartHookInput = any
export type SubagentStopHookInput = any
export type SyncHookJSONOutput = any
export type TaskCompletedHookInput = any
export type TaskCreatedHookInput = any
export type TeammateIdleHookInput = any
export type UserPromptSubmitHookInput = any

View File

@@ -0,0 +1,10 @@
/**
* Stub — control protocol types not included in source snapshot. See
* src/types/message.ts for the same scoping caveat (issue #473).
*/
/* eslint-disable @typescript-eslint/no-explicit-any */
export type SDKControlRequest = any
export type SDKControlResponse = any
export type SDKControlPermissionRequest = any
export type StdoutMessage = any

16
src/global.d.ts vendored Normal file
View File

@@ -0,0 +1,16 @@
/**
* Build-time globals replaced by the bundler at build time.
*
* `scripts/build.ts` substitutes these via Bun's `define` option, so at
* runtime the references are inlined as string literals. This declaration
* exists only to make `tsc --noEmit` aware of them — without it, every
* `MACRO.*` access fires TS2304 "Cannot find name 'MACRO'".
*/
declare const MACRO: {
VERSION: string
DISPLAY_VERSION: string
BUILD_TIME: string
ISSUES_EXPLAINER: string
PACKAGE_URL: string
NATIVE_PACKAGE_URL: string | undefined
}

25
src/types/message.ts Normal file
View File

@@ -0,0 +1,25 @@
/**
* Stub — message type definitions not included in source snapshot.
*
* The upstream Anthropic source defines a rich Message discriminated union
* with structured Content blocks, role tags, tool_use payloads, and so on.
* That file is not mirrored to this open snapshot. This stub exists so
* `tsc --noEmit` can resolve `import { Message, ... } from 'src/types/message'`
* across the ~21 callers without fixing every transitive type the call
* sites use.
*
* Once the real definitions are restored upstream-side or reconstructed
* from runtime usage, replace these `any` aliases with proper types and
* delete this comment. See issue #473 for the typecheck-foundation effort.
*/
/* eslint-disable @typescript-eslint/no-explicit-any */
export type Message = any
export type AssistantMessage = any
export type UserMessage = any
export type SystemMessage = any
export type SystemAPIErrorMessage = any
export type AttachmentMessage = any
export type ProgressMessage = any
export type HookResultMessage = any
export type NormalizedUserMessage = any

7
src/types/tools.ts Normal file
View File

@@ -0,0 +1,7 @@
/**
* Stub — tool type definitions not included in source snapshot. See
* src/types/message.ts for the same scoping caveat (issue #473).
*/
/* eslint-disable @typescript-eslint/no-explicit-any */
export type ShellProgress = any

15
src/types/utils.ts Normal file
View File

@@ -0,0 +1,15 @@
/**
* Stub — utility type definitions not included in source snapshot. See
* src/types/message.ts for the same scoping caveat (issue #473).
*/
/* eslint-disable @typescript-eslint/no-explicit-any */
export type DeepImmutable<T> = T extends any[]
? readonly DeepImmutable<T[number]>[]
: T extends object
? { readonly [K in keyof T]: DeepImmutable<T[K]> }
: T
export type Permutations<T extends string, U extends string = T> = T extends T
? T | `${T}${Permutations<Exclude<U, T>>}`
: never

View File

@@ -5,15 +5,16 @@ import { resolveRipgrepConfig, wrapRipgrepUnavailableError } from './ripgrep.js'
const MOCK_BUILTIN_PATH = path.normalize(
process.platform === 'win32'
? `node_modules/@vscode/ripgrep/bin/rg.exe`
: `node_modules/@vscode/ripgrep/bin/rg`,
? `vendor/ripgrep/${process.arch}-win32/rg.exe`
: `vendor/ripgrep/${process.arch}-${process.platform}/rg`,
)
test('falls back to system rg when @vscode/ripgrep cannot be resolved', () => {
test('ripgrepCommand falls back to system rg when builtin binary is missing', () => {
const config = resolveRipgrepConfig({
userWantsSystemRipgrep: false,
bundledMode: false,
builtinCommand: null,
builtinCommand: MOCK_BUILTIN_PATH,
builtinExists: false,
systemExecutablePath: '/usr/bin/rg',
processExecPath: '/fake/bun',
})
@@ -25,11 +26,12 @@ test('falls back to system rg when @vscode/ripgrep cannot be resolved', () => {
})
})
test('uses builtin @vscode/ripgrep path when the package resolves', () => {
test('ripgrepCommand keeps builtin mode when bundled binary exists', () => {
const config = resolveRipgrepConfig({
userWantsSystemRipgrep: false,
bundledMode: false,
builtinCommand: MOCK_BUILTIN_PATH,
builtinExists: true,
systemExecutablePath: '/usr/bin/rg',
processExecPath: '/fake/bun',
})
@@ -41,59 +43,10 @@ test('uses builtin @vscode/ripgrep path when the package resolves', () => {
})
})
test('honors USE_BUILTIN_RIPGREP=0 by selecting system rg even when builtin is available', () => {
const config = resolveRipgrepConfig({
userWantsSystemRipgrep: true,
bundledMode: false,
builtinCommand: MOCK_BUILTIN_PATH,
systemExecutablePath: '/usr/bin/rg',
processExecPath: '/fake/bun',
})
expect(config).toMatchObject({
mode: 'system',
command: 'rg',
args: [],
})
})
test('keeps embedded mode for Bun-compiled standalone executables', () => {
const config = resolveRipgrepConfig({
userWantsSystemRipgrep: false,
bundledMode: true,
builtinCommand: null,
systemExecutablePath: '/usr/bin/rg',
processExecPath: '/opt/openclaude/bin/openclaude',
})
expect(config).toMatchObject({
mode: 'embedded',
command: '/opt/openclaude/bin/openclaude',
args: ['--no-config'],
argv0: 'rg',
})
})
test('falls through to system rg as a last resort even when not on PATH', () => {
const config = resolveRipgrepConfig({
userWantsSystemRipgrep: false,
bundledMode: false,
builtinCommand: null,
systemExecutablePath: 'rg',
processExecPath: '/fake/bun',
})
expect(config).toMatchObject({
mode: 'system',
command: 'rg',
args: [],
})
})
test('wrapRipgrepUnavailableError explains missing packaged fallback', () => {
const error = wrapRipgrepUnavailableError(
{ code: 'ENOENT', message: 'spawn rg ENOENT' },
{ mode: 'builtin', command: 'C:\\fake\\node_modules\\@vscode\\ripgrep\\bin\\rg.exe', args: [] },
{ mode: 'builtin', command: 'C:\\fake\\vendor\\ripgrep\\rg.exe', args: [] },
'win32',
)

View File

@@ -5,6 +5,7 @@ import memoize from 'lodash-es/memoize.js'
import { homedir } from 'os'
import * as path from 'path'
import { logEvent } from 'src/services/analytics/index.js'
import { fileURLToPath } from 'url'
import { isInBundledMode } from './bundledMode.js'
import { logForDebugging } from './debug.js'
import { isEnvDefinedFalsy } from './envUtils.js'
@@ -14,6 +15,13 @@ import { logError } from './log.js'
import { getPlatform } from './platform.js'
import { countCharInString } from './stringUtils.js'
const __filename = fileURLToPath(import.meta.url)
// we use node:path.join instead of node:url.resolve because the former doesn't encode spaces
const __dirname = path.join(
__filename,
process.env.NODE_ENV === 'test' ? '../../../' : '../',
)
type RipgrepConfig = {
mode: 'system' | 'builtin' | 'embedded'
command: string
@@ -27,31 +35,11 @@ function isErrnoException(error: unknown): error is NodeJS.ErrnoException {
return error instanceof Error
}
/**
* Returns the ripgrep binary path provided by the @vscode/ripgrep package.
* The package downloads a platform/arch-specific binary at npm install time
* (cached under the package's bin/ directory). Returns null when the package
* cannot be resolved — for example when running as a Bun-compiled standalone
* executable that doesn't ship node_modules.
*/
function resolveBuiltinRgPath(): string | null {
try {
// Lazy require so the resolution failure path stays graceful at import
// time. The package only exports `rgPath`, so we do not need the rest.
const mod = require('@vscode/ripgrep') as { rgPath?: string }
if (mod.rgPath && existsSync(mod.rgPath)) {
return mod.rgPath
}
} catch {
// Falls through to null — caller decides the fallback.
}
return null
}
type ResolveRipgrepConfigArgs = {
userWantsSystemRipgrep: boolean
bundledMode: boolean
builtinCommand: string | null
builtinCommand: string
builtinExists: boolean
systemExecutablePath: string
processExecPath?: string
}
@@ -60,6 +48,7 @@ export function resolveRipgrepConfig({
userWantsSystemRipgrep,
bundledMode,
builtinCommand,
builtinExists,
systemExecutablePath,
processExecPath = process.execPath,
}: ResolveRipgrepConfigArgs): RipgrepConfig {
@@ -77,7 +66,7 @@ export function resolveRipgrepConfig({
}
}
if (builtinCommand) {
if (builtinExists) {
return { mode: 'builtin', command: builtinCommand, args: [] }
}
@@ -85,9 +74,7 @@ export function resolveRipgrepConfig({
return { mode: 'system', command: 'rg', args: [] }
}
// Last resort — leaves error reporting to the executor when no binary
// can be located. wrapRipgrepUnavailableError() surfaces an install hint.
return { mode: 'system', command: 'rg', args: [] }
return { mode: 'builtin', command: builtinCommand, args: [] }
}
const getRipgrepConfig = memoize((): RipgrepConfig => {
@@ -95,13 +82,19 @@ const getRipgrepConfig = memoize((): RipgrepConfig => {
process.env.USE_BUILTIN_RIPGREP,
)
const bundledMode = isInBundledMode()
const builtinCommand = resolveBuiltinRgPath()
const rgRoot = path.resolve(__dirname, 'vendor', 'ripgrep')
const builtinCommand =
process.platform === 'win32'
? path.resolve(rgRoot, `${process.arch}-win32`, 'rg.exe')
: path.resolve(rgRoot, `${process.arch}-${process.platform}`, 'rg')
const builtinExists = existsSync(builtinCommand)
const { cmd: systemExecutablePath } = findExecutable('rg', [])
return resolveRipgrepConfig({
userWantsSystemRipgrep,
bundledMode,
builtinCommand,
builtinExists,
systemExecutablePath,
})
})

View File

@@ -1,10 +1,14 @@
{
"compilerOptions": {
"target": "ES2022",
"target": "ES2023",
"lib": ["ES2023", "DOM"],
"module": "ESNext",
"moduleResolution": "bundler",
"jsx": "react-jsx",
"strict": true,
"noImplicitAny": false,
"noEmit": true,
"allowImportingTsExtensions": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,