memorix 1.8.8 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/README.md +15 -1
- package/README.zh-CN.md +15 -1
- package/dist/cli/index.js +3722 -1599
- package/dist/cli/memcode.js +0 -0
- package/dist/index.js +634 -363
- package/dist/maintenance-runner.js +285 -103
- package/dist/media-audio-runner.js +285 -103
- package/dist/media-video-runner.js +285 -103
- package/dist/memcode-runtime/CHANGELOG.md +36 -0
- package/dist/sdk.js +627 -356
- package/dist/vector-backfill-runner.d.ts +4 -1
- package/dist/vector-backfill-runner.js +288 -104
- package/docs/AGENT_OPERATOR_PLAYBOOK.md +4 -2
- package/docs/API_REFERENCE.md +14 -0
- package/docs/CONFIGURATION.md +46 -0
- package/docs/DEVELOPMENT.md +2 -2
- package/docs/INTEGRATIONS.md +1 -0
- package/docs/MULTI-DEVICE-SYNC-RESEARCH.md +344 -0
- package/docs/PERFORMANCE.md +32 -0
- package/docs/README.md +4 -2
- package/docs/hooks-architecture.md +18 -13
- package/llms-full.txt +4 -1
- package/llms.txt +3 -1
- package/package.json +2 -1
- package/plugins/antigravity/memorix/plugin.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/hermes/memorix/plugin.yaml +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/cli/command-guide.ts +9 -1
- package/src/cli/commands/agent-integrations.ts +2 -1
- package/src/cli/commands/hooks-install.ts +2 -0
- package/src/cli/commands/integrate.ts +2 -0
- package/src/cli/commands/serve-http.ts +11 -0
- package/src/cli/commands/setup.ts +11 -4
- package/src/cli/commands/sync-store.ts +191 -0
- package/src/cli/commands/sync.ts +8 -0
- package/src/hooks/handler.ts +6 -0
- package/src/hooks/installers/index.ts +181 -6
- package/src/hooks/normalizer.ts +60 -1
- package/src/hooks/types.ts +3 -1
- package/src/integrations/registry.ts +9 -0
- package/src/memory/observations.ts +15 -2
- package/src/runtime/maintenance-targets.ts +6 -0
- package/src/runtime/vector-backfill-runner.ts +185 -7
- package/src/store/orama-store.ts +85 -102
- package/src/sync/adapters/fs.ts +179 -0
- package/src/sync/adapters/github.ts +258 -0
- package/src/sync/adapters/object-store.ts +267 -0
- package/src/sync/adapters/postgres.ts +269 -0
- package/src/sync/engine.ts +353 -0
- package/src/sync/journal.ts +162 -0
- package/src/sync/merge.ts +116 -0
- package/src/sync/namespace.ts +15 -0
- package/src/sync/paging.ts +45 -0
- package/src/sync/policy.ts +37 -0
- package/src/sync/remote-factory.ts +49 -0
- package/src/sync/store-port.ts +288 -0
- package/src/sync/types.ts +183 -0
package/server.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"url": "https://github.com/AVIDS2/memorix",
|
|
8
8
|
"source": "github"
|
|
9
9
|
},
|
|
10
|
-
"version": "1.
|
|
10
|
+
"version": "1.9.0",
|
|
11
11
|
"websiteUrl": "https://mem.rglens.com",
|
|
12
12
|
"icons": [
|
|
13
13
|
{
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
{
|
|
23
23
|
"registryType": "npm",
|
|
24
24
|
"identifier": "memorix",
|
|
25
|
-
"version": "1.
|
|
25
|
+
"version": "1.9.0",
|
|
26
26
|
"runtimeHint": "npx",
|
|
27
27
|
"packageArguments": [
|
|
28
28
|
{
|
package/src/cli/command-guide.ts
CHANGED
|
@@ -209,7 +209,15 @@ const GUIDES: Record<string, CliCommandGuide> = {
|
|
|
209
209
|
},
|
|
210
210
|
sync: {
|
|
211
211
|
summary: 'Synchronize agent rules and workspace artifacts through explicit routes.',
|
|
212
|
-
usage: [
|
|
212
|
+
usage: [
|
|
213
|
+
'memorix sync rules --action status',
|
|
214
|
+
'memorix sync workspace --action scan',
|
|
215
|
+
'memorix sync store status --json',
|
|
216
|
+
'memorix sync store push|pull [--dry] [--json]',
|
|
217
|
+
'memorix sync store compact --dry --through device=sequence',
|
|
218
|
+
'memorix sync store device rotate',
|
|
219
|
+
],
|
|
220
|
+
notes: ['Store sync is opt-in, project-scoped, and never copies the live SQLite database.'],
|
|
213
221
|
},
|
|
214
222
|
ingest: {
|
|
215
223
|
summary: 'Ingest Git and image evidence into project memory.',
|
|
@@ -126,6 +126,7 @@ const GUIDANCE_AGENTS = new Set<AgentName>([
|
|
|
126
126
|
'trae',
|
|
127
127
|
'dsh',
|
|
128
128
|
'workbuddy',
|
|
129
|
+
'grok',
|
|
129
130
|
]);
|
|
130
131
|
|
|
131
132
|
const CODEX_PLUGIN_NAME = 'memorix';
|
|
@@ -167,7 +168,7 @@ function worstStatus(statuses: AgentIntegrationStatus[]): AgentIntegrationStatus
|
|
|
167
168
|
}
|
|
168
169
|
|
|
169
170
|
function isMcpConfigAgent(agent: AgentName): agent is McpConfigAgent {
|
|
170
|
-
return agent !== 'pi';
|
|
171
|
+
return agent !== 'pi' && agent !== 'grok';
|
|
171
172
|
}
|
|
172
173
|
|
|
173
174
|
function isActionableMcpRepairIssue(issue: string): boolean {
|
|
@@ -166,6 +166,7 @@ function getAgentLabel(agent: string): string {
|
|
|
166
166
|
trae: 'Trae',
|
|
167
167
|
dsh: 'DeepSeek Harness',
|
|
168
168
|
workbuddy: 'WorkBuddy',
|
|
169
|
+
grok: 'Grok Build',
|
|
169
170
|
};
|
|
170
171
|
return labels[agent] || agent;
|
|
171
172
|
}
|
|
@@ -186,6 +187,7 @@ function getAgentHint(agent: string): string {
|
|
|
186
187
|
trae: '.trae/rules/project_rules.md (rules only, no hooks system)',
|
|
187
188
|
dsh: '~/.dsh/cordis.patch.yml MCP row via `memorix setup --agent dsh` (no hooks system)',
|
|
188
189
|
workbuddy: '~/.workbuddy/mcp.json MCP row via `memorix setup --agent workbuddy` (no hooks system)',
|
|
190
|
+
grok: '~/.grok/hooks/memorix.json (own file, not RuleSync dest)',
|
|
189
191
|
codex: 'Codex plugin hooks; use `memorix setup --agent codex`',
|
|
190
192
|
};
|
|
191
193
|
return hints[agent] || '';
|
|
@@ -116,6 +116,7 @@ function getAgentLabel(agent: string): string {
|
|
|
116
116
|
trae: 'Trae',
|
|
117
117
|
dsh: 'DeepSeek Harness',
|
|
118
118
|
workbuddy: 'WorkBuddy',
|
|
119
|
+
grok: 'Grok Build',
|
|
119
120
|
};
|
|
120
121
|
return labels[agent] || agent;
|
|
121
122
|
}
|
|
@@ -134,6 +135,7 @@ function getAgentHint(agent: string): string {
|
|
|
134
135
|
trae: 'rules',
|
|
135
136
|
dsh: 'MCP patch + AGENTS.md + skills',
|
|
136
137
|
workbuddy: 'MCP + AGENTS.md',
|
|
138
|
+
grok: 'hooks file (~/.grok/hooks/memorix.json)',
|
|
137
139
|
};
|
|
138
140
|
return hints[agent] || 'integration files';
|
|
139
141
|
}
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
|
|
22
22
|
import { defineCommand } from 'citty';
|
|
23
23
|
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
24
|
+
import { getHeapStatistics } from 'node:v8';
|
|
24
25
|
import type { ObservationStore } from '../../store/obs-store.js';
|
|
25
26
|
import { resolveToolProfile } from '../../server/tool-profile.js';
|
|
26
27
|
import { scopeKnowledgeGraphToProject } from '../../memory/graph-scope.js';
|
|
@@ -1714,6 +1715,8 @@ export default defineCommand({
|
|
|
1714
1715
|
// This is the readiness signal for background start / status.
|
|
1715
1716
|
if (url.pathname === '/health') {
|
|
1716
1717
|
const { getEmbeddingRuntimeHealth } = await import('../../embedding/provider.js');
|
|
1718
|
+
const memory = process.memoryUsage();
|
|
1719
|
+
const heap = getHeapStatistics();
|
|
1717
1720
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1718
1721
|
res.end(JSON.stringify({
|
|
1719
1722
|
status: 'ok',
|
|
@@ -1725,6 +1728,14 @@ export default defineCommand({
|
|
|
1725
1728
|
projectId: defaultProject?.id ?? '__unresolved__',
|
|
1726
1729
|
projectName: defaultProject?.name ?? null,
|
|
1727
1730
|
readyAt: httpReadyAt,
|
|
1731
|
+
memory: {
|
|
1732
|
+
rss: memory.rss,
|
|
1733
|
+
heapUsed: memory.heapUsed,
|
|
1734
|
+
heapTotal: memory.heapTotal,
|
|
1735
|
+
external: memory.external,
|
|
1736
|
+
arrayBuffers: memory.arrayBuffers,
|
|
1737
|
+
heapLimit: heap.heap_size_limit,
|
|
1738
|
+
},
|
|
1728
1739
|
embedding: getEmbeddingRuntimeHealth(),
|
|
1729
1740
|
}));
|
|
1730
1741
|
return;
|
|
@@ -195,6 +195,7 @@ const SUPPORTED_SETUP_AGENTS: AgentName[] = [
|
|
|
195
195
|
'dsh',
|
|
196
196
|
'workbuddy',
|
|
197
197
|
'pi',
|
|
198
|
+
'grok',
|
|
198
199
|
];
|
|
199
200
|
|
|
200
201
|
export function getSetupAgentTargets(): AgentName[] {
|
|
@@ -211,7 +212,9 @@ export function buildSetupPlan(options: {
|
|
|
211
212
|
plugin?: boolean;
|
|
212
213
|
global?: boolean;
|
|
213
214
|
}): SetupPlan {
|
|
214
|
-
|
|
215
|
+
// Grok owns its MCP configuration through `grok mcp`; Memorix setup only
|
|
216
|
+
// installs the independent hooks/rules integration for this target.
|
|
217
|
+
const mcp = options.agent === 'grok' ? 'none' : options.mcp ?? 'stdio';
|
|
215
218
|
const actions: SetupAction[] = [];
|
|
216
219
|
const isPackageOwnedAgent = options.agent !== 'all' && PACKAGE_OWNED_INTEGRATION_AGENTS.has(options.agent);
|
|
217
220
|
const includeHooks = options.hooks ?? true;
|
|
@@ -388,7 +391,7 @@ function relativePosix(from: string, to: string): string {
|
|
|
388
391
|
return rel;
|
|
389
392
|
}
|
|
390
393
|
|
|
391
|
-
export type McpConfigAgent = Exclude<AgentName, 'pi'>;
|
|
394
|
+
export type McpConfigAgent = Exclude<AgentName, 'pi' | 'grok'>;
|
|
392
395
|
|
|
393
396
|
export function getMcpAdapter(agent: McpConfigAgent): MCPConfigAdapter {
|
|
394
397
|
const adapters: Record<McpConfigAgent, MCPConfigAdapter> = {
|
|
@@ -1453,7 +1456,7 @@ export async function installAgentSetup(agent: AgentName, plan: SetupPlan, globa
|
|
|
1453
1456
|
const hasCodeBuddyPlugin = plan.actions.includes('codebuddy-plugin') && agent === 'codebuddy';
|
|
1454
1457
|
// Codex's supported default is the user-level plugin. Never create a
|
|
1455
1458
|
// project-local .codex/config.toml as an implicit fallback.
|
|
1456
|
-
const wantsMcpConfig = plan.mcp !== 'none' && agent !== 'pi' && !(agent === 'codex' && !global);
|
|
1459
|
+
const wantsMcpConfig = plan.mcp !== 'none' && agent !== 'pi' && agent !== 'grok' && !(agent === 'codex' && !global);
|
|
1457
1460
|
|
|
1458
1461
|
if (PLUGIN_PACKAGE_AGENTS.has(agent) && plan.includePlugin && !global) {
|
|
1459
1462
|
p.log.info(`${agent}: project setup leaves user-level plugins untouched. Run \`memorix setup --agent ${agent} --global\` to install its plugin, skills, and lifecycle hooks.`);
|
|
@@ -1633,6 +1636,9 @@ export async function installAgentSetup(agent: AgentName, plan: SetupPlan, globa
|
|
|
1633
1636
|
|
|
1634
1637
|
if (agent === 'pi') {
|
|
1635
1638
|
p.log.info('pi: Pi has no MCP config lane in the current CLI; use the installed package extension and skill.');
|
|
1639
|
+
} else if (agent === 'grok') {
|
|
1640
|
+
p.log.info('grok: lifecycle hooks write ~/.grok/hooks/memorix.json. MCP stays host-owned (do not write config.toml).');
|
|
1641
|
+
if (!global) p.log.info('grok: project hooks require `/hooks-trust` or launching Grok with `--trust`.');
|
|
1636
1642
|
} else if (plan.mcp === 'stdio') {
|
|
1637
1643
|
p.log.info(`${agent}: MCP server command is \`memorix serve\``);
|
|
1638
1644
|
} else if (plan.mcp === 'http') {
|
|
@@ -1669,7 +1675,7 @@ export default defineCommand({
|
|
|
1669
1675
|
args: {
|
|
1670
1676
|
agent: {
|
|
1671
1677
|
type: 'string',
|
|
1672
|
-
description: 'Target agent (claude, codex, codebuddy, opencode, cursor, windsurf, copilot, gemini-cli, antigravity, openclaw, hermes, omp, kiro, trae, dsh, pi, all)',
|
|
1678
|
+
description: 'Target agent (claude, codex, codebuddy, opencode, cursor, windsurf, copilot, gemini-cli, antigravity, openclaw, hermes, omp, kiro, trae, dsh, pi, grok, all)',
|
|
1673
1679
|
required: false,
|
|
1674
1680
|
},
|
|
1675
1681
|
mcp: {
|
|
@@ -1738,6 +1744,7 @@ export default defineCommand({
|
|
|
1738
1744
|
{ value: 'codex', label: 'Codex', hint: 'plugin package + marketplace + MCP' },
|
|
1739
1745
|
{ value: 'copilot', label: 'GitHub Copilot CLI', hint: 'plugin package + hooks + MCP' },
|
|
1740
1746
|
{ value: 'cursor', label: 'Cursor', hint: 'MCP + rules + skills' },
|
|
1747
|
+
{ value: 'grok', label: 'Grok Build', hint: 'hooks file; MCP stays host-owned' },
|
|
1741
1748
|
{ value: 'gemini-cli', label: 'Gemini CLI', hint: 'extension + MCP' },
|
|
1742
1749
|
{ value: 'openclaw', label: 'OpenClaw', hint: 'bundle + hook pack + skills + MCP' },
|
|
1743
1750
|
{ value: 'hermes', label: 'Hermes Agent', hint: 'plugin + hooks + commands + skills + MCP' },
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `memorix sync store <push|pull|status>` — multi-device store replication.
|
|
3
|
+
*
|
|
4
|
+
* Separated from `memorix sync rules|workspace` (cross-agent rules) so the two
|
|
5
|
+
* meanings of "sync" never collide. Store sync is opt-in and provider-agnostic;
|
|
6
|
+
* the remote is selected by `MEMORIX_SYNC_PROVIDER`.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { defineCommand } from 'citty';
|
|
10
|
+
|
|
11
|
+
import { getCliProjectContext, emitError, emitResult } from './operator-shared.js';
|
|
12
|
+
import { getObservationStore, initObservationStore } from '../../store/obs-store.js';
|
|
13
|
+
import { runSync } from '../../sync/engine.js';
|
|
14
|
+
import { createSqliteSyncStore } from '../../sync/store-port.js';
|
|
15
|
+
import { resolveSyncConfig, createRemote } from '../../sync/remote-factory.js';
|
|
16
|
+
import type { SyncReport } from '../../sync/types.js';
|
|
17
|
+
|
|
18
|
+
function renderReport(report: SyncReport): string {
|
|
19
|
+
const lines = [
|
|
20
|
+
`Store sync (${report.remote}) — device ${report.deviceId}${report.dryRun ? ' [dry-run]' : ''}`,
|
|
21
|
+
`- Pushed changes: ${report.pushed}`,
|
|
22
|
+
`- Pulled batches: ${report.pulledBatches}`,
|
|
23
|
+
`- Applied upserts: ${report.applied}`,
|
|
24
|
+
`- Applied deletes: ${report.tombstoned}`,
|
|
25
|
+
`- Skipped (older): ${report.skipped}`,
|
|
26
|
+
`- Excluded locally: ${report.excluded}`,
|
|
27
|
+
`- Conflicts recorded: ${report.conflicts}`,
|
|
28
|
+
`- Pending outbox: ${report.pending}`,
|
|
29
|
+
];
|
|
30
|
+
if (report.dryRun && report.decisions.length > 0) {
|
|
31
|
+
lines.push('', 'Decisions:');
|
|
32
|
+
for (const d of report.decisions) {
|
|
33
|
+
lines.push(` ${d.syncKey} ${d.kind} -> ${d.outcome} (${d.reason})`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return lines.join('\n');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export default defineCommand({
|
|
40
|
+
meta: {
|
|
41
|
+
name: 'sync-store',
|
|
42
|
+
description: 'Replicate the local observation store across your devices (opt-in, provider-agnostic)',
|
|
43
|
+
},
|
|
44
|
+
args: {
|
|
45
|
+
dry: { type: 'boolean', description: 'Preview changes without writing anything', default: false },
|
|
46
|
+
json: { type: 'boolean', description: 'Emit JSON', default: false },
|
|
47
|
+
'push-only': { type: 'boolean', description: 'Only push local changes', default: false },
|
|
48
|
+
'pull-only': { type: 'boolean', description: 'Only pull remote changes', default: false },
|
|
49
|
+
through: { type: 'string', description: 'Compaction cutoff: device=sequence,device=sequence' },
|
|
50
|
+
yes: { type: 'boolean', description: 'Confirm destructive remote compaction', default: false },
|
|
51
|
+
},
|
|
52
|
+
run: async ({ args }) => {
|
|
53
|
+
const asJson = Boolean(args.json);
|
|
54
|
+
const action = (args._ as string[])?.[0] || 'status';
|
|
55
|
+
if (action === 'device') {
|
|
56
|
+
const { project, dataDir } = await getCliProjectContext();
|
|
57
|
+
await initObservationStore(dataDir);
|
|
58
|
+
const syncStore = createSqliteSyncStore(dataDir, getObservationStore(), project.id);
|
|
59
|
+
const subaction = (args._ as string[])?.[1] || '';
|
|
60
|
+
if (subaction !== 'rotate') {
|
|
61
|
+
emitError('expected "memorix sync store device rotate"', asJson);
|
|
62
|
+
process.exitCode = 2;
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const deviceId = syncStore.rotateDevice();
|
|
66
|
+
emitResult({ project: project.id, deviceId, rotated: true }, `Sync device identity rotated: ${deviceId}`, asJson);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (action !== 'push' && action !== 'pull' && action !== 'status' && action !== 'compact') {
|
|
70
|
+
emitError(`unknown action "${action}" (expected push|pull|status|compact|device rotate)`, asJson);
|
|
71
|
+
process.exitCode = 2;
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
let config;
|
|
76
|
+
try {
|
|
77
|
+
config = resolveSyncConfig();
|
|
78
|
+
} catch (err) {
|
|
79
|
+
emitError((err as Error).message, asJson);
|
|
80
|
+
process.exitCode = 2;
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (!config.enabled || !config.provider) {
|
|
85
|
+
emitResult(
|
|
86
|
+
{ enabled: false },
|
|
87
|
+
'Store sync is disabled. Set MEMORIX_SYNC_PROVIDER=fs|github|s3|postgres to enable it.',
|
|
88
|
+
asJson,
|
|
89
|
+
);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const { project, dataDir } = await getCliProjectContext();
|
|
94
|
+
await initObservationStore(dataDir);
|
|
95
|
+
const obsStore = getObservationStore();
|
|
96
|
+
const syncStore = createSqliteSyncStore(dataDir, obsStore, project.id);
|
|
97
|
+
|
|
98
|
+
if (action === 'compact') {
|
|
99
|
+
if (!args.yes && !args.dry) {
|
|
100
|
+
emitError('remote compaction deletes relay events; repeat with --yes after specifying --through device=sequence', asJson);
|
|
101
|
+
process.exitCode = 2;
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const raw = String(args.through ?? '');
|
|
105
|
+
const through: Record<string, number> = {};
|
|
106
|
+
for (const item of raw.split(',').map((value) => value.trim()).filter(Boolean)) {
|
|
107
|
+
const [device, sequence] = item.split('=', 2);
|
|
108
|
+
const parsed = Number(sequence);
|
|
109
|
+
if (!device || !Number.isSafeInteger(parsed) || parsed < 0) {
|
|
110
|
+
emitError('invalid --through; expected device=sequence[,device=sequence]', asJson);
|
|
111
|
+
process.exitCode = 2;
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
through[device] = parsed;
|
|
115
|
+
}
|
|
116
|
+
if (Object.keys(through).length === 0) {
|
|
117
|
+
emitError('--through is required for compaction', asJson);
|
|
118
|
+
process.exitCode = 2;
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
let remote;
|
|
122
|
+
try {
|
|
123
|
+
remote = await createRemote(config.provider, syncStore.namespace());
|
|
124
|
+
} catch (err) {
|
|
125
|
+
emitError((err as Error).message, asJson);
|
|
126
|
+
process.exitCode = 1;
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
try {
|
|
130
|
+
await remote.init({ create: false });
|
|
131
|
+
const result = await remote.compact(through, { dryRun: Boolean(args.dry) });
|
|
132
|
+
emitResult({ project: project.id, through, ...result, dryRun: Boolean(args.dry) }, `Compaction candidates: ${result.candidates}; deleted: ${result.deleted}`, asJson);
|
|
133
|
+
} catch (err) {
|
|
134
|
+
emitError((err as Error).message, asJson);
|
|
135
|
+
process.exitCode = 1;
|
|
136
|
+
} finally {
|
|
137
|
+
await remote.close();
|
|
138
|
+
}
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (action === 'status') {
|
|
143
|
+
let remote;
|
|
144
|
+
try {
|
|
145
|
+
remote = await createRemote(config.provider, syncStore.namespace());
|
|
146
|
+
} catch (err) {
|
|
147
|
+
emitError((err as Error).message, asJson);
|
|
148
|
+
process.exitCode = 1;
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
const report = await runSync(syncStore, remote, {
|
|
153
|
+
deviceId: syncStore.deviceId(),
|
|
154
|
+
dryRun: true,
|
|
155
|
+
push: true,
|
|
156
|
+
pull: true,
|
|
157
|
+
});
|
|
158
|
+
emitResult({ project: project.id, ...report }, renderReport(report), asJson);
|
|
159
|
+
} catch (err) {
|
|
160
|
+
emitError((err as Error).message, asJson);
|
|
161
|
+
process.exitCode = 1;
|
|
162
|
+
}
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const doPush = action === 'push' || !args['pull-only'];
|
|
167
|
+
const doPull = action === 'pull' || !args['push-only'];
|
|
168
|
+
|
|
169
|
+
let remote;
|
|
170
|
+
try {
|
|
171
|
+
remote = await createRemote(config.provider, syncStore.namespace());
|
|
172
|
+
} catch (err) {
|
|
173
|
+
emitError((err as Error).message, asJson);
|
|
174
|
+
process.exitCode = 1;
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
const report = await runSync(syncStore, remote, {
|
|
180
|
+
deviceId: syncStore.deviceId(),
|
|
181
|
+
dryRun: Boolean(args.dry),
|
|
182
|
+
push: doPush,
|
|
183
|
+
pull: doPull,
|
|
184
|
+
});
|
|
185
|
+
emitResult({ project: project.id, ...report }, renderReport(report), asJson);
|
|
186
|
+
} catch (err) {
|
|
187
|
+
emitError((err as Error).message, asJson);
|
|
188
|
+
process.exitCode = 1;
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
});
|
package/src/cli/commands/sync.ts
CHANGED
|
@@ -51,6 +51,14 @@ export default defineCommand({
|
|
|
51
51
|
},
|
|
52
52
|
run: async ({ args }) => {
|
|
53
53
|
const section = (args._ as string[])?.[0] || '';
|
|
54
|
+
|
|
55
|
+
// Store replication is a distinct concern from rules/workspace sync.
|
|
56
|
+
if (section === 'store') {
|
|
57
|
+
const storeCmd = (await import('./sync-store.js')).default;
|
|
58
|
+
const rest = (args._ as string[]).slice(1);
|
|
59
|
+
await storeCmd.run!({ args: { ...args, _: rest } } as never);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
54
62
|
const asJson = !!args.json;
|
|
55
63
|
|
|
56
64
|
if (section === 'rules' || section === 'workspace') {
|
package/src/hooks/handler.ts
CHANGED
|
@@ -737,6 +737,12 @@ export function formatHookOutput(
|
|
|
737
737
|
};
|
|
738
738
|
}
|
|
739
739
|
|
|
740
|
+
// Grok's documented hook contract only gives passive hooks a success exit
|
|
741
|
+
// signal; it does not define a Claude-style additionalContext response.
|
|
742
|
+
// Returning an empty object keeps Memorix capture-only and never changes
|
|
743
|
+
// Grok's prompt, tool, or stop decisions.
|
|
744
|
+
if (agent === 'grok') return {};
|
|
745
|
+
|
|
740
746
|
const finalOutput: Record<string, unknown> = { ...output };
|
|
741
747
|
const hookSpecificOutputEvents = new Set([
|
|
742
748
|
'PreToolUse',
|