ba-kit-cli 2.0.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/README.md +160 -0
- package/bin/ba-kit.js +2 -0
- package/dist/commands/cache.d.ts +8 -0
- package/dist/commands/cache.d.ts.map +1 -0
- package/dist/commands/cache.js +43 -0
- package/dist/commands/cache.js.map +1 -0
- package/dist/commands/completion.d.ts +9 -0
- package/dist/commands/completion.d.ts.map +1 -0
- package/dist/commands/completion.js +172 -0
- package/dist/commands/completion.js.map +1 -0
- package/dist/commands/init.d.ts +6 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +142 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/restore.d.ts +6 -0
- package/dist/commands/restore.d.ts.map +1 -0
- package/dist/commands/restore.js +168 -0
- package/dist/commands/restore.js.map +1 -0
- package/dist/commands/status.d.ts +6 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +154 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/update.d.ts +6 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +300 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +120 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/cache.d.ts +50 -0
- package/dist/lib/cache.d.ts.map +1 -0
- package/dist/lib/cache.js +152 -0
- package/dist/lib/cache.js.map +1 -0
- package/dist/lib/config.d.ts +25 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +67 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/copier.d.ts +10 -0
- package/dist/lib/copier.d.ts.map +1 -0
- package/dist/lib/copier.js +79 -0
- package/dist/lib/copier.js.map +1 -0
- package/dist/lib/differ.d.ts +16 -0
- package/dist/lib/differ.d.ts.map +1 -0
- package/dist/lib/differ.js +51 -0
- package/dist/lib/differ.js.map +1 -0
- package/dist/lib/downloader.d.ts +11 -0
- package/dist/lib/downloader.d.ts.map +1 -0
- package/dist/lib/downloader.js +110 -0
- package/dist/lib/downloader.js.map +1 -0
- package/dist/lib/hasher.d.ts +18 -0
- package/dist/lib/hasher.d.ts.map +1 -0
- package/dist/lib/hasher.js +54 -0
- package/dist/lib/hasher.js.map +1 -0
- package/dist/lib/manifest.d.ts +30 -0
- package/dist/lib/manifest.d.ts.map +1 -0
- package/dist/lib/manifest.js +72 -0
- package/dist/lib/manifest.js.map +1 -0
- package/dist/types.d.ts +91 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +21 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/constants.d.ts +13 -0
- package/dist/utils/constants.d.ts.map +1 -0
- package/dist/utils/constants.js +38 -0
- package/dist/utils/constants.js.map +1 -0
- package/dist/utils/errors.d.ts +60 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +99 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/logger.d.ts +43 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +107 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/prompts.d.ts +18 -0
- package/dist/utils/prompts.d.ts.map +1 -0
- package/dist/utils/prompts.js +63 -0
- package/dist/utils/prompts.js.map +1 -0
- package/man/ba-kit.1 +194 -0
- package/package.json +77 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { join, dirname } from 'path';
|
|
2
|
+
import { copyFile, mkdir, stat } from 'fs/promises';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { log, spinner } from '../utils/logger.js';
|
|
5
|
+
import { CONFIG } from '../utils/constants.js';
|
|
6
|
+
import { manifestExists, readManifest, writeManifest } from '../lib/manifest.js';
|
|
7
|
+
import { hashFile } from '../lib/hasher.js';
|
|
8
|
+
import { downloadBaKit, cleanupDownload } from '../lib/downloader.js';
|
|
9
|
+
import { selectFilesToRestore, confirmRestore } from '../utils/prompts.js';
|
|
10
|
+
/**
|
|
11
|
+
* Restore modified files to original
|
|
12
|
+
*/
|
|
13
|
+
export async function restore(file, options) {
|
|
14
|
+
const projectPath = process.cwd();
|
|
15
|
+
const agentDir = join(projectPath, CONFIG.AGENT_DIR);
|
|
16
|
+
// Check if ba-kit is installed
|
|
17
|
+
if (!await manifestExists(projectPath)) {
|
|
18
|
+
log.error('Not a BA-Kit project. Run `ba-kit init` first.');
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
const manifest = await readManifest(projectPath);
|
|
22
|
+
// Find modified files
|
|
23
|
+
const modifiedFiles = await findModifiedFiles(manifest, agentDir);
|
|
24
|
+
if (modifiedFiles.length === 0) {
|
|
25
|
+
log.success('No modified files to restore.');
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
// Determine which files to restore
|
|
29
|
+
let filesToRestore = [];
|
|
30
|
+
if (file) {
|
|
31
|
+
// Specific file requested
|
|
32
|
+
if (!modifiedFiles.includes(file)) {
|
|
33
|
+
log.error(`File not found or not modified: ${file}`);
|
|
34
|
+
log.dim('Modified files:');
|
|
35
|
+
modifiedFiles.forEach(f => log.dim(` - ${f}`));
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
filesToRestore = [file];
|
|
39
|
+
}
|
|
40
|
+
else if (options.all) {
|
|
41
|
+
// Restore all
|
|
42
|
+
filesToRestore = modifiedFiles;
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
// Interactive selection
|
|
46
|
+
console.log('');
|
|
47
|
+
console.log(chalk.bold('Modified Files:'));
|
|
48
|
+
modifiedFiles.forEach(f => console.log(` ${chalk.yellow('✎')} ${f}`));
|
|
49
|
+
console.log('');
|
|
50
|
+
filesToRestore = await selectFilesToRestore(modifiedFiles);
|
|
51
|
+
if (filesToRestore.length === 0) {
|
|
52
|
+
log.warn('No files selected.');
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// Confirm
|
|
57
|
+
if (!options.yes) {
|
|
58
|
+
const confirmed = await confirmRestore(filesToRestore.length);
|
|
59
|
+
if (!confirmed) {
|
|
60
|
+
log.warn('Aborted.');
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
let extractedPath;
|
|
65
|
+
let needsCleanup = true;
|
|
66
|
+
// Use local source if specified, otherwise download from GitHub
|
|
67
|
+
if (options.local) {
|
|
68
|
+
const localSpinner = spinner('Using local BA-Kit source...').start();
|
|
69
|
+
try {
|
|
70
|
+
extractedPath = options.local;
|
|
71
|
+
const s = await stat(extractedPath);
|
|
72
|
+
if (!s.isDirectory()) {
|
|
73
|
+
throw new Error('Local source must be a directory');
|
|
74
|
+
}
|
|
75
|
+
needsCleanup = false;
|
|
76
|
+
localSpinner.succeed('Using local source');
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
localSpinner.fail('Failed to use local source');
|
|
80
|
+
log.error(error.message);
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
const downloadSpinner = spinner('Fetching original files...').start();
|
|
86
|
+
try {
|
|
87
|
+
const result = await downloadBaKit(manifest.branch, projectPath);
|
|
88
|
+
extractedPath = result.extractedPath;
|
|
89
|
+
downloadSpinner.succeed('Fetched originals');
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
downloadSpinner.fail('Failed to fetch');
|
|
93
|
+
log.error(error.message);
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Restore files
|
|
98
|
+
const restoreSpinner = spinner('Restoring files...').start();
|
|
99
|
+
const restoredHashes = {};
|
|
100
|
+
try {
|
|
101
|
+
for (const filePath of filesToRestore) {
|
|
102
|
+
const entry = manifest.files[filePath];
|
|
103
|
+
if (!entry)
|
|
104
|
+
continue;
|
|
105
|
+
const destPath = join(agentDir, filePath);
|
|
106
|
+
const srcPath = join(extractedPath, entry.source_path);
|
|
107
|
+
// Backup first
|
|
108
|
+
const backupDir = join(projectPath, CONFIG.BACKUP_DIR, 'restore');
|
|
109
|
+
await mkdir(backupDir, { recursive: true });
|
|
110
|
+
const backupPath = join(backupDir, `${filePath.replace(/\//g, '_')}.${Date.now()}`);
|
|
111
|
+
try {
|
|
112
|
+
await copyFile(destPath, backupPath);
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
// File might be deleted, skip backup
|
|
116
|
+
}
|
|
117
|
+
// Copy original
|
|
118
|
+
await mkdir(dirname(destPath), { recursive: true });
|
|
119
|
+
await copyFile(srcPath, destPath);
|
|
120
|
+
// Update hash
|
|
121
|
+
const newHash = await hashFile(destPath);
|
|
122
|
+
restoredHashes[filePath] = {
|
|
123
|
+
...entry,
|
|
124
|
+
current_hash: newHash,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
restoreSpinner.succeed(`Restored ${filesToRestore.length} file(s)`);
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
restoreSpinner.fail('Failed to restore');
|
|
131
|
+
log.error(error.message);
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
// Update manifest
|
|
135
|
+
const newManifest = {
|
|
136
|
+
...manifest,
|
|
137
|
+
updated_at: new Date().toISOString(),
|
|
138
|
+
files: { ...manifest.files, ...restoredHashes },
|
|
139
|
+
};
|
|
140
|
+
await writeManifest(projectPath, newManifest);
|
|
141
|
+
// Cleanup
|
|
142
|
+
if (needsCleanup) {
|
|
143
|
+
await cleanupDownload(extractedPath);
|
|
144
|
+
}
|
|
145
|
+
console.log('');
|
|
146
|
+
log.success('Files restored to original state.');
|
|
147
|
+
log.dim(`Backups saved to ${CONFIG.BACKUP_DIR}/restore/`);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Find files that have been modified locally
|
|
151
|
+
*/
|
|
152
|
+
async function findModifiedFiles(manifest, agentDir) {
|
|
153
|
+
const modified = [];
|
|
154
|
+
for (const [filePath, entry] of Object.entries(manifest.files)) {
|
|
155
|
+
try {
|
|
156
|
+
const currentHash = await hashFile(join(agentDir, filePath));
|
|
157
|
+
if (currentHash !== entry.original_hash) {
|
|
158
|
+
modified.push(filePath);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
// File deleted - also counts as modified
|
|
163
|
+
modified.push(filePath);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return modified.sort();
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=restore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"restore.js","sourceRoot":"","sources":["../../src/commands/restore.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,aAAa,EAA0B,MAAM,aAAa,CAAC;AAEzE;;GAEG;AACH,wBAAsB,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAgElE"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { join } from 'path';
|
|
2
|
+
import { readdir } from 'fs/promises';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { log, table } from '../utils/logger.js';
|
|
5
|
+
import { CONFIG } from '../utils/constants.js';
|
|
6
|
+
import { manifestExists, readManifest } from '../lib/manifest.js';
|
|
7
|
+
import { hashFilesParallel } from '../lib/hasher.js';
|
|
8
|
+
import { downloadBaKit, cleanupDownload } from '../lib/downloader.js';
|
|
9
|
+
/**
|
|
10
|
+
* Show BA-Kit installation status
|
|
11
|
+
*/
|
|
12
|
+
export async function status(options) {
|
|
13
|
+
const projectPath = process.cwd();
|
|
14
|
+
const agentDir = join(projectPath, CONFIG.AGENT_DIR);
|
|
15
|
+
// Check if ba-kit is installed
|
|
16
|
+
if (!await manifestExists(projectPath)) {
|
|
17
|
+
log.error('Not a BA-Kit project. Run `ba-kit init` first.');
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
const manifest = await readManifest(projectPath);
|
|
21
|
+
// Header
|
|
22
|
+
console.log('');
|
|
23
|
+
console.log(chalk.bold('BA-Kit Status'));
|
|
24
|
+
console.log(chalk.dim('─'.repeat(50)));
|
|
25
|
+
console.log(` Installed: ${chalk.cyan('v' + manifest.version)}`);
|
|
26
|
+
console.log(` Branch: ${chalk.dim(manifest.branch)}`);
|
|
27
|
+
console.log(` Updated: ${chalk.dim(formatDate(manifest.updated_at))}`);
|
|
28
|
+
// Check remote if requested
|
|
29
|
+
if (options.remote) {
|
|
30
|
+
await checkRemote(manifest);
|
|
31
|
+
}
|
|
32
|
+
console.log(chalk.dim('─'.repeat(50)));
|
|
33
|
+
// Analyze files
|
|
34
|
+
const analysis = await analyzeFiles(manifest, agentDir);
|
|
35
|
+
// Print stats
|
|
36
|
+
const stats = {
|
|
37
|
+
unchanged: analysis.filter(f => f.status === 'unchanged').length,
|
|
38
|
+
modified: analysis.filter(f => f.status === 'modified').length,
|
|
39
|
+
deleted: analysis.filter(f => f.status === 'deleted').length,
|
|
40
|
+
added: analysis.filter(f => f.status === 'added').length,
|
|
41
|
+
};
|
|
42
|
+
console.log('');
|
|
43
|
+
console.log(` ${chalk.dim('○')} Unchanged: ${stats.unchanged}`);
|
|
44
|
+
console.log(` ${chalk.yellow('✎')} Modified: ${stats.modified}`);
|
|
45
|
+
console.log(` ${chalk.red('✗')} Deleted: ${stats.deleted}`);
|
|
46
|
+
console.log(` ${chalk.green('+')} Added: ${stats.added}`);
|
|
47
|
+
console.log('');
|
|
48
|
+
// Show details if any changes
|
|
49
|
+
if (stats.modified + stats.deleted > 0) {
|
|
50
|
+
console.log(chalk.bold('Changed Files:'));
|
|
51
|
+
for (const file of analysis) {
|
|
52
|
+
if (file.status !== 'unchanged' && file.status !== 'added') {
|
|
53
|
+
table.row(file.status, file.path);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
console.log('');
|
|
57
|
+
log.dim('Run `ba-kit update` to sync with upstream.');
|
|
58
|
+
}
|
|
59
|
+
else if (stats.added > 0) {
|
|
60
|
+
console.log(chalk.bold('User-Added Files:'));
|
|
61
|
+
for (const file of analysis.filter(f => f.status === 'added')) {
|
|
62
|
+
table.row('added', file.path);
|
|
63
|
+
}
|
|
64
|
+
console.log('');
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
log.success('All files are in sync with upstream.');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Analyze all files against manifest using parallel hashing
|
|
72
|
+
*/
|
|
73
|
+
async function analyzeFiles(manifest, agentDir) {
|
|
74
|
+
const results = [];
|
|
75
|
+
const manifestFiles = new Set(Object.keys(manifest.files));
|
|
76
|
+
// Build list of files to hash
|
|
77
|
+
const filePaths = Object.keys(manifest.files).map(f => join(agentDir, f));
|
|
78
|
+
// Hash all files in parallel for better performance
|
|
79
|
+
const hashes = await hashFilesParallel(filePaths);
|
|
80
|
+
// Check manifest files against hashes
|
|
81
|
+
for (const [filePath, entry] of Object.entries(manifest.files)) {
|
|
82
|
+
const fullPath = join(agentDir, filePath);
|
|
83
|
+
const currentHash = hashes.get(fullPath);
|
|
84
|
+
if (!currentHash) {
|
|
85
|
+
results.push({ path: filePath, status: 'deleted' });
|
|
86
|
+
}
|
|
87
|
+
else if (currentHash === entry.original_hash) {
|
|
88
|
+
results.push({ path: filePath, status: 'unchanged' });
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
results.push({ path: filePath, status: 'modified' });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// Check for user-added files
|
|
95
|
+
const allFiles = await walkDir(agentDir);
|
|
96
|
+
for (const file of allFiles) {
|
|
97
|
+
const relativePath = file.replace(agentDir + '/', '');
|
|
98
|
+
if (relativePath === CONFIG.MANIFEST_FILE)
|
|
99
|
+
continue;
|
|
100
|
+
if (!manifestFiles.has(relativePath)) {
|
|
101
|
+
results.push({ path: relativePath, status: 'added' });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return results.sort((a, b) => a.path.localeCompare(b.path));
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Walk directory recursively
|
|
108
|
+
*/
|
|
109
|
+
async function walkDir(dir) {
|
|
110
|
+
const files = [];
|
|
111
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
112
|
+
for (const entry of entries) {
|
|
113
|
+
const fullPath = join(dir, entry.name);
|
|
114
|
+
if (entry.isDirectory()) {
|
|
115
|
+
// Skip hidden dirs except .agent itself
|
|
116
|
+
if (!entry.name.startsWith('.')) {
|
|
117
|
+
files.push(...await walkDir(fullPath));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
// Skip hidden files
|
|
122
|
+
if (!entry.name.startsWith('.')) {
|
|
123
|
+
files.push(fullPath);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return files;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Check remote for updates
|
|
131
|
+
*/
|
|
132
|
+
async function checkRemote(manifest) {
|
|
133
|
+
try {
|
|
134
|
+
const { extractedPath, version } = await downloadBaKit(manifest.branch, process.cwd());
|
|
135
|
+
await cleanupDownload(extractedPath);
|
|
136
|
+
if (version !== manifest.version) {
|
|
137
|
+
console.log(` Latest: ${chalk.green('v' + version)} ${chalk.yellow('(update available)')}`);
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
console.log(` Latest: ${chalk.green('v' + version)} ${chalk.dim('(up to date)')}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
console.log(` Latest: ${chalk.red('(cannot check)')}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Format ISO date string
|
|
149
|
+
*/
|
|
150
|
+
function formatDate(isoDate) {
|
|
151
|
+
const date = new Date(isoDate);
|
|
152
|
+
return date.toLocaleDateString() + ' ' + date.toLocaleTimeString();
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../src/commands/update.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,aAAa,EAAkC,MAAM,aAAa,CAAC;AAEjF;;GAEG;AACH,wBAAsB,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAqJlE"}
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { join, dirname } from 'path';
|
|
2
|
+
import { readFile, copyFile, mkdir, stat, readdir } from 'fs/promises';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { log, spinner } from '../utils/logger.js';
|
|
5
|
+
import { CONFIG } from '../utils/constants.js';
|
|
6
|
+
import { manifestExists, readManifest, writeManifest } from '../lib/manifest.js';
|
|
7
|
+
import { hashFile } from '../lib/hasher.js';
|
|
8
|
+
import { downloadBaKit, cleanupDownload } from '../lib/downloader.js';
|
|
9
|
+
import { showDiff } from '../lib/differ.js';
|
|
10
|
+
import { promptConflict } from '../utils/prompts.js';
|
|
11
|
+
/**
|
|
12
|
+
* Update BA-Kit to latest version
|
|
13
|
+
*/
|
|
14
|
+
export async function update(options) {
|
|
15
|
+
const projectPath = process.cwd();
|
|
16
|
+
const agentDir = join(projectPath, CONFIG.AGENT_DIR);
|
|
17
|
+
// Check if ba-kit is installed
|
|
18
|
+
if (!await manifestExists(projectPath)) {
|
|
19
|
+
log.error('Not a BA-Kit project. Run `ba-kit init` first.');
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
const manifest = await readManifest(projectPath);
|
|
23
|
+
log.info(`Current version: v${manifest.version}`);
|
|
24
|
+
let extractedPath;
|
|
25
|
+
let remoteVersion;
|
|
26
|
+
let needsCleanup = true;
|
|
27
|
+
// Use local source if specified, otherwise download from GitHub
|
|
28
|
+
if (options.local) {
|
|
29
|
+
const localSpinner = spinner('Using local BA-Kit source...').start();
|
|
30
|
+
try {
|
|
31
|
+
extractedPath = options.local;
|
|
32
|
+
const s = await stat(extractedPath);
|
|
33
|
+
if (!s.isDirectory()) {
|
|
34
|
+
throw new Error('Local source must be a directory');
|
|
35
|
+
}
|
|
36
|
+
remoteVersion = await getLocalVersion(extractedPath);
|
|
37
|
+
needsCleanup = false;
|
|
38
|
+
localSpinner.succeed(`Local version: v${remoteVersion}`);
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
localSpinner.fail('Failed to use local source');
|
|
42
|
+
log.error(error.message);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
const downloadSpinner = spinner('Fetching latest BA-Kit...').start();
|
|
48
|
+
try {
|
|
49
|
+
const result = await downloadBaKit(manifest.branch, projectPath);
|
|
50
|
+
extractedPath = result.extractedPath;
|
|
51
|
+
remoteVersion = result.version;
|
|
52
|
+
downloadSpinner.succeed(`Latest version: v${remoteVersion}`);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
downloadSpinner.fail('Failed to fetch latest');
|
|
56
|
+
log.error(error.message);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// Analyze changes
|
|
61
|
+
const analyzeSpinner = spinner('Analyzing changes...').start();
|
|
62
|
+
const changes = await analyzeChanges(manifest, agentDir, extractedPath);
|
|
63
|
+
analyzeSpinner.stop();
|
|
64
|
+
// Summary
|
|
65
|
+
const stats = {
|
|
66
|
+
skip: changes.filter(c => c.action === 'skip').length,
|
|
67
|
+
update: changes.filter(c => c.action === 'update').length,
|
|
68
|
+
conflict: changes.filter(c => c.action === 'conflict').length,
|
|
69
|
+
new: changes.filter(c => c.action === 'new').length,
|
|
70
|
+
};
|
|
71
|
+
console.log('');
|
|
72
|
+
console.log(chalk.bold('Change Summary:'));
|
|
73
|
+
console.log(` ${chalk.dim('○')} No change: ${stats.skip}`);
|
|
74
|
+
console.log(` ${chalk.blue('↓')} Will update: ${stats.update}`);
|
|
75
|
+
console.log(` ${chalk.green('+')} New files: ${stats.new}`);
|
|
76
|
+
console.log(` ${chalk.red('⚡')} Conflicts: ${stats.conflict}`);
|
|
77
|
+
console.log('');
|
|
78
|
+
// Dry run mode
|
|
79
|
+
if (options.dryRun) {
|
|
80
|
+
log.info('Dry run mode - no changes applied.');
|
|
81
|
+
if (needsCleanup)
|
|
82
|
+
await cleanupDownload(extractedPath);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
// Nothing to do
|
|
86
|
+
if (stats.update === 0 && stats.conflict === 0 && stats.new === 0) {
|
|
87
|
+
log.success('Already up to date!');
|
|
88
|
+
if (needsCleanup)
|
|
89
|
+
await cleanupDownload(extractedPath);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
// Warn about conflicts in --yes mode
|
|
93
|
+
if (options.yes && stats.conflict > 0) {
|
|
94
|
+
log.warn(`${stats.conflict} conflicts found. Skipping conflicted files.`);
|
|
95
|
+
log.dim('Re-run without --yes to resolve conflicts interactively.');
|
|
96
|
+
}
|
|
97
|
+
// Apply updates
|
|
98
|
+
const updatedFiles = {};
|
|
99
|
+
let updatedCount = 0;
|
|
100
|
+
for (const change of changes) {
|
|
101
|
+
if (change.action === 'skip')
|
|
102
|
+
continue;
|
|
103
|
+
if (change.action === 'update' || change.action === 'new') {
|
|
104
|
+
// Auto-update
|
|
105
|
+
if (!options.yes) {
|
|
106
|
+
log.dim(`Updating ${change.path}...`);
|
|
107
|
+
}
|
|
108
|
+
await applyUpdate(change, agentDir, extractedPath);
|
|
109
|
+
const newHash = await hashFile(join(agentDir, change.path));
|
|
110
|
+
updatedFiles[change.path] = {
|
|
111
|
+
original_hash: newHash,
|
|
112
|
+
current_hash: newHash,
|
|
113
|
+
source_path: change.sourcePath,
|
|
114
|
+
};
|
|
115
|
+
updatedCount++;
|
|
116
|
+
}
|
|
117
|
+
if (change.action === 'conflict') {
|
|
118
|
+
// Skip conflicts in --yes mode
|
|
119
|
+
if (options.yes) {
|
|
120
|
+
log.dim(`Skipping conflict: ${change.path}`);
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
const resolved = await handleConflict(change, agentDir, extractedPath);
|
|
124
|
+
if (resolved) {
|
|
125
|
+
const newHash = await hashFile(join(agentDir, change.path));
|
|
126
|
+
updatedFiles[change.path] = {
|
|
127
|
+
original_hash: newHash,
|
|
128
|
+
current_hash: newHash,
|
|
129
|
+
source_path: change.sourcePath,
|
|
130
|
+
};
|
|
131
|
+
updatedCount++;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// Update manifest
|
|
136
|
+
const newManifest = {
|
|
137
|
+
...manifest,
|
|
138
|
+
version: remoteVersion,
|
|
139
|
+
updated_at: new Date().toISOString(),
|
|
140
|
+
files: { ...manifest.files, ...updatedFiles },
|
|
141
|
+
};
|
|
142
|
+
await writeManifest(projectPath, newManifest);
|
|
143
|
+
// Cleanup
|
|
144
|
+
if (needsCleanup)
|
|
145
|
+
await cleanupDownload(extractedPath);
|
|
146
|
+
console.log('');
|
|
147
|
+
if (updatedCount > 0) {
|
|
148
|
+
log.success(`Updated ${updatedCount} file(s) to v${remoteVersion}`);
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
log.success(`Manifest updated to v${remoteVersion}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Get version from local ba-kit source
|
|
156
|
+
*/
|
|
157
|
+
async function getLocalVersion(path) {
|
|
158
|
+
try {
|
|
159
|
+
const changelog = await readFile(join(path, 'CHANGELOG.md'), 'utf8');
|
|
160
|
+
const match = changelog.match(/##\s+\[?v?(\d+\.\d+\.\d+)/);
|
|
161
|
+
if (match)
|
|
162
|
+
return match[1];
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
// ignore
|
|
166
|
+
}
|
|
167
|
+
return 'local';
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Analyze changes between local and remote
|
|
171
|
+
*/
|
|
172
|
+
async function analyzeChanges(manifest, agentDir, remotePath) {
|
|
173
|
+
const changes = [];
|
|
174
|
+
const remoteFiles = await mapRemoteFiles(remotePath);
|
|
175
|
+
for (const [targetPath, sourcePath] of Object.entries(remoteFiles)) {
|
|
176
|
+
const localPath = join(agentDir, targetPath);
|
|
177
|
+
const remoteFullPath = join(remotePath, sourcePath);
|
|
178
|
+
const manifestEntry = manifest.files[targetPath];
|
|
179
|
+
try {
|
|
180
|
+
const localHash = await hashFile(localPath);
|
|
181
|
+
const remoteHash = await hashFile(remoteFullPath);
|
|
182
|
+
if (!manifestEntry) {
|
|
183
|
+
// New file from upstream
|
|
184
|
+
changes.push({ path: targetPath, sourcePath, action: 'new' });
|
|
185
|
+
}
|
|
186
|
+
else if (localHash === manifestEntry.original_hash) {
|
|
187
|
+
// Local unchanged
|
|
188
|
+
if (localHash === remoteHash) {
|
|
189
|
+
changes.push({ path: targetPath, action: 'skip' });
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
changes.push({ path: targetPath, sourcePath, action: 'update' });
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
// Local modified
|
|
197
|
+
if (remoteHash === manifestEntry.original_hash) {
|
|
198
|
+
// Remote unchanged, keep local
|
|
199
|
+
changes.push({ path: targetPath, action: 'skip' });
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
// Both changed - conflict!
|
|
203
|
+
changes.push({
|
|
204
|
+
path: targetPath,
|
|
205
|
+
sourcePath,
|
|
206
|
+
action: 'conflict',
|
|
207
|
+
localHash,
|
|
208
|
+
remoteHash,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
// Local file missing - treat as new
|
|
215
|
+
changes.push({ path: targetPath, sourcePath, action: 'new' });
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return changes;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Map remote files to target paths based on FILE_MAP config
|
|
222
|
+
*/
|
|
223
|
+
async function mapRemoteFiles(remotePath) {
|
|
224
|
+
const files = {};
|
|
225
|
+
async function walk(dir, targetPrefix, sourcePrefix) {
|
|
226
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
227
|
+
for (const entry of entries) {
|
|
228
|
+
if (CONFIG.EXCLUDE.includes(entry.name))
|
|
229
|
+
continue;
|
|
230
|
+
const sourcePath = join(sourcePrefix, entry.name);
|
|
231
|
+
const targetPath = join(targetPrefix, entry.name);
|
|
232
|
+
if (entry.isDirectory()) {
|
|
233
|
+
await walk(join(dir, entry.name), targetPath, sourcePath);
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
files[targetPath] = sourcePath;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
for (const [src, dest] of Object.entries(CONFIG.FILE_MAP)) {
|
|
241
|
+
const fullSrc = join(remotePath, src);
|
|
242
|
+
try {
|
|
243
|
+
const s = await stat(fullSrc);
|
|
244
|
+
if (s.isDirectory()) {
|
|
245
|
+
await walk(fullSrc, dest, src);
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
files[dest] = src;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
catch {
|
|
252
|
+
// source doesn't exist
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return files;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Apply update to a file
|
|
259
|
+
*/
|
|
260
|
+
async function applyUpdate(change, agentDir, remotePath) {
|
|
261
|
+
const destPath = join(agentDir, change.path);
|
|
262
|
+
const srcPath = join(remotePath, change.sourcePath);
|
|
263
|
+
await mkdir(dirname(destPath), { recursive: true });
|
|
264
|
+
await copyFile(srcPath, destPath);
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Handle conflict interactively
|
|
268
|
+
*/
|
|
269
|
+
async function handleConflict(change, agentDir, remotePath) {
|
|
270
|
+
const localPath = join(agentDir, change.path);
|
|
271
|
+
const remoteSrcPath = join(remotePath, change.sourcePath);
|
|
272
|
+
while (true) {
|
|
273
|
+
const action = await promptConflict(change.path);
|
|
274
|
+
switch (action) {
|
|
275
|
+
case 'keep':
|
|
276
|
+
log.dim(`Keeping local: ${change.path}`);
|
|
277
|
+
return false;
|
|
278
|
+
case 'overwrite':
|
|
279
|
+
await copyFile(remoteSrcPath, localPath);
|
|
280
|
+
log.dim(`Updated: ${change.path}`);
|
|
281
|
+
return true;
|
|
282
|
+
case 'diff': {
|
|
283
|
+
const localContent = await readFile(localPath, 'utf8');
|
|
284
|
+
const remoteContent = await readFile(remoteSrcPath, 'utf8');
|
|
285
|
+
showDiff(remoteContent, localContent, change.path);
|
|
286
|
+
break; // Loop again to prompt
|
|
287
|
+
}
|
|
288
|
+
case 'backup': {
|
|
289
|
+
const backupDir = join(agentDir, '..', CONFIG.BACKUP_DIR);
|
|
290
|
+
await mkdir(backupDir, { recursive: true });
|
|
291
|
+
const backupPath = join(backupDir, change.path.replace(/\//g, '_'));
|
|
292
|
+
await copyFile(localPath, backupPath);
|
|
293
|
+
await copyFile(remoteSrcPath, localPath);
|
|
294
|
+
log.dim(`Backed up & updated: ${change.path}`);
|
|
295
|
+
return true;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
//# sourceMappingURL=update.js.map
|