claude-cli-advanced-starter-pack 1.0.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 (67) hide show
  1. package/LICENSE +21 -0
  2. package/OVERVIEW.md +597 -0
  3. package/README.md +439 -0
  4. package/bin/gtask.js +282 -0
  5. package/bin/postinstall.js +53 -0
  6. package/package.json +69 -0
  7. package/src/agents/phase-dev-templates.js +1011 -0
  8. package/src/agents/templates.js +668 -0
  9. package/src/analysis/checklist-parser.js +414 -0
  10. package/src/analysis/codebase.js +481 -0
  11. package/src/cli/menu.js +958 -0
  12. package/src/commands/claude-audit.js +1482 -0
  13. package/src/commands/claude-settings.js +2243 -0
  14. package/src/commands/create-agent.js +681 -0
  15. package/src/commands/create-command.js +337 -0
  16. package/src/commands/create-hook.js +262 -0
  17. package/src/commands/create-phase-dev/codebase-analyzer.js +813 -0
  18. package/src/commands/create-phase-dev/documentation-generator.js +352 -0
  19. package/src/commands/create-phase-dev/post-completion.js +404 -0
  20. package/src/commands/create-phase-dev/scale-calculator.js +344 -0
  21. package/src/commands/create-phase-dev/wizard.js +492 -0
  22. package/src/commands/create-phase-dev.js +481 -0
  23. package/src/commands/create-skill.js +313 -0
  24. package/src/commands/create.js +446 -0
  25. package/src/commands/decompose.js +392 -0
  26. package/src/commands/detect-tech-stack.js +768 -0
  27. package/src/commands/explore-mcp/claude-md-updater.js +252 -0
  28. package/src/commands/explore-mcp/mcp-installer.js +346 -0
  29. package/src/commands/explore-mcp/mcp-registry.js +438 -0
  30. package/src/commands/explore-mcp.js +638 -0
  31. package/src/commands/gtask-init.js +641 -0
  32. package/src/commands/help.js +128 -0
  33. package/src/commands/init.js +1890 -0
  34. package/src/commands/install.js +250 -0
  35. package/src/commands/list.js +116 -0
  36. package/src/commands/roadmap.js +750 -0
  37. package/src/commands/setup-wizard.js +482 -0
  38. package/src/commands/setup.js +351 -0
  39. package/src/commands/sync.js +534 -0
  40. package/src/commands/test-run.js +456 -0
  41. package/src/commands/test-setup.js +456 -0
  42. package/src/commands/validate.js +67 -0
  43. package/src/config/tech-stack.defaults.json +182 -0
  44. package/src/config/tech-stack.schema.json +502 -0
  45. package/src/github/client.js +359 -0
  46. package/src/index.js +84 -0
  47. package/src/templates/claude-command.js +244 -0
  48. package/src/templates/issue-body.js +284 -0
  49. package/src/testing/config.js +411 -0
  50. package/src/utils/template-engine.js +398 -0
  51. package/src/utils/validate-templates.js +223 -0
  52. package/src/utils.js +396 -0
  53. package/templates/commands/ccasp-setup.template.md +113 -0
  54. package/templates/commands/context-audit.template.md +97 -0
  55. package/templates/commands/create-task-list.template.md +382 -0
  56. package/templates/commands/deploy-full.template.md +261 -0
  57. package/templates/commands/github-task-start.template.md +99 -0
  58. package/templates/commands/github-update.template.md +69 -0
  59. package/templates/commands/happy-start.template.md +117 -0
  60. package/templates/commands/phase-track.template.md +142 -0
  61. package/templates/commands/tunnel-start.template.md +127 -0
  62. package/templates/commands/tunnel-stop.template.md +106 -0
  63. package/templates/hooks/context-guardian.template.js +173 -0
  64. package/templates/hooks/deployment-orchestrator.template.js +219 -0
  65. package/templates/hooks/github-progress-hook.template.js +197 -0
  66. package/templates/hooks/happy-checkpoint-manager.template.js +222 -0
  67. package/templates/hooks/phase-dev-enforcer.template.js +183 -0
@@ -0,0 +1,337 @@
1
+ /**
2
+ * Create Command
3
+ *
4
+ * Interactive wizard for creating Claude Code slash commands
5
+ */
6
+
7
+ import chalk from 'chalk';
8
+ import inquirer from 'inquirer';
9
+ import ora from 'ora';
10
+ import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs';
11
+ import { join, dirname } from 'path';
12
+ import { showHeader, showSuccess, showError, showWarning, showInfo } from '../cli/menu.js';
13
+ import { generateCommandTemplate, COMPLEXITY_LEVELS } from '../agents/templates.js';
14
+
15
+ /**
16
+ * Run the create-command wizard
17
+ */
18
+ export async function runCreateCommand(options) {
19
+ showHeader('Create Slash Command');
20
+
21
+ console.log(chalk.dim('Slash commands are invoked with / in Claude Code.'));
22
+ console.log(chalk.dim('They can delegate to skills, agents, or run standalone.\n'));
23
+
24
+ // Step 1: Command name
25
+ const { name } = await inquirer.prompt([
26
+ {
27
+ type: 'input',
28
+ name: 'name',
29
+ message: 'Command name (without /):',
30
+ default: options.name || 'my-command',
31
+ validate: (input) => {
32
+ if (!/^[a-z][a-z0-9-]*$/.test(input)) {
33
+ return 'Use kebab-case (lowercase letters, numbers, hyphens)';
34
+ }
35
+ return true;
36
+ },
37
+ },
38
+ ]);
39
+
40
+ // Step 2: Description
41
+ const { description } = await inquirer.prompt([
42
+ {
43
+ type: 'input',
44
+ name: 'description',
45
+ message: 'Brief description (shown in menus):',
46
+ default: `Run ${name} workflow`,
47
+ validate: (input) => input.length > 0 || 'Description is required',
48
+ },
49
+ ]);
50
+
51
+ // Step 3: Complexity
52
+ console.log('');
53
+ console.log(chalk.cyan.bold('Complexity Levels:'));
54
+ Object.entries(COMPLEXITY_LEVELS).forEach(([key, level]) => {
55
+ console.log(chalk.dim(` ${level.name}: ${level.description} (${level.duration})`));
56
+ });
57
+ console.log('');
58
+
59
+ const { complexity } = await inquirer.prompt([
60
+ {
61
+ type: 'list',
62
+ name: 'complexity',
63
+ message: 'Expected complexity:',
64
+ choices: Object.entries(COMPLEXITY_LEVELS).map(([key, level]) => ({
65
+ name: `${level.name} - ${level.description}`,
66
+ value: key,
67
+ short: level.name,
68
+ })),
69
+ default: 'low',
70
+ },
71
+ ]);
72
+
73
+ // Step 4: Delegation
74
+ const { delegationType } = await inquirer.prompt([
75
+ {
76
+ type: 'list',
77
+ name: 'delegationType',
78
+ message: 'What does this command use?',
79
+ choices: [
80
+ { name: 'Standalone - No delegation, instructions only', value: 'standalone' },
81
+ { name: 'Skill - Delegates to a skill package', value: 'skill' },
82
+ { name: 'Agent - Spawns a specific agent', value: 'agent' },
83
+ { name: 'Both - Uses skill + specific agent workflow', value: 'both' },
84
+ ],
85
+ },
86
+ ]);
87
+
88
+ let delegatesTo = '';
89
+ if (delegationType === 'skill' || delegationType === 'both') {
90
+ const { skillName } = await inquirer.prompt([
91
+ {
92
+ type: 'input',
93
+ name: 'skillName',
94
+ message: 'Skill name:',
95
+ default: 'my-skill',
96
+ },
97
+ ]);
98
+ delegatesTo = `skill:${skillName}`;
99
+ }
100
+
101
+ if (delegationType === 'agent' || delegationType === 'both') {
102
+ const { agentName } = await inquirer.prompt([
103
+ {
104
+ type: 'input',
105
+ name: 'agentName',
106
+ message: 'Agent name:',
107
+ default: 'my-agent',
108
+ },
109
+ ]);
110
+ delegatesTo = delegationType === 'both' ? `${delegatesTo} + @${agentName}` : `@${agentName}`;
111
+ }
112
+
113
+ // Step 5: Arguments
114
+ const { hasArgs } = await inquirer.prompt([
115
+ {
116
+ type: 'confirm',
117
+ name: 'hasArgs',
118
+ message: 'Does this command accept arguments?',
119
+ default: false,
120
+ },
121
+ ]);
122
+
123
+ let args = '';
124
+ if (hasArgs) {
125
+ const { argsSpec } = await inquirer.prompt([
126
+ {
127
+ type: 'input',
128
+ name: 'argsSpec',
129
+ message: 'Arguments format (e.g., "[target] [--flag]"):',
130
+ default: '[target]',
131
+ },
132
+ ]);
133
+ args = argsSpec;
134
+ }
135
+
136
+ // Step 6: Workflow steps
137
+ const { defineSteps } = await inquirer.prompt([
138
+ {
139
+ type: 'confirm',
140
+ name: 'defineSteps',
141
+ message: 'Define workflow steps? (recommended)',
142
+ default: true,
143
+ },
144
+ ]);
145
+
146
+ const steps = [];
147
+ if (defineSteps) {
148
+ let addingSteps = true;
149
+ let stepNum = 1;
150
+
151
+ while (addingSteps) {
152
+ const { stepTitle, stepInstructions } = await inquirer.prompt([
153
+ {
154
+ type: 'input',
155
+ name: 'stepTitle',
156
+ message: `Step ${stepNum} title:`,
157
+ default: stepNum === 1 ? 'Analyze' : stepNum === 2 ? 'Execute' : 'Report',
158
+ },
159
+ {
160
+ type: 'input',
161
+ name: 'stepInstructions',
162
+ message: `Step ${stepNum} instructions:`,
163
+ default: 'Perform this step of the workflow.',
164
+ },
165
+ ]);
166
+
167
+ steps.push({ title: stepTitle, instructions: stepInstructions });
168
+ stepNum++;
169
+
170
+ const { continueAdding } = await inquirer.prompt([
171
+ {
172
+ type: 'confirm',
173
+ name: 'continueAdding',
174
+ message: 'Add another step?',
175
+ default: stepNum <= 3,
176
+ },
177
+ ]);
178
+ addingSteps = continueAdding;
179
+ }
180
+ }
181
+
182
+ // Step 7: Examples
183
+ const examples = [];
184
+ const { addExamples } = await inquirer.prompt([
185
+ {
186
+ type: 'confirm',
187
+ name: 'addExamples',
188
+ message: 'Add usage examples?',
189
+ default: true,
190
+ },
191
+ ]);
192
+
193
+ if (addExamples) {
194
+ let addingExamples = true;
195
+ while (addingExamples) {
196
+ const { exampleDesc, exampleArgs } = await inquirer.prompt([
197
+ {
198
+ type: 'input',
199
+ name: 'exampleDesc',
200
+ message: 'Example description:',
201
+ default: 'Basic usage',
202
+ },
203
+ {
204
+ type: 'input',
205
+ name: 'exampleArgs',
206
+ message: `Example args (after /${name}):`,
207
+ default: '',
208
+ },
209
+ ]);
210
+
211
+ examples.push({ description: exampleDesc, args: exampleArgs });
212
+
213
+ const { continueAdding } = await inquirer.prompt([
214
+ {
215
+ type: 'confirm',
216
+ name: 'continueAdding',
217
+ message: 'Add another example?',
218
+ default: examples.length < 2,
219
+ },
220
+ ]);
221
+ addingExamples = continueAdding;
222
+ }
223
+ }
224
+
225
+ // Step 8: Related commands
226
+ const { relatedCommands } = await inquirer.prompt([
227
+ {
228
+ type: 'input',
229
+ name: 'relatedCommands',
230
+ message: 'Related commands (comma-separated, without /):',
231
+ default: '',
232
+ filter: (input) =>
233
+ input
234
+ .split(',')
235
+ .map((c) => c.trim())
236
+ .filter(Boolean),
237
+ },
238
+ ]);
239
+
240
+ // Step 9: Output location
241
+ const { outputPath } = await inquirer.prompt([
242
+ {
243
+ type: 'list',
244
+ name: 'outputPath',
245
+ message: 'Where should the command be created?',
246
+ choices: [
247
+ { name: '.claude/commands/ (standard location)', value: '.claude/commands' },
248
+ { name: 'Custom location', value: 'custom' },
249
+ ],
250
+ },
251
+ ]);
252
+
253
+ let finalPath = join(process.cwd(), outputPath, `${name}.md`);
254
+ if (outputPath === 'custom') {
255
+ const { customPath } = await inquirer.prompt([
256
+ {
257
+ type: 'input',
258
+ name: 'customPath',
259
+ message: 'Custom path:',
260
+ default: `.claude/commands/${name}.md`,
261
+ },
262
+ ]);
263
+ finalPath = join(process.cwd(), customPath);
264
+ }
265
+
266
+ // Generate the command
267
+ const spinner = ora('Generating command...').start();
268
+
269
+ const commandContent = generateCommandTemplate({
270
+ name,
271
+ description,
272
+ complexity,
273
+ delegatesTo,
274
+ arguments: args,
275
+ steps,
276
+ examples,
277
+ relatedCommands,
278
+ });
279
+
280
+ // Ensure directory exists
281
+ const dir = dirname(finalPath);
282
+ if (!existsSync(dir)) {
283
+ mkdirSync(dir, { recursive: true });
284
+ }
285
+
286
+ // Write the command file
287
+ writeFileSync(finalPath, commandContent, 'utf8');
288
+
289
+ // Try to update INDEX.md if it exists
290
+ const indexPath = join(dir, 'INDEX.md');
291
+ let indexUpdated = false;
292
+ if (existsSync(indexPath)) {
293
+ try {
294
+ const indexContent = readFileSync(indexPath, 'utf8');
295
+ const newEntry = `| \`/${name}\` | ${description} | ${complexity} |`;
296
+
297
+ // Check if command already in index
298
+ if (!indexContent.includes(`/${name}`)) {
299
+ // Find a good place to insert (before the last table row or at end of a table)
300
+ const tableMatch = indexContent.match(/(\|[^|]+\|[^|]+\|[^|]+\|\n)+/);
301
+ if (tableMatch) {
302
+ const updatedIndex = indexContent.replace(
303
+ tableMatch[0],
304
+ tableMatch[0] + newEntry + '\n'
305
+ );
306
+ writeFileSync(indexPath, updatedIndex, 'utf8');
307
+ indexUpdated = true;
308
+ }
309
+ }
310
+ } catch {
311
+ // Ignore index update errors
312
+ }
313
+ }
314
+
315
+ spinner.succeed('Command created');
316
+
317
+ // Summary
318
+ const details = [
319
+ `Name: /${name}`,
320
+ `Complexity: ${COMPLEXITY_LEVELS[complexity].name}`,
321
+ delegatesTo ? `Delegates to: ${delegatesTo}` : '',
322
+ args ? `Arguments: ${args}` : '',
323
+ `Steps: ${steps.length}`,
324
+ '',
325
+ `Location: ${finalPath}`,
326
+ indexUpdated ? 'INDEX.md: Updated' : '',
327
+ ].filter(Boolean);
328
+
329
+ showSuccess('Command Created!', details);
330
+
331
+ // Instructions
332
+ console.log(chalk.dim('\nTo use this command in Claude Code:'));
333
+ console.log(chalk.cyan(` /${name}${args ? ` ${args}` : ''}`));
334
+ console.log('');
335
+
336
+ return { name, path: finalPath, complexity, delegatesTo };
337
+ }
@@ -0,0 +1,262 @@
1
+ /**
2
+ * Create Hook Command
3
+ *
4
+ * Interactive wizard for creating Claude Code enforcement hooks
5
+ */
6
+
7
+ import chalk from 'chalk';
8
+ import inquirer from 'inquirer';
9
+ import ora from 'ora';
10
+ import { existsSync, mkdirSync, writeFileSync } from 'fs';
11
+ import { join, dirname } from 'path';
12
+ import { showHeader, showSuccess, showError, showWarning, showInfo } from '../cli/menu.js';
13
+ import {
14
+ generateHookTemplate,
15
+ HOOK_EVENT_TYPES,
16
+ HOOK_TOOLS,
17
+ } from '../agents/templates.js';
18
+
19
+ /**
20
+ * Run the create-hook wizard
21
+ */
22
+ export async function runCreateHook(options) {
23
+ showHeader('Create Hook');
24
+
25
+ console.log(chalk.dim('Hooks enforce patterns and inject context in Claude Code.'));
26
+ console.log(chalk.dim('They run automatically on tool calls or user prompts.\n'));
27
+
28
+ // Step 1: Hook name
29
+ const { name } = await inquirer.prompt([
30
+ {
31
+ type: 'input',
32
+ name: 'name',
33
+ message: 'Hook name (kebab-case):',
34
+ default: options.name || 'my-hook',
35
+ validate: (input) => {
36
+ if (!/^[a-z][a-z0-9-]*$/.test(input)) {
37
+ return 'Use kebab-case (lowercase letters, numbers, hyphens)';
38
+ }
39
+ return true;
40
+ },
41
+ },
42
+ ]);
43
+
44
+ // Step 2: Description
45
+ const { description } = await inquirer.prompt([
46
+ {
47
+ type: 'input',
48
+ name: 'description',
49
+ message: 'What does this hook do?',
50
+ default: `Enforce patterns for ${name}`,
51
+ },
52
+ ]);
53
+
54
+ // Step 3: Event type
55
+ console.log('');
56
+ console.log(chalk.cyan.bold('Event Types:'));
57
+ Object.entries(HOOK_EVENT_TYPES).forEach(([key, event]) => {
58
+ const canBlock = event.canBlock ? chalk.green('(can block)') : chalk.dim('(cannot block)');
59
+ console.log(chalk.dim(` ${key}: ${event.description} ${canBlock}`));
60
+ });
61
+ console.log('');
62
+
63
+ const { eventType } = await inquirer.prompt([
64
+ {
65
+ type: 'list',
66
+ name: 'eventType',
67
+ message: 'When should this hook trigger?',
68
+ choices: Object.entries(HOOK_EVENT_TYPES).map(([key, event]) => ({
69
+ name: `${key} - ${event.useCase}`,
70
+ value: key,
71
+ short: key,
72
+ })),
73
+ default: 'PreToolUse',
74
+ },
75
+ ]);
76
+
77
+ // Step 4: Target tools (for PreToolUse/PostToolUse)
78
+ let tools = [];
79
+ if (eventType === 'PreToolUse' || eventType === 'PostToolUse') {
80
+ const { selectedTools } = await inquirer.prompt([
81
+ {
82
+ type: 'checkbox',
83
+ name: 'selectedTools',
84
+ message: 'Which tools should trigger this hook?',
85
+ choices: HOOK_TOOLS.map((tool) => ({
86
+ name: tool,
87
+ value: tool,
88
+ checked: tool === 'Edit' || tool === 'Write',
89
+ })),
90
+ validate: (input) => input.length > 0 || 'Select at least one tool',
91
+ },
92
+ ]);
93
+ tools = selectedTools;
94
+ }
95
+
96
+ // Step 5: Target file patterns
97
+ const { targetPatterns } = await inquirer.prompt([
98
+ {
99
+ type: 'input',
100
+ name: 'targetPatterns',
101
+ message: 'File patterns to target (comma-separated, empty = all files):',
102
+ default: 'src/,apps/',
103
+ filter: (input) =>
104
+ input
105
+ .split(',')
106
+ .map((p) => p.trim())
107
+ .filter(Boolean),
108
+ },
109
+ ]);
110
+
111
+ // Step 6: Purpose - block or warn
112
+ const { purpose } = await inquirer.prompt([
113
+ {
114
+ type: 'list',
115
+ name: 'purpose',
116
+ message: 'What should this hook do?',
117
+ choices: [
118
+ { name: 'Block - Prevent operations matching patterns', value: 'block' },
119
+ { name: 'Warn - Allow but show warnings', value: 'warn' },
120
+ { name: 'Inject - Add context to Claude\'s response', value: 'inject' },
121
+ { name: 'Log - Just log operations (no interference)', value: 'log' },
122
+ ],
123
+ },
124
+ ]);
125
+
126
+ // Step 7: Patterns based on purpose
127
+ let blockedPatterns = [];
128
+ let warningPatterns = [];
129
+ let blockReason = '';
130
+
131
+ if (purpose === 'block') {
132
+ const { patterns, reason } = await inquirer.prompt([
133
+ {
134
+ type: 'input',
135
+ name: 'patterns',
136
+ message: 'Patterns to BLOCK (comma-separated):',
137
+ default: 'console.log,debugger',
138
+ filter: (input) =>
139
+ input
140
+ .split(',')
141
+ .map((p) => p.trim())
142
+ .filter(Boolean),
143
+ },
144
+ {
145
+ type: 'input',
146
+ name: 'reason',
147
+ message: 'Why should these be blocked?',
148
+ default: 'These patterns are not allowed in production code',
149
+ },
150
+ ]);
151
+ blockedPatterns = patterns;
152
+ blockReason = reason;
153
+ } else if (purpose === 'warn') {
154
+ const { patterns } = await inquirer.prompt([
155
+ {
156
+ type: 'input',
157
+ name: 'patterns',
158
+ message: 'Patterns to WARN about (comma-separated):',
159
+ default: 'TODO,FIXME,HACK',
160
+ filter: (input) =>
161
+ input
162
+ .split(',')
163
+ .map((p) => p.trim())
164
+ .filter(Boolean),
165
+ },
166
+ ]);
167
+ warningPatterns = patterns;
168
+ }
169
+
170
+ // Step 8: Reference documentation
171
+ const { referenceDoc } = await inquirer.prompt([
172
+ {
173
+ type: 'input',
174
+ name: 'referenceDoc',
175
+ message: 'Reference documentation path (optional):',
176
+ default: '',
177
+ },
178
+ ]);
179
+
180
+ // Step 9: Output location
181
+ const { outputPath } = await inquirer.prompt([
182
+ {
183
+ type: 'list',
184
+ name: 'outputPath',
185
+ message: 'Where should the hook be created?',
186
+ choices: [
187
+ { name: '.claude/hooks/tools/ (standard location)', value: '.claude/hooks/tools' },
188
+ { name: 'Custom location', value: 'custom' },
189
+ ],
190
+ },
191
+ ]);
192
+
193
+ let finalPath = join(process.cwd(), outputPath, `${name}.js`);
194
+ if (outputPath === 'custom') {
195
+ const { customPath } = await inquirer.prompt([
196
+ {
197
+ type: 'input',
198
+ name: 'customPath',
199
+ message: 'Custom path:',
200
+ default: `.claude/hooks/tools/${name}.js`,
201
+ },
202
+ ]);
203
+ finalPath = join(process.cwd(), customPath);
204
+ }
205
+
206
+ // Generate the hook
207
+ const spinner = ora('Generating hook...').start();
208
+
209
+ const hookContent = generateHookTemplate({
210
+ name,
211
+ description,
212
+ eventType,
213
+ tools,
214
+ targetPatterns,
215
+ blockedPatterns,
216
+ warningPatterns,
217
+ blockReason,
218
+ referenceDoc,
219
+ });
220
+
221
+ // Ensure directory exists
222
+ const dir = dirname(finalPath);
223
+ if (!existsSync(dir)) {
224
+ mkdirSync(dir, { recursive: true });
225
+ }
226
+
227
+ // Write the hook file
228
+ writeFileSync(finalPath, hookContent, 'utf8');
229
+
230
+ spinner.succeed('Hook created');
231
+
232
+ // Summary
233
+ const details = [
234
+ `Name: ${name}`,
235
+ `Event: ${eventType}`,
236
+ tools.length > 0 ? `Tools: ${tools.join(', ')}` : '',
237
+ targetPatterns.length > 0 ? `File patterns: ${targetPatterns.join(', ')}` : '',
238
+ blockedPatterns.length > 0 ? `Blocked: ${blockedPatterns.join(', ')}` : '',
239
+ warningPatterns.length > 0 ? `Warnings: ${warningPatterns.join(', ')}` : '',
240
+ '',
241
+ `Location: ${finalPath}`,
242
+ ].filter(Boolean);
243
+
244
+ showSuccess('Hook Created!', details);
245
+
246
+ // Instructions
247
+ console.log(chalk.dim('\nTo activate this hook, add to .claude/settings.local.json:'));
248
+ console.log(chalk.cyan(`
249
+ {
250
+ "hooks": {
251
+ "${eventType}": [
252
+ {
253
+ "command": "node ${finalPath.replace(process.cwd(), '.')}"${tools.length > 0 ? `,
254
+ "tools": [${tools.map((t) => `"${t}"`).join(', ')}]` : ''}
255
+ }
256
+ ]
257
+ }
258
+ }
259
+ `));
260
+
261
+ return { name, path: finalPath, eventType, tools };
262
+ }