skillshop 2.0.0 → 2.1.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.
Files changed (62) hide show
  1. package/README.md +217 -104
  2. package/agents-content/000_communication-writing.block.md +11 -0
  3. package/agents-content/001_external-actions.block.md +9 -0
  4. package/agents-content/AGENTS.md +11 -0
  5. package/package.json +6 -3
  6. package/skills-content/architecture-scanner/SKILL.md +274 -0
  7. package/skills-content/architecture-scanner/files.json +10 -0
  8. package/skills-content/architecture-scanner/scan.sh +823 -0
  9. package/skills-content/change-review/SKILL.md +76 -0
  10. package/skills-content/change-review/agents/openai.yaml +4 -0
  11. package/skills-content/changelog/SKILL.md +99 -0
  12. package/skills-content/changeset/changeset.md +80 -0
  13. package/skills-content/code-buddy/SKILL.md +136 -0
  14. package/skills-content/design-system-catalog/SKILL.md +106 -0
  15. package/skills-content/design-system-catalog/extract-components.mjs +441 -0
  16. package/skills-content/design-system-catalog/generate-catalog.sh +255 -0
  17. package/skills-content/flare/SKILL.md +96 -0
  18. package/skills-content/grilling/SKILL.md +31 -0
  19. package/skills-content/install-sigmap/SKILL.md +97 -0
  20. package/skills-content/one-shot/SKILL.md +51 -0
  21. package/skills-content/release/SKILL.md +128 -0
  22. package/skills-content/shadow-clone/SKILL.md +228 -0
  23. package/skills-content/shadow-clone/extract-signatures.mjs +202 -0
  24. package/skills-content/shadow-clone/scan-repo.mjs +343 -0
  25. package/skills-content/ship/SKILL.md +95 -0
  26. package/skills-content/shots/SKILL.md +89 -0
  27. package/skills-content/shots/agents/openai.yaml +4 -0
  28. package/skills-content/skillshop/SKILL.md +104 -0
  29. package/skills-content/skillz/SKILL.md +15 -0
  30. package/skills-content/transcript-clean/SKILL.md +55 -0
  31. package/skills-content/transcript-edit/SKILL.md +80 -0
  32. package/skills-content/vitest/GENERATION.md +5 -0
  33. package/skills-content/vitest/SKILL.md +52 -0
  34. package/skills-content/vitest/references/advanced-environments.md +264 -0
  35. package/skills-content/vitest/references/advanced-projects.md +300 -0
  36. package/skills-content/vitest/references/advanced-type-testing.md +237 -0
  37. package/skills-content/vitest/references/advanced-vi.md +249 -0
  38. package/skills-content/vitest/references/core-cli.md +166 -0
  39. package/skills-content/vitest/references/core-config.md +174 -0
  40. package/skills-content/vitest/references/core-describe.md +193 -0
  41. package/skills-content/vitest/references/core-expect.md +219 -0
  42. package/skills-content/vitest/references/core-hooks.md +244 -0
  43. package/skills-content/vitest/references/core-test-api.md +233 -0
  44. package/skills-content/vitest/references/features-concurrency.md +250 -0
  45. package/skills-content/vitest/references/features-context.md +238 -0
  46. package/skills-content/vitest/references/features-coverage.md +207 -0
  47. package/skills-content/vitest/references/features-filtering.md +211 -0
  48. package/skills-content/vitest/references/features-mocking.md +265 -0
  49. package/skills-content/vitest/references/features-snapshots.md +207 -0
  50. package/skills-content/workleaf/SKILL.md +77 -0
  51. package/skills-content/worklog/SKILL.md +145 -0
  52. package/skills-content/worklog/references/summary-format.md +114 -0
  53. package/skills-content/worktree/SKILL.md +71 -0
  54. package/src/agents-instructions.js +219 -0
  55. package/src/cli.js +87 -23
  56. package/src/import.js +12 -7
  57. package/src/index.js +1 -0
  58. package/src/legacy-agents-content.js +36 -0
  59. package/src/legacy-cli.js +1234 -0
  60. package/src/library.js +9 -0
  61. package/src/specifier.js +11 -0
  62. package/src/user-install.js +48 -0
@@ -0,0 +1,193 @@
1
+ ---
2
+ name: describe-api
3
+ description: describe/suite for grouping tests into logical blocks
4
+ ---
5
+
6
+ # Describe API
7
+
8
+ Group related tests into suites for organization and shared setup.
9
+
10
+ ## Basic Usage
11
+
12
+ ```ts
13
+ import { describe, expect, test } from 'vitest'
14
+
15
+ describe('Math', () => {
16
+ test('adds numbers', () => {
17
+ expect(1 + 1).toBe(2)
18
+ })
19
+
20
+ test('subtracts numbers', () => {
21
+ expect(3 - 1).toBe(2)
22
+ })
23
+ })
24
+
25
+ // Alias: suite
26
+ import { suite } from 'vitest'
27
+ suite('equivalent to describe', () => {})
28
+ ```
29
+
30
+ ## Nested Suites
31
+
32
+ ```ts
33
+ describe('User', () => {
34
+ describe('when logged in', () => {
35
+ test('shows dashboard', () => {})
36
+ test('can update profile', () => {})
37
+ })
38
+
39
+ describe('when logged out', () => {
40
+ test('shows login page', () => {})
41
+ })
42
+ })
43
+ ```
44
+
45
+ ## Suite Options
46
+
47
+ ```ts
48
+ // All tests inherit options
49
+ describe('slow tests', { timeout: 30_000 }, () => {
50
+ test('test 1', () => {}) // 30s timeout
51
+ test('test 2', () => {}) // 30s timeout
52
+ })
53
+ ```
54
+
55
+ ## Suite Modifiers
56
+
57
+ ### Skip Suites
58
+
59
+ ```ts
60
+ describe.skip('skipped suite', () => {
61
+ test('wont run', () => {})
62
+ })
63
+
64
+ // Conditional
65
+ describe.skipIf(process.env.CI)('not in CI', () => {})
66
+ describe.runIf(!process.env.CI)('only local', () => {})
67
+ ```
68
+
69
+ ### Focus Suites
70
+
71
+ ```ts
72
+ describe.only('only this suite runs', () => {
73
+ test('runs', () => {})
74
+ })
75
+ ```
76
+
77
+ ### Todo Suites
78
+
79
+ ```ts
80
+ describe.todo('implement later')
81
+ ```
82
+
83
+ ### Concurrent Suites
84
+
85
+ ```ts
86
+ // All tests run in parallel
87
+ describe.concurrent('parallel tests', () => {
88
+ test('test 1', async ({ expect }) => {})
89
+ test('test 2', async ({ expect }) => {})
90
+ })
91
+ ```
92
+
93
+ ### Sequential in Concurrent
94
+
95
+ ```ts
96
+ describe.concurrent('parallel', () => {
97
+ test('concurrent 1', async () => {})
98
+
99
+ describe.sequential('must be sequential', () => {
100
+ test('step 1', async () => {})
101
+ test('step 2', async () => {})
102
+ })
103
+ })
104
+ ```
105
+
106
+ ### Shuffle Tests
107
+
108
+ ```ts
109
+ describe.shuffle('random order', () => {
110
+ test('test 1', () => {})
111
+ test('test 2', () => {})
112
+ test('test 3', () => {})
113
+ })
114
+
115
+ // Or with option
116
+ describe('random', { shuffle: true }, () => {})
117
+ ```
118
+
119
+ ## Parameterized Suites
120
+
121
+ ### describe.each
122
+
123
+ ```ts
124
+ describe.each([
125
+ { name: 'Chrome', version: 100 },
126
+ { name: 'Firefox', version: 90 },
127
+ ])('$name browser', ({ name, version }) => {
128
+ test('has version', () => {
129
+ expect(version).toBeGreaterThan(0)
130
+ })
131
+ })
132
+ ```
133
+
134
+ ### describe.for
135
+
136
+ ```ts
137
+ describe.for([
138
+ ['Chrome', 100],
139
+ ['Firefox', 90],
140
+ ])('%s browser', ([name, version]) => {
141
+ test('has version', () => {
142
+ expect(version).toBeGreaterThan(0)
143
+ })
144
+ })
145
+ ```
146
+
147
+ ## Hooks in Suites
148
+
149
+ ```ts
150
+ describe('Database', () => {
151
+ let db
152
+
153
+ beforeAll(async () => {
154
+ db = await createDb()
155
+ })
156
+
157
+ afterAll(async () => {
158
+ await db.close()
159
+ })
160
+
161
+ beforeEach(async () => {
162
+ await db.clear()
163
+ })
164
+
165
+ test('insert works', async () => {
166
+ await db.insert({ name: 'test' })
167
+ expect(await db.count()).toBe(1)
168
+ })
169
+ })
170
+ ```
171
+
172
+ ## Modifier Combinations
173
+
174
+ All modifiers can be chained:
175
+
176
+ ```ts
177
+ describe.skip.concurrent('skipped concurrent', () => {})
178
+ describe.only.shuffle('only and shuffled', () => {})
179
+ describe.concurrent.skip('equivalent', () => {})
180
+ ```
181
+
182
+ ## Key Points
183
+
184
+ - Top-level tests belong to an implicit file suite
185
+ - Nested suites inherit parent's options (timeout, retry, etc.)
186
+ - Hooks are scoped to their suite and nested suites
187
+ - Use `describe.concurrent` with context's `expect` for snapshots
188
+ - Shuffle order depends on `sequence.seed` config
189
+
190
+ <!--
191
+ Source references:
192
+ - https://vitest.dev/api/describe.html
193
+ -->
@@ -0,0 +1,219 @@
1
+ ---
2
+ name: expect-api
3
+ description: Assertions with matchers, asymmetric matchers, and custom matchers
4
+ ---
5
+
6
+ # Expect API
7
+
8
+ Vitest uses Chai assertions with Jest-compatible API.
9
+
10
+ ## Basic Assertions
11
+
12
+ ```ts
13
+ import { expect, test } from 'vitest'
14
+
15
+ test('assertions', () => {
16
+ // Equality
17
+ expect(1 + 1).toBe(2) // Strict equality (===)
18
+ expect({ a: 1 }).toEqual({ a: 1 }) // Deep equality
19
+
20
+ // Truthiness
21
+ expect(true).toBeTruthy()
22
+ expect(false).toBeFalsy()
23
+ expect(null).toBeNull()
24
+ expect(undefined).toBeUndefined()
25
+ expect('value').toBeDefined()
26
+
27
+ // Numbers
28
+ expect(10).toBeGreaterThan(5)
29
+ expect(10).toBeGreaterThanOrEqual(10)
30
+ expect(5).toBeLessThan(10)
31
+ expect(0.1 + 0.2).toBeCloseTo(0.3, 5)
32
+
33
+ // Strings
34
+ expect('hello world').toMatch(/world/)
35
+ expect('hello').toContain('ell')
36
+
37
+ // Arrays
38
+ expect([1, 2, 3]).toContain(2)
39
+ expect([{ a: 1 }]).toContainEqual({ a: 1 })
40
+ expect([1, 2, 3]).toHaveLength(3)
41
+
42
+ // Objects
43
+ expect({ a: 1, b: 2 }).toHaveProperty('a')
44
+ expect({ a: 1, b: 2 }).toHaveProperty('a', 1)
45
+ expect({ a: { b: 1 } }).toHaveProperty('a.b', 1)
46
+ expect({ a: 1 }).toMatchObject({ a: 1 })
47
+
48
+ // Types
49
+ expect('string').toBeTypeOf('string')
50
+ expect(new Date()).toBeInstanceOf(Date)
51
+ })
52
+ ```
53
+
54
+ ## Negation
55
+
56
+ ```ts
57
+ expect(1).not.toBe(2)
58
+ expect({ a: 1 }).not.toEqual({ a: 2 })
59
+ ```
60
+
61
+ ## Error Assertions
62
+
63
+ ```ts
64
+ // Sync errors - wrap in function
65
+ expect(() => throwError()).toThrow()
66
+ expect(() => throwError()).toThrow('message')
67
+ expect(() => throwError()).toThrow(/pattern/)
68
+ expect(() => throwError()).toThrow(CustomError)
69
+
70
+ // Async errors - use rejects
71
+ await expect(asyncThrow()).rejects.toThrow('error')
72
+ ```
73
+
74
+ ## Promise Assertions
75
+
76
+ ```ts
77
+ // Resolves
78
+ await expect(Promise.resolve(1)).resolves.toBe(1)
79
+ await expect(fetchData()).resolves.toEqual({ data: true })
80
+
81
+ // Rejects
82
+ await expect(Promise.reject('error')).rejects.toBe('error')
83
+ await expect(failingFetch()).rejects.toThrow()
84
+ ```
85
+
86
+ ## Spy/Mock Assertions
87
+
88
+ ```ts
89
+ const fn = vi.fn()
90
+ fn('arg1', 'arg2')
91
+ fn('arg3')
92
+
93
+ expect(fn).toHaveBeenCalled()
94
+ expect(fn).toHaveBeenCalledTimes(2)
95
+ expect(fn).toHaveBeenCalledWith('arg1', 'arg2')
96
+ expect(fn).toHaveBeenLastCalledWith('arg3')
97
+ expect(fn).toHaveBeenNthCalledWith(1, 'arg1', 'arg2')
98
+
99
+ expect(fn).toHaveReturned()
100
+ expect(fn).toHaveReturnedWith(value)
101
+ ```
102
+
103
+ ## Asymmetric Matchers
104
+
105
+ Use inside `toEqual`, `toHaveBeenCalledWith`, etc:
106
+
107
+ ```ts
108
+ expect({ id: 1, name: 'test' }).toEqual({
109
+ id: expect.any(Number),
110
+ name: expect.any(String),
111
+ })
112
+
113
+ expect({ a: 1, b: 2, c: 3 }).toEqual(
114
+ expect.objectContaining({ a: 1 })
115
+ )
116
+
117
+ expect([1, 2, 3, 4]).toEqual(
118
+ expect.arrayContaining([1, 3])
119
+ )
120
+
121
+ expect('hello world').toEqual(
122
+ expect.stringContaining('world')
123
+ )
124
+
125
+ expect('hello world').toEqual(
126
+ expect.stringMatching(/world$/)
127
+ )
128
+
129
+ expect({ value: null }).toEqual({
130
+ value: expect.anything() // Matches anything except null/undefined
131
+ })
132
+
133
+ // Negate with expect.not
134
+ expect([1, 2]).toEqual(
135
+ expect.not.arrayContaining([3])
136
+ )
137
+ ```
138
+
139
+ ## Soft Assertions
140
+
141
+ Continue test after failure:
142
+
143
+ ```ts
144
+ expect.soft(1).toBe(2) // Marks test failed but continues
145
+ expect.soft(2).toBe(3) // Also runs
146
+ // All failures reported at end
147
+ ```
148
+
149
+ ## Poll Assertions
150
+
151
+ Retry until passes:
152
+
153
+ ```ts
154
+ await expect.poll(() => fetchStatus()).toBe('ready')
155
+
156
+ await expect.poll(
157
+ () => document.querySelector('.element'),
158
+ { interval: 100, timeout: 5000 }
159
+ ).toBeTruthy()
160
+ ```
161
+
162
+ ## Assertion Count
163
+
164
+ ```ts
165
+ test('async assertions', async () => {
166
+ expect.assertions(2) // Exactly 2 assertions must run
167
+
168
+ await doAsync((data) => {
169
+ expect(data).toBeDefined()
170
+ expect(data.id).toBe(1)
171
+ })
172
+ })
173
+
174
+ test('at least one', () => {
175
+ expect.hasAssertions() // At least 1 assertion must run
176
+ })
177
+ ```
178
+
179
+ ## Extending Matchers
180
+
181
+ ```ts
182
+ expect.extend({
183
+ toBeWithinRange(received, floor, ceiling) {
184
+ const pass = received >= floor && received <= ceiling
185
+ return {
186
+ pass,
187
+ message: () =>
188
+ `expected ${received} to be within range ${floor} - ${ceiling}`,
189
+ }
190
+ },
191
+ })
192
+
193
+ test('custom matcher', () => {
194
+ expect(100).toBeWithinRange(90, 110)
195
+ })
196
+ ```
197
+
198
+ ## Snapshot Assertions
199
+
200
+ ```ts
201
+ expect(data).toMatchSnapshot()
202
+ expect(data).toMatchInlineSnapshot(`{ "id": 1 }`)
203
+ await expect(result).toMatchFileSnapshot('./expected.json')
204
+
205
+ expect(() => throw new Error('fail')).toThrowErrorMatchingSnapshot()
206
+ ```
207
+
208
+ ## Key Points
209
+
210
+ - Use `toBe` for primitives, `toEqual` for objects/arrays
211
+ - `toStrictEqual` checks undefined properties and array sparseness
212
+ - Always `await` async assertions (`resolves`, `rejects`, `poll`)
213
+ - Use context's `expect` in concurrent tests for correct tracking
214
+ - `toThrow` requires wrapping sync code in a function
215
+
216
+ <!--
217
+ Source references:
218
+ - https://vitest.dev/api/expect.html
219
+ -->
@@ -0,0 +1,244 @@
1
+ ---
2
+ name: lifecycle-hooks
3
+ description: beforeEach, afterEach, beforeAll, afterAll, and around hooks
4
+ ---
5
+
6
+ # Lifecycle Hooks
7
+
8
+ ## Basic Hooks
9
+
10
+ ```ts
11
+ import { afterAll, afterEach, beforeAll, beforeEach, test } from 'vitest'
12
+
13
+ beforeAll(async () => {
14
+ // Runs once before all tests in file/suite
15
+ await setupDatabase()
16
+ })
17
+
18
+ afterAll(async () => {
19
+ // Runs once after all tests in file/suite
20
+ await teardownDatabase()
21
+ })
22
+
23
+ beforeEach(async () => {
24
+ // Runs before each test
25
+ await clearTestData()
26
+ })
27
+
28
+ afterEach(async () => {
29
+ // Runs after each test
30
+ await cleanupMocks()
31
+ })
32
+ ```
33
+
34
+ ## Cleanup Return Pattern
35
+
36
+ Return cleanup function from `before*` hooks:
37
+
38
+ ```ts
39
+ beforeAll(async () => {
40
+ const server = await startServer()
41
+
42
+ // Returned function runs as afterAll
43
+ return async () => {
44
+ await server.close()
45
+ }
46
+ })
47
+
48
+ beforeEach(async () => {
49
+ const connection = await connect()
50
+
51
+ // Runs as afterEach
52
+ return () => connection.close()
53
+ })
54
+ ```
55
+
56
+ ## Scoped Hooks
57
+
58
+ Hooks apply to current suite and nested suites:
59
+
60
+ ```ts
61
+ describe('outer', () => {
62
+ beforeEach(() => console.log('outer before'))
63
+
64
+ test('test 1', () => {}) // outer before → test
65
+
66
+ describe('inner', () => {
67
+ beforeEach(() => console.log('inner before'))
68
+
69
+ test('test 2', () => {}) // outer before → inner before → test
70
+ })
71
+ })
72
+ ```
73
+
74
+ ## Hook Timeout
75
+
76
+ ```ts
77
+ beforeAll(async () => {
78
+ await slowSetup()
79
+ }, 30_000) // 30 second timeout
80
+ ```
81
+
82
+ ## Around Hooks
83
+
84
+ Wrap tests with setup/teardown context:
85
+
86
+ ```ts
87
+ import { aroundEach, test } from 'vitest'
88
+
89
+ // Wrap each test in database transaction
90
+ aroundEach(async (runTest) => {
91
+ await db.beginTransaction()
92
+ await runTest() // Must be called!
93
+ await db.rollback()
94
+ })
95
+
96
+ test('insert user', async () => {
97
+ await db.insert({ name: 'Alice' })
98
+ // Automatically rolled back after test
99
+ })
100
+ ```
101
+
102
+ ### aroundAll
103
+
104
+ Wrap entire suite:
105
+
106
+ ```ts
107
+ import { aroundAll, test } from 'vitest'
108
+
109
+ aroundAll(async (runSuite) => {
110
+ console.log('before all tests')
111
+ await runSuite() // Must be called!
112
+ console.log('after all tests')
113
+ })
114
+ ```
115
+
116
+ ### Multiple Around Hooks
117
+
118
+ Nested like onion layers:
119
+
120
+ ```ts
121
+ aroundEach(async (runTest) => {
122
+ console.log('outer before')
123
+ await runTest()
124
+ console.log('outer after')
125
+ })
126
+
127
+ aroundEach(async (runTest) => {
128
+ console.log('inner before')
129
+ await runTest()
130
+ console.log('inner after')
131
+ })
132
+
133
+ // Order: outer before → inner before → test → inner after → outer after
134
+ ```
135
+
136
+ ## Test Hooks
137
+
138
+ Inside test body:
139
+
140
+ ```ts
141
+ import { onTestFailed, onTestFinished, test } from 'vitest'
142
+
143
+ test('with cleanup', () => {
144
+ const db = connect()
145
+
146
+ // Runs after test finishes (pass or fail)
147
+ onTestFinished(() => db.close())
148
+
149
+ // Only runs if test fails
150
+ onTestFailed(({ task }) => {
151
+ console.log('Failed:', task.result?.errors)
152
+ })
153
+
154
+ db.query('SELECT * FROM users')
155
+ })
156
+ ```
157
+
158
+ ### Reusable Cleanup Pattern
159
+
160
+ ```ts
161
+ function useTestDb() {
162
+ const db = connect()
163
+ onTestFinished(() => db.close())
164
+ return db
165
+ }
166
+
167
+ test('query users', () => {
168
+ const db = useTestDb()
169
+ expect(db.query('SELECT * FROM users')).toBeDefined()
170
+ })
171
+
172
+ test('query orders', () => {
173
+ const db = useTestDb() // Fresh connection, auto-closed
174
+ expect(db.query('SELECT * FROM orders')).toBeDefined()
175
+ })
176
+ ```
177
+
178
+ ## Concurrent Test Hooks
179
+
180
+ For concurrent tests, use context's hooks:
181
+
182
+ ```ts
183
+ test.concurrent('concurrent', ({ onTestFinished }) => {
184
+ const resource = allocate()
185
+ onTestFinished(() => resource.release())
186
+ })
187
+ ```
188
+
189
+ ## Extended Test Hooks
190
+
191
+ With `test.extend`, hooks are type-aware:
192
+
193
+ ```ts
194
+ const test = base.extend<{ db: Database }>({
195
+ db: async ({}, use) => {
196
+ const db = await createDb()
197
+ await use(db)
198
+ await db.close()
199
+ },
200
+ })
201
+
202
+ // These hooks know about `db` fixture
203
+ test.beforeEach(({ db }) => {
204
+ db.seed()
205
+ })
206
+
207
+ test.afterEach(({ db }) => {
208
+ db.clear()
209
+ })
210
+ ```
211
+
212
+ ## Hook Execution Order
213
+
214
+ Default order (stack):
215
+ 1. `beforeAll` (in order)
216
+ 2. `beforeEach` (in order)
217
+ 3. Test
218
+ 4. `afterEach` (reverse order)
219
+ 5. `afterAll` (reverse order)
220
+
221
+ Configure with `sequence.hooks`:
222
+
223
+ ```ts
224
+ defineConfig({
225
+ test: {
226
+ sequence: {
227
+ hooks: 'list', // 'stack' (default), 'list', 'parallel'
228
+ },
229
+ },
230
+ })
231
+ ```
232
+
233
+ ## Key Points
234
+
235
+ - Hooks are not called during type checking
236
+ - Return cleanup function from `before*` to avoid `after*` duplication
237
+ - `aroundEach`/`aroundAll` must call `runTest()`/`runSuite()`
238
+ - `onTestFinished` always runs, even if test fails
239
+ - Use context hooks for concurrent tests
240
+
241
+ <!--
242
+ Source references:
243
+ - https://vitest.dev/api/hooks.html
244
+ -->