@gallopsystems/agent-skills 1.0.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 (52) hide show
  1. package/README.md +137 -0
  2. package/package.json +26 -0
  3. package/plugins/doctl/.claude-plugin/plugin.json +8 -0
  4. package/plugins/doctl/skills/doctl/SKILL.md +93 -0
  5. package/plugins/kysely-postgres/.claude-plugin/plugin.json +8 -0
  6. package/plugins/kysely-postgres/skills/kysely-postgres/SKILL.md +1101 -0
  7. package/plugins/kysely-postgres/skills/kysely-postgres/references/aggregations.ts +167 -0
  8. package/plugins/kysely-postgres/skills/kysely-postgres/references/ctes.ts +165 -0
  9. package/plugins/kysely-postgres/skills/kysely-postgres/references/expressions.ts +272 -0
  10. package/plugins/kysely-postgres/skills/kysely-postgres/references/joins.ts +206 -0
  11. package/plugins/kysely-postgres/skills/kysely-postgres/references/json-arrays.ts +398 -0
  12. package/plugins/kysely-postgres/skills/kysely-postgres/references/mutations.ts +199 -0
  13. package/plugins/kysely-postgres/skills/kysely-postgres/references/orderby-pagination.ts +117 -0
  14. package/plugins/kysely-postgres/skills/kysely-postgres/references/relations.ts +176 -0
  15. package/plugins/kysely-postgres/skills/kysely-postgres/references/select-where.ts +146 -0
  16. package/plugins/linear/.claude-plugin/plugin.json +8 -0
  17. package/plugins/linear/skills/linear/SKILL.md +1040 -0
  18. package/plugins/linear/skills/linear/bin/linear.mjs +1228 -0
  19. package/plugins/linear/skills/linear/tech-stack.md +273 -0
  20. package/plugins/nitro-testing/.claude-plugin/plugin.json +8 -0
  21. package/plugins/nitro-testing/skills/nitro-testing/SKILL.md +497 -0
  22. package/plugins/nitro-testing/skills/nitro-testing/async-testing.md +270 -0
  23. package/plugins/nitro-testing/skills/nitro-testing/ci-setup.md +226 -0
  24. package/plugins/nitro-testing/skills/nitro-testing/examples/global-setup.ts +90 -0
  25. package/plugins/nitro-testing/skills/nitro-testing/examples/handler.test.ts +167 -0
  26. package/plugins/nitro-testing/skills/nitro-testing/examples/setup.ts +29 -0
  27. package/plugins/nitro-testing/skills/nitro-testing/examples/test-utils-index.ts +297 -0
  28. package/plugins/nitro-testing/skills/nitro-testing/examples/vitest.config.ts +42 -0
  29. package/plugins/nitro-testing/skills/nitro-testing/factories.md +278 -0
  30. package/plugins/nitro-testing/skills/nitro-testing/frontend-testing.md +512 -0
  31. package/plugins/nitro-testing/skills/nitro-testing/test-utils.md +262 -0
  32. package/plugins/nitro-testing/skills/nitro-testing/transaction-rollback.md +183 -0
  33. package/plugins/nitro-testing/skills/nitro-testing/vitest-config.md +236 -0
  34. package/plugins/nuxt-nitro-api/.claude-plugin/plugin.json +8 -0
  35. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/SKILL.md +260 -0
  36. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/auth-patterns.md +228 -0
  37. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/composables-utils.md +174 -0
  38. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/deep-linking.md +190 -0
  39. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/examples/auth-middleware.ts +32 -0
  40. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/examples/auth-utils.ts +51 -0
  41. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/examples/deep-link-page.vue +61 -0
  42. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/examples/service-util.ts +63 -0
  43. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/examples/sse-endpoint.ts +59 -0
  44. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/examples/validation-endpoint.ts +38 -0
  45. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/fetch-patterns.md +178 -0
  46. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/nitro-tasks.md +243 -0
  47. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/page-structure.md +162 -0
  48. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/server-services.md +238 -0
  49. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/sse.md +221 -0
  50. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/ssr-client.md +166 -0
  51. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/validation.md +131 -0
  52. package/scripts/link-skills.mjs +252 -0
@@ -0,0 +1,252 @@
1
+ #!/usr/bin/env node
2
+ // Postinstall: symlink this package's skills and commands into the consuming
3
+ // project's .claude directory, so a `yarn add` / `yarn install` keeps the
4
+ // project in sync with this package's version:
5
+ //
6
+ // skills -> .claude/skills/<name> (a dir containing SKILL.md)
7
+ // commands -> .claude/commands/<rel>.md (a .md file under any commands/ dir)
8
+ //
9
+ // This runs during `yarn install`, so it MUST NOT throw — a thrown error aborts
10
+ // the whole install. Every failure path here degrades to a warning + exit 0.
11
+
12
+ import fs from 'node:fs'
13
+ import path from 'node:path'
14
+ import { fileURLToPath } from 'node:url'
15
+
16
+ // scripts/link-skills.mjs -> package root
17
+ const PKG_DIR = path.resolve(fileURLToPath(import.meta.url), '..', '..')
18
+ const PKG_DIR_REAL = realOrSelf(PKG_DIR)
19
+ const UNLINK = process.argv.includes('--unlink')
20
+ const SKIP_DIRS = new Set(['node_modules', '.git', '.claude'])
21
+
22
+ const log = (msg) => console.log(`[claude-skills] ${msg}`)
23
+ const warn = (msg) => console.warn(`[claude-skills] ${msg}`)
24
+
25
+ function realOrSelf(p) {
26
+ try {
27
+ return fs.realpathSync(p)
28
+ } catch {
29
+ return p
30
+ }
31
+ }
32
+
33
+ // Resolve the project that pulled us in. npm and Yarn (Classic + Berry) set
34
+ // INIT_CWD to the directory where the install command ran — the project root,
35
+ // or the workspace root in a monorepo, which is exactly where the shared
36
+ // .claude directory should live. Fall back to climbing out of node_modules.
37
+ function resolveProjectRoot() {
38
+ // INIT_CWD may equal PKG_DIR when installing in this repo itself; main()
39
+ // detects that and skips, so return it unconditionally here.
40
+ if (process.env.INIT_CWD) return path.resolve(process.env.INIT_CWD)
41
+
42
+ const marker = `${path.sep}node_modules${path.sep}`
43
+ const idx = PKG_DIR.indexOf(marker)
44
+ if (idx !== -1) return PKG_DIR.slice(0, idx)
45
+
46
+ return null
47
+ }
48
+
49
+ // Skills: every directory that directly contains a SKILL.md. Skills don't nest,
50
+ // so stop descending once one is found. Returns Map<name, sourceDir>.
51
+ function collectSkills(root) {
52
+ const found = new Map()
53
+ const walk = (dir) => {
54
+ let entries
55
+ try {
56
+ entries = fs.readdirSync(dir, { withFileTypes: true })
57
+ } catch {
58
+ return
59
+ }
60
+ if (entries.some((e) => e.isFile() && e.name === 'SKILL.md')) {
61
+ add(found, path.basename(dir), dir, 'skill')
62
+ return
63
+ }
64
+ for (const e of entries) {
65
+ if (e.isDirectory() && !SKIP_DIRS.has(e.name)) walk(path.join(dir, e.name))
66
+ }
67
+ }
68
+ walk(root)
69
+ return found
70
+ }
71
+
72
+ // Commands: every .md file under any directory named `commands/`, keyed by its
73
+ // path relative to that commands dir (so commands/git/sync.md -> git/sync.md).
74
+ // Returns Map<relPath, sourceFile>.
75
+ function collectCommands(root) {
76
+ const found = new Map()
77
+ const harvest = (cmdDir) => {
78
+ const walk = (dir) => {
79
+ let entries
80
+ try {
81
+ entries = fs.readdirSync(dir, { withFileTypes: true })
82
+ } catch {
83
+ return
84
+ }
85
+ for (const e of entries) {
86
+ const p = path.join(dir, e.name)
87
+ if (e.isDirectory()) walk(p)
88
+ else if (e.isFile() && e.name.endsWith('.md')) add(found, path.relative(cmdDir, p), p, 'command')
89
+ }
90
+ }
91
+ walk(cmdDir)
92
+ }
93
+ const walk = (dir) => {
94
+ let entries
95
+ try {
96
+ entries = fs.readdirSync(dir, { withFileTypes: true })
97
+ } catch {
98
+ return
99
+ }
100
+ for (const e of entries) {
101
+ if (!e.isDirectory() || SKIP_DIRS.has(e.name)) continue
102
+ const p = path.join(dir, e.name)
103
+ if (e.name === 'commands') harvest(p)
104
+ else walk(p)
105
+ }
106
+ }
107
+ walk(root)
108
+ return found
109
+ }
110
+
111
+ function add(map, key, src, kind) {
112
+ if (map.has(key)) {
113
+ warn(`duplicate ${kind} "${key}" (${src}); keeping ${map.get(key)}.`)
114
+ return
115
+ }
116
+ map.set(key, src)
117
+ }
118
+
119
+ // True only for symlinks whose real target lives inside this package — i.e.
120
+ // links we created. Used so cleanup never touches the user's own files.
121
+ function isOwnedLink(linkPath) {
122
+ let raw
123
+ try {
124
+ if (!fs.lstatSync(linkPath).isSymbolicLink()) return false
125
+ raw = fs.readlinkSync(linkPath)
126
+ } catch {
127
+ return false
128
+ }
129
+ const target = realOrSelf(path.resolve(path.dirname(linkPath), raw))
130
+ return target === PKG_DIR_REAL || target.startsWith(PKG_DIR_REAL + path.sep)
131
+ }
132
+
133
+ // Recursively remove owned links under root whose rel path isn't wanted, then
134
+ // prune directories that we emptied out. Never recurses into a symlink, so it
135
+ // won't follow a linked skill dir back into the package.
136
+ function removeStaleLinks(root, keepRels) {
137
+ const walk = (dir) => {
138
+ let entries
139
+ try {
140
+ entries = fs.readdirSync(dir, { withFileTypes: true })
141
+ } catch {
142
+ return
143
+ }
144
+ for (const e of entries) {
145
+ const p = path.join(dir, e.name)
146
+ if (e.isSymbolicLink()) {
147
+ if (!keepRels.has(path.relative(root, p)) && isOwnedLink(p)) {
148
+ try {
149
+ fs.rmSync(p, { force: true })
150
+ } catch (err) {
151
+ warn(`could not remove stale link "${path.relative(root, p)}": ${err.message}`)
152
+ }
153
+ }
154
+ } else if (e.isDirectory()) {
155
+ walk(p)
156
+ try {
157
+ if (fs.readdirSync(p).length === 0) fs.rmdirSync(p)
158
+ } catch {
159
+ // not empty or not ours — leave it
160
+ }
161
+ }
162
+ }
163
+ }
164
+ walk(root)
165
+ }
166
+
167
+ // Link every wanted item into destRoot, refreshing existing owned links and
168
+ // never clobbering a real path the user authored. Returns the count linked.
169
+ function linkInto(destRoot, wanted, kind) {
170
+ const exists = fs.existsSync(destRoot)
171
+
172
+ // Nothing to link: only tidy up previously-created links (if the dir exists),
173
+ // so we never create an empty .claude/<kind> directory in every consumer.
174
+ if (wanted.size === 0) {
175
+ if (exists) removeStaleLinks(realOrSelf(destRoot), new Set())
176
+ return 0
177
+ }
178
+
179
+ fs.mkdirSync(destRoot, { recursive: true })
180
+ // Realpath the link root so relative targets stay clean even when the project
181
+ // lives under a symlinked path (PKG_DIR is realpath-resolved via
182
+ // import.meta.url; a mismatch blows targets up to a ../../../ chain to root).
183
+ const root = realOrSelf(destRoot)
184
+ removeStaleLinks(root, new Set(wanted.keys()))
185
+
186
+ let linked = 0
187
+ for (const [rel, src] of wanted) {
188
+ const dest = path.join(root, rel)
189
+ let existing = null
190
+ try {
191
+ existing = fs.lstatSync(dest)
192
+ } catch {
193
+ // doesn't exist yet
194
+ }
195
+ if (existing) {
196
+ if (existing.isSymbolicLink()) {
197
+ fs.rmSync(dest, { force: true }) // refresh the target
198
+ } else {
199
+ warn(`"${rel}" already exists in .claude/${kind} as a real path; leaving it untouched.`)
200
+ continue
201
+ }
202
+ }
203
+ const isDir = (() => {
204
+ try {
205
+ return fs.statSync(src).isDirectory()
206
+ } catch {
207
+ return false
208
+ }
209
+ })()
210
+ try {
211
+ fs.mkdirSync(path.dirname(dest), { recursive: true }) // for nested command paths
212
+ fs.symlinkSync(path.relative(path.dirname(dest), src), dest, isDir ? 'dir' : 'file')
213
+ linked++
214
+ } catch (err) {
215
+ warn(`failed to link "${rel}": ${err.message}`)
216
+ }
217
+ }
218
+ return linked
219
+ }
220
+
221
+ function main() {
222
+ const projectRoot = resolveProjectRoot()
223
+ if (!projectRoot) {
224
+ warn('could not determine the project root (INIT_CWD unset); skipping.')
225
+ return
226
+ }
227
+ if (realOrSelf(projectRoot) === PKG_DIR_REAL && !process.env.CLAUDE_SKILLS_LINK_SELF) {
228
+ log('installed in its own repo; skipping self-link (set CLAUDE_SKILLS_LINK_SELF=1 to override).')
229
+ return
230
+ }
231
+
232
+ const skillsRoot = path.join(projectRoot, '.claude', 'skills')
233
+ const commandsRoot = path.join(projectRoot, '.claude', 'commands')
234
+
235
+ if (UNLINK) {
236
+ for (const root of [skillsRoot, commandsRoot]) {
237
+ if (fs.existsSync(root)) removeStaleLinks(realOrSelf(root), new Set())
238
+ }
239
+ log('removed managed symlinks from .claude/skills and .claude/commands.')
240
+ return
241
+ }
242
+
243
+ const skills = linkInto(skillsRoot, collectSkills(PKG_DIR), 'skills')
244
+ const commands = linkInto(commandsRoot, collectCommands(PKG_DIR), 'commands')
245
+ log(`linked ${skills} skill${skills === 1 ? '' : 's'} and ${commands} command${commands === 1 ? '' : 's'} into .claude/`)
246
+ }
247
+
248
+ try {
249
+ main()
250
+ } catch (err) {
251
+ warn(`unexpected error, skipping: ${err && err.message}`)
252
+ }