@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.
@@ -4,7 +4,7 @@ export const OMOC_AGENT_CONFIGS = [
4
4
  id: 'omoc_prometheus',
5
5
  name: 'Prometheus',
6
6
  model: {
7
- primary: 'openai/o3',
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/o3'],
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/o3'],
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/o3',
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/o3'],
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/o3'],
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
+ }
@@ -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 { merged, result } = mergeAgentConfigs(config.agents.list, OMOC_AGENT_CONFIGS, force);
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
- .action((...args) => {
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,2 @@
1
+ import { OmocPluginApi } from '../types.js';
2
+ export declare function registerPersonaCommands(api: OmocPluginApi): void;
@@ -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,2 @@
1
+ import { OmocPluginApi } from '../types.js';
2
+ export declare function registerPersonaInjector(api: OmocPluginApi): void;
@@ -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({
@@ -0,0 +1,3 @@
1
+ export declare function setActivePersona(id: string | null): void;
2
+ export declare function getActivePersona(): string | null;
3
+ export declare function resetPersonaState(): void;
@@ -0,0 +1,10 @@
1
+ let activePersonaId = null;
2
+ export function setActivePersona(id) {
3
+ activePersonaId = id;
4
+ }
5
+ export function getActivePersona() {
6
+ return activePersonaId;
7
+ }
8
+ export function resetPersonaState() {
9
+ activePersonaId = null;
10
+ }
@@ -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.6.2",
5
+ "version": "0.8.0",
6
6
  "skills": ["skills"],
7
7
  "configSchema": {
8
8
  "type": "object",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happycastle/oh-my-openclaw",
3
- "version": "0.6.2",
3
+ "version": "0.8.0",
4
4
  "description": "Oh-My-OpenClaw plugin — multi-agent orchestration, todo enforcer, ralph loop, and custom tools for OpenClaw",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",