@plaited/development-skills 0.3.5 → 0.4.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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  ISC License
2
2
 
3
- Copyright (c) 2025 Plaited
3
+ Copyright (c) 2026 Plaited Labs
4
4
 
5
5
  Permission to use, copy, modify, and/or distribute this software for any
6
6
  purpose with or without fee is hereby granted, provided that the above
package/README.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # development-skills
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/@plaited/development-skills.svg)](https://www.npmjs.com/package/@plaited/development-skills)
4
+ [![CI](https://github.com/plaited/acp-harness/actions/workflows/ci.yml/badge.svg)](https://github.com/plaited/development-skills/actions/workflows/ci.yml)
5
+ [![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://opensource.org/licenses/ISC)
6
+
7
+
3
8
  TypeScript LSP, code documentation, and validation tools. Available as both a CLI tool and as installable skills for AI coding agents.
4
9
 
5
10
  ## CLI Tool
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plaited/development-skills",
3
- "version": "0.3.5",
3
+ "version": "0.4.1",
4
4
  "description": "Development skills for Claude Code - TypeScript LSP, code documentation, and validation tools",
5
5
  "license": "ISC",
6
6
  "engines": {
@@ -1,28 +1,52 @@
1
+ import { join } from 'node:path'
2
+
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.
10
+ */
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
+ }
17
+
1
18
  /**
2
19
  * Resolve a file path to an absolute path
3
20
  *
4
21
  * @remarks
5
22
  * Handles three types of paths:
6
23
  * - Absolute paths (starting with `/`) - returned as-is
7
- * - Relative paths (starting with `.`) - resolved from cwd
24
+ * - Relative paths (starting with `.` or looking like `src/foo.ts`) - resolved from cwd
8
25
  * - Package export paths (e.g., `plaited/workshop/get-paths.ts`) - resolved via Bun.resolve()
9
26
  */
10
27
  export const resolveFilePath = async (filePath: string): Promise<string> => {
28
+ const cwd = process.cwd()
29
+
11
30
  // Absolute path
12
31
  if (filePath.startsWith('/')) {
13
32
  return filePath
14
33
  }
15
34
 
16
- // Relative path from cwd
35
+ // Explicit relative path from cwd (starts with . or ..)
17
36
  if (filePath.startsWith('.')) {
18
- return `${process.cwd()}/${filePath}`
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)) {
42
+ return join(cwd, filePath)
19
43
  }
20
44
 
21
45
  // Try package export path resolution
22
46
  try {
23
- return await Bun.resolve(filePath, process.cwd())
47
+ return await Bun.resolve(filePath, cwd)
24
48
  } catch {
25
49
  // Fall back to relative path from cwd
26
- return `${process.cwd()}/${filePath}`
50
+ return join(cwd, filePath)
27
51
  }
28
52
  }
@@ -1,4 +1,5 @@
1
1
  import { describe, expect, test } from 'bun:test'
2
+ import { join } from 'node:path'
2
3
  import { resolveFilePath } from '../resolve-file-path.ts'
3
4
 
4
5
  describe('resolveFilePath', () => {
@@ -9,9 +10,10 @@ describe('resolveFilePath', () => {
9
10
  })
10
11
 
11
12
  test('resolves relative path from cwd', async () => {
12
- const relativePath = './plugin/skills/typescript-lsp/scripts/tests/fixtures/sample.ts'
13
+ const relativePath = './src/resolve-file-path.ts'
13
14
  const result = await resolveFilePath(relativePath)
14
- expect(result).toBe(`${process.cwd()}/${relativePath}`)
15
+ // join() normalizes paths, removing the ./
16
+ expect(result).toBe(join(process.cwd(), relativePath))
15
17
  })
16
18
 
17
19
  test('resolves package export path via Bun.resolve', async () => {
@@ -28,6 +30,46 @@ describe('resolveFilePath', () => {
28
30
  const invalidPath = 'nonexistent-package/file.ts'
29
31
  const result = await resolveFilePath(invalidPath)
30
32
 
31
- expect(result).toBe(`${process.cwd()}/${invalidPath}`)
33
+ expect(result).toBe(join(process.cwd(), invalidPath))
34
+ })
35
+
36
+ test('resolves implicit relative path (src/foo.ts format) from cwd', async () => {
37
+ // Paths that look like file paths (contain / and end with extension)
38
+ // should resolve from cwd without trying Bun.resolve()
39
+ const implicitRelative = 'src/utils/parser.ts'
40
+ const result = await resolveFilePath(implicitRelative)
41
+
42
+ expect(result).toBe(join(process.cwd(), implicitRelative))
43
+ })
44
+
45
+ test('resolves various file extensions as implicit relative paths', async () => {
46
+ const extensions = ['ts', 'tsx', 'js', 'jsx', 'mjs', 'cjs', 'json']
47
+
48
+ for (const ext of extensions) {
49
+ const path = `src/file.${ext}`
50
+ const result = await resolveFilePath(path)
51
+ expect(result).toBe(join(process.cwd(), path))
52
+ }
53
+ })
54
+
55
+ test('treats bare package names without extensions as package specifiers', async () => {
56
+ // A path like 'typescript' (no slash, no extension) should try Bun.resolve()
57
+ const barePkg = 'typescript'
58
+ const result = await resolveFilePath(barePkg)
59
+
60
+ // Should resolve to node_modules, not cwd/typescript
61
+ expect(result).toContain('node_modules/typescript')
62
+ })
63
+
64
+ test('treats scoped packages as package specifiers, not implicit file paths', async () => {
65
+ // Scoped packages like @org/pkg/file.ts should NOT be treated as implicit
66
+ // relative paths, even if they contain / and end with an extension.
67
+ // They should go through Bun.resolve() first, falling back to cwd if not found.
68
+ const scopedPkg = '@nonexistent/pkg/src/file.ts'
69
+ const result = await resolveFilePath(scopedPkg)
70
+
71
+ // Falls back to cwd since package doesn't exist, but importantly it tried
72
+ // Bun.resolve() first (doesn't match looksLikeFilePath due to @ prefix)
73
+ expect(result).toBe(join(process.cwd(), scopedPkg))
32
74
  })
33
75
  })
@@ -1,47 +0,0 @@
1
- {
2
- "permissions": {
3
- "allow": [
4
- "Bash(bun test:*)",
5
- "Bash(bun run check:*)",
6
- "Bash(bun run check:write:*)",
7
- "WebFetch(domain:raw.githubusercontent.com)",
8
- "Bash(gh repo view:*)",
9
- "Bash(gh api:*)",
10
- "Bash(gh repo edit:*)",
11
- "Bash(git commit:*)",
12
- "Bash(git push:*)",
13
- "Bash(gh pr create:*)",
14
- "Bash(git add:*)",
15
- "Bash(gh run view:*)",
16
- "Bash(bunx biome check:*)",
17
- "Bash(git checkout:*)",
18
- "mcp__agent-skills-spec__SearchAgentSkills",
19
- "Bash(bunx lint-staged:*)",
20
- "Bash(git revert:*)",
21
- "Bash(bun biome check:*)",
22
- "Bash(bun scripts/fast-find.ts:*)",
23
- "mcp__bun-docs__SearchBun",
24
- "Bash(bun scripts/scan-imports.ts:*)",
25
- "Bash(bun:*)",
26
- "Bash(ls:*)",
27
- "Bash(gh pr view:*)",
28
- "Bash(gh pr diff:*)",
29
- "Bash(grep:*)",
30
- "Bash(echo:*)",
31
- "WebSearch",
32
- "Bash(gh pr comment:*)",
33
- "WebFetch(domain:github.com)",
34
- "WebFetch(domain:aiengineerguide.com)",
35
- "Bash(DEBUG_SCAFFOLD=1 bun bin/cli.ts:*)",
36
- "WebFetch(domain:cursor.com)",
37
- "WebFetch(domain:docs.factory.ai)",
38
- "WebFetch(domain:agents.md)",
39
- "Bash(git cherry-pick:*)"
40
- ]
41
- },
42
- "enableAllProjectMcpServers": true,
43
- "enabledMcpjsonServers": [
44
- "agent-skills-spec"
45
- ],
46
- "outputStyle": "Learning"
47
- }