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.
|
|
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": {
|