suemo 0.1.3 → 0.1.6
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 +48 -7
- package/package.json +1 -1
- package/skills/suemo/SKILL.md +1 -1
- package/skills/suemo/references/agents-snippet.md +1 -1
- package/skills/suemo/references/cli-reference.md +1 -1
- package/skills/suemo/references/core-workflow.md +1 -1
- package/skills/suemo/references/manual-test-plan.md +1 -1
- package/skills/suemo/references/mcp-reference.md +1 -1
- package/skills/suemo/references/schema-retention-longevity.md +1 -1
- package/skills/suemo/references/sync-local-vps.md +1 -1
- package/src/AGENTS.md +150 -0
- package/src/cli/commands/init.ts +510 -80
- package/src/cli/commands/query.ts +41 -3
- package/src/mcp/stdio.ts +3 -0
- package/src/memory/read.ts +392 -77
- package/src/types.ts +32 -4
package/README.md
CHANGED
|
@@ -52,6 +52,7 @@ For suemo, the critical capability is allowing custom functions:
|
|
|
52
52
|
- `search::*` (for `search::score`)
|
|
53
53
|
- `math::*` (for `math::mean`, `math::min`)
|
|
54
54
|
- `rand::*` (used in `wander` query)
|
|
55
|
+
- `array::*` (used in `recall` query)
|
|
55
56
|
|
|
56
57
|
If you run with strict capability mode, use an allowlist like:
|
|
57
58
|
|
|
@@ -138,7 +139,7 @@ What these commands do:
|
|
|
138
139
|
- `suemo init fastembed`
|
|
139
140
|
- checks `python-fastembed`, `python-fastapi`, `python-uvicorn` via `pacman -Q`
|
|
140
141
|
- installs `data/fastembed-server.py` to `/opt/suemo/fastembed-server.py`
|
|
141
|
-
- creates `/opt/fastembed/local.env` once (user override file; not overwritten)
|
|
142
|
+
- creates `/opt/suemo/fastembed/local.env` once (user override file; not overwritten)
|
|
142
143
|
- writes `/etc/systemd/system/suemo-fastembed.service`
|
|
143
144
|
- enables + starts `suemo-fastembed.service`
|
|
144
145
|
|
|
@@ -169,6 +170,40 @@ suemo observe "Bun is significantly faster than Node for CLI tools" \
|
|
|
169
170
|
suemo query "which JS runtime is faster"
|
|
170
171
|
```
|
|
171
172
|
|
|
173
|
+
To inspect ranking/scoring internals:
|
|
174
|
+
|
|
175
|
+
```sh
|
|
176
|
+
suemo query "which JS runtime is faster" --explain
|
|
177
|
+
suemo query "which JS runtime is faster" --explain --min-score 0.15 --relative-floor 0.75
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
`--explain` shows per-strategy contributions (vector/BM25/graph), final score, and applied thresholds.
|
|
181
|
+
|
|
182
|
+
### How query scoring works
|
|
183
|
+
|
|
184
|
+
`suemo query` computes a hybrid score per candidate and then applies score gates.
|
|
185
|
+
|
|
186
|
+
1. **Per-strategy raw scores**
|
|
187
|
+
- vector: cosine similarity between memory embedding and query embedding
|
|
188
|
+
- bm25: `search::score(1)` from full-text index
|
|
189
|
+
- graph: anchor-weighted relation strength from top vector anchors
|
|
190
|
+
|
|
191
|
+
2. **Normalization**
|
|
192
|
+
- `vector_norm = clamp((cosine - 0.2) / 0.8, 0, 1)`
|
|
193
|
+
- `bm25_norm = log1p(raw_bm25) / log1p(max_bm25_in_query)`
|
|
194
|
+
- `graph_norm = clamp(raw_graph, 0, 1)`
|
|
195
|
+
|
|
196
|
+
3. **Final score**
|
|
197
|
+
- `final = 0.5*vector_norm + 0.25*bm25_norm + 0.15*graph_norm + agreement_bonus`
|
|
198
|
+
- `agreement_bonus = 0.05` when a node matches at least two strategies
|
|
199
|
+
|
|
200
|
+
4. **Default gates (to reduce false positives)**
|
|
201
|
+
- `final >= 0.08` (`--min-score`)
|
|
202
|
+
- `final >= topScore * 0.65` (`--relative-floor`)
|
|
203
|
+
- `max(vector_norm, bm25_norm) >= 0.08` (primary evidence floor)
|
|
204
|
+
|
|
205
|
+
Use `--explain` to inspect all these values per result.
|
|
206
|
+
|
|
172
207
|
**5. Start the MCP server**
|
|
173
208
|
|
|
174
209
|
```sh
|
|
@@ -188,25 +223,31 @@ suemo serve --dev
|
|
|
188
223
|
|
|
189
224
|
## OpenCode setup (only integration target)
|
|
190
225
|
|
|
191
|
-
|
|
226
|
+
Create/update integration files automatically:
|
|
192
227
|
|
|
193
228
|
```sh
|
|
194
229
|
suemo init opencode
|
|
195
230
|
```
|
|
196
231
|
|
|
197
|
-
This
|
|
232
|
+
This updates:
|
|
198
233
|
|
|
199
|
-
-
|
|
200
|
-
-
|
|
234
|
+
- `~/.config/opencode/opencode.jsonc` (preferred) or `.json` fallback with `mcp.suemo`
|
|
235
|
+
- `~/.config/opencode/AGENTS.md` with managed `<!-- SUEMO:START --> ... <!-- SUEMO:END -->` block
|
|
236
|
+
|
|
237
|
+
Preview changes without writing files:
|
|
238
|
+
|
|
239
|
+
```sh
|
|
240
|
+
suemo init opencode --dry-run
|
|
241
|
+
```
|
|
201
242
|
|
|
202
|
-
To fetch
|
|
243
|
+
To fetch latest skill docs directly from your local checkout:
|
|
203
244
|
|
|
204
245
|
```sh
|
|
205
246
|
suemo skill
|
|
206
247
|
suemo skill core-workflow
|
|
207
248
|
```
|
|
208
249
|
|
|
209
|
-
|
|
250
|
+
This command is idempotent and avoids duplicate `mcp.suemo` entries.
|
|
210
251
|
|
|
211
252
|
---
|
|
212
253
|
|
package/package.json
CHANGED
package/skills/suemo/SKILL.md
CHANGED
package/src/AGENTS.md
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Working with suemo
|
|
2
|
+
|
|
3
|
+
suemo is your persistent memory. Use it to store what you learn, remember what matters, and build on past work.
|
|
4
|
+
|
|
5
|
+
## Before anything else
|
|
6
|
+
|
|
7
|
+
**Load the skill doc first.** It is versioned and may contain updated workflows, references, and tool signatures that supersede this file.
|
|
8
|
+
|
|
9
|
+
- CLI: `suemo skill`
|
|
10
|
+
- MCP: `skill({})`
|
|
11
|
+
|
|
12
|
+
For a specific reference: `suemo skill <reference-name>` / `skill({ reference: "<reference-name>" })`
|
|
13
|
+
|
|
14
|
+
Available references: `agents-snippet`, `cli-reference`, `core-workflow`, `manual-test-plan`, `mcp-reference`, `schema-retention-longevity`, `sync-local-vps`
|
|
15
|
+
|
|
16
|
+
Do not proceed until the skill doc is loaded.
|
|
17
|
+
|
|
18
|
+
## Session protocol
|
|
19
|
+
|
|
20
|
+
These steps are **mandatory**, not suggested.
|
|
21
|
+
|
|
22
|
+
### On session start
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
goal_list(scope: "<active-scope>")
|
|
26
|
+
query("recent work on [topic]", scope: "<active-scope>")
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Read relevant results before touching any code. Do not re-derive what is already stored.
|
|
30
|
+
|
|
31
|
+
### While working
|
|
32
|
+
|
|
33
|
+
Store anything that would save time next session:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
observe("[fact you learned or confirmed]")
|
|
37
|
+
believe("[interpretation or conclusion that might change]")
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Query only when you are **genuinely blocked** or starting a distinct new subtask. Do not query on every step — it burns context and adds noise.
|
|
41
|
+
|
|
42
|
+
### Before ending (or if interrupted)
|
|
43
|
+
|
|
44
|
+
Call this **before** your final tool invocation, not after — sessions can be cut off without warning:
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
episode_end(<session_id>, summary="<one sentence of what was accomplished>")
|
|
48
|
+
goal_resolve(<goal_id>) # only if the goal is fully done
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
If the session is interrupted and you never reach this step, that is acceptable. Incomplete episodes are better than missing observations mid-session.
|
|
52
|
+
|
|
53
|
+
## observe vs believe
|
|
54
|
+
|
|
55
|
+
Use `observe()` for facts. Use `believe()` for interpretations that could be wrong or change.
|
|
56
|
+
|
|
57
|
+
**Observe:** `"[tool] requires [flag] — without it, [failure mode]"`\
|
|
58
|
+
**Believe:** `"[tool] behaviour is likely a bug — worth retrying after an upgrade"`
|
|
59
|
+
|
|
60
|
+
Beliefs trigger contradiction detection. If you later believe the opposite, the old belief is invalidated and both are linked — useful for tracing why you changed your mind.
|
|
61
|
+
|
|
62
|
+
For uncertain things, prefer `kind: "question"` or `kind: "hypothesis"` over stating them as facts.
|
|
63
|
+
|
|
64
|
+
## What to observe
|
|
65
|
+
|
|
66
|
+
Observe your **experience**, not documentation:
|
|
67
|
+
|
|
68
|
+
- Approaches tried and whether they worked
|
|
69
|
+
- Config or flags that took effort to figure out
|
|
70
|
+
- Errors encountered and how they were resolved
|
|
71
|
+
- Decisions made and the reasoning behind them
|
|
72
|
+
- Constraints or preferences the user stated
|
|
73
|
+
|
|
74
|
+
**Good:** `"[library] [method] runs before [other step] — caused [bug] in [context]"`\
|
|
75
|
+
**Skip:** `"[library] supports [feature]"` ← already in the docs
|
|
76
|
+
|
|
77
|
+
## What not to store
|
|
78
|
+
|
|
79
|
+
- Transient state: current cursor position, open file, in-progress thought
|
|
80
|
+
- Exact code snippets from files — observe your _understanding_ of them instead
|
|
81
|
+
- Timestamps — suemo sets `valid_from` automatically
|
|
82
|
+
- Uncertain things stated as facts — use `kind: "hypothesis"` instead
|
|
83
|
+
|
|
84
|
+
## Querying
|
|
85
|
+
|
|
86
|
+
suemo uses hybrid retrieval (vector + BM25 + graph) with score gates.
|
|
87
|
+
Natural language often works, but broad prompts can still be noisy.
|
|
88
|
+
|
|
89
|
+
Use scoped, specific queries first:
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
query("what approaches did we try for N+1 query issue", scope: "<active-scope>")
|
|
93
|
+
query("past decisions about auth middleware", scope: "<active-scope>")
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
For exact strings (error codes, env var names, flag spellings), quote them:
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
query('"SURREAL_CAPS_ALLOW_FUNC" init fix', scope: "<active-scope>")
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
If ranking looks weak, inspect scoring directly:
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
suemo query "<your query>" --scope <active-scope> --explain
|
|
106
|
+
suemo query "<your query>" --scope <active-scope> --explain --min-score 0.25 --relative-floor 0.8
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
- Use `query()` when searching — you don't know which node you want
|
|
110
|
+
- Use `recall(<nodeId>)` when you have a node ID and want it plus neighbours
|
|
111
|
+
|
|
112
|
+
`recall()` also ticks the spaced-repetition clock on that node.
|
|
113
|
+
|
|
114
|
+
## Using recall vs query
|
|
115
|
+
|
|
116
|
+
- Use `query()` when you don't know _which_ memory you want — you're searching
|
|
117
|
+
- Use `recall(nodeId)` when you have a specific node ID and want it plus its neighbours — you're inspecting
|
|
118
|
+
|
|
119
|
+
`recall()` also ticks the spaced-repetition clock on that node, marking it as "actively used".
|
|
120
|
+
|
|
121
|
+
## Goals
|
|
122
|
+
|
|
123
|
+
Set a goal at the start of any multi-step task:
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
goal_set("<description of task>", scope: "<active-scope>")
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Check open goals at session start (already covered above). Resolve when done:
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
goal_resolve(<id>)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Goals are a record of intent. Abandoned goals show up in health reports as a signal something was left incomplete.
|
|
136
|
+
|
|
137
|
+
## What suemo handles automatically
|
|
138
|
+
|
|
139
|
+
- **Timestamps** — `valid_from` on write, `valid_until` on contradiction or invalidation
|
|
140
|
+
- **Deduplication** — identical writes merge; near-duplicates create a new linked node
|
|
141
|
+
- **Consolidation** — runs on a schedule; no manual trigger needed
|
|
142
|
+
- **Embeddings** — computed server-side via `fn::embed()`; never pass vectors yourself
|
|
143
|
+
|
|
144
|
+
## Health check
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
suemo health
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Signs things are working: recent consolidation run, goals resolved not abandoned, queries returning relevant results without heavy tuning.
|