mio-agent-runtime 0.2.2 → 0.2.3

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
@@ -1,49 +1,54 @@
1
- # mio-agent-runtime
2
-
3
- Mio Agent Runtime 的可发布 CLI 包。
4
-
5
- 安装一次,所有 Agent 自动接入 Mio。
6
-
7
- ## Install
8
-
9
- ```bash
10
- npm install -g mio-agent-runtime
11
- ```
12
-
13
- ## Commands
14
-
15
- ```text
16
- mio init Initialize MIO_HOME
17
- mio mcp Start Mio MCP server (stdio)
18
- mio install <host> Install Mio into a host (codex|opencode|workbuddy)
19
- mio status Show runtime and adapter status
20
- mio agents List installed agents
21
- mio --json status Machine-readable status
22
- mio --json agents Machine-readable agents
23
- ```
24
-
1
+ # mio-agent-runtime
2
+
3
+ Mio Agent Runtime 的可发布 CLI 包。
4
+
5
+ 安装一次,所有 Agent 自动接入 Mio。
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install -g mio-agent-runtime
11
+ ```
12
+
13
+ ## Commands
14
+
15
+ ```text
16
+ mio init Initialize MIO_HOME
17
+ mio mcp Start Mio MCP server (stdio)
18
+ mio install <host> Install Mio into a host (codex|opencode|workbuddy)
19
+ mio status Show runtime and adapter status
20
+ mio agents List installed agents
21
+ mio --json status Machine-readable status
22
+ mio --json agents Machine-readable agents
23
+ ```
24
+
25
25
  ## Host notes
26
26
 
27
+ - OpenCode uses `~/.config/opencode/opencode.json` for MCP registration and
28
+ `~/.config/opencode/AGENTS.md` for global instructions. `mio install opencode`
29
+ writes both files and injects Mio usage rules into `AGENTS.md` so OpenCode
30
+ calls Mio memory/observer/policy automatically. If OpenCode is already
31
+ running, restart it once so it reloads both files.
27
32
  - WorkBuddy uses `~/.workbuddy/mcp.json` and requires each custom MCP server to
28
33
  be approved in `~/.workbuddy/mcp-approvals.json`. `mio install workbuddy`
29
34
  writes both files and injects Mio usage rules into `~/.workbuddy/MEMORY.md`
30
35
  so WorkBuddy calls Mio memory/observer/policy automatically. If WorkBuddy is
31
- already running, restart it once so it reloads the approval and memory files.
32
-
33
- ## MIO_HOME
34
-
35
- 默认 `~/.mio-intelligence`,可通过环境变量 `MIO_HOME` 覆盖。
36
-
37
- ## Package layout
38
-
39
- ```text
40
- packages/mio-cli
41
- ├── bin/mio.js
42
- ├── adapters/
43
- │ ├── codex.js
44
- │ ├── opencode.js
45
- │ └── workbuddy.js
46
- ├── server/
47
- │ └── mio-intelligence-mcp/index.js
48
- └── package.json
49
- ```
36
+ already running, restart it once so it reloads the approval and memory files.
37
+
38
+ ## MIO_HOME
39
+
40
+ 默认 `~/.mio-intelligence`,可通过环境变量 `MIO_HOME` 覆盖。
41
+
42
+ ## Package layout
43
+
44
+ ```text
45
+ packages/mio-cli
46
+ ├── bin/mio.js
47
+ ├── adapters/
48
+ │ ├── codex.js
49
+ │ ├── opencode.js
50
+ │ └── workbuddy.js
51
+ ├── server/
52
+ │ └── mio-intelligence-mcp/index.js
53
+ └── package.json
54
+ ```
package/adapters/codex.js CHANGED
@@ -1,63 +1,62 @@
1
- 'use strict'
2
-
3
- const fs = require('fs')
4
- const path = require('path')
5
- const os = require('os')
6
-
7
- function configPath() {
8
- return path.join(os.homedir(), '.codex', 'config.toml')
9
- }
10
-
11
- function isInstalled() {
12
- const target = configPath()
13
- if (!fs.existsSync(target)) return false
14
- return fs.readFileSync(target, 'utf8').includes('[mcp_servers.mio-intelligence]')
15
- }
16
-
17
- function install({ node, serverScript, home, workspace, project }) {
18
- const target = configPath()
19
- const backup = `${target}.bak-mio-${Date.now()}`
20
- if (isInstalled()) {
21
- return {
22
- changed: false,
23
- configPath: target,
24
- message: 'Codex Mio MCP already installed.',
25
- }
26
- }
27
-
28
- if (fs.existsSync(target)) {
29
- fs.copyFileSync(target, backup)
30
- }
31
-
32
- const context = JSON.stringify({
33
- agentId: 'codex',
34
- project: project || path.basename(workspace || process.cwd()),
35
- workspace: workspace || process.cwd(),
36
- sessionId: 'codex-session',
37
- })
38
-
39
- fs.mkdirSync(path.dirname(target), { recursive: true })
40
- let toml = fs.existsSync(target) ? fs.readFileSync(target, 'utf8') : ''
41
- if (!toml.endsWith('\n')) toml += '\n'
42
- const section = [
43
- '[mcp_servers.mio-intelligence]',
44
- `command = '${node}'`,
45
- `args = ['${serverScript}']`,
46
- 'startup_timeout_sec = 30',
47
- '',
48
- '[mcp_servers.mio-intelligence.env]',
49
- `MIO_DATA_DIR = '${home}'`,
50
- `MIO_CONTEXT = '${context}'`,
51
- '',
52
- ].join('\n')
53
- fs.writeFileSync(target, `${toml}${section}\n`, 'utf8')
54
-
55
- return {
56
- changed: true,
57
- configPath: target,
58
- backup,
59
- message: 'Codex Mio MCP installed. Restart Codex to load it.',
60
- }
61
- }
62
-
1
+ 'use strict'
2
+
3
+ const fs = require('fs')
4
+ const path = require('path')
5
+ const os = require('os')
6
+
7
+ function configPath() {
8
+ return path.join(os.homedir(), '.codex', 'config.toml')
9
+ }
10
+
11
+ function isInstalled() {
12
+ const target = configPath()
13
+ if (!fs.existsSync(target)) return false
14
+ return fs.readFileSync(target, 'utf8').includes('[mcp_servers.mio-intelligence]')
15
+ }
16
+
17
+ function install({ node, serverScript, home, workspace }) {
18
+ const target = configPath()
19
+ const backup = `${target}.bak-mio-${Date.now()}`
20
+ if (isInstalled()) {
21
+ return {
22
+ changed: false,
23
+ configPath: target,
24
+ message: 'Codex Mio MCP already installed.',
25
+ }
26
+ }
27
+
28
+ if (fs.existsSync(target)) {
29
+ fs.copyFileSync(target, backup)
30
+ }
31
+
32
+ const context = JSON.stringify({
33
+ agentId: 'codex',
34
+ workspace: workspace || process.cwd(),
35
+ sessionId: 'codex-session',
36
+ })
37
+
38
+ fs.mkdirSync(path.dirname(target), { recursive: true })
39
+ let toml = fs.existsSync(target) ? fs.readFileSync(target, 'utf8') : ''
40
+ if (!toml.endsWith('\n')) toml += '\n'
41
+ const section = [
42
+ '[mcp_servers.mio-intelligence]',
43
+ `command = '${node}'`,
44
+ `args = ['${serverScript}']`,
45
+ 'startup_timeout_sec = 30',
46
+ '',
47
+ '[mcp_servers.mio-intelligence.env]',
48
+ `MIO_DATA_DIR = '${home}'`,
49
+ `MIO_CONTEXT = '${context}'`,
50
+ '',
51
+ ].join('\n')
52
+ fs.writeFileSync(target, `${toml}${section}\n`, 'utf8')
53
+
54
+ return {
55
+ changed: true,
56
+ configPath: target,
57
+ backup,
58
+ message: 'Codex Mio MCP installed. Restart Codex to load it.',
59
+ }
60
+ }
61
+
63
62
  module.exports = { configPath, isInstalled, install }
@@ -4,10 +4,29 @@ const fs = require('fs')
4
4
  const path = require('path')
5
5
  const os = require('os')
6
6
 
7
+ const INSTRUCTIONS_BEGIN = '<!-- MIO_INTELLIGENCE_BEGIN -->'
8
+ const INSTRUCTIONS_END = '<!-- MIO_INTELLIGENCE_END -->'
9
+ const INSTRUCTIONS_BLOCK = `${INSTRUCTIONS_BEGIN}
10
+ ## Mio Intelligence Layer
11
+
12
+ Mio MCP tools are available. Treat these as mandatory runtime rules for every workspace.
13
+
14
+ - Before designing or implementing a solution, or when unsure about prior decisions, call \`mio-intelligence_mio_memory_query\` first. Omit \`project\` unless a specific project is required; Mio derives it from the current Git repository.
15
+ - After a decision affecting architecture, deployment, dependencies, or long-term behavior, call \`mio-intelligence_mio_memory_record\` with \`kind\`: \`decision\`, \`context\`, \`problem\`, or \`note\`.
16
+ - When a tool call fails, retries, or returns an error, call \`mio-intelligence_mio_observer_ingest\` with a stable \`trace_id\`, relevant \`event_type\`, \`payload\`, and \`outcome\`.
17
+ - Before a high-risk operation such as deletion, migration, credential change, or production change, call \`mio-intelligence_mio_policy_check\` and follow its suggestion when available.
18
+ - If Mio MCP is unavailable, do not block the task. Continue normally and note the missed memory or observation.
19
+ - Do not store secrets, credentials, or raw sensitive content in Mio memory.
20
+ ${INSTRUCTIONS_END}`
21
+
7
22
  function configPath() {
8
23
  return path.join(os.homedir(), '.config', 'opencode', 'opencode.json')
9
24
  }
10
25
 
26
+ function globalInstructionsPath() {
27
+ return path.join(os.homedir(), '.config', 'opencode', 'AGENTS.md')
28
+ }
29
+
11
30
  function readConfig() {
12
31
  const target = configPath()
13
32
  if (!fs.existsSync(target)) return null
@@ -18,59 +37,112 @@ function readConfig() {
18
37
  }
19
38
  }
20
39
 
21
- function isInstalled() {
22
- const config = readConfig()
23
- return Boolean(config && config.mcp && config.mcp['mio-intelligence'])
40
+ function readGlobalInstructions() {
41
+ const target = globalInstructionsPath()
42
+ if (!fs.existsSync(target)) return ''
43
+ try {
44
+ return fs.readFileSync(target, 'utf8')
45
+ } catch (_) {
46
+ return ''
47
+ }
24
48
  }
25
49
 
26
- function install({ node, serverScript, home, workspace, project }) {
27
- const target = configPath()
28
- if (isInstalled()) {
29
- return {
30
- changed: false,
31
- configPath: target,
32
- message: 'OpenCode Mio MCP already installed.',
33
- }
50
+ function ensureGlobalInstructions() {
51
+ const target = globalInstructionsPath()
52
+ let content = readGlobalInstructions()
53
+ const original = content
54
+ const start = content.indexOf(INSTRUCTIONS_BEGIN)
55
+ if (start !== -1) {
56
+ const end = content.indexOf(INSTRUCTIONS_END, start)
57
+ content = end === -1 ? content.slice(0, start) : content.slice(0, start) + content.slice(end + INSTRUCTIONS_END.length)
58
+ content = content.trimEnd()
59
+ }
60
+ if (content) content += '\n\n'
61
+ content += INSTRUCTIONS_BLOCK + '\n'
62
+ if (content === original) {
63
+ return { changed: false, target, backup: null }
34
64
  }
35
65
 
36
66
  const backup = `${target}.bak-mio-${Date.now()}`
37
67
  if (fs.existsSync(target)) {
38
68
  fs.copyFileSync(target, backup)
39
69
  }
70
+ fs.mkdirSync(path.dirname(target), { recursive: true })
71
+ fs.writeFileSync(target, content, 'utf8')
72
+ return { changed: true, target, backup }
73
+ }
40
74
 
75
+ function isInstalled() {
76
+ const config = readConfig()
77
+ if (!config || !config.mcp || !config.mcp['mio-intelligence']) return false
78
+ const instructions = readGlobalInstructions()
79
+ return instructions.includes(INSTRUCTIONS_BEGIN) && instructions.includes(INSTRUCTIONS_END)
80
+ }
81
+
82
+ function install({ node, serverScript, home, workspace, project }) {
83
+ const target = configPath()
41
84
  const config = readConfig() || {}
42
- const existingMcp = config.mcp && typeof config.mcp === 'object' ? config.mcp : {}
43
- const context = JSON.stringify({
44
- agentId: 'opencode',
45
- project: project || path.basename(workspace || process.cwd()),
46
- workspace: workspace || process.cwd(),
47
- sessionId: 'opencode-session',
48
- })
49
- const next = {
50
- ...config,
51
- mcp: {
52
- ...existingMcp,
53
- 'mio-intelligence': {
54
- enabled: true,
55
- type: 'local',
56
- command: [node, serverScript],
57
- environment: {
58
- MIO_DATA_DIR: home,
59
- MIO_CONTEXT: context,
85
+ const configInstalled = Boolean(config.mcp && config.mcp['mio-intelligence'])
86
+
87
+ let configChanged = false
88
+ let backup = null
89
+
90
+ if (!configInstalled) {
91
+ backup = `${target}.bak-mio-${Date.now()}`
92
+ if (fs.existsSync(target)) {
93
+ fs.copyFileSync(target, backup)
94
+ }
95
+
96
+ const existingMcp = config.mcp && typeof config.mcp === 'object' ? config.mcp : {}
97
+ const context = JSON.stringify({
98
+ agentId: 'opencode',
99
+ project: project || path.basename(workspace || process.cwd()),
100
+ workspace: workspace || process.cwd(),
101
+ sessionId: 'opencode-session',
102
+ })
103
+ const next = {
104
+ ...config,
105
+ mcp: {
106
+ ...existingMcp,
107
+ 'mio-intelligence': {
108
+ enabled: true,
109
+ type: 'local',
110
+ command: [node, serverScript],
111
+ environment: {
112
+ MIO_DATA_DIR: home,
113
+ MIO_CONTEXT: context,
114
+ },
60
115
  },
61
116
  },
62
- },
117
+ }
118
+
119
+ fs.mkdirSync(path.dirname(target), { recursive: true })
120
+ fs.writeFileSync(target, `${JSON.stringify(next, null, 2)}\n`, 'utf8')
121
+ configChanged = true
63
122
  }
64
123
 
65
- fs.mkdirSync(path.dirname(target), { recursive: true })
66
- fs.writeFileSync(target, `${JSON.stringify(next, null, 2)}\n`, 'utf8')
124
+ const instructionsResult = ensureGlobalInstructions()
125
+ const changed = configChanged || instructionsResult.changed
126
+
127
+ let message
128
+ if (configChanged && instructionsResult.changed) {
129
+ message = 'OpenCode Mio MCP installed and global Mio rules injected into AGENTS.md. Restart OpenCode to load them.'
130
+ } else if (instructionsResult.changed) {
131
+ message = 'OpenCode Mio MCP already installed. Injected global Mio rules into AGENTS.md. Restart OpenCode to load them.'
132
+ } else {
133
+ message = 'OpenCode Mio MCP already installed.'
134
+ }
67
135
 
68
136
  return {
69
- changed: true,
137
+ changed,
70
138
  configPath: target,
139
+ globalInstructionsPath: instructionsResult.target,
140
+ configChanged,
141
+ instructionsChanged: instructionsResult.changed,
71
142
  backup,
72
- message: 'OpenCode Mio MCP installed. Restart OpenCode to load it.',
143
+ backupInstructions: instructionsResult.backup,
144
+ message,
73
145
  }
74
146
  }
75
147
 
76
- module.exports = { configPath, isInstalled, install }
148
+ module.exports = { configPath, globalInstructionsPath, isInstalled, install }
@@ -1,174 +1,14 @@
1
- 'use strict'
2
-
3
- const fs = require('fs')
4
- const path = require('path')
5
- const os = require('os')
6
- const crypto = require('crypto')
7
-
8
- function configDir() {
9
- return (
10
- process.env.WORKBUDDY_CONFIG_DIR ||
11
- process.env.CODEBUDDY_CONFIG_DIR ||
12
- path.join(os.homedir(), '.workbuddy')
13
- )
14
- }
15
-
16
- function configPath() {
17
- return path.join(configDir(), 'mcp.json')
18
- }
19
-
20
- function legacyConfigPath() {
21
- return path.join(configDir(), '.mcp.json')
22
- }
23
-
24
- function approvalsPath() {
25
- return path.join(configDir(), 'mcp-approvals.json')
26
- }
27
-
28
- function memoryPath() {
29
- return path.join(configDir(), 'MEMORY.md')
30
- }
31
-
32
- function readJson(file) {
33
- if (!fs.existsSync(file)) return null
34
- try {
35
- return JSON.parse(fs.readFileSync(file, 'utf8'))
36
- } catch (_) {
37
- return null
38
- }
39
- }
40
-
41
-
42
- const MEMORY_BEGIN = '<!-- MIO_INTELLIGENCE_BEGIN -->'
43
- const MEMORY_END = '<!-- MIO_INTELLIGENCE_END -->'
44
- const MEMORY_BLOCK = `${MEMORY_BEGIN}
45
- ## Mio Intelligence Layer
46
-
47
- Mio MCP tools are available. Treat these as mandatory runtime rules for every workspace.
48
-
49
- - Before designing or implementing a solution, or when unsure about prior decisions, call \`mcp__mio-intelligence__mio.memory.query\` first. Omit \`project\` unless a specific project is required; Mio derives it from the current Git repository.
50
- - After a decision affecting architecture, deployment, dependencies, or long-term behavior, call \`mcp__mio-intelligence__mio.memory.record\` with \`kind\`: \`decision\`, \`context\`, \`problem\`, or \`note\`.
51
- - When a tool call fails, retries, or returns an error, call \`mcp__mio-intelligence__mio.observer.ingest\` with a stable \`trace_id\`, relevant \`event_type\`, \`payload\`, and \`outcome\`.
52
- - Before a high-risk operation such as deletion, migration, credential change, or production change, call \`mcp__mio-intelligence__mio.policy.check\` and follow its suggestion when available.
53
- - If Mio MCP is unavailable, do not block the task. Continue normally and note the missed memory or observation.
54
- - Do not store secrets, credentials, or raw sensitive content in Mio memory.
55
- ${MEMORY_END}`
56
-
57
- function ensureMemoryRules() {
58
- const target = memoryPath()
59
- let content = fs.existsSync(target) ? fs.readFileSync(target, 'utf8') : ''
60
- const start = content.indexOf(MEMORY_BEGIN)
61
- if (start !== -1) {
62
- const end = content.indexOf(MEMORY_END, start)
63
- content = end === -1 ? content.slice(0, start) : content.slice(0, start) + content.slice(end + MEMORY_END.length)
64
- content = content.trimEnd()
65
- }
66
- const before = content
67
- if (content) content += '\n\n'
68
- content += MEMORY_BLOCK + '\n'
69
- if (content === before) return false
70
- fs.mkdirSync(path.dirname(target), { recursive: true })
71
- fs.writeFileSync(target, content, 'utf8')
72
- return true
73
- }
74
-
75
- function isInstalled() {
76
- const config = readJson(configPath())
77
- const entry = config && config.mcpServers && config.mcpServers['mio-intelligence']
78
- if (!entry) return false
79
-
80
- const approvals = readJson(approvalsPath()) || {}
81
- const memory = fs.existsSync(memoryPath()) ? fs.readFileSync(memoryPath(), 'utf8') : ''
82
- return Boolean(
83
- approvals[approvalKey('mio-intelligence', entry)] &&
84
- memory.includes(MEMORY_BEGIN) &&
85
- memory.includes(MEMORY_END)
86
- )
87
- }
88
-
89
- function approvalKey(name, entry) {
90
- let input
91
- if (entry.command) {
92
- input = `${entry.command || ''}|${(entry.args || []).map(String).sort().join(',')}|${Object.keys(entry.env || {}).sort().join(',')}`
93
- } else if (entry.url) {
94
- try {
95
- input = new URL(entry.url).origin
96
- } catch (_) {
97
- input = entry.url
98
- }
99
- } else {
100
- input = JSON.stringify(entry)
101
- }
102
-
103
- const hash = crypto.createHash('sha256').update(input).digest('hex')
104
- return `${hash}::${name}`
105
- }
106
-
107
- function writeApproval(name, entry) {
108
- const target = approvalsPath()
109
- const approvals = readJson(target) || {}
110
- approvals[approvalKey(name, entry)] = Date.now()
111
- fs.mkdirSync(path.dirname(target), { recursive: true })
112
- fs.writeFileSync(target, `${JSON.stringify(approvals, null, 2)}\n`, 'utf8')
113
- }
114
-
115
- function install({ node, serverScript, home, workspace, project }) {
116
- const target = configPath()
117
- const canonical = readJson(target) || {}
118
- const legacy = readJson(legacyConfigPath()) || {}
119
- const canonicalServers =
120
- canonical.mcpServers && typeof canonical.mcpServers === 'object' ? canonical.mcpServers : {}
121
- const legacyServers =
122
- legacy.mcpServers && typeof legacy.mcpServers === 'object' ? legacy.mcpServers : {}
123
-
124
- const config = {
125
- ...legacy,
126
- ...canonical,
127
- mcpServers: {
128
- ...legacyServers,
129
- ...canonicalServers,
130
- },
131
- }
132
-
133
- const context = JSON.stringify({
134
- agentId: 'workbuddy',
135
- project: project || path.basename(workspace || process.cwd()),
136
- workspace: workspace || process.cwd(),
137
- sessionId: 'workbuddy-session',
138
- })
139
-
140
- config.mcpServers = {
141
- ...config.mcpServers,
142
- 'mio-intelligence': {
143
- command: node,
144
- args: [serverScript],
145
- env: {
146
- MIO_DATA_DIR: home,
147
- MIO_CONTEXT: context,
148
- },
149
- },
150
- }
151
-
152
- writeApproval('mio-intelligence', config.mcpServers['mio-intelligence'])
153
- const memoryChanged = ensureMemoryRules()
154
-
155
- const backup = `${target}.bak-mio-${Date.now()}`
156
- if (fs.existsSync(target)) {
157
- fs.copyFileSync(target, backup)
158
- }
159
-
160
- fs.mkdirSync(path.dirname(target), { recursive: true })
161
- fs.writeFileSync(target, `${JSON.stringify(config, null, 2)}\n`, 'utf8')
162
-
163
- return {
164
- changed: true,
165
- configPath: target,
166
- approvalsPath: approvalsPath(),
167
- memoryPath: memoryPath(),
168
- memoryChanged,
169
- backup,
170
- message: 'WorkBuddy Mio MCP installed, approved, and auto-memory rules injected. WorkBuddy should auto-load it; restart if it was already running.',
171
- }
172
- }
173
-
174
- module.exports = { configDir, configPath, approvalsPath, memoryPath, approvalKey, isInstalled, install }
1
+ 'use strict'
2
+
3
+ function isInstalled() {
4
+ return false
5
+ }
6
+
7
+ function install() {
8
+ return {
9
+ changed: false,
10
+ message: 'WorkBuddy adapter is not implemented yet.',
11
+ }
12
+ }
13
+
14
+ module.exports = { isInstalled, install }
package/bin/mio.js CHANGED
@@ -1,74 +1,56 @@
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
- const { execFileSync } = require('child_process')
8
-
9
- const SERVER_SCRIPT = path.resolve(__dirname, '..', 'server', 'mio-intelligence-mcp', 'index.js')
10
- const MIO_HOME = process.env.MIO_HOME || path.join(os.homedir(), '.mio-intelligence')
11
- const CONFIG_FILE = path.join(MIO_HOME, 'config.json')
12
-
13
- const adapters = {
14
- codex: require('../adapters/codex.js'),
15
- opencode: require('../adapters/opencode.js'),
16
- workbuddy: require('../adapters/workbuddy.js'),
17
- }
18
-
19
- function projectFromWorkspace(workspace) {
20
- try {
21
- const commonDir = execFileSync(
22
- 'git',
23
- ['-C', workspace, 'rev-parse', '--path-format=absolute', '--git-common-dir'],
24
- { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }
25
- ).trim()
26
- if (commonDir) {
27
- const name = path.basename(path.dirname(commonDir))
28
- if (name) return name
29
- }
30
- } catch (_) {
31
- // fall through to workspace basename
32
- }
33
- return path.basename(workspace)
34
- }
35
-
36
- function ensureHome() {
37
- fs.mkdirSync(MIO_HOME, { recursive: true })
38
- fs.mkdirSync(path.join(MIO_HOME, 'logs'), { recursive: true })
39
- }
40
-
41
- function readConfig() {
42
- const fallback = { version: 1, home: MIO_HOME, agents: {} }
43
- if (!fs.existsSync(CONFIG_FILE)) return fallback
44
- try {
45
- return { ...fallback, ...JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8')) }
46
- } catch (_) {
47
- return fallback
48
- }
49
- }
50
-
51
- function writeConfig(config) {
52
- ensureHome()
53
- fs.writeFileSync(CONFIG_FILE, `${JSON.stringify(config, null, 2)}\n`, 'utf8')
54
- }
55
-
56
- function jsonOrText(payload, useJson) {
57
- if (useJson) {
58
- console.log(JSON.stringify(payload, null, 2))
59
- return
60
- }
61
- return payload
62
- }
63
-
64
- function initCommand() {
65
- ensureHome()
66
- const config = readConfig()
67
- if (!config.createdAt) config.createdAt = new Date().toISOString()
68
- writeConfig(config)
69
- console.log(`MIO_HOME ready: ${MIO_HOME}`)
70
- }
71
-
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
+
18
+ function ensureHome() {
19
+ fs.mkdirSync(MIO_HOME, { recursive: true })
20
+ fs.mkdirSync(path.join(MIO_HOME, 'logs'), { recursive: true })
21
+ }
22
+
23
+ function readConfig() {
24
+ const fallback = { version: 1, home: MIO_HOME, agents: {} }
25
+ if (!fs.existsSync(CONFIG_FILE)) return fallback
26
+ try {
27
+ return { ...fallback, ...JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8')) }
28
+ } catch (_) {
29
+ return fallback
30
+ }
31
+ }
32
+
33
+ function writeConfig(config) {
34
+ ensureHome()
35
+ fs.writeFileSync(CONFIG_FILE, `${JSON.stringify(config, null, 2)}\n`, 'utf8')
36
+ }
37
+
38
+ function jsonOrText(payload, useJson) {
39
+ if (useJson) {
40
+ console.log(JSON.stringify(payload, null, 2))
41
+ return
42
+ }
43
+ return payload
44
+ }
45
+
46
+ function initCommand() {
47
+ ensureHome()
48
+ const config = readConfig()
49
+ if (!config.createdAt) config.createdAt = new Date().toISOString()
50
+ writeConfig(config)
51
+ console.log(`MIO_HOME ready: ${MIO_HOME}`)
52
+ }
53
+
72
54
  function statusCommand(useJson) {
73
55
  const config = readConfig()
74
56
  const installed = {
@@ -86,121 +68,120 @@ function statusCommand(useJson) {
86
68
  opencodeInstalled: installed.opencode,
87
69
  workbuddyInstalled: installed.workbuddy,
88
70
  }
89
- if (useJson) {
90
- jsonOrText(payload, true)
91
- return
92
- }
71
+ if (useJson) {
72
+ jsonOrText(payload, true)
73
+ return
74
+ }
93
75
  console.log(`MIO_HOME: ${MIO_HOME}`)
94
76
  console.log(`MCP Server: ${SERVER_SCRIPT}`)
95
77
  console.log(`Codex MCP: ${installed.codex ? 'installed' : 'not installed'}`)
96
78
  console.log(`OpenCode MCP: ${installed.opencode ? 'installed' : 'not installed'}`)
97
79
  console.log(`WorkBuddy MCP: ${installed.workbuddy ? 'installed' : 'not installed'}`)
98
80
  }
99
-
100
- function agentsCommand(useJson) {
101
- const config = readConfig()
102
- const agents = config.agents || {}
103
- if (useJson) {
104
- jsonOrText(agents, true)
105
- return
106
- }
107
- const names = Object.keys(agents)
108
- if (names.length === 0) {
109
- console.log('No agents installed.')
110
- return
111
- }
112
- for (const name of names) {
113
- console.log(`${name}\t${agents[name].installedAt || ''}`)
114
- }
115
- }
116
-
117
- function installCommand(host, useJson) {
118
- if (!host) {
119
- console.error('Usage: mio install <codex|opencode|workbuddy>')
120
- process.exitCode = 1
121
- return
122
- }
123
- const adapter = adapters[host]
124
- if (!adapter) {
125
- console.error(`Unknown host: ${host}`)
126
- process.exitCode = 1
127
- return
128
- }
129
- ensureHome()
130
- const config = readConfig()
131
- let result
132
- try {
133
- result = adapter.install({
134
- node: process.execPath,
135
- serverScript: SERVER_SCRIPT,
136
- home: MIO_HOME,
137
- workspace: process.cwd(),
138
- project: projectFromWorkspace(process.cwd()),
139
- })
140
- } catch (error) {
141
- console.error(`Failed to install ${host}: ${error.message || error}`)
142
- process.exitCode = 1
143
- return
144
- }
145
- if (result.changed) {
146
- config.agents = config.agents || {}
147
- config.agents[host] = {
148
- installedAt: new Date().toISOString(),
149
- configPath: result.configPath || null,
150
- }
151
- writeConfig(config)
152
- }
153
- if (useJson) {
154
- jsonOrText(result, true)
155
- return
156
- }
157
- console.log(result.message || `${host} adapter completed.`)
158
- }
159
-
160
- function mcpCommand() {
161
- require(SERVER_SCRIPT)
162
- }
163
-
164
- function help() {
165
- console.log(`Mio Agent Runtime CLI
166
-
167
- Usage:
168
- mio init Initialize MIO_HOME
169
- mio mcp Start Mio MCP server (stdio)
170
- mio install <host> Install Mio into a host (codex|opencode|workbuddy)
171
- mio status Show runtime and adapter status
172
- mio agents List installed agents
173
- mio --json status Machine-readable status
174
- mio --json agents Machine-readable agents
175
- `)
176
- }
177
-
178
- function main() {
179
- const args = process.argv.slice(2)
180
- const jsonIndex = args.indexOf('--json')
181
- const useJson = jsonIndex !== -1
182
- if (useJson) args.splice(jsonIndex, 1)
183
- const command = args[0] || 'help'
184
-
185
- switch (command) {
186
- case 'init':
187
- return initCommand()
188
- case 'mcp':
189
- return mcpCommand()
190
- case 'install':
191
- return installCommand(args[1], useJson)
192
- case 'status':
193
- return statusCommand(useJson)
194
- case 'agents':
195
- return agentsCommand(useJson)
196
- case 'help':
197
- case '--help':
198
- return help()
199
- default:
200
- console.error(`Unknown command: ${command}`)
201
- help()
202
- process.exitCode = 1
203
- }
204
- }
205
-
206
- main()
81
+
82
+ function agentsCommand(useJson) {
83
+ const config = readConfig()
84
+ const agents = config.agents || {}
85
+ if (useJson) {
86
+ jsonOrText(agents, true)
87
+ return
88
+ }
89
+ const names = Object.keys(agents)
90
+ if (names.length === 0) {
91
+ console.log('No agents installed.')
92
+ return
93
+ }
94
+ for (const name of names) {
95
+ console.log(`${name}\t${agents[name].installedAt || ''}`)
96
+ }
97
+ }
98
+
99
+ function installCommand(host, useJson) {
100
+ if (!host) {
101
+ console.error('Usage: mio install <codex|opencode|workbuddy>')
102
+ process.exitCode = 1
103
+ return
104
+ }
105
+ const adapter = adapters[host]
106
+ if (!adapter) {
107
+ console.error(`Unknown host: ${host}`)
108
+ process.exitCode = 1
109
+ return
110
+ }
111
+ ensureHome()
112
+ const config = readConfig()
113
+ let result
114
+ try {
115
+ result = adapter.install({
116
+ node: process.execPath,
117
+ serverScript: SERVER_SCRIPT,
118
+ home: MIO_HOME,
119
+ workspace: process.cwd(),
120
+ })
121
+ } catch (error) {
122
+ console.error(`Failed to install ${host}: ${error.message || error}`)
123
+ process.exitCode = 1
124
+ return
125
+ }
126
+ if (result.changed) {
127
+ config.agents = config.agents || {}
128
+ config.agents[host] = {
129
+ installedAt: new Date().toISOString(),
130
+ configPath: result.configPath || null,
131
+ }
132
+ writeConfig(config)
133
+ }
134
+ if (useJson) {
135
+ jsonOrText(result, true)
136
+ return
137
+ }
138
+ console.log(result.message || `${host} adapter completed.`)
139
+ }
140
+
141
+ function mcpCommand() {
142
+ require(SERVER_SCRIPT)
143
+ }
144
+
145
+ function help() {
146
+ console.log(`Mio Agent Runtime CLI
147
+
148
+ Usage:
149
+ mio init Initialize MIO_HOME
150
+ mio mcp Start Mio MCP server (stdio)
151
+ mio install <host> Install Mio into a host (codex|opencode|workbuddy)
152
+ mio status Show runtime and adapter status
153
+ mio agents List installed agents
154
+ mio --json status Machine-readable status
155
+ mio --json agents Machine-readable agents
156
+ `)
157
+ }
158
+
159
+ function main() {
160
+ const args = process.argv.slice(2)
161
+ const jsonIndex = args.indexOf('--json')
162
+ const useJson = jsonIndex !== -1
163
+ if (useJson) args.splice(jsonIndex, 1)
164
+ const command = args[0] || 'help'
165
+
166
+ switch (command) {
167
+ case 'init':
168
+ return initCommand()
169
+ case 'mcp':
170
+ return mcpCommand()
171
+ case 'install':
172
+ return installCommand(args[1], useJson)
173
+ case 'status':
174
+ return statusCommand(useJson)
175
+ case 'agents':
176
+ return agentsCommand(useJson)
177
+ case 'help':
178
+ case '--help':
179
+ return help()
180
+ default:
181
+ console.error(`Unknown command: ${command}`)
182
+ help()
183
+ process.exitCode = 1
184
+ }
185
+ }
186
+
187
+ main()
package/package.json CHANGED
@@ -1,23 +1,23 @@
1
- {
2
- "name": "mio-agent-runtime",
3
- "version": "0.2.2",
4
- "description": "Mio Agent Runtime installer and MCP intelligence layer",
5
- "license": "MIT",
6
- "type": "commonjs",
7
- "bin": {
8
- "mio": "bin/mio.js"
9
- },
10
- "files": [
11
- "bin/",
12
- "server/",
13
- "adapters/",
14
- "README.md"
15
- ],
16
- "scripts": {
17
- "check": "node --check bin/mio.js && node --check adapters/codex.js && node --check adapters/opencode.js && node --check adapters/workbuddy.js && node --check server/mio-intelligence-mcp/index.js",
18
- "prepack": "npm run check"
19
- },
20
- "engines": {
21
- "node": ">=18"
22
- }
1
+ {
2
+ "name": "mio-agent-runtime",
3
+ "version": "0.2.3",
4
+ "description": "Mio Agent Runtime installer and MCP intelligence layer",
5
+ "license": "MIT",
6
+ "type": "commonjs",
7
+ "bin": {
8
+ "mio": "bin/mio.js"
9
+ },
10
+ "files": [
11
+ "bin/",
12
+ "server/",
13
+ "adapters/",
14
+ "README.md"
15
+ ],
16
+ "scripts": {
17
+ "check": "node --check bin/mio.js && node --check adapters/codex.js && node --check adapters/opencode.js && node --check adapters/workbuddy.js && node --check server/mio-intelligence-mcp/index.js",
18
+ "prepack": "npm run check"
19
+ },
20
+ "engines": {
21
+ "node": ">=18"
22
+ }
23
23
  }