om-memory-system 4.14.0-next.106 → 4.15.0-next.116
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 +106 -149
- package/dist/adapters/claude-code/hook-command.d.ts +3 -1
- package/dist/adapters/claude-code/hook-command.js +14 -0
- package/dist/adapters/claude-code/plugin-self-update.d.ts +27 -0
- package/dist/adapters/claude-code/plugin-self-update.js +103 -0
- package/dist/adapters/opencode/profile-learning.js +6 -25
- package/dist/cli/index.js +22 -0
- package/dist/cli/profile-catch-up-command.js +12 -0
- package/dist/cli/profile-cleanup-categories-command.d.ts +3 -0
- package/dist/cli/profile-cleanup-categories-command.js +92 -0
- package/dist/cli/profile-normalise-categories-command.d.ts +2 -0
- package/dist/cli/profile-normalise-categories-command.js +85 -0
- package/dist/core/extraction.d.ts +15 -0
- package/dist/core/extraction.js +36 -0
- package/dist/core/profile-analysis.js +9 -5
- package/dist/importer/claude-import.js +1 -0
- package/dist/importer/import-runs.js +9 -3
- package/dist/importer/import-sessions.d.ts +5 -0
- package/dist/importer/import-sessions.js +15 -3
- package/dist/importer/importer.d.ts +1 -0
- package/dist/importer/importer.js +1 -0
- package/dist/importer/opencode-import.d.ts +1 -0
- package/dist/importer/opencode-import.js +1 -0
- package/dist/importer/profile-import.d.ts +2 -0
- package/dist/importer/profile-import.js +4 -0
- package/dist/importer/run-import.d.ts +2 -0
- package/dist/importer/run-import.js +11 -2
- package/dist/importer/web-import-group.d.ts +57 -0
- package/dist/importer/web-import-group.js +355 -0
- package/dist/importer/web-import-jobs.d.ts +12 -6
- package/dist/importer/web-import-jobs.js +48 -2
- package/dist/importer/web-import-profile-estimate.d.ts +14 -0
- package/dist/importer/web-import-profile-estimate.js +37 -0
- package/dist/services/api-handlers.d.ts +7 -0
- package/dist/services/api-handlers.js +72 -0
- package/dist/services/turso/cross-process-write-lock.js +55 -18
- package/dist/services/user-profile/category-cleanup-job.d.ts +30 -0
- package/dist/services/user-profile/category-cleanup-job.js +50 -0
- package/dist/services/user-profile/category-cleanup-store.d.ts +8 -0
- package/dist/services/user-profile/category-cleanup-store.js +35 -0
- package/dist/services/user-profile/category-cleanup.d.ts +74 -0
- package/dist/services/user-profile/category-cleanup.js +310 -0
- package/dist/services/user-profile/category-groups.d.ts +60 -0
- package/dist/services/user-profile/category-groups.js +171 -0
- package/dist/services/user-profile/profile-candidate-matching.d.ts +19 -0
- package/dist/services/user-profile/profile-candidate-matching.js +67 -0
- package/dist/services/user-profile/profile-category.d.ts +13 -0
- package/dist/services/user-profile/profile-category.js +43 -0
- package/dist/services/user-profile/user-profile-manager.d.ts +9 -1
- package/dist/services/user-profile/user-profile-manager.js +46 -65
- package/dist/services/web-server.js +17 -2
- package/dist/services/web-update.d.ts +7 -2
- package/dist/services/web-update.js +20 -7
- package/dist/web/assets/index-B46sRbG_.js +127 -0
- package/dist/web/assets/index-BktRr9cC.css +2 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/web/assets/index-BH1QAU9V.js +0 -125
- package/dist/web/assets/index-xoKkyBBV.css +0 -2
package/README.md
CHANGED
|
@@ -1,191 +1,148 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src=".github/screenshots/omms-logo.png" alt="OMMS logo" width="96" height="96" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
1
5
|
# OMMS: Opinionated Modular Memory System
|
|
2
6
|
|
|
3
7
|
[](https://www.npmjs.com/package/om-memory-system)
|
|
4
8
|
[](https://www.npmjs.com/package/om-memory-system)
|
|
5
|
-
[](LICENSE.md)
|
|
10
|
+
|
|
11
|
+
OMMS gives AI coding agents long-term project memory. It captures technical
|
|
12
|
+
work, recalls relevant notes in later sessions and learns your working
|
|
13
|
+
preferences. One shared engine serves **OpenCode**, **Pi** and **Claude Code**,
|
|
14
|
+
so each host can retrieve memories captured by the others in the same project.
|
|
6
15
|
|
|
7
|
-
|
|
16
|
+
Memories are stored locally, by default in `~/.omms/data`. Embeddings run
|
|
17
|
+
locally by default; the first use downloads a search model. Capture and profile
|
|
18
|
+
learning make additional model calls that can send conversation content to your
|
|
19
|
+
chosen provider and incur costs. Remote embeddings are optional. OMMS removes
|
|
20
|
+
text inside `<private>` tags before storage. See [Configuration](docs/configuration.md)
|
|
21
|
+
for model choices and privacy controls.
|
|
8
22
|
|
|
9
|
-
|
|
10
|
-
short notes about what was done and decided in each project: fixes, design
|
|
11
|
-
choices, things that did not work. It brings the relevant notes back in later
|
|
12
|
-
sessions, so the agent does not start from zero every time. It also learns how
|
|
13
|
-
you like to work and keeps that as a user profile.
|
|
23
|
+
## Set up
|
|
14
24
|
|
|
15
|
-
|
|
25
|
+
Use Node.js **22.14 or later** for the terminal command and Claude Code hooks.
|
|
26
|
+
OpenCode v1 needs **1.18.29 or later**. OMMS also supports OpenCode v2 and the
|
|
16
27
|
[Pi coding agent](https://www.npmjs.com/package/@earendil-works/pi-coding-agent).
|
|
17
|
-
Both share one memory per project, so a note written in one is available in
|
|
18
|
-
the other. Everything is stored locally on your machine.
|
|
19
28
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
[
|
|
29
|
+
> Automatic history import is enabled by default and makes model calls.
|
|
30
|
+
> To disable it before your first session, set `"autoBackfill": false` in
|
|
31
|
+
> `~/.config/omms/omms.jsonc`. See [Automatic import](docs/web-ui-memory.md#automatic-import).
|
|
23
32
|
|
|
24
|
-
|
|
33
|
+
### OpenCode
|
|
25
34
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
- **Recalls when relevant.** Matching memories are added to the agent's
|
|
29
|
-
context: on every prompt in OpenCode v2 and Pi, at the start of a session in
|
|
30
|
-
OpenCode v1.
|
|
31
|
-
- **Learns your preferences.** A user profile of your habits builds up over
|
|
32
|
-
time and follows you across projects.
|
|
33
|
-
- **Imports your past sessions.** Pi and OpenCode import older history after
|
|
34
|
-
startup. A command gives you a preview and manual control.
|
|
35
|
-
- **Lets you look and edit.** A local web page shows every memory and your
|
|
36
|
-
profile.
|
|
37
|
-
- **Keeps private things private.** Text inside `<private>` tags is never
|
|
38
|
-
stored.
|
|
35
|
+
Add the package to `~/.config/opencode/opencode.json`. Keep your existing settings.
|
|
36
|
+
On Windows, use `%USERPROFILE%\.config\opencode\opencode.json`.
|
|
39
37
|
|
|
40
|
-
|
|
38
|
+
**OpenCode v2:**
|
|
41
39
|
|
|
42
|
-
|
|
40
|
+
```json
|
|
41
|
+
{ "plugins": ["om-memory-system"] }
|
|
42
|
+
```
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
**OpenCode v1:**
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
```json
|
|
47
|
+
{ "plugin": ["om-memory-system"] }
|
|
48
|
+
```
|
|
47
49
|
|
|
48
|
-
|
|
49
|
-
- **Pi coding agent**
|
|
50
|
-
- **Claude Code**, through a plugin of hooks. Capture needs your own external API. See
|
|
51
|
-
the [Claude Code adapter](docs/claude-code-adapter.md) guide for setup.
|
|
50
|
+
OpenCode v2 also supports:
|
|
52
51
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
Node.js 22.14 or later.
|
|
52
|
+
```bash
|
|
53
|
+
opencode plugin add om-memory-system
|
|
54
|
+
```
|
|
57
55
|
|
|
58
|
-
|
|
56
|
+
Restart OpenCode. The [OpenCode guide](docs/opencode-adapter.md#installation)
|
|
57
|
+
covers plugin management and its additional `cli.json` configuration.
|
|
59
58
|
|
|
60
|
-
###
|
|
59
|
+
### Pi
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pi install npm:om-memory-system
|
|
63
|
+
```
|
|
61
64
|
|
|
62
|
-
|
|
63
|
-
(on Windows, `%USERPROFILE%\.config\opencode\opencode.json`):
|
|
65
|
+
Restart Pi. See the [Pi guide](docs/pi-adapter.md).
|
|
64
66
|
|
|
65
|
-
|
|
66
|
-
// OpenCode v2
|
|
67
|
-
{ "plugins": ["om-memory-system"] }
|
|
67
|
+
### Claude Code
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
Install the OMMS plugin using the [Claude Code guide](docs/claude-code-adapter.md#installation).
|
|
70
|
+
Capture and profile learning require a configured external API; retrieval and
|
|
71
|
+
manual memory operations remain available without it. A global npm install is optional.
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
Restart OpenCode.
|
|
73
|
+
### Model and web app
|
|
75
74
|
|
|
76
|
-
|
|
75
|
+
With no model settings, OpenCode and Pi use the session model. You can choose a
|
|
76
|
+
host model or configure an external API under **Settings**. Claude Code uses
|
|
77
|
+
only the external API. See [Choosing the model](docs/configuration.md#choosing-the-model)
|
|
78
|
+
for the selection order and credentials.
|
|
77
79
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
Start a host session, then open [http://127.0.0.1:4747](http://127.0.0.1:4747).
|
|
81
|
+
Each host starts the shared web app when needed, unless it is disabled. Browse
|
|
82
|
+
captured notes on **Project memories**, or ask your agent to search memory for
|
|
83
|
+
an earlier decision.
|
|
81
84
|
|
|
82
|
-
|
|
85
|
+
### Terminal command (optional)
|
|
83
86
|
|
|
84
|
-
|
|
85
|
-
command on your `PATH`. You do not need it. OMMS runs the newest copy it finds on
|
|
86
|
-
the machine, so updating one host is enough. A global install from 4.3 or earlier
|
|
87
|
-
needs one update. See
|
|
88
|
-
[Updating and upgrading](docs/upgrading.md#one-update-updates-every-host).
|
|
89
|
-
To install it:
|
|
87
|
+
A global install puts `om-memory-system` on your `PATH`:
|
|
90
88
|
|
|
91
89
|
```bash
|
|
92
|
-
npm
|
|
90
|
+
npm install -g om-memory-system
|
|
93
91
|
om-memory-system --version
|
|
94
92
|
```
|
|
95
93
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
With no settings, OMMS uses the model of the session you are working in. To
|
|
99
|
-
use a different one, add it to `~/.config/omms/omms.jsonc`. If you have no
|
|
100
|
-
config file yet, OMMS creates this one with comments on first start.
|
|
101
|
-
|
|
102
|
-
```jsonc
|
|
103
|
-
{
|
|
104
|
-
// A model you are already signed in to in OpenCode or Pi.
|
|
105
|
-
// Use "inherit" to always follow the session's model.
|
|
106
|
-
"opencodeProvider": "openai",
|
|
107
|
-
"opencodeModel": "gpt-5.6-luna",
|
|
108
|
-
"piProvider": "openai-codex",
|
|
109
|
-
"piModel": "gpt-5.6-luna",
|
|
110
|
-
}
|
|
111
|
-
```
|
|
94
|
+
See the [CLI reference](docs/cli.md) for manual memory operations and history imports.
|
|
112
95
|
|
|
113
|
-
|
|
114
|
-
up on the Settings page's **External API** card, and choose `"external"` as a
|
|
115
|
-
host's model. See [Configuration](docs/configuration.md#choosing-the-model).
|
|
96
|
+
## Inside OMMS
|
|
116
97
|
|
|
117
|
-
|
|
98
|
+
These screenshots use synthetic examples and configuration. All visible prompts,
|
|
99
|
+
code snippets and outcomes are made up for the demonstration.
|
|
118
100
|
|
|
119
|
-
|
|
120
|
-
browser. OpenCode or the login web app serves this page. New memories appear
|
|
121
|
-
on the timeline. You can also ask the agent: "search memory for what we changed today".
|
|
101
|
+
### Project memories
|
|
122
102
|
|
|
123
|
-
|
|
103
|
+
Browse captured notes alongside example user input and technical outcomes.
|
|
124
104
|
|
|
125
|
-
|
|
126
|
-
calls. To turn it off, set `"autoBackfill": false` in
|
|
127
|
-
`~/.config/omms/omms.jsonc` before you start the agent. For a manual import or
|
|
128
|
-
a custom source, preview it inside the agent:
|
|
105
|
+

|
|
129
106
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
107
|
+
### Memory
|
|
108
|
+
|
|
109
|
+
Review history imports, manage profile learning and set memory limits. This page
|
|
110
|
+
also helps resolve missing project folders.
|
|
111
|
+
|
|
112
|
+

|
|
113
|
+
|
|
114
|
+
### Settings
|
|
115
|
+
|
|
116
|
+
Choose models and manage access. Diagnostics show capture outcomes and component health.
|
|
134
117
|
|
|
135
|
-
|
|
136
|
-
session's model; add `--model provider/id` for a cheaper one. There is also a
|
|
137
|
-
terminal version, `npx om-memory-system`, that uses an API key. See the
|
|
138
|
-
[OpenCode](docs/opencode-history-import.md) and [Pi](docs/pi-history-import.md)
|
|
139
|
-
import guides, and the [CLI reference](docs/cli.md).
|
|
140
|
-
|
|
141
|
-
## Keeping OMMS up to date
|
|
142
|
-
|
|
143
|
-
Pi shows a notice when a new version is out; update with
|
|
144
|
-
`pi update npm:om-memory-system`. On OpenCode v2, the footer shows when a new version is out;
|
|
145
|
-
run `opencode plugin update om-memory-system`. Restart the agent afterwards.
|
|
146
|
-
[UPDATES.md](UPDATES.md) explains how updates work on every host.
|
|
147
|
-
To stay on one version, install it with the number, for example
|
|
148
|
-
`pi install npm:om-memory-system@3.1.1` or
|
|
149
|
-
`opencode plugin add om-memory-system@3.1.1`; a pinned install is never
|
|
150
|
-
updated. Coming from `opencode-mem`? Your memories move over automatically, with a
|
|
151
|
-
backup first. See [Updating and upgrading](docs/upgrading.md) and
|
|
152
|
-
[CHANGELOG.md](CHANGELOG.md).
|
|
118
|
+

|
|
153
119
|
|
|
154
120
|
## Documentation
|
|
155
121
|
|
|
156
|
-
|
|
|
157
|
-
|
|
|
158
|
-
| [Using memory
|
|
159
|
-
| [
|
|
160
|
-
| [
|
|
161
|
-
| [Settings page](docs/web-ui-settings.md)
|
|
162
|
-
| [
|
|
163
|
-
| [
|
|
164
|
-
| [
|
|
165
|
-
| [
|
|
166
|
-
| [
|
|
167
|
-
| [
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
##
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
[
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
## License and links
|
|
184
|
-
|
|
185
|
-
MIT License. See [LICENSE.md](LICENSE.md).
|
|
186
|
-
|
|
187
|
-
- Repository: https://github.com/cmdaltctr/omms
|
|
188
|
-
- Upstream: https://github.com/tickernelz/opencode-mem
|
|
189
|
-
- Issues: https://github.com/cmdaltctr/omms/issues
|
|
190
|
-
|
|
191
|
-
Inspired by [opencode-supermemory](https://github.com/supermemoryai/opencode-supermemory).
|
|
122
|
+
| Guide | Covers |
|
|
123
|
+
| ------------------------------------------- | ------------------------------------------------------------ |
|
|
124
|
+
| [Using memory](docs/using-memory.md) | Daily use, capture, recall and the user profile |
|
|
125
|
+
| [Web UI](docs/web-ui.md) | Starting the app, browsing memories and safe network access |
|
|
126
|
+
| [Memory page](docs/web-ui-memory.md) | History imports, profile actions, limits and missing folders |
|
|
127
|
+
| [Settings page](docs/web-ui-settings.md) | Models, credentials, diagnostics and profile identities |
|
|
128
|
+
| [Configuration](docs/configuration.md) | Config files, model selection, privacy and embeddings |
|
|
129
|
+
| [Moving projects](docs/moving-projects.md) | Folder moves, backup and restore |
|
|
130
|
+
| [Updating and upgrading](docs/upgrading.md) | Updates, version pinning and older stores |
|
|
131
|
+
| [CLI reference](docs/cli.md) | Terminal commands |
|
|
132
|
+
| [For developers](docs/developers.md) | Building, testing and architecture |
|
|
133
|
+
| [Contributing](CONTRIBUTING.md) | Development workflow and pull requests |
|
|
134
|
+
|
|
135
|
+
History import guides: [OpenCode](docs/opencode-history-import.md),
|
|
136
|
+
[Pi](docs/pi-history-import.md) and [Claude Code](docs/claude-code-history-import.md).
|
|
137
|
+
See [UPDATES.md](UPDATES.md) for host update commands and
|
|
138
|
+
[CHANGELOG.md](CHANGELOG.md) for release changes. Existing `opencode-mem` users
|
|
139
|
+
can follow the [migration guide](docs/omms-migration.md).
|
|
140
|
+
|
|
141
|
+
## Licence
|
|
142
|
+
|
|
143
|
+
MIT. See [LICENSE.md](LICENSE.md) for copyright and attribution, and
|
|
144
|
+
[THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md) for bundled dependencies.
|
|
145
|
+
|
|
146
|
+
[Repository](https://github.com/cmdaltctr/omms) ·
|
|
147
|
+
[Issues](https://github.com/cmdaltctr/omms/issues) ·
|
|
148
|
+
[Documentation index](docs/README.md)
|
|
@@ -4,4 +4,6 @@ import { type ClaudeStatusOptions } from "./status.js";
|
|
|
4
4
|
* `om-memory-system claude-hook <event>`. Always returns 0: a hook must never
|
|
5
5
|
* block or break a Claude Code session, whatever goes wrong.
|
|
6
6
|
*/
|
|
7
|
-
export declare function runClaudeHookCommand(argv: string[], io?: ClaudeHookOptions & ClaudeStatusOptions
|
|
7
|
+
export declare function runClaudeHookCommand(argv: string[], io?: ClaudeHookOptions & ClaudeStatusOptions & {
|
|
8
|
+
skipSelfUpdate?: boolean;
|
|
9
|
+
}): Promise<number>;
|
|
@@ -9,6 +9,18 @@ async function printStatus(io) {
|
|
|
9
9
|
const status = await buildClaudeStatus({ ...inputs, fetch: io.fetch ?? globalThis.fetch });
|
|
10
10
|
(io.writeStdout ?? ((text) => process.stdout.write(text)))(`${JSON.stringify(status)}\n`);
|
|
11
11
|
}
|
|
12
|
+
/** Update the Claude plugin in the background when npm has a newer release. Never throws. */
|
|
13
|
+
async function selfUpdate(io) {
|
|
14
|
+
try {
|
|
15
|
+
const { startPluginSelfUpdate, pluginSelfUpdateDeps } = await import("./plugin-self-update.js");
|
|
16
|
+
const log = io.log ??
|
|
17
|
+
(async (message, data) => (await import("../../services/logger.js")).log(message, data));
|
|
18
|
+
await startPluginSelfUpdate(pluginSelfUpdateDeps(log));
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
/* The memory hook already ran; a failed update check changes nothing. */
|
|
22
|
+
}
|
|
23
|
+
}
|
|
12
24
|
/**
|
|
13
25
|
* `om-memory-system claude-hook <event>`. Always returns 0: a hook must never
|
|
14
26
|
* block or break a Claude Code session, whatever goes wrong.
|
|
@@ -22,6 +34,8 @@ export async function runClaudeHookCommand(argv, io = {}) {
|
|
|
22
34
|
}
|
|
23
35
|
if (isHookEvent(event)) {
|
|
24
36
|
await runClaudeHook(event, io);
|
|
37
|
+
if (event === "session-start" && !io.skipSelfUpdate)
|
|
38
|
+
await selfUpdate(io);
|
|
25
39
|
return 0;
|
|
26
40
|
}
|
|
27
41
|
// Only a known name reaches the log; argv is not trusted text.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** A second session does not start another update for the same release within this time. */
|
|
2
|
+
export declare const SELF_UPDATE_RETRY_MS: number;
|
|
3
|
+
export type PluginSelfUpdateCode = "started" | "current" | "recent" | "disabled" | "no-plugin" | "unreadable-plugin" | "unreachable" | "spawn-failed";
|
|
4
|
+
export interface PluginSelfUpdateDeps {
|
|
5
|
+
/** False when `OMMS_DISABLE_UPDATE_CHECK` is `1`. */
|
|
6
|
+
enabled: boolean;
|
|
7
|
+
/** `CLAUDE_PLUGIN_ROOT`; undefined outside a Claude plugin. */
|
|
8
|
+
pluginRoot: string | undefined;
|
|
9
|
+
readPluginVersion: (root: string) => string;
|
|
10
|
+
fetch: typeof fetch;
|
|
11
|
+
now: () => number;
|
|
12
|
+
readMarker: () => {
|
|
13
|
+
version: string;
|
|
14
|
+
startedAt: number;
|
|
15
|
+
} | null;
|
|
16
|
+
writeMarker: (value: {
|
|
17
|
+
version: string;
|
|
18
|
+
startedAt: number;
|
|
19
|
+
}) => void;
|
|
20
|
+
/** Start a process that outlives this hook. */
|
|
21
|
+
spawnDetached: (command: string, args: string[]) => void;
|
|
22
|
+
log: (message: string, data: Record<string, unknown>) => void | Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
/** Start a background plugin update when npm has a newer release. Never throws. */
|
|
25
|
+
export declare function startPluginSelfUpdate(deps: PluginSelfUpdateDeps): Promise<PluginSelfUpdateCode>;
|
|
26
|
+
/** Production inputs: the real plugin manifest, npm, and a marker file in ~/.omms. */
|
|
27
|
+
export declare function pluginSelfUpdateDeps(log: PluginSelfUpdateDeps["log"], env?: NodeJS.ProcessEnv): PluginSelfUpdateDeps;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { availableUpdate, latestNpmVersion } from "../../services/update-check.js";
|
|
6
|
+
// Claude Code installs the OMMS plugin from the `stable` branch, but its own
|
|
7
|
+
// update pass runs at its own time and can leave a new session on the old
|
|
8
|
+
// plugin. At session start OMMS compares the plugin with npm `latest` and, when
|
|
9
|
+
// it is older, runs Claude Code's own update commands in the background. The
|
|
10
|
+
// new copy loads in the next session or after /reload-plugins.
|
|
11
|
+
/** A second session does not start another update for the same release within this time. */
|
|
12
|
+
export const SELF_UPDATE_RETRY_MS = 30 * 60 * 1000;
|
|
13
|
+
/** Node script that refreshes the marketplace, then updates the plugin. Run detached. */
|
|
14
|
+
const UPDATE_SCRIPT = `
|
|
15
|
+
const { spawnSync } = require("node:child_process");
|
|
16
|
+
const shell = process.platform === "win32";
|
|
17
|
+
for (const args of [["plugin", "marketplace", "update", "omms"], ["plugin", "update", "omms@omms"]]) {
|
|
18
|
+
const run = spawnSync("claude", args, { stdio: "ignore", shell, timeout: 120000 });
|
|
19
|
+
if (run.status !== 0) process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
`;
|
|
22
|
+
/** Start a background plugin update when npm has a newer release. Never throws. */
|
|
23
|
+
export async function startPluginSelfUpdate(deps) {
|
|
24
|
+
const code = await decide(deps);
|
|
25
|
+
if (code.code === "started" || code.code === "spawn-failed") {
|
|
26
|
+
await deps.log("Claude plugin self-update", {
|
|
27
|
+
code: code.code,
|
|
28
|
+
plugin: code.plugin,
|
|
29
|
+
latest: code.latest,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
return code.code;
|
|
33
|
+
}
|
|
34
|
+
async function decide(deps) {
|
|
35
|
+
if (!deps.enabled)
|
|
36
|
+
return { code: "disabled" };
|
|
37
|
+
if (!deps.pluginRoot)
|
|
38
|
+
return { code: "no-plugin" };
|
|
39
|
+
let plugin;
|
|
40
|
+
try {
|
|
41
|
+
plugin = deps.readPluginVersion(deps.pluginRoot);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return { code: "unreadable-plugin" };
|
|
45
|
+
}
|
|
46
|
+
const latest = await latestNpmVersion(deps.fetch);
|
|
47
|
+
if (latest === null)
|
|
48
|
+
return { code: "unreachable" };
|
|
49
|
+
const newer = availableUpdate(plugin, latest);
|
|
50
|
+
if (!newer)
|
|
51
|
+
return { code: "current" };
|
|
52
|
+
const marker = deps.readMarker();
|
|
53
|
+
const now = deps.now();
|
|
54
|
+
if (marker?.version === newer && now - marker.startedAt <= SELF_UPDATE_RETRY_MS) {
|
|
55
|
+
return { code: "recent" };
|
|
56
|
+
}
|
|
57
|
+
// Write first, so a session that starts during the update does not start another.
|
|
58
|
+
deps.writeMarker({ version: newer, startedAt: now });
|
|
59
|
+
try {
|
|
60
|
+
deps.spawnDetached(process.execPath, ["-e", UPDATE_SCRIPT]);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return { code: "spawn-failed", plugin, latest: newer };
|
|
64
|
+
}
|
|
65
|
+
return { code: "started", plugin, latest: newer };
|
|
66
|
+
}
|
|
67
|
+
/** Production inputs: the real plugin manifest, npm, and a marker file in ~/.omms. */
|
|
68
|
+
export function pluginSelfUpdateDeps(log, env = process.env) {
|
|
69
|
+
const markerPath = join(homedir(), ".omms", "claude-plugin-update.json");
|
|
70
|
+
return {
|
|
71
|
+
enabled: env.OMMS_DISABLE_UPDATE_CHECK !== "1",
|
|
72
|
+
pluginRoot: env.CLAUDE_PLUGIN_ROOT || undefined,
|
|
73
|
+
readPluginVersion: (root) => {
|
|
74
|
+
const manifest = JSON.parse(readFileSync(join(root, ".claude-plugin", "plugin.json"), "utf8"));
|
|
75
|
+
if (typeof manifest.version !== "string")
|
|
76
|
+
throw new Error("no version");
|
|
77
|
+
return manifest.version;
|
|
78
|
+
},
|
|
79
|
+
fetch: globalThis.fetch,
|
|
80
|
+
now: Date.now,
|
|
81
|
+
readMarker: () => {
|
|
82
|
+
try {
|
|
83
|
+
const value = JSON.parse(readFileSync(markerPath, "utf8"));
|
|
84
|
+
return typeof value.version === "string" && typeof value.startedAt === "number"
|
|
85
|
+
? { version: value.version, startedAt: value.startedAt }
|
|
86
|
+
: null;
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
writeMarker: (value) => {
|
|
93
|
+
mkdirSync(dirname(markerPath), { recursive: true });
|
|
94
|
+
writeFileSync(markerPath, JSON.stringify(value));
|
|
95
|
+
},
|
|
96
|
+
spawnDetached: (command, args) => {
|
|
97
|
+
const child = spawn(command, args, { detached: true, stdio: "ignore", windowsHide: true });
|
|
98
|
+
child.on("error", () => { });
|
|
99
|
+
child.unref();
|
|
100
|
+
},
|
|
101
|
+
log,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
@@ -246,30 +246,7 @@ function generateChangeSummary(oldProfile, newProfile) {
|
|
|
246
246
|
return changes.length > 0 ? changes.join(", ") : "Profile refinement";
|
|
247
247
|
}
|
|
248
248
|
function buildCategorySummary(profileData) {
|
|
249
|
-
const
|
|
250
|
-
const patCats = [...new Set(profileData.patterns.map((p) => p.category))];
|
|
251
|
-
const catParts = [];
|
|
252
|
-
if (prefCats.length > 0) {
|
|
253
|
-
const catCounts = prefCats
|
|
254
|
-
.map((cat) => {
|
|
255
|
-
const cnt = profileData.preferences.filter((p) => p.category === cat).length;
|
|
256
|
-
return `${cat} (${cnt})`;
|
|
257
|
-
})
|
|
258
|
-
.join(", ");
|
|
259
|
-
catParts.push(`Preference categories: ${catCounts}`);
|
|
260
|
-
}
|
|
261
|
-
if (patCats.length > 0) {
|
|
262
|
-
const catCounts = patCats
|
|
263
|
-
.map((cat) => {
|
|
264
|
-
const cnt = profileData.patterns.filter((p) => p.category === cat).length;
|
|
265
|
-
return `${cat} (${cnt})`;
|
|
266
|
-
})
|
|
267
|
-
.join(", ");
|
|
268
|
-
catParts.push(`Pattern categories: ${catCounts}`);
|
|
269
|
-
}
|
|
270
|
-
const catSection = catParts.length > 0
|
|
271
|
-
? `## Existing Categories\nUse these exact category names when your observation fits:\n\n${catParts.join("\n")}\n`
|
|
272
|
-
: "";
|
|
249
|
+
const catSection = buildCategoryList(profileData);
|
|
273
250
|
const prefCount = profileData.preferences.length;
|
|
274
251
|
const patCount = profileData.patterns.length;
|
|
275
252
|
const wfCount = profileData.workflows.length;
|
|
@@ -350,7 +327,7 @@ CRITICAL: Only output observations grounded in the RECENT PROMPTS above. Write d
|
|
|
350
327
|
}
|
|
351
328
|
return truncate(base);
|
|
352
329
|
}
|
|
353
|
-
import { createUserProfileAnalysisSchema, USER_PROFILE_LLM_CONFIDENCE_MAX, } from "../../core/extraction.js";
|
|
330
|
+
import { buildCategoryList, createUserProfileAnalysisSchema, USER_PROFILE_LLM_CONFIDENCE_MAX, WORKFLOW_PROMPT_RULES, } from "../../core/extraction.js";
|
|
354
331
|
export { createUserProfileAnalysisSchema, USER_PROFILE_LLM_CONFIDENCE_MAX };
|
|
355
332
|
export function createUserProfileToolSchema(existingProfile) {
|
|
356
333
|
return {
|
|
@@ -523,6 +500,8 @@ CRITICAL: Detect the language used by the user in their prompts. You MUST output
|
|
|
523
500
|
|
|
524
501
|
CRITICAL: All JSON string values MUST escape double quotes with backslash. Do NOT use unescaped quotation marks inside string values.
|
|
525
502
|
|
|
503
|
+
${WORKFLOW_PROMPT_RULES}
|
|
504
|
+
|
|
526
505
|
Use the update_user_profile tool to save the ${existingProfile ? "updated" : "new"} profile.`;
|
|
527
506
|
const { z } = await import("zod");
|
|
528
507
|
const schema = createUserProfileAnalysisSchema(z);
|
|
@@ -580,6 +559,8 @@ CRITICAL: Detect the language used by the user in their prompts. You MUST output
|
|
|
580
559
|
|
|
581
560
|
CRITICAL: All JSON string values MUST escape double quotes with backslash. Do NOT use unescaped quotation marks inside string values.
|
|
582
561
|
|
|
562
|
+
${WORKFLOW_PROMPT_RULES}
|
|
563
|
+
|
|
583
564
|
Use the update_user_profile tool to save the ${existingProfile ? "updated" : "new"} profile.`;
|
|
584
565
|
const toolSchema = createUserProfileToolSchema(Boolean(existingProfile));
|
|
585
566
|
const result = await provider.executeToolCall(systemPrompt, context, toolSchema, `user-profile-${Date.now()}`);
|
package/dist/cli/index.js
CHANGED
|
@@ -18,6 +18,8 @@ Commands:
|
|
|
18
18
|
web update Update the global install and restart the web app
|
|
19
19
|
memory <mode> [options] Search, add, list, or forget memories (run with --help)
|
|
20
20
|
profile-catch-up Analyse every prompt waiting for profile learning (run with --help)
|
|
21
|
+
profile-normalise-categories Preview or confirm profile category renames (run with --help)
|
|
22
|
+
profile-cleanup-categories Group category names by meaning; preview, apply or undo (run with --help)
|
|
21
23
|
claude-hook <event> Run a Claude Code hook (used by the Claude Code plugin)
|
|
22
24
|
|
|
23
25
|
Options:
|
|
@@ -67,6 +69,14 @@ export async function runCli(argv) {
|
|
|
67
69
|
const { runProfileCatchUpCommand } = await import("./profile-catch-up-command.js");
|
|
68
70
|
return await runProfileCatchUpCommand(argv.slice(1));
|
|
69
71
|
}
|
|
72
|
+
if (argv[0] === "profile-normalise-categories") {
|
|
73
|
+
const { runProfileNormaliseCategoriesCommand } = await import("./profile-normalise-categories-command.js");
|
|
74
|
+
return await runProfileNormaliseCategoriesCommand(argv.slice(1));
|
|
75
|
+
}
|
|
76
|
+
if (argv[0] === "profile-cleanup-categories") {
|
|
77
|
+
const { runProfileCleanupCategoriesCommand } = await import("./profile-cleanup-categories-command.js");
|
|
78
|
+
return await runProfileCleanupCategoriesCommand(argv.slice(1));
|
|
79
|
+
}
|
|
70
80
|
if (argv[0] === "web") {
|
|
71
81
|
try {
|
|
72
82
|
const { runWebCommand } = await import("./web-command.js");
|
|
@@ -107,6 +117,18 @@ export async function runCli(argv) {
|
|
|
107
117
|
model = `${selected.provider}/${selected.modelId}`;
|
|
108
118
|
console.log(`Import model: ${model}`);
|
|
109
119
|
}
|
|
120
|
+
// Profile merging buffers findings (capped at 50 per type) while the embedding service is
|
|
121
|
+
// cold, and a profile-only import never embeds through the memory store. Warm it first.
|
|
122
|
+
if (!args.dryRun && !args.skipProfile) {
|
|
123
|
+
try {
|
|
124
|
+
const { memoryClient } = await import("../services/client.js");
|
|
125
|
+
await memoryClient.warmup();
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
console.warn(`Embedding warm-up failed: ${error instanceof Error ? error.message : String(error)}. ` +
|
|
129
|
+
"Profile findings may be held back until it works.");
|
|
130
|
+
}
|
|
131
|
+
}
|
|
110
132
|
const { formatHistoryImportReport, historyImportFailed, runHistoryImport } = await import("../importer/run-import.js");
|
|
111
133
|
const report = await runHistoryImport(host, args, {
|
|
112
134
|
cwd: process.cwd(),
|
|
@@ -75,6 +75,18 @@ export async function runProfileCatchUpCommand(argv, print = console.log) {
|
|
|
75
75
|
const { selectImportModel } = await import("../importer/model-selection.js");
|
|
76
76
|
const selected = selectImportModel(flags.model);
|
|
77
77
|
print(`Profile model: ${selected.provider}/${selected.modelId}`);
|
|
78
|
+
// A cold embedding service makes profile merging buffer findings (capped at 50 per type).
|
|
79
|
+
// A backlog that needs no model call never merges, so it does not start the service.
|
|
80
|
+
if (preview.calls > 0) {
|
|
81
|
+
try {
|
|
82
|
+
const { memoryClient } = await import("../services/client.js");
|
|
83
|
+
await memoryClient.warmup();
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
print(`Embedding warm-up failed: ${error instanceof Error ? error.message : String(error)}. ` +
|
|
87
|
+
"Profile findings may be held back until it works.");
|
|
88
|
+
}
|
|
89
|
+
}
|
|
78
90
|
const { drainProfileBacklog } = await import("../importer/profile-backlog.js");
|
|
79
91
|
const { catchUpLeaseHooks } = await import("../importer/profile-catch-up.js");
|
|
80
92
|
const hooks = await catchUpLeaseHooks();
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { CleanupDeps } from "../services/user-profile/category-cleanup.js";
|
|
2
|
+
/** Preview, apply or undo a meaning-based cleanup of the current user's profile categories. */
|
|
3
|
+
export declare function runProfileCleanupCategoriesCommand(argv: string[], print?: (line: string) => void, deps?: CleanupDeps): Promise<number>;
|