setup-vibeflow 0.1.1 → 0.2.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 +111 -33
  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,6 +23,43 @@ 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
 
29
65
  // --- Helpers ---
@@ -38,8 +74,8 @@ function ensureDir(dirPath) {
38
74
  }
39
75
  }
40
76
 
41
- async function downloadFile(srcPath) {
42
- const url = `${BASE_URL}/${srcPath}`;
77
+ async function downloadFile(baseUrl, srcPath) {
78
+ const url = `${baseUrl}/${srcPath}`;
43
79
  const res = await fetch(url);
44
80
  if (!res.ok) {
45
81
  throw new Error(`Failed to download ${url}: ${res.status} ${res.statusText}`);
@@ -48,27 +84,67 @@ async function downloadFile(srcPath) {
48
84
  }
49
85
 
50
86
  function extractAgentsAppendContent(fullContent) {
51
- // Remove the instruction note at the top (everything before and including the first ---)
52
87
  const separatorIndex = fullContent.indexOf('\n---\n');
53
88
  if (separatorIndex === -1) return fullContent;
54
89
  return fullContent.slice(separatorIndex + '\n---\n'.length);
55
90
  }
56
91
 
57
92
  function extractCopilotInstructionsSnippet(fullContent) {
58
- // Extract the markdown block between ```markdown and ```
59
93
  const match = fullContent.match(/```markdown\n([\s\S]*?)```/);
60
94
  if (!match) return null;
61
95
  return match[1].trim();
62
96
  }
63
97
 
98
+ function detectEdition() {
99
+ const args = process.argv.slice(2);
100
+ if (args.includes('--cursor')) return 'cursor';
101
+ if (args.includes('--copilot')) return 'copilot';
102
+ return null;
103
+ }
104
+
105
+ function printUsage() {
106
+ console.log('');
107
+ console.log(` ${pc.bold(pc.cyan('Vibeflow'))} ${pc.dim('— Setup')}`);
108
+ console.log('');
109
+ console.log(` ${pc.bold('Usage:')} npx setup-vibeflow@latest ${pc.cyan('<edition>')}`);
110
+ console.log('');
111
+ console.log(` ${pc.bold('Editions:')}`);
112
+ console.log(` ${pc.cyan('--copilot')} Install for GitHub Copilot ${pc.dim('(.github/prompts, agents, instructions)')}`);
113
+ console.log(` ${pc.cyan('--cursor')} Install for Cursor ${pc.dim('(.cursor/rules, skills)')}`);
114
+ console.log('');
115
+ console.log(` ${pc.bold('Options:')}`);
116
+ console.log(` ${pc.cyan('--force')} Overwrite existing files`);
117
+ console.log('');
118
+ console.log(` ${pc.bold('Examples:')}`);
119
+ console.log(` ${pc.dim('$')} npx setup-vibeflow@latest --copilot`);
120
+ console.log(` ${pc.dim('$')} npx setup-vibeflow@latest --cursor`);
121
+ console.log(` ${pc.dim('$')} npx setup-vibeflow@latest --cursor --force`);
122
+ console.log('');
123
+ }
124
+
64
125
  // --- Main ---
65
126
 
66
127
  async function main() {
67
128
  const force = process.argv.includes('--force');
129
+ const help = process.argv.includes('--help') || process.argv.includes('-h');
68
130
  const cwd = process.cwd();
69
131
 
132
+ if (help) {
133
+ printUsage();
134
+ process.exit(0);
135
+ }
136
+
137
+ const editionKey = detectEdition();
138
+
139
+ if (!editionKey) {
140
+ printUsage();
141
+ process.exit(1);
142
+ }
143
+
144
+ const edition = EDITIONS[editionKey];
145
+
70
146
  console.log('');
71
- console.log(` ${pc.bold(pc.cyan('Vibeflow'))} ${pc.dim('— Copilot Edition')}`);
147
+ console.log(` ${pc.bold(pc.cyan('Vibeflow'))} ${pc.dim(`— ${edition.name} Edition`)}`);
72
148
  console.log('');
73
149
 
74
150
  // Check Node.js version
@@ -78,10 +154,10 @@ async function main() {
78
154
  process.exit(1);
79
155
  }
80
156
 
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) {
157
+ // Check if already installed
158
+ const markerPath = join(cwd, edition.marker);
159
+ const markerLegacyPath = edition.markerLegacy ? join(cwd, edition.markerLegacy) : null;
160
+ if ((existsSync(markerPath) || (markerLegacyPath && existsSync(markerLegacyPath))) && !force) {
85
161
  log(pc.yellow('!'), 'Vibeflow already installed. Use --force to reinstall.');
86
162
  console.log('');
87
163
  process.exit(0);
@@ -91,7 +167,7 @@ async function main() {
91
167
  let created = 0;
92
168
  let skipped = 0;
93
169
 
94
- for (const file of FILES) {
170
+ for (const file of edition.files) {
95
171
  const destPath = join(cwd, file.dest);
96
172
  const destDir = join(destPath, '..');
97
173
 
@@ -102,7 +178,7 @@ async function main() {
102
178
  }
103
179
 
104
180
  try {
105
- const content = await downloadFile(file.src);
181
+ const content = await downloadFile(edition.baseUrl, file.src);
106
182
  ensureDir(destDir);
107
183
  writeFileSync(destPath, content, 'utf-8');
108
184
  log(pc.green('+'), file.dest);
@@ -118,7 +194,7 @@ async function main() {
118
194
  // Handle AGENTS.md
119
195
  const agentsPath = join(cwd, 'AGENTS.md');
120
196
  try {
121
- const agentsSource = await downloadFile('AGENTS.md');
197
+ const agentsSource = await downloadFile(edition.baseUrl, edition.agentsSrc);
122
198
  const appendContent = extractAgentsAppendContent(agentsSource);
123
199
 
124
200
  if (existsSync(agentsPath)) {
@@ -138,27 +214,29 @@ async function main() {
138
214
  log(pc.red('x'), `AGENTS.md — ${err.message}`);
139
215
  }
140
216
 
141
- // Handle copilot-instructions.md
142
- const copilotInstrPath = join(cwd, '.github/copilot-instructions.md');
143
- 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)')}`);
148
- } 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)')}`);
217
+ // Handle copilot-instructions.md (Copilot only)
218
+ if (edition.handleCopilotInstructions) {
219
+ const copilotInstrPath = join(cwd, '.github/copilot-instructions.md');
220
+ try {
221
+ if (existsSync(copilotInstrPath)) {
222
+ const existing = readFileSync(copilotInstrPath, 'utf-8');
223
+ if (existing.includes('vibeflow') && !force) {
224
+ log(pc.dim('-'), `${pc.dim('.github/copilot-instructions.md')} ${pc.dim('(vibeflow snippet exists, skipped)')}`);
225
+ } else {
226
+ const snippetSource = await downloadFile(edition.baseUrl, 'copilot-instructions.md');
227
+ const snippet = extractCopilotInstructionsSnippet(snippetSource);
228
+ if (snippet) {
229
+ const updated = existing.trimEnd() + '\n\n' + snippet + '\n';
230
+ writeFileSync(copilotInstrPath, updated, 'utf-8');
231
+ log(pc.green('+'), `.github/copilot-instructions.md ${pc.dim('(appended vibeflow snippet)')}`);
232
+ }
155
233
  }
234
+ } else {
235
+ log(pc.dim('-'), `${pc.dim('.github/copilot-instructions.md')} ${pc.dim('(not found, skipping — instructions auto-loaded)')}`);
156
236
  }
157
- } else {
158
- log(pc.dim('-'), `${pc.dim('.github/copilot-instructions.md')} ${pc.dim('(not found, skipping — instructions auto-loaded)')}`);
237
+ } catch (err) {
238
+ log(pc.red('x'), `.github/copilot-instructions.md ${err.message}`);
159
239
  }
160
- } catch (err) {
161
- log(pc.red('x'), `.github/copilot-instructions.md — ${err.message}`);
162
240
  }
163
241
 
164
242
  // Summary
@@ -169,7 +247,7 @@ async function main() {
169
247
  log(pc.yellow('!'), pc.bold('All files already exist. Nothing to do.'));
170
248
  }
171
249
  console.log('');
172
- log(pc.cyan('→'), `Run ${pc.bold('/vibeflow-analyze')} in Copilot Chat to get started.`);
250
+ log(pc.cyan('→'), edition.doneMessage);
173
251
  console.log('');
174
252
  }
175
253
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "setup-vibeflow",
3
- "version": "0.1.1",
3
+ "version": "0.2.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",