suemo 0.0.3 → 0.0.5
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 +10 -2
- package/package.json +1 -1
- package/src/cli/commands/believe.ts +19 -5
- package/src/cli/commands/consolidate.ts +18 -3
- package/src/cli/commands/doctor.ts +25 -3
- package/src/cli/commands/export-import.ts +26 -4
- package/src/cli/commands/goal.ts +54 -11
- package/src/cli/commands/health.ts +77 -9
- package/src/cli/commands/init.ts +35 -4
- package/src/cli/commands/observe.ts +16 -3
- package/src/cli/commands/query.ts +17 -6
- package/src/cli/commands/recall.ts +19 -3
- package/src/cli/commands/serve.ts +164 -4
- package/src/cli/commands/sync.ts +22 -3
- package/src/cli/commands/timeline.ts +17 -6
- package/src/cli/commands/wander.ts +17 -6
- package/src/cli/shared.ts +78 -2
- package/src/cognitive/consolidate.ts +8 -17
- package/src/cognitive/contradiction.ts +5 -11
- package/src/cognitive/health.ts +13 -13
- package/src/config.ts +4 -4
- package/src/db/client.ts +3 -3
- package/src/db/preflight.ts +8 -8
- package/src/db/schema.surql +1 -1
- package/src/db/schema.ts +56 -8
- package/src/embedding/index.ts +2 -2
- package/src/logger.ts +23 -1
- package/src/mcp/server.ts +12 -11
- package/src/mcp/stdio.ts +24 -6
- package/src/memory/episode.ts +6 -0
- package/src/memory/read.ts +62 -43
- package/src/memory/write.ts +12 -14
- package/src/types.ts +1 -1
package/README.md
CHANGED
|
@@ -96,6 +96,14 @@ suemo serve
|
|
|
96
96
|
# Listening on http://127.0.0.1:4242
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
+
For development with automatic restart on file changes:
|
|
100
|
+
|
|
101
|
+
```sh
|
|
102
|
+
suemo serve --dev
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
`--dev` re-runs `suemo serve` under Bun watch mode and restarts MCP when source files change.
|
|
106
|
+
|
|
99
107
|
---
|
|
100
108
|
|
|
101
109
|
## CLI Reference
|
|
@@ -161,8 +169,8 @@ export default defineConfig({
|
|
|
161
169
|
},
|
|
162
170
|
},
|
|
163
171
|
embedding: {
|
|
164
|
-
provider: '
|
|
165
|
-
dimension:
|
|
172
|
+
provider: 'surrealml', // fn::embed() — configured in SurrealDB
|
|
173
|
+
dimension: 384,
|
|
166
174
|
},
|
|
167
175
|
consolidation: {
|
|
168
176
|
trigger: 'timer',
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@ import { loadConfig } from '../../config.ts'
|
|
|
2
2
|
import { connect, disconnect } from '../../db/client.ts'
|
|
3
3
|
import { getLogger } from '../../logger.ts'
|
|
4
4
|
import { believe } from '../../memory/write.ts'
|
|
5
|
-
import { app, initCliCommand } from '../shared.ts'
|
|
5
|
+
import { app, initCliCommand, printCliJson, resolveOutputModeOrExit } from '../shared.ts'
|
|
6
6
|
|
|
7
7
|
const log = getLogger(['suemo', 'cli', 'believe'])
|
|
8
8
|
|
|
@@ -12,9 +12,17 @@ export const believeCmd = app.sub('believe')
|
|
|
12
12
|
.flags({
|
|
13
13
|
scope: { type: 'string', short: 's', description: 'Scope label' },
|
|
14
14
|
confidence: { type: 'number', description: 'Confidence 0.0–1.0', default: 1.0 },
|
|
15
|
+
json: { type: 'boolean', description: 'Output JSON result' },
|
|
16
|
+
pretty: { type: 'boolean', description: 'Human-readable output (default)' },
|
|
15
17
|
})
|
|
16
18
|
.run(async ({ args, flags }) => {
|
|
17
|
-
|
|
19
|
+
const outputMode = resolveOutputModeOrExit(flags)
|
|
20
|
+
await initCliCommand('believe', {
|
|
21
|
+
debug: flags.debug,
|
|
22
|
+
config: flags.config,
|
|
23
|
+
json: outputMode === 'json',
|
|
24
|
+
quiet: flags.quiet,
|
|
25
|
+
})
|
|
18
26
|
const config = await loadConfig(process.cwd(), flags.config)
|
|
19
27
|
const db = await connect(config.surreal)
|
|
20
28
|
try {
|
|
@@ -28,9 +36,15 @@ export const believeCmd = app.sub('believe')
|
|
|
28
36
|
scope: flags.scope,
|
|
29
37
|
confidence: flags.confidence,
|
|
30
38
|
}, config)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
39
|
+
if (outputMode === 'json') {
|
|
40
|
+
const out: Record<string, unknown> = { id: node.id, valid_from: node.valid_from }
|
|
41
|
+
if (contradicted) out.contradicted = contradicted.id
|
|
42
|
+
printCliJson(out, flags)
|
|
43
|
+
} else {
|
|
44
|
+
console.log(`Stored belief memory: ${node.id}`)
|
|
45
|
+
console.log(` valid_from: ${node.valid_from}`)
|
|
46
|
+
if (contradicted) console.log(` contradicted: ${contradicted.id}`)
|
|
47
|
+
}
|
|
34
48
|
} finally {
|
|
35
49
|
await disconnect()
|
|
36
50
|
}
|
|
@@ -2,7 +2,7 @@ import { consolidate } from '../../cognitive/consolidate.ts'
|
|
|
2
2
|
import { loadConfig } from '../../config.ts'
|
|
3
3
|
import { connect, disconnect } from '../../db/client.ts'
|
|
4
4
|
import { getLogger } from '../../logger.ts'
|
|
5
|
-
import { app, initCliCommand } from '../shared.ts'
|
|
5
|
+
import { app, initCliCommand, printCliJson, resolveOutputModeOrExit } from '../shared.ts'
|
|
6
6
|
|
|
7
7
|
const log = getLogger(['suemo', 'cli', 'consolidate'])
|
|
8
8
|
|
|
@@ -10,9 +10,17 @@ export const consolidateCmd = app.sub('consolidate')
|
|
|
10
10
|
.meta({ description: 'Manually trigger memory consolidation (NREM + REM)' })
|
|
11
11
|
.flags({
|
|
12
12
|
'nrem-only': { type: 'boolean', description: 'Run only NREM (compression) phase', default: false },
|
|
13
|
+
json: { type: 'boolean', description: 'Output full JSON' },
|
|
14
|
+
pretty: { type: 'boolean', description: 'Human-readable output (default)' },
|
|
13
15
|
})
|
|
14
16
|
.run(async ({ flags }) => {
|
|
15
|
-
|
|
17
|
+
const outputMode = resolveOutputModeOrExit(flags)
|
|
18
|
+
await initCliCommand('consolidate', {
|
|
19
|
+
debug: flags.debug,
|
|
20
|
+
config: flags.config,
|
|
21
|
+
json: outputMode === 'json',
|
|
22
|
+
quiet: flags.quiet,
|
|
23
|
+
})
|
|
16
24
|
log.debug('Running consolidate command', { nremOnly: flags['nrem-only'] })
|
|
17
25
|
const config = await loadConfig(process.cwd(), flags.config)
|
|
18
26
|
const db = await connect(config.surreal)
|
|
@@ -24,7 +32,14 @@ export const consolidateCmd = app.sub('consolidate')
|
|
|
24
32
|
llm: config.consolidation.llm,
|
|
25
33
|
embedding: config.embedding,
|
|
26
34
|
})
|
|
27
|
-
|
|
35
|
+
if (outputMode === 'json') {
|
|
36
|
+
printCliJson(run, flags)
|
|
37
|
+
} else {
|
|
38
|
+
console.log(
|
|
39
|
+
`Consolidation ${run.status}: phase=${run.phase} nodes_in=${run.nodes_in} nodes_out=${run.nodes_out}`,
|
|
40
|
+
)
|
|
41
|
+
if (run.error) console.log(` error: ${run.error}`)
|
|
42
|
+
}
|
|
28
43
|
} finally {
|
|
29
44
|
await disconnect()
|
|
30
45
|
}
|
|
@@ -3,7 +3,7 @@ import { loadConfig } from '../../config.ts'
|
|
|
3
3
|
import { connect, disconnect } from '../../db/client.ts'
|
|
4
4
|
import { checkCompatibility } from '../../db/preflight.ts'
|
|
5
5
|
import { getLogger } from '../../logger.ts'
|
|
6
|
-
import { app, initCliCommand } from '../shared.ts'
|
|
6
|
+
import { app, initCliCommand, resolveOutputModeOrExit } from '../shared.ts'
|
|
7
7
|
|
|
8
8
|
const log = getLogger(['suemo', 'cli', 'doctor'])
|
|
9
9
|
|
|
@@ -50,8 +50,19 @@ async function detectModelNames(db: Surreal): Promise<string[]> {
|
|
|
50
50
|
|
|
51
51
|
const doctorEmbedCmd = doctor.sub('embed')
|
|
52
52
|
.meta({ description: 'Diagnose fn::embed() and show setup steps' })
|
|
53
|
+
.flags({
|
|
54
|
+
json: { type: 'boolean', description: 'Machine-readable output mode' },
|
|
55
|
+
'verbose-json': { type: 'boolean', description: 'Include embeddings in JSON output (implies --json)' },
|
|
56
|
+
pretty: { type: 'boolean', description: 'Human-readable output (default)' },
|
|
57
|
+
})
|
|
53
58
|
.run(async ({ flags }) => {
|
|
54
|
-
|
|
59
|
+
const outputMode = resolveOutputModeOrExit(flags)
|
|
60
|
+
await initCliCommand('doctor embed', {
|
|
61
|
+
debug: flags.debug,
|
|
62
|
+
config: flags.config,
|
|
63
|
+
json: outputMode === 'json',
|
|
64
|
+
quiet: flags.quiet,
|
|
65
|
+
})
|
|
55
66
|
|
|
56
67
|
const config = await loadConfig(process.cwd(), flags.config)
|
|
57
68
|
const endpoint = toCliEndpoint(config.surreal.url)
|
|
@@ -115,8 +126,19 @@ const doctorEmbedCmd = doctor.sub('embed')
|
|
|
115
126
|
|
|
116
127
|
export const doctorCmd = doctor
|
|
117
128
|
.command(doctorEmbedCmd)
|
|
129
|
+
.flags({
|
|
130
|
+
json: { type: 'boolean', description: 'Machine-readable output mode' },
|
|
131
|
+
'verbose-json': { type: 'boolean', description: 'Include embeddings in JSON output (implies --json)' },
|
|
132
|
+
pretty: { type: 'boolean', description: 'Human-readable output (default)' },
|
|
133
|
+
})
|
|
118
134
|
.run(async ({ flags }) => {
|
|
119
|
-
|
|
135
|
+
const outputMode = resolveOutputModeOrExit(flags)
|
|
136
|
+
await initCliCommand('doctor', {
|
|
137
|
+
debug: flags.debug,
|
|
138
|
+
config: flags.config,
|
|
139
|
+
json: outputMode === 'json',
|
|
140
|
+
quiet: flags.quiet,
|
|
141
|
+
})
|
|
120
142
|
console.log('Use one of:')
|
|
121
143
|
console.log(' suemo doctor embed')
|
|
122
144
|
console.log('\nRun `suemo doctor --help` for full details.')
|
|
@@ -4,7 +4,7 @@ import { loadConfig } from '../../config.ts'
|
|
|
4
4
|
import { connect, disconnect } from '../../db/client.ts'
|
|
5
5
|
import { getLogger } from '../../logger.ts'
|
|
6
6
|
import type { MemoryNode, Relation } from '../../types.ts'
|
|
7
|
-
import { app, initCliCommand } from '../shared.ts'
|
|
7
|
+
import { app, initCliCommand, printCliJson, resolveOutputModeOrExit } from '../shared.ts'
|
|
8
8
|
|
|
9
9
|
const log = getLogger(['suemo', 'cli', 'export-import'])
|
|
10
10
|
|
|
@@ -14,9 +14,17 @@ export const exportCmd = app.sub('export')
|
|
|
14
14
|
.flags({
|
|
15
15
|
scope: { type: 'string', short: 's', description: 'Filter by scope' },
|
|
16
16
|
all: { type: 'boolean', description: 'Include invalidated nodes' },
|
|
17
|
+
json: { type: 'boolean', description: 'Force JSONL-safe mode (stderr logs only)' },
|
|
18
|
+
pretty: { type: 'boolean', description: 'Human-readable output mode (default)' },
|
|
17
19
|
})
|
|
18
20
|
.run(async ({ flags }) => {
|
|
19
|
-
|
|
21
|
+
const outputMode = resolveOutputModeOrExit(flags)
|
|
22
|
+
await initCliCommand('export', {
|
|
23
|
+
debug: flags.debug,
|
|
24
|
+
config: flags.config,
|
|
25
|
+
json: outputMode === 'json',
|
|
26
|
+
quiet: flags.quiet,
|
|
27
|
+
})
|
|
20
28
|
log.debug('Running export command', {
|
|
21
29
|
hasScope: Boolean(flags.scope),
|
|
22
30
|
includeInvalidated: Boolean(flags.all),
|
|
@@ -52,8 +60,18 @@ export const exportCmd = app.sub('export')
|
|
|
52
60
|
export const importCmd = app.sub('import')
|
|
53
61
|
.meta({ description: 'Import memories from a JSONL file' })
|
|
54
62
|
.args([{ name: 'file', type: 'string', required: true }])
|
|
63
|
+
.flags({
|
|
64
|
+
json: { type: 'boolean', description: 'Output JSON summary' },
|
|
65
|
+
pretty: { type: 'boolean', description: 'Human-readable output (default)' },
|
|
66
|
+
})
|
|
55
67
|
.run(async ({ args, flags }) => {
|
|
56
|
-
|
|
68
|
+
const outputMode = resolveOutputModeOrExit(flags)
|
|
69
|
+
await initCliCommand('import', {
|
|
70
|
+
debug: flags.debug,
|
|
71
|
+
config: flags.config,
|
|
72
|
+
json: outputMode === 'json',
|
|
73
|
+
quiet: flags.quiet,
|
|
74
|
+
})
|
|
57
75
|
log.debug('Running import command', { file: args.file })
|
|
58
76
|
const config = await loadConfig(process.cwd(), flags.config)
|
|
59
77
|
const db = await connect(config.surreal)
|
|
@@ -123,5 +141,9 @@ export const importCmd = app.sub('import')
|
|
|
123
141
|
rl.close()
|
|
124
142
|
await disconnect()
|
|
125
143
|
}
|
|
126
|
-
|
|
144
|
+
if (outputMode === 'json') {
|
|
145
|
+
printCliJson({ imported, skipped, errors, lines: lineNum }, flags)
|
|
146
|
+
} else {
|
|
147
|
+
console.log(`import done: imported=${imported} skipped=${skipped} errors=${errors} lines=${lineNum}`)
|
|
148
|
+
}
|
|
127
149
|
})
|
package/src/cli/commands/goal.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { loadConfig } from '../../config.ts'
|
|
|
3
3
|
import { connect, disconnect } from '../../db/client.ts'
|
|
4
4
|
import { goalList, goalResolve, goalSet } from '../../goal.ts'
|
|
5
5
|
import { getLogger } from '../../logger.ts'
|
|
6
|
-
import { app, initCliCommand } from '../shared.ts'
|
|
6
|
+
import { app, initCliCommand, printCliJson, resolveOutputModeOrExit } from '../shared.ts'
|
|
7
7
|
|
|
8
8
|
const log = getLogger(['suemo', 'cli', 'goal'])
|
|
9
9
|
|
|
@@ -17,9 +17,17 @@ const setCmd = goal.sub('set')
|
|
|
17
17
|
.flags({
|
|
18
18
|
scope: { type: 'string', short: 's', description: 'Scope label' },
|
|
19
19
|
tags: { type: 'string', short: 't', description: 'Comma-separated tags' },
|
|
20
|
+
json: { type: 'boolean', description: 'Output JSON result' },
|
|
21
|
+
pretty: { type: 'boolean', description: 'Human-readable output (default)' },
|
|
20
22
|
})
|
|
21
23
|
.run(async ({ args, flags }) => {
|
|
22
|
-
|
|
24
|
+
const outputMode = resolveOutputModeOrExit(flags)
|
|
25
|
+
await initCliCommand('goal set', {
|
|
26
|
+
debug: flags.debug,
|
|
27
|
+
config: flags.config,
|
|
28
|
+
json: outputMode === 'json',
|
|
29
|
+
quiet: flags.quiet,
|
|
30
|
+
})
|
|
23
31
|
log.debug('Running goal set command', {
|
|
24
32
|
hasScope: Boolean(flags.scope),
|
|
25
33
|
tagCount: flags.tags ? flags.tags.split(',').filter(Boolean).length : 0,
|
|
@@ -33,7 +41,12 @@ const setCmd = goal.sub('set')
|
|
|
33
41
|
...(flags.scope ? { scope: flags.scope } : {}),
|
|
34
42
|
tags: flags.tags ? flags.tags.split(',').map((t) => t.trim()) : [],
|
|
35
43
|
})
|
|
36
|
-
|
|
44
|
+
if (outputMode === 'json') {
|
|
45
|
+
printCliJson({ id: node.id, content: node.content }, flags)
|
|
46
|
+
} else {
|
|
47
|
+
console.log(`Created goal: ${node.id}`)
|
|
48
|
+
console.log(` ${node.content}`)
|
|
49
|
+
}
|
|
37
50
|
} finally {
|
|
38
51
|
if (db) await disconnect()
|
|
39
52
|
}
|
|
@@ -45,9 +58,17 @@ const listCmd = goal.sub('list')
|
|
|
45
58
|
.flags({
|
|
46
59
|
scope: { type: 'string', short: 's', description: 'Filter by scope' },
|
|
47
60
|
resolved: { type: 'boolean', description: 'Include resolved goals', default: false },
|
|
61
|
+
json: { type: 'boolean', description: 'Output JSON result' },
|
|
62
|
+
pretty: { type: 'boolean', description: 'Human-readable output (default)' },
|
|
48
63
|
})
|
|
49
64
|
.run(async ({ flags }) => {
|
|
50
|
-
|
|
65
|
+
const outputMode = resolveOutputModeOrExit(flags)
|
|
66
|
+
await initCliCommand('goal list', {
|
|
67
|
+
debug: flags.debug,
|
|
68
|
+
config: flags.config,
|
|
69
|
+
json: outputMode === 'json',
|
|
70
|
+
quiet: flags.quiet,
|
|
71
|
+
})
|
|
51
72
|
log.debug('Running goal list command', {
|
|
52
73
|
hasScope: Boolean(flags.scope),
|
|
53
74
|
includeResolved: Boolean(flags.resolved),
|
|
@@ -60,11 +81,19 @@ const listCmd = goal.sub('list')
|
|
|
60
81
|
...(flags.scope ? { scope: flags.scope } : {}),
|
|
61
82
|
includeResolved: flags.resolved,
|
|
62
83
|
})
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
84
|
+
if (outputMode === 'json') {
|
|
85
|
+
printCliJson(goals, flags)
|
|
86
|
+
} else {
|
|
87
|
+
if (goals.length === 0) {
|
|
88
|
+
console.log('No goals found.')
|
|
89
|
+
return
|
|
90
|
+
}
|
|
91
|
+
for (const g of goals) {
|
|
92
|
+
const status = g.valid_until ? `resolved ${g.valid_until}` : 'active'
|
|
93
|
+
console.log(`[${status}] ${g.id}`)
|
|
94
|
+
console.log(` ${g.content}`)
|
|
95
|
+
console.log()
|
|
96
|
+
}
|
|
68
97
|
}
|
|
69
98
|
} finally {
|
|
70
99
|
if (db) await disconnect()
|
|
@@ -75,15 +104,29 @@ const listCmd = goal.sub('list')
|
|
|
75
104
|
const resolveCmd = goal.sub('resolve')
|
|
76
105
|
.meta({ description: 'Mark a goal as resolved' })
|
|
77
106
|
.args([{ name: 'goalId', type: 'string', required: true }])
|
|
107
|
+
.flags({
|
|
108
|
+
json: { type: 'boolean', description: 'Output JSON result' },
|
|
109
|
+
pretty: { type: 'boolean', description: 'Human-readable output (default)' },
|
|
110
|
+
})
|
|
78
111
|
.run(async ({ args, flags }) => {
|
|
79
|
-
|
|
112
|
+
const outputMode = resolveOutputModeOrExit(flags)
|
|
113
|
+
await initCliCommand('goal resolve', {
|
|
114
|
+
debug: flags.debug,
|
|
115
|
+
config: flags.config,
|
|
116
|
+
json: outputMode === 'json',
|
|
117
|
+
quiet: flags.quiet,
|
|
118
|
+
})
|
|
80
119
|
log.debug('Running goal resolve command', { goalId: args.goalId })
|
|
81
120
|
const config = await loadConfig(process.cwd(), flags.config)
|
|
82
121
|
let db: Surreal | undefined
|
|
83
122
|
try {
|
|
84
123
|
db = await connect(config.surreal)
|
|
85
124
|
await goalResolve(db, args.goalId)
|
|
86
|
-
|
|
125
|
+
if (outputMode === 'json') {
|
|
126
|
+
printCliJson({ ok: true, goalId: args.goalId }, flags)
|
|
127
|
+
} else {
|
|
128
|
+
console.log(`✓ Goal ${args.goalId} resolved`)
|
|
129
|
+
}
|
|
87
130
|
} finally {
|
|
88
131
|
if (db) await disconnect()
|
|
89
132
|
}
|
|
@@ -3,7 +3,7 @@ import { healthReport, suemoStats, vitals } from '../../cognitive/health.ts'
|
|
|
3
3
|
import { loadConfig } from '../../config.ts'
|
|
4
4
|
import { connect, disconnect } from '../../db/client.ts'
|
|
5
5
|
import { getLogger } from '../../logger.ts'
|
|
6
|
-
import { app, initCliCommand } from '../shared.ts'
|
|
6
|
+
import { app, initCliCommand, printCliJson, resolveOutputModeOrExit } from '../shared.ts'
|
|
7
7
|
|
|
8
8
|
const log = getLogger(['suemo', 'cli', 'health'])
|
|
9
9
|
|
|
@@ -12,15 +12,34 @@ const health = app.sub('health')
|
|
|
12
12
|
|
|
13
13
|
const reportCmd = health.sub('report')
|
|
14
14
|
.meta({ description: 'Full health report (default)' })
|
|
15
|
+
.flags({
|
|
16
|
+
json: { type: 'boolean', description: 'Output full JSON' },
|
|
17
|
+
pretty: { type: 'boolean', description: 'Human-readable output (default)' },
|
|
18
|
+
})
|
|
15
19
|
.run(async ({ flags }) => {
|
|
16
|
-
|
|
20
|
+
const outputMode = resolveOutputModeOrExit(flags)
|
|
21
|
+
await initCliCommand('health report', {
|
|
22
|
+
debug: flags.debug,
|
|
23
|
+
config: flags.config,
|
|
24
|
+
json: outputMode === 'json',
|
|
25
|
+
quiet: flags.quiet,
|
|
26
|
+
})
|
|
17
27
|
log.debug('Running health report command')
|
|
18
28
|
const config = await loadConfig(process.cwd(), flags.config)
|
|
19
29
|
let db: Surreal | undefined
|
|
20
30
|
try {
|
|
21
31
|
db = await connect(config.surreal)
|
|
22
32
|
const report = await healthReport(db)
|
|
23
|
-
|
|
33
|
+
if (outputMode === 'json') {
|
|
34
|
+
printCliJson(report, flags)
|
|
35
|
+
} else {
|
|
36
|
+
console.log(
|
|
37
|
+
`nodes: total=${report.nodes.total} active=${report.nodes.active} consolidated=${report.nodes.consolidated}`,
|
|
38
|
+
)
|
|
39
|
+
console.log(`relations: ${report.relations}`)
|
|
40
|
+
console.log(`goals_active: ${report.goals_active} fsrs_due: ${report.fsrs_due}`)
|
|
41
|
+
console.log(`surreal: ${report.version_check.surreal_version} surrealkv=${report.version_check.surrealkv}`)
|
|
42
|
+
}
|
|
24
43
|
} finally {
|
|
25
44
|
if (db) await disconnect()
|
|
26
45
|
}
|
|
@@ -28,15 +47,30 @@ const reportCmd = health.sub('report')
|
|
|
28
47
|
|
|
29
48
|
const vitalsCmd = health.sub('vitals')
|
|
30
49
|
.meta({ description: 'Last 10 consolidation runs + node counts' })
|
|
50
|
+
.flags({
|
|
51
|
+
json: { type: 'boolean', description: 'Output full JSON' },
|
|
52
|
+
pretty: { type: 'boolean', description: 'Human-readable output (default)' },
|
|
53
|
+
})
|
|
31
54
|
.run(async ({ flags }) => {
|
|
32
|
-
|
|
55
|
+
const outputMode = resolveOutputModeOrExit(flags)
|
|
56
|
+
await initCliCommand('health vitals', {
|
|
57
|
+
debug: flags.debug,
|
|
58
|
+
config: flags.config,
|
|
59
|
+
json: outputMode === 'json',
|
|
60
|
+
quiet: flags.quiet,
|
|
61
|
+
})
|
|
33
62
|
log.debug('Running health vitals command')
|
|
34
63
|
const config = await loadConfig(process.cwd(), flags.config)
|
|
35
64
|
let db: Surreal | undefined
|
|
36
65
|
try {
|
|
37
66
|
db = await connect(config.surreal)
|
|
38
67
|
const v = await vitals(db)
|
|
39
|
-
|
|
68
|
+
if (outputMode === 'json') {
|
|
69
|
+
printCliJson(v, flags)
|
|
70
|
+
} else {
|
|
71
|
+
console.log(`last10Runs: ${v.last10Runs.length}`)
|
|
72
|
+
console.log(`kinds: ${Object.keys(v.nodesByKind).length} scopes: ${Object.keys(v.nodesByScope).length}`)
|
|
73
|
+
}
|
|
40
74
|
} finally {
|
|
41
75
|
if (db) await disconnect()
|
|
42
76
|
}
|
|
@@ -44,15 +78,30 @@ const vitalsCmd = health.sub('vitals')
|
|
|
44
78
|
|
|
45
79
|
const statsCmd = health.sub('stats')
|
|
46
80
|
.meta({ description: 'Lightweight usage stats' })
|
|
81
|
+
.flags({
|
|
82
|
+
json: { type: 'boolean', description: 'Output full JSON' },
|
|
83
|
+
pretty: { type: 'boolean', description: 'Human-readable output (default)' },
|
|
84
|
+
})
|
|
47
85
|
.run(async ({ flags }) => {
|
|
48
|
-
|
|
86
|
+
const outputMode = resolveOutputModeOrExit(flags)
|
|
87
|
+
await initCliCommand('health stats', {
|
|
88
|
+
debug: flags.debug,
|
|
89
|
+
config: flags.config,
|
|
90
|
+
json: outputMode === 'json',
|
|
91
|
+
quiet: flags.quiet,
|
|
92
|
+
})
|
|
49
93
|
log.debug('Running health stats command')
|
|
50
94
|
const config = await loadConfig(process.cwd(), flags.config)
|
|
51
95
|
let db: Surreal | undefined
|
|
52
96
|
try {
|
|
53
97
|
db = await connect(config.surreal)
|
|
54
98
|
const s = await suemoStats(db)
|
|
55
|
-
|
|
99
|
+
if (outputMode === 'json') {
|
|
100
|
+
printCliJson(s, flags)
|
|
101
|
+
} else {
|
|
102
|
+
console.log(`nodes: total=${s.totalNodes} active=${s.activeNodes} relations=${s.relations}`)
|
|
103
|
+
console.log(`writes=${s.totalWrites} queries=${s.totalQueries}`)
|
|
104
|
+
}
|
|
56
105
|
} finally {
|
|
57
106
|
if (db) await disconnect()
|
|
58
107
|
}
|
|
@@ -63,15 +112,34 @@ export const healthCmd = health
|
|
|
63
112
|
.command(vitalsCmd)
|
|
64
113
|
.command(statsCmd)
|
|
65
114
|
// Default: run the report when just `suemo health` is called
|
|
115
|
+
.flags({
|
|
116
|
+
json: { type: 'boolean', description: 'Output full JSON' },
|
|
117
|
+
pretty: { type: 'boolean', description: 'Human-readable output (default)' },
|
|
118
|
+
})
|
|
66
119
|
.run(async ({ flags }) => {
|
|
67
|
-
|
|
120
|
+
const outputMode = resolveOutputModeOrExit(flags)
|
|
121
|
+
await initCliCommand('health', {
|
|
122
|
+
debug: flags.debug,
|
|
123
|
+
config: flags.config,
|
|
124
|
+
json: outputMode === 'json',
|
|
125
|
+
quiet: flags.quiet,
|
|
126
|
+
})
|
|
68
127
|
log.debug('Running default health command')
|
|
69
128
|
const config = await loadConfig(process.cwd(), flags.config)
|
|
70
129
|
let db: Surreal | undefined
|
|
71
130
|
try {
|
|
72
131
|
db = await connect(config.surreal)
|
|
73
132
|
const report = await healthReport(db)
|
|
74
|
-
|
|
133
|
+
if (outputMode === 'json') {
|
|
134
|
+
printCliJson(report, flags)
|
|
135
|
+
} else {
|
|
136
|
+
console.log(
|
|
137
|
+
`nodes: total=${report.nodes.total} active=${report.nodes.active} consolidated=${report.nodes.consolidated}`,
|
|
138
|
+
)
|
|
139
|
+
console.log(`relations: ${report.relations}`)
|
|
140
|
+
console.log(`goals_active: ${report.goals_active} fsrs_due: ${report.fsrs_due}`)
|
|
141
|
+
console.log(`surreal: ${report.version_check.surreal_version} surrealkv=${report.version_check.surrealkv}`)
|
|
142
|
+
}
|
|
75
143
|
} finally {
|
|
76
144
|
if (db) await disconnect()
|
|
77
145
|
}
|
package/src/cli/commands/init.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { connect, disconnect } from '../../db/client.ts'
|
|
|
6
6
|
import { checkCompatibility } from '../../db/preflight.ts'
|
|
7
7
|
import { runSchema } from '../../db/schema.ts'
|
|
8
8
|
import { getLogger } from '../../logger.ts'
|
|
9
|
-
import { app, initCliCommand } from '../shared.ts'
|
|
9
|
+
import { app, initCliCommand, resolveOutputModeOrExit } from '../shared.ts'
|
|
10
10
|
|
|
11
11
|
import template from '../../config.template.ts' with { type: 'text' }
|
|
12
12
|
|
|
@@ -15,6 +15,8 @@ interface InitFlags {
|
|
|
15
15
|
config?: string | undefined
|
|
16
16
|
force?: boolean | undefined
|
|
17
17
|
yes?: boolean | undefined
|
|
18
|
+
json?: boolean | undefined
|
|
19
|
+
quiet?: boolean | undefined
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
const log = getLogger(['suemo', 'cli', 'init'])
|
|
@@ -118,9 +120,18 @@ const initConfigCmd = init.sub('config')
|
|
|
118
120
|
.meta({ description: 'Create/update ~/.suemo/suemo.ts config template' })
|
|
119
121
|
.flags({
|
|
120
122
|
force: { type: 'boolean', description: 'Overwrite existing config file' },
|
|
123
|
+
json: { type: 'boolean', description: 'Machine-readable output mode' },
|
|
124
|
+
'verbose-json': { type: 'boolean', description: 'Include embeddings in JSON output (implies --json)' },
|
|
125
|
+
pretty: { type: 'boolean', description: 'Human-readable output (default)' },
|
|
121
126
|
})
|
|
122
127
|
.run(async ({ flags }) => {
|
|
123
|
-
|
|
128
|
+
const outputMode = resolveOutputModeOrExit(flags)
|
|
129
|
+
await initCliCommand('init config', {
|
|
130
|
+
debug: flags.debug,
|
|
131
|
+
config: flags.config,
|
|
132
|
+
json: outputMode === 'json',
|
|
133
|
+
quiet: flags.quiet,
|
|
134
|
+
})
|
|
124
135
|
writeConfig(Boolean(flags.force), configOutputPath(flags))
|
|
125
136
|
})
|
|
126
137
|
|
|
@@ -128,9 +139,18 @@ const initSchemaCmd = init.sub('schema')
|
|
|
128
139
|
.meta({ description: 'Apply database schema using current config' })
|
|
129
140
|
.flags({
|
|
130
141
|
yes: { type: 'boolean', short: 'y', description: 'Skip schema confirmation prompt' },
|
|
142
|
+
json: { type: 'boolean', description: 'Machine-readable output mode' },
|
|
143
|
+
'verbose-json': { type: 'boolean', description: 'Include embeddings in JSON output (implies --json)' },
|
|
144
|
+
pretty: { type: 'boolean', description: 'Human-readable output (default)' },
|
|
131
145
|
})
|
|
132
146
|
.run(async ({ flags }) => {
|
|
133
|
-
|
|
147
|
+
const outputMode = resolveOutputModeOrExit(flags)
|
|
148
|
+
await initCliCommand('init schema', {
|
|
149
|
+
debug: flags.debug,
|
|
150
|
+
config: flags.config,
|
|
151
|
+
json: outputMode === 'json',
|
|
152
|
+
quiet: flags.quiet,
|
|
153
|
+
})
|
|
134
154
|
|
|
135
155
|
const confirmOptions: {
|
|
136
156
|
message: string
|
|
@@ -161,8 +181,19 @@ const initSchemaCmd = init.sub('schema')
|
|
|
161
181
|
export const initCmd = init
|
|
162
182
|
.command(initConfigCmd)
|
|
163
183
|
.command(initSchemaCmd)
|
|
184
|
+
.flags({
|
|
185
|
+
json: { type: 'boolean', description: 'Machine-readable output mode' },
|
|
186
|
+
'verbose-json': { type: 'boolean', description: 'Include embeddings in JSON output (implies --json)' },
|
|
187
|
+
pretty: { type: 'boolean', description: 'Human-readable output (default)' },
|
|
188
|
+
})
|
|
164
189
|
.run(async ({ flags }) => {
|
|
165
|
-
|
|
190
|
+
const outputMode = resolveOutputModeOrExit(flags)
|
|
191
|
+
await initCliCommand('init', {
|
|
192
|
+
debug: flags.debug,
|
|
193
|
+
config: flags.config,
|
|
194
|
+
json: outputMode === 'json',
|
|
195
|
+
quiet: flags.quiet,
|
|
196
|
+
})
|
|
166
197
|
console.log('Use one of:')
|
|
167
198
|
console.log(' suemo init config [--force]')
|
|
168
199
|
console.log(' suemo init schema [--yes]')
|
|
@@ -3,7 +3,7 @@ import { connect, disconnect } from '../../db/client.ts'
|
|
|
3
3
|
import { getLogger } from '../../logger.ts'
|
|
4
4
|
import { observe } from '../../memory/write.ts'
|
|
5
5
|
import { MemoryKindSchema } from '../../types.ts'
|
|
6
|
-
import { app, initCliCommand } from '../shared.ts'
|
|
6
|
+
import { app, initCliCommand, printCliJson, resolveOutputModeOrExit } from '../shared.ts'
|
|
7
7
|
|
|
8
8
|
const log = getLogger(['suemo', 'cli', 'observe'])
|
|
9
9
|
|
|
@@ -17,9 +17,17 @@ export const observeCmd = app.sub('observe')
|
|
|
17
17
|
confidence: { type: 'number', description: 'Confidence 0.0–1.0', default: 1.0 },
|
|
18
18
|
source: { type: 'string', description: 'Source label' },
|
|
19
19
|
session: { type: 'string', description: 'Session ID (attach to open episode)' },
|
|
20
|
+
json: { type: 'boolean', description: 'Output JSON result' },
|
|
21
|
+
pretty: { type: 'boolean', description: 'Human-readable output (default)' },
|
|
20
22
|
})
|
|
21
23
|
.run(async ({ args, flags }) => {
|
|
22
|
-
|
|
24
|
+
const outputMode = resolveOutputModeOrExit(flags)
|
|
25
|
+
await initCliCommand('observe', {
|
|
26
|
+
debug: flags.debug,
|
|
27
|
+
config: flags.config,
|
|
28
|
+
json: outputMode === 'json',
|
|
29
|
+
quiet: flags.quiet,
|
|
30
|
+
})
|
|
23
31
|
|
|
24
32
|
const kindParse = MemoryKindSchema.safeParse(flags.kind)
|
|
25
33
|
if (!kindParse.success) {
|
|
@@ -46,7 +54,12 @@ export const observeCmd = app.sub('observe')
|
|
|
46
54
|
source: flags.source,
|
|
47
55
|
sessionId: flags.session,
|
|
48
56
|
}, config)
|
|
49
|
-
|
|
57
|
+
if (outputMode === 'json') {
|
|
58
|
+
printCliJson({ id: node.id, kind: node.kind, valid_from: node.valid_from }, flags)
|
|
59
|
+
} else {
|
|
60
|
+
console.log(`Stored ${node.kind} memory: ${node.id}`)
|
|
61
|
+
console.log(` valid_from: ${node.valid_from}`)
|
|
62
|
+
}
|
|
50
63
|
} finally {
|
|
51
64
|
await disconnect()
|
|
52
65
|
}
|