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 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: 'surreal', // fn::embed() — configured in SurrealDB
165
- dimension: 1536,
172
+ provider: 'surrealml', // fn::embed() — configured in SurrealDB
173
+ dimension: 384,
166
174
  },
167
175
  consolidation: {
168
176
  trigger: 'timer',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "suemo",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "Persistent semantic memory for AI agents — backed by SurrealDB.",
5
5
  "author": {
6
6
  "name": "Umar Alfarouk",
@@ -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
- await initCliCommand('believe', { debug: flags.debug, config: flags.config })
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
- const out: Record<string, unknown> = { id: node.id, valid_from: node.valid_from }
32
- if (contradicted) out.contradicted = contradicted.id
33
- console.log(JSON.stringify(out, null, 2))
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
- await initCliCommand('consolidate', { debug: flags.debug, config: flags.config })
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
- console.log(JSON.stringify(run, null, 2))
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
- await initCliCommand('doctor embed', { debug: flags.debug, config: flags.config })
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
- await initCliCommand('doctor', { debug: flags.debug, config: flags.config })
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
- await initCliCommand('export', { debug: flags.debug, config: flags.config })
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
- await initCliCommand('import', { debug: flags.debug, config: flags.config })
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
- console.log(JSON.stringify({ imported, skipped, errors, lines: lineNum }, null, 2))
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
  })
@@ -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
- await initCliCommand('goal set', { debug: flags.debug, config: flags.config })
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
- console.log(JSON.stringify({ id: node.id, content: node.content }, null, 2))
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
- await initCliCommand('goal list', { debug: flags.debug, config: flags.config })
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
- for (const g of goals) {
64
- const status = g.valid_until ? `resolved ${g.valid_until}` : 'active'
65
- console.log(`[${status}] ${g.id}`)
66
- console.log(` ${g.content}`)
67
- console.log()
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
- await initCliCommand('goal resolve', { debug: flags.debug, config: flags.config })
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
- console.log(`✓ Goal ${args.goalId} resolved`)
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
- await initCliCommand('health report', { debug: flags.debug, config: flags.config })
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
- console.log(JSON.stringify(report, null, 2))
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
- await initCliCommand('health vitals', { debug: flags.debug, config: flags.config })
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
- console.log(JSON.stringify(v, null, 2))
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
- await initCliCommand('health stats', { debug: flags.debug, config: flags.config })
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
- console.log(JSON.stringify(s, null, 2))
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
- await initCliCommand('health', { debug: flags.debug, config: flags.config })
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
- console.log(JSON.stringify(report, null, 2))
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
  }
@@ -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
- await initCliCommand('init config', { debug: flags.debug, config: flags.config })
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
- await initCliCommand('init schema', { debug: flags.debug, config: flags.config })
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
- await initCliCommand('init', { debug: flags.debug, config: flags.config })
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
- await initCliCommand('observe', { debug: flags.debug, config: flags.config })
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
- console.log(JSON.stringify({ id: node.id, kind: node.kind, valid_from: node.valid_from }, null, 2))
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
  }