moshi-opencode-hooks 1.0.13 → 1.0.15
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/README.md +2 -16
- package/index.ts +26 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
OpenCode plugin that sends real-time events to the [Moshi](https://getmoshi.app) iOS app for live activity integration.
|
|
4
4
|
|
|
5
|
-

|
|
6
|
-
|
|
7
5
|
## Motivation
|
|
8
6
|
|
|
9
7
|
[Moshi](https://getmoshi.app) is an iOS SSH app that provides live activity widgets on the lock screen and Dynamic Island. While it already supports Cloud Code, this plugin brings the same live activity experience to [OpenCode](https://opencode.ai) users.
|
|
@@ -36,27 +34,15 @@ Add to `~/.config/opencode/opencode.json`:
|
|
|
36
34
|
}
|
|
37
35
|
```
|
|
38
36
|
|
|
39
|
-
## Events
|
|
40
|
-
|
|
41
|
-
The plugin sends these events to Moshi:
|
|
42
|
-
|
|
43
|
-
| OpenCode Event | Moshi Display |
|
|
44
|
-
|----------------|--------------|
|
|
45
|
-
| `session.created` | Session started |
|
|
46
|
-
| `tool.execute.before` | Running tool (Bash, Edit, Write, Read, Glob, Grep, Task) |
|
|
47
|
-
| `tool.execute.after` | Tool finished |
|
|
48
|
-
| `permission.ask` | Permission required |
|
|
49
|
-
| `session.idle` | Task complete |
|
|
50
|
-
|
|
51
37
|
## Requirements
|
|
52
38
|
|
|
53
39
|
- [OpenCode](https://opencode.ai)
|
|
54
|
-
- [Moshi iOS app](https://getmoshi.app) with
|
|
40
|
+
- [Moshi iOS app](https://getmoshi.app) with Pro subscription
|
|
55
41
|
|
|
56
42
|
## Moshi Token
|
|
57
43
|
|
|
58
44
|
Get your token from the Moshi iOS app:
|
|
59
|
-
1. Open Moshi → Settings →
|
|
45
|
+
1. Open Moshi → Settings → Agent Hooks
|
|
60
46
|
2. Copy the hook token
|
|
61
47
|
|
|
62
48
|
## Uninstall
|
package/index.ts
CHANGED
|
@@ -4,12 +4,13 @@ import type { Plugin } from "@opencode-ai/plugin"
|
|
|
4
4
|
|
|
5
5
|
const TOKEN_PATH = `${homedir()}/.config/moshi/token`
|
|
6
6
|
const API_URL = "https://api.getmoshi.app/api/v1/agent-events"
|
|
7
|
-
const INTERESTING_TOOLS = new Set(["bash", "edit", "write", "read", "glob", "grep", "task", "question", "apply_patch"])
|
|
7
|
+
const INTERESTING_TOOLS = new Set(["bash", "edit", "write", "read", "glob", "grep", "task", "question", "apply_patch", "webfetch", "websearch"])
|
|
8
8
|
|
|
9
9
|
interface HookState {
|
|
10
10
|
model?: string
|
|
11
11
|
lastToolName?: string
|
|
12
12
|
lastStopTime?: number
|
|
13
|
+
isSubagent?: boolean
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
interface AgentEvent {
|
|
@@ -121,6 +122,14 @@ function formatToolName(toolName: string): string {
|
|
|
121
122
|
return toolName.charAt(0).toUpperCase() + toolName.slice(1)
|
|
122
123
|
}
|
|
123
124
|
|
|
125
|
+
function formatModelName(model: string | undefined): string | undefined {
|
|
126
|
+
if (!model) return undefined
|
|
127
|
+
return model.replace(/^claude-/, "")
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const pkg = await import("./package.json", { assert: { type: "json" } })
|
|
131
|
+
const VERSION = pkg.default.version
|
|
132
|
+
|
|
124
133
|
export const MoshiHooks: Plugin = async ({ client, directory }) => {
|
|
125
134
|
const setupEventSubscription = async () => {
|
|
126
135
|
try {
|
|
@@ -134,13 +143,17 @@ export const MoshiHooks: Plugin = async ({ client, directory }) => {
|
|
|
134
143
|
const state = await readState(sessionId)
|
|
135
144
|
|
|
136
145
|
if (event.type === "session.created") {
|
|
146
|
+
const isSubagent = await isSubagentSession(sessionId)
|
|
137
147
|
await writeState(sessionId, {
|
|
138
148
|
model: (event as any).properties?.model,
|
|
149
|
+
isSubagent,
|
|
139
150
|
})
|
|
140
151
|
continue
|
|
141
152
|
}
|
|
142
153
|
|
|
143
154
|
if (event.type === "session.idle") {
|
|
155
|
+
if (state.isSubagent) continue
|
|
156
|
+
|
|
144
157
|
const now = Date.now() / 1000
|
|
145
158
|
if (state.lastStopTime && now - state.lastStopTime < 5) continue
|
|
146
159
|
|
|
@@ -155,7 +168,7 @@ export const MoshiHooks: Plugin = async ({ client, directory }) => {
|
|
|
155
168
|
message: "",
|
|
156
169
|
eventId: crypto.randomUUID(),
|
|
157
170
|
projectName,
|
|
158
|
-
modelName: state.model,
|
|
171
|
+
modelName: formatModelName(state.model),
|
|
159
172
|
toolName: state.lastToolName,
|
|
160
173
|
}
|
|
161
174
|
await sendAgentEvent(client, token, evt)
|
|
@@ -180,6 +193,11 @@ export const MoshiHooks: Plugin = async ({ client, directory }) => {
|
|
|
180
193
|
}
|
|
181
194
|
|
|
182
195
|
return {
|
|
196
|
+
"tui.toast.show": async (_input: unknown, output: any) => {
|
|
197
|
+
output.message = `moshi-opencode-hooks v${VERSION} is active`
|
|
198
|
+
output.type = "info"
|
|
199
|
+
},
|
|
200
|
+
|
|
183
201
|
"tool.execute.before": async (input, output) => {
|
|
184
202
|
const token = await loadToken()
|
|
185
203
|
if (!token) return
|
|
@@ -208,7 +226,7 @@ export const MoshiHooks: Plugin = async ({ client, directory }) => {
|
|
|
208
226
|
message: lines.join("\n---\n").slice(0, 512),
|
|
209
227
|
eventId: crypto.randomUUID(),
|
|
210
228
|
projectName,
|
|
211
|
-
modelName: state.model,
|
|
229
|
+
modelName: formatModelName(state.model),
|
|
212
230
|
toolName: tool,
|
|
213
231
|
}
|
|
214
232
|
await sendAgentEvent(client, token, evt)
|
|
@@ -224,7 +242,7 @@ export const MoshiHooks: Plugin = async ({ client, directory }) => {
|
|
|
224
242
|
message: "",
|
|
225
243
|
eventId: crypto.randomUUID(),
|
|
226
244
|
projectName,
|
|
227
|
-
modelName: state.model,
|
|
245
|
+
modelName: formatModelName(state.model),
|
|
228
246
|
toolName: tool,
|
|
229
247
|
}
|
|
230
248
|
await sendAgentEvent(client, token, evt)
|
|
@@ -252,7 +270,7 @@ export const MoshiHooks: Plugin = async ({ client, directory }) => {
|
|
|
252
270
|
message: "",
|
|
253
271
|
eventId: crypto.randomUUID(),
|
|
254
272
|
projectName,
|
|
255
|
-
modelName: state.model,
|
|
273
|
+
modelName: formatModelName(state.model),
|
|
256
274
|
toolName: tool,
|
|
257
275
|
}
|
|
258
276
|
await sendAgentEvent(client, token, evt)
|
|
@@ -264,6 +282,8 @@ export const MoshiHooks: Plugin = async ({ client, directory }) => {
|
|
|
264
282
|
|
|
265
283
|
const sessionID = (input as any).sessionID ?? "unknown"
|
|
266
284
|
const state = await readState(sessionID)
|
|
285
|
+
if (state.isSubagent) return
|
|
286
|
+
|
|
267
287
|
const projectName = directory ? basename(directory) : undefined
|
|
268
288
|
|
|
269
289
|
const prompt = (input as any).prompt ?? ""
|
|
@@ -277,7 +297,7 @@ export const MoshiHooks: Plugin = async ({ client, directory }) => {
|
|
|
277
297
|
message: prompt.slice(0, 256),
|
|
278
298
|
eventId: crypto.randomUUID(),
|
|
279
299
|
projectName,
|
|
280
|
-
modelName: state.model,
|
|
300
|
+
modelName: formatModelName(state.model),
|
|
281
301
|
}
|
|
282
302
|
await sendAgentEvent(client, token, evt)
|
|
283
303
|
},
|