@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.
- package/{.claude → .plaited}/rules/accuracy.md +3 -10
- package/{.claude → .plaited}/rules/code-review.md +2 -41
- package/.plaited/rules/git-workflow.md +36 -0
- package/.plaited/rules/module-organization.md +92 -0
- package/{.claude → .plaited}/rules/testing.md +81 -1
- package/package.json +3 -2
- package/src/lsp-analyze.ts +2 -2
- package/src/lsp-find.ts +15 -34
- package/src/lsp-hover.ts +2 -2
- package/src/lsp-references.ts +2 -2
- package/src/lsp-symbols.ts +2 -2
- package/src/resolve-file-path.ts +18 -28
- package/src/scaffold-rules.ts +148 -204
- package/src/tests/resolve-file-path.spec.ts +90 -51
- package/src/tests/scaffold-rules.spec.ts +148 -118
- package/.claude/commands/lsp-analyze.md +0 -66
- package/.claude/commands/lsp-find.md +0 -51
- package/.claude/commands/lsp-hover.md +0 -48
- package/.claude/commands/lsp-refs.md +0 -55
- package/.claude/commands/scaffold-rules.md +0 -221
- package/.claude/commands/validate-skill.md +0 -29
- package/.claude/rules/git-workflow.md +0 -66
- package/.claude/skills/code-documentation/SKILL.md +0 -47
- package/.claude/skills/code-documentation/references/internal-templates.md +0 -113
- package/.claude/skills/code-documentation/references/maintenance.md +0 -164
- package/.claude/skills/code-documentation/references/public-api-templates.md +0 -100
- package/.claude/skills/code-documentation/references/type-documentation.md +0 -116
- package/.claude/skills/code-documentation/references/workflow.md +0 -60
- package/.claude/skills/scaffold-rules/SKILL.md +0 -97
- package/.claude/skills/typescript-lsp/SKILL.md +0 -239
- package/.claude/skills/validate-skill/SKILL.md +0 -105
- /package/{.claude → .plaited}/rules/bun-apis.md +0 -0
- /package/{.claude → .plaited}/rules/github.md +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<!--
|
|
2
|
-
RULE TEMPLATE - Distributed via
|
|
3
|
-
Variables: {{LINK:testing}}, {{#if development-skills}}
|
|
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-
|
|
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
|
|
3
|
-
Variables: {{#if development-skills}}
|
|
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
|
|
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.
|
|
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
|
-
".
|
|
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",
|
package/src/lsp-analyze.ts
CHANGED
|
@@ -125,7 +125,7 @@ Examples:
|
|
|
125
125
|
console.error('Error: File path required')
|
|
126
126
|
process.exit(1)
|
|
127
127
|
}
|
|
128
|
-
const absolutePath =
|
|
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(
|
|
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>
|
|
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
|
-
*
|
|
13
|
+
* Search for TypeScript symbols across the workspace by name
|
|
14
14
|
*
|
|
15
|
-
* @
|
|
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('
|
|
43
|
-
console.error('
|
|
44
|
-
console.error('
|
|
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
|
-
|
|
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(
|
|
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 =
|
|
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(
|
|
78
|
+
console.error('Error:', error)
|
|
79
79
|
await client.stop()
|
|
80
80
|
process.exit(1)
|
|
81
81
|
}
|
package/src/lsp-references.ts
CHANGED
|
@@ -38,7 +38,7 @@ export const lspRefs = async (args: string[]) => {
|
|
|
38
38
|
process.exit(1)
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
const absolutePath =
|
|
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(
|
|
74
|
+
console.error('Error:', error)
|
|
75
75
|
await client.stop()
|
|
76
76
|
process.exit(1)
|
|
77
77
|
}
|
package/src/lsp-symbols.ts
CHANGED
|
@@ -28,7 +28,7 @@ export const lspSymbols = async (args: string[]) => {
|
|
|
28
28
|
process.exit(1)
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
const absolutePath =
|
|
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(
|
|
64
|
+
console.error('Error:', error)
|
|
65
65
|
await client.stop()
|
|
66
66
|
process.exit(1)
|
|
67
67
|
}
|
package/src/resolve-file-path.ts
CHANGED
|
@@ -1,52 +1,42 @@
|
|
|
1
1
|
import { join } from 'node:path'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Check if a path
|
|
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
|
|
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
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
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 =
|
|
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
|
|
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
|
-
//
|
|
35
|
+
// Everything else: try Bun.resolveSync
|
|
36
|
+
// Handles packages, exports field resolution, and implicit relative paths (via fallback)
|
|
46
37
|
try {
|
|
47
|
-
return
|
|
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
|
}
|