myaidev-method 0.2.18 ā 0.2.22
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/.claude/mcp/sparc-orchestrator-server.js +0 -0
- package/.claude/mcp/wordpress-server.js +0 -0
- package/CHANGELOG.md +145 -0
- package/README.md +205 -13
- package/TECHNICAL_ARCHITECTURE.md +64 -2
- package/bin/cli.js +169 -2
- package/dist/mcp/mcp-config.json +138 -1
- package/dist/mcp/openstack-server.js +1607 -0
- package/package.json +2 -2
- package/src/config/workflows.js +532 -0
- package/src/lib/payloadcms-utils.js +343 -10
- package/src/lib/visual-generation-utils.js +445 -294
- package/src/lib/workflow-installer.js +512 -0
- package/src/libs/security/authorization-checker.js +606 -0
- package/src/mcp/openstack-server.js +1607 -0
- package/src/scripts/openstack-setup.sh +110 -0
- package/src/scripts/security/environment-detect.js +425 -0
- package/src/templates/claude/agents/openstack-vm-manager.md +281 -0
- package/src/templates/claude/agents/osint-researcher.md +1075 -0
- package/src/templates/claude/agents/penetration-tester.md +908 -0
- package/src/templates/claude/agents/security-auditor.md +244 -0
- package/src/templates/claude/agents/security-setup.md +1094 -0
- package/src/templates/claude/agents/webapp-security-tester.md +581 -0
- package/src/templates/claude/commands/myai-configure.md +84 -0
- package/src/templates/claude/commands/myai-openstack.md +229 -0
- package/src/templates/claude/commands/sc:security-exploit.md +464 -0
- package/src/templates/claude/commands/sc:security-recon.md +281 -0
- package/src/templates/claude/commands/sc:security-report.md +756 -0
- package/src/templates/claude/commands/sc:security-scan.md +441 -0
- package/src/templates/claude/commands/sc:security-setup.md +501 -0
- package/src/templates/claude/mcp_config.json +44 -0
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MyAIDev Method - Workflow Installer
|
|
3
|
+
*
|
|
4
|
+
* Handles modular installation of workflows with dependency resolution
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import fs from 'fs/promises';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
import { WORKFLOWS, resolveWorkflowDependencies, validateWorkflow } from '../config/workflows.js';
|
|
11
|
+
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = path.dirname(__filename);
|
|
14
|
+
|
|
15
|
+
class WorkflowInstaller {
|
|
16
|
+
constructor(options = {}) {
|
|
17
|
+
this.projectRoot = options.projectRoot || process.cwd();
|
|
18
|
+
this.claudeDir = path.join(this.projectRoot, '.claude');
|
|
19
|
+
this.manifestPath = path.join(this.projectRoot, '.myaidev-manifest.json');
|
|
20
|
+
this.verbose = options.verbose || false;
|
|
21
|
+
this.dryRun = options.dryRun || false;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Log message if verbose mode is enabled
|
|
26
|
+
* @param {string} message - Message to log
|
|
27
|
+
*/
|
|
28
|
+
log(message) {
|
|
29
|
+
if (this.verbose) {
|
|
30
|
+
console.log(message);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Read installation manifest
|
|
36
|
+
* @returns {Promise<Object>} Installation manifest
|
|
37
|
+
*/
|
|
38
|
+
async readManifest() {
|
|
39
|
+
try {
|
|
40
|
+
const data = await fs.readFile(this.manifestPath, 'utf8');
|
|
41
|
+
return JSON.parse(data);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
// Manifest doesn't exist, return empty
|
|
44
|
+
return {
|
|
45
|
+
version: '1.0.0',
|
|
46
|
+
installedWorkflows: [],
|
|
47
|
+
installedAt: new Date().toISOString(),
|
|
48
|
+
lastModified: new Date().toISOString()
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Write installation manifest
|
|
55
|
+
* @param {Object} manifest - Installation manifest
|
|
56
|
+
* @returns {Promise<void>}
|
|
57
|
+
*/
|
|
58
|
+
async writeManifest(manifest) {
|
|
59
|
+
if (this.dryRun) {
|
|
60
|
+
this.log('[DRY RUN] Would write manifest to ' + this.manifestPath);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
manifest.lastModified = new Date().toISOString();
|
|
65
|
+
await fs.writeFile(
|
|
66
|
+
this.manifestPath,
|
|
67
|
+
JSON.stringify(manifest, null, 2),
|
|
68
|
+
'utf8'
|
|
69
|
+
);
|
|
70
|
+
this.log('Updated installation manifest');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Resolve workflow dependencies
|
|
75
|
+
* @param {string[]} workflowNames - Workflow names to install
|
|
76
|
+
* @returns {string[]} Resolved workflow names in dependency order
|
|
77
|
+
*/
|
|
78
|
+
resolveWorkflows(workflowNames) {
|
|
79
|
+
const resolved = new Set();
|
|
80
|
+
const order = [];
|
|
81
|
+
|
|
82
|
+
for (const name of workflowNames) {
|
|
83
|
+
const dependencies = resolveWorkflowDependencies(name, new Set());
|
|
84
|
+
for (const dep of dependencies) {
|
|
85
|
+
if (!resolved.has(dep)) {
|
|
86
|
+
order.push(dep);
|
|
87
|
+
resolved.add(dep);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return order;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Install agent files
|
|
97
|
+
* @param {string[]} agents - Agent names
|
|
98
|
+
* @returns {Promise<number>} Number of agents installed
|
|
99
|
+
*/
|
|
100
|
+
async installAgents(agents) {
|
|
101
|
+
if (agents.length === 0) return 0;
|
|
102
|
+
|
|
103
|
+
const agentsDir = path.join(this.claudeDir, 'agents');
|
|
104
|
+
if (!this.dryRun) {
|
|
105
|
+
await fs.mkdir(agentsDir, { recursive: true });
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
let installed = 0;
|
|
109
|
+
for (const agent of agents) {
|
|
110
|
+
const sourcePath = path.join(__dirname, '..', 'agents', `${agent}.md`);
|
|
111
|
+
const targetPath = path.join(agentsDir, `${agent}.md`);
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
if (this.dryRun) {
|
|
115
|
+
this.log(`[DRY RUN] Would copy agent: ${agent}`);
|
|
116
|
+
} else {
|
|
117
|
+
await fs.copyFile(sourcePath, targetPath);
|
|
118
|
+
this.log(`Installed agent: ${agent}`);
|
|
119
|
+
}
|
|
120
|
+
installed++;
|
|
121
|
+
} catch (error) {
|
|
122
|
+
console.warn(`Warning: Could not install agent ${agent}: ${error.message}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return installed;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Install command files
|
|
131
|
+
* @param {string[]} commands - Command names
|
|
132
|
+
* @returns {Promise<number>} Number of commands installed
|
|
133
|
+
*/
|
|
134
|
+
async installCommands(commands) {
|
|
135
|
+
if (commands.length === 0) return 0;
|
|
136
|
+
|
|
137
|
+
const commandsDir = path.join(this.claudeDir, 'commands');
|
|
138
|
+
if (!this.dryRun) {
|
|
139
|
+
await fs.mkdir(commandsDir, { recursive: true });
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
let installed = 0;
|
|
143
|
+
for (const command of commands) {
|
|
144
|
+
const sourcePath = path.join(__dirname, '..', 'commands', `${command}.md`);
|
|
145
|
+
const targetPath = path.join(commandsDir, `${command}.md`);
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
if (this.dryRun) {
|
|
149
|
+
this.log(`[DRY RUN] Would copy command: ${command}`);
|
|
150
|
+
} else {
|
|
151
|
+
await fs.copyFile(sourcePath, targetPath);
|
|
152
|
+
this.log(`Installed command: ${command}`);
|
|
153
|
+
}
|
|
154
|
+
installed++;
|
|
155
|
+
} catch (error) {
|
|
156
|
+
console.warn(`Warning: Could not install command ${command}: ${error.message}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return installed;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Install script files
|
|
165
|
+
* @param {string[]} scripts - Script paths
|
|
166
|
+
* @returns {Promise<number>} Number of scripts installed
|
|
167
|
+
*/
|
|
168
|
+
async installScripts(scripts) {
|
|
169
|
+
if (scripts.length === 0) return 0;
|
|
170
|
+
|
|
171
|
+
let installed = 0;
|
|
172
|
+
for (const script of scripts) {
|
|
173
|
+
const sourcePath = path.join(__dirname, '..', 'scripts', script);
|
|
174
|
+
const targetPath = path.join(this.projectRoot, 'scripts', script);
|
|
175
|
+
const targetDir = path.dirname(targetPath);
|
|
176
|
+
|
|
177
|
+
try {
|
|
178
|
+
if (this.dryRun) {
|
|
179
|
+
this.log(`[DRY RUN] Would copy script: ${script}`);
|
|
180
|
+
} else {
|
|
181
|
+
await fs.mkdir(targetDir, { recursive: true });
|
|
182
|
+
await fs.copyFile(sourcePath, targetPath);
|
|
183
|
+
this.log(`Installed script: ${script}`);
|
|
184
|
+
}
|
|
185
|
+
installed++;
|
|
186
|
+
} catch (error) {
|
|
187
|
+
console.warn(`Warning: Could not install script ${script}: ${error.message}`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return installed;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Install library files
|
|
196
|
+
* @param {string[]} libs - Library names
|
|
197
|
+
* @returns {Promise<number>} Number of libraries installed
|
|
198
|
+
*/
|
|
199
|
+
async installLibraries(libs) {
|
|
200
|
+
if (libs.length === 0) return 0;
|
|
201
|
+
|
|
202
|
+
const libsDir = path.join(this.projectRoot, 'src', 'lib');
|
|
203
|
+
if (!this.dryRun) {
|
|
204
|
+
await fs.mkdir(libsDir, { recursive: true });
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
let installed = 0;
|
|
208
|
+
for (const lib of libs) {
|
|
209
|
+
const sourcePath = path.join(__dirname, lib);
|
|
210
|
+
const targetPath = path.join(libsDir, lib);
|
|
211
|
+
|
|
212
|
+
try {
|
|
213
|
+
if (this.dryRun) {
|
|
214
|
+
this.log(`[DRY RUN] Would copy library: ${lib}`);
|
|
215
|
+
} else {
|
|
216
|
+
await fs.copyFile(sourcePath, targetPath);
|
|
217
|
+
this.log(`Installed library: ${lib}`);
|
|
218
|
+
}
|
|
219
|
+
installed++;
|
|
220
|
+
} catch (error) {
|
|
221
|
+
console.warn(`Warning: Could not install library ${lib}: ${error.message}`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return installed;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Install documentation files
|
|
230
|
+
* @param {string[]} docs - Documentation file names
|
|
231
|
+
* @returns {Promise<number>} Number of docs installed
|
|
232
|
+
*/
|
|
233
|
+
async installDocs(docs) {
|
|
234
|
+
if (docs.length === 0) return 0;
|
|
235
|
+
|
|
236
|
+
const docsDir = path.join(this.projectRoot, 'docs');
|
|
237
|
+
if (!this.dryRun) {
|
|
238
|
+
await fs.mkdir(docsDir, { recursive: true });
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
let installed = 0;
|
|
242
|
+
for (const doc of docs) {
|
|
243
|
+
const sourcePath = path.join(__dirname, '..', '..', 'docs', doc);
|
|
244
|
+
const targetPath = path.join(docsDir, doc);
|
|
245
|
+
|
|
246
|
+
try {
|
|
247
|
+
if (this.dryRun) {
|
|
248
|
+
this.log(`[DRY RUN] Would copy doc: ${doc}`);
|
|
249
|
+
} else {
|
|
250
|
+
await fs.copyFile(sourcePath, targetPath);
|
|
251
|
+
this.log(`Installed documentation: ${doc}`);
|
|
252
|
+
}
|
|
253
|
+
installed++;
|
|
254
|
+
} catch (error) {
|
|
255
|
+
console.warn(`Warning: Could not install doc ${doc}: ${error.message}`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return installed;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Configure MCP servers
|
|
264
|
+
* @param {string[]} mcpServers - MCP server names
|
|
265
|
+
* @returns {Promise<number>} Number of MCP servers configured
|
|
266
|
+
*/
|
|
267
|
+
async configureMcpServers(mcpServers) {
|
|
268
|
+
if (mcpServers.length === 0) return 0;
|
|
269
|
+
|
|
270
|
+
const mcpConfigPath = path.join(this.claudeDir, 'mcp.json');
|
|
271
|
+
|
|
272
|
+
let config = { mcpServers: {} };
|
|
273
|
+
try {
|
|
274
|
+
const data = await fs.readFile(mcpConfigPath, 'utf8');
|
|
275
|
+
config = JSON.parse(data);
|
|
276
|
+
} catch (error) {
|
|
277
|
+
// Config doesn't exist, start fresh
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
let configured = 0;
|
|
281
|
+
for (const server of mcpServers) {
|
|
282
|
+
if (server === 'myaidev') {
|
|
283
|
+
config.mcpServers.myaidev = {
|
|
284
|
+
command: 'node',
|
|
285
|
+
args: [path.join(__dirname, 'mcp', 'wordpress-mcp-server.js')]
|
|
286
|
+
};
|
|
287
|
+
this.log(`Configured MCP server: ${server}`);
|
|
288
|
+
configured++;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (configured > 0 && !this.dryRun) {
|
|
293
|
+
await fs.writeFile(mcpConfigPath, JSON.stringify(config, null, 2), 'utf8');
|
|
294
|
+
} else if (this.dryRun) {
|
|
295
|
+
this.log('[DRY RUN] Would configure MCP servers');
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return configured;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Display environment variables needed
|
|
303
|
+
* @param {string[]} envVars - Environment variable names
|
|
304
|
+
*/
|
|
305
|
+
displayEnvVars(envVars) {
|
|
306
|
+
if (envVars.length === 0) return;
|
|
307
|
+
|
|
308
|
+
console.log('\nā ļø Environment Variables Required:');
|
|
309
|
+
for (const envVar of envVars) {
|
|
310
|
+
console.log(` - ${envVar}`);
|
|
311
|
+
}
|
|
312
|
+
console.log('\nConfigure these using: npx myaidev-method configure\n');
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Install a single workflow
|
|
317
|
+
* @param {string} workflowName - Workflow name
|
|
318
|
+
* @returns {Promise<Object>} Installation results
|
|
319
|
+
*/
|
|
320
|
+
async installWorkflow(workflowName) {
|
|
321
|
+
const workflow = WORKFLOWS[workflowName];
|
|
322
|
+
if (!workflow) {
|
|
323
|
+
throw new Error(`Unknown workflow: ${workflowName}`);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Validate workflow definition
|
|
327
|
+
const validation = validateWorkflow(workflow);
|
|
328
|
+
if (!validation.valid) {
|
|
329
|
+
throw new Error(`Invalid workflow ${workflowName}: ${validation.errors.join(', ')}`);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
this.log(`\nš¦ Installing workflow: ${workflow.name}`);
|
|
333
|
+
|
|
334
|
+
const results = {
|
|
335
|
+
workflow: workflowName,
|
|
336
|
+
agents: 0,
|
|
337
|
+
commands: 0,
|
|
338
|
+
scripts: 0,
|
|
339
|
+
libs: 0,
|
|
340
|
+
docs: 0,
|
|
341
|
+
mcpServers: 0
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
// Install components
|
|
345
|
+
results.agents = await this.installAgents(workflow.agents);
|
|
346
|
+
results.commands = await this.installCommands(workflow.commands);
|
|
347
|
+
results.scripts = await this.installScripts(workflow.scripts);
|
|
348
|
+
results.libs = await this.installLibraries(workflow.libs);
|
|
349
|
+
results.docs = await this.installDocs(workflow.docs);
|
|
350
|
+
results.mcpServers = await this.configureMcpServers(workflow.mcpServers);
|
|
351
|
+
|
|
352
|
+
// Display environment variables
|
|
353
|
+
this.displayEnvVars(workflow.envVars);
|
|
354
|
+
|
|
355
|
+
return results;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Install workflows with dependencies
|
|
360
|
+
* @param {string[]} workflowNames - Workflow names to install
|
|
361
|
+
* @returns {Promise<Object>} Installation summary
|
|
362
|
+
*/
|
|
363
|
+
async install(workflowNames) {
|
|
364
|
+
console.log('š MyAIDev Method - Modular Installation\n');
|
|
365
|
+
|
|
366
|
+
// Resolve dependencies
|
|
367
|
+
const resolvedWorkflows = this.resolveWorkflows(workflowNames);
|
|
368
|
+
this.log(`Resolved workflows: ${resolvedWorkflows.join(', ')}`);
|
|
369
|
+
|
|
370
|
+
// Read existing manifest
|
|
371
|
+
const manifest = await this.readManifest();
|
|
372
|
+
const alreadyInstalled = manifest.installedWorkflows || [];
|
|
373
|
+
|
|
374
|
+
// Filter out already installed workflows
|
|
375
|
+
const toInstall = resolvedWorkflows.filter(w => !alreadyInstalled.includes(w));
|
|
376
|
+
|
|
377
|
+
if (toInstall.length === 0) {
|
|
378
|
+
console.log('ā
All requested workflows are already installed\n');
|
|
379
|
+
return { installed: [], alreadyInstalled };
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
console.log(`Installing: ${toInstall.join(', ')}\n`);
|
|
383
|
+
|
|
384
|
+
// Install each workflow
|
|
385
|
+
const results = [];
|
|
386
|
+
for (const workflowName of toInstall) {
|
|
387
|
+
try {
|
|
388
|
+
const result = await this.installWorkflow(workflowName);
|
|
389
|
+
results.push(result);
|
|
390
|
+
|
|
391
|
+
// Add to manifest
|
|
392
|
+
if (!manifest.installedWorkflows.includes(workflowName)) {
|
|
393
|
+
manifest.installedWorkflows.push(workflowName);
|
|
394
|
+
}
|
|
395
|
+
} catch (error) {
|
|
396
|
+
console.error(`ā Failed to install ${workflowName}: ${error.message}`);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// Update manifest
|
|
401
|
+
await this.writeManifest(manifest);
|
|
402
|
+
|
|
403
|
+
// Summary
|
|
404
|
+
console.log('\nā
Installation complete!\n');
|
|
405
|
+
console.log('Installed workflows:');
|
|
406
|
+
for (const result of results) {
|
|
407
|
+
const workflow = WORKFLOWS[result.workflow];
|
|
408
|
+
console.log(` ⢠${workflow.name}`);
|
|
409
|
+
console.log(` - ${result.agents} agents, ${result.commands} commands, ${result.scripts} scripts`);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
return { installed: toInstall, alreadyInstalled, results };
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Remove a workflow
|
|
417
|
+
* @param {string} workflowName - Workflow name to remove
|
|
418
|
+
* @returns {Promise<Object>} Removal results
|
|
419
|
+
*/
|
|
420
|
+
async removeWorkflow(workflowName) {
|
|
421
|
+
const workflow = WORKFLOWS[workflowName];
|
|
422
|
+
if (!workflow) {
|
|
423
|
+
throw new Error(`Unknown workflow: ${workflowName}`);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
console.log(`\nšļø Removing workflow: ${workflow.name}`);
|
|
427
|
+
|
|
428
|
+
const manifest = await this.readManifest();
|
|
429
|
+
|
|
430
|
+
// Check if workflow is installed
|
|
431
|
+
if (!manifest.installedWorkflows.includes(workflowName)) {
|
|
432
|
+
console.log(`ā ļø Workflow ${workflowName} is not installed`);
|
|
433
|
+
return { removed: false };
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// Check if other workflows depend on this one
|
|
437
|
+
const dependents = manifest.installedWorkflows.filter(w => {
|
|
438
|
+
const wf = WORKFLOWS[w];
|
|
439
|
+
return wf && wf.dependencies.includes(workflowName);
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
if (dependents.length > 0) {
|
|
443
|
+
throw new Error(
|
|
444
|
+
`Cannot remove ${workflowName}: Required by ${dependents.join(', ')}`
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
if (this.dryRun) {
|
|
449
|
+
console.log(`[DRY RUN] Would remove workflow: ${workflowName}`);
|
|
450
|
+
return { removed: true, dryRun: true };
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// Remove files (in reverse order of installation)
|
|
454
|
+
// Note: We don't actually delete files to avoid breaking other workflows
|
|
455
|
+
// Just remove from manifest
|
|
456
|
+
manifest.installedWorkflows = manifest.installedWorkflows.filter(
|
|
457
|
+
w => w !== workflowName
|
|
458
|
+
);
|
|
459
|
+
|
|
460
|
+
await this.writeManifest(manifest);
|
|
461
|
+
|
|
462
|
+
console.log(`ā
Removed workflow: ${workflow.name}\n`);
|
|
463
|
+
console.log('Note: Files were not deleted to preserve shared components');
|
|
464
|
+
|
|
465
|
+
return { removed: true };
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* List installed workflows
|
|
470
|
+
* @returns {Promise<Object>} List of installed workflows
|
|
471
|
+
*/
|
|
472
|
+
async listInstalled() {
|
|
473
|
+
const manifest = await this.readManifest();
|
|
474
|
+
const installed = manifest.installedWorkflows || [];
|
|
475
|
+
|
|
476
|
+
console.log('\nš Installed Workflows:\n');
|
|
477
|
+
|
|
478
|
+
if (installed.length === 0) {
|
|
479
|
+
console.log(' No workflows installed yet\n');
|
|
480
|
+
return { installed: [] };
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
for (const workflowName of installed) {
|
|
484
|
+
const workflow = WORKFLOWS[workflowName];
|
|
485
|
+
if (workflow) {
|
|
486
|
+
console.log(` ⢠${workflow.name} (${workflowName})`);
|
|
487
|
+
console.log(` ${workflow.description}`);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
console.log('');
|
|
492
|
+
return { installed };
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Show workflow status
|
|
497
|
+
* @returns {Promise<Object>} Workflow status
|
|
498
|
+
*/
|
|
499
|
+
async status() {
|
|
500
|
+
const manifest = await this.readManifest();
|
|
501
|
+
const installed = manifest.installedWorkflows || [];
|
|
502
|
+
const available = Object.keys(WORKFLOWS);
|
|
503
|
+
|
|
504
|
+
console.log('\nš Workflow Status:\n');
|
|
505
|
+
console.log(`Installed: ${installed.length}/${available.length}`);
|
|
506
|
+
console.log(`Last modified: ${manifest.lastModified || 'Never'}\n`);
|
|
507
|
+
|
|
508
|
+
return { installed, available, manifest };
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
export default WorkflowInstaller;
|