setup-vibeflow 0.1.1 → 0.3.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 (2) hide show
  1. package/index.js +150 -31
  2. package/package.json +5 -2
package/index.js CHANGED
@@ -8,9 +8,8 @@ import pc from 'picocolors';
8
8
 
9
9
  const REPO = 'pe-menezes/vibeflow';
10
10
  const BRANCH = 'main';
11
- const BASE_URL = `https://raw.githubusercontent.com/${REPO}/${BRANCH}/copilot`;
12
11
 
13
- const FILES = [
12
+ const COPILOT_FILES = [
14
13
  { src: 'github/prompts/vibeflow-analyze.prompt.md', dest: '.github/prompts/vibeflow-analyze.prompt.md' },
15
14
  { src: 'github/prompts/vibeflow-audit.prompt.md', dest: '.github/prompts/vibeflow-audit.prompt.md' },
16
15
  { src: 'github/prompts/vibeflow-discover.prompt.md', dest: '.github/prompts/vibeflow-discover.prompt.md' },
@@ -24,8 +23,65 @@ const FILES = [
24
23
  { src: 'github/instructions/vibeflow/vibeflow.instructions.md', dest: '.github/instructions/vibeflow/vibeflow.instructions.md' },
25
24
  ];
26
25
 
26
+ const CURSOR_FILES = [
27
+ { src: 'rules/vibeflow.mdc', dest: '.cursor/rules/vibeflow.mdc' },
28
+ { src: 'rules/vibeflow-architect.mdc', dest: '.cursor/rules/vibeflow-architect.mdc' },
29
+ { src: 'skills/vibeflow-analyze/SKILL.md', dest: '.cursor/skills/vibeflow-analyze/SKILL.md' },
30
+ { src: 'skills/vibeflow-audit/SKILL.md', dest: '.cursor/skills/vibeflow-audit/SKILL.md' },
31
+ { src: 'skills/vibeflow-discover/SKILL.md', dest: '.cursor/skills/vibeflow-discover/SKILL.md' },
32
+ { src: 'skills/vibeflow-gen-spec/SKILL.md', dest: '.cursor/skills/vibeflow-gen-spec/SKILL.md' },
33
+ { src: 'skills/vibeflow-prompt-pack/SKILL.md', dest: '.cursor/skills/vibeflow-prompt-pack/SKILL.md' },
34
+ { src: 'skills/vibeflow-quick/SKILL.md', dest: '.cursor/skills/vibeflow-quick/SKILL.md' },
35
+ { src: 'skills/vibeflow-teach/SKILL.md', dest: '.cursor/skills/vibeflow-teach/SKILL.md' },
36
+ { src: 'skills/vibeflow-stats/SKILL.md', dest: '.cursor/skills/vibeflow-stats/SKILL.md' },
37
+ { src: 'skills/vibeflow-spec-driven-dev/SKILL.md', dest: '.cursor/skills/vibeflow-spec-driven-dev/SKILL.md' },
38
+ ];
39
+
40
+ const EDITIONS = {
41
+ copilot: {
42
+ name: 'Copilot',
43
+ baseUrl: `https://raw.githubusercontent.com/${REPO}/${BRANCH}/copilot`,
44
+ files: COPILOT_FILES,
45
+ marker: '.github/prompts/vibeflow-analyze.prompt.md',
46
+ markerLegacy: '.github/prompts/vibeflow/vibeflow-analyze.prompt.md',
47
+ agentsSrc: 'AGENTS.md',
48
+ doneMessage: `Run ${pc.bold('/vibeflow-analyze')} in Copilot Chat to get started.`,
49
+ handleCopilotInstructions: true,
50
+ },
51
+ cursor: {
52
+ name: 'Cursor',
53
+ baseUrl: `https://raw.githubusercontent.com/${REPO}/${BRANCH}/cursor`,
54
+ files: CURSOR_FILES,
55
+ marker: '.cursor/rules/vibeflow.mdc',
56
+ markerLegacy: null,
57
+ agentsSrc: 'AGENTS.md',
58
+ doneMessage: `Type ${pc.bold('/vibeflow-analyze')} in Cursor Agent chat to get started.`,
59
+ handleCopilotInstructions: false,
60
+ },
61
+ };
62
+
27
63
  const DUPLICATE_MARKER = 'vibeflow-architect';
28
64
 
65
+ const GITIGNORE_MARKER = '# Vibeflow — installed + generated (remove to track in git)';
66
+
67
+ const GITIGNORE_BLOCKS = {
68
+ copilot: `
69
+ # Vibeflow — installed + generated (remove to track in git)
70
+ .vibeflow/
71
+ .github/prompts/vibeflow-*.prompt.md
72
+ .github/agents/vibeflow-architect.agent.md
73
+ .github/skills/vibeflow-spec-driven-dev/
74
+ .github/instructions/vibeflow/
75
+ `,
76
+ cursor: `
77
+ # Vibeflow — installed + generated (remove to track in git)
78
+ .vibeflow/
79
+ .cursor/rules/vibeflow.mdc
80
+ .cursor/rules/vibeflow-architect.mdc
81
+ .cursor/skills/vibeflow-*/
82
+ `,
83
+ };
84
+
29
85
  // --- Helpers ---
30
86
 
31
87
  function log(icon, msg) {
@@ -38,8 +94,8 @@ function ensureDir(dirPath) {
38
94
  }
39
95
  }
40
96
 
41
- async function downloadFile(srcPath) {
42
- const url = `${BASE_URL}/${srcPath}`;
97
+ async function downloadFile(baseUrl, srcPath) {
98
+ const url = `${baseUrl}/${srcPath}`;
43
99
  const res = await fetch(url);
44
100
  if (!res.ok) {
45
101
  throw new Error(`Failed to download ${url}: ${res.status} ${res.statusText}`);
@@ -48,27 +104,67 @@ async function downloadFile(srcPath) {
48
104
  }
49
105
 
50
106
  function extractAgentsAppendContent(fullContent) {
51
- // Remove the instruction note at the top (everything before and including the first ---)
52
107
  const separatorIndex = fullContent.indexOf('\n---\n');
53
108
  if (separatorIndex === -1) return fullContent;
54
109
  return fullContent.slice(separatorIndex + '\n---\n'.length);
55
110
  }
56
111
 
57
112
  function extractCopilotInstructionsSnippet(fullContent) {
58
- // Extract the markdown block between ```markdown and ```
59
113
  const match = fullContent.match(/```markdown\n([\s\S]*?)```/);
60
114
  if (!match) return null;
61
115
  return match[1].trim();
62
116
  }
63
117
 
118
+ function detectEdition() {
119
+ const args = process.argv.slice(2);
120
+ if (args.includes('--cursor')) return 'cursor';
121
+ if (args.includes('--copilot')) return 'copilot';
122
+ return null;
123
+ }
124
+
125
+ function printUsage() {
126
+ console.log('');
127
+ console.log(` ${pc.bold(pc.cyan('Vibeflow'))} ${pc.dim('— Setup')}`);
128
+ console.log('');
129
+ console.log(` ${pc.bold('Usage:')} npx setup-vibeflow@latest ${pc.cyan('<edition>')}`);
130
+ console.log('');
131
+ console.log(` ${pc.bold('Editions:')}`);
132
+ console.log(` ${pc.cyan('--copilot')} Install for GitHub Copilot ${pc.dim('(.github/prompts, agents, instructions)')}`);
133
+ console.log(` ${pc.cyan('--cursor')} Install for Cursor ${pc.dim('(.cursor/rules, skills)')}`);
134
+ console.log('');
135
+ console.log(` ${pc.bold('Options:')}`);
136
+ console.log(` ${pc.cyan('--force')} Overwrite existing files`);
137
+ console.log('');
138
+ console.log(` ${pc.bold('Examples:')}`);
139
+ console.log(` ${pc.dim('$')} npx setup-vibeflow@latest --copilot`);
140
+ console.log(` ${pc.dim('$')} npx setup-vibeflow@latest --cursor`);
141
+ console.log(` ${pc.dim('$')} npx setup-vibeflow@latest --cursor --force`);
142
+ console.log('');
143
+ }
144
+
64
145
  // --- Main ---
65
146
 
66
147
  async function main() {
67
148
  const force = process.argv.includes('--force');
149
+ const help = process.argv.includes('--help') || process.argv.includes('-h');
68
150
  const cwd = process.cwd();
69
151
 
152
+ if (help) {
153
+ printUsage();
154
+ process.exit(0);
155
+ }
156
+
157
+ const editionKey = detectEdition();
158
+
159
+ if (!editionKey) {
160
+ printUsage();
161
+ process.exit(1);
162
+ }
163
+
164
+ const edition = EDITIONS[editionKey];
165
+
70
166
  console.log('');
71
- console.log(` ${pc.bold(pc.cyan('Vibeflow'))} ${pc.dim('— Copilot Edition')}`);
167
+ console.log(` ${pc.bold(pc.cyan('Vibeflow'))} ${pc.dim(`— ${edition.name} Edition`)}`);
72
168
  console.log('');
73
169
 
74
170
  // Check Node.js version
@@ -78,10 +174,10 @@ async function main() {
78
174
  process.exit(1);
79
175
  }
80
176
 
81
- // Check if already installed (flat structure or legacy subdirectory structure)
82
- const markerFlat = join(cwd, '.github/prompts/vibeflow-analyze.prompt.md');
83
- const markerLegacy = join(cwd, '.github/prompts/vibeflow/vibeflow-analyze.prompt.md');
84
- if ((existsSync(markerFlat) || existsSync(markerLegacy)) && !force) {
177
+ // Check if already installed
178
+ const markerPath = join(cwd, edition.marker);
179
+ const markerLegacyPath = edition.markerLegacy ? join(cwd, edition.markerLegacy) : null;
180
+ if ((existsSync(markerPath) || (markerLegacyPath && existsSync(markerLegacyPath))) && !force) {
85
181
  log(pc.yellow('!'), 'Vibeflow already installed. Use --force to reinstall.');
86
182
  console.log('');
87
183
  process.exit(0);
@@ -91,7 +187,7 @@ async function main() {
91
187
  let created = 0;
92
188
  let skipped = 0;
93
189
 
94
- for (const file of FILES) {
190
+ for (const file of edition.files) {
95
191
  const destPath = join(cwd, file.dest);
96
192
  const destDir = join(destPath, '..');
97
193
 
@@ -102,7 +198,7 @@ async function main() {
102
198
  }
103
199
 
104
200
  try {
105
- const content = await downloadFile(file.src);
201
+ const content = await downloadFile(edition.baseUrl, file.src);
106
202
  ensureDir(destDir);
107
203
  writeFileSync(destPath, content, 'utf-8');
108
204
  log(pc.green('+'), file.dest);
@@ -118,7 +214,7 @@ async function main() {
118
214
  // Handle AGENTS.md
119
215
  const agentsPath = join(cwd, 'AGENTS.md');
120
216
  try {
121
- const agentsSource = await downloadFile('AGENTS.md');
217
+ const agentsSource = await downloadFile(edition.baseUrl, edition.agentsSrc);
122
218
  const appendContent = extractAgentsAppendContent(agentsSource);
123
219
 
124
220
  if (existsSync(agentsPath)) {
@@ -138,27 +234,50 @@ async function main() {
138
234
  log(pc.red('x'), `AGENTS.md — ${err.message}`);
139
235
  }
140
236
 
141
- // Handle copilot-instructions.md
142
- const copilotInstrPath = join(cwd, '.github/copilot-instructions.md');
237
+ // Handle copilot-instructions.md (Copilot only)
238
+ if (edition.handleCopilotInstructions) {
239
+ const copilotInstrPath = join(cwd, '.github/copilot-instructions.md');
240
+ try {
241
+ if (existsSync(copilotInstrPath)) {
242
+ const existing = readFileSync(copilotInstrPath, 'utf-8');
243
+ if (existing.includes('vibeflow') && !force) {
244
+ log(pc.dim('-'), `${pc.dim('.github/copilot-instructions.md')} ${pc.dim('(vibeflow snippet exists, skipped)')}`);
245
+ } else {
246
+ const snippetSource = await downloadFile(edition.baseUrl, 'copilot-instructions.md');
247
+ const snippet = extractCopilotInstructionsSnippet(snippetSource);
248
+ if (snippet) {
249
+ const updated = existing.trimEnd() + '\n\n' + snippet + '\n';
250
+ writeFileSync(copilotInstrPath, updated, 'utf-8');
251
+ log(pc.green('+'), `.github/copilot-instructions.md ${pc.dim('(appended vibeflow snippet)')}`);
252
+ }
253
+ }
254
+ } else {
255
+ log(pc.dim('-'), `${pc.dim('.github/copilot-instructions.md')} ${pc.dim('(not found, skipping — instructions auto-loaded)')}`);
256
+ }
257
+ } catch (err) {
258
+ log(pc.red('x'), `.github/copilot-instructions.md — ${err.message}`);
259
+ }
260
+ }
261
+
262
+ // Append Vibeflow paths to .gitignore (default: do not track; user can remove to track)
263
+ const gitignorePath = join(cwd, '.gitignore');
143
264
  try {
144
- if (existsSync(copilotInstrPath)) {
145
- const existing = readFileSync(copilotInstrPath, 'utf-8');
146
- if (existing.includes('vibeflow') && !force) {
147
- log(pc.dim('-'), `${pc.dim('.github/copilot-instructions.md')} ${pc.dim('(vibeflow snippet exists, skipped)')}`);
265
+ const block = GITIGNORE_BLOCKS[editionKey];
266
+ if (block) {
267
+ let content = '';
268
+ if (existsSync(gitignorePath)) {
269
+ content = readFileSync(gitignorePath, 'utf-8');
270
+ }
271
+ if (!content.includes(GITIGNORE_MARKER)) {
272
+ const updated = (content.trimEnd() ? content.trimEnd() + '\n' : '') + block.trim() + '\n';
273
+ writeFileSync(gitignorePath, updated, 'utf-8');
274
+ log(pc.green('+'), `.gitignore ${pc.dim('(Vibeflow paths added — remove that block to track in git)')}`);
148
275
  } else {
149
- const snippetSource = await downloadFile('copilot-instructions.md');
150
- const snippet = extractCopilotInstructionsSnippet(snippetSource);
151
- if (snippet) {
152
- const updated = existing.trimEnd() + '\n\n' + snippet + '\n';
153
- writeFileSync(copilotInstrPath, updated, 'utf-8');
154
- log(pc.green('+'), `.github/copilot-instructions.md ${pc.dim('(appended vibeflow snippet)')}`);
155
- }
276
+ log(pc.dim('-'), `${pc.dim('.gitignore')} ${pc.dim('(Vibeflow block already present)')}`);
156
277
  }
157
- } else {
158
- log(pc.dim('-'), `${pc.dim('.github/copilot-instructions.md')} ${pc.dim('(not found, skipping — instructions auto-loaded)')}`);
159
278
  }
160
279
  } catch (err) {
161
- log(pc.red('x'), `.github/copilot-instructions.md — ${err.message}`);
280
+ log(pc.red('x'), `.gitignore — ${err.message}`);
162
281
  }
163
282
 
164
283
  // Summary
@@ -169,7 +288,7 @@ async function main() {
169
288
  log(pc.yellow('!'), pc.bold('All files already exist. Nothing to do.'));
170
289
  }
171
290
  console.log('');
172
- log(pc.cyan('→'), `Run ${pc.bold('/vibeflow-analyze')} in Copilot Chat to get started.`);
291
+ log(pc.cyan('→'), edition.doneMessage);
173
292
  console.log('');
174
293
  }
175
294
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "setup-vibeflow",
3
- "version": "0.1.1",
3
+ "version": "0.3.0",
4
4
  "description": "Install Vibeflow (spec-driven development) in your project",
5
5
  "bin": {
6
6
  "setup-vibeflow": "./index.js"
@@ -13,9 +13,12 @@
13
13
  "vibeflow",
14
14
  "copilot",
15
15
  "github-copilot",
16
+ "cursor",
17
+ "cursor-ide",
16
18
  "spec-driven",
17
19
  "prompts",
18
- "agents"
20
+ "agents",
21
+ "skills"
19
22
  ],
20
23
  "author": "pe-menezes",
21
24
  "license": "MIT",