memorix 1.4.5 → 1.4.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/CHANGELOG.md +54 -0
- package/CLAUDE.md +7 -3
- package/README.md +5 -2
- package/dist/cli/index.js +1456 -1013
- package/dist/index.js +1265 -988
- package/dist/{maintenance-jobs-B1-W0ssz.d.ts → maintenance-jobs-DVRF-46r.d.ts} +1 -1
- package/dist/maintenance-runner.d.ts +1 -1
- package/dist/maintenance-runner.js +1755 -829
- package/dist/media-audio-runner.js +1760 -835
- package/dist/media-video-runner.js +1755 -830
- package/dist/memcode-runtime/CHANGELOG.md +54 -0
- package/dist/sdk.js +1257 -987
- package/dist/types.d.ts +6 -0
- package/dist/vector-backfill-runner.d.ts +1 -1
- package/dist/vector-backfill-runner.js +1751 -826
- package/docs/1.4.5-AGENT-AUTO-MEMORY-RESEARCH.md +95 -0
- package/docs/API_REFERENCE.md +23 -4
- package/docs/CONFIGURATION.md +17 -3
- package/docs/SETUP.md +5 -3
- package/package.json +1 -1
- package/plugins/antigravity/memorix/mcp_config.json +3 -1
- package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
- package/plugins/claude/memorix/.mcp.json +1 -1
- package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
- package/plugins/codebuddy/memorix/.mcp.json +1 -1
- package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/codex/memorix/.mcp.json +1 -1
- package/plugins/copilot/memorix/.mcp.json +1 -1
- package/plugins/copilot/memorix/plugin.json +1 -1
- package/plugins/gemini/memorix/gemini-extension.json +4 -2
- package/plugins/omp/memorix/package.json +1 -1
- package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/openclaw/memorix/.mcp.json +3 -1
- package/plugins/pi/memorix/package.json +1 -1
- package/server.json +2 -2
- package/src/cli/commands/dashboard.ts +14 -3
- package/src/cli/commands/ingest-commit.ts +47 -3
- package/src/cli/commands/memory.ts +17 -4
- package/src/cli/commands/purge.ts +120 -0
- package/src/cli/commands/serve-http.ts +39 -5
- package/src/cli/commands/setup.ts +3 -1
- package/src/cli/index.ts +3 -804
- package/src/codegraph/auto-context.ts +82 -1
- package/src/config/yaml-loader.ts +3 -8
- package/src/config.ts +0 -5
- package/src/evaluation/session-replay.ts +98 -0
- package/src/git/extractor.ts +5 -3
- package/src/hooks/installers/index.ts +15 -0
- package/src/knowledge/workset.ts +58 -0
- package/src/memory/long-term.ts +97 -2
- package/src/runtime/control-plane-maintenance.ts +1 -0
- package/src/runtime/isolated-maintenance.ts +1 -0
- package/src/runtime/lifecycle.ts +19 -0
- package/src/runtime/maintenance-jobs.ts +1 -0
- package/src/runtime/project-maintenance.ts +8 -0
- package/src/search/surfaced-registry.ts +62 -0
- package/src/server.ts +74 -14
- package/src/store/orama-store.ts +17 -1
- package/src/types.ts +6 -0
- package/src/cli/commands/audit-list.ts +0 -89
- package/src/cli/commands/purge-all-memory.ts +0 -85
- package/src/cli/commands/purge-project-memory.ts +0 -83
- package/src/cli/commands/uninstall-project-artifacts.ts +0 -86
- package/src/cli/tui/Sidebar.tsx +0 -179
- package/src/cli/tui/interactive-commands.ts +0 -261
- package/src/cli/tui/markdown-render.tsx +0 -371
- package/src/cli/tui/use-mouse.ts +0 -157
- package/src/cli/tui/useNavigation.ts +0 -56
- package/src/orchestrate/context-compact.ts +0 -188
- package/src/team/file-locks.ts +0 -173
- package/src/team/index.ts +0 -23
- package/src/team/messages.ts +0 -203
- package/src/team/persistence.ts +0 -96
- package/src/team/registry.ts +0 -158
- package/src/team/tasks.ts +0 -211
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Research: Claude Code Auto Memory and What Memorix Should Borrow
|
|
2
|
+
|
|
3
|
+
> Status: research note (2026-08-14, against the 1.4.5 line). This document
|
|
4
|
+
> records findings and direction; it is not a shipped feature and not a claim
|
|
5
|
+
> that any item below is implemented.
|
|
6
|
+
|
|
7
|
+
## Question
|
|
8
|
+
|
|
9
|
+
Claude Code's Auto Memory gives a single agent a file-based, recallable
|
|
10
|
+
workspace memory: `~/.claude/projects/<git-root>/memory/` with a `MEMORY.md`
|
|
11
|
+
entrypoint index and typed memory files. What is the design, how does it
|
|
12
|
+
compare with Memorix, and which parts are worth absorbing?
|
|
13
|
+
|
|
14
|
+
## Sourcing caveat
|
|
15
|
+
|
|
16
|
+
The mechanism-level details below come from community source-level analyses
|
|
17
|
+
and third-party write-ups, not from an official Anthropic specification:
|
|
18
|
+
|
|
19
|
+
- Source-level analysis of the memory system (`memdir`): project-memory.mdx
|
|
20
|
+
in a community Claude Code docs mirror
|
|
21
|
+
(https://github.com/claude-code-best/claude-code/blob/master/docs/context/project-memory.mdx)
|
|
22
|
+
- Independent critical analysis:
|
|
23
|
+
https://dev.to/chen_zhang_bac430bc7f6b95/claude-codes-memory-4-layers-of-complexity-still-just-grep-and-a-200-line-cap-2kn9
|
|
24
|
+
- Community adoption discussion:
|
|
25
|
+
https://developer.aliyun.com/article/1713756
|
|
26
|
+
|
|
27
|
+
Treat feature-flag names (`KAIROS`, `TEAMMEM`) and thresholds as observed
|
|
28
|
+
behavior of a moving target, not as stable contracts.
|
|
29
|
+
|
|
30
|
+
## The design, in brief
|
|
31
|
+
|
|
32
|
+
- Pure Markdown files, no database, no vector store. One directory per
|
|
33
|
+
sanitized Git root, shared across worktrees of that repository.
|
|
34
|
+
- `MEMORY.md` is the entrypoint index: at most 200 lines AND 25 KB, one
|
|
35
|
+
`- [Title](file.md) — one-line hook` pointer per line, loaded into every
|
|
36
|
+
session. The knowledge lives in topic files, loaded on demand.
|
|
37
|
+
- A closed four-type taxonomy (user / feedback / project / reference), each
|
|
38
|
+
type with explicit `<when_to_save>`, `<how_to_use>`, and `<body_structure>`
|
|
39
|
+
rules. Each memory file carries frontmatter whose `description` field is
|
|
40
|
+
written for the recall system, not for humans.
|
|
41
|
+
- The core saving rule: only store information that cannot be derived from
|
|
42
|
+
the current project state. Code, file paths, and Git history are
|
|
43
|
+
re-derivable and do not belong in memory.
|
|
44
|
+
- Recall is a lightweight model side-query that picks at most 5 relevant
|
|
45
|
+
memories from the manifest, with denoising for recently used tools and
|
|
46
|
+
dedup of already-surfaced files.
|
|
47
|
+
- Memory is injected as a user-context message (prompt-cache friendly).
|
|
48
|
+
- Drift defense is prompt-level: a memory that names a file, function, or
|
|
49
|
+
flag is a claim about the past — verify before recommending. An explicit
|
|
50
|
+
"ignore memory" instruction means proceed as if `MEMORY.md` were empty.
|
|
51
|
+
- Consolidation ("dream") is time/volume-triggered, merging contradictions
|
|
52
|
+
and pruning stale entries. Long-running sessions may switch to append-only
|
|
53
|
+
daily logs that a later pass distills.
|
|
54
|
+
- Security: the memory directory resolves only from user-level input
|
|
55
|
+
(environment override or a user setting). Project-level settings are
|
|
56
|
+
deliberately excluded from that resolution chain, so a hostile repository
|
|
57
|
+
cannot redirect memory storage to arbitrary paths.
|
|
58
|
+
|
|
59
|
+
## Comparison with Memorix
|
|
60
|
+
|
|
61
|
+
| Aspect | Claude Code Auto Memory | Memorix (1.4.5) |
|
|
62
|
+
| --- | --- | --- |
|
|
63
|
+
| Audience | One agent, one product | Any MCP/CLI/SDK/hooks agent, shared |
|
|
64
|
+
| Storage | Markdown files | SQLite canonical store + Orama search |
|
|
65
|
+
| Entry injection | Full index, fixed 200-line/25 KB cap | Task-lensed Workset, budget-governed |
|
|
66
|
+
| Taxonomy | Closed 4 types | 11 observation types + reasoning + Git + long-term |
|
|
67
|
+
| Recall | Paid model side-query (needs a key) | Local BM25 primary + bounded semantic fallback |
|
|
68
|
+
| Drift defense | Prompt-level verify-before-use | Freshness, attribution guard, evidence governor |
|
|
69
|
+
| Cleanup | Time/volume-triggered consolidation | Retention + dedup + consolidation pipeline |
|
|
70
|
+
| Path security | User-level-only resolution | User-level-only by construction (env var only) |
|
|
71
|
+
|
|
72
|
+
The critical review's complaint — memory trapped in one product's sandbox,
|
|
73
|
+
no cross-agent sharing — is exactly the space Memorix occupies.
|
|
74
|
+
|
|
75
|
+
## What Memorix should borrow (directions, not commitments)
|
|
76
|
+
|
|
77
|
+
Shipped (2026-08-14, continuing on the 1.4.x line):
|
|
78
|
+
1. **The "only store the underivable" rule, made explicit.** Now part of every
|
|
79
|
+
generated guidance file, with the dual-channel rule and the verify-before-use
|
|
80
|
+
/ ignore semantics.
|
|
81
|
+
2. **Dual-channel feedback.** Guidance now tells agents to record validated
|
|
82
|
+
approaches and explicit user confirmations, not only corrections.
|
|
83
|
+
3. **Session-level dedup of already-surfaced memory.** `memorix_search`
|
|
84
|
+
demotes rows already shown in the same session (never removes them) and
|
|
85
|
+
remembers the surfaced set per session; measured 25% → 19% duplicate rate
|
|
86
|
+
with hits and budget unchanged.
|
|
87
|
+
4. **Explicit ignore semantics.** Guidance now states the contract: an ignore
|
|
88
|
+
instruction means proceed as if memory were empty.
|
|
89
|
+
5. **Keep the security boundary tested.** Pinned by
|
|
90
|
+
`tests/config/data-dir-boundary.test.ts`.
|
|
91
|
+
|
|
92
|
+
Scoring lives in `tests/evaluation/session-replay-evaluation.test.ts`
|
|
93
|
+
(retrieval) and `tests/evaluation/guidance-content.test.ts` (guidance); both
|
|
94
|
+
are deterministic and offline. A future change to these behaviors must update
|
|
95
|
+
the pinned scores only with a measured improvement.
|
package/docs/API_REFERENCE.md
CHANGED
|
@@ -223,7 +223,7 @@ Important inputs:
|
|
|
223
223
|
- optional `relatedCommits`
|
|
224
224
|
- optional `relatedEntities`
|
|
225
225
|
- optional `visibility`: `project` (default), `personal`, or `team`
|
|
226
|
-
- optional `longTerm`: an explicit source-backed long-term
|
|
226
|
+
- optional `longTerm`: an explicit source-backed long-term record with `kind` (`episodic`, `semantic`, or `procedural`), an optional `scope` (`project`, `user`, or `team`), tags, and applicability
|
|
227
227
|
|
|
228
228
|
Visibility controls who can retrieve a record through agent-facing memory
|
|
229
229
|
tools. Normal memories default to `project`. `personal` and `team` writes
|
|
@@ -236,11 +236,15 @@ files, `memorix_store` returns without writing. Use `overrideReadOnly: true`
|
|
|
236
236
|
only when the user explicitly asks to preserve a record during that task.
|
|
237
237
|
|
|
238
238
|
`longTerm` is deliberately opt-in. It stores the normal Observation first, then
|
|
239
|
-
creates a separate
|
|
240
|
-
|
|
241
|
-
|
|
239
|
+
creates a separate record with an evidence reference. Because an explicit
|
|
240
|
+
request carries its own source evidence, the new record is auto-qualified on
|
|
241
|
+
the spot and enters Task Worksets as durable context; approval remains an
|
|
242
|
+
explicit operator review. Manage records with `memorix memory long-term
|
|
243
|
+
list|show|approve|archive|supersede`. Source-derived entries remain
|
|
242
244
|
project-bound; only a manual or user-confirmed `user + portable` record created
|
|
243
245
|
through the CLI may cross local projects for the same local installation.
|
|
246
|
+
Hook-captured and git-derived candidates stay in the candidate state until
|
|
247
|
+
qualified.
|
|
244
248
|
|
|
245
249
|
Example:
|
|
246
250
|
|
|
@@ -540,6 +544,21 @@ Use it to inspect:
|
|
|
540
544
|
- stage timing
|
|
541
545
|
- recent pipeline behavior
|
|
542
546
|
|
|
547
|
+
### `memorix_audit_project`
|
|
548
|
+
|
|
549
|
+
Scan the current project for observations that may have been written to the
|
|
550
|
+
wrong project bucket — entities that appear exclusively in a different
|
|
551
|
+
project. Read-only; nothing is changed.
|
|
552
|
+
|
|
553
|
+
Available in the `full` tool profile (`MEMORIX_MODE=full` or `--mode full`).
|
|
554
|
+
|
|
555
|
+
Important inputs:
|
|
556
|
+
|
|
557
|
+
- optional `threshold` — minimum occurrences in another project to flag
|
|
558
|
+
(default: 2)
|
|
559
|
+
|
|
560
|
+
Archive confirmed mis-attributed observations with `memorix_resolve`.
|
|
561
|
+
|
|
543
562
|
---
|
|
544
563
|
|
|
545
564
|
## 7. Skills and Promotion Tools
|
package/docs/CONFIGURATION.md
CHANGED
|
@@ -294,10 +294,24 @@ Server and dashboard behavior.
|
|
|
294
294
|
|
|
295
295
|
Common keys:
|
|
296
296
|
|
|
297
|
-
- `transport = "stdio"`
|
|
298
|
-
|
|
297
|
+
- `transport = "stdio"` — advisory. An explicit `memorix serve-http` still
|
|
298
|
+
runs on HTTP and prints a notice; this key documents the intended mode.
|
|
299
|
+
- `port = 3211` — default listener port for `memorix serve-http` when
|
|
300
|
+
`--port` is not given.
|
|
299
301
|
- `dashboard = true`
|
|
300
|
-
- `dashboard_port = 3210`
|
|
302
|
+
- `dashboard_port = 3210` — default port for `memorix dashboard` when
|
|
303
|
+
`--port` is not given.
|
|
304
|
+
|
|
305
|
+
### Reserved sections (parsed, not yet enforced)
|
|
306
|
+
|
|
307
|
+
These sections exist so future releases do not need a config format change.
|
|
308
|
+
Today no runtime behavior reads them; set them only as forward-looking notes.
|
|
309
|
+
|
|
310
|
+
- `[team]` (`enabled`, `workspace_collection`) — coordination defaults.
|
|
311
|
+
- `[hooks]` (`native_memcode`, `external_agents`) — hook bootstrap policy.
|
|
312
|
+
- The former `mcpServers` key in `memorix.yml` was removed: no code ever
|
|
313
|
+
consumed it, and Memorix is not an MCP aggregator. Unknown keys are
|
|
314
|
+
ignored, so old files keep loading.
|
|
301
315
|
|
|
302
316
|
---
|
|
303
317
|
|
package/docs/SETUP.md
CHANGED
|
@@ -17,9 +17,9 @@ Common runtime entry points:
|
|
|
17
17
|
|
|
18
18
|
| Entry | Use it for |
|
|
19
19
|
| --- | --- |
|
|
20
|
-
| `memorix setup --agent <agent> --global` | one-command user-level agent integration |
|
|
20
|
+
| `memorix setup --agent <agent> --global` | one-command user-level agent integration; installs the stdio server with `--mode lite` so every tool taught in the generated guidance is actually reachable |
|
|
21
21
|
| `memorix` CLI commands | direct workflows: setup, search/store, Git Memory, import/export, dashboard, diagnostics, orchestration, and automation |
|
|
22
|
-
| `memorix serve` | stdio MCP server for IDEs and coding agents; defaults to a compact `micro` tool profile |
|
|
22
|
+
| `memorix serve` | stdio MCP server for IDEs and coding agents; defaults to a compact `micro` tool profile when launched manually |
|
|
23
23
|
| `memorix background start` | long-lived HTTP MCP service plus dashboard |
|
|
24
24
|
| `memorix serve-http --port 3211` | foreground HTTP MCP for debugging or supervised launches |
|
|
25
25
|
| `memorix` / `memcode` | bundled terminal agent that uses the same Memorix memory pool |
|
|
@@ -151,7 +151,9 @@ memorix serve
|
|
|
151
151
|
|
|
152
152
|
Use this when your agent only needs a raw local stdio MCP process or you are debugging a manual config. The agent starts `memorix serve` and communicates with it over stdio.
|
|
153
153
|
|
|
154
|
-
|
|
154
|
+
A bare `memorix serve` defaults to `--mode micro`, which exposes 9 high-signal tools so agent context is not crowded by rarely used schemas. Use `memorix serve --mode lite` for the extended solo memory surface, `--mode team` for coordination tools, or `--mode full` for advanced and MCP Memory compatibility tools.
|
|
155
|
+
|
|
156
|
+
`memorix setup` writes `--mode lite` into the installed MCP config: the generated guidance teaches the extended surface (sessions, timeline, reasoning, retention), so the installed server must actually expose it. Override with `MEMORIX_MODE` if you prefer a different profile.
|
|
155
157
|
|
|
156
158
|
Generic stdio MCP config:
|
|
157
159
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "memorix",
|
|
3
3
|
"mcpName": "io.github.AVIDS2/memorix",
|
|
4
|
-
"version": "1.4.
|
|
4
|
+
"version": "1.4.6",
|
|
5
5
|
"description": "Local-first shared memory layer for AI coding agents across MCP clients, Git history, reasoning context, and project sessions.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"sideEffects": false,
|
package/server.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"url": "https://github.com/AVIDS2/memorix",
|
|
8
8
|
"source": "github"
|
|
9
9
|
},
|
|
10
|
-
"version": "1.4.
|
|
10
|
+
"version": "1.4.6",
|
|
11
11
|
"websiteUrl": "https://github.com/AVIDS2/memorix#readme",
|
|
12
12
|
"icons": [
|
|
13
13
|
{
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
{
|
|
23
23
|
"registryType": "npm",
|
|
24
24
|
"identifier": "memorix",
|
|
25
|
-
"version": "1.4.
|
|
25
|
+
"version": "1.4.6",
|
|
26
26
|
"runtimeHint": "npx",
|
|
27
27
|
"packageArguments": [
|
|
28
28
|
{
|
|
@@ -22,8 +22,8 @@ export default defineCommand({
|
|
|
22
22
|
args: {
|
|
23
23
|
port: {
|
|
24
24
|
type: 'string',
|
|
25
|
-
description: 'Port to run the dashboard on (default: 3210)',
|
|
26
|
-
|
|
25
|
+
description: 'Port to run the dashboard on (default: [server].dashboardPort or 3210)',
|
|
26
|
+
required: false,
|
|
27
27
|
},
|
|
28
28
|
},
|
|
29
29
|
run: async ({ args }) => {
|
|
@@ -31,13 +31,24 @@ export default defineCommand({
|
|
|
31
31
|
const { getProjectDataDir } = await import('../../store/persistence.js');
|
|
32
32
|
const { startDashboard } = await import('../../dashboard/server.js');
|
|
33
33
|
|
|
34
|
+
// The [server].dashboardPort config is a real default source; the
|
|
35
|
+
// explicit --port flag always wins.
|
|
36
|
+
let configuredDashboardPort: number | undefined;
|
|
37
|
+
try {
|
|
38
|
+
const { getServerConfig } = await import('../../config.js');
|
|
39
|
+
const serverConfig = getServerConfig();
|
|
40
|
+
configuredDashboardPort = typeof serverConfig.dashboardPort === 'number'
|
|
41
|
+
? serverConfig.dashboardPort
|
|
42
|
+
: undefined;
|
|
43
|
+
} catch { /* config files are optional */ }
|
|
44
|
+
|
|
34
45
|
const project = detectProject();
|
|
35
46
|
if (!project) {
|
|
36
47
|
console.error('Memorix requires a git repo to establish project identity. Run `git init` in this workspace first.');
|
|
37
48
|
process.exit(1);
|
|
38
49
|
}
|
|
39
50
|
const dataDir = await getProjectDataDir(project.id);
|
|
40
|
-
const port = parseTcpPortOrReport(args.port as string | undefined, 3210);
|
|
51
|
+
const port = parseTcpPortOrReport(args.port as string | undefined, configuredDashboardPort ?? 3210);
|
|
41
52
|
if (port === undefined) {
|
|
42
53
|
process.exitCode = 1;
|
|
43
54
|
return;
|
|
@@ -7,6 +7,37 @@
|
|
|
7
7
|
|
|
8
8
|
import { defineCommand } from 'citty';
|
|
9
9
|
import * as p from '@clack/prompts';
|
|
10
|
+
import type { CommitInfo } from '../../git/extractor.js';
|
|
11
|
+
|
|
12
|
+
export interface GitIngestPolicyInput {
|
|
13
|
+
ingestOnCommit?: boolean;
|
|
14
|
+
maxDiffSize?: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The documented git policy settings are real gates, not display-only facts:
|
|
19
|
+
* `ingest_on_commit = false` disables post-commit ingestion, and
|
|
20
|
+
* `max_diff_size` caps how much diff content is captured into the narrative.
|
|
21
|
+
*/
|
|
22
|
+
export function applyGitIngestPolicy(
|
|
23
|
+
commit: CommitInfo,
|
|
24
|
+
policy: GitIngestPolicyInput,
|
|
25
|
+
): { skip: boolean; reason?: string; diffSummary: string } {
|
|
26
|
+
if (policy.ingestOnCommit === false) {
|
|
27
|
+
return {
|
|
28
|
+
skip: true,
|
|
29
|
+
reason: 'ingest_on_commit is disabled',
|
|
30
|
+
diffSummary: commit.diffSummary,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
const cap = policy.maxDiffSize;
|
|
34
|
+
return {
|
|
35
|
+
skip: false,
|
|
36
|
+
diffSummary: typeof cap === 'number' && Number.isFinite(cap)
|
|
37
|
+
? commit.diffSummary.slice(0, Math.max(0, Math.floor(cap)))
|
|
38
|
+
: commit.diffSummary,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
10
41
|
|
|
11
42
|
export default defineCommand({
|
|
12
43
|
meta: {
|
|
@@ -42,13 +73,26 @@ export default defineCommand({
|
|
|
42
73
|
if (!auto) p.intro(`Ingest commit: ${ref}`);
|
|
43
74
|
|
|
44
75
|
try {
|
|
76
|
+
const { getGitConfig } = await import('../../config.js');
|
|
77
|
+
const gitCfg = getGitConfig();
|
|
45
78
|
const { getCommitInfo, ingestCommit } = await import('../../git/extractor.js');
|
|
46
|
-
const commit = getCommitInfo(cwd, ref);
|
|
79
|
+
const commit = getCommitInfo(cwd, ref, gitCfg.maxDiffSize ?? 500);
|
|
80
|
+
|
|
81
|
+
// The documented ingest_on_commit gate: post-commit execution stops
|
|
82
|
+
// here when disabled. An explicit manual run always proceeds.
|
|
83
|
+
const policy = applyGitIngestPolicy(commit, {
|
|
84
|
+
ingestOnCommit: gitCfg.ingestOnCommit,
|
|
85
|
+
maxDiffSize: gitCfg.maxDiffSize,
|
|
86
|
+
});
|
|
87
|
+
if (policy.skip && auto) {
|
|
88
|
+
console.error(`[memorix] Skipped ${commit.shortHash}: ${policy.reason}`);
|
|
89
|
+
process.exit(0);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
commit.diffSummary = policy.diffSummary;
|
|
47
93
|
|
|
48
94
|
// Noise filter: skip low-value commits (typo, format, lockfile, merge, etc.)
|
|
49
95
|
const { shouldFilterCommit } = await import('../../git/noise-filter.js');
|
|
50
|
-
const { getGitConfig } = await import('../../config.js');
|
|
51
|
-
const gitCfg = getGitConfig();
|
|
52
96
|
const filterResult = shouldFilterCommit(commit, {
|
|
53
97
|
skipMergeCommits: gitCfg.skipMergeCommits,
|
|
54
98
|
excludePatterns: gitCfg.excludePatterns,
|
|
@@ -467,6 +467,7 @@ export default defineCommand({
|
|
|
467
467
|
createManualLongTermMemory,
|
|
468
468
|
getLongTermMemoryDetail,
|
|
469
469
|
listLongTermMemories,
|
|
470
|
+
maybeAutoQualifyLongTermMemory,
|
|
470
471
|
promoteObservationToLongTermMemory,
|
|
471
472
|
qualifyLongTermMemory,
|
|
472
473
|
supersedeLongTermMemory,
|
|
@@ -545,9 +546,15 @@ export default defineCommand({
|
|
|
545
546
|
portability: portability(),
|
|
546
547
|
reader: longTermReader,
|
|
547
548
|
});
|
|
549
|
+
// Operator-written records carry their own source evidence.
|
|
550
|
+
const qualified = await maybeAutoQualifyLongTermMemory({
|
|
551
|
+
dataDir,
|
|
552
|
+
id: result.memory.id,
|
|
553
|
+
source: 'manual',
|
|
554
|
+
});
|
|
548
555
|
emitResult(
|
|
549
|
-
{ project, ...result },
|
|
550
|
-
`Created long-term memory
|
|
556
|
+
{ project, ...result, memory: qualified ?? result.memory },
|
|
557
|
+
`Created long-term memory ${result.memory.id}.`,
|
|
551
558
|
asJson,
|
|
552
559
|
);
|
|
553
560
|
return;
|
|
@@ -576,9 +583,15 @@ export default defineCommand({
|
|
|
576
583
|
applicability: asStringArg(args.applicability),
|
|
577
584
|
reader: longTermReader,
|
|
578
585
|
});
|
|
586
|
+
// An explicit promote request carries its own source evidence.
|
|
587
|
+
const qualified = await maybeAutoQualifyLongTermMemory({
|
|
588
|
+
dataDir,
|
|
589
|
+
id: result.memory.id,
|
|
590
|
+
sourceDetail: 'explicit',
|
|
591
|
+
});
|
|
579
592
|
emitResult(
|
|
580
|
-
{ project, sourceObservationId: observation.id, ...result },
|
|
581
|
-
`Promoted observation #${observation.id} to long-term memory
|
|
593
|
+
{ project, sourceObservationId: observation.id, ...result, memory: qualified ?? result.memory },
|
|
594
|
+
`Promoted observation #${observation.id} to long-term memory ${result.memory.id}.`,
|
|
582
595
|
asJson,
|
|
583
596
|
);
|
|
584
597
|
return;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Command: memorix purge
|
|
3
|
+
*
|
|
4
|
+
* Retire memories from retrieval. Project-scoped by default; --all covers
|
|
5
|
+
* every project. Observations and long-term records are archived (never
|
|
6
|
+
* hard-deleted) so the audit trail survives. Refuses to run outside a
|
|
7
|
+
* terminal without --yes.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { defineCommand } from 'citty';
|
|
11
|
+
import { detectProject } from '../../project/detector.js';
|
|
12
|
+
import { getProjectDataDir } from '../../store/persistence.js';
|
|
13
|
+
|
|
14
|
+
export default defineCommand({
|
|
15
|
+
meta: {
|
|
16
|
+
name: 'purge',
|
|
17
|
+
description: 'Retire memories from retrieval (current project by default, --all for every project)',
|
|
18
|
+
},
|
|
19
|
+
args: {
|
|
20
|
+
all: {
|
|
21
|
+
type: 'boolean',
|
|
22
|
+
description: 'Purge every project instead of the current one',
|
|
23
|
+
default: false,
|
|
24
|
+
},
|
|
25
|
+
yes: {
|
|
26
|
+
type: 'boolean',
|
|
27
|
+
description: 'Skip the interactive confirmation',
|
|
28
|
+
default: false,
|
|
29
|
+
},
|
|
30
|
+
json: {
|
|
31
|
+
type: 'boolean',
|
|
32
|
+
description: 'Emit a machine-readable result',
|
|
33
|
+
default: false,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
async run({ args }) {
|
|
37
|
+
const purgeAll = Boolean(args.all);
|
|
38
|
+
const yes = Boolean(args.yes);
|
|
39
|
+
const asJson = Boolean(args.json);
|
|
40
|
+
|
|
41
|
+
// Confirmation gate first: a destructive wipe never proceeds in a
|
|
42
|
+
// non-interactive shell without an explicit --yes.
|
|
43
|
+
if (!yes && !process.stdin.isTTY) {
|
|
44
|
+
console.error('[ERROR] Non-interactive environment. Add --yes to confirm.');
|
|
45
|
+
console.error('[ERROR] Refusing to purge memories without confirmation.');
|
|
46
|
+
process.exitCode = 1;
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (!yes) {
|
|
50
|
+
const readline = await import('node:readline');
|
|
51
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
52
|
+
const question = purgeAll
|
|
53
|
+
? 'Retire ALL memories from every project? (y/N) '
|
|
54
|
+
: 'Retire all memories for this project? (y/N) ';
|
|
55
|
+
const answer = await new Promise<string>((resolve) => rl.question(question, resolve));
|
|
56
|
+
rl.close();
|
|
57
|
+
if (answer.trim().toLowerCase() !== 'y') {
|
|
58
|
+
console.log('Cancelled.');
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
let projectId: string | null = null;
|
|
64
|
+
if (!purgeAll) {
|
|
65
|
+
const project = detectProject();
|
|
66
|
+
if (!project) {
|
|
67
|
+
console.error('[ERROR] No .git found — run from a project directory or pass --all.');
|
|
68
|
+
process.exitCode = 1;
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
projectId = project.id;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const dataDir = await getProjectDataDir(projectId ?? '');
|
|
75
|
+
const { initObservationStore } = await import('../../store/obs-store.js');
|
|
76
|
+
const { getAllObservations, resolveObservations } = await import('../../memory/observations.js');
|
|
77
|
+
const { withFreshIndex } = await import('../../memory/freshness.js');
|
|
78
|
+
await initObservationStore(dataDir);
|
|
79
|
+
|
|
80
|
+
const observations = await withFreshIndex(() => getAllObservations());
|
|
81
|
+
const target = purgeAll ? observations : observations.filter((o) => o.projectId === projectId);
|
|
82
|
+
const ids = target
|
|
83
|
+
.map((o) => o.id)
|
|
84
|
+
.filter((id): id is number => typeof id === 'number');
|
|
85
|
+
const archived = ids.length > 0
|
|
86
|
+
? (await resolveObservations(ids, 'archived')).resolved.length
|
|
87
|
+
: 0;
|
|
88
|
+
|
|
89
|
+
const { LongTermMemoryStore } = await import('../../memory/long-term-store.js');
|
|
90
|
+
const { archiveLongTermMemory } = await import('../../memory/long-term.js');
|
|
91
|
+
const longTermStore = new LongTermMemoryStore();
|
|
92
|
+
await longTermStore.init(dataDir);
|
|
93
|
+
let longTermArchived = 0;
|
|
94
|
+
for (const memory of longTermStore.list()) {
|
|
95
|
+
if (memory.state === 'archived' || memory.state === 'superseded') continue;
|
|
96
|
+
if (!purgeAll && memory.originProjectId !== projectId) continue;
|
|
97
|
+
try {
|
|
98
|
+
await archiveLongTermMemory({
|
|
99
|
+
dataDir,
|
|
100
|
+
id: memory.id,
|
|
101
|
+
reason: purgeAll ? 'purge --all' : 'purge (project) ' + (projectId ?? ''),
|
|
102
|
+
});
|
|
103
|
+
longTermArchived += 1;
|
|
104
|
+
} catch { /* already inactive */ }
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const result = {
|
|
108
|
+
scope: purgeAll ? 'all' : projectId,
|
|
109
|
+
observationsArchived: archived,
|
|
110
|
+
longTermArchived,
|
|
111
|
+
};
|
|
112
|
+
if (asJson) {
|
|
113
|
+
console.log(JSON.stringify(result));
|
|
114
|
+
} else {
|
|
115
|
+
console.log(
|
|
116
|
+
`[OK] Purged ${purgeAll ? 'all projects' : result.scope}: archived ${archived} observations and ${longTermArchived} long-term memories.`,
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
});
|