@lgcyaxi/oh-my-claude 1.0.0 → 1.1.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/.github/ISSUE_TEMPLATE/bug_report.md +43 -0
- package/CHANGELOG.md +53 -0
- package/CLAUDE.md +60 -0
- package/README.md +97 -12
- package/README.zh-CN.md +58 -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/changelog/v1.1.0.md +20 -0
- package/changelog/v1.1.1.md +71 -0
- package/dist/cli.js +213 -4
- package/dist/hooks/comment-checker.js +1 -1
- package/dist/hooks/task-notification.js +124 -0
- package/dist/hooks/task-tracker.js +144 -0
- package/dist/index-1dv6t98k.js +7654 -0
- package/dist/index-5ars1tn4.js +7348 -0
- package/dist/index-d79fk9ah.js +7350 -0
- package/dist/index-hzm01rkh.js +7654 -0
- package/dist/index-qrbfj4cd.js +7664 -0
- package/dist/index-ypyx3ye0.js +7349 -0
- package/dist/index.js +24 -1
- package/dist/mcp/server.js +202 -45
- package/dist/statusline/statusline.js +146 -0
- package/package.json +6 -5
- 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 +261 -2
- package/src/commands/index.ts +8 -1
- package/src/commands/omc-status.md +71 -0
- package/src/commands/omcx-issue.md +175 -0
- package/src/commands/ulw.md +144 -0
- package/src/config/loader.ts +73 -0
- package/src/config/schema.ts +25 -2
- package/src/generators/agent-generator.ts +17 -1
- package/src/hooks/comment-checker.ts +2 -2
- package/src/hooks/task-notification.ts +206 -0
- package/src/hooks/task-tracker.ts +252 -0
- package/src/installer/index.ts +55 -4
- package/src/installer/settings-merger.ts +86 -0
- package/src/installer/statusline-merger.ts +169 -0
- package/src/mcp/background-agent-server/server.ts +11 -2
- package/src/mcp/background-agent-server/task-manager.ts +83 -4
- package/src/providers/router.ts +28 -0
- package/src/statusline/formatter.ts +164 -0
- package/src/statusline/statusline.ts +103 -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,
|
|
@@ -36,8 +39,13 @@ import {
|
|
|
36
39
|
getMcpServerPath,
|
|
37
40
|
getProviderDetails,
|
|
38
41
|
getProvidersStatus,
|
|
42
|
+
getStatusLineScriptPath,
|
|
43
|
+
init_agent_generator,
|
|
44
|
+
init_agents,
|
|
45
|
+
init_installer,
|
|
39
46
|
install,
|
|
40
47
|
isMcpAgent,
|
|
48
|
+
isProviderConfigured,
|
|
41
49
|
isTaskAgent,
|
|
42
50
|
librarianAgent,
|
|
43
51
|
loadConfig,
|
|
@@ -50,10 +58,14 @@ import {
|
|
|
50
58
|
resolveProviderForCategory,
|
|
51
59
|
routeByAgent,
|
|
52
60
|
routeByCategory,
|
|
61
|
+
shouldUseFallback,
|
|
53
62
|
sisyphusAgent,
|
|
54
63
|
taskAgents,
|
|
55
64
|
uninstall
|
|
56
|
-
} from "./index-
|
|
65
|
+
} from "./index-qrbfj4cd.js";
|
|
66
|
+
|
|
67
|
+
// src/index.ts
|
|
68
|
+
init_agents();
|
|
57
69
|
// src/providers/deepseek.ts
|
|
58
70
|
var DEEPSEEK_BASE_URL = "https://api.deepseek.com/anthropic";
|
|
59
71
|
var DEEPSEEK_API_KEY_ENV = "DEEPSEEK_API_KEY";
|
|
@@ -75,11 +87,17 @@ function createMiniMaxClient() {
|
|
|
75
87
|
return createAnthropicClientFromEnv("MiniMax", MINIMAX_BASE_URL, MINIMAX_API_KEY_ENV, "MiniMax-M2.1");
|
|
76
88
|
}
|
|
77
89
|
var minimaxClient = createMiniMaxClient();
|
|
90
|
+
// src/generators/index.ts
|
|
91
|
+
init_agent_generator();
|
|
92
|
+
|
|
93
|
+
// src/index.ts
|
|
94
|
+
init_installer();
|
|
78
95
|
export {
|
|
79
96
|
zhipuClient,
|
|
80
97
|
uninstall,
|
|
81
98
|
taskAgents,
|
|
82
99
|
sisyphusAgent,
|
|
100
|
+
shouldUseFallback,
|
|
83
101
|
routeByCategory,
|
|
84
102
|
routeByAgent,
|
|
85
103
|
resolveProviderForCategory,
|
|
@@ -94,8 +112,10 @@ export {
|
|
|
94
112
|
loadConfig,
|
|
95
113
|
librarianAgent,
|
|
96
114
|
isTaskAgent,
|
|
115
|
+
isProviderConfigured,
|
|
97
116
|
isMcpAgent,
|
|
98
117
|
install,
|
|
118
|
+
getStatusLineScriptPath,
|
|
99
119
|
getProvidersStatus,
|
|
100
120
|
getProviderDetails,
|
|
101
121
|
getMcpServerPath,
|
|
@@ -107,6 +127,7 @@ export {
|
|
|
107
127
|
getConfigPath,
|
|
108
128
|
getCommandsDir,
|
|
109
129
|
getAgentsDirectory,
|
|
130
|
+
getAgentFallback,
|
|
110
131
|
getAgent,
|
|
111
132
|
generateAllAgentFiles,
|
|
112
133
|
generateAgentMarkdown,
|
|
@@ -131,9 +152,11 @@ export {
|
|
|
131
152
|
OpenRouterClient,
|
|
132
153
|
OpenAICompatibleClient,
|
|
133
154
|
OhMyClaudeConfigSchema,
|
|
155
|
+
FallbackRequiredError,
|
|
134
156
|
DEFAULT_CONFIG,
|
|
135
157
|
ConcurrencyConfigSchema,
|
|
136
158
|
CategoryConfigSchema,
|
|
137
159
|
AnthropicCompatibleClient,
|
|
160
|
+
AgentFallbackConfigSchema,
|
|
138
161
|
AgentConfigSchema
|
|
139
162
|
};
|
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 = {
|
|
@@ -18468,10 +18573,65 @@ function getAgent(name) {
|
|
|
18468
18573
|
}
|
|
18469
18574
|
|
|
18470
18575
|
// src/mcp/background-agent-server/task-manager.ts
|
|
18576
|
+
import { writeFileSync, mkdirSync, existsSync as existsSync2 } from "node:fs";
|
|
18577
|
+
import { join as join2, dirname } from "node:path";
|
|
18578
|
+
import { homedir as homedir2 } from "node:os";
|
|
18579
|
+
|
|
18580
|
+
// src/mcp/background-agent-server/concurrency.ts
|
|
18581
|
+
var activeCounts = new Map;
|
|
18582
|
+
var queues = new Map;
|
|
18583
|
+
function getConcurrencyLimit(provider) {
|
|
18584
|
+
const config2 = loadConfig();
|
|
18585
|
+
const perProvider = config2.concurrency.per_provider?.[provider];
|
|
18586
|
+
if (perProvider !== undefined) {
|
|
18587
|
+
return perProvider;
|
|
18588
|
+
}
|
|
18589
|
+
return config2.concurrency.default;
|
|
18590
|
+
}
|
|
18591
|
+
function getActiveCount(provider) {
|
|
18592
|
+
return activeCounts.get(provider) ?? 0;
|
|
18593
|
+
}
|
|
18594
|
+
function getConcurrencyStatus() {
|
|
18595
|
+
const config2 = loadConfig();
|
|
18596
|
+
const status = {};
|
|
18597
|
+
const providers = Object.keys(config2.providers);
|
|
18598
|
+
for (const provider of providers) {
|
|
18599
|
+
status[provider] = {
|
|
18600
|
+
active: getActiveCount(provider),
|
|
18601
|
+
limit: getConcurrencyLimit(provider),
|
|
18602
|
+
queued: queues.get(provider)?.length ?? 0
|
|
18603
|
+
};
|
|
18604
|
+
}
|
|
18605
|
+
return status;
|
|
18606
|
+
}
|
|
18607
|
+
|
|
18608
|
+
// src/mcp/background-agent-server/task-manager.ts
|
|
18609
|
+
var STATUS_FILE_PATH = join2(homedir2(), ".claude", "oh-my-claude", "status.json");
|
|
18471
18610
|
var tasks = new Map;
|
|
18472
18611
|
function generateTaskId() {
|
|
18473
18612
|
return `task_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
18474
18613
|
}
|
|
18614
|
+
function updateStatusFile() {
|
|
18615
|
+
try {
|
|
18616
|
+
const dir = dirname(STATUS_FILE_PATH);
|
|
18617
|
+
if (!existsSync2(dir)) {
|
|
18618
|
+
mkdirSync(dir, { recursive: true });
|
|
18619
|
+
}
|
|
18620
|
+
const activeTasks = Array.from(tasks.values()).filter((t) => t.status === "running" || t.status === "pending").map((t) => ({
|
|
18621
|
+
agent: t.agentName || t.categoryName || "unknown",
|
|
18622
|
+
startedAt: t.startedAt || t.createdAt
|
|
18623
|
+
}));
|
|
18624
|
+
const providers = getConcurrencyStatus();
|
|
18625
|
+
const status = {
|
|
18626
|
+
activeTasks,
|
|
18627
|
+
providers,
|
|
18628
|
+
updatedAt: new Date().toISOString()
|
|
18629
|
+
};
|
|
18630
|
+
writeFileSync(STATUS_FILE_PATH, JSON.stringify(status, null, 2));
|
|
18631
|
+
} catch (error2) {
|
|
18632
|
+
console.error("Failed to update status file:", error2);
|
|
18633
|
+
}
|
|
18634
|
+
}
|
|
18475
18635
|
async function launchTask(options) {
|
|
18476
18636
|
const { agentName, categoryName, prompt, systemPrompt } = options;
|
|
18477
18637
|
if (!agentName && !categoryName) {
|
|
@@ -18494,6 +18654,7 @@ async function launchTask(options) {
|
|
|
18494
18654
|
createdAt: Date.now()
|
|
18495
18655
|
};
|
|
18496
18656
|
tasks.set(taskId, task);
|
|
18657
|
+
updateStatusFile();
|
|
18497
18658
|
runTask(task, finalSystemPrompt).catch((error2) => {
|
|
18498
18659
|
console.error(`Task ${taskId} failed:`, error2);
|
|
18499
18660
|
});
|
|
@@ -18503,6 +18664,7 @@ async function runTask(task, systemPrompt) {
|
|
|
18503
18664
|
task.status = "running";
|
|
18504
18665
|
task.startedAt = Date.now();
|
|
18505
18666
|
tasks.set(task.id, task);
|
|
18667
|
+
updateStatusFile();
|
|
18506
18668
|
try {
|
|
18507
18669
|
const messages = [];
|
|
18508
18670
|
if (systemPrompt) {
|
|
@@ -18528,11 +18690,24 @@ async function runTask(task, systemPrompt) {
|
|
|
18528
18690
|
task.result = result;
|
|
18529
18691
|
task.completedAt = Date.now();
|
|
18530
18692
|
tasks.set(task.id, task);
|
|
18693
|
+
updateStatusFile();
|
|
18531
18694
|
} catch (error2) {
|
|
18532
|
-
|
|
18533
|
-
|
|
18695
|
+
if (error2 instanceof FallbackRequiredError) {
|
|
18696
|
+
task.status = "fallback_required";
|
|
18697
|
+
task.error = error2.message;
|
|
18698
|
+
task.fallback = {
|
|
18699
|
+
provider: error2.fallback.provider,
|
|
18700
|
+
model: error2.fallback.model,
|
|
18701
|
+
executionMode: error2.fallback.executionMode,
|
|
18702
|
+
reason: error2.reason
|
|
18703
|
+
};
|
|
18704
|
+
} else {
|
|
18705
|
+
task.status = "failed";
|
|
18706
|
+
task.error = error2 instanceof Error ? error2.message : String(error2);
|
|
18707
|
+
}
|
|
18534
18708
|
task.completedAt = Date.now();
|
|
18535
18709
|
tasks.set(task.id, task);
|
|
18710
|
+
updateStatusFile();
|
|
18536
18711
|
}
|
|
18537
18712
|
}
|
|
18538
18713
|
function pollTask(taskId) {
|
|
@@ -18543,7 +18718,8 @@ function pollTask(taskId) {
|
|
|
18543
18718
|
return {
|
|
18544
18719
|
status: task.status,
|
|
18545
18720
|
result: task.result,
|
|
18546
|
-
error: task.error
|
|
18721
|
+
error: task.error,
|
|
18722
|
+
fallback: task.fallback
|
|
18547
18723
|
};
|
|
18548
18724
|
}
|
|
18549
18725
|
function cancelTask(taskId) {
|
|
@@ -18555,6 +18731,7 @@ function cancelTask(taskId) {
|
|
|
18555
18731
|
task.status = "cancelled";
|
|
18556
18732
|
task.completedAt = Date.now();
|
|
18557
18733
|
tasks.set(taskId, task);
|
|
18734
|
+
updateStatusFile();
|
|
18558
18735
|
return true;
|
|
18559
18736
|
}
|
|
18560
18737
|
return false;
|
|
@@ -18569,6 +18746,9 @@ function cancelAllTasks() {
|
|
|
18569
18746
|
cancelled++;
|
|
18570
18747
|
}
|
|
18571
18748
|
}
|
|
18749
|
+
if (cancelled > 0) {
|
|
18750
|
+
updateStatusFile();
|
|
18751
|
+
}
|
|
18572
18752
|
return cancelled;
|
|
18573
18753
|
}
|
|
18574
18754
|
function listTasks(options) {
|
|
@@ -18594,34 +18774,6 @@ function cleanupTasks(maxAgeMs = 30 * 60 * 1000) {
|
|
|
18594
18774
|
return cleaned;
|
|
18595
18775
|
}
|
|
18596
18776
|
|
|
18597
|
-
// src/mcp/background-agent-server/concurrency.ts
|
|
18598
|
-
var activeCounts = new Map;
|
|
18599
|
-
var queues = new Map;
|
|
18600
|
-
function getConcurrencyLimit(provider) {
|
|
18601
|
-
const config2 = loadConfig();
|
|
18602
|
-
const perProvider = config2.concurrency.per_provider?.[provider];
|
|
18603
|
-
if (perProvider !== undefined) {
|
|
18604
|
-
return perProvider;
|
|
18605
|
-
}
|
|
18606
|
-
return config2.concurrency.default;
|
|
18607
|
-
}
|
|
18608
|
-
function getActiveCount(provider) {
|
|
18609
|
-
return activeCounts.get(provider) ?? 0;
|
|
18610
|
-
}
|
|
18611
|
-
function getConcurrencyStatus() {
|
|
18612
|
-
const config2 = loadConfig();
|
|
18613
|
-
const status = {};
|
|
18614
|
-
const providers = Object.keys(config2.providers);
|
|
18615
|
-
for (const provider of providers) {
|
|
18616
|
-
status[provider] = {
|
|
18617
|
-
active: getActiveCount(provider),
|
|
18618
|
-
limit: getConcurrencyLimit(provider),
|
|
18619
|
-
queued: queues.get(provider)?.length ?? 0
|
|
18620
|
-
};
|
|
18621
|
-
}
|
|
18622
|
-
return status;
|
|
18623
|
-
}
|
|
18624
|
-
|
|
18625
18777
|
// src/mcp/background-agent-server/server.ts
|
|
18626
18778
|
var tools = [
|
|
18627
18779
|
{
|
|
@@ -18643,6 +18795,9 @@ Use this for async operations that should run in parallel without blocking.
|
|
|
18643
18795
|
- visual-engineering: UI/UX work
|
|
18644
18796
|
- writing: Documentation and text
|
|
18645
18797
|
|
|
18798
|
+
**Fallback Behavior**:
|
|
18799
|
+
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.
|
|
18800
|
+
|
|
18646
18801
|
Note: Agents using Claude subscription (sisyphus, claude-reviewer, claude-scout) should use Claude Code's Task tool instead.`,
|
|
18647
18802
|
inputSchema: {
|
|
18648
18803
|
type: "object",
|
|
@@ -18706,8 +18861,8 @@ Note: Agents using Claude subscription (sisyphus, claude-reviewer, claude-scout)
|
|
|
18706
18861
|
properties: {
|
|
18707
18862
|
status: {
|
|
18708
18863
|
type: "string",
|
|
18709
|
-
enum: ["pending", "running", "completed", "failed", "cancelled"],
|
|
18710
|
-
description: "Filter by status"
|
|
18864
|
+
enum: ["pending", "running", "completed", "failed", "cancelled", "fallback_required"],
|
|
18865
|
+
description: "Filter by status. 'fallback_required' means the provider API key is not configured and Claude Task tool should be used instead."
|
|
18711
18866
|
},
|
|
18712
18867
|
limit: {
|
|
18713
18868
|
type: "number",
|
|
@@ -18870,7 +19025,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
18870
19025
|
...t.completedAt && {
|
|
18871
19026
|
completed: new Date(t.completedAt).toISOString()
|
|
18872
19027
|
},
|
|
18873
|
-
...t.error && { error: t.error }
|
|
19028
|
+
...t.error && { error: t.error },
|
|
19029
|
+
...t.fallback && { fallback: t.fallback }
|
|
18874
19030
|
}))
|
|
18875
19031
|
})
|
|
18876
19032
|
}
|
|
@@ -18911,6 +19067,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
18911
19067
|
async function main() {
|
|
18912
19068
|
const transport = new StdioServerTransport;
|
|
18913
19069
|
await server.connect(transport);
|
|
19070
|
+
updateStatusFile();
|
|
18914
19071
|
console.error("oh-my-claude Background Agent MCP Server running");
|
|
18915
19072
|
}
|
|
18916
19073
|
main().catch((error2) => {
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/statusline/statusline.ts
|
|
4
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { homedir } from "node:os";
|
|
7
|
+
|
|
8
|
+
// src/statusline/formatter.ts
|
|
9
|
+
var AGENT_ABBREV = {
|
|
10
|
+
oracle: "Oracle",
|
|
11
|
+
librarian: "Lib",
|
|
12
|
+
explore: "Exp",
|
|
13
|
+
"frontend-ui-ux": "UI",
|
|
14
|
+
"document-writer": "Doc"
|
|
15
|
+
};
|
|
16
|
+
var TASK_AGENT_ABBREV = {
|
|
17
|
+
Scout: "Scout",
|
|
18
|
+
Planner: "Plan",
|
|
19
|
+
General: "Gen",
|
|
20
|
+
Guide: "Guide",
|
|
21
|
+
Bash: "Bash"
|
|
22
|
+
};
|
|
23
|
+
var PROVIDER_ABBREV = {
|
|
24
|
+
deepseek: "DS",
|
|
25
|
+
zhipu: "ZP",
|
|
26
|
+
minimax: "MM",
|
|
27
|
+
openrouter: "OR"
|
|
28
|
+
};
|
|
29
|
+
function formatDuration(seconds) {
|
|
30
|
+
if (seconds < 60) {
|
|
31
|
+
return `${Math.floor(seconds)}s`;
|
|
32
|
+
}
|
|
33
|
+
const minutes = Math.floor(seconds / 60);
|
|
34
|
+
return `${minutes}m`;
|
|
35
|
+
}
|
|
36
|
+
function getAgentAbbrev(agent) {
|
|
37
|
+
if (agent.startsWith("@")) {
|
|
38
|
+
const taskAgent = agent.slice(1);
|
|
39
|
+
return `@${TASK_AGENT_ABBREV[taskAgent] || taskAgent.slice(0, 4)}`;
|
|
40
|
+
}
|
|
41
|
+
return AGENT_ABBREV[agent.toLowerCase()] || agent.slice(0, 4);
|
|
42
|
+
}
|
|
43
|
+
function getProviderAbbrev(provider) {
|
|
44
|
+
return PROVIDER_ABBREV[provider.toLowerCase()] || provider.slice(0, 2).toUpperCase();
|
|
45
|
+
}
|
|
46
|
+
function formatStatusLine(data) {
|
|
47
|
+
const parts = ["omc"];
|
|
48
|
+
const now = Date.now();
|
|
49
|
+
if (data.activeTasks.length > 0) {
|
|
50
|
+
const tasksToShow = data.activeTasks.slice(0, 3);
|
|
51
|
+
for (const task of tasksToShow) {
|
|
52
|
+
const durationSec = Math.floor((now - task.startedAt) / 1000);
|
|
53
|
+
const agentName = getAgentAbbrev(task.agent);
|
|
54
|
+
const duration = formatDuration(durationSec);
|
|
55
|
+
parts.push(`[${agentName}: ${duration}]`);
|
|
56
|
+
}
|
|
57
|
+
if (data.activeTasks.length > 3) {
|
|
58
|
+
parts.push(`+${data.activeTasks.length - 3}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const providerParts = [];
|
|
62
|
+
const providersToShow = ["deepseek", "zhipu", "minimax"];
|
|
63
|
+
for (const provider of providersToShow) {
|
|
64
|
+
const status = data.providers[provider];
|
|
65
|
+
if (status && (status.active > 0 || data.activeTasks.length > 0)) {
|
|
66
|
+
const abbrev = getProviderAbbrev(provider);
|
|
67
|
+
providerParts.push(`${abbrev}: ${status.active}/${status.limit}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (providerParts.length > 0) {
|
|
71
|
+
parts.push("|");
|
|
72
|
+
parts.push(...providerParts);
|
|
73
|
+
}
|
|
74
|
+
return parts.join(" ");
|
|
75
|
+
}
|
|
76
|
+
function formatEmptyStatusLine() {
|
|
77
|
+
return "omc ready";
|
|
78
|
+
}
|
|
79
|
+
function formatIdleStatusLine(providers) {
|
|
80
|
+
const parts = ["omc ready"];
|
|
81
|
+
const providerParts = [];
|
|
82
|
+
const providersToShow = ["deepseek", "zhipu", "minimax"];
|
|
83
|
+
for (const provider of providersToShow) {
|
|
84
|
+
const status = providers[provider];
|
|
85
|
+
if (status && status.limit > 0) {
|
|
86
|
+
const abbrev = getProviderAbbrev(provider);
|
|
87
|
+
providerParts.push(`${abbrev}: ${status.limit}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (providerParts.length > 0) {
|
|
91
|
+
parts.push("|");
|
|
92
|
+
parts.push(...providerParts);
|
|
93
|
+
}
|
|
94
|
+
return parts.join(" ");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// src/statusline/statusline.ts
|
|
98
|
+
var STATUS_FILE_PATH = join(homedir(), ".claude", "oh-my-claude", "status.json");
|
|
99
|
+
var TIMEOUT_MS = 100;
|
|
100
|
+
function readStatusFile() {
|
|
101
|
+
try {
|
|
102
|
+
if (!existsSync(STATUS_FILE_PATH)) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
const content = readFileSync(STATUS_FILE_PATH, "utf-8");
|
|
106
|
+
const data = JSON.parse(content);
|
|
107
|
+
if (!data || typeof data !== "object") {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
if (data.updatedAt) {
|
|
111
|
+
const updatedAt = new Date(data.updatedAt).getTime();
|
|
112
|
+
const age = Date.now() - updatedAt;
|
|
113
|
+
if (age > 5 * 60 * 1000) {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return data;
|
|
118
|
+
} catch {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
async function main() {
|
|
123
|
+
const timeoutId = setTimeout(() => {
|
|
124
|
+
console.log(formatEmptyStatusLine());
|
|
125
|
+
process.exit(0);
|
|
126
|
+
}, TIMEOUT_MS);
|
|
127
|
+
try {
|
|
128
|
+
let _input = "";
|
|
129
|
+
try {
|
|
130
|
+
_input = readFileSync(0, "utf-8");
|
|
131
|
+
} catch {}
|
|
132
|
+
const statusData = readStatusFile();
|
|
133
|
+
if (!statusData) {
|
|
134
|
+
console.log(formatEmptyStatusLine());
|
|
135
|
+
} else if (statusData.activeTasks.length === 0) {
|
|
136
|
+
console.log(formatIdleStatusLine(statusData.providers));
|
|
137
|
+
} else {
|
|
138
|
+
console.log(formatStatusLine(statusData));
|
|
139
|
+
}
|
|
140
|
+
} catch {
|
|
141
|
+
console.log(formatEmptyStatusLine());
|
|
142
|
+
} finally {
|
|
143
|
+
clearTimeout(timeoutId);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
main();
|
package/package.json
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lgcyaxi/oh-my-claude",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.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",
|
|
13
13
|
"build:mcp": "bun build src/mcp/background-agent-server/server.ts --outdir dist/mcp --target node",
|
|
14
|
-
"build:hooks": "bun build src/hooks/comment-checker.ts src/hooks/todo-continuation.ts --outdir dist/hooks --target node",
|
|
15
|
-
"build:
|
|
14
|
+
"build:hooks": "bun build src/hooks/comment-checker.ts src/hooks/todo-continuation.ts src/hooks/task-notification.ts src/hooks/task-tracker.ts --outdir dist/hooks --target node",
|
|
15
|
+
"build:statusline": "bun build src/statusline/statusline.ts --outdir dist/statusline --target node",
|
|
16
|
+
"build:all": "bun run build && bun run build:mcp && bun run build:hooks && bun run build:statusline",
|
|
16
17
|
"typecheck": "tsc --noEmit",
|
|
17
18
|
"test": "bun test",
|
|
18
19
|
"dev": "bun run --watch src/index.ts",
|
|
@@ -43,7 +44,7 @@
|
|
|
43
44
|
"license": "MIT",
|
|
44
45
|
"repository": {
|
|
45
46
|
"type": "git",
|
|
46
|
-
"url": "https://github.com/lgcyaxi/oh-my-claude"
|
|
47
|
+
"url": "git+https://github.com/lgcyaxi/oh-my-claude.git"
|
|
47
48
|
},
|
|
48
49
|
"homepage": "https://github.com/lgcyaxi/oh-my-claude#readme",
|
|
49
50
|
"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;
|