create-byan-agent 2.21.0 → 2.23.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,75 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
+ ## [2.23.0] - 2026-06-09
13
+
14
+ ### Added - Stub path normalizer + a 5th pre-commit gate (no _bmad/@bmad drift)
15
+
16
+ The installer generated platform stubs (`.codex/prompts`, `.github/agents`,
17
+ `.claude/skills`) across many versions; older generators wrote the legacy path
18
+ layout (`_bmad/*/agents/X.md`, `@bmad/bmm/agents/X.md`,
19
+ `@bmad-output/bmb-creations/X/X.md`), so the tracked corpus carried a mix of stale
20
+ path forms while the agent source files stayed clean. This adds the mechanism that
21
+ removes the drift and blocks its return.
22
+
23
+ - **Tool** `_byan/mcp/byan-mcp-server/lib/stub-sync.js` + `bin/byan-sync-stubs.js`:
24
+ normalizes stale `_bmad/` and `@bmad/` PATH tokens to the `_byan/` canonical
25
+ layout, in place and surgically. The `@bmad-<word>` invocation syntax and the
26
+ `_bmad-output/` artifact dir are preserved; no stub is overwritten wholesale, so
27
+ the github full-copies and hand-authored skills keep their content. `--check`
28
+ reports any residual stale ref and exits non-zero.
29
+ - **5th pre-commit gate.** `.githooks/pre-commit` runs `byan-sync-stubs --check`
30
+ after the template-fidelity gate, blocking a commit whose tracked stubs have
31
+ drifted. It self-disables when the tool or the stub dirs are absent
32
+ (installed-user no-op).
33
+ - **First run.** 101 stub files normalized (codex prompts + the Codex global
34
+ `instructions.md` + 5 github stubs + their template twins); the byan github
35
+ full-copy changed only its 3 stale path lines, its other 1059 lines untouched.
36
+ - Design mirrors the template-fidelity sync (pure rewrite rules + IO-isolated
37
+ apply); 20 unit tests pin every rule, the two preservation cases, the IO layer,
38
+ and idempotence. The tool ships in the template, so the gate is live for
39
+ installed users too.
40
+
41
+ ## [2.22.0] - 2026-06-09
42
+
43
+ ### Changed - byan_dispatch routes the model tier by task nature, not by size
44
+
45
+ `byan_dispatch` fused two unrelated decisions into one route string
46
+ (`mcp-worker-haiku`, `main-thread-opus`): a short sequential task was downgraded
47
+ to haiku purely on its length, and a long one was pinned up to opus. That is the
48
+ size-driven mis-tiering the native-workflow doctrine (`native-tiers.js`) was built
49
+ to forbid, so the two routers disagreed. This decouples the two axes and makes
50
+ `native-tiers.js` the single source of truth for the model tier across both worlds.
51
+
52
+ - **Two independent axes.** `dispatch.js` now returns
53
+ `{ score, strategy, nature, tier, model, parallelizable, reasoning }`. STRATEGY
54
+ (where the work runs: `main-thread` / `agent-subagent-worktree` / `mcp-worker`)
55
+ stays derived from the scalar score + `parallelizable`. TIER (which model) is
56
+ derived from the task NATURE, decoupled from size.
57
+ - **One source of truth.** `dispatch.js` imports `classifyLeaf` / `tierFor` /
58
+ `TIER_MODEL` directly from `native-tiers.js` — a one-way dependency toward the
59
+ tier authority rather than a duplicated rule. Only an `exploration` nature
60
+ downgrades to `haiku`; `implementation` / `verification` / `analysis` (and any
61
+ unmatched task) stay `deep` (inherit the session model). No pin-up to opus.
62
+ - **Conservative by default.** An optional `nature` arg sets the tier directly;
63
+ absent or invalid, the task text is classified, whose own default is
64
+ `implementation` (deep) — so a miss protects the work instead of downgrading it.
65
+ - **Consumers realigned.** The `byan_dispatch` tool schema gains an optional
66
+ `nature` enum; the three consuming skills (byan-byan Phase 4, byan-hermes-dispatch
67
+ step 3, byan-orchestrate) read `strategy` + `model` from the new shape. The
68
+ fused-route strings are dropped from the live routing path.
69
+ - 22 dispatch unit tests pin the contract (no downgrade for protected natures,
70
+ exploration to haiku, no pin-up, conservative fallback, strategy preserved across
71
+ the score bands) plus the hermes-e2e non-regression. Template re-synced.
72
+
73
+ ### Known debt
74
+
75
+ The legacy fused-route vocabulary (`mcp-worker-haiku` / `main-thread-opus`) still
76
+ appears in two doctrine docs (`_byan/worker/workers.md`,
77
+ `_byan/workflow/simple/byan/feature-workflow.md`) and a dead, unreferenced parallel
78
+ router (`src/core/dispatcher/execution-router.js` + its test). These have no live
79
+ consumer and are scoped to a follow-up cleanup.
80
+
12
81
  ## [2.21.0] - 2026-06-08
13
82
 
14
83
  ### Added - Template fidelity sync (the published package matches its CHANGELOG)
@@ -56,11 +56,16 @@ Never call `byan_update_apply` without explicit user consent. That tool returns
56
56
 
57
57
  ### Phase 4 — DISPATCH
58
58
  - **Who** : you + user. Route each feature to the right BYAN component.
59
- - **Decision table** per feature :
60
- - **Score < 15** inline main-thread, no subagent
61
- - **Score 15-39 parallelizable** → agent-subagent-worktree (use `byan_dispatch` MCP tool to verify)
62
- - **Score 15-39 sequential** → mcp-worker-haiku
63
- - **Score 40** → main-thread-opus or delegate to `byan-hermes-dispatch`
59
+ - **Decision table** per feature — TWO independent axes (`byan_dispatch` returns both) :
60
+ - **Strategy** (WHERE it runs), from the score :
61
+ - **Score < 15** → inline main-thread, no subagent
62
+ - **Score 15-39 parallelizable** → agent-subagent-worktree
63
+ - **Score 15-39 sequential** → mcp-worker
64
+ - **Score ≥ 40** → main-thread (heavy) or delegate to `byan-hermes-dispatch`
65
+ - **Model tier** (WHICH model), from the task NATURE — not its size (`byan_dispatch` returns it as `model`, via native-tiers, the single source of truth) :
66
+ - nature `exploration` (load/read/scan/list/parse/fetch...) → `haiku`
67
+ - nature `implementation` / `verification` / `analysis` / unknown → deep = **inherit the session model**
68
+ - Keep protected work (verify/analysis/implement) off haiku regardless of size ; no pin-up to opus. Pass an explicit `nature` to `byan_dispatch` when you know it.
64
69
  - **Output** : a table `{ feature → specialist → model → strategy → estimated_tokens }`.
65
70
  - **If no specialist matches** : halt. Ask user whether to run INT (agent recruitment) first. Do NOT fallback silently to general-purpose.
66
71
  - **Exit gate** : user validates the mapping.
@@ -50,18 +50,19 @@ Match keywords against the routing table below. Pick the single best match. If n
50
50
 
51
51
  ### 3. Pick the execution strategy (MCP call)
52
52
 
53
- Call the `byan_dispatch` MCP tool with `{ task: <goal>, parallelizable: <bool> }`. It returns `{ strategy, score, reasoning }` where strategy is one of :
53
+ Call the `byan_dispatch` MCP tool with `{ task: <goal>, parallelizable: <bool>, nature?: <leaf-type> }`. It returns `{ score, strategy, nature, tier, model, reasoning }` TWO independent axes :
54
54
 
55
- - `main-thread` do it inline, no delegation
56
- - `agent-subagent-worktree` — spawn Agent tool with isolation worktree
57
- - `mcp-worker-haiku` — spawn Agent tool with Haiku model, no worktree
58
- - `main-thread-opus` — keep in the current thread (don't delegate, Opus needed)
55
+ - **strategy** (WHERE it runs), from the score :
56
+ - `main-thread` — do it inline, no delegation
57
+ - `agent-subagent-worktree` — spawn Agent tool with isolation worktree
58
+ - `mcp-worker` — spawn Agent tool, no worktree
59
+ - **model** (WHICH model), from the task NATURE via native-tiers, not its size : `haiku` (exploration only) or `null` = deep (inherit the session model). Pass an explicit `nature` (`exploration`/`implementation`/`verification`/`analysis`) when you know it; protected natures stay off haiku.
59
60
 
60
61
  ### 4. Spawn the work
61
62
 
62
- Depending on strategy :
63
+ Depending on strategy (apply the returned `model` whenever you spawn) :
63
64
 
64
- **`main-thread` or `main-thread-opus`** : do not spawn. Execute inline yourself.
65
+ **`main-thread`** : do not spawn. Execute inline yourself — the work runs on the session model.
65
66
 
66
67
  **`agent-subagent-worktree`** : call the Agent tool with :
67
68
  ```
@@ -76,7 +77,9 @@ prompt: |
76
77
  When done, write a concise report (< 200 words).
77
78
  ```
78
79
 
79
- **`mcp-worker-haiku`** : same Agent tool call but without `isolation`, and add `model: "haiku"` in the prompt's instruction block if the receiving subagent honors it.
80
+ **`mcp-worker`** : same Agent tool call but without `isolation`. Set the Agent's `model` to the returned `model` — `haiku` for exploration nature, otherwise omit `model` to inherit the session model. The tier follows the task nature, not its size.
81
+
82
+ For any spawned strategy : pass `model` to the Agent tool when it is non-null; omit it when null so the subagent inherits the session model.
80
83
 
81
84
  ### 5. Specialist stub path lookup
82
85
 
@@ -9,7 +9,7 @@ You compose three existing building blocks into one multi-role flow :
9
9
 
10
10
  | Block | Role |
11
11
  |-------|------|
12
- | `byan_dispatch` MCP tool | Per-task execution strategy (main-thread / agent-subagent-worktree / mcp-worker-haiku / main-thread-opus) + complexity score |
12
+ | `byan_dispatch` MCP tool | Per-task strategy (main-thread / agent-subagent-worktree / mcp-worker) from the score + model tier by NATURE (haiku for exploration, else inherit the session model) + complexity score |
13
13
  | `byan-hermes-dispatch` skill | Specialist lookup (architect, dev, analyst, …) from a routing table |
14
14
  | `party-mode-native` workflow | Parallel spawn via Agent tool + worktree + coordination JSON |
15
15
 
@@ -42,7 +42,7 @@ Use this a priori mapping — override only if the task clearly needs more :
42
42
  | architect, quinn, tea, creative-problem-solver | opus | Deep reasoning, trade-offs |
43
43
  | carmack, rachid, marc, patnote | haiku | Narrow mechanical tasks |
44
44
 
45
- Then call `byan_dispatch` with each role's goal to get a complexity score. If the score demands a different tier (score >= 40bump to opus ; score < 15inline, no subagent), **override the default for that role**.
45
+ Then call `byan_dispatch` with each role's goal (and `nature` when known). Use its `score` for the STRATEGY only (score < 15inline, no subagent ; 15-39 subagent/worker ; ≥ 40 keep the heavy role in the main thread) and its nature-based `model` as the tier signal. The score sets WHERE the role runs, not WHICH model — keep protected roles (verify/analysis/implement) off haiku regardless of size, and avoid pinning a role up to opus on size alone. The per-role table above is the a-priori floor; `byan_dispatch`'s nature `model` refines it.
46
46
 
47
47
  ### 3. Compute the execution plan
48
48
 
@@ -69,7 +69,7 @@ Group roles by `parallelizable_with` graph. For each parallel cluster :
69
69
 
70
70
  - If cluster has N > 1 roles AND all use `agent-subagent-worktree` strategy → use the **party-mode-native** workflow : `coordination.initSession(roles, …)`, then dispatch all Agent tool calls in a single message.
71
71
  - If cluster has N = 1 OR strategy = `main-thread` → execute inline in the current turn.
72
- - If strategy = `mcp-worker-haiku` → spawn an Agent tool call WITHOUT worktree (faster boot, single-turn).
72
+ - If strategy = `mcp-worker` → spawn an Agent tool call WITHOUT worktree (faster boot, single-turn) ; set the Agent's model to the role's nature-based `model` (haiku for exploration, omit otherwise to inherit the session model).
73
73
 
74
74
  For each Agent tool call, the prompt must start with :
75
75
  ```
@@ -1,9 +1,10 @@
1
1
  #!/usr/bin/env bash
2
- # BYAN pre-commit hook. Four gates run in order:
2
+ # BYAN pre-commit hook. Five gates run in order:
3
3
  # 1. Strict Mode gate : block if a strict session is engaged but not completed.
4
4
  # 2. Native-workflow lint : block if a .claude/workflows/*.js couples to state.
5
5
  # 3. Template fidelity : block if install/templates/ drifted from root.
6
- # 4. Mantra floor : block if a Gen3 persona source scores below the floor.
6
+ # 4. Stub path drift : block if a tracked stub carries a stale _bmad/@bmad path ref.
7
+ # 5. Mantra floor : block if a Gen3 persona source scores below the floor.
7
8
  #
8
9
  # Install :
9
10
  # git config core.hooksPath .githooks
@@ -81,6 +82,24 @@ if [ -f "$TEMPLATE_SYNC" ]; then
81
82
  fi
82
83
  fi
83
84
 
85
+ # Stub path drift gate — the installer generated platform stubs (.codex/prompts,
86
+ # .github/agents, .claude/skills) over many versions; older generators emitted the
87
+ # legacy _bmad/@bmad path layout while the agent sources are clean. This gate
88
+ # blocks a commit whose tracked stubs still carry a stale _bmad/ or @bmad/ PATH
89
+ # ref (the @bmad- invocation syntax and the _bmad-output/ artifact dir are left
90
+ # alone). Re-normalize with the apply command, then restage. No-op if the tool is
91
+ # absent or no stub dirs exist (installed-user no-op).
92
+ STUB_SYNC="_byan/mcp/byan-mcp-server/bin/byan-sync-stubs.js"
93
+ if [ -f "$STUB_SYNC" ]; then
94
+ if ! node "$STUB_SYNC" --check --root "$(git rev-parse --show-toplevel)"; then
95
+ echo ""
96
+ echo "Commit blocked : a tracked stub carries a stale _bmad/@bmad path ref."
97
+ echo "Re-normalize with 'node $STUB_SYNC' then restage, or bypass with"
98
+ echo "'git commit --no-verify' (emergency only)."
99
+ exit 1
100
+ fi
101
+ fi
102
+
84
103
  if [ ! -f "$VALIDATOR" ]; then
85
104
  exit 0
86
105
  fi
@@ -1023,14 +1023,14 @@ Il n'est PAS un worker isole — il est un orchestrateur dans l'ecosysteme BMAD.
1023
1023
 
1024
1024
  L'agent peut executer n'importe quel workflow BMAD :
1025
1025
  - Via commande : `@bmad-{module}-{workflow}` (ex: `@bmad-bmm-create-prd`)
1026
- - Via menu handler : `exec="{project-root}/_bmad/{module}/workflows/{workflow}/workflow.md"`
1027
- - Manifeste : `{project-root}/_bmad/_config/workflow-manifest.csv`
1026
+ - Via menu handler : `exec="{project-root}/_byan/{module}/workflows/{workflow}/workflow.md"`
1027
+ - Manifeste : `{project-root}/_byan/_config/workflow-manifest.csv`
1028
1028
 
1029
1029
  ### Deleguer a d'autres Agents
1030
1030
 
1031
1031
  L'agent peut invoquer n'importe quel agent specialise :
1032
1032
  - Via commande : `@bmad-agent-{name}` (ex: `@bmad-agent-bmm-dev`)
1033
- - Via manifeste : `{project-root}/_bmad/_config/agent-manifest.csv`
1033
+ - Via manifeste : `{project-root}/_byan/_config/agent-manifest.csv`
1034
1034
  - L'agent delegue reprend le controle — l'agent courant se retire
1035
1035
 
1036
1036
  ### Acceder aux Contextes
@@ -6,7 +6,7 @@ description: 'Scientific Claim Challenger and Epistemic Guard'
6
6
  You must fully embody this agent's persona and follow all activation instructions exactly as specified. NEVER break character until given an exit command.
7
7
 
8
8
  <agent-activation CRITICAL="TRUE">
9
- 1. LOAD the FULL agent file from {project-root}/_bmad-output/bmb-creations/skeptic/skeptic.md
9
+ 1. LOAD the FULL agent file from {project-root}/_byan/agent/skeptic/skeptic.md
10
10
  2. READ its entire contents - this contains the complete agent persona, menu, and instructions
11
11
  3. LOAD the soul activation protocol from {project-root}/_byan/core/activation/soul-activation.md and EXECUTE it silently
12
12
  4. FOLLOW every step in the <activation> section precisely
@@ -25,4 +25,6 @@ expert-merise-agile,"Expert Merise","Expert Merise Agile - Assistant de Concepti
25
25
  "skeptic","The Skeptic","Scientific Claim Challenger and Epistemic Guard","[?]","Epistemic Guard + Fact-Check Specialist","Methodical challenger of all claims. Applies 3-step verification (Source / Proof type / Reproducible). Specializes in auditing documents for unsourced assertions, computing Trust Scores, and verifying reasoning chains with multiplicative confidence propagation.","Cold, methodical, impeccably polite. Speaks in structured CLAIM/CHALLENGE/VERDICT blocks. Uses Socratic method — questions before conclusions. Never hostile, always rigorous.","Challenge Before Confirm | Extraordinary claims require extraordinary evidence | Descartes Doubt | No URL generation | Strict-domain LEVEL-2 minimum","core","_byan/agent/skeptic/skeptic.md"
26
26
  "forgeron","Le Forgeron","Revelateur d ames","","Revelateur d ames — Soul Forger","Expert en interview psychologique profonde pour extraire l ame du createur depuis ses experiences de vie. Detecte emotions, valeurs, blessures fondatrices. Genere creator-soul.md et agent soul files. Calme, patient, utilise le silence comme outil.","Calme, patient, minimal, profond. Questions rares mais chaque une compte. Utilise le silence. Reflete sans projeter.","Ne jamais interpreter a la place du createur | Ne jamais precipiter | Emotions = donnees de navigation | Preuve avant sentence","bmb","_byan/agent/forgeron/forgeron.md"
27
27
  "tao","Tao","Le Tao — Directeur de Voix des Agents","道","Voice Director — Soul to Expression Bridge","Transforme les valeurs abstraites du soul.md en directives vocales concretes : tics de langage, registre, signatures verbales, vocabulaire interdit. Forge le tao.md de chaque agent. Garantit l anti-uniformite : chaque agent sonne unique.","Calme, precis, chirurgical. L oreille absolue pour les voix. Detecte le generique a la premiere phrase. Concret : jamais de regle sans exemple.","Derivation tracable : chaque tic nait d une valeur d ame | Anti-uniformite : deux agents ne sonnent jamais pareil | Exemple obligatoire | La voix sert l ame pas l inverse","core","_byan/agent/tao/tao.md"
28
+ "jimmy","Jimmy","Spécialiste Documentation Technique & Processus Internes","book-open","Technical Documentation Specialist + Outline Knowledge Custodian","Specialist in technical documentation and internal processes. Creates, maintains, and organizes operational documentation on Outline: runbooks, deployment procedures, infrastructure configs, and server and web guides. Expert in infra, web, and server work with zero approximation in procedures. Documents in French for francophone technical teams.","Professional and rigorous, direct without unnecessary jargon, and pedagogical when needed. Uses a standardized structure per document type.","Clear and actionable technical documentation | Standardized structure per document type | Systematic validation before publication | Coherently named and organized collections | Reusable templates when relevant","bmm","_byan/agent/jimmy/jimmy.md"
29
+ "mike","Mike","Gestionnaire de Projet — Spécialiste Leantime","clipboard-list","Project Manager + Leantime Integration Specialist","Project manager specialized in Leantime. Creates, organizes, and manages projects, tasks (tickets), sprints, and milestones on Leantime. Structures team work clearly and efficiently. Works in French for francophone teams.","Professional and organized, results-oriented, and direct in French. Asks targeted questions to structure the work with no superfluous content.","MVP first, the minimum viable to get moving | Validation before action, confirm before executing | Clarity through explicit names and concise descriptions | Traceability by documenting important decisions | Explicit errors with proposed alternatives","bmm","_byan/agent/mike/mike.md"
28
30
 
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { applyFix, check, STUB_DIRS } from '../lib/stub-sync.js';
5
+
6
+ // Normalize stale _bmad / @bmad PATH references in tracked agent stubs to the
7
+ // _byan/ canonical form. Two modes:
8
+ // (default) fix : rewrite stale path tokens in place (atomic per file).
9
+ // --check : report any residual stale ref and exit non-zero (no writes).
10
+ // This is the pre-commit gate's entry point.
11
+ // Usage: node bin/byan-sync-stubs.js [--check] [--root <dir>]
12
+
13
+ function parseArgs(argv) {
14
+ const args = { check: false };
15
+ for (let i = 2; i < argv.length; i++) {
16
+ if (argv[i] === '--check') args.check = true;
17
+ else if (argv[i] === '--root') args.projectRoot = argv[++i];
18
+ }
19
+ return args;
20
+ }
21
+
22
+ const args = parseArgs(process.argv);
23
+ const root = args.projectRoot || process.env.CLAUDE_PROJECT_DIR || process.cwd();
24
+
25
+ // Self-disable when none of the stub dirs exist (installed-user no-op).
26
+ const anyDir = STUB_DIRS.some(
27
+ (d) => fs.existsSync(path.join(root, d)) || fs.existsSync(path.join(root, 'install', 'templates', d)),
28
+ );
29
+ if (!anyDir) {
30
+ process.stdout.write('[byan-sync-stubs] no stub directories - nothing to normalize\n');
31
+ process.exit(0);
32
+ }
33
+
34
+ if (args.check) {
35
+ const { stale, ok, scanned } = check({ rootDir: root });
36
+ if (ok) {
37
+ process.stdout.write(`[byan-sync-stubs] OK - ${scanned} stubs free of stale _bmad/@bmad path refs\n`);
38
+ process.exit(0);
39
+ }
40
+ for (const { file, refs } of stale) {
41
+ process.stderr.write(`[byan-sync-stubs] stale: ${file} -> ${refs.join(', ')}\n`);
42
+ }
43
+ process.stderr.write(
44
+ `[byan-sync-stubs] FAIL - ${stale.length} stub(s) carry stale path refs. Run: node bin/byan-sync-stubs.js\n`,
45
+ );
46
+ process.exit(1);
47
+ }
48
+
49
+ const { fixed, scanned } = applyFix({ rootDir: root });
50
+ process.stdout.write(`[byan-sync-stubs] normalized - ${fixed.length} stub(s) fixed of ${scanned} scanned\n`);
51
+ process.exit(0);
@@ -1,23 +1,75 @@
1
- export function dispatch({ task, complexity, parallelizable }) {
1
+ import { classifyLeaf, tierFor, TIER_MODEL, LEAF_TYPES } from './native-tiers.js';
2
+
3
+ // byan_dispatch routes a unit of work along TWO independent axes:
4
+ //
5
+ // STRATEGY — WHERE the work runs (inline / isolated subagent / mcp worker).
6
+ // Derived from the scalar score + parallelizable. This is
7
+ // dispatch's own concern: orchestration.
8
+ // TIER — WHICH model the work deserves. Delegated to native-tiers (the
9
+ // single source of truth), keyed on the task's NATURE, never on its
10
+ // size. Only exploration downgrades to a cheap tier; implementation,
11
+ // verification, analysis (and anything unmatched) stay deep =
12
+ // inherit the session model. We never PIN UP to opus.
13
+ //
14
+ // Before this split the two axes were fused into one route string
15
+ // ('mcp-worker-haiku', 'main-thread-opus'), so a short sequential task was
16
+ // silently downgraded to haiku purely on length, and a long one was pinned up to
17
+ // opus — exactly the size-driven mis-tiering native-tiers' anti-downgrade doctrine
18
+ // forbids. The score still picks the strategy; the model now comes from nature.
19
+ //
20
+ // The dependency on native-tiers is intentional and one-directional: dispatch
21
+ // CONSUMES the tier source of truth, it does not duplicate it. native-tiers is a
22
+ // pure, IO-free module, so the import is safe and keeps the tiering doctrine in a
23
+ // single place.
24
+
25
+ const VALID_NATURES = new Set(Object.values(LEAF_TYPES));
26
+
27
+ export function dispatch({ task, complexity, parallelizable, nature } = {}) {
2
28
  const score =
3
29
  typeof complexity === 'number'
4
30
  ? complexity
5
31
  : Math.min(100, Math.floor((task?.length || 0) / 10));
6
32
  const isPar = parallelizable === true;
7
33
 
8
- let route, reasoning;
34
+ // Axis 1 — strategy (where). Scalar, as before, minus the fused model suffix.
35
+ let strategy, strategyReason;
9
36
  if (score < 15) {
10
- route = 'main-thread';
11
- reasoning = `Score ${score} < 15. Inline in current context, no delegation overhead.`;
37
+ strategy = 'main-thread';
38
+ strategyReason = `score ${score} < 15: inline, no delegation overhead`;
12
39
  } else if (score < 40 && isPar) {
13
- route = 'agent-subagent-worktree';
14
- reasoning = `Score ${score} + parallelizable. Spawn Claude Code Agent tool with worktree isolation.`;
40
+ strategy = 'agent-subagent-worktree';
41
+ strategyReason = `score ${score} + parallelizable: isolated subagent (worktree)`;
15
42
  } else if (score < 40) {
16
- route = 'mcp-worker-haiku';
17
- reasoning = `Score ${score}, sequential. Delegate to lightweight Haiku worker via MCP.`;
43
+ strategy = 'mcp-worker';
44
+ strategyReason = `score ${score}, sequential: delegated MCP worker`;
18
45
  } else {
19
- route = 'main-thread-opus';
20
- reasoning = `Score ${score} >= 40. Complex task, keep in main thread with Opus reasoning.`;
46
+ strategy = 'main-thread';
47
+ strategyReason = `score ${score} >= 40: heavy, kept in the main thread`;
21
48
  }
22
- return { score, route, reasoning, parallelizable: isPar };
49
+
50
+ // Axis 2 — tier (which model). By nature, via native-tiers. An explicit, valid
51
+ // nature wins; otherwise classify the task text. An unknown nature falls back to
52
+ // classification rather than guessing, and classification's own default is
53
+ // IMPLEMENTATION (deep), so the conservative path is the worst case — protected
54
+ // work is never downgraded on a miss.
55
+ const leafType = VALID_NATURES.has(nature) ? nature : classifyLeaf({ label: task || '' });
56
+ const tier = tierFor(leafType);
57
+ const model = TIER_MODEL[tier]; // 'haiku' (exploration) or null (every other nature -> inherit session model). tierFor never auto-picks balanced/'sonnet'.
58
+
59
+ const tierReason =
60
+ model === null
61
+ ? `nature=${leafType} -> ${tier}: inherit the session model (protected, not downgraded)`
62
+ : `nature=${leafType} -> ${tier}: ${model}`;
63
+
64
+ // model applies to a DELEGATED strategy (subagent / mcp-worker leaf); for a
65
+ // main-thread strategy the work runs on the session model and model is advisory.
66
+ return {
67
+ score,
68
+ strategy,
69
+ nature: leafType,
70
+ tier,
71
+ model,
72
+ parallelizable: isPar,
73
+ reasoning: `${strategyReason}. ${tierReason}.`,
74
+ };
23
75
  }
@@ -0,0 +1,158 @@
1
+ // Stub path normalizer — keep tracked agent stubs free of stale _bmad / @bmad
2
+ // PATH references.
3
+ //
4
+ // The installer generated platform stubs (.codex/prompts, .github/agents,
5
+ // .claude/skills) over many versions. Older generators emitted the legacy path
6
+ // layout (`_bmad/*/agents/X.md`, `@bmad/bmm/agents/X.md`,
7
+ // `@bmad-output/bmb-creations/X/X.md`); the current generator emits the `_byan/`
8
+ // new layout. The committed corpus therefore carries a mix of stale path forms,
9
+ // while the source agent files are clean. This module normalizes those stale
10
+ // PATH tokens to the `_byan/` canonical form, in place, touching nothing else.
11
+ //
12
+ // Two tokens look similar but are NOT paths and must survive untouched:
13
+ // - `@bmad-<word>` : the agent/workflow INVOCATION syntax (`@bmad-bmm-create-prd`,
14
+ // `@bmad-party-mode`). A command, not a file path.
15
+ // - `_bmad-output/` : the accepted output-artifact directory (planning/
16
+ // implementation artifacts), documented in CLAUDE.md.
17
+ // Both are distinguished structurally: a path token is `@bmad/` or `_bmad/`
18
+ // (immediate slash); the survivors are `@bmad-` / `_bmad-` (immediate hyphen).
19
+ // The one exception is `[@_]bmad-output/bmb-creations/<name>/<name>.md`, which is
20
+ // a stale AGENT-LOAD path (the agent now lives at `_byan/agent/<name>/`), so that
21
+ // specific sub-form IS rewritten.
22
+ //
23
+ // Design mirrors template-sync.js: the risky half (the rewrite rules) is pure and
24
+ // exhaustively unit-tested; the I/O half takes an injected `io` so tests pin
25
+ // behaviour without touching the real filesystem. The tool only ever edits stale
26
+ // path tokens — it never regenerates or overwrites a stub wholesale, so the 6
27
+ // github full-copies and the 12 hand-authored rich skills keep their content.
28
+
29
+ import fs from 'node:fs';
30
+ import path from 'node:path';
31
+
32
+ // Tracked stub directories (root-relative POSIX). Each is scanned (its .md files)
33
+ // both at root and under its install/templates/ twin. `.codex` is taken whole so
34
+ // the Codex global context file (.codex/instructions.md) is normalized alongside
35
+ // the per-agent .codex/prompts/ stubs.
36
+ export const STUB_DIRS = ['.codex', '.github/agents', '.claude/skills'];
37
+ export const TEMPLATE_PREFIX = 'install/templates';
38
+
39
+ // Canonical new-layout reference for an agent name.
40
+ function agentRef(name) {
41
+ return `_byan/agent/${name}/${name}.md`;
42
+ }
43
+
44
+ // Ordered rewrite rules. Order matters: the specific agent-load forms run before
45
+ // the generic prefix swaps, so an agent path becomes the new layout
46
+ // (`_byan/agent/X/X.md`) rather than the legacy one (`_byan/*/agents/X.md`).
47
+ const RULES = [
48
+ // bmb-creations agent load -> new-layout agent ref. Matches both @bmad-output
49
+ // and _bmad-output ONLY when followed by /bmb-creations/<dir>/<name>.md, so a
50
+ // plain _bmad-output/ artifact path is left alone. Keyed on the .md FILENAME
51
+ // (not requiring dir == filename) so it covers every form findStaleRefs flags,
52
+ // keeping --fix and --check in lockstep (no flag-but-cannot-fix gate trap).
53
+ [/[@_]bmad-output\/bmb-creations\/[a-z0-9-]+\/([a-z0-9-]+)\.md/gi, (_m, n) => agentRef(n)],
54
+ // agent path, flat or nested: (@bmad|_bmad)/(*|module)/agents/<name>(/<name>)?.md
55
+ [/(?:@bmad|_bmad)\/(?:\*|[a-z0-9_-]+)\/agents\/([a-z0-9-]+)(?:\/[a-z0-9-]+)?\.md/gi, (_m, n) => agentRef(n)],
56
+ // generic _bmad/ path prefix. Does not touch _bmad-output (that is _bmad- then
57
+ // a hyphen, never _bmad followed by a slash).
58
+ [/_bmad\//g, '_byan/'],
59
+ // generic @bmad/ path prefix. Does not touch @bmad- invocation syntax.
60
+ [/@bmad\//g, '_byan/'],
61
+ ];
62
+
63
+ // Pure: rewrite stale path tokens. Returns { text, changed }.
64
+ export function normalizeText(text) {
65
+ let out = text;
66
+ for (const [re, rep] of RULES) out = out.replace(re, rep);
67
+ return { text: out, changed: out !== text };
68
+ }
69
+
70
+ // Pure: the stale path refs a clean file must NOT contain. A path token
71
+ // (`@bmad/` or `_bmad/`) or a bmb-creations agent load. Invocation `@bmad-` and
72
+ // plain `_bmad-output/` artifacts are excluded by construction.
73
+ // The generic arm uses [^...]* (zero-or-more) so a bare `@bmad/` or `_bmad/`
74
+ // prefix is flagged even when the next char is an excluded terminator (space,
75
+ // paren, quote, backtick) — rule 3/4 rewrite the prefix regardless, so --check
76
+ // must flag it regardless too.
77
+ const STALE_RE = /(?:[@_]bmad-output\/bmb-creations\/[a-z0-9-]+\/[a-z0-9-]+\.md|(?:@bmad|_bmad)\/[^\s)`'"]*)/gi;
78
+ export function findStaleRefs(text) {
79
+ const m = text.match(STALE_RE);
80
+ return m ? [...new Set(m)] : [];
81
+ }
82
+
83
+ // Recursively list root-relative POSIX paths of every .md file under dir. Returns
84
+ // [] if dir does not exist (an installed user without these dirs is not an error).
85
+ export function walkRelMd(dir, { io = fs, base = dir } = {}) {
86
+ let entries;
87
+ try {
88
+ entries = io.readdirSync(dir, { withFileTypes: true });
89
+ } catch {
90
+ return [];
91
+ }
92
+ const out = [];
93
+ for (const e of entries) {
94
+ const full = path.join(dir, e.name);
95
+ if (e.isDirectory()) {
96
+ out.push(...walkRelMd(full, { io, base }));
97
+ } else if (e.isFile() && e.name.endsWith('.md')) {
98
+ out.push(path.relative(base, full).split(path.sep).join('/'));
99
+ }
100
+ }
101
+ return out;
102
+ }
103
+
104
+ // Every stub .md file (root-relative POSIX), root dirs + their template twins.
105
+ export function listStubFiles({ rootDir, io = fs } = {}) {
106
+ const files = [];
107
+ for (const d of STUB_DIRS) {
108
+ files.push(...walkRelMd(path.join(rootDir, d), { io, base: rootDir }));
109
+ files.push(...walkRelMd(path.join(rootDir, TEMPLATE_PREFIX, d), { io, base: rootDir }));
110
+ }
111
+ return files;
112
+ }
113
+
114
+ // Plan: which tracked stub files would change under normalization.
115
+ export function planFix({ rootDir, io = fs } = {}) {
116
+ const files = listStubFiles({ rootDir, io });
117
+ const toFix = [];
118
+ for (const rel of files) {
119
+ const { changed } = normalizeText(io.readFileSync(path.join(rootDir, rel), 'utf8'));
120
+ if (changed) toFix.push(rel);
121
+ }
122
+ return { toFix, scanned: files.length };
123
+ }
124
+
125
+ // Apply: normalize every file that needs it. Each write is atomic (stage adjacent
126
+ // tmp, rename over the target) so a crash never leaves a half-written stub.
127
+ export function applyFix({ rootDir, io = fs } = {}) {
128
+ const { toFix, scanned } = planFix({ rootDir, io });
129
+ for (const rel of toFix) {
130
+ const dest = path.join(rootDir, rel);
131
+ const { text } = normalizeText(io.readFileSync(dest, 'utf8'));
132
+ const tmp = `${dest}.tmp`;
133
+ try {
134
+ io.writeFileSync(tmp, text);
135
+ io.chmodSync(tmp, io.statSync(dest).mode & 0o777);
136
+ io.renameSync(tmp, dest);
137
+ } catch (err) {
138
+ try {
139
+ io.unlinkSync(tmp);
140
+ } catch {
141
+ void 0;
142
+ }
143
+ throw err;
144
+ }
145
+ }
146
+ return { fixed: toFix, scanned };
147
+ }
148
+
149
+ // Check: the drift verdict for --check. ok=true when no stale ref remains.
150
+ export function check({ rootDir, io = fs } = {}) {
151
+ const files = listStubFiles({ rootDir, io });
152
+ const stale = [];
153
+ for (const rel of files) {
154
+ const refs = findStaleRefs(io.readFileSync(path.join(rootDir, rel), 'utf8'));
155
+ if (refs.length) stale.push({ file: rel, refs });
156
+ }
157
+ return { stale, ok: stale.length === 0, scanned: files.length };
158
+ }
@@ -292,7 +292,7 @@ const tools = [
292
292
  {
293
293
  name: 'byan_dispatch',
294
294
  description:
295
- 'BYAN Dispatcher: given a task description and complexity score (0-100), route it to the optimal execution target. Rule-based, no API call. Returns route and reasoning.',
295
+ 'BYAN Dispatcher: routes a unit of work along two independent axes. STRATEGY (where it runs: main-thread / agent-subagent-worktree / mcp-worker) from the scalar score + parallelizable. TIER (which model) from the task NATURE via native-tiers (the single source of truth): only exploration downgrades to haiku; implementation/verification/analysis stay deep (inherit the session model); never pins up to opus. Rule-based, no API call. Returns { score, strategy, nature, tier, model, reasoning }.',
296
296
  inputSchema: {
297
297
  type: 'object',
298
298
  properties: {
@@ -305,6 +305,11 @@ const tools = [
305
305
  type: 'boolean',
306
306
  description: 'Is the task parallelizable with other tasks?',
307
307
  },
308
+ nature: {
309
+ type: 'string',
310
+ enum: ['exploration', 'implementation', 'verification', 'analysis'],
311
+ description: 'Optional task nature. A valid value sets the model tier directly; otherwise the nature is classified from the task text. Only exploration is downgrade-safe.',
312
+ },
308
313
  },
309
314
  required: ['task'],
310
315
  additionalProperties: false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-byan-agent",
3
- "version": "2.21.0",
3
+ "version": "2.23.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 (Copilot CLI, Claude Code, Codex). Merise Agile + TDD + 71 Mantras. ~54% LLM cost savings.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -1,12 +0,0 @@
1
- ---
2
- name: byan-byan-test
3
- description: BYAN Test - Token Optimized Version (-46%)
4
- ---
5
-
6
- # byan-test
7
-
8
- ## Rules
9
-
10
- - This is a TEST version of BYAN optimized for token reduction (-46%)
11
- - Full agent: _byan/bmb/agents/byan-test.md (116 lines vs 215 original)
12
- - Original BYAN still available via bmad-agent-byan
@@ -1,7 +0,0 @@
1
- ---
2
- name: byan-test-dynamic
3
- description: Test Dynamic Loading - Phase B Validation
4
- ---
5
-
6
- # test-dynamic
7
-