memorix 1.6.1 → 1.6.3
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 +35 -0
- package/TEAM.md +86 -86
- package/dist/cli/index.js +2582 -1845
- package/dist/index.js +1355 -683
- package/dist/maintenance-runner.js +1621 -1096
- package/dist/media-audio-runner.js +1612 -1088
- package/dist/media-video-runner.js +1612 -1088
- package/dist/memcode-runtime/CHANGELOG.md +35 -0
- package/dist/sdk.js +2007 -1396
- package/dist/vector-backfill-runner.js +1610 -1086
- package/docs/DESIGN_DECISIONS.md +357 -357
- package/docs/PERFORMANCE.md +15 -0
- package/package.json +2 -1
- package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
- package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
- package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/copilot/memorix/plugin.json +1 -1
- package/plugins/gemini/memorix/gemini-extension.json +1 -1
- package/plugins/omp/memorix/package.json +1 -1
- package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/pi/memorix/package.json +1 -1
- package/server.json +2 -2
- package/src/audit/index.ts +156 -156
- package/src/cli/commands/background.ts +51 -5
- package/src/cli/commands/formation.ts +48 -48
- package/src/cli/commands/git-hook-install.ts +111 -111
- package/src/cli/commands/handoff.ts +54 -54
- package/src/cli/commands/hooks-status.ts +63 -63
- package/src/cli/commands/ingest.ts +44 -44
- package/src/cli/commands/integrate-shared.ts +15 -15
- package/src/cli/commands/lock.ts +82 -82
- package/src/cli/commands/message.ts +104 -104
- package/src/cli/commands/operator-shared.ts +18 -11
- package/src/cli/commands/poll.ts +58 -58
- package/src/cli/commands/reasoning.ts +118 -118
- package/src/cli/commands/serve-http.ts +29 -7
- package/src/cli/commands/serve-shared.ts +49 -6
- package/src/cli/commands/serve.ts +4 -0
- package/src/cli/commands/skills.ts +114 -114
- package/src/cli/commands/task.ts +167 -167
- package/src/cli/commands/transfer.ts +47 -47
- package/src/cli/index.ts +406 -406
- package/src/cli/tui/ChatView.tsx +234 -234
- package/src/cli/tui/CommandBar.tsx +312 -312
- package/src/cli/tui/ContextRail.tsx +118 -118
- package/src/cli/tui/HeaderBar.tsx +72 -72
- package/src/cli/tui/LogoBanner.tsx +51 -51
- package/src/cli/tui/index.ts +41 -41
- package/src/cli/version.ts +7 -7
- package/src/cli/workbench.ts +1 -1
- package/src/dashboard/project-classification.ts +64 -64
- package/src/embedding/api-provider.ts +503 -27
- package/src/embedding/provider.ts +20 -3
- package/src/git/hooks-path.ts +85 -85
- package/src/hooks/lifecycle.ts +1 -0
- package/src/hooks/pattern-detector.ts +173 -173
- package/src/hooks/significance-filter.ts +250 -250
- package/src/llm/memory-manager.ts +328 -328
- package/src/llm/quality.ts +248 -248
- package/src/memory/attribution-guard.ts +249 -249
- package/src/memory/disclosure-policy.ts +135 -135
- package/src/memory/entity-extractor.ts +197 -197
- package/src/memory/formation/evaluate.ts +217 -217
- package/src/memory/freshness.ts +122 -122
- package/src/memory/graph.ts +197 -197
- package/src/memory/observations.ts +68 -18
- package/src/memory/refs.ts +94 -94
- package/src/memory/secret-filter.ts +79 -79
- package/src/memory/session.ts +13 -3
- package/src/orchestrate/adapters/claude-stream.ts +192 -192
- package/src/orchestrate/adapters/claude.ts +111 -111
- package/src/orchestrate/adapters/codex-stream.ts +134 -134
- package/src/orchestrate/adapters/codex.ts +41 -41
- package/src/orchestrate/adapters/gemini-stream.ts +166 -166
- package/src/orchestrate/adapters/gemini.ts +42 -42
- package/src/orchestrate/adapters/index.ts +73 -73
- package/src/orchestrate/adapters/opencode-stream.ts +143 -143
- package/src/orchestrate/adapters/opencode.ts +47 -47
- package/src/orchestrate/adapters/types.ts +77 -77
- package/src/orchestrate/capability-router.ts +284 -284
- package/src/orchestrate/cost-tracker.ts +219 -219
- package/src/orchestrate/error-recovery.ts +191 -191
- package/src/orchestrate/evidence.ts +140 -140
- package/src/orchestrate/ledger.ts +110 -110
- package/src/orchestrate/memorix-bridge.ts +343 -343
- package/src/orchestrate/output-budget.ts +80 -80
- package/src/orchestrate/permission.ts +152 -152
- package/src/orchestrate/pipeline-trace.ts +131 -131
- package/src/orchestrate/prompt-builder.ts +155 -155
- package/src/orchestrate/ring-buffer.ts +37 -37
- package/src/orchestrate/task-graph.ts +389 -389
- package/src/project/aliases.ts +374 -374
- package/src/project/launch-root.ts +115 -0
- package/src/rules/adapters/trae.ts +56 -56
- package/src/rules/syncer.ts +235 -235
- package/src/runtime/isolated-maintenance.ts +12 -1
- package/src/sdk.ts +12 -8
- package/src/search/query-expansion.ts +52 -52
- package/src/server/formation-timeout.ts +27 -27
- package/src/server.ts +38 -5
- package/src/skills/mini-skills.ts +386 -386
- package/src/store/chat-store.ts +119 -119
- package/src/store/graph-store.ts +249 -249
- package/src/store/mini-skill-store.ts +349 -349
- package/src/store/obs-store.ts +2 -2
- package/src/store/orama-store.ts +21 -3
- package/src/store/persistence-json.ts +212 -212
- package/src/store/persistence.ts +291 -291
- package/src/store/project-affinity.ts +195 -195
- package/src/store/sqlite-db.ts +2 -0
- package/src/store/sqlite-store.ts +19 -4
- package/src/team/event-bus.ts +76 -76
- package/src/team/handoff.ts +161 -161
- package/src/team/poll.ts +132 -132
- package/src/workspace/mcp-adapters/codex.ts +191 -191
- package/src/workspace/mcp-adapters/copilot.ts +105 -105
- package/src/workspace/mcp-adapters/cursor.ts +53 -53
- package/src/workspace/mcp-adapters/kiro.ts +64 -64
- package/src/workspace/mcp-adapters/opencode.ts +123 -123
- package/src/workspace/mcp-adapters/trae.ts +134 -134
- package/src/workspace/mcp-adapters/windsurf.ts +91 -91
- package/src/workspace/sanitizer.ts +60 -60
package/docs/PERFORMANCE.md
CHANGED
|
@@ -78,6 +78,7 @@ On the release development machine used for this check, the healthy HTTP service
|
|
|
78
78
|
| `MEMORIX_RERANK_TIMEOUT_MS` | provider default | Bound slow LLM rerank calls |
|
|
79
79
|
| `memorix memory search --quality fast` | n/a | Force a fully local retrieval path for a latency-sensitive call |
|
|
80
80
|
| `npm run benchmark:retrieval -- --records 1000 --runs 100` | n/a | Reproduce hot in-process lexical retrieval latency; not an end-to-end claim |
|
|
81
|
+
| `npm run gate:large-store -- --records 40000` | n/a | Exercise SDK, HTTP MCP, hook persistence, cache integrity, and reopen behavior against a large isolated store |
|
|
81
82
|
| `[codegraph].max_file_bytes` | `2097152` | Raise only when a large file is intentional source that should enter Code Memory |
|
|
82
83
|
| `memorix retention status` | report only | Inspect whether memory growth needs cleanup |
|
|
83
84
|
| `memorix retention archive` | explicit | Archive expired memories when the project gets noisy |
|
|
@@ -96,6 +97,20 @@ On the release development machine used for this check, the healthy HTTP service
|
|
|
96
97
|
`/api/maintenance` on that local dashboard before assuming a Code Memory scan
|
|
97
98
|
or lifecycle task completed.
|
|
98
99
|
|
|
100
|
+
## Large-Store Release Gate
|
|
101
|
+
|
|
102
|
+
`npm run gate:large-store -- --records 40000` builds Memorix and creates a
|
|
103
|
+
temporary Git project with an isolated data directory. It runs with embeddings
|
|
104
|
+
disabled, so it does not use provider credentials or make paid network calls.
|
|
105
|
+
The gate measures steady-state writes, first lexical search, process memory,
|
|
106
|
+
HTTP MCP and hook persistence, and an SDK close/reopen cycle. It also verifies
|
|
107
|
+
that the personal hook candidate remains durable on disk without becoming
|
|
108
|
+
visible to an unbound SDK reader.
|
|
109
|
+
|
|
110
|
+
Results are machine-specific and should be compared on the same OS and Node
|
|
111
|
+
version. The release gate is an integrity and regression check, not a public
|
|
112
|
+
latency guarantee.
|
|
113
|
+
|
|
99
114
|
## Current Optimization Opportunities
|
|
100
115
|
|
|
101
116
|
These are not release blockers, but they are reasonable future improvements:
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "memorix",
|
|
3
3
|
"mcpName": "io.github.AVIDS2/memorix",
|
|
4
|
-
"version": "1.6.
|
|
4
|
+
"version": "1.6.3",
|
|
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,
|
|
@@ -67,6 +67,7 @@
|
|
|
67
67
|
"test:e2e": "vitest run tests/e2e/",
|
|
68
68
|
"benchmark:codegraph": "node scripts/benchmark-codegraph-provider.cjs",
|
|
69
69
|
"benchmark:retrieval": "npm run build && node scripts/benchmark-retrieval.mjs",
|
|
70
|
+
"gate:large-store": "npm run build && node scripts/large-store-gate.mjs",
|
|
70
71
|
"generate:star-history": "node scripts/generate-star-history.mjs",
|
|
71
72
|
"sync:mcp-registry": "node scripts/sync-mcp-registry-manifest.mjs",
|
|
72
73
|
"check:mcp-registry": "node scripts/check-mcp-registry-manifest.mjs",
|
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.6.
|
|
10
|
+
"version": "1.6.3",
|
|
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.6.
|
|
25
|
+
"version": "1.6.3",
|
|
26
26
|
"runtimeHint": "npx",
|
|
27
27
|
"packageArguments": [
|
|
28
28
|
{
|
package/src/audit/index.ts
CHANGED
|
@@ -1,156 +1,156 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Audit Module - Track Memorix-written files
|
|
3
|
-
*
|
|
4
|
-
* Records all files written by Memorix to distinguish them from
|
|
5
|
-
* project-native files. Provides audit trail for cleanup and rollback.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import * as fs from 'node:fs/promises';
|
|
9
|
-
import * as path from 'node:path';
|
|
10
|
-
import { homedir } from 'node:os';
|
|
11
|
-
|
|
12
|
-
function getAuditFilePath(): string {
|
|
13
|
-
return process.env.MEMORIX_AUDIT_FILE || path.join(homedir(), '.memorix', 'audit.json');
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface AuditEntry {
|
|
17
|
-
type: 'hook' | 'rule' | 'other';
|
|
18
|
-
agent?: string;
|
|
19
|
-
path: string;
|
|
20
|
-
createdAt: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface ProjectAudit {
|
|
24
|
-
projectRoot: string;
|
|
25
|
-
installedAt: string;
|
|
26
|
-
entries: AuditEntry[];
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface AuditData {
|
|
30
|
-
version: string;
|
|
31
|
-
projects: Record<string, ProjectAudit>;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Load audit data from disk.
|
|
36
|
-
*/
|
|
37
|
-
export async function loadAudit(): Promise<AuditData> {
|
|
38
|
-
try {
|
|
39
|
-
const content = await fs.readFile(getAuditFilePath(), 'utf-8');
|
|
40
|
-
return JSON.parse(content) as AuditData;
|
|
41
|
-
} catch {
|
|
42
|
-
// No audit file yet
|
|
43
|
-
return {
|
|
44
|
-
version: '1.0.0',
|
|
45
|
-
projects: {},
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Save audit data to disk.
|
|
52
|
-
*/
|
|
53
|
-
export async function saveAudit(data: AuditData): Promise<void> {
|
|
54
|
-
const auditFile = getAuditFilePath();
|
|
55
|
-
const dir = path.dirname(auditFile);
|
|
56
|
-
await fs.mkdir(dir, { recursive: true });
|
|
57
|
-
await fs.writeFile(auditFile, JSON.stringify(data, null, 2), 'utf-8');
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Get project ID from project root via .git detection.
|
|
62
|
-
*/
|
|
63
|
-
export function getProjectId(projectRoot: string): string {
|
|
64
|
-
try {
|
|
65
|
-
const { detectProject } = require('../project/detector.js');
|
|
66
|
-
const project = detectProject(projectRoot);
|
|
67
|
-
if (project) return project.id;
|
|
68
|
-
} catch { /* fallback below */ }
|
|
69
|
-
const normalized = projectRoot.replace(/\\/g, '/');
|
|
70
|
-
return `untracked/${normalized}`;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Record a file written by Memorix.
|
|
75
|
-
*/
|
|
76
|
-
export async function recordFile(
|
|
77
|
-
projectRoot: string,
|
|
78
|
-
type: AuditEntry['type'],
|
|
79
|
-
filePath: string,
|
|
80
|
-
agent?: string,
|
|
81
|
-
): Promise<void> {
|
|
82
|
-
const data = await loadAudit();
|
|
83
|
-
const projectId = getProjectId(projectRoot);
|
|
84
|
-
|
|
85
|
-
if (!data.projects[projectId]) {
|
|
86
|
-
data.projects[projectId] = {
|
|
87
|
-
projectRoot,
|
|
88
|
-
installedAt: new Date().toISOString(),
|
|
89
|
-
entries: [],
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Check if entry already exists
|
|
94
|
-
const existingIndex = data.projects[projectId].entries.findIndex(
|
|
95
|
-
(e) => e.path === filePath
|
|
96
|
-
);
|
|
97
|
-
|
|
98
|
-
if (existingIndex === -1) {
|
|
99
|
-
data.projects[projectId].entries.push({
|
|
100
|
-
type,
|
|
101
|
-
agent,
|
|
102
|
-
path: filePath,
|
|
103
|
-
createdAt: new Date().toISOString(),
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
await saveAudit(data);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Get all files written by Memorix for a project.
|
|
112
|
-
*/
|
|
113
|
-
export async function getProjectFiles(projectRoot: string): Promise<AuditEntry[]> {
|
|
114
|
-
const data = await loadAudit();
|
|
115
|
-
const projectId = getProjectId(projectRoot);
|
|
116
|
-
return data.projects[projectId]?.entries || [];
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Remove a file from audit (when uninstalled).
|
|
121
|
-
*/
|
|
122
|
-
export async function removeFile(projectRoot: string, filePath: string): Promise<void> {
|
|
123
|
-
const data = await loadAudit();
|
|
124
|
-
const projectId = getProjectId(projectRoot);
|
|
125
|
-
|
|
126
|
-
if (!data.projects[projectId]) return;
|
|
127
|
-
|
|
128
|
-
data.projects[projectId].entries = data.projects[projectId].entries.filter(
|
|
129
|
-
(e) => e.path !== filePath
|
|
130
|
-
);
|
|
131
|
-
|
|
132
|
-
// If no entries left, remove the project
|
|
133
|
-
if (data.projects[projectId].entries.length === 0) {
|
|
134
|
-
delete data.projects[projectId];
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
await saveAudit(data);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Get all audit entries across all projects.
|
|
142
|
-
*/
|
|
143
|
-
export async function getAllAuditEntries(): Promise<
|
|
144
|
-
Array<{ projectId: string; entry: AuditEntry }>
|
|
145
|
-
> {
|
|
146
|
-
const data = await loadAudit();
|
|
147
|
-
const entries: Array<{ projectId: string; entry: AuditEntry }> = [];
|
|
148
|
-
|
|
149
|
-
for (const [projectId, project] of Object.entries(data.projects)) {
|
|
150
|
-
for (const entry of project.entries) {
|
|
151
|
-
entries.push({ projectId, entry });
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
return entries;
|
|
156
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Audit Module - Track Memorix-written files
|
|
3
|
+
*
|
|
4
|
+
* Records all files written by Memorix to distinguish them from
|
|
5
|
+
* project-native files. Provides audit trail for cleanup and rollback.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as fs from 'node:fs/promises';
|
|
9
|
+
import * as path from 'node:path';
|
|
10
|
+
import { homedir } from 'node:os';
|
|
11
|
+
|
|
12
|
+
function getAuditFilePath(): string {
|
|
13
|
+
return process.env.MEMORIX_AUDIT_FILE || path.join(homedir(), '.memorix', 'audit.json');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface AuditEntry {
|
|
17
|
+
type: 'hook' | 'rule' | 'other';
|
|
18
|
+
agent?: string;
|
|
19
|
+
path: string;
|
|
20
|
+
createdAt: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ProjectAudit {
|
|
24
|
+
projectRoot: string;
|
|
25
|
+
installedAt: string;
|
|
26
|
+
entries: AuditEntry[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface AuditData {
|
|
30
|
+
version: string;
|
|
31
|
+
projects: Record<string, ProjectAudit>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Load audit data from disk.
|
|
36
|
+
*/
|
|
37
|
+
export async function loadAudit(): Promise<AuditData> {
|
|
38
|
+
try {
|
|
39
|
+
const content = await fs.readFile(getAuditFilePath(), 'utf-8');
|
|
40
|
+
return JSON.parse(content) as AuditData;
|
|
41
|
+
} catch {
|
|
42
|
+
// No audit file yet
|
|
43
|
+
return {
|
|
44
|
+
version: '1.0.0',
|
|
45
|
+
projects: {},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Save audit data to disk.
|
|
52
|
+
*/
|
|
53
|
+
export async function saveAudit(data: AuditData): Promise<void> {
|
|
54
|
+
const auditFile = getAuditFilePath();
|
|
55
|
+
const dir = path.dirname(auditFile);
|
|
56
|
+
await fs.mkdir(dir, { recursive: true });
|
|
57
|
+
await fs.writeFile(auditFile, JSON.stringify(data, null, 2), 'utf-8');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Get project ID from project root via .git detection.
|
|
62
|
+
*/
|
|
63
|
+
export function getProjectId(projectRoot: string): string {
|
|
64
|
+
try {
|
|
65
|
+
const { detectProject } = require('../project/detector.js');
|
|
66
|
+
const project = detectProject(projectRoot);
|
|
67
|
+
if (project) return project.id;
|
|
68
|
+
} catch { /* fallback below */ }
|
|
69
|
+
const normalized = projectRoot.replace(/\\/g, '/');
|
|
70
|
+
return `untracked/${normalized}`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Record a file written by Memorix.
|
|
75
|
+
*/
|
|
76
|
+
export async function recordFile(
|
|
77
|
+
projectRoot: string,
|
|
78
|
+
type: AuditEntry['type'],
|
|
79
|
+
filePath: string,
|
|
80
|
+
agent?: string,
|
|
81
|
+
): Promise<void> {
|
|
82
|
+
const data = await loadAudit();
|
|
83
|
+
const projectId = getProjectId(projectRoot);
|
|
84
|
+
|
|
85
|
+
if (!data.projects[projectId]) {
|
|
86
|
+
data.projects[projectId] = {
|
|
87
|
+
projectRoot,
|
|
88
|
+
installedAt: new Date().toISOString(),
|
|
89
|
+
entries: [],
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Check if entry already exists
|
|
94
|
+
const existingIndex = data.projects[projectId].entries.findIndex(
|
|
95
|
+
(e) => e.path === filePath
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
if (existingIndex === -1) {
|
|
99
|
+
data.projects[projectId].entries.push({
|
|
100
|
+
type,
|
|
101
|
+
agent,
|
|
102
|
+
path: filePath,
|
|
103
|
+
createdAt: new Date().toISOString(),
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
await saveAudit(data);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Get all files written by Memorix for a project.
|
|
112
|
+
*/
|
|
113
|
+
export async function getProjectFiles(projectRoot: string): Promise<AuditEntry[]> {
|
|
114
|
+
const data = await loadAudit();
|
|
115
|
+
const projectId = getProjectId(projectRoot);
|
|
116
|
+
return data.projects[projectId]?.entries || [];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Remove a file from audit (when uninstalled).
|
|
121
|
+
*/
|
|
122
|
+
export async function removeFile(projectRoot: string, filePath: string): Promise<void> {
|
|
123
|
+
const data = await loadAudit();
|
|
124
|
+
const projectId = getProjectId(projectRoot);
|
|
125
|
+
|
|
126
|
+
if (!data.projects[projectId]) return;
|
|
127
|
+
|
|
128
|
+
data.projects[projectId].entries = data.projects[projectId].entries.filter(
|
|
129
|
+
(e) => e.path !== filePath
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
// If no entries left, remove the project
|
|
133
|
+
if (data.projects[projectId].entries.length === 0) {
|
|
134
|
+
delete data.projects[projectId];
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
await saveAudit(data);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Get all audit entries across all projects.
|
|
142
|
+
*/
|
|
143
|
+
export async function getAllAuditEntries(): Promise<
|
|
144
|
+
Array<{ projectId: string; entry: AuditEntry }>
|
|
145
|
+
> {
|
|
146
|
+
const data = await loadAudit();
|
|
147
|
+
const entries: Array<{ projectId: string; entry: AuditEntry }> = [];
|
|
148
|
+
|
|
149
|
+
for (const [projectId, project] of Object.entries(data.projects)) {
|
|
150
|
+
for (const entry of project.entries) {
|
|
151
|
+
entries.push({ projectId, entry });
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return entries;
|
|
156
|
+
}
|
|
@@ -19,9 +19,13 @@
|
|
|
19
19
|
|
|
20
20
|
import { defineCommand } from 'citty';
|
|
21
21
|
import * as fs from 'node:fs';
|
|
22
|
+
import path from 'node:path';
|
|
23
|
+
import { fileURLToPath } from 'node:url';
|
|
22
24
|
import { spawn, execFileSync } from 'node:child_process';
|
|
23
25
|
import { randomBytes } from 'node:crypto';
|
|
26
|
+
import os from 'node:os';
|
|
24
27
|
import { parseTcpPort } from '../port.js';
|
|
28
|
+
import { homeProjectRootError, inheritProjectRootFromHome, isHomeDirectory } from '../../project/launch-root.js';
|
|
25
29
|
|
|
26
30
|
// ============================================================
|
|
27
31
|
// Paths & Types
|
|
@@ -38,6 +42,16 @@ interface BackgroundState {
|
|
|
38
42
|
startCwd?: string;
|
|
39
43
|
}
|
|
40
44
|
|
|
45
|
+
export function resolveBackgroundCwd(cwd: string, homeDir: string = os.homedir()): string {
|
|
46
|
+
if (!isHomeDirectory(cwd, homeDir)) return cwd;
|
|
47
|
+
const inherited = inheritProjectRootFromHome({
|
|
48
|
+
homeDir,
|
|
49
|
+
envProjectRoot: process.env.MEMORIX_PROJECT_ROOT,
|
|
50
|
+
});
|
|
51
|
+
if (inherited) return inherited;
|
|
52
|
+
throw new Error(homeProjectRootError(cwd));
|
|
53
|
+
}
|
|
54
|
+
|
|
41
55
|
function getMemorixDir(): string {
|
|
42
56
|
const home = process.env.HOME || process.env.USERPROFILE || '';
|
|
43
57
|
// Use join-style path to ensure native separators on Windows
|
|
@@ -73,6 +87,33 @@ function parseBackgroundPort(value: string | undefined): number {
|
|
|
73
87
|
return parseTcpPort(value, 3211);
|
|
74
88
|
}
|
|
75
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Resolve the CLI file that `background start` should spawn.
|
|
92
|
+
* `process.argv[1]` follows the launcher shim and can point at a different
|
|
93
|
+
* install (for example `$HOME/node_modules/memorix` after `bun add` in $HOME).
|
|
94
|
+
* When this module is bundled into `dist/cli/index.js`, `../index.js` is the
|
|
95
|
+
* library stdio entry (`dist/index.js`) — never spawn that.
|
|
96
|
+
*/
|
|
97
|
+
function resolveBackgroundCliEntryFrom(here: string, argv1: string): string {
|
|
98
|
+
const dir = path.dirname(here);
|
|
99
|
+
if (path.basename(here) === 'index.js' && path.basename(dir) === 'cli') {
|
|
100
|
+
return here;
|
|
101
|
+
}
|
|
102
|
+
const compiledSibling = path.resolve(dir, '..', 'index.js');
|
|
103
|
+
if (fs.existsSync(compiledSibling) && path.basename(path.dirname(compiledSibling)) === 'cli') {
|
|
104
|
+
return compiledSibling;
|
|
105
|
+
}
|
|
106
|
+
return argv1;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function resolveBackgroundCliEntry(argv1 = process.argv[1]): string {
|
|
110
|
+
try {
|
|
111
|
+
return resolveBackgroundCliEntryFrom(fileURLToPath(import.meta.url), argv1);
|
|
112
|
+
} catch {
|
|
113
|
+
return argv1;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
76
117
|
function parseLogLines(value: string | undefined): number {
|
|
77
118
|
return parseBoundedPositiveInteger(value, 50, 'Log line count', 10_000);
|
|
78
119
|
}
|
|
@@ -343,13 +384,15 @@ export async function doStart(port: number): Promise<void> {
|
|
|
343
384
|
|
|
344
385
|
// 5. Find the memorix CLI entry point
|
|
345
386
|
// We need to spawn `node <cli-entry> serve-http --port <port>`
|
|
346
|
-
//
|
|
347
|
-
|
|
387
|
+
// Prefer this package's compiled CLI so a bun/npm shim cannot spawn a
|
|
388
|
+
// different (often unpatched) memorix from ~/node_modules.
|
|
389
|
+
const cliEntry = resolveBackgroundCliEntry();
|
|
348
390
|
|
|
349
391
|
// 6. Start the service without a visible console. A regular non-detached child
|
|
350
392
|
// can be reclaimed with its parent by terminal hosts on Windows; a detached Node
|
|
351
393
|
// child can flash a console. Start-Process gives us a hidden, independent child
|
|
352
394
|
// and its real Node PID. POSIX retains the direct detached child path.
|
|
395
|
+
const startCwd = resolveBackgroundCwd(process.cwd());
|
|
353
396
|
let pid: number;
|
|
354
397
|
try {
|
|
355
398
|
if (process.platform === 'win32') {
|
|
@@ -357,7 +400,7 @@ export async function doStart(port: number): Promise<void> {
|
|
|
357
400
|
nodePath: process.execPath,
|
|
358
401
|
cliEntry,
|
|
359
402
|
port,
|
|
360
|
-
cwd:
|
|
403
|
+
cwd: startCwd,
|
|
361
404
|
stdoutLogFile: `${logFile}.stdout`,
|
|
362
405
|
stderrLogFile: logFile,
|
|
363
406
|
});
|
|
@@ -367,7 +410,7 @@ export async function doStart(port: number): Promise<void> {
|
|
|
367
410
|
detached: true,
|
|
368
411
|
stdio: ['ignore', logFd, logFd],
|
|
369
412
|
env: { ...process.env },
|
|
370
|
-
cwd:
|
|
413
|
+
cwd: startCwd,
|
|
371
414
|
});
|
|
372
415
|
child.unref();
|
|
373
416
|
fs.closeSync(logFd);
|
|
@@ -388,7 +431,7 @@ export async function doStart(port: number): Promise<void> {
|
|
|
388
431
|
startedAt: new Date().toISOString(),
|
|
389
432
|
logFile,
|
|
390
433
|
instanceToken,
|
|
391
|
-
startCwd
|
|
434
|
+
startCwd,
|
|
392
435
|
});
|
|
393
436
|
|
|
394
437
|
// 8. Print essential info immediately
|
|
@@ -792,4 +835,7 @@ export const _testing = {
|
|
|
792
835
|
parseBackgroundPort,
|
|
793
836
|
parseLogLines,
|
|
794
837
|
formatBackgroundCommandError,
|
|
838
|
+
resolveBackgroundCliEntry,
|
|
839
|
+
resolveBackgroundCliEntryFrom,
|
|
840
|
+
resolveBackgroundCwd,
|
|
795
841
|
};
|
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
import { defineCommand } from 'citty';
|
|
2
|
-
import { getBeforeAfterMetrics, getMetricsSummary } from '../../memory/formation/index.js';
|
|
3
|
-
import { emitError, emitResult } from './operator-shared.js';
|
|
4
|
-
|
|
5
|
-
export default defineCommand({
|
|
6
|
-
meta: {
|
|
7
|
-
name: 'formation',
|
|
8
|
-
description: 'Inspect Memory Formation Pipeline runtime metrics',
|
|
9
|
-
},
|
|
10
|
-
args: {
|
|
11
|
-
json: { type: 'boolean', description: 'Emit machine-readable JSON output' },
|
|
12
|
-
},
|
|
13
|
-
run: async ({ args }) => {
|
|
14
|
-
const action = (args._ as string[])?.[0] || '';
|
|
15
|
-
const asJson = !!args.json;
|
|
16
|
-
|
|
17
|
-
try {
|
|
18
|
-
switch (action) {
|
|
19
|
-
case 'metrics': {
|
|
20
|
-
const summary = getMetricsSummary();
|
|
21
|
-
const beforeAfter = getBeforeAfterMetrics();
|
|
22
|
-
emitResult(
|
|
23
|
-
{ summary, beforeAfter },
|
|
24
|
-
summary.total === 0
|
|
25
|
-
? 'Formation Pipeline: No metrics collected yet.'
|
|
26
|
-
: [
|
|
27
|
-
'Formation Pipeline Metrics',
|
|
28
|
-
`- Total observations processed: ${summary.total}`,
|
|
29
|
-
`- Average value score: ${summary.avgValueScore.toFixed(3)}`,
|
|
30
|
-
`- Average processing time: ${summary.avgDurationMs.toFixed(1)}ms`,
|
|
31
|
-
].join('\n'),
|
|
32
|
-
asJson,
|
|
33
|
-
);
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
default:
|
|
38
|
-
console.log('Memorix Formation Commands');
|
|
39
|
-
console.log('');
|
|
40
|
-
console.log('Usage:');
|
|
41
|
-
console.log(' memorix formation metrics');
|
|
42
|
-
}
|
|
43
|
-
} catch (error) {
|
|
44
|
-
emitError(error instanceof Error ? error.message : String(error), asJson);
|
|
45
|
-
}
|
|
46
|
-
},
|
|
47
|
-
});
|
|
48
|
-
|
|
1
|
+
import { defineCommand } from 'citty';
|
|
2
|
+
import { getBeforeAfterMetrics, getMetricsSummary } from '../../memory/formation/index.js';
|
|
3
|
+
import { emitError, emitResult } from './operator-shared.js';
|
|
4
|
+
|
|
5
|
+
export default defineCommand({
|
|
6
|
+
meta: {
|
|
7
|
+
name: 'formation',
|
|
8
|
+
description: 'Inspect Memory Formation Pipeline runtime metrics',
|
|
9
|
+
},
|
|
10
|
+
args: {
|
|
11
|
+
json: { type: 'boolean', description: 'Emit machine-readable JSON output' },
|
|
12
|
+
},
|
|
13
|
+
run: async ({ args }) => {
|
|
14
|
+
const action = (args._ as string[])?.[0] || '';
|
|
15
|
+
const asJson = !!args.json;
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
switch (action) {
|
|
19
|
+
case 'metrics': {
|
|
20
|
+
const summary = getMetricsSummary();
|
|
21
|
+
const beforeAfter = getBeforeAfterMetrics();
|
|
22
|
+
emitResult(
|
|
23
|
+
{ summary, beforeAfter },
|
|
24
|
+
summary.total === 0
|
|
25
|
+
? 'Formation Pipeline: No metrics collected yet.'
|
|
26
|
+
: [
|
|
27
|
+
'Formation Pipeline Metrics',
|
|
28
|
+
`- Total observations processed: ${summary.total}`,
|
|
29
|
+
`- Average value score: ${summary.avgValueScore.toFixed(3)}`,
|
|
30
|
+
`- Average processing time: ${summary.avgDurationMs.toFixed(1)}ms`,
|
|
31
|
+
].join('\n'),
|
|
32
|
+
asJson,
|
|
33
|
+
);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
default:
|
|
38
|
+
console.log('Memorix Formation Commands');
|
|
39
|
+
console.log('');
|
|
40
|
+
console.log('Usage:');
|
|
41
|
+
console.log(' memorix formation metrics');
|
|
42
|
+
}
|
|
43
|
+
} catch (error) {
|
|
44
|
+
emitError(error instanceof Error ? error.message : String(error), asJson);
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
|