moshi-opencode-hooks 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/.github/workflows/publish.yml +25 -0
- package/CLAUDE.md +17 -0
- package/README.md +38 -0
- package/bun.lock +35 -0
- package/index.ts +209 -0
- package/package.json +31 -0
- package/src/cli.ts +103 -0
- package/tsconfig.json +29 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
name: Publish to npm
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*'
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
publish:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
id-token: write
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: oven-sh/setup-bun@v2
|
|
17
|
+
with:
|
|
18
|
+
bun-version: latest
|
|
19
|
+
- run: bun install
|
|
20
|
+
- run: bun run build
|
|
21
|
+
- run: |
|
|
22
|
+
echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc
|
|
23
|
+
npm publish --provenance --access public
|
|
24
|
+
env:
|
|
25
|
+
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# moshi-opencode-hooks
|
|
2
|
+
|
|
3
|
+
OpenCode plugin that sends events to Moshi for live activity integration.
|
|
4
|
+
|
|
5
|
+
## Building
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun run index.ts # Test run
|
|
9
|
+
bun tsc --noEmit # Type check
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Publishing to npm
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
bun run build
|
|
16
|
+
npm publish
|
|
17
|
+
```
|
package/README.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# moshi-opencode-hooks
|
|
2
|
+
|
|
3
|
+
OpenCode plugin for Moshi live activity integration. Provides real-time progress updates in the Moshi iOS app when using OpenCode.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
1. **Install the token** (if not already done with Claude Code):
|
|
8
|
+
```bash
|
|
9
|
+
bunx moshi-hooks token Xjqyek7li0vXgBnIlvXEn3VJgdhWf8qW
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
2. **Add as npm plugin** in `~/.config/opencode/opencode.json`:
|
|
13
|
+
```json
|
|
14
|
+
{
|
|
15
|
+
"$schema": "https://opencode.ai/config.json",
|
|
16
|
+
"plugin": ["moshi-opencode-hooks"]
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Or use a local path for development:
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"$schema": "https://opencode.ai/config.json",
|
|
24
|
+
"plugin": ["/path/to/moshi-opencode-hooks"]
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Events Sent to Moshi
|
|
29
|
+
|
|
30
|
+
- `tool.execute.before/after` - Tool execution (Bash, Edit, Write, Read, Glob, Grep, Task)
|
|
31
|
+
- `permission.ask` - Permission prompts
|
|
32
|
+
- `session.created` - Session start
|
|
33
|
+
- `session.idle` - Task completion
|
|
34
|
+
|
|
35
|
+
## Requirements
|
|
36
|
+
|
|
37
|
+
- OpenCode
|
|
38
|
+
- Moshi iOS app with Cloud Code hook token
|
package/bun.lock
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"lockfileVersion": 1,
|
|
3
|
+
"configVersion": 1,
|
|
4
|
+
"workspaces": {
|
|
5
|
+
"": {
|
|
6
|
+
"name": "moshi-opencode-hooks",
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"@opencode-ai/plugin": "^1.2.27",
|
|
9
|
+
},
|
|
10
|
+
"devDependencies": {
|
|
11
|
+
"@types/bun": "latest",
|
|
12
|
+
},
|
|
13
|
+
"peerDependencies": {
|
|
14
|
+
"typescript": "^5",
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
"packages": {
|
|
19
|
+
"@opencode-ai/plugin": ["@opencode-ai/plugin@1.2.27", "", { "dependencies": { "@opencode-ai/sdk": "1.2.27", "zod": "4.1.8" } }, "sha512-h+8Bw9v9nghMg7T+SUCTzxlIhOrsTqXW7U0HVLGQST5DjbN7uyCUM51roZWZ8LRjGxzbzFhvPnY1bj8i+ioZyw=="],
|
|
20
|
+
|
|
21
|
+
"@opencode-ai/sdk": ["@opencode-ai/sdk@1.2.27", "", {}, "sha512-Wk0o/I+Fo+wE3zgvlJDs8Fb67KlKqX0PrV8dK5adSDkANq6r4Z25zXJg2iOir+a8ntg3rAcpel1OY4FV/TwRUA=="],
|
|
22
|
+
|
|
23
|
+
"@types/bun": ["@types/bun@1.3.11", "", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="],
|
|
24
|
+
|
|
25
|
+
"@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="],
|
|
26
|
+
|
|
27
|
+
"bun-types": ["bun-types@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="],
|
|
28
|
+
|
|
29
|
+
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
|
30
|
+
|
|
31
|
+
"undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
|
|
32
|
+
|
|
33
|
+
"zod": ["zod@4.1.8", "", {}, "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ=="],
|
|
34
|
+
}
|
|
35
|
+
}
|
package/index.ts
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { homedir } from "os"
|
|
2
|
+
import { basename } from "path"
|
|
3
|
+
import type { Plugin } from "@opencode-ai/plugin"
|
|
4
|
+
|
|
5
|
+
const TOKEN_PATH = `${homedir()}/.config/moshi/token`
|
|
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"])
|
|
8
|
+
|
|
9
|
+
interface HookState {
|
|
10
|
+
model?: string
|
|
11
|
+
lastToolName?: string
|
|
12
|
+
lastStopTime?: number
|
|
13
|
+
sessionStartTime?: number
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface AgentEvent {
|
|
17
|
+
source: "opencode"
|
|
18
|
+
eventType: "user_prompt" | "pre_tool" | "post_tool" | "notification" | "stop" | "agent_turn_complete"
|
|
19
|
+
sessionId: string
|
|
20
|
+
category: "approval_required" | "task_complete" | "tool_running" | "tool_finished" | "info" | "error"
|
|
21
|
+
title: string
|
|
22
|
+
message: string
|
|
23
|
+
eventId: string
|
|
24
|
+
projectName?: string
|
|
25
|
+
modelName?: string
|
|
26
|
+
toolName?: string
|
|
27
|
+
contextPercent?: number
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function statePath(sessionId: string): string {
|
|
31
|
+
return `/tmp/moshi-opencode-hook-${sessionId}.json`
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function readState(sessionId: string): Promise<HookState> {
|
|
35
|
+
try {
|
|
36
|
+
const file = Bun.file(statePath(sessionId))
|
|
37
|
+
if (!file.size) return {}
|
|
38
|
+
return await file.json()
|
|
39
|
+
} catch {
|
|
40
|
+
return {}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function writeState(sessionId: string, patch: Partial<HookState>): Promise<void> {
|
|
45
|
+
const existing = await readState(sessionId)
|
|
46
|
+
await Bun.write(statePath(sessionId), JSON.stringify({ ...existing, ...patch }))
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function loadToken(): Promise<string | null> {
|
|
50
|
+
try {
|
|
51
|
+
const text = await Bun.file(TOKEN_PATH).text()
|
|
52
|
+
return text.trim() || null
|
|
53
|
+
} catch {
|
|
54
|
+
return null
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function sendEvent(token: string, event: AgentEvent): Promise<void> {
|
|
59
|
+
const body = JSON.stringify(event)
|
|
60
|
+
const headers = {
|
|
61
|
+
"Content-Type": "application/json",
|
|
62
|
+
"Authorization": `Bearer ${token}`,
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
const res = await fetch(API_URL, {
|
|
67
|
+
method: "POST",
|
|
68
|
+
headers,
|
|
69
|
+
body,
|
|
70
|
+
signal: AbortSignal.timeout(5000),
|
|
71
|
+
})
|
|
72
|
+
if (!res.ok && res.status >= 500) {
|
|
73
|
+
console.error(`[moshi-hooks] API error: ${res.status}`)
|
|
74
|
+
}
|
|
75
|
+
} catch (err) {
|
|
76
|
+
console.error(`[moshi-hooks] Failed to send event:`, err)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function formatToolName(toolName: string): string {
|
|
81
|
+
return toolName.charAt(0).toUpperCase() + toolName.slice(1)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export const MoshiHooks: Plugin = async ({ client, directory }) => {
|
|
85
|
+
const setupEventSubscription = async () => {
|
|
86
|
+
try {
|
|
87
|
+
const events = await client.event.subscribe()
|
|
88
|
+
for await (const event of events.stream) {
|
|
89
|
+
const token = await loadToken()
|
|
90
|
+
if (!token) break
|
|
91
|
+
|
|
92
|
+
const sessionId = (event as any).sessionId ?? "unknown"
|
|
93
|
+
const projectName = directory ? basename(directory) : undefined
|
|
94
|
+
const state = await readState(sessionId)
|
|
95
|
+
|
|
96
|
+
if (event.type === "session.created") {
|
|
97
|
+
await writeState(sessionId, {
|
|
98
|
+
sessionStartTime: Date.now() / 1000,
|
|
99
|
+
model: (event as any).properties?.model,
|
|
100
|
+
})
|
|
101
|
+
continue
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (event.type === "session.idle") {
|
|
105
|
+
const now = Date.now() / 1000
|
|
106
|
+
if (state.lastStopTime && now - state.lastStopTime < 5) continue
|
|
107
|
+
|
|
108
|
+
await writeState(sessionId, { lastStopTime: now })
|
|
109
|
+
|
|
110
|
+
const evt: AgentEvent = {
|
|
111
|
+
source: "opencode",
|
|
112
|
+
eventType: "stop",
|
|
113
|
+
sessionId,
|
|
114
|
+
category: "task_complete",
|
|
115
|
+
title: "Task Complete",
|
|
116
|
+
message: "",
|
|
117
|
+
eventId: crypto.randomUUID(),
|
|
118
|
+
projectName,
|
|
119
|
+
modelName: state.model,
|
|
120
|
+
toolName: state.lastToolName,
|
|
121
|
+
}
|
|
122
|
+
await sendEvent(token, evt)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
} catch (err) {
|
|
126
|
+
console.error(`[moshi-hooks] Event subscription error:`, err)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
setupEventSubscription()
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
"tool.execute.before": async (input, _output) => {
|
|
134
|
+
const token = await loadToken()
|
|
135
|
+
if (!token) return
|
|
136
|
+
|
|
137
|
+
const { tool, sessionID } = input
|
|
138
|
+
if (!tool || !INTERESTING_TOOLS.has(tool.toLowerCase())) return
|
|
139
|
+
|
|
140
|
+
await writeState(sessionID, { lastToolName: tool })
|
|
141
|
+
|
|
142
|
+
const state = await readState(sessionID)
|
|
143
|
+
const projectName = directory ? basename(directory) : undefined
|
|
144
|
+
|
|
145
|
+
const evt: AgentEvent = {
|
|
146
|
+
source: "opencode",
|
|
147
|
+
eventType: "pre_tool",
|
|
148
|
+
sessionId: sessionID,
|
|
149
|
+
category: "tool_running",
|
|
150
|
+
title: `Running ${formatToolName(tool)}`,
|
|
151
|
+
message: "",
|
|
152
|
+
eventId: crypto.randomUUID(),
|
|
153
|
+
projectName,
|
|
154
|
+
modelName: state.model,
|
|
155
|
+
toolName: tool,
|
|
156
|
+
}
|
|
157
|
+
await sendEvent(token, evt)
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
"tool.execute.after": async (input, output) => {
|
|
161
|
+
const token = await loadToken()
|
|
162
|
+
if (!token) return
|
|
163
|
+
|
|
164
|
+
const { tool, sessionID } = input
|
|
165
|
+
if (!tool || !INTERESTING_TOOLS.has(tool.toLowerCase())) return
|
|
166
|
+
|
|
167
|
+
const state = await readState(sessionID)
|
|
168
|
+
const projectName = directory ? basename(directory) : undefined
|
|
169
|
+
|
|
170
|
+
const evt: AgentEvent = {
|
|
171
|
+
source: "opencode",
|
|
172
|
+
eventType: "post_tool",
|
|
173
|
+
sessionId: sessionID,
|
|
174
|
+
category: "tool_finished",
|
|
175
|
+
title: `Finished ${formatToolName(tool)}`,
|
|
176
|
+
message: "",
|
|
177
|
+
eventId: crypto.randomUUID(),
|
|
178
|
+
projectName,
|
|
179
|
+
modelName: state.model,
|
|
180
|
+
toolName: tool,
|
|
181
|
+
}
|
|
182
|
+
await sendEvent(token, evt)
|
|
183
|
+
},
|
|
184
|
+
|
|
185
|
+
"permission.ask": async (input, output) => {
|
|
186
|
+
const token = await loadToken()
|
|
187
|
+
if (!token) return
|
|
188
|
+
|
|
189
|
+
const sessionID = (input as any).sessionID ?? "unknown"
|
|
190
|
+
const state = await readState(sessionID)
|
|
191
|
+
const projectName = directory ? basename(directory) : undefined
|
|
192
|
+
|
|
193
|
+
const prompt = (input as any).prompt ?? ""
|
|
194
|
+
|
|
195
|
+
const evt: AgentEvent = {
|
|
196
|
+
source: "opencode",
|
|
197
|
+
eventType: "notification",
|
|
198
|
+
sessionId: sessionID,
|
|
199
|
+
category: "approval_required",
|
|
200
|
+
title: "Permission Required",
|
|
201
|
+
message: prompt.slice(0, 256),
|
|
202
|
+
eventId: crypto.randomUUID(),
|
|
203
|
+
projectName,
|
|
204
|
+
modelName: state.model,
|
|
205
|
+
}
|
|
206
|
+
await sendEvent(token, evt)
|
|
207
|
+
},
|
|
208
|
+
}
|
|
209
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "moshi-opencode-hooks",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "OpenCode plugin for Moshi live activity integration",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/goniz/moshi-opencode-hooks"
|
|
8
|
+
},
|
|
9
|
+
"module": "index.ts",
|
|
10
|
+
"type": "module",
|
|
11
|
+
"main": "index.ts",
|
|
12
|
+
"bin": {
|
|
13
|
+
"moshi-opencode-hooks": "./src/cli.ts"
|
|
14
|
+
},
|
|
15
|
+
"exports": {
|
|
16
|
+
".": "./index.ts"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "bun build index.ts --outdir=dist --target=bun --format=esm",
|
|
20
|
+
"typecheck": "bun tsc --noEmit"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/bun": "latest"
|
|
24
|
+
},
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"typescript": "^5"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@opencode-ai/plugin": "^1.2.27"
|
|
30
|
+
}
|
|
31
|
+
}
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { homedir } from "os"
|
|
4
|
+
import { dirname } from "path"
|
|
5
|
+
|
|
6
|
+
const TOKEN_PATH = `${homedir()}/.config/moshi/token`
|
|
7
|
+
const OPENCODE_CONFIG_PATH = `${homedir()}/.config/opencode/opencode.json`
|
|
8
|
+
const PLUGIN_SOURCE = "moshi-opencode-hooks"
|
|
9
|
+
const HOOK_IDENTIFIER = "moshi-opencode-hooks"
|
|
10
|
+
|
|
11
|
+
async function loadConfig(): Promise<Record<string, unknown>> {
|
|
12
|
+
try {
|
|
13
|
+
const file = Bun.file(OPENCODE_CONFIG_PATH)
|
|
14
|
+
if (!file.size) return {}
|
|
15
|
+
return await file.json()
|
|
16
|
+
} catch {
|
|
17
|
+
return {}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function saveConfig(config: Record<string, unknown>): Promise<void> {
|
|
22
|
+
const { mkdir } = await import("fs/promises")
|
|
23
|
+
await mkdir(dirname(OPENCODE_CONFIG_PATH), { recursive: true })
|
|
24
|
+
await Bun.write(OPENCODE_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n")
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function isMoshiHook(entry: string): boolean {
|
|
28
|
+
return entry.includes(HOOK_IDENTIFIER) || entry.includes("moshi-opencode-hooks")
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function setup(): Promise<void> {
|
|
32
|
+
const config = await loadConfig()
|
|
33
|
+
const plugins: string[] = Array.isArray(config.plugin) ? config.plugin : []
|
|
34
|
+
|
|
35
|
+
const filtered = plugins.filter((p) => !isMoshiHook(p))
|
|
36
|
+
filtered.push(PLUGIN_SOURCE)
|
|
37
|
+
|
|
38
|
+
config.plugin = filtered
|
|
39
|
+
await saveConfig(config)
|
|
40
|
+
console.log(`moshi-opencode-hooks: registered in ${OPENCODE_CONFIG_PATH}`)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function uninstall(): Promise<void> {
|
|
44
|
+
const config = await loadConfig()
|
|
45
|
+
const plugins: string[] = Array.isArray(config.plugin) ? config.plugin : []
|
|
46
|
+
|
|
47
|
+
const filtered = plugins.filter((p) => !isMoshiHook(p))
|
|
48
|
+
config.plugin = filtered
|
|
49
|
+
|
|
50
|
+
await saveConfig(config)
|
|
51
|
+
console.log(`moshi-opencode-hooks: removed from ${OPENCODE_CONFIG_PATH}`)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function setToken(value: string): Promise<void> {
|
|
55
|
+
const { mkdir } = await import("fs/promises")
|
|
56
|
+
await mkdir(dirname(TOKEN_PATH), { recursive: true })
|
|
57
|
+
await Bun.write(TOKEN_PATH, value + "\n")
|
|
58
|
+
console.log(`moshi-opencode-hooks: token saved to ${TOKEN_PATH}`)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function getToken(): Promise<void> {
|
|
62
|
+
try {
|
|
63
|
+
const text = await Bun.file(TOKEN_PATH).text()
|
|
64
|
+
console.log(text.trim() || `no token found (expected at ${TOKEN_PATH})`)
|
|
65
|
+
} catch {
|
|
66
|
+
console.log(`no token found (expected at ${TOKEN_PATH})`)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function main() {
|
|
71
|
+
const subcommand = process.argv[2]
|
|
72
|
+
|
|
73
|
+
if (subcommand === "setup") {
|
|
74
|
+
await setup()
|
|
75
|
+
return
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (subcommand === "uninstall") {
|
|
79
|
+
await uninstall()
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (subcommand === "token") {
|
|
84
|
+
const value = process.argv[3]
|
|
85
|
+
if (value) {
|
|
86
|
+
await setToken(value)
|
|
87
|
+
} else {
|
|
88
|
+
await getToken()
|
|
89
|
+
}
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
console.log("Usage:")
|
|
94
|
+
console.log(" moshi-opencode-hooks setup Register plugin")
|
|
95
|
+
console.log(" moshi-opencode-hooks uninstall Remove plugin")
|
|
96
|
+
console.log(" moshi-opencode-hooks token [value] Show or set API token")
|
|
97
|
+
process.exit(0)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
main().catch((err) => {
|
|
101
|
+
console.error(`moshi-opencode-hooks: error:`, err)
|
|
102
|
+
process.exit(1)
|
|
103
|
+
})
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
// Environment setup & latest features
|
|
4
|
+
"lib": ["ESNext"],
|
|
5
|
+
"target": "ESNext",
|
|
6
|
+
"module": "Preserve",
|
|
7
|
+
"moduleDetection": "force",
|
|
8
|
+
"jsx": "react-jsx",
|
|
9
|
+
"allowJs": true,
|
|
10
|
+
|
|
11
|
+
// Bundler mode
|
|
12
|
+
"moduleResolution": "bundler",
|
|
13
|
+
"allowImportingTsExtensions": true,
|
|
14
|
+
"verbatimModuleSyntax": true,
|
|
15
|
+
"noEmit": true,
|
|
16
|
+
|
|
17
|
+
// Best practices
|
|
18
|
+
"strict": true,
|
|
19
|
+
"skipLibCheck": true,
|
|
20
|
+
"noFallthroughCasesInSwitch": true,
|
|
21
|
+
"noUncheckedIndexedAccess": true,
|
|
22
|
+
"noImplicitOverride": true,
|
|
23
|
+
|
|
24
|
+
// Some stricter flags (disabled by default)
|
|
25
|
+
"noUnusedLocals": false,
|
|
26
|
+
"noUnusedParameters": false,
|
|
27
|
+
"noPropertyAccessFromIndexSignature": false
|
|
28
|
+
}
|
|
29
|
+
}
|