@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.
- package/{.plaited → .agents}/rules/accuracy.md +1 -1
- package/{.plaited → .agents}/rules/testing.md +1 -1
- package/{.plaited → .agents}/skills/scaffold-rules/SKILL.md +8 -8
- package/README.md +3 -3
- package/package.json +2 -3
- package/src/scaffold-rules.ts +13 -13
- package/src/tests/resolve-file-path.spec.ts +0 -21
- package/src/tests/scaffold-rules.spec.ts +18 -18
- /package/{.plaited → .agents}/rules/bun.md +0 -0
- /package/{.plaited → .agents}/rules/core.md +0 -0
- /package/{.plaited → .agents}/rules/documentation.md +0 -0
- /package/{.plaited → .agents}/rules/modules.md +0 -0
- /package/{.plaited → .agents}/rules/workflow.md +0 -0
- /package/{.plaited → .agents}/skills/code-documentation/SKILL.md +0 -0
- /package/{.plaited → .agents}/skills/code-documentation/references/internal-templates.md +0 -0
- /package/{.plaited → .agents}/skills/code-documentation/references/maintenance.md +0 -0
- /package/{.plaited → .agents}/skills/code-documentation/references/public-api-templates.md +0 -0
- /package/{.plaited → .agents}/skills/code-documentation/references/type-documentation.md +0 -0
- /package/{.plaited → .agents}/skills/code-documentation/references/workflow.md +0 -0
- /package/{.plaited → .agents}/skills/optimize-agents-md/SKILL.md +0 -0
- /package/{.plaited → .agents}/skills/typescript-lsp/SKILL.md +0 -0
- /package/{.plaited → .agents}/skills/validate-skill/SKILL.md +0 -0
|
@@ -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
|
|
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 `.
|
|
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
|
-
.
|
|
55
|
+
.agents/rules/ ← Canonical location (files copied here)
|
|
56
56
|
├── testing.md
|
|
57
57
|
├── bun.md
|
|
58
58
|
└── ...
|
|
59
59
|
|
|
60
|
-
.claude/rules -> ../.
|
|
61
|
-
.cursor/rules -> ../.
|
|
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
|
-
| `.
|
|
66
|
+
| `.agents/` only | ✓ | None | No |
|
|
67
67
|
| `.claude/` only | ✓ | `.claude/rules` | No |
|
|
68
68
|
| `.cursor/` only | ✓ | `.cursor/rules` | No |
|
|
69
|
-
| `.
|
|
70
|
-
| `.
|
|
71
|
-
| `.
|
|
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
|
-
|
|
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.
|
|
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
|
-
".
|
|
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",
|
package/src/scaffold-rules.ts
CHANGED
|
@@ -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 `.
|
|
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 .
|
|
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 .
|
|
22
|
-
const ALL_AGENTS = ['.
|
|
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 = '.
|
|
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 -> ../.
|
|
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, '../.
|
|
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 .
|
|
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 .
|
|
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 === '../.
|
|
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('../.
|
|
120
|
+
await symlink('../.agents/rules', rulesLink)
|
|
121
121
|
}
|
|
122
|
-
actions.push(`symlink: ${agent}/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('.
|
|
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('.
|
|
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, '.
|
|
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 .
|
|
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, '.
|
|
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, `.
|
|
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 .
|
|
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, '.
|
|
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('../.
|
|
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('../.
|
|
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('.
|
|
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, '.
|
|
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 .
|
|
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 .
|
|
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('.
|
|
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, '.
|
|
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, '.
|
|
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, '.
|
|
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, '.
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|