create-byan-agent 2.42.0 → 2.45.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/CHANGELOG.md +118 -0
  2. package/install/bin/byan-kanban.js +1 -1
  3. package/install/bin/create-byan-agent-v2.js +111 -111
  4. package/install/lib/byan-leantime-integration.js +2 -2
  5. package/install/lib/byan-web-integration.js +5 -5
  6. package/install/lib/claude-native-setup.js +11 -11
  7. package/install/lib/codex-native-setup.js +3 -3
  8. package/install/lib/exchange/agent-packager.js +1 -1
  9. package/install/lib/mcp-extensions/gdrive.js +5 -5
  10. package/install/lib/mcp-extensions/index.js +5 -5
  11. package/install/lib/phase2-chat.js +21 -21
  12. package/install/lib/platforms/claude-code.js +2 -2
  13. package/install/lib/project-agents-generator.js +4 -4
  14. package/install/lib/staging-consent.js +1 -1
  15. package/install/lib/subagent-generator.js +2 -3
  16. package/install/lib/utils/logger.js +4 -4
  17. package/install/lib/utils/node-detector.js +1 -1
  18. package/install/lib/yanstaller/agent-launcher.js +1 -1
  19. package/install/lib/yanstaller/index.js +11 -116
  20. package/install/lib/yanstaller/platform-selector.js +11 -11
  21. package/install/src/byan-v2/dispatcher/task-router.js +1 -1
  22. package/install/src/index.js +1 -1
  23. package/install/src/webui/api.js +0 -13
  24. package/install/src/webui/chat/bridge.js +1 -1
  25. package/install/src/webui/chat/cli-detector.js +1 -1
  26. package/install/templates/.claude/CLAUDE.md +1 -0
  27. package/install/templates/.claude/hooks/inject-voice-anchor.js +20 -1
  28. package/install/templates/.claude/hooks/lib/plain-language.js +155 -0
  29. package/install/templates/.claude/hooks/plain-language-check.js +53 -0
  30. package/install/templates/.claude/rules/native-workflows.md +22 -14
  31. package/install/templates/.claude/rules/plain-language.md +73 -0
  32. package/install/templates/.claude/settings.json +4 -0
  33. package/install/templates/_byan/COMPLETION-REPORT.md +0 -1
  34. package/install/templates/_byan/agent/byan/byan-tao.md +12 -0
  35. package/install/templates/_byan/core/activation/soul-activation.md +3 -3
  36. package/install/templates/_byan/mcp/byan-mcp-server/bin/byan-lint-workflows.js +6 -3
  37. package/install/templates/_byan/mcp/byan-mcp-server/lib/native-tiers.js +35 -17
  38. package/install/templates/_byan/mcp/byan-mcp-server/lib/workflows-lint.js +38 -0
  39. package/install/templates/_byan/mcp/byan-mcp-server/server.js +37 -0
  40. package/install/templates/_byan/worker/workers.md +1 -1
  41. package/install/templates/_byan/workflow/simple/byan/data/mantras.yaml +15 -4
  42. package/package.json +1 -1
  43. package/src/byan-v2/dispatcher/task-router.js +1 -1
  44. package/src/byan-v2/lib/layout-resolver.js +3 -6
  45. package/install/lib/yanstaller/installer.js +0 -138
  46. package/install/lib/yanstaller/interviewer.js +0 -88
  47. package/install/lib/yanstaller/recommender.js +0 -102
  48. package/install/lib/yanstaller/troubleshooter.js +0 -89
  49. package/install/lib/yanstaller/validator.js +0 -198
  50. package/install/lib/yanstaller/wizard.js +0 -109
  51. package/install/src/byan-v2/dispatcher/task-tool-interface-mock.js +0 -134
  52. package/install/src/byan-v2/dispatcher/task-tool-interface.js +0 -123
  53. package/install/templates/_bmad/bmb/agents/agent-builder.md +0 -59
  54. package/install/templates/_bmad/bmb/agents/byan-test.md +0 -116
  55. package/install/templates/_bmad/bmb/agents/byan.md +0 -215
  56. package/install/templates/_bmad/bmb/agents/marc.md +0 -303
  57. package/install/templates/_bmad/bmb/agents/module-builder.md +0 -60
  58. package/install/templates/_bmad/bmb/agents/patnote.md +0 -495
  59. package/install/templates/_bmad/bmb/agents/rachid.md +0 -184
  60. package/install/templates/_bmad/bmb/agents/workflow-builder.md +0 -61
  61. package/install/templates/_bmad/bmb/workflows/byan/data/mantras.yaml +0 -272
  62. package/install/templates/_bmad/bmb/workflows/byan/data/templates.yaml +0 -59
  63. package/install/templates/_bmad/bmb/workflows/byan/delete-agent-workflow.md +0 -657
  64. package/install/templates/_bmad/bmb/workflows/byan/edit-agent-workflow.md +0 -688
  65. package/install/templates/_bmad/bmb/workflows/byan/interview-workflow.md +0 -753
  66. package/install/templates/_bmad/bmb/workflows/byan/quick-create-workflow.md +0 -450
  67. package/install/templates/_bmad/bmb/workflows/byan/templates/base-agent-template.md +0 -79
  68. package/install/templates/_bmad/bmb/workflows/byan/validate-agent-workflow.md +0 -676
  69. package/install/templates/_bmad/core/agents/carmack.md +0 -238
  70. package/src/byan-v2/dispatcher/task-tool-interface-mock.js +0 -134
  71. package/src/byan-v2/dispatcher/task-tool-interface.js +0 -123
@@ -33,13 +33,13 @@ const PLATFORM_INFO = {
33
33
  displayName: 'Claude Code',
34
34
  native: true,
35
35
  specialist: 'claude',
36
- icon: '🎭'
36
+ icon: ''
37
37
  },
38
38
  'codex': {
39
39
  displayName: 'OpenCode/Codex',
40
40
  native: true, // NOW NATIVE!
41
41
  specialist: 'codex',
42
- icon: '📝'
42
+ icon: ''
43
43
  }
44
44
  };
45
45
 
@@ -50,7 +50,7 @@ const PLATFORM_INFO = {
50
50
  * @returns {Promise<PlatformSelectionResult>}
51
51
  */
52
52
  async function select(detectionResult) {
53
- logger.info('\n🎯 Platform Selection\n');
53
+ logger.info('\nPlatform Selection\n');
54
54
 
55
55
  // Build platform choices from detection
56
56
  const choices = buildChoices(detectionResult.platforms);
@@ -75,7 +75,7 @@ async function select(detectionResult) {
75
75
  {
76
76
  type: 'list',
77
77
  name: 'primary',
78
- message: '🎯 Choose your PRIMARY platform for native agent invocation:',
78
+ message: 'Choose your PRIMARY platform for native agent invocation:',
79
79
  choices: [
80
80
  ...nativePlatforms.map(c => ({
81
81
  name: `${c.icon} ${c.name} - ${c.agentSpecialist ? `@bmad-agent-${c.agentSpecialist}` : 'No specialist'}`,
@@ -84,11 +84,11 @@ async function select(detectionResult) {
84
84
  })),
85
85
  new inquirer.Separator(),
86
86
  {
87
- name: '🔧 Advanced: Install on multiple platforms',
87
+ name: 'Advanced: Install on multiple platforms',
88
88
  value: 'multi'
89
89
  },
90
90
  {
91
- name: '⏭️ Skip native integration (manual install only)',
91
+ name: 'Skip native integration (manual install only)',
92
92
  value: 'skip'
93
93
  }
94
94
  ]
@@ -125,7 +125,7 @@ async function select(detectionResult) {
125
125
  message: 'Choose installation target:',
126
126
  choices: [
127
127
  {
128
- name: `🚀 Auto (detect & install all available) - ${choices.length} platform(s)`,
128
+ name: `Auto (detect & install all available) - ${choices.length} platform(s)`,
129
129
  value: 'auto'
130
130
  },
131
131
  ...choices.map(c => ({
@@ -133,7 +133,7 @@ async function select(detectionResult) {
133
133
  value: `single:${c.id}`
134
134
  })),
135
135
  {
136
- name: '🔧 Custom (select multiple)',
136
+ name: 'Custom (select multiple)',
137
137
  value: 'custom'
138
138
  }
139
139
  ]
@@ -167,7 +167,7 @@ function buildChoices(detectedPlatforms) {
167
167
  displayName: p.name,
168
168
  native: false,
169
169
  specialist: null,
170
- icon: ''
170
+ icon: ''
171
171
  };
172
172
 
173
173
  return {
@@ -189,8 +189,8 @@ function buildChoices(detectedPlatforms) {
189
189
  * @returns {string}
190
190
  */
191
191
  function formatPlatformChoice(choice) {
192
- const nativeBadge = choice.native ? 'Native' : '💬 Conversational';
193
- const statusBadge = choice.detected ? '' : '';
192
+ const nativeBadge = choice.native ? 'Native' : 'Conversational';
193
+ const statusBadge = choice.detected ? '[OK]' : '[x]';
194
194
 
195
195
  return `${choice.icon} ${choice.name} (${nativeBadge}) ${statusBadge}`;
196
196
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * TaskRouter - Routes tasks based on complexity
3
- * Integrates ComplexityScorer + TaskToolInterface
3
+ * Integrates ComplexityScorer
4
4
  *
5
5
  * Thresholds:
6
6
  * - < 30: task-tool only
@@ -59,7 +59,7 @@ class Dashboard {
59
59
  *
60
60
  * @example
61
61
  * const byan = createByanInstance({ workerCount: 2 });
62
- * await byan.executeWorkflow('_bmad/workflows/create-prd/workflow.yaml');
62
+ * await byan.executeWorkflow('_byan/workflows/create-prd/workflow.yaml');
63
63
  */
64
64
  function createByanInstance(options = {}) {
65
65
  const {
@@ -165,19 +165,6 @@ const routes = {
165
165
  await sleep(300);
166
166
  }
167
167
 
168
- if (yanstaller) {
169
- try {
170
- await yanstaller.install({
171
- mode: config.mode || 'full',
172
- platforms: config.platforms,
173
- yes: true,
174
- projectRoot
175
- });
176
- } catch (err) {
177
- server.broadcastLog('warn', `Yanstaller: ${err.message} (continuing with stub install)`);
178
- }
179
- }
180
-
181
168
  ensureDirectoryStructure(projectRoot);
182
169
  writeBaseConfig(projectRoot, config);
183
170
 
@@ -26,7 +26,7 @@ class Bridge {
26
26
  if (!agentName) return null;
27
27
 
28
28
  // Layout resolver: Gen3 _byan/agent/<name>/ first, Gen2 flat +
29
- // per-module, Gen1 _bmad/ fallback.
29
+ // per-module.
30
30
  const hit = layoutResolver.resolveAgent(agentName, { projectRoot: this.projectRoot });
31
31
  return hit ? hit.path : null;
32
32
  }
@@ -96,7 +96,7 @@ function parseFrontmatter(content) {
96
96
  // Map a resolver layout (+ rel) to the legacy source label, so API responses
97
97
  // stay stable: Gen2 flat / Gen3 -> 'byan', Gen2 per-module -> 'bmad-<mod>'.
98
98
  function sourceForLayout(layout, rel) {
99
- if (layout === 'gen2-module' || layout === 'gen1') {
99
+ if (layout === 'gen2-module') {
100
100
  const mod = String(rel).split('/')[1];
101
101
  return `bmad-${mod}`;
102
102
  }
@@ -33,6 +33,7 @@ Voir @.claude/rules/hermes-dispatcher.md pour les commandes Hermes.
33
33
 
34
34
  - Pas d'emojis dans le code, commits, ou specs techniques (Mantra IA-23)
35
35
  - Code auto-documente, commentaires uniquement pour le POURQUOI (Mantra IA-24)
36
+ - Parler reel: francais clair et coherent avec l'utilisateur, zero jargon interne / anglais gratuit (Mantra IA-26, voir @.claude/rules/plain-language.md)
36
37
  - Format commits: `type: description` (feat, fix, docs, refactor, test, chore)
37
38
  - Simplicite d'abord - Rasoir d'Ockham (Mantra #37)
38
39
  - Challenge Before Confirm - Valider avant d'accepter (Mantra IA-16)
@@ -26,6 +26,7 @@
26
26
  const fs = require('fs');
27
27
  const path = require('path');
28
28
  const { buildTaoContext, turnCounterPath } = require('./inject-tao');
29
+ const pl = require('./lib/plain-language');
29
30
 
30
31
  const ANCHOR = [
31
32
  'Voix BYAN (rappel par tour ; tao complet chargé au démarrage de session) :',
@@ -33,6 +34,7 @@ const ANCHOR = [
33
34
  '- Challenge avant de confirmer ; questionne les absolus (Mantra IA-16).',
34
35
  '- Signatures : "Attends — pourquoi ?", "OK. On construit.", "Ça, c\'est du générique.".',
35
36
  '- Zéro emoji. Orienté solution : on cherche la meilleure option, pas le mur.',
37
+ '- Français réel et cohérent (Mantra IA-26) : pas d\'anglais gratuit (dis "redémarrer le conteneur", pas "cutoff"), pas de jargon interne brut, pas de métaphore collée de travers ("forger" un token).',
36
38
  ].join('\n');
37
39
 
38
40
  const DEFAULT_REFRESH_EVERY = 12;
@@ -77,6 +79,15 @@ function decideAnchor({ turn, every, fullTao }) {
77
79
  return { mode: 'anchor', additionalContext: ANCHOR };
78
80
  }
79
81
 
82
+ // Append a plain-language slip reminder (IA-25) to the injected context when the
83
+ // previous turn tripped the forward net. Pure so it is unit-testable; the fs read
84
+ // + clear stays in the require.main path below. A missing/empty hit list is a
85
+ // no-op, so this never changes the anchor on a clean turn.
86
+ function withSlipReminder(baseContext, hits) {
87
+ const reminder = pl.formatReminder(hits);
88
+ return reminder ? `${baseContext}\n${reminder}` : baseContext;
89
+ }
90
+
80
91
  if (require.main === module) {
81
92
  const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
82
93
  const every = refreshEvery();
@@ -84,9 +95,16 @@ if (require.main === module) {
84
95
  writeTurn(projectDir, turn);
85
96
  const fullTao = every > 0 && turn % every === 0 ? buildTaoContext(projectDir) : '';
86
97
  const { additionalContext } = decideAnchor({ turn, every, fullTao });
98
+ // Forward net: if the previous turn slipped into jargon, remind now and clear
99
+ // the flag (one-shot). Read/clear here, formatting stays pure in withSlipReminder.
100
+ const slipHits = pl.readSlip(projectDir);
101
+ if (slipHits && slipHits.length) pl.clearSlip(projectDir);
87
102
  process.stdout.write(
88
103
  JSON.stringify({
89
- hookSpecificOutput: { hookEventName: 'UserPromptSubmit', additionalContext },
104
+ hookSpecificOutput: {
105
+ hookEventName: 'UserPromptSubmit',
106
+ additionalContext: withSlipReminder(additionalContext, slipHits),
107
+ },
90
108
  })
91
109
  );
92
110
  }
@@ -98,5 +116,6 @@ module.exports = {
98
116
  readTurn,
99
117
  writeTurn,
100
118
  decideAnchor,
119
+ withSlipReminder,
101
120
  DEFAULT_REFRESH_EVERY,
102
121
  };
@@ -0,0 +1,155 @@
1
+ 'use strict';
2
+
3
+ // Plain-language core — the shared, testable heart of the "parler reel" guard
4
+ // (Mantra IA-26). Two jobs, no I/O beyond the slip flag:
5
+ //
6
+ // 1. scanText: find the KNOWN repeat-offender words in an assistant reply and
7
+ // return each with its plain-French replacement. This is the mechanical net
8
+ // for words we already know we misuse; the generative rule (the principle in
9
+ // .claude/rules/plain-language.md + the voice anchor) handles the long tail.
10
+ // 2. slip flag: a tiny file under _byan-output/ that the Stop hook writes when a
11
+ // reply slipped, and the next-turn voice anchor reads + clears to remind the
12
+ // agent. NO re-answer, NO blocking — the correction is carried FORWARD to the
13
+ // next turn, which is the whole point (avoid the costly regen loop).
14
+
15
+ const fs = require('fs');
16
+ const path = require('path');
17
+
18
+ // Known repeat-offenders -> plain French. Deliberately conservative: only terms
19
+ // with a clean everyday equivalent and a low false-positive risk once code is
20
+ // stripped. Common technical borrowings that have no French equivalent (commit,
21
+ // cache, token) are NOT here — the rule keeps them, explained once.
22
+ const OFFENDERS = Object.freeze([
23
+ { term: 'inline', good: 'directement (je le fais moi-meme)' },
24
+ { term: 'cutoff', good: "l'action reelle (ex: redemarrer, couper)" },
25
+ { term: 'housekeeping', good: 'rangement / menage du code' },
26
+ { term: 'downgrade', good: 'retrograder / baisser en gamme' },
27
+ { term: 'advisory', good: 'signalement non bloquant' },
28
+ { term: 'wrapper', good: 'enveloppe / surcouche' },
29
+ { term: 'fallback', good: 'repli / solution de secours' },
30
+ { term: 'throughput', good: 'debit' },
31
+ { term: 'overhead', good: 'surcout' },
32
+ { term: 'gate', good: 'point de controle / porte' },
33
+ { term: 'leaf', good: 'etape / tache' },
34
+ { term: 'tier', good: 'niveau / gamme' },
35
+ ]);
36
+
37
+ // A metaphor misuse, not a single word: "forger" applied to a token/jeton.
38
+ // Matched separately so the replacement can name the real verb.
39
+ const METAPHOR_OFFENDERS = Object.freeze([
40
+ {
41
+ id: 'forger-token',
42
+ re: /forg\w*\s+(?:un |une |des |le |les |la )?(?:token|jeton)/i,
43
+ label: 'forger un token',
44
+ good: 'generer / creer un token',
45
+ },
46
+ ]);
47
+
48
+ // A word boundary that treats accented letters as part of a word, so \btier\b
49
+ // style matching does NOT fire inside "metier", "chantier", "quartier"... The
50
+ // native \b is wrong here because JS \w excludes accented chars, splitting French
51
+ // words. This lookbehind/lookahead pair is the correct French-aware boundary.
52
+ const LEFT = '(?<![A-Za-zÀ-ÿ0-9_])';
53
+ const RIGHT = '(?![A-Za-zÀ-ÿ0-9_])';
54
+
55
+ function escapeRegExp(s) {
56
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
57
+ }
58
+
59
+ function termRegExp(term) {
60
+ return new RegExp(LEFT + escapeRegExp(term) + RIGHT, 'i');
61
+ }
62
+
63
+ // Remove fenced code blocks and inline `code` spans before scanning, so quoting a
64
+ // file, flag, or function literally named "tier" / "gate" does not trip the net.
65
+ // Prose is what we police; quoted code is not prose.
66
+ function stripCode(text) {
67
+ if (typeof text !== 'string' || !text) return '';
68
+ return text
69
+ .replace(/```[\s\S]*?```/g, ' ')
70
+ .replace(/`[^`]*`/g, ' ');
71
+ }
72
+
73
+ // Find every distinct offender present in the prose. Returns [{ bad, good }].
74
+ function scanText(text) {
75
+ const prose = stripCode(text);
76
+ if (!prose) return [];
77
+ const hits = [];
78
+ const seen = new Set();
79
+ for (const { term, good } of OFFENDERS) {
80
+ if (termRegExp(term).test(prose) && !seen.has(term)) {
81
+ seen.add(term);
82
+ hits.push({ bad: term, good });
83
+ }
84
+ }
85
+ for (const m of METAPHOR_OFFENDERS) {
86
+ if (m.re.test(prose) && !seen.has(m.id)) {
87
+ seen.add(m.id);
88
+ hits.push({ bad: m.label, good: m.good });
89
+ }
90
+ }
91
+ return hits;
92
+ }
93
+
94
+ // A short French reminder, for injection at the NEXT turn. Bounded to a few
95
+ // offenders so the note stays tiny even if a reply slipped many times.
96
+ function formatReminder(hits) {
97
+ if (!Array.isArray(hits) || hits.length === 0) return '';
98
+ const shown = hits.slice(0, 6)
99
+ .map((h) => `"${h.bad}" -> ${h.good}`)
100
+ .join(' ; ');
101
+ return [
102
+ 'Rappel langage (IA-26) : au dernier tour tu as glisse du jargon ou de',
103
+ `l'anglais gratuit -> ${shown}. Reformule en francais reel et coherent ce`,
104
+ 'tour-ci, sans refaire la reponse precedente.',
105
+ ].join(' ');
106
+ }
107
+
108
+ // The slip flag lives under _byan-output/ (gitignored), same family as the other
109
+ // hook sidecars. It is a transient one-turn signal, never source of truth.
110
+ function slipPath(projectDir) {
111
+ return path.join(projectDir, '_byan-output', '.jargon-slip.json');
112
+ }
113
+
114
+ function writeSlip(projectDir, hits) {
115
+ try {
116
+ const p = slipPath(projectDir);
117
+ fs.mkdirSync(path.dirname(p), { recursive: true });
118
+ fs.writeFileSync(p, JSON.stringify({ hits }));
119
+ return true;
120
+ } catch {
121
+ return false; // best-effort: a write failure must never block a turn
122
+ }
123
+ }
124
+
125
+ function readSlip(projectDir) {
126
+ try {
127
+ const raw = fs.readFileSync(slipPath(projectDir), 'utf8');
128
+ const parsed = JSON.parse(raw);
129
+ return Array.isArray(parsed.hits) ? parsed.hits : [];
130
+ } catch {
131
+ return null; // no flag (or unreadable) -> nothing to remind
132
+ }
133
+ }
134
+
135
+ function clearSlip(projectDir) {
136
+ try {
137
+ fs.rmSync(slipPath(projectDir), { force: true });
138
+ } catch {
139
+ // never block
140
+ }
141
+ }
142
+
143
+ module.exports = {
144
+ OFFENDERS,
145
+ METAPHOR_OFFENDERS,
146
+ escapeRegExp,
147
+ termRegExp,
148
+ stripCode,
149
+ scanText,
150
+ formatReminder,
151
+ slipPath,
152
+ writeSlip,
153
+ readSlip,
154
+ clearSlip,
155
+ };
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ // Stop hook — the FORWARD net for the "parler reel" rule (Mantra IA-26).
5
+ //
6
+ // It reads the assistant reply that just finished, looks for the known
7
+ // jargon/anglais repeat-offenders, and — if it finds any — writes a one-turn
8
+ // slip flag under _byan-output/. It NEVER blocks the turn: there is no re-answer,
9
+ // no regen. The next-turn voice anchor (inject-voice-anchor.js) reads the flag,
10
+ // reminds the agent in plain French, and clears it. Carrying the correction
11
+ // FORWARD is deliberate: a blocking guard would force a costly regen AND the user
12
+ // has already read the slip anyway (no pre-display hook exists).
13
+ //
14
+ // Always exits 0. A read/scan/write failure degrades to a no-op.
15
+
16
+ const fs = require('fs');
17
+ const { extractLastAssistantText } = require('./lib/transcript-read');
18
+ const pl = require('./lib/plain-language');
19
+
20
+ // Testable core: scan the finished reply, flag a slip if any. Returns the hits.
21
+ function detectAndFlag(payload, projectDir) {
22
+ const text = extractLastAssistantText(payload);
23
+ const hits = pl.scanText(text);
24
+ if (hits.length > 0) pl.writeSlip(projectDir, hits);
25
+ return hits;
26
+ }
27
+
28
+ function readStdin() {
29
+ try {
30
+ return fs.readFileSync(0, 'utf8');
31
+ } catch {
32
+ return '';
33
+ }
34
+ }
35
+
36
+ if (require.main === module) {
37
+ const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
38
+ let payload = {};
39
+ try {
40
+ const raw = readStdin();
41
+ if (raw && raw.trim()) payload = JSON.parse(raw);
42
+ } catch {
43
+ payload = {};
44
+ }
45
+ try {
46
+ detectAndFlag(payload, projectDir);
47
+ } catch {
48
+ // never block end-of-turn
49
+ }
50
+ process.stdout.write('{}');
51
+ }
52
+
53
+ module.exports = { detectAndFlag };
@@ -29,14 +29,20 @@ The tiering decision lives in one place — `_byan/mcp/byan-mcp-server/lib/nativ
29
29
  (tier vocabulary, leaf classifier, model map). Three tiers:
30
30
 
31
31
  - **cheap (`model: 'haiku'`)** — a pure EXPLORATION leaf (read/load/parse/detect).
32
- - **balanced (`model: 'sonnet'`)** — MECHANICAL verification, opt-in ONLY through
33
- the `mech-` label prefix (`mech-validate-json`): a binary, judgment-free check
34
- (JSON parses, schema matches, lint passes). Semantic/adversarial verification
35
- is NOT mechanical and stays deep. The prefix is an authoring declaration the
36
- linter holds the script to: a `mech-` leaf without `model: 'sonnet'` is a hard
37
- violation (`mechanical-without-model` / `mechanical-below-tier`).
38
- - **deep (OMIT `opts.model`)**implementation / verification / analysis leaves
39
- inherit the session model (no pin-up to opus).
32
+ - **balanced (`model: 'sonnet'`)** — two classes land here. (1) MECHANICAL
33
+ verification, opt-in ONLY through the `mech-` label prefix (`mech-validate-json`):
34
+ a binary, judgment-free check (JSON parses, schema matches, lint passes). (2)
35
+ ANALYSIS leaves (score/rank/assess/design/nfr/coverage/recommend/synthesize),
36
+ auto-routed by keyword: judgment-bearing but rarely frontier, so sonnet by
37
+ default. This deliberately OVERRIDES the session model for analysis even on an
38
+ Opus / high-effort session (that is the cost lever) — escape a frontier analysis
39
+ leaf with the `deep-` prefix. A `mech-` leaf without `model: 'sonnet'` is a hard violation; an analysis
40
+ leaf may carry `model: 'sonnet'` (its tier) but not `haiku` (`analysis-below-tier`).
41
+ Semantic/adversarial VERIFICATION is NOT analysis and stays deep.
42
+ - **deep (OMIT `opts.model`)** — implementation + verification leaves inherit the
43
+ session model (no pin-up to opus). A genuinely frontier ANALYSIS leaf opts back to
44
+ deep with the `deep-` label prefix (`deep-assess-architecture`), the mirror of the
45
+ `mech-` opt-in.
40
46
 
41
47
  The linter splits the two directions:
42
48
 
@@ -44,12 +50,14 @@ The linter splits the two directions:
44
50
  pin-up, or a half-applied `mech-` opt-in is a contract violation
45
51
  (`modelRoutingViolations` + `mechanicalLabelViolations`). This is the STRICT-2
46
52
  No Downgrade net.
47
- - **Ceiling (ADVISORY, non-blocking)** — an exploration-labelled leaf that runs
48
- deep is *reported* (`byan-lint-workflows.js --advise`), not forced. `classifyLeaf`
49
- is permissive: many exploration-labelled leaves legitimately stay deep because
50
- they bear a HALT/prerequisite gate, a classification, or an exact conversion
51
- consumed verbatim downstream. The human owns that per-leaf call. Forcing haiku
52
- on them would be the very downgrade the floor forbids.
53
+ - **Ceiling (ADVISORY, non-blocking)** — an exploration- OR analysis-labelled leaf
54
+ that runs deep is *reported* (`byan-lint-workflows.js --advise`:
55
+ `untiered-exploration` -> `model: 'haiku'`, `untiered-analysis` -> `model: 'sonnet'`),
56
+ not forced. `classifyLeaf` is permissive: many such leaves legitimately stay deep
57
+ because they bear a HALT/prerequisite gate, a classification, an exact conversion
58
+ consumed verbatim downstream, or genuinely frontier reasoning (`deep-` prefix). The
59
+ human owns that per-leaf call. Forcing a downgrade would be the very regression the
60
+ floor forbids.
53
61
 
54
62
  ## Ad-hoc scripts — the tier gate hook
55
63
 
@@ -0,0 +1,73 @@
1
+ # Parler Reel — Langage clair, cohérent, sans jargon (Mantra IA-26)
2
+
3
+ > L'utilisateur doit te comprendre sans dictionnaire. Un agent qui parle en
4
+ > jargon interne, en anglais gratuit ou en métaphore collée de travers force
5
+ > l'utilisateur à traduire — c'est du travail qu'on lui refile. Cette règle
6
+ > s'applique à TOUS les agents BYAN, comme "zéro emoji" (IA-23).
7
+
8
+ ## Le principe (ce qui fait le vrai travail)
9
+
10
+ Parle en français réel et cohérent — des mots qu'un humain dit vraiment,
11
+ technique ou pas.
12
+
13
+ - **Pas d'anglais quand le français existe.** "redémarrer le conteneur", pas
14
+ "faire un cutoff". "solution de secours", pas "fallback".
15
+ - **Pas de métaphore collée de travers.** On ne "forge" pas des tokens : on les
16
+ génère, on les crée.
17
+ - **Pas de jargon interne du projet balancé brut** (leaf, tier, downgrade, gate,
18
+ inline, advisory...) : dis ce que ça FAIT, en clair.
19
+ - **Un terme technique anglais sans équivalent** (commit, cache, token) : tu le
20
+ gardes, mais tu l'expliques une fois en clair à la première utilisation.
21
+ - **Test simple** : ton responsable technique doit tout comprendre sans
22
+ dictionnaire.
23
+
24
+ Le principe est génératif : il te fait CHOISIR le bon mot, y compris pour les
25
+ mots pourris qui ne sont pas dans la liste ci-dessous. La liste n'est qu'un
26
+ rappel des récidivistes connus.
27
+
28
+ ## La liste (récidivistes connus -> mot normal)
29
+
30
+ | Mot pourri | Dis plutôt |
31
+ |------------|------------|
32
+ | inline | directement (je le fais moi-même) |
33
+ | cutoff | l'action réelle (redémarrer, couper) |
34
+ | housekeeping | rangement / ménage du code |
35
+ | downgrade | rétrograder / baisser en gamme |
36
+ | advisory | signalement non bloquant |
37
+ | wrapper | enveloppe / surcouche |
38
+ | fallback | repli / solution de secours |
39
+ | throughput | débit |
40
+ | overhead | surcoût |
41
+ | gate | point de contrôle / porte |
42
+ | leaf | étape / tâche |
43
+ | tier | niveau / gamme |
44
+ | "forger" un token | générer / créer un token |
45
+
46
+ ## Le mécanisme (comment c'est tenu, sans boucle de réécriture)
47
+
48
+ Trois couches, aucune ne refait une réponse déjà affichée :
49
+
50
+ 1. **La règle, partout.** Ce fichier + le mantra IA-26 (`mantras.yaml`,
51
+ `mantras-sources.md`) + un pointeur dans `CLAUDE.md`. Tous les agents en
52
+ héritent (comme IA-23).
53
+ 2. **La voix de BYAN, gardée fraîche.** Une ligne dans le rappel par tour
54
+ (`.claude/hooks/inject-voice-anchor.js`) + des entrées dans
55
+ `_byan/agent/byan/tao.md` (Section 4, Vocabulaire Interdit).
56
+ 3. **Le filet vers l'avant (sans blocage).** À la fin de chaque réponse, le
57
+ programme `.claude/hooks/plain-language-check.js` (Stop) repère les
58
+ récidivistes connus et écrit un drapeau sous `_byan-output/.jargon-slip.json`.
59
+ Le rappel du tour SUIVANT lit le drapeau, le signale en clair, et l'efface.
60
+ Pas de réécriture, pas de régénération : la correction est portée au tour
61
+ d'après. C'est volontaire — un blocage forcerait une régénération coûteuse et
62
+ l'utilisateur a déjà lu le dérapage de toute façon (aucun contrôle ne
63
+ s'exécute avant l'affichage).
64
+
65
+ Le cœur logique est isolé dans `.claude/hooks/lib/plain-language.js` (liste +
66
+ détection + drapeau), testé par `.claude/__tests__/plain-language.test.js`.
67
+
68
+ ## Coût
69
+
70
+ Le filet ne coûte quasiment rien : le programme qui relit tourne en local, aucun
71
+ appel au modèle. Le rappel ajoute une poignée de mots par tour, du même ordre
72
+ que le rappel de voix déjà présent. Ce n'est PAS une économie de tokens — c'est
73
+ du confort de compréhension pour l'utilisateur, assumé comme tel.
@@ -82,6 +82,10 @@
82
82
  "type": "command",
83
83
  "command": "p=\"$CLAUDE_PROJECT_DIR/.claude/hooks/punt-guard.js\"; [ -f \"$p\" ] || exit 0; exec node \"$p\""
84
84
  },
85
+ {
86
+ "type": "command",
87
+ "command": "p=\"$CLAUDE_PROJECT_DIR/.claude/hooks/plain-language-check.js\"; [ -f \"$p\" ] || exit 0; exec node \"$p\""
88
+ },
85
89
  {
86
90
  "type": "command",
87
91
  "command": "p=\"$CLAUDE_PROJECT_DIR/.claude/hooks/drain-advisory.js\"; [ -f \"$p\" ] || exit 0; exec node \"$p\""
@@ -99,7 +99,6 @@ Suite à la migration `_byan` → `_byan`, la structure BYAN v2 a été complét
99
99
  - ComplexityScorer: Analyse complexité tâches (0.0-1.0)
100
100
  - TaskRouter: Routage intelligent (local vs délégation)
101
101
  - LocalExecutor: Exécution locale
102
- - TaskToolInterface: Interface avec sous-agents Copilot CLI
103
102
 
104
103
  3. **Generation Worker** (`generation/`)
105
104
  - ProfileTemplate: Templates markdown avec placeholders
@@ -155,6 +155,18 @@ Exemple : "Attends. Ca touche au noyau. On est jamais assez parano. Qu'est-ce qu
155
155
  **Pourquoi :** Faux enthousiasme. BYAN est sincere ou silencieux.
156
156
  **Au lieu de ca :** "Oui." ou "C'est ca."
157
157
 
158
+ **Interdit :** l'anglais gratuit quand le francais existe ("cutoff", "fallback", "housekeeping", "wrapper", "throughput", "overhead")
159
+ **Pourquoi :** Mantra IA-26 Parler Reel. Le lecteur doit comprendre sans dictionnaire.
160
+ **Au lieu de ca :** le mot francais ("redemarrer le conteneur", "solution de secours", "menage du code", "surcouche", "debit", "surcout")
161
+
162
+ **Interdit :** le jargon interne du projet balance brut ("leaf", "tier", "downgrade", "gate", "inline", "advisory")
163
+ **Pourquoi :** Mantra IA-26. C'est le vocabulaire de la cuisine interne, pas celui de l'utilisateur.
164
+ **Au lieu de ca :** dis ce que ca FAIT en clair ("etape", "niveau", "baisser en gamme", "point de controle", "directement", "signalement non bloquant")
165
+
166
+ **Interdit :** la metaphore collee de travers ("forger un token")
167
+ **Pourquoi :** Mantra IA-26. On forge une ame, pas un token.
168
+ **Au lieu de ca :** le verbe reel ("generer un token", "creer un token")
169
+
158
170
  ---
159
171
 
160
172
  ### Section 5 — Non-dits
@@ -144,14 +144,14 @@ Il n'est PAS un worker isole — il est un orchestrateur dans l'ecosysteme BMAD.
144
144
 
145
145
  L'agent peut executer n'importe quel workflow BMAD :
146
146
  - Via commande : `@bmad-{module}-{workflow}` (ex: `@bmad-bmm-create-prd`)
147
- - Via menu handler : `exec="{project-root}/_bmad/{module}/workflows/{workflow}/workflow.md"`
148
- - Manifeste : `{project-root}/_bmad/_config/workflow-manifest.csv`
147
+ - Via menu handler : `exec="{project-root}/_byan/{module}/workflows/{workflow}/workflow.md"`
148
+ - Manifeste : `{project-root}/_byan/_config/workflow-manifest.csv`
149
149
 
150
150
  ### Deleguer a d'autres Agents
151
151
 
152
152
  L'agent peut invoquer n'importe quel agent specialise :
153
153
  - Via commande : `@bmad-agent-{name}` (ex: `@bmad-agent-bmm-dev`)
154
- - Via manifeste : `{project-root}/_bmad/_config/agent-manifest.csv`
154
+ - Via manifeste : `{project-root}/_byan/_config/agent-manifest.csv`
155
155
  - L'agent delegue reprend le controle — l'agent courant se retire
156
156
 
157
157
  ### Acceder aux Contextes
@@ -2,7 +2,7 @@
2
2
  import fs from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { execFileSync } from 'node:child_process';
5
- import { validateContract, untieredExplorationViolations } from '../lib/workflows-lint.js';
5
+ import { validateContract, untieredExplorationViolations, untieredAnalysisViolations } from '../lib/workflows-lint.js';
6
6
 
7
7
  // Validate native workflow scripts under .claude/workflows/ against the full
8
8
  // contract (state-coupling + clock/RNG + meta-literal) AND node --check syntax.
@@ -57,10 +57,13 @@ for (const file of files) {
57
57
  }
58
58
  }
59
59
 
60
- // Non-blocking tiering advisory: exploration leaves that run deep.
60
+ // Non-blocking tiering advisory: exploration + analysis leaves that run deep.
61
61
  for (const a of untieredExplorationViolations(src)) {
62
62
  advisories.push({ file, ...a });
63
63
  }
64
+ for (const a of untieredAnalysisViolations(src)) {
65
+ advisories.push({ file, ...a });
66
+ }
64
67
  }
65
68
 
66
69
  // Tiering advisory is informational only — it never changes the exit code.
@@ -71,7 +74,7 @@ if (advisories.length) {
71
74
  }
72
75
  } else {
73
76
  process.stdout.write(
74
- `[byan-lint-workflows] advisory: ${advisories.length} exploration leaf(s) run deep (rerun with --advise to list; keep deep if they bear a gate/judgment, else add model: 'haiku')\n`
77
+ `[byan-lint-workflows] advisory: ${advisories.length} downgrade-eligible leaf(s) run deep (rerun with --advise to list; exploration -> model: 'haiku', analysis -> model: 'sonnet', or keep deep if genuinely hard)\n`
75
78
  );
76
79
  }
77
80
  }