@mark-gozner/aigile-method 0.4.5
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.md +26 -0
- package/README.md +300 -0
- package/core/agent-teams/team-all.yaml +24 -0
- package/core/agent-teams/team-company.yaml +17 -0
- package/core/agent-teams/team-enterprise.yaml +17 -0
- package/core/agent-teams/team-fullstack.yaml +16 -0
- package/core/agent-teams/team-ide-minimal.yaml +10 -0
- package/core/agents/aigile-master.md +476 -0
- package/core/agents/aigile-orchestrator.agent.md +200 -0
- package/core/agents/analyst.md +45 -0
- package/core/agents/architect.md +43 -0
- package/core/agents/code-tour.agent.md +208 -0
- package/core/agents/dev.agent.md +145 -0
- package/core/agents/dev.md +130 -0
- package/core/agents/expert-react-frontend-engineer.agent.md +741 -0
- package/core/agents/pm.md +33 -0
- package/core/agents/po.md +35 -0
- package/core/agents/qa.md +38 -0
- package/core/agents/sm.md +31 -0
- package/core/agents/ui-expert.md +39 -0
- package/core/agents/ux-expert.md +31 -0
- package/core/checklists/architect-checklist.md +246 -0
- package/core/checklists/change-checklist.md +182 -0
- package/core/checklists/pm-checklist.md +286 -0
- package/core/checklists/po-master-checklist.md +291 -0
- package/core/checklists/story-dod-checklist.md +94 -0
- package/core/checklists/story-draft-checklist.md +153 -0
- package/core/core-config.yaml +22 -0
- package/core/data/aigile-kb.md +112 -0
- package/core/data/brainstorming-techniques.md +73 -0
- package/core/data/elicitation-methods.md +42 -0
- package/core/data/technical-preferences.md +52 -0
- package/core/data/test-levels-framework.md +43 -0
- package/core/data/test-priorities-matrix.md +26 -0
- package/core/instructions/csharp.instructions.md +656 -0
- package/core/instructions/dotnet/csharp.instructions.md +656 -0
- package/core/instructions/java/java.instructions.md +67 -0
- package/core/instructions/java/spring-boot.instructions.md +122 -0
- package/core/instructions/java.instructions.md +67 -0
- package/core/instructions/spring-boot.instructions.md +122 -0
- package/core/prompts/README.md +11 -0
- package/core/prompts/architecture/architecture-blueprint-generator.prompt.md +322 -0
- package/core/prompts/architecture/architecture-validation.prompt.md +71 -0
- package/core/prompts/architecture/file-tree-generator.prompt.md +405 -0
- package/core/prompts/architecture/technical-project-analyze.prompt.md +43 -0
- package/core/prompts/architecture-blueprint-generator.prompt.md +322 -0
- package/core/prompts/architecture-validation.prompt.md +71 -0
- package/core/prompts/code-review.prompt.md +107 -0
- package/core/prompts/confluence-in-md.prompt.md +167 -0
- package/core/prompts/copilot-instructions-blueprint-generator.prompt.md +294 -0
- package/core/prompts/create-implementation-plan.prompt.md +157 -0
- package/core/prompts/create-oo-component-documentation.prompt.md +193 -0
- package/core/prompts/file-tree-generator.prompt.md +405 -0
- package/core/prompts/generate-unit-tests.prompt.md +291 -0
- package/core/prompts/java/java-doc.prompt.md +24 -0
- package/core/prompts/java/java-junit.prompt.md +64 -0
- package/core/prompts/java/junit-5.prompt.md +64 -0
- package/core/prompts/java-doc.prompt.md +24 -0
- package/core/prompts/java-junit.prompt.md +64 -0
- package/core/prompts/junit-5.prompt.md +64 -0
- package/core/prompts/release-notes/README.md +11 -0
- package/core/prompts/release-notes/release-notes.prompt.md +723 -0
- package/core/prompts/release-notes.prompt.md +723 -0
- package/core/prompts/technical-project-analyze.prompt.md +43 -0
- package/core/tasks/advanced-elicitation.md +119 -0
- package/core/tasks/check-story-implemented.md +44 -0
- package/core/tasks/code-arch-review-with-github.md +40 -0
- package/core/tasks/create-architecture-doc.md +55 -0
- package/core/tasks/create-jira-epic-from-confluence.md +70 -0
- package/core/tasks/create-jira-story-from-confluence.md +39 -0
- package/core/tasks/create-jira-story-from-text.md +39 -0
- package/core/tasks/create-next-story.md +35 -0
- package/core/tasks/create-prd-doc.md +54 -0
- package/core/tasks/create-stories-from-epic.md +66 -0
- package/core/tasks/create-tasks-for-story.md +60 -0
- package/core/tasks/document-project.md +69 -0
- package/core/tasks/execute-checklist.md +37 -0
- package/core/tasks/explain-story-from-jira.md +44 -0
- package/core/tasks/facilitate-brainstorming-session.md +69 -0
- package/core/tasks/figma-audit-design-system.md +20 -0
- package/core/tasks/front-end-spec-from-design.md +33 -0
- package/core/tasks/gate.md +64 -0
- package/core/tasks/groom-jira-story.md +52 -0
- package/core/tasks/help.md +27 -0
- package/core/tasks/implement-freeform-work-item.md +30 -0
- package/core/tasks/implement-story-from-jira.md +63 -0
- package/core/tasks/implement-unit-tests.md +45 -0
- package/core/tasks/market-research-from-context7.md +37 -0
- package/core/tasks/review-story.md +30 -0
- package/core/tasks/sonarqube-hotspot-review.md +39 -0
- package/core/tasks/standup-digest.md +21 -0
- package/core/tasks/sync-jira-backlog.md +32 -0
- package/core/tasks/test-design.md +68 -0
- package/core/tasks/validate-next-story.md +37 -0
- package/core/tasks/verify-jira-story-e2e.md +45 -0
- package/core/templates/architecture-tmpl.yaml +651 -0
- package/core/templates/brainstorming-output-tmpl.yaml +156 -0
- package/core/templates/brownfield-architecture-tmpl.yaml +477 -0
- package/core/templates/brownfield-prd-tmpl.yaml +281 -0
- package/core/templates/front-end-architecture-tmpl.yaml +219 -0
- package/core/templates/front-end-spec-tmpl.yaml +350 -0
- package/core/templates/fullstack-architecture-tmpl.yaml +824 -0
- package/core/templates/market-research-tmpl.yaml +253 -0
- package/core/templates/prd-tmpl.yaml +203 -0
- package/core/templates/project-brief-tmpl.yaml +222 -0
- package/core/templates/qa-gate-tmpl.yaml +103 -0
- package/core/templates/story-tmpl.yaml +138 -0
- package/core/workflows/brownfield-fullstack.yaml +298 -0
- package/core/workflows/brownfield-service.yaml +188 -0
- package/core/workflows/brownfield-ui.yaml +198 -0
- package/core/workflows/greenfield-fullstack.yaml +241 -0
- package/core/workflows/greenfield-service.yaml +207 -0
- package/core/workflows/greenfield-ui.yaml +236 -0
- package/dist/agents/aigile-master.txt +500 -0
- package/dist/agents/aigile-orchestrator.agent.txt +224 -0
- package/dist/agents/analyst.txt +69 -0
- package/dist/agents/architect.txt +67 -0
- package/dist/agents/code-tour.agent.txt +232 -0
- package/dist/agents/dev.agent.txt +169 -0
- package/dist/agents/dev.txt +154 -0
- package/dist/agents/expert-react-frontend-engineer.agent.txt +765 -0
- package/dist/agents/pm.txt +57 -0
- package/dist/agents/po.txt +59 -0
- package/dist/agents/qa.txt +62 -0
- package/dist/agents/sm.txt +55 -0
- package/dist/agents/ui-expert.txt +63 -0
- package/dist/agents/ux-expert.txt +55 -0
- package/dist/dev-agent-bundle.txt +154 -0
- package/dist/teams/team-company.txt +10789 -0
- package/docs/mcp-servers.md +102 -0
- package/docs/orchestrator-guide.md +526 -0
- package/mcp/servers.json +108 -0
- package/mcp/servers.yaml +124 -0
- package/package.json +72 -0
- package/tools/cli.js +1864 -0
- package/tools/installer/README.md +24 -0
- package/tools/installer/lib/ide-setup.js +295 -0
- package/tools/installer/lib/installer.js +131 -0
- package/tools/md-assets/web-agent-startup-instructions.md +21 -0
- package/tools/postinstall.js +72 -0
- package/tools/shared/bannerArt.js +68 -0
- package/tools/validate-bundles.js +54 -0
- package/tools/verify-publish-registry.js +34 -0
package/tools/cli.js
ADDED
|
@@ -0,0 +1,1864 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { createInterface, emitKeypressEvents } from 'node:readline';
|
|
6
|
+
import { printInstallBanner } from './shared/bannerArt.js';
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = path.dirname(__filename);
|
|
10
|
+
const root = path.resolve(__dirname, '..');
|
|
11
|
+
|
|
12
|
+
// --- npm registry constants (public npm.js registry is the default) ------------------
|
|
13
|
+
const AIGILE_SCOPE = '@mark-gozner';
|
|
14
|
+
const AIGILE_PACKAGE = '@mark-gozner/aigile-method';
|
|
15
|
+
|
|
16
|
+
// Lazy-load package version (works when installed as dependency)
|
|
17
|
+
let cachedPkgVersion = null;
|
|
18
|
+
function getPackageVersion() {
|
|
19
|
+
if (cachedPkgVersion) return cachedPkgVersion;
|
|
20
|
+
const pkgPath = path.join(root, 'package.json');
|
|
21
|
+
try {
|
|
22
|
+
const raw = read(pkgPath);
|
|
23
|
+
const json = JSON.parse(raw);
|
|
24
|
+
cachedPkgVersion = json.version || '0.0.0';
|
|
25
|
+
} catch {
|
|
26
|
+
cachedPkgVersion = '0.0.0';
|
|
27
|
+
}
|
|
28
|
+
return cachedPkgVersion;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function read(p) { return fs.readFileSync(p, 'utf8'); }
|
|
32
|
+
function write(p, c) { fs.mkdirSync(path.dirname(p), { recursive: true }); fs.writeFileSync(p, c, 'utf8'); }
|
|
33
|
+
|
|
34
|
+
function copyDir(src, dest, filter) {
|
|
35
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
36
|
+
for (const e of entries) {
|
|
37
|
+
const sp = path.join(src, e.name);
|
|
38
|
+
const dp = path.join(dest, e.name);
|
|
39
|
+
if (e.isDirectory()) {
|
|
40
|
+
copyDir(sp, dp, filter);
|
|
41
|
+
} else {
|
|
42
|
+
if (!filter || filter(sp)) {
|
|
43
|
+
write(dp, read(sp));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function listFilesRecursive(dir) {
|
|
50
|
+
const files = [];
|
|
51
|
+
if (!fs.existsSync(dir)) return files;
|
|
52
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
53
|
+
const p = path.join(dir, entry.name);
|
|
54
|
+
if (entry.isDirectory()) files.push(...listFilesRecursive(p));
|
|
55
|
+
else files.push(p);
|
|
56
|
+
}
|
|
57
|
+
return files;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function bundle() {
|
|
61
|
+
// Attempt to read optional startup header; fall back to a minimal banner if missing
|
|
62
|
+
const headerPath = path.join(root, 'tools', 'md-assets', 'web-agent-startup-instructions.md');
|
|
63
|
+
let header = '# AIgile Bundle\n\n';
|
|
64
|
+
if (fs.existsSync(headerPath)) {
|
|
65
|
+
try { header = read(headerPath); } catch { /* ignore and use fallback */ }
|
|
66
|
+
} else {
|
|
67
|
+
header += 'Default header (web-agent-startup-instructions.md not found).';
|
|
68
|
+
}
|
|
69
|
+
const SEP = '====================';
|
|
70
|
+
const bundleRoot = '.aigile-core';
|
|
71
|
+
|
|
72
|
+
// Helper to wrap content with START/END markers using dot-prefixed bundle root
|
|
73
|
+
const wrap = (absPath, content) => {
|
|
74
|
+
const rel = path.relative(root, absPath).replace(/\\/g, '/');
|
|
75
|
+
const mapped = `${bundleRoot}/${rel.replace(/^core\//, '')}`; // map core/* -> .aigile-core/*
|
|
76
|
+
return [
|
|
77
|
+
`${SEP} START: ${mapped} ${SEP}`,
|
|
78
|
+
content.trim(),
|
|
79
|
+
`${SEP} END: ${mapped} ${SEP}`,
|
|
80
|
+
''
|
|
81
|
+
].join('\n');
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// 1) Build TEAM bundle like BMAD's dist/teams/*.txt
|
|
85
|
+
const teamPath = path.join(root, 'core', 'agent-teams', 'team-company.yaml');
|
|
86
|
+
const teamOut = [header];
|
|
87
|
+
if (fs.existsSync(teamPath)) {
|
|
88
|
+
teamOut.push(wrap(teamPath, read(teamPath)));
|
|
89
|
+
}
|
|
90
|
+
const buckets = ['agents', 'tasks', 'templates', 'checklists', 'data', 'workflows'];
|
|
91
|
+
for (const b of buckets) {
|
|
92
|
+
const dir = path.join(root, 'core', b);
|
|
93
|
+
for (const p of listFilesRecursive(dir)) {
|
|
94
|
+
teamOut.push(wrap(p, read(p)));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
write(path.join(root, 'dist', 'teams', 'team-company.txt'), teamOut.join('\n'));
|
|
98
|
+
console.log('✅ Wrote dist/teams/team-company.txt');
|
|
99
|
+
|
|
100
|
+
// 2) Per-agent bundles like BMAD's dist/agents/*.txt
|
|
101
|
+
const agentsDir = path.join(root, 'core', 'agents');
|
|
102
|
+
if (fs.existsSync(agentsDir)) {
|
|
103
|
+
for (const f of fs.readdirSync(agentsDir)) {
|
|
104
|
+
const p = path.join(agentsDir, f);
|
|
105
|
+
if (fs.statSync(p).isFile() && f.endsWith('.md')) {
|
|
106
|
+
const agentOut = [header, wrap(p, read(p))];
|
|
107
|
+
write(path.join(root, 'dist', 'agents', f.replace(/\.md$/, '.txt')), agentOut.join('\n'));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
console.log('✅ Wrote dist/agents/*.txt');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// 3) Dev-focused bundle (agent + dev tasks)
|
|
114
|
+
const devFile = path.join(agentsDir, 'dev.md');
|
|
115
|
+
if (fs.existsSync(devFile)) {
|
|
116
|
+
const devOut = [header, wrap(devFile, read(devFile))];
|
|
117
|
+
const devTasks = path.join(root, 'core', 'tasks', 'dev');
|
|
118
|
+
for (const p of listFilesRecursive(devTasks)) {
|
|
119
|
+
devOut.push(wrap(p, read(p)));
|
|
120
|
+
}
|
|
121
|
+
write(path.join(root, 'dist', 'dev-agent-bundle.txt'), devOut.join('\n'));
|
|
122
|
+
console.log('✅ Wrote dist/dev-agent-bundle.txt');
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ---------------------------------------------------------------------------
|
|
127
|
+
// Auth helpers (new simplified bootstrap)
|
|
128
|
+
// ---------------------------------------------------------------------------
|
|
129
|
+
|
|
130
|
+
function getUserHome() {
|
|
131
|
+
return process.env.HOME || process.env.USERPROFILE || process.env.USERDIR || process.cwd();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function readUserNpmrc() {
|
|
135
|
+
const home = getUserHome();
|
|
136
|
+
const npmrcPath = path.join(home, '.npmrc');
|
|
137
|
+
if (!fs.existsSync(npmrcPath)) return { path: npmrcPath, lines: [] };
|
|
138
|
+
try { return { path: npmrcPath, lines: fs.readFileSync(npmrcPath, 'utf8').split(/\r?\n/) }; }
|
|
139
|
+
catch { return { path: npmrcPath, lines: [] }; }
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function writeUserNpmrc(npmrcPath, lines) {
|
|
143
|
+
const content = lines.filter(l => l.trim().length).join('\n') + '\n';
|
|
144
|
+
fs.writeFileSync(npmrcPath, content, 'utf8');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async function promptHidden(question) {
|
|
148
|
+
if (!process.stdin.isTTY) return null; // cannot prompt
|
|
149
|
+
return await new Promise(resolve => {
|
|
150
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
151
|
+
const stdin = process.stdin;
|
|
152
|
+
const onData = (char) => {
|
|
153
|
+
char = char + '';
|
|
154
|
+
switch (char) {
|
|
155
|
+
case '\n':
|
|
156
|
+
case '\r':
|
|
157
|
+
case '\u0004':
|
|
158
|
+
stdin.pause();
|
|
159
|
+
break;
|
|
160
|
+
default:
|
|
161
|
+
process.stdout.clearLine(0);
|
|
162
|
+
process.stdout.cursorTo(0);
|
|
163
|
+
process.stdout.write(question + ' ' + '*'.repeat(buffer.length));
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
let buffer = '';
|
|
168
|
+
process.stdout.write(question + ' ');
|
|
169
|
+
stdin.setRawMode(true);
|
|
170
|
+
stdin.resume();
|
|
171
|
+
stdin.on('data', (char) => {
|
|
172
|
+
char = char + '';
|
|
173
|
+
if (char === '\n' || char === '\r' || char === '\u0004') {
|
|
174
|
+
stdin.setRawMode(false);
|
|
175
|
+
stdin.removeListener('data', onData);
|
|
176
|
+
rl.close();
|
|
177
|
+
process.stdout.write('\n');
|
|
178
|
+
resolve(buffer.trim());
|
|
179
|
+
} else if (char === '\u0003') { // ctrl+c
|
|
180
|
+
stdin.setRawMode(false);
|
|
181
|
+
stdin.removeListener('data', onData);
|
|
182
|
+
rl.close();
|
|
183
|
+
process.stdout.write('\n');
|
|
184
|
+
resolve(null);
|
|
185
|
+
} else if (char === '\u0008' || char === '\u007f') { // backspace
|
|
186
|
+
buffer = buffer.slice(0, -1);
|
|
187
|
+
process.stdout.clearLine(0); process.stdout.cursorTo(0);
|
|
188
|
+
process.stdout.write(question + ' ' + '*'.repeat(buffer.length));
|
|
189
|
+
} else {
|
|
190
|
+
buffer += char;
|
|
191
|
+
process.stdout.clearLine(0); process.stdout.cursorTo(0);
|
|
192
|
+
process.stdout.write(question + ' ' + '*'.repeat(buffer.length));
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async function ensureAuthConfigured({ tokenFromFlags = null, nonInteractive = false } = {}) {
|
|
199
|
+
const { path: npmrcPath, lines } = readUserNpmrc();
|
|
200
|
+
let changed = false;
|
|
201
|
+
|
|
202
|
+
// For public npm, no special registry configuration is needed
|
|
203
|
+
// Users can optionally set NPM_TOKEN for publishing
|
|
204
|
+
return { configured: true, changed: false, npmrcPath };
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function pruneLegacy(dest) {
|
|
208
|
+
// Remove artifacts that earlier installer versions created
|
|
209
|
+
// - docs/ (skeleton docs)
|
|
210
|
+
// - .aigile (marker file)
|
|
211
|
+
const targets = [
|
|
212
|
+
path.join(dest, 'docs'),
|
|
213
|
+
path.join(dest, '.aigile')
|
|
214
|
+
];
|
|
215
|
+
let removed = [];
|
|
216
|
+
for (const p of targets) {
|
|
217
|
+
try {
|
|
218
|
+
if (fs.existsSync(p)) {
|
|
219
|
+
const stat = fs.statSync(p);
|
|
220
|
+
if (stat.isDirectory()) fs.rmSync(p, { recursive: true, force: true });
|
|
221
|
+
else fs.rmSync(p, { force: true });
|
|
222
|
+
removed.push(p);
|
|
223
|
+
}
|
|
224
|
+
} catch { /* no-op */ }
|
|
225
|
+
}
|
|
226
|
+
if (removed.length) {
|
|
227
|
+
const color = (code) => (s) => `\x1b[${code}m${s}\x1b[0m`;
|
|
228
|
+
const yellow = color(33);
|
|
229
|
+
console.log(yellow(`Pruned legacy artifacts:`));
|
|
230
|
+
for (const p of removed) console.log(` - ${p}`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
async function askYesNo(question, def = true) {
|
|
235
|
+
return new Promise((resolve) => {
|
|
236
|
+
const rl = process.stdin.isTTY ? createInterface({ input: process.stdin, output: process.stdout }) : null;
|
|
237
|
+
const q = `${question} ${def ? '[Y/n]' : '[y/N]'} `;
|
|
238
|
+
if (!rl) {
|
|
239
|
+
resolve(def);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
rl.question(q, (ans) => {
|
|
243
|
+
rl.close();
|
|
244
|
+
const a = (ans || '').trim().toLowerCase();
|
|
245
|
+
if (!a) return resolve(def);
|
|
246
|
+
resolve(a.startsWith('y'));
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
async function multiSelectPrompt({ title, options, defaults = [] }) {
|
|
252
|
+
return new Promise((resolve) => {
|
|
253
|
+
if (!process.stdin.isTTY) {
|
|
254
|
+
resolve(defaults);
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
258
|
+
emitKeypressEvents(process.stdin, rl);
|
|
259
|
+
if (process.stdin.isTTY) process.stdin.setRawMode(true);
|
|
260
|
+
|
|
261
|
+
const state = new Set(defaults);
|
|
262
|
+
let index = 0;
|
|
263
|
+
|
|
264
|
+
const render = () => {
|
|
265
|
+
const lines = [];
|
|
266
|
+
lines.push(`\n${title}`);
|
|
267
|
+
lines.push('Use Up/Down to move, Space to toggle, Enter to confirm');
|
|
268
|
+
for (let i = 0; i < options.length; i++) {
|
|
269
|
+
const o = options[i];
|
|
270
|
+
const cursor = i === index ? '>' : ' ';
|
|
271
|
+
const checked = state.has(o.value) ? '[x]' : '[ ]';
|
|
272
|
+
lines.push(` ${cursor} ${checked} ${o.label}`);
|
|
273
|
+
}
|
|
274
|
+
// Clear and re-render
|
|
275
|
+
rl.output.write('\x1b[2J\x1b[0f');
|
|
276
|
+
rl.output.write(lines.join('\n'));
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
const onKey = (_str, key) => {
|
|
280
|
+
if (key.name === 'down') { index = (index + 1) % options.length; render(); }
|
|
281
|
+
else if (key.name === 'up') { index = (index - 1 + options.length) % options.length; render(); }
|
|
282
|
+
else if (key.name === 'space') {
|
|
283
|
+
const val = options[index].value;
|
|
284
|
+
if (state.has(val)) state.delete(val); else state.add(val);
|
|
285
|
+
render();
|
|
286
|
+
}
|
|
287
|
+
else if (key.name === 'return' || (key.ctrl && key.name === 'm')) {
|
|
288
|
+
cleanup();
|
|
289
|
+
resolve([...state]);
|
|
290
|
+
}
|
|
291
|
+
else if (key.name === 'escape' || (key.ctrl && key.name === 'c')) {
|
|
292
|
+
cleanup();
|
|
293
|
+
resolve(defaults);
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
const cleanup = () => {
|
|
298
|
+
process.stdin.off('keypress', onKey);
|
|
299
|
+
if (process.stdin.isTTY) process.stdin.setRawMode(false);
|
|
300
|
+
rl.close();
|
|
301
|
+
console.log();
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
process.stdin.on('keypress', onKey);
|
|
305
|
+
render();
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
async function installInteractive({ autoYes = false }) {
|
|
310
|
+
// Default to GitHub Copilot-focused install
|
|
311
|
+
const dest = process.cwd();
|
|
312
|
+
printInstallBanner({ showGMSignature: true });
|
|
313
|
+
|
|
314
|
+
const forcedJavaInstructions = process.argv.includes('--with-java-instructions');
|
|
315
|
+
let addMcpJson = false;
|
|
316
|
+
let addMcpSettings = false;
|
|
317
|
+
let addJavaInstructions = forcedJavaInstructions;
|
|
318
|
+
if (autoYes) {
|
|
319
|
+
addMcpJson = false;
|
|
320
|
+
addMcpSettings = false;
|
|
321
|
+
} else {
|
|
322
|
+
const selections = await multiSelectPrompt({
|
|
323
|
+
title: 'Select optional components:',
|
|
324
|
+
options: [
|
|
325
|
+
{ label: 'Install MCP servers (.vscode/mcp.json from mcp/servers.json)', value: 'mcpJson' },
|
|
326
|
+
{ label: 'Add mock .vscode/settings.json MCP endpoints', value: 'mcpSettings' },
|
|
327
|
+
{ label: 'Include Java contributor instructions (.github/instructions/java.instructions.md)', value: 'javaInstructions' },
|
|
328
|
+
],
|
|
329
|
+
defaults: forcedJavaInstructions ? ['mcpJson', 'javaInstructions'] : ['mcpJson'],
|
|
330
|
+
});
|
|
331
|
+
addMcpJson = selections.includes('mcpJson');
|
|
332
|
+
addMcpSettings = selections.includes('mcpSettings');
|
|
333
|
+
if (!forcedJavaInstructions) addJavaInstructions = selections.includes('javaInstructions');
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
installGithubCopilot(dest, { addJavaInstructions });
|
|
337
|
+
// After core install, attempt to add consumer convenience scripts (opt-out with AIGILE_ADD_SCRIPTS=0)
|
|
338
|
+
maybeAddConsumerScripts(dest);
|
|
339
|
+
if (process.argv.includes('--prune-legacy')) pruneLegacy(dest);
|
|
340
|
+
if (addMcpSettings) writeMockMcp(dest);
|
|
341
|
+
if (addMcpJson) writeMcpJson(dest);
|
|
342
|
+
|
|
343
|
+
// Always write essential VS Code settings for orchestrator functionality
|
|
344
|
+
writeVscodeSettings(dest);
|
|
345
|
+
|
|
346
|
+
writeManifest(dest, { addMcpJson, addMcpSettings, addJavaInstructions });
|
|
347
|
+
|
|
348
|
+
// Print a concise, colorized summary
|
|
349
|
+
try {
|
|
350
|
+
// Check for new agents format first, fallback to legacy chatmodes
|
|
351
|
+
const agentsDir = path.join(dest, '.github', 'agents');
|
|
352
|
+
const chatModesDir = path.join(dest, '.github', 'chatmodes');
|
|
353
|
+
const agentCount = fs.existsSync(agentsDir) ? fs.readdirSync(agentsDir).filter(f => f.endsWith('.agent.md')).length : 0;
|
|
354
|
+
const modeCount = fs.existsSync(chatModesDir) ? fs.readdirSync(chatModesDir).filter(f => f.endsWith('.chatmode.md')).length : 0;
|
|
355
|
+
const totalAgents = agentCount + modeCount;
|
|
356
|
+
const coreDir = path.join(dest, '.aigile-core');
|
|
357
|
+
const coreFiles = listFilesRecursive(coreDir).length;
|
|
358
|
+
const color = (code) => (s) => `\x1b[${code}m${s}\x1b[0m`;
|
|
359
|
+
const green = color(32);
|
|
360
|
+
const cyan = color(36);
|
|
361
|
+
const yellow = color(33);
|
|
362
|
+
console.log(`\n${green('Summary:')}`);
|
|
363
|
+
console.log(` - .aigile-core files: ${cyan(String(coreFiles))}`);
|
|
364
|
+
console.log(` - Custom agents created: ${cyan(String(agentCount))}`);
|
|
365
|
+
if (modeCount > 0) console.log(` - Legacy chat modes: ${cyan(String(modeCount))} (consider upgrading)`);
|
|
366
|
+
if (addMcpJson) console.log(` - ${yellow('.vscode/mcp.json')} generated (fill API keys)`);
|
|
367
|
+
if (addMcpSettings) console.log(` - ${yellow('.vscode/settings.json')} updated with mock MCP servers`);
|
|
368
|
+
if (addJavaInstructions) console.log(` - ${yellow('.github/instructions/java.instructions.md')} copied`);
|
|
369
|
+
console.log(`\n${green('Next steps:')}`);
|
|
370
|
+
let step = 1;
|
|
371
|
+
console.log(` ${step++}) Open VS Code in this folder`);
|
|
372
|
+
console.log(` ${step++}) Use GitHub Copilot Chat and select an agent from the ${yellow('Agents dropdown')}`);
|
|
373
|
+
if (addMcpJson) console.log(` ${step++}) When prompted, provide the required API keys for MCP servers`);
|
|
374
|
+
if (addJavaInstructions) console.log(` ${step++}) Review ${yellow('.github/instructions/java.instructions.md')} for team guidelines`);
|
|
375
|
+
} catch { }
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function kebabCaseName(fileName) {
|
|
379
|
+
return fileName.replace(/\.[^/.]+$/, '')
|
|
380
|
+
.replace(/[^a-zA-Z0-9]+/g, '-')
|
|
381
|
+
.replace(/^-+|-+$/g, '')
|
|
382
|
+
.toLowerCase();
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function extractAgentTitle(agentMd) {
|
|
386
|
+
// Best effort: find `title:` inside the first ```yaml block
|
|
387
|
+
const m = agentMd.match(/```ya?ml\r?\n([\s\S]*?)\r?\n```/);
|
|
388
|
+
if (!m) return null;
|
|
389
|
+
const yamlBlock = m[1];
|
|
390
|
+
const t = yamlBlock.match(/\btitle\s*:\s*(.+)/);
|
|
391
|
+
return t ? t[1].trim() : null;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function toTitleCase(str) {
|
|
395
|
+
return str.replace(/[-_]+/g, ' ').replace(/\s+/g, ' ').trim().replace(/\b\w/g, c => c.toUpperCase());
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function readJsonIfExists(p) {
|
|
399
|
+
if (!fs.existsSync(p)) return null;
|
|
400
|
+
try { return JSON.parse(read(p)); } catch { return null; }
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function buildMcpCatalog() {
|
|
404
|
+
const serversJson = path.join(root, 'mcp', 'servers.json');
|
|
405
|
+
const doc = readJsonIfExists(serversJson);
|
|
406
|
+
if (!doc || !doc.servers) return [];
|
|
407
|
+
const catalog = [];
|
|
408
|
+
for (const [id, cfg] of Object.entries(doc.servers)) {
|
|
409
|
+
const lower = id.toLowerCase();
|
|
410
|
+
const item = { id, hints: [] };
|
|
411
|
+
if (lower.includes('github') || id === 'github.com') {
|
|
412
|
+
item.hints.push('Search repos, issues, code, and pull requests');
|
|
413
|
+
item.hints.push('Summarize diffs and review PRs');
|
|
414
|
+
item.hints.push('List branches, files, commit history');
|
|
415
|
+
}
|
|
416
|
+
if (lower.includes('atlassian')) {
|
|
417
|
+
item.hints.push('Query Jira issues, transitions, and comments');
|
|
418
|
+
item.hints.push('Search Confluence pages and spaces');
|
|
419
|
+
}
|
|
420
|
+
if (lower.includes('sonarqube')) {
|
|
421
|
+
item.hints.push('Get code quality metrics and hotspots');
|
|
422
|
+
item.hints.push('List vulnerabilities and code smells');
|
|
423
|
+
}
|
|
424
|
+
if (lower.includes('playwright')) {
|
|
425
|
+
item.hints.push('Generate and run browser tests');
|
|
426
|
+
item.hints.push('Record traces and produce reports');
|
|
427
|
+
}
|
|
428
|
+
if (lower.includes('memory')) {
|
|
429
|
+
item.hints.push('Store and recall project notes across sessions');
|
|
430
|
+
}
|
|
431
|
+
if (lower.includes('toolkit')) {
|
|
432
|
+
item.hints.push('Access gateway-provided tools (project-specific)');
|
|
433
|
+
}
|
|
434
|
+
if (lower.includes('sequentialthinking')) {
|
|
435
|
+
item.hints.push('Plan multi-step tasks and maintain checklists');
|
|
436
|
+
}
|
|
437
|
+
if (lower.includes('context7')) {
|
|
438
|
+
item.hints.push('Search knowledge bases and ground responses');
|
|
439
|
+
}
|
|
440
|
+
if (item.hints.length === 0) item.hints.push('General purpose server');
|
|
441
|
+
catalog.push(item);
|
|
442
|
+
}
|
|
443
|
+
return catalog;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
function listTasksForAgent(agentId) {
|
|
447
|
+
// Return specific tasks per agent based on BMAD approach
|
|
448
|
+
// Only include agent-specific tasks, plus selective common tasks
|
|
449
|
+
const agentTaskMappings = {
|
|
450
|
+
'aigile-orchestrator': [
|
|
451
|
+
'help.md'
|
|
452
|
+
// Orchestrator doesn't execute tasks directly - it coordinates agents
|
|
453
|
+
],
|
|
454
|
+
'aigile-master': [
|
|
455
|
+
'document-project.md',
|
|
456
|
+
'create-architecture-doc.md',
|
|
457
|
+
'create-prd-doc.md',
|
|
458
|
+
'execute-checklist.md',
|
|
459
|
+
'advanced-elicitation.md',
|
|
460
|
+
'facilitate-brainstorming-session.md',
|
|
461
|
+
'help.md'
|
|
462
|
+
// Master can execute any task, but these are commonly used
|
|
463
|
+
],
|
|
464
|
+
analyst: [
|
|
465
|
+
'facilitate-brainstorming-session.md',
|
|
466
|
+
'market-research-from-context7.md',
|
|
467
|
+
'advanced-elicitation.md'
|
|
468
|
+
],
|
|
469
|
+
architect: [
|
|
470
|
+
'code-arch-review-with-github.md',
|
|
471
|
+
'document-project.md',
|
|
472
|
+
'create-architecture-doc.md',
|
|
473
|
+
'execute-checklist.md'
|
|
474
|
+
],
|
|
475
|
+
dev: [
|
|
476
|
+
'check-story-implemented.md',
|
|
477
|
+
'explain-story-from-jira.md',
|
|
478
|
+
'implement-freeform-work-item.md',
|
|
479
|
+
'implement-story-from-jira.md',
|
|
480
|
+
'implement-unit-tests.md',
|
|
481
|
+
'execute-checklist.md',
|
|
482
|
+
'validate-next-story.md'
|
|
483
|
+
],
|
|
484
|
+
pm: [
|
|
485
|
+
'sync-jira-backlog.md',
|
|
486
|
+
'create-next-story.md',
|
|
487
|
+
'create-prd-doc.md'
|
|
488
|
+
],
|
|
489
|
+
po: [
|
|
490
|
+
'create-jira-epic-from-confluence.md',
|
|
491
|
+
'create-stories-from-epic.md',
|
|
492
|
+
'create-tasks-for-story.md',
|
|
493
|
+
'create-jira-story-from-confluence.md',
|
|
494
|
+
'create-jira-story-from-text.md',
|
|
495
|
+
'groom-jira-story.md',
|
|
496
|
+
'create-prd-doc.md',
|
|
497
|
+
'create-next-story.md'
|
|
498
|
+
],
|
|
499
|
+
qa: [
|
|
500
|
+
'gate.md',
|
|
501
|
+
'sonarqube-hotspot-review.md',
|
|
502
|
+
'test-design.md',
|
|
503
|
+
'verify-jira-story-e2e.md',
|
|
504
|
+
'review-story.md'
|
|
505
|
+
],
|
|
506
|
+
sm: [
|
|
507
|
+
'standup-digest.md'
|
|
508
|
+
],
|
|
509
|
+
'ui-expert': [
|
|
510
|
+
'figma-audit-design-system.md'
|
|
511
|
+
],
|
|
512
|
+
'ux-expert': [
|
|
513
|
+
'front-end-spec-from-design.md'
|
|
514
|
+
]
|
|
515
|
+
};
|
|
516
|
+
|
|
517
|
+
// Handle both ui-expert and ui folder mapping
|
|
518
|
+
if (agentId === 'ui') agentId = 'ui-expert';
|
|
519
|
+
if (agentId === 'ux') agentId = 'ux-expert';
|
|
520
|
+
|
|
521
|
+
return agentTaskMappings[agentId] || [];
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
function generateTurboChatmode({ agentId, agentTitle, agentMd, mcpCatalog, tasks }) {
|
|
525
|
+
// Deprecated: replaced by generateStructuredChatmode
|
|
526
|
+
return agentMd;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
function extractAgentMeta(agentMd) {
|
|
530
|
+
const meta = { name: null, title: null, whenToUse: null, persona: {}, corePrinciples: [] };
|
|
531
|
+
const m = agentMd.match(/```ya?ml\r?\n([\s\S]*?)\r?\n```/);
|
|
532
|
+
if (!m) return meta;
|
|
533
|
+
const yaml = m[1];
|
|
534
|
+
|
|
535
|
+
const name = yaml.match(/\bname\s*:\s*(.+)/);
|
|
536
|
+
const title = yaml.match(/\btitle\s*:\s*(.+)/);
|
|
537
|
+
const wtu = yaml.match(/\bwhenToUse\s*:\s*([\s\S]*?)(?:\r?\n\s*[a-zA-Z_][\w-]*\s*:|$)/);
|
|
538
|
+
if (name) meta.name = name[1].trim();
|
|
539
|
+
if (title) meta.title = title[1].trim();
|
|
540
|
+
if (wtu) meta.whenToUse = wtu[1].trim().replace(/^\|\s*\r?\n?/, '').replace(/^"|"$/g, '');
|
|
541
|
+
|
|
542
|
+
// persona fields (best-effort global matches inside YAML block)
|
|
543
|
+
const role = yaml.match(/\bpersona:\s*[\s\S]*?\brole\s*:\s*([^\r\n]+)/);
|
|
544
|
+
const style = yaml.match(/\bpersona:\s*[\s\S]*?\bstyle\s*:\s*([^\r\n]+)/);
|
|
545
|
+
const identity = yaml.match(/\bpersona:\s*[\s\S]*?\bidentity\s*:\s*([^\r\n]+)/);
|
|
546
|
+
const focus = yaml.match(/\bpersona:\s*[\s\S]*?\bfocus\s*:\s*([^\r\n]+)/);
|
|
547
|
+
if (role) meta.persona.role = role[1].trim();
|
|
548
|
+
if (style) meta.persona.style = style[1].trim();
|
|
549
|
+
if (identity) meta.persona.identity = identity[1].trim();
|
|
550
|
+
if (focus) meta.persona.focus = focus[1].trim();
|
|
551
|
+
|
|
552
|
+
// core_principles list under persona:
|
|
553
|
+
// Find start of 'core_principles:' within persona and collect '- ' lines
|
|
554
|
+
const cpStart = yaml.match(/\bpersona:\s*[\s\S]*?\bcore_principles\s*:\s*\r?\n/);
|
|
555
|
+
if (cpStart) {
|
|
556
|
+
// Slice from match index to end and iterate lines
|
|
557
|
+
const startIdx = cpStart.index + cpStart[0].length;
|
|
558
|
+
const rest = yaml.slice(startIdx).split(/\r?\n/);
|
|
559
|
+
for (const line of rest) {
|
|
560
|
+
if (/^\s*-\s+/.test(line)) {
|
|
561
|
+
meta.corePrinciples.push(line.replace(/^\s*-\s+/, '').trim());
|
|
562
|
+
} else if (/^\S/.test(line)) {
|
|
563
|
+
// Reached next top-level key
|
|
564
|
+
break;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
return meta;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
function relevantDependencies(agentId) {
|
|
572
|
+
const result = { templates: [], data: [], checklists: [] };
|
|
573
|
+
|
|
574
|
+
// Agent-specific template mappings based on BMAD approach
|
|
575
|
+
const templateMappings = {
|
|
576
|
+
'aigile-orchestrator': [], // Orchestrator doesn't use templates directly
|
|
577
|
+
'aigile-master': ['architecture-tmpl.yaml', 'prd-tmpl.yaml', 'project-brief-tmpl.yaml', 'story-tmpl.yaml', 'qa-gate-tmpl.yaml', 'front-end-spec-tmpl.yaml', 'market-research-tmpl.yaml'], // Master has access to all templates
|
|
578
|
+
analyst: ['project-brief-tmpl.yaml', 'market-research-tmpl.yaml'],
|
|
579
|
+
architect: ['architecture-tmpl.yaml'],
|
|
580
|
+
po: ['story-tmpl.yaml', 'prd-tmpl.yaml'],
|
|
581
|
+
qa: ['qa-gate-tmpl.yaml', 'story-tmpl.yaml'],
|
|
582
|
+
'ui-expert': ['front-end-spec-tmpl.yaml'],
|
|
583
|
+
'ux-expert': ['front-end-spec-tmpl.yaml']
|
|
584
|
+
};
|
|
585
|
+
|
|
586
|
+
// Agent-specific data file mappings
|
|
587
|
+
const dataMappings = {
|
|
588
|
+
'aigile-orchestrator': ['aigile-kb.md'], // Orchestrator has access to knowledge base
|
|
589
|
+
'aigile-master': ['aigile-kb.md', 'brainstorming-techniques.md', 'elicitation-methods.md', 'technical-preferences.md', 'test-levels-framework.md', 'test-priorities-matrix.md'], // Master has access to all data files
|
|
590
|
+
analyst: ['brainstorming-techniques.md', 'elicitation-methods.md', 'aigile-kb.md'],
|
|
591
|
+
architect: ['technical-preferences.md', 'aigile-kb.md'],
|
|
592
|
+
qa: ['test-levels-framework.md', 'test-priorities-matrix.md', 'technical-preferences.md', 'aigile-kb.md'],
|
|
593
|
+
dev: ['technical-preferences.md', 'test-levels-framework.md', 'test-priorities-matrix.md', 'aigile-kb.md'],
|
|
594
|
+
pm: ['aigile-kb.md'],
|
|
595
|
+
po: ['aigile-kb.md'],
|
|
596
|
+
sm: ['aigile-kb.md'],
|
|
597
|
+
'ui-expert': ['aigile-kb.md'],
|
|
598
|
+
'ux-expert': ['aigile-kb.md']
|
|
599
|
+
};
|
|
600
|
+
|
|
601
|
+
// Agent-specific checklist mappings based on role responsibilities
|
|
602
|
+
const checklistMappings = {
|
|
603
|
+
'aigile-orchestrator': [], // Orchestrator doesn't execute checklists directly
|
|
604
|
+
'aigile-master': ['architect-checklist.md', 'change-checklist.md', 'pm-checklist.md', 'po-master-checklist.md', 'story-dod-checklist.md', 'story-draft-checklist.md'], // Master has access to all checklists
|
|
605
|
+
analyst: ['change-checklist.md'], // For impact analysis and requirements changes
|
|
606
|
+
architect: ['architect-checklist.md', 'change-checklist.md'], // Architecture decisions and technical changes
|
|
607
|
+
pm: ['pm-checklist.md', 'change-checklist.md'], // Project management and change coordination
|
|
608
|
+
po: ['po-master-checklist.md', 'story-draft-checklist.md'], // Product ownership and story creation
|
|
609
|
+
dev: ['story-dod-checklist.md'], // Development completion criteria
|
|
610
|
+
qa: ['story-dod-checklist.md'], // Quality validation of completed stories
|
|
611
|
+
sm: [], // Scrum Master focuses on process facilitation, no specific checklists
|
|
612
|
+
'ui-expert': ['story-dod-checklist.md'], // UI deliverables completion
|
|
613
|
+
'ux-expert': ['story-draft-checklist.md'] // UX requirements and story preparation
|
|
614
|
+
};
|
|
615
|
+
|
|
616
|
+
// Handle both ui-expert and ui folder mapping
|
|
617
|
+
if (agentId === 'ui') agentId = 'ui-expert';
|
|
618
|
+
if (agentId === 'ux') agentId = 'ux-expert';
|
|
619
|
+
|
|
620
|
+
result.templates = templateMappings[agentId] || [];
|
|
621
|
+
result.data = dataMappings[agentId] || [];
|
|
622
|
+
result.checklists = checklistMappings[agentId] || [];
|
|
623
|
+
|
|
624
|
+
return result;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
function listAllTaskFiles(agentId) {
|
|
628
|
+
// DEPRECATED: Use listTasksForAgent instead for more precise control
|
|
629
|
+
const result = [];
|
|
630
|
+
for (const bucket of [agentId, 'common']) {
|
|
631
|
+
const dir = path.join(root, 'core', 'tasks', bucket);
|
|
632
|
+
if (fs.existsSync(dir)) {
|
|
633
|
+
for (const entry of fs.readdirSync(dir)) {
|
|
634
|
+
const p = path.join(dir, entry);
|
|
635
|
+
if (fs.statSync(p).isFile()) result.push(entry);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
return Array.from(new Set(result)).sort();
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
function listAllChecklists() {
|
|
643
|
+
const dir = path.join(root, 'core', 'checklists');
|
|
644
|
+
const out = [];
|
|
645
|
+
if (fs.existsSync(dir)) {
|
|
646
|
+
for (const entry of fs.readdirSync(dir)) {
|
|
647
|
+
const p = path.join(dir, entry);
|
|
648
|
+
if (fs.statSync(p).isFile()) out.push(entry);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
return out.sort();
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
function baseTools() {
|
|
655
|
+
// Base toolset available to ALL agents (official GitHub Copilot MCP tools + a few helpful extras)
|
|
656
|
+
return [
|
|
657
|
+
// Official Copilot tools requested
|
|
658
|
+
'extensions',
|
|
659
|
+
'todos',
|
|
660
|
+
'codebase',
|
|
661
|
+
'usages',
|
|
662
|
+
'vscodeAPI',
|
|
663
|
+
'think',
|
|
664
|
+
'problems',
|
|
665
|
+
'changes',
|
|
666
|
+
'testFailure',
|
|
667
|
+
'openSimpleBrowser',
|
|
668
|
+
'fetch',
|
|
669
|
+
'findTestFiles',
|
|
670
|
+
'searchResults',
|
|
671
|
+
'githubRepo',
|
|
672
|
+
'runCommands',
|
|
673
|
+
'runTasks',
|
|
674
|
+
'editFiles',
|
|
675
|
+
'runNotebooks',
|
|
676
|
+
'search',
|
|
677
|
+
'new',
|
|
678
|
+
// Helpful extras already supported in this CLI integration
|
|
679
|
+
'terminalSelection',
|
|
680
|
+
'terminalLastCommand'
|
|
681
|
+
];
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
// Catalog of official MCP tool names for known servers
|
|
685
|
+
const TOOL_CATALOG = {
|
|
686
|
+
github: [
|
|
687
|
+
// Repository operations
|
|
688
|
+
'search_repositories', 'get_repository', 'list_repositories', 'fork_repository',
|
|
689
|
+
// File operations
|
|
690
|
+
'get_file', 'create_file', 'update_file', 'delete_file', 'search_code',
|
|
691
|
+
// Branch operations
|
|
692
|
+
'list_branches', 'create_branch', 'get_branch',
|
|
693
|
+
// Commit operations
|
|
694
|
+
'list_commits', 'get_commit',
|
|
695
|
+
// Issue operations
|
|
696
|
+
'search_issues', 'list_issues', 'get_issue', 'create_issue', 'update_issue', 'add_comment',
|
|
697
|
+
// Pull request operations
|
|
698
|
+
'list_pull_requests', 'get_pull_request', 'create_pull_request', 'update_pull_request',
|
|
699
|
+
// Release operations
|
|
700
|
+
'list_releases', 'get_release', 'get_latest_release',
|
|
701
|
+
// User operations
|
|
702
|
+
'get_user', 'search_users', 'get_authenticated_user'
|
|
703
|
+
],
|
|
704
|
+
atlassian: [
|
|
705
|
+
// Jira read-only operations (official MCP tool names)
|
|
706
|
+
'jira_search',
|
|
707
|
+
'jira_get_issue',
|
|
708
|
+
'jira_get_all_projects',
|
|
709
|
+
'jira_get_project_issues',
|
|
710
|
+
'jira_get_transitions',
|
|
711
|
+
'jira_search_fields',
|
|
712
|
+
'jira_get_agile_boards',
|
|
713
|
+
'jira_get_board_issues',
|
|
714
|
+
'jira_get_sprints_from_board',
|
|
715
|
+
'jira_get_sprint_issues',
|
|
716
|
+
'jira_get_issue_link_types',
|
|
717
|
+
'jira_get_user_profile',
|
|
718
|
+
'jira_get_project_versions',
|
|
719
|
+
// Confluence read-only operations (official MCP tool names)
|
|
720
|
+
'confluence_search',
|
|
721
|
+
'confluence_get_page',
|
|
722
|
+
'confluence_get_page_children',
|
|
723
|
+
'confluence_get_comments',
|
|
724
|
+
'confluence_get_labels',
|
|
725
|
+
'confluence_search_user'
|
|
726
|
+
],
|
|
727
|
+
playwright: [
|
|
728
|
+
// Navigation
|
|
729
|
+
'navigate', 'reload', 'go_back', 'go_forward',
|
|
730
|
+
// Interaction
|
|
731
|
+
'click', 'type', 'press_key', 'fill', 'select_option',
|
|
732
|
+
// Waiting
|
|
733
|
+
'wait_for_selector', 'wait_for_load_state', 'wait_for_timeout',
|
|
734
|
+
// Extraction
|
|
735
|
+
'get_text', 'get_attribute', 'get_property', 'screenshot',
|
|
736
|
+
// Browser management
|
|
737
|
+
'new_page', 'close_page', 'set_viewport_size'
|
|
738
|
+
],
|
|
739
|
+
memory: [
|
|
740
|
+
'create_entities', 'create_relations', 'add_observations', 'delete_entities', 'delete_observations', 'delete_relations', 'read_graph', 'search_nodes', 'open_nodes'
|
|
741
|
+
],
|
|
742
|
+
sequentialthinking: [
|
|
743
|
+
'sequentialthinking'
|
|
744
|
+
],
|
|
745
|
+
context7: [
|
|
746
|
+
'get-library-docs', 'resolve-library-id'
|
|
747
|
+
]
|
|
748
|
+
};
|
|
749
|
+
|
|
750
|
+
function mcpToolsForAgent(agentId) {
|
|
751
|
+
// Return minimal MCP tool list per agent based on their tasks
|
|
752
|
+
const serverMappings = {
|
|
753
|
+
'aigile-orchestrator': ['memory', 'sequentialthinking'], // Orchestrator coordinates agents
|
|
754
|
+
'aigile-master': ['context7', 'atlassian', 'github.com', 'sonarqube', 'playwright', 'figma', 'memory', 'sequentialthinking'], // Master has access to all tools
|
|
755
|
+
analyst: ['context7', 'memory', 'sequentialthinking'],
|
|
756
|
+
architect: ['github.com', 'context7', 'memory', 'sequentialthinking'],
|
|
757
|
+
dev: ['atlassian', 'github.com', 'sonarqube', 'memory', 'sequentialthinking'],
|
|
758
|
+
pm: ['atlassian', 'sequentialthinking'],
|
|
759
|
+
po: ['atlassian', 'memory', 'sequentialthinking'],
|
|
760
|
+
qa: ['sonarqube', 'playwright', 'atlassian', 'memory', 'sequentialthinking'],
|
|
761
|
+
sm: ['atlassian', 'memory', 'sequentialthinking'],
|
|
762
|
+
ui: ['memory', 'sequentialthinking'], // Note: figma not available in servers.json
|
|
763
|
+
ux: ['memory', 'sequentialthinking'],
|
|
764
|
+
'ui-expert': ['memory', 'sequentialthinking'],
|
|
765
|
+
'ux-expert': ['memory', 'sequentialthinking']
|
|
766
|
+
};
|
|
767
|
+
|
|
768
|
+
const agentServers = serverMappings[agentId] || ['sequentialthinking'];
|
|
769
|
+
const tools = new Set();
|
|
770
|
+
|
|
771
|
+
// Map server names to actual tools from TOOL_CATALOG
|
|
772
|
+
for (const serverName of agentServers) {
|
|
773
|
+
const serverKey = serverName.toLowerCase();
|
|
774
|
+
if (serverKey.includes('github') || serverKey === 'github.com') {
|
|
775
|
+
for (const tool of TOOL_CATALOG.github) tools.add(tool);
|
|
776
|
+
} else if (serverKey.includes('atlassian')) {
|
|
777
|
+
for (const tool of TOOL_CATALOG.atlassian) tools.add(tool);
|
|
778
|
+
} else if (serverKey.includes('playwright')) {
|
|
779
|
+
for (const tool of TOOL_CATALOG.playwright) tools.add(tool);
|
|
780
|
+
} else if (serverKey.includes('memory')) {
|
|
781
|
+
for (const tool of TOOL_CATALOG.memory) tools.add(tool);
|
|
782
|
+
} else if (serverKey.includes('sequentialthinking')) {
|
|
783
|
+
for (const tool of TOOL_CATALOG.sequentialthinking) tools.add(tool);
|
|
784
|
+
} else if (serverKey.includes('context7')) {
|
|
785
|
+
for (const tool of TOOL_CATALOG.context7) tools.add(tool);
|
|
786
|
+
}
|
|
787
|
+
// For servers not in TOOL_CATALOG (sonarqube, toolkit), add server name as fallback
|
|
788
|
+
else if (['sonarqube', 'toolkit'].includes(serverKey)) {
|
|
789
|
+
tools.add(serverKey); // known servers without explicit tool catalog
|
|
790
|
+
} else {
|
|
791
|
+
tools.add(serverName); // fallback for unknown servers
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
return Array.from(tools);
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
function mcpServersForAgent(agentId) {
|
|
799
|
+
// Return MCP server list per agent - all non-orchestrator agents get the core 3 MCPs
|
|
800
|
+
const coreMcpServers = ['github.com', 'sequentialthinking', 'context7'];
|
|
801
|
+
|
|
802
|
+
// Orchestrator has NO MCP tools - only runSubagent
|
|
803
|
+
if (agentId === 'aigile-orchestrator') {
|
|
804
|
+
return [];
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
return coreMcpServers;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
function mcpToolsFromServers() {
|
|
811
|
+
const serversJson = path.join(root, 'mcp', 'servers.json');
|
|
812
|
+
const doc = readJsonIfExists(serversJson);
|
|
813
|
+
if (!doc || !doc.servers) return [];
|
|
814
|
+
|
|
815
|
+
const detectedToolNames = new Set();
|
|
816
|
+
|
|
817
|
+
for (const [id, cfg] of Object.entries(doc.servers)) {
|
|
818
|
+
const idLower = id.toLowerCase();
|
|
819
|
+
const args = Array.isArray(cfg?.args) ? cfg.args.map(String) : [];
|
|
820
|
+
const command = String(cfg?.command || '');
|
|
821
|
+
const joined = `${command} ${args.join(' ')}`.toLowerCase();
|
|
822
|
+
|
|
823
|
+
// Heuristics to match known servers
|
|
824
|
+
const isGithub = joined.includes('github-mcp-server') || idLower.includes('github') || idLower === 'github.com' || (cfg?.env && ('GITHUB_HOST' in cfg.env));
|
|
825
|
+
const isAtlassian = joined.includes('mcp-atlassian') || idLower.includes('atlassian');
|
|
826
|
+
const isPlaywright = joined.includes('@playwright/mcp') || idLower.includes('playwright');
|
|
827
|
+
const isMemory = joined.includes('server-memory') || idLower.includes('memory');
|
|
828
|
+
const isSequential = joined.includes('server-sequential-thinking') || idLower.includes('sequentialthinking');
|
|
829
|
+
|
|
830
|
+
if (isGithub) {
|
|
831
|
+
for (const t of TOOL_CATALOG.github) detectedToolNames.add(t);
|
|
832
|
+
continue;
|
|
833
|
+
}
|
|
834
|
+
if (isAtlassian) {
|
|
835
|
+
for (const t of TOOL_CATALOG.atlassian) detectedToolNames.add(t);
|
|
836
|
+
continue;
|
|
837
|
+
}
|
|
838
|
+
if (isPlaywright) {
|
|
839
|
+
for (const t of TOOL_CATALOG.playwright) detectedToolNames.add(t);
|
|
840
|
+
continue;
|
|
841
|
+
}
|
|
842
|
+
if (isMemory) {
|
|
843
|
+
for (const t of TOOL_CATALOG.memory) detectedToolNames.add(t);
|
|
844
|
+
continue;
|
|
845
|
+
}
|
|
846
|
+
if (isSequential) {
|
|
847
|
+
for (const t of TOOL_CATALOG.sequentialthinking) detectedToolNames.add(t);
|
|
848
|
+
continue;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
// Unknown server types (e.g., context7, sonarqube, toolkit) -> skip explicit tools (host will discover)
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
return Array.from(detectedToolNames);
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
function agentIcon(agentId) {
|
|
858
|
+
const map = {
|
|
859
|
+
'aigile-orchestrator': '🎭',
|
|
860
|
+
'aigile-master': '🧙',
|
|
861
|
+
dev: '💻',
|
|
862
|
+
qa: '🧪',
|
|
863
|
+
pm: '📋',
|
|
864
|
+
po: '🧭',
|
|
865
|
+
sm: '⏱️',
|
|
866
|
+
analyst: '🧠',
|
|
867
|
+
architect: '🏗️',
|
|
868
|
+
'ux-expert': '🎨',
|
|
869
|
+
'ui-expert': '🖌️'
|
|
870
|
+
};
|
|
871
|
+
return map[agentId] || '🤖';
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
function personaFor(agentId) {
|
|
875
|
+
const overrides = {
|
|
876
|
+
dev: {
|
|
877
|
+
role: 'Expert Senior Software Engineer & Implementation Specialist',
|
|
878
|
+
style: 'Systematic, test-driven, documentation-focused, methodical',
|
|
879
|
+
identity: 'Implements stories by executing tasks sequentially with comprehensive testing and validation',
|
|
880
|
+
focus: 'Code quality, test coverage, story implementation, and technical debt management'
|
|
881
|
+
},
|
|
882
|
+
qa: {
|
|
883
|
+
role: 'Senior Quality Engineer & Test Architect',
|
|
884
|
+
style: 'Risk-focused, systematic, thorough, evidence-based',
|
|
885
|
+
identity: 'Ensures quality through comprehensive testing strategies and automated validation',
|
|
886
|
+
focus: 'Test design, quality gates, risk assessment, and defect prevention'
|
|
887
|
+
},
|
|
888
|
+
pm: {
|
|
889
|
+
role: 'Technical Project Manager & Delivery Lead',
|
|
890
|
+
style: 'Organized, tracking-focused, stakeholder-oriented, results-driven',
|
|
891
|
+
identity: 'Coordinates delivery through planning, tracking, and impediment resolution',
|
|
892
|
+
focus: 'Sprint planning, backlog management, delivery tracking, and team coordination'
|
|
893
|
+
},
|
|
894
|
+
po: {
|
|
895
|
+
role: 'Product Owner & Requirements Authority',
|
|
896
|
+
style: 'User-focused, value-driven, prioritization-expert, clear communicator',
|
|
897
|
+
identity: 'Defines product direction through story creation, grooming, and acceptance criteria',
|
|
898
|
+
focus: 'Backlog grooming, story quality, acceptance criteria, and stakeholder alignment'
|
|
899
|
+
},
|
|
900
|
+
sm: {
|
|
901
|
+
role: 'Scrum Master & Agile Coach',
|
|
902
|
+
style: 'Facilitative, process-focused, team-oriented, impediment-removing',
|
|
903
|
+
identity: 'Enables team effectiveness through agile practices and continuous improvement',
|
|
904
|
+
focus: 'Sprint facilitation, team health, process improvement, and impediment resolution'
|
|
905
|
+
},
|
|
906
|
+
analyst: {
|
|
907
|
+
role: 'Senior Business Analyst & Requirements Engineer',
|
|
908
|
+
style: 'Analytical, detail-oriented, stakeholder-focused, methodical',
|
|
909
|
+
identity: 'Bridges business and technical domains through requirements elicitation and analysis',
|
|
910
|
+
focus: 'Requirements gathering, stakeholder analysis, process modeling, and solution design'
|
|
911
|
+
},
|
|
912
|
+
architect: {
|
|
913
|
+
role: 'Software Architect & Technical Design Authority',
|
|
914
|
+
style: 'Strategic, pattern-focused, scalability-minded, standards-driven',
|
|
915
|
+
identity: 'Defines technical direction through architecture decisions and design reviews',
|
|
916
|
+
focus: 'System architecture, design patterns, technical decisions, and architectural reviews'
|
|
917
|
+
},
|
|
918
|
+
'ux-expert': {
|
|
919
|
+
role: 'UX Research Specialist & User Advocate',
|
|
920
|
+
style: 'User-centered, research-driven, empathetic, evidence-based',
|
|
921
|
+
identity: 'Advocates for users through research, journey mapping, and experience design',
|
|
922
|
+
focus: 'User research, journey mapping, usability testing, and experience optimization'
|
|
923
|
+
},
|
|
924
|
+
'ui-expert': {
|
|
925
|
+
role: 'UI Design Specialist & Visual Design Authority',
|
|
926
|
+
style: 'Visual-focused, systematic, brand-conscious, detail-oriented',
|
|
927
|
+
identity: 'Creates compelling interfaces through design systems and visual specifications',
|
|
928
|
+
focus: 'Design systems, UI specifications, visual consistency, and interaction design'
|
|
929
|
+
},
|
|
930
|
+
};
|
|
931
|
+
// Handle both ui-expert and ui folder mapping
|
|
932
|
+
if (agentId === 'ui') agentId = 'ui-expert';
|
|
933
|
+
if (agentId === 'ux') agentId = 'ux-expert';
|
|
934
|
+
|
|
935
|
+
const base = {
|
|
936
|
+
role: 'Expert practitioner for this discipline',
|
|
937
|
+
style: 'Concise, pragmatic, detail-oriented, solution-focused',
|
|
938
|
+
identity: 'Operates via executable tasks and evidence-backed decisions',
|
|
939
|
+
focus: 'Executing tasks with precision and minimal context overhead'
|
|
940
|
+
};
|
|
941
|
+
return { ...base, ...(overrides[agentId] || {}) };
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
function principlesFor(agentId) {
|
|
945
|
+
const base = [
|
|
946
|
+
'CRITICAL: Only load dependency files when explicitly executing a task; do not pre-load entire docs.',
|
|
947
|
+
'CRITICAL: Follow task instructions exactly; they are executable workflows.',
|
|
948
|
+
'MANDATORY: Tasks with elicit=true require user interaction; never skip elicitation.',
|
|
949
|
+
'Numbered Options: Always present choices as numbered lists for easy selection.'
|
|
950
|
+
];
|
|
951
|
+
|
|
952
|
+
const agentSpecific = {
|
|
953
|
+
dev: [
|
|
954
|
+
'CRITICAL: Do not begin development until a story is approved and ready.',
|
|
955
|
+
'CRITICAL: Only update designated sections in story files as instructed by tasks.',
|
|
956
|
+
'TESTING: All code changes require corresponding unit tests.',
|
|
957
|
+
'VALIDATION: Run tests and quality checks before marking tasks complete.',
|
|
958
|
+
'DOCUMENTATION: Update technical documentation for architectural changes.'
|
|
959
|
+
],
|
|
960
|
+
qa: [
|
|
961
|
+
'QUALITY GATES: Block releases that do not meet defined quality standards.',
|
|
962
|
+
'RISK ASSESSMENT: Always evaluate potential impact before approving changes.',
|
|
963
|
+
'TEST COVERAGE: Ensure comprehensive test coverage across all test levels.',
|
|
964
|
+
'DEFECT TRACKING: Document and track all identified issues to resolution.'
|
|
965
|
+
],
|
|
966
|
+
pm: [
|
|
967
|
+
'DELIVERY FOCUS: Prioritize actions that directly impact sprint delivery.',
|
|
968
|
+
'STAKEHOLDER COMMUNICATION: Keep stakeholders informed of progress and blockers.',
|
|
969
|
+
'RESOURCE MANAGEMENT: Optimize team capacity and workload distribution.',
|
|
970
|
+
'SCOPE CONTROL: Prevent scope creep through proper change management.'
|
|
971
|
+
],
|
|
972
|
+
po: [
|
|
973
|
+
'USER VALUE: Every story must deliver clear business or user value.',
|
|
974
|
+
'ACCEPTANCE CRITERIA: All stories require clear, testable acceptance criteria.',
|
|
975
|
+
'BACKLOG HEALTH: Maintain a well-groomed, prioritized product backlog.',
|
|
976
|
+
'STAKEHOLDER ALIGNMENT: Ensure stories align with business objectives.'
|
|
977
|
+
],
|
|
978
|
+
sm: [
|
|
979
|
+
'TEAM EMPOWERMENT: Enable team self-organization and decision-making.',
|
|
980
|
+
'IMPEDIMENT REMOVAL: Actively identify and resolve team blockers.',
|
|
981
|
+
'PROCESS IMPROVEMENT: Continuously refine team practices and workflows.',
|
|
982
|
+
'FACILITATION: Guide meetings and ceremonies for maximum effectiveness.'
|
|
983
|
+
],
|
|
984
|
+
analyst: [
|
|
985
|
+
'REQUIREMENTS CLARITY: Ensure all requirements are clear, complete, and testable.',
|
|
986
|
+
'STAKEHOLDER ENGAGEMENT: Actively involve stakeholders in elicitation sessions.',
|
|
987
|
+
'SOLUTION ALIGNMENT: Verify proposed solutions meet business needs.',
|
|
988
|
+
'CHANGE IMPACT: Assess impact of changes on existing processes and systems.'
|
|
989
|
+
],
|
|
990
|
+
architect: [
|
|
991
|
+
'ARCHITECTURAL INTEGRITY: Maintain consistency with established architectural principles.',
|
|
992
|
+
'SCALABILITY FOCUS: Design for future growth and changing requirements.',
|
|
993
|
+
'TECHNICAL DEBT: Balance feature delivery with technical debt management.',
|
|
994
|
+
'STANDARDS COMPLIANCE: Ensure all designs adhere to enterprise standards.'
|
|
995
|
+
],
|
|
996
|
+
'ux-expert': [
|
|
997
|
+
'USER ADVOCACY: Always prioritize user needs and experience quality.',
|
|
998
|
+
'RESEARCH-DRIVEN: Base decisions on user research and behavioral data.',
|
|
999
|
+
'ACCESSIBILITY: Ensure designs are accessible to all users.',
|
|
1000
|
+
'USABILITY TESTING: Validate designs through user testing and feedback.'
|
|
1001
|
+
],
|
|
1002
|
+
'ui-expert': [
|
|
1003
|
+
'DESIGN CONSISTENCY: Maintain visual consistency across all interfaces.',
|
|
1004
|
+
'DESIGN SYSTEM: Leverage and contribute to the established design system.',
|
|
1005
|
+
'RESPONSIVE DESIGN: Ensure designs work across all device types and sizes.',
|
|
1006
|
+
'VISUAL HIERARCHY: Create clear information hierarchy and user flow.'
|
|
1007
|
+
]
|
|
1008
|
+
};
|
|
1009
|
+
|
|
1010
|
+
// Handle both ui-expert and ui folder mapping
|
|
1011
|
+
if (agentId === 'ui') agentId = 'ui-expert';
|
|
1012
|
+
if (agentId === 'ux') agentId = 'ux-expert';
|
|
1013
|
+
|
|
1014
|
+
return [...base, ...(agentSpecific[agentId] || [])];
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
function commandsFor(agentId) {
|
|
1018
|
+
const cmds = [
|
|
1019
|
+
{ help: 'Show numbered list of commands to select from' },
|
|
1020
|
+
{ explain: 'Explain in detail what and why you did the last action' },
|
|
1021
|
+
{ exit: 'Say goodbye and exit this persona' },
|
|
1022
|
+
];
|
|
1023
|
+
|
|
1024
|
+
const agentSpecific = {
|
|
1025
|
+
dev: [
|
|
1026
|
+
{
|
|
1027
|
+
'develop-story': [
|
|
1028
|
+
'order-of-execution: "Read (first or next) task→Implement→Write tests→Validate→Mark complete"',
|
|
1029
|
+
'blocking: "HALT for missing deps, ambiguity, repeated failures, or failing regression"',
|
|
1030
|
+
'ready-for-review: "All validations pass and standards met"',
|
|
1031
|
+
'completion: "All tasks checked, tests/regression pass, update file list and DoD checklist"'
|
|
1032
|
+
]
|
|
1033
|
+
},
|
|
1034
|
+
{ 'run-tests': 'Execute unit tests and integration tests for current work' },
|
|
1035
|
+
{ 'quality-check': 'Run code quality analysis and resolve issues' }
|
|
1036
|
+
],
|
|
1037
|
+
qa: [
|
|
1038
|
+
{ 'test-story': 'Design and execute comprehensive tests for a story' },
|
|
1039
|
+
{ 'quality-gate': 'Evaluate if story meets quality standards for release' },
|
|
1040
|
+
{ 'risk-assess': 'Analyze potential risks and impacts of changes' }
|
|
1041
|
+
],
|
|
1042
|
+
pm: [
|
|
1043
|
+
{ 'sync-backlog': 'Synchronize and update project backlog from Jira' },
|
|
1044
|
+
{ 'create-prd': 'Create or update PRD based on templates and config' },
|
|
1045
|
+
{ 'track-progress': 'Review and report on sprint progress' },
|
|
1046
|
+
{ 'plan-sprint': 'Plan upcoming sprint capacity and commitments' }
|
|
1047
|
+
],
|
|
1048
|
+
po: [
|
|
1049
|
+
{ 'groom-backlog': 'Review and refine backlog items for upcoming sprints' },
|
|
1050
|
+
{ 'create-epic': 'Create an epic from a Confluence page (with save-to-docs)' },
|
|
1051
|
+
{ 'create-stories': 'Generate stories from an epic (with Jira fallback)' },
|
|
1052
|
+
{ 'create-tasks': 'Generate implementation tasks for a story' },
|
|
1053
|
+
{ 'create-story': 'Create new user story with acceptance criteria' },
|
|
1054
|
+
{ 'create-prd': 'Create or update PRD based on templates and config' },
|
|
1055
|
+
{ 'prioritize': 'Re-prioritize backlog based on business value' }
|
|
1056
|
+
],
|
|
1057
|
+
sm: [
|
|
1058
|
+
{ 'facilitate-standup': 'Generate standup digest and facilitate meeting' },
|
|
1059
|
+
{ 'remove-impediment': 'Identify and resolve team blockers' },
|
|
1060
|
+
{ 'improve-process': 'Suggest process improvements based on team feedback' }
|
|
1061
|
+
],
|
|
1062
|
+
analyst: [
|
|
1063
|
+
{ 'elicit-requirements': 'Conduct requirements gathering session' },
|
|
1064
|
+
{ 'analyze-process': 'Analyze current state process and identify improvements' },
|
|
1065
|
+
{ 'research-market': 'Conduct market research using available data sources' }
|
|
1066
|
+
],
|
|
1067
|
+
architect: [
|
|
1068
|
+
{ 'review-architecture': 'Conduct architecture review of code or design' },
|
|
1069
|
+
{ 'design-solution': 'Create architectural solution for business requirements' },
|
|
1070
|
+
{ 'create-architecture-doc': 'Create or update architecture documentation per config' },
|
|
1071
|
+
{ 'document-decisions': 'Document architectural decisions and rationale' }
|
|
1072
|
+
],
|
|
1073
|
+
'ux-expert': [
|
|
1074
|
+
{ 'research-users': 'Conduct user research and analysis' },
|
|
1075
|
+
{ 'map-journey': 'Create user journey maps and experience flows' },
|
|
1076
|
+
{ 'test-usability': 'Design and conduct usability testing sessions' }
|
|
1077
|
+
],
|
|
1078
|
+
'ui-expert': [
|
|
1079
|
+
{ 'audit-design': 'Audit design system compliance and consistency' },
|
|
1080
|
+
{ 'create-specs': 'Create detailed UI specifications from designs' },
|
|
1081
|
+
{ 'review-interface': 'Review interface design for usability and standards' }
|
|
1082
|
+
]
|
|
1083
|
+
};
|
|
1084
|
+
|
|
1085
|
+
// Handle both ui-expert and ui folder mapping
|
|
1086
|
+
if (agentId === 'ui') agentId = 'ui-expert';
|
|
1087
|
+
if (agentId === 'ux') agentId = 'ux-expert';
|
|
1088
|
+
|
|
1089
|
+
return [...cmds, ...(agentSpecific[agentId] || [])];
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
function generateStructuredChatmode({ agentId, agentMeta, agentMd, tools, tasks, checklists }) {
|
|
1093
|
+
// Enhanced Copilot-focused structured chat mode inspired by BMAD approach
|
|
1094
|
+
const title = agentMeta.title || toTitleCase(agentId);
|
|
1095
|
+
const description = agentMeta.whenToUse || `Use for ${title} responsibilities and workflows.`;
|
|
1096
|
+
|
|
1097
|
+
// Collect tools (dedupe) including MCP tool names so Copilot can expose them when available
|
|
1098
|
+
const agentMcpTools = mcpToolsForAgent(agentId);
|
|
1099
|
+
const toolList = Array.from(new Set([...baseTools(), ...tools, ...agentMcpTools])).sort();
|
|
1100
|
+
|
|
1101
|
+
// Front matter: include name & title (BMAD style adds more semantic cues). Copilot currently
|
|
1102
|
+
// keyes on description/tools; adding name/title is future-proof and clarifies mode list.
|
|
1103
|
+
const fm = [
|
|
1104
|
+
'---',
|
|
1105
|
+
`name: ${agentId}`,
|
|
1106
|
+
`title: ${JSON.stringify(title)}`,
|
|
1107
|
+
`description: ${JSON.stringify(description)}`,
|
|
1108
|
+
`tools: ${JSON.stringify(toolList)}`,
|
|
1109
|
+
'version: 1',
|
|
1110
|
+
'---',
|
|
1111
|
+
''
|
|
1112
|
+
].join('\n');
|
|
1113
|
+
|
|
1114
|
+
const metaName = agentMeta.name || title;
|
|
1115
|
+
const person = { ...personaFor(agentId), ...(agentMeta.persona || {}) };
|
|
1116
|
+
const combinedPrinciples = (agentMeta.corePrinciples && agentMeta.corePrinciples.length)
|
|
1117
|
+
? agentMeta.corePrinciples
|
|
1118
|
+
: principlesFor(agentId);
|
|
1119
|
+
const deps = relevantDependencies(agentId);
|
|
1120
|
+
const allChecklists = Array.from(new Set([...(deps.checklists || []), ...(checklists || [])])).sort();
|
|
1121
|
+
|
|
1122
|
+
// Build the core SYSTEM directives first (outside YAML code fence) so Copilot treats it as high-priority.
|
|
1123
|
+
const out = [];
|
|
1124
|
+
out.push(`# ${title} (Agent Mode)`);
|
|
1125
|
+
out.push('');
|
|
1126
|
+
out.push('SYSTEM DIRECTIVES: You are activating a specialized software delivery role. Adopt the exact persona, principles, and constrained command workflow defined below. Do not revert to a generic assistant unless explicitly told to exit this mode. Always announce uncertainties, request clarifications for ambiguous tasks, and prefer incremental, testable outputs.');
|
|
1127
|
+
out.push('');
|
|
1128
|
+
out.push('Use *help at any time to list commands. Commands ALWAYS start with * (asterisk).');
|
|
1129
|
+
out.push('');
|
|
1130
|
+
out.push('---');
|
|
1131
|
+
out.push('## COMPLETE AGENT DEFINITION (YAML)');
|
|
1132
|
+
out.push('```yaml');
|
|
1133
|
+
out.push('activation-instructions:');
|
|
1134
|
+
out.push(' - Read this entire file BEFORE responding.');
|
|
1135
|
+
out.push(' - Adopt persona + principles immediately.');
|
|
1136
|
+
out.push(' - Greet user with your role + offer *help.');
|
|
1137
|
+
out.push(' - Only load dependency file content from APPENDIX when executing a specific command/task referencing it.');
|
|
1138
|
+
out.push(' - For multi-step work: Plan → Ask clarifying questions (if needed) → Execute iteratively.');
|
|
1139
|
+
out.push('agent:');
|
|
1140
|
+
out.push(` id: ${agentId}`);
|
|
1141
|
+
out.push(` name: ${metaName}`);
|
|
1142
|
+
out.push(` title: ${title}`);
|
|
1143
|
+
out.push(` icon: ${agentIcon(agentId)}`);
|
|
1144
|
+
out.push(` whenToUse: ${JSON.stringify(description)}`);
|
|
1145
|
+
out.push('persona:');
|
|
1146
|
+
out.push(` role: ${person.role}`);
|
|
1147
|
+
out.push(` style: ${person.style}`);
|
|
1148
|
+
out.push(` identity: ${person.identity}`);
|
|
1149
|
+
out.push(` focus: ${person.focus}`);
|
|
1150
|
+
out.push('core_principles:');
|
|
1151
|
+
for (const p of combinedPrinciples) out.push(` - ${p}`);
|
|
1152
|
+
out.push('commands:');
|
|
1153
|
+
for (const c of commandsFor(agentId)) {
|
|
1154
|
+
const key = Object.keys(c)[0];
|
|
1155
|
+
const val = c[key];
|
|
1156
|
+
if (Array.isArray(val)) {
|
|
1157
|
+
out.push(` - ${key}:`);
|
|
1158
|
+
for (const el of val) out.push(` - ${el}`);
|
|
1159
|
+
} else {
|
|
1160
|
+
out.push(` - ${key}: ${val}`);
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
out.push('dependencies:');
|
|
1164
|
+
if (tasks.length) {
|
|
1165
|
+
out.push(' tasks:');
|
|
1166
|
+
for (const t of tasks) out.push(` - ${t}`);
|
|
1167
|
+
}
|
|
1168
|
+
if (deps.templates.length) {
|
|
1169
|
+
out.push(' templates:');
|
|
1170
|
+
for (const t of deps.templates) out.push(` - ${t}`);
|
|
1171
|
+
}
|
|
1172
|
+
if (allChecklists.length) {
|
|
1173
|
+
out.push(' checklists:');
|
|
1174
|
+
for (const c of allChecklists) out.push(` - ${c}`);
|
|
1175
|
+
}
|
|
1176
|
+
if (deps.data.length) {
|
|
1177
|
+
out.push(' data:');
|
|
1178
|
+
for (const d of deps.data) out.push(` - ${d}`);
|
|
1179
|
+
}
|
|
1180
|
+
out.push('```');
|
|
1181
|
+
out.push('');
|
|
1182
|
+
out.push('---');
|
|
1183
|
+
out.push('## RUNTIME BEHAVIOR');
|
|
1184
|
+
out.push('- Always reflect briefly before large actions.');
|
|
1185
|
+
out.push('- When using a command that maps to a task file, load only that task content (from appendix) and follow it verbatim.');
|
|
1186
|
+
out.push('- If user asks for something outside your scope, explain limitation and suggest appropriate agent (if known).');
|
|
1187
|
+
out.push('');
|
|
1188
|
+
out.push('## COMMAND → DEPENDENCY RESOLUTION EXAMPLES');
|
|
1189
|
+
out.push('These are illustrative; match flexibly:');
|
|
1190
|
+
out.push('- "draft story" → tasks/create-next-story.md or tasks/create-jira-story-from-text.md');
|
|
1191
|
+
out.push('- "architecture doc" → templates/architecture-tmpl.yaml');
|
|
1192
|
+
out.push('- "front-end spec" → templates/front-end-spec-tmpl.yaml');
|
|
1193
|
+
out.push('');
|
|
1194
|
+
out.push('## APPENDIX: ORIGINAL AGENT SOURCE (REFERENCE ONLY)');
|
|
1195
|
+
out.push('<agent-source>');
|
|
1196
|
+
out.push(agentMd.trim());
|
|
1197
|
+
out.push('</agent-source>');
|
|
1198
|
+
|
|
1199
|
+
// NOTE: We intentionally do NOT inline every dependency file to keep prompt weight lean.
|
|
1200
|
+
// Copilot can open referenced files from the workspace (.aigile-core) when needed.
|
|
1201
|
+
|
|
1202
|
+
return fm + out.join('\n');
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
/**
|
|
1206
|
+
* Generate a custom agent file (.agent.md) for the new GitHub Copilot custom agents format
|
|
1207
|
+
* See: https://code.visualstudio.com/docs/copilot/customization/custom-agents
|
|
1208
|
+
*/
|
|
1209
|
+
function generateCustomAgent({ agentId, agentMeta, agentMd, tools, tasks, checklists }) {
|
|
1210
|
+
const title = agentMeta.title || toTitleCase(agentId);
|
|
1211
|
+
const description = agentMeta.whenToUse || `Use for ${title} responsibilities and workflows.`;
|
|
1212
|
+
const metaName = agentMeta.name || title;
|
|
1213
|
+
|
|
1214
|
+
// Full tool list for all agents (except orchestrator)
|
|
1215
|
+
const FULL_AGENT_TOOLS = [
|
|
1216
|
+
// VS Code tools
|
|
1217
|
+
'vscode/extensions', 'vscode/getProjectSetupInfo', 'vscode/installExtension', 'vscode/newWorkspace',
|
|
1218
|
+
'vscode/openSimpleBrowser', 'vscode/runCommand', 'vscode/askQuestions', 'vscode/switchAgent',
|
|
1219
|
+
'vscode/vscodeAPI', 'vscode/memory',
|
|
1220
|
+
// Execute tools
|
|
1221
|
+
'execute/getTerminalOutput', 'execute/runTask', 'execute/createAndRunTask', 'execute/runInTerminal',
|
|
1222
|
+
'execute/runNotebookCell', 'execute/testFailure', 'execute/runTests',
|
|
1223
|
+
// Read tools
|
|
1224
|
+
'read/terminalSelection', 'read/terminalLastCommand', 'read/getTaskOutput', 'read/getNotebookSummary',
|
|
1225
|
+
'read/problems', 'read/readFile', 'read/readNotebookCellOutput',
|
|
1226
|
+
// Agent tools
|
|
1227
|
+
'agent/runSubagent',
|
|
1228
|
+
// Atlassian MCP tools
|
|
1229
|
+
'atlassian/confluence_get_comments', 'atlassian/confluence_get_labels', 'atlassian/confluence_get_page',
|
|
1230
|
+
'atlassian/confluence_get_page_children', 'atlassian/confluence_search', 'atlassian/confluence_search_user',
|
|
1231
|
+
'atlassian/jira_batch_get_changelogs', 'atlassian/jira_download_attachments', 'atlassian/jira_get_agile_boards',
|
|
1232
|
+
'atlassian/jira_get_all_projects', 'atlassian/jira_get_board_issues', 'atlassian/jira_get_issue',
|
|
1233
|
+
'atlassian/jira_get_link_types', 'atlassian/jira_get_project_issues', 'atlassian/jira_get_project_versions',
|
|
1234
|
+
'atlassian/jira_get_sprint_issues', 'atlassian/jira_get_sprints_from_board', 'atlassian/jira_get_transitions',
|
|
1235
|
+
'atlassian/jira_get_user_profile', 'atlassian/jira_get_worklog', 'atlassian/jira_search', 'atlassian/jira_search_fields',
|
|
1236
|
+
// Context7 MCP
|
|
1237
|
+
'context7/get-library-docs', 'context7/resolve-library-id',
|
|
1238
|
+
// Sequential Thinking MCP
|
|
1239
|
+
'sequentialthinking/sequentialthinking',
|
|
1240
|
+
// Edit tools
|
|
1241
|
+
'edit/createDirectory', 'edit/createFile', 'edit/createJupyterNotebook',
|
|
1242
|
+
'edit/editFiles', 'edit/editNotebook',
|
|
1243
|
+
// Search tools
|
|
1244
|
+
'search/changes', 'search/codebase', 'search/fileSearch', 'search/listDirectory',
|
|
1245
|
+
'search/searchResults', 'search/textSearch', 'search/usages', 'search/searchSubagent',
|
|
1246
|
+
// Web tools
|
|
1247
|
+
'web/fetch', 'web/githubRepo',
|
|
1248
|
+
// GitHub MCP
|
|
1249
|
+
'github/add_comment_to_pending_review', 'github/add_issue_comment', 'github/assign_copilot_to_issue',
|
|
1250
|
+
'github/create_branch', 'github/create_or_update_file', 'github/create_pull_request',
|
|
1251
|
+
'github/create_repository', 'github/delete_file', 'github/fork_repository', 'github/get_commit',
|
|
1252
|
+
'github/get_file_contents', 'github/get_label', 'github/get_latest_release', 'github/get_me',
|
|
1253
|
+
'github/get_release_by_tag', 'github/get_tag', 'github/get_team_members', 'github/get_teams',
|
|
1254
|
+
'github/issue_read', 'github/issue_write', 'github/list_branches', 'github/list_commits',
|
|
1255
|
+
'github/list_issue_types', 'github/list_issues', 'github/list_pull_requests', 'github/list_releases',
|
|
1256
|
+
'github/list_tags', 'github/merge_pull_request', 'github/pull_request_read',
|
|
1257
|
+
'github/pull_request_review_write', 'github/push_files', 'github/request_copilot_review',
|
|
1258
|
+
'github/search_code', 'github/search_issues', 'github/search_pull_requests',
|
|
1259
|
+
'github/search_repositories', 'github/search_users', 'github/sub_issue_write',
|
|
1260
|
+
'github/update_pull_request', 'github/update_pull_request_branch',
|
|
1261
|
+
// Upstash Context7 MCP
|
|
1262
|
+
'upstash/context7/query-docs', 'upstash/context7/resolve-library-id',
|
|
1263
|
+
// GitHub PR extension tools
|
|
1264
|
+
'github.vscode-pull-request-github/issue_fetch', 'github.vscode-pull-request-github/suggest-fix',
|
|
1265
|
+
'github.vscode-pull-request-github/searchSyntax', 'github.vscode-pull-request-github/doSearch',
|
|
1266
|
+
'github.vscode-pull-request-github/renderIssues', 'github.vscode-pull-request-github/activePullRequest',
|
|
1267
|
+
'github.vscode-pull-request-github/openPullRequest',
|
|
1268
|
+
// Other tools
|
|
1269
|
+
'todo'
|
|
1270
|
+
];
|
|
1271
|
+
|
|
1272
|
+
// Use full tool list for all agents except orchestrator
|
|
1273
|
+
const toolList = agentId === 'aigile-orchestrator'
|
|
1274
|
+
? ['agent/runSubagent']
|
|
1275
|
+
: [...new Set([...FULL_AGENT_TOOLS, ...tools])].sort();
|
|
1276
|
+
|
|
1277
|
+
// Generate handoffs based on agent role
|
|
1278
|
+
const handoffs = generateHandoffsFor(agentId);
|
|
1279
|
+
|
|
1280
|
+
// Build YAML frontmatter in new custom agents format
|
|
1281
|
+
const fmLines = [
|
|
1282
|
+
'---',
|
|
1283
|
+
`name: ${title}`,
|
|
1284
|
+
`description: "${description}"`,
|
|
1285
|
+
`argument-hint: "Describe your ${agentId} task"`,
|
|
1286
|
+
'tools:'
|
|
1287
|
+
];
|
|
1288
|
+
for (const t of toolList) {
|
|
1289
|
+
fmLines.push(` - ${t}`);
|
|
1290
|
+
}
|
|
1291
|
+
fmLines.push('model: Claude Opus 4.5');
|
|
1292
|
+
fmLines.push('infer: true');
|
|
1293
|
+
|
|
1294
|
+
if (handoffs.length > 0) {
|
|
1295
|
+
fmLines.push('handoffs:');
|
|
1296
|
+
for (const h of handoffs) {
|
|
1297
|
+
fmLines.push(` - label: "${h.label}"`);
|
|
1298
|
+
fmLines.push(` agent: ${h.agent}`);
|
|
1299
|
+
fmLines.push(` prompt: "${h.prompt}"`);
|
|
1300
|
+
fmLines.push(` send: ${h.send}`);
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
fmLines.push('---');
|
|
1304
|
+
fmLines.push('');
|
|
1305
|
+
|
|
1306
|
+
// Build the agent body
|
|
1307
|
+
const person = { ...personaFor(agentId), ...(agentMeta.persona || {}) };
|
|
1308
|
+
const combinedPrinciples = (agentMeta.corePrinciples && agentMeta.corePrinciples.length)
|
|
1309
|
+
? agentMeta.corePrinciples
|
|
1310
|
+
: principlesFor(agentId);
|
|
1311
|
+
const deps = relevantDependencies(agentId);
|
|
1312
|
+
|
|
1313
|
+
const body = [];
|
|
1314
|
+
body.push(`# ${title}`);
|
|
1315
|
+
body.push('');
|
|
1316
|
+
body.push(`You are the **${title}** - a specialized AIgile framework agent.`);
|
|
1317
|
+
body.push('');
|
|
1318
|
+
body.push('## Core Identity');
|
|
1319
|
+
body.push('');
|
|
1320
|
+
body.push(`- **Role**: ${person.role}`);
|
|
1321
|
+
body.push(`- **Style**: ${person.style}`);
|
|
1322
|
+
body.push(`- **Focus**: ${person.focus}`);
|
|
1323
|
+
body.push('');
|
|
1324
|
+
body.push('## Operating Principles');
|
|
1325
|
+
body.push('');
|
|
1326
|
+
for (const p of combinedPrinciples) {
|
|
1327
|
+
body.push(`- ${p}`);
|
|
1328
|
+
}
|
|
1329
|
+
body.push('');
|
|
1330
|
+
body.push('## Subagent Delegation');
|
|
1331
|
+
body.push('');
|
|
1332
|
+
body.push('Use `#tool:runSubagent` to delegate specialized tasks to other agents when appropriate:');
|
|
1333
|
+
body.push('');
|
|
1334
|
+
body.push('| Agent | Specialty |');
|
|
1335
|
+
body.push('|-------|-----------|');
|
|
1336
|
+
body.push('| `dev` | Code implementation, debugging, TDD |');
|
|
1337
|
+
body.push('| `architect` | Architecture review, documentation |');
|
|
1338
|
+
body.push('| `po` | Story creation, backlog management |');
|
|
1339
|
+
body.push('| `qa` | Testing, quality gates |');
|
|
1340
|
+
body.push('| `analyst` | Requirements analysis |');
|
|
1341
|
+
body.push('| `pm` | Project planning |');
|
|
1342
|
+
body.push('');
|
|
1343
|
+
|
|
1344
|
+
if (tasks.length > 0) {
|
|
1345
|
+
body.push('## Available Tasks');
|
|
1346
|
+
body.push('');
|
|
1347
|
+
for (const t of tasks) {
|
|
1348
|
+
body.push(`- \`${t}\``);
|
|
1349
|
+
}
|
|
1350
|
+
body.push('');
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
if (deps.templates.length > 0) {
|
|
1354
|
+
body.push('## Templates');
|
|
1355
|
+
body.push('');
|
|
1356
|
+
body.push('Reference templates in `.aigile-core/templates/`:');
|
|
1357
|
+
body.push('');
|
|
1358
|
+
for (const t of deps.templates) {
|
|
1359
|
+
body.push(`- ${t}`);
|
|
1360
|
+
}
|
|
1361
|
+
body.push('');
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
body.push('## Original Agent Definition');
|
|
1365
|
+
body.push('');
|
|
1366
|
+
body.push('<details>');
|
|
1367
|
+
body.push('<summary>Expand for full agent configuration</summary>');
|
|
1368
|
+
body.push('');
|
|
1369
|
+
body.push(agentMd.trim());
|
|
1370
|
+
body.push('');
|
|
1371
|
+
body.push('</details>');
|
|
1372
|
+
|
|
1373
|
+
return fmLines.join('\n') + body.join('\n');
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
/**
|
|
1377
|
+
* Generate handoff buttons for a given agent
|
|
1378
|
+
*/
|
|
1379
|
+
function generateHandoffsFor(agentId) {
|
|
1380
|
+
const handoffMappings = {
|
|
1381
|
+
'aigile-orchestrator': [
|
|
1382
|
+
{ label: '🏗️ Architecture Review', agent: 'architect', prompt: 'Analyze the architecture and provide recommendations.', send: false },
|
|
1383
|
+
{ label: '💻 Start Implementation', agent: 'dev', prompt: 'Implement the plan using test-driven development.', send: false },
|
|
1384
|
+
{ label: '🧪 Run QA Gate', agent: 'qa', prompt: 'Review and test the implementation for quality.', send: false },
|
|
1385
|
+
{ label: '📋 Refine Backlog', agent: 'po', prompt: 'Refine the backlog items based on discussion.', send: false },
|
|
1386
|
+
],
|
|
1387
|
+
dev: [
|
|
1388
|
+
{ label: '🧪 Request QA Review', agent: 'qa', prompt: 'Review the implementation above for quality.', send: false },
|
|
1389
|
+
{ label: '🏗️ Architecture Check', agent: 'architect', prompt: 'Verify architecture compliance.', send: false },
|
|
1390
|
+
],
|
|
1391
|
+
architect: [
|
|
1392
|
+
{ label: '💻 Start Implementation', agent: 'dev', prompt: 'Implement the architecture recommendations.', send: false },
|
|
1393
|
+
{ label: '📋 Create Stories', agent: 'po', prompt: 'Create backlog items for architecture changes.', send: false },
|
|
1394
|
+
],
|
|
1395
|
+
po: [
|
|
1396
|
+
{ label: '💻 Implement Story', agent: 'dev', prompt: 'Implement the story defined above.', send: false },
|
|
1397
|
+
{ label: '🧠 Analyze Further', agent: 'analyst', prompt: 'Provide deeper analysis of requirements.', send: false },
|
|
1398
|
+
],
|
|
1399
|
+
qa: [
|
|
1400
|
+
{ label: '💻 Fix Issues', agent: 'dev', prompt: 'Fix the issues identified in QA review.', send: false },
|
|
1401
|
+
{ label: '📋 Update Stories', agent: 'po', prompt: 'Update stories based on QA findings.', send: false },
|
|
1402
|
+
],
|
|
1403
|
+
analyst: [
|
|
1404
|
+
{ label: '📋 Create Stories', agent: 'po', prompt: 'Create stories from analysis findings.', send: false },
|
|
1405
|
+
{ label: '🏗️ Architecture Review', agent: 'architect', prompt: 'Review architecture implications.', send: false },
|
|
1406
|
+
],
|
|
1407
|
+
pm: [
|
|
1408
|
+
{ label: '📋 Refine Stories', agent: 'po', prompt: 'Refine the stories in the plan.', send: false },
|
|
1409
|
+
],
|
|
1410
|
+
sm: [],
|
|
1411
|
+
'ui-expert': [
|
|
1412
|
+
{ label: '💻 Implement UI', agent: 'dev', prompt: 'Implement the UI specifications.', send: false },
|
|
1413
|
+
],
|
|
1414
|
+
'ux-expert': [
|
|
1415
|
+
{ label: '🖌️ Design System', agent: 'ui-expert', prompt: 'Create design system components.', send: false },
|
|
1416
|
+
{ label: '💻 Implement UX', agent: 'dev', prompt: 'Implement the UX recommendations.', send: false },
|
|
1417
|
+
],
|
|
1418
|
+
};
|
|
1419
|
+
|
|
1420
|
+
return handoffMappings[agentId] || [];
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
function installGithubCopilot(dest, { addJavaInstructions = false, useLegacyFormat = false } = {}) {
|
|
1424
|
+
// 1) Copy core to .aigile-core (clean, IDE-friendly)
|
|
1425
|
+
const srcCore = path.join(root, 'core');
|
|
1426
|
+
const dstCore = path.join(dest, '.aigile-core');
|
|
1427
|
+
if (fs.existsSync(srcCore)) {
|
|
1428
|
+
copyDir(srcCore, dstCore);
|
|
1429
|
+
}
|
|
1430
|
+
// 2) Generate GitHub Copilot custom agents in .github/agents/ (new format)
|
|
1431
|
+
// Or legacy chatmodes in .github/chatmodes/ if explicitly requested
|
|
1432
|
+
const agentsDir = path.join(srcCore, 'agents');
|
|
1433
|
+
const outputDir = useLegacyFormat
|
|
1434
|
+
? path.join(dest, '.github', 'chatmodes')
|
|
1435
|
+
: path.join(dest, '.github', 'agents');
|
|
1436
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
1437
|
+
const mcpCatalog = buildMcpCatalog();
|
|
1438
|
+
const generatedAgents = [];
|
|
1439
|
+
if (fs.existsSync(agentsDir)) {
|
|
1440
|
+
const files = fs.readdirSync(agentsDir).filter(f => f.endsWith('.md'));
|
|
1441
|
+
for (const f of files) {
|
|
1442
|
+
const agentPath = path.join(agentsDir, f);
|
|
1443
|
+
const agentMd = read(agentPath);
|
|
1444
|
+
// Handle both new .agent.md and legacy .md formats
|
|
1445
|
+
const isNativeAgent = f.endsWith('.agent.md');
|
|
1446
|
+
const agentId = isNativeAgent ? f.replace(/\.agent\.md$/, '') : f.replace(/\.[^/.]+$/, '');
|
|
1447
|
+
const agentMeta = extractAgentMeta(agentMd);
|
|
1448
|
+
const tools = [];
|
|
1449
|
+
const allTasks = listTasksForAgent(agentId);
|
|
1450
|
+
const allChecklists = []; // Use only agent-specific checklists from relevantDependencies()
|
|
1451
|
+
|
|
1452
|
+
// If already in native .agent.md format, copy directly
|
|
1453
|
+
if (isNativeAgent && !useLegacyFormat) {
|
|
1454
|
+
const outName = f;
|
|
1455
|
+
write(path.join(outputDir, outName), agentMd);
|
|
1456
|
+
generatedAgents.push({ id: agentId, format: 'native' });
|
|
1457
|
+
continue;
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
// Generate structured agent/chatmode file
|
|
1461
|
+
const outContent = useLegacyFormat
|
|
1462
|
+
? generateStructuredChatmode({
|
|
1463
|
+
agentId,
|
|
1464
|
+
agentMeta,
|
|
1465
|
+
agentMd,
|
|
1466
|
+
tools,
|
|
1467
|
+
tasks: allTasks,
|
|
1468
|
+
checklists: allChecklists,
|
|
1469
|
+
})
|
|
1470
|
+
: generateCustomAgent({
|
|
1471
|
+
agentId,
|
|
1472
|
+
agentMeta,
|
|
1473
|
+
agentMd,
|
|
1474
|
+
tools,
|
|
1475
|
+
tasks: allTasks,
|
|
1476
|
+
checklists: allChecklists,
|
|
1477
|
+
});
|
|
1478
|
+
const outName = useLegacyFormat
|
|
1479
|
+
? `${kebabCaseName(f)}.chatmode.md`
|
|
1480
|
+
: `${kebabCaseName(f)}.agent.md`;
|
|
1481
|
+
write(path.join(outputDir, outName), outContent);
|
|
1482
|
+
generatedAgents.push({ id: agentId, format: useLegacyFormat ? 'chatmode' : 'agent' });
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
console.log(`✅ Created ${generatedAgents.length} custom ${useLegacyFormat ? 'chatmodes' : 'agents'} in ${useLegacyFormat ? '.github/chatmodes' : '.github/agents'}/`);
|
|
1486
|
+
// Copy core prompts into consumer .github/prompts for convenience (if provided in repo)
|
|
1487
|
+
try {
|
|
1488
|
+
const promptsSrc = path.join(root, 'core', 'prompts');
|
|
1489
|
+
const promptsDst = path.join(dest, '.github', 'prompts');
|
|
1490
|
+
if (fs.existsSync(promptsSrc)) {
|
|
1491
|
+
copyDir(promptsSrc, promptsDst);
|
|
1492
|
+
console.log('✅ Copied prompts to .github/prompts');
|
|
1493
|
+
}
|
|
1494
|
+
} catch (e) { /* non-fatal */ }
|
|
1495
|
+
let javaInstructionsCopied = false;
|
|
1496
|
+
if (addJavaInstructions) {
|
|
1497
|
+
try {
|
|
1498
|
+
const javaSrc = path.join(root, 'core', 'instructions', 'java.instructions.md');
|
|
1499
|
+
if (fs.existsSync(javaSrc)) {
|
|
1500
|
+
const instructionsDir = path.join(dest, '.github', 'instructions');
|
|
1501
|
+
fs.mkdirSync(instructionsDir, { recursive: true });
|
|
1502
|
+
fs.copyFileSync(javaSrc, path.join(instructionsDir, 'java.instructions.md'));
|
|
1503
|
+
javaInstructionsCopied = true;
|
|
1504
|
+
} else {
|
|
1505
|
+
console.warn('⚠️ Java instructions template not found at core/instructions/java.instructions.md');
|
|
1506
|
+
}
|
|
1507
|
+
} catch (e) {
|
|
1508
|
+
console.warn('⚠️ Failed to copy Java instructions:', e.message);
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
const color = (code) => (s) => `\x1b[${code}m${s}\x1b[0m`;
|
|
1512
|
+
const green = color(32);
|
|
1513
|
+
if (javaInstructionsCopied) console.log(green('✅ Added Java instructions to .github/instructions'));
|
|
1514
|
+
console.log(green('✅ Installed AIgile for GitHub Copilot into'), dest);
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
function maybeAddConsumerScripts(dest) {
|
|
1518
|
+
if (process.env.AIGILE_ADD_SCRIPTS === '0') return;
|
|
1519
|
+
const pkgPath = path.join(dest, 'package.json');
|
|
1520
|
+
if (!fs.existsSync(pkgPath)) return; // consumer has no package.json
|
|
1521
|
+
try {
|
|
1522
|
+
const pkg = JSON.parse(read(pkgPath));
|
|
1523
|
+
pkg.scripts = pkg.scripts || {};
|
|
1524
|
+
let changed = false;
|
|
1525
|
+
const additions = {
|
|
1526
|
+
'aigile:install': 'aigile-method install --yes',
|
|
1527
|
+
'aigile:bundle': 'aigile-method bundle',
|
|
1528
|
+
'aigile:status': 'aigile-method status',
|
|
1529
|
+
'aigile:list': 'aigile-method list:agents'
|
|
1530
|
+
};
|
|
1531
|
+
for (const [k, v] of Object.entries(additions)) {
|
|
1532
|
+
if (!pkg.scripts[k]) { pkg.scripts[k] = v; changed = true; }
|
|
1533
|
+
}
|
|
1534
|
+
if (changed) write(pkgPath, JSON.stringify(pkg, null, 2));
|
|
1535
|
+
} catch { /* ignore */ }
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
function writeManifest(dest, { addMcpJson = false, addMcpSettings = false, addJavaInstructions = false } = {}) {
|
|
1539
|
+
try {
|
|
1540
|
+
const manifest = {
|
|
1541
|
+
name: '@mark-gozner/aigile-method',
|
|
1542
|
+
version: getPackageVersion(),
|
|
1543
|
+
installedAt: new Date().toISOString(),
|
|
1544
|
+
format: 'agents', // New format indicator
|
|
1545
|
+
agents: [],
|
|
1546
|
+
customAgents: 0,
|
|
1547
|
+
chatModes: 0, // Legacy
|
|
1548
|
+
prompts: 0,
|
|
1549
|
+
mcp: { serversJson: !!addMcpJson, mockSettings: !!addMcpSettings },
|
|
1550
|
+
instructions: { java: !!addJavaInstructions },
|
|
1551
|
+
features: {
|
|
1552
|
+
customAgents: true,
|
|
1553
|
+
handoffs: true,
|
|
1554
|
+
runSubagent: true,
|
|
1555
|
+
mcp: !!addMcpJson
|
|
1556
|
+
}
|
|
1557
|
+
};
|
|
1558
|
+
const agentsDir = path.join(dest, '.aigile-core', 'agents');
|
|
1559
|
+
if (fs.existsSync(agentsDir)) {
|
|
1560
|
+
manifest.agents = fs.readdirSync(agentsDir).filter(f => f.endsWith('.md')).map(f => f.replace(/\\.agent\\.md$/, '').replace(/\\.md$/, ''));
|
|
1561
|
+
}
|
|
1562
|
+
// Count new custom agents format
|
|
1563
|
+
const customAgentsDir = path.join(dest, '.github', 'agents');
|
|
1564
|
+
if (fs.existsSync(customAgentsDir)) {
|
|
1565
|
+
manifest.customAgents = fs.readdirSync(customAgentsDir).filter(f => f.endsWith('.agent.md')).length;
|
|
1566
|
+
}
|
|
1567
|
+
// Count legacy chatmodes
|
|
1568
|
+
const chatModesDir = path.join(dest, '.github', 'chatmodes');
|
|
1569
|
+
if (fs.existsSync(chatModesDir)) {
|
|
1570
|
+
manifest.chatModes = fs.readdirSync(chatModesDir).filter(f => f.endsWith('.chatmode.md')).length;
|
|
1571
|
+
}
|
|
1572
|
+
const promptsDir = path.join(dest, '.github', 'prompts');
|
|
1573
|
+
if (fs.existsSync(promptsDir)) {
|
|
1574
|
+
manifest.prompts = fs.readdirSync(promptsDir).filter(f => f.endsWith('.md') || f.endsWith('.prompt.md') || f.endsWith('.txt')).length;
|
|
1575
|
+
}
|
|
1576
|
+
write(path.join(dest, '.aigile-manifest.json'), JSON.stringify(manifest, null, 2));
|
|
1577
|
+
} catch { /* ignore */ }
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
function showStatus(cwd) {
|
|
1581
|
+
const manifestPath = path.join(cwd, '.aigile-manifest.json');
|
|
1582
|
+
if (!fs.existsSync(manifestPath)) {
|
|
1583
|
+
console.log('No AIgile installation manifest found in this directory. Run `aigile-method install --yes` to initialize.');
|
|
1584
|
+
return;
|
|
1585
|
+
}
|
|
1586
|
+
try {
|
|
1587
|
+
const data = JSON.parse(read(manifestPath));
|
|
1588
|
+
console.log('\\nAIgile Installation Status');
|
|
1589
|
+
console.log('---------------------------');
|
|
1590
|
+
console.log(`Version: ${data.version}`);
|
|
1591
|
+
console.log(`Installed At: ${data.installedAt}`);
|
|
1592
|
+
console.log(`Format: ${data.format || 'legacy'}`);
|
|
1593
|
+
console.log(`Agents: ${data.agents?.length || 0}`);
|
|
1594
|
+
console.log(`Custom Agents: ${data.customAgents || 0}`);
|
|
1595
|
+
if (data.chatModes > 0) console.log(`Chat Modes: ${data.chatModes} (legacy)`);
|
|
1596
|
+
if (typeof data.prompts !== 'undefined') console.log(`Prompts: ${data.prompts}`);
|
|
1597
|
+
console.log(`MCP Servers: ${data.mcp?.serversJson ? 'yes' : 'no'}`);
|
|
1598
|
+
console.log(`MCP Mock Sets: ${data.mcp?.mockSettings ? 'yes' : 'no'}`);
|
|
1599
|
+
if (data.instructions) {
|
|
1600
|
+
console.log(`Java Instructions: ${data.instructions.java ? 'yes' : 'no'}`);
|
|
1601
|
+
}
|
|
1602
|
+
if (data.features) {
|
|
1603
|
+
console.log('\\nFeatures:');
|
|
1604
|
+
console.log(` - Custom Agents: ${data.features.customAgents ? 'yes' : 'no'}`);
|
|
1605
|
+
console.log(` - Handoffs: ${data.features.handoffs ? 'yes' : 'no'}`);
|
|
1606
|
+
console.log(` - runSubagent: ${data.features.runSubagent ? 'yes' : 'no'}`);
|
|
1607
|
+
}
|
|
1608
|
+
if (data.agents?.length) {
|
|
1609
|
+
console.log('\\nAgents:');
|
|
1610
|
+
console.log(' ' + data.agents.join(', '));
|
|
1611
|
+
}
|
|
1612
|
+
} catch (e) {
|
|
1613
|
+
console.log('Corrupt manifest file. Re-run install to regenerate.');
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
function listAgentsCommand(cwd) {
|
|
1618
|
+
const agentsDir = path.join(cwd, '.aigile-core', 'agents');
|
|
1619
|
+
if (!fs.existsSync(agentsDir)) {
|
|
1620
|
+
console.log('No .aigile-core/agents directory found. Run install first.');
|
|
1621
|
+
return;
|
|
1622
|
+
}
|
|
1623
|
+
const agents = fs.readdirSync(agentsDir).filter(f => f.endsWith('.md')).map(f => f.replace(/\.md$/, ''));
|
|
1624
|
+
if (!agents.length) { console.log('No agents installed.'); return; }
|
|
1625
|
+
console.log('Installed agents:');
|
|
1626
|
+
for (const a of agents) console.log(' - ' + a);
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
function parseFlags(argv) {
|
|
1630
|
+
const flags = new Set();
|
|
1631
|
+
const args = [];
|
|
1632
|
+
for (const a of argv) {
|
|
1633
|
+
if (a.startsWith('--')) flags.add(a);
|
|
1634
|
+
else args.push(a);
|
|
1635
|
+
}
|
|
1636
|
+
return { flags, args };
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
function writeMockMcp(dest) {
|
|
1640
|
+
// Always also write VS Code settings for MCP endpoints
|
|
1641
|
+
const vscodeDir = path.join(dest, '.vscode');
|
|
1642
|
+
fs.mkdirSync(vscodeDir, { recursive: true });
|
|
1643
|
+
const vscodeSettingsPath = path.join(vscodeDir, 'settings.json');
|
|
1644
|
+
let settings = {};
|
|
1645
|
+
if (fs.existsSync(vscodeSettingsPath)) {
|
|
1646
|
+
try { settings = JSON.parse(read(vscodeSettingsPath)); } catch { }
|
|
1647
|
+
}
|
|
1648
|
+
// Enable custom agent as subagent (experimental) - required for orchestrator delegation
|
|
1649
|
+
settings['chat.customAgentInSubagent.enabled'] = true;
|
|
1650
|
+
settings['aigile.mcp.enabled'] = true;
|
|
1651
|
+
settings['aigile.mcp.servers'] = {
|
|
1652
|
+
sequentialthinking: {
|
|
1653
|
+
enabled: true,
|
|
1654
|
+
url: 'https://mock.sequentialthinking.local',
|
|
1655
|
+
apiKey: 'MOCK_SEQ_KEY'
|
|
1656
|
+
},
|
|
1657
|
+
context7: {
|
|
1658
|
+
enabled: true,
|
|
1659
|
+
url: 'https://mock.context7.local',
|
|
1660
|
+
apiKey: 'MOCK_CTX_KEY'
|
|
1661
|
+
},
|
|
1662
|
+
atlassian: {
|
|
1663
|
+
enabled: true,
|
|
1664
|
+
url: 'https://mock.atlassian.local',
|
|
1665
|
+
email: 'user@example.com',
|
|
1666
|
+
apiToken: 'MOCK_ATL_TOKEN',
|
|
1667
|
+
products: ['jira', 'confluence'],
|
|
1668
|
+
jira: {
|
|
1669
|
+
cloudBaseUrl: 'https://yourcompany.atlassian.net',
|
|
1670
|
+
projectKeysAllowlist: ['ENG']
|
|
1671
|
+
},
|
|
1672
|
+
confluence: {
|
|
1673
|
+
cloudBaseUrl: 'https://yourcompany.atlassian.net/wiki',
|
|
1674
|
+
spacesAllowlist: ['ENG']
|
|
1675
|
+
}
|
|
1676
|
+
},
|
|
1677
|
+
github: {
|
|
1678
|
+
enabled: true,
|
|
1679
|
+
url: 'https://mock.github-mcp.local',
|
|
1680
|
+
token: 'MOCK_GH_TOKEN',
|
|
1681
|
+
orgAllowlist: ['your-org'],
|
|
1682
|
+
repoReadAllowlist: ['your-org/*']
|
|
1683
|
+
},
|
|
1684
|
+
figma: {
|
|
1685
|
+
enabled: true,
|
|
1686
|
+
url: 'https://mock.figma-mcp.local',
|
|
1687
|
+
personalAccessToken: 'MOCK_FIGMA_TOKEN',
|
|
1688
|
+
teamAllowlist: ['Design Systems']
|
|
1689
|
+
}
|
|
1690
|
+
};
|
|
1691
|
+
write(vscodeSettingsPath, JSON.stringify(settings, null, 2));
|
|
1692
|
+
console.log('✅ Updated .vscode/settings.json with mock MCP servers');
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
function writeMcpJson(dest) {
|
|
1696
|
+
// Install MCP servers by copying from repo mcp/servers.json into .vscode/mcp.json (preferred)
|
|
1697
|
+
const vscodeDir = path.join(dest, '.vscode');
|
|
1698
|
+
fs.mkdirSync(vscodeDir, { recursive: true });
|
|
1699
|
+
const mcpPath = path.join(vscodeDir, 'mcp.json');
|
|
1700
|
+
const repoServersJson = path.join(root, 'mcp', 'servers.json');
|
|
1701
|
+
if (fs.existsSync(repoServersJson)) {
|
|
1702
|
+
write(mcpPath, read(repoServersJson));
|
|
1703
|
+
console.log('✅ Installed MCP servers from mcp/servers.json into .vscode/mcp.json');
|
|
1704
|
+
return;
|
|
1705
|
+
}
|
|
1706
|
+
// Fallback minimal template if repo JSON is missing
|
|
1707
|
+
const tpl = {
|
|
1708
|
+
servers: {
|
|
1709
|
+
"github.com": {
|
|
1710
|
+
command: "docker",
|
|
1711
|
+
args: [
|
|
1712
|
+
"run", "-i", "--rm", "-e", "GITHUB_PERSONAL_ACCESS_TOKEN", "-e", "GITHUB_DYNAMIC_TOOLSETS", "ghcr.io/github/github-mcp-server"
|
|
1713
|
+
],
|
|
1714
|
+
env: {
|
|
1715
|
+
GITHUB_PERSONAL_ACCESS_TOKEN: "${input:github_com_personal_access_token}",
|
|
1716
|
+
GITHUB_DYNAMIC_TOOLSETS: "1"
|
|
1717
|
+
}
|
|
1718
|
+
},
|
|
1719
|
+
sequentialthinking: {
|
|
1720
|
+
command: "npx",
|
|
1721
|
+
args: ["-y", "@modelcontextprotocol/server-sequential-thinking"],
|
|
1722
|
+
env: { SEQUENTIAL_THINKING_MAX_STEPS: "8" }
|
|
1723
|
+
},
|
|
1724
|
+
context7: {
|
|
1725
|
+
command: "npx",
|
|
1726
|
+
args: ["-y", "@context7/mcp-server"],
|
|
1727
|
+
env: { CONTEXT7_API_KEY: "${input:context7_api_key}", CONTEXT7_DEFAULT_KB: "general" }
|
|
1728
|
+
}
|
|
1729
|
+
},
|
|
1730
|
+
inputs: [
|
|
1731
|
+
{ id: "github_com_personal_access_token", type: "promptString", description: "GitHub.com PAT" },
|
|
1732
|
+
{ id: "context7_api_key", type: "promptString", description: "Context7 API key" }
|
|
1733
|
+
]
|
|
1734
|
+
};
|
|
1735
|
+
write(mcpPath, JSON.stringify(tpl, null, 2));
|
|
1736
|
+
console.log('✅ Wrote .vscode/mcp.json (template) — update API keys via VS Code prompts');
|
|
1737
|
+
}
|
|
1738
|
+
|
|
1739
|
+
/**
|
|
1740
|
+
* Write essential VS Code settings for AIgile orchestrator functionality
|
|
1741
|
+
* This is always called during installation to enable custom agent as subagent
|
|
1742
|
+
*/
|
|
1743
|
+
function writeVscodeSettings(dest) {
|
|
1744
|
+
const vscodeDir = path.join(dest, '.vscode');
|
|
1745
|
+
fs.mkdirSync(vscodeDir, { recursive: true });
|
|
1746
|
+
const vscodeSettingsPath = path.join(vscodeDir, 'settings.json');
|
|
1747
|
+
let settings = {};
|
|
1748
|
+
if (fs.existsSync(vscodeSettingsPath)) {
|
|
1749
|
+
try { settings = JSON.parse(read(vscodeSettingsPath)); } catch { }
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
// Enable custom agent as subagent (experimental) - required for orchestrator delegation
|
|
1753
|
+
// This allows runSubagent calls to use the target agent's toolset
|
|
1754
|
+
settings['chat.customAgentInSubagent.enabled'] = true;
|
|
1755
|
+
|
|
1756
|
+
write(vscodeSettingsPath, JSON.stringify(settings, null, 2));
|
|
1757
|
+
console.log('✅ Configured VS Code settings for orchestrator subagent delegation');
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
const cmd = process.argv[2];
|
|
1761
|
+
const { flags } = parseFlags(process.argv.slice(3));
|
|
1762
|
+
if (cmd === 'bundle') bundle();
|
|
1763
|
+
else if (cmd === 'install') {
|
|
1764
|
+
const autoYes = flags.has('--yes') || flags.has('-y');
|
|
1765
|
+
const addMcpJsonFlag = flags.has('--with-mcp');
|
|
1766
|
+
const addMcpSettingsFlag = flags.has('--with-mcp-settings');
|
|
1767
|
+
const addJavaInstructionsFlag = flags.has('--with-java-instructions');
|
|
1768
|
+
// If flags explicitly provided, skip prompts and just do what flags request
|
|
1769
|
+
if (addMcpJsonFlag || addMcpSettingsFlag || addJavaInstructionsFlag) {
|
|
1770
|
+
printInstallBanner({ showGMSignature: true });
|
|
1771
|
+
const dest = process.cwd();
|
|
1772
|
+
installGithubCopilot(dest, { addJavaInstructions: addJavaInstructionsFlag });
|
|
1773
|
+
maybeAddConsumerScripts(dest);
|
|
1774
|
+
if (flags.has('--prune-legacy')) pruneLegacy(dest);
|
|
1775
|
+
if (addMcpSettingsFlag) writeMockMcp(dest);
|
|
1776
|
+
if (addMcpJsonFlag) writeMcpJson(dest);
|
|
1777
|
+
// Always write essential VS Code settings for orchestrator functionality
|
|
1778
|
+
writeVscodeSettings(dest);
|
|
1779
|
+
writeManifest(dest, { addMcpJson: addMcpJsonFlag, addMcpSettings: addMcpSettingsFlag, addJavaInstructions: addJavaInstructionsFlag });
|
|
1780
|
+
} else {
|
|
1781
|
+
await installInteractive({ autoYes });
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1784
|
+
else if (cmd === 'status') {
|
|
1785
|
+
showStatus(process.cwd());
|
|
1786
|
+
}
|
|
1787
|
+
else if (cmd === 'list:agents') {
|
|
1788
|
+
listAgentsCommand(process.cwd());
|
|
1789
|
+
}
|
|
1790
|
+
else if (cmd === 'auth') {
|
|
1791
|
+
// For public npm, auth is handled via `npm login` or NPM_TOKEN environment variable
|
|
1792
|
+
console.log('For public npm registry, use `npm login` or set NPM_TOKEN environment variable.');
|
|
1793
|
+
console.log('No additional auth configuration is needed for @mark-gozner/aigile-method.');
|
|
1794
|
+
}
|
|
1795
|
+
else if (cmd === 'init' || !cmd) {
|
|
1796
|
+
// Unified, easiest path for new users: ensure auth then install.
|
|
1797
|
+
// Flags: --token <value>, --with-mcp, --with-mcp-settings, --prune-legacy, --yes
|
|
1798
|
+
const argvRest = process.argv.slice(2); // may include 'init'
|
|
1799
|
+
const passThrough = new Set();
|
|
1800
|
+
for (let i = 0; i < argvRest.length; i++) {
|
|
1801
|
+
const a = argvRest[i];
|
|
1802
|
+
if (a === 'init') continue;
|
|
1803
|
+
if (a === '--token' && argvRest[i+1]) { i++; continue; } // skip legacy token flag
|
|
1804
|
+
if (a.startsWith('--token=')) { continue; } // skip legacy token flag
|
|
1805
|
+
passThrough.add(a);
|
|
1806
|
+
}
|
|
1807
|
+
// Proceed with install (non-interactive by default)
|
|
1808
|
+
printInstallBanner({ showGMSignature: true });
|
|
1809
|
+
const dest = process.cwd();
|
|
1810
|
+
const addMcpJson = passThrough.has('--with-mcp');
|
|
1811
|
+
const addMcpSettings = passThrough.has('--with-mcp-settings');
|
|
1812
|
+
const addJavaInstructions = passThrough.has('--with-java-instructions');
|
|
1813
|
+
installGithubCopilot(dest, { addJavaInstructions });
|
|
1814
|
+
maybeAddConsumerScripts(dest);
|
|
1815
|
+
if (passThrough.has('--prune-legacy')) pruneLegacy(dest);
|
|
1816
|
+
if (addMcpSettings) writeMockMcp(dest);
|
|
1817
|
+
if (addMcpJson) writeMcpJson(dest);
|
|
1818
|
+
// Always write essential VS Code settings for orchestrator functionality
|
|
1819
|
+
writeVscodeSettings(dest);
|
|
1820
|
+
writeManifest(dest, { addMcpJson, addMcpSettings, addJavaInstructions });
|
|
1821
|
+
console.log('✅ Init complete');
|
|
1822
|
+
}
|
|
1823
|
+
else if (cmd === 'auth-install') {
|
|
1824
|
+
// Deprecated: For public npm, just use init or install directly
|
|
1825
|
+
console.log('Note: auth-install is deprecated for public npm. Running install instead...');
|
|
1826
|
+
printInstallBanner({ showGMSignature: true });
|
|
1827
|
+
const dest = process.cwd();
|
|
1828
|
+
const argvRest = process.argv.slice(3);
|
|
1829
|
+
const addMcpJson = argvRest.includes('--with-mcp');
|
|
1830
|
+
const addMcpSettings = argvRest.includes('--with-mcp-settings');
|
|
1831
|
+
const addJavaInstructions = argvRest.includes('--with-java-instructions');
|
|
1832
|
+
installGithubCopilot(dest, { addJavaInstructions });
|
|
1833
|
+
maybeAddConsumerScripts(dest);
|
|
1834
|
+
if (argvRest.includes('--prune-legacy')) pruneLegacy(dest);
|
|
1835
|
+
if (addMcpSettings) writeMockMcp(dest);
|
|
1836
|
+
if (addMcpJson) writeMcpJson(dest);
|
|
1837
|
+
writeVscodeSettings(dest);
|
|
1838
|
+
writeManifest(dest, { addMcpJson, addMcpSettings, addJavaInstructions });
|
|
1839
|
+
console.log('✅ Install complete');
|
|
1840
|
+
}
|
|
1841
|
+
else {
|
|
1842
|
+
console.log('Usage: aigile <command> [flags]\\n');
|
|
1843
|
+
console.log('Commands:');
|
|
1844
|
+
console.log(' init Install .aigile-core and custom agents (default)');
|
|
1845
|
+
console.log(' install Install/update .aigile-core and custom agents');
|
|
1846
|
+
console.log(' bundle Build dist/ text bundles');
|
|
1847
|
+
console.log(' status Show installation manifest summary');
|
|
1848
|
+
console.log(' list:agents List installed agent IDs');
|
|
1849
|
+
console.log('\\nHelpful flags:');
|
|
1850
|
+
console.log(' --with-mcp Include .vscode/mcp.json');
|
|
1851
|
+
console.log(' --with-mcp-settings Include mock .vscode/settings.json MCP endpoints');
|
|
1852
|
+
console.log(' --with-java-instructions Copy Java instructions into .github/instructions');
|
|
1853
|
+
console.log(' --prune-legacy Remove legacy artifacts (old .aigile, docs skeleton, chatmodes)');
|
|
1854
|
+
console.log(' --use-legacy-format Use deprecated .chatmode.md format instead of .agent.md');
|
|
1855
|
+
console.log(' --yes Skip interactive component selection for install');
|
|
1856
|
+
console.log('\\nExamples:');
|
|
1857
|
+
console.log(' npx @mark-gozner/aigile-method # installs agents and core');
|
|
1858
|
+
console.log(' npx @mark-gozner/aigile-method init --with-mcp');
|
|
1859
|
+
console.log(' npx @mark-gozner/aigile-method install --yes');
|
|
1860
|
+
console.log('\\nNew Features (v0.3.0):');
|
|
1861
|
+
console.log(' - Custom Agents: Uses new .github/agents/*.agent.md format');
|
|
1862
|
+
console.log(' - Handoffs: Agents can hand off to other specialists');
|
|
1863
|
+
console.log(' - runSubagent: Autonomous task delegation between agents');
|
|
1864
|
+
}
|