@plaited/development-skills 0.5.0 → 0.6.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/{.claude → .plaited}/rules/accuracy.md +3 -10
- package/{.claude → .plaited}/rules/code-review.md +3 -10
- package/.plaited/rules/documentation.md +41 -0
- package/.plaited/rules/git-workflow.md +31 -0
- package/{.claude → .plaited}/rules/github.md +4 -4
- package/{.claude → .plaited}/rules/module-organization.md +0 -7
- package/{.claude → .plaited}/rules/testing.md +0 -5
- package/package.json +2 -2
- package/src/lsp-analyze.ts +1 -1
- package/src/lsp-find.ts +1 -1
- package/src/lsp-hover.ts +1 -1
- package/src/lsp-references.ts +1 -1
- package/src/lsp-symbols.ts +1 -1
- package/src/scaffold-rules.ts +146 -205
- package/src/tests/scaffold-rules.spec.ts +239 -110
- package/.claude/commands/lsp-analyze.md +0 -66
- package/.claude/commands/lsp-find.md +0 -74
- package/.claude/commands/lsp-hover.md +0 -57
- package/.claude/commands/lsp-refs.md +0 -64
- 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 -104
- package/.claude/skills/typescript-lsp/SKILL.md +0 -249
- package/.claude/skills/validate-skill/SKILL.md +0 -105
- /package/{.claude → .plaited}/rules/bun-apis.md +0 -0
|
@@ -9,12 +9,9 @@ type Template = {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
type ScaffoldOutput = {
|
|
12
|
-
agent: string
|
|
13
12
|
rulesPath: string
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
supportsAgentsMd: boolean
|
|
17
|
-
agentsMdContent?: string
|
|
13
|
+
agentsMdSection: string
|
|
14
|
+
claudeMdReference: string
|
|
18
15
|
templates: Record<string, Template>
|
|
19
16
|
}
|
|
20
17
|
|
|
@@ -22,7 +19,7 @@ const binDir = join(import.meta.dir, '../../bin')
|
|
|
22
19
|
|
|
23
20
|
describe('scaffold-rules', () => {
|
|
24
21
|
test('outputs JSON with all templates', async () => {
|
|
25
|
-
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules
|
|
22
|
+
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules`.json()
|
|
26
23
|
|
|
27
24
|
expect(result).toHaveProperty('templates')
|
|
28
25
|
expect(result.templates).toBeObject()
|
|
@@ -35,10 +32,11 @@ describe('scaffold-rules', () => {
|
|
|
35
32
|
expect(templateKeys).toContain('git-workflow')
|
|
36
33
|
expect(templateKeys).toContain('github')
|
|
37
34
|
expect(templateKeys).toContain('testing')
|
|
35
|
+
expect(templateKeys).toContain('module-organization')
|
|
38
36
|
})
|
|
39
37
|
|
|
40
38
|
test('each template has required properties', async () => {
|
|
41
|
-
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules
|
|
39
|
+
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules`.json()
|
|
42
40
|
|
|
43
41
|
for (const [ruleId, template] of Object.entries(result.templates)) {
|
|
44
42
|
expect(template).toHaveProperty('filename')
|
|
@@ -52,7 +50,7 @@ describe('scaffold-rules', () => {
|
|
|
52
50
|
})
|
|
53
51
|
|
|
54
52
|
test('removes template headers from content', async () => {
|
|
55
|
-
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules
|
|
53
|
+
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules`.json()
|
|
56
54
|
|
|
57
55
|
// Check that template headers are removed
|
|
58
56
|
for (const template of Object.values(result.templates)) {
|
|
@@ -62,7 +60,7 @@ describe('scaffold-rules', () => {
|
|
|
62
60
|
})
|
|
63
61
|
|
|
64
62
|
test('processes development-skills conditionals', async () => {
|
|
65
|
-
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules
|
|
63
|
+
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules`.json()
|
|
66
64
|
|
|
67
65
|
const accuracy = result.templates.accuracy
|
|
68
66
|
expect(accuracy).toBeDefined()
|
|
@@ -78,8 +76,7 @@ describe('scaffold-rules', () => {
|
|
|
78
76
|
})
|
|
79
77
|
|
|
80
78
|
test('filters to specific rules when requested', async () => {
|
|
81
|
-
const result: ScaffoldOutput =
|
|
82
|
-
await $`bun ${binDir}/cli.ts scaffold-rules --rules testing --rules bun-apis --format=json`.json()
|
|
79
|
+
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules --rules testing --rules bun-apis`.json()
|
|
83
80
|
|
|
84
81
|
const templateKeys = Object.keys(result.templates)
|
|
85
82
|
|
|
@@ -94,7 +91,7 @@ describe('scaffold-rules', () => {
|
|
|
94
91
|
})
|
|
95
92
|
|
|
96
93
|
test('extracts meaningful descriptions', async () => {
|
|
97
|
-
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules
|
|
94
|
+
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules`.json()
|
|
98
95
|
|
|
99
96
|
// Check a few descriptions
|
|
100
97
|
const accuracy = result.templates.accuracy
|
|
@@ -108,179 +105,311 @@ describe('scaffold-rules', () => {
|
|
|
108
105
|
expect(testing!.description.length).toBeGreaterThan(10)
|
|
109
106
|
})
|
|
110
107
|
|
|
111
|
-
test('exits with error for invalid agent', async () => {
|
|
112
|
-
const proc = Bun.spawn(['bun', `${binDir}/cli.ts`, 'scaffold-rules', '--agent=invalid'], {
|
|
113
|
-
stderr: 'pipe',
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
const exitCode = await proc.exited
|
|
117
|
-
expect(exitCode).not.toBe(0)
|
|
118
|
-
})
|
|
119
|
-
|
|
120
108
|
test('handles missing bundled rules directory gracefully', async () => {
|
|
121
109
|
// This test ensures the script fails gracefully if templates are missing
|
|
122
|
-
// In production, .
|
|
123
|
-
const result = await $`bun ${binDir}/cli.ts scaffold-rules
|
|
110
|
+
// In production, .plaited/rules/ should always be bundled with the package
|
|
111
|
+
const result = await $`bun ${binDir}/cli.ts scaffold-rules`.nothrow().quiet()
|
|
124
112
|
|
|
125
|
-
// Should succeed because .
|
|
113
|
+
// Should succeed because .plaited/rules/ exists in development
|
|
126
114
|
expect(result.exitCode).toBe(0)
|
|
127
115
|
})
|
|
128
116
|
|
|
129
|
-
describe('
|
|
130
|
-
test('defaults to
|
|
131
|
-
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules
|
|
117
|
+
describe('output structure', () => {
|
|
118
|
+
test('defaults to .plaited/rules path', async () => {
|
|
119
|
+
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules`.json()
|
|
132
120
|
|
|
133
|
-
expect(result.
|
|
134
|
-
expect(result.rulesPath).toBe('.claude/rules')
|
|
135
|
-
expect(result.format).toBe('multi-file')
|
|
136
|
-
expect(result.supportsAgentsMd).toBe(false)
|
|
121
|
+
expect(result.rulesPath).toBe('.plaited/rules')
|
|
137
122
|
})
|
|
138
123
|
|
|
139
|
-
test('
|
|
140
|
-
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules
|
|
124
|
+
test('includes claudeMdReference with markers and @AGENTS.md', async () => {
|
|
125
|
+
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules`.json()
|
|
141
126
|
|
|
142
|
-
|
|
143
|
-
expect(
|
|
127
|
+
expect(result.claudeMdReference).toContain('<!-- PLAITED-RULES-START -->')
|
|
128
|
+
expect(result.claudeMdReference).toContain('<!-- PLAITED-RULES-END -->')
|
|
129
|
+
expect(result.claudeMdReference).toContain('@AGENTS.md')
|
|
130
|
+
expect(result.claudeMdReference).toContain('## Project Rules')
|
|
131
|
+
})
|
|
144
132
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
expect(gitWorkflow!.content).toContain('single-quoted strings')
|
|
133
|
+
test('includes agentsMdSection with markers and dual format', async () => {
|
|
134
|
+
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules`.json()
|
|
148
135
|
|
|
149
|
-
|
|
150
|
-
expect(
|
|
151
|
-
|
|
136
|
+
expect(result.agentsMdSection).toContain('<!-- PLAITED-RULES-START -->')
|
|
137
|
+
expect(result.agentsMdSection).toContain('<!-- PLAITED-RULES-END -->')
|
|
138
|
+
// Should have @ syntax for Claude Code
|
|
139
|
+
expect(result.agentsMdSection).toContain('@.plaited/rules/')
|
|
140
|
+
// Should have markdown links for other tools
|
|
141
|
+
expect(result.agentsMdSection).toContain('](.plaited/rules/')
|
|
142
|
+
expect(result.agentsMdSection).toContain('## Rules')
|
|
152
143
|
})
|
|
153
144
|
|
|
154
|
-
test('
|
|
155
|
-
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules
|
|
145
|
+
test('claudeMdReference does not list individual rules', async () => {
|
|
146
|
+
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules`.json()
|
|
156
147
|
|
|
157
|
-
|
|
158
|
-
expect(
|
|
159
|
-
|
|
160
|
-
// Claude supports slash commands
|
|
161
|
-
expect(accuracy!.content).toContain('/lsp-hover')
|
|
162
|
-
expect(accuracy!.content).toContain('/lsp-find')
|
|
148
|
+
// claudeMdReference should be a simple reference to AGENTS.md, not a list of rules
|
|
149
|
+
expect(result.claudeMdReference).not.toContain('.plaited/rules/')
|
|
150
|
+
expect(result.claudeMdReference).toContain('@AGENTS.md')
|
|
163
151
|
})
|
|
164
152
|
|
|
165
|
-
test('
|
|
166
|
-
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules
|
|
153
|
+
test('agentsMdSection lists all selected rules in dual format', async () => {
|
|
154
|
+
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules`.json()
|
|
167
155
|
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
156
|
+
for (const [ruleId, template] of Object.entries(result.templates)) {
|
|
157
|
+
// Each rule should have both @ syntax and markdown link
|
|
158
|
+
expect(result.agentsMdSection).toContain(`@.plaited/rules/${template.filename}`)
|
|
159
|
+
expect(result.agentsMdSection).toContain(`[${ruleId}](.plaited/rules/${template.filename})`)
|
|
160
|
+
}
|
|
172
161
|
})
|
|
173
162
|
})
|
|
174
163
|
|
|
175
|
-
describe('
|
|
176
|
-
test('
|
|
177
|
-
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules
|
|
164
|
+
describe('claudeMdReference behavior', () => {
|
|
165
|
+
test('has exact expected content structure', async () => {
|
|
166
|
+
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules`.json()
|
|
167
|
+
|
|
168
|
+
// Verify the exact structure of claudeMdReference
|
|
169
|
+
const lines = result.claudeMdReference.split('\n')
|
|
170
|
+
expect(lines[0]).toBe('<!-- PLAITED-RULES-START -->')
|
|
171
|
+
expect(lines[1]).toBe('')
|
|
172
|
+
expect(lines[2]).toBe('## Project Rules')
|
|
173
|
+
expect(lines[3]).toBe('')
|
|
174
|
+
expect(lines[4]).toBe('See @AGENTS.md for shared development rules.')
|
|
175
|
+
expect(lines[5]).toBe('')
|
|
176
|
+
expect(lines[6]).toBe('<!-- PLAITED-RULES-END -->')
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
test('is constant regardless of rules selected', async () => {
|
|
180
|
+
const allRules: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules`.json()
|
|
181
|
+
const oneRule: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules --rules testing`.json()
|
|
182
|
+
const twoRules: ScaffoldOutput =
|
|
183
|
+
await $`bun ${binDir}/cli.ts scaffold-rules --rules testing --rules accuracy`.json()
|
|
178
184
|
|
|
179
|
-
|
|
180
|
-
expect(
|
|
181
|
-
expect(
|
|
182
|
-
expect(result.supportsAgentsMd).toBe(true)
|
|
185
|
+
// claudeMdReference should be identical in all cases
|
|
186
|
+
expect(oneRule.claudeMdReference).toBe(allRules.claudeMdReference)
|
|
187
|
+
expect(twoRules.claudeMdReference).toBe(allRules.claudeMdReference)
|
|
183
188
|
})
|
|
184
189
|
|
|
185
|
-
test('
|
|
186
|
-
const
|
|
190
|
+
test('is constant regardless of rules-dir', async () => {
|
|
191
|
+
const defaultPath: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules`.json()
|
|
192
|
+
const customPath: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules --rules-dir=.cursor/rules`.json()
|
|
193
|
+
const anotherPath: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules --rules-dir=custom/path`.json()
|
|
187
194
|
|
|
188
|
-
|
|
189
|
-
expect(
|
|
190
|
-
expect(
|
|
191
|
-
expect(result.agentsMdContent).toContain('## Rules')
|
|
195
|
+
// claudeMdReference should be identical regardless of rules-dir
|
|
196
|
+
expect(customPath.claudeMdReference).toBe(defaultPath.claudeMdReference)
|
|
197
|
+
expect(anotherPath.claudeMdReference).toBe(defaultPath.claudeMdReference)
|
|
192
198
|
})
|
|
193
199
|
|
|
194
|
-
test('
|
|
195
|
-
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules
|
|
200
|
+
test('uses shared markers with agentsMdSection', async () => {
|
|
201
|
+
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules`.json()
|
|
196
202
|
|
|
197
|
-
|
|
203
|
+
// Both sections should use the same markers for consistency
|
|
204
|
+
const startMarker = '<!-- PLAITED-RULES-START -->'
|
|
205
|
+
const endMarker = '<!-- PLAITED-RULES-END -->'
|
|
198
206
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
207
|
+
expect(result.claudeMdReference).toContain(startMarker)
|
|
208
|
+
expect(result.claudeMdReference).toContain(endMarker)
|
|
209
|
+
expect(result.agentsMdSection).toContain(startMarker)
|
|
210
|
+
expect(result.agentsMdSection).toContain(endMarker)
|
|
211
|
+
})
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
describe('agentsMdSection with filtered rules', () => {
|
|
215
|
+
test('only includes selected rules in dual format', async () => {
|
|
216
|
+
const result: ScaffoldOutput =
|
|
217
|
+
await $`bun ${binDir}/cli.ts scaffold-rules --rules testing --rules bun-apis`.json()
|
|
218
|
+
|
|
219
|
+
// Should contain selected rules (both @ and markdown formats)
|
|
220
|
+
expect(result.agentsMdSection).toContain('@.plaited/rules/testing.md')
|
|
221
|
+
expect(result.agentsMdSection).toContain('[testing]')
|
|
222
|
+
expect(result.agentsMdSection).toContain('@.plaited/rules/bun-apis.md')
|
|
223
|
+
expect(result.agentsMdSection).toContain('[bun-apis]')
|
|
224
|
+
|
|
225
|
+
// Should NOT contain other rules
|
|
226
|
+
expect(result.agentsMdSection).not.toContain('@.plaited/rules/accuracy.md')
|
|
227
|
+
expect(result.agentsMdSection).not.toContain('[accuracy]')
|
|
228
|
+
expect(result.agentsMdSection).not.toContain('@.plaited/rules/git-workflow.md')
|
|
203
229
|
})
|
|
204
230
|
|
|
205
|
-
test('
|
|
206
|
-
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules --
|
|
231
|
+
test('has valid structure with single rule in dual format', async () => {
|
|
232
|
+
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules --rules testing`.json()
|
|
233
|
+
|
|
234
|
+
expect(result.agentsMdSection).toContain('<!-- PLAITED-RULES-START -->')
|
|
235
|
+
expect(result.agentsMdSection).toContain('## Rules')
|
|
236
|
+
// Both formats present
|
|
237
|
+
expect(result.agentsMdSection).toContain('@.plaited/rules/testing.md')
|
|
238
|
+
expect(result.agentsMdSection).toContain('[testing](.plaited/rules/testing.md)')
|
|
239
|
+
expect(result.agentsMdSection).toContain('<!-- PLAITED-RULES-END -->')
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
test('has empty rules list when no rules match', async () => {
|
|
243
|
+
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules --rules nonexistent`.nothrow().json()
|
|
244
|
+
|
|
245
|
+
// Should still have valid structure with markers
|
|
246
|
+
expect(result.agentsMdSection).toContain('<!-- PLAITED-RULES-START -->')
|
|
247
|
+
expect(result.agentsMdSection).toContain('## Rules')
|
|
248
|
+
expect(result.agentsMdSection).toContain('<!-- PLAITED-RULES-END -->')
|
|
249
|
+
|
|
250
|
+
// But no rule references (neither @ nor markdown)
|
|
251
|
+
expect(result.agentsMdSection).not.toMatch(/@\.plaited\/rules\/.*\.md/)
|
|
252
|
+
expect(result.agentsMdSection).not.toMatch(/\[.*\]\(\.plaited\/rules\/.*\.md\)/)
|
|
253
|
+
})
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
describe('template content', () => {
|
|
257
|
+
test('git-workflow uses standard commit format', async () => {
|
|
258
|
+
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules`.json()
|
|
207
259
|
|
|
208
260
|
const gitWorkflow = result.templates['git-workflow']
|
|
209
261
|
expect(gitWorkflow).toBeDefined()
|
|
210
|
-
expect(gitWorkflow!.content).not.toContain('sandbox environment')
|
|
211
262
|
expect(gitWorkflow!.content).toContain('multi-line commit')
|
|
263
|
+
expect(gitWorkflow!.content).toContain('git commit -m')
|
|
212
264
|
})
|
|
213
265
|
|
|
214
|
-
test('
|
|
215
|
-
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules
|
|
266
|
+
test('accuracy uses CLI syntax for LSP tools', async () => {
|
|
267
|
+
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules`.json()
|
|
216
268
|
|
|
217
269
|
const accuracy = result.templates.accuracy
|
|
218
270
|
expect(accuracy).toBeDefined()
|
|
219
271
|
|
|
220
|
-
// Should use CLI
|
|
272
|
+
// Should use CLI syntax
|
|
221
273
|
expect(accuracy!.content).toContain('bunx @plaited/development-skills lsp-')
|
|
222
|
-
expect(accuracy!.content).not.toContain('/lsp-hover')
|
|
223
274
|
})
|
|
224
275
|
|
|
225
|
-
test('
|
|
226
|
-
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules
|
|
276
|
+
test('cross-references use path syntax', async () => {
|
|
277
|
+
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules`.json()
|
|
227
278
|
|
|
228
279
|
const accuracy = result.templates.accuracy
|
|
229
280
|
expect(accuracy).toBeDefined()
|
|
230
281
|
expect(accuracy!.content).toContain('.plaited/rules/testing.md')
|
|
282
|
+
expect(accuracy!.content).not.toContain('{{LINK:testing}}')
|
|
231
283
|
})
|
|
232
284
|
})
|
|
233
285
|
|
|
234
286
|
describe('path customization', () => {
|
|
235
|
-
test('
|
|
236
|
-
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules --
|
|
287
|
+
test('--rules-dir overrides default rules path', async () => {
|
|
288
|
+
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules --rules-dir=.cursor/rules`.json()
|
|
237
289
|
|
|
238
|
-
expect(result.
|
|
290
|
+
expect(result.rulesPath).toBe('.cursor/rules')
|
|
239
291
|
})
|
|
240
292
|
|
|
241
|
-
test('
|
|
242
|
-
const result: ScaffoldOutput =
|
|
243
|
-
await $`bun ${binDir}/cli.ts scaffold-rules --agent=agents-md --rules-dir=.cursor/rules --format=json`.json()
|
|
293
|
+
test('claudeMdReference is path-independent', async () => {
|
|
294
|
+
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules --rules-dir=.cursor/rules`.json()
|
|
244
295
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
expect(result.
|
|
248
|
-
expect(result.agentsMdContent).not.toContain('.plaited/rules/')
|
|
296
|
+
// claudeMdReference always references AGENTS.md regardless of rules path
|
|
297
|
+
expect(result.claudeMdReference).toContain('@AGENTS.md')
|
|
298
|
+
expect(result.claudeMdReference).not.toContain('.cursor/rules')
|
|
249
299
|
})
|
|
250
300
|
|
|
251
|
-
test('
|
|
252
|
-
const result: ScaffoldOutput =
|
|
253
|
-
await $`bun ${binDir}/cli.ts scaffold-rules --agent=agents-md --agents-md-path=docs/AGENTS.md --format=json`.json()
|
|
301
|
+
test('agentsMdSection uses custom path in both formats', async () => {
|
|
302
|
+
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules --rules-dir=.cursor/rules`.json()
|
|
254
303
|
|
|
255
|
-
|
|
304
|
+
// Both @ syntax and markdown links should use custom path
|
|
305
|
+
expect(result.agentsMdSection).toContain('@.cursor/rules/')
|
|
306
|
+
expect(result.agentsMdSection).toContain('](.cursor/rules/')
|
|
307
|
+
expect(result.agentsMdSection).not.toContain('.plaited/rules/')
|
|
256
308
|
})
|
|
257
309
|
|
|
258
310
|
test('cross-references use custom rules-dir', async () => {
|
|
259
|
-
const result: ScaffoldOutput =
|
|
260
|
-
await $`bun ${binDir}/cli.ts scaffold-rules --agent=agents-md --rules-dir=.factory/rules --format=json`.json()
|
|
311
|
+
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules --rules-dir=.factory/rules`.json()
|
|
261
312
|
|
|
262
313
|
const accuracy = result.templates.accuracy
|
|
263
314
|
expect(accuracy).toBeDefined()
|
|
264
315
|
expect(accuracy!.content).toContain('.factory/rules/testing.md')
|
|
265
316
|
})
|
|
266
317
|
|
|
267
|
-
test('short
|
|
268
|
-
const result: ScaffoldOutput =
|
|
269
|
-
await $`bun ${binDir}/cli.ts scaffold-rules --agent=agents-md -d custom/rules -m custom/AGENTS.md --format=json`.json()
|
|
318
|
+
test('short flag -d works', async () => {
|
|
319
|
+
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules -d custom/rules`.json()
|
|
270
320
|
|
|
271
321
|
expect(result.rulesPath).toBe('custom/rules')
|
|
272
|
-
expect(result.agentsMdPath).toBe('custom/AGENTS.md')
|
|
273
322
|
})
|
|
323
|
+
})
|
|
274
324
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
325
|
+
describe('--list flag', () => {
|
|
326
|
+
test('outputs array of available rules', async () => {
|
|
327
|
+
const result = await $`bun ${binDir}/cli.ts scaffold-rules --list`.json()
|
|
328
|
+
|
|
329
|
+
expect(result).toBeArray()
|
|
330
|
+
expect(result.length).toBeGreaterThan(0)
|
|
331
|
+
|
|
332
|
+
// Each entry should have id and filename
|
|
333
|
+
for (const rule of result) {
|
|
334
|
+
expect(rule).toHaveProperty('id')
|
|
335
|
+
expect(rule).toHaveProperty('filename')
|
|
336
|
+
expect(rule.filename).toBe(`${rule.id}.md`)
|
|
337
|
+
}
|
|
338
|
+
})
|
|
339
|
+
|
|
340
|
+
test('includes expected rules', async () => {
|
|
341
|
+
const result = await $`bun ${binDir}/cli.ts scaffold-rules --list`.json()
|
|
342
|
+
const ids = result.map((r: { id: string }) => r.id)
|
|
343
|
+
|
|
344
|
+
expect(ids).toContain('accuracy')
|
|
345
|
+
expect(ids).toContain('testing')
|
|
346
|
+
expect(ids).toContain('bun-apis')
|
|
347
|
+
})
|
|
348
|
+
|
|
349
|
+
test('short flag -l works', async () => {
|
|
350
|
+
const result = await $`bun ${binDir}/cli.ts scaffold-rules -l`.json()
|
|
351
|
+
|
|
352
|
+
expect(result).toBeArray()
|
|
353
|
+
expect(result.length).toBeGreaterThan(0)
|
|
354
|
+
})
|
|
355
|
+
})
|
|
356
|
+
|
|
357
|
+
describe('--rules validation', () => {
|
|
358
|
+
test('warns about unknown rules', async () => {
|
|
359
|
+
const result = await $`bun ${binDir}/cli.ts scaffold-rules --rules nonexistent --rules testing`.nothrow()
|
|
360
|
+
|
|
361
|
+
// Should still succeed but with warning
|
|
362
|
+
expect(result.exitCode).toBe(0)
|
|
363
|
+
expect(result.stderr.toString()).toContain('Warning: Unknown rules: nonexistent')
|
|
364
|
+
})
|
|
365
|
+
|
|
366
|
+
test('shows available rules in warning', async () => {
|
|
367
|
+
const result = await $`bun ${binDir}/cli.ts scaffold-rules --rules fake-rule`.nothrow()
|
|
368
|
+
|
|
369
|
+
expect(result.stderr.toString()).toContain('Available rules:')
|
|
370
|
+
expect(result.stderr.toString()).toContain('testing')
|
|
371
|
+
})
|
|
372
|
+
})
|
|
373
|
+
|
|
374
|
+
describe('edge cases', () => {
|
|
375
|
+
test('handles all rules filtered out gracefully', async () => {
|
|
376
|
+
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules --rules nonexistent`.nothrow().json()
|
|
377
|
+
|
|
378
|
+
// Should return valid output with empty templates
|
|
379
|
+
expect(result.templates).toEqual({})
|
|
380
|
+
expect(result.claudeMdReference).toContain('<!-- PLAITED-RULES-START -->')
|
|
381
|
+
expect(result.claudeMdReference).toContain('<!-- PLAITED-RULES-END -->')
|
|
382
|
+
})
|
|
383
|
+
|
|
384
|
+
test('description extraction falls back for heading-only content', async () => {
|
|
385
|
+
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules`.json()
|
|
386
|
+
|
|
387
|
+
// All templates should have non-empty descriptions
|
|
388
|
+
for (const template of Object.values(result.templates)) {
|
|
389
|
+
expect(template.description).toBeTruthy()
|
|
390
|
+
expect(template.description.length).toBeGreaterThan(0)
|
|
391
|
+
}
|
|
392
|
+
})
|
|
393
|
+
|
|
394
|
+
test('processes nested conditionals correctly', async () => {
|
|
395
|
+
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules --rules accuracy`.json()
|
|
278
396
|
|
|
279
|
-
expect(result.rulesPath).toBe('.my-rules')
|
|
280
|
-
// Cross-references should use custom path
|
|
281
397
|
const accuracy = result.templates.accuracy
|
|
282
398
|
expect(accuracy).toBeDefined()
|
|
283
|
-
|
|
399
|
+
|
|
400
|
+
// Should not contain any unprocessed conditional syntax
|
|
401
|
+
expect(accuracy!.content).not.toContain('{{#if')
|
|
402
|
+
expect(accuracy!.content).not.toContain('{{^if')
|
|
403
|
+
expect(accuracy!.content).not.toContain('{{/if}}')
|
|
404
|
+
})
|
|
405
|
+
|
|
406
|
+
test('template content has no excessive blank lines', async () => {
|
|
407
|
+
const result: ScaffoldOutput = await $`bun ${binDir}/cli.ts scaffold-rules`.json()
|
|
408
|
+
|
|
409
|
+
for (const template of Object.values(result.templates)) {
|
|
410
|
+
// Should not have 3+ consecutive newlines
|
|
411
|
+
expect(template.content).not.toMatch(/\n{3,}/)
|
|
412
|
+
}
|
|
284
413
|
})
|
|
285
414
|
})
|
|
286
415
|
})
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Analyze a TypeScript file structure, exports, and symbols
|
|
3
|
-
allowed-tools: Bash
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# LSP Analyze
|
|
7
|
-
|
|
8
|
-
Batch analysis of a TypeScript/JavaScript file. Get an overview of exports, symbols, and optionally type info at specific positions.
|
|
9
|
-
|
|
10
|
-
**Arguments:** $ARGUMENTS
|
|
11
|
-
|
|
12
|
-
## Usage
|
|
13
|
-
|
|
14
|
-
```
|
|
15
|
-
/lsp-analyze <file> [options]
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
Options:
|
|
19
|
-
- `--symbols` or `-s`: List all symbols
|
|
20
|
-
- `--exports` or `-e`: List only exported symbols (default if no options)
|
|
21
|
-
- `--hover <line:char>`: Get type info at position (repeatable)
|
|
22
|
-
- `--refs <line:char>`: Find references at position (repeatable)
|
|
23
|
-
- `--all`: Run symbols + exports analysis
|
|
24
|
-
|
|
25
|
-
## Instructions
|
|
26
|
-
|
|
27
|
-
### Step 1: Parse Arguments
|
|
28
|
-
|
|
29
|
-
Extract file path and options from `$ARGUMENTS`.
|
|
30
|
-
|
|
31
|
-
If file is missing, show usage:
|
|
32
|
-
```
|
|
33
|
-
Usage: /lsp-analyze <file> [options]
|
|
34
|
-
|
|
35
|
-
Examples:
|
|
36
|
-
/lsp-analyze src/utils/parser.ts --exports
|
|
37
|
-
/lsp-analyze src/lib/config.ts --all
|
|
38
|
-
/lsp-analyze src/index.ts --hover 50:10 --refs 60:5
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
Default to `--exports` if no options provided.
|
|
42
|
-
|
|
43
|
-
### Step 2: Run LSP Analyze
|
|
44
|
-
|
|
45
|
-
Execute the development-skills CLI command:
|
|
46
|
-
```bash
|
|
47
|
-
bunx @plaited/development-skills lsp-analyze <file> [options]
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
### Step 3: Format Output
|
|
51
|
-
|
|
52
|
-
Parse the JSON output and present in a structured format:
|
|
53
|
-
|
|
54
|
-
**File:** `<file>`
|
|
55
|
-
|
|
56
|
-
**Exports:**
|
|
57
|
-
| Name | Kind | Line |
|
|
58
|
-
|------|------|------|
|
|
59
|
-
| ... | ... | ... |
|
|
60
|
-
|
|
61
|
-
**Symbols:** (if requested)
|
|
62
|
-
| Name | Kind | Line |
|
|
63
|
-
|------|------|------|
|
|
64
|
-
| ... | ... | ... |
|
|
65
|
-
|
|
66
|
-
For hover/refs results, format as described in the individual commands.
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Search for TypeScript SYMBOLS (functions, types, classes) - NOT text
|
|
3
|
-
allowed-tools: Bash
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# LSP Find
|
|
7
|
-
|
|
8
|
-
Search for TypeScript **symbols** (functions, types, classes, variables) across the codebase.
|
|
9
|
-
|
|
10
|
-
**Arguments:** $ARGUMENTS
|
|
11
|
-
|
|
12
|
-
## When to Use Each Tool
|
|
13
|
-
|
|
14
|
-
| Tool | Purpose |
|
|
15
|
-
|------|---------|
|
|
16
|
-
| **Glob** | Find files by pattern |
|
|
17
|
-
| **Grep** | Search text content |
|
|
18
|
-
| **lsp-find** | Search TypeScript symbols |
|
|
19
|
-
| **lsp-hover** | Get type info + TSDoc documentation |
|
|
20
|
-
|
|
21
|
-
## Usage
|
|
22
|
-
|
|
23
|
-
```
|
|
24
|
-
/lsp-find <query> <context-file>
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
- `query`: TypeScript symbol name (function, type, class, variable)
|
|
28
|
-
- `context-file`: Any `.ts` file in the project for LSP context
|
|
29
|
-
|
|
30
|
-
## When NOT to Use
|
|
31
|
-
|
|
32
|
-
```
|
|
33
|
-
# ❌ WRONG: Searching for text (use Grep instead)
|
|
34
|
-
/lsp-find scaffold
|
|
35
|
-
/lsp-find TODO
|
|
36
|
-
|
|
37
|
-
# ❌ WRONG: Missing context file
|
|
38
|
-
/lsp-find createClient
|
|
39
|
-
|
|
40
|
-
# ✅ CORRECT: Symbol search with context file
|
|
41
|
-
/lsp-find createClient src/lsp-client.ts
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
## Instructions
|
|
45
|
-
|
|
46
|
-
### Step 1: Parse Arguments
|
|
47
|
-
|
|
48
|
-
Extract query and context file from `$ARGUMENTS`.
|
|
49
|
-
|
|
50
|
-
If either is missing, show usage:
|
|
51
|
-
```
|
|
52
|
-
Usage: /lsp-find <query> <context-file>
|
|
53
|
-
|
|
54
|
-
Examples:
|
|
55
|
-
/lsp-find LspClient src/lsp-client.ts
|
|
56
|
-
/lsp-find createClient src/app.ts
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
### Step 2: Run LSP Find
|
|
60
|
-
|
|
61
|
-
Execute the development-skills CLI command:
|
|
62
|
-
```bash
|
|
63
|
-
bunx @plaited/development-skills lsp-find <query> <context-file>
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
### Step 3: Format Output
|
|
67
|
-
|
|
68
|
-
Parse the JSON output and present results as a table:
|
|
69
|
-
|
|
70
|
-
| Symbol | Kind | File | Line |
|
|
71
|
-
|--------|------|------|------|
|
|
72
|
-
| ... | ... | ... | ... |
|
|
73
|
-
|
|
74
|
-
Group results by file if there are many matches. Highlight the most relevant matches (exact name matches first).
|