@robbiesrobotics/alice-agents 1.1.1 → 1.2.1
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/SELF-HEALING-SPEC.md +2503 -0
- package/lib/installer.mjs +17 -1
- package/package.json +4 -1
- package/snapshots/schema-snapshot.json +221 -0
- package/snapshots/tool-snapshot.json +37 -0
- package/tools/compatibility-checker.mjs +268 -0
- package/tools/local-remediation.mjs +231 -0
package/lib/installer.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs';
|
|
1
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
2
2
|
import { join, dirname } from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
4
|
import { execSync } from 'node:child_process';
|
|
@@ -184,6 +184,22 @@ function printSummary(mode, tier, agents, preset, userInfo) {
|
|
|
184
184
|
export async function runInstall(options = {}) {
|
|
185
185
|
const auto = options.yes || false;
|
|
186
186
|
|
|
187
|
+
// Check health flag first (before banner)
|
|
188
|
+
if (process.argv.includes('--health')) {
|
|
189
|
+
const healthPath = join(process.env.HOME, '.openclaw', '.alice-health-alert.json');
|
|
190
|
+
if (existsSync(healthPath)) {
|
|
191
|
+
const alerts = JSON.parse(readFileSync(healthPath, 'utf8'));
|
|
192
|
+
console.log('\n⚠️ A.L.I.C.E. Health Report\n');
|
|
193
|
+
for (const alert of alerts.alerts) {
|
|
194
|
+
console.log(` [${alert.severity}] ${alert.category}: ${alert.field}`);
|
|
195
|
+
console.log(` ${alert.description}\n`);
|
|
196
|
+
}
|
|
197
|
+
process.exit(0);
|
|
198
|
+
}
|
|
199
|
+
console.log('✅ A.L.I.C.E. is healthy\n');
|
|
200
|
+
process.exit(0);
|
|
201
|
+
}
|
|
202
|
+
|
|
187
203
|
printBanner();
|
|
188
204
|
|
|
189
205
|
// 1. Detect OpenClaw — offer to install if missing
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@robbiesrobotics/alice-agents",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "A.L.I.C.E. — 28 AI agents for OpenClaw. One conversation, one team.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"alice-agents": "bin/alice-install.mjs"
|
|
@@ -20,7 +20,10 @@
|
|
|
20
20
|
"files": [
|
|
21
21
|
"bin/",
|
|
22
22
|
"lib/",
|
|
23
|
+
"tools/",
|
|
24
|
+
"snapshots/",
|
|
23
25
|
"templates/",
|
|
26
|
+
"SELF-HEALING-SPEC.md",
|
|
24
27
|
"README.md"
|
|
25
28
|
],
|
|
26
29
|
"publishConfig": {
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
{
|
|
2
|
+
"meta": {
|
|
3
|
+
"capturedAt": "2026-03-16T00:00:00Z",
|
|
4
|
+
"openclawVersion": "2026.3.13",
|
|
5
|
+
"aliceVersion": "1.1.1",
|
|
6
|
+
"description": "Config fields A.L.I.C.E. depends on",
|
|
7
|
+
"checksum": "458186002246ca46"
|
|
8
|
+
},
|
|
9
|
+
"agents": {
|
|
10
|
+
"defaults": {
|
|
11
|
+
"model": {
|
|
12
|
+
"path": "agents.defaults.model",
|
|
13
|
+
"required": true,
|
|
14
|
+
"fields": {
|
|
15
|
+
"primary": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"example": "anthropic/claude-sonnet-4-6"
|
|
18
|
+
},
|
|
19
|
+
"fallbacks": {
|
|
20
|
+
"type": "array"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"workspace": {
|
|
25
|
+
"path": "agents.defaults.workspace",
|
|
26
|
+
"type": "string",
|
|
27
|
+
"required": true
|
|
28
|
+
},
|
|
29
|
+
"heartbeat": {
|
|
30
|
+
"path": "agents.defaults.heartbeat",
|
|
31
|
+
"required": false,
|
|
32
|
+
"fields": {
|
|
33
|
+
"every": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"example": "15m"
|
|
36
|
+
},
|
|
37
|
+
"target": {
|
|
38
|
+
"type": "string",
|
|
39
|
+
"allowedValues": [
|
|
40
|
+
"last",
|
|
41
|
+
"new"
|
|
42
|
+
]
|
|
43
|
+
},
|
|
44
|
+
"directPolicy": {
|
|
45
|
+
"type": "string",
|
|
46
|
+
"allowedValues": [
|
|
47
|
+
"allow",
|
|
48
|
+
"deny"
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
"prompt": {
|
|
52
|
+
"type": "string"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"maxConcurrent": {
|
|
57
|
+
"path": "agents.defaults.maxConcurrent",
|
|
58
|
+
"type": "number",
|
|
59
|
+
"required": false
|
|
60
|
+
},
|
|
61
|
+
"subagents": {
|
|
62
|
+
"path": "agents.defaults.subagents",
|
|
63
|
+
"fields": {
|
|
64
|
+
"maxConcurrent": {
|
|
65
|
+
"type": "number"
|
|
66
|
+
},
|
|
67
|
+
"archiveAfterMinutes": {
|
|
68
|
+
"type": "number"
|
|
69
|
+
},
|
|
70
|
+
"runTimeoutSeconds": {
|
|
71
|
+
"type": "number"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
"compaction": {
|
|
76
|
+
"path": "agents.defaults.compaction",
|
|
77
|
+
"fields": {
|
|
78
|
+
"mode": {
|
|
79
|
+
"type": "string",
|
|
80
|
+
"allowedValues": [
|
|
81
|
+
"safeguard",
|
|
82
|
+
"auto",
|
|
83
|
+
"off"
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
"list_entry": {
|
|
90
|
+
"description": "Schema for each entry in agents.list",
|
|
91
|
+
"fields": {
|
|
92
|
+
"id": {
|
|
93
|
+
"type": "string",
|
|
94
|
+
"required": true
|
|
95
|
+
},
|
|
96
|
+
"name": {
|
|
97
|
+
"type": "string",
|
|
98
|
+
"required": true
|
|
99
|
+
},
|
|
100
|
+
"workspace": {
|
|
101
|
+
"type": "string",
|
|
102
|
+
"required": true
|
|
103
|
+
},
|
|
104
|
+
"identity": {
|
|
105
|
+
"required": true,
|
|
106
|
+
"fields": {
|
|
107
|
+
"name": {
|
|
108
|
+
"type": "string"
|
|
109
|
+
},
|
|
110
|
+
"theme": {
|
|
111
|
+
"type": "string"
|
|
112
|
+
},
|
|
113
|
+
"emoji": {
|
|
114
|
+
"type": "string"
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
"sandbox": {
|
|
119
|
+
"required": true,
|
|
120
|
+
"fields": {
|
|
121
|
+
"mode": {
|
|
122
|
+
"type": "string",
|
|
123
|
+
"allowedValues": [
|
|
124
|
+
"off",
|
|
125
|
+
"all"
|
|
126
|
+
]
|
|
127
|
+
},
|
|
128
|
+
"scope": {
|
|
129
|
+
"type": "string",
|
|
130
|
+
"allowedValues": [
|
|
131
|
+
"agent",
|
|
132
|
+
"workspace"
|
|
133
|
+
]
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
"tools": {
|
|
138
|
+
"required": true,
|
|
139
|
+
"variants": {
|
|
140
|
+
"profile_based": {
|
|
141
|
+
"fields": {
|
|
142
|
+
"profile": {
|
|
143
|
+
"type": "string",
|
|
144
|
+
"allowedValues": [
|
|
145
|
+
"full",
|
|
146
|
+
"coding",
|
|
147
|
+
"basic"
|
|
148
|
+
]
|
|
149
|
+
},
|
|
150
|
+
"alsoAllow": {
|
|
151
|
+
"type": "array"
|
|
152
|
+
},
|
|
153
|
+
"deny": {
|
|
154
|
+
"type": "array"
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
"explicit": {
|
|
159
|
+
"fields": {
|
|
160
|
+
"allow": {
|
|
161
|
+
"type": "array",
|
|
162
|
+
"required": true
|
|
163
|
+
},
|
|
164
|
+
"deny": {
|
|
165
|
+
"type": "array"
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
"model": {
|
|
172
|
+
"type": "string",
|
|
173
|
+
"required": false
|
|
174
|
+
},
|
|
175
|
+
"default": {
|
|
176
|
+
"type": "boolean",
|
|
177
|
+
"required": false
|
|
178
|
+
},
|
|
179
|
+
"subagents": {
|
|
180
|
+
"type": "object",
|
|
181
|
+
"required": false
|
|
182
|
+
},
|
|
183
|
+
"groupChat": {
|
|
184
|
+
"type": "object",
|
|
185
|
+
"required": false
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
"tools": {
|
|
191
|
+
"agentToAgent": {
|
|
192
|
+
"path": "tools.agentToAgent",
|
|
193
|
+
"fields": {
|
|
194
|
+
"allow": {
|
|
195
|
+
"type": "array",
|
|
196
|
+
"description": "Agent IDs that can receive spawned tasks"
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
"hooks": {
|
|
202
|
+
"knownHooks": [
|
|
203
|
+
"boot-md",
|
|
204
|
+
"bootstrap-extra-files",
|
|
205
|
+
"command-logger",
|
|
206
|
+
"session-memory"
|
|
207
|
+
],
|
|
208
|
+
"path": "hooks.entries"
|
|
209
|
+
},
|
|
210
|
+
"sessions": {
|
|
211
|
+
"path": "sessions",
|
|
212
|
+
"fields": {
|
|
213
|
+
"plugins": {
|
|
214
|
+
"type": "object"
|
|
215
|
+
},
|
|
216
|
+
"capabilities": {
|
|
217
|
+
"type": "object"
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"meta": {
|
|
3
|
+
"capturedAt": "2026-03-16T00:00:00Z",
|
|
4
|
+
"openclawVersion": "2026.3.13",
|
|
5
|
+
"aliceVersion": "1.1.1",
|
|
6
|
+
"description": "Tools A.L.I.C.E. references in agent configs"
|
|
7
|
+
},
|
|
8
|
+
"tools": {
|
|
9
|
+
"read": { "criticality": "core", "usedBy": ["all"] },
|
|
10
|
+
"write": { "criticality": "core", "usedBy": ["all"] },
|
|
11
|
+
"edit": { "criticality": "core", "usedBy": ["dylan","darius","avery","isaac","felix","caleb","devon","quinn","alex","daphne"] },
|
|
12
|
+
"exec": { "criticality": "core", "usedBy": ["dylan","darius","avery","isaac","owen","selena","devon","quinn","alex","felix","caleb"] },
|
|
13
|
+
"process": { "criticality": "core", "usedBy": ["dylan","darius","avery","isaac","devon","quinn"] },
|
|
14
|
+
"apply_patch": { "criticality": "core", "usedBy": ["dylan","darius","avery","isaac","felix","caleb","devon","quinn","alex","daphne"] },
|
|
15
|
+
"web_search": { "criticality": "core", "usedBy": ["all"] },
|
|
16
|
+
"web_fetch": { "criticality": "core", "usedBy": ["all"] },
|
|
17
|
+
"browser": { "criticality": "optional", "usedBy": ["olivia","rowan","quinn","uma","alex","tommy","nadia","morgan","sloane"] },
|
|
18
|
+
"canvas": { "criticality": "optional", "usedBy": ["olivia","nadia"] },
|
|
19
|
+
"message": { "criticality": "optional", "usedBy": ["olivia","clara","sophie","sloane","morgan","eva","parker"] },
|
|
20
|
+
"tts": { "criticality": "optional", "usedBy": ["olivia"] },
|
|
21
|
+
"sessions_spawn": { "criticality": "core", "usedBy": ["olivia"] },
|
|
22
|
+
"sessions_send": { "criticality": "core", "usedBy": ["olivia"] },
|
|
23
|
+
"sessions_list": { "criticality": "core", "usedBy": ["olivia","all"] },
|
|
24
|
+
"sessions_history":{ "criticality": "core", "usedBy": ["olivia","all"] },
|
|
25
|
+
"session_status": { "criticality": "core", "usedBy": ["olivia","all"] },
|
|
26
|
+
"agents_list": { "criticality": "core", "usedBy": ["olivia"] },
|
|
27
|
+
"cron": { "criticality": "optional", "usedBy": ["olivia","avery","tommy","sloane","morgan","eva","parker"] },
|
|
28
|
+
"memory_search": { "criticality": "core", "usedBy": ["olivia"] },
|
|
29
|
+
"memory_get": { "criticality": "core", "usedBy": ["olivia"] },
|
|
30
|
+
"image": { "criticality": "optional", "usedBy": ["olivia"] },
|
|
31
|
+
"pdf": { "criticality": "optional", "usedBy": ["olivia"] }
|
|
32
|
+
},
|
|
33
|
+
"profiles": {
|
|
34
|
+
"full": { "description": "All tools available", "usedBy": ["olivia"] },
|
|
35
|
+
"coding": { "description": "Code-focused toolset with exec/edit/apply_patch", "usedBy": ["dylan","darius","avery","isaac","devon","felix","quinn","alex","caleb"] }
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* A.L.I.C.E. Compatibility Checker
|
|
4
|
+
* Diffs current OpenClaw installation against stored snapshots.
|
|
5
|
+
* Exit 0 = compatible, 1 = breaking changes found.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { readFileSync, existsSync, writeFileSync } from 'node:fs';
|
|
9
|
+
import { join, dirname } from 'node:path';
|
|
10
|
+
import { fileURLToPath } from 'node:url';
|
|
11
|
+
import { execSync } from 'node:child_process';
|
|
12
|
+
import { createHash } from 'node:crypto';
|
|
13
|
+
|
|
14
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
const ROOT = join(__dirname, '..');
|
|
16
|
+
|
|
17
|
+
// --- CLI args ---
|
|
18
|
+
const args = process.argv.slice(2);
|
|
19
|
+
const verbose = args.includes('--verbose');
|
|
20
|
+
const outputFile = args.find(a => a.startsWith('--output='))?.split('=')[1];
|
|
21
|
+
|
|
22
|
+
function log(...a) { if (verbose) console.log(...a); }
|
|
23
|
+
function warn(msg) { console.warn(` ⚠️ ${msg}`); }
|
|
24
|
+
|
|
25
|
+
// --- Helpers ---
|
|
26
|
+
function loadJSON(path) {
|
|
27
|
+
if (!existsSync(path)) return null;
|
|
28
|
+
return JSON.parse(readFileSync(path, 'utf8'));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function getOpenClawVersion() {
|
|
32
|
+
try {
|
|
33
|
+
const out = execSync('openclaw --version 2>/dev/null', { encoding: 'utf8', stdio: ['pipe','pipe','pipe'] });
|
|
34
|
+
const m = out.match(/(\d{4}\.\d+\.\d+)/);
|
|
35
|
+
return m ? m[1] : 'unknown';
|
|
36
|
+
} catch { return 'unknown'; }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function getOpenClawConfig() {
|
|
40
|
+
const configPath = join(process.env.HOME, '.openclaw', 'openclaw.json');
|
|
41
|
+
return loadJSON(configPath);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// --- Diff functions ---
|
|
45
|
+
|
|
46
|
+
function diffConfigSchema(snapshot, liveConfig) {
|
|
47
|
+
const changes = [];
|
|
48
|
+
|
|
49
|
+
// Check agents.defaults fields
|
|
50
|
+
const defaults = liveConfig?.agents?.defaults || {};
|
|
51
|
+
const snap = snapshot?.agents?.defaults || {};
|
|
52
|
+
|
|
53
|
+
if (!defaults.model?.primary) {
|
|
54
|
+
changes.push({
|
|
55
|
+
category: 'config',
|
|
56
|
+
severity: 'high',
|
|
57
|
+
field: 'agents.defaults.model.primary',
|
|
58
|
+
change: 'Field missing or undefined',
|
|
59
|
+
autoFixable: false
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Check sandbox allowedValues
|
|
64
|
+
const agents = liveConfig?.agents?.list || [];
|
|
65
|
+
for (const agent of agents) {
|
|
66
|
+
const mode = agent?.sandbox?.mode;
|
|
67
|
+
const allowed = ['off', 'all'];
|
|
68
|
+
if (mode && !allowed.includes(mode)) {
|
|
69
|
+
changes.push({
|
|
70
|
+
category: 'config',
|
|
71
|
+
severity: 'high',
|
|
72
|
+
field: `agents.list[${agent.id}].sandbox.mode`,
|
|
73
|
+
change: `Unexpected value '${mode}', expected one of: ${allowed.join(', ')}`,
|
|
74
|
+
autoFixable: false
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Check compaction mode
|
|
80
|
+
const compactionMode = defaults?.compaction?.mode;
|
|
81
|
+
const validModes = ['safeguard', 'auto', 'off'];
|
|
82
|
+
if (compactionMode && !validModes.includes(compactionMode)) {
|
|
83
|
+
changes.push({
|
|
84
|
+
category: 'behavioral',
|
|
85
|
+
severity: 'medium',
|
|
86
|
+
field: 'agents.defaults.compaction.mode',
|
|
87
|
+
change: `Value '${compactionMode}' may not be valid. Expected: ${validModes.join(', ')}`,
|
|
88
|
+
autoFixable: false
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return changes;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function diffToolAPI(snapshot, liveConfig) {
|
|
96
|
+
const changes = [];
|
|
97
|
+
const snapTools = snapshot?.tools || {};
|
|
98
|
+
|
|
99
|
+
// Get all tools referenced in agent configs
|
|
100
|
+
const referencedTools = new Set();
|
|
101
|
+
const referencedProfiles = new Set();
|
|
102
|
+
|
|
103
|
+
for (const agent of liveConfig?.agents?.list || []) {
|
|
104
|
+
const t = agent?.tools || {};
|
|
105
|
+
if (t.profile) referencedProfiles.add(t.profile);
|
|
106
|
+
for (const tool of [...(t.allow || []), ...(t.alsoAllow || []), ...(t.deny || [])]) {
|
|
107
|
+
referencedTools.add(tool);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const knownTools = new Set(Object.keys(snapTools));
|
|
112
|
+
|
|
113
|
+
// Check for tools in use that aren't in our snapshot (may be new — not breaking)
|
|
114
|
+
for (const tool of referencedTools) {
|
|
115
|
+
if (!knownTools.has(tool)) {
|
|
116
|
+
log(` Note: Tool '${tool}' not in snapshot — may be new`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Check snapshot profiles still exist
|
|
121
|
+
const snapProfiles = snapshot?.profiles || {};
|
|
122
|
+
for (const profile of referencedProfiles) {
|
|
123
|
+
if (!snapProfiles[profile]) {
|
|
124
|
+
// Can't verify, just note
|
|
125
|
+
log(` Note: Profile '${profile}' used but not in snapshot`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return changes;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function diffBehavioral(snapshot, liveConfig) {
|
|
133
|
+
const changes = [];
|
|
134
|
+
const defaults = liveConfig?.agents?.defaults || {};
|
|
135
|
+
const snapDefaults = snapshot?.agents?.defaults || {};
|
|
136
|
+
|
|
137
|
+
// Check heartbeat still works
|
|
138
|
+
if (defaults.heartbeat && !defaults.heartbeat.every) {
|
|
139
|
+
changes.push({
|
|
140
|
+
category: 'behavioral',
|
|
141
|
+
severity: 'medium',
|
|
142
|
+
field: 'agents.defaults.heartbeat.every',
|
|
143
|
+
change: 'Heartbeat interval missing',
|
|
144
|
+
autoFixable: true,
|
|
145
|
+
fix: { type: 'field-add', path: 'agents.defaults.heartbeat.every', value: '15m' }
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Check subagents runTimeoutSeconds still present
|
|
150
|
+
if (defaults.subagents && !defaults.subagents.runTimeoutSeconds) {
|
|
151
|
+
changes.push({
|
|
152
|
+
category: 'behavioral',
|
|
153
|
+
severity: 'low',
|
|
154
|
+
field: 'agents.defaults.subagents.runTimeoutSeconds',
|
|
155
|
+
change: 'runTimeoutSeconds missing from subagents defaults',
|
|
156
|
+
autoFixable: true,
|
|
157
|
+
fix: { type: 'field-add', path: 'agents.defaults.subagents.runTimeoutSeconds', value: 900 }
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return changes;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function diffSkills() {
|
|
165
|
+
const changes = [];
|
|
166
|
+
// Check if skills directory structure is intact
|
|
167
|
+
const skillsPath = join(
|
|
168
|
+
process.env.HOME,
|
|
169
|
+
'.local/share/fnm/node-versions',
|
|
170
|
+
);
|
|
171
|
+
// Skills live in node_modules/openclaw/skills — check a known skill exists
|
|
172
|
+
try {
|
|
173
|
+
execSync('ls ~/.local/share/fnm/node-versions/*/installation/lib/node_modules/openclaw/skills/coding-agent/SKILL.md 2>/dev/null | head -1', {
|
|
174
|
+
encoding: 'utf8', shell: '/bin/sh', stdio: ['pipe','pipe','pipe']
|
|
175
|
+
});
|
|
176
|
+
} catch {
|
|
177
|
+
changes.push({
|
|
178
|
+
category: 'skills',
|
|
179
|
+
severity: 'medium',
|
|
180
|
+
field: 'skills.coding-agent',
|
|
181
|
+
change: 'coding-agent skill not found in expected location',
|
|
182
|
+
autoFixable: false
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
return changes;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// --- Main ---
|
|
189
|
+
async function main() {
|
|
190
|
+
const openclawVersion = getOpenClawVersion();
|
|
191
|
+
const aliceVersion = loadJSON(join(ROOT, 'package.json'))?.version || 'unknown';
|
|
192
|
+
|
|
193
|
+
console.log(`\n 🔍 A.L.I.C.E. Compatibility Checker`);
|
|
194
|
+
console.log(` OpenClaw: ${openclawVersion} | A.L.I.C.E.: ${aliceVersion}\n`);
|
|
195
|
+
|
|
196
|
+
const schemaSnapshot = loadJSON(join(ROOT, 'snapshots', 'schema-snapshot.json'));
|
|
197
|
+
const toolSnapshot = loadJSON(join(ROOT, 'snapshots', 'tool-snapshot.json'));
|
|
198
|
+
const liveConfig = getOpenClawConfig();
|
|
199
|
+
|
|
200
|
+
if (!schemaSnapshot || !toolSnapshot) {
|
|
201
|
+
warn('Snapshots missing — cannot check compatibility');
|
|
202
|
+
process.exit(0);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (!liveConfig) {
|
|
206
|
+
warn('OpenClaw config not found — is OpenClaw installed and configured?');
|
|
207
|
+
process.exit(1);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const breakingChanges = [
|
|
211
|
+
...diffConfigSchema(schemaSnapshot, liveConfig),
|
|
212
|
+
...diffToolAPI(toolSnapshot, liveConfig),
|
|
213
|
+
...diffBehavioral(schemaSnapshot, liveConfig),
|
|
214
|
+
...diffSkills()
|
|
215
|
+
];
|
|
216
|
+
|
|
217
|
+
const criticals = breakingChanges.filter(c => c.severity === 'critical' || c.severity === 'high');
|
|
218
|
+
const mediums = breakingChanges.filter(c => c.severity === 'medium');
|
|
219
|
+
const lows = breakingChanges.filter(c => c.severity === 'low');
|
|
220
|
+
const autoFixable = breakingChanges.filter(c => c.autoFixable);
|
|
221
|
+
|
|
222
|
+
const report = {
|
|
223
|
+
generatedAt: new Date().toISOString(),
|
|
224
|
+
openclawVersion,
|
|
225
|
+
aliceVersion,
|
|
226
|
+
snapshotVersion: schemaSnapshot.meta?.openclawVersion,
|
|
227
|
+
compatible: breakingChanges.length === 0,
|
|
228
|
+
summary: {
|
|
229
|
+
total: breakingChanges.length,
|
|
230
|
+
critical: criticals.length,
|
|
231
|
+
medium: mediums.length,
|
|
232
|
+
low: lows.length,
|
|
233
|
+
autoFixable: autoFixable.length
|
|
234
|
+
},
|
|
235
|
+
breakingChanges
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
if (outputFile) {
|
|
239
|
+
writeFileSync(outputFile, JSON.stringify(report, null, 2));
|
|
240
|
+
console.log(` Report written to: ${outputFile}`);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (breakingChanges.length === 0) {
|
|
244
|
+
console.log(' ✅ Compatible — no breaking changes detected\n');
|
|
245
|
+
process.exit(0);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
console.log(` Found ${breakingChanges.length} issue(s):\n`);
|
|
249
|
+
for (const c of breakingChanges) {
|
|
250
|
+
const icon = c.severity === 'high' || c.severity === 'critical' ? '🔴' :
|
|
251
|
+
c.severity === 'medium' ? '🟡' : '🟢';
|
|
252
|
+
const fix = c.autoFixable ? ' [auto-fixable]' : ' [manual review]';
|
|
253
|
+
console.log(` ${icon} [${c.category}] ${c.field}`);
|
|
254
|
+
console.log(` ${c.change}${fix}\n`);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (autoFixable.length > 0) {
|
|
258
|
+
console.log(` 💡 ${autoFixable.length} issue(s) can be auto-fixed.`);
|
|
259
|
+
console.log(` Run: npx @robbiesrobotics/alice-agents --health\n`);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
process.exit(criticals.length > 0 ? 1 : 0);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
main().catch(err => {
|
|
266
|
+
console.error('Checker error:', err.message);
|
|
267
|
+
process.exit(1);
|
|
268
|
+
});
|