@octocodeai/octocode-engine 16.5.0 → 16.5.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.
package/README.md CHANGED
@@ -122,7 +122,7 @@ Benchmark files:
122
122
 
123
123
  ## Tools
124
124
 
125
- Octocode ships **13 research tools**; the same implementations run identically over [MCP](#mcp) and the [CLI](#cli). `ghCloneRepo` is opt-in (`ENABLE_CLONE=true`); local tools require `ENABLE_LOCAL` (default: on). All flags: [Configuration Reference](https://github.com/bgauryy/octocode/blob/main/docs/mcp/CONFIGURATION.md).
125
+ Octocode ships **13 research tools**; the same implementations run identically over [MCP](#mcp) and the [CLI](#cli). `ghCloneRepo` is opt-in for MCP (`ENABLE_CLONE=true`) and enabled by default for CLI; local tools require `ENABLE_LOCAL` (CLI default: on, MCP default: off). All flags: [Configuration Reference](https://github.com/bgauryy/octocode/blob/main/docs/mcp/CONFIGURATION.md).
126
126
 
127
127
  **Token knobs.** `concise:true` returns path/title-only lists. `minify` controls file read density: `symbols` = skeleton with line numbers, `standard` = comments/blanks stripped (default), `none` = exact bytes.
128
128
 
@@ -247,8 +247,8 @@ Local paths route to local tools; `owner/repo[/path]` targets route to GitHub to
247
247
  | `octocode repo <keywords...>` | Discover GitHub repositories |
248
248
  | `octocode pkg <package\|keywords>` | Search npm and hand off to source repositories |
249
249
  | `octocode binary <file>` | Inspect archives, compressed files, and native binaries |
250
- | `octocode unzip <archive>` | Unpack an archive to `<octocode-home>/unzip/<name>-<timestamp>/`, then use local `ls`, `grep`, `cat`, and `lsp` |
251
- | `octocode clone <owner/repo[/path][@branch]>` | Clone a repo or subtree to the Octocode home repo cache for local/LSP analysis (`ENABLE_CLONE=true`) |
250
+ | `octocode unzip <archive>` | Unpack an archive to `<octocode-home>/tmp/unzip/<name>-<timestamp>/`, then use local `ls`, `grep`, `cat`, and `lsp` |
251
+ | `octocode clone <owner/repo[/path][@branch]>` | Clone a repo or subtree to `<octocode-home>/tmp/clone/` for local/LSP analysis (`ENABLE_CLONE=true`) |
252
252
  | `octocode tools` | List tools, read schemas, or run any MCP tool directly from the terminal |
253
253
  | `octocode context` | Print agent-facing protocol, system prompt, tool descriptions, and schemas |
254
254
  | `octocode install` | Configure Octocode in MCP clients |
@@ -272,7 +272,7 @@ environment variables > <octocode-home>/.octocoderc > built-in defaults
272
272
  2. **Global config**: `<octocode-home>/.octocoderc`, machine-wide defaults read by **both the CLI and the MCP server**.
273
273
  3. **Built-in defaults**: used when neither is set.
274
274
 
275
- **Octocode home** (`<octocode-home>`) holds the global config, encrypted credentials, sessions, stats, logs, and the clone cache. Its location is fixed per platform (there is no override):
275
+ **Octocode home** (`<octocode-home>`) holds the global config, encrypted credentials, sessions, stats, logs, and tmp materialization caches. Its location is fixed per platform (there is no override):
276
276
 
277
277
  | Platform | Location |
278
278
  |----------|----------|
@@ -308,7 +308,7 @@ The **Scope** column shows where a setting applies: `Both`, or `MCP` (the CLI ig
308
308
  |---------|-------------------|---------|-------|--------------|
309
309
  | `OCTOCODE_TOKEN` / `GH_TOKEN` / `GITHUB_TOKEN` | env only | unset | Both | GitHub token, in priority order. Tokens stay in env, never in `.octocoderc`. |
310
310
  | `GITHUB_API_URL` | `github.apiUrl` | `https://api.github.com` | Both | API endpoint; use `/api/v3` for GitHub Enterprise. |
311
- | `ENABLE_LOCAL` | `local.enabled` | `true` | **MCP** | Turns local filesystem + LSP tools on/off for the MCP server. The CLI always has local tools enabled and ignores this. |
311
+ | `ENABLE_LOCAL` | `local.enabled` | CLI `true`, MCP `false` | Both | Turns local filesystem + LSP tools on/off. |
312
312
  | `ENABLE_CLONE` | `local.enableClone` | CLI `true`, MCP `false` | Both | `ghCloneRepo` and directory fetch. Default differs by surface; set `false` to disable in either. |
313
313
  | `WORKSPACE_ROOT` | `local.workspaceRoot` | `cwd` | Both | Absolute root for resolving relative local paths. |
314
314
  | `ALLOWED_PATHS` | `local.allowedPaths` | `[]` | Both | Extra path allowlist for local access; empty means home directory only after validation. |
@@ -317,7 +317,7 @@ The **Scope** column shows where a setting applies: `Both`, or `MCP` (the CLI ig
317
317
  | `MAX_RETRIES` | `network.maxRetries` | `3` | Both | Retry attempts (clamped `0..10`). |
318
318
  | `OCTOCODE_OUTPUT_FORMAT` | `output.format` | `yaml` | Both | Response format: `yaml` or `json`. |
319
319
 
320
- > **Local and clone defaults differ by surface.** The **CLI** is a local terminal, so local tools are always enabled (`ENABLE_LOCAL` is ignored) and clone is enabled by default. The **MCP server** honors `ENABLE_LOCAL` (default on) and defaults clone to off, so a deployment can control what an assistant may touch. An explicit `ENABLE_CLONE=false` (env or `.octocoderc`) disables clone in either surface.
320
+ > **Local and clone defaults differ by surface.** The **CLI** honors `ENABLE_LOCAL` and defaults local tools on; clone is enabled by default. The **MCP server** honors `ENABLE_LOCAL` and defaults local tools and clone off, so a deployment can control what an assistant may touch. Explicit env or `.octocoderc` values override those defaults.
321
321
 
322
322
  ### Example `.octocoderc`
323
323
 
@@ -329,7 +329,7 @@ Drop this at `<octocode-home>/.octocoderc` for machine-wide defaults shared by t
329
329
  "github": { "apiUrl": "https://api.github.com" },
330
330
 
331
331
  "local": {
332
- "enabled": true, // MCP only; the CLI always has local tools on
332
+ "enabled": true, // overrides the surface default for CLI and MCP
333
333
  "enableClone": false, // false disables ghCloneRepo for CLI and MCP
334
334
  "workspaceRoot": "~/code", // base for relative local paths (absolute or ~)
335
335
  "allowedPaths": [] // extra dirs the local tools may read
@@ -425,12 +425,12 @@ Four code-intelligence axes; three are native to the Rust engine and need no ext
425
425
 
426
426
  | Axis | What it does | How to use it |
427
427
  |------|--------------|---------------|
428
- | **Structural AST** | Tree-sitter shape queries (`pattern` or YAML `rule`) across 19 grammars. | `localSearchCode mode:"structural"` · CLI `grep --pattern`/`--rule` |
428
+ | **Structural AST** | Tree-sitter shape queries (`pattern` or YAML `rule`) across 33 grammars. | `localSearchCode mode:"structural"` · CLI `grep --pattern`/`--rule` |
429
429
  | **Signature outline** | Body-free skeleton with line numbers from real tree-sitter parsing, no heuristics. An anti-growth guard returns the real file when a skeleton wouldn't be smaller. | `minify:"symbols"` · CLI `cat --mode symbols` |
430
430
  | **Content minification** | Comment/whitespace stripping for 70+ languages and config formats; HTML/Vue/Svelte also minify embedded `<style>`/`<script>`. | `minify:"standard"` (default) |
431
431
  | **LSP navigation** | definition, references, callers/callees, callHierarchy, hover, typeDefinition, implementation, documentSymbols, via an installed language server; JS/TS also have a native, no-server path. | `lspGetSemantics` · CLI `lsp` / `ls --symbols` |
432
432
 
433
- 📋 **Full support matrix:** every extension with its exact AST, signature, LSP, and minify capability, machine-generated from the shipped binary, lives in **[`benchmark/SUPPORT.md`](https://github.com/bgauryy/octocode/blob/main/packages/octocode-benchmark/benchmark/SUPPORT.md)** (143 extensions: 38 AST, 25 signature, 33 LSP, 105 minify-only). Regenerate or verify with `yarn workspace @octocodeai/octocode-benchmark matrix:check`.
433
+ 📋 **Full support matrix:** every extension with its exact AST, signature, LSP, and minify capability, machine-generated from the shipped binary, lives in **[`benchmark/SUPPORT.md`](https://github.com/bgauryy/octocode/blob/main/packages/octocode-benchmark/benchmark/SUPPORT.md)** (150 extensions: 61 AST, 47 signature, 56 LSP, 89 minify-only). Regenerate or verify with `yarn workspace @octocodeai/octocode-benchmark matrix:check`.
434
434
 
435
435
  ---
436
436
 
@@ -544,15 +544,3 @@ Tune Pi's behavior with an `APPEND_SYSTEM.md` (a compact starter lives at [`docs
544
544
  ### The Manifest
545
545
 
546
546
  **"Code is Truth, but Context is the Map."** Read the [Manifest of Octocode for Research Driven Development](https://github.com/bgauryy/octocode/blob/main/MANIFEST.md) to understand the philosophy behind Octocode.
547
-
548
- ---
549
-
550
- ### Contributing
551
-
552
- See the [Development Guide](https://github.com/bgauryy/octocode/blob/main/docs/DEVELOPMENT_GUIDE.md) for monorepo setup, testing, and contribution guidelines.
553
-
554
- ---
555
-
556
- <div align="center">
557
- <sub>Built for the AI engineering community.</sub>
558
- </div>
@@ -1,7 +1,7 @@
1
1
  export { LSPClient } from './client.js';
2
2
  export { detectLanguageId, getLanguageServerForFile } from './config.js';
3
3
  export { acquirePooledClient, getLspStatus, isLanguageServerAvailable, LSP_UNAVAILABLE_HINT, pooledClientCount, releaseAllPooledClients, releasePooledClientForFile, type LspStatusInput, type LspStatusResult, } from './manager.js';
4
- export { SymbolResolver } from './resolver.js';
4
+ export { resolveImportAliasDefinitions, SymbolResolver, type ImportAliasDefinitionInput, } from './resolver.js';
5
5
  export { safeReadFile, validateLSPServerPath } from './validation.js';
6
6
  export { resolveWorkspaceRootForFile } from './workspaceRoot.js';
7
7
  export type { CallHierarchyItem, CodeSnippet, ExactPosition, FuzzyPosition, IncomingCall, InitializationOptions, LanguageServerCommand, LanguageServerConfig, LSPPaginationInfo, LSPRange, OutgoingCall, ReferenceLocation, ReferencesByFile, SymbolKind, UserLanguageServerConfig, } from './types.js';
package/dist/lsp/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export { LSPClient } from './client.js';
2
2
  export { detectLanguageId, getLanguageServerForFile } from './config.js';
3
3
  export { acquirePooledClient, getLspStatus, isLanguageServerAvailable, LSP_UNAVAILABLE_HINT, pooledClientCount, releaseAllPooledClients, releasePooledClientForFile, } from './manager.js';
4
- export { SymbolResolver } from './resolver.js';
4
+ export { resolveImportAliasDefinitions, SymbolResolver, } from './resolver.js';
5
5
  export { safeReadFile, validateLSPServerPath } from './validation.js';
6
6
  export { resolveWorkspaceRootForFile } from './workspaceRoot.js';
@@ -1,4 +1,4 @@
1
- import type { ExactPosition, FuzzyPosition } from './types.js';
1
+ import type { CodeSnippet, ExactPosition, FuzzyPosition } from './types.js';
2
2
  export declare class SymbolResolutionError extends Error {
3
3
  readonly symbolName: string;
4
4
  readonly lineHint: number;
@@ -15,6 +15,11 @@ interface ResolvedSymbol {
15
15
  lineOffset: number;
16
16
  lineContent: string;
17
17
  }
18
+ export interface ImportAliasDefinitionInput {
19
+ anchorUri: string;
20
+ symbolName: string;
21
+ locations: CodeSnippet[];
22
+ }
18
23
  export declare function resolveSymbolPosition(filePath: string, symbolName: string, lineHint?: number, orderHint?: number): Promise<ResolvedSymbol>;
19
24
  export declare function resolveSymbolPosition(content: string, fuzzy: FuzzyPosition): ResolvedSymbol;
20
25
  export declare class SymbolResolver {
@@ -23,4 +28,5 @@ export declare class SymbolResolver {
23
28
  resolvePosition(filePath: string, fuzzy: FuzzyPosition): Promise<ResolvedSymbol>;
24
29
  resolvePositionFromContent(content: string, fuzzy: FuzzyPosition): ResolvedSymbol;
25
30
  }
31
+ export declare function resolveImportAliasDefinitions({ anchorUri, symbolName, locations, }: ImportAliasDefinitionInput): Promise<CodeSnippet[]>;
26
32
  export {};
@@ -1,3 +1,5 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import path from 'node:path';
1
3
  import { nativeBinding } from './native.js';
2
4
  export class SymbolResolutionError extends Error {
3
5
  symbolName;
@@ -71,3 +73,118 @@ export class SymbolResolver {
71
73
  }
72
74
  }
73
75
  }
76
+ export async function resolveImportAliasDefinitions({ anchorUri, symbolName, locations, }) {
77
+ const resolved = await Promise.all(locations.map(location => resolveImportAliasDefinition(anchorUri, symbolName, location)));
78
+ return resolved;
79
+ }
80
+ async function resolveImportAliasDefinition(anchorUri, symbolName, location) {
81
+ const locationPath = snippetPath(location.uri, anchorUri);
82
+ if (!isSamePath(locationPath, anchorUri))
83
+ return location;
84
+ if (!isImportSnippet(location.content))
85
+ return location;
86
+ const importTarget = importTargetForSymbol(location.content, symbolName);
87
+ if (!importTarget?.moduleSpecifier.startsWith('.'))
88
+ return location;
89
+ const targetPath = await resolveLocalModulePath(locationPath, importTarget.moduleSpecifier);
90
+ if (!targetPath)
91
+ return location;
92
+ const content = await readFile(targetPath, 'utf-8');
93
+ const declaration = findExportedDeclaration(content, importTarget.exportedName);
94
+ if (!declaration)
95
+ return location;
96
+ return {
97
+ uri: targetPath,
98
+ range: {
99
+ start: { line: declaration.line - 1, character: declaration.character },
100
+ end: { line: declaration.line - 1, character: declaration.character },
101
+ },
102
+ displayRange: { startLine: declaration.line, endLine: declaration.line },
103
+ content: declaration.content,
104
+ };
105
+ }
106
+ function isImportSnippet(content) {
107
+ return /^\s*import\s/.test(content.trim());
108
+ }
109
+ function importTargetForSymbol(importLine, symbolName) {
110
+ const namedImport = importLine.match(/import\s*\{([^}]+)\}\s*from\s*['"]([^'"]+)['"]/);
111
+ const namedImports = namedImport?.[1];
112
+ const namedModulePath = namedImport?.[2];
113
+ if (namedImports && namedModulePath) {
114
+ for (const part of namedImports.split(',')) {
115
+ const [original, alias] = part
116
+ .trim()
117
+ .split(/\s+as\s+/)
118
+ .map(value => value.trim());
119
+ if (alias === symbolName || original === symbolName) {
120
+ return {
121
+ moduleSpecifier: namedModulePath,
122
+ exportedName: original,
123
+ };
124
+ }
125
+ }
126
+ }
127
+ const defaultImport = importLine.match(/import\s+([A-Za-z_$][\w$]*)\s+from\s*['"]([^'"]+)['"]/);
128
+ const defaultName = defaultImport?.[1];
129
+ const defaultModulePath = defaultImport?.[2];
130
+ if (defaultName === symbolName && defaultModulePath) {
131
+ return { moduleSpecifier: defaultModulePath, exportedName: symbolName };
132
+ }
133
+ return undefined;
134
+ }
135
+ async function resolveLocalModulePath(importerPath, moduleSpecifier) {
136
+ const basePath = path.resolve(path.dirname(filePathFromUri(importerPath)), moduleSpecifier);
137
+ const extension = path.extname(basePath);
138
+ const sourcePath = extension ? basePath.slice(0, -extension.length) : basePath;
139
+ const candidates = [
140
+ ...(extension === '.js' || extension === '.jsx'
141
+ ? [`${sourcePath}.ts`, `${sourcePath}.tsx`]
142
+ : []),
143
+ basePath,
144
+ `${basePath}.ts`,
145
+ `${basePath}.tsx`,
146
+ `${basePath}.js`,
147
+ `${basePath}.jsx`,
148
+ path.join(basePath, 'index.ts'),
149
+ path.join(basePath, 'index.tsx'),
150
+ path.join(basePath, 'index.js'),
151
+ path.join(basePath, 'index.jsx'),
152
+ ];
153
+ for (const candidate of candidates) {
154
+ try {
155
+ await readFile(candidate, 'utf-8');
156
+ return candidate;
157
+ }
158
+ catch {
159
+ // Try the next TypeScript/JavaScript resolution candidate.
160
+ }
161
+ }
162
+ return undefined;
163
+ }
164
+ function findExportedDeclaration(content, symbolName) {
165
+ const escaped = symbolName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
166
+ const declarationPattern = new RegExp(`^\\s*export\\s+(?:default\\s+)?(?:async\\s+)?(?:function|class|interface|type|const|let|var|enum)\\s+${escaped}\\b`);
167
+ const lines = content.split(/\r?\n/);
168
+ for (const [index, line] of lines.entries()) {
169
+ if (!declarationPattern.test(line))
170
+ continue;
171
+ return {
172
+ line: index + 1,
173
+ character: Math.max(0, line.indexOf(symbolName)),
174
+ content: line.trim(),
175
+ };
176
+ }
177
+ return undefined;
178
+ }
179
+ function isSamePath(left, right) {
180
+ return (path.resolve(filePathFromUri(left)) === path.resolve(filePathFromUri(right)));
181
+ }
182
+ function snippetPath(uri, anchorUri) {
183
+ const filePath = filePathFromUri(uri);
184
+ if (path.isAbsolute(filePath))
185
+ return filePath;
186
+ return path.resolve(path.dirname(filePathFromUri(anchorUri)), filePath);
187
+ }
188
+ function filePathFromUri(uri) {
189
+ return uri.startsWith('file://') ? new URL(uri).pathname : uri;
190
+ }
@@ -0,0 +1,11 @@
1
+ export declare const DISCOVERY_IGNORED_FOLDER_NAMES: string[];
2
+ export declare const DISCOVERY_IGNORED_FILE_NAMES: string[];
3
+ export declare const DISCOVERY_IGNORED_FILE_EXTENSIONS: string[];
4
+ export interface DiscoveryExtensionOptions {
5
+ lowercase?: boolean;
6
+ fallback?: string;
7
+ leadingDot?: boolean;
8
+ }
9
+ export declare function shouldIgnoreDiscoveryDir(folderName: string): boolean;
10
+ export declare function shouldIgnoreDiscoveryFile(filePath: string): boolean;
11
+ export declare function getDiscoveryExtension(filePath: string, options?: DiscoveryExtensionOptions): string;
@@ -0,0 +1,264 @@
1
+ export const DISCOVERY_IGNORED_FOLDER_NAMES = [
2
+ '.github',
3
+ '.git',
4
+ '.vscode',
5
+ '.devcontainer',
6
+ '.config',
7
+ '.cargo',
8
+ '.changeset',
9
+ '.husky',
10
+ '.aspect',
11
+ '.eslint-plugin-local',
12
+ '.yarn',
13
+ '.gemini',
14
+ '.ng-dev',
15
+ '.configurations',
16
+ '.tx',
17
+ 'dist',
18
+ 'build',
19
+ 'out',
20
+ 'output',
21
+ 'target',
22
+ 'release',
23
+ 'node_modules',
24
+ 'vendor',
25
+ 'third_party',
26
+ 'tmp',
27
+ 'temp',
28
+ 'cache',
29
+ '.cache',
30
+ '.tmp',
31
+ '.pytest_cache',
32
+ '.tox',
33
+ '.venv',
34
+ '.mypy_cache',
35
+ '.next',
36
+ '.svelte-kit',
37
+ '.turbo',
38
+ '.angular',
39
+ '.dart_tool',
40
+ '__pycache__',
41
+ '.ruff_cache',
42
+ '.nox',
43
+ 'htmlcov',
44
+ 'cover',
45
+ '.gradle',
46
+ '.m2',
47
+ '.sbt',
48
+ '.bloop',
49
+ '.metals',
50
+ '.bsp',
51
+ 'bin',
52
+ 'obj',
53
+ 'TestResults',
54
+ 'BenchmarkDotNet.Artifacts',
55
+ '.vendor-new',
56
+ 'Godeps',
57
+ 'composer.phar',
58
+ '.phpunit.result.cache',
59
+ '.bundle',
60
+ '.byebug_history',
61
+ '.rspec_status',
62
+ '.mvn',
63
+ '.aws',
64
+ '.gcp',
65
+ 'fastlane',
66
+ 'DerivedData',
67
+ 'xcuserdata',
68
+ 'local.properties',
69
+ '.navigation',
70
+ 'captures',
71
+ '.externalNativeBuild',
72
+ '.cxx',
73
+ '.idea',
74
+ '.idea_modules',
75
+ '.vs',
76
+ '.history',
77
+ 'coverage',
78
+ '.nyc_output',
79
+ 'logs',
80
+ 'log',
81
+ '.DS_Store',
82
+ ];
83
+ export const DISCOVERY_IGNORED_FILE_NAMES = [
84
+ 'package-lock.json',
85
+ '.secrets',
86
+ '.secret',
87
+ 'secrets.json',
88
+ 'secrets.yaml',
89
+ 'secrets.yml',
90
+ 'credentials.json',
91
+ 'credentials.yaml',
92
+ 'credentials.yml',
93
+ 'auth.json',
94
+ 'auth.yaml',
95
+ 'auth.yml',
96
+ 'api-keys.json',
97
+ 'api_keys.json',
98
+ 'service-account.json',
99
+ 'service_account.json',
100
+ 'private-key.pem',
101
+ 'private_key.pem',
102
+ 'id_rsa',
103
+ 'id_dsa',
104
+ 'id_ecdsa',
105
+ 'id_ed25519',
106
+ 'keyfile',
107
+ 'keyfile.json',
108
+ 'gcloud-service-key.json',
109
+ 'firebase-adminsdk.json',
110
+ 'google-services.json',
111
+ 'GoogleService-Info.plist',
112
+ '.DS_Store',
113
+ 'Thumbs.db',
114
+ 'db.sqlite3',
115
+ 'db.sqlite3-journal',
116
+ '.eslintcache',
117
+ '.stylelintcache',
118
+ '.node_repl_history',
119
+ '.yarn-integrity',
120
+ 'celerybeat-schedule',
121
+ 'celerybeat.pid',
122
+ 'ThirdPartyNoticeText.txt',
123
+ 'ThirdPartyNotices.txt',
124
+ 'cglicenses.json',
125
+ 'cgmanifest.json',
126
+ ];
127
+ export const DISCOVERY_IGNORED_FILE_EXTENSIONS = [
128
+ '.lock',
129
+ '.log',
130
+ '.tmp',
131
+ '.temp',
132
+ '.cache',
133
+ '.bak',
134
+ '.backup',
135
+ '.orig',
136
+ '.swp',
137
+ '.swo',
138
+ '.rej',
139
+ '.pid',
140
+ '.seed',
141
+ '.old',
142
+ '.save',
143
+ '.temporary',
144
+ '.exe',
145
+ '.dll',
146
+ '.so',
147
+ '.dylib',
148
+ '.a',
149
+ '.lib',
150
+ '.o',
151
+ '.obj',
152
+ '.bin',
153
+ '.class',
154
+ '.pdb',
155
+ '.dSYM',
156
+ '.pyc',
157
+ '.pyo',
158
+ '.pyd',
159
+ '.jar',
160
+ '.war',
161
+ '.ear',
162
+ '.nar',
163
+ '.db',
164
+ '.sqlite',
165
+ '.sqlite3',
166
+ '.mdb',
167
+ '.accdb',
168
+ '.zip',
169
+ '.tar',
170
+ '.gz',
171
+ '.bz2',
172
+ '.xz',
173
+ '.lz',
174
+ '.lzma',
175
+ '.Z',
176
+ '.tgz',
177
+ '.rar',
178
+ '.7z',
179
+ '.deb',
180
+ '.rpm',
181
+ '.pkg',
182
+ '.dmg',
183
+ '.msi',
184
+ '.appx',
185
+ '.snap',
186
+ '.map',
187
+ '.d.ts.map',
188
+ '.min.js',
189
+ '.min.css',
190
+ '.key',
191
+ '.pem',
192
+ '.p12',
193
+ '.pfx',
194
+ '.crt',
195
+ '.cer',
196
+ '.der',
197
+ '.csr',
198
+ '.jks',
199
+ '.keystore',
200
+ '.truststore',
201
+ '.kate-swp',
202
+ '.gnome-desktop',
203
+ '.sublime-project',
204
+ '.sublime-workspace',
205
+ '.iml',
206
+ '.iws',
207
+ '.ipr',
208
+ '.patch',
209
+ '.diff',
210
+ '.prof',
211
+ '.profile',
212
+ '.trace',
213
+ '.perf',
214
+ '.coverage',
215
+ '.egg-info',
216
+ '.egg',
217
+ '.mo',
218
+ '.pot',
219
+ '.setup',
220
+ '.paket.template',
221
+ ];
222
+ export function shouldIgnoreDiscoveryDir(folderName) {
223
+ const normalized = folderName.replace(/\\/g, '/');
224
+ const name = normalized.split('/').filter(Boolean).pop() ?? normalized;
225
+ return DISCOVERY_IGNORED_FOLDER_NAMES.includes(name);
226
+ }
227
+ export function shouldIgnoreDiscoveryFile(filePath) {
228
+ const normalizedPath = filePath.replace(/\\/g, '/');
229
+ const fileName = normalizedPath.split('/').pop() || '';
230
+ for (const ext of DISCOVERY_IGNORED_FILE_EXTENSIONS) {
231
+ if (fileName.endsWith(ext)) {
232
+ return true;
233
+ }
234
+ }
235
+ if (DISCOVERY_IGNORED_FILE_NAMES.includes(fileName)) {
236
+ return true;
237
+ }
238
+ const pathParts = normalizedPath.split('/');
239
+ for (const part of pathParts) {
240
+ if (DISCOVERY_IGNORED_FOLDER_NAMES.includes(part)) {
241
+ return true;
242
+ }
243
+ }
244
+ return false;
245
+ }
246
+ export function getDiscoveryExtension(filePath, options) {
247
+ const basename = filePath.split(/[\\/]/).pop() ?? filePath;
248
+ const fallback = options?.fallback ?? '';
249
+ let ext = fallback;
250
+ if (basename.startsWith('.')) {
251
+ const dotfileExt = basename.slice(1);
252
+ ext = dotfileExt.includes('.')
253
+ ? (basename.split('.').pop() ?? fallback)
254
+ : dotfileExt;
255
+ }
256
+ else {
257
+ const lastDot = basename.lastIndexOf('.');
258
+ ext = lastDot === -1 ? fallback : basename.slice(lastDot + 1);
259
+ }
260
+ const normalized = options?.lowercase ? ext.toLowerCase() : ext;
261
+ if (!normalized || !options?.leadingDot)
262
+ return normalized;
263
+ return normalized.startsWith('.') ? normalized : `.${normalized}`;
264
+ }
@@ -7,6 +7,8 @@ export { withSecurityValidation, withBasicSecurityValidation, configureSecurity,
7
7
  export type { SecurityDepsConfig } from './withSecurityValidation.js';
8
8
  export { extractResearchFields, extractRepoOwnerFromParams, } from './paramExtractors.js';
9
9
  export { shouldIgnore, shouldIgnorePath, shouldIgnoreFile, } from './ignoredPathFilter.js';
10
+ export { DISCOVERY_IGNORED_FILE_EXTENSIONS, DISCOVERY_IGNORED_FILE_NAMES, DISCOVERY_IGNORED_FOLDER_NAMES, getDiscoveryExtension, shouldIgnoreDiscoveryDir, shouldIgnoreDiscoveryFile, } from './discoveryFilter.js';
11
+ export type { DiscoveryExtensionOptions } from './discoveryFilter.js';
10
12
  export { redactPath } from './pathUtils.js';
11
13
  export { ALLOWED_COMMANDS, DANGEROUS_PATTERNS, PATTERN_DANGEROUS_PATTERNS, } from './securityConstants.js';
12
14
  export { IGNORED_PATH_PATTERNS } from './pathPatterns.js';
@@ -5,6 +5,7 @@ export { validateCommand } from './commandValidator.js';
5
5
  export { withSecurityValidation, withBasicSecurityValidation, configureSecurity, } from './withSecurityValidation.js';
6
6
  export { extractResearchFields, extractRepoOwnerFromParams, } from './paramExtractors.js';
7
7
  export { shouldIgnore, shouldIgnorePath, shouldIgnoreFile, } from './ignoredPathFilter.js';
8
+ export { DISCOVERY_IGNORED_FILE_EXTENSIONS, DISCOVERY_IGNORED_FILE_NAMES, DISCOVERY_IGNORED_FOLDER_NAMES, getDiscoveryExtension, shouldIgnoreDiscoveryDir, shouldIgnoreDiscoveryFile, } from './discoveryFilter.js';
8
9
  export { redactPath } from './pathUtils.js';
9
10
  export { ALLOWED_COMMANDS, DANGEROUS_PATTERNS, PATTERN_DANGEROUS_PATTERNS, } from './securityConstants.js';
10
11
  export { IGNORED_PATH_PATTERNS } from './pathPatterns.js';