memorix 1.5.1 → 1.6.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 +23 -0
- package/README.md +3 -3
- package/README.zh-CN.md +3 -3
- package/TEAM.md +86 -86
- package/dist/cli/index.js +243 -42
- package/dist/cli/memcode.js +0 -0
- package/dist/index.js +167 -20
- package/dist/maintenance-runner.js +5 -4
- package/dist/media-audio-runner.js +5 -4
- package/dist/media-video-runner.js +5 -4
- package/dist/memcode-runtime/CHANGELOG.md +23 -0
- package/dist/sdk.js +166 -19
- package/dist/vector-backfill-runner.js +5 -4
- package/docs/1.2.4-PERSISTENT-MEMORY-DELIVERY.md +1 -1
- package/docs/API_REFERENCE.md +1 -1
- package/docs/DESIGN_DECISIONS.md +357 -357
- package/docs/DEVELOPMENT.md +5 -3
- 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/codegraph.ts +48 -5
- package/src/cli/commands/context.ts +20 -4
- 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/poll.ts +58 -58
- package/src/cli/commands/reasoning.ts +118 -118
- package/src/cli/commands/resume.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/codegraph/auto-context.ts +4 -1
- package/src/codegraph/context-pack.ts +3 -1
- package/src/codegraph/external-provider.ts +93 -0
- package/src/dashboard/project-classification.ts +64 -64
- package/src/git/hooks-path.ts +85 -85
- package/src/hooks/installers/index.ts +1 -1
- package/src/hooks/official-skills.ts +3 -3
- package/src/hooks/pattern-detector.ts +173 -173
- package/src/hooks/rules/memorix-agent-rules.md +1 -1
- package/src/hooks/significance-filter.ts +250 -250
- package/src/knowledge/context-receipt.ts +41 -0
- package/src/knowledge/workflows.ts +6 -3
- package/src/knowledge/workset.ts +56 -2
- 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/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/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 +15 -3
- 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/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/docs/DEVELOPMENT.md
CHANGED
|
@@ -326,9 +326,11 @@ Notes:
|
|
|
326
326
|
- `server.json` is the official MCP Registry record. Keep it synchronized with
|
|
327
327
|
`package.json` by running `npm run sync:mcp-registry`; verify it with
|
|
328
328
|
`npm run check:mcp-registry` before a release.
|
|
329
|
-
- The GitHub publish workflow
|
|
330
|
-
publish the same verified version to the official MCP Registry.
|
|
331
|
-
|
|
329
|
+
- The GitHub publish workflow can publish npm first, then uses GitHub OIDC to
|
|
330
|
+
publish the same verified version to the official MCP Registry. When npm was
|
|
331
|
+
already published manually, run the workflow with `publish_npm=false` to
|
|
332
|
+
publish only the Registry record. It does not require a Registry token or
|
|
333
|
+
private key in repository secrets.
|
|
332
334
|
- `@memorix/ai`, `@memorix/agent-core`, `@memorix/tui`, and `@memorix/memcode` are internal workspaces. Their code is bundled into the root `memorix` distribution and they must stay `private` unless the project deliberately establishes an owned npm scope and a separate package-support contract.
|
|
333
335
|
- npm publish is usually manual, especially when 2FA is enabled
|
|
334
336
|
- GitHub release automation should not be treated as a substitute for manual runtime validation
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "memorix",
|
|
3
3
|
"mcpName": "io.github.AVIDS2/memorix",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.6.0",
|
|
5
5
|
"description": "Local-first shared memory layer for AI coding agents across MCP clients, Git history, reasoning context, and project sessions.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"sideEffects": false,
|
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.6.0",
|
|
11
11
|
"websiteUrl": "https://github.com/AVIDS2/memorix#readme",
|
|
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.6.0",
|
|
26
26
|
"runtimeHint": "npx",
|
|
27
27
|
"packageArguments": [
|
|
28
28
|
{
|
package/src/audit/index.ts
CHANGED
|
@@ -1,156 +1,156 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Audit Module - Track Memorix-written files
|
|
3
|
-
*
|
|
4
|
-
* Records all files written by Memorix to distinguish them from
|
|
5
|
-
* project-native files. Provides audit trail for cleanup and rollback.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import * as fs from 'node:fs/promises';
|
|
9
|
-
import * as path from 'node:path';
|
|
10
|
-
import { homedir } from 'node:os';
|
|
11
|
-
|
|
12
|
-
function getAuditFilePath(): string {
|
|
13
|
-
return process.env.MEMORIX_AUDIT_FILE || path.join(homedir(), '.memorix', 'audit.json');
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface AuditEntry {
|
|
17
|
-
type: 'hook' | 'rule' | 'other';
|
|
18
|
-
agent?: string;
|
|
19
|
-
path: string;
|
|
20
|
-
createdAt: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface ProjectAudit {
|
|
24
|
-
projectRoot: string;
|
|
25
|
-
installedAt: string;
|
|
26
|
-
entries: AuditEntry[];
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface AuditData {
|
|
30
|
-
version: string;
|
|
31
|
-
projects: Record<string, ProjectAudit>;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Load audit data from disk.
|
|
36
|
-
*/
|
|
37
|
-
export async function loadAudit(): Promise<AuditData> {
|
|
38
|
-
try {
|
|
39
|
-
const content = await fs.readFile(getAuditFilePath(), 'utf-8');
|
|
40
|
-
return JSON.parse(content) as AuditData;
|
|
41
|
-
} catch {
|
|
42
|
-
// No audit file yet
|
|
43
|
-
return {
|
|
44
|
-
version: '1.0.0',
|
|
45
|
-
projects: {},
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Save audit data to disk.
|
|
52
|
-
*/
|
|
53
|
-
export async function saveAudit(data: AuditData): Promise<void> {
|
|
54
|
-
const auditFile = getAuditFilePath();
|
|
55
|
-
const dir = path.dirname(auditFile);
|
|
56
|
-
await fs.mkdir(dir, { recursive: true });
|
|
57
|
-
await fs.writeFile(auditFile, JSON.stringify(data, null, 2), 'utf-8');
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Get project ID from project root via .git detection.
|
|
62
|
-
*/
|
|
63
|
-
export function getProjectId(projectRoot: string): string {
|
|
64
|
-
try {
|
|
65
|
-
const { detectProject } = require('../project/detector.js');
|
|
66
|
-
const project = detectProject(projectRoot);
|
|
67
|
-
if (project) return project.id;
|
|
68
|
-
} catch { /* fallback below */ }
|
|
69
|
-
const normalized = projectRoot.replace(/\\/g, '/');
|
|
70
|
-
return `untracked/${normalized}`;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Record a file written by Memorix.
|
|
75
|
-
*/
|
|
76
|
-
export async function recordFile(
|
|
77
|
-
projectRoot: string,
|
|
78
|
-
type: AuditEntry['type'],
|
|
79
|
-
filePath: string,
|
|
80
|
-
agent?: string,
|
|
81
|
-
): Promise<void> {
|
|
82
|
-
const data = await loadAudit();
|
|
83
|
-
const projectId = getProjectId(projectRoot);
|
|
84
|
-
|
|
85
|
-
if (!data.projects[projectId]) {
|
|
86
|
-
data.projects[projectId] = {
|
|
87
|
-
projectRoot,
|
|
88
|
-
installedAt: new Date().toISOString(),
|
|
89
|
-
entries: [],
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Check if entry already exists
|
|
94
|
-
const existingIndex = data.projects[projectId].entries.findIndex(
|
|
95
|
-
(e) => e.path === filePath
|
|
96
|
-
);
|
|
97
|
-
|
|
98
|
-
if (existingIndex === -1) {
|
|
99
|
-
data.projects[projectId].entries.push({
|
|
100
|
-
type,
|
|
101
|
-
agent,
|
|
102
|
-
path: filePath,
|
|
103
|
-
createdAt: new Date().toISOString(),
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
await saveAudit(data);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Get all files written by Memorix for a project.
|
|
112
|
-
*/
|
|
113
|
-
export async function getProjectFiles(projectRoot: string): Promise<AuditEntry[]> {
|
|
114
|
-
const data = await loadAudit();
|
|
115
|
-
const projectId = getProjectId(projectRoot);
|
|
116
|
-
return data.projects[projectId]?.entries || [];
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Remove a file from audit (when uninstalled).
|
|
121
|
-
*/
|
|
122
|
-
export async function removeFile(projectRoot: string, filePath: string): Promise<void> {
|
|
123
|
-
const data = await loadAudit();
|
|
124
|
-
const projectId = getProjectId(projectRoot);
|
|
125
|
-
|
|
126
|
-
if (!data.projects[projectId]) return;
|
|
127
|
-
|
|
128
|
-
data.projects[projectId].entries = data.projects[projectId].entries.filter(
|
|
129
|
-
(e) => e.path !== filePath
|
|
130
|
-
);
|
|
131
|
-
|
|
132
|
-
// If no entries left, remove the project
|
|
133
|
-
if (data.projects[projectId].entries.length === 0) {
|
|
134
|
-
delete data.projects[projectId];
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
await saveAudit(data);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Get all audit entries across all projects.
|
|
142
|
-
*/
|
|
143
|
-
export async function getAllAuditEntries(): Promise<
|
|
144
|
-
Array<{ projectId: string; entry: AuditEntry }>
|
|
145
|
-
> {
|
|
146
|
-
const data = await loadAudit();
|
|
147
|
-
const entries: Array<{ projectId: string; entry: AuditEntry }> = [];
|
|
148
|
-
|
|
149
|
-
for (const [projectId, project] of Object.entries(data.projects)) {
|
|
150
|
-
for (const entry of project.entries) {
|
|
151
|
-
entries.push({ projectId, entry });
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
return entries;
|
|
156
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Audit Module - Track Memorix-written files
|
|
3
|
+
*
|
|
4
|
+
* Records all files written by Memorix to distinguish them from
|
|
5
|
+
* project-native files. Provides audit trail for cleanup and rollback.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as fs from 'node:fs/promises';
|
|
9
|
+
import * as path from 'node:path';
|
|
10
|
+
import { homedir } from 'node:os';
|
|
11
|
+
|
|
12
|
+
function getAuditFilePath(): string {
|
|
13
|
+
return process.env.MEMORIX_AUDIT_FILE || path.join(homedir(), '.memorix', 'audit.json');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface AuditEntry {
|
|
17
|
+
type: 'hook' | 'rule' | 'other';
|
|
18
|
+
agent?: string;
|
|
19
|
+
path: string;
|
|
20
|
+
createdAt: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ProjectAudit {
|
|
24
|
+
projectRoot: string;
|
|
25
|
+
installedAt: string;
|
|
26
|
+
entries: AuditEntry[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface AuditData {
|
|
30
|
+
version: string;
|
|
31
|
+
projects: Record<string, ProjectAudit>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Load audit data from disk.
|
|
36
|
+
*/
|
|
37
|
+
export async function loadAudit(): Promise<AuditData> {
|
|
38
|
+
try {
|
|
39
|
+
const content = await fs.readFile(getAuditFilePath(), 'utf-8');
|
|
40
|
+
return JSON.parse(content) as AuditData;
|
|
41
|
+
} catch {
|
|
42
|
+
// No audit file yet
|
|
43
|
+
return {
|
|
44
|
+
version: '1.0.0',
|
|
45
|
+
projects: {},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Save audit data to disk.
|
|
52
|
+
*/
|
|
53
|
+
export async function saveAudit(data: AuditData): Promise<void> {
|
|
54
|
+
const auditFile = getAuditFilePath();
|
|
55
|
+
const dir = path.dirname(auditFile);
|
|
56
|
+
await fs.mkdir(dir, { recursive: true });
|
|
57
|
+
await fs.writeFile(auditFile, JSON.stringify(data, null, 2), 'utf-8');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Get project ID from project root via .git detection.
|
|
62
|
+
*/
|
|
63
|
+
export function getProjectId(projectRoot: string): string {
|
|
64
|
+
try {
|
|
65
|
+
const { detectProject } = require('../project/detector.js');
|
|
66
|
+
const project = detectProject(projectRoot);
|
|
67
|
+
if (project) return project.id;
|
|
68
|
+
} catch { /* fallback below */ }
|
|
69
|
+
const normalized = projectRoot.replace(/\\/g, '/');
|
|
70
|
+
return `untracked/${normalized}`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Record a file written by Memorix.
|
|
75
|
+
*/
|
|
76
|
+
export async function recordFile(
|
|
77
|
+
projectRoot: string,
|
|
78
|
+
type: AuditEntry['type'],
|
|
79
|
+
filePath: string,
|
|
80
|
+
agent?: string,
|
|
81
|
+
): Promise<void> {
|
|
82
|
+
const data = await loadAudit();
|
|
83
|
+
const projectId = getProjectId(projectRoot);
|
|
84
|
+
|
|
85
|
+
if (!data.projects[projectId]) {
|
|
86
|
+
data.projects[projectId] = {
|
|
87
|
+
projectRoot,
|
|
88
|
+
installedAt: new Date().toISOString(),
|
|
89
|
+
entries: [],
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Check if entry already exists
|
|
94
|
+
const existingIndex = data.projects[projectId].entries.findIndex(
|
|
95
|
+
(e) => e.path === filePath
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
if (existingIndex === -1) {
|
|
99
|
+
data.projects[projectId].entries.push({
|
|
100
|
+
type,
|
|
101
|
+
agent,
|
|
102
|
+
path: filePath,
|
|
103
|
+
createdAt: new Date().toISOString(),
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
await saveAudit(data);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Get all files written by Memorix for a project.
|
|
112
|
+
*/
|
|
113
|
+
export async function getProjectFiles(projectRoot: string): Promise<AuditEntry[]> {
|
|
114
|
+
const data = await loadAudit();
|
|
115
|
+
const projectId = getProjectId(projectRoot);
|
|
116
|
+
return data.projects[projectId]?.entries || [];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Remove a file from audit (when uninstalled).
|
|
121
|
+
*/
|
|
122
|
+
export async function removeFile(projectRoot: string, filePath: string): Promise<void> {
|
|
123
|
+
const data = await loadAudit();
|
|
124
|
+
const projectId = getProjectId(projectRoot);
|
|
125
|
+
|
|
126
|
+
if (!data.projects[projectId]) return;
|
|
127
|
+
|
|
128
|
+
data.projects[projectId].entries = data.projects[projectId].entries.filter(
|
|
129
|
+
(e) => e.path !== filePath
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
// If no entries left, remove the project
|
|
133
|
+
if (data.projects[projectId].entries.length === 0) {
|
|
134
|
+
delete data.projects[projectId];
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
await saveAudit(data);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Get all audit entries across all projects.
|
|
142
|
+
*/
|
|
143
|
+
export async function getAllAuditEntries(): Promise<
|
|
144
|
+
Array<{ projectId: string; entry: AuditEntry }>
|
|
145
|
+
> {
|
|
146
|
+
const data = await loadAudit();
|
|
147
|
+
const entries: Array<{ projectId: string; entry: AuditEntry }> = [];
|
|
148
|
+
|
|
149
|
+
for (const [projectId, project] of Object.entries(data.projects)) {
|
|
150
|
+
for (const entry of project.entries) {
|
|
151
|
+
entries.push({ projectId, entry });
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return entries;
|
|
156
|
+
}
|
|
@@ -5,11 +5,14 @@ import { assembleContextPackForTask, attachTaskWorkset, buildContextPackPrompt }
|
|
|
5
5
|
import { backfillMissingObservationCodeRefs } from '../../codegraph/binder.js';
|
|
6
6
|
import { collectCurrentProjectFacts, formatGitFact } from '../../codegraph/current-facts.js';
|
|
7
7
|
import { resolveTaskLens } from '../../codegraph/task-lens.js';
|
|
8
|
-
import { getExternalCodeGraphContext, inspectExternalCodeGraph } from '../../codegraph/external-provider.js';
|
|
8
|
+
import { getExternalCodeGraphContext, inspectExternalCodeGraph, runExternalCodeGraphLifecycle } from '../../codegraph/external-provider.js';
|
|
9
9
|
import type { CodeGraphProviderQuality } from '../../codegraph/types.js';
|
|
10
10
|
import { getResolvedConfig } from '../../config/resolved-config.js';
|
|
11
|
+
import { buildBoundedContextReceipt } from '../../knowledge/context-receipt.js';
|
|
12
|
+
import { WORKFLOW_AGENT_TARGETS } from '../../knowledge/workflows.js';
|
|
11
13
|
import { getAllObservations } from '../../memory/observations.js';
|
|
12
14
|
import { filterReadableObservations } from '../../memory/visibility.js';
|
|
15
|
+
import type { AgentTarget } from '../../types.js';
|
|
13
16
|
import { emitError, emitResult, getCliProjectContext, parsePositiveInt } from './operator-shared.js';
|
|
14
17
|
|
|
15
18
|
function formatSnapshotStatus(status: ReturnType<CodeGraphStore['status']>): string[] {
|
|
@@ -48,6 +51,7 @@ function formatStatus(status: ReturnType<CodeGraphStore['status']>, quality?: Co
|
|
|
48
51
|
`- Persistent provider: ${status.provider} (heuristic local index)`,
|
|
49
52
|
`- External semantic CodeGraph: ${quality.external.state}`
|
|
50
53
|
+ (quality.external.reason ? ` (${quality.external.reason})` : ''),
|
|
54
|
+
`- Task-scoped provider: ${quality.selected} (${quality.selectedQuality})`,
|
|
51
55
|
]
|
|
52
56
|
: []),
|
|
53
57
|
].join('\n');
|
|
@@ -57,6 +61,8 @@ function formatUsageHint(): string {
|
|
|
57
61
|
return [
|
|
58
62
|
'Usage:',
|
|
59
63
|
' memorix codegraph refresh',
|
|
64
|
+
' memorix codegraph init # explicit local CodeGraph initialization',
|
|
65
|
+
' memorix codegraph sync # explicit local CodeGraph synchronization',
|
|
60
66
|
' memorix codegraph status --json',
|
|
61
67
|
' memorix codegraph diff [--from <snapshot>] [--to <snapshot>]',
|
|
62
68
|
' memorix codegraph impact --path <relative-source-path>',
|
|
@@ -116,25 +122,34 @@ function compactFacts(project: { rootPath: string }): { facts: string[]; dirty:
|
|
|
116
122
|
return { facts, dirty: current.git.dirty };
|
|
117
123
|
}
|
|
118
124
|
|
|
125
|
+
function coerceAgentTarget(input?: string): AgentTarget | undefined {
|
|
126
|
+
const value = input?.trim().toLowerCase();
|
|
127
|
+
if (!value) return undefined;
|
|
128
|
+
if (WORKFLOW_AGENT_TARGETS.includes(value as AgentTarget)) return value as AgentTarget;
|
|
129
|
+
throw new Error('agent must be one of: ' + WORKFLOW_AGENT_TARGETS.join(', '));
|
|
130
|
+
}
|
|
131
|
+
|
|
119
132
|
export default defineCommand({
|
|
120
133
|
meta: {
|
|
121
134
|
name: 'codegraph',
|
|
122
135
|
description: 'Inspect and refresh CodeGraph Memory for the current project',
|
|
123
136
|
},
|
|
124
137
|
args: {
|
|
125
|
-
action: { type: 'string', description: 'Action: status, refresh, diff, impact, or context-pack' },
|
|
138
|
+
action: { type: 'string', description: 'Action: status, refresh, diff, impact, init, sync, or context-pack' },
|
|
126
139
|
task: { type: 'string', description: 'Task text for context-pack' },
|
|
127
140
|
path: { type: 'string', description: 'Indexed relative source path for impact' },
|
|
128
141
|
limit: { type: 'string', description: 'Max active memories to inspect for context-pack' },
|
|
129
142
|
from: { type: 'string', description: 'Earlier CodeGraph snapshot id for diff' },
|
|
130
143
|
to: { type: 'string', description: 'Later CodeGraph snapshot id for diff' },
|
|
131
144
|
impactLimit: { type: 'string', description: 'Maximum graph relations to inspect for diff impact' },
|
|
145
|
+
agent: { type: 'string', description: 'Optional target agent for compatible workflow selection' },
|
|
132
146
|
json: { type: 'boolean', description: 'Emit machine-readable JSON output' },
|
|
147
|
+
briefJson: { type: 'boolean', description: 'Emit only the bounded agent brief and receipt JSON for context-pack' },
|
|
133
148
|
},
|
|
134
149
|
run: async ({ args }) => {
|
|
135
150
|
const positional = (args._ as string[]) ?? [];
|
|
136
151
|
const action = (positional[0] || (args.action as string | undefined) || 'status').toLowerCase();
|
|
137
|
-
const asJson = !!args.json;
|
|
152
|
+
const asJson = !!args.json || !!args.briefJson;
|
|
138
153
|
|
|
139
154
|
try {
|
|
140
155
|
const { project, dataDir, reader } = await getCliProjectContext();
|
|
@@ -206,6 +221,27 @@ export default defineCommand({
|
|
|
206
221
|
return;
|
|
207
222
|
}
|
|
208
223
|
|
|
224
|
+
case 'init':
|
|
225
|
+
case 'sync': {
|
|
226
|
+
const lifecycle = await runExternalCodeGraphLifecycle({
|
|
227
|
+
action,
|
|
228
|
+
projectRoot: project.rootPath,
|
|
229
|
+
mode: codegraphConfig.externalContext,
|
|
230
|
+
command: codegraphConfig.externalCommand,
|
|
231
|
+
timeoutMs: codegraphConfig.externalTimeoutMs,
|
|
232
|
+
});
|
|
233
|
+
emitResult(
|
|
234
|
+
{ project, lifecycle, providerQuality: lifecycle.quality },
|
|
235
|
+
[
|
|
236
|
+
lifecycle.message,
|
|
237
|
+
`- External semantic CodeGraph: ${lifecycle.health.state}`,
|
|
238
|
+
`- Task-scoped provider: ${lifecycle.quality.selected} (${lifecycle.quality.selectedQuality})`,
|
|
239
|
+
].join('\n'),
|
|
240
|
+
asJson,
|
|
241
|
+
);
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
209
245
|
case 'diff': {
|
|
210
246
|
const snapshots = store.listSnapshots(project.id, 2);
|
|
211
247
|
const fromSnapshotId = (args.from as string | undefined)?.trim() || snapshots[1]?.id;
|
|
@@ -299,13 +335,20 @@ export default defineCommand({
|
|
|
299
335
|
? { runtimeCautions: [{ kind: 'external-codegraph-fallback' as const, message: external.caution }] }
|
|
300
336
|
: {}),
|
|
301
337
|
reader,
|
|
338
|
+
agent: coerceAgentTarget(args.agent as string | undefined),
|
|
302
339
|
});
|
|
303
|
-
emitResult(
|
|
340
|
+
emitResult(
|
|
341
|
+
args.briefJson && pack.workset
|
|
342
|
+
? buildBoundedContextReceipt({ workset: pack.workset, providerQuality: external.quality })
|
|
343
|
+
: { project, pack, providerQuality: external.quality },
|
|
344
|
+
buildContextPackPrompt(pack),
|
|
345
|
+
asJson,
|
|
346
|
+
);
|
|
304
347
|
return;
|
|
305
348
|
}
|
|
306
349
|
|
|
307
350
|
default:
|
|
308
|
-
emitError(`unknown codegraph action "${action}". Use "status", "refresh", "diff", "impact", or "context-pack".`, asJson);
|
|
351
|
+
emitError(`unknown codegraph action "${action}". Use "status", "refresh", "init", "sync", "diff", "impact", or "context-pack".`, asJson);
|
|
309
352
|
}
|
|
310
353
|
} catch (error) {
|
|
311
354
|
emitError(error instanceof Error ? error.message : String(error), asJson);
|
|
@@ -7,6 +7,9 @@ import {
|
|
|
7
7
|
} from '../../codegraph/auto-context.js';
|
|
8
8
|
import { getAllObservations } from '../../memory/observations.js';
|
|
9
9
|
import { filterReadableObservations } from '../../memory/visibility.js';
|
|
10
|
+
import { buildBoundedContextReceipt } from '../../knowledge/context-receipt.js';
|
|
11
|
+
import { WORKFLOW_AGENT_TARGETS } from '../../knowledge/workflows.js';
|
|
12
|
+
import type { AgentTarget } from '../../types.js';
|
|
10
13
|
import { emitError, emitResult, getCliProjectContext } from './operator-shared.js';
|
|
11
14
|
|
|
12
15
|
function coerceRefreshMode(input?: string): AutoContextRefreshMode {
|
|
@@ -15,6 +18,13 @@ function coerceRefreshMode(input?: string): AutoContextRefreshMode {
|
|
|
15
18
|
throw new Error('refresh must be one of: auto, always, never');
|
|
16
19
|
}
|
|
17
20
|
|
|
21
|
+
function coerceAgentTarget(input?: string): AgentTarget | undefined {
|
|
22
|
+
const value = input?.trim().toLowerCase();
|
|
23
|
+
if (!value) return undefined;
|
|
24
|
+
if (WORKFLOW_AGENT_TARGETS.includes(value as AgentTarget)) return value as AgentTarget;
|
|
25
|
+
throw new Error('agent must be one of: ' + WORKFLOW_AGENT_TARGETS.join(', '));
|
|
26
|
+
}
|
|
27
|
+
|
|
18
28
|
export default defineCommand({
|
|
19
29
|
meta: {
|
|
20
30
|
name: 'context',
|
|
@@ -29,10 +39,12 @@ export default defineCommand({
|
|
|
29
39
|
},
|
|
30
40
|
resume: { type: 'boolean', description: 'Always include the bounded prior-work projection' },
|
|
31
41
|
refresh: { type: 'string', description: 'Project scan policy: auto, always, or never' },
|
|
42
|
+
agent: { type: 'string', description: 'Optional target agent for compatible workflow selection' },
|
|
32
43
|
json: { type: 'boolean', description: 'Emit machine-readable JSON output' },
|
|
44
|
+
briefJson: { type: 'boolean', description: 'Emit only the bounded agent brief and receipt JSON' },
|
|
33
45
|
},
|
|
34
46
|
run: async ({ args }) => {
|
|
35
|
-
const asJson = !!args.json;
|
|
47
|
+
const asJson = !!args.json || !!args.briefJson;
|
|
36
48
|
const task = (args.task as string | undefined)?.trim()
|
|
37
49
|
|| (args.input as string | undefined)?.trim()
|
|
38
50
|
|| undefined;
|
|
@@ -44,13 +56,13 @@ export default defineCommand({
|
|
|
44
56
|
dataDir,
|
|
45
57
|
observations: filterReadableObservations(getAllObservations(), reader),
|
|
46
58
|
task,
|
|
59
|
+
agent: coerceAgentTarget(args.agent as string | undefined),
|
|
47
60
|
refresh: coerceRefreshMode(args.refresh as string | undefined),
|
|
48
61
|
reader,
|
|
49
62
|
...(args.resume ? { continuation: 'always' as const } : {}),
|
|
50
63
|
});
|
|
51
64
|
|
|
52
|
-
|
|
53
|
-
{
|
|
65
|
+
const detailed = {
|
|
54
66
|
project,
|
|
55
67
|
lens: context.lens,
|
|
56
68
|
brief: buildAutoProjectBrief(context),
|
|
@@ -61,7 +73,11 @@ export default defineCommand({
|
|
|
61
73
|
workset: context.workset,
|
|
62
74
|
...(context.task ? { task: context.task } : {}),
|
|
63
75
|
...(context.continuation ? { continuation: context.continuation } : {}),
|
|
64
|
-
|
|
76
|
+
};
|
|
77
|
+
emitResult(
|
|
78
|
+
args.briefJson
|
|
79
|
+
? buildBoundedContextReceipt({ workset: context.workset, providerQuality: context.providerQuality })
|
|
80
|
+
: detailed,
|
|
65
81
|
formatAutoProjectContextPrompt(context),
|
|
66
82
|
asJson,
|
|
67
83
|
);
|