suemo 0.0.1 → 0.0.2
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 +37 -10
- package/package.json +1 -1
- package/src/cli/commands/believe.ts +22 -12
- package/src/cli/commands/consolidate.ts +18 -11
- package/src/cli/commands/doctor.ts +123 -0
- package/src/cli/commands/export-import.ts +58 -47
- package/src/cli/commands/goal.ts +52 -27
- package/src/cli/commands/health.ts +35 -17
- package/src/cli/commands/init.ts +155 -75
- package/src/cli/commands/observe.ts +25 -13
- package/src/cli/commands/query.ts +23 -7
- package/src/cli/commands/recall.ts +12 -6
- package/src/cli/commands/serve.ts +14 -5
- package/src/cli/commands/sync.ts +15 -6
- package/src/cli/commands/timeline.ts +30 -18
- package/src/cli/commands/wander.ts +27 -16
- package/src/cli/index.ts +3 -4
- package/src/cli/shared.ts +34 -0
- package/src/cognitive/consolidate.ts +48 -19
- package/src/cognitive/contradiction.ts +19 -7
- package/src/config.template.ts +36 -0
- package/src/config.ts +41 -12
- package/src/db/preflight.ts +32 -6
- package/src/db/schema.surql +11 -8
- package/src/db/schema.ts +6 -3
- package/src/embedding/index.ts +52 -0
- package/src/embedding/openai-compatible.ts +43 -0
- package/src/goal.ts +3 -1
- package/src/mcp/dispatch.ts +134 -0
- package/src/mcp/server.ts +25 -2
- package/src/mcp/stdio.ts +314 -0
- package/src/mcp/tools.ts +8 -89
- package/src/memory/read.ts +74 -19
- package/src/memory/write.ts +54 -18
- package/src/cli/commands/shared.ts +0 -20
package/README.md
CHANGED
|
@@ -58,23 +58,24 @@ bun install
|
|
|
58
58
|
**1. Create config**
|
|
59
59
|
|
|
60
60
|
```sh
|
|
61
|
-
bun run src/cli/index.ts init
|
|
62
|
-
# or, once linked: suemo init
|
|
61
|
+
bun run src/cli/index.ts init config
|
|
62
|
+
# or, once linked: suemo init config
|
|
63
63
|
```
|
|
64
64
|
|
|
65
65
|
This writes `~/.suemo/suemo.ts`. Edit it with your SurrealDB URL, credentials, and LLM endpoint.
|
|
66
66
|
|
|
67
67
|
**2. Apply schema**
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
Apply schema after you set/edit namespace/database:
|
|
70
70
|
|
|
71
71
|
```sh
|
|
72
|
-
|
|
73
|
-
SURREAL_USER=root \
|
|
74
|
-
SURREAL_PASS=root \
|
|
75
|
-
suemo init
|
|
72
|
+
suemo init schema
|
|
76
73
|
```
|
|
77
74
|
|
|
75
|
+
Pass `--yes` to skip schema confirmation in non-interactive flows.
|
|
76
|
+
|
|
77
|
+
`suemo init` now shows guidance by default; run `init config` and `init schema` explicitly.
|
|
78
|
+
|
|
78
79
|
**3. Store a memory**
|
|
79
80
|
|
|
80
81
|
```sh
|
|
@@ -107,8 +108,10 @@ Global flags (inherited by all commands):
|
|
|
107
108
|
-d, --debug Verbose debug logging
|
|
108
109
|
|
|
109
110
|
Commands:
|
|
110
|
-
init
|
|
111
|
-
|
|
111
|
+
init Show init subcommands and usage guidance
|
|
112
|
+
init config Create/update ~/.suemo/suemo.ts
|
|
113
|
+
init schema Apply DB schema from current config (with confirm)
|
|
114
|
+
serve Start the MCP server (HTTP or stdio)
|
|
112
115
|
observe <content> Store an observation
|
|
113
116
|
believe <content> Store a belief (triggers contradiction detection)
|
|
114
117
|
query <input> Hybrid semantic search
|
|
@@ -119,6 +122,7 @@ Commands:
|
|
|
119
122
|
goal list List active goals
|
|
120
123
|
goal resolve <id> Mark a goal achieved
|
|
121
124
|
consolidate Run NREM + REM consolidation pipeline
|
|
125
|
+
doctor embed Diagnose fn::embed setup and print fix steps
|
|
122
126
|
health Memory health report
|
|
123
127
|
health vitals Last 10 consolidation runs + node counts
|
|
124
128
|
sync Push memories to a remote SurrealDB instance
|
|
@@ -130,7 +134,16 @@ Commands:
|
|
|
130
134
|
|
|
131
135
|
## Config
|
|
132
136
|
|
|
133
|
-
Config lives at `~/.suemo/suemo.ts` (user-level) or `suemo.config.ts` in the project root. Project-local takes precedence.
|
|
137
|
+
Config lives at `~/.suemo/suemo.ts` (user-level) or `suemo.config.ts` in the project root. Project-local takes precedence.
|
|
138
|
+
|
|
139
|
+
Resolution order:
|
|
140
|
+
|
|
141
|
+
1. `--config <path>`
|
|
142
|
+
2. `SUEMO_CONFIG_PATH`
|
|
143
|
+
3. project-local (`suemo.config.ts` / `suemo.config.js`)
|
|
144
|
+
4. user-level (`~/.suemo/suemo.ts`)
|
|
145
|
+
|
|
146
|
+
Set `SUEMO_DEBUG=1` (or `true`, `yes`, `on`) to enable debug logging globally for CLI commands.
|
|
134
147
|
|
|
135
148
|
```ts
|
|
136
149
|
// ~/.suemo/suemo.ts
|
|
@@ -181,6 +194,20 @@ export default {
|
|
|
181
194
|
suemo serve --config ~/.suemo/opencode.ts
|
|
182
195
|
```
|
|
183
196
|
|
|
197
|
+
For local stdio transport (no network port), run:
|
|
198
|
+
|
|
199
|
+
```sh
|
|
200
|
+
suemo serve --stdio
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
If stdio startup fails with an embedding preflight error, run:
|
|
204
|
+
|
|
205
|
+
```sh
|
|
206
|
+
suemo doctor embed
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
This prints your active target (`url`, `namespace`, `database`) and step-by-step commands to import a model and define `fn::embed()` for that exact database.
|
|
210
|
+
|
|
184
211
|
---
|
|
185
212
|
|
|
186
213
|
## MCP Tools
|
package/package.json
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { loadConfig } from '../../config.ts'
|
|
2
2
|
import { connect, disconnect } from '../../db/client.ts'
|
|
3
|
-
import {
|
|
3
|
+
import { getLogger } from '../../logger.ts'
|
|
4
4
|
import { believe } from '../../memory/write.ts'
|
|
5
|
-
import { app } from '../shared.ts'
|
|
5
|
+
import { app, initCliCommand } from '../shared.ts'
|
|
6
|
+
|
|
7
|
+
const log = getLogger(['suemo', 'cli', 'believe'])
|
|
6
8
|
|
|
7
9
|
export const believeCmd = app.sub('believe')
|
|
8
10
|
.meta({ description: 'Store a belief (triggers contradiction detection)' })
|
|
@@ -12,16 +14,24 @@ export const believeCmd = app.sub('believe')
|
|
|
12
14
|
confidence: { type: 'number', description: 'Confidence 0.0–1.0', default: 1.0 },
|
|
13
15
|
})
|
|
14
16
|
.run(async ({ args, flags }) => {
|
|
15
|
-
await
|
|
17
|
+
await initCliCommand('believe', { debug: flags.debug, config: flags.config })
|
|
16
18
|
const config = await loadConfig(process.cwd(), flags.config)
|
|
17
19
|
const db = await connect(config.surreal)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
20
|
+
try {
|
|
21
|
+
log.debug('Running believe command', {
|
|
22
|
+
hasScope: Boolean(flags.scope),
|
|
23
|
+
confidence: flags.confidence,
|
|
24
|
+
contentLength: args.content.length,
|
|
25
|
+
})
|
|
26
|
+
const { node, contradicted } = await believe(db, {
|
|
27
|
+
content: args.content,
|
|
28
|
+
scope: flags.scope,
|
|
29
|
+
confidence: flags.confidence,
|
|
30
|
+
}, 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))
|
|
34
|
+
} finally {
|
|
35
|
+
await disconnect()
|
|
36
|
+
}
|
|
27
37
|
})
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { consolidate } from '../../cognitive/consolidate.ts'
|
|
2
2
|
import { loadConfig } from '../../config.ts'
|
|
3
3
|
import { connect, disconnect } from '../../db/client.ts'
|
|
4
|
-
import {
|
|
5
|
-
import { app } from '../shared.ts'
|
|
4
|
+
import { getLogger } from '../../logger.ts'
|
|
5
|
+
import { app, initCliCommand } from '../shared.ts'
|
|
6
|
+
|
|
7
|
+
const log = getLogger(['suemo', 'cli', 'consolidate'])
|
|
6
8
|
|
|
7
9
|
export const consolidateCmd = app.sub('consolidate')
|
|
8
10
|
.meta({ description: 'Manually trigger memory consolidation (NREM + REM)' })
|
|
@@ -10,15 +12,20 @@ export const consolidateCmd = app.sub('consolidate')
|
|
|
10
12
|
'nrem-only': { type: 'boolean', description: 'Run only NREM (compression) phase', default: false },
|
|
11
13
|
})
|
|
12
14
|
.run(async ({ flags }) => {
|
|
13
|
-
await
|
|
15
|
+
await initCliCommand('consolidate', { debug: flags.debug, config: flags.config })
|
|
16
|
+
log.debug('Running consolidate command', { nremOnly: flags['nrem-only'] })
|
|
14
17
|
const config = await loadConfig(process.cwd(), flags.config)
|
|
15
18
|
const db = await connect(config.surreal)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
try {
|
|
20
|
+
const run = await consolidate(db, {
|
|
21
|
+
nremOnly: flags['nrem-only'],
|
|
22
|
+
nremSimilarityThreshold: config.consolidation.nremSimilarityThreshold,
|
|
23
|
+
remRelationThreshold: config.consolidation.remRelationThreshold,
|
|
24
|
+
llm: config.consolidation.llm,
|
|
25
|
+
embedding: config.embedding,
|
|
26
|
+
})
|
|
27
|
+
console.log(JSON.stringify(run, null, 2))
|
|
28
|
+
} finally {
|
|
29
|
+
await disconnect()
|
|
30
|
+
}
|
|
24
31
|
})
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import type { Surreal } from 'surrealdb'
|
|
2
|
+
import { loadConfig } from '../../config.ts'
|
|
3
|
+
import { connect, disconnect } from '../../db/client.ts'
|
|
4
|
+
import { checkCompatibility } from '../../db/preflight.ts'
|
|
5
|
+
import { getLogger } from '../../logger.ts'
|
|
6
|
+
import { app, initCliCommand } from '../shared.ts'
|
|
7
|
+
|
|
8
|
+
const log = getLogger(['suemo', 'cli', 'doctor'])
|
|
9
|
+
|
|
10
|
+
const doctor = app.sub('doctor')
|
|
11
|
+
.meta({ description: 'Diagnostics and setup guidance' })
|
|
12
|
+
|
|
13
|
+
function toCliEndpoint(url: string): string {
|
|
14
|
+
if (url.startsWith('ws://')) return `http://${url.slice('ws://'.length)}`
|
|
15
|
+
if (url.startsWith('wss://')) return `https://${url.slice('wss://'.length)}`
|
|
16
|
+
return url
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function printStatus(label: string, ok: boolean, detail?: string): void {
|
|
20
|
+
const prefix = ok ? '✓' : '✗'
|
|
21
|
+
if (detail) {
|
|
22
|
+
console.log(`${prefix} ${label}: ${detail}`)
|
|
23
|
+
return
|
|
24
|
+
}
|
|
25
|
+
console.log(`${prefix} ${label}`)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function probeEmbed(db: Surreal): Promise<{ ok: boolean; error?: string }> {
|
|
29
|
+
try {
|
|
30
|
+
await db.query('RETURN fn::embed("suemo doctor embed probe")')
|
|
31
|
+
return { ok: true }
|
|
32
|
+
} catch (error) {
|
|
33
|
+
return { ok: false, error: String(error) }
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function detectModelNames(db: Surreal): Promise<string[]> {
|
|
38
|
+
try {
|
|
39
|
+
const result = await db.query<unknown[]>('INFO FOR DB')
|
|
40
|
+
const first = Array.isArray(result) ? result[0] : null
|
|
41
|
+
const row = Array.isArray(first) ? first[0] : first
|
|
42
|
+
if (!row || typeof row !== 'object') return []
|
|
43
|
+
const models = (row as { models?: unknown }).models
|
|
44
|
+
if (!models || typeof models !== 'object') return []
|
|
45
|
+
return Object.keys(models as Record<string, unknown>)
|
|
46
|
+
} catch {
|
|
47
|
+
return []
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const doctorEmbedCmd = doctor.sub('embed')
|
|
52
|
+
.meta({ description: 'Diagnose fn::embed() and show setup steps' })
|
|
53
|
+
.run(async ({ flags }) => {
|
|
54
|
+
await initCliCommand('doctor embed', { debug: flags.debug, config: flags.config })
|
|
55
|
+
|
|
56
|
+
const config = await loadConfig(process.cwd(), flags.config)
|
|
57
|
+
const endpoint = toCliEndpoint(config.surreal.url)
|
|
58
|
+
|
|
59
|
+
console.log('Embedding diagnostics target:')
|
|
60
|
+
console.log(` url: ${config.surreal.url}`)
|
|
61
|
+
console.log(` namespace: ${config.surreal.namespace}`)
|
|
62
|
+
console.log(` database: ${config.surreal.database}`)
|
|
63
|
+
console.log()
|
|
64
|
+
|
|
65
|
+
let db: Surreal | undefined
|
|
66
|
+
try {
|
|
67
|
+
db = await connect(config.surreal)
|
|
68
|
+
|
|
69
|
+
const compat = await checkCompatibility(db, {
|
|
70
|
+
requireEmbedding: false,
|
|
71
|
+
context: 'cli:doctor-embed',
|
|
72
|
+
})
|
|
73
|
+
const embedProbe = await probeEmbed(db)
|
|
74
|
+
const modelNames = await detectModelNames(db)
|
|
75
|
+
|
|
76
|
+
printStatus('SurrealDB version', compat.surrealVersion !== 'unknown', compat.surrealVersion)
|
|
77
|
+
printStatus('SurrealKV', compat.surrealkv)
|
|
78
|
+
printStatus('Retention >= 90d', compat.retention_ok)
|
|
79
|
+
printStatus('fn::embed()', embedProbe.ok, embedProbe.ok ? 'callable' : 'not callable')
|
|
80
|
+
printStatus(
|
|
81
|
+
'Imported models in DB',
|
|
82
|
+
modelNames.length > 0,
|
|
83
|
+
modelNames.length > 0 ? modelNames.join(', ') : 'none detected',
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
if (embedProbe.ok) {
|
|
87
|
+
console.log('\n✅ fn::embed() is configured. You should be able to run:')
|
|
88
|
+
console.log(` suemo serve --stdio --config ${flags.config ?? '~/.suemo/suemo.ts'}`)
|
|
89
|
+
return
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
log.warn('fn::embed probe failed', { error: embedProbe.error ?? 'unknown' })
|
|
93
|
+
console.log('\nHow to set up fn::embed() (step-by-step):')
|
|
94
|
+
console.log('\n1) Ensure SurrealDB CLI exposes ML commands:')
|
|
95
|
+
console.log(' surreal ml --help')
|
|
96
|
+
console.log('\n2) Import a .surml embedding model into this exact NS/DB:')
|
|
97
|
+
console.log(
|
|
98
|
+
` surreal ml import --conn ${endpoint} --user <USER> --pass <PASS> --ns ${config.surreal.namespace} --db ${config.surreal.database} path/to/your-model.surml`,
|
|
99
|
+
)
|
|
100
|
+
console.log('\n3) Define fn::embed wrapper to your imported model:')
|
|
101
|
+
console.log(
|
|
102
|
+
` surreal sql --conn ${endpoint} --user <USER> --pass <PASS> --ns ${config.surreal.namespace} --db ${config.surreal.database} --query "DEFINE FUNCTION OVERWRITE fn::embed(\\$text: string) { RETURN ml::your_model<1.0.0>(\\$text); };"`,
|
|
103
|
+
)
|
|
104
|
+
console.log('\n4) Verify function now works:')
|
|
105
|
+
console.log(
|
|
106
|
+
` surreal sql --conn ${endpoint} --user <USER> --pass <PASS> --ns ${config.surreal.namespace} --db ${config.surreal.database} --query 'RETURN fn::embed("suemo test");'`,
|
|
107
|
+
)
|
|
108
|
+
console.log('\n5) Start MCP stdio server:')
|
|
109
|
+
console.log(` suemo serve --stdio --config ${flags.config ?? '~/.suemo/suemo.ts'}`)
|
|
110
|
+
console.log('\nIf step 4 still fails, rerun: suemo doctor embed --config ~/.suemo/suemo.ts --debug')
|
|
111
|
+
} finally {
|
|
112
|
+
if (db) await disconnect()
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
export const doctorCmd = doctor
|
|
117
|
+
.command(doctorEmbedCmd)
|
|
118
|
+
.run(async ({ flags }) => {
|
|
119
|
+
await initCliCommand('doctor', { debug: flags.debug, config: flags.config })
|
|
120
|
+
console.log('Use one of:')
|
|
121
|
+
console.log(' suemo doctor embed')
|
|
122
|
+
console.log('\nRun `suemo doctor --help` for full details.')
|
|
123
|
+
})
|
|
@@ -2,9 +2,11 @@ import { createReadStream } from 'node:fs'
|
|
|
2
2
|
import { createInterface } from 'node:readline'
|
|
3
3
|
import { loadConfig } from '../../config.ts'
|
|
4
4
|
import { connect, disconnect } from '../../db/client.ts'
|
|
5
|
-
import {
|
|
5
|
+
import { getLogger } from '../../logger.ts'
|
|
6
6
|
import type { MemoryNode, Relation } from '../../types.ts'
|
|
7
|
-
import { app } from '../shared.ts'
|
|
7
|
+
import { app, initCliCommand } from '../shared.ts'
|
|
8
|
+
|
|
9
|
+
const log = getLogger(['suemo', 'cli', 'export-import'])
|
|
8
10
|
|
|
9
11
|
// ── export ─────────────────────────────────────────────────────────────────
|
|
10
12
|
export const exportCmd = app.sub('export')
|
|
@@ -14,31 +16,36 @@ export const exportCmd = app.sub('export')
|
|
|
14
16
|
all: { type: 'boolean', description: 'Include invalidated nodes' },
|
|
15
17
|
})
|
|
16
18
|
.run(async ({ flags }) => {
|
|
17
|
-
await
|
|
19
|
+
await initCliCommand('export', { debug: flags.debug, config: flags.config })
|
|
20
|
+
log.debug('Running export command', {
|
|
21
|
+
hasScope: Boolean(flags.scope),
|
|
22
|
+
includeInvalidated: Boolean(flags.all),
|
|
23
|
+
})
|
|
18
24
|
const config = await loadConfig(process.cwd(), flags.config)
|
|
19
25
|
const db = await connect(config.surreal)
|
|
26
|
+
try {
|
|
27
|
+
const activeFilter = flags.all ? 'true' : '(valid_until = NONE OR valid_until > time::now())'
|
|
28
|
+
const scopeFilter = '($scope = NONE OR scope = $scope)'
|
|
20
29
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const [nodesResult, relationsResult] = await Promise.all([
|
|
25
|
-
db.query<[MemoryNode[]]>(
|
|
26
|
-
`
|
|
30
|
+
const [nodesResult, relationsResult] = await Promise.all([
|
|
31
|
+
db.query<[MemoryNode[]]>(
|
|
32
|
+
`
|
|
27
33
|
SELECT * FROM memory WHERE ${activeFilter} AND ${scopeFilter} ORDER BY created_at ASC
|
|
28
34
|
`,
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
35
|
+
{ scope: flags.scope ?? null },
|
|
36
|
+
),
|
|
37
|
+
db.query<[Relation[]]>('SELECT * FROM relates_to ORDER BY created_at ASC'),
|
|
38
|
+
])
|
|
33
39
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
40
|
+
for (const node of nodesResult[0] ?? []) {
|
|
41
|
+
process.stdout.write(JSON.stringify({ _type: 'memory', ...node }) + '\n')
|
|
42
|
+
}
|
|
43
|
+
for (const rel of relationsResult[0] ?? []) {
|
|
44
|
+
process.stdout.write(JSON.stringify({ _type: 'relation', ...rel }) + '\n')
|
|
45
|
+
}
|
|
46
|
+
} finally {
|
|
47
|
+
await disconnect()
|
|
39
48
|
}
|
|
40
|
-
|
|
41
|
-
await disconnect()
|
|
42
49
|
})
|
|
43
50
|
|
|
44
51
|
// ── import ──────────────────────────────────────────────────────────────────
|
|
@@ -46,7 +53,8 @@ export const importCmd = app.sub('import')
|
|
|
46
53
|
.meta({ description: 'Import memories from a JSONL file' })
|
|
47
54
|
.args([{ name: 'file', type: 'string', required: true }])
|
|
48
55
|
.run(async ({ args, flags }) => {
|
|
49
|
-
await
|
|
56
|
+
await initCliCommand('import', { debug: flags.debug, config: flags.config })
|
|
57
|
+
log.debug('Running import command', { file: args.file })
|
|
50
58
|
const config = await loadConfig(process.cwd(), flags.config)
|
|
51
59
|
const db = await connect(config.surreal)
|
|
52
60
|
|
|
@@ -56,36 +64,39 @@ export const importCmd = app.sub('import')
|
|
|
56
64
|
let skipped = 0
|
|
57
65
|
let errors = 0
|
|
58
66
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
try {
|
|
68
|
+
for await (const line of rl) {
|
|
69
|
+
lineNum++
|
|
70
|
+
if (!line.trim()) continue
|
|
71
|
+
let row: Record<string, unknown>
|
|
72
|
+
try {
|
|
73
|
+
row = JSON.parse(line)
|
|
74
|
+
} catch {
|
|
75
|
+
console.error(`Line ${lineNum}: invalid JSON — stopping`)
|
|
76
|
+
break
|
|
77
|
+
}
|
|
69
78
|
|
|
70
|
-
|
|
71
|
-
|
|
79
|
+
const type = row['_type']
|
|
80
|
+
delete row['_type']
|
|
72
81
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
+
try {
|
|
83
|
+
if (type === 'memory') {
|
|
84
|
+
await db.query('INSERT IGNORE INTO memory $row', { row })
|
|
85
|
+
imported++
|
|
86
|
+
} else if (type === 'relation') {
|
|
87
|
+
await db.query('INSERT IGNORE INTO relates_to $row', { row })
|
|
88
|
+
imported++
|
|
89
|
+
} else {
|
|
90
|
+
skipped++
|
|
91
|
+
}
|
|
92
|
+
} catch (e) {
|
|
93
|
+
console.error(`Line ${lineNum}: insert failed — ${String(e)}`)
|
|
94
|
+
errors++
|
|
82
95
|
}
|
|
83
|
-
} catch (e) {
|
|
84
|
-
console.error(`Line ${lineNum}: insert failed — ${String(e)}`)
|
|
85
|
-
errors++
|
|
86
96
|
}
|
|
97
|
+
} finally {
|
|
98
|
+
rl.close()
|
|
99
|
+
await disconnect()
|
|
87
100
|
}
|
|
88
|
-
|
|
89
|
-
await disconnect()
|
|
90
101
|
console.log(JSON.stringify({ imported, skipped, errors, lines: lineNum }, null, 2))
|
|
91
102
|
})
|
package/src/cli/commands/goal.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import type { Surreal } from 'surrealdb'
|
|
1
2
|
import { loadConfig } from '../../config.ts'
|
|
2
3
|
import { connect, disconnect } from '../../db/client.ts'
|
|
3
4
|
import { goalList, goalResolve, goalSet } from '../../goal.ts'
|
|
4
|
-
import {
|
|
5
|
-
import { app } from '../shared.ts'
|
|
5
|
+
import { getLogger } from '../../logger.ts'
|
|
6
|
+
import { app, initCliCommand } from '../shared.ts'
|
|
7
|
+
|
|
8
|
+
const log = getLogger(['suemo', 'cli', 'goal'])
|
|
6
9
|
|
|
7
10
|
const goal = app.sub('goal')
|
|
8
11
|
.meta({ description: 'Manage goal nodes' })
|
|
@@ -16,15 +19,24 @@ const setCmd = goal.sub('set')
|
|
|
16
19
|
tags: { type: 'string', short: 't', description: 'Comma-separated tags' },
|
|
17
20
|
})
|
|
18
21
|
.run(async ({ args, flags }) => {
|
|
19
|
-
await
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
tags: flags.tags ? flags.tags.split(',').map((t) => t.trim()) : [],
|
|
22
|
+
await initCliCommand('goal set', { debug: flags.debug, config: flags.config })
|
|
23
|
+
log.debug('Running goal set command', {
|
|
24
|
+
hasScope: Boolean(flags.scope),
|
|
25
|
+
tagCount: flags.tags ? flags.tags.split(',').filter(Boolean).length : 0,
|
|
26
|
+
contentLength: args.content.length,
|
|
25
27
|
})
|
|
26
|
-
await
|
|
27
|
-
|
|
28
|
+
const config = await loadConfig(process.cwd(), flags.config)
|
|
29
|
+
let db: Surreal | undefined
|
|
30
|
+
try {
|
|
31
|
+
db = await connect(config.surreal)
|
|
32
|
+
const node = await goalSet(db, args.content, config, {
|
|
33
|
+
...(flags.scope ? { scope: flags.scope } : {}),
|
|
34
|
+
tags: flags.tags ? flags.tags.split(',').map((t) => t.trim()) : [],
|
|
35
|
+
})
|
|
36
|
+
console.log(JSON.stringify({ id: node.id, content: node.content }, null, 2))
|
|
37
|
+
} finally {
|
|
38
|
+
if (db) await disconnect()
|
|
39
|
+
}
|
|
28
40
|
})
|
|
29
41
|
|
|
30
42
|
// suemo goal list
|
|
@@ -35,19 +47,27 @@ const listCmd = goal.sub('list')
|
|
|
35
47
|
resolved: { type: 'boolean', description: 'Include resolved goals', default: false },
|
|
36
48
|
})
|
|
37
49
|
.run(async ({ flags }) => {
|
|
38
|
-
await
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
...(flags.scope ? { scope: flags.scope } : {}),
|
|
43
|
-
includeResolved: flags.resolved,
|
|
50
|
+
await initCliCommand('goal list', { debug: flags.debug, config: flags.config })
|
|
51
|
+
log.debug('Running goal list command', {
|
|
52
|
+
hasScope: Boolean(flags.scope),
|
|
53
|
+
includeResolved: Boolean(flags.resolved),
|
|
44
54
|
})
|
|
45
|
-
await
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
55
|
+
const config = await loadConfig(process.cwd(), flags.config)
|
|
56
|
+
let db: Surreal | undefined
|
|
57
|
+
try {
|
|
58
|
+
db = await connect(config.surreal)
|
|
59
|
+
const goals = await goalList(db, {
|
|
60
|
+
...(flags.scope ? { scope: flags.scope } : {}),
|
|
61
|
+
includeResolved: flags.resolved,
|
|
62
|
+
})
|
|
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()
|
|
68
|
+
}
|
|
69
|
+
} finally {
|
|
70
|
+
if (db) await disconnect()
|
|
51
71
|
}
|
|
52
72
|
})
|
|
53
73
|
|
|
@@ -56,12 +76,17 @@ const resolveCmd = goal.sub('resolve')
|
|
|
56
76
|
.meta({ description: 'Mark a goal as resolved' })
|
|
57
77
|
.args([{ name: 'goalId', type: 'string', required: true }])
|
|
58
78
|
.run(async ({ args, flags }) => {
|
|
59
|
-
await
|
|
79
|
+
await initCliCommand('goal resolve', { debug: flags.debug, config: flags.config })
|
|
80
|
+
log.debug('Running goal resolve command', { goalId: args.goalId })
|
|
60
81
|
const config = await loadConfig(process.cwd(), flags.config)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
82
|
+
let db: Surreal | undefined
|
|
83
|
+
try {
|
|
84
|
+
db = await connect(config.surreal)
|
|
85
|
+
await goalResolve(db, args.goalId)
|
|
86
|
+
console.log(`✓ Goal ${args.goalId} resolved`)
|
|
87
|
+
} finally {
|
|
88
|
+
if (db) await disconnect()
|
|
89
|
+
}
|
|
65
90
|
})
|
|
66
91
|
|
|
67
92
|
// Export the container with its subcommands registered
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import type { Surreal } from 'surrealdb'
|
|
1
2
|
import { healthReport, vitals } from '../../cognitive/health.ts'
|
|
2
3
|
import { loadConfig } from '../../config.ts'
|
|
3
4
|
import { connect, disconnect } from '../../db/client.ts'
|
|
4
|
-
import {
|
|
5
|
-
import { app } from '../shared.ts'
|
|
5
|
+
import { getLogger } from '../../logger.ts'
|
|
6
|
+
import { app, initCliCommand } from '../shared.ts'
|
|
7
|
+
|
|
8
|
+
const log = getLogger(['suemo', 'cli', 'health'])
|
|
6
9
|
|
|
7
10
|
const health = app.sub('health')
|
|
8
11
|
.meta({ description: 'Memory health report and vitals' })
|
|
@@ -10,23 +13,33 @@ const health = app.sub('health')
|
|
|
10
13
|
const reportCmd = health.sub('report')
|
|
11
14
|
.meta({ description: 'Full health report (default)' })
|
|
12
15
|
.run(async ({ flags }) => {
|
|
13
|
-
await
|
|
16
|
+
await initCliCommand('health report', { debug: flags.debug, config: flags.config })
|
|
17
|
+
log.debug('Running health report command')
|
|
14
18
|
const config = await loadConfig(process.cwd(), flags.config)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
let db: Surreal | undefined
|
|
20
|
+
try {
|
|
21
|
+
db = await connect(config.surreal)
|
|
22
|
+
const report = await healthReport(db)
|
|
23
|
+
console.log(JSON.stringify(report, null, 2))
|
|
24
|
+
} finally {
|
|
25
|
+
if (db) await disconnect()
|
|
26
|
+
}
|
|
19
27
|
})
|
|
20
28
|
|
|
21
29
|
const vitalsCmd = health.sub('vitals')
|
|
22
30
|
.meta({ description: 'Last 10 consolidation runs + node counts' })
|
|
23
31
|
.run(async ({ flags }) => {
|
|
24
|
-
await
|
|
32
|
+
await initCliCommand('health vitals', { debug: flags.debug, config: flags.config })
|
|
33
|
+
log.debug('Running health vitals command')
|
|
25
34
|
const config = await loadConfig(process.cwd(), flags.config)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
35
|
+
let db: Surreal | undefined
|
|
36
|
+
try {
|
|
37
|
+
db = await connect(config.surreal)
|
|
38
|
+
const v = await vitals(db)
|
|
39
|
+
console.log(JSON.stringify(v, null, 2))
|
|
40
|
+
} finally {
|
|
41
|
+
if (db) await disconnect()
|
|
42
|
+
}
|
|
30
43
|
})
|
|
31
44
|
|
|
32
45
|
export const healthCmd = health
|
|
@@ -34,10 +47,15 @@ export const healthCmd = health
|
|
|
34
47
|
.command(vitalsCmd)
|
|
35
48
|
// Default: run the report when just `suemo health` is called
|
|
36
49
|
.run(async ({ flags }) => {
|
|
37
|
-
await
|
|
50
|
+
await initCliCommand('health', { debug: flags.debug, config: flags.config })
|
|
51
|
+
log.debug('Running default health command')
|
|
38
52
|
const config = await loadConfig(process.cwd(), flags.config)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
53
|
+
let db: Surreal | undefined
|
|
54
|
+
try {
|
|
55
|
+
db = await connect(config.surreal)
|
|
56
|
+
const report = await healthReport(db)
|
|
57
|
+
console.log(JSON.stringify(report, null, 2))
|
|
58
|
+
} finally {
|
|
59
|
+
if (db) await disconnect()
|
|
60
|
+
}
|
|
43
61
|
})
|