create-byan-agent 2.9.7 → 2.9.8

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
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ---
9
9
 
10
+ ## [2.9.8] - 2026-04-21
11
+
12
+ ### Fixed - `update-byan-agent` CLI broken on fresh npm install
13
+
14
+ - **`update-byan-agent/` added to `package.json` `files` array** — previously only `update-byan-agent/bin/` shipped (because it was listed in `bin`), but `update-byan-agent/lib/` (analyzer, backup, customization-detector) was missing. Running `update-byan-agent` crashed with `Cannot find module '../lib/analyzer'`.
15
+ - **Observed on**: npm install 2.9.7, `cd ~/byan_web && update-byan-agent` → `MODULE_NOT_FOUND`.
16
+
17
+ ---
18
+
10
19
  ## [2.9.7] - 2026-04-21
11
20
 
12
21
  ### Fixed - MCP server empty-directory install bug
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-byan-agent",
3
- "version": "2.9.7",
3
+ "version": "2.9.8",
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 + 64 Mantras. ~54% LLM cost savings.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -48,6 +48,7 @@
48
48
  "files": [
49
49
  "src/",
50
50
  "install/",
51
+ "update-byan-agent/",
51
52
  "README.md",
52
53
  "CHANGELOG.md",
53
54
  "LICENSE"
@@ -0,0 +1,136 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const https = require('https');
4
+ const yaml = require('js-yaml');
5
+
6
+ /**
7
+ * Analyzer - Version checking and comparison module
8
+ * Validates current BYAN installation version vs npm registry
9
+ */
10
+ class Analyzer {
11
+ constructor(installPath) {
12
+ this.installPath = installPath;
13
+ this.configPath = path.join(installPath, '_byan', 'bmb', 'config.yaml');
14
+ }
15
+
16
+ /**
17
+ * Check current version from _byan/bmb/config.yaml
18
+ * @returns {Promise<string|null>} Current version or null if not found
19
+ */
20
+ async checkCurrentVersion() {
21
+ try {
22
+ if (!fs.existsSync(this.configPath)) {
23
+ return null;
24
+ }
25
+
26
+ const configContent = fs.readFileSync(this.configPath, 'utf8');
27
+ const config = yaml.load(configContent);
28
+
29
+ return config.byan_version || null;
30
+ } catch (error) {
31
+ throw new Error(`Erreur lecture config.yaml: ${error.message}`);
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Fetch latest version from npm registry
37
+ * @returns {Promise<string>} Latest version available on npm
38
+ */
39
+ async fetchLatestVersion() {
40
+ return new Promise((resolve, reject) => {
41
+ const options = {
42
+ hostname: 'registry.npmjs.org',
43
+ path: '/create-byan-agent/latest',
44
+ method: 'GET',
45
+ headers: {
46
+ 'Accept': 'application/json'
47
+ }
48
+ };
49
+
50
+ const req = https.request(options, (res) => {
51
+ let data = '';
52
+
53
+ res.on('data', (chunk) => {
54
+ data += chunk;
55
+ });
56
+
57
+ res.on('end', () => {
58
+ try {
59
+ const json = JSON.parse(data);
60
+ resolve(json.version);
61
+ } catch (error) {
62
+ reject(new Error(`Erreur parsing npm response: ${error.message}`));
63
+ }
64
+ });
65
+ });
66
+
67
+ req.on('error', (error) => {
68
+ reject(new Error(`Erreur connexion npm registry: ${error.message}`));
69
+ });
70
+
71
+ req.end();
72
+ });
73
+ }
74
+
75
+ /**
76
+ * Compare two semver versions
77
+ * @param {string} current - Current version (e.g., "2.6.0")
78
+ * @param {string} latest - Latest version (e.g., "2.6.1")
79
+ * @returns {number} -1 if current < latest, 0 if equal, 1 if current > latest
80
+ */
81
+ compare(current, latest) {
82
+ const parseCurrent = this._parseSemver(current);
83
+ const parseLatest = this._parseSemver(latest);
84
+
85
+ if (parseCurrent.major !== parseLatest.major) {
86
+ return parseCurrent.major < parseLatest.major ? -1 : 1;
87
+ }
88
+ if (parseCurrent.minor !== parseLatest.minor) {
89
+ return parseCurrent.minor < parseLatest.minor ? -1 : 1;
90
+ }
91
+ if (parseCurrent.patch !== parseLatest.patch) {
92
+ return parseCurrent.patch < parseLatest.patch ? -1 : 1;
93
+ }
94
+
95
+ return 0;
96
+ }
97
+
98
+ /**
99
+ * Parse semver string to object
100
+ * @param {string} version - Version string (e.g., "2.6.1")
101
+ * @returns {{major: number, minor: number, patch: number}}
102
+ */
103
+ _parseSemver(version) {
104
+ const parts = version.split('.').map(Number);
105
+ return {
106
+ major: parts[0] || 0,
107
+ minor: parts[1] || 0,
108
+ patch: parts[2] || 0
109
+ };
110
+ }
111
+
112
+ /**
113
+ * Check if update is available
114
+ * @returns {Promise<{current: string, latest: string, needsUpdate: boolean}>}
115
+ */
116
+ async checkVersion() {
117
+ const current = await this.checkCurrentVersion();
118
+ const latest = await this.fetchLatestVersion();
119
+
120
+ if (!current) {
121
+ throw new Error('Version actuelle introuvable dans config.yaml');
122
+ }
123
+
124
+ const comparison = this.compare(current, latest);
125
+
126
+ return {
127
+ current,
128
+ latest,
129
+ needsUpdate: comparison < 0,
130
+ upToDate: comparison === 0,
131
+ ahead: comparison > 0
132
+ };
133
+ }
134
+ }
135
+
136
+ module.exports = Analyzer;
@@ -0,0 +1,204 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { execSync } = require('child_process');
4
+
5
+ /**
6
+ * Backup - Safe backup and restore operations for BYAN updates
7
+ * Creates timestamped backups and manages rollback functionality
8
+ */
9
+ class Backup {
10
+ constructor(installPath) {
11
+ this.installPath = installPath;
12
+ this.byanDir = path.join(installPath, '_byan');
13
+ this.backupBaseDir = path.join(installPath, '_byan.backup');
14
+ }
15
+
16
+ /**
17
+ * Create backup of _byan directory with timestamp
18
+ * @returns {Promise<string>} Path to created backup
19
+ */
20
+ async create() {
21
+ try {
22
+ if (!fs.existsSync(this.byanDir)) {
23
+ throw new Error('Directory _byan/ introuvable');
24
+ }
25
+
26
+ // Create backup directory with timestamp
27
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').split('T')[0] +
28
+ '_' + new Date().toTimeString().split(' ')[0].replace(/:/g, '-');
29
+ const backupPath = path.join(this.backupBaseDir, `backup-${timestamp}`);
30
+
31
+ // Create backup base directory if not exists
32
+ if (!fs.existsSync(this.backupBaseDir)) {
33
+ fs.mkdirSync(this.backupBaseDir, { recursive: true });
34
+ }
35
+
36
+ // Copy _byan to backup location
37
+ this._copyRecursive(this.byanDir, backupPath);
38
+
39
+ // Save backup metadata
40
+ const metadataPath = path.join(backupPath, '.backup-metadata.json');
41
+ const metadata = {
42
+ timestamp: new Date().toISOString(),
43
+ originalPath: this.byanDir,
44
+ backupPath: backupPath
45
+ };
46
+ fs.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2));
47
+
48
+ return backupPath;
49
+ } catch (error) {
50
+ throw new Error(`Erreur creation backup: ${error.message}`);
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Restore from backup directory
56
+ * @param {string} backupPath - Path to backup directory (optional, uses latest if not provided)
57
+ * @returns {Promise<boolean>} Success status
58
+ */
59
+ async restore(backupPath = null) {
60
+ try {
61
+ // If no backup path provided, find latest
62
+ if (!backupPath) {
63
+ backupPath = await this.getLatestBackup();
64
+ }
65
+
66
+ if (!fs.existsSync(backupPath)) {
67
+ throw new Error(`Backup introuvable: ${backupPath}`);
68
+ }
69
+
70
+ // Remove current _byan directory
71
+ if (fs.existsSync(this.byanDir)) {
72
+ this._removeRecursive(this.byanDir);
73
+ }
74
+
75
+ // Restore from backup
76
+ this._copyRecursive(backupPath, this.byanDir);
77
+
78
+ // Remove backup metadata file from restored directory
79
+ const metadataPath = path.join(this.byanDir, '.backup-metadata.json');
80
+ if (fs.existsSync(metadataPath)) {
81
+ fs.unlinkSync(metadataPath);
82
+ }
83
+
84
+ return true;
85
+ } catch (error) {
86
+ throw new Error(`Erreur restoration backup: ${error.message}`);
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Get latest backup path
92
+ * @returns {Promise<string|null>} Latest backup path or null
93
+ */
94
+ async getLatestBackup() {
95
+ if (!fs.existsSync(this.backupBaseDir)) {
96
+ return null;
97
+ }
98
+
99
+ const backups = fs.readdirSync(this.backupBaseDir)
100
+ .filter(name => name.startsWith('backup-'))
101
+ .map(name => path.join(this.backupBaseDir, name))
102
+ .filter(p => fs.statSync(p).isDirectory())
103
+ .sort((a, b) => {
104
+ const statA = fs.statSync(a);
105
+ const statB = fs.statSync(b);
106
+ return statB.mtime.getTime() - statA.mtime.getTime();
107
+ });
108
+
109
+ return backups.length > 0 ? backups[0] : null;
110
+ }
111
+
112
+ /**
113
+ * List all available backups
114
+ * @returns {Promise<Array>} List of backup info objects
115
+ */
116
+ async listBackups() {
117
+ if (!fs.existsSync(this.backupBaseDir)) {
118
+ return [];
119
+ }
120
+
121
+ const backups = fs.readdirSync(this.backupBaseDir)
122
+ .filter(name => name.startsWith('backup-'))
123
+ .map(name => {
124
+ const backupPath = path.join(this.backupBaseDir, name);
125
+ const metadataPath = path.join(backupPath, '.backup-metadata.json');
126
+
127
+ let metadata = {};
128
+ if (fs.existsSync(metadataPath)) {
129
+ metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf8'));
130
+ }
131
+
132
+ const stats = fs.statSync(backupPath);
133
+
134
+ return {
135
+ name,
136
+ path: backupPath,
137
+ timestamp: metadata.timestamp || stats.mtime.toISOString(),
138
+ size: this._getDirectorySize(backupPath)
139
+ };
140
+ })
141
+ .sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp));
142
+
143
+ return backups;
144
+ }
145
+
146
+ /**
147
+ * Copy directory recursively
148
+ * @param {string} src - Source directory
149
+ * @param {string} dest - Destination directory
150
+ */
151
+ _copyRecursive(src, dest) {
152
+ if (!fs.existsSync(dest)) {
153
+ fs.mkdirSync(dest, { recursive: true });
154
+ }
155
+
156
+ const entries = fs.readdirSync(src, { withFileTypes: true });
157
+
158
+ for (const entry of entries) {
159
+ const srcPath = path.join(src, entry.name);
160
+ const destPath = path.join(dest, entry.name);
161
+
162
+ if (entry.isDirectory()) {
163
+ this._copyRecursive(srcPath, destPath);
164
+ } else {
165
+ fs.copyFileSync(srcPath, destPath);
166
+ }
167
+ }
168
+ }
169
+
170
+ /**
171
+ * Remove directory recursively
172
+ * @param {string} dirPath - Directory to remove
173
+ */
174
+ _removeRecursive(dirPath) {
175
+ if (fs.existsSync(dirPath)) {
176
+ fs.rmSync(dirPath, { recursive: true, force: true });
177
+ }
178
+ }
179
+
180
+ /**
181
+ * Get directory size in bytes
182
+ * @param {string} dirPath - Directory path
183
+ * @returns {number} Size in bytes
184
+ */
185
+ _getDirectorySize(dirPath) {
186
+ let size = 0;
187
+
188
+ const entries = fs.readdirSync(dirPath, { withFileTypes: true });
189
+
190
+ for (const entry of entries) {
191
+ const entryPath = path.join(dirPath, entry.name);
192
+
193
+ if (entry.isDirectory()) {
194
+ size += this._getDirectorySize(entryPath);
195
+ } else {
196
+ size += fs.statSync(entryPath).size;
197
+ }
198
+ }
199
+
200
+ return size;
201
+ }
202
+ }
203
+
204
+ module.exports = Backup;
@@ -0,0 +1,7 @@
1
+ // Conflict-resolver Module - MVP placeholder
2
+ class conflict-resolver {
3
+ constructor(installPath) {
4
+ this.installPath = installPath;
5
+ }
6
+ }
7
+ module.exports = conflict-resolver;
@@ -0,0 +1,75 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ /**
5
+ * CustomizationDetector - Detects user customizations in BYAN files
6
+ * Helps preserve user changes during updates
7
+ */
8
+ class CustomizationDetector {
9
+ constructor(installPath) {
10
+ this.installPath = installPath;
11
+ this.byanDir = path.join(installPath, '_byan');
12
+ }
13
+
14
+ /**
15
+ * Detect customized files that should be preserved
16
+ * @returns {Promise<Array>} List of customized file paths
17
+ */
18
+ async detectCustomizations() {
19
+ const customizations = [];
20
+
21
+ // Preserve config.yaml
22
+ const configPath = path.join(this.byanDir, 'bmb', 'config.yaml');
23
+ if (fs.existsSync(configPath)) {
24
+ customizations.push({
25
+ path: configPath,
26
+ type: 'config',
27
+ preserve: true
28
+ });
29
+ }
30
+
31
+ // Preserve _memory directory
32
+ const memoryDir = path.join(this.byanDir, '_memory');
33
+ if (fs.existsSync(memoryDir)) {
34
+ customizations.push({
35
+ path: memoryDir,
36
+ type: 'memory',
37
+ preserve: true
38
+ });
39
+ }
40
+
41
+ // Preserve _byan-output directory
42
+ const outputDir = path.join(this.installPath, '_byan-output');
43
+ if (fs.existsSync(outputDir)) {
44
+ customizations.push({
45
+ path: outputDir,
46
+ type: 'output',
47
+ preserve: true
48
+ });
49
+ }
50
+
51
+ return customizations;
52
+ }
53
+
54
+ /**
55
+ * Check if a file has been modified by user
56
+ * @param {string} filePath - File to check
57
+ * @returns {Promise<boolean>} True if modified
58
+ */
59
+ async isModified(filePath) {
60
+ // Simple check based on modification time
61
+ // In a more advanced version, could compare with original checksums
62
+ if (!fs.existsSync(filePath)) {
63
+ return false;
64
+ }
65
+
66
+ const stats = fs.statSync(filePath);
67
+ const now = Date.now();
68
+ const hoursSinceModification = (now - stats.mtime.getTime()) / (1000 * 60 * 60);
69
+
70
+ // If modified in last 24h, consider it customized
71
+ return hoursSinceModification < 24;
72
+ }
73
+ }
74
+
75
+ module.exports = CustomizationDetector;
@@ -0,0 +1,7 @@
1
+ // Validator Module - MVP placeholder
2
+ class validator {
3
+ constructor(installPath) {
4
+ this.installPath = installPath;
5
+ }
6
+ }
7
+ module.exports = validator;
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "update-byan-agent",
3
+ "version": "1.0.0",
4
+ "description": "CLI tool for managing BYAN updates with intelligent conflict detection and customization preservation",
5
+ "bin": {
6
+ "update-byan-agent": "bin/update-byan-agent.js"
7
+ },
8
+ "scripts": {
9
+ "start": "node bin/update-byan-agent.js",
10
+ "test": "jest",
11
+ "lint": "eslint bin/ lib/",
12
+ "prepublish": "npm test"
13
+ },
14
+ "keywords": [
15
+ "byan",
16
+ "update",
17
+ "version-management",
18
+ "conflict-resolution",
19
+ "backup",
20
+ "merge",
21
+ "ai-agent",
22
+ "bmad",
23
+ "merise",
24
+ "tdd"
25
+ ],
26
+ "author": "Yan",
27
+ "license": "MIT",
28
+ "dependencies": {
29
+ "chalk": "^4.1.2",
30
+ "commander": "^11.1.0",
31
+ "diff": "^5.1.0",
32
+ "fs-extra": "^11.2.0",
33
+ "inquirer": "^8.2.5",
34
+ "js-yaml": "^4.1.0",
35
+ "ora": "^5.4.1"
36
+ },
37
+ "devDependencies": {
38
+ "eslint": "^8.50.0",
39
+ "jest": "^29.7.0"
40
+ },
41
+ "engines": {
42
+ "node": ">=18.0.0"
43
+ },
44
+ "files": [
45
+ "bin/",
46
+ "lib/",
47
+ "README.md",
48
+ "CHANGELOG.md",
49
+ "LICENSE"
50
+ ]
51
+ }