@plaited/development-skills 0.4.1 → 0.6.0

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.
Files changed (33) hide show
  1. package/{.claude → .plaited}/rules/accuracy.md +3 -10
  2. package/{.claude → .plaited}/rules/code-review.md +2 -41
  3. package/.plaited/rules/git-workflow.md +36 -0
  4. package/.plaited/rules/module-organization.md +92 -0
  5. package/{.claude → .plaited}/rules/testing.md +81 -1
  6. package/package.json +3 -2
  7. package/src/lsp-analyze.ts +2 -2
  8. package/src/lsp-find.ts +15 -34
  9. package/src/lsp-hover.ts +2 -2
  10. package/src/lsp-references.ts +2 -2
  11. package/src/lsp-symbols.ts +2 -2
  12. package/src/resolve-file-path.ts +18 -28
  13. package/src/scaffold-rules.ts +148 -204
  14. package/src/tests/resolve-file-path.spec.ts +90 -51
  15. package/src/tests/scaffold-rules.spec.ts +148 -118
  16. package/.claude/commands/lsp-analyze.md +0 -66
  17. package/.claude/commands/lsp-find.md +0 -51
  18. package/.claude/commands/lsp-hover.md +0 -48
  19. package/.claude/commands/lsp-refs.md +0 -55
  20. package/.claude/commands/scaffold-rules.md +0 -221
  21. package/.claude/commands/validate-skill.md +0 -29
  22. package/.claude/rules/git-workflow.md +0 -66
  23. package/.claude/skills/code-documentation/SKILL.md +0 -47
  24. package/.claude/skills/code-documentation/references/internal-templates.md +0 -113
  25. package/.claude/skills/code-documentation/references/maintenance.md +0 -164
  26. package/.claude/skills/code-documentation/references/public-api-templates.md +0 -100
  27. package/.claude/skills/code-documentation/references/type-documentation.md +0 -116
  28. package/.claude/skills/code-documentation/references/workflow.md +0 -60
  29. package/.claude/skills/scaffold-rules/SKILL.md +0 -97
  30. package/.claude/skills/typescript-lsp/SKILL.md +0 -239
  31. package/.claude/skills/validate-skill/SKILL.md +0 -105
  32. /package/{.claude → .plaited}/rules/bun-apis.md +0 -0
  33. /package/{.claude → .plaited}/rules/github.md +0 -0
@@ -1,6 +1,6 @@
1
1
  <!--
2
- RULE TEMPLATE - Distributed via /scaffold-rules
3
- Variables: {{LINK:testing}}, {{#if development-skills}}, {{#if supports-slash-commands}}
2
+ RULE TEMPLATE - Distributed via scaffold-rules skill
3
+ Variables: {{LINK:testing}}, {{#if development-skills}}
4
4
  -->
5
5
 
6
6
  # Accuracy and Confidence Standards
@@ -19,7 +19,7 @@ Variables: {{LINK:testing}}, {{#if development-skills}}, {{#if supports-slash-co
19
19
  {{#if development-skills}}
20
20
  - **For TypeScript/JavaScript projects**: When @plaited/development-skills is installed, prefer LSP tools for type-aware verification:
21
21
  - Use `lsp-find` to search for symbols, types, and patterns across the workspace
22
- - Use `lsp-references` to find all usages of a symbol
22
+ - Use `lsp-refs` to find all usages of a symbol
23
23
  - Use `lsp-hover` to verify type signatures
24
24
  - Use `lsp-analyze` for batch analysis of file structure
25
25
  {{/if}}
@@ -30,14 +30,7 @@ Variables: {{LINK:testing}}, {{#if development-skills}}, {{#if supports-slash-co
30
30
  4. **Tool-Assisted Verification**: Use available tools to enhance verification accuracy:
31
31
  {{#if development-skills}}
32
32
  - **TypeScript LSP tools** (when available): Use for type-aware analysis of `.ts`, `.tsx`, `.js`, `.jsx` files
33
- {{/if}}
34
- {{#if supports-slash-commands}}
35
- - Available via `/lsp-hover`, `/lsp-find`, `/lsp-refs`, `/lsp-analyze` commands
36
- {{/if}}
37
- {{^if supports-slash-commands}}
38
- {{#if development-skills}}
39
33
  - Available via `bunx @plaited/development-skills lsp-*` commands
40
- {{/if}}
41
34
  {{/if}}
42
35
  - **WebFetch**: Retrieve current documentation from authoritative sources (MDN Web Docs, WHATWG specs) when using web platform APIs
43
36
  - These tools complement (but do not replace) reading live code - always verify outputs against actual implementation
@@ -1,6 +1,6 @@
1
1
  <!--
2
- RULE TEMPLATE - Distributed via /scaffold-rules
3
- Variables: {{#if development-skills}}, {{#if supports-slash-commands}}
2
+ RULE TEMPLATE - Distributed via scaffold-rules skill
3
+ Variables: {{#if development-skills}}
4
4
  -->
5
5
 
6
6
  # Code Review Standards
@@ -17,16 +17,9 @@ When working with AgentSkills directories (`.claude/skills/`, `.cursor/skills/`,
17
17
 
18
18
  {{#if development-skills}}
19
19
  **Validate structure:**
20
- {{#if supports-slash-commands}}
21
- ```
22
- /validate-skill <path>
23
- ```
24
- {{/if}}
25
- {{^if supports-slash-commands}}
26
20
  ```bash
27
21
  bunx @plaited/development-skills validate-skill <path>
28
22
  ```
29
- {{/if}}
30
23
 
31
24
  This checks:
32
25
  - SKILL.md exists with required frontmatter (name, description)
@@ -221,38 +214,6 @@ import config from './config.json'
221
214
 
222
215
  **Rationale:** Import attributes (ES2025) explicitly declare module types to the runtime. The `with { type: 'json' }` syntax is the standard (replacing the deprecated `assert` keyword). This provides runtime enforcement—if the file isn't valid JSON, the import fails.
223
216
 
224
- ## Module Organization
225
-
226
- Organize module exports into separate files by category:
227
-
228
- ```
229
- module/
230
- ├── module.types.ts # Type definitions only
231
- ├── module.schemas.ts # Zod schemas and schema-inferred types
232
- ├── module.constants.ts # Constants and enum-like objects
233
- ├── module-client.ts # Implementation
234
- └── index.ts # Public API exports (barrel file)
235
- ```
236
-
237
- ### Key Principles
238
-
239
- - **Types file**: Contains only type definitions, does NOT re-export from sibling files
240
- - **Schemas file**: Contains Zod schemas and their inferred types, does NOT export constants
241
- - **Constants file**: Contains all constant values (error codes, method names, defaults)
242
- - **Barrel file**: Uses wildcard exports; non-public files are simply not included
243
-
244
- ```typescript
245
- // ✅ Good: Direct imports from specific files
246
- import type { Config } from './module.types.ts'
247
- import { ConfigSchema } from './module.schemas.ts'
248
- import { METHODS, ERROR_CODES } from './module.constants.ts'
249
-
250
- // ❌ Avoid: Expecting types file to re-export everything
251
- import { Config, ConfigSchema, METHODS } from './module.types.ts'
252
- ```
253
-
254
- **Rationale:** Prevents circular dependencies, makes dependencies explicit, improves tree-shaking.
255
-
256
217
  ## Documentation Standards
257
218
 
258
219
  ### Mermaid Diagrams Only
@@ -0,0 +1,36 @@
1
+ <!--
2
+ RULE TEMPLATE - Distributed via scaffold-rules skill
3
+ Variables: {{LINK:*}}
4
+ -->
5
+
6
+ # Git Workflow
7
+
8
+ ## Commit Message Format
9
+
10
+ Use multi-line commit messages for detailed changes:
11
+
12
+ ```bash
13
+ git commit -m "refactor: description here
14
+
15
+ Additional context on second line."
16
+ ```
17
+
18
+ ## Pre-commit Hooks
19
+
20
+ **Never use `--no-verify`** to bypass pre-commit hooks. If hooks fail, it indicates a real issue that must be fixed:
21
+
22
+ 1. Investigate the error message
23
+ 2. Fix the underlying issue (lint errors, format issues, test failures)
24
+ 3. Re-run the commit
25
+
26
+ Using `--no-verify` masks problems and defeats the purpose of automated quality checks.
27
+
28
+ ## Commit Conventions
29
+
30
+ Follow conventional commits format:
31
+ - `feat:` - New features
32
+ - `fix:` - Bug fixes
33
+ - `refactor:` - Code changes that neither fix bugs nor add features
34
+ - `docs:` - Documentation only changes
35
+ - `chore:` - Maintenance tasks
36
+ - `test:` - Adding or updating tests
@@ -0,0 +1,92 @@
1
+ <!--
2
+ RULE TEMPLATE - Distributed via scaffold-rules skill
3
+ Variables: {{#if bun}}
4
+ -->
5
+
6
+ # Module Organization
7
+
8
+ ## No Index Files
9
+
10
+ **Never use `index.ts` or `index.js` files.** These create implicit magic and complicate debugging.
11
+
12
+ ### Re-export Pattern
13
+
14
+ Use named re-export files at the parent level, matching the folder name:
15
+
16
+ ```
17
+ src/
18
+ ├── acp/ # Feature module
19
+ │ ├── acp.types.ts
20
+ │ ├── acp.schemas.ts
21
+ │ └── acp.ts # Main implementation
22
+ ├── acp.ts # Re-exports public API from acp/
23
+ ├── utils/
24
+ │ └── format.ts
25
+ └── utils.ts # Re-exports public API from utils/
26
+ ```
27
+
28
+ ### Single Feature Packages
29
+
30
+ When a package has one primary feature, expose that re-export file directly as main:
31
+
32
+ ```json
33
+ {
34
+ "main": "src/acp.ts",
35
+ "exports": {
36
+ ".": "./src/acp.ts",
37
+ "./utils": "./src/utils.ts"
38
+ }
39
+ }
40
+ ```
41
+
42
+ Only use `main.ts` if the package truly has multiple co-equal entry points that need a unified export.
43
+
44
+ ## Explicit Import Extensions
45
+
46
+ {{#if bun}}
47
+ Always include `.ts` extensions in imports. Bun runs TypeScript natively—no compilation required:
48
+ {{/if}}
49
+
50
+ ```typescript
51
+ // ✅ Good
52
+ import { Config } from './module.types.ts'
53
+ import { createClient } from '../acp/acp.ts'
54
+
55
+ // ❌ Avoid
56
+ import { Config } from './module.types'
57
+ ```
58
+
59
+ **Rationale:** Explicit extensions enable direct execution, clearer module graphs, and align with ES module standards. With `allowImportingTsExtensions: true`, TypeScript supports this pattern.
60
+
61
+ ## File Organization Within Modules
62
+
63
+ ```
64
+ feature/
65
+ ├── feature.types.ts # Type definitions only
66
+ ├── feature.schemas.ts # Zod schemas + inferred types
67
+ ├── feature.constants.ts # Constants, error codes
68
+ └── feature.ts # Main implementation
69
+ ```
70
+
71
+ - **Types file**: Type definitions only, no re-exports from siblings
72
+ - **Schemas file**: Zod schemas and `z.infer<>` types
73
+ - **Constants file**: All constant values
74
+ - **Main file**: Primary implementation, named after the feature
75
+
76
+ ### Key Principles
77
+
78
+ - **Direct imports**: Import from specific files, not through re-exports within a module
79
+ - **Re-exports at boundaries**: Only use re-export files to expose public API at module boundaries
80
+ - **No circular re-exports**: Types file does NOT re-export from siblings
81
+
82
+ ```typescript
83
+ // ✅ Good: Direct imports from specific files
84
+ import type { Config } from './feature.types.ts'
85
+ import { ConfigSchema } from './feature.schemas.ts'
86
+ import { ERROR_CODES } from './feature.constants.ts'
87
+
88
+ // ❌ Avoid: Expecting one file to re-export everything
89
+ import { Config, ConfigSchema, ERROR_CODES } from './feature.types.ts'
90
+ ```
91
+
92
+ **Rationale:** Prevents circular dependencies, makes dependencies explicit, improves tree-shaking.
@@ -1,5 +1,5 @@
1
1
  <!--
2
- RULE TEMPLATE - Distributed via /scaffold-rules
2
+ RULE TEMPLATE - Distributed via scaffold-rules skill
3
3
  Variables: {{#if agent:claude}}
4
4
  -->
5
5
 
@@ -75,6 +75,86 @@ If a value might not exist, the test should either:
75
75
  2. Assert that it doesn't exist (if that's the expected behavior)
76
76
  3. Restructure the test to ensure the value is always present
77
77
 
78
+ ## Test Coverage Principles
79
+
80
+ ### Expansion Checklist
81
+
82
+ When writing tests, ensure coverage of:
83
+
84
+ - [ ] **Happy path** - Expected normal usage
85
+ - [ ] **Edge cases** - Empty strings, special characters, boundary values
86
+ - [ ] **Fallback paths** - What happens when try/catch catches?
87
+ - [ ] **Real dependencies** - Use installed packages (e.g., `@modelcontextprotocol/sdk`) not fake paths
88
+ - [ ] **Category organization** - Group related tests in `describe` blocks
89
+
90
+ ### Meaningful Test Coverage
91
+
92
+ Tests should verify behavior, not just exercise code. Each test should answer: "What breaks if this test fails?"
93
+
94
+ **Coverage priorities:**
95
+ 1. **Happy path** - Normal expected usage
96
+ 2. **Edge cases** - Boundary conditions, empty inputs, unusual formats
97
+ 3. **Error paths** - Invalid inputs, missing dependencies, fallback behavior
98
+ 4. **Real integrations** - Use actual installed packages when testing module resolution
99
+
100
+ ### Use Real Dependencies
101
+
102
+ When testing features that interact with packages or the file system, prefer real installed packages over mocked/fake paths:
103
+
104
+ ```typescript
105
+ // ✅ Good: Real scoped package with subpath exports
106
+ test('resolves scoped package subpath', () => {
107
+ const result = resolveFilePath('@modelcontextprotocol/sdk/client')
108
+ expect(result).toContain('node_modules/@modelcontextprotocol/sdk')
109
+ })
110
+
111
+ // ✅ Good: Real package deep subpath
112
+ test('resolves deep subpath with extension', () => {
113
+ const result = resolveFilePath('@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js')
114
+ expect(result).toContain('bearerAuth')
115
+ })
116
+ ```
117
+
118
+ ### Test Both Branches
119
+
120
+ When code has conditional logic, try/catch, or fallbacks, test both paths:
121
+
122
+ ```typescript
123
+ describe('package resolution', () => {
124
+ test('success: resolves existing package', () => {
125
+ const result = resolveFilePath('typescript')
126
+ expect(result).toContain('node_modules/typescript')
127
+ })
128
+
129
+ test('fallback: returns cwd path for non-existent package', () => {
130
+ const result = resolveFilePath('nonexistent-package')
131
+ expect(result).toBe(join(cwd, 'nonexistent-package'))
132
+ })
133
+ })
134
+ ```
135
+
136
+ ### Organize with Describe Blocks
137
+
138
+ Group tests by category for better readability and failure diagnosis:
139
+
140
+ ```typescript
141
+ describe('resolveFilePath', () => {
142
+ describe('absolute paths', () => {
143
+ test('returns as-is', () => { /* ... */ })
144
+ })
145
+
146
+ describe('relative paths with extension', () => {
147
+ test('resolves ./ paths', () => { /* ... */ })
148
+ test('resolves ../ paths', () => { /* ... */ })
149
+ })
150
+
151
+ describe('scoped package specifiers', () => {
152
+ test('resolves subpath exports', () => { /* ... */ })
153
+ test('falls back for non-existent', () => { /* ... */ })
154
+ })
155
+ })
156
+ ```
157
+
78
158
  ## Docker Integration Tests
79
159
 
80
160
  Tests that require external services or API keys can run in Docker containers for consistent, isolated execution.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plaited/development-skills",
3
- "version": "0.4.1",
3
+ "version": "0.6.0",
4
4
  "description": "Development skills for Claude Code - TypeScript LSP, code documentation, and validation tools",
5
5
  "license": "ISC",
6
6
  "engines": {
@@ -21,7 +21,7 @@
21
21
  "files": [
22
22
  "bin/",
23
23
  "src/",
24
- ".claude/"
24
+ ".plaited/"
25
25
  ],
26
26
  "publishConfig": {
27
27
  "access": "public"
@@ -48,6 +48,7 @@
48
48
  },
49
49
  "devDependencies": {
50
50
  "@biomejs/biome": "2.3.11",
51
+ "@modelcontextprotocol/sdk": "1.22.0",
51
52
  "@types/bun": "1.3.6",
52
53
  "format-package": "7.0.0",
53
54
  "lint-staged": "16.2.7",
@@ -125,7 +125,7 @@ Examples:
125
125
  console.error('Error: File path required')
126
126
  process.exit(1)
127
127
  }
128
- const absolutePath = await resolveFilePath(filePath)
128
+ const absolutePath = resolveFilePath(filePath)
129
129
  const uri = `file://${absolutePath}`
130
130
  const rootUri = `file://${process.cwd()}`
131
131
 
@@ -211,7 +211,7 @@ Examples:
211
211
 
212
212
  console.log(JSON.stringify(result, null, 2))
213
213
  } catch (error) {
214
- console.error(`Error: ${error}`)
214
+ console.error('Error:', error)
215
215
  await client.stop()
216
216
  process.exit(1)
217
217
  }
package/src/lsp-find.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env bun
2
2
  /**
3
- * Search for symbols across the workspace by name
3
+ * Search for TypeScript symbols across the workspace by name
4
4
  *
5
- * Usage: bun lsp-find.ts <query> [file]
5
+ * Usage: bun lsp-find.ts <query> <context-file>
6
6
  */
7
7
 
8
8
  import { parseArgs } from 'node:util'
@@ -10,25 +10,9 @@ import { LspClient } from './lsp-client.ts'
10
10
  import { resolveFilePath } from './resolve-file-path.ts'
11
11
 
12
12
  /**
13
- * Find a default context file when none is provided
13
+ * Search for TypeScript symbols across the workspace by name
14
14
  *
15
- * @remarks
16
- * Checks common TypeScript entry points in order of preference
17
- */
18
- const findDefaultContextFile = async (): Promise<string | null> => {
19
- const candidates = [`${process.cwd()}/src/index.ts`, `${process.cwd()}/src/main.ts`, `${process.cwd()}/index.ts`]
20
- for (const candidate of candidates) {
21
- if (await Bun.file(candidate).exists()) {
22
- return candidate
23
- }
24
- }
25
- return null
26
- }
27
-
28
- /**
29
- * Search for symbols across the workspace by name
30
- *
31
- * @param args - Command line arguments [query, file?]
15
+ * @param args - Command line arguments [query, context-file]
32
16
  */
33
17
  export const lspFind = async (args: string[]) => {
34
18
  const { positionals } = parseArgs({
@@ -38,10 +22,15 @@ export const lspFind = async (args: string[]) => {
38
22
 
39
23
  const [query, filePath] = positionals
40
24
 
41
- if (!query) {
42
- console.error('Usage: lsp-find <query> [file]')
43
- console.error(' query: Symbol name or partial name to search')
44
- console.error(' file: Optional file to open for project context')
25
+ if (!query || !filePath) {
26
+ console.error('Error: context-file required')
27
+ console.error('')
28
+ console.error('lsp-find searches TypeScript SYMBOLS (functions, types, classes).')
29
+ console.error(' - Use Glob to find files by pattern')
30
+ console.error(' - Use Grep to search text content')
31
+ console.error(' - Use lsp-find <query> <context-file> for symbol search')
32
+ console.error('')
33
+ console.error('Provide any .ts file as context-file (e.g., src/app.ts)')
45
34
  process.exit(1)
46
35
  }
47
36
 
@@ -51,15 +40,7 @@ export const lspFind = async (args: string[]) => {
51
40
  try {
52
41
  await client.start()
53
42
 
54
- // Open a file to establish project context if provided, otherwise find a default
55
- const contextFile = filePath ? await resolveFilePath(filePath) : await findDefaultContextFile()
56
-
57
- if (!contextFile) {
58
- console.error('Error: No context file found.')
59
- console.error('Provide a file path or ensure src/index.ts, src/main.ts, or index.ts exists.')
60
- await client.stop()
61
- process.exit(1)
62
- }
43
+ const contextFile = resolveFilePath(filePath)
63
44
 
64
45
  const file = Bun.file(contextFile)
65
46
  if (!(await file.exists())) {
@@ -88,7 +69,7 @@ export const lspFind = async (args: string[]) => {
88
69
 
89
70
  console.log(JSON.stringify(result, null, 2))
90
71
  } catch (error) {
91
- console.error(`Error: ${error}`)
72
+ console.error('Error:', error)
92
73
  await client.stop()
93
74
  process.exit(1)
94
75
  }
package/src/lsp-hover.ts CHANGED
@@ -38,7 +38,7 @@ export const lspHover = async (args: string[]) => {
38
38
  process.exit(1)
39
39
  }
40
40
 
41
- const absolutePath = await resolveFilePath(filePath)
41
+ const absolutePath = resolveFilePath(filePath)
42
42
  const uri = `file://${absolutePath}`
43
43
  const rootUri = `file://${process.cwd()}`
44
44
 
@@ -75,7 +75,7 @@ export const lspHover = async (args: string[]) => {
75
75
  console.log('null')
76
76
  }
77
77
  } catch (error) {
78
- console.error(`Error: ${error}`)
78
+ console.error('Error:', error)
79
79
  await client.stop()
80
80
  process.exit(1)
81
81
  }
@@ -38,7 +38,7 @@ export const lspRefs = async (args: string[]) => {
38
38
  process.exit(1)
39
39
  }
40
40
 
41
- const absolutePath = await resolveFilePath(filePath)
41
+ const absolutePath = resolveFilePath(filePath)
42
42
  const uri = `file://${absolutePath}`
43
43
  const rootUri = `file://${process.cwd()}`
44
44
 
@@ -71,7 +71,7 @@ export const lspRefs = async (args: string[]) => {
71
71
 
72
72
  console.log(JSON.stringify(result, null, 2))
73
73
  } catch (error) {
74
- console.error(`Error: ${error}`)
74
+ console.error('Error:', error)
75
75
  await client.stop()
76
76
  process.exit(1)
77
77
  }
@@ -28,7 +28,7 @@ export const lspSymbols = async (args: string[]) => {
28
28
  process.exit(1)
29
29
  }
30
30
 
31
- const absolutePath = await resolveFilePath(filePath)
31
+ const absolutePath = resolveFilePath(filePath)
32
32
  const uri = `file://${absolutePath}`
33
33
  const rootUri = `file://${process.cwd()}`
34
34
 
@@ -61,7 +61,7 @@ export const lspSymbols = async (args: string[]) => {
61
61
 
62
62
  console.log(JSON.stringify(result, null, 2))
63
63
  } catch (error) {
64
- console.error(`Error: ${error}`)
64
+ console.error('Error:', error)
65
65
  await client.stop()
66
66
  process.exit(1)
67
67
  }
@@ -1,52 +1,42 @@
1
1
  import { join } from 'node:path'
2
2
 
3
3
  /**
4
- * Check if a path looks like a file path (vs a package specifier)
5
- *
6
- * @remarks
7
- * File paths typically contain a `/` and end with a file extension.
8
- * Package specifiers are bare like `lodash` or scoped like `@org/pkg`.
9
- * Scoped packages starting with `@` are excluded even if they have extensions.
4
+ * Check if a path has a source file extension
10
5
  */
11
- const looksLikeFilePath = (path: string): boolean => {
12
- // Scoped packages like @org/pkg/file.ts should use Bun.resolve()
13
- if (path.startsWith('@')) return false
14
- // Contains a slash and ends with a common source file extension
15
- return path.includes('/') && /\.(tsx?|jsx?|mjs|cjs|json)$/.test(path)
16
- }
6
+ const hasSourceExtension = (path: string): boolean => /\.(tsx?|jsx?|mjs|cjs|json)$/.test(path)
17
7
 
18
8
  /**
19
9
  * Resolve a file path to an absolute path
20
10
  *
21
11
  * @remarks
22
- * Handles three types of paths:
23
- * - Absolute paths (starting with `/`) - returned as-is
24
- * - Relative paths (starting with `.` or looking like `src/foo.ts`) - resolved from cwd
25
- * - Package export paths (e.g., `plaited/workshop/get-paths.ts`) - resolved via Bun.resolve()
12
+ * Resolution order:
13
+ * 1. Absolute paths (starting with `/`) - returned as-is
14
+ * 2. Explicit relative with extension (`./foo.ts`, `../bar.ts`) - resolved from cwd
15
+ * 3. Everything else - try Bun.resolveSync(), fallback to cwd
16
+ *
17
+ * The "everything else" category includes:
18
+ * - Package specifiers: `@org/pkg`, `lodash`, `typescript/lib/typescript.js`
19
+ * - Relative without extension: `./testing` (for package.json exports)
20
+ * - Implicit relative: `src/foo.ts` (fails Bun.resolveSync, falls back to cwd)
26
21
  */
27
- export const resolveFilePath = async (filePath: string): Promise<string> => {
22
+ export const resolveFilePath = (filePath: string): string => {
28
23
  const cwd = process.cwd()
29
24
 
30
- // Absolute path
25
+ // Absolute path - return as-is
31
26
  if (filePath.startsWith('/')) {
32
27
  return filePath
33
28
  }
34
29
 
35
- // Explicit relative path from cwd (starts with . or ..)
36
- if (filePath.startsWith('.')) {
37
- return join(cwd, filePath)
38
- }
39
-
40
- // Implicit relative path (looks like a file path, e.g., src/utils/foo.ts)
41
- if (looksLikeFilePath(filePath)) {
30
+ // Explicit relative with extension - resolve directly from cwd
31
+ if (filePath.startsWith('.') && hasSourceExtension(filePath)) {
42
32
  return join(cwd, filePath)
43
33
  }
44
34
 
45
- // Try package export path resolution
35
+ // Everything else: try Bun.resolveSync
36
+ // Handles packages, exports field resolution, and implicit relative paths (via fallback)
46
37
  try {
47
- return await Bun.resolve(filePath, cwd)
38
+ return Bun.resolveSync(filePath, cwd)
48
39
  } catch {
49
- // Fall back to relative path from cwd
50
40
  return join(cwd, filePath)
51
41
  }
52
42
  }