create-byan-agent 2.53.0 → 2.54.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,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
+ ## [2.54.0] - 2026-07-21
13
+
14
+ ### Added — Garde de fraicheur des skills (SessionStart)
15
+ - Nouveau hook `skill-freshness-check.js` (coeur pur `lib/skill-freshness.js`) :
16
+ au demarrage de session, compare chaque `.claude/skills/<n>/SKILL.md` du
17
+ projet avec la copie globale homonyme `~/.claude/skills/<n>/SKILL.md`, par
18
+ CONTENU (l'egalite d'octets dit "fidele" ; une date recente ne dit rien).
19
+ En cas de divergence, injecte un signalement borne (6 noms max) avec la
20
+ commande de synchro exacte. Silencieux quand tout est fidele, quand la copie
21
+ globale n'existe pas, ou quand le projet n'a pas de skills ; sort en 0 dans
22
+ tous les chemins (ne bloque pas une session).
23
+ - Ferme le piege constate le 2026-07-21 : une copie globale du 30 juin masquait
24
+ le skill projet — le rail auto-dispatch livre en 2.53.0 ne se declenchait pas
25
+ car `/byan-byan` chargeait la copie perimee. Point verifie aupres de la doc
26
+ officielle Claude Code : la regle de priorite skills user vs projet en cas de
27
+ collision de nom n'y est pas clairement documentee (le hook signale donc le
28
+ FAIT de la divergence, sans affirmer une regle de chargement).
29
+ - Aucune ecriture automatique dans `~/.claude/skills` : une variante user-level
30
+ peut etre deliberee — le hook signale, l'humain tranche.
31
+ - Cablage : enregistre sous SessionStart dans `.claude/settings.json` (repo +
32
+ template) ; les deux fichiers shippent via le miroir `template-sync.js`.
33
+
12
34
  ## [2.53.0] - 2026-07-21
13
35
 
14
36
  ### Added — Rail automatique : workflow natif byan-auto-dispatch (F2)
@@ -0,0 +1,83 @@
1
+ 'use strict';
2
+
3
+ // skill-freshness — the pure core of the stale-global-skill guard.
4
+ //
5
+ // The trap this closes (observed 2026-07-21): a manual copy of a skill under
6
+ // ~/.claude/skills/<name>/SKILL.md drifts silently, and the slash command can
7
+ // load THAT copy instead of the project's .claude/skills/<name>/SKILL.md — so a
8
+ // feature shipped in the project skill stays invisible (the 2.53.0 auto-dispatch
9
+ // rail did not fire because of a June 30 global copy). The official Claude Code
10
+ // docs do not clearly settle the name-collision priority between the two levels
11
+ // (checked 2026-07-21), so this guard flags the FACT — the two files differ —
12
+ // and hands the human the exact sync command; it does not claim a loading rule
13
+ // and it never writes into ~/.claude itself (a user-level variant may be
14
+ // deliberate; the human decides).
15
+ //
16
+ // Pure: comparison + decision + message. The I/O shell (SessionStart hook)
17
+ // feeds it directories and prints the reminder.
18
+
19
+ const fs = require('fs');
20
+ const path = require('path');
21
+
22
+ // Compare the project's skills with same-named global copies, by CONTENT.
23
+ // A newer mtime does not mean "up to date"; byte equality means "faithful".
24
+ // Returns { diverged: [name...], checked: <count> }; every fs error on one
25
+ // skill is swallowed (that skill is simply not compared) so a permission oddity
26
+ // can never break the caller.
27
+ function compareSkills({ projectSkillsDir, globalSkillsDir }) {
28
+ const out = { diverged: [], checked: 0 };
29
+ let names;
30
+ try {
31
+ names = fs.readdirSync(projectSkillsDir, { withFileTypes: true })
32
+ .filter((e) => e.isDirectory())
33
+ .map((e) => e.name);
34
+ } catch {
35
+ return out; // project has no skills dir -> nothing to compare
36
+ }
37
+ for (const name of names) {
38
+ try {
39
+ const proj = path.join(projectSkillsDir, name, 'SKILL.md');
40
+ const glob = path.join(globalSkillsDir, name, 'SKILL.md');
41
+ if (!fs.existsSync(glob)) continue; // no global twin -> no masking risk
42
+ const a = fs.readFileSync(proj);
43
+ const b = fs.readFileSync(glob);
44
+ out.checked += 1;
45
+ if (!a.equals(b)) out.diverged.push(name);
46
+ } catch {
47
+ // unreadable pair -> skip silently (never block a session start)
48
+ }
49
+ }
50
+ return out;
51
+ }
52
+
53
+ // Bounded, factual reminder. Empty string when nothing diverged (the hook then
54
+ // prints nothing at all). Names are capped so a badly drifted machine does not
55
+ // flood the context; the sync command is exact and copy-pastable.
56
+ const MAX_NAMES = 6;
57
+
58
+ // A skill dir name lands verbatim in the injected context and in the suggested
59
+ // cp command. Legit names are kebab-case; strip control chars and whitespace
60
+ // runs from anything else so a crafted dir name cannot shape the reminder.
61
+ function cleanName(n) {
62
+ return String(n).replace(/[\u0000-\u001F\u007F]+/g, ' ').replace(/\s+/g, ' ').trim();
63
+ }
64
+
65
+ function formatReminder(diverged, { projectSkillsDir = '.claude/skills', globalSkillsDir = '~/.claude/skills' } = {}) {
66
+ if (!Array.isArray(diverged) || diverged.length === 0) return '';
67
+ const names = diverged.map(cleanName);
68
+ const shown = names.slice(0, MAX_NAMES).join(', ');
69
+ const more = names.length > MAX_NAMES ? ` (+${names.length - MAX_NAMES} autres)` : '';
70
+ const one = names[0];
71
+ return [
72
+ `Skills globaux divergents detectes : ${shown}${more}.`,
73
+ `La copie ${globalSkillsDir}/<nom>/SKILL.md differe du skill projet — elle peut etre celle que la commande /<nom> charge (constate sur ce piege le 2026-07-21).`,
74
+ `Synchronise depuis le projet (ex: cp ${projectSkillsDir}/${one}/SKILL.md ${globalSkillsDir}/${one}/SKILL.md) ou supprime la copie globale si elle n'est pas voulue.`,
75
+ `Rien n'est modifie automatiquement : une variante user-level peut etre deliberee.`,
76
+ ].join(' ');
77
+ }
78
+
79
+ module.exports = {
80
+ compareSkills,
81
+ formatReminder,
82
+ MAX_NAMES,
83
+ };
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ // SessionStart hook — stale-global-skill guard (I/O shell over
5
+ // lib/skill-freshness.js).
6
+ //
7
+ // At each session start, compare the project's .claude/skills/<n>/SKILL.md with
8
+ // the same-named ~/.claude/skills/<n>/SKILL.md. On a content divergence, inject
9
+ // one bounded factual reminder (names + exact sync command). Silent when
10
+ // everything is faithful, when there is no global copy, or when the project has
11
+ // no skills. NEVER blocks a session: exit 0 on every path, including internal
12
+ // errors. It never writes into ~/.claude — it reports, the human decides.
13
+
14
+ const os = require('os');
15
+ const path = require('path');
16
+ const freshness = require('./lib/skill-freshness');
17
+
18
+ function buildContext(projectDir, homeDir) {
19
+ const projectSkillsDir = path.join(projectDir, '.claude', 'skills');
20
+ const globalSkillsDir = path.join(homeDir, '.claude', 'skills');
21
+ const { diverged } = freshness.compareSkills({ projectSkillsDir, globalSkillsDir });
22
+ return freshness.formatReminder(diverged, {
23
+ projectSkillsDir: '.claude/skills',
24
+ globalSkillsDir: path.join(homeDir, '.claude', 'skills'),
25
+ });
26
+ }
27
+
28
+ if (require.main === module) {
29
+ try {
30
+ const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
31
+ const ctx = buildContext(projectDir, os.homedir());
32
+ if (ctx) {
33
+ process.stdout.write(
34
+ JSON.stringify({
35
+ hookSpecificOutput: {
36
+ hookEventName: 'SessionStart',
37
+ additionalContext: ctx,
38
+ },
39
+ })
40
+ );
41
+ }
42
+ } catch {
43
+ // A guard must never take a session down with it.
44
+ }
45
+ process.exit(0);
46
+ }
47
+
48
+ module.exports = { buildContext };
@@ -15,6 +15,10 @@
15
15
  {
16
16
  "type": "command",
17
17
  "command": "p=\"$CLAUDE_PROJECT_DIR/.claude/hooks/soul-memory-check.js\"; [ -f \"$p\" ] || exit 0; exec node \"$p\""
18
+ },
19
+ {
20
+ "type": "command",
21
+ "command": "p=\"$CLAUDE_PROJECT_DIR/.claude/hooks/skill-freshness-check.js\"; [ -f \"$p\" ] || exit 0; exec node \"$p\""
18
22
  }
19
23
  ]
20
24
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-byan-agent",
3
- "version": "2.53.0",
3
+ "version": "2.54.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": {