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.
Files changed (122) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +3 -3
  3. package/README.zh-CN.md +3 -3
  4. package/TEAM.md +86 -86
  5. package/dist/cli/index.js +243 -42
  6. package/dist/cli/memcode.js +0 -0
  7. package/dist/index.js +167 -20
  8. package/dist/maintenance-runner.js +5 -4
  9. package/dist/media-audio-runner.js +5 -4
  10. package/dist/media-video-runner.js +5 -4
  11. package/dist/memcode-runtime/CHANGELOG.md +23 -0
  12. package/dist/sdk.js +166 -19
  13. package/dist/vector-backfill-runner.js +5 -4
  14. package/docs/1.2.4-PERSISTENT-MEMORY-DELIVERY.md +1 -1
  15. package/docs/API_REFERENCE.md +1 -1
  16. package/docs/DESIGN_DECISIONS.md +357 -357
  17. package/docs/DEVELOPMENT.md +5 -3
  18. package/package.json +1 -1
  19. package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
  20. package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
  21. package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
  22. package/plugins/copilot/memorix/plugin.json +1 -1
  23. package/plugins/gemini/memorix/gemini-extension.json +1 -1
  24. package/plugins/omp/memorix/package.json +1 -1
  25. package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
  26. package/plugins/pi/memorix/package.json +1 -1
  27. package/server.json +2 -2
  28. package/src/audit/index.ts +156 -156
  29. package/src/cli/commands/codegraph.ts +48 -5
  30. package/src/cli/commands/context.ts +20 -4
  31. package/src/cli/commands/formation.ts +48 -48
  32. package/src/cli/commands/git-hook-install.ts +111 -111
  33. package/src/cli/commands/handoff.ts +54 -54
  34. package/src/cli/commands/hooks-status.ts +63 -63
  35. package/src/cli/commands/ingest.ts +44 -44
  36. package/src/cli/commands/integrate-shared.ts +15 -15
  37. package/src/cli/commands/lock.ts +82 -82
  38. package/src/cli/commands/message.ts +104 -104
  39. package/src/cli/commands/poll.ts +58 -58
  40. package/src/cli/commands/reasoning.ts +118 -118
  41. package/src/cli/commands/resume.ts +4 -0
  42. package/src/cli/commands/skills.ts +114 -114
  43. package/src/cli/commands/task.ts +167 -167
  44. package/src/cli/commands/transfer.ts +47 -47
  45. package/src/cli/index.ts +406 -406
  46. package/src/cli/tui/ChatView.tsx +234 -234
  47. package/src/cli/tui/CommandBar.tsx +312 -312
  48. package/src/cli/tui/ContextRail.tsx +118 -118
  49. package/src/cli/tui/HeaderBar.tsx +72 -72
  50. package/src/cli/tui/LogoBanner.tsx +51 -51
  51. package/src/cli/tui/index.ts +41 -41
  52. package/src/cli/version.ts +7 -7
  53. package/src/cli/workbench.ts +1 -1
  54. package/src/codegraph/auto-context.ts +4 -1
  55. package/src/codegraph/context-pack.ts +3 -1
  56. package/src/codegraph/external-provider.ts +93 -0
  57. package/src/dashboard/project-classification.ts +64 -64
  58. package/src/git/hooks-path.ts +85 -85
  59. package/src/hooks/installers/index.ts +1 -1
  60. package/src/hooks/official-skills.ts +3 -3
  61. package/src/hooks/pattern-detector.ts +173 -173
  62. package/src/hooks/rules/memorix-agent-rules.md +1 -1
  63. package/src/hooks/significance-filter.ts +250 -250
  64. package/src/knowledge/context-receipt.ts +41 -0
  65. package/src/knowledge/workflows.ts +6 -3
  66. package/src/knowledge/workset.ts +56 -2
  67. package/src/llm/memory-manager.ts +328 -328
  68. package/src/llm/quality.ts +248 -248
  69. package/src/memory/attribution-guard.ts +249 -249
  70. package/src/memory/disclosure-policy.ts +135 -135
  71. package/src/memory/entity-extractor.ts +197 -197
  72. package/src/memory/formation/evaluate.ts +217 -217
  73. package/src/memory/freshness.ts +122 -122
  74. package/src/memory/graph.ts +197 -197
  75. package/src/memory/refs.ts +94 -94
  76. package/src/memory/secret-filter.ts +79 -79
  77. package/src/orchestrate/adapters/claude-stream.ts +192 -192
  78. package/src/orchestrate/adapters/claude.ts +111 -111
  79. package/src/orchestrate/adapters/codex-stream.ts +134 -134
  80. package/src/orchestrate/adapters/codex.ts +41 -41
  81. package/src/orchestrate/adapters/gemini-stream.ts +166 -166
  82. package/src/orchestrate/adapters/gemini.ts +42 -42
  83. package/src/orchestrate/adapters/index.ts +73 -73
  84. package/src/orchestrate/adapters/opencode-stream.ts +143 -143
  85. package/src/orchestrate/adapters/opencode.ts +47 -47
  86. package/src/orchestrate/adapters/types.ts +77 -77
  87. package/src/orchestrate/capability-router.ts +284 -284
  88. package/src/orchestrate/cost-tracker.ts +219 -219
  89. package/src/orchestrate/error-recovery.ts +191 -191
  90. package/src/orchestrate/evidence.ts +140 -140
  91. package/src/orchestrate/ledger.ts +110 -110
  92. package/src/orchestrate/memorix-bridge.ts +343 -343
  93. package/src/orchestrate/output-budget.ts +80 -80
  94. package/src/orchestrate/permission.ts +152 -152
  95. package/src/orchestrate/pipeline-trace.ts +131 -131
  96. package/src/orchestrate/prompt-builder.ts +155 -155
  97. package/src/orchestrate/ring-buffer.ts +37 -37
  98. package/src/orchestrate/task-graph.ts +389 -389
  99. package/src/project/aliases.ts +374 -374
  100. package/src/rules/adapters/trae.ts +56 -56
  101. package/src/rules/syncer.ts +235 -235
  102. package/src/search/query-expansion.ts +52 -52
  103. package/src/server/formation-timeout.ts +27 -27
  104. package/src/server.ts +15 -3
  105. package/src/skills/mini-skills.ts +386 -386
  106. package/src/store/chat-store.ts +119 -119
  107. package/src/store/graph-store.ts +249 -249
  108. package/src/store/mini-skill-store.ts +349 -349
  109. package/src/store/persistence-json.ts +212 -212
  110. package/src/store/persistence.ts +291 -291
  111. package/src/store/project-affinity.ts +195 -195
  112. package/src/team/event-bus.ts +76 -76
  113. package/src/team/handoff.ts +161 -161
  114. package/src/team/poll.ts +132 -132
  115. package/src/workspace/mcp-adapters/codex.ts +191 -191
  116. package/src/workspace/mcp-adapters/copilot.ts +105 -105
  117. package/src/workspace/mcp-adapters/cursor.ts +53 -53
  118. package/src/workspace/mcp-adapters/kiro.ts +64 -64
  119. package/src/workspace/mcp-adapters/opencode.ts +123 -123
  120. package/src/workspace/mcp-adapters/trae.ts +134 -134
  121. package/src/workspace/mcp-adapters/windsurf.ts +91 -91
  122. package/src/workspace/sanitizer.ts +60 -60
@@ -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 publishes npm first, then uses GitHub OIDC to
330
- publish the same verified version to the official MCP Registry. It does not
331
- require a Registry token or private key in repository secrets.
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.5.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,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://anthropic.com/claude-code/plugin.schema.json",
3
3
  "name": "memorix",
4
- "version": "1.5.1",
4
+ "version": "1.6.0",
5
5
  "description": "Shared workspace memory for Claude Code and other AI coding agents.",
6
6
  "author": {
7
7
  "name": "AVIDS2",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memorix",
3
- "version": "1.5.1",
3
+ "version": "1.6.0",
4
4
  "description": "Local-first project memory for CodeBuddy Code and other coding agents.",
5
5
  "author": {
6
6
  "name": "AVIDS2",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memorix",
3
- "version": "1.5.1",
3
+ "version": "1.6.0",
4
4
  "description": "Shared workspace memory for Codex and other AI coding agents.",
5
5
  "author": {
6
6
  "name": "AVIDS2",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memorix",
3
- "version": "1.5.1",
3
+ "version": "1.6.0",
4
4
  "description": "Shared local workspace memory for GitHub Copilot CLI and other AI coding agents.",
5
5
  "author": {
6
6
  "name": "AVIDS2",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memorix",
3
- "version": "1.5.1",
3
+ "version": "1.6.0",
4
4
  "mcpServers": {
5
5
  "memorix": {
6
6
  "command": "memorix",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memorix-omp-package",
3
- "version": "1.5.1",
3
+ "version": "1.6.0",
4
4
  "description": "Memorix shared workspace memory package for Oh-my-Pi.",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memorix",
3
- "version": "1.5.1",
3
+ "version": "1.6.0",
4
4
  "description": "Shared workspace memory for OpenClaw and other AI coding agents.",
5
5
  "author": {
6
6
  "name": "AVIDS2",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memorix-pi-package",
3
- "version": "1.5.1",
3
+ "version": "1.6.0",
4
4
  "description": "Memorix shared workspace memory package for Pi coding agent.",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [
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.5.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.5.1",
25
+ "version": "1.6.0",
26
26
  "runtimeHint": "npx",
27
27
  "packageArguments": [
28
28
  {
@@ -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({ project, pack, providerQuality: external.quality }, buildContextPackPrompt(pack), asJson);
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
- emitResult(
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
  );