@happycastle/oh-my-openclaw 0.7.0 → 0.8.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/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 +17 -0
- package/dist/cli/model-presets.js +72 -0
- package/dist/cli/setup.d.ts +6 -0
- package/dist/cli/setup.js +148 -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,17 @@
|
|
|
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 const MODEL_TIERS: ModelTier[];
|
|
11
|
+
export declare function buildCustomPreset(tierModels: Record<ModelTier, string>): ProviderPreset;
|
|
12
|
+
export declare function registerCustomPreset(name: string, preset: ProviderPreset): void;
|
|
13
|
+
export declare function getProviderNames(): string[];
|
|
14
|
+
export declare function applyProviderPreset(agentId: string, provider: string): {
|
|
15
|
+
primary: string;
|
|
16
|
+
fallbacks?: string[];
|
|
17
|
+
} | undefined;
|
|
@@ -0,0 +1,72 @@
|
|
|
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
|
+
custom: 'Custom (enter model IDs manually)',
|
|
42
|
+
};
|
|
43
|
+
export const MODEL_TIERS = ['planning', 'worker', 'orchestrator', 'lightweight', 'visual'];
|
|
44
|
+
export function buildCustomPreset(tierModels) {
|
|
45
|
+
const preset = {};
|
|
46
|
+
for (const tier of MODEL_TIERS) {
|
|
47
|
+
preset[tier] = { primary: tierModels[tier], fallbacks: [] };
|
|
48
|
+
}
|
|
49
|
+
return preset;
|
|
50
|
+
}
|
|
51
|
+
export function registerCustomPreset(name, preset) {
|
|
52
|
+
PROVIDER_PRESETS[name] = preset;
|
|
53
|
+
if (!PROVIDER_LABELS[name]) {
|
|
54
|
+
PROVIDER_LABELS[name] = name;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export function getProviderNames() {
|
|
58
|
+
return Object.keys(PROVIDER_PRESETS);
|
|
59
|
+
}
|
|
60
|
+
export function applyProviderPreset(agentId, provider) {
|
|
61
|
+
const preset = PROVIDER_PRESETS[provider];
|
|
62
|
+
if (!preset)
|
|
63
|
+
return undefined;
|
|
64
|
+
const tier = AGENT_TIER_MAP[agentId];
|
|
65
|
+
if (!tier)
|
|
66
|
+
return undefined;
|
|
67
|
+
const config = preset[tier];
|
|
68
|
+
return {
|
|
69
|
+
primary: config.primary,
|
|
70
|
+
fallbacks: config.fallbacks.length > 0 ? config.fallbacks : undefined,
|
|
71
|
+
};
|
|
72
|
+
}
|
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, MODEL_TIERS, applyProviderPreset, getProviderNames, buildCustomPreset, registerCustomPreset, } from './model-presets.js';
|
|
5
7
|
const CONFIG_FILENAMES = [
|
|
6
8
|
'openclaw.json5',
|
|
7
9
|
'openclaw.json',
|
|
@@ -66,8 +68,129 @@ 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
|
+
function printPreview(logger, provider) {
|
|
97
|
+
const preset = PROVIDER_PRESETS[provider];
|
|
98
|
+
for (const [tier, label] of Object.entries(TIER_LABELS)) {
|
|
99
|
+
const config = preset[tier];
|
|
100
|
+
const agents = Object.entries(AGENT_TIER_MAP)
|
|
101
|
+
.filter(([, t]) => t === tier)
|
|
102
|
+
.map(([id]) => id.replace('omoc_', ''))
|
|
103
|
+
.join(', ');
|
|
104
|
+
logger.info(` ${label} (${agents}):`);
|
|
105
|
+
logger.info(` → ${config.primary}`);
|
|
106
|
+
if (config.fallbacks.length > 0) {
|
|
107
|
+
logger.info(` fallback: ${config.fallbacks.join(', ')}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
async function runCustomProviderFlow(rl, logger) {
|
|
112
|
+
logger.info('');
|
|
113
|
+
logger.info(' Enter model IDs for each tier.');
|
|
114
|
+
logger.info(' Format: provider/model (e.g., cliproxy/claude-opus-4-6, z.ai/gpt-5.3-codex)');
|
|
115
|
+
logger.info('');
|
|
116
|
+
const tierModels = {};
|
|
117
|
+
for (const tier of MODEL_TIERS) {
|
|
118
|
+
const label = TIER_LABELS[tier];
|
|
119
|
+
const agents = Object.entries(AGENT_TIER_MAP)
|
|
120
|
+
.filter(([, t]) => t === tier)
|
|
121
|
+
.map(([id]) => id.replace('omoc_', ''))
|
|
122
|
+
.join(', ');
|
|
123
|
+
let model = '';
|
|
124
|
+
while (!model) {
|
|
125
|
+
model = await askQuestion(rl, ` ${label} (${agents}): `);
|
|
126
|
+
if (!model) {
|
|
127
|
+
logger.info(' Model ID required.');
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
tierModels[tier] = model;
|
|
131
|
+
}
|
|
132
|
+
const customPreset = buildCustomPreset(tierModels);
|
|
133
|
+
const customName = '_custom_' + Date.now();
|
|
134
|
+
registerCustomPreset(customName, customPreset);
|
|
135
|
+
return customName;
|
|
136
|
+
}
|
|
137
|
+
export async function runInteractiveSetup(logger) {
|
|
138
|
+
const rl = readline.createInterface({
|
|
139
|
+
input: process.stdin,
|
|
140
|
+
output: process.stdout,
|
|
141
|
+
});
|
|
142
|
+
try {
|
|
143
|
+
logger.info('');
|
|
144
|
+
logger.info('🗺️ Oh-My-OpenClaw Agent Setup');
|
|
145
|
+
logger.info('─'.repeat(40));
|
|
146
|
+
logger.info('');
|
|
147
|
+
const presetProviders = getProviderNames();
|
|
148
|
+
const choices = [...presetProviders, 'custom'];
|
|
149
|
+
const choiceCount = choices.length;
|
|
150
|
+
logger.info('Step 1/2: Select your AI provider');
|
|
151
|
+
logger.info('');
|
|
152
|
+
choices.forEach((p, i) => {
|
|
153
|
+
logger.info(` ${i + 1}. ${PROVIDER_LABELS[p] ?? p}`);
|
|
154
|
+
});
|
|
155
|
+
logger.info('');
|
|
156
|
+
let provider = '';
|
|
157
|
+
while (!provider) {
|
|
158
|
+
const answer = await askQuestion(rl, ` Select (1-${choiceCount}): `);
|
|
159
|
+
const idx = parseInt(answer, 10) - 1;
|
|
160
|
+
if (idx >= 0 && idx < choiceCount) {
|
|
161
|
+
provider = choices[idx];
|
|
162
|
+
}
|
|
163
|
+
else if (choices.includes(answer.toLowerCase())) {
|
|
164
|
+
provider = answer.toLowerCase();
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
logger.info(` Invalid choice. Enter 1-${choiceCount}.`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (provider === 'custom') {
|
|
171
|
+
provider = await runCustomProviderFlow(rl, logger);
|
|
172
|
+
}
|
|
173
|
+
logger.info('');
|
|
174
|
+
logger.info(` ✓ Selected: ${PROVIDER_LABELS[provider] ?? 'Custom'}`);
|
|
175
|
+
logger.info('');
|
|
176
|
+
logger.info('Step 2/2: Model configuration preview');
|
|
177
|
+
logger.info('');
|
|
178
|
+
printPreview(logger, provider);
|
|
179
|
+
logger.info('');
|
|
180
|
+
const confirm = await askQuestion(rl, ' Apply this configuration? (Y/n): ');
|
|
181
|
+
if (confirm.toLowerCase() === 'n' || confirm.toLowerCase() === 'no') {
|
|
182
|
+
logger.info(' Setup cancelled.');
|
|
183
|
+
return { provider: '' };
|
|
184
|
+
}
|
|
185
|
+
logger.info('');
|
|
186
|
+
return { provider };
|
|
187
|
+
}
|
|
188
|
+
finally {
|
|
189
|
+
rl.close();
|
|
190
|
+
}
|
|
191
|
+
}
|
|
69
192
|
export function runSetup(options) {
|
|
70
|
-
const { logger, force = false, dryRun = false } = options;
|
|
193
|
+
const { logger, force = false, dryRun = false, provider } = options;
|
|
71
194
|
const configPath = options.configPath ?? findConfigPath(options.workspaceDir);
|
|
72
195
|
if (!configPath) {
|
|
73
196
|
throw new Error('Could not find OpenClaw config file. Searched for: ' +
|
|
@@ -90,7 +213,13 @@ export function runSetup(options) {
|
|
|
90
213
|
if (!config.agents.list) {
|
|
91
214
|
config.agents.list = [];
|
|
92
215
|
}
|
|
93
|
-
const
|
|
216
|
+
const agentConfigs = provider
|
|
217
|
+
? applyProviderToConfigs(OMOC_AGENT_CONFIGS, provider)
|
|
218
|
+
: OMOC_AGENT_CONFIGS;
|
|
219
|
+
if (provider) {
|
|
220
|
+
logger.info(`Using provider preset: ${PROVIDER_LABELS[provider] ?? provider}`);
|
|
221
|
+
}
|
|
222
|
+
const { merged, result } = mergeAgentConfigs(config.agents.list, agentConfigs, force);
|
|
94
223
|
config.agents.list = merged;
|
|
95
224
|
if (dryRun) {
|
|
96
225
|
logger.info('[dry-run] Would write config to: ' + configPath);
|
|
@@ -123,16 +252,31 @@ export function registerSetupCli(ctx) {
|
|
|
123
252
|
.option('--force', 'Overwrite existing OmOC agent configs', false)
|
|
124
253
|
.option('--dry-run', 'Preview changes without writing', false)
|
|
125
254
|
.option('--config <path>', 'Path to OpenClaw config file')
|
|
126
|
-
.
|
|
255
|
+
.option('--provider <name>', 'AI provider preset: anthropic, openai, google (skips interactive)')
|
|
256
|
+
.action(async (...args) => {
|
|
127
257
|
const opts = (args[0] ?? {});
|
|
128
258
|
try {
|
|
259
|
+
let provider = opts.provider;
|
|
260
|
+
if (provider && !PROVIDER_PRESETS[provider]) {
|
|
261
|
+
const valid = getProviderNames().join(', ');
|
|
262
|
+
throw new Error(`Unknown provider "${provider}". Valid: ${valid}`);
|
|
263
|
+
}
|
|
264
|
+
if (!provider && process.stdin.isTTY) {
|
|
265
|
+
const result = await runInteractiveSetup(ctx.logger);
|
|
266
|
+
if (!result.provider)
|
|
267
|
+
return;
|
|
268
|
+
provider = result.provider;
|
|
269
|
+
}
|
|
129
270
|
runSetup({
|
|
130
271
|
configPath: opts.config,
|
|
131
272
|
workspaceDir: ctx.workspaceDir,
|
|
132
|
-
force: opts.force,
|
|
273
|
+
force: provider ? true : opts.force,
|
|
133
274
|
dryRun: opts.dryRun,
|
|
275
|
+
provider,
|
|
134
276
|
logger: ctx.logger,
|
|
135
277
|
});
|
|
278
|
+
ctx.logger.info('');
|
|
279
|
+
ctx.logger.info('✓ Setup complete! Restart OpenClaw to apply changes.');
|
|
136
280
|
}
|
|
137
281
|
catch (err) {
|
|
138
282
|
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.1",
|
|
6
6
|
"skills": ["skills"],
|
|
7
7
|
"configSchema": {
|
|
8
8
|
"type": "object",
|
package/package.json
CHANGED