Skip to content

Commit aec92d6

Browse files
Support extension-less files (Dockerfile, Makefile, Jenkinsfile, etc.)
Adds a DEFAULT_EXTENSIONLESS_FILENAMES set of well-known files that have no extension but should be indexed. When the file traversal encounters a file with no extension, it checks this set before skipping. Configurable via CUSTOM_EXTENSIONLESS_FILENAMES env var (comma-separated). Fixes #191 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 3df8e9c commit aec92d6

File tree

1 file changed

+46
-2
lines changed

1 file changed

+46
-2
lines changed

packages/core/src/context.ts

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,23 @@ const DEFAULT_SUPPORTED_EXTENSIONS = [
3636
'.dockerfile', '.tf', '.hcl',
3737
];
3838

39+
/**
40+
* Well-known extension-less filenames that should be indexed.
41+
* Users can extend this via CUSTOM_EXTENSIONLESS_FILENAMES env var (comma-separated).
42+
*/
43+
const DEFAULT_EXTENSIONLESS_FILENAMES = new Set([
44+
'Dockerfile', 'dockerfile',
45+
'Makefile', 'makefile', 'GNUmakefile',
46+
'Jenkinsfile',
47+
'Vagrantfile',
48+
'Gemfile', 'Rakefile', 'Guardfile',
49+
'Procfile',
50+
'Brewfile',
51+
'Caddyfile',
52+
'Fastfile',
53+
'Appfile',
54+
]);
55+
3956
const DEFAULT_IGNORE_PATTERNS = [
4057
// Common build output and dependency directories
4158
'node_modules/**',
@@ -105,6 +122,7 @@ export class Context {
105122
private codeSplitter: Splitter;
106123
private supportedExtensions: string[];
107124
private ignorePatterns: string[];
125+
private extensionlessFilenames: Set<string>;
108126
private synchronizers = new Map<string, FileSynchronizer>();
109127

110128
constructor(config: ContextConfig = {}) {
@@ -148,7 +166,14 @@ export class Context {
148166
// Remove duplicates
149167
this.ignorePatterns = [...new Set(allIgnorePatterns)];
150168

151-
console.log(`[Context] 🔧 Initialized with ${this.supportedExtensions.length} supported extensions and ${this.ignorePatterns.length} ignore patterns`);
169+
// Build extensionless filename set from defaults + env
170+
const envExtensionlessFilenames = this.getCustomExtensionlessFilenamesFromEnv();
171+
this.extensionlessFilenames = new Set([
172+
...DEFAULT_EXTENSIONLESS_FILENAMES,
173+
...envExtensionlessFilenames,
174+
]);
175+
176+
console.log(`[Context] 🔧 Initialized with ${this.supportedExtensions.length} supported extensions, ${this.extensionlessFilenames.size} extensionless filenames, and ${this.ignorePatterns.length} ignore patterns`);
152177
if (envCustomExtensions.length > 0) {
153178
console.log(`[Context] 📎 Loaded ${envCustomExtensions.length} custom extensions from environment: ${envCustomExtensions.join(', ')}`);
154179
}
@@ -680,7 +705,11 @@ export class Context {
680705
await traverseDirectory(fullPath);
681706
} else if (entry.isFile()) {
682707
const ext = path.extname(entry.name);
683-
if (this.supportedExtensions.includes(ext)) {
708+
if (ext === '') {
709+
if (this.extensionlessFilenames.has(entry.name)) {
710+
files.push(fullPath);
711+
}
712+
} else if (this.supportedExtensions.includes(ext)) {
684713
files.push(fullPath);
685714
}
686715
}
@@ -1161,6 +1190,21 @@ export class Context {
11611190
}
11621191
}
11631192

1193+
/**
1194+
* Get custom extensionless filenames from environment variables.
1195+
* Supports CUSTOM_EXTENSIONLESS_FILENAMES as comma-separated list (e.g. "Dockerfile,Makefile").
1196+
*/
1197+
private getCustomExtensionlessFilenamesFromEnv(): string[] {
1198+
const envValue = envManager.get('CUSTOM_EXTENSIONLESS_FILENAMES');
1199+
if (!envValue) return [];
1200+
try {
1201+
return envValue.split(',').map(f => f.trim()).filter(f => f.length > 0);
1202+
} catch (error) {
1203+
console.warn(`[Context] ⚠️ Failed to parse CUSTOM_EXTENSIONLESS_FILENAMES: ${error}`);
1204+
return [];
1205+
}
1206+
}
1207+
11641208
/**
11651209
* Add custom extensions (from MCP or other sources) without replacing existing ones
11661210
* @param customExtensions Array of custom extensions to add

0 commit comments

Comments
 (0)