setup-vibeflow 0.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 (2) hide show
  1. package/index.js +180 -0
  2. package/package.json +29 -0
package/index.js ADDED
@@ -0,0 +1,180 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
4
+ import { join } from 'node:path';
5
+ import pc from 'picocolors';
6
+
7
+ // --- Config ---
8
+
9
+ const REPO = 'pe-menezes/vibeflow';
10
+ const BRANCH = 'main';
11
+ const BASE_URL = `https://raw.githubusercontent.com/${REPO}/${BRANCH}/copilot`;
12
+
13
+ const FILES = [
14
+ { src: 'github/prompts/vibeflow-analyze.prompt.md', dest: '.github/prompts/vibeflow-analyze.prompt.md' },
15
+ { src: 'github/prompts/vibeflow-audit.prompt.md', dest: '.github/prompts/vibeflow-audit.prompt.md' },
16
+ { src: 'github/prompts/vibeflow-discover.prompt.md', dest: '.github/prompts/vibeflow-discover.prompt.md' },
17
+ { src: 'github/prompts/vibeflow-gen-spec.prompt.md', dest: '.github/prompts/vibeflow-gen-spec.prompt.md' },
18
+ { src: 'github/prompts/vibeflow-prompt-pack.prompt.md', dest: '.github/prompts/vibeflow-prompt-pack.prompt.md' },
19
+ { src: 'github/prompts/vibeflow-quick.prompt.md', dest: '.github/prompts/vibeflow-quick.prompt.md' },
20
+ { src: 'github/prompts/vibeflow-stats.prompt.md', dest: '.github/prompts/vibeflow-stats.prompt.md' },
21
+ { src: 'github/prompts/vibeflow-teach.prompt.md', dest: '.github/prompts/vibeflow-teach.prompt.md' },
22
+ { src: 'github/agents/vibeflow-architect.agent.md', dest: '.github/agents/vibeflow-architect.agent.md' },
23
+ { src: 'github/skills/vibeflow-spec-driven-dev/SKILL.md', dest: '.github/skills/vibeflow-spec-driven-dev/SKILL.md' },
24
+ { src: 'github/instructions/vibeflow/vibeflow.instructions.md', dest: '.github/instructions/vibeflow/vibeflow.instructions.md' },
25
+ ];
26
+
27
+ const DUPLICATE_MARKER = 'vibeflow-architect';
28
+
29
+ // --- Helpers ---
30
+
31
+ function log(icon, msg) {
32
+ console.log(` ${icon} ${msg}`);
33
+ }
34
+
35
+ function ensureDir(dirPath) {
36
+ if (!existsSync(dirPath)) {
37
+ mkdirSync(dirPath, { recursive: true });
38
+ }
39
+ }
40
+
41
+ async function downloadFile(srcPath) {
42
+ const url = `${BASE_URL}/${srcPath}`;
43
+ const res = await fetch(url);
44
+ if (!res.ok) {
45
+ throw new Error(`Failed to download ${url}: ${res.status} ${res.statusText}`);
46
+ }
47
+ return res.text();
48
+ }
49
+
50
+ function extractAgentsAppendContent(fullContent) {
51
+ // Remove the instruction note at the top (everything before and including the first ---)
52
+ const separatorIndex = fullContent.indexOf('\n---\n');
53
+ if (separatorIndex === -1) return fullContent;
54
+ return fullContent.slice(separatorIndex + '\n---\n'.length);
55
+ }
56
+
57
+ function extractCopilotInstructionsSnippet(fullContent) {
58
+ // Extract the markdown block between ```markdown and ```
59
+ const match = fullContent.match(/```markdown\n([\s\S]*?)```/);
60
+ if (!match) return null;
61
+ return match[1].trim();
62
+ }
63
+
64
+ // --- Main ---
65
+
66
+ async function main() {
67
+ const force = process.argv.includes('--force');
68
+ const cwd = process.cwd();
69
+
70
+ console.log('');
71
+ console.log(` ${pc.bold(pc.cyan('Vibeflow'))} ${pc.dim('— Copilot Edition')}`);
72
+ console.log('');
73
+
74
+ // Check Node.js version
75
+ const nodeVersion = parseInt(process.versions.node.split('.')[0]);
76
+ if (nodeVersion < 18) {
77
+ log(pc.red('x'), `Node.js 18+ required (you have ${process.versions.node})`);
78
+ process.exit(1);
79
+ }
80
+
81
+ // Check if already installed
82
+ const markerFile = join(cwd, '.github/prompts/vibeflow-analyze.prompt.md');
83
+ if (existsSync(markerFile) && !force) {
84
+ log(pc.yellow('!'), 'Vibeflow already installed. Use --force to reinstall.');
85
+ console.log('');
86
+ process.exit(0);
87
+ }
88
+
89
+ // Download and install files
90
+ let created = 0;
91
+ let skipped = 0;
92
+
93
+ for (const file of FILES) {
94
+ const destPath = join(cwd, file.dest);
95
+ const destDir = join(destPath, '..');
96
+
97
+ if (existsSync(destPath) && !force) {
98
+ log(pc.dim('-'), `${pc.dim(file.dest)} ${pc.dim('(exists, skipped)')}`);
99
+ skipped++;
100
+ continue;
101
+ }
102
+
103
+ try {
104
+ const content = await downloadFile(file.src);
105
+ ensureDir(destDir);
106
+ writeFileSync(destPath, content, 'utf-8');
107
+ log(pc.green('+'), file.dest);
108
+ created++;
109
+ } catch (err) {
110
+ log(pc.red('x'), `${file.dest} — ${err.message}`);
111
+ process.exit(1);
112
+ }
113
+ }
114
+
115
+ console.log('');
116
+
117
+ // Handle AGENTS.md
118
+ const agentsPath = join(cwd, 'AGENTS.md');
119
+ try {
120
+ const agentsSource = await downloadFile('AGENTS.md');
121
+ const appendContent = extractAgentsAppendContent(agentsSource);
122
+
123
+ if (existsSync(agentsPath)) {
124
+ const existing = readFileSync(agentsPath, 'utf-8');
125
+ if (existing.includes(DUPLICATE_MARKER) && !force) {
126
+ log(pc.dim('-'), `${pc.dim('AGENTS.md')} ${pc.dim('(vibeflow block exists, skipped)')}`);
127
+ } else {
128
+ const updated = existing.trimEnd() + '\n\n' + appendContent;
129
+ writeFileSync(agentsPath, updated, 'utf-8');
130
+ log(pc.green('+'), `AGENTS.md ${pc.dim('(appended vibeflow block)')}`);
131
+ }
132
+ } else {
133
+ writeFileSync(agentsPath, appendContent, 'utf-8');
134
+ log(pc.green('+'), `AGENTS.md ${pc.dim('(created)')}`);
135
+ }
136
+ } catch (err) {
137
+ log(pc.red('x'), `AGENTS.md — ${err.message}`);
138
+ }
139
+
140
+ // Handle copilot-instructions.md
141
+ const copilotInstrPath = join(cwd, '.github/copilot-instructions.md');
142
+ try {
143
+ if (existsSync(copilotInstrPath)) {
144
+ const existing = readFileSync(copilotInstrPath, 'utf-8');
145
+ if (existing.includes('vibeflow') && !force) {
146
+ log(pc.dim('-'), `${pc.dim('.github/copilot-instructions.md')} ${pc.dim('(vibeflow snippet exists, skipped)')}`);
147
+ } else {
148
+ const snippetSource = await downloadFile('copilot-instructions.md');
149
+ const snippet = extractCopilotInstructionsSnippet(snippetSource);
150
+ if (snippet) {
151
+ const updated = existing.trimEnd() + '\n\n' + snippet + '\n';
152
+ writeFileSync(copilotInstrPath, updated, 'utf-8');
153
+ log(pc.green('+'), `.github/copilot-instructions.md ${pc.dim('(appended vibeflow snippet)')}`);
154
+ }
155
+ }
156
+ } else {
157
+ log(pc.dim('-'), `${pc.dim('.github/copilot-instructions.md')} ${pc.dim('(not found, skipping — instructions auto-loaded)')}`);
158
+ }
159
+ } catch (err) {
160
+ log(pc.red('x'), `.github/copilot-instructions.md — ${err.message}`);
161
+ }
162
+
163
+ // Summary
164
+ console.log('');
165
+ if (created > 0) {
166
+ log(pc.green('✓'), pc.bold(`Done! ${created} files installed.`));
167
+ } else if (skipped > 0) {
168
+ log(pc.yellow('!'), pc.bold('All files already exist. Nothing to do.'));
169
+ }
170
+ console.log('');
171
+ log(pc.cyan('→'), `Run ${pc.bold('/vibeflow-analyze')} in Copilot Chat to get started.`);
172
+ console.log('');
173
+ }
174
+
175
+ main().catch((err) => {
176
+ console.error('');
177
+ console.error(` ${pc.red('Error:')} ${err.message}`);
178
+ console.error('');
179
+ process.exit(1);
180
+ });
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "setup-vibeflow",
3
+ "version": "0.1.0",
4
+ "description": "Install Vibeflow (spec-driven development) in your project",
5
+ "bin": {
6
+ "setup-vibeflow": "./index.js"
7
+ },
8
+ "type": "module",
9
+ "engines": {
10
+ "node": ">=18"
11
+ },
12
+ "keywords": [
13
+ "vibeflow",
14
+ "copilot",
15
+ "github-copilot",
16
+ "spec-driven",
17
+ "prompts",
18
+ "agents"
19
+ ],
20
+ "author": "pe-menezes",
21
+ "license": "MIT",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/pe-menezes/vibeflow"
25
+ },
26
+ "dependencies": {
27
+ "picocolors": "^1.1.0"
28
+ }
29
+ }