@lgcyaxi/oh-my-claude 1.0.1 → 1.1.2
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 +30 -0
- package/CLAUDE.md +60 -0
- package/README.md +69 -1
- package/README.zh-CN.md +30 -0
- package/changelog/v1.1.0.md +20 -0
- package/changelog/v1.1.1.md +71 -0
- package/changelog/v1.1.2.md +15 -0
- package/dist/cli.js +132 -11
- 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-814gp2s3.js +7664 -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 +14 -1
- package/dist/mcp/server.js +64 -28
- package/dist/statusline/statusline.js +146 -0
- package/package.json +4 -3
- package/src/cli.ts +150 -10
- 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/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 +88 -2
- package/src/installer/statusline-merger.ts +169 -0
- package/src/mcp/background-agent-server/server.ts +5 -0
- package/src/mcp/background-agent-server/task-manager.ts +53 -0
- package/src/statusline/formatter.ts +164 -0
- package/src/statusline/statusline.ts +103 -0
package/dist/index.js
CHANGED
|
@@ -39,6 +39,10 @@ import {
|
|
|
39
39
|
getMcpServerPath,
|
|
40
40
|
getProviderDetails,
|
|
41
41
|
getProvidersStatus,
|
|
42
|
+
getStatusLineScriptPath,
|
|
43
|
+
init_agent_generator,
|
|
44
|
+
init_agents,
|
|
45
|
+
init_installer,
|
|
42
46
|
install,
|
|
43
47
|
isMcpAgent,
|
|
44
48
|
isProviderConfigured,
|
|
@@ -58,7 +62,10 @@ import {
|
|
|
58
62
|
sisyphusAgent,
|
|
59
63
|
taskAgents,
|
|
60
64
|
uninstall
|
|
61
|
-
} from "./index-
|
|
65
|
+
} from "./index-814gp2s3.js";
|
|
66
|
+
|
|
67
|
+
// src/index.ts
|
|
68
|
+
init_agents();
|
|
62
69
|
// src/providers/deepseek.ts
|
|
63
70
|
var DEEPSEEK_BASE_URL = "https://api.deepseek.com/anthropic";
|
|
64
71
|
var DEEPSEEK_API_KEY_ENV = "DEEPSEEK_API_KEY";
|
|
@@ -80,6 +87,11 @@ function createMiniMaxClient() {
|
|
|
80
87
|
return createAnthropicClientFromEnv("MiniMax", MINIMAX_BASE_URL, MINIMAX_API_KEY_ENV, "MiniMax-M2.1");
|
|
81
88
|
}
|
|
82
89
|
var minimaxClient = createMiniMaxClient();
|
|
90
|
+
// src/generators/index.ts
|
|
91
|
+
init_agent_generator();
|
|
92
|
+
|
|
93
|
+
// src/index.ts
|
|
94
|
+
init_installer();
|
|
83
95
|
export {
|
|
84
96
|
zhipuClient,
|
|
85
97
|
uninstall,
|
|
@@ -103,6 +115,7 @@ export {
|
|
|
103
115
|
isProviderConfigured,
|
|
104
116
|
isMcpAgent,
|
|
105
117
|
install,
|
|
118
|
+
getStatusLineScriptPath,
|
|
106
119
|
getProvidersStatus,
|
|
107
120
|
getProviderDetails,
|
|
108
121
|
getMcpServerPath,
|
package/dist/mcp/server.js
CHANGED
|
@@ -18573,10 +18573,65 @@ function getAgent(name) {
|
|
|
18573
18573
|
}
|
|
18574
18574
|
|
|
18575
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");
|
|
18576
18610
|
var tasks = new Map;
|
|
18577
18611
|
function generateTaskId() {
|
|
18578
18612
|
return `task_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
18579
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
|
+
}
|
|
18580
18635
|
async function launchTask(options) {
|
|
18581
18636
|
const { agentName, categoryName, prompt, systemPrompt } = options;
|
|
18582
18637
|
if (!agentName && !categoryName) {
|
|
@@ -18599,6 +18654,7 @@ async function launchTask(options) {
|
|
|
18599
18654
|
createdAt: Date.now()
|
|
18600
18655
|
};
|
|
18601
18656
|
tasks.set(taskId, task);
|
|
18657
|
+
updateStatusFile();
|
|
18602
18658
|
runTask(task, finalSystemPrompt).catch((error2) => {
|
|
18603
18659
|
console.error(`Task ${taskId} failed:`, error2);
|
|
18604
18660
|
});
|
|
@@ -18608,6 +18664,7 @@ async function runTask(task, systemPrompt) {
|
|
|
18608
18664
|
task.status = "running";
|
|
18609
18665
|
task.startedAt = Date.now();
|
|
18610
18666
|
tasks.set(task.id, task);
|
|
18667
|
+
updateStatusFile();
|
|
18611
18668
|
try {
|
|
18612
18669
|
const messages = [];
|
|
18613
18670
|
if (systemPrompt) {
|
|
@@ -18633,6 +18690,7 @@ async function runTask(task, systemPrompt) {
|
|
|
18633
18690
|
task.result = result;
|
|
18634
18691
|
task.completedAt = Date.now();
|
|
18635
18692
|
tasks.set(task.id, task);
|
|
18693
|
+
updateStatusFile();
|
|
18636
18694
|
} catch (error2) {
|
|
18637
18695
|
if (error2 instanceof FallbackRequiredError) {
|
|
18638
18696
|
task.status = "fallback_required";
|
|
@@ -18649,6 +18707,7 @@ async function runTask(task, systemPrompt) {
|
|
|
18649
18707
|
}
|
|
18650
18708
|
task.completedAt = Date.now();
|
|
18651
18709
|
tasks.set(task.id, task);
|
|
18710
|
+
updateStatusFile();
|
|
18652
18711
|
}
|
|
18653
18712
|
}
|
|
18654
18713
|
function pollTask(taskId) {
|
|
@@ -18672,6 +18731,7 @@ function cancelTask(taskId) {
|
|
|
18672
18731
|
task.status = "cancelled";
|
|
18673
18732
|
task.completedAt = Date.now();
|
|
18674
18733
|
tasks.set(taskId, task);
|
|
18734
|
+
updateStatusFile();
|
|
18675
18735
|
return true;
|
|
18676
18736
|
}
|
|
18677
18737
|
return false;
|
|
@@ -18686,6 +18746,9 @@ function cancelAllTasks() {
|
|
|
18686
18746
|
cancelled++;
|
|
18687
18747
|
}
|
|
18688
18748
|
}
|
|
18749
|
+
if (cancelled > 0) {
|
|
18750
|
+
updateStatusFile();
|
|
18751
|
+
}
|
|
18689
18752
|
return cancelled;
|
|
18690
18753
|
}
|
|
18691
18754
|
function listTasks(options) {
|
|
@@ -18711,34 +18774,6 @@ function cleanupTasks(maxAgeMs = 30 * 60 * 1000) {
|
|
|
18711
18774
|
return cleaned;
|
|
18712
18775
|
}
|
|
18713
18776
|
|
|
18714
|
-
// src/mcp/background-agent-server/concurrency.ts
|
|
18715
|
-
var activeCounts = new Map;
|
|
18716
|
-
var queues = new Map;
|
|
18717
|
-
function getConcurrencyLimit(provider) {
|
|
18718
|
-
const config2 = loadConfig();
|
|
18719
|
-
const perProvider = config2.concurrency.per_provider?.[provider];
|
|
18720
|
-
if (perProvider !== undefined) {
|
|
18721
|
-
return perProvider;
|
|
18722
|
-
}
|
|
18723
|
-
return config2.concurrency.default;
|
|
18724
|
-
}
|
|
18725
|
-
function getActiveCount(provider) {
|
|
18726
|
-
return activeCounts.get(provider) ?? 0;
|
|
18727
|
-
}
|
|
18728
|
-
function getConcurrencyStatus() {
|
|
18729
|
-
const config2 = loadConfig();
|
|
18730
|
-
const status = {};
|
|
18731
|
-
const providers = Object.keys(config2.providers);
|
|
18732
|
-
for (const provider of providers) {
|
|
18733
|
-
status[provider] = {
|
|
18734
|
-
active: getActiveCount(provider),
|
|
18735
|
-
limit: getConcurrencyLimit(provider),
|
|
18736
|
-
queued: queues.get(provider)?.length ?? 0
|
|
18737
|
-
};
|
|
18738
|
-
}
|
|
18739
|
-
return status;
|
|
18740
|
-
}
|
|
18741
|
-
|
|
18742
18777
|
// src/mcp/background-agent-server/server.ts
|
|
18743
18778
|
var tools = [
|
|
18744
18779
|
{
|
|
@@ -19032,6 +19067,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
19032
19067
|
async function main() {
|
|
19033
19068
|
const transport = new StdioServerTransport;
|
|
19034
19069
|
await server.connect(transport);
|
|
19070
|
+
updateStatusFile();
|
|
19035
19071
|
console.error("oh-my-claude Background Agent MCP Server running");
|
|
19036
19072
|
}
|
|
19037
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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lgcyaxi/oh-my-claude",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "Multi-agent orchestration plugin for Claude Code with multi-provider support",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -11,8 +11,9 @@
|
|
|
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",
|
package/src/cli.ts
CHANGED
|
@@ -19,7 +19,7 @@ import { loadConfig } from "./config";
|
|
|
19
19
|
program
|
|
20
20
|
.name("oh-my-claude")
|
|
21
21
|
.description("Multi-agent orchestration plugin for Claude Code")
|
|
22
|
-
.version("1.
|
|
22
|
+
.version("1.1.2");
|
|
23
23
|
|
|
24
24
|
// Install command
|
|
25
25
|
program
|
|
@@ -206,6 +206,7 @@ program
|
|
|
206
206
|
console.log(` ${status.components.agents ? ok("Agent files generated") : fail("Agent files generated")}`);
|
|
207
207
|
console.log(` ${status.components.hooks ? ok("Hooks configured") : fail("Hooks configured")}`);
|
|
208
208
|
console.log(` ${status.components.mcp ? ok("MCP server configured") : fail("MCP server configured")}`);
|
|
209
|
+
console.log(` ${status.components.statusLine ? ok("StatusLine configured") : warn("StatusLine not configured")}`);
|
|
209
210
|
console.log(` ${status.components.config ? ok("Configuration file exists") : fail("Configuration file exists")}`);
|
|
210
211
|
|
|
211
212
|
|
|
@@ -243,11 +244,13 @@ program
|
|
|
243
244
|
"omc-explore",
|
|
244
245
|
"omc-plan",
|
|
245
246
|
"omc-start-work",
|
|
247
|
+
"omc-status",
|
|
246
248
|
// Quick action commands
|
|
247
249
|
"omcx-commit",
|
|
248
250
|
"omcx-implement",
|
|
249
251
|
"omcx-refactor",
|
|
250
252
|
"omcx-docs",
|
|
253
|
+
"omcx-issue",
|
|
251
254
|
];
|
|
252
255
|
console.log(` ${subheader("Agent commands (omc-):")}`);
|
|
253
256
|
for (const cmd of expectedCommands.filter(c => c.startsWith("omc-"))) {
|
|
@@ -265,7 +268,7 @@ program
|
|
|
265
268
|
// Detailed MCP status
|
|
266
269
|
console.log(`\n${header("MCP Server (detailed):")}`);
|
|
267
270
|
try {
|
|
268
|
-
const mcpList = execSync("claude mcp list
|
|
271
|
+
const mcpList = execSync("claude mcp list", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
269
272
|
const omcLine = mcpList.split("\n").find((line: string) => line.includes("oh-my-claude-background"));
|
|
270
273
|
if (omcLine) {
|
|
271
274
|
const isConnected = omcLine.includes("✓ Connected");
|
|
@@ -291,12 +294,43 @@ program
|
|
|
291
294
|
// Hooks detail
|
|
292
295
|
console.log(`\n${header("Hooks (detailed):")}`);
|
|
293
296
|
const hooksDir = join(homedir(), ".claude", "oh-my-claude", "hooks");
|
|
294
|
-
const expectedHooks = ["comment-checker.js", "todo-continuation.js"];
|
|
297
|
+
const expectedHooks = ["comment-checker.js", "todo-continuation.js", "task-notification.js"];
|
|
295
298
|
for (const hook of expectedHooks) {
|
|
296
299
|
const hookPath = join(hooksDir, hook);
|
|
297
300
|
const exists = existsSync(hookPath);
|
|
298
301
|
console.log(` ${exists ? ok(hook) : fail(hook)}`);
|
|
299
302
|
}
|
|
303
|
+
|
|
304
|
+
// StatusLine detail
|
|
305
|
+
console.log(`\n${header("StatusLine (detailed):")}`);
|
|
306
|
+
const statusLineDir = join(homedir(), ".claude", "oh-my-claude", "dist", "statusline");
|
|
307
|
+
const statusLineScript = join(statusLineDir, "statusline.js");
|
|
308
|
+
const statusFileExists = existsSync(statusLineScript);
|
|
309
|
+
console.log(` ${statusFileExists ? ok("statusline.js installed") : fail("statusline.js not installed")}`);
|
|
310
|
+
|
|
311
|
+
try {
|
|
312
|
+
const settingsPath = join(homedir(), ".claude", "settings.json");
|
|
313
|
+
if (existsSync(settingsPath)) {
|
|
314
|
+
const settings = JSON.parse(require("node:fs").readFileSync(settingsPath, "utf-8"));
|
|
315
|
+
if (settings.statusLine) {
|
|
316
|
+
const cmd = settings.statusLine.command || "";
|
|
317
|
+
const isOurs = cmd.includes("oh-my-claude");
|
|
318
|
+
const isWrapper = cmd.includes("statusline-wrapper");
|
|
319
|
+
console.log(` ${ok("StatusLine configured in settings.json")}`);
|
|
320
|
+
if (isWrapper) {
|
|
321
|
+
console.log(` Mode: ${c.yellow}Merged (wrapper)${c.reset}`);
|
|
322
|
+
} else if (isOurs) {
|
|
323
|
+
console.log(` Mode: ${c.green}Direct${c.reset}`);
|
|
324
|
+
} else {
|
|
325
|
+
console.log(` Mode: ${c.cyan}External${c.reset}`);
|
|
326
|
+
}
|
|
327
|
+
} else {
|
|
328
|
+
console.log(` ${warn("StatusLine not configured in settings.json")}`);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
} catch {
|
|
332
|
+
console.log(` ${fail("Failed to read settings.json")}`);
|
|
333
|
+
}
|
|
300
334
|
}
|
|
301
335
|
|
|
302
336
|
// Check providers
|
|
@@ -423,8 +457,10 @@ program
|
|
|
423
457
|
let latestVersion = "unknown";
|
|
424
458
|
try {
|
|
425
459
|
console.log(`${dimText("Checking npm registry for latest version...")}`);
|
|
426
|
-
|
|
460
|
+
// Use stdio: 'pipe' to suppress stderr instead of shell redirection (Windows compatible)
|
|
461
|
+
const npmInfo = execSync(`npm view ${PACKAGE_NAME} version`, {
|
|
427
462
|
encoding: "utf-8",
|
|
463
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
428
464
|
}).trim();
|
|
429
465
|
latestVersion = npmInfo;
|
|
430
466
|
console.log(`Latest version: ${c.cyan}${latestVersion}${c.reset}\n`);
|
|
@@ -455,12 +491,16 @@ program
|
|
|
455
491
|
console.log(header("Updating oh-my-claude...\n"));
|
|
456
492
|
|
|
457
493
|
try {
|
|
458
|
-
// Step 1: Clear npx cache for the package
|
|
494
|
+
// Step 1: Clear npx cache for the package (optional, may fail on some systems)
|
|
459
495
|
console.log(`${dimText("Clearing npx cache...")}`);
|
|
460
496
|
try {
|
|
461
|
-
|
|
497
|
+
// Use stdio: 'pipe' for Windows compatibility instead of shell redirection
|
|
498
|
+
execSync(`npx --yes clear-npx-cache`, {
|
|
499
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
500
|
+
timeout: 10000
|
|
501
|
+
});
|
|
462
502
|
} catch {
|
|
463
|
-
// Ignore errors - cache clear is optional
|
|
503
|
+
// Ignore errors - cache clear is optional and may not be available
|
|
464
504
|
}
|
|
465
505
|
|
|
466
506
|
// Step 2: Install latest version via npx
|
|
@@ -487,6 +527,106 @@ program
|
|
|
487
527
|
}
|
|
488
528
|
});
|
|
489
529
|
|
|
530
|
+
// StatusLine command
|
|
531
|
+
program
|
|
532
|
+
.command("statusline")
|
|
533
|
+
.description("Manage statusline integration")
|
|
534
|
+
.option("--enable", "Enable statusline")
|
|
535
|
+
.option("--disable", "Disable statusline")
|
|
536
|
+
.option("--status", "Show current statusline configuration")
|
|
537
|
+
.action((options) => {
|
|
538
|
+
const { readFileSync, existsSync } = require("node:fs");
|
|
539
|
+
const { join } = require("node:path");
|
|
540
|
+
const { homedir } = require("node:os");
|
|
541
|
+
|
|
542
|
+
// Color helpers
|
|
543
|
+
const useColor = process.stdout.isTTY;
|
|
544
|
+
const c = {
|
|
545
|
+
reset: useColor ? "\x1b[0m" : "",
|
|
546
|
+
bold: useColor ? "\x1b[1m" : "",
|
|
547
|
+
dim: useColor ? "\x1b[2m" : "",
|
|
548
|
+
green: useColor ? "\x1b[32m" : "",
|
|
549
|
+
red: useColor ? "\x1b[31m" : "",
|
|
550
|
+
yellow: useColor ? "\x1b[33m" : "",
|
|
551
|
+
cyan: useColor ? "\x1b[36m" : "",
|
|
552
|
+
};
|
|
553
|
+
|
|
554
|
+
const ok = (text: string) => `${c.green}+${c.reset} ${text}`;
|
|
555
|
+
const fail = (text: string) => `${c.red}x${c.reset} ${text}`;
|
|
556
|
+
const warn = (text: string) => `${c.yellow}!${c.reset} ${text}`;
|
|
557
|
+
|
|
558
|
+
const settingsPath = join(homedir(), ".claude", "settings.json");
|
|
559
|
+
|
|
560
|
+
if (options.status || (!options.enable && !options.disable)) {
|
|
561
|
+
// Show status
|
|
562
|
+
console.log(`${c.bold}StatusLine Status${c.reset}\n`);
|
|
563
|
+
|
|
564
|
+
if (!existsSync(settingsPath)) {
|
|
565
|
+
console.log(fail("settings.json not found"));
|
|
566
|
+
process.exit(1);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
try {
|
|
570
|
+
const settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
571
|
+
|
|
572
|
+
if (!settings.statusLine) {
|
|
573
|
+
console.log(fail("StatusLine not configured"));
|
|
574
|
+
console.log(`\nRun ${c.cyan}oh-my-claude statusline --enable${c.reset} to enable.`);
|
|
575
|
+
} else {
|
|
576
|
+
const cmd = settings.statusLine.command || "";
|
|
577
|
+
const isOurs = cmd.includes("oh-my-claude");
|
|
578
|
+
const isWrapper = cmd.includes("statusline-wrapper");
|
|
579
|
+
|
|
580
|
+
console.log(ok("StatusLine configured"));
|
|
581
|
+
console.log(` Command: ${c.dim}${cmd}${c.reset}`);
|
|
582
|
+
|
|
583
|
+
if (isWrapper) {
|
|
584
|
+
console.log(` Mode: ${c.yellow}Merged (wrapper)${c.reset}`);
|
|
585
|
+
} else if (isOurs) {
|
|
586
|
+
console.log(` Mode: ${c.green}Direct${c.reset}`);
|
|
587
|
+
} else {
|
|
588
|
+
console.log(` Mode: ${c.cyan}External${c.reset}`);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
} catch (error) {
|
|
592
|
+
console.log(fail(`Failed to read settings: ${error}`));
|
|
593
|
+
process.exit(1);
|
|
594
|
+
}
|
|
595
|
+
} else if (options.enable) {
|
|
596
|
+
// Enable statusline
|
|
597
|
+
const { installStatusLine } = require("./installer/settings-merger");
|
|
598
|
+
const { getStatusLineScriptPath } = require("./installer");
|
|
599
|
+
|
|
600
|
+
try {
|
|
601
|
+
const result = installStatusLine(getStatusLineScriptPath());
|
|
602
|
+
if (result.installed) {
|
|
603
|
+
console.log(ok("StatusLine enabled"));
|
|
604
|
+
if (result.wrapperCreated) {
|
|
605
|
+
console.log(warn("Wrapper created to merge with existing statusLine"));
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
} catch (error) {
|
|
609
|
+
console.log(fail(`Failed to enable statusline: ${error}`));
|
|
610
|
+
process.exit(1);
|
|
611
|
+
}
|
|
612
|
+
} else if (options.disable) {
|
|
613
|
+
// Disable statusline
|
|
614
|
+
const { uninstallStatusLine } = require("./installer/settings-merger");
|
|
615
|
+
|
|
616
|
+
try {
|
|
617
|
+
const result = uninstallStatusLine();
|
|
618
|
+
if (result) {
|
|
619
|
+
console.log(ok("StatusLine disabled"));
|
|
620
|
+
} else {
|
|
621
|
+
console.log(warn("StatusLine was not configured"));
|
|
622
|
+
}
|
|
623
|
+
} catch (error) {
|
|
624
|
+
console.log(fail(`Failed to disable statusline: ${error}`));
|
|
625
|
+
process.exit(1);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
});
|
|
629
|
+
|
|
490
630
|
// Setup MCP command
|
|
491
631
|
program
|
|
492
632
|
.command("setup-mcp")
|
|
@@ -598,7 +738,7 @@ program
|
|
|
598
738
|
if (installThinking) {
|
|
599
739
|
console.log(`${c.bold}Anthropic Official:${c.reset}`);
|
|
600
740
|
try {
|
|
601
|
-
const mcpList = execSync("claude mcp list
|
|
741
|
+
const mcpList = execSync("claude mcp list", { encoding: "utf-8" });
|
|
602
742
|
if (mcpList.includes("sequential-thinking")) {
|
|
603
743
|
console.log(` ${ok("sequential-thinking already installed")}`);
|
|
604
744
|
} else {
|
|
@@ -621,7 +761,7 @@ program
|
|
|
621
761
|
} else {
|
|
622
762
|
try {
|
|
623
763
|
// Check if already installed
|
|
624
|
-
const mcpList = execSync("claude mcp list
|
|
764
|
+
const mcpList = execSync("claude mcp list", { encoding: "utf-8" });
|
|
625
765
|
if (mcpList.includes("MiniMax")) {
|
|
626
766
|
console.log(` ${ok("MiniMax already installed")}`);
|
|
627
767
|
} else {
|
|
@@ -648,7 +788,7 @@ program
|
|
|
648
788
|
for (const [key, server] of glmServers) {
|
|
649
789
|
try {
|
|
650
790
|
// Check if already installed
|
|
651
|
-
const mcpList = execSync("claude mcp list
|
|
791
|
+
const mcpList = execSync("claude mcp list", { encoding: "utf-8" });
|
|
652
792
|
if (mcpList.includes(server.name)) {
|
|
653
793
|
console.log(` ${ok(`${server.name} already installed`)}`);
|
|
654
794
|
} else {
|
package/src/commands/index.ts
CHANGED
|
@@ -18,6 +18,7 @@ export const agentCommands = [
|
|
|
18
18
|
"omc-explore",
|
|
19
19
|
"omc-plan",
|
|
20
20
|
"omc-start-work",
|
|
21
|
+
"omc-status",
|
|
21
22
|
] as const;
|
|
22
23
|
|
|
23
24
|
// Quick action commands (omcx- prefix)
|
|
@@ -26,10 +27,16 @@ export const actionCommands = [
|
|
|
26
27
|
"omcx-implement",
|
|
27
28
|
"omcx-refactor",
|
|
28
29
|
"omcx-docs",
|
|
30
|
+
"omcx-issue",
|
|
31
|
+
] as const;
|
|
32
|
+
|
|
33
|
+
// Special mode commands
|
|
34
|
+
export const modeCommands = [
|
|
35
|
+
"ulw", // Ultrawork mode - work until done
|
|
29
36
|
] as const;
|
|
30
37
|
|
|
31
38
|
// All commands
|
|
32
|
-
export const commands = [...agentCommands, ...actionCommands] as const;
|
|
39
|
+
export const commands = [...agentCommands, ...actionCommands, ...modeCommands] as const;
|
|
33
40
|
|
|
34
41
|
export type CommandName = typeof commands[number];
|
|
35
42
|
|