create-byan-agent 2.9.4 → 2.9.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/install/bin/byan-cleanup.js +156 -0
  2. package/install/bin/byan-kanban.js +159 -0
  3. package/install/bin/byan-ledger.js +45 -0
  4. package/install/bin/create-byan-agent-v2.js +15 -1
  5. package/install/lib/cleanup/detector.js +154 -0
  6. package/install/lib/cleanup/executor.js +72 -0
  7. package/install/lib/staging-consent.js +149 -0
  8. package/install/lib/subagent-generator.js +208 -0
  9. package/install/lib/token-ledger.js +131 -0
  10. package/install/templates/.claude/agents/bmad-bmad-master.md +14 -0
  11. package/install/templates/.claude/agents/bmad-bmb-agent-builder.md +14 -0
  12. package/install/templates/.claude/agents/bmad-bmb-module-builder.md +14 -0
  13. package/install/templates/.claude/agents/bmad-bmb-workflow-builder.md +14 -0
  14. package/install/templates/.claude/agents/bmad-bmm-analyst.md +14 -0
  15. package/install/templates/.claude/agents/bmad-bmm-architect.md +14 -0
  16. package/install/templates/.claude/agents/bmad-bmm-dev.md +14 -0
  17. package/install/templates/.claude/agents/bmad-bmm-pm.md +14 -0
  18. package/install/templates/.claude/agents/bmad-bmm-quick-flow-solo-dev.md +14 -0
  19. package/install/templates/.claude/agents/bmad-bmm-quinn.md +14 -0
  20. package/install/templates/.claude/agents/bmad-bmm-sm.md +14 -0
  21. package/install/templates/.claude/agents/bmad-bmm-tech-writer.md +14 -0
  22. package/install/templates/.claude/agents/bmad-bmm-ux-designer.md +14 -0
  23. package/install/templates/.claude/agents/bmad-byan-v2.md +14 -0
  24. package/install/templates/.claude/agents/bmad-byan.md +152 -0
  25. package/install/templates/.claude/agents/bmad-carmack.md +14 -0
  26. package/install/templates/.claude/agents/bmad-cis-brainstorming-coach.md +14 -0
  27. package/install/templates/.claude/agents/bmad-cis-creative-problem-solver.md +14 -0
  28. package/install/templates/.claude/agents/bmad-cis-design-thinking-coach.md +14 -0
  29. package/install/templates/.claude/agents/bmad-cis-innovation-strategist.md +14 -0
  30. package/install/templates/.claude/agents/bmad-cis-presentation-master.md +14 -0
  31. package/install/templates/.claude/agents/bmad-cis-storyteller.md +14 -0
  32. package/install/templates/.claude/agents/bmad-claude.md +26 -0
  33. package/install/templates/.claude/agents/bmad-codex.md +26 -0
  34. package/install/templates/.claude/agents/bmad-compliance.md +68 -0
  35. package/install/templates/.claude/agents/bmad-drawio.md +25 -0
  36. package/install/templates/.claude/agents/bmad-expert-merise-agile.md +54 -0
  37. package/install/templates/.claude/agents/bmad-fact-checker.md +14 -0
  38. package/install/templates/.claude/agents/bmad-forgeron.md +14 -0
  39. package/install/templates/.claude/agents/bmad-hermes.md +59 -0
  40. package/install/templates/.claude/agents/bmad-marc.md +25 -0
  41. package/install/templates/.claude/agents/bmad-patnote.md +26 -0
  42. package/install/templates/.claude/agents/bmad-rachid.md +25 -0
  43. package/install/templates/.claude/agents/bmad-tao.md +14 -0
  44. package/install/templates/.claude/agents/bmad-tea-tea.md +14 -0
  45. package/install/templates/.claude/agents/bmad-yanstaller.md +47 -0
  46. package/install/templates/.claude/hooks/fact-check-absolutes.js +185 -0
  47. package/install/templates/.claude/hooks/fd-phase-guard.js +87 -0
  48. package/install/templates/.claude/hooks/fd-response-check.js +92 -0
  49. package/install/templates/.claude/hooks/lib/failure-detector.js +14 -0
  50. package/install/templates/.claude/hooks/pre-compact-save.js +148 -0
  51. package/install/templates/.claude/hooks/stage-to-byan.js +119 -0
  52. package/install/templates/.claude/hooks/tool-failure-guard.js +6 -0
  53. package/install/templates/.claude/hooks/tool-transparency.js +4 -0
  54. package/install/templates/.claude/settings.json +27 -0
  55. package/install/templates/.claude/skills/byan-byan/SKILL.md +115 -163
  56. package/install/templates/.claude/skills/byan-orchestrate/SKILL.md +100 -0
  57. package/install/templates/.githooks/pre-commit +75 -0
  58. package/install/templates/.github/extensions/byan-staging/extension.mjs +169 -0
  59. package/install/templates/.github/extensions/byan-staging/package.json +8 -0
  60. package/install/templates/_byan/mcp/byan-mcp-server/lib/copilot.js +148 -0
  61. package/install/templates/_byan/mcp/byan-mcp-server/lib/fd-state.js +163 -0
  62. package/install/templates/_byan/mcp/byan-mcp-server/lib/kanban.js +226 -0
  63. package/install/templates/_byan/mcp/byan-mcp-server/lib/peer-review.js +187 -0
  64. package/install/templates/_byan/mcp/byan-mcp-server/server.js +463 -0
  65. package/install/templates/detector.js +154 -0
  66. package/package.json +6 -7
  67. package/src/loadbalancer/capability-matrix.js +157 -0
  68. package/src/loadbalancer/config.js +141 -0
  69. package/src/loadbalancer/graceful-degradation.js +212 -0
  70. package/src/loadbalancer/health-probe.js +151 -0
  71. package/src/loadbalancer/hooks/claude-hooks.js +53 -0
  72. package/src/loadbalancer/hooks/copilot-hooks.js +74 -0
  73. package/src/loadbalancer/index.js +81 -0
  74. package/src/loadbalancer/loadbalancer.default.yaml +65 -0
  75. package/src/loadbalancer/loadbalancer.js +324 -0
  76. package/src/loadbalancer/mcp-server.js +304 -0
  77. package/src/loadbalancer/metrics.js +146 -0
  78. package/src/loadbalancer/native/claude-integration.js +64 -0
  79. package/src/loadbalancer/native/copilot-integration.js +59 -0
  80. package/src/loadbalancer/pressure-score.js +102 -0
  81. package/src/loadbalancer/providers/base-provider.js +80 -0
  82. package/src/loadbalancer/providers/byan-api-provider.js +132 -0
  83. package/src/loadbalancer/providers/claude-provider.js +113 -0
  84. package/src/loadbalancer/providers/copilot-provider.js +104 -0
  85. package/src/loadbalancer/rate-limit-tracker.js +216 -0
  86. package/src/loadbalancer/session-bridge.js +179 -0
  87. package/src/loadbalancer/state/db.js +211 -0
  88. package/src/loadbalancer/state/migrations/001-initial.sql +50 -0
  89. package/src/loadbalancer/tools/index.js +123 -0
  90. package/src/loadbalancer/velocity-estimator.js +147 -0
  91. package/src/staging/staging.js +394 -0
  92. package/update-byan-agent/bin/update-byan-agent.js +27 -2
  93. package/API-BYAN-V2.md +0 -741
  94. package/BMAD-QUICK-REFERENCE.md +0 -370
  95. package/CHANGELOG-v2.1.0.md +0 -371
  96. package/MIGRATION-v2.0-to-v2.1.md +0 -430
  97. package/README-BYAN-V2.md +0 -446
  98. package/TEST-GUIDE-v2.3.2.md +0 -161
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Installer step — BYAN memory-sync opt-in + consent (SM5).
3
+ *
4
+ * Shown during create-byan-agent. Only prompts when the user already
5
+ * provided a byan_web URL + token (via setupByanWebIntegration), since
6
+ * memory-sync without credentials is a no-op.
7
+ *
8
+ * On opt-in, writes :
9
+ * _byan/config.yaml → memory_sync: { enabled: true }
10
+ * OR loadbalancer.yaml if _byan/config.yaml not present
11
+ *
12
+ * Prints a clear consent notice listing what gets sent and how to
13
+ * disable. The user must type "oui" / "yes" to enable — no default to
14
+ * true.
15
+ */
16
+
17
+ const fs = require('fs-extra');
18
+ const path = require('path');
19
+ const yaml = require('js-yaml');
20
+ const inquirer = require('inquirer');
21
+ const chalk = require('chalk');
22
+
23
+ const CONSENT_NOTICE = [
24
+ '',
25
+ chalk.yellow.bold('BYAN memory-sync — consent requis'),
26
+ '',
27
+ 'Si vous activez cette option, apres chaque interaction avec',
28
+ 'Claude Code ou Copilot CLI, BYAN envoie automatiquement a votre',
29
+ 'instance byan_web les elements suivants :',
30
+ '',
31
+ ' - messages utilisateur (prompts)',
32
+ ' - reponses assistant',
33
+ ' - chemins de fichiers modifies',
34
+ ' - sessionId et timestamp',
35
+ '',
36
+ 'Filtrage applique AVANT envoi :',
37
+ ' - chit-chat (moins de 50 caracteres) -> ignore',
38
+ ' - doublons (hash SHA256 du contenu) -> ignore',
39
+ ' - categories : fact | decision | blocker | artifact',
40
+ '',
41
+ 'Les donnees sont stockees dans VOTRE instance byan_web',
42
+ '(pas de tierce partie). Le token JWT identifie l auteur.',
43
+ '',
44
+ chalk.cyan('Desactiver plus tard :'),
45
+ ' - editer _byan/config.yaml -> memory_sync: { enabled: false }',
46
+ ' - OU invoquer le skill -> /byan-no-stage pour un turn',
47
+ '',
48
+ ].join('\n');
49
+
50
+ async function promptConsent({ skipPrompts, defaultAnswer } = {}) {
51
+ if (skipPrompts) return { enabled: defaultAnswer === true };
52
+
53
+ console.log(CONSENT_NOTICE);
54
+
55
+ const { enable } = await inquirer.prompt([
56
+ {
57
+ type: 'confirm',
58
+ name: 'enable',
59
+ message:
60
+ 'Activer la synchronisation automatique de vos conversations vers byan_web ?',
61
+ default: false,
62
+ },
63
+ ]);
64
+
65
+ return { enabled: enable };
66
+ }
67
+
68
+ function configPaths(projectRoot) {
69
+ return {
70
+ byanConfig: path.join(projectRoot, '_byan', 'config.yaml'),
71
+ lbConfig: path.join(projectRoot, 'loadbalancer.yaml'),
72
+ };
73
+ }
74
+
75
+ async function writeMemorySyncFlag(projectRoot, enabled) {
76
+ const { byanConfig, lbConfig } = configPaths(projectRoot);
77
+
78
+ // Prefer _byan/config.yaml (BYAN primary config). Fall back to
79
+ // loadbalancer.yaml only if _byan/config.yaml is missing AND
80
+ // loadbalancer.yaml already exists.
81
+ let target;
82
+ if (await fs.pathExists(byanConfig)) {
83
+ target = byanConfig;
84
+ } else if (await fs.pathExists(lbConfig)) {
85
+ target = lbConfig;
86
+ } else {
87
+ target = byanConfig;
88
+ }
89
+ await fs.ensureDir(path.dirname(target));
90
+
91
+ let doc = {};
92
+ if (await fs.pathExists(target)) {
93
+ try {
94
+ doc = yaml.load(await fs.readFile(target, 'utf8')) || {};
95
+ } catch {
96
+ doc = {};
97
+ }
98
+ }
99
+ doc.memory_sync = { ...(doc.memory_sync || {}), enabled: enabled === true };
100
+
101
+ await fs.writeFile(target, yaml.dump(doc), 'utf8');
102
+ return target;
103
+ }
104
+
105
+ async function setupStagingConsent(projectRoot, options = {}) {
106
+ const tokenPresent = options.byanWebConfigured === true;
107
+ if (!tokenPresent) {
108
+ if (!options.quiet) {
109
+ console.log(
110
+ chalk.gray(' i memory-sync skipped (no byan_web token configured)')
111
+ );
112
+ }
113
+ return { configured: false, reason: 'no_token' };
114
+ }
115
+
116
+ const { enabled } = await promptConsent({
117
+ skipPrompts: options.skipPrompts === true,
118
+ defaultAnswer: options.presetEnabled === true,
119
+ });
120
+
121
+ const target = await writeMemorySyncFlag(projectRoot, enabled);
122
+
123
+ if (!options.quiet) {
124
+ if (enabled) {
125
+ console.log(chalk.green(' OK memory-sync ENABLED in ' + path.relative(projectRoot, target)));
126
+ console.log(
127
+ chalk.gray(
128
+ ' a chaque fin de turn, votre hook Stop (Claude) et votre'
129
+ )
130
+ );
131
+ console.log(
132
+ chalk.gray(
133
+ ' extension Copilot staging enverront les memoires a byan_web.'
134
+ )
135
+ );
136
+ } else {
137
+ console.log(chalk.gray(' i memory-sync left DISABLED (opt-in declined)'));
138
+ }
139
+ }
140
+
141
+ return { configured: true, enabled, configPath: target };
142
+ }
143
+
144
+ module.exports = {
145
+ setupStagingConsent,
146
+ writeMemorySyncFlag,
147
+ promptConsent,
148
+ CONSENT_NOTICE,
149
+ };
@@ -0,0 +1,208 @@
1
+ /**
2
+ * BMAD subagent generator (AW2).
3
+ *
4
+ * Reads a BYAN Copilot agent stub (.github/agents/<name>.md — YAML
5
+ * frontmatter + XML persona block) and emits a Claude Code native
6
+ * subagent file at .claude/agents/bmad-<slug>.md with :
7
+ * - frontmatter : name, description, model, color
8
+ * - body : system prompt derived from <persona> + <rules> + <menu>
9
+ *
10
+ * The resulting file makes the BMAD agent available as a
11
+ * subagent_type in Claude Code's Agent tool after a session restart.
12
+ * Model assignment is driven by ROLE_MODEL_MAP (token-optimal defaults).
13
+ */
14
+
15
+ const path = require('path');
16
+ const fs = require('fs');
17
+
18
+ const ROLE_MODEL_MAP = {
19
+ // Reasoning-heavy → opus
20
+ architect: 'opus',
21
+ 'bmm-architect': 'opus',
22
+ tea: 'opus',
23
+ 'tea-tea': 'opus',
24
+ quinn: 'opus',
25
+ 'bmm-quinn': 'opus',
26
+ 'bmad-master': 'opus',
27
+ byan: 'opus',
28
+ 'creative-problem-solver': 'opus',
29
+ 'cis-creative-problem-solver': 'opus',
30
+
31
+ // Narrow mechanical → haiku
32
+ carmack: 'haiku',
33
+ rachid: 'haiku',
34
+ marc: 'haiku',
35
+ patnote: 'haiku',
36
+ drawio: 'haiku',
37
+ };
38
+
39
+ const ROLE_COLOR_MAP = {
40
+ opus: 'purple',
41
+ sonnet: 'blue',
42
+ haiku: 'cyan',
43
+ };
44
+
45
+ function normalizeName(stubFilename) {
46
+ return stubFilename
47
+ .replace(/^bmad-agent-/, '')
48
+ .replace(/\.md$/, '');
49
+ }
50
+
51
+ function pickModel(name) {
52
+ return ROLE_MODEL_MAP[name] || 'sonnet';
53
+ }
54
+
55
+ function parseFrontmatter(raw) {
56
+ const m = raw.match(/^---\s*\n([\s\S]*?)\n---\s*\n/);
57
+ if (!m) return {};
58
+ const out = {};
59
+ for (const line of m[1].split('\n')) {
60
+ const kv = line.match(/^(\w[\w-]*)\s*:\s*(.*)$/);
61
+ if (!kv) continue;
62
+ let v = kv[2].trim();
63
+ if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'"))) {
64
+ v = v.slice(1, -1);
65
+ }
66
+ out[kv[1]] = v;
67
+ }
68
+ return out;
69
+ }
70
+
71
+ function extractXmlBlock(raw) {
72
+ const m = raw.match(/```xml\s*\n([\s\S]*?)\n```/);
73
+ return m ? m[1] : '';
74
+ }
75
+
76
+ function extractSection(xml, tag) {
77
+ const re = new RegExp(`<${tag}(?:\\s+[^>]*)?>([\\s\\S]*?)<\\/${tag}>`);
78
+ const m = xml.match(re);
79
+ return m ? m[1].trim() : null;
80
+ }
81
+
82
+ function extractListItems(xml, parentTag, itemTag) {
83
+ const parent = extractSection(xml, parentTag);
84
+ if (!parent) return [];
85
+ const re = new RegExp(`<${itemTag}(?:\\s+[^>]*)?>([\\s\\S]*?)<\\/${itemTag}>`, 'g');
86
+ const items = [];
87
+ let m;
88
+ while ((m = re.exec(parent)) !== null) items.push(m[1].trim());
89
+ return items;
90
+ }
91
+
92
+ function cleanXmlTags(text) {
93
+ if (!text) return '';
94
+ return text
95
+ .replace(/<\/?[a-zA-Z_][\w-]*(\s+[^>]*)?>/g, '')
96
+ .replace(/\n{3,}/g, '\n\n')
97
+ .trim();
98
+ }
99
+
100
+ function buildBody({ fm, persona, rules, menu, capabilities }) {
101
+ const lines = [];
102
+ lines.push(`# ${fm.name}`);
103
+ lines.push('');
104
+ if (fm.description) {
105
+ lines.push(fm.description);
106
+ lines.push('');
107
+ }
108
+
109
+ if (persona) {
110
+ lines.push('## Persona');
111
+ lines.push('');
112
+ lines.push(cleanXmlTags(persona));
113
+ lines.push('');
114
+ }
115
+
116
+ if (rules && rules.length) {
117
+ lines.push('## Operating rules');
118
+ lines.push('');
119
+ for (const r of rules) lines.push(`- ${cleanXmlTags(r)}`);
120
+ lines.push('');
121
+ }
122
+
123
+ if (capabilities && capabilities.length) {
124
+ lines.push('## Capabilities');
125
+ lines.push('');
126
+ for (const c of capabilities) lines.push(`- ${cleanXmlTags(c)}`);
127
+ lines.push('');
128
+ }
129
+
130
+ if (menu && menu.length) {
131
+ lines.push('## Menu commands');
132
+ lines.push('');
133
+ for (const m of menu) lines.push(`- ${cleanXmlTags(m)}`);
134
+ lines.push('');
135
+ }
136
+
137
+ lines.push('## Reporting contract');
138
+ lines.push('');
139
+ lines.push(
140
+ 'When invoked via the Agent tool, stay in the persona above. Respond with a concise JSON report when the task completes : { status: "ok|partial|failed", summary: "<200 words", files_changed: [paths], next_steps: [] }.'
141
+ );
142
+
143
+ return lines.join('\n');
144
+ }
145
+
146
+ function generateSubagent(stubPath, options = {}) {
147
+ const raw = fs.readFileSync(stubPath, 'utf8');
148
+ const fm = parseFrontmatter(raw);
149
+ if (!fm.name) throw new Error(`stub missing name frontmatter: ${stubPath}`);
150
+
151
+ const baseName = options.name || normalizeName(path.basename(stubPath));
152
+ const xml = extractXmlBlock(raw);
153
+
154
+ const persona = extractSection(xml, 'persona') || '';
155
+ const rules = extractListItems(xml, 'rules', 'r');
156
+ const menu = extractListItems(xml, 'menu', 'item');
157
+ const capabilities = extractListItems(xml, 'capabilities', 'cap');
158
+
159
+ const subagentName = options.subagentName || `bmad-${baseName}`;
160
+ const model = options.model || pickModel(baseName);
161
+ const color = options.color || ROLE_COLOR_MAP[model] || 'blue';
162
+
163
+ const description =
164
+ options.description ||
165
+ fm.description ||
166
+ `BMAD ${baseName} agent — invoke for ${baseName} role tasks`;
167
+
168
+ const body = buildBody({
169
+ fm: { ...fm, name: subagentName },
170
+ persona,
171
+ rules,
172
+ menu,
173
+ capabilities,
174
+ });
175
+
176
+ const content = `---\nname: ${subagentName}\ndescription: ${escapeYaml(description)}\nmodel: ${model}\ncolor: ${color}\n---\n\n${body}\n`;
177
+
178
+ const outPath =
179
+ options.outPath ||
180
+ path.join('.claude', 'agents', `${subagentName}.md`);
181
+
182
+ if (options.write !== false) {
183
+ fs.mkdirSync(path.dirname(outPath), { recursive: true });
184
+ fs.writeFileSync(outPath, content);
185
+ }
186
+
187
+ return { outPath, subagentName, model, color, content };
188
+ }
189
+
190
+ function escapeYaml(v) {
191
+ if (typeof v !== 'string') return '';
192
+ if (/[:#\n]/.test(v)) return JSON.stringify(v);
193
+ return v;
194
+ }
195
+
196
+ module.exports = {
197
+ ROLE_MODEL_MAP,
198
+ ROLE_COLOR_MAP,
199
+ normalizeName,
200
+ pickModel,
201
+ parseFrontmatter,
202
+ extractXmlBlock,
203
+ extractSection,
204
+ extractListItems,
205
+ cleanXmlTags,
206
+ buildBody,
207
+ generateSubagent,
208
+ };
@@ -0,0 +1,131 @@
1
+ /**
2
+ * BYAN token ledger.
3
+ *
4
+ * Reads _byan-output/tool-log.jsonl produced by the transparency hooks
5
+ * and aggregates per-session token estimates :
6
+ * - total tool calls
7
+ * - total est input + output tokens
8
+ * - per-tool breakdown (count + tokens)
9
+ * - failure summary
10
+ *
11
+ * Estimates are based on char/4 heuristic. Real token counts would need
12
+ * Anthropic's count_tokens API which requires authentication — skipped
13
+ * for now, good-enough for budget reasoning.
14
+ */
15
+
16
+ const fs = require('fs');
17
+ const path = require('path');
18
+
19
+ function readLog(logPath) {
20
+ if (!fs.existsSync(logPath)) return [];
21
+ return fs
22
+ .readFileSync(logPath, 'utf8')
23
+ .split('\n')
24
+ .filter(Boolean)
25
+ .map((line) => {
26
+ try {
27
+ return JSON.parse(line);
28
+ } catch {
29
+ return null;
30
+ }
31
+ })
32
+ .filter(Boolean);
33
+ }
34
+
35
+ function summarize(entries, { since } = {}) {
36
+ const cutoff = since ? Date.parse(since) : 0;
37
+ const filtered = entries.filter((e) => {
38
+ const t = Date.parse(e.timestamp);
39
+ return Number.isFinite(t) && t >= cutoff;
40
+ });
41
+
42
+ const stats = {
43
+ total_entries: filtered.length,
44
+ pre_count: 0,
45
+ post_count: 0,
46
+ est_input_tokens: 0,
47
+ est_output_tokens: 0,
48
+ by_tool: {},
49
+ failures: [],
50
+ };
51
+
52
+ for (const e of filtered) {
53
+ if (e.phase === 'pre') {
54
+ stats.pre_count += 1;
55
+ stats.est_input_tokens += Number(e.est_input_tokens || 0);
56
+ } else if (e.phase === 'post') {
57
+ stats.post_count += 1;
58
+ stats.est_output_tokens += Number(e.est_output_tokens || 0);
59
+ if (e.ok === false) {
60
+ stats.failures.push({
61
+ timestamp: e.timestamp,
62
+ tool: e.tool,
63
+ kind: e.failure_kind,
64
+ });
65
+ }
66
+ }
67
+
68
+ const tool = e.tool || 'unknown';
69
+ if (!stats.by_tool[tool]) {
70
+ stats.by_tool[tool] = { calls: 0, input: 0, output: 0, failures: 0 };
71
+ }
72
+ if (e.phase === 'pre') stats.by_tool[tool].calls += 1;
73
+ stats.by_tool[tool].input += Number(e.est_input_tokens || 0);
74
+ stats.by_tool[tool].output += Number(e.est_output_tokens || 0);
75
+ if (e.ok === false) stats.by_tool[tool].failures += 1;
76
+ }
77
+
78
+ stats.est_total_tokens = stats.est_input_tokens + stats.est_output_tokens;
79
+ return stats;
80
+ }
81
+
82
+ function renderReport(stats) {
83
+ const lines = [];
84
+ lines.push('BYAN token ledger');
85
+ lines.push('');
86
+ lines.push(` Tool calls : ${stats.pre_count}`);
87
+ lines.push(` Completed : ${stats.post_count}`);
88
+ lines.push(` Est. input tokens : ${stats.est_input_tokens.toLocaleString()}`);
89
+ lines.push(` Est. output tokens : ${stats.est_output_tokens.toLocaleString()}`);
90
+ lines.push(` Est. total tokens : ${stats.est_total_tokens.toLocaleString()}`);
91
+ lines.push(` Failures : ${stats.failures.length}`);
92
+
93
+ lines.push('');
94
+ lines.push(' Per tool :');
95
+ const rows = Object.entries(stats.by_tool).sort(
96
+ (a, b) => b[1].input + b[1].output - (a[1].input + a[1].output)
97
+ );
98
+ for (const [tool, s] of rows) {
99
+ const total = s.input + s.output;
100
+ const failNote = s.failures > 0 ? ` [${s.failures} fail]` : '';
101
+ lines.push(
102
+ ` ${tool.padEnd(16)} calls=${String(s.calls).padStart(3)} in=${s.input
103
+ .toString()
104
+ .padStart(6)} out=${s.output.toString().padStart(6)} total=${total
105
+ .toString()
106
+ .padStart(7)}${failNote}`
107
+ );
108
+ }
109
+
110
+ if (stats.failures.length > 0) {
111
+ lines.push('');
112
+ lines.push(' Recent failures :');
113
+ for (const f of stats.failures.slice(-5)) {
114
+ lines.push(` ${f.timestamp} ${f.tool} (${f.kind})`);
115
+ }
116
+ }
117
+
118
+ return lines.join('\n');
119
+ }
120
+
121
+ function defaultLogPath(projectRoot) {
122
+ const root = projectRoot || process.env.CLAUDE_PROJECT_DIR || process.cwd();
123
+ return path.join(root, '_byan-output', 'tool-log.jsonl');
124
+ }
125
+
126
+ module.exports = {
127
+ readLog,
128
+ summarize,
129
+ renderReport,
130
+ defaultLogPath,
131
+ };
@@ -0,0 +1,14 @@
1
+ ---
2
+ name: bmad-bmad-master
3
+ description: bmad-master agent
4
+ model: opus
5
+ color: purple
6
+ ---
7
+
8
+ # bmad-bmad-master
9
+
10
+ bmad-master agent
11
+
12
+ ## Reporting contract
13
+
14
+ When invoked via the Agent tool, stay in the persona above. Respond with a concise JSON report when the task completes : { status: "ok|partial|failed", summary: "<200 words", files_changed: [paths], next_steps: [] }.
@@ -0,0 +1,14 @@
1
+ ---
2
+ name: bmad-bmb-agent-builder
3
+ description: agent-builder agent
4
+ model: sonnet
5
+ color: blue
6
+ ---
7
+
8
+ # bmad-bmb-agent-builder
9
+
10
+ agent-builder agent
11
+
12
+ ## Reporting contract
13
+
14
+ When invoked via the Agent tool, stay in the persona above. Respond with a concise JSON report when the task completes : { status: "ok|partial|failed", summary: "<200 words", files_changed: [paths], next_steps: [] }.
@@ -0,0 +1,14 @@
1
+ ---
2
+ name: bmad-bmb-module-builder
3
+ description: module-builder agent
4
+ model: sonnet
5
+ color: blue
6
+ ---
7
+
8
+ # bmad-bmb-module-builder
9
+
10
+ module-builder agent
11
+
12
+ ## Reporting contract
13
+
14
+ When invoked via the Agent tool, stay in the persona above. Respond with a concise JSON report when the task completes : { status: "ok|partial|failed", summary: "<200 words", files_changed: [paths], next_steps: [] }.
@@ -0,0 +1,14 @@
1
+ ---
2
+ name: bmad-bmb-workflow-builder
3
+ description: workflow-builder agent
4
+ model: sonnet
5
+ color: blue
6
+ ---
7
+
8
+ # bmad-bmb-workflow-builder
9
+
10
+ workflow-builder agent
11
+
12
+ ## Reporting contract
13
+
14
+ When invoked via the Agent tool, stay in the persona above. Respond with a concise JSON report when the task completes : { status: "ok|partial|failed", summary: "<200 words", files_changed: [paths], next_steps: [] }.
@@ -0,0 +1,14 @@
1
+ ---
2
+ name: bmad-bmm-analyst
3
+ description: analyst agent
4
+ model: sonnet
5
+ color: blue
6
+ ---
7
+
8
+ # bmad-bmm-analyst
9
+
10
+ analyst agent
11
+
12
+ ## Reporting contract
13
+
14
+ When invoked via the Agent tool, stay in the persona above. Respond with a concise JSON report when the task completes : { status: "ok|partial|failed", summary: "<200 words", files_changed: [paths], next_steps: [] }.
@@ -0,0 +1,14 @@
1
+ ---
2
+ name: bmad-bmm-architect
3
+ description: architect agent
4
+ model: opus
5
+ color: purple
6
+ ---
7
+
8
+ # bmad-bmm-architect
9
+
10
+ architect agent
11
+
12
+ ## Reporting contract
13
+
14
+ When invoked via the Agent tool, stay in the persona above. Respond with a concise JSON report when the task completes : { status: "ok|partial|failed", summary: "<200 words", files_changed: [paths], next_steps: [] }.
@@ -0,0 +1,14 @@
1
+ ---
2
+ name: bmad-bmm-dev
3
+ description: dev agent
4
+ model: sonnet
5
+ color: blue
6
+ ---
7
+
8
+ # bmad-bmm-dev
9
+
10
+ dev agent
11
+
12
+ ## Reporting contract
13
+
14
+ When invoked via the Agent tool, stay in the persona above. Respond with a concise JSON report when the task completes : { status: "ok|partial|failed", summary: "<200 words", files_changed: [paths], next_steps: [] }.
@@ -0,0 +1,14 @@
1
+ ---
2
+ name: bmad-bmm-pm
3
+ description: pm agent
4
+ model: sonnet
5
+ color: blue
6
+ ---
7
+
8
+ # bmad-bmm-pm
9
+
10
+ pm agent
11
+
12
+ ## Reporting contract
13
+
14
+ When invoked via the Agent tool, stay in the persona above. Respond with a concise JSON report when the task completes : { status: "ok|partial|failed", summary: "<200 words", files_changed: [paths], next_steps: [] }.
@@ -0,0 +1,14 @@
1
+ ---
2
+ name: bmad-bmm-quick-flow-solo-dev
3
+ description: quick-flow-solo-dev agent
4
+ model: sonnet
5
+ color: blue
6
+ ---
7
+
8
+ # bmad-bmm-quick-flow-solo-dev
9
+
10
+ quick-flow-solo-dev agent
11
+
12
+ ## Reporting contract
13
+
14
+ When invoked via the Agent tool, stay in the persona above. Respond with a concise JSON report when the task completes : { status: "ok|partial|failed", summary: "<200 words", files_changed: [paths], next_steps: [] }.
@@ -0,0 +1,14 @@
1
+ ---
2
+ name: bmad-bmm-quinn
3
+ description: quinn agent
4
+ model: opus
5
+ color: purple
6
+ ---
7
+
8
+ # bmad-bmm-quinn
9
+
10
+ quinn agent
11
+
12
+ ## Reporting contract
13
+
14
+ When invoked via the Agent tool, stay in the persona above. Respond with a concise JSON report when the task completes : { status: "ok|partial|failed", summary: "<200 words", files_changed: [paths], next_steps: [] }.
@@ -0,0 +1,14 @@
1
+ ---
2
+ name: bmad-bmm-sm
3
+ description: sm agent
4
+ model: sonnet
5
+ color: blue
6
+ ---
7
+
8
+ # bmad-bmm-sm
9
+
10
+ sm agent
11
+
12
+ ## Reporting contract
13
+
14
+ When invoked via the Agent tool, stay in the persona above. Respond with a concise JSON report when the task completes : { status: "ok|partial|failed", summary: "<200 words", files_changed: [paths], next_steps: [] }.
@@ -0,0 +1,14 @@
1
+ ---
2
+ name: bmad-bmm-tech-writer
3
+ description: tech-writer agent
4
+ model: sonnet
5
+ color: blue
6
+ ---
7
+
8
+ # bmad-bmm-tech-writer
9
+
10
+ tech-writer agent
11
+
12
+ ## Reporting contract
13
+
14
+ When invoked via the Agent tool, stay in the persona above. Respond with a concise JSON report when the task completes : { status: "ok|partial|failed", summary: "<200 words", files_changed: [paths], next_steps: [] }.
@@ -0,0 +1,14 @@
1
+ ---
2
+ name: bmad-bmm-ux-designer
3
+ description: ux-designer agent
4
+ model: sonnet
5
+ color: blue
6
+ ---
7
+
8
+ # bmad-bmm-ux-designer
9
+
10
+ ux-designer agent
11
+
12
+ ## Reporting contract
13
+
14
+ When invoked via the Agent tool, stay in the persona above. Respond with a concise JSON report when the task completes : { status: "ok|partial|failed", summary: "<200 words", files_changed: [paths], next_steps: [] }.