mio-agent-runtime 0.3.5 → 0.3.7

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 CHANGED
@@ -10,9 +10,9 @@ Mio Agent Runtime 的可发布 CLI 包。
10
10
  npm install -g mio-agent-runtime
11
11
  ```
12
12
 
13
- > 新机器 / 升级:请安装 `>=0.3.4`(Codex 被动观察)或 `>=0.3.5`
14
- > (Codex + OpenCode 被动观察)。旧版本只有主动 MCP 规则注入,
15
- > 没有「无感存/取」。安装后执行一次 `mio init && mio install <host>`。
13
+ > 新机器 / 升级:请安装 `>=0.3.6`(Codex + OpenCode + Hermes 被动观察)。
14
+ > 旧版本只有主动 MCP 规则注入,没有「无感存/取」。安装后执行一次
15
+ > `mio init && mio install <host>`。
16
16
 
17
17
  ## Commands
18
18
 
@@ -37,6 +37,9 @@ mio --json agents Machine-readable agents
37
37
  running, restart it once so it reloads both files. `mio observe` also
38
38
  passively tails the OpenCode session database (`opencode db`) without
39
39
  requiring the model to call Mio tools.
40
+ - Hermes uses `hermes mcp add mio-intelligence --command node --args <server>`
41
+ to register the Mio MCP server. Hermes has native memory and reads
42
+ AGENTS.md Context Files, so retrieval works without Mio rule injection.
40
43
  - WorkBuddy uses `~/.workbuddy/mcp.json` and requires each custom MCP server to
41
44
  be approved in `~/.workbuddy/mcp-approvals.json`. `mio install workbuddy`
42
45
  writes both files and injects Mio usage rules into
@@ -72,6 +75,12 @@ OpenCode 会话不依赖模型自觉:`mio observe` 通过 `opencode db` 增量
72
75
  把摘要写回 `<workspace>/AGENTS.md` 的 `MIO_CONTEXT` 块,供 OpenCode 下一会话
73
76
  被动取用。首次启动只回填最近 24h 的 part,避免历史会话灌入记忆库。
74
77
 
78
+ Hermes(Nous hermes-agent)同理:`mio observe` 以只读方式增量读取
79
+ `<LocalAppData>/hermes/state.db`(`messages.id` 自增游标,无需调用 hermes CLI),
80
+ 把每个用户回合的 `task_outcome` 写入 `MIO_HOME`(`source=hermes-observer`)。
81
+ Hermes 本身有原生记忆,Mio 只负责让 Hermes 经验进入跨 Agent 生态;仅当会话
82
+ 发生在真实 git 仓库时才写回该仓库的 `AGENTS.md`,避免污染用户主目录。
83
+
75
84
  ## MIO_HOME
76
85
 
77
86
  默认 `~/.mio-intelligence`,可通过环境变量 `MIO_HOME` 覆盖。
@@ -0,0 +1,90 @@
1
+ 'use strict'
2
+
3
+ const fs = require('fs')
4
+ const path = require('path')
5
+ const os = require('os')
6
+ const { spawnSync } = require('child_process')
7
+
8
+ const SERVER_NAME = 'mio-intelligence'
9
+
10
+ function hermesBin() {
11
+ if (process.env.HERMES_BIN && fs.existsSync(process.env.HERMES_BIN)) return process.env.HERMES_BIN
12
+ const candidates = [
13
+ path.join(os.homedir(), 'AppData', 'Local', 'hermes', 'hermes-agent', 'bin', 'hermes.exe'),
14
+ path.join(os.homedir(), '.hermes', 'bin', 'hermes.exe'),
15
+ ]
16
+ for (const c of candidates) {
17
+ if (fs.existsSync(c)) return c
18
+ }
19
+ return null
20
+ }
21
+
22
+ function runHermes(args) {
23
+ const bin = hermesBin()
24
+ // `hermes mcp add` asks to confirm tool enablement interactively; pre-answer
25
+ // it so the install is non-interactive.
26
+ const input = args[0] === 'mcp' && args[1] === 'add' ? 'Y\n' : undefined
27
+ let res
28
+ try {
29
+ res = bin
30
+ ? spawnSync(bin, args, { encoding: 'utf8', timeout: 60000, windowsHide: true, input: input })
31
+ : spawnSync('hermes', args, { encoding: 'utf8', timeout: 60000, shell: true, windowsHide: true, input: input })
32
+ } catch (_) {
33
+ return null
34
+ }
35
+ if (res.error || res.status !== 0) return null
36
+ return (res.stdout || '') + (res.stderr || '')
37
+ }
38
+
39
+ function isInstalled() {
40
+ const out = runHermes(['mcp', 'list'])
41
+ return Boolean(out && out.indexOf(SERVER_NAME) !== -1)
42
+ }
43
+
44
+ function install({ node, serverScript, home, workspace, project }) {
45
+ if (!hermesBin() && !isHermesOnPath()) {
46
+ return {
47
+ changed: false,
48
+ message: 'Hermes binary not found. Install hermes-agent first.',
49
+ configPath: null,
50
+ }
51
+ }
52
+ if (isInstalled()) {
53
+ return { changed: false, message: 'Hermes Mio MCP already installed.', configPath: null }
54
+ }
55
+
56
+ const context = JSON.stringify({
57
+ agentId: 'hermes',
58
+ project: project || path.basename(workspace || process.cwd()),
59
+ workspace: workspace || process.cwd(),
60
+ sessionId: 'hermes-session',
61
+ })
62
+
63
+ const args = [
64
+ 'mcp', 'add', SERVER_NAME,
65
+ '--command', node,
66
+ '--args', serverScript,
67
+ '--env', 'MIO_DATA_DIR=' + home, 'MIO_CONTEXT=' + context,
68
+ ]
69
+ const out = runHermes(args)
70
+ const installed = isInstalled()
71
+
72
+ let message
73
+ if (installed) {
74
+ message = 'Hermes Mio MCP installed (config: <LocalAppData>/hermes/config.yaml). Start a new Hermes session to load it.'
75
+ } else {
76
+ message = 'Hermes mcp add returned but Mio was not detected. Run `hermes mcp add ' + SERVER_NAME + ' --command ' + node + ' --args ' + serverScript + '` manually and answer Y to the prompt.'
77
+ }
78
+ return { changed: installed, configPath: null, message }
79
+ }
80
+
81
+ function isHermesOnPath() {
82
+ try {
83
+ const res = spawnSync('where', ['hermes'], { encoding: 'utf8', shell: true })
84
+ return res.status === 0 && /hermes/.test(res.stdout || '')
85
+ } catch (_) {
86
+ return false
87
+ }
88
+ }
89
+
90
+ module.exports = { hermesBin, isInstalled, install }
package/bin/mio.js CHANGED
@@ -1,249 +1,255 @@
1
- #!/usr/bin/env node
2
- 'use strict'
3
-
4
- const fs = require('fs')
5
- const path = require('path')
6
- const os = require('os')
7
-
8
- const SERVER_SCRIPT = path.resolve(__dirname, '..', 'server', 'mio-intelligence-mcp', 'index.js')
9
- const MIO_HOME = process.env.MIO_HOME || path.join(os.homedir(), '.mio-intelligence')
10
- const CONFIG_FILE = path.join(MIO_HOME, 'config.json')
11
-
12
- const adapters = {
13
- codex: require('../adapters/codex.js'),
14
- opencode: require('../adapters/opencode.js'),
15
- workbuddy: require('../adapters/workbuddy.js'),
16
- }
17
- const observer = require('../observe/observer.js')
18
-
19
- function ensureHome() {
20
- fs.mkdirSync(MIO_HOME, { recursive: true })
21
- fs.mkdirSync(path.join(MIO_HOME, 'logs'), { recursive: true })
22
- }
23
-
24
- function readConfig() {
25
- const fallback = { version: 1, home: MIO_HOME, agents: {} }
26
- if (!fs.existsSync(CONFIG_FILE)) return fallback
27
- try {
28
- return { ...fallback, ...JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8')) }
29
- } catch (_) {
30
- return fallback
31
- }
32
- }
33
-
34
- function writeConfig(config) {
35
- ensureHome()
36
- fs.writeFileSync(CONFIG_FILE, `${JSON.stringify(config, null, 2)}\n`, 'utf8')
37
- }
38
-
39
- function jsonOrText(payload, useJson) {
40
- if (useJson) {
41
- console.log(JSON.stringify(payload, null, 2))
42
- return
43
- }
44
- return payload
45
- }
46
-
47
- function initCommand() {
48
- ensureHome()
49
- const config = readConfig()
50
- if (!config.createdAt) config.createdAt = new Date().toISOString()
51
- writeConfig(config)
52
- console.log(`MIO_HOME ready: ${MIO_HOME}`)
53
- }
54
-
55
- function statusCommand(useJson) {
56
- const config = readConfig()
57
- const installed = {
58
- codex: adapters.codex.isInstalled(),
59
- opencode: adapters.opencode.isInstalled(),
60
- workbuddy: adapters.workbuddy.isInstalled(),
61
- }
62
- const payload = {
63
- version: config.version,
64
- home: MIO_HOME,
65
- serverScript: SERVER_SCRIPT,
66
- serverScriptExists: fs.existsSync(SERVER_SCRIPT),
67
- agents: config.agents || {},
68
- codexInstalled: installed.codex,
69
- opencodeInstalled: installed.opencode,
70
- workbuddyInstalled: installed.workbuddy,
1
+ #!/usr/bin/env node
2
+ 'use strict'
3
+
4
+ const fs = require('fs')
5
+ const path = require('path')
6
+ const os = require('os')
7
+
8
+ const SERVER_SCRIPT = path.resolve(__dirname, '..', 'server', 'mio-intelligence-mcp', 'index.js')
9
+ const MIO_HOME = process.env.MIO_HOME || path.join(os.homedir(), '.mio-intelligence')
10
+ const CONFIG_FILE = path.join(MIO_HOME, 'config.json')
11
+
12
+ const adapters = {
13
+ codex: require('../adapters/codex.js'),
14
+ opencode: require('../adapters/opencode.js'),
15
+ workbuddy: require('../adapters/workbuddy.js'),
16
+ hermes: require('../adapters/hermes.js'),
17
+ }
18
+ const observer = require('../observe/observer.js')
19
+
20
+ function ensureHome() {
21
+ fs.mkdirSync(MIO_HOME, { recursive: true })
22
+ fs.mkdirSync(path.join(MIO_HOME, 'logs'), { recursive: true })
23
+ }
24
+
25
+ function readConfig() {
26
+ const fallback = { version: 1, home: MIO_HOME, agents: {} }
27
+ if (!fs.existsSync(CONFIG_FILE)) return fallback
28
+ try {
29
+ return { ...fallback, ...JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8')) }
30
+ } catch (_) {
31
+ return fallback
32
+ }
33
+ }
34
+
35
+ function writeConfig(config) {
36
+ ensureHome()
37
+ fs.writeFileSync(CONFIG_FILE, `${JSON.stringify(config, null, 2)}\n`, 'utf8')
38
+ }
39
+
40
+ function jsonOrText(payload, useJson) {
41
+ if (useJson) {
42
+ console.log(JSON.stringify(payload, null, 2))
43
+ return
44
+ }
45
+ return payload
46
+ }
47
+
48
+ function initCommand() {
49
+ ensureHome()
50
+ const config = readConfig()
51
+ if (!config.createdAt) config.createdAt = new Date().toISOString()
52
+ writeConfig(config)
53
+ console.log(`MIO_HOME ready: ${MIO_HOME}`)
54
+ }
55
+
56
+ function statusCommand(useJson) {
57
+ const config = readConfig()
58
+ const installed = {
59
+ codex: adapters.codex.isInstalled(),
60
+ opencode: adapters.opencode.isInstalled(),
61
+ workbuddy: adapters.workbuddy.isInstalled(),
62
+ hermes: adapters.hermes.isInstalled(),
63
+ }
64
+ const payload = {
65
+ version: config.version,
66
+ home: MIO_HOME,
67
+ serverScript: SERVER_SCRIPT,
68
+ serverScriptExists: fs.existsSync(SERVER_SCRIPT),
69
+ agents: config.agents || {},
70
+ codexInstalled: installed.codex,
71
+ opencodeInstalled: installed.opencode,
72
+ workbuddyInstalled: installed.workbuddy,
73
+ hermesInstalled: installed.hermes,
74
+ hermesObserverReady: fs.existsSync(observer.hermesDbPath()),
71
75
  opencodeObserverReady: fs.existsSync(observer.opencodeDbPath()),
72
- observerRunning: observer.isRunning(MIO_HOME),
73
- observerPid: observer.isRunning(MIO_HOME) ? observer.readPid(MIO_HOME) : null,
74
- }
75
- if (useJson) {
76
- jsonOrText(payload, true)
77
- return
78
- }
79
- console.log(`MIO_HOME: ${MIO_HOME}`)
80
- console.log(`MCP Server: ${SERVER_SCRIPT}`)
81
- console.log(`Codex MCP: ${installed.codex ? 'installed' : 'not installed'}`)
76
+ observerRunning: observer.isRunning(MIO_HOME),
77
+ observerPid: observer.isRunning(MIO_HOME) ? observer.readPid(MIO_HOME) : null,
78
+ }
79
+ if (useJson) {
80
+ jsonOrText(payload, true)
81
+ return
82
+ }
83
+ console.log(`MIO_HOME: ${MIO_HOME}`)
84
+ console.log(`MCP Server: ${SERVER_SCRIPT}`)
85
+ console.log(`Codex MCP: ${installed.codex ? 'installed' : 'not installed'}`)
82
86
  console.log(`OpenCode MCP: ${installed.opencode ? 'installed' : 'not installed'}`)
83
- console.log(`OpenCode observer: ${fs.existsSync(observer.opencodeDbPath()) ? 'ready' : 'no database'}`)
84
- console.log(`WorkBuddy MCP: ${installed.workbuddy ? 'installed' : 'not installed'}`)
85
- console.log(`Observer: ${observer.isRunning(MIO_HOME) ? 'running (pid ' + observer.readPid(MIO_HOME) + ')' : 'not running'}`)
86
- }
87
-
88
- function agentsCommand(useJson) {
89
- const config = readConfig()
90
- const agents = config.agents || {}
91
- if (useJson) {
92
- jsonOrText(agents, true)
93
- return
94
- }
95
- const names = Object.keys(agents)
96
- if (names.length === 0) {
97
- console.log('No agents installed.')
98
- return
99
- }
100
- for (const name of names) {
101
- console.log(`${name}\t${agents[name].installedAt || ''}`)
102
- }
103
- }
104
-
105
- function installCommand(host, useJson) {
106
- if (!host) {
107
- console.error('Usage: mio install <codex|opencode|workbuddy>')
108
- process.exitCode = 1
109
- return
110
- }
111
- const adapter = adapters[host]
112
- if (!adapter) {
113
- console.error(`Unknown host: ${host}`)
114
- process.exitCode = 1
115
- return
116
- }
117
- ensureHome()
118
- const config = readConfig()
119
- let result
120
- try {
121
- result = adapter.install({
122
- node: process.execPath,
123
- serverScript: SERVER_SCRIPT,
124
- home: MIO_HOME,
125
- workspace: process.cwd(),
126
- })
127
- } catch (error) {
128
- console.error(`Failed to install ${host}: ${error.message || error}`)
129
- process.exitCode = 1
130
- return
131
- }
132
- if (result.changed) {
133
- config.agents = config.agents || {}
134
- config.agents[host] = {
135
- installedAt: new Date().toISOString(),
136
- configPath: result.configPath || null,
137
- }
138
- writeConfig(config)
139
- }
140
- try {
141
- observer.runCycle(MIO_HOME, observer.loadState(MIO_HOME), observer.createSinks(MIO_HOME))
142
- } catch (_) {}
143
- let observerStarted = false
144
- try {
145
- observerStarted = observer.startBackground(MIO_HOME).started
146
- } catch (_) {}
147
- if (useJson) {
148
- result.observer = {
149
- running: observer.isRunning(MIO_HOME),
150
- started: observerStarted,
151
- }
152
- jsonOrText(result, true)
153
- return
154
- }
155
- console.log(result.message || `${host} adapter completed.`)
156
- console.log(
157
- observer.isRunning(MIO_HOME)
158
- ? 'Observer daemon running.'
159
- : 'Observer daemon not running. Run `mio observe --start`.'
160
- )
161
- }
162
-
163
- function mcpCommand() {
164
- require(SERVER_SCRIPT)
165
- }
166
-
167
- function observeCommand(args) {
168
- const flags = args.slice(1)
169
- const useJson = flags.includes('--json')
170
- if (flags.includes('--start')) {
171
- const result = observer.startBackground(MIO_HOME)
172
- if (useJson) return jsonOrText(result, true)
173
- console.log(result.started ? 'Observer started.' : 'Observer already running.')
174
- return
175
- }
176
- if (flags.includes('--stop')) {
177
- const result = observer.stopBackground(MIO_HOME)
178
- if (useJson) return jsonOrText(result, true)
179
- console.log(result.stopped ? 'Observer stopped.' : 'Observer was not running.')
180
- return
181
- }
182
- if (flags.includes('--status')) {
183
- const running = observer.isRunning(MIO_HOME)
184
- const pid = observer.readPid(MIO_HOME)
185
- if (useJson) return jsonOrText({ running, pid }, true)
186
- console.log(running ? 'Observer: running (pid ' + pid + ')' : 'Observer: not running')
187
- return
188
- }
189
- if (flags.includes('--once')) {
190
- const res = observer.runCycle(
191
- MIO_HOME,
192
- observer.loadState(MIO_HOME),
193
- observer.createSinks(MIO_HOME)
194
- )
195
- if (useJson) return jsonOrText(res, true)
196
- console.log('Observed files=' + res.files + ' lines=' + res.lines + ' outcomes=' + res.outcomes)
197
- return
198
- }
199
- console.log('Observing WorkBuddy transcripts into ' + MIO_HOME + ' (Ctrl+C to stop)...')
200
- observer.startForeground(MIO_HOME)
201
- }
202
-
203
- function help() {
204
- console.log(`Mio Agent Runtime CLI
205
-
206
- Usage:
207
- mio init Initialize MIO_HOME
208
- mio mcp Start Mio MCP server (stdio)
209
- mio install <host> Install Mio into a host (codex|opencode|workbuddy)
210
- mio status Show runtime and adapter status
211
- mio agents List installed agents
212
- mio observe Watch WorkBuddy transcripts and auto-ingest task outcomes
213
- mio observe --start|--stop|--status|--once Manage the background observer daemon
214
- mio --json status Machine-readable status
215
- mio --json agents Machine-readable agents
216
- `)
217
- }
218
-
219
- function main() {
220
- const args = process.argv.slice(2)
221
- const jsonIndex = args.indexOf('--json')
222
- const useJson = jsonIndex !== -1
223
- if (useJson) args.splice(jsonIndex, 1)
224
- const command = args[0] || 'help'
225
-
226
- switch (command) {
227
- case 'init':
228
- return initCommand()
229
- case 'mcp':
230
- return mcpCommand()
231
- case 'install':
232
- return installCommand(args[1], useJson)
233
- case 'status':
234
- return statusCommand(useJson)
235
- case 'agents':
236
- return agentsCommand(useJson)
237
- case 'observe':
238
- return observeCommand(args)
239
- case 'help':
240
- case '--help':
241
- return help()
242
- default:
243
- console.error(`Unknown command: ${command}`)
244
- help()
245
- process.exitCode = 1
246
- }
247
- }
248
-
249
- main()
87
+ console.log(`OpenCode observer: ${fs.existsSync(observer.opencodeDbPath()) ? 'ready' : 'no database'}`)
88
+ console.log(`WorkBuddy MCP: ${installed.workbuddy ? 'installed' : 'not installed'}`)
89
+ console.log(`Hermes MCP: ${installed.hermes ? 'installed' : 'not installed'}`)
90
+ console.log(`Hermes observer: ${fs.existsSync(observer.hermesDbPath()) ? 'ready' : 'no database'}`)
91
+ console.log(`Observer: ${observer.isRunning(MIO_HOME) ? 'running (pid ' + observer.readPid(MIO_HOME) + ')' : 'not running'}`)
92
+ }
93
+
94
+ function agentsCommand(useJson) {
95
+ const config = readConfig()
96
+ const agents = config.agents || {}
97
+ if (useJson) {
98
+ jsonOrText(agents, true)
99
+ return
100
+ }
101
+ const names = Object.keys(agents)
102
+ if (names.length === 0) {
103
+ console.log('No agents installed.')
104
+ return
105
+ }
106
+ for (const name of names) {
107
+ console.log(`${name}\t${agents[name].installedAt || ''}`)
108
+ }
109
+ }
110
+
111
+ function installCommand(host, useJson) {
112
+ if (!host) {
113
+ console.error('Usage: mio install <codex|opencode|workbuddy|hermes>')
114
+ process.exitCode = 1
115
+ return
116
+ }
117
+ const adapter = adapters[host]
118
+ if (!adapter) {
119
+ console.error(`Unknown host: ${host}`)
120
+ process.exitCode = 1
121
+ return
122
+ }
123
+ ensureHome()
124
+ const config = readConfig()
125
+ let result
126
+ try {
127
+ result = adapter.install({
128
+ node: process.execPath,
129
+ serverScript: SERVER_SCRIPT,
130
+ home: MIO_HOME,
131
+ workspace: process.cwd(),
132
+ })
133
+ } catch (error) {
134
+ console.error(`Failed to install ${host}: ${error.message || error}`)
135
+ process.exitCode = 1
136
+ return
137
+ }
138
+ if (result.changed) {
139
+ config.agents = config.agents || {}
140
+ config.agents[host] = {
141
+ installedAt: new Date().toISOString(),
142
+ configPath: result.configPath || null,
143
+ }
144
+ writeConfig(config)
145
+ }
146
+ try {
147
+ observer.runCycle(MIO_HOME, observer.loadState(MIO_HOME), observer.createSinks(MIO_HOME))
148
+ } catch (_) {}
149
+ let observerStarted = false
150
+ try {
151
+ observerStarted = observer.startBackground(MIO_HOME).started
152
+ } catch (_) {}
153
+ if (useJson) {
154
+ result.observer = {
155
+ running: observer.isRunning(MIO_HOME),
156
+ started: observerStarted,
157
+ }
158
+ jsonOrText(result, true)
159
+ return
160
+ }
161
+ console.log(result.message || `${host} adapter completed.`)
162
+ console.log(
163
+ observer.isRunning(MIO_HOME)
164
+ ? 'Observer daemon running.'
165
+ : 'Observer daemon not running. Run `mio observe --start`.'
166
+ )
167
+ }
168
+
169
+ function mcpCommand() {
170
+ require(SERVER_SCRIPT)
171
+ }
172
+
173
+ function observeCommand(args) {
174
+ const flags = args.slice(1)
175
+ const useJson = flags.includes('--json')
176
+ if (flags.includes('--start')) {
177
+ const result = observer.startBackground(MIO_HOME)
178
+ if (useJson) return jsonOrText(result, true)
179
+ console.log(result.started ? 'Observer started.' : 'Observer already running.')
180
+ return
181
+ }
182
+ if (flags.includes('--stop')) {
183
+ const result = observer.stopBackground(MIO_HOME)
184
+ if (useJson) return jsonOrText(result, true)
185
+ console.log(result.stopped ? 'Observer stopped.' : 'Observer was not running.')
186
+ return
187
+ }
188
+ if (flags.includes('--status')) {
189
+ const running = observer.isRunning(MIO_HOME)
190
+ const pid = observer.readPid(MIO_HOME)
191
+ if (useJson) return jsonOrText({ running, pid }, true)
192
+ console.log(running ? 'Observer: running (pid ' + pid + ')' : 'Observer: not running')
193
+ return
194
+ }
195
+ if (flags.includes('--once')) {
196
+ const res = observer.runCycle(
197
+ MIO_HOME,
198
+ observer.loadState(MIO_HOME),
199
+ observer.createSinks(MIO_HOME)
200
+ )
201
+ if (useJson) return jsonOrText(res, true)
202
+ console.log('Observed files=' + res.files + ' lines=' + res.lines + ' outcomes=' + res.outcomes)
203
+ return
204
+ }
205
+ console.log('Observing WorkBuddy transcripts into ' + MIO_HOME + ' (Ctrl+C to stop)...')
206
+ observer.startForeground(MIO_HOME)
207
+ }
208
+
209
+ function help() {
210
+ console.log(`Mio Agent Runtime CLI
211
+
212
+ Usage:
213
+ mio init Initialize MIO_HOME
214
+ mio mcp Start Mio MCP server (stdio)
215
+ mio install <host> Install Mio into a host (codex|opencode|workbuddy|hermes)
216
+ mio status Show runtime and adapter status
217
+ mio agents List installed agents
218
+ mio observe Watch WorkBuddy transcripts and auto-ingest task outcomes
219
+ mio observe --start|--stop|--status|--once Manage the background observer daemon
220
+ mio --json status Machine-readable status
221
+ mio --json agents Machine-readable agents
222
+ `)
223
+ }
224
+
225
+ function main() {
226
+ const args = process.argv.slice(2)
227
+ const jsonIndex = args.indexOf('--json')
228
+ const useJson = jsonIndex !== -1
229
+ if (useJson) args.splice(jsonIndex, 1)
230
+ const command = args[0] || 'help'
231
+
232
+ switch (command) {
233
+ case 'init':
234
+ return initCommand()
235
+ case 'mcp':
236
+ return mcpCommand()
237
+ case 'install':
238
+ return installCommand(args[1], useJson)
239
+ case 'status':
240
+ return statusCommand(useJson)
241
+ case 'agents':
242
+ return agentsCommand(useJson)
243
+ case 'observe':
244
+ return observeCommand(args)
245
+ case 'help':
246
+ case '--help':
247
+ return help()
248
+ default:
249
+ console.error(`Unknown command: ${command}`)
250
+ help()
251
+ process.exitCode = 1
252
+ }
253
+ }
254
+
255
+ main()