@happycastle/oh-my-openclaw 0.6.2 → 0.8.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/dist/agents/agent-configs.js +6 -6
- package/dist/agents/persona-prompts.d.ts +14 -0
- package/dist/agents/persona-prompts.js +74 -0
- package/dist/cli/model-presets.d.ts +14 -0
- package/dist/cli/model-presets.js +57 -0
- package/dist/cli/setup.d.ts +6 -0
- package/dist/cli/setup.js +114 -4
- package/dist/commands/persona-commands.d.ts +2 -0
- package/dist/commands/persona-commands.js +70 -0
- package/dist/hooks/persona-injector.d.ts +2 -0
- package/dist/hooks/persona-injector.js +27 -0
- package/dist/index.js +18 -0
- package/dist/utils/persona-state.d.ts +3 -0
- package/dist/utils/persona-state.js +10 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
|
@@ -4,7 +4,7 @@ export const OMOC_AGENT_CONFIGS = [
|
|
|
4
4
|
id: 'omoc_prometheus',
|
|
5
5
|
name: 'Prometheus',
|
|
6
6
|
model: {
|
|
7
|
-
primary: 'openai/
|
|
7
|
+
primary: 'openai/gpt-5.3-codex',
|
|
8
8
|
fallbacks: ['anthropic/claude-opus-4-6'],
|
|
9
9
|
},
|
|
10
10
|
identity: {
|
|
@@ -37,7 +37,7 @@ export const OMOC_AGENT_CONFIGS = [
|
|
|
37
37
|
name: 'Sisyphus-Junior',
|
|
38
38
|
model: {
|
|
39
39
|
primary: 'anthropic/claude-opus-4-6',
|
|
40
|
-
fallbacks: ['openai/
|
|
40
|
+
fallbacks: ['openai/gpt-5.3-codex'],
|
|
41
41
|
},
|
|
42
42
|
identity: {
|
|
43
43
|
name: 'Sisyphus-Junior',
|
|
@@ -55,7 +55,7 @@ export const OMOC_AGENT_CONFIGS = [
|
|
|
55
55
|
name: 'Hephaestus',
|
|
56
56
|
model: {
|
|
57
57
|
primary: 'anthropic/claude-opus-4-6',
|
|
58
|
-
fallbacks: ['openai/
|
|
58
|
+
fallbacks: ['openai/gpt-5.3-codex'],
|
|
59
59
|
},
|
|
60
60
|
identity: {
|
|
61
61
|
name: 'Hephaestus',
|
|
@@ -72,7 +72,7 @@ export const OMOC_AGENT_CONFIGS = [
|
|
|
72
72
|
id: 'omoc_oracle',
|
|
73
73
|
name: 'Oracle',
|
|
74
74
|
model: {
|
|
75
|
-
primary: 'openai/
|
|
75
|
+
primary: 'openai/gpt-5.3-codex',
|
|
76
76
|
fallbacks: ['anthropic/claude-opus-4-6'],
|
|
77
77
|
},
|
|
78
78
|
identity: {
|
|
@@ -121,7 +121,7 @@ export const OMOC_AGENT_CONFIGS = [
|
|
|
121
121
|
name: 'Metis',
|
|
122
122
|
model: {
|
|
123
123
|
primary: 'anthropic/claude-opus-4-6',
|
|
124
|
-
fallbacks: ['openai/
|
|
124
|
+
fallbacks: ['openai/gpt-5.3-codex'],
|
|
125
125
|
},
|
|
126
126
|
identity: {
|
|
127
127
|
name: 'Metis',
|
|
@@ -139,7 +139,7 @@ export const OMOC_AGENT_CONFIGS = [
|
|
|
139
139
|
name: 'Momus',
|
|
140
140
|
model: {
|
|
141
141
|
primary: 'anthropic/claude-opus-4-6',
|
|
142
|
-
fallbacks: ['openai/
|
|
142
|
+
fallbacks: ['openai/gpt-5.3-codex'],
|
|
143
143
|
},
|
|
144
144
|
identity: {
|
|
145
145
|
name: 'Momus',
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve user input ("omoc_atlas", "atlas", or "Atlas") to a canonical agent config ID.
|
|
3
|
+
*/
|
|
4
|
+
export declare function resolvePersonaId(input: string): string | null;
|
|
5
|
+
export declare function readPersonaPromptSync(agentId: string): string;
|
|
6
|
+
export declare function readPersonaPrompt(agentId: string): Promise<string>;
|
|
7
|
+
export declare function listPersonas(): Array<{
|
|
8
|
+
id: string;
|
|
9
|
+
shortName: string;
|
|
10
|
+
displayName: string;
|
|
11
|
+
emoji: string;
|
|
12
|
+
theme: string;
|
|
13
|
+
}>;
|
|
14
|
+
export declare const DEFAULT_PERSONA_ID = "omoc_atlas";
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
|
+
import { promises as fs } from 'fs';
|
|
3
|
+
import { dirname, join } from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { OMOC_AGENT_CONFIGS } from './agent-configs.js';
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = dirname(__filename);
|
|
8
|
+
// From dist/agents/ → plugin root is ../../ (same pattern as workflow-commands.ts)
|
|
9
|
+
const PLUGIN_ROOT = join(__dirname, '..', '..');
|
|
10
|
+
const AGENT_MD_MAP = {
|
|
11
|
+
omoc_atlas: 'atlas',
|
|
12
|
+
omoc_prometheus: 'prometheus',
|
|
13
|
+
omoc_sisyphus: 'sisyphus-junior',
|
|
14
|
+
omoc_hephaestus: 'hephaestus',
|
|
15
|
+
omoc_oracle: 'oracle',
|
|
16
|
+
omoc_explore: 'explore',
|
|
17
|
+
omoc_librarian: 'librarian',
|
|
18
|
+
omoc_metis: 'metis',
|
|
19
|
+
omoc_momus: 'momus',
|
|
20
|
+
omoc_looker: 'multimodal-looker',
|
|
21
|
+
omoc_frontend: 'frontend',
|
|
22
|
+
};
|
|
23
|
+
const SHORT_ID_MAP = {};
|
|
24
|
+
for (const id of Object.keys(AGENT_MD_MAP)) {
|
|
25
|
+
SHORT_ID_MAP[id.replace('omoc_', '')] = id;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Resolve user input ("omoc_atlas", "atlas", or "Atlas") to a canonical agent config ID.
|
|
29
|
+
*/
|
|
30
|
+
export function resolvePersonaId(input) {
|
|
31
|
+
const lower = input.toLowerCase().trim();
|
|
32
|
+
if (AGENT_MD_MAP[lower])
|
|
33
|
+
return lower;
|
|
34
|
+
if (SHORT_ID_MAP[lower])
|
|
35
|
+
return SHORT_ID_MAP[lower];
|
|
36
|
+
const byName = OMOC_AGENT_CONFIGS.find((a) => a.name?.toLowerCase() === lower || a.identity?.name?.toLowerCase() === lower);
|
|
37
|
+
return byName?.id ?? null;
|
|
38
|
+
}
|
|
39
|
+
export function readPersonaPromptSync(agentId) {
|
|
40
|
+
const mdName = AGENT_MD_MAP[agentId];
|
|
41
|
+
if (!mdName) {
|
|
42
|
+
return `[OmOC] Unknown persona: ${agentId}`;
|
|
43
|
+
}
|
|
44
|
+
const agentPath = join(PLUGIN_ROOT, '..', 'agents', `${mdName}.md`);
|
|
45
|
+
try {
|
|
46
|
+
return readFileSync(agentPath, 'utf-8');
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return `[OmOC] Could not read persona file: agents/${mdName}.md (looked in ${agentPath})`;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export async function readPersonaPrompt(agentId) {
|
|
53
|
+
const mdName = AGENT_MD_MAP[agentId];
|
|
54
|
+
if (!mdName) {
|
|
55
|
+
return `[OmOC] Unknown persona: ${agentId}`;
|
|
56
|
+
}
|
|
57
|
+
const agentPath = join(PLUGIN_ROOT, '..', 'agents', `${mdName}.md`);
|
|
58
|
+
try {
|
|
59
|
+
return await fs.readFile(agentPath, 'utf-8');
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return `[OmOC] Could not read persona file: agents/${mdName}.md (looked in ${agentPath})`;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
export function listPersonas() {
|
|
66
|
+
return OMOC_AGENT_CONFIGS.map((agent) => ({
|
|
67
|
+
id: agent.id,
|
|
68
|
+
shortName: agent.id.replace('omoc_', ''),
|
|
69
|
+
displayName: agent.identity?.name ?? agent.name ?? agent.id,
|
|
70
|
+
emoji: agent.identity?.emoji ?? '',
|
|
71
|
+
theme: agent.identity?.theme ?? '',
|
|
72
|
+
}));
|
|
73
|
+
}
|
|
74
|
+
export const DEFAULT_PERSONA_ID = 'omoc_atlas';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type ModelTier = 'planning' | 'worker' | 'orchestrator' | 'lightweight' | 'visual';
|
|
2
|
+
export type ModelConfig = {
|
|
3
|
+
primary: string;
|
|
4
|
+
fallbacks: string[];
|
|
5
|
+
};
|
|
6
|
+
export type ProviderPreset = Record<ModelTier, ModelConfig>;
|
|
7
|
+
export declare const PROVIDER_PRESETS: Record<string, ProviderPreset>;
|
|
8
|
+
export declare const AGENT_TIER_MAP: Record<string, ModelTier>;
|
|
9
|
+
export declare const PROVIDER_LABELS: Record<string, string>;
|
|
10
|
+
export declare function getProviderNames(): string[];
|
|
11
|
+
export declare function applyProviderPreset(agentId: string, provider: string): {
|
|
12
|
+
primary: string;
|
|
13
|
+
fallbacks?: string[];
|
|
14
|
+
} | undefined;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export const PROVIDER_PRESETS = {
|
|
2
|
+
anthropic: {
|
|
3
|
+
planning: { primary: 'anthropic/claude-opus-4-6', fallbacks: ['openai/gpt-5.3-codex'] },
|
|
4
|
+
worker: { primary: 'anthropic/claude-opus-4-6', fallbacks: ['openai/gpt-5.3-codex'] },
|
|
5
|
+
orchestrator: { primary: 'anthropic/claude-sonnet-4-6', fallbacks: ['openai/gpt-4.1'] },
|
|
6
|
+
lightweight: { primary: 'anthropic/claude-sonnet-4-6', fallbacks: [] },
|
|
7
|
+
visual: { primary: 'google/gemini-2.5-pro', fallbacks: ['anthropic/claude-sonnet-4-6'] },
|
|
8
|
+
},
|
|
9
|
+
openai: {
|
|
10
|
+
planning: { primary: 'openai/gpt-5.3-codex', fallbacks: ['anthropic/claude-opus-4-6'] },
|
|
11
|
+
worker: { primary: 'openai/gpt-5.3-codex', fallbacks: ['anthropic/claude-opus-4-6'] },
|
|
12
|
+
orchestrator: { primary: 'openai/gpt-4.1', fallbacks: ['anthropic/claude-sonnet-4-6'] },
|
|
13
|
+
lightweight: { primary: 'openai/gpt-4.1-mini', fallbacks: [] },
|
|
14
|
+
visual: { primary: 'google/gemini-2.5-pro', fallbacks: ['openai/gpt-4.1'] },
|
|
15
|
+
},
|
|
16
|
+
google: {
|
|
17
|
+
planning: { primary: 'google/gemini-3.1-pro', fallbacks: ['anthropic/claude-opus-4-6'] },
|
|
18
|
+
worker: { primary: 'google/gemini-3.1-pro', fallbacks: ['anthropic/claude-opus-4-6'] },
|
|
19
|
+
orchestrator: { primary: 'google/gemini-3-flash', fallbacks: ['anthropic/claude-sonnet-4-6'] },
|
|
20
|
+
lightweight: { primary: 'google/gemini-3-flash', fallbacks: [] },
|
|
21
|
+
visual: { primary: 'google/gemini-2.5-pro', fallbacks: ['google/gemini-3-flash'] },
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
export const AGENT_TIER_MAP = {
|
|
25
|
+
omoc_prometheus: 'planning',
|
|
26
|
+
omoc_oracle: 'planning',
|
|
27
|
+
omoc_metis: 'planning',
|
|
28
|
+
omoc_momus: 'planning',
|
|
29
|
+
omoc_sisyphus: 'worker',
|
|
30
|
+
omoc_hephaestus: 'worker',
|
|
31
|
+
omoc_atlas: 'orchestrator',
|
|
32
|
+
omoc_explore: 'lightweight',
|
|
33
|
+
omoc_librarian: 'lightweight',
|
|
34
|
+
omoc_looker: 'visual',
|
|
35
|
+
omoc_frontend: 'visual',
|
|
36
|
+
};
|
|
37
|
+
export const PROVIDER_LABELS = {
|
|
38
|
+
anthropic: 'Anthropic (Claude)',
|
|
39
|
+
openai: 'OpenAI (GPT)',
|
|
40
|
+
google: 'Google (Gemini)',
|
|
41
|
+
};
|
|
42
|
+
export function getProviderNames() {
|
|
43
|
+
return Object.keys(PROVIDER_PRESETS);
|
|
44
|
+
}
|
|
45
|
+
export function applyProviderPreset(agentId, provider) {
|
|
46
|
+
const preset = PROVIDER_PRESETS[provider];
|
|
47
|
+
if (!preset)
|
|
48
|
+
return undefined;
|
|
49
|
+
const tier = AGENT_TIER_MAP[agentId];
|
|
50
|
+
if (!tier)
|
|
51
|
+
return undefined;
|
|
52
|
+
const config = preset[tier];
|
|
53
|
+
return {
|
|
54
|
+
primary: config.primary,
|
|
55
|
+
fallbacks: config.fallbacks.length > 0 ? config.fallbacks : undefined,
|
|
56
|
+
};
|
|
57
|
+
}
|
package/dist/cli/setup.d.ts
CHANGED
|
@@ -37,11 +37,17 @@ export declare function mergeAgentConfigs(existing: Array<{
|
|
|
37
37
|
}>;
|
|
38
38
|
result: MergeResult;
|
|
39
39
|
};
|
|
40
|
+
export declare function applyProviderToConfigs(configs: OmocAgentConfig[], provider: string): OmocAgentConfig[];
|
|
41
|
+
export declare function runInteractiveSetup(logger: Logger): Promise<{
|
|
42
|
+
provider: string;
|
|
43
|
+
}>;
|
|
40
44
|
export interface SetupOptions {
|
|
41
45
|
configPath?: string;
|
|
42
46
|
workspaceDir?: string;
|
|
43
47
|
force?: boolean;
|
|
44
48
|
dryRun?: boolean;
|
|
49
|
+
provider?: string;
|
|
50
|
+
interactive?: boolean;
|
|
45
51
|
logger: Logger;
|
|
46
52
|
}
|
|
47
53
|
export declare function runSetup(options: SetupOptions): MergeResult;
|
package/dist/cli/setup.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import * as readline from 'node:readline';
|
|
3
4
|
import JSON5 from 'json5';
|
|
4
5
|
import { OMOC_AGENT_CONFIGS } from '../agents/agent-configs.js';
|
|
6
|
+
import { PROVIDER_PRESETS, PROVIDER_LABELS, AGENT_TIER_MAP, applyProviderPreset, getProviderNames, } from './model-presets.js';
|
|
5
7
|
const CONFIG_FILENAMES = [
|
|
6
8
|
'openclaw.json5',
|
|
7
9
|
'openclaw.json',
|
|
@@ -66,8 +68,95 @@ export function mergeAgentConfigs(existing, incoming, force) {
|
|
|
66
68
|
}
|
|
67
69
|
return { merged, result };
|
|
68
70
|
}
|
|
71
|
+
export function applyProviderToConfigs(configs, provider) {
|
|
72
|
+
return configs.map((agent) => {
|
|
73
|
+
const modelOverride = applyProviderPreset(agent.id, provider);
|
|
74
|
+
if (!modelOverride)
|
|
75
|
+
return agent;
|
|
76
|
+
return {
|
|
77
|
+
...agent,
|
|
78
|
+
model: modelOverride.fallbacks
|
|
79
|
+
? { primary: modelOverride.primary, fallbacks: modelOverride.fallbacks }
|
|
80
|
+
: modelOverride.primary,
|
|
81
|
+
};
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
function askQuestion(rl, question) {
|
|
85
|
+
return new Promise((resolve) => {
|
|
86
|
+
rl.question(question, (answer) => resolve(answer.trim()));
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
const TIER_LABELS = {
|
|
90
|
+
planning: 'Planning/Architecture',
|
|
91
|
+
worker: 'Implementation Workers',
|
|
92
|
+
orchestrator: 'Task Orchestrator',
|
|
93
|
+
lightweight: 'Search/Research',
|
|
94
|
+
visual: 'Visual/Frontend',
|
|
95
|
+
};
|
|
96
|
+
export async function runInteractiveSetup(logger) {
|
|
97
|
+
const rl = readline.createInterface({
|
|
98
|
+
input: process.stdin,
|
|
99
|
+
output: process.stdout,
|
|
100
|
+
});
|
|
101
|
+
try {
|
|
102
|
+
logger.info('');
|
|
103
|
+
logger.info('🗺️ Oh-My-OpenClaw Agent Setup');
|
|
104
|
+
logger.info('─'.repeat(40));
|
|
105
|
+
logger.info('');
|
|
106
|
+
const providers = getProviderNames();
|
|
107
|
+
logger.info('Step 1/2: Select your primary AI provider');
|
|
108
|
+
logger.info('');
|
|
109
|
+
providers.forEach((p, i) => {
|
|
110
|
+
logger.info(` ${i + 1}. ${PROVIDER_LABELS[p] ?? p}`);
|
|
111
|
+
});
|
|
112
|
+
logger.info('');
|
|
113
|
+
let provider = '';
|
|
114
|
+
while (!provider) {
|
|
115
|
+
const answer = await askQuestion(rl, ' Select (1-3): ');
|
|
116
|
+
const idx = parseInt(answer, 10) - 1;
|
|
117
|
+
if (idx >= 0 && idx < providers.length) {
|
|
118
|
+
provider = providers[idx];
|
|
119
|
+
}
|
|
120
|
+
else if (providers.includes(answer.toLowerCase())) {
|
|
121
|
+
provider = answer.toLowerCase();
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
logger.info(' Invalid choice. Enter 1, 2, or 3.');
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
logger.info('');
|
|
128
|
+
logger.info(` ✓ Selected: ${PROVIDER_LABELS[provider]}`);
|
|
129
|
+
logger.info('');
|
|
130
|
+
logger.info('Step 2/2: Model configuration preview');
|
|
131
|
+
logger.info('');
|
|
132
|
+
const preset = PROVIDER_PRESETS[provider];
|
|
133
|
+
for (const [tier, label] of Object.entries(TIER_LABELS)) {
|
|
134
|
+
const config = preset[tier];
|
|
135
|
+
const agents = Object.entries(AGENT_TIER_MAP)
|
|
136
|
+
.filter(([, t]) => t === tier)
|
|
137
|
+
.map(([id]) => id.replace('omoc_', ''))
|
|
138
|
+
.join(', ');
|
|
139
|
+
logger.info(` ${label} (${agents}):`);
|
|
140
|
+
logger.info(` → ${config.primary}`);
|
|
141
|
+
if (config.fallbacks.length > 0) {
|
|
142
|
+
logger.info(` fallback: ${config.fallbacks.join(', ')}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
logger.info('');
|
|
146
|
+
const confirm = await askQuestion(rl, ' Apply this configuration? (Y/n): ');
|
|
147
|
+
if (confirm.toLowerCase() === 'n' || confirm.toLowerCase() === 'no') {
|
|
148
|
+
logger.info(' Setup cancelled.');
|
|
149
|
+
return { provider: '' };
|
|
150
|
+
}
|
|
151
|
+
logger.info('');
|
|
152
|
+
return { provider };
|
|
153
|
+
}
|
|
154
|
+
finally {
|
|
155
|
+
rl.close();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
69
158
|
export function runSetup(options) {
|
|
70
|
-
const { logger, force = false, dryRun = false } = options;
|
|
159
|
+
const { logger, force = false, dryRun = false, provider } = options;
|
|
71
160
|
const configPath = options.configPath ?? findConfigPath(options.workspaceDir);
|
|
72
161
|
if (!configPath) {
|
|
73
162
|
throw new Error('Could not find OpenClaw config file. Searched for: ' +
|
|
@@ -90,7 +179,13 @@ export function runSetup(options) {
|
|
|
90
179
|
if (!config.agents.list) {
|
|
91
180
|
config.agents.list = [];
|
|
92
181
|
}
|
|
93
|
-
const
|
|
182
|
+
const agentConfigs = provider
|
|
183
|
+
? applyProviderToConfigs(OMOC_AGENT_CONFIGS, provider)
|
|
184
|
+
: OMOC_AGENT_CONFIGS;
|
|
185
|
+
if (provider) {
|
|
186
|
+
logger.info(`Using provider preset: ${PROVIDER_LABELS[provider] ?? provider}`);
|
|
187
|
+
}
|
|
188
|
+
const { merged, result } = mergeAgentConfigs(config.agents.list, agentConfigs, force);
|
|
94
189
|
config.agents.list = merged;
|
|
95
190
|
if (dryRun) {
|
|
96
191
|
logger.info('[dry-run] Would write config to: ' + configPath);
|
|
@@ -123,16 +218,31 @@ export function registerSetupCli(ctx) {
|
|
|
123
218
|
.option('--force', 'Overwrite existing OmOC agent configs', false)
|
|
124
219
|
.option('--dry-run', 'Preview changes without writing', false)
|
|
125
220
|
.option('--config <path>', 'Path to OpenClaw config file')
|
|
126
|
-
.
|
|
221
|
+
.option('--provider <name>', 'AI provider preset: anthropic, openai, google (skips interactive)')
|
|
222
|
+
.action(async (...args) => {
|
|
127
223
|
const opts = (args[0] ?? {});
|
|
128
224
|
try {
|
|
225
|
+
let provider = opts.provider;
|
|
226
|
+
if (provider && !PROVIDER_PRESETS[provider]) {
|
|
227
|
+
const valid = getProviderNames().join(', ');
|
|
228
|
+
throw new Error(`Unknown provider "${provider}". Valid: ${valid}`);
|
|
229
|
+
}
|
|
230
|
+
if (!provider && process.stdin.isTTY) {
|
|
231
|
+
const result = await runInteractiveSetup(ctx.logger);
|
|
232
|
+
if (!result.provider)
|
|
233
|
+
return;
|
|
234
|
+
provider = result.provider;
|
|
235
|
+
}
|
|
129
236
|
runSetup({
|
|
130
237
|
configPath: opts.config,
|
|
131
238
|
workspaceDir: ctx.workspaceDir,
|
|
132
|
-
force: opts.force,
|
|
239
|
+
force: provider ? true : opts.force,
|
|
133
240
|
dryRun: opts.dryRun,
|
|
241
|
+
provider,
|
|
134
242
|
logger: ctx.logger,
|
|
135
243
|
});
|
|
244
|
+
ctx.logger.info('');
|
|
245
|
+
ctx.logger.info('✓ Setup complete! Restart OpenClaw to apply changes.');
|
|
136
246
|
}
|
|
137
247
|
catch (err) {
|
|
138
248
|
ctx.logger.error(`Setup failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { getActivePersona, setActivePersona, resetPersonaState } from '../utils/persona-state.js';
|
|
2
|
+
import { resolvePersonaId, listPersonas, DEFAULT_PERSONA_ID } from '../agents/persona-prompts.js';
|
|
3
|
+
export function registerPersonaCommands(api) {
|
|
4
|
+
api.registerCommand({
|
|
5
|
+
name: 'omoc',
|
|
6
|
+
description: 'OmOC mode — activate, switch, or list personas',
|
|
7
|
+
acceptsArgs: true,
|
|
8
|
+
handler: async (ctx) => {
|
|
9
|
+
const args = (ctx.args ?? '').trim().toLowerCase();
|
|
10
|
+
if (!args) {
|
|
11
|
+
setActivePersona(DEFAULT_PERSONA_ID);
|
|
12
|
+
const personas = listPersonas();
|
|
13
|
+
const defaultPersona = personas.find((p) => p.id === DEFAULT_PERSONA_ID);
|
|
14
|
+
const name = defaultPersona
|
|
15
|
+
? `${defaultPersona.emoji} ${defaultPersona.displayName}`
|
|
16
|
+
: DEFAULT_PERSONA_ID;
|
|
17
|
+
return {
|
|
18
|
+
text: `# OmOC Mode: ON\n\nActive persona: **${name}**\n\nThe persona prompt will be injected into all new agent sessions.\n\nUse \`/omoc list\` to see available personas, or \`/omoc <name>\` to switch.`,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
if (args === 'off') {
|
|
22
|
+
const wasActive = getActivePersona();
|
|
23
|
+
resetPersonaState();
|
|
24
|
+
return {
|
|
25
|
+
text: wasActive
|
|
26
|
+
? `# OmOC Mode: OFF\n\nPersona **${wasActive}** deactivated. Agent sessions will use default behavior.`
|
|
27
|
+
: '# OmOC Mode: OFF\n\nNo persona was active.',
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
if (args === 'list') {
|
|
31
|
+
const personas = listPersonas();
|
|
32
|
+
const activeId = getActivePersona();
|
|
33
|
+
const lines = personas.map((p) => {
|
|
34
|
+
const active = p.id === activeId ? ' ← active' : '';
|
|
35
|
+
return `| ${p.emoji} | \`${p.shortName}\` | ${p.displayName} | ${p.theme} |${active}`;
|
|
36
|
+
});
|
|
37
|
+
return {
|
|
38
|
+
text: [
|
|
39
|
+
'# OmOC Personas',
|
|
40
|
+
'',
|
|
41
|
+
`Active: ${activeId ? `**${activeId}**` : '_none_'}`,
|
|
42
|
+
'',
|
|
43
|
+
'| | Command | Name | Role |',
|
|
44
|
+
'|---|---------|------|------|',
|
|
45
|
+
...lines,
|
|
46
|
+
'',
|
|
47
|
+
'Usage: `/omoc <command>` — e.g., `/omoc prometheus`',
|
|
48
|
+
].join('\n'),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
const resolvedId = resolvePersonaId(args);
|
|
52
|
+
if (!resolvedId) {
|
|
53
|
+
const personas = listPersonas();
|
|
54
|
+
const available = personas.map((p) => `\`${p.shortName}\``).join(', ');
|
|
55
|
+
return {
|
|
56
|
+
text: `# Unknown Persona: "${args}"\n\nAvailable personas: ${available}\n\nUse \`/omoc list\` for details.`,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
setActivePersona(resolvedId);
|
|
60
|
+
const personas = listPersonas();
|
|
61
|
+
const switched = personas.find((p) => p.id === resolvedId);
|
|
62
|
+
const displayName = switched
|
|
63
|
+
? `${switched.emoji} ${switched.displayName}`
|
|
64
|
+
: resolvedId;
|
|
65
|
+
return {
|
|
66
|
+
text: `# Persona Switched\n\nActive persona: **${displayName}**\n\nThe ${switched?.theme ?? 'persona'} prompt will be injected into all new agent sessions.`,
|
|
67
|
+
};
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { getActivePersona } from '../utils/persona-state.js';
|
|
2
|
+
import { readPersonaPromptSync } from '../agents/persona-prompts.js';
|
|
3
|
+
export function registerPersonaInjector(api) {
|
|
4
|
+
api.registerHook('agent:bootstrap', (event) => {
|
|
5
|
+
const personaId = getActivePersona();
|
|
6
|
+
if (!personaId) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
if (!event.context.bootstrapFiles) {
|
|
10
|
+
event.context.bootstrapFiles = [];
|
|
11
|
+
}
|
|
12
|
+
try {
|
|
13
|
+
const content = readPersonaPromptSync(personaId);
|
|
14
|
+
event.context.bootstrapFiles.push({
|
|
15
|
+
path: `omoc://persona/${personaId}`,
|
|
16
|
+
content,
|
|
17
|
+
});
|
|
18
|
+
api.logger.info(`[omoc] Persona injected: ${personaId}`);
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
api.logger.error(`[omoc] Failed to inject persona ${personaId}:`, err);
|
|
22
|
+
}
|
|
23
|
+
}, {
|
|
24
|
+
name: 'oh-my-openclaw.persona-injector',
|
|
25
|
+
description: 'Injects active persona prompt into agent bootstrap',
|
|
26
|
+
});
|
|
27
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -12,6 +12,8 @@ import { registerCheckpointTool } from './tools/checkpoint.js';
|
|
|
12
12
|
import { registerWorkflowCommands } from './commands/workflow-commands.js';
|
|
13
13
|
import { registerRalphCommands } from './commands/ralph-commands.js';
|
|
14
14
|
import { registerStatusCommands } from './commands/status-commands.js';
|
|
15
|
+
import { registerPersonaCommands } from './commands/persona-commands.js';
|
|
16
|
+
import { registerPersonaInjector } from './hooks/persona-injector.js';
|
|
15
17
|
import { registerSetupCli } from './cli/setup.js';
|
|
16
18
|
const registry = {
|
|
17
19
|
hooks: [],
|
|
@@ -55,6 +57,14 @@ export default function register(api) {
|
|
|
55
57
|
catch (err) {
|
|
56
58
|
api.logger.error(`[${PLUGIN_ID}] Failed to register startup hook:`, err);
|
|
57
59
|
}
|
|
60
|
+
try {
|
|
61
|
+
registerPersonaInjector(api);
|
|
62
|
+
registry.hooks.push('persona-injector');
|
|
63
|
+
api.logger.info(`[${PLUGIN_ID}] Persona injector hook registered`);
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
api.logger.error(`[${PLUGIN_ID}] Failed to register Persona Injector:`, err);
|
|
67
|
+
}
|
|
58
68
|
try {
|
|
59
69
|
registerRalphLoop(api);
|
|
60
70
|
registry.services.push('ralph-loop');
|
|
@@ -111,6 +121,14 @@ export default function register(api) {
|
|
|
111
121
|
catch (err) {
|
|
112
122
|
api.logger.error(`[${PLUGIN_ID}] Failed to register Status commands:`, err);
|
|
113
123
|
}
|
|
124
|
+
try {
|
|
125
|
+
registerPersonaCommands(api);
|
|
126
|
+
registry.commands.push('omoc');
|
|
127
|
+
api.logger.info(`[${PLUGIN_ID}] Persona command registered (/omoc)`);
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
api.logger.error(`[${PLUGIN_ID}] Failed to register Persona commands:`, err);
|
|
131
|
+
}
|
|
114
132
|
try {
|
|
115
133
|
api.registerCli((ctx) => {
|
|
116
134
|
registerSetupCli({
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "oh-my-openclaw",
|
|
3
3
|
"name": "Oh-My-OpenClaw",
|
|
4
4
|
"description": "Multi-agent orchestration plugin \u2014 11 agents, category-based model routing, todo enforcer, ralph loop, agent setup CLI, and custom tools",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.8.0",
|
|
6
6
|
"skills": ["skills"],
|
|
7
7
|
"configSchema": {
|
|
8
8
|
"type": "object",
|
package/package.json
CHANGED