@happycastle/oh-my-openclaw 0.5.0 โ 0.6.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.d.ts +31 -0
- package/dist/agents/agent-configs.js +188 -0
- package/dist/cli/setup.d.ts +60 -0
- package/dist/cli/setup.js +145 -0
- package/dist/commands/ralph-commands.js +7 -6
- package/dist/commands/status-commands.js +2 -2
- package/dist/commands/workflow-commands.js +5 -2
- package/dist/index.js +23 -7
- package/dist/tools/checkpoint.js +7 -5
- package/dist/types.d.ts +10 -0
- package/dist/utils/config.js +1 -1
- package/openclaw.plugin.json +2 -2
- package/package.json +1 -1
- package/skills/workflow-tool-patterns.md +1 -1
- package/workflows/plan.md +2 -2
- package/workflows/start-work.md +2 -2
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Defines the Oh-My-OpenClaw plugin's local agent configuration contracts
|
|
3
|
+
* and the canonical list of built-in OMOC agent definitions.
|
|
4
|
+
*/
|
|
5
|
+
export type OmocAgentConfig = {
|
|
6
|
+
id: string;
|
|
7
|
+
name?: string;
|
|
8
|
+
model?: string | {
|
|
9
|
+
primary: string;
|
|
10
|
+
fallbacks?: string[];
|
|
11
|
+
};
|
|
12
|
+
skills?: string[];
|
|
13
|
+
identity?: {
|
|
14
|
+
name?: string;
|
|
15
|
+
theme?: string;
|
|
16
|
+
emoji?: string;
|
|
17
|
+
};
|
|
18
|
+
subagents?: {
|
|
19
|
+
allowAgents?: string[];
|
|
20
|
+
model?: string | {
|
|
21
|
+
primary: string;
|
|
22
|
+
fallbacks?: string[];
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
tools?: {
|
|
26
|
+
profile?: 'minimal' | 'coding' | 'messaging' | 'full';
|
|
27
|
+
allow?: string[];
|
|
28
|
+
deny?: string[];
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
export declare const OMOC_AGENT_CONFIGS: OmocAgentConfig[];
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
export const OMOC_AGENT_CONFIGS = [
|
|
2
|
+
// Strategic planning agent.
|
|
3
|
+
{
|
|
4
|
+
id: 'omoc_prometheus',
|
|
5
|
+
name: 'Prometheus',
|
|
6
|
+
model: {
|
|
7
|
+
primary: 'openai/o3',
|
|
8
|
+
fallbacks: ['anthropic/claude-opus-4-6'],
|
|
9
|
+
},
|
|
10
|
+
identity: {
|
|
11
|
+
name: 'Prometheus',
|
|
12
|
+
emoji: '๐ฅ',
|
|
13
|
+
theme: 'Strategic Planner',
|
|
14
|
+
},
|
|
15
|
+
tools: { profile: 'full' },
|
|
16
|
+
subagents: { allowAgents: ['*'] },
|
|
17
|
+
},
|
|
18
|
+
// Task orchestration coordinator (cheap/fast tier โ lightweight orchestrator).
|
|
19
|
+
{
|
|
20
|
+
id: 'omoc_atlas',
|
|
21
|
+
name: 'Atlas',
|
|
22
|
+
model: {
|
|
23
|
+
primary: 'anthropic/claude-sonnet-4-6',
|
|
24
|
+
fallbacks: ['openai/gpt-4.1'],
|
|
25
|
+
},
|
|
26
|
+
identity: {
|
|
27
|
+
name: 'Atlas',
|
|
28
|
+
emoji: '๐บ๏ธ',
|
|
29
|
+
theme: 'Task Orchestrator',
|
|
30
|
+
},
|
|
31
|
+
tools: { profile: 'full' },
|
|
32
|
+
subagents: { allowAgents: ['*'] },
|
|
33
|
+
},
|
|
34
|
+
// Primary implementation worker (opus-tier โ high-quality output).
|
|
35
|
+
{
|
|
36
|
+
id: 'omoc_sisyphus',
|
|
37
|
+
name: 'Sisyphus-Junior',
|
|
38
|
+
model: {
|
|
39
|
+
primary: 'anthropic/claude-opus-4-6',
|
|
40
|
+
fallbacks: ['openai/o3'],
|
|
41
|
+
},
|
|
42
|
+
identity: {
|
|
43
|
+
name: 'Sisyphus-Junior',
|
|
44
|
+
emoji: '๐ชจ',
|
|
45
|
+
theme: 'Implementation Worker',
|
|
46
|
+
},
|
|
47
|
+
tools: { profile: 'full' },
|
|
48
|
+
subagents: {
|
|
49
|
+
allowAgents: ['omoc_explore', 'omoc_librarian', 'omoc_oracle'],
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
// Deep implementation specialist.
|
|
53
|
+
{
|
|
54
|
+
id: 'omoc_hephaestus',
|
|
55
|
+
name: 'Hephaestus',
|
|
56
|
+
model: {
|
|
57
|
+
primary: 'anthropic/claude-opus-4-6',
|
|
58
|
+
fallbacks: ['openai/o3'],
|
|
59
|
+
},
|
|
60
|
+
identity: {
|
|
61
|
+
name: 'Hephaestus',
|
|
62
|
+
emoji: '๐จ',
|
|
63
|
+
theme: 'Deep Implementation',
|
|
64
|
+
},
|
|
65
|
+
tools: { profile: 'full' },
|
|
66
|
+
subagents: {
|
|
67
|
+
allowAgents: ['omoc_explore', 'omoc_librarian', 'omoc_oracle'],
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
// Read-only architecture consultant.
|
|
71
|
+
{
|
|
72
|
+
id: 'omoc_oracle',
|
|
73
|
+
name: 'Oracle',
|
|
74
|
+
model: {
|
|
75
|
+
primary: 'openai/o3',
|
|
76
|
+
fallbacks: ['anthropic/claude-opus-4-6'],
|
|
77
|
+
},
|
|
78
|
+
identity: {
|
|
79
|
+
name: 'Oracle',
|
|
80
|
+
emoji: '๐๏ธ',
|
|
81
|
+
theme: 'Architecture Consultant',
|
|
82
|
+
},
|
|
83
|
+
tools: {
|
|
84
|
+
profile: 'coding',
|
|
85
|
+
deny: ['write', 'edit', 'apply_patch', 'sessions_spawn'],
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
// Read-only codebase search specialist.
|
|
89
|
+
{
|
|
90
|
+
id: 'omoc_explore',
|
|
91
|
+
name: 'Explore',
|
|
92
|
+
model: 'anthropic/claude-sonnet-4-6',
|
|
93
|
+
identity: {
|
|
94
|
+
name: 'Explore',
|
|
95
|
+
emoji: '๐',
|
|
96
|
+
theme: 'Codebase Search',
|
|
97
|
+
},
|
|
98
|
+
tools: {
|
|
99
|
+
profile: 'coding',
|
|
100
|
+
deny: ['write', 'edit', 'apply_patch', 'sessions_spawn'],
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
// Read-only documentation research specialist.
|
|
104
|
+
{
|
|
105
|
+
id: 'omoc_librarian',
|
|
106
|
+
name: 'Librarian',
|
|
107
|
+
model: 'anthropic/claude-sonnet-4-6',
|
|
108
|
+
identity: {
|
|
109
|
+
name: 'Librarian',
|
|
110
|
+
emoji: '๐',
|
|
111
|
+
theme: 'Documentation Research',
|
|
112
|
+
},
|
|
113
|
+
tools: {
|
|
114
|
+
profile: 'coding',
|
|
115
|
+
deny: ['write', 'edit', 'apply_patch', 'sessions_spawn'],
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
// Read-only pre-planning analyst.
|
|
119
|
+
{
|
|
120
|
+
id: 'omoc_metis',
|
|
121
|
+
name: 'Metis',
|
|
122
|
+
model: {
|
|
123
|
+
primary: 'anthropic/claude-opus-4-6',
|
|
124
|
+
fallbacks: ['openai/o3'],
|
|
125
|
+
},
|
|
126
|
+
identity: {
|
|
127
|
+
name: 'Metis',
|
|
128
|
+
emoji: '๐ง ',
|
|
129
|
+
theme: 'Pre-Planning Analyst',
|
|
130
|
+
},
|
|
131
|
+
tools: {
|
|
132
|
+
profile: 'coding',
|
|
133
|
+
deny: ['write', 'edit', 'apply_patch', 'sessions_spawn'],
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
// Read-only plan review specialist.
|
|
137
|
+
{
|
|
138
|
+
id: 'omoc_momus',
|
|
139
|
+
name: 'Momus',
|
|
140
|
+
model: {
|
|
141
|
+
primary: 'anthropic/claude-opus-4-6',
|
|
142
|
+
fallbacks: ['openai/o3'],
|
|
143
|
+
},
|
|
144
|
+
identity: {
|
|
145
|
+
name: 'Momus',
|
|
146
|
+
emoji: '๐ญ',
|
|
147
|
+
theme: 'Plan Reviewer',
|
|
148
|
+
},
|
|
149
|
+
tools: {
|
|
150
|
+
profile: 'coding',
|
|
151
|
+
deny: ['write', 'edit', 'apply_patch', 'sessions_spawn'],
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
// Multimodal visual analysis specialist (read-only โ allowlist approach).
|
|
155
|
+
{
|
|
156
|
+
id: 'omoc_looker',
|
|
157
|
+
name: 'Multimodal Looker',
|
|
158
|
+
model: {
|
|
159
|
+
primary: 'google/gemini-2.5-flash',
|
|
160
|
+
fallbacks: ['anthropic/claude-sonnet-4-6'],
|
|
161
|
+
},
|
|
162
|
+
identity: {
|
|
163
|
+
name: 'Multimodal Looker',
|
|
164
|
+
emoji: '๐๏ธ',
|
|
165
|
+
theme: 'Visual Analysis',
|
|
166
|
+
},
|
|
167
|
+
tools: {
|
|
168
|
+
allow: ['read'],
|
|
169
|
+
deny: ['write', 'edit', 'apply_patch', 'sessions_spawn'],
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
// Frontend-focused visual engineering specialist.
|
|
173
|
+
{
|
|
174
|
+
id: 'omoc_frontend',
|
|
175
|
+
name: 'Frontend',
|
|
176
|
+
model: {
|
|
177
|
+
primary: 'google/gemini-2.5-pro',
|
|
178
|
+
fallbacks: ['anthropic/claude-sonnet-4-6'],
|
|
179
|
+
},
|
|
180
|
+
identity: {
|
|
181
|
+
name: 'Frontend',
|
|
182
|
+
emoji: '๐จ',
|
|
183
|
+
theme: 'Visual Engineering',
|
|
184
|
+
},
|
|
185
|
+
tools: { profile: 'coding' },
|
|
186
|
+
subagents: { allowAgents: ['omoc_explore', 'omoc_librarian'] },
|
|
187
|
+
},
|
|
188
|
+
];
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { type OmocAgentConfig } from '../agents/agent-configs.js';
|
|
2
|
+
type AgentsSection = {
|
|
3
|
+
defaults?: Record<string, unknown>;
|
|
4
|
+
list?: Array<{
|
|
5
|
+
id: string;
|
|
6
|
+
[key: string]: unknown;
|
|
7
|
+
}>;
|
|
8
|
+
};
|
|
9
|
+
type ConfigShape = {
|
|
10
|
+
agents?: AgentsSection;
|
|
11
|
+
[key: string]: unknown;
|
|
12
|
+
};
|
|
13
|
+
type Logger = {
|
|
14
|
+
info: (msg: string) => void;
|
|
15
|
+
warn: (msg: string) => void;
|
|
16
|
+
error: (msg: string) => void;
|
|
17
|
+
};
|
|
18
|
+
export declare function findConfigPath(workspaceDir?: string): string | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Lenient JSON5 parser: strips // and /* comments, removes trailing commas,
|
|
21
|
+
* then delegates to JSON.parse.
|
|
22
|
+
*/
|
|
23
|
+
export declare function parseConfig(raw: string): ConfigShape;
|
|
24
|
+
export declare function serializeConfig(config: ConfigShape): string;
|
|
25
|
+
export interface MergeResult {
|
|
26
|
+
added: string[];
|
|
27
|
+
skipped: string[];
|
|
28
|
+
updated: string[];
|
|
29
|
+
}
|
|
30
|
+
export declare function mergeAgentConfigs(existing: Array<{
|
|
31
|
+
id: string;
|
|
32
|
+
[key: string]: unknown;
|
|
33
|
+
}>, incoming: OmocAgentConfig[], force: boolean): {
|
|
34
|
+
merged: Array<{
|
|
35
|
+
id: string;
|
|
36
|
+
[key: string]: unknown;
|
|
37
|
+
}>;
|
|
38
|
+
result: MergeResult;
|
|
39
|
+
};
|
|
40
|
+
export interface SetupOptions {
|
|
41
|
+
configPath?: string;
|
|
42
|
+
workspaceDir?: string;
|
|
43
|
+
force?: boolean;
|
|
44
|
+
dryRun?: boolean;
|
|
45
|
+
logger: Logger;
|
|
46
|
+
}
|
|
47
|
+
export declare function runSetup(options: SetupOptions): MergeResult;
|
|
48
|
+
export declare function registerSetupCli(ctx: {
|
|
49
|
+
program: {
|
|
50
|
+
command: (name: string) => CommandBuilder;
|
|
51
|
+
};
|
|
52
|
+
workspaceDir?: string;
|
|
53
|
+
logger: Logger;
|
|
54
|
+
}): void;
|
|
55
|
+
type CommandBuilder = {
|
|
56
|
+
description: (desc: string) => CommandBuilder;
|
|
57
|
+
option: (flags: string, desc: string, defaultValue?: unknown) => CommandBuilder;
|
|
58
|
+
action: (fn: (...args: unknown[]) => void) => CommandBuilder;
|
|
59
|
+
};
|
|
60
|
+
export {};
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { OMOC_AGENT_CONFIGS } from '../agents/agent-configs.js';
|
|
4
|
+
const CONFIG_FILENAMES = [
|
|
5
|
+
'openclaw.json5',
|
|
6
|
+
'openclaw.json',
|
|
7
|
+
'openclaw.yaml',
|
|
8
|
+
'openclaw.yml',
|
|
9
|
+
];
|
|
10
|
+
export function findConfigPath(workspaceDir) {
|
|
11
|
+
const searchDirs = [];
|
|
12
|
+
if (workspaceDir) {
|
|
13
|
+
searchDirs.push(workspaceDir);
|
|
14
|
+
const parent = path.dirname(workspaceDir);
|
|
15
|
+
if (parent !== workspaceDir) {
|
|
16
|
+
searchDirs.push(parent);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
searchDirs.push(process.cwd());
|
|
20
|
+
const homeDir = process.env['HOME'] ?? process.env['USERPROFILE'];
|
|
21
|
+
if (homeDir) {
|
|
22
|
+
searchDirs.push(path.join(homeDir, '.openclaw'));
|
|
23
|
+
}
|
|
24
|
+
for (const dir of searchDirs) {
|
|
25
|
+
for (const filename of CONFIG_FILENAMES) {
|
|
26
|
+
const candidate = path.join(dir, filename);
|
|
27
|
+
if (fs.existsSync(candidate)) {
|
|
28
|
+
return candidate;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Lenient JSON5 parser: strips // and /* comments, removes trailing commas,
|
|
36
|
+
* then delegates to JSON.parse.
|
|
37
|
+
*/
|
|
38
|
+
export function parseConfig(raw) {
|
|
39
|
+
const stripped = raw
|
|
40
|
+
.replace(/\/\/.*$/gm, '')
|
|
41
|
+
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
42
|
+
.replace(/,\s*([\]}])/g, '$1');
|
|
43
|
+
return JSON.parse(stripped);
|
|
44
|
+
}
|
|
45
|
+
export function serializeConfig(config) {
|
|
46
|
+
return JSON.stringify(config, null, 2) + '\n';
|
|
47
|
+
}
|
|
48
|
+
export function mergeAgentConfigs(existing, incoming, force) {
|
|
49
|
+
const result = { added: [], skipped: [], updated: [] };
|
|
50
|
+
const merged = [...existing];
|
|
51
|
+
const existingIds = new Set(existing.map((a) => a.id));
|
|
52
|
+
for (const agent of incoming) {
|
|
53
|
+
if (existingIds.has(agent.id)) {
|
|
54
|
+
if (force) {
|
|
55
|
+
const idx = merged.findIndex((a) => a.id === agent.id);
|
|
56
|
+
if (idx !== -1) {
|
|
57
|
+
merged[idx] = agent;
|
|
58
|
+
result.updated.push(agent.id);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
result.skipped.push(agent.id);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
merged.push(agent);
|
|
67
|
+
result.added.push(agent.id);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return { merged, result };
|
|
71
|
+
}
|
|
72
|
+
export function runSetup(options) {
|
|
73
|
+
const { logger, force = false, dryRun = false } = options;
|
|
74
|
+
const configPath = options.configPath ?? findConfigPath(options.workspaceDir);
|
|
75
|
+
if (!configPath) {
|
|
76
|
+
throw new Error('Could not find OpenClaw config file. Searched for: ' +
|
|
77
|
+
CONFIG_FILENAMES.join(', ') +
|
|
78
|
+
'\nSpecify the path with --config <path>');
|
|
79
|
+
}
|
|
80
|
+
if (!fs.existsSync(configPath)) {
|
|
81
|
+
throw new Error(`Config file not found: ${configPath}`);
|
|
82
|
+
}
|
|
83
|
+
logger.info(`Found config: ${configPath}`);
|
|
84
|
+
const raw = fs.readFileSync(configPath, 'utf-8');
|
|
85
|
+
if (configPath.endsWith('.yaml') || configPath.endsWith('.yml')) {
|
|
86
|
+
throw new Error('YAML config files are not supported by omoc-setup. ' +
|
|
87
|
+
'Please convert to JSON or JSON5, or manually add agent configs.');
|
|
88
|
+
}
|
|
89
|
+
const config = parseConfig(raw);
|
|
90
|
+
if (!config.agents) {
|
|
91
|
+
config.agents = {};
|
|
92
|
+
}
|
|
93
|
+
if (!config.agents.list) {
|
|
94
|
+
config.agents.list = [];
|
|
95
|
+
}
|
|
96
|
+
const { merged, result } = mergeAgentConfigs(config.agents.list, OMOC_AGENT_CONFIGS, force);
|
|
97
|
+
config.agents.list = merged;
|
|
98
|
+
if (dryRun) {
|
|
99
|
+
logger.info('[dry-run] Would write config to: ' + configPath);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
const backupPath = configPath + '.bak';
|
|
103
|
+
fs.copyFileSync(configPath, backupPath);
|
|
104
|
+
logger.info(`Backup created: ${backupPath}`);
|
|
105
|
+
fs.writeFileSync(configPath, serializeConfig(config), 'utf-8');
|
|
106
|
+
logger.info(`Config updated: ${configPath}`);
|
|
107
|
+
}
|
|
108
|
+
if (result.added.length > 0) {
|
|
109
|
+
logger.info(`Added ${result.added.length} agent(s): ${result.added.join(', ')}`);
|
|
110
|
+
}
|
|
111
|
+
if (result.updated.length > 0) {
|
|
112
|
+
logger.info(`Updated ${result.updated.length} agent(s): ${result.updated.join(', ')}`);
|
|
113
|
+
}
|
|
114
|
+
if (result.skipped.length > 0) {
|
|
115
|
+
logger.info(`Skipped ${result.skipped.length} existing agent(s): ${result.skipped.join(', ')}`);
|
|
116
|
+
}
|
|
117
|
+
if (result.added.length === 0 && result.updated.length === 0) {
|
|
118
|
+
logger.info('No changes needed โ all OmOC agents already present.');
|
|
119
|
+
}
|
|
120
|
+
return result;
|
|
121
|
+
}
|
|
122
|
+
export function registerSetupCli(ctx) {
|
|
123
|
+
ctx.program
|
|
124
|
+
.command('setup')
|
|
125
|
+
.description('Inject OmOC agent definitions into your OpenClaw config')
|
|
126
|
+
.option('--force', 'Overwrite existing OmOC agent configs', false)
|
|
127
|
+
.option('--dry-run', 'Preview changes without writing', false)
|
|
128
|
+
.option('--config <path>', 'Path to OpenClaw config file')
|
|
129
|
+
.action((...args) => {
|
|
130
|
+
const opts = (args[0] ?? {});
|
|
131
|
+
try {
|
|
132
|
+
runSetup({
|
|
133
|
+
configPath: opts.config,
|
|
134
|
+
workspaceDir: ctx.workspaceDir,
|
|
135
|
+
force: opts.force,
|
|
136
|
+
dryRun: opts.dryRun,
|
|
137
|
+
logger: ctx.logger,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
ctx.logger.error(`Setup failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
142
|
+
process.exitCode = 1;
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
}
|
|
@@ -2,10 +2,11 @@ import { startLoop, stopLoop, getStatus } from '../services/ralph-loop.js';
|
|
|
2
2
|
import { getMessageCount } from '../hooks/message-monitor.js';
|
|
3
3
|
import { getConfig } from '../utils/config.js';
|
|
4
4
|
export function registerRalphCommands(api) {
|
|
5
|
-
// /
|
|
5
|
+
// /ralph_loop command
|
|
6
6
|
api.registerCommand({
|
|
7
|
-
name: '
|
|
7
|
+
name: 'ralph_loop',
|
|
8
8
|
description: 'Start the Ralph Loop self-completion mechanism',
|
|
9
|
+
acceptsArgs: true,
|
|
9
10
|
handler: async (ctx) => {
|
|
10
11
|
const args = (ctx.args || '').trim().split(/\s+/).filter(Boolean);
|
|
11
12
|
const config = getConfig(api);
|
|
@@ -22,9 +23,9 @@ export function registerRalphCommands(api) {
|
|
|
22
23
|
};
|
|
23
24
|
},
|
|
24
25
|
});
|
|
25
|
-
// /
|
|
26
|
+
// /ralph_stop command
|
|
26
27
|
api.registerCommand({
|
|
27
|
-
name: '
|
|
28
|
+
name: 'ralph_stop',
|
|
28
29
|
description: 'Stop the active Ralph Loop',
|
|
29
30
|
handler: async () => {
|
|
30
31
|
const result = await stopLoop();
|
|
@@ -33,9 +34,9 @@ export function registerRalphCommands(api) {
|
|
|
33
34
|
};
|
|
34
35
|
},
|
|
35
36
|
});
|
|
36
|
-
// /
|
|
37
|
+
// /omoc_status command
|
|
37
38
|
api.registerCommand({
|
|
38
|
-
name: '
|
|
39
|
+
name: 'omoc_status',
|
|
39
40
|
description: 'Show Oh-My-OpenClaw plugin status',
|
|
40
41
|
handler: async () => {
|
|
41
42
|
const config = getConfig(api);
|
|
@@ -5,7 +5,7 @@ import { getStatus as getRalphStatus } from '../services/ralph-loop.js';
|
|
|
5
5
|
import { getMessageCount } from '../hooks/message-monitor.js';
|
|
6
6
|
export function registerStatusCommands(api) {
|
|
7
7
|
api.registerCommand({
|
|
8
|
-
name: '
|
|
8
|
+
name: 'omoc_health',
|
|
9
9
|
description: 'Plugin health check (auto-reply, no AI invocation)',
|
|
10
10
|
handler: async () => {
|
|
11
11
|
const config = getConfig(api);
|
|
@@ -24,7 +24,7 @@ export function registerStatusCommands(api) {
|
|
|
24
24
|
},
|
|
25
25
|
});
|
|
26
26
|
api.registerCommand({
|
|
27
|
-
name: '
|
|
27
|
+
name: 'omoc_config',
|
|
28
28
|
description: 'Show current plugin configuration (auto-reply)',
|
|
29
29
|
handler: () => {
|
|
30
30
|
const config = getConfig(api);
|
|
@@ -17,7 +17,8 @@ async function readWorkflow(workflowName) {
|
|
|
17
17
|
export function registerWorkflowCommands(api) {
|
|
18
18
|
api.registerCommand({
|
|
19
19
|
name: 'ultrawork',
|
|
20
|
-
description: 'Full planning
|
|
20
|
+
description: 'Full planning โ execution โ verification workflow',
|
|
21
|
+
acceptsArgs: true,
|
|
21
22
|
handler: async (ctx) => {
|
|
22
23
|
const taskDescription = ctx.args || 'No task specified';
|
|
23
24
|
const workflow = await readWorkflow('ultrawork');
|
|
@@ -29,6 +30,7 @@ export function registerWorkflowCommands(api) {
|
|
|
29
30
|
api.registerCommand({
|
|
30
31
|
name: 'plan',
|
|
31
32
|
description: 'Create a structured execution plan',
|
|
33
|
+
acceptsArgs: true,
|
|
32
34
|
handler: async (ctx) => {
|
|
33
35
|
const topic = ctx.args || 'No topic specified';
|
|
34
36
|
const workflow = await readWorkflow('plan');
|
|
@@ -38,8 +40,9 @@ export function registerWorkflowCommands(api) {
|
|
|
38
40
|
},
|
|
39
41
|
});
|
|
40
42
|
api.registerCommand({
|
|
41
|
-
name: '
|
|
43
|
+
name: 'start_work',
|
|
42
44
|
description: 'Execute an approved plan',
|
|
45
|
+
acceptsArgs: true,
|
|
43
46
|
handler: async (ctx) => {
|
|
44
47
|
const planPath = ctx.args || 'most recent plan';
|
|
45
48
|
const workflow = await readWorkflow('start-work');
|
package/dist/index.js
CHANGED
|
@@ -12,12 +12,13 @@ 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
|
-
|
|
15
|
+
import { registerSetupCli } from './cli/setup.js';
|
|
16
16
|
const registry = {
|
|
17
17
|
hooks: [],
|
|
18
18
|
services: [],
|
|
19
19
|
tools: [],
|
|
20
20
|
commands: [],
|
|
21
|
+
cli: [],
|
|
21
22
|
};
|
|
22
23
|
export default function register(api) {
|
|
23
24
|
const config = getConfig(api);
|
|
@@ -88,28 +89,42 @@ export default function register(api) {
|
|
|
88
89
|
}
|
|
89
90
|
try {
|
|
90
91
|
registerWorkflowCommands(api);
|
|
91
|
-
registry.commands.push('ultrawork', 'plan', '
|
|
92
|
-
api.logger.info(`[${PLUGIN_ID}] Workflow commands registered (ultrawork, plan,
|
|
92
|
+
registry.commands.push('ultrawork', 'plan', 'start_work');
|
|
93
|
+
api.logger.info(`[${PLUGIN_ID}] Workflow commands registered (ultrawork, plan, start_work)`);
|
|
93
94
|
}
|
|
94
95
|
catch (err) {
|
|
95
96
|
api.logger.error(`[${PLUGIN_ID}] Failed to register Workflow commands:`, err);
|
|
96
97
|
}
|
|
97
98
|
try {
|
|
98
99
|
registerRalphCommands(api);
|
|
99
|
-
registry.commands.push('
|
|
100
|
-
api.logger.info(`[${PLUGIN_ID}] Ralph commands registered (
|
|
100
|
+
registry.commands.push('ralph_loop', 'ralph_stop', 'omoc_status');
|
|
101
|
+
api.logger.info(`[${PLUGIN_ID}] Ralph commands registered (ralph_loop, ralph_stop, omoc_status)`);
|
|
101
102
|
}
|
|
102
103
|
catch (err) {
|
|
103
104
|
api.logger.error(`[${PLUGIN_ID}] Failed to register Ralph commands:`, err);
|
|
104
105
|
}
|
|
105
106
|
try {
|
|
106
107
|
registerStatusCommands(api);
|
|
107
|
-
registry.commands.push('
|
|
108
|
-
api.logger.info(`[${PLUGIN_ID}] Status commands registered (
|
|
108
|
+
registry.commands.push('omoc_health', 'omoc_config');
|
|
109
|
+
api.logger.info(`[${PLUGIN_ID}] Status commands registered (omoc_health, omoc_config)`);
|
|
109
110
|
}
|
|
110
111
|
catch (err) {
|
|
111
112
|
api.logger.error(`[${PLUGIN_ID}] Failed to register Status commands:`, err);
|
|
112
113
|
}
|
|
114
|
+
try {
|
|
115
|
+
api.registerCli((ctx) => {
|
|
116
|
+
registerSetupCli({
|
|
117
|
+
program: ctx.program,
|
|
118
|
+
workspaceDir: ctx.workspaceDir,
|
|
119
|
+
logger: ctx.logger,
|
|
120
|
+
});
|
|
121
|
+
}, { commands: ['setup'] });
|
|
122
|
+
registry.cli.push('setup');
|
|
123
|
+
api.logger.info(`[${PLUGIN_ID}] CLI command registered (setup)`);
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
api.logger.error(`[${PLUGIN_ID}] Failed to register CLI:`, err);
|
|
127
|
+
}
|
|
113
128
|
api.registerGatewayMethod('oh-my-openclaw.status', () => {
|
|
114
129
|
return {
|
|
115
130
|
ok: true,
|
|
@@ -119,6 +134,7 @@ export default function register(api) {
|
|
|
119
134
|
services: [...registry.services],
|
|
120
135
|
tools: [...registry.tools],
|
|
121
136
|
commands: [...registry.commands],
|
|
137
|
+
cli: [...registry.cli],
|
|
122
138
|
config: {
|
|
123
139
|
todo_enforcer_enabled: config.todo_enforcer_enabled,
|
|
124
140
|
comment_checker_enabled: config.comment_checker_enabled,
|
package/dist/tools/checkpoint.js
CHANGED
|
@@ -3,12 +3,14 @@ import { promises as fs } from 'fs';
|
|
|
3
3
|
import { join } from 'path';
|
|
4
4
|
import { readState, writeState, ensureDir } from '../utils/state.js';
|
|
5
5
|
import { getConfig } from '../utils/config.js';
|
|
6
|
+
// Use Type.Unsafe with enum instead of Type.Union([Type.Literal(...)]) to avoid
|
|
7
|
+
// generating JSON Schema "const" keyword, which Gemini API does not support.
|
|
6
8
|
const CheckpointParamsSchema = Type.Object({
|
|
7
|
-
action: Type.
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
action: Type.Unsafe({
|
|
10
|
+
type: 'string',
|
|
11
|
+
enum: ['save', 'load', 'list'],
|
|
12
|
+
description: 'Checkpoint operation',
|
|
13
|
+
}),
|
|
12
14
|
task: Type.Optional(Type.String({ description: 'Current task name (for save)' })),
|
|
13
15
|
step: Type.Optional(Type.String({ description: 'Current step name (for save)' })),
|
|
14
16
|
changed_files: Type.Optional(Type.Array(Type.String(), { description: 'Files modified since last checkpoint' })),
|
package/dist/types.d.ts
CHANGED
|
@@ -85,6 +85,7 @@ export interface CommandRegistration<TCtx = {
|
|
|
85
85
|
}> {
|
|
86
86
|
name: string;
|
|
87
87
|
description: string;
|
|
88
|
+
acceptsArgs?: boolean;
|
|
88
89
|
handler: (ctx: TCtx) => {
|
|
89
90
|
text: string;
|
|
90
91
|
} | Promise<{
|
|
@@ -99,6 +100,7 @@ export interface ServiceRegistration {
|
|
|
99
100
|
stop?: () => Promise<void>;
|
|
100
101
|
}
|
|
101
102
|
export interface OmocPluginApi {
|
|
103
|
+
pluginConfig?: PluginConfig;
|
|
102
104
|
config: PluginConfig;
|
|
103
105
|
logger: {
|
|
104
106
|
info: (...args: unknown[]) => void;
|
|
@@ -112,4 +114,12 @@ export interface OmocPluginApi {
|
|
|
112
114
|
}>(config: CommandRegistration<TCtx>) => void;
|
|
113
115
|
registerService: (config: ServiceRegistration) => void;
|
|
114
116
|
registerGatewayMethod: (name: string, handler: () => unknown) => void;
|
|
117
|
+
registerCli: (registrar: (ctx: {
|
|
118
|
+
program: unknown;
|
|
119
|
+
config: unknown;
|
|
120
|
+
workspaceDir?: string;
|
|
121
|
+
logger: OmocPluginApi['logger'];
|
|
122
|
+
}) => void | Promise<void>, opts?: {
|
|
123
|
+
commands?: string[];
|
|
124
|
+
}) => void;
|
|
115
125
|
}
|
package/dist/utils/config.js
CHANGED
|
@@ -12,7 +12,7 @@ export function getConfig(api) {
|
|
|
12
12
|
tmux_socket: '/tmp/openclaw-tmux-sockets/openclaw.sock',
|
|
13
13
|
model_routing: undefined,
|
|
14
14
|
};
|
|
15
|
-
const config = { ...defaults, ...api.config };
|
|
15
|
+
const config = { ...defaults, ...(api.pluginConfig ?? api.config) };
|
|
16
16
|
const validation = validateConfig(config);
|
|
17
17
|
if (!validation.valid) {
|
|
18
18
|
api.logger.warn(`Config validation failed: ${validation.errors.join(', ')}`);
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "oh-my-openclaw",
|
|
3
3
|
"name": "Oh-My-OpenClaw",
|
|
4
|
-
"description": "Multi-agent orchestration plugin \u2014
|
|
5
|
-
"version": "0.
|
|
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.0",
|
|
6
6
|
"skills": ["skills"],
|
|
7
7
|
"configSchema": {
|
|
8
8
|
"type": "object",
|
package/package.json
CHANGED
|
@@ -23,7 +23,7 @@ OmO์ `src/tools/` ๊ตฌ์กฐ๋ฅผ ๊ธฐ์ค์ผ๋ก, OpenClaw์์ ์ค์ฌ์ฉ ๊ฐ๋ฅํ
|
|
|
23
23
|
| `lsp/*` (goto/references) | ์ฝ๋ ํ์/๊ฒ์ฆ | `exec` ๋๊ตฌ๋ก ๋ฆฐํฐ/ํ์
์ฒด์ปค ์คํ โ ๊ฒฐ๊ณผ๋ก ๊ฒ์ฆ |
|
|
24
24
|
| `interactive-bash` | ์ฅ์๊ฐ/์ํธ์์ฉ ์
ธ | `exec`(`pty: true`) ๋๋ tmux ์ฐ๋ |
|
|
25
25
|
| `bash` | ์์ท ๋ช
๋ น | `exec`(๋๊ธฐ), `exec`(`background: true`) โ `process`(`poll`) |
|
|
26
|
-
| `slashcommand` | ๋ช
๋ น ์ํฌํ๋ก์ฐ ๊ตฌ๋ | OpenClaw ์คํฌ `/ultrawork`, `/plan`, `/
|
|
26
|
+
| `slashcommand` | ๋ช
๋ น ์ํฌํ๋ก์ฐ ๊ตฌ๋ | OpenClaw ์คํฌ `/ultrawork`, `/plan`, `/start_work` (์ฌ๋์ ์ปค๋งจ๋) |
|
|
27
27
|
| `session-manager` | ์ธ์
ํ์/์ฌ๊ฐ | `sessions_list`, `sessions_history`, `session_status` |
|
|
28
28
|
| `skill-mcp` | ์คํฌ ๊ธฐ๋ฐ ๋๊ตฌ ํธ์ถ | OpenClaw ์คํฌ ์์คํ
(`read` โ SKILL.md ์ฐธ์กฐ) |
|
|
29
29
|
| `look-at` | ๋ฉํฐ๋ชจ๋ฌ ๋ถ์ | `image` ๋๊ตฌ + Gemini CLI tmux ์ฐ๋ |
|
package/workflows/plan.md
CHANGED
|
@@ -11,7 +11,7 @@ Strategic planning workflow that analyzes requirements and creates a structured
|
|
|
11
11
|
- Starting a new feature or project
|
|
12
12
|
- Complex multi-step tasks
|
|
13
13
|
- When requirements are ambiguous and need clarification
|
|
14
|
-
- Before any `/ultrawork` or `/
|
|
14
|
+
- Before any `/ultrawork` or `/start_work` invocation
|
|
15
15
|
|
|
16
16
|
## Workflow Steps
|
|
17
17
|
|
|
@@ -103,7 +103,7 @@ Strategic planning workflow that analyzes requirements and creates a structured
|
|
|
103
103
|
|
|
104
104
|
## Integration with Other Workflows
|
|
105
105
|
|
|
106
|
-
- After plan approval, use `/
|
|
106
|
+
- After plan approval, use `/start_work` to begin execution
|
|
107
107
|
- Or use `/ultrawork` for fully automated execution without stops
|
|
108
108
|
- Plan files persist in `workspace/plans/` for future reference
|
|
109
109
|
|
package/workflows/start-work.md
CHANGED
|
@@ -17,7 +17,7 @@ Execute an approved plan by delegating tasks to appropriate worker agents, track
|
|
|
17
17
|
|
|
18
18
|
1. **Load the plan**
|
|
19
19
|
- Read the most recent plan from `workspace/plans/`
|
|
20
|
-
- Or specify a plan: `/
|
|
20
|
+
- Or specify a plan: `/start_work <plan-file>`
|
|
21
21
|
- Verify plan status is "approved"
|
|
22
22
|
- Update plan status to "in-progress"
|
|
23
23
|
|
|
@@ -106,5 +106,5 @@ After each task completion, output:
|
|
|
106
106
|
## Integration
|
|
107
107
|
|
|
108
108
|
- Plans come from `/plan` workflow
|
|
109
|
-
- `/ultrawork` combines `/plan` + `/
|
|
109
|
+
- `/ultrawork` combines `/plan` + `/start_work` automatically
|
|
110
110
|
- Wisdom notepads persist across sessions for future reference
|