memorix 1.4.4 → 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 +69 -0
- package/CLAUDE.md +7 -3
- package/README.md +14 -4
- package/README.zh-CN.md +9 -2
- package/dist/cli/index.js +1802 -1138
- package/dist/index.js +1298 -991
- 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 +69 -0
- package/dist/sdk.js +1290 -990
- package/dist/types.d.ts +7 -1
- 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/AGENT_OPERATOR_PLAYBOOK.md +3 -2
- package/docs/API_REFERENCE.md +23 -4
- package/docs/CONFIGURATION.md +17 -3
- package/docs/INTEGRATIONS.md +9 -2
- package/docs/KNOWN_ISSUES_AND_ROADMAP.md +1 -0
- package/docs/MEMCODE.md +1 -1
- package/docs/SETUP.md +14 -4
- package/llms-full.txt +5 -4
- package/llms.txt +1 -1
- 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/agent-integrations.ts +7 -1
- package/src/cli/commands/dashboard.ts +14 -3
- package/src/cli/commands/hooks-install.ts +2 -0
- package/src/cli/commands/hooks-preview.ts +5 -0
- package/src/cli/commands/ingest-commit.ts +47 -3
- package/src/cli/commands/integrate.ts +2 -0
- 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 +60 -3
- package/src/cli/commands/uninstall.ts +20 -0
- 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 +58 -2
- package/src/hooks/types.ts +2 -0
- package/src/integrations/registry.ts +9 -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 +7 -1
- package/src/workspace/mcp-adapters/dsh.ts +147 -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
|
@@ -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
|
-
});
|
package/src/cli/tui/Sidebar.tsx
DELETED
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Sidebar right panel with quick action hints and health snapshot.
|
|
3
|
-
*
|
|
4
|
-
* Modern design: Unicode separators, status dots, active indicator
|
|
5
|
-
* with arrow symbol, brand-colored section headers.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import React from 'react';
|
|
9
|
-
import { Box, Text, useInput } from 'ink';
|
|
10
|
-
import { COLORS, SEP, STATUS_DOTS, SYMBOLS } from './theme.js';
|
|
11
|
-
import type { HealthInfo, BackgroundInfo } from './data.js';
|
|
12
|
-
import type { ViewType } from './theme.js';
|
|
13
|
-
|
|
14
|
-
interface SidebarProps {
|
|
15
|
-
health: HealthInfo;
|
|
16
|
-
background: BackgroundInfo;
|
|
17
|
-
onAction: (cmd: string) => void;
|
|
18
|
-
activeView: ViewType;
|
|
19
|
-
/** When true, Sidebar captures shortcut keys and drives navigation. */
|
|
20
|
-
isFocused?: boolean;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const ACTIONS = [
|
|
24
|
-
{ key: 's', label: 'Search', cmd: '/search' },
|
|
25
|
-
{ key: 'r', label: 'Remember', cmd: '/remember' },
|
|
26
|
-
{ key: 'v', label: 'Recent', cmd: '/recent' },
|
|
27
|
-
{ key: 'd', label: 'Doctor', cmd: '/doctor' },
|
|
28
|
-
{ key: 'b', label: 'Background', cmd: '/background' },
|
|
29
|
-
{ key: 'w', label: 'Dashboard', cmd: '/dashboard' },
|
|
30
|
-
{ key: 'p', label: 'Project', cmd: '/project' },
|
|
31
|
-
{ key: 'c', label: 'Configure', cmd: '/configure' },
|
|
32
|
-
{ key: 'i', label: 'Integrate', cmd: '/integrate' },
|
|
33
|
-
{ key: 'h', label: 'Home', cmd: '/home' },
|
|
34
|
-
];
|
|
35
|
-
|
|
36
|
-
const SIDEBAR_WIDTH = 32;
|
|
37
|
-
const INNER_WIDTH = SIDEBAR_WIDTH - 4;
|
|
38
|
-
const ACTION_LABEL_WIDTH = INNER_WIDTH - 2;
|
|
39
|
-
const VALUE_WIDTH = INNER_WIDTH - 8;
|
|
40
|
-
|
|
41
|
-
function separator(width: number): string {
|
|
42
|
-
return SEP.thin.repeat(width);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function colorForMode(mode: string): string {
|
|
46
|
-
const normalized = mode.toLowerCase();
|
|
47
|
-
if (normalized.includes('hybrid')) return COLORS.success;
|
|
48
|
-
if (normalized.includes('vector')) return COLORS.accent;
|
|
49
|
-
return COLORS.warning;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function charWidth(ch: string): number {
|
|
53
|
-
const cp = ch.codePointAt(0)!;
|
|
54
|
-
return ((cp >= 0x4E00 && cp <= 0x9FFF) || (cp >= 0x3040 && cp <= 0x30FF) ||
|
|
55
|
-
(cp >= 0xAC00 && cp <= 0xD7AF) || (cp >= 0xFF01 && cp <= 0xFF60) ||
|
|
56
|
-
(cp >= 0x3400 && cp <= 0x4DBF)) ? 2 : 1;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function fit(text: string, max: number): string {
|
|
60
|
-
if (max <= 0) return '';
|
|
61
|
-
let width = 0;
|
|
62
|
-
for (const ch of text) {
|
|
63
|
-
width += charWidth(ch);
|
|
64
|
-
if (width > max) break;
|
|
65
|
-
}
|
|
66
|
-
if (width <= max) return text.padEnd(text.length + (max - width));
|
|
67
|
-
if (max === 1) return '…';
|
|
68
|
-
let result = '';
|
|
69
|
-
let used = 0;
|
|
70
|
-
for (const ch of text) {
|
|
71
|
-
const cw = charWidth(ch);
|
|
72
|
-
if (used + cw > max - 1) break;
|
|
73
|
-
result += ch;
|
|
74
|
-
used += cw;
|
|
75
|
-
}
|
|
76
|
-
const truncated = `${result}…`;
|
|
77
|
-
return truncated.padEnd(truncated.length + Math.max(0, max - (used + 1)));
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// Build a key→cmd lookup from ACTIONS for O(1) dispatch
|
|
81
|
-
const KEY_TO_CMD: Record<string, string> = {};
|
|
82
|
-
for (const a of ACTIONS) KEY_TO_CMD[a.key] = a.cmd;
|
|
83
|
-
|
|
84
|
-
export function Sidebar({ health, background, onAction, activeView, isFocused = false }: SidebarProps): React.ReactElement {
|
|
85
|
-
// ── Interactive navigation: Sidebar owns shortcut key dispatch ──
|
|
86
|
-
useInput((ch, key) => {
|
|
87
|
-
// Esc: return home from any secondary view
|
|
88
|
-
if (key.escape && activeView !== 'home') {
|
|
89
|
-
onAction('/home');
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
const cmd = KEY_TO_CMD[ch];
|
|
93
|
-
if (cmd) {
|
|
94
|
-
onAction(cmd);
|
|
95
|
-
}
|
|
96
|
-
}, { isActive: isFocused });
|
|
97
|
-
|
|
98
|
-
// Map view types to sidebar action commands for highlight
|
|
99
|
-
const activeCmd = ACTIONS.find(a => {
|
|
100
|
-
const viewMap: Record<string, string> = {
|
|
101
|
-
'/search': 'search', '/recent': 'recent', '/doctor': 'doctor',
|
|
102
|
-
'/background': 'background', '/dashboard': 'dashboard',
|
|
103
|
-
'/project': 'project', '/configure': 'configure',
|
|
104
|
-
'/integrate': 'integrate', '/home': 'home',
|
|
105
|
-
};
|
|
106
|
-
return viewMap[a.cmd] === activeView;
|
|
107
|
-
})?.cmd;
|
|
108
|
-
|
|
109
|
-
return (
|
|
110
|
-
<Box
|
|
111
|
-
flexDirection="column"
|
|
112
|
-
width={SIDEBAR_WIDTH}
|
|
113
|
-
borderStyle="single"
|
|
114
|
-
borderColor={COLORS.border}
|
|
115
|
-
paddingX={1}
|
|
116
|
-
>
|
|
117
|
-
<Box flexDirection="column" marginBottom={1}>
|
|
118
|
-
<Text color={COLORS.brand} bold>Quick Actions</Text>
|
|
119
|
-
<Text color={COLORS.border}>{separator(INNER_WIDTH)}</Text>
|
|
120
|
-
{ACTIONS.map((action) => {
|
|
121
|
-
const isActive = action.cmd === activeCmd;
|
|
122
|
-
return (
|
|
123
|
-
<Box key={action.key}>
|
|
124
|
-
<Text color={isActive ? COLORS.brand : COLORS.muted}>
|
|
125
|
-
{`${isActive ? SYMBOLS.arrow : action.key} `}
|
|
126
|
-
</Text>
|
|
127
|
-
<Text color={isActive ? COLORS.accent : COLORS.text} bold={isActive}>{fit(action.label, ACTION_LABEL_WIDTH)}</Text>
|
|
128
|
-
</Box>
|
|
129
|
-
);
|
|
130
|
-
})}
|
|
131
|
-
</Box>
|
|
132
|
-
|
|
133
|
-
<Box flexDirection="column">
|
|
134
|
-
<Text color={COLORS.brand} bold>Health</Text>
|
|
135
|
-
<Text color={COLORS.border}>{separator(INNER_WIDTH)}</Text>
|
|
136
|
-
|
|
137
|
-
<Box>
|
|
138
|
-
<Text color={COLORS.muted}>{'Embed'.padEnd(8)}</Text>
|
|
139
|
-
<Text color={
|
|
140
|
-
health.embeddingProvider === 'ready' ? COLORS.success
|
|
141
|
-
: health.embeddingProvider === 'unavailable' ? COLORS.warning
|
|
142
|
-
: COLORS.muted
|
|
143
|
-
}>
|
|
144
|
-
{fit(`${health.embeddingProvider === 'ready' ? STATUS_DOTS.ok : health.embeddingProvider === 'unavailable' ? STATUS_DOTS.warn : STATUS_DOTS.off} ${health.embeddingLabel}`, VALUE_WIDTH)}
|
|
145
|
-
</Text>
|
|
146
|
-
</Box>
|
|
147
|
-
<Box>
|
|
148
|
-
<Text color={COLORS.muted}>{'Search'.padEnd(8)}</Text>
|
|
149
|
-
<Text color={colorForMode(health.searchModeLabel)}>{fit(`${STATUS_DOTS.ok} ${health.searchModeLabel}`, VALUE_WIDTH)}</Text>
|
|
150
|
-
</Box>
|
|
151
|
-
<Box>
|
|
152
|
-
<Text color={COLORS.muted}>{'Sess'.padEnd(8)}</Text>
|
|
153
|
-
<Text color={COLORS.text}>{fit(`${health.sessions}`, VALUE_WIDTH)}</Text>
|
|
154
|
-
</Box>
|
|
155
|
-
|
|
156
|
-
<Box marginTop={1} flexDirection="column">
|
|
157
|
-
<Text color={COLORS.brand} bold>Background</Text>
|
|
158
|
-
<Text color={COLORS.border}>{separator(INNER_WIDTH)}</Text>
|
|
159
|
-
<Box>
|
|
160
|
-
<Text color={COLORS.muted}>{'Status'.padEnd(8)}</Text>
|
|
161
|
-
<Text color={background.healthy ? COLORS.success : background.running ? COLORS.warning : COLORS.muted}>
|
|
162
|
-
{fit(`${background.healthy ? STATUS_DOTS.running : background.running ? STATUS_DOTS.warn : STATUS_DOTS.stopped} ${background.healthy ? 'Running' : background.running ? 'Warning' : 'Stopped'}`, VALUE_WIDTH)}
|
|
163
|
-
</Text>
|
|
164
|
-
</Box>
|
|
165
|
-
{background.port && (
|
|
166
|
-
<Box>
|
|
167
|
-
<Text color={COLORS.muted}>{'Port'.padEnd(8)}</Text>
|
|
168
|
-
<Text color={COLORS.text}>{fit(`${background.port}`, VALUE_WIDTH)}</Text>
|
|
169
|
-
</Box>
|
|
170
|
-
)}
|
|
171
|
-
</Box>
|
|
172
|
-
</Box>
|
|
173
|
-
|
|
174
|
-
<Box marginTop={1}>
|
|
175
|
-
<Text color={COLORS.muted} italic>Keys or /cmd</Text>
|
|
176
|
-
</Box>
|
|
177
|
-
</Box>
|
|
178
|
-
);
|
|
179
|
-
}
|
|
@@ -1,261 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Interactive Commands — @clack/prompts flows that run outside of Ink
|
|
3
|
-
*
|
|
4
|
-
* These commands use interactive prompts (select, text, confirm) that
|
|
5
|
-
* require normal stdin/stdout. They're called when the user exits the
|
|
6
|
-
* Ink TUI temporarily.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import * as p from '@clack/prompts';
|
|
10
|
-
import * as fs from 'node:fs';
|
|
11
|
-
|
|
12
|
-
// ── Configure ──────────────────────────────────────────────────
|
|
13
|
-
|
|
14
|
-
export async function runConfigureInline(): Promise<void> {
|
|
15
|
-
const configPath = `${process.env.HOME || process.env.USERPROFILE}/.memorix/config.json`;
|
|
16
|
-
|
|
17
|
-
const loadConfig = () => {
|
|
18
|
-
try {
|
|
19
|
-
if (fs.existsSync(configPath)) {
|
|
20
|
-
return JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
21
|
-
}
|
|
22
|
-
} catch { /* ignore */ }
|
|
23
|
-
return {};
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
const saveConfig = (config: Record<string, unknown>) => {
|
|
27
|
-
const dir = `${process.env.HOME || process.env.USERPROFILE}/.memorix`;
|
|
28
|
-
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
29
|
-
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
while (true) {
|
|
33
|
-
const config = loadConfig();
|
|
34
|
-
|
|
35
|
-
const section = await p.select({
|
|
36
|
-
message: 'What would you like to configure?',
|
|
37
|
-
options: [
|
|
38
|
-
{ value: 'llm', label: 'LLM Enhanced Mode', hint: 'smart dedup + fact extraction' },
|
|
39
|
-
{ value: 'embedding', label: 'Embedding Provider', hint: 'semantic search' },
|
|
40
|
-
{ value: 'behavior', label: 'Behavior Settings', hint: 'session inject, auto-cleanup' },
|
|
41
|
-
{ value: 'show', label: 'Show current config', hint: 'view settings' },
|
|
42
|
-
{ value: 'back', label: '← Back', hint: 'return to workbench' },
|
|
43
|
-
],
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
if (p.isCancel(section) || section === 'back') return;
|
|
47
|
-
|
|
48
|
-
if (section === 'show') {
|
|
49
|
-
console.log('\nCurrent configuration:');
|
|
50
|
-
console.log(` Config file: ${configPath}`);
|
|
51
|
-
console.log(` LLM Provider: ${config.llm?.provider ?? 'not configured'}`);
|
|
52
|
-
console.log(` LLM Model: ${config.llm?.model ?? 'default'}`);
|
|
53
|
-
console.log(` Embedding: ${config.embedding ?? 'off (BM25 only)'}`);
|
|
54
|
-
console.log('');
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (section === 'llm') {
|
|
59
|
-
const provider = await p.select({
|
|
60
|
-
message: 'Select LLM provider:',
|
|
61
|
-
options: [
|
|
62
|
-
{ value: 'openai', label: 'OpenAI', hint: 'gpt-4o-mini recommended' },
|
|
63
|
-
{ value: 'anthropic', label: 'Anthropic', hint: 'claude-3-haiku' },
|
|
64
|
-
{ value: 'openrouter', label: 'OpenRouter', hint: 'multi-provider' },
|
|
65
|
-
{ value: 'custom', label: 'Custom endpoint', hint: 'OpenAI-compatible' },
|
|
66
|
-
{ value: 'disable', label: 'Disable LLM', hint: 'free heuristic mode' },
|
|
67
|
-
],
|
|
68
|
-
});
|
|
69
|
-
if (p.isCancel(provider)) continue;
|
|
70
|
-
|
|
71
|
-
if (provider === 'disable') {
|
|
72
|
-
config.llm = undefined;
|
|
73
|
-
saveConfig(config);
|
|
74
|
-
p.log.success('LLM mode disabled.');
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const apiKey = await p.password({ message: 'Enter API key:' });
|
|
79
|
-
if (p.isCancel(apiKey) || !apiKey) continue;
|
|
80
|
-
|
|
81
|
-
let defaultModel = 'gpt-4o-mini';
|
|
82
|
-
if (provider === 'anthropic') defaultModel = 'claude-3-haiku-20240307';
|
|
83
|
-
|
|
84
|
-
let baseUrl: string | undefined;
|
|
85
|
-
if (provider === 'custom') {
|
|
86
|
-
const url = await p.text({
|
|
87
|
-
message: 'Base URL (OpenAI-compatible):',
|
|
88
|
-
placeholder: 'http://localhost:11434/v1',
|
|
89
|
-
});
|
|
90
|
-
if (p.isCancel(url)) continue;
|
|
91
|
-
if (url) baseUrl = url;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const customModel = await p.text({
|
|
95
|
-
message: 'Model name:',
|
|
96
|
-
placeholder: defaultModel,
|
|
97
|
-
defaultValue: defaultModel,
|
|
98
|
-
});
|
|
99
|
-
if (p.isCancel(customModel)) continue;
|
|
100
|
-
|
|
101
|
-
config.llm = {
|
|
102
|
-
provider: provider === 'custom' ? 'openai' : provider,
|
|
103
|
-
apiKey,
|
|
104
|
-
model: customModel || defaultModel,
|
|
105
|
-
baseUrl,
|
|
106
|
-
};
|
|
107
|
-
saveConfig(config);
|
|
108
|
-
p.log.success(`LLM configured: ${config.llm.model}`);
|
|
109
|
-
continue;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
if (section === 'embedding') {
|
|
113
|
-
const embedding = await p.select({
|
|
114
|
-
message: 'Select embedding provider:',
|
|
115
|
-
options: [
|
|
116
|
-
{ value: 'off', label: 'Off (default)', hint: 'BM25 fulltext only' },
|
|
117
|
-
{ value: 'api', label: 'API (recommended)', hint: 'OpenAI-compatible' },
|
|
118
|
-
{ value: 'fastembed', label: 'FastEmbed', hint: 'local ONNX' },
|
|
119
|
-
{ value: 'transformers', label: 'Transformers', hint: 'local JS/WASM' },
|
|
120
|
-
],
|
|
121
|
-
});
|
|
122
|
-
if (p.isCancel(embedding)) continue;
|
|
123
|
-
|
|
124
|
-
if (embedding === 'api') {
|
|
125
|
-
const apiKey = await p.password({ message: 'Embedding API key (empty = BM25 fallback):' });
|
|
126
|
-
if (p.isCancel(apiKey)) continue;
|
|
127
|
-
|
|
128
|
-
const baseUrl = await p.text({
|
|
129
|
-
message: 'Base URL:',
|
|
130
|
-
placeholder: 'https://api.openai.com/v1',
|
|
131
|
-
defaultValue: '',
|
|
132
|
-
});
|
|
133
|
-
if (p.isCancel(baseUrl)) continue;
|
|
134
|
-
|
|
135
|
-
const model = await p.select({
|
|
136
|
-
message: 'Embedding model:',
|
|
137
|
-
options: [
|
|
138
|
-
{ value: 'text-embedding-3-small', label: 'text-embedding-3-small' },
|
|
139
|
-
{ value: 'text-embedding-3-large', label: 'text-embedding-3-large' },
|
|
140
|
-
{ value: 'custom', label: 'Custom model' },
|
|
141
|
-
],
|
|
142
|
-
});
|
|
143
|
-
if (p.isCancel(model)) continue;
|
|
144
|
-
|
|
145
|
-
let modelName: string = model;
|
|
146
|
-
if (model === 'custom') {
|
|
147
|
-
const name = await p.text({ message: 'Model name:', placeholder: 'BAAI/bge-m3' });
|
|
148
|
-
if (p.isCancel(name) || !name) continue;
|
|
149
|
-
modelName = name;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
config.embedding = 'api';
|
|
153
|
-
config.embeddingApi = {
|
|
154
|
-
apiKey: apiKey || undefined,
|
|
155
|
-
baseUrl: baseUrl || undefined,
|
|
156
|
-
model: modelName,
|
|
157
|
-
};
|
|
158
|
-
saveConfig(config);
|
|
159
|
-
p.log.success(`API embedding configured: ${modelName}`);
|
|
160
|
-
continue;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
config.embedding = embedding;
|
|
164
|
-
delete config.embeddingApi;
|
|
165
|
-
saveConfig(config);
|
|
166
|
-
p.log.success(`Embedding set to: ${embedding}`);
|
|
167
|
-
continue;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
if (section === 'behavior') {
|
|
171
|
-
const current = config.behavior ?? {};
|
|
172
|
-
const sessionInject = await p.select({
|
|
173
|
-
message: `Session injection (current: ${current.sessionInject ?? 'minimal'})`,
|
|
174
|
-
options: [
|
|
175
|
-
{ value: 'full', label: 'Full' },
|
|
176
|
-
{ value: 'minimal', label: 'Minimal (default)' },
|
|
177
|
-
{ value: 'silent', label: 'Silent' },
|
|
178
|
-
],
|
|
179
|
-
});
|
|
180
|
-
if (p.isCancel(sessionInject)) continue;
|
|
181
|
-
|
|
182
|
-
config.behavior = { ...current, sessionInject };
|
|
183
|
-
saveConfig(config);
|
|
184
|
-
p.log.success('Behavior settings saved.');
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// ── Cleanup ────────────────────────────────────────────────────
|
|
190
|
-
|
|
191
|
-
export async function runCleanupInline(): Promise<void> {
|
|
192
|
-
const action = await p.select({
|
|
193
|
-
message: 'Cleanup options:',
|
|
194
|
-
options: [
|
|
195
|
-
{ value: 'project-artifacts', label: 'Uninstall project artifacts', hint: 'remove hook files' },
|
|
196
|
-
{ value: 'project-memory', label: 'Purge project memory', hint: 'delete current project memories' },
|
|
197
|
-
{ value: 'all-memory', label: 'Purge all memory', hint: '[WARN] delete ALL memories' },
|
|
198
|
-
{ value: 'back', label: '← Back', hint: 'return to workbench' },
|
|
199
|
-
],
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
if (p.isCancel(action) || action === 'back') return;
|
|
203
|
-
|
|
204
|
-
switch (action) {
|
|
205
|
-
case 'project-artifacts': {
|
|
206
|
-
const m = await import('../commands/uninstall-project-artifacts.js');
|
|
207
|
-
await m.default.run?.({ args: { _: [] }, rawArgs: [], cmd: m.default } as any);
|
|
208
|
-
break;
|
|
209
|
-
}
|
|
210
|
-
case 'project-memory': {
|
|
211
|
-
const m = await import('../commands/purge-project-memory.js');
|
|
212
|
-
await m.default.run?.({ args: { _: [] }, rawArgs: [], cmd: m.default } as any);
|
|
213
|
-
break;
|
|
214
|
-
}
|
|
215
|
-
case 'all-memory': {
|
|
216
|
-
const m = await import('../commands/purge-all-memory.js');
|
|
217
|
-
await m.default.run?.({ args: { _: [] }, rawArgs: [], cmd: m.default } as any);
|
|
218
|
-
break;
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// ── Ingest ─────────────────────────────────────────────────────
|
|
224
|
-
|
|
225
|
-
export async function runIngestInline(): Promise<void> {
|
|
226
|
-
const action = await p.select({
|
|
227
|
-
message: 'Git → Memory:',
|
|
228
|
-
options: [
|
|
229
|
-
{ value: 'commit', label: 'Ingest commit', hint: 'single commit → memory' },
|
|
230
|
-
{ value: 'log', label: 'Ingest log', hint: 'batch recent commits → memories' },
|
|
231
|
-
{ value: 'git-hook', label: 'Install git hook', hint: 'auto-capture on every commit' },
|
|
232
|
-
{ value: 'git-hook-uninstall', label: 'Uninstall git hook' },
|
|
233
|
-
{ value: 'back', label: '← Back', hint: 'return to workbench' },
|
|
234
|
-
],
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
if (p.isCancel(action) || action === 'back') return;
|
|
238
|
-
|
|
239
|
-
switch (action) {
|
|
240
|
-
case 'commit': {
|
|
241
|
-
const m = await import('../commands/ingest-commit.js');
|
|
242
|
-
await m.default.run?.({ args: { _: [] }, rawArgs: [], cmd: m.default } as any);
|
|
243
|
-
break;
|
|
244
|
-
}
|
|
245
|
-
case 'log': {
|
|
246
|
-
const m = await import('../commands/ingest-log.js');
|
|
247
|
-
await m.default.run?.({ args: { _: [] }, rawArgs: [], cmd: m.default } as any);
|
|
248
|
-
break;
|
|
249
|
-
}
|
|
250
|
-
case 'git-hook': {
|
|
251
|
-
const m = await import('../commands/git-hook-install.js');
|
|
252
|
-
await m.default.run?.({ args: { _: [] }, rawArgs: [], cmd: m.default } as any);
|
|
253
|
-
break;
|
|
254
|
-
}
|
|
255
|
-
case 'git-hook-uninstall': {
|
|
256
|
-
const m = await import('../commands/git-hook-uninstall.js');
|
|
257
|
-
await m.default.run?.({ args: { _: [] }, rawArgs: [], cmd: m.default } as any);
|
|
258
|
-
break;
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
}
|