@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
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# /omc-status
|
|
2
|
+
|
|
3
|
+
Display status dashboard for oh-my-claude MCP background agents.
|
|
4
|
+
|
|
5
|
+
## Instructions
|
|
6
|
+
|
|
7
|
+
The user wants to see the current status of MCP background agents. Display a formatted ASCII dashboard showing:
|
|
8
|
+
|
|
9
|
+
1. **Active Tasks** - Currently running background tasks
|
|
10
|
+
2. **Provider Concurrency** - Usage bars for each provider
|
|
11
|
+
3. **Recent Tasks** - Last 5 completed/failed tasks
|
|
12
|
+
|
|
13
|
+
**Step 1: Get system status**
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
Use mcp__oh-my-claude-background__get_status to get:
|
|
17
|
+
- Provider configuration (which have API keys)
|
|
18
|
+
- Concurrency limits per provider
|
|
19
|
+
- Current active task counts
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Step 2: Get task list**
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
Use mcp__oh-my-claude-background__list_tasks with:
|
|
26
|
+
- limit: 20
|
|
27
|
+
(Gets recent tasks including running, completed, failed, fallback_required)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Step 3: Display dashboard**
|
|
31
|
+
|
|
32
|
+
Format the output as an ASCII dashboard:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
oh-my-claude Status Dashboard
|
|
36
|
+
==============================
|
|
37
|
+
|
|
38
|
+
ACTIVE TASKS (count)
|
|
39
|
+
[agent] task_id... Provider [progress] status duration
|
|
40
|
+
|
|
41
|
+
PROVIDER CONCURRENCY
|
|
42
|
+
Provider: [bar] active/limit active, queued queued
|
|
43
|
+
|
|
44
|
+
RECENT TASKS (last 5)
|
|
45
|
+
[status] task_id agent time_ago
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**ASCII Progress Bar Helper:**
|
|
49
|
+
|
|
50
|
+
For concurrency bars, use:
|
|
51
|
+
- Full blocks: active tasks
|
|
52
|
+
- Empty blocks: remaining capacity
|
|
53
|
+
- 10 character width total
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
Example: 4/10 active -> "4/10 active"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Status indicators:**
|
|
60
|
+
- `running` - Task is executing
|
|
61
|
+
- `completed` - Task finished successfully
|
|
62
|
+
- `failed` - Task encountered an error
|
|
63
|
+
- `fallback_required` - Provider API key not configured
|
|
64
|
+
|
|
65
|
+
**If no tasks:**
|
|
66
|
+
Display a message that no background tasks have been run yet, and suggest using `/omc-oracle` or other agent commands.
|
|
67
|
+
|
|
68
|
+
**If MCP server not available:**
|
|
69
|
+
Display an error message suggesting to run `oh-my-claude doctor` to check configuration.
|
|
70
|
+
|
|
71
|
+
Now gather the status and display the dashboard.
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# /omcx-issue
|
|
2
|
+
|
|
3
|
+
Report a bug to oh-my-claude GitHub Issues.
|
|
4
|
+
|
|
5
|
+
## Instructions
|
|
6
|
+
|
|
7
|
+
Create a bug report for the oh-my-claude project with auto-collected environment diagnostics.
|
|
8
|
+
|
|
9
|
+
### Prerequisites Check
|
|
10
|
+
|
|
11
|
+
**FIRST**: Verify GitHub MCP is available by calling:
|
|
12
|
+
```
|
|
13
|
+
mcp__plugin_github_github__get_me()
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
If this fails or the tool is not available, display:
|
|
17
|
+
```
|
|
18
|
+
GitHub MCP is not available. To report issues, you need the GitHub MCP plugin.
|
|
19
|
+
|
|
20
|
+
Install it with:
|
|
21
|
+
claude mcp add github -- npx -y @modelcontextprotocol/server-github
|
|
22
|
+
|
|
23
|
+
Or report manually at: https://github.com/lgcyaxi/oh-my-claude/issues/new
|
|
24
|
+
```
|
|
25
|
+
Then stop execution.
|
|
26
|
+
|
|
27
|
+
### Workflow
|
|
28
|
+
|
|
29
|
+
1. **Collect Environment Diagnostics**
|
|
30
|
+
|
|
31
|
+
Run these commands to gather system info:
|
|
32
|
+
```bash
|
|
33
|
+
# oh-my-claude version
|
|
34
|
+
oh-my-claude --version 2>/dev/null || npx @lgcyaxi/oh-my-claude --version 2>/dev/null || echo "unknown"
|
|
35
|
+
|
|
36
|
+
# OS info
|
|
37
|
+
uname -s -r
|
|
38
|
+
|
|
39
|
+
# Node.js version
|
|
40
|
+
node --version
|
|
41
|
+
|
|
42
|
+
# Bun version (optional)
|
|
43
|
+
bun --version 2>/dev/null || echo "not installed"
|
|
44
|
+
|
|
45
|
+
# Installation type detection
|
|
46
|
+
which oh-my-claude >/dev/null 2>&1 && echo "global" || echo "npx"
|
|
47
|
+
|
|
48
|
+
# Doctor output (sanitized)
|
|
49
|
+
oh-my-claude doctor --no-color 2>/dev/null || npx @lgcyaxi/oh-my-claude doctor --no-color 2>/dev/null || echo "doctor command failed"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
2. **Gather Issue Details from User**
|
|
53
|
+
|
|
54
|
+
Ask the user for:
|
|
55
|
+
- Brief summary of the issue (1 line - used as title)
|
|
56
|
+
- What happened (actual behavior)
|
|
57
|
+
- What was expected
|
|
58
|
+
- Steps to reproduce (optional)
|
|
59
|
+
- Any additional context
|
|
60
|
+
|
|
61
|
+
3. **Generate Issue Draft**
|
|
62
|
+
|
|
63
|
+
Format the issue body:
|
|
64
|
+
```markdown
|
|
65
|
+
## Description
|
|
66
|
+
[User's brief summary]
|
|
67
|
+
|
|
68
|
+
## What happened
|
|
69
|
+
[User's actual behavior description]
|
|
70
|
+
|
|
71
|
+
## Expected behavior
|
|
72
|
+
[User's expected behavior]
|
|
73
|
+
|
|
74
|
+
## Steps to reproduce
|
|
75
|
+
[User's steps, or "N/A" if not provided]
|
|
76
|
+
|
|
77
|
+
## Environment
|
|
78
|
+
|
|
79
|
+
| Component | Value |
|
|
80
|
+
|-----------|-------|
|
|
81
|
+
| oh-my-claude | [version] |
|
|
82
|
+
| OS | [os info] |
|
|
83
|
+
| Node.js | [version] |
|
|
84
|
+
| Bun | [version or "not installed"] |
|
|
85
|
+
| Installation | [global/npx] |
|
|
86
|
+
|
|
87
|
+
<details>
|
|
88
|
+
<summary>Doctor output</summary>
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
[doctor command output - sanitized, no API keys]
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
</details>
|
|
95
|
+
|
|
96
|
+
## Additional context
|
|
97
|
+
[User's additional context, or "None"]
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
*Reported via `/omcx-issue` command*
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
4. **Show Draft and Request Confirmation**
|
|
104
|
+
|
|
105
|
+
Display the complete issue to the user:
|
|
106
|
+
```
|
|
107
|
+
Issue Draft
|
|
108
|
+
-----------
|
|
109
|
+
|
|
110
|
+
Title: [Bug] [summary]
|
|
111
|
+
|
|
112
|
+
[issue body]
|
|
113
|
+
|
|
114
|
+
-----------
|
|
115
|
+
|
|
116
|
+
Does this look correct?
|
|
117
|
+
- "yes" or "submit" to create the issue
|
|
118
|
+
- "edit" to modify before submitting
|
|
119
|
+
- "cancel" to abort
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
5. **Submit Issue**
|
|
123
|
+
|
|
124
|
+
On confirmation, create the issue:
|
|
125
|
+
```
|
|
126
|
+
mcp__plugin_github_github__issue_write(
|
|
127
|
+
method: "create",
|
|
128
|
+
owner: "lgcyaxi",
|
|
129
|
+
repo: "oh-my-claude",
|
|
130
|
+
title: "[Bug] <user's summary>",
|
|
131
|
+
body: "<generated body>",
|
|
132
|
+
labels: ["bug", "user-reported"]
|
|
133
|
+
)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
6. **Report Result**
|
|
137
|
+
|
|
138
|
+
On success:
|
|
139
|
+
```
|
|
140
|
+
Issue created successfully!
|
|
141
|
+
|
|
142
|
+
View at: https://github.com/lgcyaxi/oh-my-claude/issues/<number>
|
|
143
|
+
|
|
144
|
+
Thank you for helping improve oh-my-claude!
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
On failure:
|
|
148
|
+
```
|
|
149
|
+
Failed to create issue: <error message>
|
|
150
|
+
|
|
151
|
+
You can report manually at: https://github.com/lgcyaxi/oh-my-claude/issues/new
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Important Notes
|
|
155
|
+
|
|
156
|
+
- Always sanitize doctor output - never include API keys
|
|
157
|
+
- Use `npx @lgcyaxi/oh-my-claude` as fallback when global command unavailable
|
|
158
|
+
- Labels "bug" and "user-reported" are applied automatically
|
|
159
|
+
- Issue title format: `[Bug] <user's summary>`
|
|
160
|
+
|
|
161
|
+
### Arguments
|
|
162
|
+
|
|
163
|
+
`/omcx-issue [quick description]`
|
|
164
|
+
|
|
165
|
+
- If description provided: Use as the issue summary
|
|
166
|
+
- If no description: Prompt user for issue summary
|
|
167
|
+
|
|
168
|
+
### Examples
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
/omcx-issue commands not installing properly
|
|
172
|
+
/omcx-issue MCP server fails to connect
|
|
173
|
+
/omcx-issue oracle agent returns empty response
|
|
174
|
+
/omcx-issue
|
|
175
|
+
```
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# /ulw - Ultrawork Mode
|
|
2
|
+
|
|
3
|
+
**ULTRAWORK MODE ACTIVATED!**
|
|
4
|
+
|
|
5
|
+
You are now operating in **MAXIMUM PERFORMANCE MODE**. All restrictions on effort, thoroughness, and completion are lifted. You will work relentlessly until the task is FULLY complete.
|
|
6
|
+
|
|
7
|
+
## Core Mandate
|
|
8
|
+
|
|
9
|
+
**ZERO PARTIAL COMPLETION. 100% DELIVERY OR NOTHING.**
|
|
10
|
+
|
|
11
|
+
This is not a suggestion. This is your operating contract:
|
|
12
|
+
- NO scope reduction
|
|
13
|
+
- NO mock versions or placeholders
|
|
14
|
+
- NO skipped requirements
|
|
15
|
+
- NO "good enough" stopping points
|
|
16
|
+
- NO deleting tests to make builds pass
|
|
17
|
+
- NO leaving code in broken state
|
|
18
|
+
|
|
19
|
+
**60-80% completion is FAILURE. Only 100% is acceptable.**
|
|
20
|
+
|
|
21
|
+
## Agent Utilization (MANDATORY)
|
|
22
|
+
|
|
23
|
+
You MUST leverage ALL available agents to their fullest potential:
|
|
24
|
+
|
|
25
|
+
### Sync Agents (Task tool)
|
|
26
|
+
| Agent | Use For | When |
|
|
27
|
+
|-------|---------|------|
|
|
28
|
+
| **Explore** | Codebase search, pattern finding | FIRST - before any implementation |
|
|
29
|
+
| **Claude-Reviewer** | Code review, verification | AFTER - every significant change |
|
|
30
|
+
| **Claude-Scout** | Fast exploration, quick checks | PARALLEL - multiple searches |
|
|
31
|
+
|
|
32
|
+
### Async Agents (MCP background)
|
|
33
|
+
| Agent | Use For | When |
|
|
34
|
+
|-------|---------|------|
|
|
35
|
+
| **Oracle** | Architecture decisions, deep reasoning | When stuck or uncertain |
|
|
36
|
+
| **Librarian** | External docs, library research | Before using unfamiliar APIs |
|
|
37
|
+
| **Frontend-UI-UX** | Visual/UI implementation | All frontend work |
|
|
38
|
+
| **Document-Writer** | Documentation, README | After implementation |
|
|
39
|
+
|
|
40
|
+
**FIRE PARALLEL AGENTS AGGRESSIVELY.** Launch 5-10+ background tasks if needed. Don't wait - collect results when ready.
|
|
41
|
+
|
|
42
|
+
## Execution Protocol
|
|
43
|
+
|
|
44
|
+
### 1. Immediate Planning
|
|
45
|
+
```
|
|
46
|
+
FIRST ACTION: Create comprehensive TodoWrite list
|
|
47
|
+
- Break down EVERY step
|
|
48
|
+
- Include verification steps
|
|
49
|
+
- Include documentation steps
|
|
50
|
+
- NO hidden work - everything tracked
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 2. Aggressive Parallelization
|
|
54
|
+
```
|
|
55
|
+
LAUNCH PARALLEL:
|
|
56
|
+
- Explore agents for codebase context
|
|
57
|
+
- Librarian for external documentation
|
|
58
|
+
- Multiple search paths simultaneously
|
|
59
|
+
|
|
60
|
+
DO NOT wait for one search to complete before starting another.
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 3. Implementation with Verification
|
|
64
|
+
```
|
|
65
|
+
FOR EACH CHANGE:
|
|
66
|
+
1. Implement the change
|
|
67
|
+
2. Run diagnostics immediately
|
|
68
|
+
3. Run tests if applicable
|
|
69
|
+
4. Mark todo complete ONLY after verification passes
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### 4. Zero-Tolerance Quality Gates
|
|
73
|
+
|
|
74
|
+
**Before marking ANY task complete:**
|
|
75
|
+
- [ ] Code compiles/transpiles without errors
|
|
76
|
+
- [ ] Linter passes (no suppressions added)
|
|
77
|
+
- [ ] Tests pass (no tests deleted or skipped)
|
|
78
|
+
- [ ] Build succeeds (if applicable)
|
|
79
|
+
- [ ] Functionality verified with actual execution
|
|
80
|
+
|
|
81
|
+
**If verification fails:**
|
|
82
|
+
1. Fix immediately
|
|
83
|
+
2. Re-verify
|
|
84
|
+
3. After 3 failures: STOP, consult Oracle, then ask user
|
|
85
|
+
|
|
86
|
+
### 5. Completion Criteria
|
|
87
|
+
|
|
88
|
+
The task is NOT complete until:
|
|
89
|
+
- [ ] ALL todo items marked done
|
|
90
|
+
- [ ] ALL verification gates passed
|
|
91
|
+
- [ ] ALL user requirements addressed
|
|
92
|
+
- [ ] Evidence collected for each completion claim
|
|
93
|
+
|
|
94
|
+
## Anti-Patterns (BLOCKED)
|
|
95
|
+
|
|
96
|
+
| Violation | Consequence |
|
|
97
|
+
|-----------|-------------|
|
|
98
|
+
| Claiming "done" without verification | REJECTED - redo with proof |
|
|
99
|
+
| Reducing scope without user approval | REJECTED - implement full scope |
|
|
100
|
+
| Skipping agent delegation | REJECTED - suboptimal execution |
|
|
101
|
+
| Batch-completing todos | REJECTED - defeats tracking |
|
|
102
|
+
| Leaving broken code | REJECTED - fix before proceeding |
|
|
103
|
+
|
|
104
|
+
## Work Until Done Protocol
|
|
105
|
+
|
|
106
|
+
**If session ends with incomplete todos:**
|
|
107
|
+
1. Boulder state persists your progress
|
|
108
|
+
2. Next session: `/omc-start-work` to resume
|
|
109
|
+
3. Continue from first unchecked item
|
|
110
|
+
4. NEVER restart from scratch
|
|
111
|
+
|
|
112
|
+
**If you hit a blocker:**
|
|
113
|
+
1. Document the blocker explicitly
|
|
114
|
+
2. Consult Oracle for architecture advice
|
|
115
|
+
3. If still blocked: Ask user for guidance
|
|
116
|
+
4. NEVER silently give up
|
|
117
|
+
|
|
118
|
+
## Response Format
|
|
119
|
+
|
|
120
|
+
Start your response with:
|
|
121
|
+
```
|
|
122
|
+
**ULTRAWORK MODE ENABLED!**
|
|
123
|
+
|
|
124
|
+
[Immediately begin planning/executing - no preamble]
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Then execute with maximum intensity until COMPLETE.
|
|
128
|
+
|
|
129
|
+
## Arguments
|
|
130
|
+
|
|
131
|
+
`/ulw [task description]`
|
|
132
|
+
|
|
133
|
+
- Provide the task you want completed
|
|
134
|
+
- Or use after `/omc-plan` to execute an existing plan with ultrawork intensity
|
|
135
|
+
|
|
136
|
+
## Examples
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
/ulw implement the authentication system from the plan
|
|
140
|
+
/ulw fix all type errors in the codebase
|
|
141
|
+
/ulw add comprehensive test coverage for the API
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**NOW EXECUTE. NO HALF MEASURES. WORK UNTIL DONE.**
|
|
@@ -31,7 +31,7 @@ interface ToolInput {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
interface HookResponse {
|
|
34
|
-
decision: "approve" | "block"
|
|
34
|
+
decision: "approve" | "block";
|
|
35
35
|
reason?: string;
|
|
36
36
|
}
|
|
37
37
|
|
|
@@ -125,7 +125,7 @@ async function main() {
|
|
|
125
125
|
|
|
126
126
|
// Only check Edit and Write tools
|
|
127
127
|
if (toolInput.tool !== "Edit" && toolInput.tool !== "Write") {
|
|
128
|
-
const response: HookResponse = { decision: "
|
|
128
|
+
const response: HookResponse = { decision: "approve" };
|
|
129
129
|
console.log(JSON.stringify(response));
|
|
130
130
|
return;
|
|
131
131
|
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Task Notification Hook (PostToolUse)
|
|
4
|
+
*
|
|
5
|
+
* Monitors for MCP background task completions and provides
|
|
6
|
+
* notifications in the Claude Code output.
|
|
7
|
+
*
|
|
8
|
+
* Usage in settings.json:
|
|
9
|
+
* {
|
|
10
|
+
* "hooks": {
|
|
11
|
+
* "PostToolUse": [{
|
|
12
|
+
* "matcher": "mcp__oh-my-claude-background__.*",
|
|
13
|
+
* "hooks": [{
|
|
14
|
+
* "type": "command",
|
|
15
|
+
* "command": "node ~/.claude/oh-my-claude/hooks/task-notification.js"
|
|
16
|
+
* }]
|
|
17
|
+
* }]
|
|
18
|
+
* }
|
|
19
|
+
* }
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { readFileSync, existsSync, writeFileSync, mkdirSync } from "node:fs";
|
|
23
|
+
import { join, dirname } from "node:path";
|
|
24
|
+
import { homedir } from "node:os";
|
|
25
|
+
|
|
26
|
+
interface PostToolUseInput {
|
|
27
|
+
tool: string;
|
|
28
|
+
tool_input?: Record<string, unknown>;
|
|
29
|
+
tool_output?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface HookResponse {
|
|
33
|
+
decision: "approve";
|
|
34
|
+
hookSpecificOutput?: {
|
|
35
|
+
hookEventName: "PostToolUse";
|
|
36
|
+
additionalContext?: string;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// File to track notified task IDs (prevent duplicate notifications)
|
|
41
|
+
const NOTIFIED_FILE_PATH = join(homedir(), ".claude", "oh-my-claude", "notified-tasks.json");
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Load list of already-notified task IDs
|
|
45
|
+
*/
|
|
46
|
+
function loadNotifiedTasks(): Set<string> {
|
|
47
|
+
try {
|
|
48
|
+
if (!existsSync(NOTIFIED_FILE_PATH)) {
|
|
49
|
+
return new Set();
|
|
50
|
+
}
|
|
51
|
+
const content = readFileSync(NOTIFIED_FILE_PATH, "utf-8");
|
|
52
|
+
const data = JSON.parse(content);
|
|
53
|
+
// Clean up old entries (older than 1 hour)
|
|
54
|
+
const oneHourAgo = Date.now() - 60 * 60 * 1000;
|
|
55
|
+
const filtered = Object.entries(data)
|
|
56
|
+
.filter(([_, timestamp]) => (timestamp as number) > oneHourAgo)
|
|
57
|
+
.map(([id]) => id);
|
|
58
|
+
return new Set(filtered);
|
|
59
|
+
} catch {
|
|
60
|
+
return new Set();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Save notified task ID
|
|
66
|
+
*/
|
|
67
|
+
function saveNotifiedTask(taskId: string): void {
|
|
68
|
+
try {
|
|
69
|
+
const dir = dirname(NOTIFIED_FILE_PATH);
|
|
70
|
+
if (!existsSync(dir)) {
|
|
71
|
+
mkdirSync(dir, { recursive: true });
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let data: Record<string, number> = {};
|
|
75
|
+
if (existsSync(NOTIFIED_FILE_PATH)) {
|
|
76
|
+
try {
|
|
77
|
+
data = JSON.parse(readFileSync(NOTIFIED_FILE_PATH, "utf-8"));
|
|
78
|
+
} catch {
|
|
79
|
+
data = {};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Clean up old entries
|
|
84
|
+
const oneHourAgo = Date.now() - 60 * 60 * 1000;
|
|
85
|
+
for (const [id, timestamp] of Object.entries(data)) {
|
|
86
|
+
if (timestamp < oneHourAgo) {
|
|
87
|
+
delete data[id];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
data[taskId] = Date.now();
|
|
92
|
+
writeFileSync(NOTIFIED_FILE_PATH, JSON.stringify(data));
|
|
93
|
+
} catch {
|
|
94
|
+
// Silently fail
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Format duration for display
|
|
100
|
+
*/
|
|
101
|
+
function formatDuration(ms: number): string {
|
|
102
|
+
const seconds = Math.floor(ms / 1000);
|
|
103
|
+
if (seconds < 60) {
|
|
104
|
+
return `${seconds}s`;
|
|
105
|
+
}
|
|
106
|
+
const minutes = Math.floor(seconds / 60);
|
|
107
|
+
const remainingSeconds = seconds % 60;
|
|
108
|
+
return `${minutes}m ${remainingSeconds}s`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function main() {
|
|
112
|
+
// Read input from stdin
|
|
113
|
+
let inputData = "";
|
|
114
|
+
try {
|
|
115
|
+
inputData = readFileSync(0, "utf-8");
|
|
116
|
+
} catch {
|
|
117
|
+
console.log(JSON.stringify({ decision: "approve" }));
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (!inputData.trim()) {
|
|
122
|
+
console.log(JSON.stringify({ decision: "approve" }));
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
let toolInput: PostToolUseInput;
|
|
127
|
+
try {
|
|
128
|
+
toolInput = JSON.parse(inputData);
|
|
129
|
+
} catch {
|
|
130
|
+
console.log(JSON.stringify({ decision: "approve" }));
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Only process MCP tool outputs
|
|
135
|
+
if (!toolInput.tool?.includes("oh-my-claude-background")) {
|
|
136
|
+
console.log(JSON.stringify({ decision: "approve" }));
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Check for poll_task or list_tasks responses that contain completed tasks
|
|
141
|
+
const toolOutput = toolInput.tool_output;
|
|
142
|
+
if (!toolOutput) {
|
|
143
|
+
console.log(JSON.stringify({ decision: "approve" }));
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
const output = JSON.parse(toolOutput);
|
|
149
|
+
const notifiedTasks = loadNotifiedTasks();
|
|
150
|
+
const notifications: string[] = [];
|
|
151
|
+
|
|
152
|
+
// Check for single task completion (poll_task)
|
|
153
|
+
if (output.status === "completed" && toolInput.tool?.includes("poll_task")) {
|
|
154
|
+
// This is handled by the poll itself, no need for additional notification
|
|
155
|
+
console.log(JSON.stringify({ decision: "approve" }));
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Check for task list with completed tasks
|
|
160
|
+
if (output.tasks && Array.isArray(output.tasks)) {
|
|
161
|
+
for (const task of output.tasks) {
|
|
162
|
+
if (
|
|
163
|
+
(task.status === "completed" || task.status === "failed") &&
|
|
164
|
+
task.id &&
|
|
165
|
+
!notifiedTasks.has(task.id)
|
|
166
|
+
) {
|
|
167
|
+
// Calculate duration if we have timestamps
|
|
168
|
+
let durationStr = "";
|
|
169
|
+
if (task.created && task.completed) {
|
|
170
|
+
const created = new Date(task.created).getTime();
|
|
171
|
+
const completed = new Date(task.completed).getTime();
|
|
172
|
+
durationStr = ` (${formatDuration(completed - created)})`;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const statusIcon = task.status === "completed" ? "+" : "!";
|
|
176
|
+
const agentName = task.agent || "unknown";
|
|
177
|
+
notifications.push(
|
|
178
|
+
`[${statusIcon}] ${agentName}: ${task.status}${durationStr}`
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
saveNotifiedTask(task.id);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (notifications.length > 0) {
|
|
187
|
+
const response: HookResponse = {
|
|
188
|
+
decision: "approve",
|
|
189
|
+
hookSpecificOutput: {
|
|
190
|
+
hookEventName: "PostToolUse",
|
|
191
|
+
additionalContext: `\n[omc] ${notifications.join(" | ")}`,
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
console.log(JSON.stringify(response));
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
} catch {
|
|
198
|
+
// Parsing failed, just approve
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
console.log(JSON.stringify({ decision: "approve" }));
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
main().catch(() => {
|
|
205
|
+
console.log(JSON.stringify({ decision: "approve" }));
|
|
206
|
+
});
|