mio-agent-runtime 0.5.16 → 0.5.22
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 +163 -107
- package/adapters/claude-code.js +141 -0
- package/bin/mio.js +493 -245
- package/observe/observer.js +286 -2
- package/package.json +26 -24
- package/server/creativity-engine.js +397 -0
- package/server/digest.js +266 -0
- package/server/host-capabilities.js +5 -0
- package/server/memory-store.js +376 -0
- package/server/mio-intelligence-mcp/README.md +2 -2
- package/server/mio-intelligence-mcp/index.js +2325 -1966
- package/server/retention.js +150 -0
package/bin/mio.js
CHANGED
|
@@ -1,24 +1,28 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict'
|
|
3
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
4
|
const fs = require('fs')
|
|
5
5
|
const path = require('path')
|
|
6
6
|
const os = require('os')
|
|
7
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
|
-
hermes: require('../adapters/hermes.js'),
|
|
18
|
-
|
|
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
|
+
hermes: require('../adapters/hermes.js'),
|
|
18
|
+
claude: require('../adapters/claude-code.js'),
|
|
19
|
+
}
|
|
19
20
|
const observer = require('../observe/observer.js')
|
|
20
21
|
const { getEvolutionStatus, formatEvolutionStatusText } = require('../server/runtime-modules.js')
|
|
21
22
|
const { createEvolutionCutoverTools } = require('../server/evolution-cutover.js')
|
|
23
|
+
const { createMemoryStore } = require('../server/memory-store.js')
|
|
24
|
+
const { createRetention } = require('../server/retention.js')
|
|
25
|
+
const { createDigest } = require('../server/digest.js')
|
|
22
26
|
|
|
23
27
|
function appendJsonl(file, value) {
|
|
24
28
|
fs.mkdirSync(path.dirname(file), { recursive: true })
|
|
@@ -217,211 +221,440 @@ async function evolutionCommand(args, useJson) {
|
|
|
217
221
|
console.error('Usage: mio evolution status|shadow record|dual-write record|cutover readiness|cutover apply|authority plan|migration plan')
|
|
218
222
|
process.exitCode = 1
|
|
219
223
|
}
|
|
220
|
-
|
|
221
|
-
function ensureHome() {
|
|
222
|
-
fs.mkdirSync(MIO_HOME, { recursive: true })
|
|
223
|
-
fs.mkdirSync(path.join(MIO_HOME, 'logs'), { recursive: true })
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
return
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
if (
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
console.
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
const
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
console.log(
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
224
|
+
|
|
225
|
+
function ensureHome() {
|
|
226
|
+
fs.mkdirSync(MIO_HOME, { recursive: true })
|
|
227
|
+
fs.mkdirSync(path.join(MIO_HOME, 'logs'), { recursive: true })
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// CLI memory store: same ranking/persistence as the MCP server, pointed at
|
|
231
|
+
// MIO_HOME. No recordQuery hook — a terminal command cannot be followed by a
|
|
232
|
+
// task_outcome, so a recorded query would only be phase-0 noise.
|
|
233
|
+
function cliMemoryStore() {
|
|
234
|
+
return createMemoryStore({
|
|
235
|
+
dataDir: MIO_HOME,
|
|
236
|
+
projectName,
|
|
237
|
+
agentId: () => 'cli',
|
|
238
|
+
})
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function splitTagsOption(args, name) {
|
|
242
|
+
const raw = optionValue(args, name)
|
|
243
|
+
if (raw === undefined) return []
|
|
244
|
+
return raw
|
|
245
|
+
.split(',')
|
|
246
|
+
.map((tag) => tag.trim())
|
|
247
|
+
.filter(Boolean)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function recallCommand(args, useJson) {
|
|
251
|
+
const query = args[1]
|
|
252
|
+
if (!query) {
|
|
253
|
+
console.error('Usage: mio recall "<query>" [--project name] [--scope project|global|all] [--kind kind] [--tags a,b] [--limit n] [--json]')
|
|
254
|
+
process.exitCode = 1
|
|
255
|
+
return
|
|
256
|
+
}
|
|
257
|
+
const flags = args.slice(2)
|
|
258
|
+
const store = cliMemoryStore()
|
|
259
|
+
let result
|
|
260
|
+
try {
|
|
261
|
+
result = store.queryMemory({
|
|
262
|
+
query,
|
|
263
|
+
project: optionValue(flags, '--project'),
|
|
264
|
+
scope: optionValue(flags, '--scope'),
|
|
265
|
+
kind: optionValue(flags, '--kind'),
|
|
266
|
+
tags: splitTagsOption(flags, '--tags'),
|
|
267
|
+
limit: parseNumberOption(flags, '--limit'),
|
|
268
|
+
})
|
|
269
|
+
} catch (error) {
|
|
270
|
+
console.error(error.message || error)
|
|
271
|
+
process.exitCode = 1
|
|
272
|
+
return
|
|
273
|
+
}
|
|
274
|
+
if (useJson) return jsonOrText(result, true)
|
|
275
|
+
console.log(
|
|
276
|
+
`Mio memory: ${result.count} result(s) for "${result.query}" (project=${result.project}, scope=${result.scope})`
|
|
277
|
+
)
|
|
278
|
+
result.results.forEach((record, index) => {
|
|
279
|
+
const date = (record.timestamp || '').slice(0, 10)
|
|
280
|
+
console.log(`${index + 1}. [${record.kind || 'note'}] ${record.content}`)
|
|
281
|
+
const meta = [
|
|
282
|
+
(record.tags || []).length > 0 ? `tags: ${record.tags.join(', ')}` : null,
|
|
283
|
+
record.source ? `source: ${record.source}` : null,
|
|
284
|
+
date,
|
|
285
|
+
record.id,
|
|
286
|
+
]
|
|
287
|
+
.filter(Boolean)
|
|
288
|
+
.join(' | ')
|
|
289
|
+
console.log(` ${meta}`)
|
|
290
|
+
})
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function tracesCommand(args, useJson) {
|
|
294
|
+
const flags = args.slice(1)
|
|
295
|
+
const store = cliMemoryStore()
|
|
296
|
+
let result
|
|
297
|
+
try {
|
|
298
|
+
result = store.queryTraces({
|
|
299
|
+
event_type: optionValue(flags, '--type'),
|
|
300
|
+
outcome: optionValue(flags, '--outcome'),
|
|
301
|
+
project: optionValue(flags, '--project'),
|
|
302
|
+
agent: optionValue(flags, '--agent'),
|
|
303
|
+
host: optionValue(flags, '--host'),
|
|
304
|
+
since: optionValue(flags, '--since'),
|
|
305
|
+
until: optionValue(flags, '--until'),
|
|
306
|
+
limit: parseNumberOption(flags, '--limit'),
|
|
307
|
+
include_payload: !flagPresent(flags, '--compact'),
|
|
308
|
+
})
|
|
309
|
+
} catch (error) {
|
|
310
|
+
console.error(error.message || error)
|
|
311
|
+
process.exitCode = 1
|
|
312
|
+
return
|
|
313
|
+
}
|
|
314
|
+
if (useJson) return jsonOrText(result, true)
|
|
315
|
+
const filters = Object.entries(result.filters)
|
|
316
|
+
.filter(([, value]) => value !== null && value !== undefined)
|
|
317
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
318
|
+
.join(', ')
|
|
319
|
+
console.log(
|
|
320
|
+
`Mio traces: ${result.matched} matched / ${result.total} total${filters ? ' (' + filters + ')' : ''}`
|
|
321
|
+
)
|
|
322
|
+
const summary = Object.entries(result.summary.byOutcome)
|
|
323
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
324
|
+
.join(' ')
|
|
325
|
+
if (summary) console.log(`outcomes: ${summary}`)
|
|
326
|
+
result.results.forEach((trace, index) => {
|
|
327
|
+
const time = (trace.timestamp || '').replace('T', ' ').slice(0, 19)
|
|
328
|
+
const brief = trace.payload && trace.payload.summary ? ' | ' + String(trace.payload.summary).slice(0, 100) : ''
|
|
329
|
+
console.log(
|
|
330
|
+
`${index + 1}. ${time} [${trace.event_type || '?'}${trace.outcome ? '/' + trace.outcome : ''}] ${trace.agent || '?'} @ ${trace.project || '?'}${brief}`
|
|
331
|
+
)
|
|
332
|
+
console.log(` ${trace.id} trace_id=${trace.trace_id || '-'}`)
|
|
333
|
+
})
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function digestCommand(args, useJson) {
|
|
337
|
+
const flags = args.slice(1)
|
|
338
|
+
const days = parseNumberOption(flags, '--days') || 7
|
|
339
|
+
const writeBack = flagPresent(flags, '--write-back')
|
|
340
|
+
const outDir = path.join(MIO_HOME, 'digest')
|
|
341
|
+
|
|
342
|
+
const report = createDigest({ home: MIO_HOME }).generate({
|
|
343
|
+
days,
|
|
344
|
+
project: optionValue(flags, '--project'),
|
|
345
|
+
})
|
|
346
|
+
|
|
347
|
+
if (writeBack) {
|
|
348
|
+
// Feed the actionable lines back into each active workspace context file
|
|
349
|
+
// (AGENTS.md / CLAUDE.md MIO_CONTEXT block), so every agent passively
|
|
350
|
+
// receives the digest's value on its next session.
|
|
351
|
+
const stateFile = path.join(MIO_HOME, 'observe-state.json')
|
|
352
|
+
let state = {}
|
|
353
|
+
try {
|
|
354
|
+
state = JSON.parse(fs.readFileSync(stateFile, 'utf8'))
|
|
355
|
+
} catch (_) {}
|
|
356
|
+
state.contexts = state.contexts || {}
|
|
357
|
+
const stamp = new Date().toLocaleString('zh-CN', { hour12: false })
|
|
358
|
+
const rate =
|
|
359
|
+
report.overview.tasks > 0
|
|
360
|
+
? Math.round(((report.overview.byOutcome.success || 0) / report.overview.tasks) * 100)
|
|
361
|
+
: 0
|
|
362
|
+
const headline =
|
|
363
|
+
`digest(${days}d): 任务 ${report.overview.tasks} 条 成功率 ${rate}%, error ${report.overview.errorTraces}` +
|
|
364
|
+
(report.suggestions.length > 0 ? ' | ' + report.suggestions[0] : '')
|
|
365
|
+
const targetCwds = optionValue(flags, '--cwd')
|
|
366
|
+
? [optionValue(flags, '--cwd')]
|
|
367
|
+
: Object.keys(state.contexts)
|
|
368
|
+
let written = 0
|
|
369
|
+
for (const cwd of targetCwds) {
|
|
370
|
+
try {
|
|
371
|
+
observer.updateProjectContext(state, cwd, stamp + ' | ' + headline, 'CLAUDE.md')
|
|
372
|
+
observer.updateProjectContext(state, cwd, stamp + ' | ' + headline, 'AGENTS.md')
|
|
373
|
+
written += 1
|
|
374
|
+
} catch (_) {}
|
|
375
|
+
}
|
|
376
|
+
fs.mkdirSync(MIO_HOME, { recursive: true })
|
|
377
|
+
fs.writeFileSync(stateFile, JSON.stringify(state) + '\n', 'utf8')
|
|
378
|
+
report.writeBack = { workspaces: written, headline }
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Persist the report so it becomes part of the data asset itself.
|
|
382
|
+
fs.mkdirSync(outDir, { recursive: true })
|
|
383
|
+
const reportFile = path.join(outDir, 'digest-' + report.generatedAt.slice(0, 10) + '.md')
|
|
384
|
+
fs.writeFileSync(reportFile, report.markdown, 'utf8')
|
|
385
|
+
report.reportFile = reportFile
|
|
386
|
+
|
|
387
|
+
if (useJson) return jsonOrText(report, true)
|
|
388
|
+
console.log(report.markdown)
|
|
389
|
+
console.log(`Report saved: ${reportFile}`)
|
|
390
|
+
if (report.writeBack) {
|
|
391
|
+
console.log(`Write-back: ${report.writeBack.workspaces} workspace(s) <- "${report.writeBack.headline}"`)
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function pruneCommand(args, useJson) {
|
|
396
|
+
const flags = args.slice(1)
|
|
397
|
+
const days = parseNumberOption(flags, '--days') || 30
|
|
398
|
+
const includeMemory = flagPresent(flags, '--memory')
|
|
399
|
+
const dryRun = flagPresent(flags, '--dry-run')
|
|
400
|
+
if (includeMemory && !dryRun && !flagPresent(flags, '--yes')) {
|
|
401
|
+
console.error('Pruning memory.jsonl is destructive. Re-run with --yes to confirm (a .bak-prune-* backup is still written).')
|
|
402
|
+
process.exitCode = 1
|
|
403
|
+
return
|
|
404
|
+
}
|
|
405
|
+
const retention = createRetention({ home: MIO_HOME })
|
|
406
|
+
let result
|
|
407
|
+
try {
|
|
408
|
+
result = retention.prune({ days, includeMemory, dryRun })
|
|
409
|
+
} catch (error) {
|
|
410
|
+
console.error(error.message || error)
|
|
411
|
+
process.exitCode = 1
|
|
412
|
+
return
|
|
413
|
+
}
|
|
414
|
+
if (useJson) return jsonOrText(result, true)
|
|
415
|
+
console.log(
|
|
416
|
+
`Prune ${dryRun ? 'plan (dry-run)' : 'result'}: cutoff ${result.cutoff}, days=${days}${includeMemory ? ', memory included' : ''}`
|
|
417
|
+
)
|
|
418
|
+
for (const file of result.files) {
|
|
419
|
+
console.log(`- ${file.file}: ${file.total} total -> keep ${file.kept}, remove ${file.removed}`)
|
|
420
|
+
}
|
|
421
|
+
console.log(`Total removed: ${result.totalRemoved}`)
|
|
422
|
+
if (dryRun) console.log('Dry run - nothing written. Re-run without --dry-run to apply.')
|
|
423
|
+
else console.log('Backups written as <file>.bak-prune-<ts> before rewriting.')
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function rememberCommand(args, useJson) {
|
|
427
|
+
const content = args[1]
|
|
428
|
+
if (!content) {
|
|
429
|
+
console.error('Usage: mio remember "<content>" [--kind decision|context|problem|note] [--tags a,b] [--scope project|global] [--project name] [--json]')
|
|
430
|
+
process.exitCode = 1
|
|
431
|
+
return
|
|
432
|
+
}
|
|
433
|
+
const flags = args.slice(2)
|
|
434
|
+
const store = cliMemoryStore()
|
|
435
|
+
let record
|
|
436
|
+
try {
|
|
437
|
+
record = store.recordMemory({
|
|
438
|
+
content,
|
|
439
|
+
kind: optionValue(flags, '--kind'),
|
|
440
|
+
tags: splitTagsOption(flags, '--tags'),
|
|
441
|
+
scope: optionValue(flags, '--scope'),
|
|
442
|
+
project: optionValue(flags, '--project'),
|
|
443
|
+
source: 'cli',
|
|
444
|
+
})
|
|
445
|
+
} catch (error) {
|
|
446
|
+
console.error(error.message || error)
|
|
447
|
+
process.exitCode = 1
|
|
448
|
+
return
|
|
449
|
+
}
|
|
450
|
+
if (useJson) return jsonOrText(record, true)
|
|
451
|
+
console.log(`Recorded ${record.id} (kind=${record.kind}, project=${record.project || 'global'}, scope=${record.scope})`)
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
function readConfig() {
|
|
455
|
+
const fallback = { version: 1, home: MIO_HOME, agents: {} }
|
|
456
|
+
if (!fs.existsSync(CONFIG_FILE)) return fallback
|
|
457
|
+
try {
|
|
458
|
+
return { ...fallback, ...JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8')) }
|
|
459
|
+
} catch (_) {
|
|
460
|
+
return fallback
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
function writeConfig(config) {
|
|
465
|
+
ensureHome()
|
|
466
|
+
fs.writeFileSync(CONFIG_FILE, `${JSON.stringify(config, null, 2)}\n`, 'utf8')
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
function jsonOrText(payload, useJson) {
|
|
470
|
+
if (useJson) {
|
|
471
|
+
console.log(JSON.stringify(payload, null, 2))
|
|
472
|
+
return
|
|
473
|
+
}
|
|
474
|
+
return payload
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
function initCommand() {
|
|
478
|
+
ensureHome()
|
|
479
|
+
const config = readConfig()
|
|
480
|
+
if (!config.createdAt) config.createdAt = new Date().toISOString()
|
|
481
|
+
writeConfig(config)
|
|
482
|
+
console.log(`MIO_HOME ready: ${MIO_HOME}`)
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
function statusCommand(useJson) {
|
|
486
|
+
const config = readConfig()
|
|
487
|
+
const codexRestart = adapters.codex.restartWarningMessage()
|
|
488
|
+
const opencodeRestart = adapters.opencode.restartWarningMessage()
|
|
489
|
+
const installed = {
|
|
490
|
+
codex: adapters.codex.isInstalled(),
|
|
491
|
+
opencode: adapters.opencode.isInstalled(),
|
|
492
|
+
workbuddy: adapters.workbuddy.isInstalled(),
|
|
493
|
+
hermes: adapters.hermes.isInstalled(),
|
|
494
|
+
claude: adapters.claude.isInstalled(),
|
|
495
|
+
}
|
|
496
|
+
const payload = {
|
|
497
|
+
version: config.version,
|
|
498
|
+
home: MIO_HOME,
|
|
499
|
+
serverScript: SERVER_SCRIPT,
|
|
500
|
+
serverScriptExists: fs.existsSync(SERVER_SCRIPT),
|
|
501
|
+
agents: config.agents || {},
|
|
502
|
+
codexInstalled: installed.codex,
|
|
503
|
+
opencodeInstalled: installed.opencode,
|
|
504
|
+
workbuddyInstalled: installed.workbuddy,
|
|
505
|
+
hermesInstalled: installed.hermes,
|
|
506
|
+
claudeInstalled: installed.claude,
|
|
507
|
+
codexNeedsRestart: Boolean(codexRestart),
|
|
508
|
+
opencodeNeedsRestart: Boolean(opencodeRestart),
|
|
509
|
+
codexRestartHint: codexRestart,
|
|
510
|
+
opencodeRestartHint: opencodeRestart,
|
|
511
|
+
hermesObserverReady: fs.existsSync(observer.hermesDbPath()),
|
|
512
|
+
claudeObserverReady: fs.existsSync(observer.claudeProjectsDir()),
|
|
513
|
+
opencodeObserverReady: fs.existsSync(observer.opencodeDbPath()),
|
|
514
|
+
observerRunning: observer.isRunning(MIO_HOME),
|
|
515
|
+
observerPid: observer.isRunning(MIO_HOME) ? observer.readPid(MIO_HOME) : null,
|
|
516
|
+
}
|
|
517
|
+
if (useJson) {
|
|
518
|
+
jsonOrText(payload, true)
|
|
519
|
+
return
|
|
520
|
+
}
|
|
521
|
+
console.log(`MIO_HOME: ${MIO_HOME}`)
|
|
522
|
+
console.log(`MCP Server: ${SERVER_SCRIPT}`)
|
|
523
|
+
console.log(`Codex MCP: ${installed.codex ? 'installed' : 'not installed'}`)
|
|
524
|
+
if (codexRestart) console.log(`Codex app: running with an older Mio config - fully restart Codex to load it`)
|
|
525
|
+
console.log(`OpenCode MCP: ${installed.opencode ? 'installed' : 'not installed'}`)
|
|
526
|
+
if (opencodeRestart) console.log(`OpenCode Desktop: running with an older Mio config - restart it to load it`)
|
|
527
|
+
console.log(`OpenCode observer: ${fs.existsSync(observer.opencodeDbPath()) ? 'ready' : 'no database'}`)
|
|
528
|
+
console.log(`WorkBuddy MCP: ${installed.workbuddy ? 'installed' : 'not installed'}`)
|
|
529
|
+
console.log(`Hermes MCP: ${installed.hermes ? 'installed' : 'not installed'}`)
|
|
530
|
+
console.log(`Hermes observer: ${fs.existsSync(observer.hermesDbPath()) ? 'ready' : 'no database'}`)
|
|
531
|
+
console.log(`Claude Code MCP: ${installed.claude ? 'installed' : 'not installed'}`)
|
|
532
|
+
console.log(`Claude Code observer: ${fs.existsSync(observer.claudeProjectsDir()) ? 'ready' : 'no transcripts'}`)
|
|
533
|
+
console.log(`Observer: ${observer.isRunning(MIO_HOME) ? 'running (pid ' + observer.readPid(MIO_HOME) + ')' : 'not running'}`)
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
function agentsCommand(useJson) {
|
|
537
|
+
const config = readConfig()
|
|
538
|
+
const agents = config.agents || {}
|
|
539
|
+
if (useJson) {
|
|
540
|
+
jsonOrText(agents, true)
|
|
541
|
+
return
|
|
542
|
+
}
|
|
543
|
+
const names = Object.keys(agents)
|
|
544
|
+
if (names.length === 0) {
|
|
545
|
+
console.log('No agents installed.')
|
|
546
|
+
return
|
|
547
|
+
}
|
|
548
|
+
for (const name of names) {
|
|
549
|
+
console.log(`${name}\t${agents[name].installedAt || ''}`)
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
function installCommand(host, useJson) {
|
|
554
|
+
if (!host) {
|
|
555
|
+
console.error('Usage: mio install <codex|opencode|workbuddy|hermes|claude>')
|
|
556
|
+
process.exitCode = 1
|
|
557
|
+
return
|
|
558
|
+
}
|
|
559
|
+
const adapter = adapters[host]
|
|
560
|
+
if (!adapter) {
|
|
561
|
+
console.error(`Unknown host: ${host}`)
|
|
562
|
+
process.exitCode = 1
|
|
563
|
+
return
|
|
564
|
+
}
|
|
565
|
+
ensureHome()
|
|
566
|
+
const config = readConfig()
|
|
567
|
+
let result
|
|
568
|
+
try {
|
|
569
|
+
result = adapter.install({
|
|
570
|
+
node: process.execPath,
|
|
571
|
+
serverScript: SERVER_SCRIPT,
|
|
572
|
+
home: MIO_HOME,
|
|
573
|
+
workspace: process.cwd(),
|
|
574
|
+
})
|
|
575
|
+
} catch (error) {
|
|
576
|
+
console.error(`Failed to install ${host}: ${error.message || error}`)
|
|
577
|
+
process.exitCode = 1
|
|
578
|
+
return
|
|
579
|
+
}
|
|
580
|
+
if (result.changed) {
|
|
581
|
+
config.agents = config.agents || {}
|
|
582
|
+
config.agents[host] = {
|
|
583
|
+
installedAt: new Date().toISOString(),
|
|
584
|
+
configPath: result.configPath || null,
|
|
585
|
+
}
|
|
586
|
+
writeConfig(config)
|
|
587
|
+
}
|
|
588
|
+
try {
|
|
589
|
+
observer.runCycle(MIO_HOME, observer.loadState(MIO_HOME), observer.createSinks(MIO_HOME))
|
|
590
|
+
} catch (_) {}
|
|
591
|
+
let observerStarted = false
|
|
592
|
+
try {
|
|
593
|
+
observerStarted = observer.startBackground(MIO_HOME).started
|
|
594
|
+
} catch (_) {}
|
|
595
|
+
if (useJson) {
|
|
596
|
+
result.observer = {
|
|
597
|
+
running: observer.isRunning(MIO_HOME),
|
|
598
|
+
started: observerStarted,
|
|
599
|
+
}
|
|
600
|
+
jsonOrText(result, true)
|
|
601
|
+
return
|
|
602
|
+
}
|
|
603
|
+
console.log(result.message || `${host} adapter completed.`)
|
|
604
|
+
console.log(
|
|
605
|
+
observer.isRunning(MIO_HOME)
|
|
606
|
+
? 'Observer daemon running.'
|
|
607
|
+
: 'Observer daemon not running. Run `mio observe --start`.'
|
|
608
|
+
)
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
function mcpCommand() {
|
|
612
|
+
require(SERVER_SCRIPT)
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
function observeCommand(args) {
|
|
616
|
+
const flags = args.slice(1)
|
|
617
|
+
const useJson = flags.includes('--json')
|
|
618
|
+
if (flags.includes('--start')) {
|
|
619
|
+
const result = observer.startBackground(MIO_HOME)
|
|
620
|
+
if (useJson) return jsonOrText(result, true)
|
|
621
|
+
console.log(result.started ? 'Observer started.' : 'Observer already running.')
|
|
622
|
+
return
|
|
623
|
+
}
|
|
624
|
+
if (flags.includes('--stop')) {
|
|
625
|
+
const result = observer.stopBackground(MIO_HOME)
|
|
626
|
+
if (useJson) return jsonOrText(result, true)
|
|
627
|
+
console.log(result.stopped ? 'Observer stopped.' : 'Observer was not running.')
|
|
628
|
+
return
|
|
629
|
+
}
|
|
630
|
+
if (flags.includes('--status')) {
|
|
631
|
+
const running = observer.isRunning(MIO_HOME)
|
|
632
|
+
const pid = observer.readPid(MIO_HOME)
|
|
633
|
+
if (useJson) return jsonOrText({ running, pid }, true)
|
|
634
|
+
console.log(running ? 'Observer: running (pid ' + pid + ')' : 'Observer: not running')
|
|
635
|
+
return
|
|
636
|
+
}
|
|
637
|
+
if (flags.includes('--once')) {
|
|
638
|
+
const res = observer.runCycle(
|
|
639
|
+
MIO_HOME,
|
|
640
|
+
observer.loadState(MIO_HOME),
|
|
641
|
+
observer.createSinks(MIO_HOME)
|
|
642
|
+
)
|
|
643
|
+
if (useJson) return jsonOrText(res, true)
|
|
644
|
+
console.log('Observed files=' + res.files + ' lines=' + res.lines + ' outcomes=' + res.outcomes)
|
|
645
|
+
return
|
|
646
|
+
}
|
|
647
|
+
console.log('Observing WorkBuddy transcripts into ' + MIO_HOME + ' (Ctrl+C to stop)...')
|
|
648
|
+
observer.startForeground(MIO_HOME)
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
function help() {
|
|
652
|
+
console.log(`Mio Agent Runtime CLI
|
|
653
|
+
|
|
654
|
+
Usage:
|
|
655
|
+
mio init Initialize MIO_HOME
|
|
656
|
+
mio mcp Start Mio MCP server (stdio)
|
|
657
|
+
mio install <host> Install Mio into a host (codex|opencode|workbuddy|hermes|claude)
|
|
425
658
|
mio status Show runtime and adapter status
|
|
426
659
|
mio agents List installed agents
|
|
427
660
|
mio evolution status Show composed evolution module health
|
|
@@ -433,42 +666,57 @@ Usage:
|
|
|
433
666
|
mio evolution cutover apply --dry-run Dry-run a cutover plan without switching authority
|
|
434
667
|
mio observe Watch WorkBuddy transcripts and auto-ingest task outcomes
|
|
435
668
|
mio observe --start|--stop|--status|--once Manage the background observer daemon
|
|
669
|
+
mio recall "<query>" Search Mio memory from the terminal (same ranking as mio.memory.query)
|
|
670
|
+
mio traces Show recent observer traces (--type/--outcome/--agent/--since/--limit/--compact)
|
|
671
|
+
mio remember "<content>" Write a memory record from the terminal (same schema as mio.memory.record)
|
|
672
|
+
mio prune --days 30 Trim old traces/queries/reuse records and observe.log (--dry-run to preview; --memory needs --yes)
|
|
673
|
+
mio digest --days 7 Aggregate traces/memory/reuse into an actionable report (--write-back feeds agent context files; --json)
|
|
436
674
|
mio --json status Machine-readable status
|
|
437
675
|
mio --json agents Machine-readable agents
|
|
438
676
|
mio --json evolution status Machine-readable evolution module health
|
|
439
677
|
`)
|
|
440
678
|
}
|
|
441
|
-
|
|
679
|
+
|
|
442
680
|
async function main() {
|
|
443
|
-
const args = process.argv.slice(2)
|
|
444
|
-
const jsonIndex = args.indexOf('--json')
|
|
445
|
-
const useJson = jsonIndex !== -1
|
|
446
|
-
if (useJson) args.splice(jsonIndex, 1)
|
|
447
|
-
const command = args[0] || 'help'
|
|
448
|
-
|
|
449
|
-
switch (command) {
|
|
450
|
-
case 'init':
|
|
451
|
-
return initCommand()
|
|
452
|
-
case 'mcp':
|
|
453
|
-
return mcpCommand()
|
|
454
|
-
case 'install':
|
|
455
|
-
return installCommand(args[1], useJson)
|
|
456
|
-
case 'status':
|
|
457
|
-
return statusCommand(useJson)
|
|
458
|
-
case 'agents':
|
|
459
|
-
return agentsCommand(useJson)
|
|
681
|
+
const args = process.argv.slice(2)
|
|
682
|
+
const jsonIndex = args.indexOf('--json')
|
|
683
|
+
const useJson = jsonIndex !== -1
|
|
684
|
+
if (useJson) args.splice(jsonIndex, 1)
|
|
685
|
+
const command = args[0] || 'help'
|
|
686
|
+
|
|
687
|
+
switch (command) {
|
|
688
|
+
case 'init':
|
|
689
|
+
return initCommand()
|
|
690
|
+
case 'mcp':
|
|
691
|
+
return mcpCommand()
|
|
692
|
+
case 'install':
|
|
693
|
+
return installCommand(args[1], useJson)
|
|
694
|
+
case 'status':
|
|
695
|
+
return statusCommand(useJson)
|
|
696
|
+
case 'agents':
|
|
697
|
+
return agentsCommand(useJson)
|
|
460
698
|
case 'observe':
|
|
461
699
|
return observeCommand(args)
|
|
700
|
+
case 'recall':
|
|
701
|
+
return recallCommand(args, useJson)
|
|
702
|
+
case 'traces':
|
|
703
|
+
return tracesCommand(args, useJson)
|
|
704
|
+
case 'remember':
|
|
705
|
+
return rememberCommand(args, useJson)
|
|
706
|
+
case 'prune':
|
|
707
|
+
return pruneCommand(args, useJson)
|
|
708
|
+
case 'digest':
|
|
709
|
+
return digestCommand(args, useJson)
|
|
462
710
|
case 'evolution':
|
|
463
711
|
return evolutionCommand(args, useJson)
|
|
464
|
-
case 'help':
|
|
465
|
-
case '--help':
|
|
466
|
-
return help()
|
|
467
|
-
default:
|
|
468
|
-
console.error(`Unknown command: ${command}`)
|
|
469
|
-
help()
|
|
470
|
-
process.exitCode = 1
|
|
471
|
-
}
|
|
712
|
+
case 'help':
|
|
713
|
+
case '--help':
|
|
714
|
+
return help()
|
|
715
|
+
default:
|
|
716
|
+
console.error(`Unknown command: ${command}`)
|
|
717
|
+
help()
|
|
718
|
+
process.exitCode = 1
|
|
719
|
+
}
|
|
472
720
|
}
|
|
473
721
|
|
|
474
722
|
main().catch((error) => {
|