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.
Files changed (212) hide show
  1. package/CHANGELOG.md +30 -1
  2. package/README.md +18 -4
  3. package/README.zh-CN.md +18 -4
  4. package/TEAM.md +86 -86
  5. package/dist/cli/index.js +15919 -14055
  6. package/dist/cli/index.js.map +1 -1
  7. package/dist/index.js +1997 -1021
  8. package/dist/index.js.map +1 -1
  9. package/dist/maintenance-runner.d.ts +1 -1
  10. package/dist/maintenance-runner.js +8481 -8005
  11. package/dist/maintenance-runner.js.map +1 -1
  12. package/dist/memcode-runtime/CHANGELOG.md +30 -1
  13. package/dist/sdk.d.ts +7 -2
  14. package/dist/sdk.js +2022 -1024
  15. package/dist/sdk.js.map +1 -1
  16. package/dist/types.d.ts +49 -1
  17. package/dist/types.js.map +1 -1
  18. package/docs/1.2.2-MEMORY-CONTROL-PLANE.md +434 -0
  19. package/docs/AGENT_OPERATOR_PLAYBOOK.md +4 -0
  20. package/docs/API_REFERENCE.md +27 -5
  21. package/docs/DESIGN_DECISIONS.md +357 -357
  22. package/docs/DEVELOPMENT.md +4 -0
  23. package/docs/README.md +1 -1
  24. package/docs/SETUP.md +7 -1
  25. package/docs/dev-log/progress.txt +91 -11
  26. package/docs/knowledge/workflows/memorix-release.md +57 -0
  27. package/package.json +1 -1
  28. package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
  29. package/src/audit/index.ts +156 -156
  30. package/src/cli/command-guide.ts +192 -0
  31. package/src/cli/commands/audit-list.ts +89 -89
  32. package/src/cli/commands/audit.ts +9 -4
  33. package/src/cli/commands/background.ts +659 -659
  34. package/src/cli/commands/cleanup.ts +5 -1
  35. package/src/cli/commands/codegraph.ts +17 -8
  36. package/src/cli/commands/context.ts +3 -2
  37. package/src/cli/commands/doctor.ts +4 -2
  38. package/src/cli/commands/explain.ts +9 -3
  39. package/src/cli/commands/formation.ts +48 -48
  40. package/src/cli/commands/git-hook-install.ts +111 -111
  41. package/src/cli/commands/handoff.ts +75 -61
  42. package/src/cli/commands/hooks-status.ts +63 -63
  43. package/src/cli/commands/identity.ts +116 -0
  44. package/src/cli/commands/ingest-commit.ts +153 -153
  45. package/src/cli/commands/ingest-image.ts +71 -69
  46. package/src/cli/commands/ingest-log.ts +180 -180
  47. package/src/cli/commands/ingest.ts +44 -44
  48. package/src/cli/commands/integrate-shared.ts +15 -15
  49. package/src/cli/commands/knowledge.ts +40 -0
  50. package/src/cli/commands/lock.ts +93 -92
  51. package/src/cli/commands/memory.ts +58 -21
  52. package/src/cli/commands/message.ts +123 -118
  53. package/src/cli/commands/operator-shared.ts +98 -3
  54. package/src/cli/commands/poll.ts +74 -64
  55. package/src/cli/commands/purge-all-memory.ts +85 -85
  56. package/src/cli/commands/purge-project-memory.ts +83 -83
  57. package/src/cli/commands/reasoning.ts +135 -121
  58. package/src/cli/commands/retention.ts +9 -4
  59. package/src/cli/commands/serve-http.ts +22 -43
  60. package/src/cli/commands/serve-shared.ts +118 -118
  61. package/src/cli/commands/session.ts +29 -3
  62. package/src/cli/commands/setup.ts +9 -3
  63. package/src/cli/commands/skills.ts +124 -119
  64. package/src/cli/commands/status.ts +4 -3
  65. package/src/cli/commands/task.ts +193 -184
  66. package/src/cli/commands/team.ts +14 -10
  67. package/src/cli/commands/transfer.ts +108 -55
  68. package/src/cli/commands/uninstall-project-artifacts.ts +85 -85
  69. package/src/cli/identity.ts +89 -0
  70. package/src/cli/index.ts +96 -19
  71. package/src/cli/invocation.ts +115 -0
  72. package/src/cli/tui/ChatView.tsx +234 -234
  73. package/src/cli/tui/CommandBar.tsx +312 -312
  74. package/src/cli/tui/ContextRail.tsx +118 -118
  75. package/src/cli/tui/HeaderBar.tsx +72 -72
  76. package/src/cli/tui/LogoBanner.tsx +51 -51
  77. package/src/cli/tui/Sidebar.tsx +179 -179
  78. package/src/cli/tui/chat-service.ts +41 -18
  79. package/src/cli/tui/data.ts +23 -44
  80. package/src/cli/tui/index.ts +41 -41
  81. package/src/cli/tui/markdown-render.tsx +371 -371
  82. package/src/cli/tui/operator-context.ts +60 -0
  83. package/src/cli/tui/use-mouse.ts +157 -157
  84. package/src/cli/tui/useNavigation.ts +56 -56
  85. package/src/cli/tui/views/MemoryView.tsx +10 -8
  86. package/src/cli/update-checker.ts +211 -211
  87. package/src/cli/version.ts +7 -7
  88. package/src/cli/workbench.ts +1 -1
  89. package/src/codegraph/auto-context.ts +34 -17
  90. package/src/codegraph/context-pack.ts +1 -0
  91. package/src/codegraph/current-facts.ts +19 -1
  92. package/src/codegraph/project-context.ts +2 -0
  93. package/src/codegraph/task-lens.ts +49 -5
  94. package/src/compact/engine.ts +26 -10
  95. package/src/compact/index-format.ts +25 -2
  96. package/src/compact/token-budget.ts +74 -74
  97. package/src/dashboard/project-classification.ts +64 -64
  98. package/src/dashboard/server.ts +58 -52
  99. package/src/embedding/fastembed-provider.ts +142 -142
  100. package/src/embedding/transformers-provider.ts +111 -111
  101. package/src/git/extractor.ts +209 -209
  102. package/src/git/hooks-path.ts +85 -85
  103. package/src/hooks/admission.ts +117 -0
  104. package/src/hooks/handler.ts +98 -91
  105. package/src/hooks/pattern-detector.ts +173 -173
  106. package/src/hooks/significance-filter.ts +250 -250
  107. package/src/knowledge/claims.ts +51 -1
  108. package/src/knowledge/context-assembly.ts +97 -0
  109. package/src/knowledge/types.ts +1 -0
  110. package/src/knowledge/workflows.ts +34 -3
  111. package/src/knowledge/workset.ts +179 -10
  112. package/src/llm/memory-manager.ts +328 -328
  113. package/src/llm/provider.ts +885 -885
  114. package/src/llm/quality.ts +248 -248
  115. package/src/memory/admission.ts +57 -0
  116. package/src/memory/attribution-guard.ts +249 -249
  117. package/src/memory/auto-relations.ts +21 -0
  118. package/src/memory/consolidation.ts +13 -2
  119. package/src/memory/disclosure-policy.ts +140 -135
  120. package/src/memory/entity-extractor.ts +197 -197
  121. package/src/memory/export-import.ts +11 -3
  122. package/src/memory/formation/evaluate.ts +217 -217
  123. package/src/memory/formation/extract.ts +361 -361
  124. package/src/memory/formation/index.ts +417 -417
  125. package/src/memory/formation/resolve.ts +344 -344
  126. package/src/memory/formation/types.ts +315 -315
  127. package/src/memory/freshness.ts +122 -122
  128. package/src/memory/graph-context.ts +8 -2
  129. package/src/memory/graph-scope.ts +46 -0
  130. package/src/memory/graph.ts +197 -197
  131. package/src/memory/observations.ts +162 -4
  132. package/src/memory/quality-audit.ts +2 -0
  133. package/src/memory/refs.ts +94 -94
  134. package/src/memory/retention.ts +22 -2
  135. package/src/memory/secret-filter.ts +79 -79
  136. package/src/memory/session.ts +5 -2
  137. package/src/memory/visibility.ts +80 -0
  138. package/src/multimodal/image-loader.ts +143 -143
  139. package/src/orchestrate/adapters/claude-stream.ts +192 -192
  140. package/src/orchestrate/adapters/claude.ts +111 -111
  141. package/src/orchestrate/adapters/codex-stream.ts +134 -134
  142. package/src/orchestrate/adapters/codex.ts +41 -41
  143. package/src/orchestrate/adapters/gemini-stream.ts +166 -166
  144. package/src/orchestrate/adapters/gemini.ts +42 -42
  145. package/src/orchestrate/adapters/index.ts +73 -73
  146. package/src/orchestrate/adapters/opencode-stream.ts +143 -143
  147. package/src/orchestrate/adapters/opencode.ts +47 -47
  148. package/src/orchestrate/adapters/spawn-helper.ts +286 -286
  149. package/src/orchestrate/adapters/types.ts +77 -77
  150. package/src/orchestrate/capability-router.ts +284 -284
  151. package/src/orchestrate/context-compact.ts +188 -188
  152. package/src/orchestrate/cost-tracker.ts +219 -219
  153. package/src/orchestrate/error-recovery.ts +191 -191
  154. package/src/orchestrate/evidence.ts +140 -140
  155. package/src/orchestrate/ledger.ts +110 -110
  156. package/src/orchestrate/memorix-bridge.ts +378 -340
  157. package/src/orchestrate/output-budget.ts +80 -80
  158. package/src/orchestrate/permission.ts +152 -152
  159. package/src/orchestrate/pipeline-trace.ts +131 -131
  160. package/src/orchestrate/prompt-builder.ts +155 -155
  161. package/src/orchestrate/ring-buffer.ts +37 -37
  162. package/src/orchestrate/task-graph.ts +389 -389
  163. package/src/orchestrate/verify-gate.ts +33 -10
  164. package/src/orchestrate/worktree.ts +232 -232
  165. package/src/project/aliases.ts +374 -374
  166. package/src/project/detector.ts +268 -268
  167. package/src/rules/adapters/claude-code.ts +99 -99
  168. package/src/rules/adapters/codex.ts +97 -97
  169. package/src/rules/adapters/copilot.ts +124 -124
  170. package/src/rules/adapters/cursor.ts +114 -114
  171. package/src/rules/adapters/kiro.ts +126 -126
  172. package/src/rules/adapters/trae.ts +56 -56
  173. package/src/rules/adapters/windsurf.ts +83 -83
  174. package/src/rules/syncer.ts +235 -235
  175. package/src/runtime/control-plane-maintenance.ts +1 -0
  176. package/src/runtime/isolated-maintenance.ts +1 -0
  177. package/src/runtime/lifecycle.ts +18 -0
  178. package/src/runtime/maintenance-jobs.ts +1 -0
  179. package/src/runtime/maintenance-runner.ts +2 -0
  180. package/src/runtime/project-maintenance.ts +89 -0
  181. package/src/sdk.ts +334 -304
  182. package/src/search/intent-detector.ts +289 -289
  183. package/src/search/query-expansion.ts +52 -52
  184. package/src/server/formation-timeout.ts +27 -27
  185. package/src/server.ts +334 -93
  186. package/src/skills/mini-skills.ts +386 -386
  187. package/src/store/chat-store.ts +119 -119
  188. package/src/store/graph-store.ts +249 -249
  189. package/src/store/mini-skill-store.ts +349 -349
  190. package/src/store/orama-store.ts +61 -6
  191. package/src/store/persistence-json.ts +212 -212
  192. package/src/store/persistence.ts +291 -291
  193. package/src/store/project-affinity.ts +195 -195
  194. package/src/store/sqlite-db.ts +23 -1
  195. package/src/store/sqlite-store.ts +12 -2
  196. package/src/team/event-bus.ts +76 -76
  197. package/src/team/file-locks.ts +173 -173
  198. package/src/team/handoff.ts +168 -161
  199. package/src/team/messages.ts +203 -203
  200. package/src/team/poll.ts +132 -132
  201. package/src/team/tasks.ts +211 -211
  202. package/src/types.ts +51 -0
  203. package/src/wiki/generator.ts +2 -0
  204. package/src/workspace/mcp-adapters/codex.ts +191 -191
  205. package/src/workspace/mcp-adapters/copilot.ts +105 -105
  206. package/src/workspace/mcp-adapters/cursor.ts +53 -53
  207. package/src/workspace/mcp-adapters/kiro.ts +64 -64
  208. package/src/workspace/mcp-adapters/opencode.ts +123 -123
  209. package/src/workspace/mcp-adapters/trae.ts +134 -134
  210. package/src/workspace/mcp-adapters/windsurf.ts +91 -91
  211. package/src/workspace/sanitizer.ts +60 -60
  212. package/src/workspace/workflow-sync.ts +131 -131
@@ -23,6 +23,8 @@ import { defineCommand } from 'citty';
23
23
  import type { IncomingMessage, ServerResponse } from 'node:http';
24
24
  import type { ObservationStore } from '../../store/obs-store.js';
25
25
  import { resolveToolProfile } from '../../server/tool-profile.js';
26
+ import { scopeKnowledgeGraphToProject } from '../../memory/graph-scope.js';
27
+ import { canManageObservation, filterReadableObservations } from '../../memory/visibility.js';
26
28
 
27
29
  export const DEFAULT_SESSION_TIMEOUT_MS = 30 * 60 * 1000;
28
30
 
@@ -557,7 +559,7 @@ export default defineCommand({
557
559
 
558
560
  async function loadDashboardObservations(dataDir: string) {
559
561
  const store = await getDashboardObservationStore(dataDir);
560
- return store.loadAll();
562
+ return filterReadableObservations(await store.loadAll(), {});
561
563
  }
562
564
 
563
565
  async function loadDashboardProjectObservations(
@@ -566,7 +568,10 @@ export default defineCommand({
566
568
  status?: string,
567
569
  ) {
568
570
  const store = await getDashboardObservationStore(dataDir);
569
- return store.loadByProject(projectId, status ? { status } : undefined);
571
+ return filterReadableObservations(
572
+ await store.loadByProject(projectId, status ? { status } : undefined),
573
+ { projectId },
574
+ );
570
575
  }
571
576
 
572
577
  // Resolve static directory (dist/dashboard/static)
@@ -764,6 +769,7 @@ export default defineCommand({
764
769
  source?: string;
765
770
  commitHash?: string;
766
771
  filesModified?: string[];
772
+ relatedEntities?: string[];
767
773
  status?: string;
768
774
  importance?: number;
769
775
  accessCount?: number;
@@ -860,12 +866,7 @@ export default defineCommand({
860
866
  } catch { /* best effort */ }
861
867
 
862
868
  // Project-scoped graph counts (must match /graph and /export)
863
- const projectEntityNames = new Set(
864
- observations.filter(o => o.entityName).map(o => o.entityName!)
865
- );
866
- const projectEntities = graph.entities.filter((e: any) => projectEntityNames.has(e.name));
867
- const projectEntitySet = new Set(projectEntities.map((e: any) => e.name));
868
- const projectRelations = graph.relations.filter((r: any) => projectEntitySet.has(r.from) && projectEntitySet.has(r.to));
869
+ const projectGraph = scopeKnowledgeGraphToProject(graph, observations);
869
870
 
870
871
  let maintenance = { total: 0, pending: 0, running: 0, retrying: 0, completed: 0, failed: 0 };
871
872
  let lifecycle: unknown;
@@ -877,8 +878,8 @@ export default defineCommand({
877
878
  } catch { /* optional maintenance diagnostics */ }
878
879
 
879
880
  sendJson({
880
- entities: projectEntities.length,
881
- relations: projectRelations.length,
881
+ entities: projectGraph.entities.length,
882
+ relations: projectGraph.relations.length,
882
883
  observations: observations.length,
883
884
  nextId,
884
885
  typeCounts,
@@ -930,19 +931,9 @@ export default defineCommand({
930
931
  await initGraphStore(graphDataDir);
931
932
  const fullGraph = { entities: getGraphStore().loadEntities(), relations: getGraphStore().loadRelations() };
932
933
 
933
- // Project-scope the graph: only include entities that have observations in this project
934
- const allObs = await loadDashboardProjectObservations(graphDataDir, graphProjectId, 'active') as Array<{ entityName?: string }>;
935
- const projectEntityNames = new Set(
936
- allObs
937
- .filter(o => o.entityName)
938
- .map(o => o.entityName!)
939
- );
940
-
941
- const entities = fullGraph.entities.filter(e => projectEntityNames.has(e.name));
942
- const entityNameSet = new Set(entities.map(e => e.name));
943
- const relations = fullGraph.relations.filter(r => entityNameSet.has(r.from) && entityNameSet.has(r.to));
944
-
945
- sendJson({ entities, relations });
934
+ const allObs = await loadDashboardProjectObservations(graphDataDir, graphProjectId, 'active');
935
+ const scoped = scopeKnowledgeGraphToProject(fullGraph, allObs);
936
+ sendJson({ entities: scoped.entities, relations: scoped.relations });
946
937
  return;
947
938
  }
948
939
 
@@ -1012,24 +1003,15 @@ export default defineCommand({
1012
1003
  const allObs = await loadDashboardProjectObservations(kgDataDir, kgProjectId, 'active');
1013
1004
  const skills = await getMiniSkillStore().loadByProject(kgProjectId);
1014
1005
 
1015
- // Project-scope graph store (same logic as /api/graph)
1016
1006
  const fullGraph = { entities: getGraphStore().loadEntities(), relations: getGraphStore().loadRelations() };
1017
- const graphObs = await loadDashboardProjectObservations(kgDataDir, kgProjectId, 'active') as Array<{ entityName?: string }>;
1018
- const projectEntityNames = new Set(
1019
- graphObs
1020
- .filter(o => o.entityName)
1021
- .map(o => o.entityName!)
1022
- );
1023
- const scopedEntities = fullGraph.entities.filter(e => projectEntityNames.has(e.name));
1024
- const scopedEntityNameSet = new Set(scopedEntities.map(e => e.name));
1025
- const scopedRelations = fullGraph.relations.filter(r => scopedEntityNameSet.has(r.from) && scopedEntityNameSet.has(r.to));
1007
+ const scoped = scopeKnowledgeGraphToProject(fullGraph, allObs);
1026
1008
 
1027
1009
  const graph = generateKnowledgeGraph({
1028
1010
  projectId: kgProjectId,
1029
1011
  observations: allObs,
1030
1012
  miniSkills: skills,
1031
- graphEntities: scopedEntities,
1032
- graphRelations: scopedRelations,
1013
+ graphEntities: scoped.entities,
1014
+ graphRelations: scoped.relations,
1033
1015
  });
1034
1016
 
1035
1017
  sendJson(graph);
@@ -1343,6 +1325,8 @@ export default defineCommand({
1343
1325
  sendJson({ error: 'Observation not found' }, 404);
1344
1326
  } else if ((matchObs as any).projectId !== delProjectId) {
1345
1327
  sendJson({ error: `Observation #${obsId} belongs to project "${(matchObs as any).projectId}", not "${delProjectId}"` }, 403);
1328
+ } else if (!canManageObservation(matchObs, { projectId: delProjectId })) {
1329
+ sendJson({ error: `Observation #${obsId} is not manageable from the unbound dashboard.` }, 403);
1346
1330
  } else {
1347
1331
  await store.remove(obsId);
1348
1332
  // Sync: clean up graph entity references
@@ -1369,19 +1353,14 @@ export default defineCommand({
1369
1353
  const { initGraphStore, getGraphStore } = await import('../../store/graph-store.js');
1370
1354
  await initGraphStore(expDataDir);
1371
1355
  const fullGraph = { entities: getGraphStore().loadEntities(), relations: getGraphStore().loadRelations() };
1372
- const observations = await loadDashboardProjectObservations(expDataDir, expProjectId, 'active') as Array<{ entityName?: string }>;
1356
+ const observations = await loadDashboardProjectObservations(expDataDir, expProjectId, 'active');
1373
1357
  const expStore = await getDashboardObservationStore(expDataDir);
1374
1358
  const nextId = await expStore.loadIdCounter();
1375
- const exportEntityNames = new Set(
1376
- observations.filter(o => o.entityName).map(o => o.entityName!),
1377
- );
1378
- const exportEntities = fullGraph.entities.filter((e: any) => exportEntityNames.has(e.name));
1379
- const exportEntitySet = new Set(exportEntities.map((e: any) => e.name));
1380
- const exportRelations = fullGraph.relations.filter((r: any) => exportEntitySet.has(r.from) && exportEntitySet.has(r.to));
1359
+ const scoped = scopeKnowledgeGraphToProject(fullGraph, observations);
1381
1360
  const exportData = {
1382
1361
  project: { id: expProjectId, name: expProjectName },
1383
1362
  exportedAt: new Date().toISOString(),
1384
- graph: { entities: exportEntities, relations: exportRelations },
1363
+ graph: { entities: scoped.entities, relations: scoped.relations },
1385
1364
  observations,
1386
1365
  nextId,
1387
1366
  };
@@ -1,118 +1,118 @@
1
- import type { ProjectInfo } from '../../types.js';
2
-
3
- export interface ResolveServeProjectOptions {
4
- cwdArg?: string;
5
- envProjectRoot?: string;
6
- initCwd?: string;
7
- processCwd: string;
8
- homeDir: string;
9
- lastKnownProjectRoot?: string;
10
- }
11
-
12
- export interface ResolveServeProjectDeps {
13
- detectProject: (cwd: string) => ProjectInfo | null;
14
- findGitInSubdirs: (dir: string) => string | null;
15
- isSystemDirectory: (dir: string) => boolean;
16
- }
17
-
18
- export interface ServeProjectResolution {
19
- projectRoot: string;
20
- detectedProject: ProjectInfo | null;
21
- source: 'direct' | 'subdir' | 'last-known' | 'home' | 'home-subdir' | 'unresolved';
22
- messages: string[];
23
- error?: string;
24
- }
25
-
26
- export function resolveServeProject(
27
- options: ResolveServeProjectOptions,
28
- deps: ResolveServeProjectDeps,
29
- ): ServeProjectResolution {
30
- let projectRoot =
31
- options.cwdArg ||
32
- options.envProjectRoot ||
33
- options.initCwd ||
34
- options.processCwd;
35
-
36
- const messages: string[] = [`[memorix] Starting with cwd: ${projectRoot}`];
37
-
38
- let detected = deps.detectProject(projectRoot);
39
- if (detected) {
40
- return {
41
- projectRoot,
42
- detectedProject: detected,
43
- source: 'direct',
44
- messages,
45
- };
46
- }
47
-
48
- const subGit = deps.findGitInSubdirs(projectRoot);
49
- if (subGit) {
50
- projectRoot = subGit;
51
- detected = deps.detectProject(subGit);
52
- if (detected) {
53
- messages.push(`[memorix] Found .git in subdirectory: ${subGit}`);
54
- return {
55
- projectRoot,
56
- detectedProject: detected,
57
- source: 'subdir',
58
- messages,
59
- };
60
- }
61
- }
62
-
63
- if (deps.isSystemDirectory(projectRoot)) {
64
- messages.push(`[memorix] System directory detected: ${projectRoot}`);
65
- messages.push('[memorix] Your IDE launched memorix from a non-workspace directory.');
66
- messages.push('[memorix] Fix: add --cwd to your MCP config, or use an IDE/client that exposes workspace roots.');
67
-
68
- if (options.lastKnownProjectRoot) {
69
- detected = deps.detectProject(options.lastKnownProjectRoot);
70
- if (detected) {
71
- messages.push(`[memorix] Restored last known project: ${options.lastKnownProjectRoot}`);
72
- return {
73
- projectRoot: options.lastKnownProjectRoot,
74
- detectedProject: detected,
75
- source: 'last-known',
76
- messages,
77
- };
78
- }
79
- }
80
-
81
- detected = deps.detectProject(options.homeDir);
82
- if (detected) {
83
- messages.push(`[memorix] Restored project from home directory: ${options.homeDir}`);
84
- return {
85
- projectRoot: options.homeDir,
86
- detectedProject: detected,
87
- source: 'home',
88
- messages,
89
- };
90
- }
91
-
92
- const homeSubGit = deps.findGitInSubdirs(options.homeDir);
93
- if (homeSubGit) {
94
- detected = deps.detectProject(homeSubGit);
95
- if (detected) {
96
- messages.push(`[memorix] Found .git in home subdirectory: ${homeSubGit}`);
97
- return {
98
- projectRoot: homeSubGit,
99
- detectedProject: detected,
100
- source: 'home-subdir',
101
- messages,
102
- };
103
- }
104
- }
105
- }
106
-
107
- messages.push('[memorix] Unable to establish a reliable git-backed project context.');
108
- messages.push('[memorix] Memorix now refuses to silently fall back to untracked/* in stdio mode.');
109
-
110
- return {
111
- projectRoot,
112
- detectedProject: null,
113
- source: 'unresolved',
114
- messages,
115
- error:
116
- 'No git project could be resolved from the current workspace. Open the repo root, pass --cwd, or use an MCP client that sends workspace roots.',
117
- };
118
- }
1
+ import type { ProjectInfo } from '../../types.js';
2
+
3
+ export interface ResolveServeProjectOptions {
4
+ cwdArg?: string;
5
+ envProjectRoot?: string;
6
+ initCwd?: string;
7
+ processCwd: string;
8
+ homeDir: string;
9
+ lastKnownProjectRoot?: string;
10
+ }
11
+
12
+ export interface ResolveServeProjectDeps {
13
+ detectProject: (cwd: string) => ProjectInfo | null;
14
+ findGitInSubdirs: (dir: string) => string | null;
15
+ isSystemDirectory: (dir: string) => boolean;
16
+ }
17
+
18
+ export interface ServeProjectResolution {
19
+ projectRoot: string;
20
+ detectedProject: ProjectInfo | null;
21
+ source: 'direct' | 'subdir' | 'last-known' | 'home' | 'home-subdir' | 'unresolved';
22
+ messages: string[];
23
+ error?: string;
24
+ }
25
+
26
+ export function resolveServeProject(
27
+ options: ResolveServeProjectOptions,
28
+ deps: ResolveServeProjectDeps,
29
+ ): ServeProjectResolution {
30
+ let projectRoot =
31
+ options.cwdArg ||
32
+ options.envProjectRoot ||
33
+ options.initCwd ||
34
+ options.processCwd;
35
+
36
+ const messages: string[] = [`[memorix] Starting with cwd: ${projectRoot}`];
37
+
38
+ let detected = deps.detectProject(projectRoot);
39
+ if (detected) {
40
+ return {
41
+ projectRoot,
42
+ detectedProject: detected,
43
+ source: 'direct',
44
+ messages,
45
+ };
46
+ }
47
+
48
+ const subGit = deps.findGitInSubdirs(projectRoot);
49
+ if (subGit) {
50
+ projectRoot = subGit;
51
+ detected = deps.detectProject(subGit);
52
+ if (detected) {
53
+ messages.push(`[memorix] Found .git in subdirectory: ${subGit}`);
54
+ return {
55
+ projectRoot,
56
+ detectedProject: detected,
57
+ source: 'subdir',
58
+ messages,
59
+ };
60
+ }
61
+ }
62
+
63
+ if (deps.isSystemDirectory(projectRoot)) {
64
+ messages.push(`[memorix] System directory detected: ${projectRoot}`);
65
+ messages.push('[memorix] Your IDE launched memorix from a non-workspace directory.');
66
+ messages.push('[memorix] Fix: add --cwd to your MCP config, or use an IDE/client that exposes workspace roots.');
67
+
68
+ if (options.lastKnownProjectRoot) {
69
+ detected = deps.detectProject(options.lastKnownProjectRoot);
70
+ if (detected) {
71
+ messages.push(`[memorix] Restored last known project: ${options.lastKnownProjectRoot}`);
72
+ return {
73
+ projectRoot: options.lastKnownProjectRoot,
74
+ detectedProject: detected,
75
+ source: 'last-known',
76
+ messages,
77
+ };
78
+ }
79
+ }
80
+
81
+ detected = deps.detectProject(options.homeDir);
82
+ if (detected) {
83
+ messages.push(`[memorix] Restored project from home directory: ${options.homeDir}`);
84
+ return {
85
+ projectRoot: options.homeDir,
86
+ detectedProject: detected,
87
+ source: 'home',
88
+ messages,
89
+ };
90
+ }
91
+
92
+ const homeSubGit = deps.findGitInSubdirs(options.homeDir);
93
+ if (homeSubGit) {
94
+ detected = deps.detectProject(homeSubGit);
95
+ if (detected) {
96
+ messages.push(`[memorix] Found .git in home subdirectory: ${homeSubGit}`);
97
+ return {
98
+ projectRoot: homeSubGit,
99
+ detectedProject: detected,
100
+ source: 'home-subdir',
101
+ messages,
102
+ };
103
+ }
104
+ }
105
+ }
106
+
107
+ messages.push('[memorix] Unable to establish a reliable git-backed project context.');
108
+ messages.push('[memorix] Memorix now refuses to silently fall back to untracked/* in stdio mode.');
109
+
110
+ return {
111
+ projectRoot,
112
+ detectedProject: null,
113
+ source: 'unresolved',
114
+ messages,
115
+ error:
116
+ 'No git project could be resolved from the current workspace. Open the repo root, pass --cwd, or use an MCP client that sends workspace roots.',
117
+ };
118
+ }
@@ -5,6 +5,8 @@ import { computeWatermark } from '../../team/poll.js';
5
5
  import { withFreshIndex } from '../../memory/freshness.js';
6
6
  import { getAllObservations } from '../../memory/observations.js';
7
7
  import { getObservationStore } from '../../store/obs-store.js';
8
+ import { filterReadableObservations } from '../../memory/visibility.js';
9
+ import { saveCliIdentity } from '../identity.js';
8
10
  import { emitError, emitResult, getCliProjectContext, parsePositiveInt } from './operator-shared.js';
9
11
 
10
12
  export default defineCommand({
@@ -18,6 +20,7 @@ export default defineCommand({
18
20
  instanceId: { type: 'string', description: 'Stable instance identity across restarts' },
19
21
  projectRoot: { type: 'string', description: 'Absolute project root used to bind the session context' },
20
22
  joinTeam: { type: 'boolean', description: 'Explicitly join orchestration coordination state for this session' },
23
+ use: { type: 'boolean', description: 'Make the joined coordination identity active for later CLI commands' },
21
24
  role: { type: 'string', description: 'Explicit role override used only when --joinTeam is set' },
22
25
  sessionId: { type: 'string', description: 'Custom session ID (optional)' },
23
26
  summary: { type: 'string', description: 'Structured session summary for session end' },
@@ -35,6 +38,14 @@ export default defineCommand({
35
38
 
36
39
  switch (action) {
37
40
  case 'start': {
41
+ if (args.use && !args.joinTeam) {
42
+ emitError('use requires --joinTeam when starting a session.', asJson);
43
+ return;
44
+ }
45
+ if (args.use && !args.agent && !args.agentType) {
46
+ emitError('use requires --agent or --agentType so Memorix can activate a coordination identity.', asJson);
47
+ return;
48
+ }
38
49
  const result = await startSession(dataDir, project.id, {
39
50
  sessionId: args.sessionId as string | undefined,
40
51
  agent: args.agent as string | undefined,
@@ -61,6 +72,16 @@ export default defineCommand({
61
72
  teamJoinNotice = 'Coordination join skipped: provide --agent or --agentType to create a coordination identity.';
62
73
  }
63
74
 
75
+ let identityActivated = false;
76
+ if (args.use && agentRecord) {
77
+ await saveCliIdentity(dataDir, {
78
+ agentId: agentRecord.agent_id,
79
+ projectId: project.id,
80
+ activatedAt: new Date().toISOString(),
81
+ });
82
+ identityActivated = true;
83
+ }
84
+
64
85
  let watermark = computeWatermark(0, 0, 0);
65
86
  let rescuedAgentIds: string[] = [];
66
87
  let availableTasks = 0;
@@ -70,8 +91,11 @@ export default defineCommand({
70
91
  const store = getObservationStore();
71
92
  const currentGen = store.getGeneration();
72
93
  const projectObs = await withFreshIndex(() =>
73
- getAllObservations().filter(
74
- (obs) => obs.projectId === project.id && (obs.writeGeneration ?? 0) > lastSeen,
94
+ filterReadableObservations(
95
+ getAllObservations().filter(
96
+ (obs) => obs.projectId === project.id && (obs.writeGeneration ?? 0) > lastSeen,
97
+ ),
98
+ { projectId: project.id, agentId: agentRecord.agent_id, isTeamMember: agentRecord.status === 'active' },
75
99
  ),
76
100
  );
77
101
  watermark = computeWatermark(lastSeen, currentGen, projectObs.length);
@@ -97,6 +121,7 @@ export default defineCommand({
97
121
  joined: !!agentRecord,
98
122
  notice: teamJoinNotice,
99
123
  },
124
+ identityActivated,
100
125
  watermark,
101
126
  rescue: {
102
127
  staleAgents: rescuedAgentIds,
@@ -113,6 +138,7 @@ export default defineCommand({
113
138
  agentRecord
114
139
  ? `Agent: ${agentRecord.name} [${agentRecord.agent_type}] as ${agentRecord.role} (${agentRecord.agent_id})`
115
140
  : '',
141
+ identityActivated ? 'CLI identity activated for follow-up memory and coordination commands.' : '',
116
142
  !agentRecord ? 'Coordination identity: not joined (memory/session context only)' : '',
117
143
  teamJoinNotice ?? '',
118
144
  agentRecord && watermark.newObservationCount > 0
@@ -173,7 +199,7 @@ export default defineCommand({
173
199
  console.log('Memorix Session Commands');
174
200
  console.log('');
175
201
  console.log('Usage:');
176
- console.log(' memorix session start [--agent codex --agentType codex --instanceId abc] [--projectRoot <path>] [--joinTeam]');
202
+ console.log(' memorix session start [--agent codex --agentType codex --instanceId abc] [--projectRoot <path>] [--joinTeam --use]');
177
203
  console.log(' memorix session end --sessionId <id> [--summary "..."]');
178
204
  console.log(' memorix session context [--limit 3]');
179
205
  console.log('');
@@ -1062,7 +1062,7 @@ function tryInstallOmpPackage(packagePath: string): { ok: boolean; message: stri
1062
1062
  };
1063
1063
  }
1064
1064
 
1065
- async function installAgentSetup(agent: AgentName, plan: SetupPlan, global: boolean): Promise<void> {
1065
+ export async function installAgentSetup(agent: AgentName, plan: SetupPlan, global: boolean): Promise<void> {
1066
1066
  const { installAgentGuidance, installHooks } = await import('../../hooks/installers/index.js');
1067
1067
  const targetRoot = global ? homedir() : process.cwd();
1068
1068
  const hasPluginPackage = plan.actions.includes('plugin-package') && PLUGIN_PACKAGE_AGENTS.has(agent);
@@ -1200,13 +1200,19 @@ async function installAgentSetup(agent: AgentName, plan: SetupPlan, global: bool
1200
1200
  } else if (hasOmpPackage) {
1201
1201
  p.log.info(`${agent}: extension command, hook events, and skills are bundled in the Oh-my-Pi package`);
1202
1202
  } else if (hasExtensionPackage) {
1203
- if (plan.actions.includes('hooks') || plan.actions.includes('project-guidance')) {
1203
+ if (plan.actions.includes('hooks')) {
1204
1204
  const result = await installHooks(agent, targetRoot, global);
1205
1205
  p.log.success(`${agent}: integration files -> ${result.configPath}`);
1206
+ } else if (plan.actions.includes('project-guidance')) {
1207
+ const rulesPath = await installAgentGuidance(agent, targetRoot, global);
1208
+ p.log.success(`${agent}: guidance -> ${rulesPath}`);
1206
1209
  }
1207
- } else if (agent === 'opencode' || plan.actions.includes('hooks') || plan.actions.includes('project-guidance')) {
1210
+ } else if (plan.actions.includes('hooks')) {
1208
1211
  const result = await installHooks(agent, targetRoot, global);
1209
1212
  p.log.success(`${agent}: integration files -> ${result.configPath}`);
1213
+ } else if (plan.actions.includes('project-guidance')) {
1214
+ const rulesPath = await installAgentGuidance(agent, targetRoot, global);
1215
+ p.log.success(`${agent}: guidance -> ${rulesPath}`);
1210
1216
  }
1211
1217
 
1212
1218
  if (agent === 'pi') {