vibesuite 1.3.2 → 2.0.1
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.
- package/README.md +8 -1
- package/assets/.agent/skills/avoid-feature-creep/SKILL.md +307 -0
- package/assets/.agent/skills/avoid-feature-creep/agents/openai.yaml +3 -0
- package/assets/.agent/skills/avoid-feature-creep/assets/large-logo.png +0 -0
- package/assets/.agent/skills/avoid-feature-creep/assets/small-logo.svg +17 -0
- package/assets/.agent/skills/convex/SKILL.md +62 -0
- package/assets/.agent/skills/convex/agents/openai.yaml +3 -0
- package/assets/.agent/skills/convex/assets/large-logo.png +0 -0
- package/assets/.agent/skills/convex/assets/small-logo.svg +17 -0
- package/assets/.agent/skills/convex-agents/SKILL.md +516 -0
- package/assets/.agent/skills/convex-agents/agents/openai.yaml +3 -0
- package/assets/.agent/skills/convex-agents/assets/large-logo.png +0 -0
- package/assets/.agent/skills/convex-agents/assets/small-logo.svg +17 -0
- package/assets/.agent/skills/convex-best-practices/SKILL.md +369 -0
- package/assets/.agent/skills/convex-best-practices/agents/openai.yaml +3 -0
- package/assets/.agent/skills/convex-best-practices/assets/large-logo.png +0 -0
- package/assets/.agent/skills/convex-best-practices/assets/small-logo.svg +17 -0
- package/assets/.agent/skills/convex-component-authoring/SKILL.md +457 -0
- package/assets/.agent/skills/convex-component-authoring/agents/openai.yaml +3 -0
- package/assets/.agent/skills/convex-component-authoring/assets/large-logo.png +0 -0
- package/assets/.agent/skills/convex-component-authoring/assets/small-logo.svg +17 -0
- package/assets/.agent/skills/convex-cron-jobs/SKILL.md +604 -0
- package/assets/.agent/skills/convex-cron-jobs/agents/openai.yaml +3 -0
- package/assets/.agent/skills/convex-cron-jobs/assets/large-logo.png +0 -0
- package/assets/.agent/skills/convex-cron-jobs/assets/small-logo.svg +17 -0
- package/assets/.agent/skills/convex-file-storage/SKILL.md +467 -0
- package/assets/.agent/skills/convex-file-storage/agents/openai.yaml +3 -0
- package/assets/.agent/skills/convex-file-storage/assets/large-logo.png +0 -0
- package/assets/.agent/skills/convex-file-storage/assets/small-logo.svg +17 -0
- package/assets/.agent/skills/convex-functions/SKILL.md +458 -0
- package/assets/.agent/skills/convex-functions/agents/openai.yaml +3 -0
- package/assets/.agent/skills/convex-functions/assets/large-logo.png +0 -0
- package/assets/.agent/skills/convex-functions/assets/small-logo.svg +17 -0
- package/assets/.agent/skills/convex-http-actions/SKILL.md +733 -0
- package/assets/.agent/skills/convex-http-actions/agents/openai.yaml +3 -0
- package/assets/.agent/skills/convex-http-actions/assets/large-logo.png +0 -0
- package/assets/.agent/skills/convex-http-actions/assets/small-logo.svg +17 -0
- package/assets/.agent/skills/convex-migrations/SKILL.md +712 -0
- package/assets/.agent/skills/convex-migrations/agents/openai.yaml +3 -0
- package/assets/.agent/skills/convex-migrations/assets/large-logo.png +0 -0
- package/assets/.agent/skills/convex-migrations/assets/small-logo.svg +17 -0
- package/assets/.agent/skills/convex-realtime/SKILL.md +443 -0
- package/assets/.agent/skills/convex-realtime/agents/openai.yaml +3 -0
- package/assets/.agent/skills/convex-realtime/assets/large-logo.png +0 -0
- package/assets/.agent/skills/convex-realtime/assets/small-logo.svg +17 -0
- package/assets/.agent/skills/convex-schema-validator/SKILL.md +400 -0
- package/assets/.agent/skills/convex-schema-validator/agents/openai.yaml +3 -0
- package/assets/.agent/skills/convex-schema-validator/assets/large-logo.png +0 -0
- package/assets/.agent/skills/convex-schema-validator/assets/small-logo.svg +17 -0
- package/assets/.agent/skills/convex-security-audit/SKILL.md +539 -0
- package/assets/.agent/skills/convex-security-audit/agents/openai.yaml +3 -0
- package/assets/.agent/skills/convex-security-audit/assets/large-logo.png +0 -0
- package/assets/.agent/skills/convex-security-audit/assets/small-logo.svg +17 -0
- package/assets/.agent/skills/convex-security-check/SKILL.md +378 -0
- package/assets/.agent/skills/convex-security-check/agents/openai.yaml +3 -0
- package/assets/.agent/skills/convex-security-check/assets/large-logo.png +0 -0
- package/assets/.agent/skills/convex-security-check/assets/small-logo.svg +17 -0
- package/assets/.agent/skills/github-ops/SKILL.md +4 -4
- package/assets/.agent/skills/google-trends/SKILL.md +7 -7
- package/assets/.agent/skills/optimize-agent-context/SKILL.md +97 -0
- package/assets/.agent/skills/youtube-pipeline/SKILL.md +10 -10
- package/assets/.agent/workflows/LEGACY/init_smart_ops.md +2 -2
- package/assets/.agent/workflows/agent_reset.md +2 -2
- package/assets/.agent/workflows/mode-orchestrator.md +114 -640
- package/assets/.agent/workflows/mode-visionary.md +192 -0
- package/assets/.agent/workflows/optimize-agent-context.md +54 -0
- package/assets/.agent/workflows/remotion-build.md +17 -17
- package/assets/.agent/workflows/stitch.md +4 -4
- package/assets/VibeCode-Agents/custom_modes.yaml +1257 -0
- package/assets/VibeCode-Agents/vibe-orchestrator.yaml +427 -145
- package/assets/VibeCode-Agents/vibe-visionary.yaml +617 -0
- package/package.json +2 -2
- package/src/cli.js +416 -20
- package/src/harness.js +281 -0
- package/src/store.js +239 -0
package/src/harness.js
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import pc from 'picocolors';
|
|
5
|
+
|
|
6
|
+
// ─── Cross-Platform Home Directory ───────────────────────────────────────────
|
|
7
|
+
const HOME = os.homedir();
|
|
8
|
+
const IS_WINDOWS = process.platform === 'win32';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Resolve %APPDATA% on Windows, fallback for Mac/Linux
|
|
12
|
+
*/
|
|
13
|
+
function getAppData() {
|
|
14
|
+
if (IS_WINDOWS) {
|
|
15
|
+
return process.env.APPDATA || path.join(HOME, 'AppData', 'Roaming');
|
|
16
|
+
}
|
|
17
|
+
// Mac: ~/Library/Application Support, Linux: ~/.config
|
|
18
|
+
return process.env.XDG_CONFIG_HOME || path.join(HOME, '.config');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// ─── Harness Registry ────────────────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Each harness adapter defines:
|
|
25
|
+
* - name: Human-readable name
|
|
26
|
+
* - detect: Function returning true if the harness is installed
|
|
27
|
+
* - rootPath: Root config directory of the harness
|
|
28
|
+
* - targets: Where to copy skills, workflows, and yamls
|
|
29
|
+
* - skills: Path to global skills directory (or null if unsupported)
|
|
30
|
+
* - workflows: Path to global workflows directory (or null if unsupported)
|
|
31
|
+
* - yamls: Array of paths for custom_modes.yaml (or null)
|
|
32
|
+
*/
|
|
33
|
+
export const HARNESS_MAP = {
|
|
34
|
+
antigravity: {
|
|
35
|
+
name: 'Antigravity',
|
|
36
|
+
rootPath: path.join(HOME, '.gemini', 'antigravity'),
|
|
37
|
+
detect() {
|
|
38
|
+
return fs.existsSync(this.rootPath);
|
|
39
|
+
},
|
|
40
|
+
targets: {
|
|
41
|
+
skills: path.join(HOME, '.gemini', 'antigravity', 'skills'),
|
|
42
|
+
workflows: path.join(HOME, '.gemini', 'antigravity', 'global_workflows'),
|
|
43
|
+
yamls: null,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
kilocode: {
|
|
48
|
+
name: 'KiloCode',
|
|
49
|
+
rootPath: path.join(HOME, '.kilocode'),
|
|
50
|
+
detect() {
|
|
51
|
+
return fs.existsSync(this.rootPath);
|
|
52
|
+
},
|
|
53
|
+
targets: {
|
|
54
|
+
skills: path.join(HOME, '.kilocode', 'skills'),
|
|
55
|
+
workflows: path.join(HOME, '.kilocode', 'workflows'),
|
|
56
|
+
yamls: [
|
|
57
|
+
path.join(HOME, '.kilocode', 'cli', 'global', 'settings', 'custom_modes.yaml'),
|
|
58
|
+
path.join(getAppData(), 'Antigravity', 'User', 'globalStorage', 'kilocode.kilo-code', 'settings', 'custom_modes.yaml'),
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
windsurf: {
|
|
64
|
+
name: 'Windsurf',
|
|
65
|
+
rootPath: path.join(HOME, '.codeium', 'windsurf'),
|
|
66
|
+
detect() {
|
|
67
|
+
return fs.existsSync(this.rootPath);
|
|
68
|
+
},
|
|
69
|
+
targets: {
|
|
70
|
+
skills: path.join(HOME, '.codeium', 'windsurf', 'skills'),
|
|
71
|
+
workflows: path.join(HOME, '.codeium', 'windsurf', 'global_workflows'),
|
|
72
|
+
yamls: null,
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
cursor: {
|
|
77
|
+
name: 'Cursor',
|
|
78
|
+
rootPath: path.join(HOME, '.cursor'),
|
|
79
|
+
detect() {
|
|
80
|
+
return fs.existsSync(this.rootPath);
|
|
81
|
+
},
|
|
82
|
+
targets: {
|
|
83
|
+
skills: path.join(HOME, '.cursor', 'skills'),
|
|
84
|
+
workflows: null, // Cursor uses rules, not workflows
|
|
85
|
+
yamls: null,
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
gemini_cli: {
|
|
90
|
+
name: 'Gemini CLI',
|
|
91
|
+
rootPath: path.join(HOME, '.gemini'),
|
|
92
|
+
detect() {
|
|
93
|
+
// Only match bare Gemini CLI — not Antigravity (which also lives under .gemini)
|
|
94
|
+
return fs.existsSync(this.rootPath) && !fs.existsSync(path.join(HOME, '.gemini', 'antigravity'));
|
|
95
|
+
},
|
|
96
|
+
targets: {
|
|
97
|
+
skills: path.join(HOME, '.gemini', 'skills'),
|
|
98
|
+
workflows: null,
|
|
99
|
+
yamls: null,
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// ─── Detection ───────────────────────────────────────────────────────────────
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Scans the filesystem and returns an array of detected harness objects.
|
|
108
|
+
* Each object includes { id, name, rootPath, targets }.
|
|
109
|
+
* @returns {Array<{id: string, name: string, rootPath: string, targets: object}>}
|
|
110
|
+
*/
|
|
111
|
+
export function detectHarnesses() {
|
|
112
|
+
const detected = [];
|
|
113
|
+
|
|
114
|
+
for (const [id, harness] of Object.entries(HARNESS_MAP)) {
|
|
115
|
+
if (harness.detect()) {
|
|
116
|
+
detected.push({
|
|
117
|
+
id,
|
|
118
|
+
name: harness.name,
|
|
119
|
+
rootPath: harness.rootPath,
|
|
120
|
+
targets: harness.targets,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return detected;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Pretty-prints harness detection results.
|
|
130
|
+
* @param {Array} detected - Array from detectHarnesses()
|
|
131
|
+
*/
|
|
132
|
+
export function printHarnessStatus(detected) {
|
|
133
|
+
const detectedIds = new Set(detected.map(h => h.id));
|
|
134
|
+
|
|
135
|
+
console.log(pc.cyan('\n📡 Detecting AI harnesses...\n'));
|
|
136
|
+
|
|
137
|
+
for (const [id, harness] of Object.entries(HARNESS_MAP)) {
|
|
138
|
+
if (detectedIds.has(id)) {
|
|
139
|
+
console.log(pc.green(` ✔ ${harness.name.padEnd(16)} ${pc.dim(harness.rootPath)}`));
|
|
140
|
+
} else {
|
|
141
|
+
console.log(pc.dim(` ✗ ${harness.name.padEnd(16)} (not found)`));
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
console.log('');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ─── Sync Operations ─────────────────────────────────────────────────────────
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Syncs a source directory to a harness target path.
|
|
152
|
+
* Uses hard copy (fs.copy) — no symlinks.
|
|
153
|
+
*
|
|
154
|
+
* @param {string} sourcePath - Source directory (e.g., ~/.vibesuite/skills/)
|
|
155
|
+
* @param {string} targetPath - Destination directory (e.g., ~/.gemini/antigravity/skills/)
|
|
156
|
+
* @param {string} label - Human-readable label for logging
|
|
157
|
+
* @returns {Promise<number>} - Number of items copied
|
|
158
|
+
*/
|
|
159
|
+
export async function syncDirectory(sourcePath, targetPath, label = '') {
|
|
160
|
+
if (!targetPath) return 0;
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
await fs.ensureDir(targetPath);
|
|
164
|
+
|
|
165
|
+
const items = await fs.readdir(sourcePath);
|
|
166
|
+
let count = 0;
|
|
167
|
+
|
|
168
|
+
for (const item of items) {
|
|
169
|
+
const src = path.join(sourcePath, item);
|
|
170
|
+
const dest = path.join(targetPath, item);
|
|
171
|
+
|
|
172
|
+
await fs.copy(src, dest, { overwrite: true });
|
|
173
|
+
count++;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (label) {
|
|
177
|
+
console.log(pc.green(` ✔ ${label} (${count} items)`));
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return count;
|
|
181
|
+
} catch (error) {
|
|
182
|
+
if (label) {
|
|
183
|
+
console.log(pc.red(` ✗ ${label}: ${error.message}`));
|
|
184
|
+
}
|
|
185
|
+
return 0;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Syncs a single file to one or more target paths.
|
|
191
|
+
* Used for custom_modes.yaml → KiloCode dual-path sync.
|
|
192
|
+
*
|
|
193
|
+
* @param {string} sourceFile - Source file path
|
|
194
|
+
* @param {string|string[]} targetPaths - One or more destination file paths
|
|
195
|
+
* @param {string} label - Human-readable label for logging
|
|
196
|
+
* @returns {Promise<number>} - Number of successful copies
|
|
197
|
+
*/
|
|
198
|
+
export async function syncFile(sourceFile, targetPaths, label = '') {
|
|
199
|
+
if (!targetPaths) return 0;
|
|
200
|
+
|
|
201
|
+
const paths = Array.isArray(targetPaths) ? targetPaths : [targetPaths];
|
|
202
|
+
let count = 0;
|
|
203
|
+
|
|
204
|
+
for (const targetPath of paths) {
|
|
205
|
+
try {
|
|
206
|
+
await fs.ensureDir(path.dirname(targetPath));
|
|
207
|
+
await fs.copy(sourceFile, targetPath, { overwrite: true });
|
|
208
|
+
count++;
|
|
209
|
+
} catch (error) {
|
|
210
|
+
console.log(pc.yellow(` ⚠ Could not sync to ${path.basename(path.dirname(targetPath))}: ${error.message}`));
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (label && count > 0) {
|
|
215
|
+
console.log(pc.green(` ✔ ${label} (${count} path${count > 1 ? 's' : ''})`));
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return count;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Syncs skills, workflows, and yamls from the global store to a single harness.
|
|
223
|
+
*
|
|
224
|
+
* @param {{id: string, name: string, targets: object}} harness - Harness config
|
|
225
|
+
* @param {string} storePath - Path to the global store (~/.vibesuite/)
|
|
226
|
+
* @returns {Promise<{skills: number, workflows: number, yamls: number}>}
|
|
227
|
+
*/
|
|
228
|
+
export async function syncToHarness(harness, storePath) {
|
|
229
|
+
const results = { skills: 0, workflows: 0, yamls: 0 };
|
|
230
|
+
|
|
231
|
+
console.log(pc.cyan(`\n 📡 Syncing to ${harness.name}...`));
|
|
232
|
+
|
|
233
|
+
// Skills
|
|
234
|
+
const skillsStore = path.join(storePath, 'skills');
|
|
235
|
+
if (harness.targets.skills && await fs.pathExists(skillsStore)) {
|
|
236
|
+
results.skills = await syncDirectory(
|
|
237
|
+
skillsStore,
|
|
238
|
+
harness.targets.skills,
|
|
239
|
+
`Skills → ${harness.name}`
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Workflows
|
|
244
|
+
const workflowsStore = path.join(storePath, 'workflows');
|
|
245
|
+
if (harness.targets.workflows && await fs.pathExists(workflowsStore)) {
|
|
246
|
+
results.workflows = await syncDirectory(
|
|
247
|
+
workflowsStore,
|
|
248
|
+
harness.targets.workflows,
|
|
249
|
+
`Workflows → ${harness.name}`
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// YAMLs (KiloCode custom_modes.yaml dual-path)
|
|
254
|
+
const yamlSource = path.join(storePath, 'agents', 'custom_modes.yaml');
|
|
255
|
+
if (harness.targets.yamls && await fs.pathExists(yamlSource)) {
|
|
256
|
+
results.yamls = await syncFile(
|
|
257
|
+
yamlSource,
|
|
258
|
+
harness.targets.yamls,
|
|
259
|
+
`custom_modes.yaml → ${harness.name}`
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return results;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Syncs from the global store to ALL specified harnesses.
|
|
268
|
+
*
|
|
269
|
+
* @param {Array} harnesses - Array of harness configs from detectHarnesses()
|
|
270
|
+
* @param {string} storePath - Path to the global store (~/.vibesuite/)
|
|
271
|
+
* @returns {Promise<object>} - Summary of sync results
|
|
272
|
+
*/
|
|
273
|
+
export async function syncToAllHarnesses(harnesses, storePath) {
|
|
274
|
+
const summary = {};
|
|
275
|
+
|
|
276
|
+
for (const harness of harnesses) {
|
|
277
|
+
summary[harness.id] = await syncToHarness(harness, storePath);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return summary;
|
|
281
|
+
}
|
package/src/store.js
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import pc from 'picocolors';
|
|
5
|
+
import { PATHS } from './utils.js';
|
|
6
|
+
|
|
7
|
+
// ─── Global Store Path ───────────────────────────────────────────────────────
|
|
8
|
+
const HOME = os.homedir();
|
|
9
|
+
export const STORE_PATH = path.join(HOME, '.vibesuite');
|
|
10
|
+
export const MANIFEST_PATH = path.join(STORE_PATH, 'manifest.json');
|
|
11
|
+
|
|
12
|
+
// ─── Store Structure ─────────────────────────────────────────────────────────
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Creates the global store directory structure at ~/.vibesuite/
|
|
16
|
+
* Idempotent — safe to call multiple times.
|
|
17
|
+
* @returns {Promise<void>}
|
|
18
|
+
*/
|
|
19
|
+
export async function initGlobalStore() {
|
|
20
|
+
await fs.ensureDir(path.join(STORE_PATH, 'skills'));
|
|
21
|
+
await fs.ensureDir(path.join(STORE_PATH, 'workflows'));
|
|
22
|
+
await fs.ensureDir(path.join(STORE_PATH, 'agents'));
|
|
23
|
+
|
|
24
|
+
// Create manifest if it doesn't exist
|
|
25
|
+
if (!await fs.pathExists(MANIFEST_PATH)) {
|
|
26
|
+
const manifest = createDefaultManifest();
|
|
27
|
+
await writeManifest(manifest);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// ─── Manifest ────────────────────────────────────────────────────────────────
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Default manifest structure
|
|
35
|
+
*/
|
|
36
|
+
function createDefaultManifest() {
|
|
37
|
+
return {
|
|
38
|
+
version: '2.0.0',
|
|
39
|
+
createdAt: new Date().toISOString(),
|
|
40
|
+
updatedAt: new Date().toISOString(),
|
|
41
|
+
linkedHarnesses: [],
|
|
42
|
+
installed: {
|
|
43
|
+
skills: [],
|
|
44
|
+
workflows: [],
|
|
45
|
+
agents: [],
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Reads the manifest from disk.
|
|
52
|
+
* @returns {Promise<object>}
|
|
53
|
+
*/
|
|
54
|
+
export async function getManifest() {
|
|
55
|
+
try {
|
|
56
|
+
if (await fs.pathExists(MANIFEST_PATH)) {
|
|
57
|
+
return await fs.readJson(MANIFEST_PATH);
|
|
58
|
+
}
|
|
59
|
+
} catch {
|
|
60
|
+
// Corrupted manifest — recreate
|
|
61
|
+
}
|
|
62
|
+
return createDefaultManifest();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Writes the manifest to disk.
|
|
67
|
+
* @param {object} manifest
|
|
68
|
+
* @returns {Promise<void>}
|
|
69
|
+
*/
|
|
70
|
+
export async function writeManifest(manifest) {
|
|
71
|
+
manifest.updatedAt = new Date().toISOString();
|
|
72
|
+
await fs.writeJson(MANIFEST_PATH, manifest, { spaces: 2 });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ─── Populate Store from Package Assets ──────────────────────────────────────
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Copies skills from the bundled package assets into the global store.
|
|
79
|
+
* @param {'core'|'all'|string[]} mode - Which skills to install
|
|
80
|
+
* @returns {Promise<string[]>} - List of skill names copied
|
|
81
|
+
*/
|
|
82
|
+
export async function populateSkills(mode) {
|
|
83
|
+
const storeSkills = path.join(STORE_PATH, 'skills');
|
|
84
|
+
await fs.ensureDir(storeSkills);
|
|
85
|
+
|
|
86
|
+
const coreSkills = [
|
|
87
|
+
'ai-sdk', 'code-review', 'component-analysis', 'context7',
|
|
88
|
+
'nextjs-standards', 'security-audit', 'spawn-task', 'stitch',
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
let skillsToCopy = [];
|
|
92
|
+
|
|
93
|
+
if (mode === 'core') {
|
|
94
|
+
skillsToCopy = coreSkills;
|
|
95
|
+
} else if (mode === 'all') {
|
|
96
|
+
// Read all skill directories from package assets
|
|
97
|
+
const entries = await fs.readdir(PATHS.skills);
|
|
98
|
+
for (const entry of entries) {
|
|
99
|
+
const stat = await fs.stat(path.join(PATHS.skills, entry));
|
|
100
|
+
if (stat.isDirectory()) {
|
|
101
|
+
skillsToCopy.push(entry);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
} else if (Array.isArray(mode)) {
|
|
105
|
+
skillsToCopy = mode;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const copied = [];
|
|
109
|
+
for (const skill of skillsToCopy) {
|
|
110
|
+
const src = path.join(PATHS.skills, skill);
|
|
111
|
+
const dest = path.join(storeSkills, skill);
|
|
112
|
+
try {
|
|
113
|
+
if (await fs.pathExists(src)) {
|
|
114
|
+
await fs.copy(src, dest, { overwrite: true });
|
|
115
|
+
copied.push(skill);
|
|
116
|
+
}
|
|
117
|
+
} catch (error) {
|
|
118
|
+
console.log(pc.yellow(` ⚠ Could not copy skill "${skill}": ${error.message}`));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return copied;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Copies workflows from the bundled package assets into the global store.
|
|
127
|
+
* @param {'core'|'all'|'no-legacy'|string[]} mode - Which workflows to install
|
|
128
|
+
* @returns {Promise<string[]>} - List of workflow filenames copied
|
|
129
|
+
*/
|
|
130
|
+
export async function populateWorkflows(mode) {
|
|
131
|
+
const storeWorkflows = path.join(STORE_PATH, 'workflows');
|
|
132
|
+
await fs.ensureDir(storeWorkflows);
|
|
133
|
+
|
|
134
|
+
const coreWorkflows = [
|
|
135
|
+
'vibe-genesis.md', 'vibe-design.md', 'vibe-build.md',
|
|
136
|
+
'vibe-continueBuild.md', 'vibe-finalize.md', 'vibe-spawnTask.md',
|
|
137
|
+
'vibe-primeAgent.md', 'vibe-syncDocs.md', 'stitch.md',
|
|
138
|
+
'mode-orchestrator.md', 'mode-architect.md', 'mode-code.md',
|
|
139
|
+
'mode-debug.md', 'mode-ask.md', 'mode-review.md',
|
|
140
|
+
];
|
|
141
|
+
|
|
142
|
+
let workflowsToCopy = [];
|
|
143
|
+
|
|
144
|
+
if (mode === 'core') {
|
|
145
|
+
workflowsToCopy = coreWorkflows;
|
|
146
|
+
} else if (mode === 'all' || mode === 'no-legacy') {
|
|
147
|
+
const entries = await fs.readdir(PATHS.workflows);
|
|
148
|
+
workflowsToCopy = entries.filter(f => {
|
|
149
|
+
if (!f.endsWith('.md')) return false;
|
|
150
|
+
if (mode === 'no-legacy' && f.startsWith('LEGACY')) return false;
|
|
151
|
+
return true;
|
|
152
|
+
});
|
|
153
|
+
} else if (Array.isArray(mode)) {
|
|
154
|
+
workflowsToCopy = mode;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const copied = [];
|
|
158
|
+
for (const workflow of workflowsToCopy) {
|
|
159
|
+
const src = path.join(PATHS.workflows, workflow);
|
|
160
|
+
const dest = path.join(storeWorkflows, workflow);
|
|
161
|
+
try {
|
|
162
|
+
if (await fs.pathExists(src)) {
|
|
163
|
+
await fs.copy(src, dest, { overwrite: true });
|
|
164
|
+
copied.push(workflow);
|
|
165
|
+
}
|
|
166
|
+
} catch (error) {
|
|
167
|
+
console.log(pc.yellow(` ⚠ Could not copy workflow "${workflow}": ${error.message}`));
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return copied;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Copies agent YAMLs (custom_modes.yaml, etc.) into the global store.
|
|
176
|
+
* @returns {Promise<string[]>} - List of YAML filenames copied
|
|
177
|
+
*/
|
|
178
|
+
export async function populateAgentYamls() {
|
|
179
|
+
const storeAgents = path.join(STORE_PATH, 'agents');
|
|
180
|
+
await fs.ensureDir(storeAgents);
|
|
181
|
+
|
|
182
|
+
const copied = [];
|
|
183
|
+
try {
|
|
184
|
+
const entries = await fs.readdir(PATHS.agentsYaml);
|
|
185
|
+
for (const entry of entries) {
|
|
186
|
+
const src = path.join(PATHS.agentsYaml, entry);
|
|
187
|
+
const dest = path.join(storeAgents, entry);
|
|
188
|
+
await fs.copy(src, dest, { overwrite: true });
|
|
189
|
+
copied.push(entry);
|
|
190
|
+
}
|
|
191
|
+
} catch (error) {
|
|
192
|
+
console.log(pc.yellow(` ⚠ Could not copy agent YAMLs: ${error.message}`));
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return copied;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// ─── Store Queries ───────────────────────────────────────────────────────────
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Lists all skills currently in the global store.
|
|
202
|
+
* @returns {Promise<string[]>}
|
|
203
|
+
*/
|
|
204
|
+
export async function getStoreSkills() {
|
|
205
|
+
const skillsDir = path.join(STORE_PATH, 'skills');
|
|
206
|
+
try {
|
|
207
|
+
const entries = await fs.readdir(skillsDir);
|
|
208
|
+
const skills = [];
|
|
209
|
+
for (const entry of entries) {
|
|
210
|
+
const stat = await fs.stat(path.join(skillsDir, entry));
|
|
211
|
+
if (stat.isDirectory()) skills.push(entry);
|
|
212
|
+
}
|
|
213
|
+
return skills;
|
|
214
|
+
} catch {
|
|
215
|
+
return [];
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Lists all workflows currently in the global store.
|
|
221
|
+
* @returns {Promise<string[]>}
|
|
222
|
+
*/
|
|
223
|
+
export async function getStoreWorkflows() {
|
|
224
|
+
const workflowsDir = path.join(STORE_PATH, 'workflows');
|
|
225
|
+
try {
|
|
226
|
+
const entries = await fs.readdir(workflowsDir);
|
|
227
|
+
return entries.filter(f => f.endsWith('.md'));
|
|
228
|
+
} catch {
|
|
229
|
+
return [];
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Checks if the global store has been initialized.
|
|
235
|
+
* @returns {Promise<boolean>}
|
|
236
|
+
*/
|
|
237
|
+
export async function isStoreInitialized() {
|
|
238
|
+
return fs.pathExists(MANIFEST_PATH);
|
|
239
|
+
}
|