memorix 1.2.1 → 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 (199) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +14 -2
  3. package/README.zh-CN.md +14 -2
  4. package/TEAM.md +86 -86
  5. package/dist/cli/index.js +15407 -13779
  6. package/dist/cli/index.js.map +1 -1
  7. package/dist/index.js +1321 -529
  8. package/dist/index.js.map +1 -1
  9. package/dist/maintenance-runner.d.ts +1 -1
  10. package/dist/maintenance-runner.js +8458 -8087
  11. package/dist/maintenance-runner.js.map +1 -1
  12. package/dist/memcode-runtime/CHANGELOG.md +16 -0
  13. package/dist/sdk.d.ts +7 -2
  14. package/dist/sdk.js +1349 -535
  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 +24 -4
  21. package/docs/DESIGN_DECISIONS.md +357 -357
  22. package/docs/README.md +1 -1
  23. package/docs/dev-log/progress.txt +91 -11
  24. package/package.json +1 -1
  25. package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
  26. package/src/audit/index.ts +156 -156
  27. package/src/cli/command-guide.ts +192 -0
  28. package/src/cli/commands/audit-list.ts +89 -89
  29. package/src/cli/commands/audit.ts +9 -4
  30. package/src/cli/commands/background.ts +659 -659
  31. package/src/cli/commands/cleanup.ts +5 -1
  32. package/src/cli/commands/codegraph.ts +15 -5
  33. package/src/cli/commands/context.ts +3 -2
  34. package/src/cli/commands/doctor.ts +4 -2
  35. package/src/cli/commands/explain.ts +9 -3
  36. package/src/cli/commands/formation.ts +48 -48
  37. package/src/cli/commands/git-hook-install.ts +111 -111
  38. package/src/cli/commands/handoff.ts +75 -61
  39. package/src/cli/commands/hooks-status.ts +63 -63
  40. package/src/cli/commands/identity.ts +116 -0
  41. package/src/cli/commands/ingest-commit.ts +153 -153
  42. package/src/cli/commands/ingest-image.ts +71 -69
  43. package/src/cli/commands/ingest-log.ts +180 -180
  44. package/src/cli/commands/ingest.ts +44 -44
  45. package/src/cli/commands/integrate-shared.ts +15 -15
  46. package/src/cli/commands/lock.ts +93 -92
  47. package/src/cli/commands/memory.ts +58 -21
  48. package/src/cli/commands/message.ts +123 -118
  49. package/src/cli/commands/operator-shared.ts +98 -3
  50. package/src/cli/commands/poll.ts +74 -64
  51. package/src/cli/commands/purge-all-memory.ts +85 -85
  52. package/src/cli/commands/purge-project-memory.ts +83 -83
  53. package/src/cli/commands/reasoning.ts +135 -121
  54. package/src/cli/commands/retention.ts +9 -4
  55. package/src/cli/commands/serve-http.ts +8 -2
  56. package/src/cli/commands/serve-shared.ts +118 -118
  57. package/src/cli/commands/session.ts +29 -3
  58. package/src/cli/commands/skills.ts +124 -119
  59. package/src/cli/commands/status.ts +4 -3
  60. package/src/cli/commands/task.ts +193 -184
  61. package/src/cli/commands/team.ts +14 -10
  62. package/src/cli/commands/transfer.ts +108 -55
  63. package/src/cli/commands/uninstall-project-artifacts.ts +85 -85
  64. package/src/cli/identity.ts +89 -0
  65. package/src/cli/index.ts +96 -19
  66. package/src/cli/invocation.ts +115 -0
  67. package/src/cli/tui/ChatView.tsx +234 -234
  68. package/src/cli/tui/CommandBar.tsx +312 -312
  69. package/src/cli/tui/ContextRail.tsx +118 -118
  70. package/src/cli/tui/HeaderBar.tsx +72 -72
  71. package/src/cli/tui/LogoBanner.tsx +51 -51
  72. package/src/cli/tui/Sidebar.tsx +179 -179
  73. package/src/cli/tui/chat-service.ts +41 -18
  74. package/src/cli/tui/data.ts +23 -44
  75. package/src/cli/tui/index.ts +41 -41
  76. package/src/cli/tui/markdown-render.tsx +371 -371
  77. package/src/cli/tui/operator-context.ts +60 -0
  78. package/src/cli/tui/use-mouse.ts +157 -157
  79. package/src/cli/tui/useNavigation.ts +56 -56
  80. package/src/cli/tui/views/MemoryView.tsx +10 -8
  81. package/src/cli/update-checker.ts +211 -211
  82. package/src/cli/version.ts +7 -7
  83. package/src/cli/workbench.ts +1 -1
  84. package/src/codegraph/auto-context.ts +31 -2
  85. package/src/codegraph/context-pack.ts +1 -0
  86. package/src/codegraph/project-context.ts +2 -0
  87. package/src/compact/engine.ts +26 -10
  88. package/src/compact/index-format.ts +25 -2
  89. package/src/compact/token-budget.ts +74 -74
  90. package/src/dashboard/project-classification.ts +64 -64
  91. package/src/dashboard/server.ts +46 -9
  92. package/src/embedding/fastembed-provider.ts +142 -142
  93. package/src/embedding/transformers-provider.ts +111 -111
  94. package/src/git/extractor.ts +209 -209
  95. package/src/git/hooks-path.ts +85 -85
  96. package/src/hooks/admission.ts +117 -0
  97. package/src/hooks/handler.ts +98 -91
  98. package/src/hooks/pattern-detector.ts +173 -173
  99. package/src/hooks/significance-filter.ts +250 -250
  100. package/src/knowledge/context-assembly.ts +97 -0
  101. package/src/knowledge/workset.ts +179 -10
  102. package/src/llm/memory-manager.ts +328 -328
  103. package/src/llm/provider.ts +885 -885
  104. package/src/llm/quality.ts +248 -248
  105. package/src/memory/admission.ts +57 -0
  106. package/src/memory/attribution-guard.ts +249 -249
  107. package/src/memory/consolidation.ts +13 -2
  108. package/src/memory/disclosure-policy.ts +140 -135
  109. package/src/memory/entity-extractor.ts +197 -197
  110. package/src/memory/export-import.ts +11 -3
  111. package/src/memory/formation/evaluate.ts +217 -217
  112. package/src/memory/formation/extract.ts +361 -361
  113. package/src/memory/formation/index.ts +417 -417
  114. package/src/memory/formation/resolve.ts +344 -344
  115. package/src/memory/formation/types.ts +315 -315
  116. package/src/memory/freshness.ts +122 -122
  117. package/src/memory/graph-context.ts +8 -2
  118. package/src/memory/graph.ts +197 -197
  119. package/src/memory/observations.ts +162 -4
  120. package/src/memory/quality-audit.ts +2 -0
  121. package/src/memory/refs.ts +94 -94
  122. package/src/memory/retention.ts +22 -2
  123. package/src/memory/secret-filter.ts +79 -79
  124. package/src/memory/session.ts +5 -2
  125. package/src/memory/visibility.ts +80 -0
  126. package/src/multimodal/image-loader.ts +143 -143
  127. package/src/orchestrate/adapters/claude-stream.ts +192 -192
  128. package/src/orchestrate/adapters/claude.ts +111 -111
  129. package/src/orchestrate/adapters/codex-stream.ts +134 -134
  130. package/src/orchestrate/adapters/codex.ts +41 -41
  131. package/src/orchestrate/adapters/gemini-stream.ts +166 -166
  132. package/src/orchestrate/adapters/gemini.ts +42 -42
  133. package/src/orchestrate/adapters/index.ts +73 -73
  134. package/src/orchestrate/adapters/opencode-stream.ts +143 -143
  135. package/src/orchestrate/adapters/opencode.ts +47 -47
  136. package/src/orchestrate/adapters/spawn-helper.ts +286 -286
  137. package/src/orchestrate/adapters/types.ts +77 -77
  138. package/src/orchestrate/capability-router.ts +284 -284
  139. package/src/orchestrate/context-compact.ts +188 -188
  140. package/src/orchestrate/cost-tracker.ts +219 -219
  141. package/src/orchestrate/error-recovery.ts +191 -191
  142. package/src/orchestrate/evidence.ts +140 -140
  143. package/src/orchestrate/ledger.ts +110 -110
  144. package/src/orchestrate/memorix-bridge.ts +378 -340
  145. package/src/orchestrate/output-budget.ts +80 -80
  146. package/src/orchestrate/permission.ts +152 -152
  147. package/src/orchestrate/pipeline-trace.ts +131 -131
  148. package/src/orchestrate/prompt-builder.ts +155 -155
  149. package/src/orchestrate/ring-buffer.ts +37 -37
  150. package/src/orchestrate/task-graph.ts +389 -389
  151. package/src/orchestrate/worktree.ts +232 -232
  152. package/src/project/aliases.ts +374 -374
  153. package/src/project/detector.ts +268 -268
  154. package/src/rules/adapters/claude-code.ts +99 -99
  155. package/src/rules/adapters/codex.ts +97 -97
  156. package/src/rules/adapters/copilot.ts +124 -124
  157. package/src/rules/adapters/cursor.ts +114 -114
  158. package/src/rules/adapters/kiro.ts +126 -126
  159. package/src/rules/adapters/trae.ts +56 -56
  160. package/src/rules/adapters/windsurf.ts +83 -83
  161. package/src/rules/syncer.ts +235 -235
  162. package/src/runtime/control-plane-maintenance.ts +1 -0
  163. package/src/runtime/isolated-maintenance.ts +1 -0
  164. package/src/runtime/lifecycle.ts +18 -0
  165. package/src/runtime/maintenance-jobs.ts +1 -0
  166. package/src/runtime/maintenance-runner.ts +2 -0
  167. package/src/runtime/project-maintenance.ts +89 -0
  168. package/src/sdk.ts +334 -304
  169. package/src/search/intent-detector.ts +289 -289
  170. package/src/search/query-expansion.ts +52 -52
  171. package/src/server/formation-timeout.ts +27 -27
  172. package/src/server.ts +260 -81
  173. package/src/skills/mini-skills.ts +386 -386
  174. package/src/store/chat-store.ts +119 -119
  175. package/src/store/graph-store.ts +249 -249
  176. package/src/store/mini-skill-store.ts +349 -349
  177. package/src/store/orama-store.ts +61 -6
  178. package/src/store/persistence-json.ts +212 -212
  179. package/src/store/persistence.ts +291 -291
  180. package/src/store/project-affinity.ts +195 -195
  181. package/src/store/sqlite-db.ts +23 -1
  182. package/src/store/sqlite-store.ts +12 -2
  183. package/src/team/event-bus.ts +76 -76
  184. package/src/team/file-locks.ts +173 -173
  185. package/src/team/handoff.ts +168 -161
  186. package/src/team/messages.ts +203 -203
  187. package/src/team/poll.ts +132 -132
  188. package/src/team/tasks.ts +211 -211
  189. package/src/types.ts +51 -0
  190. package/src/wiki/generator.ts +2 -0
  191. package/src/workspace/mcp-adapters/codex.ts +191 -191
  192. package/src/workspace/mcp-adapters/copilot.ts +105 -105
  193. package/src/workspace/mcp-adapters/cursor.ts +53 -53
  194. package/src/workspace/mcp-adapters/kiro.ts +64 -64
  195. package/src/workspace/mcp-adapters/opencode.ts +123 -123
  196. package/src/workspace/mcp-adapters/trae.ts +134 -134
  197. package/src/workspace/mcp-adapters/windsurf.ts +91 -91
  198. package/src/workspace/sanitizer.ts +60 -60
  199. package/src/workspace/workflow-sync.ts +131 -131
@@ -24,6 +24,7 @@ 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
26
  import { scopeKnowledgeGraphToProject } from '../../memory/graph-scope.js';
27
+ import { canManageObservation, filterReadableObservations } from '../../memory/visibility.js';
27
28
 
28
29
  export const DEFAULT_SESSION_TIMEOUT_MS = 30 * 60 * 1000;
29
30
 
@@ -558,7 +559,7 @@ export default defineCommand({
558
559
 
559
560
  async function loadDashboardObservations(dataDir: string) {
560
561
  const store = await getDashboardObservationStore(dataDir);
561
- return store.loadAll();
562
+ return filterReadableObservations(await store.loadAll(), {});
562
563
  }
563
564
 
564
565
  async function loadDashboardProjectObservations(
@@ -567,7 +568,10 @@ export default defineCommand({
567
568
  status?: string,
568
569
  ) {
569
570
  const store = await getDashboardObservationStore(dataDir);
570
- return store.loadByProject(projectId, status ? { status } : undefined);
571
+ return filterReadableObservations(
572
+ await store.loadByProject(projectId, status ? { status } : undefined),
573
+ { projectId },
574
+ );
571
575
  }
572
576
 
573
577
  // Resolve static directory (dist/dashboard/static)
@@ -1321,6 +1325,8 @@ export default defineCommand({
1321
1325
  sendJson({ error: 'Observation not found' }, 404);
1322
1326
  } else if ((matchObs as any).projectId !== delProjectId) {
1323
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);
1324
1330
  } else {
1325
1331
  await store.remove(obsId);
1326
1332
  // Sync: clean up graph entity references
@@ -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('');
@@ -1,123 +1,128 @@
1
- import { defineCommand } from 'citty';
1
+ import { defineCommand } from 'citty';
2
2
  import type { AgentTarget } from '../../types.js';
3
3
  import { getObservationStore } from '../../store/obs-store.js';
4
- import { emitError, emitResult, getCliProjectContext } from './operator-shared.js';
5
-
6
- export default defineCommand({
7
- meta: {
8
- name: 'skills',
9
- description: 'Discover, generate, and inspect project skills from the CLI',
10
- },
11
- args: {
12
- name: { type: 'string', description: 'Skill name for "show"' },
13
- target: { type: 'string', description: 'Target agent for generated skills' },
14
- write: { type: 'boolean', description: 'Write generated skills to disk' },
15
- json: { type: 'boolean', description: 'Emit machine-readable JSON output' },
16
- },
17
- run: async ({ args }) => {
18
- const action = (args._ as string[])?.[0] || '';
19
- const asJson = !!args.json;
20
-
21
- try {
22
- const { project } = await getCliProjectContext();
23
- const { SkillsEngine } = await import('../../skills/engine.js');
24
- const engine = new SkillsEngine(project.rootPath);
25
-
26
- switch (action) {
27
- case 'list': {
28
- const skills = engine.listSkills();
29
- emitResult(
30
- { project, skills },
31
- skills.length === 0
32
- ? 'No skills found in project or global agent directories.'
33
- : skills.map((skill) => `- ${skill.name} (${skill.sourceAgent})`).join('\n'),
34
- asJson,
35
- );
36
- return;
37
- }
38
-
39
- case 'generate': {
40
- const observations = (await getObservationStore().loadAll())
41
- .filter((obs) => obs.projectId === project.id)
4
+ import { filterReadableObservations, resolveObservationVisibility } from '../../memory/visibility.js';
5
+ import { emitError, emitResult, getCliProjectContext } from './operator-shared.js';
6
+
7
+ export default defineCommand({
8
+ meta: {
9
+ name: 'skills',
10
+ description: 'Discover, generate, and inspect project skills from the CLI',
11
+ },
12
+ args: {
13
+ name: { type: 'string', description: 'Skill name for "show"' },
14
+ target: { type: 'string', description: 'Target agent for generated skills' },
15
+ write: { type: 'boolean', description: 'Write generated skills to disk' },
16
+ json: { type: 'boolean', description: 'Emit machine-readable JSON output' },
17
+ },
18
+ run: async ({ args }) => {
19
+ const action = (args._ as string[])?.[0] || '';
20
+ const asJson = !!args.json;
21
+
22
+ try {
23
+ const { project, reader } = await getCliProjectContext();
24
+ const { SkillsEngine } = await import('../../skills/engine.js');
25
+ const engine = new SkillsEngine(project.rootPath);
26
+
27
+ switch (action) {
28
+ case 'list': {
29
+ const skills = engine.listSkills();
30
+ emitResult(
31
+ { project, skills },
32
+ skills.length === 0
33
+ ? 'No skills found in project or global agent directories.'
34
+ : skills.map((skill) => `- ${skill.name} (${skill.sourceAgent})`).join('\n'),
35
+ asJson,
36
+ );
37
+ return;
38
+ }
39
+
40
+ case 'generate': {
41
+ const observations = filterReadableObservations(
42
+ await getObservationStore().loadAll(),
43
+ reader,
44
+ ).filter((observation) => resolveObservationVisibility(observation) === 'project')
42
45
  .map((obs) => ({
43
- id: obs.id,
44
- entityName: obs.entityName,
45
- type: obs.type,
46
- title: obs.title,
47
- narrative: obs.narrative,
48
- facts: obs.facts,
49
- concepts: obs.concepts,
50
- filesModified: obs.filesModified,
51
- createdAt: obs.createdAt,
52
- status: obs.status,
53
- source: obs.source,
54
- }));
55
- const skills = engine.generateFromObservations(observations);
56
- const written: string[] = [];
57
- if (args.write && args.target) {
58
- for (const skill of skills) {
59
- const writtenPath = engine.writeSkill(skill, args.target as AgentTarget);
60
- if (writtenPath) written.push(writtenPath);
61
- }
62
- }
63
- emitResult(
64
- { project, skills, written },
65
- skills.length === 0
66
- ? 'No skill-worthy patterns found yet.'
67
- : skills.map((skill) => `- ${skill.name}: ${skill.description}`).join('\n'),
68
- asJson,
69
- );
70
- return;
71
- }
72
-
73
- case 'show': {
74
- const name = (args.name as string | undefined)?.trim();
75
- if (!name) {
76
- emitError('name is required for "memorix skills show"', asJson);
77
- return;
78
- }
79
- const direct = engine.injectSkill(name);
80
- if (direct) {
81
- emitResult({ project, skill: direct }, direct.content, asJson);
82
- return;
83
- }
84
-
85
- const observations = (await getObservationStore().loadAll())
86
- .filter((obs) => obs.projectId === project.id)
46
+ id: obs.id,
47
+ entityName: obs.entityName,
48
+ type: obs.type,
49
+ title: obs.title,
50
+ narrative: obs.narrative,
51
+ facts: obs.facts,
52
+ concepts: obs.concepts,
53
+ filesModified: obs.filesModified,
54
+ createdAt: obs.createdAt,
55
+ status: obs.status,
56
+ source: obs.source,
57
+ }));
58
+ const skills = engine.generateFromObservations(observations);
59
+ const written: string[] = [];
60
+ if (args.write && args.target) {
61
+ for (const skill of skills) {
62
+ const writtenPath = engine.writeSkill(skill, args.target as AgentTarget);
63
+ if (writtenPath) written.push(writtenPath);
64
+ }
65
+ }
66
+ emitResult(
67
+ { project, skills, written },
68
+ skills.length === 0
69
+ ? 'No skill-worthy patterns found yet.'
70
+ : skills.map((skill) => `- ${skill.name}: ${skill.description}`).join('\n'),
71
+ asJson,
72
+ );
73
+ return;
74
+ }
75
+
76
+ case 'show': {
77
+ const name = (args.name as string | undefined)?.trim();
78
+ if (!name) {
79
+ emitError('name is required for "memorix skills show"', asJson);
80
+ return;
81
+ }
82
+ const direct = engine.injectSkill(name);
83
+ if (direct) {
84
+ emitResult({ project, skill: direct }, direct.content, asJson);
85
+ return;
86
+ }
87
+
88
+ const observations = filterReadableObservations(
89
+ await getObservationStore().loadAll(),
90
+ reader,
91
+ ).filter((observation) => resolveObservationVisibility(observation) === 'project')
87
92
  .map((obs) => ({
88
- id: obs.id,
89
- entityName: obs.entityName,
90
- type: obs.type,
91
- title: obs.title,
92
- narrative: obs.narrative,
93
- facts: obs.facts,
94
- concepts: obs.concepts,
95
- filesModified: obs.filesModified,
96
- createdAt: obs.createdAt,
97
- status: obs.status,
98
- source: obs.source,
99
- }));
100
- const generated = engine.generateFromObservations(observations);
101
- const skill = generated.find((entry) => entry.name.toLowerCase() === name.toLowerCase());
102
- if (!skill) {
103
- emitError(`Skill "${name}" not found`, asJson);
104
- return;
105
- }
106
- emitResult({ project, skill }, skill.content, asJson);
107
- return;
108
- }
109
-
110
- default:
111
- console.log('Memorix Skills Commands');
112
- console.log('');
113
- console.log('Usage:');
114
- console.log(' memorix skills list');
115
- console.log(' memorix skills generate [--write --target codex]');
116
- console.log(' memorix skills show --name <skill>');
117
- }
118
- } catch (error) {
119
- emitError(error instanceof Error ? error.message : String(error), asJson);
120
- }
121
- },
122
- });
123
-
93
+ id: obs.id,
94
+ entityName: obs.entityName,
95
+ type: obs.type,
96
+ title: obs.title,
97
+ narrative: obs.narrative,
98
+ facts: obs.facts,
99
+ concepts: obs.concepts,
100
+ filesModified: obs.filesModified,
101
+ createdAt: obs.createdAt,
102
+ status: obs.status,
103
+ source: obs.source,
104
+ }));
105
+ const generated = engine.generateFromObservations(observations);
106
+ const skill = generated.find((entry) => entry.name.toLowerCase() === name.toLowerCase());
107
+ if (!skill) {
108
+ emitError(`Skill "${name}" not found`, asJson);
109
+ return;
110
+ }
111
+ emitResult({ project, skill }, skill.content, asJson);
112
+ return;
113
+ }
114
+
115
+ default:
116
+ console.log('Memorix Skills Commands');
117
+ console.log('');
118
+ console.log('Usage:');
119
+ console.log(' memorix skills list');
120
+ console.log(' memorix skills generate [--write --target codex]');
121
+ console.log(' memorix skills show --name <skill>');
122
+ }
123
+ } catch (error) {
124
+ emitError(error instanceof Error ? error.message : String(error), asJson);
125
+ }
126
+ },
127
+ });
128
+
@@ -39,8 +39,8 @@ export default defineCommand({
39
39
  const { initObservationStore, getObservationStore } = await import('../../store/obs-store.js');
40
40
  await initObservationStore(dataDir);
41
41
  const store = getObservationStore();
42
- const data = await store.loadAll() as Array<{ projectId?: string; status?: string }>;
43
- const projectObs = data.filter(o => o.projectId === project.id);
42
+ const { filterReadableObservations } = await import('../../memory/visibility.js');
43
+ const projectObs = filterReadableObservations(await store.loadAll(), { projectId: project.id });
44
44
  obsCount = projectObs.length;
45
45
  activeCount = projectObs.filter(o => (o.status ?? 'active') === 'active').length;
46
46
  } catch { /* ignore */ }
@@ -165,7 +165,8 @@ export default defineCommand({
165
165
  const { initObservationStore, getObservationStore } = await import('../../store/obs-store.js');
166
166
  await initObservationStore(dataDir);
167
167
  const store = getObservationStore();
168
- const allObs = await store.loadAll() as Array<{ source?: string; type?: string }>;
168
+ const { filterReadableObservations } = await import('../../memory/visibility.js');
169
+ const allObs = filterReadableObservations(await store.loadAll(), { projectId: project.id });
169
170
  const gitCount = allObs.filter(o => o.source === 'git').length;
170
171
  const reasoningCount = allObs.filter(o => o.type === 'reasoning').length;
171
172
  if (gitCount > 0 || reasoningCount > 0) {