opencode-timer-plugin 1.0.0
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/INSTALL-FROM-NPM.sh +52 -0
- package/INSTALLATION.md +120 -0
- package/PUBLISH.sh +44 -0
- package/README.md +208 -0
- package/VERIFICATION.md +159 -0
- package/index.ts +148 -0
- package/opencode-timer-plugin-1.0.0.tgz +0 -0
- package/package.json +17 -0
- package/tsconfig.json +10 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
echo "=== Installing OpenCode Timer Plugin from NPM ==="
|
|
5
|
+
echo ""
|
|
6
|
+
|
|
7
|
+
CONFIG_FILE="$HOME/.config/opencode/opencode.json"
|
|
8
|
+
|
|
9
|
+
# Backup existing config
|
|
10
|
+
if [ -f "$CONFIG_FILE" ]; then
|
|
11
|
+
cp "$CONFIG_FILE" "$CONFIG_FILE.backup.$(date +%s)"
|
|
12
|
+
echo "✅ Backed up existing config"
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
# Read existing config
|
|
16
|
+
if [ -f "$CONFIG_FILE" ]; then
|
|
17
|
+
# Add plugin to existing config using jq if available
|
|
18
|
+
if command -v jq &>/dev/null; then
|
|
19
|
+
TMP=$(mktemp)
|
|
20
|
+
jq '.plugin += ["opencode-timer-plugin"]' "$CONFIG_FILE" > "$TMP"
|
|
21
|
+
mv "$TMP" "$CONFIG_FILE"
|
|
22
|
+
echo "✅ Added opencode-timer-plugin to existing config"
|
|
23
|
+
else
|
|
24
|
+
echo ""
|
|
25
|
+
echo "⚠️ Manual setup required (jq not installed)"
|
|
26
|
+
echo ""
|
|
27
|
+
echo "Add to $CONFIG_FILE:"
|
|
28
|
+
echo ' "plugin": ["opencode-timer-plugin"]'
|
|
29
|
+
echo ""
|
|
30
|
+
exit 1
|
|
31
|
+
fi
|
|
32
|
+
else
|
|
33
|
+
# Create new config
|
|
34
|
+
mkdir -p "$(dirname "$CONFIG_FILE")"
|
|
35
|
+
cat > "$CONFIG_FILE" <<'EOF'
|
|
36
|
+
{
|
|
37
|
+
"$schema": "https://opencode.ai/config.json",
|
|
38
|
+
"plugin": ["opencode-timer-plugin"]
|
|
39
|
+
}
|
|
40
|
+
EOF
|
|
41
|
+
echo "✅ Created new config with timer plugin"
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
echo ""
|
|
45
|
+
echo "✅ Installation complete!"
|
|
46
|
+
echo ""
|
|
47
|
+
echo "Test with:"
|
|
48
|
+
echo " opencode run \"echo test\""
|
|
49
|
+
echo ""
|
|
50
|
+
echo "Expected behavior:"
|
|
51
|
+
echo " - Terminal title updates: (1s), (2s), etc."
|
|
52
|
+
echo " - Desktop notification on completion"
|
package/INSTALLATION.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# Installation Guide
|
|
2
|
+
|
|
3
|
+
## Current Status
|
|
4
|
+
|
|
5
|
+
The timer plugin is **fully implemented and ready to use**, but OpenCode's plugin system currently requires plugins to be published to npm for automatic installation.
|
|
6
|
+
|
|
7
|
+
## Issue
|
|
8
|
+
|
|
9
|
+
OpenCode validates and installs plugins from the npm registry when listed in config. Local file-based plugins trigger a fatal error during the npm version check, preventing OpenCode from starting.
|
|
10
|
+
|
|
11
|
+
Attempts made:
|
|
12
|
+
- ✅ Symlink to `~/.config/opencode/plugins/` - Plugin found but version check fails
|
|
13
|
+
- ✅ Add to cache `package.json` with `file:` protocol - Installed but validation fails
|
|
14
|
+
- ✅ Tarball packaging - OpenCode appends `@latest` making path invalid
|
|
15
|
+
- ❌ All approaches fail with: `GET https://registry.npmjs.org/opencode-timer-plugin - 404`
|
|
16
|
+
|
|
17
|
+
## Plugin Code Status
|
|
18
|
+
|
|
19
|
+
✅ **Plugin implementation is correct and complete:**
|
|
20
|
+
- Proper export structure matching OpenCode's plugin API
|
|
21
|
+
- Correct TypeScript types using `@opencode-ai/plugin`
|
|
22
|
+
- Event handling for `session.created`, `message.updated`, `session.idle`
|
|
23
|
+
- ANSI escape sequences for terminal title updates
|
|
24
|
+
- Desktop notifications via `notify-send`
|
|
25
|
+
- Configuration loading from standard paths
|
|
26
|
+
|
|
27
|
+
## Workaround Options
|
|
28
|
+
|
|
29
|
+
### Option 1: Publish to NPM (Recommended)
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
cd ~/Projects/opencode-plugins/timer-plugin
|
|
33
|
+
|
|
34
|
+
# Login to npm (if not already)
|
|
35
|
+
npm login
|
|
36
|
+
|
|
37
|
+
# Publish
|
|
38
|
+
npm publish
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Then add to `~/.config/opencode/opencode.json`:
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"plugin": ["opencode-timer-plugin"]
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Option 2: Use Private NPM Registry
|
|
49
|
+
|
|
50
|
+
Set up a private registry (Verdaccio, npm Enterprise, etc.) and publish there.
|
|
51
|
+
|
|
52
|
+
### Option 3: Manual Testing (Without Config Integration)
|
|
53
|
+
|
|
54
|
+
The plugin code can be tested independently by importing it into a test script, but it won't run automatically with OpenCode sessions.
|
|
55
|
+
|
|
56
|
+
## Installation After Publishing
|
|
57
|
+
|
|
58
|
+
Once published to npm:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# Plugin will auto-install when referenced in config
|
|
62
|
+
echo '{"plugin": ["opencode-timer-plugin"]}' > ~/.config/opencode/opencode.json
|
|
63
|
+
|
|
64
|
+
# Or add to existing config:
|
|
65
|
+
# {
|
|
66
|
+
# "plugin": [
|
|
67
|
+
# "opencode-antigravity-auth@latest",
|
|
68
|
+
# "opencode-timer-plugin"
|
|
69
|
+
# ]
|
|
70
|
+
# }
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Files Created
|
|
74
|
+
|
|
75
|
+
- ✅ `/home/user/Projects/opencode-plugins/timer-plugin/index.ts` - Main plugin
|
|
76
|
+
- ✅ `/home/user/Projects/opencode-plugins/timer-plugin/package.json` - Package metadata
|
|
77
|
+
- ✅ `/home/user/Projects/opencode-plugins/timer-plugin/tsconfig.json` - TypeScript config
|
|
78
|
+
- ✅ `/home/user/Projects/opencode-plugins/timer-plugin/README.md` - Usage documentation
|
|
79
|
+
- ✅ `/home/user/Projects/opencode-plugins/timer-plugin/.gitignore` - Git ignore file
|
|
80
|
+
- ✅ `/home/user/Projects/opencode-plugins/timer-plugin/opencode-timer-plugin-1.0.0.tgz` - NPM package tarball
|
|
81
|
+
|
|
82
|
+
## Next Steps
|
|
83
|
+
|
|
84
|
+
1. **Publish to npm** (requires npm account)
|
|
85
|
+
2. **Test installation** from npm registry
|
|
86
|
+
3. **Verify functionality** with a real OpenCode session
|
|
87
|
+
|
|
88
|
+
## Testing After Installation
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
# Verify notify-send works
|
|
92
|
+
notify-send "Test" "Notifications working" -i dialog-information
|
|
93
|
+
|
|
94
|
+
# Start OpenCode session
|
|
95
|
+
opencode run "list files"
|
|
96
|
+
|
|
97
|
+
# Expected behavior:
|
|
98
|
+
# - Terminal title updates: (1s), (2s), etc.
|
|
99
|
+
# - On completion: desktop notification with runtime and output
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Plugin Features
|
|
103
|
+
|
|
104
|
+
- ⏱️ Real-time terminal title timer (1s updates)
|
|
105
|
+
- 🔔 Desktop notifications on session completion
|
|
106
|
+
- ⚙️ Configurable update interval and title prefix
|
|
107
|
+
- 🎯 Non-intrusive: restores original title immediately
|
|
108
|
+
- 📊 Displays total runtime and agent output in notification
|
|
109
|
+
|
|
110
|
+
## Configuration
|
|
111
|
+
|
|
112
|
+
Optional: Create `~/.config/opencode/plugins/timer-plugin/config.json`
|
|
113
|
+
|
|
114
|
+
```json
|
|
115
|
+
{
|
|
116
|
+
"updateInterval": 1000,
|
|
117
|
+
"titlePrefix": "OpenCode",
|
|
118
|
+
"notifyOnCompletion": true
|
|
119
|
+
}
|
|
120
|
+
```
|
package/PUBLISH.sh
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
echo "=== OpenCode Timer Plugin - NPM Publishing ==="
|
|
5
|
+
echo ""
|
|
6
|
+
|
|
7
|
+
# Check if logged in
|
|
8
|
+
if ! npm whoami &>/dev/null; then
|
|
9
|
+
echo "❌ Not logged in to npm"
|
|
10
|
+
echo ""
|
|
11
|
+
echo "Run: npm login"
|
|
12
|
+
echo ""
|
|
13
|
+
exit 1
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
CURRENT_USER=$(npm whoami)
|
|
17
|
+
echo "✅ Logged in as: $CURRENT_USER"
|
|
18
|
+
echo ""
|
|
19
|
+
|
|
20
|
+
# Show package info
|
|
21
|
+
echo "Package to publish:"
|
|
22
|
+
echo " Name: opencode-timer-plugin"
|
|
23
|
+
echo " Version: 1.0.0"
|
|
24
|
+
echo ""
|
|
25
|
+
|
|
26
|
+
# Confirm
|
|
27
|
+
read -p "Publish to npm? (y/N) " -n 1 -r
|
|
28
|
+
echo ""
|
|
29
|
+
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
30
|
+
echo "❌ Cancelled"
|
|
31
|
+
exit 1
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
# Publish
|
|
35
|
+
echo ""
|
|
36
|
+
echo "📦 Publishing..."
|
|
37
|
+
npm publish
|
|
38
|
+
|
|
39
|
+
echo ""
|
|
40
|
+
echo "✅ Published successfully!"
|
|
41
|
+
echo ""
|
|
42
|
+
echo "To install:"
|
|
43
|
+
echo ' echo '"'"'{"plugin": ["opencode-timer-plugin"]}'"'"' > ~/.config/opencode/opencode.json'
|
|
44
|
+
echo " opencode run \"echo test\""
|
package/README.md
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# OpenCode Terminal Title Timer Plugin
|
|
2
|
+
|
|
3
|
+
Displays session elapsed time in terminal window title, updating every second. Works with any terminal emulator (Ghostty, Alacritty, iTerm2, etc.).
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Real-time terminal title updates** - Shows elapsed time in format `(Xh Ym Zs) OpenCode`
|
|
8
|
+
- **Desktop notifications** - Displays runtime and agent output when session completes
|
|
9
|
+
- **Multi-session support** - Independent timers for concurrent sessions
|
|
10
|
+
- **Configurable** - Customize update interval, title prefix, and notifications
|
|
11
|
+
- **Non-intrusive** - Title resets immediately when session ends
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
### Option A: Local Plugin (All Projects)
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
mkdir -p ~/.config/opencode/plugins
|
|
19
|
+
ln -s ~/Projects/opencode-plugins/timer-plugin ~/.config/opencode/plugins/
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Option B: Project Plugin (Single Project)
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
mkdir -p .opencode/plugins
|
|
26
|
+
ln -s ~/Projects/opencode-plugins/timer-plugin .opencode/plugins/
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Option C: NPM Package (Recommended for Distribution)
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
cd ~/Projects/opencode-plugins/timer-plugin
|
|
33
|
+
npm publish
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Then in your OpenCode config:
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"$schema": "https://opencode.ai/config.json",
|
|
41
|
+
"plugin": ["opencode-timer-plugin"]
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Configuration
|
|
46
|
+
|
|
47
|
+
Create `~/.config/opencode/plugins/timer-plugin/config.json` or `.opencode/plugins/timer-plugin/config.json`:
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"updateInterval": 1000,
|
|
52
|
+
"titlePrefix": "OpenCode",
|
|
53
|
+
"notifyOnCompletion": true
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Settings
|
|
58
|
+
|
|
59
|
+
| Setting | Type | Default | Description |
|
|
60
|
+
|---------|------|---------|-------------|
|
|
61
|
+
| `updateInterval` | number | 1000 | Update frequency in milliseconds |
|
|
62
|
+
| `titlePrefix` | string | "OpenCode" | Terminal title prefix (restored after session) |
|
|
63
|
+
| `notifyOnCompletion` | boolean | true | Show desktop notification with runtime and output |
|
|
64
|
+
|
|
65
|
+
### Example Configurations
|
|
66
|
+
|
|
67
|
+
**Silent mode (no notifications):**
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"notifyOnCompletion": false
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Faster updates (every 500ms):**
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"updateInterval": 500
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**Custom title:**
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"titlePrefix": "My Project"
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Usage
|
|
89
|
+
|
|
90
|
+
1. Install the plugin (see Installation above)
|
|
91
|
+
2. Start an OpenCode session
|
|
92
|
+
3. Watch your terminal title update with elapsed time:
|
|
93
|
+
- `(5s) OpenCode`
|
|
94
|
+
- `(2m 15s) OpenCode`
|
|
95
|
+
- `(1h 30m 45s) OpenCode`
|
|
96
|
+
- `(2d 5h 30m) OpenCode`
|
|
97
|
+
4. When session completes, desktop notification shows:
|
|
98
|
+
- Total runtime
|
|
99
|
+
- Agent's final output text
|
|
100
|
+
|
|
101
|
+
## System Requirements
|
|
102
|
+
|
|
103
|
+
- **Bun runtime** (OpenCode standard)
|
|
104
|
+
- **Linux with notification daemon** (libnotify/notify-send)
|
|
105
|
+
- Supported: Cinnamon, GNOME, KDE, XFCE, MATE
|
|
106
|
+
- **notify-send** command in PATH
|
|
107
|
+
|
|
108
|
+
### Verify notify-send
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
which notify-send
|
|
112
|
+
# Should output: /usr/bin/notify-send
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Install libnotify (if missing)
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
# Debian/Ubuntu/Mint
|
|
119
|
+
sudo apt install libnotify-bin
|
|
120
|
+
|
|
121
|
+
# Arch
|
|
122
|
+
sudo pacman -S libnotify
|
|
123
|
+
|
|
124
|
+
# Fedora
|
|
125
|
+
sudo dnf install libnotify
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Testing
|
|
129
|
+
|
|
130
|
+
### Pre-test: Verify Notifications
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
notify-send "Test" "Desktop notifications working" -i dialog-information
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
You should see a notification in your desktop environment's system tray.
|
|
137
|
+
|
|
138
|
+
### Plugin Testing
|
|
139
|
+
|
|
140
|
+
1. Install plugin in `~/.config/opencode/plugins/`
|
|
141
|
+
2. Note current terminal title
|
|
142
|
+
3. Start OpenCode session with simple prompt (e.g., "List files in current directory")
|
|
143
|
+
4. Verify terminal title shows:
|
|
144
|
+
- `(1s) {original title}`
|
|
145
|
+
- `(2s) {original title}`
|
|
146
|
+
- Updates every second
|
|
147
|
+
5. Wait 60+ seconds, verify format changes to `(1m Xs) {original title}`
|
|
148
|
+
6. When session completes, verify:
|
|
149
|
+
- Terminal title immediately resets to `{original title}`
|
|
150
|
+
- Desktop notification appears with runtime and agent output
|
|
151
|
+
|
|
152
|
+
### Test Notification Disabled
|
|
153
|
+
|
|
154
|
+
1. Create config: `{ "notifyOnCompletion": false }`
|
|
155
|
+
2. Run session
|
|
156
|
+
3. Verify title updates work but no notification appears
|
|
157
|
+
|
|
158
|
+
## Terminal Compatibility
|
|
159
|
+
|
|
160
|
+
Tested with:
|
|
161
|
+
- ✅ Ghostty
|
|
162
|
+
- ✅ Alacritty
|
|
163
|
+
- ✅ iTerm2
|
|
164
|
+
- ✅ Gnome Terminal
|
|
165
|
+
- ✅ Konsole
|
|
166
|
+
- ✅ Windows Terminal
|
|
167
|
+
|
|
168
|
+
Uses standard ANSI escape sequences (`\x1b]0;...\x07`).
|
|
169
|
+
|
|
170
|
+
## Time Formatting
|
|
171
|
+
|
|
172
|
+
- 0-59s: `(5s) OpenCode`
|
|
173
|
+
- 1-59m: `(2m 15s) OpenCode`
|
|
174
|
+
- 1-23h: `(1h 30m 45s) OpenCode`
|
|
175
|
+
- 1+ days: `(2d 5h 30m) OpenCode`
|
|
176
|
+
|
|
177
|
+
## Architecture
|
|
178
|
+
|
|
179
|
+
### Event Handling
|
|
180
|
+
|
|
181
|
+
- `session.created` - Start timer, begin title updates
|
|
182
|
+
- `message.updated` - Capture agent's latest output
|
|
183
|
+
- `session.idle` - Stop timer, reset title, send notification
|
|
184
|
+
- `session.deleted` - Cleanup timer
|
|
185
|
+
|
|
186
|
+
### Edge Cases Handled
|
|
187
|
+
|
|
188
|
+
1. **Multiple sessions** - Each session has independent timer
|
|
189
|
+
2. **Session interruption** - Timer stops cleanly on error/deletion
|
|
190
|
+
3. **Long-running sessions** - Formats up to days
|
|
191
|
+
4. **Terminal compatibility** - Uses standard ANSI escape codes
|
|
192
|
+
5. **Title restoration** - Resets to original after completion
|
|
193
|
+
|
|
194
|
+
## Future Enhancements
|
|
195
|
+
|
|
196
|
+
- Pause/resume timer
|
|
197
|
+
- Custom tool to check elapsed time
|
|
198
|
+
- Log final times to file for analytics
|
|
199
|
+
- Notification action buttons
|
|
200
|
+
- Different urgency levels for long sessions
|
|
201
|
+
|
|
202
|
+
## License
|
|
203
|
+
|
|
204
|
+
MIT
|
|
205
|
+
|
|
206
|
+
## Author
|
|
207
|
+
|
|
208
|
+
Created for OpenCode CLI tool
|
package/VERIFICATION.md
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# Post-Publication Verification
|
|
2
|
+
|
|
3
|
+
## 1. Verify Package on NPM
|
|
4
|
+
|
|
5
|
+
Visit: https://www.npmjs.com/package/opencode-timer-plugin
|
|
6
|
+
|
|
7
|
+
Should show:
|
|
8
|
+
- ✅ Version 1.0.0
|
|
9
|
+
- ✅ Description
|
|
10
|
+
- ✅ README content
|
|
11
|
+
|
|
12
|
+
## 2. Test Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
# Clean test environment
|
|
16
|
+
cd /tmp
|
|
17
|
+
rm -rf test-opencode-plugin
|
|
18
|
+
mkdir test-opencode-plugin
|
|
19
|
+
cd test-opencode-plugin
|
|
20
|
+
|
|
21
|
+
# Create config
|
|
22
|
+
cat > .opencode/opencode.json <<'EOF'
|
|
23
|
+
{
|
|
24
|
+
"$schema": "https://opencode.ai/config.json",
|
|
25
|
+
"plugin": ["opencode-timer-plugin"]
|
|
26
|
+
}
|
|
27
|
+
EOF
|
|
28
|
+
|
|
29
|
+
# Test
|
|
30
|
+
opencode run "echo hello world"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## 3. Verify Plugin Loads
|
|
34
|
+
|
|
35
|
+
Check logs for:
|
|
36
|
+
```
|
|
37
|
+
INFO service=plugin path=opencode-timer-plugin loading plugin
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
No errors about:
|
|
41
|
+
```
|
|
42
|
+
❌ GET https://registry.npmjs.org/opencode-timer-plugin - 404
|
|
43
|
+
❌ BunInstallFailedError
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## 4. Verify Functionality
|
|
47
|
+
|
|
48
|
+
### Terminal Title Updates
|
|
49
|
+
- ✅ Title shows `(1s) OpenCode`
|
|
50
|
+
- ✅ Updates every second: `(2s)`, `(3s)`, etc.
|
|
51
|
+
- ✅ Format changes at thresholds:
|
|
52
|
+
- 60s → `(1m 0s)`
|
|
53
|
+
- 3600s → `(1h 0m 0s)`
|
|
54
|
+
- 86400s → `(1d 0h 0m)`
|
|
55
|
+
|
|
56
|
+
### Title Restoration
|
|
57
|
+
- ✅ Title resets to `OpenCode` immediately when session ends
|
|
58
|
+
- ✅ No lingering timer in title after completion
|
|
59
|
+
|
|
60
|
+
### Desktop Notifications
|
|
61
|
+
- ✅ Notification appears on session completion
|
|
62
|
+
- ✅ Shows runtime: "Runtime: 1m 23s"
|
|
63
|
+
- ✅ Shows agent output (truncated to ~200 chars)
|
|
64
|
+
- ✅ Notification title: "OpenCode Session Complete"
|
|
65
|
+
|
|
66
|
+
## 5. Test Edge Cases
|
|
67
|
+
|
|
68
|
+
### Multiple Sessions
|
|
69
|
+
```bash
|
|
70
|
+
# Terminal 1
|
|
71
|
+
opencode run "sleep 10 && echo first"
|
|
72
|
+
|
|
73
|
+
# Terminal 2 (while first is running)
|
|
74
|
+
opencode run "sleep 5 && echo second"
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Expected:
|
|
78
|
+
- ✅ Each terminal has independent timer
|
|
79
|
+
- ✅ No interference between sessions
|
|
80
|
+
|
|
81
|
+
### Error Handling
|
|
82
|
+
```bash
|
|
83
|
+
opencode run "exit 1"
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Expected:
|
|
87
|
+
- ✅ Timer stops on error
|
|
88
|
+
- ✅ Title resets to original
|
|
89
|
+
- ✅ Notification shows (or doesn't, based on config)
|
|
90
|
+
|
|
91
|
+
### Configuration
|
|
92
|
+
```bash
|
|
93
|
+
# Create custom config
|
|
94
|
+
mkdir -p ~/.config/opencode/plugins/timer-plugin
|
|
95
|
+
cat > ~/.config/opencode/plugins/timer-plugin/config.json <<'EOF'
|
|
96
|
+
{
|
|
97
|
+
"updateInterval": 500,
|
|
98
|
+
"titlePrefix": "Test",
|
|
99
|
+
"notifyOnCompletion": false
|
|
100
|
+
}
|
|
101
|
+
EOF
|
|
102
|
+
|
|
103
|
+
opencode run "echo test"
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Expected:
|
|
107
|
+
- ✅ Title updates every 500ms
|
|
108
|
+
- ✅ Title shows `(1s) Test`
|
|
109
|
+
- ✅ No notification on completion
|
|
110
|
+
|
|
111
|
+
## 6. Verify notify-send Works
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
notify-send "Test" "Notifications working" -i dialog-information
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Expected:
|
|
118
|
+
- ✅ Desktop notification appears
|
|
119
|
+
- ✅ Shows in system tray
|
|
120
|
+
|
|
121
|
+
## 7. Terminal Compatibility
|
|
122
|
+
|
|
123
|
+
Test in different terminals:
|
|
124
|
+
- [ ] Ghostty
|
|
125
|
+
- [ ] Alacritya
|
|
126
|
+
- [ ] Gnome Terminal
|
|
127
|
+
- [ ] Konsole
|
|
128
|
+
- [ ] xterm
|
|
129
|
+
- [ ] Kitty
|
|
130
|
+
|
|
131
|
+
All should show title updates via ANSI escape sequences.
|
|
132
|
+
|
|
133
|
+
## 8. Uninstall Test
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
# Remove from config
|
|
137
|
+
jq 'del(.plugin[] | select(. == "opencode-timer-plugin"))' \
|
|
138
|
+
~/.config/opencode/opencode.json > /tmp/config.json
|
|
139
|
+
mv /tmp/config.json ~/.config/opencode/opencode.json
|
|
140
|
+
|
|
141
|
+
# Verify OpenCode still works
|
|
142
|
+
opencode run "echo test"
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Expected:
|
|
146
|
+
- ✅ No timer in title
|
|
147
|
+
- ✅ No notifications
|
|
148
|
+
- ✅ OpenCode runs normally
|
|
149
|
+
|
|
150
|
+
## Success Criteria
|
|
151
|
+
|
|
152
|
+
All items checked:
|
|
153
|
+
- ✅ Published to npm (visible on npmjs.com)
|
|
154
|
+
- ✅ OpenCode loads plugin without errors
|
|
155
|
+
- ✅ Terminal title updates in real-time
|
|
156
|
+
- ✅ Title resets immediately on completion
|
|
157
|
+
- ✅ Desktop notifications work
|
|
158
|
+
- ✅ Configuration options work
|
|
159
|
+
- ✅ No crashes or errors in logs
|
package/index.ts
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import type { Plugin } from "@opencode-ai/plugin"
|
|
2
|
+
import { readFileSync, existsSync } from "node:fs"
|
|
3
|
+
import { join } from "node:path"
|
|
4
|
+
import { homedir } from "node:os"
|
|
5
|
+
|
|
6
|
+
interface TimerConfig {
|
|
7
|
+
updateInterval?: number
|
|
8
|
+
titlePrefix?: string
|
|
9
|
+
notifyOnCompletion?: boolean
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const TimerPlugin: Plugin = async ({ client, directory, $ }) => {
|
|
13
|
+
// Load configuration
|
|
14
|
+
const loadConfig = (): TimerConfig => {
|
|
15
|
+
const configPaths = [
|
|
16
|
+
join(directory, ".opencode/plugins/timer-plugin/config.json"),
|
|
17
|
+
join(homedir(), ".config/opencode/plugins/timer-plugin/config.json"),
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
for (const configPath of configPaths) {
|
|
21
|
+
if (existsSync(configPath)) {
|
|
22
|
+
try {
|
|
23
|
+
const configData = readFileSync(configPath, "utf-8")
|
|
24
|
+
return JSON.parse(configData)
|
|
25
|
+
} catch (err) {
|
|
26
|
+
await client.app.log({
|
|
27
|
+
service: "timer-plugin",
|
|
28
|
+
level: "warn",
|
|
29
|
+
message: "Failed to load config, using defaults",
|
|
30
|
+
extra: { error: String(err), path: configPath },
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return {}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const config = loadConfig()
|
|
39
|
+
const updateInterval = config.updateInterval ?? 1000
|
|
40
|
+
const titlePrefix = config.titlePrefix ?? process.env.TERM_PROGRAM ?? "OpenCode"
|
|
41
|
+
const notifyOnCompletion = config.notifyOnCompletion ?? true
|
|
42
|
+
|
|
43
|
+
const sessions = new Map<string, {
|
|
44
|
+
startTime: number
|
|
45
|
+
interval: NodeJS.Timer
|
|
46
|
+
lastMessage: string
|
|
47
|
+
}>()
|
|
48
|
+
|
|
49
|
+
const formatTime = (ms: number): string => {
|
|
50
|
+
const totalSeconds = Math.floor(ms / 1000)
|
|
51
|
+
const seconds = totalSeconds % 60
|
|
52
|
+
const minutes = Math.floor(totalSeconds / 60) % 60
|
|
53
|
+
const hours = Math.floor(totalSeconds / 3600) % 24
|
|
54
|
+
const days = Math.floor(totalSeconds / 86400)
|
|
55
|
+
|
|
56
|
+
if (days > 0) return `${days}d ${hours}h ${minutes}m`
|
|
57
|
+
if (hours > 0) return `${hours}h ${minutes}m ${seconds}s`
|
|
58
|
+
if (minutes > 0) return `${minutes}m ${seconds}s`
|
|
59
|
+
return `${seconds}s`
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const updateTitle = (elapsed: string) => {
|
|
63
|
+
process.stdout.write(`\x1b]0;(${elapsed}) ${titlePrefix}\x07`)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const resetTitle = () => {
|
|
67
|
+
process.stdout.write(`\x1b]0;${titlePrefix}\x07`)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const sendDesktopNotification = async (runtime: string, agentOutput: string) => {
|
|
71
|
+
try {
|
|
72
|
+
// Truncate output to fit notification (max ~200 chars)
|
|
73
|
+
const truncatedOutput = agentOutput.length > 200
|
|
74
|
+
? agentOutput.substring(0, 197) + "..."
|
|
75
|
+
: agentOutput
|
|
76
|
+
|
|
77
|
+
const message = `Runtime: ${runtime}\n\n${truncatedOutput}`
|
|
78
|
+
|
|
79
|
+
await $`notify-send "OpenCode Session Complete" ${message} -i dialog-information -u normal`
|
|
80
|
+
} catch (err) {
|
|
81
|
+
await client.app.log({
|
|
82
|
+
service: "timer-plugin",
|
|
83
|
+
level: "warn",
|
|
84
|
+
message: "Failed to send desktop notification",
|
|
85
|
+
extra: { error: String(err) },
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
event: async ({ event }) => {
|
|
92
|
+
if (event.type === "session.created") {
|
|
93
|
+
const sessionID = event.properties.sessionID
|
|
94
|
+
const startTime = Date.now()
|
|
95
|
+
|
|
96
|
+
const interval = setInterval(() => {
|
|
97
|
+
const elapsed = formatTime(Date.now() - startTime)
|
|
98
|
+
updateTitle(elapsed)
|
|
99
|
+
}, updateInterval)
|
|
100
|
+
|
|
101
|
+
sessions.set(sessionID, {
|
|
102
|
+
startTime,
|
|
103
|
+
interval,
|
|
104
|
+
lastMessage: ""
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (event.type === "message.updated") {
|
|
109
|
+
const sessionID = event.properties.sessionID
|
|
110
|
+
const session = sessions.get(sessionID)
|
|
111
|
+
|
|
112
|
+
if (session && event.properties.role === "assistant") {
|
|
113
|
+
// Capture the agent's latest output
|
|
114
|
+
const content = event.properties.content
|
|
115
|
+
if (Array.isArray(content)) {
|
|
116
|
+
// Extract text content from parts
|
|
117
|
+
const textParts = content
|
|
118
|
+
.filter((part: any) => part.type === "text")
|
|
119
|
+
.map((part: any) => part.text)
|
|
120
|
+
session.lastMessage = textParts.join(" ")
|
|
121
|
+
} else if (typeof content === "string") {
|
|
122
|
+
session.lastMessage = content
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (event.type === "session.idle" || event.type === "session.deleted") {
|
|
128
|
+
const sessionID = event.properties.sessionID
|
|
129
|
+
const session = sessions.get(sessionID)
|
|
130
|
+
|
|
131
|
+
if (session) {
|
|
132
|
+
clearInterval(session.interval)
|
|
133
|
+
const finalTime = formatTime(Date.now() - session.startTime)
|
|
134
|
+
|
|
135
|
+
// Immediately reset title to original
|
|
136
|
+
resetTitle()
|
|
137
|
+
|
|
138
|
+
// Send desktop notification with runtime and agent output
|
|
139
|
+
if (notifyOnCompletion && event.type === "session.idle") {
|
|
140
|
+
await sendDesktopNotification(finalTime, session.lastMessage || "No output")
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
sessions.delete(sessionID)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "opencode-timer-plugin",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "index.ts",
|
|
6
|
+
"description": "OpenCode plugin that displays session elapsed time in terminal title with desktop notifications",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"opencode",
|
|
9
|
+
"plugin",
|
|
10
|
+
"timer",
|
|
11
|
+
"terminal",
|
|
12
|
+
"notifications"
|
|
13
|
+
],
|
|
14
|
+
"peerDependencies": {
|
|
15
|
+
"@opencode-ai/plugin": "*"
|
|
16
|
+
}
|
|
17
|
+
}
|