@silverassist/agents-toolkit 2.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.
- package/LICENSE +135 -0
- package/README.md +388 -0
- package/bin/cli.js +940 -0
- package/package.json +52 -0
- package/src/index.js +58 -0
- package/templates/agents/AGENTS.codex.md +195 -0
- package/templates/agents/AGENTS.md +195 -0
- package/templates/agents/CLAUDE.md +213 -0
- package/templates/agents/copilot-instructions.md +83 -0
- package/templates/shared/instructions/css-styling.instructions.md +142 -0
- package/templates/shared/instructions/documentation-language.instructions.md +216 -0
- package/templates/shared/instructions/github-workflow.instructions.md +245 -0
- package/templates/shared/instructions/php-standards.instructions.md +293 -0
- package/templates/shared/instructions/react-components.instructions.md +196 -0
- package/templates/shared/instructions/server-actions.instructions.md +429 -0
- package/templates/shared/instructions/testing-standards.instructions.md +264 -0
- package/templates/shared/instructions/tests.instructions.md +103 -0
- package/templates/shared/instructions/typescript.instructions.md +106 -0
- package/templates/shared/instructions/wordpress-plugin-architecture.instructions.md +238 -0
- package/templates/shared/prompts/README.md +129 -0
- package/templates/shared/prompts/_partials/README.md +57 -0
- package/templates/shared/prompts/_partials/documentation.md +203 -0
- package/templates/shared/prompts/_partials/git-operations.md +171 -0
- package/templates/shared/prompts/_partials/github-integration.md +100 -0
- package/templates/shared/prompts/_partials/jira-integration.md +155 -0
- package/templates/shared/prompts/_partials/pr-template.md +216 -0
- package/templates/shared/prompts/_partials/validations.md +87 -0
- package/templates/shared/prompts/add-tests.prompt.md +151 -0
- package/templates/shared/prompts/analyze-github-issue.prompt.md +73 -0
- package/templates/shared/prompts/analyze-ticket.prompt.md +63 -0
- package/templates/shared/prompts/create-plan.prompt.md +118 -0
- package/templates/shared/prompts/create-pr.prompt.md +139 -0
- package/templates/shared/prompts/finalize-pr.prompt.md +150 -0
- package/templates/shared/prompts/fix-issues.prompt.md +92 -0
- package/templates/shared/prompts/new-wp-component.prompt.md +51 -0
- package/templates/shared/prompts/new-wp-plugin.prompt.md +46 -0
- package/templates/shared/prompts/prepare-pr.prompt.md +123 -0
- package/templates/shared/prompts/prepare-release.prompt.md +74 -0
- package/templates/shared/prompts/quality-check.prompt.md +45 -0
- package/templates/shared/prompts/review-code.prompt.md +81 -0
- package/templates/shared/prompts/work-github-issue.prompt.md +96 -0
- package/templates/shared/prompts/work-ticket.prompt.md +98 -0
- package/templates/shared/skills/README.md +53 -0
- package/templates/shared/skills/component-architecture/SKILL.md +321 -0
- package/templates/shared/skills/create-component/SKILL.md +714 -0
- package/templates/shared/skills/domain-driven-design/SKILL.md +277 -0
- package/templates/shared/skills/plugin-creation/SKILL.md +1094 -0
- package/templates/shared/skills/quality-checks/SKILL.md +493 -0
- package/templates/shared/skills/release-management/SKILL.md +649 -0
- package/templates/shared/skills/testing/SKILL.md +682 -0
- package/templates/shared/skills/testing-patterns/SKILL.md +243 -0
package/bin/cli.js
ADDED
|
@@ -0,0 +1,940 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* CLI tool for installing and managing AI agent prompts
|
|
5
|
+
* @module agents-toolkit/cli
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import { fileURLToPath } from 'url';
|
|
11
|
+
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = path.dirname(__filename);
|
|
14
|
+
|
|
15
|
+
const TEMPLATES_DIR = path.join(__dirname, '..', 'templates');
|
|
16
|
+
const COLORS = {
|
|
17
|
+
reset: '\x1b[0m',
|
|
18
|
+
bright: '\x1b[1m',
|
|
19
|
+
green: '\x1b[32m',
|
|
20
|
+
yellow: '\x1b[33m',
|
|
21
|
+
blue: '\x1b[34m',
|
|
22
|
+
red: '\x1b[31m',
|
|
23
|
+
cyan: '\x1b[36m',
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const DEFAULT_CONFIG = {
|
|
27
|
+
stack: 'all',
|
|
28
|
+
tracker: 'all',
|
|
29
|
+
jira: {
|
|
30
|
+
projectKey: 'PROJECT',
|
|
31
|
+
baseUrl: 'https://your-org.atlassian.net'
|
|
32
|
+
},
|
|
33
|
+
git: {
|
|
34
|
+
defaultBranch: 'dev',
|
|
35
|
+
branchPrefix: {
|
|
36
|
+
feature: 'feature/',
|
|
37
|
+
bugfix: 'bugfix/',
|
|
38
|
+
hotfix: 'hotfix/'
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
pr: {
|
|
42
|
+
targetBranch: 'dev',
|
|
43
|
+
template: 'default'
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* File categorization for stack/tracker filtering
|
|
49
|
+
*/
|
|
50
|
+
const FILE_CATEGORIES = {
|
|
51
|
+
instructions: {
|
|
52
|
+
react: ['css-styling', 'react-components', 'server-actions', 'tests', 'typescript'],
|
|
53
|
+
wordpress: ['php-standards', 'wordpress-plugin-architecture', 'testing-standards'],
|
|
54
|
+
universal: ['documentation-language', 'github-workflow'],
|
|
55
|
+
},
|
|
56
|
+
prompts: {
|
|
57
|
+
react: [],
|
|
58
|
+
wordpress: ['new-wp-component', 'new-wp-plugin', 'quality-check'],
|
|
59
|
+
universal: [
|
|
60
|
+
'analyze-ticket', 'work-ticket', 'analyze-github-issue', 'work-github-issue',
|
|
61
|
+
'create-plan', 'create-pr', 'prepare-pr', 'finalize-pr',
|
|
62
|
+
'review-code', 'fix-issues', 'add-tests', 'prepare-release',
|
|
63
|
+
],
|
|
64
|
+
jira: ['analyze-ticket', 'work-ticket', 'create-pr', 'finalize-pr'],
|
|
65
|
+
github: ['analyze-github-issue', 'work-github-issue'],
|
|
66
|
+
},
|
|
67
|
+
partials: {
|
|
68
|
+
jira: ['jira-integration'],
|
|
69
|
+
github: ['github-integration'],
|
|
70
|
+
universal: ['git-operations', 'pr-template', 'validations', 'documentation'],
|
|
71
|
+
},
|
|
72
|
+
skills: {
|
|
73
|
+
react: ['component-architecture', 'testing-patterns'],
|
|
74
|
+
wordpress: ['create-component', 'plugin-creation', 'quality-checks', 'testing'],
|
|
75
|
+
universal: ['domain-driven-design', 'release-management'],
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Determine if a file should be included based on stack/tracker filters
|
|
81
|
+
* @param {string} filename - File basename without extension
|
|
82
|
+
* @param {string} category - Category: instructions, prompts, partials, or skills
|
|
83
|
+
* @param {{ stack: string, tracker: string }} filters - Active filters
|
|
84
|
+
* @returns {boolean} Whether to include the file
|
|
85
|
+
*/
|
|
86
|
+
function shouldIncludeFile(filename, category, { stack, tracker }) {
|
|
87
|
+
const cats = FILE_CATEGORIES[category];
|
|
88
|
+
if (!cats) return true;
|
|
89
|
+
|
|
90
|
+
if (cats.universal && cats.universal.includes(filename)) {
|
|
91
|
+
if (tracker !== 'all' && cats.jira && cats.jira.includes(filename) && tracker !== 'jira') return false;
|
|
92
|
+
if (tracker !== 'all' && cats.github && cats.github.includes(filename) && tracker !== 'github') return false;
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (stack !== 'all') {
|
|
97
|
+
if (cats.react && cats.react.includes(filename)) return stack === 'react';
|
|
98
|
+
if (cats.wordpress && cats.wordpress.includes(filename)) return stack === 'wordpress';
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (tracker !== 'all') {
|
|
102
|
+
if (cats.jira && cats.jira.includes(filename)) return tracker === 'jira';
|
|
103
|
+
if (cats.github && cats.github.includes(filename)) return tracker === 'github';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Print colored message to console
|
|
111
|
+
* @param {string} message - Message to print
|
|
112
|
+
* @param {string} color - Color key from COLORS
|
|
113
|
+
*/
|
|
114
|
+
function log(message, color = 'reset') {
|
|
115
|
+
console.log(`${COLORS[color]}${message}${COLORS.reset}`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Print success message
|
|
120
|
+
* @param {string} message - Message to print
|
|
121
|
+
*/
|
|
122
|
+
function success(message) {
|
|
123
|
+
log(`✅ ${message}`, 'green');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Print warning message
|
|
128
|
+
* @param {string} message - Message to print
|
|
129
|
+
*/
|
|
130
|
+
function warn(message) {
|
|
131
|
+
log(`⚠️ ${message}`, 'yellow');
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Print error message
|
|
136
|
+
* @param {string} message - Message to print
|
|
137
|
+
*/
|
|
138
|
+
function error(message) {
|
|
139
|
+
log(`❌ ${message}`, 'red');
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Print info message
|
|
144
|
+
* @param {string} message - Message to print
|
|
145
|
+
*/
|
|
146
|
+
function info(message) {
|
|
147
|
+
log(`ℹ️ ${message}`, 'blue');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Get the target directory for Copilot installation
|
|
152
|
+
* @returns {string} Path to .github directory
|
|
153
|
+
*/
|
|
154
|
+
function getTargetDir() {
|
|
155
|
+
return path.join(process.cwd(), '.github');
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Get the target directory for Claude Code installation
|
|
160
|
+
* @returns {string} Path to .claude directory
|
|
161
|
+
*/
|
|
162
|
+
function getClaudeTargetDir() {
|
|
163
|
+
return path.join(process.cwd(), '.claude');
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Strip GitHub Copilot frontmatter from a prompt file
|
|
168
|
+
* Removes the ---\nagent: ...\ndescription: ...\n--- block
|
|
169
|
+
* @param {string} content - File content
|
|
170
|
+
* @returns {string} Content without Copilot frontmatter
|
|
171
|
+
*/
|
|
172
|
+
function stripCopilotFrontmatter(content) {
|
|
173
|
+
return content.replace(/^---\n(?:[\s\S]*?\n)?---\n\n?/, '');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Adapt path references in prompt content for Claude Code
|
|
178
|
+
* @param {string} content - File content
|
|
179
|
+
* @returns {string} Content with updated paths
|
|
180
|
+
*/
|
|
181
|
+
function adaptPathsForClaude(content) {
|
|
182
|
+
return content
|
|
183
|
+
.replace(/\.github\/copilot-instructions\.md/g, 'CLAUDE.md')
|
|
184
|
+
.replace(/\.github\/prompts\/_partials\//g, '.claude/commands/_partials/');
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Copy a directory recursively
|
|
189
|
+
* @param {string} src - Source directory
|
|
190
|
+
* @param {string} dest - Destination directory
|
|
191
|
+
* @param {Object} options - Copy options
|
|
192
|
+
* @param {boolean} options.force - Overwrite existing files
|
|
193
|
+
* @param {boolean} options.dryRun - Only show what would be copied
|
|
194
|
+
* @param {(name: string) => string} [options.renameFile] - Optional file rename function
|
|
195
|
+
* @param {(content: string) => string} [options.transformContent] - Optional content transform
|
|
196
|
+
* @param {(name: string) => boolean} [options.filter] - Optional file filter function
|
|
197
|
+
* @returns {{ written: number, planned: number }} Number of written/planned files
|
|
198
|
+
*/
|
|
199
|
+
function copyDir(src, dest, options = {}) {
|
|
200
|
+
const {
|
|
201
|
+
force = false,
|
|
202
|
+
dryRun = false,
|
|
203
|
+
renameFile = (name) => name,
|
|
204
|
+
transformContent = null,
|
|
205
|
+
filter = null,
|
|
206
|
+
dirFilter = null,
|
|
207
|
+
} = options;
|
|
208
|
+
const totals = { written: 0, planned: 0 };
|
|
209
|
+
|
|
210
|
+
if (!fs.existsSync(src)) {
|
|
211
|
+
return totals;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (!dryRun && !fs.existsSync(dest)) {
|
|
215
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
219
|
+
|
|
220
|
+
for (const entry of entries) {
|
|
221
|
+
const srcPath = path.join(src, entry.name);
|
|
222
|
+
|
|
223
|
+
if (entry.isDirectory()) {
|
|
224
|
+
if (dirFilter && !dirFilter(entry.name)) {
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
const nestedOptions = { ...options };
|
|
228
|
+
if (entry.name === '_partials' && options.partialsFilter) {
|
|
229
|
+
nestedOptions.filter = options.partialsFilter;
|
|
230
|
+
}
|
|
231
|
+
const nested = copyDir(srcPath, path.join(dest, entry.name), nestedOptions);
|
|
232
|
+
totals.written += nested.written;
|
|
233
|
+
totals.planned += nested.planned;
|
|
234
|
+
} else {
|
|
235
|
+
if (filter && !filter(entry.name)) {
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const destName = renameFile(entry.name);
|
|
240
|
+
const destPath = path.join(dest, destName);
|
|
241
|
+
const exists = fs.existsSync(destPath);
|
|
242
|
+
|
|
243
|
+
if (exists && !force) {
|
|
244
|
+
warn(`Skipping existing file: ${path.relative(process.cwd(), destPath)}`);
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
totals.planned++;
|
|
249
|
+
|
|
250
|
+
if (dryRun) {
|
|
251
|
+
info(`Would copy: ${path.relative(process.cwd(), destPath)}`);
|
|
252
|
+
} else {
|
|
253
|
+
if (transformContent) {
|
|
254
|
+
const rawContent = fs.readFileSync(srcPath, 'utf-8');
|
|
255
|
+
fs.writeFileSync(destPath, transformContent(rawContent));
|
|
256
|
+
} else {
|
|
257
|
+
fs.copyFileSync(srcPath, destPath);
|
|
258
|
+
}
|
|
259
|
+
totals.written++;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return totals;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function getInstallScope(options = {}) {
|
|
268
|
+
const {
|
|
269
|
+
promptsOnly = false,
|
|
270
|
+
partialsOnly = false,
|
|
271
|
+
skillsOnly = false,
|
|
272
|
+
instructionsOnly = false,
|
|
273
|
+
} = options;
|
|
274
|
+
|
|
275
|
+
const hasSpecificFlag = promptsOnly || partialsOnly || skillsOnly || instructionsOnly;
|
|
276
|
+
|
|
277
|
+
return {
|
|
278
|
+
shouldInstallPrompts: !hasSpecificFlag || promptsOnly || partialsOnly,
|
|
279
|
+
shouldInstallInstructions: !hasSpecificFlag || instructionsOnly,
|
|
280
|
+
shouldInstallSkills: !hasSpecificFlag || skillsOnly,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function getChangeCount(result, dryRun) {
|
|
285
|
+
return dryRun ? result.planned : result.written;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function ensureConfigFile({ dryRun = false } = {}) {
|
|
289
|
+
const configPath = path.join(process.cwd(), '.agents-toolkit.json');
|
|
290
|
+
|
|
291
|
+
if (fs.existsSync(configPath)) {
|
|
292
|
+
return { written: 0, planned: 0 };
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (dryRun) {
|
|
296
|
+
info('Would create .agents-toolkit.json');
|
|
297
|
+
return { written: 0, planned: 1 };
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
fs.writeFileSync(configPath, JSON.stringify(DEFAULT_CONFIG, null, 2));
|
|
301
|
+
success('Created .agents-toolkit.json config file');
|
|
302
|
+
return { written: 1, planned: 1 };
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function installCopilotInstructions({ targetDir, dryRun = false } = {}) {
|
|
306
|
+
const result = { written: 0, planned: 0 };
|
|
307
|
+
const copilotInstructionsPath = path.join(targetDir, 'copilot-instructions.md');
|
|
308
|
+
const templatePath = path.join(TEMPLATES_DIR, 'agents', 'copilot-instructions.md');
|
|
309
|
+
|
|
310
|
+
if (!fs.existsSync(templatePath)) {
|
|
311
|
+
return result;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const templateContent = fs.readFileSync(templatePath, 'utf-8');
|
|
315
|
+
|
|
316
|
+
if (fs.existsSync(copilotInstructionsPath)) {
|
|
317
|
+
const existingContent = fs.readFileSync(copilotInstructionsPath, 'utf-8');
|
|
318
|
+
const marker = '## 🔄 Copilot Agent Workflow';
|
|
319
|
+
|
|
320
|
+
if (existingContent.includes(marker)) {
|
|
321
|
+
info('copilot-instructions.md already contains key sections');
|
|
322
|
+
return result;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
result.planned++;
|
|
326
|
+
if (dryRun) {
|
|
327
|
+
info('Would append key sections to existing copilot-instructions.md');
|
|
328
|
+
return result;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const sectionsToAppend = templateContent.split('\n').slice(4).join('\n');
|
|
332
|
+
const newContent = `${existingContent}\n\n<!-- Added by agents-toolkit -->\n${sectionsToAppend}`;
|
|
333
|
+
fs.writeFileSync(copilotInstructionsPath, newContent);
|
|
334
|
+
success('Appended key sections to existing copilot-instructions.md');
|
|
335
|
+
result.written++;
|
|
336
|
+
return result;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
result.planned++;
|
|
340
|
+
if (dryRun) {
|
|
341
|
+
info('Would create copilot-instructions.md');
|
|
342
|
+
return result;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
fs.writeFileSync(copilotInstructionsPath, templateContent);
|
|
346
|
+
success('Created copilot-instructions.md with key sections');
|
|
347
|
+
result.written++;
|
|
348
|
+
return result;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function getAgentsTemplateBody(templateContent) {
|
|
352
|
+
const lines = templateContent.split('\n');
|
|
353
|
+
const dividerIndex = lines.indexOf('---');
|
|
354
|
+
|
|
355
|
+
if (dividerIndex === -1) {
|
|
356
|
+
return templateContent;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return lines.slice(dividerIndex + 1).join('\n').trimStart();
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function installAgentsFile(options = {}) {
|
|
363
|
+
const {
|
|
364
|
+
templatePath,
|
|
365
|
+
force = false,
|
|
366
|
+
append = false,
|
|
367
|
+
dryRun = false,
|
|
368
|
+
} = options;
|
|
369
|
+
|
|
370
|
+
const result = { written: 0, planned: 0 };
|
|
371
|
+
const agentsPath = path.join(process.cwd(), 'AGENTS.md');
|
|
372
|
+
|
|
373
|
+
if (!fs.existsSync(templatePath)) {
|
|
374
|
+
return result;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const agentsExists = fs.existsSync(agentsPath);
|
|
378
|
+
|
|
379
|
+
if (!agentsExists || force) {
|
|
380
|
+
result.planned++;
|
|
381
|
+
if (dryRun) {
|
|
382
|
+
info(agentsExists ? 'Would update AGENTS.md in project root' : 'Would create AGENTS.md in project root');
|
|
383
|
+
return result;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
fs.copyFileSync(templatePath, agentsPath);
|
|
387
|
+
success(agentsExists ? 'Updated AGENTS.md in project root' : 'Created AGENTS.md in project root');
|
|
388
|
+
result.written++;
|
|
389
|
+
return result;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
if (!append) {
|
|
393
|
+
info('AGENTS.md already exists in project root (use --force to overwrite or --append to merge)');
|
|
394
|
+
return result;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const existingContent = fs.readFileSync(agentsPath, 'utf-8');
|
|
398
|
+
const mergeMarker = '## 🔄 Agent Workflow (Complex Tasks)';
|
|
399
|
+
|
|
400
|
+
if (existingContent.includes(mergeMarker)) {
|
|
401
|
+
info('AGENTS.md already contains workflow sections');
|
|
402
|
+
return result;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
result.planned++;
|
|
406
|
+
if (dryRun) {
|
|
407
|
+
info('Would append missing sections to AGENTS.md');
|
|
408
|
+
return result;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
const templateContent = fs.readFileSync(templatePath, 'utf-8');
|
|
412
|
+
const templateBody = getAgentsTemplateBody(templateContent);
|
|
413
|
+
const mergedContent = `${existingContent}\n\n<!-- Added by agents-toolkit (--append) -->\n\n${templateBody}`;
|
|
414
|
+
fs.writeFileSync(agentsPath, mergedContent);
|
|
415
|
+
success('Appended missing sections to AGENTS.md');
|
|
416
|
+
result.written++;
|
|
417
|
+
return result;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
function installGitBasedTarget(options = {}, target = 'copilot') {
|
|
421
|
+
const {
|
|
422
|
+
force = false,
|
|
423
|
+
append = false,
|
|
424
|
+
dryRun = false,
|
|
425
|
+
filters = { stack: 'all', tracker: 'all' },
|
|
426
|
+
} = options;
|
|
427
|
+
const isCodex = target === 'codex';
|
|
428
|
+
const targetDir = getTargetDir();
|
|
429
|
+
const scope = getInstallScope(options);
|
|
430
|
+
let totalChanges = 0;
|
|
431
|
+
|
|
432
|
+
const makeFilter = (category) => (name) => {
|
|
433
|
+
const basename = name.replace(/\.(prompt\.md|instructions\.md|md)$/, '');
|
|
434
|
+
return shouldIncludeFile(basename, category, filters);
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
const promptsFilter = makeFilter('prompts');
|
|
438
|
+
const partialsFilter = makeFilter('partials');
|
|
439
|
+
|
|
440
|
+
log(isCodex ? '\n⚡ Codex Installer\n' : '\n📦 Agents Toolkit Installer\n', 'bright');
|
|
441
|
+
|
|
442
|
+
if (dryRun) {
|
|
443
|
+
info('Dry run mode - no files will be copied\n');
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if (scope.shouldInstallPrompts) {
|
|
447
|
+
info('Installing prompts...');
|
|
448
|
+
const result = copyDir(path.join(TEMPLATES_DIR, 'shared', 'prompts'), path.join(targetDir, 'prompts'), { force, dryRun, filter: promptsFilter, partialsFilter });
|
|
449
|
+
totalChanges += getChangeCount(result, dryRun);
|
|
450
|
+
|
|
451
|
+
if (!dryRun && result.written > 0) {
|
|
452
|
+
success(`Installed ${result.written} prompt files`);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
if (scope.shouldInstallInstructions) {
|
|
457
|
+
info('Installing instructions...');
|
|
458
|
+
const result = copyDir(path.join(TEMPLATES_DIR, 'shared', 'instructions'), path.join(targetDir, 'instructions'), { force, dryRun, filter: makeFilter('instructions') });
|
|
459
|
+
totalChanges += getChangeCount(result, dryRun);
|
|
460
|
+
|
|
461
|
+
if (!dryRun && result.written > 0) {
|
|
462
|
+
success(`Installed ${result.written} instruction files`);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
if (scope.shouldInstallSkills) {
|
|
467
|
+
info('Installing skills...');
|
|
468
|
+
const result = copyDir(path.join(TEMPLATES_DIR, 'shared', 'skills'), path.join(targetDir, 'skills'), { force, dryRun, dirFilter: makeFilter('skills') });
|
|
469
|
+
totalChanges += getChangeCount(result, dryRun);
|
|
470
|
+
|
|
471
|
+
if (!dryRun && result.written > 0) {
|
|
472
|
+
success(`Installed ${result.written} skill files`);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
const configResult = ensureConfigFile({ dryRun });
|
|
477
|
+
totalChanges += getChangeCount(configResult, dryRun);
|
|
478
|
+
|
|
479
|
+
if (scope.shouldInstallInstructions && !isCodex) {
|
|
480
|
+
const copilotInstructionsResult = installCopilotInstructions({ targetDir, dryRun });
|
|
481
|
+
totalChanges += getChangeCount(copilotInstructionsResult, dryRun);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
if (scope.shouldInstallInstructions) {
|
|
485
|
+
const agentsTemplatePath = isCodex
|
|
486
|
+
? path.join(TEMPLATES_DIR, 'agents', 'AGENTS.codex.md')
|
|
487
|
+
: path.join(TEMPLATES_DIR, 'agents', 'AGENTS.md');
|
|
488
|
+
const agentsResult = installAgentsFile({ templatePath: agentsTemplatePath, force, append, dryRun });
|
|
489
|
+
totalChanges += getChangeCount(agentsResult, dryRun);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
console.log('');
|
|
493
|
+
if (dryRun) {
|
|
494
|
+
info(`Dry run complete. ${totalChanges} files would be installed.`);
|
|
495
|
+
} else if (totalChanges > 0) {
|
|
496
|
+
success(`Installation complete! ${totalChanges} files installed.`);
|
|
497
|
+
console.log('');
|
|
498
|
+
info('Next steps:');
|
|
499
|
+
console.log(' 1. Update .agents-toolkit.json with your Jira project key');
|
|
500
|
+
if (isCodex) {
|
|
501
|
+
console.log(' 2. Review AGENTS.md in the project root');
|
|
502
|
+
console.log(' 3. Run Codex from this project root');
|
|
503
|
+
} else {
|
|
504
|
+
console.log(' 2. Configure Atlassian MCP in VS Code');
|
|
505
|
+
console.log(' 3. Run prompts via Command Palette > "GitHub Copilot: Run Prompt"');
|
|
506
|
+
}
|
|
507
|
+
} else {
|
|
508
|
+
warn('No new files installed. Use --force to overwrite existing files.');
|
|
509
|
+
}
|
|
510
|
+
console.log('');
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* Install prompts to target directory
|
|
515
|
+
* @param {Object} options - Install options
|
|
516
|
+
*/
|
|
517
|
+
function install(options = {}) {
|
|
518
|
+
installGitBasedTarget(options, 'copilot');
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Install files for Codex
|
|
523
|
+
* @param {Object} options - Install options
|
|
524
|
+
*/
|
|
525
|
+
function installCodex(options = {}) {
|
|
526
|
+
installGitBasedTarget(options, 'codex');
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Install Claude Code files (CLAUDE.md + .claude/commands/)
|
|
531
|
+
* @param {Object} options - Install options
|
|
532
|
+
*/
|
|
533
|
+
function installClaude(options = {}) {
|
|
534
|
+
const { force = false, dryRun = false, filters = { stack: 'all', tracker: 'all' } } = options;
|
|
535
|
+
const scope = getInstallScope(options);
|
|
536
|
+
const claudeDir = getClaudeTargetDir();
|
|
537
|
+
const githubDir = getTargetDir();
|
|
538
|
+
let totalChanges = 0;
|
|
539
|
+
|
|
540
|
+
const makeFilter = (category) => (name) => {
|
|
541
|
+
const basename = name.replace(/\.(prompt\.md|instructions\.md|md)$/, '');
|
|
542
|
+
return shouldIncludeFile(basename, category, filters);
|
|
543
|
+
};
|
|
544
|
+
|
|
545
|
+
const promptsFilter = makeFilter('prompts');
|
|
546
|
+
const partialsFilter = makeFilter('partials');
|
|
547
|
+
|
|
548
|
+
log('\n🤖 Claude Code Installer\n', 'bright');
|
|
549
|
+
|
|
550
|
+
if (dryRun) {
|
|
551
|
+
info('Dry run mode - no files will be copied\n');
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
if (scope.shouldInstallPrompts) {
|
|
555
|
+
info('Installing slash commands...');
|
|
556
|
+
const result = copyDir(path.join(TEMPLATES_DIR, 'shared', 'prompts'), path.join(claudeDir, 'commands'), {
|
|
557
|
+
force,
|
|
558
|
+
dryRun,
|
|
559
|
+
filter: promptsFilter,
|
|
560
|
+
partialsFilter,
|
|
561
|
+
renameFile: (name) => name.replace(/\.prompt\.md$/, '.md'),
|
|
562
|
+
transformContent: (content) => adaptPathsForClaude(stripCopilotFrontmatter(content)),
|
|
563
|
+
});
|
|
564
|
+
totalChanges += getChangeCount(result, dryRun);
|
|
565
|
+
|
|
566
|
+
if (!dryRun && result.written > 0) {
|
|
567
|
+
success(`Installed ${result.written} command files to .claude/commands/`);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
if (scope.shouldInstallInstructions) {
|
|
572
|
+
info('Installing instructions...');
|
|
573
|
+
const result = copyDir(path.join(TEMPLATES_DIR, 'shared', 'instructions'), path.join(githubDir, 'instructions'), { force, dryRun, filter: makeFilter('instructions') });
|
|
574
|
+
totalChanges += getChangeCount(result, dryRun);
|
|
575
|
+
|
|
576
|
+
if (!dryRun && result.written > 0) {
|
|
577
|
+
success(`Installed ${result.written} instruction files`);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
if (scope.shouldInstallSkills) {
|
|
582
|
+
info('Installing skills...');
|
|
583
|
+
const result = copyDir(path.join(TEMPLATES_DIR, 'shared', 'skills'), path.join(githubDir, 'skills'), { force, dryRun, dirFilter: makeFilter('skills') });
|
|
584
|
+
totalChanges += getChangeCount(result, dryRun);
|
|
585
|
+
|
|
586
|
+
if (!dryRun && result.written > 0) {
|
|
587
|
+
success(`Installed ${result.written} skill files`);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
if (scope.shouldInstallInstructions) {
|
|
592
|
+
const claudeMdPath = path.join(process.cwd(), 'CLAUDE.md');
|
|
593
|
+
const claudeMdTemplate = path.join(TEMPLATES_DIR, 'agents', 'CLAUDE.md');
|
|
594
|
+
|
|
595
|
+
if (fs.existsSync(claudeMdTemplate)) {
|
|
596
|
+
const exists = fs.existsSync(claudeMdPath);
|
|
597
|
+
if (exists && !force) {
|
|
598
|
+
info('CLAUDE.md already exists (use --force to overwrite)');
|
|
599
|
+
} else {
|
|
600
|
+
totalChanges += 1;
|
|
601
|
+
if (dryRun) {
|
|
602
|
+
info(exists ? 'Would update CLAUDE.md' : 'Would create CLAUDE.md');
|
|
603
|
+
} else {
|
|
604
|
+
fs.copyFileSync(claudeMdTemplate, claudeMdPath);
|
|
605
|
+
success(exists ? 'Updated CLAUDE.md' : 'Created CLAUDE.md');
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
const configResult = ensureConfigFile({ dryRun });
|
|
612
|
+
totalChanges += getChangeCount(configResult, dryRun);
|
|
613
|
+
|
|
614
|
+
console.log('');
|
|
615
|
+
if (dryRun) {
|
|
616
|
+
info(`Dry run complete. ${totalChanges} files would be installed.`);
|
|
617
|
+
} else if (totalChanges > 0) {
|
|
618
|
+
success(`Installation complete! ${totalChanges} files installed.`);
|
|
619
|
+
console.log('');
|
|
620
|
+
info('Next steps:');
|
|
621
|
+
console.log(' 1. Update .agents-toolkit.json with your Jira project key');
|
|
622
|
+
console.log(' 2. Configure Atlassian MCP in Claude Code settings');
|
|
623
|
+
console.log(' 3. Run slash commands with /analyze-ticket, /work-ticket, etc.');
|
|
624
|
+
} else {
|
|
625
|
+
warn('No new files installed. Use --force to overwrite existing files.');
|
|
626
|
+
}
|
|
627
|
+
console.log('');
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* List available prompts
|
|
632
|
+
*/
|
|
633
|
+
function list() {
|
|
634
|
+
log('\n📋 Available Prompts\n', 'bright');
|
|
635
|
+
|
|
636
|
+
const promptsDir = path.join(TEMPLATES_DIR, 'shared', 'prompts');
|
|
637
|
+
|
|
638
|
+
if (!fs.existsSync(promptsDir)) {
|
|
639
|
+
error('Templates directory not found');
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
const prompts = fs.readdirSync(promptsDir)
|
|
644
|
+
.filter(f => f.endsWith('.prompt.md'))
|
|
645
|
+
.map(f => f.replace('.prompt.md', ''));
|
|
646
|
+
|
|
647
|
+
log('Workflow Prompts:', 'cyan');
|
|
648
|
+
const workflowPrompts = ['analyze-ticket', 'create-plan', 'work-ticket', 'prepare-pr', 'create-pr', 'finalize-pr'];
|
|
649
|
+
workflowPrompts.forEach((p, i) => {
|
|
650
|
+
if (prompts.includes(p)) {
|
|
651
|
+
console.log(` ${i + 1}. ${p}`);
|
|
652
|
+
}
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
console.log('');
|
|
656
|
+
log('Utility Prompts:', 'cyan');
|
|
657
|
+
const utilityPrompts = prompts.filter(p => !workflowPrompts.includes(p));
|
|
658
|
+
utilityPrompts.forEach(p => {
|
|
659
|
+
console.log(` • ${p}`);
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
console.log('');
|
|
663
|
+
log('Partials:', 'cyan');
|
|
664
|
+
const partialsDir = path.join(promptsDir, '_partials');
|
|
665
|
+
if (fs.existsSync(partialsDir)) {
|
|
666
|
+
const partials = fs.readdirSync(partialsDir)
|
|
667
|
+
.filter(f => f.endsWith('.md') && f !== 'README.md');
|
|
668
|
+
partials.forEach(p => {
|
|
669
|
+
console.log(` • ${p.replace('.md', '')}`);
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
console.log('');
|
|
674
|
+
log('Skills:', 'cyan');
|
|
675
|
+
const skillsDir = path.join(TEMPLATES_DIR, 'shared', 'skills');
|
|
676
|
+
if (fs.existsSync(skillsDir)) {
|
|
677
|
+
const skills = fs.readdirSync(skillsDir, { withFileTypes: true })
|
|
678
|
+
.filter(d => d.isDirectory())
|
|
679
|
+
.map(d => d.name);
|
|
680
|
+
skills.forEach(s => {
|
|
681
|
+
console.log(` • ${s}`);
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
console.log('');
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
/**
|
|
688
|
+
* Show help message
|
|
689
|
+
*/
|
|
690
|
+
function showHelp() {
|
|
691
|
+
log('\n📦 Agents Toolkit\n', 'bright');
|
|
692
|
+
console.log('Usage: agents-toolkit <command> [options]\n');
|
|
693
|
+
|
|
694
|
+
log('Commands:', 'cyan');
|
|
695
|
+
console.log(' install Install prompts (default target: copilot)');
|
|
696
|
+
console.log(' list List available prompts');
|
|
697
|
+
console.log(' update Update existing prompts (alias for install --force)');
|
|
698
|
+
console.log(' help Show this help message');
|
|
699
|
+
|
|
700
|
+
console.log('');
|
|
701
|
+
log('Options:', 'cyan');
|
|
702
|
+
console.log(' --force, -f Overwrite existing files');
|
|
703
|
+
console.log(' --target <name> Target installer: copilot | claude | codex');
|
|
704
|
+
console.log(' --stack <name> Filter by stack: react | wordpress | all (default: all)');
|
|
705
|
+
console.log(' --tracker <name> Filter by tracker: jira | github | all (default: all)');
|
|
706
|
+
console.log(' --claude Install for Claude Code (.claude/commands/ + CLAUDE.md)');
|
|
707
|
+
console.log(' --codex Install for Codex (AGENTS.md + shared .github files)');
|
|
708
|
+
console.log(' --append Append missing AGENTS.md sections instead of overwriting');
|
|
709
|
+
console.log(' --prompts-only Only install prompts (no instructions/skills)');
|
|
710
|
+
console.log(' --instructions-only Only install instructions');
|
|
711
|
+
console.log(' --partials-only Only install partials');
|
|
712
|
+
console.log(' --skills-only Only install skills');
|
|
713
|
+
console.log(' --dry-run Show what would be installed');
|
|
714
|
+
|
|
715
|
+
console.log('');
|
|
716
|
+
log('Examples:', 'cyan');
|
|
717
|
+
console.log(' npx agents-toolkit install # All content');
|
|
718
|
+
console.log(' npx agents-toolkit install --stack react # React/TS only');
|
|
719
|
+
console.log(' npx agents-toolkit install --stack wordpress # PHP/WordPress only');
|
|
720
|
+
console.log(' npx agents-toolkit install --tracker github # GitHub Issues workflow');
|
|
721
|
+
console.log(' npx agents-toolkit install --tracker jira # Jira workflow');
|
|
722
|
+
console.log(' npx agents-toolkit install --target codex');
|
|
723
|
+
console.log(' npx agents-toolkit install --target=claude');
|
|
724
|
+
console.log(' npx agents-toolkit install --force');
|
|
725
|
+
console.log(' npx agents-toolkit install --append --instructions-only');
|
|
726
|
+
console.log(' npx agents-toolkit install --claude --force');
|
|
727
|
+
console.log(' npx agents-toolkit install --codex --force');
|
|
728
|
+
console.log(' npx agents-toolkit install --prompts-only');
|
|
729
|
+
console.log(' npx agents-toolkit list');
|
|
730
|
+
console.log('');
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
/**
|
|
734
|
+
* Parse command line arguments
|
|
735
|
+
* @returns {Object} Parsed arguments
|
|
736
|
+
*/
|
|
737
|
+
function parseArgs() {
|
|
738
|
+
const args = process.argv.slice(2);
|
|
739
|
+
const command = args[0] || 'help';
|
|
740
|
+
const flags = args.slice(1);
|
|
741
|
+
|
|
742
|
+
let target = null;
|
|
743
|
+
let stack = null;
|
|
744
|
+
let tracker = null;
|
|
745
|
+
for (let i = 0; i < flags.length; i++) {
|
|
746
|
+
const arg = flags[i];
|
|
747
|
+
if (arg === '--target') {
|
|
748
|
+
const value = flags[i + 1];
|
|
749
|
+
if (value && !value.startsWith('-')) {
|
|
750
|
+
target = value;
|
|
751
|
+
i++;
|
|
752
|
+
} else {
|
|
753
|
+
target = '';
|
|
754
|
+
}
|
|
755
|
+
} else if (arg.startsWith('--target=')) {
|
|
756
|
+
target = arg.split('=').slice(1).join('=');
|
|
757
|
+
} else if (arg === '--stack') {
|
|
758
|
+
const value = flags[i + 1];
|
|
759
|
+
if (value && !value.startsWith('-')) {
|
|
760
|
+
stack = value;
|
|
761
|
+
i++;
|
|
762
|
+
} else {
|
|
763
|
+
stack = '';
|
|
764
|
+
}
|
|
765
|
+
} else if (arg.startsWith('--stack=')) {
|
|
766
|
+
stack = arg.split('=').slice(1).join('=');
|
|
767
|
+
} else if (arg === '--tracker') {
|
|
768
|
+
const value = flags[i + 1];
|
|
769
|
+
if (value && !value.startsWith('-')) {
|
|
770
|
+
tracker = value;
|
|
771
|
+
i++;
|
|
772
|
+
} else {
|
|
773
|
+
tracker = '';
|
|
774
|
+
}
|
|
775
|
+
} else if (arg.startsWith('--tracker=')) {
|
|
776
|
+
tracker = arg.split('=').slice(1).join('=');
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
const options = {
|
|
781
|
+
force: flags.includes('--force') || flags.includes('-f'),
|
|
782
|
+
promptsOnly: flags.includes('--prompts-only'),
|
|
783
|
+
partialsOnly: flags.includes('--partials-only'),
|
|
784
|
+
skillsOnly: flags.includes('--skills-only'),
|
|
785
|
+
instructionsOnly: flags.includes('--instructions-only'),
|
|
786
|
+
dryRun: flags.includes('--dry-run'),
|
|
787
|
+
claude: flags.includes('--claude'),
|
|
788
|
+
codex: flags.includes('--codex'),
|
|
789
|
+
append: flags.includes('--append'),
|
|
790
|
+
target,
|
|
791
|
+
stack,
|
|
792
|
+
tracker,
|
|
793
|
+
};
|
|
794
|
+
|
|
795
|
+
return { command, options };
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
function resolveInstallTarget(options = {}) {
|
|
799
|
+
const legacyTargets = [];
|
|
800
|
+
|
|
801
|
+
if (options.claude) {
|
|
802
|
+
legacyTargets.push('claude');
|
|
803
|
+
}
|
|
804
|
+
if (options.codex) {
|
|
805
|
+
legacyTargets.push('codex');
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
let explicitTarget = null;
|
|
809
|
+
if (options.target !== null && options.target !== undefined) {
|
|
810
|
+
explicitTarget = options.target.trim().toLowerCase();
|
|
811
|
+
if (!explicitTarget) {
|
|
812
|
+
error('Missing value for --target. Use copilot, claude, or codex.');
|
|
813
|
+
process.exit(1);
|
|
814
|
+
}
|
|
815
|
+
if (!['copilot', 'claude', 'codex'].includes(explicitTarget)) {
|
|
816
|
+
error(`Invalid --target value: ${options.target}. Use copilot, claude, or codex.`);
|
|
817
|
+
process.exit(1);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
if (legacyTargets.length > 1) {
|
|
822
|
+
error('Use either --claude or --codex, not both.');
|
|
823
|
+
process.exit(1);
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
if (explicitTarget && legacyTargets.length > 0 && legacyTargets[0] !== explicitTarget) {
|
|
827
|
+
error(`Conflicting target flags: --target ${explicitTarget} and --${legacyTargets[0]}.`);
|
|
828
|
+
process.exit(1);
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
if (explicitTarget) {
|
|
832
|
+
return explicitTarget;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
if (legacyTargets.length === 1) {
|
|
836
|
+
return legacyTargets[0];
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
return 'copilot';
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
/**
|
|
843
|
+
* Resolve stack and tracker filters from flags or config file
|
|
844
|
+
* @param {Object} options - Parsed CLI options
|
|
845
|
+
* @returns {{ stack: string, tracker: string }} Resolved filters
|
|
846
|
+
*/
|
|
847
|
+
function resolveFilters(options = {}) {
|
|
848
|
+
const validStacks = ['react', 'wordpress', 'all'];
|
|
849
|
+
const validTrackers = ['jira', 'github', 'all'];
|
|
850
|
+
|
|
851
|
+
let stack = 'all';
|
|
852
|
+
let tracker = 'all';
|
|
853
|
+
|
|
854
|
+
const configPath = path.join(process.cwd(), '.agents-toolkit.json');
|
|
855
|
+
if (fs.existsSync(configPath)) {
|
|
856
|
+
try {
|
|
857
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
858
|
+
if (config.stack) stack = config.stack;
|
|
859
|
+
if (config.tracker) tracker = config.tracker;
|
|
860
|
+
} catch {
|
|
861
|
+
// Ignore invalid config
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
if (options.stack !== null && options.stack !== undefined) {
|
|
866
|
+
const value = options.stack.trim().toLowerCase();
|
|
867
|
+
if (!value) {
|
|
868
|
+
error('Missing value for --stack. Use react, wordpress, or all.');
|
|
869
|
+
process.exit(1);
|
|
870
|
+
}
|
|
871
|
+
if (!validStacks.includes(value)) {
|
|
872
|
+
error(`Invalid --stack value: ${options.stack}. Use react, wordpress, or all.`);
|
|
873
|
+
process.exit(1);
|
|
874
|
+
}
|
|
875
|
+
stack = value;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
if (options.tracker !== null && options.tracker !== undefined) {
|
|
879
|
+
const value = options.tracker.trim().toLowerCase();
|
|
880
|
+
if (!value) {
|
|
881
|
+
error('Missing value for --tracker. Use jira, github, or all.');
|
|
882
|
+
process.exit(1);
|
|
883
|
+
}
|
|
884
|
+
if (!validTrackers.includes(value)) {
|
|
885
|
+
error(`Invalid --tracker value: ${options.tracker}. Use jira, github, or all.`);
|
|
886
|
+
process.exit(1);
|
|
887
|
+
}
|
|
888
|
+
tracker = value;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
return { stack, tracker };
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
/**
|
|
895
|
+
* Main CLI entry point
|
|
896
|
+
*/
|
|
897
|
+
function main() {
|
|
898
|
+
const { command, options } = parseArgs();
|
|
899
|
+
const target = (command === 'install' || command === 'update')
|
|
900
|
+
? resolveInstallTarget(options)
|
|
901
|
+
: null;
|
|
902
|
+
const filters = (command === 'install' || command === 'update')
|
|
903
|
+
? resolveFilters(options)
|
|
904
|
+
: { stack: 'all', tracker: 'all' };
|
|
905
|
+
|
|
906
|
+
switch (command) {
|
|
907
|
+
case 'install':
|
|
908
|
+
if (target === 'claude') {
|
|
909
|
+
installClaude({ ...options, filters });
|
|
910
|
+
} else if (target === 'codex') {
|
|
911
|
+
installCodex({ ...options, filters });
|
|
912
|
+
} else {
|
|
913
|
+
install({ ...options, filters });
|
|
914
|
+
}
|
|
915
|
+
break;
|
|
916
|
+
case 'update':
|
|
917
|
+
if (target === 'claude') {
|
|
918
|
+
installClaude({ ...options, force: true, filters });
|
|
919
|
+
} else if (target === 'codex') {
|
|
920
|
+
installCodex({ ...options, force: true, filters });
|
|
921
|
+
} else {
|
|
922
|
+
install({ ...options, force: true, filters });
|
|
923
|
+
}
|
|
924
|
+
break;
|
|
925
|
+
case 'list':
|
|
926
|
+
list();
|
|
927
|
+
break;
|
|
928
|
+
case 'help':
|
|
929
|
+
case '--help':
|
|
930
|
+
case '-h':
|
|
931
|
+
showHelp();
|
|
932
|
+
break;
|
|
933
|
+
default:
|
|
934
|
+
error(`Unknown command: ${command}`);
|
|
935
|
+
showHelp();
|
|
936
|
+
process.exit(1);
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
main();
|