@robbiesrobotics/alice-agents 1.2.8 → 1.3.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/LICENSE +21 -0
- package/README.md +12 -5
- package/bin/.gitkeep +0 -0
- package/bin/alice-install.mjs +26 -1
- package/lib/.gitkeep +0 -0
- package/lib/config-merger.mjs +68 -10
- package/lib/doctor.mjs +169 -0
- package/lib/installer.mjs +32 -7
- package/lib/prompter.mjs +16 -6
- package/lib/workspace-scaffolder.mjs +1 -1
- package/package.json +5 -1
- package/snapshots/.gitkeep +0 -0
- package/templates/skills/.gitkeep +0 -0
- package/templates/skills/claude-code/.gitkeep +0 -0
- package/templates/workspaces/.gitkeep +0 -0
- package/templates/workspaces/_shared/.gitkeep +0 -0
- package/templates/workspaces/_shared/MEMORY.md +22 -0
- package/tools/.gitkeep +0 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Robbies Robotics (av3.ai)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -51,16 +51,23 @@ An orchestrator (Olivia) backed by specialist agents across every domain:
|
|
|
51
51
|
| **Eva** | Executive Assistant | 📌 | Pro |
|
|
52
52
|
| **Parker** | Project Management | 📅 | Pro |
|
|
53
53
|
|
|
54
|
-
## Model
|
|
54
|
+
## Model Flexibility
|
|
55
|
+
|
|
56
|
+
A.L.I.C.E. works with **whatever model you already have configured** in OpenClaw/NemoClaw. There's no required API key — just use what you've got.
|
|
57
|
+
|
|
58
|
+
When you install, the installer will **auto-detect your configured model** and use it by default. You can also choose from presets or specify a custom model.
|
|
55
59
|
|
|
56
60
|
| Preset | Models | Best For |
|
|
57
61
|
|--------|--------|----------|
|
|
58
|
-
| **
|
|
59
|
-
| **
|
|
62
|
+
| **Auto-detect** (default) | Your current OpenClaw model | Zero friction — works with what you have |
|
|
63
|
+
| **Sonnet** | claude-sonnet-4-6 for all | Balanced speed + quality (requires Anthropic key) |
|
|
64
|
+
| **Opus + Sonnet** | Opus for orchestrator, Sonnet for specialists | Maximum quality (requires Anthropic key) |
|
|
60
65
|
| **OpenAI** | GPT-5.4 / GPT-5.4-mini | OpenAI users |
|
|
61
|
-
| **Local (Ollama)** | Local models | Privacy, offline use |
|
|
66
|
+
| **Local (Ollama)** | Local models | Privacy, offline use, no API key needed |
|
|
62
67
|
| **Custom** | Your choice | Full control |
|
|
63
68
|
|
|
69
|
+
> **Tip:** We prefer Claude Opus for orchestration when available — but A.L.I.C.E. is model-agnostic. Whatever model OpenClaw has configured will work.
|
|
70
|
+
|
|
64
71
|
## Install Options
|
|
65
72
|
|
|
66
73
|
```bash
|
|
@@ -132,7 +139,7 @@ If you're already running NemoClaw, A.L.I.C.E. works out of the box — no extra
|
|
|
132
139
|
|
|
133
140
|
- [OpenClaw](https://openclaw.ai) or [NemoClaw](https://nemoclaw.com) installed and configured
|
|
134
141
|
- Node.js 18+
|
|
135
|
-
- At least one
|
|
142
|
+
- At least one model configured in OpenClaw or NemoClaw — no specific API key required. A.L.I.C.E. uses whatever you already have set up.
|
|
136
143
|
|
|
137
144
|
## License
|
|
138
145
|
|
package/bin/.gitkeep
ADDED
|
File without changes
|
package/bin/alice-install.mjs
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
4
|
+
import { createRequire } from 'node:module';
|
|
3
5
|
import { runInstall, runUninstall } from '../lib/installer.mjs';
|
|
6
|
+
import { runDoctor } from '../lib/doctor.mjs';
|
|
4
7
|
|
|
5
8
|
const args = process.argv.slice(2);
|
|
6
9
|
const flags = new Set(args);
|
|
7
10
|
|
|
11
|
+
if (flags.has('--version') || flags.has('-v')) {
|
|
12
|
+
const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url)));
|
|
13
|
+
console.log(pkg.version);
|
|
14
|
+
process.exit(0);
|
|
15
|
+
}
|
|
16
|
+
|
|
8
17
|
if (flags.has('--help') || flags.has('-h')) {
|
|
9
18
|
console.log(`
|
|
10
19
|
alice-agents — A.L.I.C.E. Agent Framework Installer
|
|
@@ -12,17 +21,33 @@ if (flags.has('--help') || flags.has('-h')) {
|
|
|
12
21
|
Usage:
|
|
13
22
|
npx @robbiesrobotics/alice-agents Interactive install
|
|
14
23
|
npx @robbiesrobotics/alice-agents --yes Non-interactive install with defaults
|
|
24
|
+
npx @robbiesrobotics/alice-agents --update Non-interactive upgrade to latest agents
|
|
15
25
|
npx @robbiesrobotics/alice-agents --uninstall Remove A.L.I.C.E. agents from config
|
|
26
|
+
npx @robbiesrobotics/alice-agents --doctor Run diagnostics on your A.L.I.C.E. install
|
|
27
|
+
npx @robbiesrobotics/alice-agents --version Show version
|
|
16
28
|
npx @robbiesrobotics/alice-agents --help Show this help
|
|
17
29
|
|
|
18
30
|
Options:
|
|
19
31
|
--yes Skip prompts, use defaults (Sonnet, Starter tier)
|
|
32
|
+
--update Non-interactive upgrade (alias for --yes with upgrade mode)
|
|
20
33
|
--uninstall Remove A.L.I.C.E. agents (preserves non-ALICE agents)
|
|
34
|
+
--doctor Run diagnostics and check install health
|
|
35
|
+
--version Print package version
|
|
21
36
|
`);
|
|
22
37
|
process.exit(0);
|
|
23
38
|
}
|
|
24
39
|
|
|
25
|
-
if (flags.has('--
|
|
40
|
+
if (flags.has('--doctor')) {
|
|
41
|
+
runDoctor().then((ok) => process.exit(ok ? 0 : 1)).catch((err) => {
|
|
42
|
+
console.error(' ❌ Doctor failed:', err.message);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
});
|
|
45
|
+
} else if (flags.has('--update')) {
|
|
46
|
+
runInstall({ yes: true, modeOverride: 'upgrade' }).catch((err) => {
|
|
47
|
+
console.error(' ❌ Update failed:', err.message);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
});
|
|
50
|
+
} else if (flags.has('--uninstall')) {
|
|
26
51
|
runUninstall({ yes: flags.has('--yes') }).catch((err) => {
|
|
27
52
|
console.error(' ❌ Uninstall failed:', err.message);
|
|
28
53
|
process.exit(1);
|
package/lib/.gitkeep
ADDED
|
File without changes
|
package/lib/config-merger.mjs
CHANGED
|
@@ -15,6 +15,43 @@ export function readConfig() {
|
|
|
15
15
|
return JSON.parse(raw);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Inspect the existing OpenClaw/NemoClaw config and return what models
|
|
20
|
+
* and providers the user already has set up. This is used to avoid
|
|
21
|
+
* forcing Anthropic/Claude on users who haven't configured those keys.
|
|
22
|
+
*
|
|
23
|
+
* Returns null if config can't be read.
|
|
24
|
+
*/
|
|
25
|
+
export function detectAvailableModels() {
|
|
26
|
+
if (!configExists()) return null;
|
|
27
|
+
try {
|
|
28
|
+
const config = readConfig();
|
|
29
|
+
|
|
30
|
+
// Current agents.defaults.model (set by a prior install or manually)
|
|
31
|
+
const agentDefaults = config?.agents?.defaults?.model || {};
|
|
32
|
+
|
|
33
|
+
// Top-level model override (some users set this)
|
|
34
|
+
const globalModel = config?.model;
|
|
35
|
+
|
|
36
|
+
// Configured provider keys (e.g. ['anthropic', 'openai', 'ollama'])
|
|
37
|
+
const providers = Object.keys(config?.models?.providers || {});
|
|
38
|
+
|
|
39
|
+
const primary = agentDefaults.primary || globalModel || null;
|
|
40
|
+
const orchestrator = agentDefaults.orchestrator || primary;
|
|
41
|
+
const fallbacks = agentDefaults.fallbacks || [];
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
primary,
|
|
45
|
+
orchestrator,
|
|
46
|
+
fallbacks,
|
|
47
|
+
providers,
|
|
48
|
+
hasModel: !!primary,
|
|
49
|
+
};
|
|
50
|
+
} catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
18
55
|
function backupConfig() {
|
|
19
56
|
const ts = Date.now();
|
|
20
57
|
const backupPath = join(OPENCLAW_DIR, `openclaw.json.bak.alice-${ts}`);
|
|
@@ -29,8 +66,17 @@ function writeConfigAtomic(config) {
|
|
|
29
66
|
renameSync(tmpPath, CONFIG_PATH);
|
|
30
67
|
}
|
|
31
68
|
|
|
32
|
-
function getModelConfig(preset, customModels) {
|
|
69
|
+
function getModelConfig(preset, customModels, detectedModels) {
|
|
33
70
|
switch (preset) {
|
|
71
|
+
case 'detected':
|
|
72
|
+
// Use whatever the user already has configured — don't touch model settings at all
|
|
73
|
+
return {
|
|
74
|
+
primary: detectedModels?.primary || null,
|
|
75
|
+
orchestrator: detectedModels?.orchestrator || detectedModels?.primary || null,
|
|
76
|
+
fallbacks: detectedModels?.fallbacks || [],
|
|
77
|
+
models: {},
|
|
78
|
+
preserve: true, // signal: don't overwrite model defaults
|
|
79
|
+
};
|
|
34
80
|
case 'sonnet':
|
|
35
81
|
return {
|
|
36
82
|
primary: 'anthropic/claude-sonnet-4-6',
|
|
@@ -71,8 +117,8 @@ function getModelConfig(preset, customModels) {
|
|
|
71
117
|
};
|
|
72
118
|
case 'custom':
|
|
73
119
|
return {
|
|
74
|
-
primary: customModels?.primary ||
|
|
75
|
-
orchestrator: customModels?.orchestrator || customModels?.primary ||
|
|
120
|
+
primary: customModels?.primary || null,
|
|
121
|
+
orchestrator: customModels?.orchestrator || customModels?.primary || null,
|
|
76
122
|
fallbacks: [],
|
|
77
123
|
models: {},
|
|
78
124
|
};
|
|
@@ -110,7 +156,8 @@ function buildAgentEntry(agent, modelCfg) {
|
|
|
110
156
|
export function mergeConfig({ agents, mode, preset, customModels }) {
|
|
111
157
|
const backupPath = backupConfig();
|
|
112
158
|
const config = readConfig();
|
|
113
|
-
const
|
|
159
|
+
const detectedModels = detectAvailableModels();
|
|
160
|
+
const modelCfg = getModelConfig(preset, customModels, detectedModels);
|
|
114
161
|
|
|
115
162
|
// Build agent entries
|
|
116
163
|
const aliceEntries = agents.map((a) => buildAgentEntry(a, modelCfg));
|
|
@@ -140,13 +187,24 @@ export function mergeConfig({ agents, mode, preset, customModels }) {
|
|
|
140
187
|
|
|
141
188
|
// Set defaults
|
|
142
189
|
config.agents.defaults = config.agents.defaults || {};
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
190
|
+
|
|
191
|
+
if (modelCfg.preserve) {
|
|
192
|
+
// User's existing model config is intact — don't overwrite it.
|
|
193
|
+
// Only initialize the key if it doesn't exist at all (safety net).
|
|
194
|
+
config.agents.defaults.model = config.agents.defaults.model || {};
|
|
195
|
+
} else if (modelCfg.primary) {
|
|
196
|
+
config.agents.defaults.model = {
|
|
197
|
+
primary: modelCfg.primary,
|
|
198
|
+
fallbacks: modelCfg.fallbacks,
|
|
199
|
+
};
|
|
200
|
+
if (modelCfg.orchestrator && modelCfg.orchestrator !== modelCfg.primary) {
|
|
201
|
+
config.agents.defaults.model.orchestrator = modelCfg.orchestrator;
|
|
202
|
+
}
|
|
203
|
+
if (Object.keys(modelCfg.models).length > 0) {
|
|
204
|
+
config.agents.defaults.models = modelCfg.models;
|
|
205
|
+
}
|
|
149
206
|
}
|
|
207
|
+
// If modelCfg.primary is null (custom/detected with nothing set), leave existing model config alone
|
|
150
208
|
config.agents.defaults.workspace = join(OPENCLAW_DIR, 'workspace-olivia');
|
|
151
209
|
config.agents.defaults.compaction = config.agents.defaults.compaction || { mode: 'safeguard' };
|
|
152
210
|
config.agents.defaults.maxConcurrent = config.agents.defaults.maxConcurrent || 4;
|
package/lib/doctor.mjs
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
2
|
+
import { join, dirname } from 'node:path';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { execSync } from 'node:child_process';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const HOME = homedir();
|
|
9
|
+
const OPENCLAW_DIR = join(HOME, '.openclaw');
|
|
10
|
+
|
|
11
|
+
const STARTER_AGENTS = [
|
|
12
|
+
'olivia', 'dylan', 'selena', 'devon', 'quinn',
|
|
13
|
+
'felix', 'daphne', 'rowan', 'darius', 'sophie',
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
function check(label, ok, hint) {
|
|
17
|
+
const icon = ok ? '✓' : '✗';
|
|
18
|
+
console.log(` ${icon} ${label}`);
|
|
19
|
+
if (!ok && hint) console.log(` → ${hint}`);
|
|
20
|
+
return ok;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function getRuntime() {
|
|
24
|
+
try {
|
|
25
|
+
const v = execSync('nemoclaw --version', { stdio: 'pipe', encoding: 'utf8' }).trim();
|
|
26
|
+
return { name: 'NemoClaw', ok: true, version: v };
|
|
27
|
+
} catch {}
|
|
28
|
+
try {
|
|
29
|
+
const v = execSync('openclaw --version', { stdio: 'pipe', encoding: 'utf8' }).trim();
|
|
30
|
+
return { name: 'OpenClaw', ok: true, version: v };
|
|
31
|
+
} catch {}
|
|
32
|
+
return { name: 'openclaw/nemoclaw', ok: false, version: null };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function loadConfig() {
|
|
36
|
+
const configPath = join(OPENCLAW_DIR, 'openclaw.json');
|
|
37
|
+
if (!existsSync(configPath)) return null;
|
|
38
|
+
try {
|
|
39
|
+
return JSON.parse(readFileSync(configPath, 'utf8'));
|
|
40
|
+
} catch {
|
|
41
|
+
return 'invalid';
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export async function runDoctor() {
|
|
46
|
+
console.log('\n 🩺 A.L.I.C.E. Doctor — Diagnostic Report\n');
|
|
47
|
+
let allOk = true;
|
|
48
|
+
|
|
49
|
+
// 1. Runtime check
|
|
50
|
+
const runtime = getRuntime();
|
|
51
|
+
const runtimeOk = check(
|
|
52
|
+
runtime.ok
|
|
53
|
+
? `${runtime.name} installed (${runtime.version})`
|
|
54
|
+
: `${runtime.name} not found`,
|
|
55
|
+
runtime.ok,
|
|
56
|
+
'Install OpenClaw: npm install -g openclaw'
|
|
57
|
+
);
|
|
58
|
+
allOk = allOk && runtimeOk;
|
|
59
|
+
|
|
60
|
+
// 2. Config exists and valid JSON
|
|
61
|
+
const configPath = join(OPENCLAW_DIR, 'openclaw.json');
|
|
62
|
+
const configExists = existsSync(configPath);
|
|
63
|
+
check('openclaw.json exists', configExists, 'Run: openclaw configure');
|
|
64
|
+
|
|
65
|
+
if (!configExists) {
|
|
66
|
+
allOk = false;
|
|
67
|
+
console.log('\n ⚠️ Cannot continue checks — openclaw.json missing.\n');
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const config = loadConfig();
|
|
72
|
+
const configValid = config !== null && config !== 'invalid';
|
|
73
|
+
check(
|
|
74
|
+
'openclaw.json is valid JSON',
|
|
75
|
+
configValid,
|
|
76
|
+
`Fix JSON syntax in ${configPath}`
|
|
77
|
+
);
|
|
78
|
+
if (!configValid) {
|
|
79
|
+
allOk = false;
|
|
80
|
+
console.log('\n ⚠️ Cannot continue checks — config is invalid JSON.\n');
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// 3. A.L.I.C.E. agents in config
|
|
85
|
+
const configAgents = Array.isArray(config.agents) ? config.agents : [];
|
|
86
|
+
const agentsInConfig = configAgents
|
|
87
|
+
.filter((a) => a && STARTER_AGENTS.includes(a.id))
|
|
88
|
+
.map((a) => a.id);
|
|
89
|
+
|
|
90
|
+
const agentsOk = agentsInConfig.length > 0;
|
|
91
|
+
check(
|
|
92
|
+
agentsOk
|
|
93
|
+
? `A.L.I.C.E. agents in config: ${agentsInConfig.join(', ')}`
|
|
94
|
+
: 'No A.L.I.C.E. agents found in config',
|
|
95
|
+
agentsOk,
|
|
96
|
+
'Run: npx @robbiesrobotics/alice-agents to install'
|
|
97
|
+
);
|
|
98
|
+
allOk = allOk && agentsOk;
|
|
99
|
+
|
|
100
|
+
// Check for missing agents from full starter set
|
|
101
|
+
if (agentsInConfig.length > 0 && agentsInConfig.length < STARTER_AGENTS.length) {
|
|
102
|
+
const missing = STARTER_AGENTS.filter((id) => !agentsInConfig.includes(id));
|
|
103
|
+
check(
|
|
104
|
+
`All starter agents present (missing: ${missing.join(', ')})`,
|
|
105
|
+
false,
|
|
106
|
+
'Run: npx @robbiesrobotics/alice-agents --update'
|
|
107
|
+
);
|
|
108
|
+
allOk = false;
|
|
109
|
+
} else if (agentsInConfig.length === STARTER_AGENTS.length) {
|
|
110
|
+
check('All starter agents present', true);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// 4. Agent workspaces exist on disk
|
|
114
|
+
if (agentsInConfig.length > 0) {
|
|
115
|
+
let missingWorkspaces = [];
|
|
116
|
+
for (const id of agentsInConfig) {
|
|
117
|
+
const wsDir = join(OPENCLAW_DIR, `workspace-${id}`);
|
|
118
|
+
if (!existsSync(wsDir)) {
|
|
119
|
+
missingWorkspaces.push(id);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
const workspacesOk = missingWorkspaces.length === 0;
|
|
123
|
+
check(
|
|
124
|
+
workspacesOk
|
|
125
|
+
? `Agent workspaces exist (${agentsInConfig.length})`
|
|
126
|
+
: `Agent workspaces missing: ${missingWorkspaces.join(', ')}`,
|
|
127
|
+
workspacesOk,
|
|
128
|
+
'Run: npx @robbiesrobotics/alice-agents --update to scaffold missing workspaces'
|
|
129
|
+
);
|
|
130
|
+
allOk = allOk && workspacesOk;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// 5. At least one model/provider configured
|
|
134
|
+
let modelOk = false;
|
|
135
|
+
let modelLabel = null;
|
|
136
|
+
|
|
137
|
+
if (config.default_model) {
|
|
138
|
+
modelOk = true;
|
|
139
|
+
modelLabel = config.default_model;
|
|
140
|
+
} else if (config.models && Object.keys(config.models).length > 0) {
|
|
141
|
+
modelOk = true;
|
|
142
|
+
modelLabel = Object.keys(config.models)[0];
|
|
143
|
+
} else if (config.providers && Object.keys(config.providers).length > 0) {
|
|
144
|
+
modelOk = true;
|
|
145
|
+
modelLabel = Object.keys(config.providers)[0];
|
|
146
|
+
} else if (config.llm && Object.keys(config.llm).length > 0) {
|
|
147
|
+
modelOk = true;
|
|
148
|
+
modelLabel = Object.keys(config.llm)[0];
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
check(
|
|
152
|
+
modelOk
|
|
153
|
+
? `Model/provider configured: ${modelLabel}`
|
|
154
|
+
: 'No model/provider configured',
|
|
155
|
+
modelOk,
|
|
156
|
+
'Run: openclaw configure to set up a model provider'
|
|
157
|
+
);
|
|
158
|
+
allOk = allOk && modelOk;
|
|
159
|
+
|
|
160
|
+
// Summary
|
|
161
|
+
console.log();
|
|
162
|
+
if (allOk) {
|
|
163
|
+
console.log(' ✅ A.L.I.C.E. is healthy!\n');
|
|
164
|
+
} else {
|
|
165
|
+
console.log(' ⚠️ Issues found — follow the hints above to fix them.\n');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return allOk;
|
|
169
|
+
}
|
package/lib/installer.mjs
CHANGED
|
@@ -2,7 +2,7 @@ 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';
|
|
5
|
-
import { configExists, mergeConfig, removeAliceAgents } from './config-merger.mjs';
|
|
5
|
+
import { configExists, mergeConfig, removeAliceAgents, detectAvailableModels } from './config-merger.mjs';
|
|
6
6
|
import { scaffoldAll } from './workspace-scaffolder.mjs';
|
|
7
7
|
import { readManifest, writeManifest } from './manifest.mjs';
|
|
8
8
|
import {
|
|
@@ -170,11 +170,18 @@ function printBanner() {
|
|
|
170
170
|
console.log();
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
function printSummary(mode, tier, agents, preset, userInfo) {
|
|
173
|
+
function printSummary(mode, tier, agents, preset, userInfo, detectedModels) {
|
|
174
|
+
const modelLabel =
|
|
175
|
+
preset === 'detected'
|
|
176
|
+
? `${detectedModels?.primary || 'your configured model'} (detected)`
|
|
177
|
+
: preset === 'custom'
|
|
178
|
+
? `custom`
|
|
179
|
+
: preset;
|
|
180
|
+
|
|
174
181
|
console.log('\n ── Install Summary ──────────────────────────────');
|
|
175
182
|
console.log(` Mode: ${mode}`);
|
|
176
183
|
console.log(` Tier: ${tier} (${agents.length} agents)`);
|
|
177
|
-
console.log(` Model: ${
|
|
184
|
+
console.log(` Model: ${modelLabel}`);
|
|
178
185
|
console.log(` User: ${userInfo.name}`);
|
|
179
186
|
console.log(` Timezone: ${userInfo.timezone}`);
|
|
180
187
|
console.log(' ─────────────────────────────────────────────────');
|
|
@@ -229,11 +236,26 @@ export async function runInstall(options = {}) {
|
|
|
229
236
|
console.log(' 💡 Tip: Upgrade to NemoClaw for enterprise security: https://nvidia.com/nemoclaw\n');
|
|
230
237
|
}
|
|
231
238
|
|
|
239
|
+
// Detect what models the user already has configured
|
|
240
|
+
const detectedModels = detectAvailableModels();
|
|
241
|
+
if (detectedModels?.hasModel) {
|
|
242
|
+
console.log(` ✔ Detected configured model: ${detectedModels.primary}`);
|
|
243
|
+
if (detectedModels.providers.length > 0) {
|
|
244
|
+
console.log(` Providers: ${detectedModels.providers.join(', ')}\n`);
|
|
245
|
+
} else {
|
|
246
|
+
console.log();
|
|
247
|
+
}
|
|
248
|
+
} else {
|
|
249
|
+
console.log(' ℹ No model configured yet — you\'ll be prompted to choose one.\n');
|
|
250
|
+
}
|
|
251
|
+
|
|
232
252
|
const allAgents = loadAgentRegistry();
|
|
233
253
|
|
|
234
254
|
// 2. Install mode
|
|
235
255
|
let mode;
|
|
236
|
-
if (
|
|
256
|
+
if (options.modeOverride) {
|
|
257
|
+
mode = options.modeOverride;
|
|
258
|
+
} else if (auto) {
|
|
237
259
|
const manifest = readManifest();
|
|
238
260
|
mode = manifest ? 'upgrade' : 'fresh';
|
|
239
261
|
} else {
|
|
@@ -269,9 +291,12 @@ export async function runInstall(options = {}) {
|
|
|
269
291
|
// 4. Model preset
|
|
270
292
|
let preset, customModels;
|
|
271
293
|
if (auto) {
|
|
272
|
-
|
|
294
|
+
// Non-interactive: use whatever the user already has configured.
|
|
295
|
+
// Only fall back to sonnet if nothing is detected (e.g. fresh OpenClaw install
|
|
296
|
+
// where the user explicitly set up Claude credentials).
|
|
297
|
+
preset = detectedModels?.hasModel ? 'detected' : 'sonnet';
|
|
273
298
|
} else {
|
|
274
|
-
preset = await promptModelPreset();
|
|
299
|
+
preset = await promptModelPreset(detectedModels);
|
|
275
300
|
if (preset === 'custom') {
|
|
276
301
|
customModels = await promptCustomModel();
|
|
277
302
|
}
|
|
@@ -307,7 +332,7 @@ export async function runInstall(options = {}) {
|
|
|
307
332
|
const agents = allAgents;
|
|
308
333
|
|
|
309
334
|
// 6. Confirmation
|
|
310
|
-
printSummary(mode, tier, agents, preset, userInfo);
|
|
335
|
+
printSummary(mode, tier, agents, preset, userInfo, detectedModels);
|
|
311
336
|
|
|
312
337
|
if (!auto) {
|
|
313
338
|
const ok = await confirm(' Proceed with installation?');
|
package/lib/prompter.mjs
CHANGED
|
@@ -78,19 +78,29 @@ export async function promptUserInfo() {
|
|
|
78
78
|
return { name, timezone: tz, notes };
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
export async function promptModelPreset() {
|
|
82
|
-
|
|
83
|
-
|
|
81
|
+
export async function promptModelPreset(detectedModels) {
|
|
82
|
+
const options = [];
|
|
83
|
+
|
|
84
|
+
// If the user already has a model configured, put it first
|
|
85
|
+
if (detectedModels?.hasModel) {
|
|
86
|
+
const label = `Use your current model (${detectedModels.primary}) — recommended`;
|
|
87
|
+
options.push({ label, value: 'detected' });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
options.push(
|
|
91
|
+
{ label: 'Sonnet — claude-sonnet-4-6 for all agents', value: 'sonnet' },
|
|
84
92
|
{ label: 'Opus + Sonnet — Opus for orchestrator, Sonnet for specialists', value: 'opus-sonnet' },
|
|
85
93
|
{ label: 'OpenAI — GPT-5.4 / GPT-5.4-mini', value: 'openai' },
|
|
86
94
|
{ label: 'Local (Ollama) — requires local models', value: 'local' },
|
|
87
95
|
{ label: 'Custom — specify your own model strings', value: 'custom' },
|
|
88
|
-
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
return choose('\n Model selection:', options);
|
|
89
99
|
}
|
|
90
100
|
|
|
91
101
|
export async function promptCustomModel() {
|
|
92
|
-
const primary = await input('Primary model (e.g., anthropic/claude-sonnet-4-6)');
|
|
93
|
-
const orchestrator = await input('Orchestrator model (or same as primary)', primary);
|
|
102
|
+
const primary = await input(' Primary model (e.g., anthropic/claude-sonnet-4-6, openai/gpt-4o, ollama/llama3)');
|
|
103
|
+
const orchestrator = await input(' Orchestrator model (or same as primary)', primary);
|
|
94
104
|
return { primary, orchestrator };
|
|
95
105
|
}
|
|
96
106
|
|
|
@@ -12,7 +12,7 @@ const SKILLS_DIR = join(OPENCLAW_DIR, 'skills');
|
|
|
12
12
|
const PRODUCT_FILES = ['SOUL.md', 'AGENTS.md', 'IDENTITY.md', 'TOOLS.md'];
|
|
13
13
|
|
|
14
14
|
// User files — only created if missing
|
|
15
|
-
const USER_FILES = ['PLAYBOOK.md', 'LEARNINGS.md', 'FEEDBACK.md', 'USER.md'];
|
|
15
|
+
const USER_FILES = ['PLAYBOOK.md', 'LEARNINGS.md', 'FEEDBACK.md', 'USER.md', 'MEMORY.md'];
|
|
16
16
|
|
|
17
17
|
function renderTemplate(template, vars) {
|
|
18
18
|
let result = template;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@robbiesrobotics/alice-agents",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
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"
|
|
@@ -28,6 +28,10 @@
|
|
|
28
28
|
"SELF-HEALING-SPEC.md",
|
|
29
29
|
"README.md"
|
|
30
30
|
],
|
|
31
|
+
"scripts": {
|
|
32
|
+
"test": "node --test test/*.test.mjs",
|
|
33
|
+
"test:check": "node --check lib/*.mjs bin/*.mjs"
|
|
34
|
+
},
|
|
31
35
|
"publishConfig": {
|
|
32
36
|
"access": "public"
|
|
33
37
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# MEMORY.md — {{agentName}}
|
|
2
|
+
|
|
3
|
+
> Persistent memory for {{agentName}}, {{agentDomain}} specialist.
|
|
4
|
+
> This file is yours — add notes, context, and learnings that should persist across sessions.
|
|
5
|
+
|
|
6
|
+
## Identity
|
|
7
|
+
|
|
8
|
+
- **Name:** {{agentName}}
|
|
9
|
+
- **Domain:** {{agentDomain}}
|
|
10
|
+
- **Team:** A.L.I.C.E.
|
|
11
|
+
|
|
12
|
+
## Active Projects
|
|
13
|
+
|
|
14
|
+
<!-- Add ongoing projects and their current status here -->
|
|
15
|
+
|
|
16
|
+
## Key Notes
|
|
17
|
+
|
|
18
|
+
<!-- Jot down anything important to remember across sessions -->
|
|
19
|
+
|
|
20
|
+
## Decisions Made
|
|
21
|
+
|
|
22
|
+
<!-- Record significant decisions and their rationale -->
|
package/tools/.gitkeep
ADDED
|
File without changes
|