memorix 1.2.0 → 1.2.2
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 +30 -1
- package/README.md +18 -4
- package/README.zh-CN.md +18 -4
- package/TEAM.md +86 -86
- package/dist/cli/index.js +15919 -14055
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +1997 -1021
- package/dist/index.js.map +1 -1
- package/dist/maintenance-runner.d.ts +1 -1
- package/dist/maintenance-runner.js +8481 -8005
- package/dist/maintenance-runner.js.map +1 -1
- package/dist/memcode-runtime/CHANGELOG.md +30 -1
- package/dist/sdk.d.ts +7 -2
- package/dist/sdk.js +2022 -1024
- package/dist/sdk.js.map +1 -1
- package/dist/types.d.ts +49 -1
- package/dist/types.js.map +1 -1
- package/docs/1.2.2-MEMORY-CONTROL-PLANE.md +434 -0
- package/docs/AGENT_OPERATOR_PLAYBOOK.md +4 -0
- package/docs/API_REFERENCE.md +27 -5
- package/docs/DESIGN_DECISIONS.md +357 -357
- package/docs/DEVELOPMENT.md +4 -0
- package/docs/README.md +1 -1
- package/docs/SETUP.md +7 -1
- package/docs/dev-log/progress.txt +91 -11
- package/docs/knowledge/workflows/memorix-release.md +57 -0
- package/package.json +1 -1
- package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
- package/src/audit/index.ts +156 -156
- package/src/cli/command-guide.ts +192 -0
- package/src/cli/commands/audit-list.ts +89 -89
- package/src/cli/commands/audit.ts +9 -4
- package/src/cli/commands/background.ts +659 -659
- package/src/cli/commands/cleanup.ts +5 -1
- package/src/cli/commands/codegraph.ts +17 -8
- package/src/cli/commands/context.ts +3 -2
- package/src/cli/commands/doctor.ts +4 -2
- package/src/cli/commands/explain.ts +9 -3
- package/src/cli/commands/formation.ts +48 -48
- package/src/cli/commands/git-hook-install.ts +111 -111
- package/src/cli/commands/handoff.ts +75 -61
- package/src/cli/commands/hooks-status.ts +63 -63
- package/src/cli/commands/identity.ts +116 -0
- package/src/cli/commands/ingest-commit.ts +153 -153
- package/src/cli/commands/ingest-image.ts +71 -69
- package/src/cli/commands/ingest-log.ts +180 -180
- package/src/cli/commands/ingest.ts +44 -44
- package/src/cli/commands/integrate-shared.ts +15 -15
- package/src/cli/commands/knowledge.ts +40 -0
- package/src/cli/commands/lock.ts +93 -92
- package/src/cli/commands/memory.ts +58 -21
- package/src/cli/commands/message.ts +123 -118
- package/src/cli/commands/operator-shared.ts +98 -3
- package/src/cli/commands/poll.ts +74 -64
- 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 +135 -121
- package/src/cli/commands/retention.ts +9 -4
- package/src/cli/commands/serve-http.ts +22 -43
- package/src/cli/commands/serve-shared.ts +118 -118
- package/src/cli/commands/session.ts +29 -3
- package/src/cli/commands/setup.ts +9 -3
- package/src/cli/commands/skills.ts +124 -119
- package/src/cli/commands/status.ts +4 -3
- package/src/cli/commands/task.ts +193 -184
- package/src/cli/commands/team.ts +14 -10
- package/src/cli/commands/transfer.ts +108 -55
- package/src/cli/commands/uninstall-project-artifacts.ts +85 -85
- package/src/cli/identity.ts +89 -0
- package/src/cli/index.ts +96 -19
- package/src/cli/invocation.ts +115 -0
- 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/chat-service.ts +41 -18
- package/src/cli/tui/data.ts +23 -44
- package/src/cli/tui/index.ts +41 -41
- package/src/cli/tui/markdown-render.tsx +371 -371
- package/src/cli/tui/operator-context.ts +60 -0
- package/src/cli/tui/use-mouse.ts +157 -157
- package/src/cli/tui/useNavigation.ts +56 -56
- package/src/cli/tui/views/MemoryView.tsx +10 -8
- package/src/cli/update-checker.ts +211 -211
- package/src/cli/version.ts +7 -7
- package/src/cli/workbench.ts +1 -1
- package/src/codegraph/auto-context.ts +34 -17
- package/src/codegraph/context-pack.ts +1 -0
- package/src/codegraph/current-facts.ts +19 -1
- package/src/codegraph/project-context.ts +2 -0
- package/src/codegraph/task-lens.ts +49 -5
- package/src/compact/engine.ts +26 -10
- package/src/compact/index-format.ts +25 -2
- package/src/compact/token-budget.ts +74 -74
- package/src/dashboard/project-classification.ts +64 -64
- package/src/dashboard/server.ts +58 -52
- package/src/embedding/fastembed-provider.ts +142 -142
- package/src/embedding/transformers-provider.ts +111 -111
- package/src/git/extractor.ts +209 -209
- package/src/git/hooks-path.ts +85 -85
- package/src/hooks/admission.ts +117 -0
- package/src/hooks/handler.ts +98 -91
- package/src/hooks/pattern-detector.ts +173 -173
- package/src/hooks/significance-filter.ts +250 -250
- package/src/knowledge/claims.ts +51 -1
- package/src/knowledge/context-assembly.ts +97 -0
- package/src/knowledge/types.ts +1 -0
- package/src/knowledge/workflows.ts +34 -3
- package/src/knowledge/workset.ts +179 -10
- package/src/llm/memory-manager.ts +328 -328
- package/src/llm/provider.ts +885 -885
- package/src/llm/quality.ts +248 -248
- package/src/memory/admission.ts +57 -0
- package/src/memory/attribution-guard.ts +249 -249
- package/src/memory/auto-relations.ts +21 -0
- package/src/memory/consolidation.ts +13 -2
- package/src/memory/disclosure-policy.ts +140 -135
- package/src/memory/entity-extractor.ts +197 -197
- package/src/memory/export-import.ts +11 -3
- 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-context.ts +8 -2
- package/src/memory/graph-scope.ts +46 -0
- package/src/memory/graph.ts +197 -197
- package/src/memory/observations.ts +162 -4
- package/src/memory/quality-audit.ts +2 -0
- package/src/memory/refs.ts +94 -94
- package/src/memory/retention.ts +22 -2
- package/src/memory/secret-filter.ts +79 -79
- package/src/memory/session.ts +5 -2
- package/src/memory/visibility.ts +80 -0
- package/src/multimodal/image-loader.ts +143 -143
- 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/spawn-helper.ts +286 -286
- 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 +378 -340
- 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/orchestrate/verify-gate.ts +33 -10
- package/src/orchestrate/worktree.ts +232 -232
- package/src/project/aliases.ts +374 -374
- package/src/project/detector.ts +268 -268
- package/src/rules/adapters/claude-code.ts +99 -99
- package/src/rules/adapters/codex.ts +97 -97
- package/src/rules/adapters/copilot.ts +124 -124
- package/src/rules/adapters/cursor.ts +114 -114
- package/src/rules/adapters/kiro.ts +126 -126
- package/src/rules/adapters/trae.ts +56 -56
- package/src/rules/adapters/windsurf.ts +83 -83
- package/src/rules/syncer.ts +235 -235
- package/src/runtime/control-plane-maintenance.ts +1 -0
- package/src/runtime/isolated-maintenance.ts +1 -0
- package/src/runtime/lifecycle.ts +18 -0
- package/src/runtime/maintenance-jobs.ts +1 -0
- package/src/runtime/maintenance-runner.ts +2 -0
- package/src/runtime/project-maintenance.ts +89 -0
- package/src/sdk.ts +334 -304
- package/src/search/intent-detector.ts +289 -289
- package/src/search/query-expansion.ts +52 -52
- package/src/server/formation-timeout.ts +27 -27
- package/src/server.ts +334 -93
- 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/orama-store.ts +61 -6
- 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 +23 -1
- package/src/store/sqlite-store.ts +12 -2
- package/src/team/event-bus.ts +76 -76
- package/src/team/file-locks.ts +173 -173
- package/src/team/handoff.ts +168 -161
- package/src/team/messages.ts +203 -203
- package/src/team/poll.ts +132 -132
- package/src/team/tasks.ts +211 -211
- package/src/types.ts +51 -0
- package/src/wiki/generator.ts +2 -0
- 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/workspace/workflow-sync.ts +131 -131
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { defineCommand } from 'citty';
|
|
2
2
|
import { CodeGraphStore } from '../../codegraph/store.js';
|
|
3
3
|
import { ClaimStore } from '../../knowledge/claim-store.js';
|
|
4
|
+
import { reviewClaim } from '../../knowledge/claims.js';
|
|
4
5
|
import { applyKnowledgeProposal, compileKnowledgeWorkspace, lintKnowledgeWorkspace } from '../../knowledge/wiki.js';
|
|
5
6
|
import { initializeKnowledgeWorkspace, loadKnowledgeWorkspace } from '../../knowledge/workspace.js';
|
|
6
7
|
import { KnowledgeWorkspaceStore } from '../../knowledge/workspace-store.js';
|
|
@@ -28,6 +29,8 @@ function usage(): string {
|
|
|
28
29
|
' memorix knowledge init [--mode local]',
|
|
29
30
|
' memorix knowledge init --mode versioned --path C:\\project\\docs\\knowledge',
|
|
30
31
|
' memorix knowledge status',
|
|
32
|
+
' memorix knowledge claims',
|
|
33
|
+
' memorix knowledge review --id <claim-id> --review approved --detail "checked current source evidence"',
|
|
31
34
|
' memorix knowledge compile',
|
|
32
35
|
' memorix knowledge lint',
|
|
33
36
|
' memorix knowledge apply --proposal <id> [--force]',
|
|
@@ -57,6 +60,11 @@ function workflowVerdict(value: unknown): 'passed' | 'failed' | 'not-run' | unde
|
|
|
57
60
|
throw new Error('workflow verdict must be passed, failed, or not-run');
|
|
58
61
|
}
|
|
59
62
|
|
|
63
|
+
function claimReviewState(value: unknown): 'approved' | 'rejected' {
|
|
64
|
+
if (value === 'approved' || value === 'rejected') return value;
|
|
65
|
+
throw new Error('claim review must be approved or rejected');
|
|
66
|
+
}
|
|
67
|
+
|
|
60
68
|
function evidenceList(value: unknown): string[] {
|
|
61
69
|
if (typeof value !== 'string' || !value.trim()) return [];
|
|
62
70
|
return [...new Set(value.split(',').map(item => item.trim()).filter(Boolean))];
|
|
@@ -81,6 +89,8 @@ export default defineCommand({
|
|
|
81
89
|
failure: { type: 'string', description: 'Sanitized failure reason for a workflow run' },
|
|
82
90
|
snapshot: { type: 'string', description: 'Starting code snapshot id for a workflow run' },
|
|
83
91
|
evidence: { type: 'string', description: 'Comma-separated evidence ids selected for a workflow run' },
|
|
92
|
+
review: { type: 'string', description: 'Claim review verdict: approved or rejected' },
|
|
93
|
+
detail: { type: 'string', description: 'Evidence check performed for a claim review' },
|
|
84
94
|
json: { type: 'boolean', description: 'Emit machine-readable JSON output' },
|
|
85
95
|
},
|
|
86
96
|
run: async ({ args }) => {
|
|
@@ -120,6 +130,36 @@ export default defineCommand({
|
|
|
120
130
|
const workspaceStore = new KnowledgeWorkspaceStore();
|
|
121
131
|
await workspaceStore.init(dataDir);
|
|
122
132
|
|
|
133
|
+
if (action === 'claims') {
|
|
134
|
+
const claims = claimStore.listClaims(project.id, { limit: 100 });
|
|
135
|
+
emitResult(
|
|
136
|
+
{ project, workspace, claims },
|
|
137
|
+
'Claims: ' + claims.length + ' total, ' + claims.filter(claim => claim.reviewState === 'needs-review').length + ' awaiting review.',
|
|
138
|
+
asJson,
|
|
139
|
+
);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (action === 'review') {
|
|
144
|
+
const claimId = requiredText(args.id, 'claim id');
|
|
145
|
+
const existing = claimStore.getClaim(claimId);
|
|
146
|
+
if (!existing || existing.projectId !== project.id) {
|
|
147
|
+
emitError('Claim was not found for this knowledge workspace.', asJson);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const claim = reviewClaim(claimStore, {
|
|
151
|
+
claimId,
|
|
152
|
+
reviewState: claimReviewState(args.review),
|
|
153
|
+
detail: requiredText(args.detail, 'review detail'),
|
|
154
|
+
});
|
|
155
|
+
emitResult(
|
|
156
|
+
{ project, workspace, claim },
|
|
157
|
+
'Claim ' + claim.id + ' marked ' + claim.reviewState + '.',
|
|
158
|
+
asJson,
|
|
159
|
+
);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
123
163
|
if (action === 'workflow') {
|
|
124
164
|
const workflowAction = (positional[1] || 'list').toLowerCase();
|
|
125
165
|
const workflowStore = new WorkflowStore();
|
package/src/cli/commands/lock.ts
CHANGED
|
@@ -1,96 +1,97 @@
|
|
|
1
|
-
import { defineCommand } from 'citty';
|
|
2
|
-
import { emitError, emitResult, getCliProjectContext, 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 {
|
|
19
|
-
const { project, teamStore } = await getCliProjectContext();
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
1
|
+
import { defineCommand } from 'citty';
|
|
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 {
|
|
19
|
+
const { project, teamStore, identity } = await getCliProjectContext();
|
|
20
|
+
const agentId = resolveCliActorId(args.agentId, identity);
|
|
21
|
+
|
|
22
|
+
switch (action) {
|
|
23
|
+
case 'lock': {
|
|
24
|
+
if (!args.file || !agentId) {
|
|
25
|
+
emitError('file and an active CLI identity or agentId are required for "memorix lock lock"', asJson);
|
|
25
26
|
return;
|
|
26
27
|
}
|
|
27
|
-
const result = teamStore.acquireLock(project.id, args.file as string,
|
|
28
|
-
if (!result.success) {
|
|
29
|
-
emitError(`File is already locked by ${shortId(result.lockedBy)}`, asJson);
|
|
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(
|
|
34
|
+
{ project, file: args.file, lockedBy: agentId },
|
|
35
|
+
`Locked: ${args.file as string}`,
|
|
36
|
+
asJson,
|
|
37
|
+
);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
case 'unlock': {
|
|
42
|
+
if (!args.file || !agentId) {
|
|
43
|
+
emitError('file and an active CLI identity or agentId are required for "memorix lock unlock"', asJson);
|
|
30
44
|
return;
|
|
31
45
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
default:
|
|
85
|
-
console.log('Memorix Lock Commands');
|
|
86
|
-
console.log('');
|
|
87
|
-
console.log('Usage:');
|
|
88
|
-
console.log(' memorix lock lock --file src/app.ts --agentId <id>');
|
|
89
|
-
console.log(' memorix lock unlock --file src/app.ts --agentId <id>');
|
|
90
|
-
console.log(' memorix lock status [--file src/app.ts] [--agentId <id>]');
|
|
91
|
-
}
|
|
92
|
-
} catch (error) {
|
|
93
|
-
emitError(error instanceof Error ? error.message : String(error), asJson);
|
|
94
|
-
}
|
|
95
|
-
},
|
|
96
|
-
});
|
|
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
|
+
|
|
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
|
+
});
|
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
import { defineCommand } from 'citty';
|
|
2
2
|
import { compactDetail, compactSearch, compactTimeline } from '../../compact/engine.js';
|
|
3
3
|
import { withFreshIndex } from '../../memory/freshness.js';
|
|
4
|
-
import { getAllObservations, getProjectObservations, resolveObservations, storeObservation, suggestTopicKey } from '../../memory/observations.js';
|
|
4
|
+
import { getAllObservations, getObservation, getProjectObservations, resolveObservations, storeObservation, suggestTopicKey } from '../../memory/observations.js';
|
|
5
5
|
import { buildGraphContextPacket, formatGraphContextPrompt } from '../../memory/graph-context.js';
|
|
6
|
-
import {
|
|
6
|
+
import { canManageObservation, filterReadableObservations, resolveObservationVisibility } from '../../memory/visibility.js';
|
|
7
|
+
import {
|
|
8
|
+
coerceObservationStatus,
|
|
9
|
+
coerceObservationType,
|
|
10
|
+
emitError,
|
|
11
|
+
emitResult,
|
|
12
|
+
getCliProjectContext,
|
|
13
|
+
parseCsvList,
|
|
14
|
+
parsePositiveInt,
|
|
15
|
+
resolveCliWriteScope,
|
|
16
|
+
} from './operator-shared.js';
|
|
7
17
|
|
|
8
18
|
export default defineCommand({
|
|
9
19
|
meta: {
|
|
@@ -19,6 +29,7 @@ export default defineCommand({
|
|
|
19
29
|
facts: { type: 'string', description: 'Comma-separated facts' },
|
|
20
30
|
files: { type: 'string', description: 'Comma-separated file list' },
|
|
21
31
|
concepts: { type: 'string', description: 'Comma-separated concept list' },
|
|
32
|
+
visibility: { type: 'string', description: 'Memory visibility: project (default), personal, or team' },
|
|
22
33
|
ids: { type: 'string', description: 'Comma-separated observation IDs' },
|
|
23
34
|
id: { type: 'string', description: 'Single observation ID' },
|
|
24
35
|
status: { type: 'string', description: 'Resolved or archived' },
|
|
@@ -32,6 +43,7 @@ export default defineCommand({
|
|
|
32
43
|
after: { type: 'string', description: 'Timeline depth after anchor' },
|
|
33
44
|
threshold: { type: 'string', description: 'Similarity threshold for consolidate' },
|
|
34
45
|
dryRun: { type: 'boolean', description: 'Preview changes without mutating data' },
|
|
46
|
+
'dry-run': { type: 'boolean', description: 'Kebab-case alias for --dryRun' },
|
|
35
47
|
trigger: { type: 'string', description: 'Custom trigger text for promoted mini-skills' },
|
|
36
48
|
instruction: { type: 'string', description: 'Custom instruction for promoted mini-skills' },
|
|
37
49
|
tags: { type: 'string', description: 'Comma-separated extra tags for promoted mini-skills' },
|
|
@@ -44,7 +56,7 @@ export default defineCommand({
|
|
|
44
56
|
const asJson = !!args.json;
|
|
45
57
|
|
|
46
58
|
try {
|
|
47
|
-
const { project } = await getCliProjectContext({ searchIndex: true });
|
|
59
|
+
const { project, dataDir, reader, identity } = await getCliProjectContext({ searchIndex: true });
|
|
48
60
|
|
|
49
61
|
switch (action) {
|
|
50
62
|
case 'search': {
|
|
@@ -54,7 +66,7 @@ export default defineCommand({
|
|
|
54
66
|
return;
|
|
55
67
|
}
|
|
56
68
|
const limit = parsePositiveInt(args.limit as string | undefined, 10);
|
|
57
|
-
const result = await compactSearch({ query, limit, projectId: project.id });
|
|
69
|
+
const result = await compactSearch({ query, limit, projectId: project.id, reader });
|
|
58
70
|
emitResult({ project, entries: result.entries }, result.formatted, asJson);
|
|
59
71
|
return;
|
|
60
72
|
}
|
|
@@ -67,7 +79,7 @@ export default defineCommand({
|
|
|
67
79
|
emitError('query is required for "memorix memory graph-context"', asJson);
|
|
68
80
|
return;
|
|
69
81
|
}
|
|
70
|
-
const observations = getAllObservations();
|
|
82
|
+
const observations = filterReadableObservations(getAllObservations(), reader);
|
|
71
83
|
const packet = buildGraphContextPacket(observations, {
|
|
72
84
|
projectId: project.id,
|
|
73
85
|
query,
|
|
@@ -92,7 +104,7 @@ export default defineCommand({
|
|
|
92
104
|
|
|
93
105
|
case 'recent': {
|
|
94
106
|
const limit = parsePositiveInt(args.limit as string | undefined, 10);
|
|
95
|
-
const observations = getProjectObservations(project.id)
|
|
107
|
+
const observations = filterReadableObservations(getProjectObservations(project.id), reader)
|
|
96
108
|
.filter((obs) => (obs.status ?? 'active') === 'active')
|
|
97
109
|
.slice(-limit)
|
|
98
110
|
.reverse();
|
|
@@ -118,6 +130,10 @@ export default defineCommand({
|
|
|
118
130
|
(args.topicKey as string | undefined)?.trim() ||
|
|
119
131
|
suggestTopicKey(type, title) ||
|
|
120
132
|
undefined;
|
|
133
|
+
const writeScope = resolveCliWriteScope(
|
|
134
|
+
{ reader, identity },
|
|
135
|
+
args.visibility as string | undefined,
|
|
136
|
+
);
|
|
121
137
|
const result = await storeObservation({
|
|
122
138
|
entityName: (args.entity as string | undefined)?.trim() || 'general',
|
|
123
139
|
type,
|
|
@@ -129,6 +145,7 @@ export default defineCommand({
|
|
|
129
145
|
projectId: project.id,
|
|
130
146
|
topicKey,
|
|
131
147
|
source: 'manual',
|
|
148
|
+
...writeScope,
|
|
132
149
|
});
|
|
133
150
|
emitResult(
|
|
134
151
|
{ project, observation: result.observation, upserted: result.upserted },
|
|
@@ -166,7 +183,7 @@ export default defineCommand({
|
|
|
166
183
|
? `obs:${numericId}@${project.id}`
|
|
167
184
|
: ref;
|
|
168
185
|
});
|
|
169
|
-
const result = await compactDetail(scopedRefs);
|
|
186
|
+
const result = await compactDetail(scopedRefs, { reader });
|
|
170
187
|
emitResult({ project, documents: result.documents }, result.formatted, asJson);
|
|
171
188
|
return;
|
|
172
189
|
}
|
|
@@ -182,6 +199,7 @@ export default defineCommand({
|
|
|
182
199
|
project.id,
|
|
183
200
|
parsePositiveInt(args.before as string | undefined, 3),
|
|
184
201
|
parsePositiveInt(args.after as string | undefined, 3),
|
|
202
|
+
reader,
|
|
185
203
|
);
|
|
186
204
|
emitResult({ project, timeline: result.timeline }, result.formatted, asJson);
|
|
187
205
|
return;
|
|
@@ -196,7 +214,15 @@ export default defineCommand({
|
|
|
196
214
|
return;
|
|
197
215
|
}
|
|
198
216
|
const status = coerceObservationStatus(args.status as string | undefined);
|
|
199
|
-
const
|
|
217
|
+
const authorizedIds = ids.filter((id) => {
|
|
218
|
+
const observation = getObservation(id, project.id);
|
|
219
|
+
return observation ? canManageObservation(observation, reader) : false;
|
|
220
|
+
});
|
|
221
|
+
if (authorizedIds.length === 0) {
|
|
222
|
+
emitError('No requested observations are manageable with the active CLI scope.', asJson);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
const result = await resolveObservations(authorizedIds, status);
|
|
200
226
|
emitResult(
|
|
201
227
|
{ project, result, status },
|
|
202
228
|
`Resolved ${result.resolved.length} observation(s) to ${status}${result.notFound.length > 0 ? `; not found: ${result.notFound.join(', ')}` : ''}`,
|
|
@@ -207,7 +233,7 @@ export default defineCommand({
|
|
|
207
233
|
|
|
208
234
|
case 'deduplicate': {
|
|
209
235
|
const query = (args.query as string | undefined)?.trim();
|
|
210
|
-
const dryRun = !!args.dryRun;
|
|
236
|
+
const dryRun = !!args.dryRun || !!args['dry-run'];
|
|
211
237
|
const { isLLMEnabled } = await import('../../llm/provider.js');
|
|
212
238
|
if (!isLLMEnabled()) {
|
|
213
239
|
emitResult(
|
|
@@ -220,7 +246,10 @@ export default defineCommand({
|
|
|
220
246
|
|
|
221
247
|
const { deduplicateMemory } = await import('../../llm/memory-manager.js');
|
|
222
248
|
const allObs = await withFreshIndex(() =>
|
|
223
|
-
|
|
249
|
+
filterReadableObservations(
|
|
250
|
+
getAllObservations().filter((obs) => (obs.status ?? 'active') === 'active' && obs.projectId === project.id),
|
|
251
|
+
reader,
|
|
252
|
+
),
|
|
224
253
|
);
|
|
225
254
|
|
|
226
255
|
if (allObs.length < 2) {
|
|
@@ -230,7 +259,7 @@ export default defineCommand({
|
|
|
230
259
|
|
|
231
260
|
let candidates = allObs;
|
|
232
261
|
if (query) {
|
|
233
|
-
const searchResult = await compactSearch({ query, limit: 20, projectId: project.id, status: 'active' });
|
|
262
|
+
const searchResult = await compactSearch({ query, limit: 20, projectId: project.id, status: 'active', reader });
|
|
234
263
|
const ids = new Set(searchResult.entries.map((entry) => entry.id));
|
|
235
264
|
candidates = allObs.filter((obs) => ids.has(obs.id));
|
|
236
265
|
} else {
|
|
@@ -277,7 +306,13 @@ export default defineCommand({
|
|
|
277
306
|
return;
|
|
278
307
|
}
|
|
279
308
|
|
|
280
|
-
const result = await resolveObservations(
|
|
309
|
+
const result = await resolveObservations(
|
|
310
|
+
[...new Set(toResolve)].filter((id) => {
|
|
311
|
+
const observation = getObservation(id, project.id);
|
|
312
|
+
return observation ? canManageObservation(observation, reader) : false;
|
|
313
|
+
}),
|
|
314
|
+
'resolved',
|
|
315
|
+
);
|
|
281
316
|
emitResult(
|
|
282
317
|
{ project, actions, resolved: result.resolved, notFound: result.notFound, dryRun: false },
|
|
283
318
|
`Resolved ${result.resolved.length} duplicate observation(s).`,
|
|
@@ -292,7 +327,7 @@ export default defineCommand({
|
|
|
292
327
|
const { findConsolidationCandidates, executeConsolidation } = await import('../../memory/consolidation.js');
|
|
293
328
|
|
|
294
329
|
if (consolidationAction === 'preview') {
|
|
295
|
-
const clusters = await findConsolidationCandidates(
|
|
330
|
+
const clusters = await findConsolidationCandidates(project.rootPath, project.id, { threshold });
|
|
296
331
|
emitResult(
|
|
297
332
|
{ project, clusters, action: consolidationAction },
|
|
298
333
|
clusters.length === 0
|
|
@@ -306,7 +341,7 @@ export default defineCommand({
|
|
|
306
341
|
}
|
|
307
342
|
|
|
308
343
|
if (consolidationAction === 'execute') {
|
|
309
|
-
const result = await executeConsolidation(
|
|
344
|
+
const result = await executeConsolidation(project.rootPath, project.id, { threshold });
|
|
310
345
|
emitResult(
|
|
311
346
|
{ project, action: consolidationAction, ...result },
|
|
312
347
|
result.clustersFound === 0
|
|
@@ -325,11 +360,10 @@ export default defineCommand({
|
|
|
325
360
|
const promoteAction = (args.action as string | undefined) || 'promote';
|
|
326
361
|
const { promoteToMiniSkill, loadAllMiniSkills, deleteMiniSkill } = await import('../../skills/mini-skills.js');
|
|
327
362
|
const { initMiniSkillStore } = await import('../../store/mini-skill-store.js');
|
|
328
|
-
const { dataDir } = await getCliProjectContext();
|
|
329
363
|
await initMiniSkillStore(dataDir);
|
|
330
364
|
|
|
331
365
|
if (promoteAction === 'list') {
|
|
332
|
-
const skills = await loadAllMiniSkills(
|
|
366
|
+
const skills = await loadAllMiniSkills(project.rootPath);
|
|
333
367
|
emitResult(
|
|
334
368
|
{ project, skills, action: promoteAction },
|
|
335
369
|
skills.length === 0 ? 'No mini-skills found.' : skills.map((skill) => `- #${skill.id} ${skill.title}`).join('\n'),
|
|
@@ -344,7 +378,7 @@ export default defineCommand({
|
|
|
344
378
|
emitError('skillId is required for "memorix memory promote --action delete"', asJson);
|
|
345
379
|
return;
|
|
346
380
|
}
|
|
347
|
-
const deleted = await deleteMiniSkill(
|
|
381
|
+
const deleted = await deleteMiniSkill(project.rootPath, skillId);
|
|
348
382
|
if (!deleted) {
|
|
349
383
|
emitError(`Mini-skill #${skillId} not found`, asJson);
|
|
350
384
|
return;
|
|
@@ -361,12 +395,15 @@ export default defineCommand({
|
|
|
361
395
|
return;
|
|
362
396
|
}
|
|
363
397
|
const observations = await withFreshIndex(() => getAllObservations());
|
|
364
|
-
const matched =
|
|
398
|
+
const matched = filterReadableObservations(
|
|
399
|
+
observations.filter((obs) => obs.projectId === project.id && ids.includes(obs.id)),
|
|
400
|
+
reader,
|
|
401
|
+
).filter((observation) => resolveObservationVisibility(observation) === 'project');
|
|
365
402
|
if (matched.length === 0) {
|
|
366
|
-
emitError(`No observations found for IDs: ${ids.join(', ')}
|
|
403
|
+
emitError(`No project-visible observations found for IDs: ${ids.join(', ')}. Private and team records cannot be promoted into shared skills.`, asJson);
|
|
367
404
|
return;
|
|
368
405
|
}
|
|
369
|
-
const skill = await promoteToMiniSkill(
|
|
406
|
+
const skill = await promoteToMiniSkill(project.rootPath, project.id, matched, {
|
|
370
407
|
trigger: args.trigger as string | undefined,
|
|
371
408
|
instruction: args.instruction as string | undefined,
|
|
372
409
|
tags: parseCsvList(args.tags as string | undefined),
|
|
@@ -385,7 +422,7 @@ export default defineCommand({
|
|
|
385
422
|
console.log('Usage:');
|
|
386
423
|
console.log(' memorix memory search --query "timeout bug" [--limit 10]');
|
|
387
424
|
console.log(' memorix memory recent [--limit 10]');
|
|
388
|
-
console.log(' memorix memory store --text "..." [--title "..."] [--type discovery]');
|
|
425
|
+
console.log(' memorix memory store --text "..." [--title "..."] [--type discovery] [--visibility project|personal|team]');
|
|
389
426
|
console.log(' memorix memory suggest-topic-key --type decision --title "..."');
|
|
390
427
|
console.log(' memorix memory detail --id 42');
|
|
391
428
|
console.log(' memorix memory detail obs:42@org/project');
|