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.
- package/CHANGELOG.md +118 -0
- package/install/bin/byan-kanban.js +1 -1
- package/install/bin/create-byan-agent-v2.js +111 -111
- package/install/lib/byan-leantime-integration.js +2 -2
- package/install/lib/byan-web-integration.js +5 -5
- package/install/lib/claude-native-setup.js +11 -11
- package/install/lib/codex-native-setup.js +3 -3
- package/install/lib/exchange/agent-packager.js +1 -1
- package/install/lib/mcp-extensions/gdrive.js +5 -5
- package/install/lib/mcp-extensions/index.js +5 -5
- package/install/lib/phase2-chat.js +21 -21
- package/install/lib/platforms/claude-code.js +2 -2
- package/install/lib/project-agents-generator.js +4 -4
- package/install/lib/staging-consent.js +1 -1
- package/install/lib/subagent-generator.js +2 -3
- package/install/lib/utils/logger.js +4 -4
- package/install/lib/utils/node-detector.js +1 -1
- package/install/lib/yanstaller/agent-launcher.js +1 -1
- package/install/lib/yanstaller/index.js +11 -116
- package/install/lib/yanstaller/platform-selector.js +11 -11
- package/install/src/byan-v2/dispatcher/task-router.js +1 -1
- package/install/src/index.js +1 -1
- package/install/src/webui/api.js +0 -13
- package/install/src/webui/chat/bridge.js +1 -1
- package/install/src/webui/chat/cli-detector.js +1 -1
- package/install/templates/.claude/CLAUDE.md +1 -0
- package/install/templates/.claude/hooks/inject-voice-anchor.js +20 -1
- package/install/templates/.claude/hooks/lib/plain-language.js +155 -0
- package/install/templates/.claude/hooks/plain-language-check.js +53 -0
- package/install/templates/.claude/rules/native-workflows.md +22 -14
- package/install/templates/.claude/rules/plain-language.md +73 -0
- package/install/templates/.claude/settings.json +4 -0
- package/install/templates/_byan/COMPLETION-REPORT.md +0 -1
- package/install/templates/_byan/agent/byan/byan-tao.md +12 -0
- package/install/templates/_byan/core/activation/soul-activation.md +3 -3
- package/install/templates/_byan/mcp/byan-mcp-server/bin/byan-lint-workflows.js +6 -3
- package/install/templates/_byan/mcp/byan-mcp-server/lib/native-tiers.js +35 -17
- package/install/templates/_byan/mcp/byan-mcp-server/lib/workflows-lint.js +38 -0
- package/install/templates/_byan/mcp/byan-mcp-server/server.js +37 -0
- package/install/templates/_byan/worker/workers.md +1 -1
- package/install/templates/_byan/workflow/simple/byan/data/mantras.yaml +15 -4
- package/package.json +1 -1
- package/src/byan-v2/dispatcher/task-router.js +1 -1
- package/src/byan-v2/lib/layout-resolver.js +3 -6
- package/install/lib/yanstaller/installer.js +0 -138
- package/install/lib/yanstaller/interviewer.js +0 -88
- package/install/lib/yanstaller/recommender.js +0 -102
- package/install/lib/yanstaller/troubleshooter.js +0 -89
- package/install/lib/yanstaller/validator.js +0 -198
- package/install/lib/yanstaller/wizard.js +0 -109
- package/install/src/byan-v2/dispatcher/task-tool-interface-mock.js +0 -134
- package/install/src/byan-v2/dispatcher/task-tool-interface.js +0 -123
- package/install/templates/_bmad/bmb/agents/agent-builder.md +0 -59
- package/install/templates/_bmad/bmb/agents/byan-test.md +0 -116
- package/install/templates/_bmad/bmb/agents/byan.md +0 -215
- package/install/templates/_bmad/bmb/agents/marc.md +0 -303
- package/install/templates/_bmad/bmb/agents/module-builder.md +0 -60
- package/install/templates/_bmad/bmb/agents/patnote.md +0 -495
- package/install/templates/_bmad/bmb/agents/rachid.md +0 -184
- package/install/templates/_bmad/bmb/agents/workflow-builder.md +0 -61
- package/install/templates/_bmad/bmb/workflows/byan/data/mantras.yaml +0 -272
- package/install/templates/_bmad/bmb/workflows/byan/data/templates.yaml +0 -59
- package/install/templates/_bmad/bmb/workflows/byan/delete-agent-workflow.md +0 -657
- package/install/templates/_bmad/bmb/workflows/byan/edit-agent-workflow.md +0 -688
- package/install/templates/_bmad/bmb/workflows/byan/interview-workflow.md +0 -753
- package/install/templates/_bmad/bmb/workflows/byan/quick-create-workflow.md +0 -450
- package/install/templates/_bmad/bmb/workflows/byan/templates/base-agent-template.md +0 -79
- package/install/templates/_bmad/bmb/workflows/byan/validate-agent-workflow.md +0 -676
- package/install/templates/_bmad/core/agents/carmack.md +0 -238
- package/src/byan-v2/dispatcher/task-tool-interface-mock.js +0 -134
- 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('\
|
|
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: '
|
|
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: '
|
|
87
|
+
name: 'Advanced: Install on multiple platforms',
|
|
88
88
|
value: 'multi'
|
|
89
89
|
},
|
|
90
90
|
{
|
|
91
|
-
name: '
|
|
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:
|
|
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: '
|
|
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 ? '
|
|
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
|
}
|
package/install/src/index.js
CHANGED
|
@@ -59,7 +59,7 @@ class Dashboard {
|
|
|
59
59
|
*
|
|
60
60
|
* @example
|
|
61
61
|
* const byan = createByanInstance({ workerCount: 2 });
|
|
62
|
-
* await byan.executeWorkflow('
|
|
62
|
+
* await byan.executeWorkflow('_byan/workflows/create-prd/workflow.yaml');
|
|
63
63
|
*/
|
|
64
64
|
function createByanInstance(options = {}) {
|
|
65
65
|
const {
|
package/install/src/webui/api.js
CHANGED
|
@@ -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
|
|
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'
|
|
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: {
|
|
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'`)** —
|
|
33
|
-
the `mech-` label prefix (`mech-validate-json`):
|
|
34
|
-
(JSON parses, schema matches, lint passes).
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
-
|
|
39
|
-
|
|
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
|
|
48
|
-
deep is *reported* (`byan-lint-workflows.js --advise
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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}/
|
|
148
|
-
- Manifeste : `{project-root}/
|
|
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}/
|
|
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}
|
|
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
|
}
|