vinta-ai-workflows 0.1.7

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 (40) hide show
  1. package/CHANGELOG.md +830 -0
  2. package/LICENSE +21 -0
  3. package/README.md +547 -0
  4. package/package.json +27 -0
  5. package/schemas/README.md +84 -0
  6. package/schemas/mcp-preflight-cache.v1.schema.json +50 -0
  7. package/schemas/prs-context-comments.v1.schema.json +51 -0
  8. package/schemas/prs-context-frontmatter.v1.schema.json +77 -0
  9. package/schemas/sub-agent.v1.schema.json +105 -0
  10. package/schemas/vinta-ai-workflows-config.v1.schema.json +283 -0
  11. package/skills/vinta-analyze-codebase/SKILL.md +325 -0
  12. package/skills/vinta-bootstrap-ai-tools/SKILL.md +375 -0
  13. package/skills/vinta-bootstrap-ai-tools/resources/stacks/README.md +50 -0
  14. package/skills/vinta-bootstrap-ai-tools/resources/stacks/django/notes.md +52 -0
  15. package/skills/vinta-bootstrap-ai-tools/resources/stacks/medplum/notes.md +45 -0
  16. package/skills/vinta-bootstrap-ai-tools/resources/stacks/nextjs-app-router/notes.md +35 -0
  17. package/skills/vinta-bootstrap-ai-tools/resources/stacks/python-package/notes.md +31 -0
  18. package/skills/vinta-bootstrap-ai-tools/resources/stacks/typescript-monorepo/notes.md +32 -0
  19. package/skills/vinta-derive-skills/SKILL.md +200 -0
  20. package/skills/vinta-derive-skills/resources/amend-plan-template.md +355 -0
  21. package/skills/vinta-derive-skills/resources/foundation-skills/add-one-off-script/SKILL.md +292 -0
  22. package/skills/vinta-derive-skills/resources/foundation-skills/add-one-off-script/resources/one_off_script_base.py +602 -0
  23. package/skills/vinta-derive-skills/resources/foundation-skills/add-one-off-script/resources/one_off_script_base.ts +624 -0
  24. package/skills/vinta-derive-skills/resources/foundation-skills/create-qa-use-cases/SKILL.md +232 -0
  25. package/skills/vinta-derive-skills/resources/foundation-skills/create-spec/SKILL.md +235 -0
  26. package/skills/vinta-derive-skills/resources/foundation-skills/open-pr-from-context/SKILL.md +78 -0
  27. package/skills/vinta-derive-skills/resources/foundation-skills/open-pr-from-context/scripts/open-pr.sh +357 -0
  28. package/skills/vinta-derive-skills/resources/foundation-skills/plan-feature/SKILL.md +418 -0
  29. package/skills/vinta-derive-skills/resources/implement-plan-template.md +375 -0
  30. package/skills/vinta-derive-skills/resources/prs-context-template.md +91 -0
  31. package/skills/vinta-derive-skills/resources/systematic-debugging-mcp-tools.md +125 -0
  32. package/skills/vinta-derive-skills/resources/systematic-debugging-template.md +167 -0
  33. package/skills/vinta-derive-subagents/SKILL.md +192 -0
  34. package/skills/vinta-install-ai-tools-setup/SKILL.md +138 -0
  35. package/skills/vinta-install-ai-tools-setup/resources/setup-ai-tools.mjs +392 -0
  36. package/skills/vinta-migrate-plans-specs/SKILL.md +229 -0
  37. package/skills/vinta-sync-ai-tools/SKILL.md +184 -0
  38. package/skills/vinta-update-project-skills/SKILL.md +178 -0
  39. package/skills/vinta-write-agents-md/SKILL.md +192 -0
  40. package/vinta-ai-workflows.mjs +316 -0
@@ -0,0 +1,316 @@
1
+ #!/usr/bin/env node
2
+ // vinta-ai-workflows.mjs — install vinta-ai-workflows builder skills into a
3
+ // target project's per-vendor skill directory (Claude Code, Codex, Cursor,
4
+ // VS Code + Copilot). Project scope only; skills are namespaced with the
5
+ // `vinta-` prefix so they don't collide with the user's own project skills.
6
+ //
7
+ // Usage (after `npm i -D vinta-ai-workflows`):
8
+ // npx vinta-ai-workflows install --tool <name> [opts]
9
+ // npx vinta-ai-workflows update --tool <name> [opts] # uninstall + install
10
+ // npx vinta-ai-workflows uninstall --tool <name> [opts]
11
+ // npx vinta-ai-workflows list
12
+ //
13
+ // Or directly: node vinta-ai-workflows.mjs <cmd> [opts]
14
+ //
15
+ // Tools:
16
+ // claude-code .claude/skills/
17
+ // codex .agents/skills/ (official Codex path; walks up to repo root)
18
+ // cursor .cursor/skills/
19
+ // copilot .github/skills/
20
+ // agents .agents/skills/ universal — covers Codex + Cursor + Copilot
21
+ // all install into every vendor-specific path above
22
+ //
23
+ // Common options:
24
+ // --target <dir> Project root to install into. Defaults to $PWD.
25
+ // --skills <a,b,c> Subset of skills (default: all skills under ./skills).
26
+ // --copy Copy files instead of symlinking. Survives repo
27
+ // deletion but won't pick up upstream skill updates
28
+ // without re-running `update`.
29
+ // --dry-run, -n Show planned changes; touch nothing.
30
+ //
31
+ // The vinta- skills are one-shot bootstrappers — install, run them once via
32
+ // the AI tool, then `uninstall` to clean up. Use `update` to refresh the
33
+ // installed skills against the latest source (handy when this repo gets
34
+ // pulled with new versions and you copied rather than symlinked). See
35
+ // README.md for invocation per tool.
36
+
37
+ import {
38
+ readdirSync, lstatSync, mkdirSync, rmSync, rmdirSync,
39
+ symlinkSync, unlinkSync, cpSync, writeFileSync, existsSync, realpathSync,
40
+ } from 'node:fs';
41
+ import { join, resolve, dirname, relative } from 'node:path';
42
+ import { fileURLToPath } from 'node:url';
43
+
44
+ const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
45
+ const SKILLS_SRC = resolve(SCRIPT_DIR, 'skills');
46
+ const MARKER = '.installed-by-vinta-ai-workflows';
47
+
48
+ // ── Vendor target paths ──────────────────────────────────────────────────
49
+ //
50
+ // Sources (verified 2026-05):
51
+ // Claude Code — .claude/skills/ (native)
52
+ // Codex — .agents/skills/ (developers.openai.com/codex/skills,
53
+ // walks up to repo root)
54
+ // Cursor — .cursor/skills/ OR .agents/skills/ (cursor.com/docs/skills)
55
+ // Copilot — .github/skills/, .claude/skills/, .agents/skills/
56
+ // (code.visualstudio.com/docs/copilot/customization/agent-skills)
57
+ //
58
+ // `.agents/skills/` is the universal path — recognized by Codex, Cursor, and
59
+ // Copilot. We expose it as the `agents` virtual tool for one-shot installs.
60
+
61
+ const TOOLS = {
62
+ 'claude-code': { project: '.claude/skills', label: 'Claude Code' },
63
+ codex: { project: '.agents/skills', label: 'OpenAI Codex' },
64
+ cursor: { project: '.cursor/skills', label: 'Cursor' },
65
+ copilot: { project: '.github/skills', label: 'VS Code + Copilot' },
66
+ agents: { project: '.agents/skills', label: 'Universal (Codex + Cursor + Copilot)' },
67
+ };
68
+
69
+ const TOOL_ALIASES = {
70
+ 'claude-code': 'claude-code',
71
+ claude: 'claude-code',
72
+ codex: 'codex',
73
+ 'openai-codex': 'codex',
74
+ cursor: 'cursor',
75
+ copilot: 'copilot',
76
+ vscode: 'copilot',
77
+ 'vscode-copilot': 'copilot',
78
+ 'github-copilot': 'copilot',
79
+ agents: 'agents',
80
+ universal: 'agents',
81
+ };
82
+
83
+ const ALL_TOOLS = ['claude-code', 'codex', 'cursor', 'copilot'];
84
+
85
+ // ── CLI parsing ──────────────────────────────────────────────────────────
86
+
87
+ function printHelp() {
88
+ console.log(`vinta-ai-workflows skills installer
89
+
90
+ Commands:
91
+ install Place skills under <target>/.<vendor>/skills/
92
+ update Re-install (uninstall + install) so latest source content lands
93
+ uninstall Remove skills (only artifacts created by this script)
94
+ list List available skills
95
+
96
+ Options:
97
+ --tool <name> claude-code | codex | cursor | copilot | agents | all
98
+ --target <dir> Project root (default: cwd)
99
+ --skills <a,b,c> Subset of skills
100
+ --copy Copy instead of symlink (recommended for shared repos
101
+ where contributors don't have this clone)
102
+ --dry-run, -n Plan only
103
+ --help, -h This help
104
+
105
+ Tip: --tool agents writes to .agents/skills/, which Codex, Cursor, and Copilot
106
+ all recognize. Use it to cover three tools with one install.`);
107
+ }
108
+
109
+ function parseArgs(argv) {
110
+ const opts = {
111
+ cmd: argv[0],
112
+ tool: null,
113
+ target: process.cwd(),
114
+ copy: false,
115
+ skills: null,
116
+ dryRun: false,
117
+ };
118
+ for (let i = 1; i < argv.length; i++) {
119
+ const a = argv[i];
120
+ const eq = (k) => a === k || a.startsWith(`${k}=`);
121
+ const val = (k) => (a.startsWith(`${k}=`) ? a.slice(k.length + 1) : argv[++i]);
122
+ if (eq('--tool')) opts.tool = val('--tool');
123
+ else if (eq('--target')) opts.target = resolve(val('--target'));
124
+ else if (a === '--copy') opts.copy = true;
125
+ else if (eq('--skills')) {
126
+ opts.skills = val('--skills').split(',').map((s) => s.trim()).filter(Boolean);
127
+ } else if (a === '--dry-run' || a === '-n') opts.dryRun = true;
128
+ else if (a === '--help' || a === '-h') { printHelp(); process.exit(0); }
129
+ else { console.error(`unknown arg: ${a}`); process.exit(2); }
130
+ }
131
+ return opts;
132
+ }
133
+
134
+ function resolveTools(tool) {
135
+ if (!tool) { console.error('--tool required (or use "all")'); process.exit(2); }
136
+ if (tool === 'all') return ALL_TOOLS;
137
+ const canonical = TOOL_ALIASES[tool.toLowerCase()];
138
+ if (!canonical) {
139
+ const allowed = Object.keys(TOOL_ALIASES).sort().concat('all').join(', ');
140
+ console.error(`unknown tool "${tool}". Allowed: ${allowed}`);
141
+ process.exit(2);
142
+ }
143
+ return [canonical];
144
+ }
145
+
146
+ // ── Skill discovery ──────────────────────────────────────────────────────
147
+
148
+ function listSkills() {
149
+ if (!existsSync(SKILLS_SRC)) {
150
+ console.error(`skills source missing: ${SKILLS_SRC}`);
151
+ process.exit(1);
152
+ }
153
+ return readdirSync(SKILLS_SRC, { withFileTypes: true })
154
+ .filter((d) => d.isDirectory() && existsSync(join(SKILLS_SRC, d.name, 'SKILL.md')))
155
+ .map((d) => d.name)
156
+ .sort();
157
+ }
158
+
159
+ function resolveSkillSet(requested) {
160
+ const all = listSkills();
161
+ if (!requested?.length) return all;
162
+ const missing = requested.filter((s) => !all.includes(s));
163
+ if (missing.length) {
164
+ console.error(`unknown skills: ${missing.join(', ')}`);
165
+ console.error(`available: ${all.join(', ')}`);
166
+ process.exit(2);
167
+ }
168
+ return requested;
169
+ }
170
+
171
+ function targetDir(tool, target) {
172
+ return resolve(target, TOOLS[tool].project);
173
+ }
174
+
175
+ // ── Install ──────────────────────────────────────────────────────────────
176
+
177
+ function install(opts) {
178
+ const tools = resolveTools(opts.tool);
179
+ const skills = resolveSkillSet(opts.skills);
180
+
181
+ // Dedupe by destination directory so `--tool all` + `agents` overlap
182
+ // doesn't double-install (codex and agents both write to .agents/skills/).
183
+ const seenBaseDirs = new Set();
184
+
185
+ for (const tool of tools) {
186
+ const baseDir = targetDir(tool, opts.target);
187
+ if (seenBaseDirs.has(baseDir)) {
188
+ console.log(`[${tool}] ${baseDir} already covered — skipping`);
189
+ continue;
190
+ }
191
+ seenBaseDirs.add(baseDir);
192
+
193
+ if (!opts.dryRun) mkdirSync(baseDir, { recursive: true });
194
+
195
+ for (const skill of skills) {
196
+ const src = join(SKILLS_SRC, skill);
197
+ const dest = join(baseDir, skill);
198
+
199
+ if (opts.dryRun) {
200
+ console.log(`[${tool}] would ${opts.copy ? 'copy' : 'symlink'} ${skill} → ${dest}`);
201
+ continue;
202
+ }
203
+
204
+ try {
205
+ const st = lstatSync(dest);
206
+ if (st.isSymbolicLink() || st.isFile()) unlinkSync(dest);
207
+ else if (st.isDirectory()) rmSync(dest, { recursive: true, force: true });
208
+ } catch { /* not present */ }
209
+
210
+ if (opts.copy) {
211
+ cpSync(src, dest, { recursive: true });
212
+ writeFileSync(
213
+ join(dest, MARKER),
214
+ `Installed by vinta-ai-workflows (vinta-ai-workflows.mjs)\nsource: ${src}\n`,
215
+ );
216
+ } else {
217
+ const rel = relative(dirname(dest), src);
218
+ symlinkSync(rel, dest, 'dir');
219
+ }
220
+ console.log(`[${tool}] installed ${skill} → ${dest}`);
221
+ }
222
+ }
223
+ }
224
+
225
+ // ── Uninstall ────────────────────────────────────────────────────────────
226
+ //
227
+ // Safety: removes only
228
+ // - symlinks whose realpath is inside SKILLS_SRC (or dangling)
229
+ // - directories containing the MARKER file
230
+ // User-authored or hand-edited entries are left alone.
231
+
232
+ function uninstall(opts) {
233
+ const tools = resolveTools(opts.tool);
234
+ const skills = resolveSkillSet(opts.skills);
235
+
236
+ const seenBaseDirs = new Set();
237
+
238
+ for (const tool of tools) {
239
+ const baseDir = targetDir(tool, opts.target);
240
+ if (seenBaseDirs.has(baseDir)) continue;
241
+ seenBaseDirs.add(baseDir);
242
+
243
+ if (!existsSync(baseDir)) {
244
+ console.log(`[${tool}] ${baseDir} not present — nothing to do`);
245
+ continue;
246
+ }
247
+
248
+ for (const skill of skills) {
249
+ const dest = join(baseDir, skill);
250
+ let st;
251
+ try { st = lstatSync(dest); } catch { continue; }
252
+
253
+ if (st.isSymbolicLink()) {
254
+ let real;
255
+ try { real = realpathSync(dest); } catch { real = null; }
256
+ if (real === null || real.startsWith(SKILLS_SRC)) {
257
+ if (opts.dryRun) console.log(`[${tool}] would remove symlink ${dest}`);
258
+ else { unlinkSync(dest); console.log(`[${tool}] uninstalled ${skill}`); }
259
+ } else {
260
+ console.warn(`[${tool}] ${dest} symlinks outside skills source — leaving`);
261
+ }
262
+ } else if (st.isDirectory()) {
263
+ if (existsSync(join(dest, MARKER))) {
264
+ if (opts.dryRun) console.log(`[${tool}] would remove ${dest}`);
265
+ else { rmSync(dest, { recursive: true, force: true }); console.log(`[${tool}] uninstalled ${skill}`); }
266
+ } else {
267
+ console.warn(`[${tool}] ${dest} lacks marker (hand-installed?) — leaving`);
268
+ }
269
+ } else {
270
+ console.warn(`[${tool}] ${dest} unexpected entry type — leaving`);
271
+ }
272
+ }
273
+
274
+ try {
275
+ const remaining = readdirSync(baseDir);
276
+ if (remaining.length === 0) {
277
+ if (opts.dryRun) console.log(`[${tool}] would remove empty ${baseDir}`);
278
+ else { rmdirSync(baseDir); console.log(`[${tool}] removed empty ${baseDir}`); }
279
+ }
280
+ } catch { /* ignore */ }
281
+ }
282
+ }
283
+
284
+ // ── Update (uninstall then install) ──────────────────────────────────────
285
+
286
+ function update(opts) {
287
+ console.log('# update — phase 1: uninstall existing');
288
+ uninstall(opts);
289
+ console.log('# update — phase 2: install latest');
290
+ install(opts);
291
+ }
292
+
293
+ // ── List ─────────────────────────────────────────────────────────────────
294
+
295
+ function list() {
296
+ const skills = listSkills();
297
+ console.log(`Skills available in ${SKILLS_SRC}:`);
298
+ for (const s of skills) console.log(` ${s}`);
299
+ }
300
+
301
+ // ── Main ─────────────────────────────────────────────────────────────────
302
+
303
+ const opts = parseArgs(process.argv.slice(2));
304
+ switch (opts.cmd) {
305
+ case 'install': install(opts); break;
306
+ case 'update': update(opts); break;
307
+ case 'uninstall': uninstall(opts); break;
308
+ case 'list': list(); break;
309
+ case undefined:
310
+ case '--help':
311
+ case '-h': printHelp(); break;
312
+ default:
313
+ console.error(`unknown command: ${opts.cmd}`);
314
+ printHelp();
315
+ process.exit(2);
316
+ }