| status | in-progress | |
|---|---|---|
| worktree | filename-based-language-detection | |
| project | semantic-code-search | |
| tags |
|
|
| created | 2026-05-08 |
- Problem:
Router.getHandler()inpackages/runtime/src/index.ts:56-63only matches files by extension (filePath.endsWith(ext)). Files without extensions —Dockerfile,Makefile,Jenkinsfile, etc. — are silently skipped during indexing. This blocks issues #9 (Makefile) and #10 (Dockerfile). - Scope:
- In-scope: Add optional
filenamesfield toParserHandler.match, update router/validation/tree-sitter-helper, create a Dockerfile plugin as the first consumer, tests for all changes - Out-of-scope: Makefile plugin (#9), Jenkinsfile, Vagrantfile — trivial follow-ups once this infrastructure lands
- In-scope: Add optional
- Constraints:
- No breaking changes —
filenamesis optional, existing plugins are unaffected - Extension matching takes priority over filename matching (existing behavior preserved)
- Validation must allow handlers with only filenames (no extensions), only extensions (existing), or both
- No tree-sitter grammar exists for Dockerfile on npm — use
parseByLineshelper
- No breaking changes —
- Repo touchpoints:
packages/plugin-api/src/types.ts—ParserHandlerinterfacepackages/plugin-api/src/validation.ts—validateHandler()/validateHandlers()packages/plugin-api/src/validation.test.ts— validation testspackages/runtime/src/index.ts—Routerclasspackages/runtime/src/router.test.ts— router testspackages/plugin-helpers-tree-sitter/src/index.ts—TreeSitterHandlerOptions/createTreeSitterHandler()packages/plugin-helpers-tree-sitter/src/index.test.ts— tree-sitter helper testspackages/plugins/lang-dockerfile/— new plugin (package.json, tsconfig, vitest config, src/index.ts, src/index.test.ts)packages/default-plugins/src/index.ts— register Dockerfile pluginpackages/default-plugins/package.json— add dependency
- Definition of done:
Router.getHandler('path/to/Dockerfile')returns the dockerfile handlerRouter.getHandler('path/to/Containerfile')returns the dockerfile handlerRouter.getHandler('path/to/foo.dockerfile')returns the dockerfile handlerRouter.getHandler('path/to/foo.containerfile')returns the dockerfile handler- Existing extension-based routing is unchanged
- Validation rejects duplicate filenames across handlers
- Validation allows handlers with only filenames and no extensions
- All existing tests still pass
- New tests cover filename matching, conflict warnings, validation, and the Dockerfile plugin
-
1) Add
filenamestoParserHandler.matchtype- Change: Add optional
filenames?: string[]to thematchobject in theParserHandlerinterface - Files:
packages/plugin-api/src/types.ts - Acceptance: TypeScript compiles. Existing code unaffected since field is optional.
- Change: Add optional
-
2) Update
Routerto support filename-based matching- Change:
- Add
private filenameOwners = new Map<string, string>()alongsideextensionOwners - In
registerPlugin(), iteratehandler.match.filenames(when present) and populatefilenameOwnerswith conflict warnings (same pattern as extensions) - In
getHandler(), after the extension loop returns null, add a second pass that checkspath.basename(filePath)againsthandler.match.filenames. Importpathfrom'path'.
- Add
- Files:
packages/runtime/src/index.ts - Acceptance:
router.getHandler('/some/path/Dockerfile')returns the correct handler. Extension matching still takes priority.
- Change:
-
3) Add router tests for filename matching
- Change: Add tests to the existing router test file:
- Route files by filename (Dockerfile, Containerfile)
- Return null for unmatched filenames
- Extension match takes priority over filename match
- Warn on duplicate filename registration across handlers
getHandlers()includes filename-only handlers
- Files:
packages/runtime/src/router.test.ts - Acceptance: All new tests pass via
npx vitest runinpackages/runtime/
- Change: Add tests to the existing router test file:
-
4) Update validation to support filenames
- Change:
- Relax the "at least one extension required" check to "at least one extension OR filename required"
- Add filename format validation: each filename must be non-empty, must not contain
/or\ - Add cross-handler duplicate filename detection (same pattern as extension duplicates)
- Files:
packages/plugin-api/src/validation.ts - Acceptance:
validateHandler()accepts handlers with only filenames, rejects empty match (no extensions AND no filenames), detects duplicate filenames across handlers
- Change:
-
5) Add validation tests for filenames
- Change: Add test cases:
- Pass when handler has filenames but no extensions
- Pass when handler has both filenames and extensions
- Fail when handler has neither filenames nor extensions
- Fail when filename contains
/or\ - Fail when filename is empty string
- Detect duplicate filenames across handlers
validateHandlers()detects filename conflicts
- Files:
packages/plugin-api/src/validation.test.ts - Acceptance: All new tests pass via
npx vitest runinpackages/plugin-api/
- Change: Add test cases:
-
6) Update tree-sitter helper to accept filenames
- Change:
- Add optional
filenames?: string[]toTreeSitterHandlerOptions - In
createTreeSitterHandler(), passfilenamesthrough to thematchobject:match: { extensions: options.extensions, ...(options.filenames && { filenames: options.filenames }) } - Make
extensionsoptional inTreeSitterHandlerOptions(default to[]) since a handler might only have filenames
- Add optional
- Files:
packages/plugin-helpers-tree-sitter/src/index.ts - Acceptance: TypeScript compiles. Existing callers unaffected.
- Change:
-
7) Add tree-sitter helper test for filenames passthrough
- Change: Add a test that creates a handler with
filenames: ['Makefile']and verifieshandler.match.filenamescontains it. Also test that omitting filenames results in nofilenamesproperty on match. - Files:
packages/plugin-helpers-tree-sitter/src/index.test.ts - Acceptance: Tests pass via
npx vitest runinpackages/plugin-helpers-tree-sitter/
- Change: Add a test that creates a handler with
-
8) Create the Dockerfile plugin
- Change: Create a new plugin package
packages/plugins/lang-dockerfile/following thelang-textpattern:package.json: name@elastic/scs-plugin-lang-dockerfile, dependencies on@elastic/scs-plugin-apitsconfig.json: extends../../../tsconfig.base.jsontsconfig.build.json: extends./tsconfig.json, excludes testsvitest.config.ts: standard config matching other pluginssrc/index.ts: exportcreateDockerfileHandler()returning aParserHandlerwith:name: 'dockerfile'match: { extensions: ['.dockerfile', '.containerfile'], filenames: ['Dockerfile', 'Containerfile'] }parse: useparseByLines(context, 'dockerfile', 'code')from@elastic/scs-plugin-api
- Files:
packages/plugins/lang-dockerfile/package.json,packages/plugins/lang-dockerfile/tsconfig.json,packages/plugins/lang-dockerfile/tsconfig.build.json,packages/plugins/lang-dockerfile/vitest.config.ts,packages/plugins/lang-dockerfile/src/index.ts - Acceptance:
createDockerfileHandler()returns a validParserHandler. Handler parses a simple Dockerfile into chunks.
- Change: Create a new plugin package
-
9) Add Dockerfile plugin tests
- Change: Create test file with:
- Verify handler name is
'dockerfile' - Verify
match.extensionscontains.dockerfileand.containerfile - Verify
match.filenamescontainsDockerfileandContainerfile - Parse a sample Dockerfile (multi-line with FROM, RUN, COPY, CMD) and verify chunks are produced with correct language and metrics
- Verify handler name is
- Files:
packages/plugins/lang-dockerfile/src/index.test.ts - Acceptance: Tests pass via
npx vitest runinpackages/plugins/lang-dockerfile/
- Change: Create test file with:
-
10) Register Dockerfile plugin in default-plugins
- Change:
- Add
import { createDockerfileHandler } from '@elastic/scs-plugin-lang-dockerfile'to the imports - Add a new
definePlugin({ pluginId: '@elastic/scs-plugin-lang-dockerfile', apiVersion: 1, handlers: [createDockerfileHandler()] })entry todefaultPlugins - Add
"@elastic/scs-plugin-lang-dockerfile": "^0.0.0"topackage.jsondependencies
- Add
- Files:
packages/default-plugins/src/index.ts,packages/default-plugins/package.json - Acceptance:
defaultPluginsincludes the dockerfile handler. Build succeeds.
- Change:
-
11) Install dependencies and build
- Change: Run
npm installfrom repo root to link the new package, then run the build to verify everything compiles - Files: all
- Acceptance:
npm installsucceeds,npm run buildsucceeds (or the equivalent workspace build command)
- Change: Run
-
12) Code review
- Change: Use the
tasktool withsubagent_type: "code-review"to run an isolated code review against the branch diff (git diff main...HEAD). Fix any critical findings. - Files: all modified files
- Acceptance: No critical findings remain
- Change: Use the
-
13) Run all checks and fix issues
- Change: Run the repo's test suite, linting, type-checking, and formatting. Fix any failures.
- Files: all modified files
- Acceptance: All checks pass
- The
tree-sitter-dockerfilepackage on npm is a security placeholder (0.0.1-security), not a real grammar. This is why the Dockerfile plugin usesparseByLinesinstead of tree-sitter. - The existing
lang-textplugin (packages/plugins/lang-text/src/index.ts) is the closest pattern to follow for the Dockerfile plugin. - The
parseByLineshelper is exported from@elastic/scs-plugin-apiand chunks files by line count usingdefaultChunkLinesandchunkOverlapLinesfrom context options. - Existing plugins that could benefit from
filenamesin the future:lang-bash(for files like.bashrc,.bash_profile), a futurelang-makefile(#9). - The
isSharedExtensionAllowedpattern in the router currently only applies to extensions (.hshared between C and C++). There's no equivalent need for shared filenames yet, but the filename conflict detection should follow the same architecture for consistency. - Issue reference: GitHub issue #4 (filename detection) and #10 (Dockerfile support).