@lgcyaxi/oh-my-claude 1.0.0 → 1.0.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/CHANGELOG.md +31 -0
- package/README.md +28 -11
- package/README.zh-CN.md +28 -11
- package/bin/oh-my-claude.js +4 -2
- package/changelog/v1.0.0.md +28 -0
- package/changelog/v1.0.1.md +28 -0
- package/dist/cli.js +94 -2
- package/dist/index-5ars1tn4.js +7348 -0
- package/dist/index.js +11 -1
- package/dist/mcp/server.js +138 -17
- package/package.json +3 -3
- package/src/agents/document-writer.ts +5 -0
- package/src/agents/explore.ts +5 -0
- package/src/agents/frontend-ui-ux.ts +5 -0
- package/src/agents/librarian.ts +5 -0
- package/src/agents/oracle.ts +5 -0
- package/src/agents/types.ts +11 -0
- package/src/cli.ts +126 -1
- package/src/config/loader.ts +73 -0
- package/src/config/schema.ts +25 -2
- package/src/generators/agent-generator.ts +17 -1
- package/src/mcp/background-agent-server/server.ts +6 -2
- package/src/mcp/background-agent-server/task-manager.ts +30 -4
- package/src/providers/router.ts +28 -0
package/dist/index.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AgentConfigSchema,
|
|
3
|
+
AgentFallbackConfigSchema,
|
|
3
4
|
AnthropicCompatibleClient,
|
|
4
5
|
CategoryConfigSchema,
|
|
5
6
|
ConcurrencyConfigSchema,
|
|
6
7
|
DEFAULT_CONFIG,
|
|
8
|
+
FallbackRequiredError,
|
|
7
9
|
OhMyClaudeConfigSchema,
|
|
8
10
|
OpenAICompatibleClient,
|
|
9
11
|
OpenRouterClient,
|
|
@@ -25,6 +27,7 @@ import {
|
|
|
25
27
|
generateAgentMarkdown,
|
|
26
28
|
generateAllAgentFiles,
|
|
27
29
|
getAgent,
|
|
30
|
+
getAgentFallback,
|
|
28
31
|
getAgentsDirectory,
|
|
29
32
|
getCommandsDir,
|
|
30
33
|
getConfigPath,
|
|
@@ -38,6 +41,7 @@ import {
|
|
|
38
41
|
getProvidersStatus,
|
|
39
42
|
install,
|
|
40
43
|
isMcpAgent,
|
|
44
|
+
isProviderConfigured,
|
|
41
45
|
isTaskAgent,
|
|
42
46
|
librarianAgent,
|
|
43
47
|
loadConfig,
|
|
@@ -50,10 +54,11 @@ import {
|
|
|
50
54
|
resolveProviderForCategory,
|
|
51
55
|
routeByAgent,
|
|
52
56
|
routeByCategory,
|
|
57
|
+
shouldUseFallback,
|
|
53
58
|
sisyphusAgent,
|
|
54
59
|
taskAgents,
|
|
55
60
|
uninstall
|
|
56
|
-
} from "./index-
|
|
61
|
+
} from "./index-5ars1tn4.js";
|
|
57
62
|
// src/providers/deepseek.ts
|
|
58
63
|
var DEEPSEEK_BASE_URL = "https://api.deepseek.com/anthropic";
|
|
59
64
|
var DEEPSEEK_API_KEY_ENV = "DEEPSEEK_API_KEY";
|
|
@@ -80,6 +85,7 @@ export {
|
|
|
80
85
|
uninstall,
|
|
81
86
|
taskAgents,
|
|
82
87
|
sisyphusAgent,
|
|
88
|
+
shouldUseFallback,
|
|
83
89
|
routeByCategory,
|
|
84
90
|
routeByAgent,
|
|
85
91
|
resolveProviderForCategory,
|
|
@@ -94,6 +100,7 @@ export {
|
|
|
94
100
|
loadConfig,
|
|
95
101
|
librarianAgent,
|
|
96
102
|
isTaskAgent,
|
|
103
|
+
isProviderConfigured,
|
|
97
104
|
isMcpAgent,
|
|
98
105
|
install,
|
|
99
106
|
getProvidersStatus,
|
|
@@ -107,6 +114,7 @@ export {
|
|
|
107
114
|
getConfigPath,
|
|
108
115
|
getCommandsDir,
|
|
109
116
|
getAgentsDirectory,
|
|
117
|
+
getAgentFallback,
|
|
110
118
|
getAgent,
|
|
111
119
|
generateAllAgentFiles,
|
|
112
120
|
generateAgentMarkdown,
|
|
@@ -131,9 +139,11 @@ export {
|
|
|
131
139
|
OpenRouterClient,
|
|
132
140
|
OpenAICompatibleClient,
|
|
133
141
|
OhMyClaudeConfigSchema,
|
|
142
|
+
FallbackRequiredError,
|
|
134
143
|
DEFAULT_CONFIG,
|
|
135
144
|
ConcurrencyConfigSchema,
|
|
136
145
|
CategoryConfigSchema,
|
|
137
146
|
AnthropicCompatibleClient,
|
|
147
|
+
AgentFallbackConfigSchema,
|
|
138
148
|
AgentConfigSchema
|
|
139
149
|
};
|
package/dist/mcp/server.js
CHANGED
|
@@ -16671,6 +16671,11 @@ var ProviderConfigSchema = exports_external.object({
|
|
|
16671
16671
|
api_key_env: exports_external.string().optional(),
|
|
16672
16672
|
note: exports_external.string().optional()
|
|
16673
16673
|
});
|
|
16674
|
+
var AgentFallbackConfigSchema = exports_external.object({
|
|
16675
|
+
provider: exports_external.string(),
|
|
16676
|
+
model: exports_external.string(),
|
|
16677
|
+
executionMode: exports_external.enum(["task", "mcp"]).optional()
|
|
16678
|
+
});
|
|
16674
16679
|
var AgentConfigSchema = exports_external.object({
|
|
16675
16680
|
provider: exports_external.string(),
|
|
16676
16681
|
model: exports_external.string(),
|
|
@@ -16679,7 +16684,8 @@ var AgentConfigSchema = exports_external.object({
|
|
|
16679
16684
|
thinking: exports_external.object({
|
|
16680
16685
|
enabled: exports_external.boolean(),
|
|
16681
16686
|
budget_tokens: exports_external.number().optional()
|
|
16682
|
-
}).optional()
|
|
16687
|
+
}).optional(),
|
|
16688
|
+
fallback: AgentFallbackConfigSchema.optional()
|
|
16683
16689
|
});
|
|
16684
16690
|
var CategoryConfigSchema = exports_external.object({
|
|
16685
16691
|
provider: exports_external.string(),
|
|
@@ -16736,19 +16742,32 @@ var OhMyClaudeConfigSchema = exports_external.object({
|
|
|
16736
16742
|
oracle: {
|
|
16737
16743
|
provider: "deepseek",
|
|
16738
16744
|
model: "deepseek-reasoner",
|
|
16739
|
-
temperature: 0.1
|
|
16745
|
+
temperature: 0.1,
|
|
16746
|
+
fallback: { provider: "claude", model: "claude-opus-4-5", executionMode: "task" }
|
|
16747
|
+
},
|
|
16748
|
+
librarian: {
|
|
16749
|
+
provider: "zhipu",
|
|
16750
|
+
model: "glm-4.7",
|
|
16751
|
+
temperature: 0.3,
|
|
16752
|
+
fallback: { provider: "claude", model: "claude-sonnet-4-5", executionMode: "task" }
|
|
16753
|
+
},
|
|
16754
|
+
explore: {
|
|
16755
|
+
provider: "deepseek",
|
|
16756
|
+
model: "deepseek-chat",
|
|
16757
|
+
temperature: 0.1,
|
|
16758
|
+
fallback: { provider: "claude", model: "claude-haiku-4-5", executionMode: "task" }
|
|
16740
16759
|
},
|
|
16741
|
-
librarian: { provider: "zhipu", model: "glm-4.7", temperature: 0.3 },
|
|
16742
|
-
explore: { provider: "deepseek", model: "deepseek-chat", temperature: 0.1 },
|
|
16743
16760
|
"frontend-ui-ux": {
|
|
16744
16761
|
provider: "zhipu",
|
|
16745
16762
|
model: "glm-4v-flash",
|
|
16746
|
-
temperature: 0.7
|
|
16763
|
+
temperature: 0.7,
|
|
16764
|
+
fallback: { provider: "claude", model: "claude-sonnet-4-5", executionMode: "task" }
|
|
16747
16765
|
},
|
|
16748
16766
|
"document-writer": {
|
|
16749
16767
|
provider: "minimax",
|
|
16750
16768
|
model: "MiniMax-M2.1",
|
|
16751
|
-
temperature: 0.5
|
|
16769
|
+
temperature: 0.5,
|
|
16770
|
+
fallback: { provider: "claude", model: "claude-sonnet-4-5", executionMode: "task" }
|
|
16752
16771
|
}
|
|
16753
16772
|
}),
|
|
16754
16773
|
categories: exports_external.record(exports_external.string(), CategoryConfigSchema).default({
|
|
@@ -16850,6 +16869,50 @@ function getProviderDetails(config2, providerName) {
|
|
|
16850
16869
|
}
|
|
16851
16870
|
return null;
|
|
16852
16871
|
}
|
|
16872
|
+
function isProviderConfigured(config2, providerName) {
|
|
16873
|
+
const providerConfig = config2.providers[providerName];
|
|
16874
|
+
if (!providerConfig) {
|
|
16875
|
+
return false;
|
|
16876
|
+
}
|
|
16877
|
+
if (providerConfig.type === "claude-subscription") {
|
|
16878
|
+
return true;
|
|
16879
|
+
}
|
|
16880
|
+
if (providerConfig.api_key_env) {
|
|
16881
|
+
const apiKey = process.env[providerConfig.api_key_env];
|
|
16882
|
+
return !!apiKey && apiKey.length > 0;
|
|
16883
|
+
}
|
|
16884
|
+
return false;
|
|
16885
|
+
}
|
|
16886
|
+
function getAgentFallback(config2, agentName) {
|
|
16887
|
+
const agentConfig = config2.agents[agentName];
|
|
16888
|
+
if (agentConfig?.fallback) {
|
|
16889
|
+
return {
|
|
16890
|
+
provider: agentConfig.fallback.provider,
|
|
16891
|
+
model: agentConfig.fallback.model,
|
|
16892
|
+
executionMode: agentConfig.fallback.executionMode
|
|
16893
|
+
};
|
|
16894
|
+
}
|
|
16895
|
+
return null;
|
|
16896
|
+
}
|
|
16897
|
+
function shouldUseFallback(config2, agentName) {
|
|
16898
|
+
const agentConfig = config2.agents[agentName];
|
|
16899
|
+
if (!agentConfig) {
|
|
16900
|
+
return { useFallback: false };
|
|
16901
|
+
}
|
|
16902
|
+
if (!isProviderConfigured(config2, agentConfig.provider)) {
|
|
16903
|
+
const fallback = getAgentFallback(config2, agentName);
|
|
16904
|
+
if (fallback) {
|
|
16905
|
+
const providerConfig = config2.providers[agentConfig.provider];
|
|
16906
|
+
const envVar = providerConfig?.api_key_env ?? `${agentConfig.provider.toUpperCase()}_API_KEY`;
|
|
16907
|
+
return {
|
|
16908
|
+
useFallback: true,
|
|
16909
|
+
reason: `${envVar} is not set`,
|
|
16910
|
+
fallback
|
|
16911
|
+
};
|
|
16912
|
+
}
|
|
16913
|
+
}
|
|
16914
|
+
return { useFallback: false };
|
|
16915
|
+
}
|
|
16853
16916
|
// src/providers/router.ts
|
|
16854
16917
|
var clientCache = new Map;
|
|
16855
16918
|
function getProviderClient(providerName, config2) {
|
|
@@ -16889,6 +16952,19 @@ function getProviderClient(providerName, config2) {
|
|
|
16889
16952
|
clientCache.set(providerName, client);
|
|
16890
16953
|
return client;
|
|
16891
16954
|
}
|
|
16955
|
+
|
|
16956
|
+
class FallbackRequiredError extends Error {
|
|
16957
|
+
agentName;
|
|
16958
|
+
fallback;
|
|
16959
|
+
reason;
|
|
16960
|
+
constructor(message, agentName, fallback, reason) {
|
|
16961
|
+
super(message);
|
|
16962
|
+
this.agentName = agentName;
|
|
16963
|
+
this.fallback = fallback;
|
|
16964
|
+
this.reason = reason;
|
|
16965
|
+
this.name = "FallbackRequiredError";
|
|
16966
|
+
}
|
|
16967
|
+
}
|
|
16892
16968
|
async function routeByAgent(agentName, messages, options) {
|
|
16893
16969
|
const config2 = loadConfig();
|
|
16894
16970
|
const agentConfig = resolveProviderForAgent(config2, agentName);
|
|
@@ -16899,6 +16975,10 @@ async function routeByAgent(agentName, messages, options) {
|
|
|
16899
16975
|
if (providerDetails?.type === "claude-subscription") {
|
|
16900
16976
|
throw new Error(`Agent "${agentName}" uses Claude subscription. Use Claude Code's Task tool instead of MCP.`);
|
|
16901
16977
|
}
|
|
16978
|
+
const fallbackCheck = shouldUseFallback(config2, agentName);
|
|
16979
|
+
if (fallbackCheck.useFallback && fallbackCheck.fallback) {
|
|
16980
|
+
throw new FallbackRequiredError(`Agent "${agentName}" requires fallback: ${fallbackCheck.reason}. Use Task tool with ${fallbackCheck.fallback.model} instead.`, agentName, fallbackCheck.fallback, fallbackCheck.reason ?? "Provider not configured");
|
|
16981
|
+
}
|
|
16902
16982
|
const client = getProviderClient(agentConfig.provider, config2);
|
|
16903
16983
|
const request = {
|
|
16904
16984
|
model: agentConfig.model,
|
|
@@ -17903,7 +17983,12 @@ var oracleAgent = {
|
|
|
17903
17983
|
defaultTemperature: 0.1,
|
|
17904
17984
|
executionMode: "mcp",
|
|
17905
17985
|
category: "advisor",
|
|
17906
|
-
restrictedTools: ["Edit", "Write", "Task"]
|
|
17986
|
+
restrictedTools: ["Edit", "Write", "Task"],
|
|
17987
|
+
fallback: {
|
|
17988
|
+
provider: "claude",
|
|
17989
|
+
model: "claude-opus-4-5",
|
|
17990
|
+
executionMode: "task"
|
|
17991
|
+
}
|
|
17907
17992
|
};
|
|
17908
17993
|
// src/agents/librarian.ts
|
|
17909
17994
|
var LIBRARIAN_PROMPT = `# THE LIBRARIAN
|
|
@@ -18109,7 +18194,12 @@ var librarianAgent = {
|
|
|
18109
18194
|
defaultTemperature: 0.3,
|
|
18110
18195
|
executionMode: "mcp",
|
|
18111
18196
|
category: "explorer",
|
|
18112
|
-
restrictedTools: ["Edit", "Write"]
|
|
18197
|
+
restrictedTools: ["Edit", "Write"],
|
|
18198
|
+
fallback: {
|
|
18199
|
+
provider: "claude",
|
|
18200
|
+
model: "claude-sonnet-4-5",
|
|
18201
|
+
executionMode: "task"
|
|
18202
|
+
}
|
|
18113
18203
|
};
|
|
18114
18204
|
// src/agents/explore.ts
|
|
18115
18205
|
var EXPLORE_PROMPT = `You are a codebase search specialist. Your job: find files and code, return actionable results.
|
|
@@ -18212,7 +18302,12 @@ var exploreAgent = {
|
|
|
18212
18302
|
defaultTemperature: 0.1,
|
|
18213
18303
|
executionMode: "mcp",
|
|
18214
18304
|
category: "explorer",
|
|
18215
|
-
restrictedTools: ["Edit", "Write", "Task"]
|
|
18305
|
+
restrictedTools: ["Edit", "Write", "Task"],
|
|
18306
|
+
fallback: {
|
|
18307
|
+
provider: "claude",
|
|
18308
|
+
model: "claude-haiku-4-5",
|
|
18309
|
+
executionMode: "task"
|
|
18310
|
+
}
|
|
18216
18311
|
};
|
|
18217
18312
|
// src/agents/frontend-ui-ux.ts
|
|
18218
18313
|
var FRONTEND_UI_UX_PROMPT = `# Role: Designer-Turned-Developer
|
|
@@ -18308,7 +18403,12 @@ var frontendUiUxAgent = {
|
|
|
18308
18403
|
defaultModel: "glm-4v-flash",
|
|
18309
18404
|
defaultTemperature: 0.7,
|
|
18310
18405
|
executionMode: "mcp",
|
|
18311
|
-
category: "specialist"
|
|
18406
|
+
category: "specialist",
|
|
18407
|
+
fallback: {
|
|
18408
|
+
provider: "claude",
|
|
18409
|
+
model: "claude-sonnet-4-5",
|
|
18410
|
+
executionMode: "task"
|
|
18411
|
+
}
|
|
18312
18412
|
};
|
|
18313
18413
|
// src/agents/document-writer.ts
|
|
18314
18414
|
var DOCUMENT_WRITER_PROMPT = `<role>
|
|
@@ -18449,7 +18549,12 @@ var documentWriterAgent = {
|
|
|
18449
18549
|
defaultModel: "MiniMax-M2.1",
|
|
18450
18550
|
defaultTemperature: 0.5,
|
|
18451
18551
|
executionMode: "mcp",
|
|
18452
|
-
category: "specialist"
|
|
18552
|
+
category: "specialist",
|
|
18553
|
+
fallback: {
|
|
18554
|
+
provider: "claude",
|
|
18555
|
+
model: "claude-sonnet-4-5",
|
|
18556
|
+
executionMode: "task"
|
|
18557
|
+
}
|
|
18453
18558
|
};
|
|
18454
18559
|
// src/agents/index.ts
|
|
18455
18560
|
var agents = {
|
|
@@ -18529,8 +18634,19 @@ async function runTask(task, systemPrompt) {
|
|
|
18529
18634
|
task.completedAt = Date.now();
|
|
18530
18635
|
tasks.set(task.id, task);
|
|
18531
18636
|
} catch (error2) {
|
|
18532
|
-
|
|
18533
|
-
|
|
18637
|
+
if (error2 instanceof FallbackRequiredError) {
|
|
18638
|
+
task.status = "fallback_required";
|
|
18639
|
+
task.error = error2.message;
|
|
18640
|
+
task.fallback = {
|
|
18641
|
+
provider: error2.fallback.provider,
|
|
18642
|
+
model: error2.fallback.model,
|
|
18643
|
+
executionMode: error2.fallback.executionMode,
|
|
18644
|
+
reason: error2.reason
|
|
18645
|
+
};
|
|
18646
|
+
} else {
|
|
18647
|
+
task.status = "failed";
|
|
18648
|
+
task.error = error2 instanceof Error ? error2.message : String(error2);
|
|
18649
|
+
}
|
|
18534
18650
|
task.completedAt = Date.now();
|
|
18535
18651
|
tasks.set(task.id, task);
|
|
18536
18652
|
}
|
|
@@ -18543,7 +18659,8 @@ function pollTask(taskId) {
|
|
|
18543
18659
|
return {
|
|
18544
18660
|
status: task.status,
|
|
18545
18661
|
result: task.result,
|
|
18546
|
-
error: task.error
|
|
18662
|
+
error: task.error,
|
|
18663
|
+
fallback: task.fallback
|
|
18547
18664
|
};
|
|
18548
18665
|
}
|
|
18549
18666
|
function cancelTask(taskId) {
|
|
@@ -18643,6 +18760,9 @@ Use this for async operations that should run in parallel without blocking.
|
|
|
18643
18760
|
- visual-engineering: UI/UX work
|
|
18644
18761
|
- writing: Documentation and text
|
|
18645
18762
|
|
|
18763
|
+
**Fallback Behavior**:
|
|
18764
|
+
If the provider's API key is not configured, the task will return with status "fallback_required" and include fallback info. When this happens, use Claude Code's Task tool with the specified fallback model instead.
|
|
18765
|
+
|
|
18646
18766
|
Note: Agents using Claude subscription (sisyphus, claude-reviewer, claude-scout) should use Claude Code's Task tool instead.`,
|
|
18647
18767
|
inputSchema: {
|
|
18648
18768
|
type: "object",
|
|
@@ -18706,8 +18826,8 @@ Note: Agents using Claude subscription (sisyphus, claude-reviewer, claude-scout)
|
|
|
18706
18826
|
properties: {
|
|
18707
18827
|
status: {
|
|
18708
18828
|
type: "string",
|
|
18709
|
-
enum: ["pending", "running", "completed", "failed", "cancelled"],
|
|
18710
|
-
description: "Filter by status"
|
|
18829
|
+
enum: ["pending", "running", "completed", "failed", "cancelled", "fallback_required"],
|
|
18830
|
+
description: "Filter by status. 'fallback_required' means the provider API key is not configured and Claude Task tool should be used instead."
|
|
18711
18831
|
},
|
|
18712
18832
|
limit: {
|
|
18713
18833
|
type: "number",
|
|
@@ -18870,7 +18990,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
18870
18990
|
...t.completedAt && {
|
|
18871
18991
|
completed: new Date(t.completedAt).toISOString()
|
|
18872
18992
|
},
|
|
18873
|
-
...t.error && { error: t.error }
|
|
18993
|
+
...t.error && { error: t.error },
|
|
18994
|
+
...t.fallback && { fallback: t.fallback }
|
|
18874
18995
|
}))
|
|
18875
18996
|
})
|
|
18876
18997
|
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lgcyaxi/oh-my-claude",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Multi-agent orchestration plugin for Claude Code with multi-provider support",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
8
|
"bin": {
|
|
9
|
-
"oh-my-claude": "
|
|
9
|
+
"oh-my-claude": "bin/oh-my-claude.js"
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
12
|
"build": "bun build src/index.ts src/cli.ts --outdir dist --target node --splitting",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"license": "MIT",
|
|
44
44
|
"repository": {
|
|
45
45
|
"type": "git",
|
|
46
|
-
"url": "https://github.com/lgcyaxi/oh-my-claude"
|
|
46
|
+
"url": "git+https://github.com/lgcyaxi/oh-my-claude.git"
|
|
47
47
|
},
|
|
48
48
|
"homepage": "https://github.com/lgcyaxi/oh-my-claude#readme",
|
|
49
49
|
"bugs": {
|
|
@@ -146,6 +146,11 @@ export const documentWriterAgent: AgentDefinition = {
|
|
|
146
146
|
defaultTemperature: 0.5,
|
|
147
147
|
executionMode: "mcp",
|
|
148
148
|
category: "specialist",
|
|
149
|
+
fallback: {
|
|
150
|
+
provider: "claude",
|
|
151
|
+
model: "claude-sonnet-4-5",
|
|
152
|
+
executionMode: "task",
|
|
153
|
+
},
|
|
149
154
|
};
|
|
150
155
|
|
|
151
156
|
export default documentWriterAgent;
|
package/src/agents/explore.ts
CHANGED
|
@@ -108,6 +108,11 @@ export const exploreAgent: AgentDefinition = {
|
|
|
108
108
|
executionMode: "mcp",
|
|
109
109
|
category: "explorer",
|
|
110
110
|
restrictedTools: ["Edit", "Write", "Task"],
|
|
111
|
+
fallback: {
|
|
112
|
+
provider: "claude",
|
|
113
|
+
model: "claude-haiku-4-5",
|
|
114
|
+
executionMode: "task",
|
|
115
|
+
},
|
|
111
116
|
};
|
|
112
117
|
|
|
113
118
|
export default exploreAgent;
|
|
@@ -101,6 +101,11 @@ export const frontendUiUxAgent: AgentDefinition = {
|
|
|
101
101
|
defaultTemperature: 0.7,
|
|
102
102
|
executionMode: "mcp",
|
|
103
103
|
category: "specialist",
|
|
104
|
+
fallback: {
|
|
105
|
+
provider: "claude",
|
|
106
|
+
model: "claude-sonnet-4-5",
|
|
107
|
+
executionMode: "task",
|
|
108
|
+
},
|
|
104
109
|
};
|
|
105
110
|
|
|
106
111
|
export default frontendUiUxAgent;
|
package/src/agents/librarian.ts
CHANGED
|
@@ -211,6 +211,11 @@ export const librarianAgent: AgentDefinition = {
|
|
|
211
211
|
executionMode: "mcp",
|
|
212
212
|
category: "explorer",
|
|
213
213
|
restrictedTools: ["Edit", "Write"],
|
|
214
|
+
fallback: {
|
|
215
|
+
provider: "claude",
|
|
216
|
+
model: "claude-sonnet-4-5",
|
|
217
|
+
executionMode: "task",
|
|
218
|
+
},
|
|
214
219
|
};
|
|
215
220
|
|
|
216
221
|
export default librarianAgent;
|
package/src/agents/oracle.ts
CHANGED
|
@@ -99,6 +99,11 @@ export const oracleAgent: AgentDefinition = {
|
|
|
99
99
|
executionMode: "mcp",
|
|
100
100
|
category: "advisor",
|
|
101
101
|
restrictedTools: ["Edit", "Write", "Task"],
|
|
102
|
+
fallback: {
|
|
103
|
+
provider: "claude",
|
|
104
|
+
model: "claude-opus-4-5",
|
|
105
|
+
executionMode: "task",
|
|
106
|
+
},
|
|
102
107
|
};
|
|
103
108
|
|
|
104
109
|
export default oracleAgent;
|
package/src/agents/types.ts
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
* Agent definition types for oh-my-claude
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
export interface AgentFallback {
|
|
6
|
+
/** Fallback provider (usually "claude") */
|
|
7
|
+
provider: string;
|
|
8
|
+
/** Fallback model */
|
|
9
|
+
model: string;
|
|
10
|
+
/** Fallback execution mode (usually "task" for Claude) */
|
|
11
|
+
executionMode: "task" | "mcp";
|
|
12
|
+
}
|
|
13
|
+
|
|
5
14
|
export interface AgentDefinition {
|
|
6
15
|
/** Agent identifier (used in config) */
|
|
7
16
|
name: string;
|
|
@@ -21,6 +30,8 @@ export interface AgentDefinition {
|
|
|
21
30
|
category: "orchestrator" | "reviewer" | "scout" | "advisor" | "explorer" | "specialist" | "planner";
|
|
22
31
|
/** Tools this agent should NOT use (restrictions) */
|
|
23
32
|
restrictedTools?: string[];
|
|
33
|
+
/** Fallback configuration when primary provider API key is not available */
|
|
34
|
+
fallback?: AgentFallback;
|
|
24
35
|
}
|
|
25
36
|
|
|
26
37
|
export interface AgentMarkdownOptions {
|
package/src/cli.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* npx oh-my-claude uninstall # Uninstall oh-my-claude
|
|
8
8
|
* npx oh-my-claude status # Check installation status
|
|
9
9
|
* npx oh-my-claude doctor # Diagnose configuration issues
|
|
10
|
+
* npx oh-my-claude update # Update oh-my-claude to latest version
|
|
10
11
|
* npx oh-my-claude setup-mcp # Install official MCP servers (MiniMax, GLM)
|
|
11
12
|
*/
|
|
12
13
|
|
|
@@ -18,7 +19,7 @@ import { loadConfig } from "./config";
|
|
|
18
19
|
program
|
|
19
20
|
.name("oh-my-claude")
|
|
20
21
|
.description("Multi-agent orchestration plugin for Claude Code")
|
|
21
|
-
.version("1.0.
|
|
22
|
+
.version("1.0.1");
|
|
22
23
|
|
|
23
24
|
// Install command
|
|
24
25
|
program
|
|
@@ -362,6 +363,130 @@ program
|
|
|
362
363
|
}
|
|
363
364
|
});
|
|
364
365
|
|
|
366
|
+
// Update command
|
|
367
|
+
program
|
|
368
|
+
.command("update")
|
|
369
|
+
.description("Update oh-my-claude to the latest version")
|
|
370
|
+
.option("--check", "Only check for updates without installing")
|
|
371
|
+
.option("--force", "Force update even if already on latest version")
|
|
372
|
+
.action(async (options) => {
|
|
373
|
+
const { execSync } = require("node:child_process");
|
|
374
|
+
const { readFileSync, existsSync } = require("node:fs");
|
|
375
|
+
const { join } = require("node:path");
|
|
376
|
+
const { homedir } = require("node:os");
|
|
377
|
+
|
|
378
|
+
// Color helpers
|
|
379
|
+
const useColor = process.stdout.isTTY;
|
|
380
|
+
const c = {
|
|
381
|
+
reset: useColor ? "\x1b[0m" : "",
|
|
382
|
+
bold: useColor ? "\x1b[1m" : "",
|
|
383
|
+
dim: useColor ? "\x1b[2m" : "",
|
|
384
|
+
green: useColor ? "\x1b[32m" : "",
|
|
385
|
+
red: useColor ? "\x1b[31m" : "",
|
|
386
|
+
yellow: useColor ? "\x1b[33m" : "",
|
|
387
|
+
cyan: useColor ? "\x1b[36m" : "",
|
|
388
|
+
magenta: useColor ? "\x1b[35m" : "",
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
const ok = (text: string) => `${c.green}✓${c.reset} ${text}`;
|
|
392
|
+
const fail = (text: string) => `${c.red}✗${c.reset} ${text}`;
|
|
393
|
+
const warn = (text: string) => `${c.yellow}⚠${c.reset} ${text}`;
|
|
394
|
+
const header = (text: string) => `${c.cyan}${c.bold}${text}${c.reset}`;
|
|
395
|
+
const dimText = (text: string) => `${c.dim}${text}${c.reset}`;
|
|
396
|
+
|
|
397
|
+
const PACKAGE_NAME = "@lgcyaxi/oh-my-claude";
|
|
398
|
+
|
|
399
|
+
console.log(`${c.bold}${c.magenta}oh-my-claude Update${c.reset}\n`);
|
|
400
|
+
|
|
401
|
+
// Get current version
|
|
402
|
+
let currentVersion = "unknown";
|
|
403
|
+
try {
|
|
404
|
+
// Try to read from package.json in the installed location
|
|
405
|
+
const installDir = join(homedir(), ".claude", "oh-my-claude");
|
|
406
|
+
const localPkgPath = join(installDir, "package.json");
|
|
407
|
+
|
|
408
|
+
if (existsSync(localPkgPath)) {
|
|
409
|
+
const pkg = JSON.parse(readFileSync(localPkgPath, "utf-8"));
|
|
410
|
+
currentVersion = pkg.version;
|
|
411
|
+
} else {
|
|
412
|
+
// Fall back to the version from the running CLI
|
|
413
|
+
currentVersion = program.version() || "unknown";
|
|
414
|
+
}
|
|
415
|
+
} catch (error) {
|
|
416
|
+
// Use the CLI version as fallback
|
|
417
|
+
currentVersion = program.version() || "unknown";
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
console.log(`Current version: ${c.cyan}${currentVersion}${c.reset}`);
|
|
421
|
+
|
|
422
|
+
// Fetch latest version from npm
|
|
423
|
+
let latestVersion = "unknown";
|
|
424
|
+
try {
|
|
425
|
+
console.log(`${dimText("Checking npm registry for latest version...")}`);
|
|
426
|
+
const npmInfo = execSync(`npm view ${PACKAGE_NAME} version 2>/dev/null`, {
|
|
427
|
+
encoding: "utf-8",
|
|
428
|
+
}).trim();
|
|
429
|
+
latestVersion = npmInfo;
|
|
430
|
+
console.log(`Latest version: ${c.cyan}${latestVersion}${c.reset}\n`);
|
|
431
|
+
} catch (error) {
|
|
432
|
+
console.log(`${fail("Failed to fetch latest version from npm")}`);
|
|
433
|
+
console.log(`${dimText("Check your internet connection or try again later")}\n`);
|
|
434
|
+
process.exit(1);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Compare versions
|
|
438
|
+
const isUpToDate = currentVersion === latestVersion;
|
|
439
|
+
const needsUpdate = !isUpToDate || options.force;
|
|
440
|
+
|
|
441
|
+
if (isUpToDate && !options.force) {
|
|
442
|
+
console.log(ok("You are already on the latest version!"));
|
|
443
|
+
process.exit(0);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if (options.check) {
|
|
447
|
+
if (needsUpdate) {
|
|
448
|
+
console.log(warn(`Update available: ${currentVersion} → ${latestVersion}`));
|
|
449
|
+
console.log(`\nRun ${c.cyan}npx ${PACKAGE_NAME} update${c.reset} to update.`);
|
|
450
|
+
}
|
|
451
|
+
process.exit(0);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Perform update
|
|
455
|
+
console.log(header("Updating oh-my-claude...\n"));
|
|
456
|
+
|
|
457
|
+
try {
|
|
458
|
+
// Step 1: Clear npx cache for the package
|
|
459
|
+
console.log(`${dimText("Clearing npx cache...")}`);
|
|
460
|
+
try {
|
|
461
|
+
execSync(`npx --yes clear-npx-cache 2>/dev/null || true`, { stdio: "pipe" });
|
|
462
|
+
} catch {
|
|
463
|
+
// Ignore errors - cache clear is optional
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// Step 2: Install latest version via npx
|
|
467
|
+
console.log(`${dimText("Downloading latest version...")}`);
|
|
468
|
+
|
|
469
|
+
// Use npx with --yes to ensure latest version is fetched
|
|
470
|
+
// The @latest tag forces npm to check for the newest version
|
|
471
|
+
const updateCmd = `npx --yes ${PACKAGE_NAME}@latest install --force`;
|
|
472
|
+
console.log(`${dimText(`Running: ${updateCmd}`)}\n`);
|
|
473
|
+
|
|
474
|
+
execSync(updateCmd, { stdio: "inherit" });
|
|
475
|
+
|
|
476
|
+
console.log(`\n${ok("Update complete!")}`);
|
|
477
|
+
console.log(`Updated from ${c.yellow}${currentVersion}${c.reset} to ${c.green}${latestVersion}${c.reset}`);
|
|
478
|
+
|
|
479
|
+
// Show changelog hint
|
|
480
|
+
console.log(`\n${dimText("View changelog at: https://github.com/lgcyaxi/oh-my-claude/blob/main/CHANGELOG.md")}`);
|
|
481
|
+
|
|
482
|
+
} catch (error) {
|
|
483
|
+
console.log(`\n${fail("Update failed")}`);
|
|
484
|
+
console.log(`${dimText("Try running manually:")}`);
|
|
485
|
+
console.log(` ${c.cyan}npx ${PACKAGE_NAME}@latest install --force${c.reset}`);
|
|
486
|
+
process.exit(1);
|
|
487
|
+
}
|
|
488
|
+
});
|
|
489
|
+
|
|
365
490
|
// Setup MCP command
|
|
366
491
|
program
|
|
367
492
|
.command("setup-mcp")
|
package/src/config/loader.ts
CHANGED
|
@@ -103,3 +103,76 @@ export function getProviderDetails(
|
|
|
103
103
|
}
|
|
104
104
|
return null;
|
|
105
105
|
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Check if a provider is configured (has API key set)
|
|
109
|
+
*/
|
|
110
|
+
export function isProviderConfigured(
|
|
111
|
+
config: OhMyClaudeConfig,
|
|
112
|
+
providerName: string
|
|
113
|
+
): boolean {
|
|
114
|
+
const providerConfig = config.providers[providerName];
|
|
115
|
+
if (!providerConfig) {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Claude subscription is always "configured"
|
|
120
|
+
if (providerConfig.type === "claude-subscription") {
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Check if API key environment variable is set
|
|
125
|
+
if (providerConfig.api_key_env) {
|
|
126
|
+
const apiKey = process.env[providerConfig.api_key_env];
|
|
127
|
+
return !!apiKey && apiKey.length > 0;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Get fallback configuration for an agent
|
|
135
|
+
*/
|
|
136
|
+
export function getAgentFallback(
|
|
137
|
+
config: OhMyClaudeConfig,
|
|
138
|
+
agentName: string
|
|
139
|
+
): { provider: string; model: string; executionMode?: string } | null {
|
|
140
|
+
const agentConfig = config.agents[agentName];
|
|
141
|
+
if (agentConfig?.fallback) {
|
|
142
|
+
return {
|
|
143
|
+
provider: agentConfig.fallback.provider,
|
|
144
|
+
model: agentConfig.fallback.model,
|
|
145
|
+
executionMode: agentConfig.fallback.executionMode,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Check if an agent should use fallback (primary provider not configured)
|
|
153
|
+
*/
|
|
154
|
+
export function shouldUseFallback(
|
|
155
|
+
config: OhMyClaudeConfig,
|
|
156
|
+
agentName: string
|
|
157
|
+
): { useFallback: boolean; reason?: string; fallback?: { provider: string; model: string; executionMode?: string } } {
|
|
158
|
+
const agentConfig = config.agents[agentName];
|
|
159
|
+
if (!agentConfig) {
|
|
160
|
+
return { useFallback: false };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Check if primary provider is configured
|
|
164
|
+
if (!isProviderConfigured(config, agentConfig.provider)) {
|
|
165
|
+
const fallback = getAgentFallback(config, agentName);
|
|
166
|
+
if (fallback) {
|
|
167
|
+
const providerConfig = config.providers[agentConfig.provider];
|
|
168
|
+
const envVar = providerConfig?.api_key_env ?? `${agentConfig.provider.toUpperCase()}_API_KEY`;
|
|
169
|
+
return {
|
|
170
|
+
useFallback: true,
|
|
171
|
+
reason: `${envVar} is not set`,
|
|
172
|
+
fallback,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return { useFallback: false };
|
|
178
|
+
}
|