@robbiesrobotics/alice-agents 1.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 +120 -0
- package/bin/alice-install.mjs +35 -0
- package/lib/config-merger.mjs +185 -0
- package/lib/installer.mjs +233 -0
- package/lib/manifest.mjs +33 -0
- package/lib/prompter.mjs +102 -0
- package/lib/workspace-scaffolder.mjs +112 -0
- package/package.json +29 -0
- package/templates/agents.json +487 -0
- package/templates/workspaces/_shared/AGENTS-coding.md +52 -0
- package/templates/workspaces/_shared/AGENTS-orchestrator.md +45 -0
- package/templates/workspaces/_shared/AGENTS.md +50 -0
- package/templates/workspaces/_shared/FEEDBACK.md +7 -0
- package/templates/workspaces/_shared/IDENTITY.md +6 -0
- package/templates/workspaces/_shared/LEARNINGS.md +5 -0
- package/templates/workspaces/_shared/PLAYBOOK.md +7 -0
- package/templates/workspaces/_shared/SOUL-coding.md +32 -0
- package/templates/workspaces/_shared/SOUL-orchestrator.md +31 -0
- package/templates/workspaces/_shared/SOUL.md +32 -0
- package/templates/workspaces/_shared/TOOLS-coding.md +30 -0
- package/templates/workspaces/_shared/TOOLS.md +15 -0
- package/templates/workspaces/_shared/USER.md +10 -0
package/README.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# ๐ง A.L.I.C.E. โ 28 AI Agents for OpenClaw
|
|
2
|
+
|
|
3
|
+
**Adaptive Learning & Intelligent Coordination Engine**
|
|
4
|
+
|
|
5
|
+
One conversation. One orchestrator. Twenty-eight specialists. A.L.I.C.E. turns OpenClaw into a full AI team โ talk to Olivia, and she routes your request to the right expert.
|
|
6
|
+
|
|
7
|
+
## Quick Start
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx @robbiesrobotics/alice-agents
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
That's it. The installer detects your OpenClaw installation, asks a few questions, and sets up everything.
|
|
14
|
+
|
|
15
|
+
## What You Get
|
|
16
|
+
|
|
17
|
+
An orchestrator (Olivia) backed by specialist agents across every domain:
|
|
18
|
+
|
|
19
|
+
| Agent | Domain | Emoji | Tier |
|
|
20
|
+
|-------|--------|-------|------|
|
|
21
|
+
| **Olivia** | Orchestration | ๐ง | Starter |
|
|
22
|
+
| **Dylan** | Development | ๐ป | Starter |
|
|
23
|
+
| **Selena** | Security | ๐ก๏ธ | Starter |
|
|
24
|
+
| **Devon** | DevOps | ๐ | Starter |
|
|
25
|
+
| **Quinn** | QA/Testing | โ
| Starter |
|
|
26
|
+
| **Felix** | Frontend | ๐ฅ๏ธ | Starter |
|
|
27
|
+
| **Daphne** | Documentation | ๐ | Starter |
|
|
28
|
+
| **Rowan** | Research | ๐ | Starter |
|
|
29
|
+
| **Darius** | Data | ๐ | Starter |
|
|
30
|
+
| **Sophie** | Support | ๐ฌ | Starter |
|
|
31
|
+
| **Hannah** | HR | ๐ฅ | Pro |
|
|
32
|
+
| **Aiden** | Analytics | ๐ | Pro |
|
|
33
|
+
| **Clara** | Communication | โ๏ธ | Pro |
|
|
34
|
+
| **Avery** | Automation | โ๏ธ | Pro |
|
|
35
|
+
| **Owen** | Operations | ๐ง | Pro |
|
|
36
|
+
| **Isaac** | Integration | ๐ | Pro |
|
|
37
|
+
| **Tommy** | Travel | โ๏ธ | Pro |
|
|
38
|
+
| **Sloane** | Sales | ๐ผ | Pro |
|
|
39
|
+
| **Nadia** | UI/UX Design | ๐จ | Pro |
|
|
40
|
+
| **Morgan** | Marketing | ๐ฃ | Pro |
|
|
41
|
+
| **Alex** | API Crawling | ๐ท๏ธ | Pro |
|
|
42
|
+
| **Uma** | UX Research | ๐งช | Pro |
|
|
43
|
+
| **Caleb** | CRM | ๐๏ธ | Pro |
|
|
44
|
+
| **Elena** | Estimation | ๐ | Pro |
|
|
45
|
+
| **Audrey** | Accounting | ๐ฐ | Pro |
|
|
46
|
+
| **Logan** | Legal | โ๏ธ | Pro |
|
|
47
|
+
| **Eva** | Executive Assistant | ๐ | Pro |
|
|
48
|
+
| **Parker** | Project Management | ๐
| Pro |
|
|
49
|
+
|
|
50
|
+
## Model Presets
|
|
51
|
+
|
|
52
|
+
| Preset | Models | Best For |
|
|
53
|
+
|--------|--------|----------|
|
|
54
|
+
| **Sonnet** (default) | claude-sonnet-4-6 for all | Balanced speed + quality |
|
|
55
|
+
| **Opus + Sonnet** | Opus for orchestrator, Sonnet for specialists | Maximum orchestration quality |
|
|
56
|
+
| **OpenAI** | GPT-4.1 / GPT-4.1-mini | OpenAI users |
|
|
57
|
+
| **Local (Ollama)** | Local models | Privacy, offline use |
|
|
58
|
+
| **Custom** | Your choice | Full control |
|
|
59
|
+
|
|
60
|
+
## Install Options
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# Interactive install
|
|
64
|
+
npx @robbiesrobotics/alice-agents
|
|
65
|
+
|
|
66
|
+
# Non-interactive with defaults (Sonnet, Starter tier)
|
|
67
|
+
npx @robbiesrobotics/alice-agents --yes
|
|
68
|
+
|
|
69
|
+
# Show help
|
|
70
|
+
npx @robbiesrobotics/alice-agents --help
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Install Modes
|
|
74
|
+
|
|
75
|
+
- **Fresh** โ Replaces the agents section in `openclaw.json` (recommended for first install)
|
|
76
|
+
- **Merge** โ Adds A.L.I.C.E. agents alongside your existing agents
|
|
77
|
+
- **Upgrade** โ Updates product files (SOUL.md, AGENTS.md, etc.) without touching user customizations
|
|
78
|
+
|
|
79
|
+
## Upgrade
|
|
80
|
+
|
|
81
|
+
Re-run the installer and choose "Upgrade":
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
npx @robbiesrobotics/alice-agents
|
|
85
|
+
# Select: Upgrade
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
This updates agent templates and config while preserving your PLAYBOOK.md, LEARNINGS.md, FEEDBACK.md, and memory/ directories.
|
|
89
|
+
|
|
90
|
+
## Uninstall
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
npx @robbiesrobotics/alice-agents --uninstall
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Removes A.L.I.C.E. agents from `openclaw.json` while preserving any non-ALICE agents. Creates a backup before making changes.
|
|
97
|
+
|
|
98
|
+
## How It Works
|
|
99
|
+
|
|
100
|
+
1. **You talk to Olivia** โ she's your single point of contact
|
|
101
|
+
2. **Olivia routes to specialists** โ "Build me an API" โ Dylan (Development)
|
|
102
|
+
3. **Specialists do the work** โ using their domain-specific tools and expertise
|
|
103
|
+
4. **Olivia synthesizes** โ combines results and presents them to you
|
|
104
|
+
|
|
105
|
+
Each agent has its own workspace with:
|
|
106
|
+
- `SOUL.md` โ personality and values
|
|
107
|
+
- `AGENTS.md` โ operating instructions
|
|
108
|
+
- `PLAYBOOK.md` โ learned patterns (evolves over time)
|
|
109
|
+
- `LEARNINGS.md` โ task log
|
|
110
|
+
- `memory/` โ persistent context between sessions
|
|
111
|
+
|
|
112
|
+
## Requirements
|
|
113
|
+
|
|
114
|
+
- [OpenClaw](https://openclaw.com) installed and configured
|
|
115
|
+
- Node.js 18+
|
|
116
|
+
- At least one AI provider configured (Anthropic, OpenAI, or Ollama)
|
|
117
|
+
|
|
118
|
+
## License
|
|
119
|
+
|
|
120
|
+
MIT
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { runInstall, runUninstall } from '../lib/installer.mjs';
|
|
4
|
+
|
|
5
|
+
const args = process.argv.slice(2);
|
|
6
|
+
const flags = new Set(args);
|
|
7
|
+
|
|
8
|
+
if (flags.has('--help') || flags.has('-h')) {
|
|
9
|
+
console.log(`
|
|
10
|
+
alice-agents โ A.L.I.C.E. Agent Framework Installer
|
|
11
|
+
|
|
12
|
+
Usage:
|
|
13
|
+
npx @robbiesrobotics/alice-agents Interactive install
|
|
14
|
+
npx @robbiesrobotics/alice-agents --yes Non-interactive install with defaults
|
|
15
|
+
npx @robbiesrobotics/alice-agents --uninstall Remove A.L.I.C.E. agents from config
|
|
16
|
+
npx @robbiesrobotics/alice-agents --help Show this help
|
|
17
|
+
|
|
18
|
+
Options:
|
|
19
|
+
--yes Skip prompts, use defaults (Sonnet, Starter tier)
|
|
20
|
+
--uninstall Remove A.L.I.C.E. agents (preserves non-ALICE agents)
|
|
21
|
+
`);
|
|
22
|
+
process.exit(0);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (flags.has('--uninstall')) {
|
|
26
|
+
runUninstall({ yes: flags.has('--yes') }).catch((err) => {
|
|
27
|
+
console.error(' โ Uninstall failed:', err.message);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
});
|
|
30
|
+
} else {
|
|
31
|
+
runInstall({ yes: flags.has('--yes') }).catch((err) => {
|
|
32
|
+
console.error(' โ Install failed:', err.message);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, renameSync, existsSync } from 'node:fs';
|
|
2
|
+
import { join, dirname } from 'node:path';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { randomBytes } from 'node:crypto';
|
|
5
|
+
|
|
6
|
+
const OPENCLAW_DIR = join(homedir(), '.openclaw');
|
|
7
|
+
const CONFIG_PATH = join(OPENCLAW_DIR, 'openclaw.json');
|
|
8
|
+
|
|
9
|
+
export function configExists() {
|
|
10
|
+
return existsSync(CONFIG_PATH);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function readConfig() {
|
|
14
|
+
const raw = readFileSync(CONFIG_PATH, 'utf8');
|
|
15
|
+
return JSON.parse(raw);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function backupConfig() {
|
|
19
|
+
const ts = Date.now();
|
|
20
|
+
const backupPath = join(OPENCLAW_DIR, `openclaw.json.bak.alice-${ts}`);
|
|
21
|
+
const raw = readFileSync(CONFIG_PATH, 'utf8');
|
|
22
|
+
writeFileSync(backupPath, raw, 'utf8');
|
|
23
|
+
return backupPath;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function writeConfigAtomic(config) {
|
|
27
|
+
const tmpPath = join(OPENCLAW_DIR, `.openclaw.json.tmp.${randomBytes(4).toString('hex')}`);
|
|
28
|
+
writeFileSync(tmpPath, JSON.stringify(config, null, 2) + '\n', 'utf8');
|
|
29
|
+
renameSync(tmpPath, CONFIG_PATH);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function getModelConfig(preset, customModels) {
|
|
33
|
+
switch (preset) {
|
|
34
|
+
case 'sonnet':
|
|
35
|
+
return {
|
|
36
|
+
primary: 'anthropic/claude-sonnet-4-6',
|
|
37
|
+
orchestrator: 'anthropic/claude-sonnet-4-6',
|
|
38
|
+
fallbacks: ['anthropic/claude-sonnet-4-6', 'anthropic/claude-haiku-4-5'],
|
|
39
|
+
models: {
|
|
40
|
+
'anthropic/claude-sonnet-4-6': {},
|
|
41
|
+
'anthropic/claude-haiku-4-5': {},
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
case 'opus-sonnet':
|
|
45
|
+
return {
|
|
46
|
+
primary: 'anthropic/claude-sonnet-4-6',
|
|
47
|
+
orchestrator: 'anthropic/claude-opus-4-6',
|
|
48
|
+
fallbacks: ['anthropic/claude-opus-4-6', 'anthropic/claude-sonnet-4-6', 'anthropic/claude-haiku-4-5'],
|
|
49
|
+
models: {
|
|
50
|
+
'anthropic/claude-opus-4-6': {},
|
|
51
|
+
'anthropic/claude-sonnet-4-6': {},
|
|
52
|
+
'anthropic/claude-haiku-4-5': {},
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
case 'openai':
|
|
56
|
+
return {
|
|
57
|
+
primary: 'openai/gpt-4.1-mini',
|
|
58
|
+
orchestrator: 'openai/gpt-4.1',
|
|
59
|
+
fallbacks: ['openai/gpt-4.1', 'openai/gpt-4.1-mini'],
|
|
60
|
+
models: {
|
|
61
|
+
'openai/gpt-4.1': {},
|
|
62
|
+
'openai/gpt-4.1-mini': {},
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
case 'local':
|
|
66
|
+
return {
|
|
67
|
+
primary: 'ollama/qwen3.5:27b',
|
|
68
|
+
orchestrator: 'ollama/qwen3.5:27b',
|
|
69
|
+
fallbacks: [],
|
|
70
|
+
models: {},
|
|
71
|
+
};
|
|
72
|
+
case 'custom':
|
|
73
|
+
return {
|
|
74
|
+
primary: customModels?.primary || 'anthropic/claude-sonnet-4-6',
|
|
75
|
+
orchestrator: customModels?.orchestrator || customModels?.primary || 'anthropic/claude-sonnet-4-6',
|
|
76
|
+
fallbacks: [],
|
|
77
|
+
models: {},
|
|
78
|
+
};
|
|
79
|
+
default:
|
|
80
|
+
return getModelConfig('sonnet');
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function buildAgentEntry(agent, modelCfg) {
|
|
85
|
+
const entry = {
|
|
86
|
+
id: agent.id,
|
|
87
|
+
name: agent.name,
|
|
88
|
+
workspace: join(OPENCLAW_DIR, `workspace-${agent.id}`),
|
|
89
|
+
identity: {
|
|
90
|
+
name: agent.name,
|
|
91
|
+
theme: agent.theme,
|
|
92
|
+
emoji: agent.emoji,
|
|
93
|
+
},
|
|
94
|
+
sandbox: agent.sandbox,
|
|
95
|
+
tools: agent.tools,
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// Orchestrator gets special model + extra config
|
|
99
|
+
if (agent.isOrchestrator) {
|
|
100
|
+
entry.default = true;
|
|
101
|
+
entry.model = modelCfg.orchestrator;
|
|
102
|
+
if (agent.extraConfig) {
|
|
103
|
+
Object.assign(entry, agent.extraConfig);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return entry;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function mergeConfig({ agents, mode, preset, customModels }) {
|
|
111
|
+
const backupPath = backupConfig();
|
|
112
|
+
const config = readConfig();
|
|
113
|
+
const modelCfg = getModelConfig(preset, customModels);
|
|
114
|
+
|
|
115
|
+
// Build agent entries
|
|
116
|
+
const aliceEntries = agents.map((a) => buildAgentEntry(a, modelCfg));
|
|
117
|
+
const aliceIds = new Set(agents.map((a) => a.id));
|
|
118
|
+
|
|
119
|
+
if (mode === 'fresh') {
|
|
120
|
+
// Replace agents entirely
|
|
121
|
+
config.agents = config.agents || {};
|
|
122
|
+
config.agents.list = aliceEntries;
|
|
123
|
+
} else if (mode === 'merge') {
|
|
124
|
+
// Preserve non-ALICE agents, add/replace ALICE ones
|
|
125
|
+
config.agents = config.agents || {};
|
|
126
|
+
const existing = (config.agents.list || []).filter((a) => !aliceIds.has(a.id));
|
|
127
|
+
config.agents.list = [...aliceEntries, ...existing];
|
|
128
|
+
} else if (mode === 'upgrade') {
|
|
129
|
+
// Only update existing ALICE agents, don't add new ones
|
|
130
|
+
config.agents = config.agents || {};
|
|
131
|
+
const list = config.agents.list || [];
|
|
132
|
+
config.agents.list = list.map((existing) => {
|
|
133
|
+
if (aliceIds.has(existing.id)) {
|
|
134
|
+
const updated = aliceEntries.find((a) => a.id === existing.id);
|
|
135
|
+
return { ...updated, workspace: existing.workspace };
|
|
136
|
+
}
|
|
137
|
+
return existing;
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Set defaults
|
|
142
|
+
config.agents.defaults = config.agents.defaults || {};
|
|
143
|
+
config.agents.defaults.model = {
|
|
144
|
+
primary: modelCfg.primary,
|
|
145
|
+
fallbacks: modelCfg.fallbacks,
|
|
146
|
+
};
|
|
147
|
+
if (Object.keys(modelCfg.models).length > 0) {
|
|
148
|
+
config.agents.defaults.models = modelCfg.models;
|
|
149
|
+
}
|
|
150
|
+
config.agents.defaults.workspace = join(OPENCLAW_DIR, 'workspace-olivia');
|
|
151
|
+
config.agents.defaults.compaction = config.agents.defaults.compaction || { mode: 'safeguard' };
|
|
152
|
+
config.agents.defaults.maxConcurrent = config.agents.defaults.maxConcurrent || 4;
|
|
153
|
+
config.agents.defaults.subagents = config.agents.defaults.subagents || {
|
|
154
|
+
maxConcurrent: 4,
|
|
155
|
+
archiveAfterMinutes: 120,
|
|
156
|
+
runTimeoutSeconds: 900,
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
// Agent-to-agent communication
|
|
160
|
+
config.tools = config.tools || {};
|
|
161
|
+
config.tools.agentToAgent = config.tools.agentToAgent || {};
|
|
162
|
+
config.tools.agentToAgent.enabled = true;
|
|
163
|
+
config.tools.agentToAgent.allow = [...aliceIds];
|
|
164
|
+
|
|
165
|
+
writeConfigAtomic(config);
|
|
166
|
+
return { backupPath, agentCount: aliceEntries.length };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export function removeAliceAgents(agentIds) {
|
|
170
|
+
const backupPath = backupConfig();
|
|
171
|
+
const config = readConfig();
|
|
172
|
+
const idsToRemove = new Set(agentIds);
|
|
173
|
+
|
|
174
|
+
if (config.agents?.list) {
|
|
175
|
+
config.agents.list = config.agents.list.filter((a) => !idsToRemove.has(a.id));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Clean up agentToAgent allow list
|
|
179
|
+
if (config.tools?.agentToAgent?.allow) {
|
|
180
|
+
config.tools.agentToAgent.allow = config.tools.agentToAgent.allow.filter((id) => !idsToRemove.has(id));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
writeConfigAtomic(config);
|
|
184
|
+
return { backupPath };
|
|
185
|
+
}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { join, dirname } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { configExists, mergeConfig, removeAliceAgents } from './config-merger.mjs';
|
|
5
|
+
import { scaffoldAll } from './workspace-scaffolder.mjs';
|
|
6
|
+
import { readManifest, writeManifest } from './manifest.mjs';
|
|
7
|
+
import {
|
|
8
|
+
promptInstallMode,
|
|
9
|
+
promptUserInfo,
|
|
10
|
+
promptModelPreset,
|
|
11
|
+
promptCustomModel,
|
|
12
|
+
promptTier,
|
|
13
|
+
confirm,
|
|
14
|
+
input,
|
|
15
|
+
closePrompt,
|
|
16
|
+
detectUserName,
|
|
17
|
+
detectTimezone,
|
|
18
|
+
} from './prompter.mjs';
|
|
19
|
+
|
|
20
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
21
|
+
|
|
22
|
+
function loadAgentRegistry() {
|
|
23
|
+
const raw = readFileSync(join(__dirname, '..', 'templates', 'agents.json'), 'utf8');
|
|
24
|
+
return JSON.parse(raw);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function filterByTier(agents, tier) {
|
|
28
|
+
if (tier === 'pro') return agents;
|
|
29
|
+
return agents.filter((a) => a.tier === 'starter');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function printBanner() {
|
|
33
|
+
console.log();
|
|
34
|
+
console.log(' โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ');
|
|
35
|
+
console.log(' โ โ');
|
|
36
|
+
console.log(' โ ๐ง A.L.I.C.E. Agent Framework Installer โ');
|
|
37
|
+
console.log(' โ โ');
|
|
38
|
+
console.log(' โ Adaptive Learning & Intelligent Coordination โ');
|
|
39
|
+
console.log(' โ Engine โ 28 specialists, one conversation. โ');
|
|
40
|
+
console.log(' โ โ');
|
|
41
|
+
console.log(' โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ');
|
|
42
|
+
console.log();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function printSummary(mode, tier, agents, preset, userInfo) {
|
|
46
|
+
console.log('\n โโ Install Summary โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ');
|
|
47
|
+
console.log(` Mode: ${mode}`);
|
|
48
|
+
console.log(` Tier: ${tier} (${agents.length} agents)`);
|
|
49
|
+
console.log(` Model: ${preset}`);
|
|
50
|
+
console.log(` User: ${userInfo.name}`);
|
|
51
|
+
console.log(` Timezone: ${userInfo.timezone}`);
|
|
52
|
+
console.log(' โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ');
|
|
53
|
+
console.log();
|
|
54
|
+
console.log(' Agents:');
|
|
55
|
+
for (const a of agents) {
|
|
56
|
+
console.log(` ${a.emoji} ${a.name.padEnd(10)} โ ${a.domain}`);
|
|
57
|
+
}
|
|
58
|
+
console.log();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export async function runInstall(options = {}) {
|
|
62
|
+
const auto = options.yes || false;
|
|
63
|
+
|
|
64
|
+
printBanner();
|
|
65
|
+
|
|
66
|
+
// 1. Detect OpenClaw
|
|
67
|
+
if (!configExists()) {
|
|
68
|
+
console.error(' โ OpenClaw not found. Install OpenClaw first:');
|
|
69
|
+
console.error(' https://openclaw.com/docs/install');
|
|
70
|
+
console.error();
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
console.log(' โ OpenClaw detected\n');
|
|
74
|
+
|
|
75
|
+
const allAgents = loadAgentRegistry();
|
|
76
|
+
|
|
77
|
+
// 2. Install mode
|
|
78
|
+
let mode;
|
|
79
|
+
if (auto) {
|
|
80
|
+
const manifest = readManifest();
|
|
81
|
+
mode = manifest ? 'upgrade' : 'fresh';
|
|
82
|
+
} else {
|
|
83
|
+
mode = await promptInstallMode();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (mode === 'upgrade') {
|
|
87
|
+
const manifest = readManifest();
|
|
88
|
+
if (!manifest) {
|
|
89
|
+
console.log(' โ No previous install found. Switching to fresh install.\n');
|
|
90
|
+
mode = 'fresh';
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (mode === 'fresh' && !auto) {
|
|
95
|
+
const ok = await confirm(' โ This will replace the agents section in openclaw.json. Continue?');
|
|
96
|
+
if (!ok) {
|
|
97
|
+
console.log(' Aborted.');
|
|
98
|
+
closePrompt();
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// 3. User info
|
|
104
|
+
let userInfo;
|
|
105
|
+
if (auto) {
|
|
106
|
+
userInfo = { name: detectUserName(), timezone: detectTimezone(), notes: '' };
|
|
107
|
+
} else {
|
|
108
|
+
console.log('\n โโ About You โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n');
|
|
109
|
+
userInfo = await promptUserInfo();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// 4. Model preset
|
|
113
|
+
let preset, customModels;
|
|
114
|
+
if (auto) {
|
|
115
|
+
preset = 'sonnet';
|
|
116
|
+
} else {
|
|
117
|
+
preset = await promptModelPreset();
|
|
118
|
+
if (preset === 'custom') {
|
|
119
|
+
customModels = await promptCustomModel();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// 5. Tier selection
|
|
124
|
+
let tier;
|
|
125
|
+
if (auto) {
|
|
126
|
+
tier = 'starter';
|
|
127
|
+
} else {
|
|
128
|
+
tier = await promptTier();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const agents = filterByTier(allAgents, tier);
|
|
132
|
+
|
|
133
|
+
// 6. Confirmation
|
|
134
|
+
printSummary(mode, tier, agents, preset, userInfo);
|
|
135
|
+
|
|
136
|
+
if (!auto) {
|
|
137
|
+
const ok = await confirm(' Proceed with installation?');
|
|
138
|
+
if (!ok) {
|
|
139
|
+
console.log(' Aborted.');
|
|
140
|
+
closePrompt();
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
closePrompt();
|
|
146
|
+
|
|
147
|
+
// Execute
|
|
148
|
+
console.log('\n Installing...\n');
|
|
149
|
+
|
|
150
|
+
// Merge config
|
|
151
|
+
const { backupPath, agentCount } = mergeConfig({
|
|
152
|
+
agents,
|
|
153
|
+
mode,
|
|
154
|
+
preset,
|
|
155
|
+
customModels,
|
|
156
|
+
});
|
|
157
|
+
console.log(` โ Config updated (backup: ${backupPath})`);
|
|
158
|
+
|
|
159
|
+
// Scaffold workspaces
|
|
160
|
+
const results = scaffoldAll(agents, userInfo);
|
|
161
|
+
let newWorkspaces = 0;
|
|
162
|
+
let updatedWorkspaces = 0;
|
|
163
|
+
for (const r of results) {
|
|
164
|
+
if (r.skipped.length === 0) {
|
|
165
|
+
newWorkspaces++;
|
|
166
|
+
} else {
|
|
167
|
+
updatedWorkspaces++;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
console.log(` โ Workspaces: ${newWorkspaces} created, ${updatedWorkspaces} updated`);
|
|
171
|
+
|
|
172
|
+
// Write manifest
|
|
173
|
+
const existing = readManifest();
|
|
174
|
+
writeManifest({
|
|
175
|
+
installedAt: existing?.installedAt,
|
|
176
|
+
tier,
|
|
177
|
+
agents: agents.map((a) => a.id),
|
|
178
|
+
userName: userInfo.name,
|
|
179
|
+
userTimezone: userInfo.timezone,
|
|
180
|
+
modelPreset: preset,
|
|
181
|
+
});
|
|
182
|
+
console.log(' โ Manifest written');
|
|
183
|
+
|
|
184
|
+
console.log(`\n ๐ A.L.I.C.E. installed! ${agentCount} agents ready.`);
|
|
185
|
+
console.log(' Restart OpenClaw to activate: openclaw gateway restart\n');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export async function runUninstall(options = {}) {
|
|
189
|
+
const auto = options.yes || false;
|
|
190
|
+
|
|
191
|
+
console.log('\n ๐ง A.L.I.C.E. Uninstaller\n');
|
|
192
|
+
|
|
193
|
+
const manifest = readManifest();
|
|
194
|
+
if (!manifest) {
|
|
195
|
+
console.error(' โ No A.L.I.C.E. installation found (no manifest).');
|
|
196
|
+
process.exit(1);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
console.log(` Found: ${manifest.agents.length} agents installed`);
|
|
200
|
+
console.log(` Tier: ${manifest.tier}`);
|
|
201
|
+
console.log();
|
|
202
|
+
|
|
203
|
+
if (!auto) {
|
|
204
|
+
const { createInterface } = await import('node:readline');
|
|
205
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
206
|
+
const answer = await new Promise((resolve) => {
|
|
207
|
+
rl.question(' Remove A.L.I.C.E. agents from config? [y/N] ', resolve);
|
|
208
|
+
});
|
|
209
|
+
rl.close();
|
|
210
|
+
if (!answer.toLowerCase().startsWith('y')) {
|
|
211
|
+
console.log(' Aborted.');
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const { backupPath } = removeAliceAgents(manifest.agents);
|
|
217
|
+
console.log(` โ Agents removed from config (backup: ${backupPath})`);
|
|
218
|
+
|
|
219
|
+
if (!auto) {
|
|
220
|
+
const { createInterface } = await import('node:readline');
|
|
221
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
222
|
+
const answer = await new Promise((resolve) => {
|
|
223
|
+
rl.question(' Also remove workspace directories? [y/N] ', resolve);
|
|
224
|
+
});
|
|
225
|
+
rl.close();
|
|
226
|
+
if (answer.toLowerCase().startsWith('y')) {
|
|
227
|
+
console.log(' โ Workspace removal not implemented (safety measure).');
|
|
228
|
+
console.log(' Remove manually: rm -rf ~/.openclaw/workspace-{agent-id}');
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
console.log('\n โ A.L.I.C.E. uninstalled. Restart OpenClaw: openclaw gateway restart\n');
|
|
233
|
+
}
|
package/lib/manifest.mjs
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
|
|
5
|
+
const MANIFEST_NAME = '.alice-manifest.json';
|
|
6
|
+
|
|
7
|
+
export function getManifestPath() {
|
|
8
|
+
return join(homedir(), '.openclaw', MANIFEST_NAME);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function readManifest() {
|
|
12
|
+
try {
|
|
13
|
+
const raw = readFileSync(getManifestPath(), 'utf8');
|
|
14
|
+
return JSON.parse(raw);
|
|
15
|
+
} catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function writeManifest(data) {
|
|
21
|
+
const manifest = {
|
|
22
|
+
version: '1.0.0',
|
|
23
|
+
installedAt: data.installedAt || new Date().toISOString(),
|
|
24
|
+
updatedAt: new Date().toISOString(),
|
|
25
|
+
tier: data.tier,
|
|
26
|
+
agents: data.agents,
|
|
27
|
+
userName: data.userName,
|
|
28
|
+
userTimezone: data.userTimezone,
|
|
29
|
+
modelPreset: data.modelPreset,
|
|
30
|
+
};
|
|
31
|
+
writeFileSync(getManifestPath(), JSON.stringify(manifest, null, 2) + '\n', 'utf8');
|
|
32
|
+
return manifest;
|
|
33
|
+
}
|