wolverine-ai 2.6.3 → 2.7.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 +9 -0
- package/bin/wolverine.js +39 -1
- package/package.json +1 -1
- package/src/agent/research-agent.js +1 -1
- package/src/brain/brain.js +4 -0
- package/src/core/wolverine.js +23 -0
- package/src/index.js +6 -0
- package/src/skills/backup.js +163 -0
package/README.md
CHANGED
|
@@ -552,6 +552,15 @@ All backups stored in **`~/.wolverine-safe-backups/`** — outside the project d
|
|
|
552
552
|
- **Retention**: unstable/verified pruned after 7 days, stable keeps 1/day after 7 days
|
|
553
553
|
- Protected files never overwritten during rollback: `settings.json`, `db.js`, `.env.local`
|
|
554
554
|
|
|
555
|
+
```bash
|
|
556
|
+
# CLI commands
|
|
557
|
+
wolverine --backup "before auth changes" # create snapshot
|
|
558
|
+
wolverine --list-backups # show all with status/age
|
|
559
|
+
wolverine --rollback mngt8mwb-v0sm # restore specific backup
|
|
560
|
+
wolverine --rollback-latest # restore most recent
|
|
561
|
+
wolverine --undo-rollback # undo last rollback
|
|
562
|
+
```
|
|
563
|
+
|
|
555
564
|
**Rollback & Recovery:**
|
|
556
565
|
|
|
557
566
|
| Action | What it does |
|
package/bin/wolverine.js
CHANGED
|
@@ -72,7 +72,45 @@ if (args.includes("--restore")) {
|
|
|
72
72
|
process.exit(0);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
// --
|
|
75
|
+
// --backup: create server snapshot
|
|
76
|
+
if (args.includes("--backup")) {
|
|
77
|
+
const reason = args[args.indexOf("--backup") + 1] || "manual";
|
|
78
|
+
const { backup } = require("../src/skills/backup");
|
|
79
|
+
backup(process.cwd(), reason);
|
|
80
|
+
process.exit(0);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// --list-backups: list all server snapshots
|
|
84
|
+
if (args.includes("--list-backups")) {
|
|
85
|
+
const { listBackups } = require("../src/skills/backup");
|
|
86
|
+
listBackups(process.cwd());
|
|
87
|
+
process.exit(0);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// --rollback: rollback to specific backup
|
|
91
|
+
if (args.includes("--rollback") && !args.includes("--rollback-latest") && !args.includes("--undo-rollback")) {
|
|
92
|
+
const id = args[args.indexOf("--rollback") + 1];
|
|
93
|
+
if (!id) { console.log("Usage: wolverine --rollback <backup-id>"); process.exit(1); }
|
|
94
|
+
const { rollback } = require("../src/skills/backup");
|
|
95
|
+
const result = rollback(process.cwd(), id);
|
|
96
|
+
process.exit(result.success ? 0 : 1);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// --rollback-latest: rollback to most recent backup
|
|
100
|
+
if (args.includes("--rollback-latest")) {
|
|
101
|
+
const { rollbackLatest } = require("../src/skills/backup");
|
|
102
|
+
const result = rollbackLatest(process.cwd());
|
|
103
|
+
process.exit(result.success ? 0 : 1);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// --undo-rollback: undo last rollback
|
|
107
|
+
if (args.includes("--undo-rollback")) {
|
|
108
|
+
const { undoRollback } = require("../src/skills/backup");
|
|
109
|
+
const result = undoRollback(process.cwd());
|
|
110
|
+
process.exit(result.success ? 0 : 1);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// --backups: list safe backups (update snapshots)
|
|
76
114
|
if (args.includes("--backups")) {
|
|
77
115
|
const { listSafeBackups } = require("../src/skills/update");
|
|
78
116
|
const backups = listSafeBackups();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wolverine-ai",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.0",
|
|
4
4
|
"description": "Self-healing Node.js server framework powered by AI. Catches crashes, diagnoses errors, generates fixes, verifies, and restarts — automatically.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -55,7 +55,7 @@ class ResearchAgent {
|
|
|
55
55
|
|
|
56
56
|
const namespace = success ? "fixes" : "errors";
|
|
57
57
|
const prefix = success ? "FIXED" : "FAILED";
|
|
58
|
-
const text = `${prefix}: ${safeError} in ${filePath}. ${success ? "Solution" : "Attempted"}: ${safeExplanation}`;
|
|
58
|
+
const text = `${prefix}: ${safeError} in ${filePath}. ${success ? "Solution" : "Attempted (DO NOT REPEAT)"}: ${safeExplanation}`;
|
|
59
59
|
|
|
60
60
|
await this.brain.remember(namespace, text, { type: success ? "fix-success" : "fix-failure", file: filePath });
|
|
61
61
|
console.log(chalk.gray(` 🧠 ${success ? "✅" : "❌"} Recorded ${prefix.toLowerCase()} for ${safeError.slice(0, 50)}`));
|
package/src/brain/brain.js
CHANGED
|
@@ -237,6 +237,10 @@ const SEED_DOCS = [
|
|
|
237
237
|
text: "Dependency manager skill (src/skills/deps.js): structured npm dependency analysis + repair. diagnose(errorMessage, cwd) returns {diagnosed, category, summary, fixes} — categories: missing_install, missing_package, version_conflict, outdated_api, corrupted_modules. healthReport(cwd) returns full health check: npm audit (vulnerabilities), outdated packages, peer dep conflicts, unused packages, lock file status, health score 0-100. getMigration(packageName) returns known upgrade paths: express→fastify (5.6x faster), moment→dayjs (2KB vs 70KB), request→node-fetch (deprecated), body-parser→built-in, callbacks→async/await. Agent tools: audit_deps (full health check), check_migration (upgrade paths). Heal pipeline uses diagnose() in tryOperationalFix before AI — zero tokens for dependency issues.",
|
|
238
238
|
metadata: { topic: "skill-deps" },
|
|
239
239
|
},
|
|
240
|
+
{
|
|
241
|
+
text: "Backup skill (src/skills/backup.js): agent-friendly backup/rollback. Functions: backup(cwd, reason) creates snapshot, rollback(cwd, id) restores specific backup, rollbackLatest(cwd) restores most recent, undoRollback(cwd) undoes last rollback, listBackups(cwd) shows all with status/age/reason. Agent can use via bash_exec: node -e \"require('./src/skills/backup').backup('.', 'before change')\". CLI: wolverine --backup 'reason', wolverine --list-backups, wolverine --rollback <id>, wolverine --rollback-latest, wolverine --undo-rollback. All stored in ~/.wolverine-safe-backups/snapshots/. Create backup BEFORE any risky server change.",
|
|
242
|
+
metadata: { topic: "backup-skill" },
|
|
243
|
+
},
|
|
240
244
|
{
|
|
241
245
|
text: "CRITICAL: Never run raw 'npm install wolverine-ai' or 'git pull' to update — these OVERWRITE server/, .wolverine/ (brain, backups, events), and .env.local. Always use the safe update skill: wolverine --update (CLI), safeUpdate(cwd) (programmatic), or let auto-update handle it. ALL backups (heal snapshots + update snapshots) stored in ~/.wolverine-safe-backups/ (OUTSIDE project, survives git clean, rm -rf, project deletion). Structure: ~/.wolverine-safe-backups/snapshots/ (heal backups), ~/.wolverine-safe-backups/updates/ (pre-update snapshots), ~/.wolverine-safe-backups/manifest.json (backup registry). Old .wolverine/backups/ auto-migrated on first run. Restore with: wolverine --restore <name>. List: wolverine --backups.",
|
|
242
246
|
metadata: { topic: "safe-update-warning" },
|
package/src/core/wolverine.js
CHANGED
|
@@ -413,6 +413,29 @@ async function _healImpl({ stderr, cwd, sandbox, notifier, rateLimiter, backupMa
|
|
|
413
413
|
});
|
|
414
414
|
}
|
|
415
415
|
|
|
416
|
+
// Record outcome to brain — both successes AND failures with full context
|
|
417
|
+
if (brain && brain._initialized) {
|
|
418
|
+
try {
|
|
419
|
+
if (goalResult.success) {
|
|
420
|
+
await brain.remember("fixes",
|
|
421
|
+
`FIXED (${goalResult.mode}, iter ${goalResult.iteration}): ${parsed.errorMessage} in ${parsed.filePath}:${parsed.line}. ` +
|
|
422
|
+
`Solution: ${goalResult.explanation?.slice(0, 200)}. Files: ${(goalResult.agentStats?.filesModified || []).join(", ")}`,
|
|
423
|
+
{ type: "fix-success", file: parsed.filePath, error: parsed.errorMessage, mode: goalResult.mode }
|
|
424
|
+
);
|
|
425
|
+
} else {
|
|
426
|
+
// Record failure with all attempts so brain knows what NOT to do
|
|
427
|
+
const attemptSummary = (goalResult.attempts || []).map(a =>
|
|
428
|
+
`Iter ${a.iteration} (${a.mode}): ${a.explanation?.slice(0, 80)}`
|
|
429
|
+
).join("; ");
|
|
430
|
+
await brain.remember("errors",
|
|
431
|
+
`UNRESOLVED: ${parsed.errorMessage} in ${parsed.filePath}:${parsed.line}. ` +
|
|
432
|
+
`Tried ${goalResult.iteration} iterations: ${attemptSummary}. All failed.`,
|
|
433
|
+
{ type: "fix-failure", file: parsed.filePath, error: parsed.errorMessage, attempts: goalResult.iteration }
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
} catch {}
|
|
437
|
+
}
|
|
438
|
+
|
|
416
439
|
if (goalResult.success) {
|
|
417
440
|
if (logger) logger.info(EVENT_TYPES.HEAL_SUCCESS, goalResult.explanation, { iteration: goalResult.iteration, mode: goalResult.mode });
|
|
418
441
|
return { healed: true, ...goalResult };
|
package/src/index.js
CHANGED
|
@@ -37,6 +37,7 @@ const { sqlGuard, SafeDB, scanForInjection, idempotencyGuard, idempotencyAfterHo
|
|
|
37
37
|
const { diagnose: diagnoseDeps, healthReport: depsHealthReport, getMigration } = require("./skills/deps");
|
|
38
38
|
const { checkForUpdate, upgrade: upgradeWolverine, getCurrentVersion } = require("./platform/auto-update");
|
|
39
39
|
const { safeUpdate, createSafeBackup, listSafeBackups, restoreFromSafeBackup } = require("./skills/update");
|
|
40
|
+
const { backup, rollback, rollbackLatest, undoRollback, listBackups } = require("./skills/backup");
|
|
40
41
|
|
|
41
42
|
module.exports = {
|
|
42
43
|
// Core
|
|
@@ -108,4 +109,9 @@ module.exports = {
|
|
|
108
109
|
createSafeBackup,
|
|
109
110
|
listSafeBackups,
|
|
110
111
|
restoreFromSafeBackup,
|
|
112
|
+
backup,
|
|
113
|
+
rollback,
|
|
114
|
+
rollbackLatest,
|
|
115
|
+
undoRollback,
|
|
116
|
+
listBackups,
|
|
111
117
|
};
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backup Skill — agent-friendly backup/rollback interface.
|
|
3
|
+
*
|
|
4
|
+
* Wraps BackupManager with simple callable functions and CLI commands.
|
|
5
|
+
* The agent can create snapshots, list backups, rollback, and undo —
|
|
6
|
+
* all through bash_exec or direct function calls.
|
|
7
|
+
*
|
|
8
|
+
* CLI commands (via wolverine):
|
|
9
|
+
* wolverine --backup "reason" Create a snapshot
|
|
10
|
+
* wolverine --rollback <id> Rollback to a specific backup
|
|
11
|
+
* wolverine --rollback-latest Rollback to most recent backup
|
|
12
|
+
* wolverine --undo-rollback Undo the last rollback
|
|
13
|
+
* wolverine --list-backups List all backups with status
|
|
14
|
+
*
|
|
15
|
+
* Programmatic:
|
|
16
|
+
* const { backup, rollback, listBackups } = require("wolverine-ai");
|
|
17
|
+
*
|
|
18
|
+
* Agent tool usage (via bash_exec):
|
|
19
|
+
* bash_exec: node -e "require('./src/skills/backup').backup('.', 'before risky change')"
|
|
20
|
+
* bash_exec: node -e "require('./src/skills/backup').rollbackLatest('.')"
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
const path = require("path");
|
|
24
|
+
const chalk = require("chalk");
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Create a backup snapshot of server/.
|
|
28
|
+
* @param {string} cwd — project root
|
|
29
|
+
* @param {string} reason — why this backup was created
|
|
30
|
+
* @returns {{ id, fileCount, reason }}
|
|
31
|
+
*/
|
|
32
|
+
function backup(cwd, reason = "manual") {
|
|
33
|
+
const { BackupManager } = require("../backup/backup-manager");
|
|
34
|
+
const bm = new BackupManager(cwd);
|
|
35
|
+
const id = bm.createBackup(reason);
|
|
36
|
+
const entry = bm.manifest.backups.find(b => b.id === id);
|
|
37
|
+
console.log(chalk.green(` 💾 Backup created: ${id} (${entry?.fileCount || 0} files) — ${reason}`));
|
|
38
|
+
return { id, fileCount: entry?.fileCount || 0, reason };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Rollback to a specific backup.
|
|
43
|
+
* @param {string} cwd — project root
|
|
44
|
+
* @param {string} backupId — backup to restore
|
|
45
|
+
* @returns {{ success, preRollbackId }}
|
|
46
|
+
*/
|
|
47
|
+
function rollback(cwd, backupId) {
|
|
48
|
+
const { BackupManager } = require("../backup/backup-manager");
|
|
49
|
+
const bm = new BackupManager(cwd);
|
|
50
|
+
const result = bm.rollbackTo(backupId);
|
|
51
|
+
if (result.success) {
|
|
52
|
+
console.log(chalk.green(` ↩️ Rolled back to ${backupId} (pre-rollback: ${result.preRollbackId})`));
|
|
53
|
+
} else {
|
|
54
|
+
console.log(chalk.red(` ❌ Rollback failed: backup ${backupId} not found`));
|
|
55
|
+
}
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Rollback to the most recent backup.
|
|
61
|
+
*/
|
|
62
|
+
function rollbackLatest(cwd) {
|
|
63
|
+
const { BackupManager } = require("../backup/backup-manager");
|
|
64
|
+
const bm = new BackupManager(cwd);
|
|
65
|
+
const result = bm.rollbackLatest();
|
|
66
|
+
if (result.success) {
|
|
67
|
+
console.log(chalk.green(` ↩️ Rolled back to latest backup (pre-rollback: ${result.preRollbackId})`));
|
|
68
|
+
} else {
|
|
69
|
+
console.log(chalk.red(" ❌ No backups available to rollback"));
|
|
70
|
+
}
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Undo the last rollback.
|
|
76
|
+
*/
|
|
77
|
+
function undoRollback(cwd) {
|
|
78
|
+
const { BackupManager } = require("../backup/backup-manager");
|
|
79
|
+
const bm = new BackupManager(cwd);
|
|
80
|
+
const result = bm.undoRollback();
|
|
81
|
+
if (result.success) {
|
|
82
|
+
console.log(chalk.green(" ↩️ Undo rollback — restored pre-rollback state"));
|
|
83
|
+
} else {
|
|
84
|
+
console.log(chalk.red(" ❌ No rollback to undo"));
|
|
85
|
+
}
|
|
86
|
+
return result;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* List all backups with status, age, reason, file count.
|
|
91
|
+
* @returns {Array<{ id, status, reason, fileCount, age, timestamp }>}
|
|
92
|
+
*/
|
|
93
|
+
function listBackups(cwd) {
|
|
94
|
+
const { BackupManager } = require("../backup/backup-manager");
|
|
95
|
+
const bm = new BackupManager(cwd);
|
|
96
|
+
const backups = bm.getAll();
|
|
97
|
+
const stats = bm.getStats();
|
|
98
|
+
|
|
99
|
+
console.log(chalk.bold(`\n Backups: ${stats.total} total (${stats.stable} stable, ${stats.verified} verified, ${stats.unstable} unstable)\n`));
|
|
100
|
+
|
|
101
|
+
for (const b of backups.slice(-15).reverse()) {
|
|
102
|
+
const age = Math.round((Date.now() - b.timestamp) / 60000);
|
|
103
|
+
const ageStr = age < 60 ? `${age}m ago` : `${Math.round(age / 60)}h ago`;
|
|
104
|
+
const icon = b.status === "stable" ? "🟢" : b.status === "verified" ? "🔵" : "⚪";
|
|
105
|
+
console.log(` ${icon} ${b.id} ${b.status.padEnd(9)} ${b.fileCount} files ${ageStr} ${b.reason || ""}`);
|
|
106
|
+
}
|
|
107
|
+
console.log("");
|
|
108
|
+
|
|
109
|
+
return backups.map(b => ({
|
|
110
|
+
id: b.id,
|
|
111
|
+
status: b.status,
|
|
112
|
+
reason: b.reason,
|
|
113
|
+
fileCount: b.fileCount,
|
|
114
|
+
age: Math.round((Date.now() - b.timestamp) / 60000),
|
|
115
|
+
timestamp: b.timestamp,
|
|
116
|
+
}));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Get rollback log.
|
|
121
|
+
*/
|
|
122
|
+
function getRollbackLog(cwd) {
|
|
123
|
+
const { BackupManager } = require("../backup/backup-manager");
|
|
124
|
+
const bm = new BackupManager(cwd);
|
|
125
|
+
return bm.getRollbackLog();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ── Skill Metadata ──
|
|
129
|
+
|
|
130
|
+
const SKILL_NAME = "backup";
|
|
131
|
+
const SKILL_DESCRIPTION = "Backup and rollback for server/ directory. Create snapshots before risky changes, rollback to any previous state, undo rollbacks. All backups stored safely in ~/.wolverine-safe-backups/. Agent can use via bash_exec or direct tool calls.";
|
|
132
|
+
const SKILL_KEYWORDS = ["backup", "rollback", "restore", "undo", "snapshot", "revert", "save", "recovery"];
|
|
133
|
+
const SKILL_USAGE = `// Create backup before making changes
|
|
134
|
+
const { backup } = require("wolverine-ai");
|
|
135
|
+
backup(process.cwd(), "before adding auth routes");
|
|
136
|
+
|
|
137
|
+
// List all backups
|
|
138
|
+
const { listBackups } = require("wolverine-ai");
|
|
139
|
+
listBackups(process.cwd());
|
|
140
|
+
|
|
141
|
+
// Rollback to specific backup
|
|
142
|
+
const { rollback } = require("wolverine-ai");
|
|
143
|
+
rollback(process.cwd(), "mngt8mwb-v0sm");
|
|
144
|
+
|
|
145
|
+
// Rollback to latest
|
|
146
|
+
const { rollbackLatest } = require("wolverine-ai");
|
|
147
|
+
rollbackLatest(process.cwd());
|
|
148
|
+
|
|
149
|
+
// Undo last rollback
|
|
150
|
+
const { undoRollback } = require("wolverine-ai");
|
|
151
|
+
undoRollback(process.cwd());
|
|
152
|
+
|
|
153
|
+
// CLI:
|
|
154
|
+
// wolverine --backup "before auth changes"
|
|
155
|
+
// wolverine --list-backups
|
|
156
|
+
// wolverine --rollback mngt8mwb-v0sm
|
|
157
|
+
// wolverine --rollback-latest
|
|
158
|
+
// wolverine --undo-rollback`;
|
|
159
|
+
|
|
160
|
+
module.exports = {
|
|
161
|
+
SKILL_NAME, SKILL_DESCRIPTION, SKILL_KEYWORDS, SKILL_USAGE,
|
|
162
|
+
backup, rollback, rollbackLatest, undoRollback, listBackups, getRollbackLog,
|
|
163
|
+
};
|