memorix 1.4.3 → 1.4.4
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 +19 -0
- package/README.md +11 -4
- package/README.zh-CN.md +7 -1
- package/TEAM.md +86 -86
- package/assets/star-history-dark.svg +8 -8
- package/assets/star-history-light.svg +8 -8
- package/assets/star-history.json +16 -2
- package/dist/cli/index.js +194 -109
- package/dist/cli/memcode.js +0 -0
- package/dist/index.js +39 -12
- package/dist/maintenance-runner.js +9 -7
- package/dist/media-audio-runner.js +9 -7
- package/dist/media-video-runner.js +9 -7
- package/dist/memcode/index.js +9 -7
- package/dist/memcode-runtime/CHANGELOG.md +19 -0
- package/dist/sdk.js +38 -11
- package/dist/vector-backfill-runner.js +9 -7
- package/docs/AGENT_OPERATOR_PLAYBOOK.md +3 -3
- package/docs/API_REFERENCE.md +9 -5
- package/docs/CONFIGURATION.md +15 -5
- package/docs/DESIGN_DECISIONS.md +357 -357
- package/docs/DEVELOPMENT.md +2 -2
- package/docs/KNOWN_ISSUES_AND_ROADMAP.md +3 -3
- package/docs/MODULES.md +1 -1
- package/docs/README.md +1 -1
- package/docs/SETUP.md +3 -3
- package/docs/hooks-architecture.md +2 -1
- package/llms-full.txt +4 -4
- package/llms.txt +2 -2
- package/package.json +1 -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/audit-list.ts +89 -89
- 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/media-image.ts +27 -0
- package/src/cli/commands/media.ts +8 -1
- package/src/cli/commands/memory.ts +44 -34
- package/src/cli/commands/message.ts +104 -104
- package/src/cli/commands/operator-shared.ts +62 -18
- package/src/cli/commands/poll.ts +58 -58
- package/src/cli/commands/purge-all-memory.ts +85 -85
- package/src/cli/commands/purge-project-memory.ts +83 -83
- package/src/cli/commands/reasoning.ts +118 -118
- 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/commands/uninstall-project-artifacts.ts +85 -85
- 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/Sidebar.tsx +179 -179
- package/src/cli/tui/index.ts +41 -41
- package/src/cli/tui/markdown-render.tsx +371 -371
- package/src/cli/tui/use-mouse.ts +157 -157
- package/src/cli/tui/useNavigation.ts +56 -56
- package/src/cli/version.ts +7 -7
- package/src/cli/workbench.ts +1 -1
- package/src/dashboard/project-classification.ts +64 -64
- package/src/git/hooks-path.ts +85 -85
- 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/provider.ts +885 -885
- package/src/llm/quality.ts +248 -248
- package/src/media/minimax.ts +9 -1
- 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/formation/extract.ts +361 -361
- package/src/memory/formation/index.ts +417 -417
- package/src/memory/formation/resolve.ts +344 -344
- package/src/memory/formation/types.ts +315 -315
- package/src/memory/freshness.ts +122 -122
- package/src/memory/graph.ts +197 -197
- package/src/memory/refs.ts +94 -94
- package/src/memory/secret-filter.ts +79 -79
- 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/context-compact.ts +188 -188
- 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/rules/adapters/trae.ts +56 -56
- package/src/rules/syncer.ts +235 -235
- package/src/search/query-expansion.ts +52 -52
- package/src/server/formation-timeout.ts +27 -27
- package/src/server.ts +23 -1
- 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/persistence-json.ts +212 -212
- package/src/store/persistence.ts +291 -291
- package/src/store/project-affinity.ts +195 -195
- package/src/team/event-bus.ts +76 -76
- package/src/team/file-locks.ts +173 -173
- package/src/team/handoff.ts +161 -161
- package/src/team/messages.ts +203 -203
- package/src/team/poll.ts +132 -132
- package/src/team/tasks.ts +211 -211
- 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/dist/memcode-runtime/docs/images/exy.png +0 -0
|
@@ -1,71 +1,71 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CLI Command: memorix hooks status
|
|
3
|
-
*
|
|
4
|
-
* Show hook installation status for all agents.
|
|
1
|
+
/**
|
|
2
|
+
* CLI Command: memorix hooks status
|
|
3
|
+
*
|
|
4
|
+
* Show hook installation status for all agents.
|
|
5
5
|
* Status marks:
|
|
6
6
|
* [OK] - installed and verified (config-based agent, file read directly)
|
|
7
7
|
* [??] - installed but unverified (plugin-based agent, runtime load not confirmed)
|
|
8
8
|
* [!!] - installed but outdated
|
|
9
9
|
* [ ] - not installed
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { defineCommand } from 'citty';
|
|
13
|
-
|
|
14
|
-
export default defineCommand({
|
|
15
|
-
meta: {
|
|
16
|
-
name: 'status',
|
|
17
|
-
description: 'Show hook installation status for all agents',
|
|
18
|
-
},
|
|
19
|
-
run: async () => {
|
|
20
|
-
const { getHookStatus } = await import('../../hooks/installers/index.js');
|
|
21
|
-
const { AGENT_SUPPORT_TIER } = await import('../../hooks/types.js');
|
|
22
|
-
const os = await import('node:os');
|
|
23
|
-
let cwd: string;
|
|
24
|
-
try { cwd = process.cwd(); } catch { cwd = os.homedir(); }
|
|
25
|
-
|
|
26
|
-
const statuses = await getHookStatus(cwd);
|
|
27
|
-
|
|
28
|
-
console.log('\nMemorix Hooks Status');
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { defineCommand } from 'citty';
|
|
13
|
+
|
|
14
|
+
export default defineCommand({
|
|
15
|
+
meta: {
|
|
16
|
+
name: 'status',
|
|
17
|
+
description: 'Show hook installation status for all agents',
|
|
18
|
+
},
|
|
19
|
+
run: async () => {
|
|
20
|
+
const { getHookStatus } = await import('../../hooks/installers/index.js');
|
|
21
|
+
const { AGENT_SUPPORT_TIER } = await import('../../hooks/types.js');
|
|
22
|
+
const os = await import('node:os');
|
|
23
|
+
let cwd: string;
|
|
24
|
+
try { cwd = process.cwd(); } catch { cwd = os.homedir(); }
|
|
25
|
+
|
|
26
|
+
const statuses = await getHookStatus(cwd);
|
|
27
|
+
|
|
28
|
+
console.log('\nMemorix Hooks Status');
|
|
29
29
|
console.log('-'.repeat(50));
|
|
30
|
-
|
|
31
|
-
let hasOutdated = false;
|
|
32
|
-
let hasUnverified = false;
|
|
33
|
-
let hasRuntimeIssue = false;
|
|
34
|
-
for (const { agent, installed, outdated, verified, runtimeReady, configPath } of statuses) {
|
|
35
|
-
const icon = installed
|
|
36
|
-
? (!runtimeReady ? '[!!]' : (outdated ? '[!!]' : (verified ? '[OK]' : '[??]')))
|
|
37
|
-
: '[ ]';
|
|
38
|
-
const label = agent.charAt(0).toUpperCase() + agent.slice(1);
|
|
39
|
-
const tier = AGENT_SUPPORT_TIER[agent];
|
|
30
|
+
|
|
31
|
+
let hasOutdated = false;
|
|
32
|
+
let hasUnverified = false;
|
|
33
|
+
let hasRuntimeIssue = false;
|
|
34
|
+
for (const { agent, installed, outdated, verified, runtimeReady, configPath } of statuses) {
|
|
35
|
+
const icon = installed
|
|
36
|
+
? (!runtimeReady ? '[!!]' : (outdated ? '[!!]' : (verified ? '[OK]' : '[??]')))
|
|
37
|
+
: '[ ]';
|
|
38
|
+
const label = agent.charAt(0).toUpperCase() + agent.slice(1);
|
|
39
|
+
const tier = AGENT_SUPPORT_TIER[agent];
|
|
40
40
|
const tierLabel = tier === 'core' ? '[core]' : tier === 'extended' ? '[extended]' : '[community]';
|
|
41
|
-
const suffix = !runtimeReady && installed
|
|
42
|
-
? ' (installed but runtime NOT ready — missing pwsh on Windows)'
|
|
43
|
-
: outdated
|
|
44
|
-
? ' (outdated - re-run `memorix hooks install`)'
|
|
45
|
-
: (!verified && installed ? ' (plugin file installed, runtime load unverified)' : '');
|
|
46
|
-
console.log(`${icon} ${tierLabel} ${label.padEnd(12)} ${installed ? configPath + suffix : '(not installed)'}`);
|
|
47
|
-
if (outdated) hasOutdated = true;
|
|
48
|
-
if (!verified && installed) hasUnverified = true;
|
|
49
|
-
if (!runtimeReady && installed) hasRuntimeIssue = true;
|
|
50
|
-
}
|
|
51
|
-
|
|
41
|
+
const suffix = !runtimeReady && installed
|
|
42
|
+
? ' (installed but runtime NOT ready — missing pwsh on Windows)'
|
|
43
|
+
: outdated
|
|
44
|
+
? ' (outdated - re-run `memorix hooks install`)'
|
|
45
|
+
: (!verified && installed ? ' (plugin file installed, runtime load unverified)' : '');
|
|
46
|
+
console.log(`${icon} ${tierLabel} ${label.padEnd(12)} ${installed ? configPath + suffix : '(not installed)'}`);
|
|
47
|
+
if (outdated) hasOutdated = true;
|
|
48
|
+
if (!verified && installed) hasUnverified = true;
|
|
49
|
+
if (!runtimeReady && installed) hasRuntimeIssue = true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
52
|
console.log('\nSupport tiers: [core] core | [extended] extended | [community] community');
|
|
53
|
-
|
|
54
|
-
if (hasOutdated) {
|
|
55
|
-
console.log('\n[warn] Outdated hooks detected. Run `memorix hooks install` to update.');
|
|
56
|
-
}
|
|
57
|
-
if (hasUnverified) {
|
|
58
|
-
console.log('\n[info] [??] = plugin file installed but runtime load unverified.');
|
|
59
|
-
console.log(' For OpenCode: verify by running `opencode --log-level DEBUG` and checking');
|
|
60
|
-
console.log(' for [memorix-plugin] messages in the log output.');
|
|
61
|
-
}
|
|
62
|
-
if (hasRuntimeIssue) {
|
|
63
|
-
console.log('\n[warn] [!!] = installed but runtime will fail.');
|
|
64
|
-
console.log(' Copilot on Windows requires PowerShell v6+ (pwsh.exe) for hook execution.');
|
|
65
|
-
console.log(' Install pwsh: https://learn.microsoft.com/powershell/scripting/install/installing-powershell');
|
|
66
|
-
console.log(' Without pwsh, Copilot hooks will fail at runtime with "spawn pwsh.exe ENOENT".');
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
console.log('\nRun `memorix hooks install` to set up hooks for detected agents.');
|
|
70
|
-
},
|
|
71
|
-
});
|
|
53
|
+
|
|
54
|
+
if (hasOutdated) {
|
|
55
|
+
console.log('\n[warn] Outdated hooks detected. Run `memorix hooks install` to update.');
|
|
56
|
+
}
|
|
57
|
+
if (hasUnverified) {
|
|
58
|
+
console.log('\n[info] [??] = plugin file installed but runtime load unverified.');
|
|
59
|
+
console.log(' For OpenCode: verify by running `opencode --log-level DEBUG` and checking');
|
|
60
|
+
console.log(' for [memorix-plugin] messages in the log output.');
|
|
61
|
+
}
|
|
62
|
+
if (hasRuntimeIssue) {
|
|
63
|
+
console.log('\n[warn] [!!] = installed but runtime will fail.');
|
|
64
|
+
console.log(' Copilot on Windows requires PowerShell v6+ (pwsh.exe) for hook execution.');
|
|
65
|
+
console.log(' Install pwsh: https://learn.microsoft.com/powershell/scripting/install/installing-powershell');
|
|
66
|
+
console.log(' Without pwsh, Copilot hooks will fail at runtime with "spawn pwsh.exe ENOENT".');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
console.log('\nRun `memorix hooks install` to set up hooks for detected agents.');
|
|
70
|
+
},
|
|
71
|
+
});
|
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CLI Command: memorix ingest
|
|
3
|
-
*
|
|
4
|
-
* Parent command for Git→Memory and image ingestion.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { defineCommand } from 'citty';
|
|
8
|
-
|
|
9
|
-
export default defineCommand({
|
|
10
|
-
meta: {
|
|
11
|
-
name: 'ingest',
|
|
12
|
-
description: 'Ingest engineering knowledge from Git and images',
|
|
13
|
-
},
|
|
14
|
-
subCommands: {
|
|
15
|
-
commit: () => import('./ingest-commit.js').then((module) => module.default),
|
|
16
|
-
log: () => import('./ingest-log.js').then((module) => module.default),
|
|
17
|
-
image: () => import('./ingest-image.js').then((module) => module.default),
|
|
18
|
-
},
|
|
19
|
-
run: async ({ args }) => {
|
|
20
|
-
const action = (args._ as string[])?.[0] || '';
|
|
21
|
-
if (action === 'commit') {
|
|
22
|
-
const module = await import('./ingest-commit.js');
|
|
23
|
-
await module.default.run?.({ args: { ...args, _: [] }, rawArgs: [], cmd: module.default } as any);
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
if (action === 'log') {
|
|
27
|
-
const module = await import('./ingest-log.js');
|
|
28
|
-
await module.default.run?.({ args: { ...args, _: [] }, rawArgs: [], cmd: module.default } as any);
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
if (action === 'image') {
|
|
32
|
-
const module = await import('./ingest-image.js');
|
|
33
|
-
await module.default.run?.({ args: { ...args, _: [] }, rawArgs: [], cmd: module.default } as any);
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
console.log('Memorix Ingest Commands');
|
|
38
|
-
console.log('');
|
|
39
|
-
console.log('Usage:');
|
|
40
|
-
console.log(' memorix ingest commit [--ref HEAD]');
|
|
41
|
-
console.log(' memorix ingest log [--count 10]');
|
|
42
|
-
console.log(' memorix ingest image --path ./diagram.png');
|
|
43
|
-
},
|
|
44
|
-
});
|
|
1
|
+
/**
|
|
2
|
+
* CLI Command: memorix ingest
|
|
3
|
+
*
|
|
4
|
+
* Parent command for Git→Memory and image ingestion.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { defineCommand } from 'citty';
|
|
8
|
+
|
|
9
|
+
export default defineCommand({
|
|
10
|
+
meta: {
|
|
11
|
+
name: 'ingest',
|
|
12
|
+
description: 'Ingest engineering knowledge from Git and images',
|
|
13
|
+
},
|
|
14
|
+
subCommands: {
|
|
15
|
+
commit: () => import('./ingest-commit.js').then((module) => module.default),
|
|
16
|
+
log: () => import('./ingest-log.js').then((module) => module.default),
|
|
17
|
+
image: () => import('./ingest-image.js').then((module) => module.default),
|
|
18
|
+
},
|
|
19
|
+
run: async ({ args }) => {
|
|
20
|
+
const action = (args._ as string[])?.[0] || '';
|
|
21
|
+
if (action === 'commit') {
|
|
22
|
+
const module = await import('./ingest-commit.js');
|
|
23
|
+
await module.default.run?.({ args: { ...args, _: [] }, rawArgs: [], cmd: module.default } as any);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (action === 'log') {
|
|
27
|
+
const module = await import('./ingest-log.js');
|
|
28
|
+
await module.default.run?.({ args: { ...args, _: [] }, rawArgs: [], cmd: module.default } as any);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (action === 'image') {
|
|
32
|
+
const module = await import('./ingest-image.js');
|
|
33
|
+
await module.default.run?.({ args: { ...args, _: [] }, rawArgs: [], cmd: module.default } as any);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
console.log('Memorix Ingest Commands');
|
|
38
|
+
console.log('');
|
|
39
|
+
console.log('Usage:');
|
|
40
|
+
console.log(' memorix ingest commit [--ref HEAD]');
|
|
41
|
+
console.log(' memorix ingest log [--count 10]');
|
|
42
|
+
console.log(' memorix ingest image --path ./diagram.png');
|
|
43
|
+
},
|
|
44
|
+
});
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { homedir } from 'node:os';
|
|
2
|
-
|
|
3
|
-
export function getIntegrationTargetRoot(
|
|
4
|
-
useGlobalDefaults: boolean,
|
|
5
|
-
cwd: string,
|
|
6
|
-
homeDir = homedir(),
|
|
7
|
-
): string {
|
|
8
|
-
return useGlobalDefaults ? homeDir : cwd;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function getIntegrationScopeLabel(useGlobalDefaults: boolean): string {
|
|
12
|
-
return useGlobalDefaults
|
|
13
|
-
? 'global defaults'
|
|
14
|
-
: 'current project';
|
|
15
|
-
}
|
|
1
|
+
import { homedir } from 'node:os';
|
|
2
|
+
|
|
3
|
+
export function getIntegrationTargetRoot(
|
|
4
|
+
useGlobalDefaults: boolean,
|
|
5
|
+
cwd: string,
|
|
6
|
+
homeDir = homedir(),
|
|
7
|
+
): string {
|
|
8
|
+
return useGlobalDefaults ? homeDir : cwd;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function getIntegrationScopeLabel(useGlobalDefaults: boolean): string {
|
|
12
|
+
return useGlobalDefaults
|
|
13
|
+
? 'global defaults'
|
|
14
|
+
: 'current project';
|
|
15
|
+
}
|
package/src/cli/commands/lock.ts
CHANGED
|
@@ -1,97 +1,97 @@
|
|
|
1
|
-
import { defineCommand } from 'citty';
|
|
1
|
+
import { defineCommand } from 'citty';
|
|
2
2
|
import { emitError, emitResult, getCliProjectContext, resolveCliActorId, shortId } from './operator-shared.js';
|
|
3
|
-
|
|
4
|
-
export default defineCommand({
|
|
5
|
-
meta: {
|
|
6
|
-
name: 'lock',
|
|
7
|
-
description: 'Acquire, release, and inspect advisory team file locks',
|
|
8
|
-
},
|
|
9
|
-
args: {
|
|
10
|
-
file: { type: 'string', description: 'File path to lock or inspect' },
|
|
11
|
-
agentId: { type: 'string', description: 'Agent ID acquiring or releasing the lock' },
|
|
12
|
-
json: { type: 'boolean', description: 'Emit machine-readable JSON output' },
|
|
13
|
-
},
|
|
14
|
-
run: async ({ args }) => {
|
|
15
|
-
const action = (args._ as string[])?.[0] || '';
|
|
16
|
-
const asJson = !!args.json;
|
|
17
|
-
|
|
18
|
-
try {
|
|
3
|
+
|
|
4
|
+
export default defineCommand({
|
|
5
|
+
meta: {
|
|
6
|
+
name: 'lock',
|
|
7
|
+
description: 'Acquire, release, and inspect advisory team file locks',
|
|
8
|
+
},
|
|
9
|
+
args: {
|
|
10
|
+
file: { type: 'string', description: 'File path to lock or inspect' },
|
|
11
|
+
agentId: { type: 'string', description: 'Agent ID acquiring or releasing the lock' },
|
|
12
|
+
json: { type: 'boolean', description: 'Emit machine-readable JSON output' },
|
|
13
|
+
},
|
|
14
|
+
run: async ({ args }) => {
|
|
15
|
+
const action = (args._ as string[])?.[0] || '';
|
|
16
|
+
const asJson = !!args.json;
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
19
|
const { project, teamStore, identity } = await getCliProjectContext();
|
|
20
20
|
const agentId = resolveCliActorId(args.agentId, identity);
|
|
21
|
-
|
|
22
|
-
switch (action) {
|
|
23
|
-
case 'lock': {
|
|
21
|
+
|
|
22
|
+
switch (action) {
|
|
23
|
+
case 'lock': {
|
|
24
24
|
if (!args.file || !agentId) {
|
|
25
25
|
emitError('file and an active CLI identity or agentId are required for "memorix lock lock"', asJson);
|
|
26
26
|
return;
|
|
27
27
|
}
|
|
28
28
|
const result = teamStore.acquireLock(project.id, args.file as string, agentId);
|
|
29
|
-
if (!result.success) {
|
|
30
|
-
emitError(`File is already locked by ${shortId(result.lockedBy)}`, asJson);
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
emitResult(
|
|
29
|
+
if (!result.success) {
|
|
30
|
+
emitError(`File is already locked by ${shortId(result.lockedBy)}`, asJson);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
emitResult(
|
|
34
34
|
{ project, file: args.file, lockedBy: agentId },
|
|
35
|
-
`Locked: ${args.file as string}`,
|
|
36
|
-
asJson,
|
|
37
|
-
);
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
case 'unlock': {
|
|
35
|
+
`Locked: ${args.file as string}`,
|
|
36
|
+
asJson,
|
|
37
|
+
);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
case 'unlock': {
|
|
42
42
|
if (!args.file || !agentId) {
|
|
43
43
|
emitError('file and an active CLI identity or agentId are required for "memorix lock unlock"', asJson);
|
|
44
44
|
return;
|
|
45
45
|
}
|
|
46
46
|
const released = teamStore.releaseLock(project.id, args.file as string, agentId);
|
|
47
|
-
if (!released) {
|
|
48
|
-
emitError('Cannot unlock: not the owner or the file is not locked', asJson);
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
emitResult(
|
|
52
|
-
{ project, file: args.file, released: true },
|
|
53
|
-
`Unlocked: ${args.file as string}`,
|
|
54
|
-
asJson,
|
|
55
|
-
);
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
case 'status': {
|
|
60
|
-
if (args.file) {
|
|
61
|
-
const lock = teamStore.getLockStatus(project.id, args.file as string);
|
|
62
|
-
emitResult(
|
|
63
|
-
{ project, lock },
|
|
64
|
-
lock
|
|
65
|
-
? `Locked by ${teamStore.getAgent(lock.locked_by)?.name ?? shortId(lock.locked_by)}: ${args.file as string}`
|
|
66
|
-
: `Unlocked: ${args.file as string}`,
|
|
67
|
-
asJson,
|
|
68
|
-
);
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
|
|
47
|
+
if (!released) {
|
|
48
|
+
emitError('Cannot unlock: not the owner or the file is not locked', asJson);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
emitResult(
|
|
52
|
+
{ project, file: args.file, released: true },
|
|
53
|
+
`Unlocked: ${args.file as string}`,
|
|
54
|
+
asJson,
|
|
55
|
+
);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
case 'status': {
|
|
60
|
+
if (args.file) {
|
|
61
|
+
const lock = teamStore.getLockStatus(project.id, args.file as string);
|
|
62
|
+
emitResult(
|
|
63
|
+
{ project, lock },
|
|
64
|
+
lock
|
|
65
|
+
? `Locked by ${teamStore.getAgent(lock.locked_by)?.name ?? shortId(lock.locked_by)}: ${args.file as string}`
|
|
66
|
+
: `Unlocked: ${args.file as string}`,
|
|
67
|
+
asJson,
|
|
68
|
+
);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
72
|
const locks = teamStore.listLocks(project.id, agentId);
|
|
73
|
-
emitResult(
|
|
74
|
-
{ project, locks },
|
|
75
|
-
locks.length === 0
|
|
76
|
-
? 'No active file locks.'
|
|
77
|
-
: locks
|
|
78
|
-
.map((lock) => `- ${lock.file} -> ${teamStore.getAgent(lock.locked_by)?.name ?? shortId(lock.locked_by)}`)
|
|
79
|
-
.join('\n'),
|
|
80
|
-
asJson,
|
|
81
|
-
);
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
default:
|
|
86
|
-
console.log('Memorix Lock Commands');
|
|
87
|
-
console.log('');
|
|
88
|
-
console.log('Usage:');
|
|
89
|
-
console.log(' memorix lock lock --file src/app.ts --agentId <id>');
|
|
90
|
-
console.log(' memorix lock unlock --file src/app.ts --agentId <id>');
|
|
91
|
-
console.log(' memorix lock status [--file src/app.ts] [--agentId <id>]');
|
|
92
|
-
}
|
|
93
|
-
} catch (error) {
|
|
94
|
-
emitError(error instanceof Error ? error.message : String(error), asJson);
|
|
95
|
-
}
|
|
96
|
-
},
|
|
97
|
-
});
|
|
73
|
+
emitResult(
|
|
74
|
+
{ project, locks },
|
|
75
|
+
locks.length === 0
|
|
76
|
+
? 'No active file locks.'
|
|
77
|
+
: locks
|
|
78
|
+
.map((lock) => `- ${lock.file} -> ${teamStore.getAgent(lock.locked_by)?.name ?? shortId(lock.locked_by)}`)
|
|
79
|
+
.join('\n'),
|
|
80
|
+
asJson,
|
|
81
|
+
);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
default:
|
|
86
|
+
console.log('Memorix Lock Commands');
|
|
87
|
+
console.log('');
|
|
88
|
+
console.log('Usage:');
|
|
89
|
+
console.log(' memorix lock lock --file src/app.ts --agentId <id>');
|
|
90
|
+
console.log(' memorix lock unlock --file src/app.ts --agentId <id>');
|
|
91
|
+
console.log(' memorix lock status [--file src/app.ts] [--agentId <id>]');
|
|
92
|
+
}
|
|
93
|
+
} catch (error) {
|
|
94
|
+
emitError(error instanceof Error ? error.message : String(error), asJson);
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { readFile, stat } from 'node:fs/promises';
|
|
2
|
+
|
|
3
|
+
import { MAX_VISION_IMAGE_BYTES } from '../../multimodal/image-payload.js';
|
|
4
|
+
|
|
5
|
+
function matchesImagePrefix(bytes: Buffer, prefix: number[]): boolean {
|
|
6
|
+
return prefix.every((byte, index) => bytes[index] === byte);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function detectReferenceImageMimeType(bytes: Buffer): string | undefined {
|
|
10
|
+
if (bytes.length >= 8 && matchesImagePrefix(bytes, [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a])) return 'image/png';
|
|
11
|
+
if (bytes.length >= 3 && matchesImagePrefix(bytes, [0xff, 0xd8, 0xff])) return 'image/jpeg';
|
|
12
|
+
if (bytes.length >= 6 && (bytes.subarray(0, 6).toString('ascii') === 'GIF87a' || bytes.subarray(0, 6).toString('ascii') === 'GIF89a')) return 'image/gif';
|
|
13
|
+
if (bytes.length >= 12 && bytes.subarray(0, 4).toString('ascii') === 'RIFF' && bytes.subarray(8, 12).toString('ascii') === 'WEBP') return 'image/webp';
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function readSubjectImage(filePath: string): Promise<{ data: string; mimeType: string }> {
|
|
18
|
+
const fileStats = await stat(filePath);
|
|
19
|
+
if (fileStats.size > MAX_VISION_IMAGE_BYTES) {
|
|
20
|
+
throw new Error(`Reference image exceeds the ${MAX_VISION_IMAGE_BYTES}-byte visual analysis limit`);
|
|
21
|
+
}
|
|
22
|
+
const bytes = await readFile(filePath);
|
|
23
|
+
if (bytes.length === 0) throw new Error(`Reference image is empty: ${filePath}`);
|
|
24
|
+
const mimeType = detectReferenceImageMimeType(bytes);
|
|
25
|
+
if (!mimeType) throw new Error('Reference image must be a PNG, JPEG, GIF, or WebP file');
|
|
26
|
+
return { data: bytes.toString('base64'), mimeType };
|
|
27
|
+
}
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
import { launchMediaVideoRunner, queueMiniMaxVideoGeneration } from '../../media/video-jobs.js';
|
|
19
19
|
import { launchMediaAudioRunner, queueAudioTranscription } from '../../media/audio-jobs.js';
|
|
20
20
|
import type { MediaKind } from '../../media/types.js';
|
|
21
|
+
import { readSubjectImage } from './media-image.js';
|
|
21
22
|
import {
|
|
22
23
|
emitError,
|
|
23
24
|
emitResult,
|
|
@@ -118,7 +119,7 @@ function help(): string {
|
|
|
118
119
|
' memorix media attach --asset <asset-id> [--title "Architecture"] [--text "..."]',
|
|
119
120
|
' memorix media embed --asset <asset-id>',
|
|
120
121
|
' memorix media similar --asset <asset-id> [--limit 10]',
|
|
121
|
-
' memorix media generate image --prompt "..." [--model image-01] [--attach]',
|
|
122
|
+
' memorix media generate image --prompt "..." [--model image-01] [--image ./reference.png] [--attach]',
|
|
122
123
|
' memorix media generate video --prompt "..." [--model MiniMax-H3] [--duration 5] [--ratio 16:9] [--attach]',
|
|
123
124
|
' memorix media status --job <media-job-id>',
|
|
124
125
|
' memorix media cancel --job <media-job-id>',
|
|
@@ -145,6 +146,7 @@ export default defineCommand({
|
|
|
145
146
|
concepts: { type: 'string', description: 'Comma-separated concepts when attaching' },
|
|
146
147
|
visibility: { type: 'string', description: 'Observation visibility when attaching' },
|
|
147
148
|
prompt: { type: 'string', description: 'Generation prompt' },
|
|
149
|
+
image: { type: 'string', description: 'Path to a reference image for image-to-image generation' },
|
|
148
150
|
model: { type: 'string', description: 'Provider model' },
|
|
149
151
|
provider: { type: 'string', description: 'Audio transcription provider: openai or groq' },
|
|
150
152
|
language: { type: 'string', description: 'Optional ISO-639-1 hint for audio transcription' },
|
|
@@ -223,6 +225,10 @@ export default defineCommand({
|
|
|
223
225
|
);
|
|
224
226
|
return;
|
|
225
227
|
}
|
|
228
|
+
const referenceImagePath = getValue(args.image);
|
|
229
|
+
const subjectImages = referenceImagePath
|
|
230
|
+
? [await readSubjectImage(referenceImagePath)]
|
|
231
|
+
: undefined;
|
|
226
232
|
const generated = await generateMiniMaxImages({
|
|
227
233
|
dataDir,
|
|
228
234
|
projectId: project.id,
|
|
@@ -235,6 +241,7 @@ export default defineCommand({
|
|
|
235
241
|
height: parseOptionalInteger(args.height, 'height', 1, 8_192),
|
|
236
242
|
seed: parseOptionalInteger(args.seed, 'seed', 0, 2_147_483_647),
|
|
237
243
|
promptOptimizer: args.promptOptimizer === true || args['prompt-optimizer'] === true,
|
|
244
|
+
subjectImages,
|
|
238
245
|
maxBytes: parseByteLimit(args.maxBytes ?? args['max-bytes'], 100 * 1024 * 1024),
|
|
239
246
|
});
|
|
240
247
|
const observations = args.attach === true
|