memorix 1.4.5 → 1.5.0
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 +78 -0
- package/CLAUDE.md +7 -3
- package/README.md +5 -2
- package/dist/cli/index.js +1470 -1014
- package/dist/index.js +1279 -989
- 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 +1769 -830
- package/dist/media-audio-runner.js +1774 -836
- package/dist/media-video-runner.js +1769 -831
- package/dist/memcode-runtime/CHANGELOG.md +78 -0
- package/dist/sdk.js +1271 -988
- package/dist/types.d.ts +6 -0
- package/dist/vector-backfill-runner.d.ts +1 -1
- package/dist/vector-backfill-runner.js +1765 -827
- package/docs/1.4.5-AGENT-AUTO-MEMORY-RESEARCH.md +95 -0
- package/docs/API_REFERENCE.md +23 -4
- package/docs/CONFIGURATION.md +29 -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 +23 -7
- 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
package/src/server.ts
CHANGED
|
@@ -52,6 +52,7 @@ import type { FormationConfig, SearchHit, FormedMemory, FormationStage, Formatio
|
|
|
52
52
|
import { parseFormationTimeoutMs } from './server/formation-timeout.js';
|
|
53
53
|
import { withTimeout } from './timeout.js';
|
|
54
54
|
import { sanitizeCredentials } from './memory/secret-filter.js';
|
|
55
|
+
import { getSurfacedIds, recordSurfacedIds } from './search/surfaced-registry.js';
|
|
55
56
|
import {
|
|
56
57
|
createProjectBindingController,
|
|
57
58
|
type ProjectBindingController,
|
|
@@ -508,6 +509,12 @@ export async function createMemorixServer(
|
|
|
508
509
|
kind: 'consolidation',
|
|
509
510
|
dedupeKey: 'startup-consolidation',
|
|
510
511
|
});
|
|
512
|
+
lifecycle.enqueueLongTermMaintenance({
|
|
513
|
+
dataDir: projectDir,
|
|
514
|
+
projectId: project.id,
|
|
515
|
+
source: 'startup',
|
|
516
|
+
queue,
|
|
517
|
+
});
|
|
511
518
|
}
|
|
512
519
|
|
|
513
520
|
// This check is only a small SQLite metadata query. The actual directory
|
|
@@ -1152,7 +1159,7 @@ export async function createMemorixServer(
|
|
|
1152
1159
|
let longTermNote = '';
|
|
1153
1160
|
if (longTerm) {
|
|
1154
1161
|
try {
|
|
1155
|
-
const { promoteObservationToLongTermMemory } = await import('./memory/long-term.js');
|
|
1162
|
+
const { promoteObservationToLongTermMemory, maybeAutoQualifyLongTermMemory } = await import('./memory/long-term.js');
|
|
1156
1163
|
const promoted = await promoteObservationToLongTermMemory({
|
|
1157
1164
|
dataDir: projectDir,
|
|
1158
1165
|
observation: obs,
|
|
@@ -1166,7 +1173,28 @@ export async function createMemorixServer(
|
|
|
1166
1173
|
...(reader.isTeamMember ? { isTeamMember: true } : {}),
|
|
1167
1174
|
},
|
|
1168
1175
|
});
|
|
1169
|
-
|
|
1176
|
+
// An explicit store call carries its own source evidence: qualify
|
|
1177
|
+
// on the spot instead of waiting for a manual CLI review.
|
|
1178
|
+
const autoQualified = await maybeAutoQualifyLongTermMemory({
|
|
1179
|
+
dataDir: projectDir,
|
|
1180
|
+
id: promoted.memory.id,
|
|
1181
|
+
sourceDetail: 'explicit',
|
|
1182
|
+
});
|
|
1183
|
+
if (autoQualified) {
|
|
1184
|
+
// A fresh explicit record retires stale same-title records on the
|
|
1185
|
+
// durable rule leg instead of waiting for the next session start.
|
|
1186
|
+
try {
|
|
1187
|
+
const { enqueueLongTermMaintenance } = await import('./runtime/lifecycle.js');
|
|
1188
|
+
enqueueLongTermMaintenance({
|
|
1189
|
+
dataDir: projectDir,
|
|
1190
|
+
projectId: project.id,
|
|
1191
|
+
source: 'long-term:' + promoted.memory.id,
|
|
1192
|
+
});
|
|
1193
|
+
} catch { /* maintenance is optional; the record already qualified */ }
|
|
1194
|
+
}
|
|
1195
|
+
longTermNote = autoQualified
|
|
1196
|
+
? `\nLong-term memory: ${promoted.memory.id} (${promoted.memory.kind}/${promoted.memory.scope}, auto-qualified) and now enters briefs as durable context. Audit with \`memorix memory long-term list\`.`
|
|
1197
|
+
: `\nLong-term candidate: ${promoted.memory.id} (${promoted.memory.kind}/${promoted.memory.scope}). Review it with the CLI before it is delivered automatically.`;
|
|
1170
1198
|
} catch (longTermError) {
|
|
1171
1199
|
longTermNote = `\n[WARN] Observation stored, but long-term candidate was not created: ${longTermError instanceof Error ? longTermError.message : 'unknown error'}`;
|
|
1172
1200
|
}
|
|
@@ -1394,6 +1422,17 @@ export async function createMemorixServer(
|
|
|
1394
1422
|
const boundary = requireExplicitAutopilotExpansion('memorix_search', purpose);
|
|
1395
1423
|
if (boundary) return boundary;
|
|
1396
1424
|
}
|
|
1425
|
+
|
|
1426
|
+
// Session dedup scope: prefer the active session id, fall back to the
|
|
1427
|
+
// bound project. Best-effort bookkeeping — losing it only costs one
|
|
1428
|
+
// redundant result row, never data.
|
|
1429
|
+
let surfacedKey = `project:${project.id}`;
|
|
1430
|
+
try {
|
|
1431
|
+
const { getActiveSession } = await import('./memory/session.js');
|
|
1432
|
+
const active = await getActiveSession(projectDir, project.id);
|
|
1433
|
+
if (active) surfacedKey = `session:${active.id}`;
|
|
1434
|
+
} catch { /* fall back to the project key */ }
|
|
1435
|
+
|
|
1397
1436
|
return withFreshIndex(async () => {
|
|
1398
1437
|
|
|
1399
1438
|
const safeLimit = limit != null ? coerceNumber(limit, 20) : undefined;
|
|
@@ -1415,6 +1454,7 @@ export async function createMemorixServer(
|
|
|
1415
1454
|
source: source as 'agent' | 'git' | 'manual' | undefined,
|
|
1416
1455
|
quality: quality as 'fast' | 'balanced' | 'thorough',
|
|
1417
1456
|
reader: getObservationReader(scope === 'global' ? 'global' : 'project'),
|
|
1457
|
+
surfacedIds: getSurfacedIds(surfacedKey),
|
|
1418
1458
|
});
|
|
1419
1459
|
|
|
1420
1460
|
let result;
|
|
@@ -1436,6 +1476,10 @@ export async function createMemorixServer(
|
|
|
1436
1476
|
throw error;
|
|
1437
1477
|
}
|
|
1438
1478
|
|
|
1479
|
+
// These rows were just shown to the model — remember them so later
|
|
1480
|
+
// searches in the same session demote instead of re-showing.
|
|
1481
|
+
recordSurfacedIds(surfacedKey, result.entries.map(entry => entry.id));
|
|
1482
|
+
|
|
1439
1483
|
const activeBoundary = getActiveAutopilotRetrievalBoundary();
|
|
1440
1484
|
if (
|
|
1441
1485
|
scope !== 'global'
|
|
@@ -4998,6 +5042,12 @@ export async function createMemorixServer(
|
|
|
4998
5042
|
return undefined;
|
|
4999
5043
|
}
|
|
5000
5044
|
|
|
5045
|
+
// The advertised schema matches what the operator actually enabled: a
|
|
5046
|
+
// closed gate means the action does not exist for the agent, not that it
|
|
5047
|
+
// exists and throws. The handler keeps its own runtime checks as well.
|
|
5048
|
+
const mediaGenerationEnabled = process.env.MEMORIX_MCP_MEDIA_GENERATION === '1';
|
|
5049
|
+
const mediaTranscriptionEnabled = process.env.MEMORIX_MCP_MEDIA_TRANSCRIPTION === '1';
|
|
5050
|
+
const mediaGenerationOrTranscription = mediaGenerationEnabled || mediaTranscriptionEnabled;
|
|
5001
5051
|
server.registerTool(
|
|
5002
5052
|
'memorix_media',
|
|
5003
5053
|
{
|
|
@@ -5008,7 +5058,11 @@ export async function createMemorixServer(
|
|
|
5008
5058
|
'Use the CLI for destructive removal, quota cleanup, and direct generation. ' +
|
|
5009
5059
|
'MCP generation is disabled by default and requires MEMORIX_MCP_MEDIA_GENERATION=1 after the operator reviews provider billing.',
|
|
5010
5060
|
inputSchema: {
|
|
5011
|
-
action: z.enum([
|
|
5061
|
+
action: z.enum([
|
|
5062
|
+
'import', 'attach', 'list', 'show', 'derive-pdf', 'status', 'cancel',
|
|
5063
|
+
...(mediaTranscriptionEnabled ? ['derive-audio'] : []),
|
|
5064
|
+
...(mediaGenerationEnabled ? ['generate-image', 'generate-video'] : []),
|
|
5065
|
+
] as [string, ...string[]]),
|
|
5012
5066
|
path: z.string().optional().describe('Explicit local image/audio/video/PDF path for import.'),
|
|
5013
5067
|
assetId: z.string().optional().describe('Controlled MediaAsset ID for attach/show.'),
|
|
5014
5068
|
jobId: z.string().optional().describe('Durable media job ID for status.'),
|
|
@@ -5016,20 +5070,26 @@ export async function createMemorixServer(
|
|
|
5016
5070
|
limit: z.number().int().min(1).max(100).optional().describe('Maximum assets to list.'),
|
|
5017
5071
|
title: z.string().optional().describe('Observation title when attaching generated/imported output.'),
|
|
5018
5072
|
narrative: z.string().optional().describe('Short retrieval text when attaching an asset.'),
|
|
5019
|
-
prompt: z.string().optional().describe('Explicit MiniMax generation prompt.'),
|
|
5020
|
-
image: z.string().optional().describe('Base64-encoded reference image for image-to-image generation.'),
|
|
5021
|
-
model: z.string().max(160).optional().describe('Provider model for the selected media action.'),
|
|
5022
|
-
provider: z.enum(['openai', 'groq']).optional().describe('Explicit audio transcription provider.'),
|
|
5023
|
-
language: z.string().max(32).optional().describe('Optional ISO-639-1 hint for audio transcription.'),
|
|
5024
|
-
region: z.enum(['global', 'cn']).optional().describe('MiniMax deployment region.'),
|
|
5025
|
-
n: z.number().int().min(1).max(4).optional().describe('Image output count.'),
|
|
5026
|
-
ratio: z.enum(['adaptive', '1:1', '16:9', '4:3', '3:2', '2:3', '3:4', '9:16', '21:9']).optional().describe('Image or video aspect ratio.'),
|
|
5027
|
-
width: z.number().int().min(1).max(8192).optional().describe('Requested image width.'),
|
|
5028
|
-
height: z.number().int().min(1).max(8192).optional().describe('Requested image height.'),
|
|
5029
|
-
duration: z.union([z.literal(5), z.literal(10)]).optional().describe('MiniMax video duration in seconds.'),
|
|
5030
5073
|
maxPages: z.number().int().min(1).max(100).optional().describe('Bounded PDF extraction page limit.'),
|
|
5031
5074
|
maxChars: z.number().int().min(1).max(60_000).optional().describe('Bounded PDF extraction character limit.'),
|
|
5032
5075
|
attach: z.boolean().optional().describe('Attach generated/imported output to normal project memory explicitly.'),
|
|
5076
|
+
...(mediaGenerationOrTranscription ? {
|
|
5077
|
+
model: z.string().max(160).optional().describe('Provider model for the selected media action.'),
|
|
5078
|
+
prompt: z.string().optional().describe('Explicit prompt for generation or transcription.'),
|
|
5079
|
+
} : {}),
|
|
5080
|
+
...(mediaTranscriptionEnabled ? {
|
|
5081
|
+
provider: z.enum(['openai', 'groq']).optional().describe('Explicit audio transcription provider.'),
|
|
5082
|
+
language: z.string().max(32).optional().describe('Optional ISO-639-1 hint for audio transcription.'),
|
|
5083
|
+
} : {}),
|
|
5084
|
+
...(mediaGenerationEnabled ? {
|
|
5085
|
+
image: z.string().optional().describe('Base64-encoded reference image for image-to-image generation.'),
|
|
5086
|
+
region: z.enum(['global', 'cn']).optional().describe('MiniMax deployment region.'),
|
|
5087
|
+
n: z.number().int().min(1).max(4).optional().describe('Image output count.'),
|
|
5088
|
+
ratio: z.enum(['adaptive', '1:1', '16:9', '4:3', '3:2', '2:3', '3:4', '9:16', '21:9']).optional().describe('Image or video aspect ratio.'),
|
|
5089
|
+
width: z.number().int().min(1).max(8192).optional().describe('Requested image width.'),
|
|
5090
|
+
height: z.number().int().min(1).max(8192).optional().describe('Requested image height.'),
|
|
5091
|
+
duration: z.union([z.literal(5), z.literal(10)]).optional().describe('MiniMax video duration in seconds.'),
|
|
5092
|
+
} : {}),
|
|
5033
5093
|
},
|
|
5034
5094
|
},
|
|
5035
5095
|
async ({ action, path: assetPath, assetId, jobId, kind, limit, title, narrative, prompt, image, model, provider, language, region, n, ratio, width, height, duration, maxPages, maxChars, attach }) => {
|
package/src/store/orama-store.ts
CHANGED
|
@@ -587,7 +587,9 @@ export async function searchObservations(options: SearchOptions): Promise<IndexE
|
|
|
587
587
|
|
|
588
588
|
// Orama's vector/hybrid search can leak cross-project hits even when `where`
|
|
589
589
|
// is present, so always keep enough headroom for a deterministic post-filter.
|
|
590
|
-
|
|
590
|
+
// Session dedup demotes already-surfaced entries after ranking, so it needs
|
|
591
|
+
// the same headroom to let unseen candidates rise into the final limit.
|
|
592
|
+
const requestLimit = projectIds || (options.surfacedIds && options.surfacedIds.length > 0)
|
|
591
593
|
? (options.limit ?? 20) * 3
|
|
592
594
|
: (options.limit ?? 20);
|
|
593
595
|
|
|
@@ -902,6 +904,20 @@ export async function searchObservations(options: SearchOptions): Promise<IndexE
|
|
|
902
904
|
intermediate.sort((a, b) => b.score - a.score);
|
|
903
905
|
}
|
|
904
906
|
|
|
907
|
+
// ── Session dedup (demote, never remove) ────────────────────────
|
|
908
|
+
// Entries already surfaced earlier in the same session are demoted so
|
|
909
|
+
// unseen evidence can rise into the final limit. A demoted entry is never
|
|
910
|
+
// removed: when it is the strong match for a new question, it still wins
|
|
911
|
+
// on score and answers that turn.
|
|
912
|
+
if (options.surfacedIds && options.surfacedIds.length > 0) {
|
|
913
|
+
const SURFACED_PENALTY = 0.5;
|
|
914
|
+
const surfaced = new Set(options.surfacedIds);
|
|
915
|
+
intermediate = intermediate.map(entry =>
|
|
916
|
+
surfaced.has(entry.id) ? { ...entry, score: (entry.score ?? 0) * SURFACED_PENALTY } : entry,
|
|
917
|
+
);
|
|
918
|
+
intermediate.sort((a, b) => (b.score ?? 0) - (a.score ?? 0));
|
|
919
|
+
}
|
|
920
|
+
|
|
905
921
|
// Temporal filtering: since/until date range
|
|
906
922
|
if (options.since) {
|
|
907
923
|
const sinceDate = new Date(options.since).getTime();
|
package/src/types.ts
CHANGED
|
@@ -317,6 +317,12 @@ export interface SearchOptions {
|
|
|
317
317
|
trackAccess?: boolean;
|
|
318
318
|
/** Internal reader context for agent-facing visibility filtering. */
|
|
319
319
|
reader?: ObservationReader;
|
|
320
|
+
/**
|
|
321
|
+
* Observation ids already surfaced earlier in the same session. Surfaced
|
|
322
|
+
* entries are demoted (never removed) so unseen evidence can rise into the
|
|
323
|
+
* result limit; a strong match still answers a new question.
|
|
324
|
+
*/
|
|
325
|
+
surfacedIds?: number[];
|
|
320
326
|
/**
|
|
321
327
|
* `fast` stays fully local, `balanced` (default) permits embeddings, and
|
|
322
328
|
* `thorough` explicitly permits optional LLM query rewrite and reranking.
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CLI Command: memorix audit list
|
|
3
|
-
*
|
|
4
|
-
* List all files written by Memorix (audit trail).
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { defineCommand } from 'citty';
|
|
8
|
-
import * as p from '@clack/prompts';
|
|
9
|
-
|
|
10
|
-
export default defineCommand({
|
|
11
|
-
meta: {
|
|
12
|
-
name: 'list',
|
|
13
|
-
description: 'List Memorix-written files (audit trail)',
|
|
14
|
-
},
|
|
15
|
-
args: {
|
|
16
|
-
project: {
|
|
17
|
-
type: 'string',
|
|
18
|
-
description: 'Filter by project (optional)',
|
|
19
|
-
required: false,
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
run: async ({ args }) => {
|
|
23
|
-
const { getAllAuditEntries, getProjectId } = await import('../../audit/index.js');
|
|
24
|
-
const os = await import('node:os');
|
|
25
|
-
let cwd: string;
|
|
26
|
-
try {
|
|
27
|
-
cwd = process.cwd();
|
|
28
|
-
} catch {
|
|
29
|
-
cwd = os.homedir();
|
|
30
|
-
console.log(`[WARN] Could not access current directory, using home: ${cwd}`);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
p.intro('Memorix Audit Trail');
|
|
34
|
-
|
|
35
|
-
const allEntries = await getAllAuditEntries();
|
|
36
|
-
|
|
37
|
-
if (allEntries.length === 0) {
|
|
38
|
-
console.log('No Memorix-written files found.');
|
|
39
|
-
p.outro('Audit trail is empty.');
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Filter by project if specified
|
|
44
|
-
let entries = allEntries;
|
|
45
|
-
if (args.project) {
|
|
46
|
-
const targetProjectId = getProjectId(args.project);
|
|
47
|
-
entries = allEntries.filter((e) => e.projectId === targetProjectId);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (entries.length === 0) {
|
|
51
|
-
console.log('No Memorix-written files found for this project.');
|
|
52
|
-
p.outro('Audit trail is empty for this project.');
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Group by project
|
|
57
|
-
const byProject: Record<string, Array<{ entry: import('../../audit/index.js').AuditEntry }>> = {};
|
|
58
|
-
for (const { projectId, entry } of entries) {
|
|
59
|
-
if (!byProject[projectId]) {
|
|
60
|
-
byProject[projectId] = [];
|
|
61
|
-
}
|
|
62
|
-
byProject[projectId].push({ entry });
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Display by project
|
|
66
|
-
console.log('');
|
|
67
|
-
for (const [projectId, items] of Object.entries(byProject)) {
|
|
68
|
-
console.log(`Project: ${projectId}`);
|
|
69
|
-
console.log('');
|
|
70
|
-
|
|
71
|
-
for (const { entry } of items) {
|
|
72
|
-
const icon = entry.type === 'hook' ? '[HOOK]' : '[TASK]';
|
|
73
|
-
const agent = entry.agent ? ` (${entry.agent})` : '';
|
|
74
|
-
console.log(`${icon} ${entry.path}${agent}`);
|
|
75
|
-
console.log(` Created: ${entry.createdAt}`);
|
|
76
|
-
console.log('');
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// Summary
|
|
81
|
-
const hookCount = entries.filter((e) => e.entry.type === 'hook').length;
|
|
82
|
-
const ruleCount = entries.filter((e) => e.entry.type === 'rule').length;
|
|
83
|
-
const projectCount = new Set(entries.map((e) => e.projectId)).size;
|
|
84
|
-
|
|
85
|
-
p.note(`${projectCount} projects, ${hookCount} hooks, ${ruleCount} rules`);
|
|
86
|
-
|
|
87
|
-
p.outro('Audit trail complete.');
|
|
88
|
-
},
|
|
89
|
-
});
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CLI Command: memorix purge all-memory
|
|
3
|
-
*
|
|
4
|
-
* Delete ALL memories (dangerous operation).
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { defineCommand } from 'citty';
|
|
8
|
-
import * as p from '@clack/prompts';
|
|
9
|
-
|
|
10
|
-
export default defineCommand({
|
|
11
|
-
meta: {
|
|
12
|
-
name: 'all-memory',
|
|
13
|
-
description: 'Delete ALL memories (dangerous)',
|
|
14
|
-
},
|
|
15
|
-
args: {},
|
|
16
|
-
run: async ({}) => {
|
|
17
|
-
p.intro('[WARN] Purge All Memory');
|
|
18
|
-
|
|
19
|
-
// Show warning
|
|
20
|
-
console.log('');
|
|
21
|
-
console.log('[WARN] WARNING: This will delete ALL memories!');
|
|
22
|
-
console.log('');
|
|
23
|
-
console.log('This will remove:');
|
|
24
|
-
console.log(' - All observations from all projects');
|
|
25
|
-
console.log(' - All entities and relations');
|
|
26
|
-
console.log(' - All embeddings');
|
|
27
|
-
console.log('');
|
|
28
|
-
console.log('This action CANNOT be undone.');
|
|
29
|
-
console.log('');
|
|
30
|
-
console.log('Project artifacts (hook files) will be preserved.');
|
|
31
|
-
console.log('');
|
|
32
|
-
|
|
33
|
-
// First confirmation
|
|
34
|
-
const confirmed1 = await p.confirm({
|
|
35
|
-
message: 'Are you sure you want to delete ALL memories?',
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
if (p.isCancel(confirmed1) || !confirmed1) {
|
|
39
|
-
p.outro('Purge cancelled.');
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Second confirmation (type "DELETE" to confirm)
|
|
44
|
-
const confirmed2 = await p.text({
|
|
45
|
-
message: 'Type "DELETE" to confirm:',
|
|
46
|
-
validate: (value) => {
|
|
47
|
-
if (value !== 'DELETE') {
|
|
48
|
-
return 'Please type "DELETE" to confirm';
|
|
49
|
-
}
|
|
50
|
-
return undefined;
|
|
51
|
-
},
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
if (p.isCancel(confirmed2) || confirmed2 !== 'DELETE') {
|
|
55
|
-
p.outro('Purge cancelled.');
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Delete all memories
|
|
60
|
-
try {
|
|
61
|
-
const {
|
|
62
|
-
getAllObservations,
|
|
63
|
-
resolveObservations,
|
|
64
|
-
} = await import('../../memory/observations.js');
|
|
65
|
-
const { withFreshIndex } = await import('../../memory/freshness.js');
|
|
66
|
-
const observations = await withFreshIndex(() => getAllObservations());
|
|
67
|
-
const ids = observations.map((o) => o.id);
|
|
68
|
-
|
|
69
|
-
if (ids.length === 0) {
|
|
70
|
-
console.log('No observations found.');
|
|
71
|
-
p.outro('No memories to purge.');
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const result = await resolveObservations(ids, 'archived');
|
|
76
|
-
|
|
77
|
-
console.log(`[OK] Archived ${result.resolved.length} observations`);
|
|
78
|
-
|
|
79
|
-
p.outro(`All memory purged. ${result.resolved.length} observations archived.`);
|
|
80
|
-
} catch (err) {
|
|
81
|
-
console.error(`[ERROR] Failed to purge all memory: ${err}`);
|
|
82
|
-
p.outro('Purge failed.');
|
|
83
|
-
}
|
|
84
|
-
},
|
|
85
|
-
});
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CLI Command: memorix purge project-memory
|
|
3
|
-
*
|
|
4
|
-
* Delete memories for the current project only.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { defineCommand } from 'citty';
|
|
8
|
-
import * as p from '@clack/prompts';
|
|
9
|
-
|
|
10
|
-
export default defineCommand({
|
|
11
|
-
meta: {
|
|
12
|
-
name: 'project-memory',
|
|
13
|
-
description: 'Delete memories for current project',
|
|
14
|
-
},
|
|
15
|
-
args: {},
|
|
16
|
-
run: async ({}) => {
|
|
17
|
-
const os = await import('node:os');
|
|
18
|
-
let cwd: string;
|
|
19
|
-
try { cwd = process.cwd(); } catch { cwd = os.homedir(); }
|
|
20
|
-
|
|
21
|
-
p.intro('Purge Project Memory');
|
|
22
|
-
|
|
23
|
-
// Show what will be deleted
|
|
24
|
-
console.log('');
|
|
25
|
-
console.log('Will delete memories for:');
|
|
26
|
-
console.log(` Project: ${cwd}`);
|
|
27
|
-
console.log('');
|
|
28
|
-
console.log('This will remove all observations, entities, and relations');
|
|
29
|
-
console.log('associated with this project from the memory database.');
|
|
30
|
-
console.log('');
|
|
31
|
-
console.log('Project artifacts (hook files) will be preserved.');
|
|
32
|
-
console.log('');
|
|
33
|
-
|
|
34
|
-
// Confirm
|
|
35
|
-
const confirmed = await p.confirm({
|
|
36
|
-
message: 'Continue purge?',
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
if (p.isCancel(confirmed) || !confirmed) {
|
|
40
|
-
p.outro('Purge cancelled.');
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Get project ID
|
|
45
|
-
const projectId = getProjectId(cwd);
|
|
46
|
-
|
|
47
|
-
// Delete project memories
|
|
48
|
-
try {
|
|
49
|
-
const {
|
|
50
|
-
getProjectObservations,
|
|
51
|
-
resolveObservations,
|
|
52
|
-
} = await import('../../memory/observations.js');
|
|
53
|
-
const { withFreshIndex } = await import('../../memory/freshness.js');
|
|
54
|
-
const observations = await withFreshIndex(() => getProjectObservations(projectId));
|
|
55
|
-
const ids = observations.map((o) => o.id);
|
|
56
|
-
|
|
57
|
-
if (ids.length === 0) {
|
|
58
|
-
console.log('No observations found for this project.');
|
|
59
|
-
p.outro('No memories to purge.');
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const result = await resolveObservations(ids, 'archived');
|
|
64
|
-
|
|
65
|
-
console.log(`[OK] Archived ${result.resolved.length} observations for project`);
|
|
66
|
-
|
|
67
|
-
p.outro(`Project memory purged. ${result.resolved.length} observations archived.`);
|
|
68
|
-
} catch (err) {
|
|
69
|
-
console.error(`[ERROR] Failed to purge project memory: ${err}`);
|
|
70
|
-
p.outro('Purge failed.');
|
|
71
|
-
}
|
|
72
|
-
},
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
function getProjectId(projectRoot: string): string {
|
|
76
|
-
try {
|
|
77
|
-
const { detectProject } = require('../project/detector.js');
|
|
78
|
-
const project = detectProject(projectRoot);
|
|
79
|
-
if (project) return project.id;
|
|
80
|
-
} catch { /* fallback below */ }
|
|
81
|
-
const normalized = projectRoot.replace(/\\/g, '/');
|
|
82
|
-
return `untracked/${normalized}`;
|
|
83
|
-
}
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CLI Command: memorix uninstall project-artifacts
|
|
3
|
-
*
|
|
4
|
-
* Uninstall project-level hook files only (preserve memory data).
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { defineCommand } from 'citty';
|
|
8
|
-
import * as p from '@clack/prompts';
|
|
9
|
-
|
|
10
|
-
export default defineCommand({
|
|
11
|
-
meta: {
|
|
12
|
-
name: 'project-artifacts',
|
|
13
|
-
description: 'Uninstall project hook files (preserve memory data)',
|
|
14
|
-
},
|
|
15
|
-
args: {
|
|
16
|
-
agent: {
|
|
17
|
-
type: 'string',
|
|
18
|
-
description: 'Target agent (all if omitted)',
|
|
19
|
-
required: false,
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
run: async ({ args }) => {
|
|
23
|
-
const { detectInstalledAgents, uninstallHooks } = await import('../../hooks/installers/index.js');
|
|
24
|
-
const os = await import('node:os');
|
|
25
|
-
let cwd: string;
|
|
26
|
-
try { cwd = process.cwd(); } catch { cwd = os.homedir(); }
|
|
27
|
-
|
|
28
|
-
p.intro('Uninstall Project Artifacts');
|
|
29
|
-
|
|
30
|
-
let agents: string[];
|
|
31
|
-
if (args.agent) {
|
|
32
|
-
agents = [args.agent];
|
|
33
|
-
} else {
|
|
34
|
-
agents = await detectInstalledAgents();
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (agents.length === 0) {
|
|
38
|
-
console.log('No supported agents detected.');
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Show what will be uninstalled
|
|
43
|
-
console.log('');
|
|
44
|
-
console.log('Will uninstall hooks for:');
|
|
45
|
-
for (const agent of agents) {
|
|
46
|
-
console.log(` - ${agent}`);
|
|
47
|
-
}
|
|
48
|
-
console.log('');
|
|
49
|
-
console.log('This will remove hook files from the project:');
|
|
50
|
-
console.log(' - .claude/settings.local.json');
|
|
51
|
-
console.log(' - .windsurf/hooks.json');
|
|
52
|
-
console.log(' - .cursor/hooks.json');
|
|
53
|
-
console.log(' - .github/hooks/memorix.json');
|
|
54
|
-
console.log(' - .cursor/rules/memorix.mdc');
|
|
55
|
-
console.log(' - etc.');
|
|
56
|
-
console.log('');
|
|
57
|
-
console.log('Memory data will be preserved.');
|
|
58
|
-
console.log('');
|
|
59
|
-
|
|
60
|
-
// Confirm
|
|
61
|
-
const confirmed = await p.confirm({
|
|
62
|
-
message: 'Continue uninstall?',
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
if (p.isCancel(confirmed) || !confirmed) {
|
|
66
|
-
p.outro('Uninstall cancelled.');
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Uninstall
|
|
71
|
-
for (const agent of agents) {
|
|
72
|
-
const ok = await uninstallHooks(
|
|
73
|
-
agent as import('../../hooks/types.js').AgentName,
|
|
74
|
-
cwd,
|
|
75
|
-
false, // project-level
|
|
76
|
-
);
|
|
77
|
-
if (ok) {
|
|
78
|
-
console.log(`[OK] ${agent}: hooks removed`);
|
|
79
|
-
} else {
|
|
80
|
-
console.log(`[SKIP] ${agent}: no hooks found`);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
p.outro('Project artifacts uninstalled. Memory data preserved.');
|
|
85
|
-
},
|
|
86
|
-
});
|