create-byan-agent 2.44.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.
package/CHANGELOG.md CHANGED
@@ -9,6 +9,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
+ ## [2.45.0] - 2026-07-15
13
+
14
+ ### Added - Plain-language guard for every agent (Mantra IA-26 "Parler Reel")
15
+
16
+ - New team-wide rule: agents speak clear, coherent French to the user — no gratuitous
17
+ English when a French word exists ("redemarrer le conteneur", not "cutoff"), no raw
18
+ internal jargon (leaf/tier/downgrade/gate/inline/advisory), no misapplied metaphor
19
+ (you do not "forge" a token). A technical term with no French equivalent (commit,
20
+ cache, token) is kept but explained once. Test: the reader understands with no
21
+ dictionary. This is the sibling of IA-23 (no emoji) and applies to ALL agents.
22
+ - Mechanism, in three layers, none of which re-generates an already-shown reply:
23
+ 1. The rule reaches every agent: mantra `IA-26` (`mantras.yaml` + `mantras-sources.md`),
24
+ `.claude/rules/plain-language.md`, and a pointer in `CLAUDE.md`.
25
+ 2. BYAN's own voice stays fresh: a line in the per-turn reminder
26
+ (`inject-voice-anchor.js`) + entries in `tao.md` Section 4 (Vocabulaire Interdit).
27
+ 3. A forward net (non-blocking): the Stop hook `plain-language-check.js` spots the
28
+ known repeat-offenders in the finished reply and writes a one-turn flag under
29
+ `_byan-output/`; the next-turn reminder reads it, signals it in plain French, and
30
+ clears it. No blocking, no re-answer — the correction is carried to the next turn
31
+ (a blocking guard would force a costly regen and the user has already read the slip).
32
+ - Core logic isolated in `.claude/hooks/lib/plain-language.js` (French-aware word
33
+ boundary so "metier"/"chantier" stay clear of the "tier" match; code spans stripped
34
+ before scanning). Tests: `.claude/__tests__/plain-language.test.js`. Full suite green
35
+ (2629 jest). Shipped via `install/templates/`.
36
+
12
37
  ## [2.44.0] - 2026-07-15
13
38
 
14
39
  ### Changed - Revived the Sonnet middle tier for native-workflow model routing
@@ -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 };
@@ -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\""
@@ -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
@@ -1004,6 +1004,27 @@ const tools = [
1004
1004
  additionalProperties: false,
1005
1005
  },
1006
1006
  },
1007
+ // Selective RAG retrieval — returns the top-k most relevant knowledge bodies
1008
+ // VERBATIM (never truncated; negations/prohibitions are returned intact).
1009
+ // GET /api/projects/:projectId/knowledge/retrieve?q=...&k=10&tokenBudget=0
1010
+ // Backed by PG FTS (ts_rank) in prod, LIKE-degraded on SQLite (dev/tests).
1011
+ // Use this instead of knowledge_list when you only need a focused subset.
1012
+ {
1013
+ name: 'byan_api_knowledge_retrieve',
1014
+ description:
1015
+ 'Retrieve the top-k most relevant knowledge entries for a query using full-text search (PG) or LIKE fallback (SQLite). Returns bodies VERBATIM — negations and prohibitions are never truncated. GET /api/projects/:projectId/knowledge/retrieve?q=...&k=10&tokenBudget=0. RBAC viewer required. projectId and q are required. Requires BYAN_API_TOKEN.',
1016
+ inputSchema: {
1017
+ type: 'object',
1018
+ properties: {
1019
+ projectId: { type: 'string', description: 'Project id (required — RBAC guard).' },
1020
+ q: { type: 'string', description: 'Search query (required).' },
1021
+ k: { type: 'number', description: 'Max number of results (default 10).' },
1022
+ tokenBudget: { type: 'number', description: 'Total token budget (0 = unlimited).' },
1023
+ },
1024
+ required: ['projectId', 'q'],
1025
+ additionalProperties: false,
1026
+ },
1027
+ },
1007
1028
 
1008
1029
  // ─── Memory ───────────────────────────────────────────────────────────
1009
1030
  // Route: GET /api/projects/:projectId/memory (RBAC viewer)
@@ -1437,6 +1458,7 @@ const REMOTE_SAFE_TOOLS = new Set([
1437
1458
  'byan_api_workflow_runs_get',
1438
1459
  'byan_api_knowledge_list',
1439
1460
  'byan_api_knowledge_get',
1461
+ 'byan_api_knowledge_retrieve',
1440
1462
  'byan_api_memory_list',
1441
1463
  'byan_api_memory_search',
1442
1464
  'byan_api_custom_agents_list',
@@ -1982,6 +2004,21 @@ export function createByanServer({ token, remoteOnly = false } = {}) {
1982
2004
  return { content: [{ type: 'text', text: JSON.stringify(body, null, 2) }] };
1983
2005
  }
1984
2006
 
2007
+ if (name === 'byan_api_knowledge_retrieve') {
2008
+ requireToken();
2009
+ if (!args.projectId) throw new Error('projectId is required (RBAC: knowledge is project-scoped).');
2010
+ if (!args.q || String(args.q).trim() === '') throw new Error('q (query) is required.');
2011
+ const qs = buildQuery({
2012
+ q: args.q,
2013
+ k: args.k,
2014
+ tokenBudget: args.tokenBudget,
2015
+ });
2016
+ const body = await apiRequest(
2017
+ `/api/projects/${encodeURIComponent(args.projectId)}/knowledge/retrieve${qs}`
2018
+ );
2019
+ return { content: [{ type: 'text', text: JSON.stringify(body, null, 2) }] };
2020
+ }
2021
+
1985
2022
  if (name === 'byan_api_memory_list') {
1986
2023
  requireToken();
1987
2024
  if (!args.projectId) throw new Error('projectId is required (RBAC: memory is project-scoped).');
@@ -262,11 +262,22 @@ agents_ia:
262
262
  - "Contraintes légales/business"
263
263
  - "TODOs avec ticket"
264
264
 
265
+ - id: "IA-26"
266
+ name: "Parler Réel"
267
+ category: "code_quality"
268
+ description: "Parler en français réel et cohérent à l'utilisateur : pas d'anglais gratuit, pas de jargon interne brut, pas de métaphore collée de travers. Un terme technique sans équivalent est gardé mais expliqué une fois. Le test : le lecteur comprend sans dictionnaire."
269
+ priority: "critique"
270
+ forbidden:
271
+ - "Anglais quand le français existe (cutoff, fallback, housekeeping)"
272
+ - "Jargon interne balancé brut (leaf, tier, downgrade, gate, inline, advisory)"
273
+ - "Métaphore collée de travers (forger un token)"
274
+ rule_file: ".claude/rules/plain-language.md"
275
+
265
276
  metadata:
266
- total_mantras: 64
277
+ total_mantras: 65
267
278
  conception_count: 39
268
- agents_ia_count: 25
269
- version: "1.0.0"
270
- last_updated: "2026-02-02"
279
+ agents_ia_count: 26
280
+ version: "1.0.1"
281
+ last_updated: "2026-07-15"
271
282
  authors: ["Yan", "Carson"]
272
283
  methodology: "Merise Agile + TDD"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-byan-agent",
3
- "version": "2.44.0",
3
+ "version": "2.45.0",
4
4
  "description": "BYAN v2.8 - Intelligent AI agent creator with ELO trust system + scientific fact-check + Hermes universal dispatcher + native Claude Code integration (hooks, skills, MCP server). Multi-platform (Claude Code, Codex). Merise Agile + TDD + 71 Mantras. ~54% LLM cost savings.",
5
5
  "main": "src/index.js",
6
6
  "bin": {