@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.
Files changed (49) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +43 -0
  2. package/CHANGELOG.md +53 -0
  3. package/CLAUDE.md +60 -0
  4. package/README.md +97 -12
  5. package/README.zh-CN.md +58 -11
  6. package/bin/oh-my-claude.js +4 -2
  7. package/changelog/v1.0.0.md +28 -0
  8. package/changelog/v1.0.1.md +28 -0
  9. package/changelog/v1.1.0.md +20 -0
  10. package/changelog/v1.1.1.md +71 -0
  11. package/dist/cli.js +213 -4
  12. package/dist/hooks/comment-checker.js +1 -1
  13. package/dist/hooks/task-notification.js +124 -0
  14. package/dist/hooks/task-tracker.js +144 -0
  15. package/dist/index-1dv6t98k.js +7654 -0
  16. package/dist/index-5ars1tn4.js +7348 -0
  17. package/dist/index-d79fk9ah.js +7350 -0
  18. package/dist/index-hzm01rkh.js +7654 -0
  19. package/dist/index-qrbfj4cd.js +7664 -0
  20. package/dist/index-ypyx3ye0.js +7349 -0
  21. package/dist/index.js +24 -1
  22. package/dist/mcp/server.js +202 -45
  23. package/dist/statusline/statusline.js +146 -0
  24. package/package.json +6 -5
  25. package/src/agents/document-writer.ts +5 -0
  26. package/src/agents/explore.ts +5 -0
  27. package/src/agents/frontend-ui-ux.ts +5 -0
  28. package/src/agents/librarian.ts +5 -0
  29. package/src/agents/oracle.ts +5 -0
  30. package/src/agents/types.ts +11 -0
  31. package/src/cli.ts +261 -2
  32. package/src/commands/index.ts +8 -1
  33. package/src/commands/omc-status.md +71 -0
  34. package/src/commands/omcx-issue.md +175 -0
  35. package/src/commands/ulw.md +144 -0
  36. package/src/config/loader.ts +73 -0
  37. package/src/config/schema.ts +25 -2
  38. package/src/generators/agent-generator.ts +17 -1
  39. package/src/hooks/comment-checker.ts +2 -2
  40. package/src/hooks/task-notification.ts +206 -0
  41. package/src/hooks/task-tracker.ts +252 -0
  42. package/src/installer/index.ts +55 -4
  43. package/src/installer/settings-merger.ts +86 -0
  44. package/src/installer/statusline-merger.ts +169 -0
  45. package/src/mcp/background-agent-server/server.ts +11 -2
  46. package/src/mcp/background-agent-server/task-manager.ts +83 -4
  47. package/src/providers/router.ts +28 -0
  48. package/src/statusline/formatter.ts +164 -0
  49. 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-ejmtq3zc.js";
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
  };
@@ -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
- task.status = "failed";
18533
- task.error = error2 instanceof Error ? error2.message : String(error2);
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.0.0",
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": "./bin/oh-my-claude.js"
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:all": "bun run build && bun run build:mcp && bun run build:hooks",
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;