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/src/cli/index.ts
CHANGED
|
@@ -1,406 +1,406 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Memorix CLI
|
|
3
|
-
*
|
|
4
|
-
* Command-line interface for Memorix management.
|
|
5
|
-
* Built with: citty (1.1K stars, zero-deps) + @clack/prompts (7.4K stars)
|
|
6
|
-
*
|
|
7
|
-
* Commands:
|
|
8
|
-
* memorix — Enter memcode TUI (native coding agent)
|
|
9
|
-
* memorix memcode — Enter memcode TUI (explicit)
|
|
10
|
-
* memorix ask "q" — Single-shot chat question (pipe: echo "q" | memorix ask)
|
|
11
|
-
* memorix serve — Start MCP Server on stdio
|
|
12
|
-
* memorix status — Show project info + rules sync status
|
|
13
|
-
* memorix sync — Interactive cross-agent rule sync
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import { defineCommand, runMain } from 'citty';
|
|
17
|
-
import * as p from '@clack/prompts';
|
|
18
|
-
import { getCliVersion } from './version.js';
|
|
19
|
-
import { importBundledMemcode } from './memcode-bootstrap.js';
|
|
20
|
-
import { installCliPipeErrorGuard } from './pipe-errors.js';
|
|
21
|
-
import { normalizeCliInvocation } from './invocation.js';
|
|
22
|
-
import { printCliGuideForHelp, renderCliGuide } from './command-guide.js';
|
|
23
|
-
|
|
24
|
-
installCliPipeErrorGuard();
|
|
25
|
-
|
|
26
|
-
const NO_GIT_MSG = 'Memorix requires a git repo to establish project identity. Run `git init` in this workspace first.';
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Set PI_PACKAGE_DIR so bundled memcode can find its theme files.
|
|
30
|
-
* When tsup bundles memcode into the CLI, __dirname points to dist/cli/,
|
|
31
|
-
* not packages/memcode/. This env var tells config.ts where to look.
|
|
32
|
-
*
|
|
33
|
-
* In dev: resolves to packages/memcode/
|
|
34
|
-
* In global npm install: packages/memcode/ won't exist; theme files
|
|
35
|
-
* should be copied to dist/memcode/ by the build (see tsup onSuccess).
|
|
36
|
-
*/
|
|
37
|
-
function ensureMemcodePackageDir(): void {
|
|
38
|
-
if (process.env.MEMCODE_PACKAGE_DIR) return;
|
|
39
|
-
// Walk up from __dirname (dist/cli/) to find packages/memcode/package.json
|
|
40
|
-
const path = require('node:path') as typeof import('node:path');
|
|
41
|
-
const fs = require('node:fs') as typeof import('node:fs');
|
|
42
|
-
let dir: string = __dirname;
|
|
43
|
-
for (let i = 0; i < 5; i++) {
|
|
44
|
-
const candidate = path.join(dir, 'packages', 'memcode');
|
|
45
|
-
if (fs.existsSync(path.join(candidate, 'package.json'))) {
|
|
46
|
-
process.env.MEMCODE_PACKAGE_DIR = candidate;
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
const parent = path.dirname(dir);
|
|
50
|
-
if (parent === dir) break;
|
|
51
|
-
dir = parent;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const DIM = '\x1b[2m';
|
|
56
|
-
const RESET = '\x1b[0m';
|
|
57
|
-
const CYAN = '\x1b[36m';
|
|
58
|
-
const YELLOW = '\x1b[33m';
|
|
59
|
-
|
|
60
|
-
async function runAsk(question: string): Promise<void> {
|
|
61
|
-
// Use a smooth dots-style spinner (same frames as TUI ink-spinner "dots")
|
|
62
|
-
// instead of @clack's ASCII spinner which flickers in non-Ink terminals.
|
|
63
|
-
const dotsFrames = ['⠋','⠙','⠹','⠸','⠼','⠴','⠦','⠧','⠇','⠏'];
|
|
64
|
-
let frameIdx = 0;
|
|
65
|
-
let interval: ReturnType<typeof setInterval> | null = null;
|
|
66
|
-
|
|
67
|
-
const startSmoothSpinner = (msg: string) => {
|
|
68
|
-
if (process.stdout.isTTY) {
|
|
69
|
-
process.stdout.write(`${CYAN}${dotsFrames[0]}${RESET} ${msg}`);
|
|
70
|
-
interval = setInterval(() => {
|
|
71
|
-
frameIdx = (frameIdx + 1) % dotsFrames.length;
|
|
72
|
-
process.stdout.write(`\r${CYAN}${dotsFrames[frameIdx]}${RESET} ${msg}`);
|
|
73
|
-
}, 80);
|
|
74
|
-
} else {
|
|
75
|
-
process.stderr.write(`${msg}...\n`);
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
const stopSmoothSpinner = () => {
|
|
80
|
-
if (interval) { clearInterval(interval); interval = null; }
|
|
81
|
-
if (process.stdout.isTTY) {
|
|
82
|
-
process.stdout.write('\r' + ' '.repeat(40) + '\r'); // clear line
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
startSmoothSpinner('Thinking…');
|
|
87
|
-
|
|
88
|
-
try {
|
|
89
|
-
const { askMemoryQuestion } = await import('./tui/chat-service.js');
|
|
90
|
-
const result = await askMemoryQuestion(question);
|
|
91
|
-
|
|
92
|
-
stopSmoothSpinner();
|
|
93
|
-
|
|
94
|
-
// Output the answer
|
|
95
|
-
console.log('');
|
|
96
|
-
console.log(result.answer);
|
|
97
|
-
|
|
98
|
-
// Show sources if any
|
|
99
|
-
if (result.sources.length > 0) {
|
|
100
|
-
console.log('');
|
|
101
|
-
console.log(`${DIM}Sources:${RESET}`);
|
|
102
|
-
for (const src of result.sources.slice(0, 5)) {
|
|
103
|
-
console.log(` ${DIM}[obs:${src.id}]${RESET} ${src.title}`);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Show warnings
|
|
108
|
-
if (result.warning) {
|
|
109
|
-
console.log('');
|
|
110
|
-
console.log(`${YELLOW}[WARN] ${result.warning}${RESET}`);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Metadata footer
|
|
114
|
-
const meta: string[] = [];
|
|
115
|
-
if (result.usedLLM && result.llmModel) meta.push(result.llmModel);
|
|
116
|
-
if (result.searchMode) meta.push(result.searchMode);
|
|
117
|
-
if (result.toolCallsCount) meta.push(`${result.toolCallsCount} tool call${result.toolCallsCount > 1 ? 's' : ''}`);
|
|
118
|
-
if (meta.length > 0) {
|
|
119
|
-
console.log(`${DIM} ${meta.join(' · ')}${RESET}`);
|
|
120
|
-
}
|
|
121
|
-
} catch (err) {
|
|
122
|
-
stopSmoothSpinner();
|
|
123
|
-
p.log.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
124
|
-
process.exitCode = 1;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// ============================================================
|
|
129
|
-
// Main command
|
|
130
|
-
// ============================================================
|
|
131
|
-
|
|
132
|
-
async function runMemoryShortcut(action: string, args: Record<string, unknown>): Promise<void> {
|
|
133
|
-
const { detectProject } = await import('../project/detector.js');
|
|
134
|
-
if (!detectProject(process.cwd())) {
|
|
135
|
-
console.log(NO_GIT_MSG);
|
|
136
|
-
process.exitCode = 1;
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
const memory = await import('./commands/memory.js');
|
|
140
|
-
await memory.default.run?.({
|
|
141
|
-
args: { ...args, _: [action] },
|
|
142
|
-
rawArgs: [],
|
|
143
|
-
cmd: memory.default,
|
|
144
|
-
} as any);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const main = defineCommand({
|
|
148
|
-
meta: {
|
|
149
|
-
name: 'memorix',
|
|
150
|
-
version: getCliVersion(),
|
|
151
|
-
description: 'Local-first memory control plane for AI coding agents through CLI, MCP, and local workflows',
|
|
152
|
-
},
|
|
153
|
-
subCommands: {
|
|
154
|
-
// One-shot product commands (primary user paths)
|
|
155
|
-
ask: () => Promise.resolve(defineCommand({
|
|
156
|
-
meta: { name: 'ask', description: 'Ask Memorix a question (single-shot chat). Pipe: echo "q" | memorix ask' },
|
|
157
|
-
args: {
|
|
158
|
-
question: { type: 'positional', description: 'Question to ask (or pipe via stdin)', required: false },
|
|
159
|
-
},
|
|
160
|
-
async run({ args }) {
|
|
161
|
-
let q = (args.question as string) || '';
|
|
162
|
-
// Read from stdin if no positional arg and stdin is piped
|
|
163
|
-
if (!q && !process.stdin.isTTY) {
|
|
164
|
-
q = await new Promise<string>((resolve) => {
|
|
165
|
-
let data = '';
|
|
166
|
-
process.stdin.setEncoding('utf-8');
|
|
167
|
-
process.stdin.on('data', (chunk) => { data += chunk; });
|
|
168
|
-
process.stdin.on('end', () => resolve(data.trim()));
|
|
169
|
-
process.stdin.on('error', () => resolve(''));
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
if (!q) {
|
|
173
|
-
p.log.error('No question provided. Usage: memorix ask "your question" or echo "q" | memorix ask');
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
await runAsk(q);
|
|
177
|
-
},
|
|
178
|
-
})),
|
|
179
|
-
search: () => Promise.resolve(defineCommand({
|
|
180
|
-
meta: { name: 'search', description: 'Shortcut for `memorix memory search`' },
|
|
181
|
-
args: {
|
|
182
|
-
query: { type: 'positional', description: 'Search query', required: true },
|
|
183
|
-
limit: { type: 'string', description: 'Maximum results' },
|
|
184
|
-
quality: { type: 'string', description: 'Retrieval profile: fast, balanced (default), or thorough' },
|
|
185
|
-
json: { type: 'boolean', description: 'Emit machine-readable JSON output' },
|
|
186
|
-
},
|
|
187
|
-
async run({ args }) {
|
|
188
|
-
await runMemoryShortcut('search', {
|
|
189
|
-
query: args.query,
|
|
190
|
-
limit: args.limit,
|
|
191
|
-
quality: args.quality,
|
|
192
|
-
json: args.json,
|
|
193
|
-
});
|
|
194
|
-
},
|
|
195
|
-
})),
|
|
196
|
-
remember: () => Promise.resolve(defineCommand({
|
|
197
|
-
meta: { name: 'remember', description: 'Shortcut for `memorix memory store`' },
|
|
198
|
-
args: {
|
|
199
|
-
text: { type: 'positional', description: 'Text to remember', required: true },
|
|
200
|
-
title: { type: 'string', description: 'Optional observation title' },
|
|
201
|
-
type: { type: 'string', description: 'Observation type' },
|
|
202
|
-
visibility: { type: 'string', description: 'project (default), personal, or team' },
|
|
203
|
-
json: { type: 'boolean', description: 'Emit machine-readable JSON output' },
|
|
204
|
-
},
|
|
205
|
-
async run({ args }) {
|
|
206
|
-
await runMemoryShortcut('store', {
|
|
207
|
-
text: args.text,
|
|
208
|
-
title: args.title,
|
|
209
|
-
type: args.type,
|
|
210
|
-
visibility: args.visibility,
|
|
211
|
-
json: args.json,
|
|
212
|
-
});
|
|
213
|
-
},
|
|
214
|
-
})),
|
|
215
|
-
recent: () => Promise.resolve(defineCommand({
|
|
216
|
-
meta: { name: 'recent', description: 'Shortcut for `memorix memory recent`' },
|
|
217
|
-
args: {
|
|
218
|
-
limit: { type: 'string', description: 'Maximum results' },
|
|
219
|
-
json: { type: 'boolean', description: 'Emit machine-readable JSON output' },
|
|
220
|
-
},
|
|
221
|
-
async run({ args }) { await runMemoryShortcut('recent', { limit: args.limit, json: args.json }); },
|
|
222
|
-
})),
|
|
223
|
-
help: () => Promise.resolve(defineCommand({
|
|
224
|
-
meta: { name: 'help', description: 'Show action-oriented help for a command group' },
|
|
225
|
-
args: {
|
|
226
|
-
command: { type: 'positional', description: 'Command group to inspect', required: false },
|
|
227
|
-
},
|
|
228
|
-
async run({ args }) {
|
|
229
|
-
console.log(renderCliGuide(args.command as string | undefined));
|
|
230
|
-
},
|
|
231
|
-
})),
|
|
232
|
-
// Infrastructure commands
|
|
233
|
-
init: () => import('./commands/init.js').then(m => m.default),
|
|
234
|
-
setup: () => import('./commands/setup.js').then(m => m.default),
|
|
235
|
-
config: () => Promise.resolve(defineCommand({
|
|
236
|
-
meta: { name: 'config', description: 'Inspect Memorix TOML configuration' },
|
|
237
|
-
subCommands: {
|
|
238
|
-
path: () => import('./commands/config-path.js').then(m => m.default),
|
|
239
|
-
get: () => import('./commands/config-get.js').then(m => m.default),
|
|
240
|
-
migrate: () => import('./commands/config-migrate.js').then(m => m.default),
|
|
241
|
-
},
|
|
242
|
-
})),
|
|
243
|
-
integrate: () => import('./commands/integrate.js').then(m => m.default),
|
|
244
|
-
memory: () => import('./commands/memory.js').then(m => m.default),
|
|
245
|
-
context: () => import('./commands/context.js').then(m => m.default),
|
|
246
|
-
resume: () => import('./commands/resume.js').then(m => m.default),
|
|
247
|
-
explain: () => import('./commands/explain.js').then(m => m.default),
|
|
248
|
-
codegraph: () => import('./commands/codegraph.js').then(m => m.default),
|
|
249
|
-
checkpoint: () => import('./commands/checkpoint.js').then(m => m.default),
|
|
250
|
-
knowledge: () => import('./commands/knowledge.js').then(m => m.default),
|
|
251
|
-
reasoning: () => import('./commands/reasoning.js').then(m => m.default),
|
|
252
|
-
retention: () => import('./commands/retention.js').then(m => m.default),
|
|
253
|
-
formation: () => import('./commands/formation.js').then(m => m.default),
|
|
254
|
-
audit: () => import('./commands/audit.js').then(m => m.default),
|
|
255
|
-
transfer: () => import('./commands/transfer.js').then(m => m.default),
|
|
256
|
-
skills: () => import('./commands/skills.js').then(m => m.default),
|
|
257
|
-
identity: () => import('./commands/identity.js').then(m => m.default),
|
|
258
|
-
session: () => import('./commands/session.js').then(m => m.default),
|
|
259
|
-
team: () => import('./commands/team.js').then(m => m.default),
|
|
260
|
-
task: () => import('./commands/task.js').then(m => m.default),
|
|
261
|
-
message: () => import('./commands/message.js').then(m => m.default),
|
|
262
|
-
lock: () => import('./commands/lock.js').then(m => m.default),
|
|
263
|
-
handoff: () => import('./commands/handoff.js').then(m => m.default),
|
|
264
|
-
poll: () => import('./commands/poll.js').then(m => m.default),
|
|
265
|
-
receipt: () => import('./commands/receipt.js').then(m => m.default),
|
|
266
|
-
serve: () => import('./commands/serve.js').then(m => m.default),
|
|
267
|
-
'serve-http': () => import('./commands/serve-http.js').then(m => m.default),
|
|
268
|
-
status: () => import('./commands/status.js').then(m => m.default),
|
|
269
|
-
sync: () => import('./commands/sync.js').then(m => m.default),
|
|
270
|
-
hook: () => import('./commands/hook.js').then(m => m.default),
|
|
271
|
-
hooks: () => import('./commands/hooks.js').then(m => m.default),
|
|
272
|
-
ingest: () => import('./commands/ingest.js').then(m => m.default),
|
|
273
|
-
media: () => import('./commands/media.js').then(m => m.default),
|
|
274
|
-
'git-hook': () => import('./commands/git-hook-install.js').then(m => m.default),
|
|
275
|
-
'git-hook-uninstall': () => import('./commands/git-hook-uninstall.js').then(m => m.default),
|
|
276
|
-
background: () => import('./commands/background.js').then(m => m.default),
|
|
277
|
-
bg: () => import('./commands/background.js').then(m => m.default),
|
|
278
|
-
bs: () => Promise.resolve(defineCommand({
|
|
279
|
-
meta: { name: 'bs', description: 'Shortcut: background start' },
|
|
280
|
-
args: { port: { type: 'string', description: 'HTTP port (default: 3211)', required: false } },
|
|
281
|
-
async run({ args }) {
|
|
282
|
-
// Directly invoke background start instead of going through citty's CommandContext
|
|
283
|
-
const port = parseInt((args.port as string) || '3211', 10);
|
|
284
|
-
const { doStart } = await import('./commands/background.js');
|
|
285
|
-
await doStart(port);
|
|
286
|
-
},
|
|
287
|
-
})),
|
|
288
|
-
doctor: () => import('./commands/doctor.js').then(m => m.default),
|
|
289
|
-
repair: () => import('./commands/repair.js').then(m => m.default),
|
|
290
|
-
dashboard: () => import('./commands/dashboard.js').then(m => m.default),
|
|
291
|
-
cleanup: () => import('./commands/cleanup.js').then(m => m.default),
|
|
292
|
-
purge: () => import('./commands/purge.js').then(m => m.default),
|
|
293
|
-
uninstall: () => import('./commands/uninstall.js').then(m => m.default),
|
|
294
|
-
orchestrate: () => import('./commands/orchestrate.js').then(m => m.default),
|
|
295
|
-
workbench: () => Promise.resolve(defineCommand({
|
|
296
|
-
meta: { name: 'workbench', description: 'Open the interactive terminal memory control plane' },
|
|
297
|
-
async run() {
|
|
298
|
-
const { startWorkbench } = await import('./workbench.js');
|
|
299
|
-
await startWorkbench();
|
|
300
|
-
},
|
|
301
|
-
})),
|
|
302
|
-
memcode: () => Promise.resolve(defineCommand({
|
|
303
|
-
meta: { name: 'memcode', description: 'Enter memcode TUI — native coding agent with memory' },
|
|
304
|
-
async run() {
|
|
305
|
-
try {
|
|
306
|
-
const { runCli } = await importBundledMemcode();
|
|
307
|
-
await runCli(process.argv.slice(3));
|
|
308
|
-
} catch (err) {
|
|
309
|
-
console.error('Failed to start memcode:', err instanceof Error ? err.message : err);
|
|
310
|
-
process.exit(1);
|
|
311
|
-
}
|
|
312
|
-
},
|
|
313
|
-
})),
|
|
314
|
-
},
|
|
315
|
-
async run() {
|
|
316
|
-
// Guard: if citty already resolved a subcommand, its run() was called before this.
|
|
317
|
-
// Detect by checking if the first CLI arg matches a registered subcommand name.
|
|
318
|
-
const firstArg = process.argv[2];
|
|
319
|
-
const knownSubs = ['ask', 'search', 'remember', 'recent', 'help', 'workbench', 'memcode', 'config',
|
|
320
|
-
'init', 'setup', 'integrate', 'memory', 'context', 'resume', 'explain', 'codegraph', 'checkpoint', 'knowledge', 'reasoning', 'retention', 'formation', 'audit', 'transfer', 'skills', 'identity',
|
|
321
|
-
'session', 'team', 'task', 'message', 'lock', 'handoff', 'poll',
|
|
322
|
-
'receipt',
|
|
323
|
-
'serve', 'serve-http', 'status', 'sync',
|
|
324
|
-
'hook', 'hooks', 'ingest', 'media', 'git-hook', 'git-hook-uninstall',
|
|
325
|
-
'background', 'bg', 'bs', 'doctor', 'repair', 'dashboard', 'cleanup', 'purge', 'uninstall', 'orchestrate'];
|
|
326
|
-
if (firstArg && knownSubs.includes(firstArg)) return;
|
|
327
|
-
|
|
328
|
-
// No subcommand provided — enter memcode TUI (native coding agent)
|
|
329
|
-
if (!firstArg) {
|
|
330
|
-
try {
|
|
331
|
-
const { runCli } = await importBundledMemcode();
|
|
332
|
-
await runCli(process.argv.slice(2));
|
|
333
|
-
return;
|
|
334
|
-
} catch (err) {
|
|
335
|
-
console.error('Failed to start memcode:', err instanceof Error ? err.message : err);
|
|
336
|
-
process.exit(1);
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// Fallback: show usage hint
|
|
341
|
-
if (process.stdout.isTTY && process.stdin.isTTY) {
|
|
342
|
-
// Fire-and-forget: background update check. Default is notify-only; stderr only, never blocks TUI.
|
|
343
|
-
import('./update-checker.js').then(m => m.checkForUpdates()).catch(() => {});
|
|
344
|
-
const { startWorkbench } = await import('./workbench.js');
|
|
345
|
-
await startWorkbench();
|
|
346
|
-
} else {
|
|
347
|
-
// Non-interactive mode: show usage hint
|
|
348
|
-
console.error(`Memorix v${getCliVersion()} — Local-first memory control plane\n`);
|
|
349
|
-
console.error('Usage: memorix <command>\n');
|
|
350
|
-
console.error('Commands:');
|
|
351
|
-
console.error(' help Show action-oriented help (`memorix memory --help`)');
|
|
352
|
-
console.error(' workbench Open interactive terminal memory control plane');
|
|
353
|
-
console.error(' memcode Enter memcode TUI (native coding agent)');
|
|
354
|
-
console.error(' ask "q" Ask Memorix a question (single-shot chat)');
|
|
355
|
-
console.error(' Pipe: echo "q" | memorix ask');
|
|
356
|
-
console.error(' background Start/stop/status background control plane');
|
|
357
|
-
console.error(' session Start/end/context for coding sessions');
|
|
358
|
-
console.error(' memory Search/store/detail/timeline/resolve observations');
|
|
359
|
-
console.error(' context Show the Memory Autopilot brief for this project');
|
|
360
|
-
console.error(' resume Resume prior work with one bounded project brief');
|
|
361
|
-
console.error(' explain Explain where Memorix project context comes from');
|
|
362
|
-
console.error(' codegraph Refresh/status/context-pack for CodeGraph Memory');
|
|
363
|
-
console.error(' checkpoint Inspect native compact continuity checkpoints');
|
|
364
|
-
console.error(' knowledge Review source-backed knowledge pages and project workflows');
|
|
365
|
-
console.error(' reasoning Store/search decision rationale');
|
|
366
|
-
console.error(' retention Inspect stale/archive status');
|
|
367
|
-
console.error(' formation Inspect Memory Formation metrics');
|
|
368
|
-
console.error(' audit Audit trail and project attribution checks');
|
|
369
|
-
console.error(' transfer Export/import memory snapshots');
|
|
370
|
-
console.error(' skills List/generate/show project skills');
|
|
371
|
-
console.error(' identity Select the explicit CLI actor for private/team memory');
|
|
372
|
-
console.error(' team Join/status/role operations for coordination state');
|
|
373
|
-
console.error(' task Create/claim/complete/list team tasks');
|
|
374
|
-
console.error(' message Send/broadcast/read team messages');
|
|
375
|
-
console.error(' lock Manage advisory file locks');
|
|
376
|
-
console.error(' handoff Create durable handoff artifacts');
|
|
377
|
-
console.error(' poll Snapshot project coordination state');
|
|
378
|
-
console.error(' receipt Privacy-safe memory handoff diagnostic');
|
|
379
|
-
console.error(' serve-http Start HTTP MCP + dashboard control plane');
|
|
380
|
-
console.error(' serve Start MCP server on stdio');
|
|
381
|
-
console.error(' init Create global defaults or project config');
|
|
382
|
-
console.error(' setup Install Memorix plugin/MCP/rules/hooks for an agent');
|
|
383
|
-
console.error(' repair Repair Memorix-owned agent integration files');
|
|
384
|
-
console.error(' config Show TOML config paths and resolved values');
|
|
385
|
-
console.error(' integrate Install one IDE integration into the current repo');
|
|
386
|
-
console.error(' status Show project info + stats');
|
|
387
|
-
console.error(' dashboard Open standalone dashboard (read-mostly)');
|
|
388
|
-
console.error(' hooks Open legacy hook installer menu');
|
|
389
|
-
console.error(' cleanup Remove old memories');
|
|
390
|
-
console.error(' purge Retire all memories (project by default, --all for everything)');
|
|
391
|
-
console.error(' sync Rules/workspace sync plus interactive wizard');
|
|
392
|
-
console.error(' ingest Ingest commit, log, or image knowledge');
|
|
393
|
-
console.error('\nRun `memorix` in an interactive terminal for memcode TUI.');
|
|
394
|
-
}
|
|
395
|
-
},
|
|
396
|
-
});
|
|
397
|
-
|
|
398
|
-
try {
|
|
399
|
-
normalizeCliInvocation();
|
|
400
|
-
if (!printCliGuideForHelp()) {
|
|
401
|
-
runMain(main);
|
|
402
|
-
}
|
|
403
|
-
} catch (error) {
|
|
404
|
-
console.error(`Memorix CLI invocation error: ${error instanceof Error ? error.message : String(error)}`);
|
|
405
|
-
process.exitCode = 1;
|
|
406
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Memorix CLI
|
|
3
|
+
*
|
|
4
|
+
* Command-line interface for Memorix management.
|
|
5
|
+
* Built with: citty (1.1K stars, zero-deps) + @clack/prompts (7.4K stars)
|
|
6
|
+
*
|
|
7
|
+
* Commands:
|
|
8
|
+
* memorix — Enter memcode TUI (native coding agent)
|
|
9
|
+
* memorix memcode — Enter memcode TUI (explicit)
|
|
10
|
+
* memorix ask "q" — Single-shot chat question (pipe: echo "q" | memorix ask)
|
|
11
|
+
* memorix serve — Start MCP Server on stdio
|
|
12
|
+
* memorix status — Show project info + rules sync status
|
|
13
|
+
* memorix sync — Interactive cross-agent rule sync
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { defineCommand, runMain } from 'citty';
|
|
17
|
+
import * as p from '@clack/prompts';
|
|
18
|
+
import { getCliVersion } from './version.js';
|
|
19
|
+
import { importBundledMemcode } from './memcode-bootstrap.js';
|
|
20
|
+
import { installCliPipeErrorGuard } from './pipe-errors.js';
|
|
21
|
+
import { normalizeCliInvocation } from './invocation.js';
|
|
22
|
+
import { printCliGuideForHelp, renderCliGuide } from './command-guide.js';
|
|
23
|
+
|
|
24
|
+
installCliPipeErrorGuard();
|
|
25
|
+
|
|
26
|
+
const NO_GIT_MSG = 'Memorix requires a git repo to establish project identity. Run `git init` in this workspace first.';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Set PI_PACKAGE_DIR so bundled memcode can find its theme files.
|
|
30
|
+
* When tsup bundles memcode into the CLI, __dirname points to dist/cli/,
|
|
31
|
+
* not packages/memcode/. This env var tells config.ts where to look.
|
|
32
|
+
*
|
|
33
|
+
* In dev: resolves to packages/memcode/
|
|
34
|
+
* In global npm install: packages/memcode/ won't exist; theme files
|
|
35
|
+
* should be copied to dist/memcode/ by the build (see tsup onSuccess).
|
|
36
|
+
*/
|
|
37
|
+
function ensureMemcodePackageDir(): void {
|
|
38
|
+
if (process.env.MEMCODE_PACKAGE_DIR) return;
|
|
39
|
+
// Walk up from __dirname (dist/cli/) to find packages/memcode/package.json
|
|
40
|
+
const path = require('node:path') as typeof import('node:path');
|
|
41
|
+
const fs = require('node:fs') as typeof import('node:fs');
|
|
42
|
+
let dir: string = __dirname;
|
|
43
|
+
for (let i = 0; i < 5; i++) {
|
|
44
|
+
const candidate = path.join(dir, 'packages', 'memcode');
|
|
45
|
+
if (fs.existsSync(path.join(candidate, 'package.json'))) {
|
|
46
|
+
process.env.MEMCODE_PACKAGE_DIR = candidate;
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const parent = path.dirname(dir);
|
|
50
|
+
if (parent === dir) break;
|
|
51
|
+
dir = parent;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const DIM = '\x1b[2m';
|
|
56
|
+
const RESET = '\x1b[0m';
|
|
57
|
+
const CYAN = '\x1b[36m';
|
|
58
|
+
const YELLOW = '\x1b[33m';
|
|
59
|
+
|
|
60
|
+
async function runAsk(question: string): Promise<void> {
|
|
61
|
+
// Use a smooth dots-style spinner (same frames as TUI ink-spinner "dots")
|
|
62
|
+
// instead of @clack's ASCII spinner which flickers in non-Ink terminals.
|
|
63
|
+
const dotsFrames = ['⠋','⠙','⠹','⠸','⠼','⠴','⠦','⠧','⠇','⠏'];
|
|
64
|
+
let frameIdx = 0;
|
|
65
|
+
let interval: ReturnType<typeof setInterval> | null = null;
|
|
66
|
+
|
|
67
|
+
const startSmoothSpinner = (msg: string) => {
|
|
68
|
+
if (process.stdout.isTTY) {
|
|
69
|
+
process.stdout.write(`${CYAN}${dotsFrames[0]}${RESET} ${msg}`);
|
|
70
|
+
interval = setInterval(() => {
|
|
71
|
+
frameIdx = (frameIdx + 1) % dotsFrames.length;
|
|
72
|
+
process.stdout.write(`\r${CYAN}${dotsFrames[frameIdx]}${RESET} ${msg}`);
|
|
73
|
+
}, 80);
|
|
74
|
+
} else {
|
|
75
|
+
process.stderr.write(`${msg}...\n`);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const stopSmoothSpinner = () => {
|
|
80
|
+
if (interval) { clearInterval(interval); interval = null; }
|
|
81
|
+
if (process.stdout.isTTY) {
|
|
82
|
+
process.stdout.write('\r' + ' '.repeat(40) + '\r'); // clear line
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
startSmoothSpinner('Thinking…');
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
const { askMemoryQuestion } = await import('./tui/chat-service.js');
|
|
90
|
+
const result = await askMemoryQuestion(question);
|
|
91
|
+
|
|
92
|
+
stopSmoothSpinner();
|
|
93
|
+
|
|
94
|
+
// Output the answer
|
|
95
|
+
console.log('');
|
|
96
|
+
console.log(result.answer);
|
|
97
|
+
|
|
98
|
+
// Show sources if any
|
|
99
|
+
if (result.sources.length > 0) {
|
|
100
|
+
console.log('');
|
|
101
|
+
console.log(`${DIM}Sources:${RESET}`);
|
|
102
|
+
for (const src of result.sources.slice(0, 5)) {
|
|
103
|
+
console.log(` ${DIM}[obs:${src.id}]${RESET} ${src.title}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Show warnings
|
|
108
|
+
if (result.warning) {
|
|
109
|
+
console.log('');
|
|
110
|
+
console.log(`${YELLOW}[WARN] ${result.warning}${RESET}`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Metadata footer
|
|
114
|
+
const meta: string[] = [];
|
|
115
|
+
if (result.usedLLM && result.llmModel) meta.push(result.llmModel);
|
|
116
|
+
if (result.searchMode) meta.push(result.searchMode);
|
|
117
|
+
if (result.toolCallsCount) meta.push(`${result.toolCallsCount} tool call${result.toolCallsCount > 1 ? 's' : ''}`);
|
|
118
|
+
if (meta.length > 0) {
|
|
119
|
+
console.log(`${DIM} ${meta.join(' · ')}${RESET}`);
|
|
120
|
+
}
|
|
121
|
+
} catch (err) {
|
|
122
|
+
stopSmoothSpinner();
|
|
123
|
+
p.log.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
124
|
+
process.exitCode = 1;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ============================================================
|
|
129
|
+
// Main command
|
|
130
|
+
// ============================================================
|
|
131
|
+
|
|
132
|
+
async function runMemoryShortcut(action: string, args: Record<string, unknown>): Promise<void> {
|
|
133
|
+
const { detectProject } = await import('../project/detector.js');
|
|
134
|
+
if (!detectProject(process.cwd())) {
|
|
135
|
+
console.log(NO_GIT_MSG);
|
|
136
|
+
process.exitCode = 1;
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const memory = await import('./commands/memory.js');
|
|
140
|
+
await memory.default.run?.({
|
|
141
|
+
args: { ...args, _: [action] },
|
|
142
|
+
rawArgs: [],
|
|
143
|
+
cmd: memory.default,
|
|
144
|
+
} as any);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const main = defineCommand({
|
|
148
|
+
meta: {
|
|
149
|
+
name: 'memorix',
|
|
150
|
+
version: getCliVersion(),
|
|
151
|
+
description: 'Local-first memory control plane for AI coding agents through CLI, MCP, and local workflows',
|
|
152
|
+
},
|
|
153
|
+
subCommands: {
|
|
154
|
+
// One-shot product commands (primary user paths)
|
|
155
|
+
ask: () => Promise.resolve(defineCommand({
|
|
156
|
+
meta: { name: 'ask', description: 'Ask Memorix a question (single-shot chat). Pipe: echo "q" | memorix ask' },
|
|
157
|
+
args: {
|
|
158
|
+
question: { type: 'positional', description: 'Question to ask (or pipe via stdin)', required: false },
|
|
159
|
+
},
|
|
160
|
+
async run({ args }) {
|
|
161
|
+
let q = (args.question as string) || '';
|
|
162
|
+
// Read from stdin if no positional arg and stdin is piped
|
|
163
|
+
if (!q && !process.stdin.isTTY) {
|
|
164
|
+
q = await new Promise<string>((resolve) => {
|
|
165
|
+
let data = '';
|
|
166
|
+
process.stdin.setEncoding('utf-8');
|
|
167
|
+
process.stdin.on('data', (chunk) => { data += chunk; });
|
|
168
|
+
process.stdin.on('end', () => resolve(data.trim()));
|
|
169
|
+
process.stdin.on('error', () => resolve(''));
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
if (!q) {
|
|
173
|
+
p.log.error('No question provided. Usage: memorix ask "your question" or echo "q" | memorix ask');
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
await runAsk(q);
|
|
177
|
+
},
|
|
178
|
+
})),
|
|
179
|
+
search: () => Promise.resolve(defineCommand({
|
|
180
|
+
meta: { name: 'search', description: 'Shortcut for `memorix memory search`' },
|
|
181
|
+
args: {
|
|
182
|
+
query: { type: 'positional', description: 'Search query', required: true },
|
|
183
|
+
limit: { type: 'string', description: 'Maximum results' },
|
|
184
|
+
quality: { type: 'string', description: 'Retrieval profile: fast, balanced (default), or thorough' },
|
|
185
|
+
json: { type: 'boolean', description: 'Emit machine-readable JSON output' },
|
|
186
|
+
},
|
|
187
|
+
async run({ args }) {
|
|
188
|
+
await runMemoryShortcut('search', {
|
|
189
|
+
query: args.query,
|
|
190
|
+
limit: args.limit,
|
|
191
|
+
quality: args.quality,
|
|
192
|
+
json: args.json,
|
|
193
|
+
});
|
|
194
|
+
},
|
|
195
|
+
})),
|
|
196
|
+
remember: () => Promise.resolve(defineCommand({
|
|
197
|
+
meta: { name: 'remember', description: 'Shortcut for `memorix memory store`' },
|
|
198
|
+
args: {
|
|
199
|
+
text: { type: 'positional', description: 'Text to remember', required: true },
|
|
200
|
+
title: { type: 'string', description: 'Optional observation title' },
|
|
201
|
+
type: { type: 'string', description: 'Observation type' },
|
|
202
|
+
visibility: { type: 'string', description: 'project (default), personal, or team' },
|
|
203
|
+
json: { type: 'boolean', description: 'Emit machine-readable JSON output' },
|
|
204
|
+
},
|
|
205
|
+
async run({ args }) {
|
|
206
|
+
await runMemoryShortcut('store', {
|
|
207
|
+
text: args.text,
|
|
208
|
+
title: args.title,
|
|
209
|
+
type: args.type,
|
|
210
|
+
visibility: args.visibility,
|
|
211
|
+
json: args.json,
|
|
212
|
+
});
|
|
213
|
+
},
|
|
214
|
+
})),
|
|
215
|
+
recent: () => Promise.resolve(defineCommand({
|
|
216
|
+
meta: { name: 'recent', description: 'Shortcut for `memorix memory recent`' },
|
|
217
|
+
args: {
|
|
218
|
+
limit: { type: 'string', description: 'Maximum results' },
|
|
219
|
+
json: { type: 'boolean', description: 'Emit machine-readable JSON output' },
|
|
220
|
+
},
|
|
221
|
+
async run({ args }) { await runMemoryShortcut('recent', { limit: args.limit, json: args.json }); },
|
|
222
|
+
})),
|
|
223
|
+
help: () => Promise.resolve(defineCommand({
|
|
224
|
+
meta: { name: 'help', description: 'Show action-oriented help for a command group' },
|
|
225
|
+
args: {
|
|
226
|
+
command: { type: 'positional', description: 'Command group to inspect', required: false },
|
|
227
|
+
},
|
|
228
|
+
async run({ args }) {
|
|
229
|
+
console.log(renderCliGuide(args.command as string | undefined));
|
|
230
|
+
},
|
|
231
|
+
})),
|
|
232
|
+
// Infrastructure commands
|
|
233
|
+
init: () => import('./commands/init.js').then(m => m.default),
|
|
234
|
+
setup: () => import('./commands/setup.js').then(m => m.default),
|
|
235
|
+
config: () => Promise.resolve(defineCommand({
|
|
236
|
+
meta: { name: 'config', description: 'Inspect Memorix TOML configuration' },
|
|
237
|
+
subCommands: {
|
|
238
|
+
path: () => import('./commands/config-path.js').then(m => m.default),
|
|
239
|
+
get: () => import('./commands/config-get.js').then(m => m.default),
|
|
240
|
+
migrate: () => import('./commands/config-migrate.js').then(m => m.default),
|
|
241
|
+
},
|
|
242
|
+
})),
|
|
243
|
+
integrate: () => import('./commands/integrate.js').then(m => m.default),
|
|
244
|
+
memory: () => import('./commands/memory.js').then(m => m.default),
|
|
245
|
+
context: () => import('./commands/context.js').then(m => m.default),
|
|
246
|
+
resume: () => import('./commands/resume.js').then(m => m.default),
|
|
247
|
+
explain: () => import('./commands/explain.js').then(m => m.default),
|
|
248
|
+
codegraph: () => import('./commands/codegraph.js').then(m => m.default),
|
|
249
|
+
checkpoint: () => import('./commands/checkpoint.js').then(m => m.default),
|
|
250
|
+
knowledge: () => import('./commands/knowledge.js').then(m => m.default),
|
|
251
|
+
reasoning: () => import('./commands/reasoning.js').then(m => m.default),
|
|
252
|
+
retention: () => import('./commands/retention.js').then(m => m.default),
|
|
253
|
+
formation: () => import('./commands/formation.js').then(m => m.default),
|
|
254
|
+
audit: () => import('./commands/audit.js').then(m => m.default),
|
|
255
|
+
transfer: () => import('./commands/transfer.js').then(m => m.default),
|
|
256
|
+
skills: () => import('./commands/skills.js').then(m => m.default),
|
|
257
|
+
identity: () => import('./commands/identity.js').then(m => m.default),
|
|
258
|
+
session: () => import('./commands/session.js').then(m => m.default),
|
|
259
|
+
team: () => import('./commands/team.js').then(m => m.default),
|
|
260
|
+
task: () => import('./commands/task.js').then(m => m.default),
|
|
261
|
+
message: () => import('./commands/message.js').then(m => m.default),
|
|
262
|
+
lock: () => import('./commands/lock.js').then(m => m.default),
|
|
263
|
+
handoff: () => import('./commands/handoff.js').then(m => m.default),
|
|
264
|
+
poll: () => import('./commands/poll.js').then(m => m.default),
|
|
265
|
+
receipt: () => import('./commands/receipt.js').then(m => m.default),
|
|
266
|
+
serve: () => import('./commands/serve.js').then(m => m.default),
|
|
267
|
+
'serve-http': () => import('./commands/serve-http.js').then(m => m.default),
|
|
268
|
+
status: () => import('./commands/status.js').then(m => m.default),
|
|
269
|
+
sync: () => import('./commands/sync.js').then(m => m.default),
|
|
270
|
+
hook: () => import('./commands/hook.js').then(m => m.default),
|
|
271
|
+
hooks: () => import('./commands/hooks.js').then(m => m.default),
|
|
272
|
+
ingest: () => import('./commands/ingest.js').then(m => m.default),
|
|
273
|
+
media: () => import('./commands/media.js').then(m => m.default),
|
|
274
|
+
'git-hook': () => import('./commands/git-hook-install.js').then(m => m.default),
|
|
275
|
+
'git-hook-uninstall': () => import('./commands/git-hook-uninstall.js').then(m => m.default),
|
|
276
|
+
background: () => import('./commands/background.js').then(m => m.default),
|
|
277
|
+
bg: () => import('./commands/background.js').then(m => m.default),
|
|
278
|
+
bs: () => Promise.resolve(defineCommand({
|
|
279
|
+
meta: { name: 'bs', description: 'Shortcut: background start' },
|
|
280
|
+
args: { port: { type: 'string', description: 'HTTP port (default: 3211)', required: false } },
|
|
281
|
+
async run({ args }) {
|
|
282
|
+
// Directly invoke background start instead of going through citty's CommandContext
|
|
283
|
+
const port = parseInt((args.port as string) || '3211', 10);
|
|
284
|
+
const { doStart } = await import('./commands/background.js');
|
|
285
|
+
await doStart(port);
|
|
286
|
+
},
|
|
287
|
+
})),
|
|
288
|
+
doctor: () => import('./commands/doctor.js').then(m => m.default),
|
|
289
|
+
repair: () => import('./commands/repair.js').then(m => m.default),
|
|
290
|
+
dashboard: () => import('./commands/dashboard.js').then(m => m.default),
|
|
291
|
+
cleanup: () => import('./commands/cleanup.js').then(m => m.default),
|
|
292
|
+
purge: () => import('./commands/purge.js').then(m => m.default),
|
|
293
|
+
uninstall: () => import('./commands/uninstall.js').then(m => m.default),
|
|
294
|
+
orchestrate: () => import('./commands/orchestrate.js').then(m => m.default),
|
|
295
|
+
workbench: () => Promise.resolve(defineCommand({
|
|
296
|
+
meta: { name: 'workbench', description: 'Open the interactive terminal memory control plane' },
|
|
297
|
+
async run() {
|
|
298
|
+
const { startWorkbench } = await import('./workbench.js');
|
|
299
|
+
await startWorkbench();
|
|
300
|
+
},
|
|
301
|
+
})),
|
|
302
|
+
memcode: () => Promise.resolve(defineCommand({
|
|
303
|
+
meta: { name: 'memcode', description: 'Enter memcode TUI — native coding agent with memory' },
|
|
304
|
+
async run() {
|
|
305
|
+
try {
|
|
306
|
+
const { runCli } = await importBundledMemcode();
|
|
307
|
+
await runCli(process.argv.slice(3));
|
|
308
|
+
} catch (err) {
|
|
309
|
+
console.error('Failed to start memcode:', err instanceof Error ? err.message : err);
|
|
310
|
+
process.exit(1);
|
|
311
|
+
}
|
|
312
|
+
},
|
|
313
|
+
})),
|
|
314
|
+
},
|
|
315
|
+
async run() {
|
|
316
|
+
// Guard: if citty already resolved a subcommand, its run() was called before this.
|
|
317
|
+
// Detect by checking if the first CLI arg matches a registered subcommand name.
|
|
318
|
+
const firstArg = process.argv[2];
|
|
319
|
+
const knownSubs = ['ask', 'search', 'remember', 'recent', 'help', 'workbench', 'memcode', 'config',
|
|
320
|
+
'init', 'setup', 'integrate', 'memory', 'context', 'resume', 'explain', 'codegraph', 'checkpoint', 'knowledge', 'reasoning', 'retention', 'formation', 'audit', 'transfer', 'skills', 'identity',
|
|
321
|
+
'session', 'team', 'task', 'message', 'lock', 'handoff', 'poll',
|
|
322
|
+
'receipt',
|
|
323
|
+
'serve', 'serve-http', 'status', 'sync',
|
|
324
|
+
'hook', 'hooks', 'ingest', 'media', 'git-hook', 'git-hook-uninstall',
|
|
325
|
+
'background', 'bg', 'bs', 'doctor', 'repair', 'dashboard', 'cleanup', 'purge', 'uninstall', 'orchestrate'];
|
|
326
|
+
if (firstArg && knownSubs.includes(firstArg)) return;
|
|
327
|
+
|
|
328
|
+
// No subcommand provided — enter memcode TUI (native coding agent)
|
|
329
|
+
if (!firstArg) {
|
|
330
|
+
try {
|
|
331
|
+
const { runCli } = await importBundledMemcode();
|
|
332
|
+
await runCli(process.argv.slice(2));
|
|
333
|
+
return;
|
|
334
|
+
} catch (err) {
|
|
335
|
+
console.error('Failed to start memcode:', err instanceof Error ? err.message : err);
|
|
336
|
+
process.exit(1);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Fallback: show usage hint
|
|
341
|
+
if (process.stdout.isTTY && process.stdin.isTTY) {
|
|
342
|
+
// Fire-and-forget: background update check. Default is notify-only; stderr only, never blocks TUI.
|
|
343
|
+
import('./update-checker.js').then(m => m.checkForUpdates()).catch(() => {});
|
|
344
|
+
const { startWorkbench } = await import('./workbench.js');
|
|
345
|
+
await startWorkbench();
|
|
346
|
+
} else {
|
|
347
|
+
// Non-interactive mode: show usage hint
|
|
348
|
+
console.error(`Memorix v${getCliVersion()} — Local-first memory control plane\n`);
|
|
349
|
+
console.error('Usage: memorix <command>\n');
|
|
350
|
+
console.error('Commands:');
|
|
351
|
+
console.error(' help Show action-oriented help (`memorix memory --help`)');
|
|
352
|
+
console.error(' workbench Open interactive terminal memory control plane');
|
|
353
|
+
console.error(' memcode Enter memcode TUI (native coding agent)');
|
|
354
|
+
console.error(' ask "q" Ask Memorix a question (single-shot chat)');
|
|
355
|
+
console.error(' Pipe: echo "q" | memorix ask');
|
|
356
|
+
console.error(' background Start/stop/status background control plane');
|
|
357
|
+
console.error(' session Start/end/context for coding sessions');
|
|
358
|
+
console.error(' memory Search/store/detail/timeline/resolve observations');
|
|
359
|
+
console.error(' context Show the Memory Autopilot brief for this project');
|
|
360
|
+
console.error(' resume Resume prior work with one bounded project brief');
|
|
361
|
+
console.error(' explain Explain where Memorix project context comes from');
|
|
362
|
+
console.error(' codegraph Refresh/status/context-pack for CodeGraph Memory');
|
|
363
|
+
console.error(' checkpoint Inspect native compact continuity checkpoints');
|
|
364
|
+
console.error(' knowledge Review source-backed knowledge pages and project workflows');
|
|
365
|
+
console.error(' reasoning Store/search decision rationale');
|
|
366
|
+
console.error(' retention Inspect stale/archive status');
|
|
367
|
+
console.error(' formation Inspect Memory Formation metrics');
|
|
368
|
+
console.error(' audit Audit trail and project attribution checks');
|
|
369
|
+
console.error(' transfer Export/import memory snapshots');
|
|
370
|
+
console.error(' skills List/generate/show project skills');
|
|
371
|
+
console.error(' identity Select the explicit CLI actor for private/team memory');
|
|
372
|
+
console.error(' team Join/status/role operations for coordination state');
|
|
373
|
+
console.error(' task Create/claim/complete/list team tasks');
|
|
374
|
+
console.error(' message Send/broadcast/read team messages');
|
|
375
|
+
console.error(' lock Manage advisory file locks');
|
|
376
|
+
console.error(' handoff Create durable handoff artifacts');
|
|
377
|
+
console.error(' poll Snapshot project coordination state');
|
|
378
|
+
console.error(' receipt Privacy-safe memory handoff diagnostic');
|
|
379
|
+
console.error(' serve-http Start HTTP MCP + dashboard control plane');
|
|
380
|
+
console.error(' serve Start MCP server on stdio');
|
|
381
|
+
console.error(' init Create global defaults or project config');
|
|
382
|
+
console.error(' setup Install Memorix plugin/MCP/rules/hooks for an agent');
|
|
383
|
+
console.error(' repair Repair Memorix-owned agent integration files');
|
|
384
|
+
console.error(' config Show TOML config paths and resolved values');
|
|
385
|
+
console.error(' integrate Install one IDE integration into the current repo');
|
|
386
|
+
console.error(' status Show project info + stats');
|
|
387
|
+
console.error(' dashboard Open standalone dashboard (read-mostly)');
|
|
388
|
+
console.error(' hooks Open legacy hook installer menu');
|
|
389
|
+
console.error(' cleanup Remove old memories');
|
|
390
|
+
console.error(' purge Retire all memories (project by default, --all for everything)');
|
|
391
|
+
console.error(' sync Rules/workspace sync plus interactive wizard');
|
|
392
|
+
console.error(' ingest Ingest commit, log, or image knowledge');
|
|
393
|
+
console.error('\nRun `memorix` in an interactive terminal for memcode TUI.');
|
|
394
|
+
}
|
|
395
|
+
},
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
try {
|
|
399
|
+
normalizeCliInvocation();
|
|
400
|
+
if (!printCliGuideForHelp()) {
|
|
401
|
+
runMain(main);
|
|
402
|
+
}
|
|
403
|
+
} catch (error) {
|
|
404
|
+
console.error(`Memorix CLI invocation error: ${error instanceof Error ? error.message : String(error)}`);
|
|
405
|
+
process.exitCode = 1;
|
|
406
|
+
}
|