@plaited/development-skills 0.6.4 → 0.7.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.
@@ -28,4 +28,4 @@
28
28
  - Code review: Read files before commenting
29
29
  - Patterns: Confirm examples reflect actual usage
30
30
 
31
- See .plaited/rules/testing.md for verification in test contexts.
31
+ See .agents/rules/testing.md for verification in test contexts.
@@ -14,7 +14,7 @@
14
14
 
15
15
  **Use real dependencies** - Prefer installed packages over mocks when testing module resolution
16
16
  *Verify:* Review test imports for fake paths
17
- *Fix:* Use actual package like `@modelcontextprotocol/sdk/client`
17
+ *Fix:* Use actual package like `typescript`
18
18
 
19
19
  **Organize with describe** - Group related tests in `describe('feature', () => {...})`
20
20
  *Verify:* Check for flat test structure
@@ -34,7 +34,7 @@ bunx @plaited/development-skills scaffold-rules
34
34
  ```
35
35
 
36
36
  This will:
37
- 1. Copy rules to `.plaited/rules/` (canonical location)
37
+ 1. Copy rules to `.agents/rules/` (canonical location)
38
38
  2. Create symlinks in `.claude/rules` and `.cursor/rules` (if those directories exist)
39
39
  3. Fallback: append links to `AGENTS.md` if no agent directories found
40
40
 
@@ -52,23 +52,23 @@ Tell the user what was created based on the `actions` output.
52
52
  ## How It Works
53
53
 
54
54
  ```
55
- .plaited/rules/ ← Canonical location (files copied here)
55
+ .agents/rules/ ← Canonical location (files copied here)
56
56
  ├── testing.md
57
57
  ├── bun.md
58
58
  └── ...
59
59
 
60
- .claude/rules -> ../.plaited/rules ← Symlink (if .claude/ exists)
61
- .cursor/rules -> ../.plaited/rules ← Symlink (if .cursor/ exists)
60
+ .claude/rules -> ../.agents/rules ← Symlink (if .claude/ exists)
61
+ .cursor/rules -> ../.agents/rules ← Symlink (if .cursor/ exists)
62
62
  ```
63
63
 
64
64
  | Project has... | Copy | Symlinks | AGENTS.md |
65
65
  |----------------|------|----------|-----------|
66
- | `.plaited/` only | ✓ | None | No |
66
+ | `.agents/` only | ✓ | None | No |
67
67
  | `.claude/` only | ✓ | `.claude/rules` | No |
68
68
  | `.cursor/` only | ✓ | `.cursor/rules` | No |
69
- | `.plaited/` + `.claude/` | ✓ | `.claude/rules` | No |
70
- | `.plaited/` + `.cursor/` | ✓ | `.cursor/rules` | No |
71
- | `.plaited/` + `.claude/` + `.cursor/` | ✓ | Both | No |
69
+ | `.agents/` + `.claude/` | ✓ | `.claude/rules` | No |
70
+ | `.agents/` + `.cursor/` | ✓ | `.cursor/rules` | No |
71
+ | `.agents/` + `.claude/` + `.cursor/` | ✓ | Both | No |
72
72
  | None of the above | ✓ | None | ✓ Append links |
73
73
 
74
74
  ## Related Skills
package/README.md CHANGED
@@ -42,11 +42,11 @@ LSP understands re-exports, aliases, and type relationships.
42
42
  ## Install for AI Agents
43
43
 
44
44
  ```bash
45
- curl -fsSL https://raw.githubusercontent.com/plaited/skills-installer/main/install.sh | bash -s -- --agent <agent-name> --project development-skills
45
+ npx skills add plaited/development-skills
46
+ # or
47
+ bunx skills add plaited/development-skills
46
48
  ```
47
49
 
48
- **Agents:** `claude` · `cursor` · `copilot` · `codex` · `gemini` · `windsurf` · `opencode` · `amp` · `goose` · `factory`
49
-
50
50
  ## Commands
51
51
 
52
52
  | Command | What it does |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plaited/development-skills",
3
- "version": "0.6.4",
3
+ "version": "0.7.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
- ".plaited/"
24
+ ".agents/"
25
25
  ],
26
26
  "publishConfig": {
27
27
  "access": "public"
@@ -48,7 +48,6 @@
48
48
  },
49
49
  "devDependencies": {
50
50
  "@biomejs/biome": "2.3.11",
51
- "@modelcontextprotocol/sdk": "1.22.0",
52
51
  "@types/bun": "1.3.6",
53
52
  "format-package": "7.0.0",
54
53
  "lint-staged": "16.2.7",
@@ -2,7 +2,7 @@
2
2
  /**
3
3
  * Scaffold development rules - Copy bundled rules and create symlinks
4
4
  *
5
- * Copies rules from the package to `.plaited/rules/` (canonical location),
5
+ * Copies rules from the package to `.agents/rules/` (canonical location),
6
6
  * creates symlinks for `.claude/` and `.cursor/` agent directories,
7
7
  * and falls back to appending links in `AGENTS.md` if no agent dirs exist.
8
8
  *
@@ -15,18 +15,18 @@ import { mkdir, readdir, readlink, stat, symlink } from 'node:fs/promises'
15
15
  import { join } from 'node:path'
16
16
  import { parseArgs } from 'node:util'
17
17
 
18
- /** Agents that get symlinks to .plaited/rules (not .plaited itself) */
18
+ /** Agents that get symlinks to .agents/rules (not .agents itself) */
19
19
  const SYMLINK_AGENTS = ['.claude', '.cursor'] as const
20
20
 
21
- /** All supported agent directories (including .plaited which gets direct copy) */
22
- const ALL_AGENTS = ['.plaited', ...SYMLINK_AGENTS] as const
21
+ /** All supported agent directories (including .agents which gets direct copy) */
22
+ const ALL_AGENTS = ['.agents', ...SYMLINK_AGENTS] as const
23
23
 
24
24
  /** Canonical rules location */
25
- const TARGET_RULES = '.plaited/rules' as const
25
+ const TARGET_RULES = '.agents/rules' as const
26
26
 
27
27
  /**
28
28
  * NOTE: This tool only scaffolds RULES, not skills.
29
- * Skills symlinks (.claude/skills -> ../.plaited/skills) are managed separately
29
+ * Skills symlinks (.claude/skills -> ../.agents/skills) are managed separately
30
30
  * via the skills-installer or manual setup.
31
31
  */
32
32
 
@@ -59,7 +59,7 @@ export const scaffoldRules = async (args: string[]): Promise<void> => {
59
59
  const dryRun = values['dry-run'] as boolean | undefined
60
60
  const listOnly = values.list as boolean | undefined
61
61
 
62
- const sourceRules = join(import.meta.dir, '../.plaited/rules')
62
+ const sourceRules = join(import.meta.dir, '../.agents/rules')
63
63
  const cwd = process.cwd()
64
64
 
65
65
  // Get available rules
@@ -74,7 +74,7 @@ export const scaffoldRules = async (args: string[]): Promise<void> => {
74
74
 
75
75
  const actions: string[] = []
76
76
 
77
- // Check for agent directories BEFORE copying (since copy creates .plaited/)
77
+ // Check for agent directories BEFORE copying (since copy creates .agents/)
78
78
  // This determines whether to fall back to AGENTS.md append
79
79
  let hadAgentDirBeforeScaffold = false
80
80
  for (const agent of ALL_AGENTS) {
@@ -84,7 +84,7 @@ export const scaffoldRules = async (args: string[]): Promise<void> => {
84
84
  }
85
85
  }
86
86
 
87
- // 1. Copy rules to .plaited/rules/ (canonical location, serves .plaited agent)
87
+ // 1. Copy rules to .agents/rules/ (canonical location, serves .agents agent)
88
88
  const targetDir = join(cwd, TARGET_RULES)
89
89
  if (!dryRun) {
90
90
  await mkdir(targetDir, { recursive: true })
@@ -108,7 +108,7 @@ export const scaffoldRules = async (args: string[]): Promise<void> => {
108
108
  // Check if symlink already exists and points to right place
109
109
  try {
110
110
  const existing = await readlink(rulesLink)
111
- if (existing === '../.plaited/rules') {
111
+ if (existing === '../.agents/rules') {
112
112
  actions.push(`skip: ${agent}/rules (symlink exists)`)
113
113
  continue
114
114
  }
@@ -117,9 +117,9 @@ export const scaffoldRules = async (args: string[]): Promise<void> => {
117
117
  }
118
118
 
119
119
  if (!dryRun) {
120
- await symlink('../.plaited/rules', rulesLink)
120
+ await symlink('../.agents/rules', rulesLink)
121
121
  }
122
- actions.push(`symlink: ${agent}/rules -> ../.plaited/rules`)
122
+ actions.push(`symlink: ${agent}/rules -> ../.agents/rules`)
123
123
  }
124
124
  }
125
125
 
@@ -130,7 +130,7 @@ export const scaffoldRules = async (args: string[]): Promise<void> => {
130
130
 
131
131
  if (await agentsMd.exists()) {
132
132
  const content = await agentsMd.text()
133
- if (content.includes('.plaited/rules')) {
133
+ if (content.includes('.agents/rules')) {
134
134
  actions.push('skip: AGENTS.md (already has rules)')
135
135
  } else {
136
136
  const links = rules.map((f) => `- [${f.replace('.md', '')}](${TARGET_RULES}/${f})`).join('\n')
@@ -75,27 +75,6 @@ describe('resolveFilePath', () => {
75
75
  })
76
76
 
77
77
  describe('scoped package specifiers', () => {
78
- test('resolves scoped package subpath export', () => {
79
- // @modelcontextprotocol/sdk/client is a defined export
80
- const result = resolveFilePath('@modelcontextprotocol/sdk/client')
81
- expect(result).toContain('node_modules/@modelcontextprotocol/sdk')
82
- expect(result).toContain('client')
83
- })
84
-
85
- test('resolves scoped package server subpath', () => {
86
- const result = resolveFilePath('@modelcontextprotocol/sdk/server')
87
- expect(result).toContain('node_modules/@modelcontextprotocol/sdk')
88
- expect(result).toContain('server')
89
- })
90
-
91
- test('resolves scoped package deep subpath with extension', () => {
92
- // Deep subpath with .js extension via wildcard export "./*"
93
- const path = '@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js'
94
- const result = resolveFilePath(path)
95
- expect(result).toContain('node_modules/@modelcontextprotocol/sdk')
96
- expect(result).toContain('bearerAuth')
97
- })
98
-
99
78
  test('falls back to cwd for non-existent scoped package', () => {
100
79
  const scopedPkg = '@nonexistent/pkg/src/file.ts'
101
80
  const result = resolveFilePath(scopedPkg)
@@ -63,7 +63,7 @@ describe('scaffold-rules', () => {
63
63
  const result: ScaffoldOutput = await $`cd ${testDir} && bun ${binDir}/cli.ts scaffold-rules --dry-run`.json()
64
64
 
65
65
  expect(result.dryRun).toBe(true)
66
- expect(result.targetRules).toBe('.plaited/rules')
66
+ expect(result.targetRules).toBe('.agents/rules')
67
67
  expect(result.actions).toBeArray()
68
68
  })
69
69
 
@@ -81,7 +81,7 @@ describe('scaffold-rules', () => {
81
81
  test('does not create files in dry-run mode', async () => {
82
82
  await $`cd ${testDir} && bun ${binDir}/cli.ts scaffold-rules --dry-run`.json()
83
83
 
84
- const rulesDir = Bun.file(join(testDir, '.plaited/rules'))
84
+ const rulesDir = Bun.file(join(testDir, '.agents/rules'))
85
85
  expect(await rulesDir.exists()).toBe(false)
86
86
  })
87
87
 
@@ -93,10 +93,10 @@ describe('scaffold-rules', () => {
93
93
  })
94
94
 
95
95
  describe('copy behavior', () => {
96
- test('copies rules to .plaited/rules/', async () => {
96
+ test('copies rules to .agents/rules/', async () => {
97
97
  await $`cd ${testDir} && bun ${binDir}/cli.ts scaffold-rules`.quiet()
98
98
 
99
- const coreRule = Bun.file(join(testDir, '.plaited/rules/core.md'))
99
+ const coreRule = Bun.file(join(testDir, '.agents/rules/core.md'))
100
100
  expect(await coreRule.exists()).toBe(true)
101
101
 
102
102
  const content = await coreRule.text()
@@ -109,15 +109,15 @@ describe('scaffold-rules', () => {
109
109
  const expectedRules = ['accuracy', 'bun', 'core', 'documentation', 'modules', 'testing', 'workflow']
110
110
 
111
111
  for (const rule of expectedRules) {
112
- const ruleFile = Bun.file(join(testDir, `.plaited/rules/${rule}.md`))
112
+ const ruleFile = Bun.file(join(testDir, `.agents/rules/${rule}.md`))
113
113
  expect(await ruleFile.exists()).toBe(true)
114
114
  }
115
115
  })
116
116
 
117
- test('creates .plaited/rules directory if missing', async () => {
117
+ test('creates .agents/rules directory if missing', async () => {
118
118
  await $`cd ${testDir} && bun ${binDir}/cli.ts scaffold-rules`.quiet()
119
119
 
120
- const rulesDir = await Bun.file(join(testDir, '.plaited/rules/core.md')).exists()
120
+ const rulesDir = await Bun.file(join(testDir, '.agents/rules/core.md')).exists()
121
121
  expect(rulesDir).toBe(true)
122
122
  })
123
123
  })
@@ -131,7 +131,7 @@ describe('scaffold-rules', () => {
131
131
 
132
132
  // Check symlink exists and points to right place
133
133
  const linkTarget = await readlink(join(testDir, '.claude/rules'))
134
- expect(linkTarget).toBe('../.plaited/rules')
134
+ expect(linkTarget).toBe('../.agents/rules')
135
135
  })
136
136
 
137
137
  test('creates symlink for .cursor/rules when .cursor exists', async () => {
@@ -141,7 +141,7 @@ describe('scaffold-rules', () => {
141
141
  await $`cd ${testDir} && bun ${binDir}/cli.ts scaffold-rules`.quiet()
142
142
 
143
143
  const linkTarget = await readlink(join(testDir, '.cursor/rules'))
144
- expect(linkTarget).toBe('../.plaited/rules')
144
+ expect(linkTarget).toBe('../.agents/rules')
145
145
  })
146
146
 
147
147
  test('skips symlink if already exists with correct target', async () => {
@@ -174,22 +174,22 @@ describe('scaffold-rules', () => {
174
174
 
175
175
  const content = await Bun.file(join(testDir, 'AGENTS.md')).text()
176
176
  expect(content).toContain('## Rules')
177
- expect(content).toContain('.plaited/rules/')
177
+ expect(content).toContain('.agents/rules/')
178
178
  })
179
179
 
180
180
  test('does not append if agent dir exists', async () => {
181
181
  await Bun.write(join(testDir, 'AGENTS.md'), '# AGENTS\n\nSome content\n')
182
- await mkdir(join(testDir, '.plaited'), { recursive: true })
182
+ await mkdir(join(testDir, '.agents'), { recursive: true })
183
183
 
184
184
  await $`cd ${testDir} && bun ${binDir}/cli.ts scaffold-rules`.quiet()
185
185
 
186
186
  const content = await Bun.file(join(testDir, 'AGENTS.md')).text()
187
- // Should not have appended rules section (since .plaited exists)
187
+ // Should not have appended rules section (since .agents exists)
188
188
  expect(content).not.toContain('## Rules')
189
189
  })
190
190
 
191
191
  test('skips if AGENTS.md already has rules', async () => {
192
- await Bun.write(join(testDir, 'AGENTS.md'), '# AGENTS\n\nSee .plaited/rules/ for rules\n')
192
+ await Bun.write(join(testDir, 'AGENTS.md'), '# AGENTS\n\nSee .agents/rules/ for rules\n')
193
193
 
194
194
  const result: ScaffoldOutput = await $`cd ${testDir} && bun ${binDir}/cli.ts scaffold-rules`.json()
195
195
 
@@ -207,7 +207,7 @@ describe('scaffold-rules', () => {
207
207
  expect(result).toHaveProperty('actions')
208
208
 
209
209
  expect(result.dryRun).toBe(false)
210
- expect(result.targetRules).toBe('.plaited/rules')
210
+ expect(result.targetRules).toBe('.agents/rules')
211
211
  expect(result.actions).toBeArray()
212
212
  })
213
213
  })
@@ -248,7 +248,7 @@ describe('scaffold-rules', () => {
248
248
  test('core.md contains TypeScript conventions', async () => {
249
249
  await $`cd ${testDir} && bun ${binDir}/cli.ts scaffold-rules`.quiet()
250
250
 
251
- const content = await Bun.file(join(testDir, '.plaited/rules/core.md')).text()
251
+ const content = await Bun.file(join(testDir, '.agents/rules/core.md')).text()
252
252
  expect(content).toContain('Type over interface')
253
253
  expect(content).toContain('Arrow functions')
254
254
  })
@@ -256,7 +256,7 @@ describe('scaffold-rules', () => {
256
256
  test('testing.md contains test conventions', async () => {
257
257
  await $`cd ${testDir} && bun ${binDir}/cli.ts scaffold-rules`.quiet()
258
258
 
259
- const content = await Bun.file(join(testDir, '.plaited/rules/testing.md')).text()
259
+ const content = await Bun.file(join(testDir, '.agents/rules/testing.md')).text()
260
260
  expect(content).toContain('test not it')
261
261
  expect(content).toContain('No conditional assertions')
262
262
  })
@@ -264,7 +264,7 @@ describe('scaffold-rules', () => {
264
264
  test('rules include verification patterns', async () => {
265
265
  await $`cd ${testDir} && bun ${binDir}/cli.ts scaffold-rules`.quiet()
266
266
 
267
- const content = await Bun.file(join(testDir, '.plaited/rules/core.md')).text()
267
+ const content = await Bun.file(join(testDir, '.agents/rules/core.md')).text()
268
268
  expect(content).toContain('*Verify:*')
269
269
  expect(content).toContain('*Fix:*')
270
270
  })
@@ -272,7 +272,7 @@ describe('scaffold-rules', () => {
272
272
  test('rules are compressed (no verbose examples)', async () => {
273
273
  await $`cd ${testDir} && bun ${binDir}/cli.ts scaffold-rules`.quiet()
274
274
 
275
- const content = await Bun.file(join(testDir, '.plaited/rules/core.md')).text()
275
+ const content = await Bun.file(join(testDir, '.agents/rules/core.md')).text()
276
276
 
277
277
  // Should not have verbose code blocks with Good/Avoid patterns
278
278
  expect(content).not.toContain('// ✅ Good')
File without changes
File without changes
File without changes
File without changes
File without changes