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
@@ -1,71 +1,71 @@
1
- /**
2
- * CLI Command: memorix hooks status
3
- *
4
- * Show hook installation status for all agents.
1
+ /**
2
+ * CLI Command: memorix hooks status
3
+ *
4
+ * Show hook installation status for all agents.
5
5
  * Status marks:
6
6
  * [OK] - installed and verified (config-based agent, file read directly)
7
7
  * [??] - installed but unverified (plugin-based agent, runtime load not confirmed)
8
8
  * [!!] - installed but outdated
9
9
  * [ ] - not installed
10
- */
11
-
12
- import { defineCommand } from 'citty';
13
-
14
- export default defineCommand({
15
- meta: {
16
- name: 'status',
17
- description: 'Show hook installation status for all agents',
18
- },
19
- run: async () => {
20
- const { getHookStatus } = await import('../../hooks/installers/index.js');
21
- const { AGENT_SUPPORT_TIER } = await import('../../hooks/types.js');
22
- const os = await import('node:os');
23
- let cwd: string;
24
- try { cwd = process.cwd(); } catch { cwd = os.homedir(); }
25
-
26
- const statuses = await getHookStatus(cwd);
27
-
28
- console.log('\nMemorix Hooks Status');
10
+ */
11
+
12
+ import { defineCommand } from 'citty';
13
+
14
+ export default defineCommand({
15
+ meta: {
16
+ name: 'status',
17
+ description: 'Show hook installation status for all agents',
18
+ },
19
+ run: async () => {
20
+ const { getHookStatus } = await import('../../hooks/installers/index.js');
21
+ const { AGENT_SUPPORT_TIER } = await import('../../hooks/types.js');
22
+ const os = await import('node:os');
23
+ let cwd: string;
24
+ try { cwd = process.cwd(); } catch { cwd = os.homedir(); }
25
+
26
+ const statuses = await getHookStatus(cwd);
27
+
28
+ console.log('\nMemorix Hooks Status');
29
29
  console.log('-'.repeat(50));
30
-
31
- let hasOutdated = false;
32
- let hasUnverified = false;
33
- let hasRuntimeIssue = false;
34
- for (const { agent, installed, outdated, verified, runtimeReady, configPath } of statuses) {
35
- const icon = installed
36
- ? (!runtimeReady ? '[!!]' : (outdated ? '[!!]' : (verified ? '[OK]' : '[??]')))
37
- : '[ ]';
38
- const label = agent.charAt(0).toUpperCase() + agent.slice(1);
39
- const tier = AGENT_SUPPORT_TIER[agent];
30
+
31
+ let hasOutdated = false;
32
+ let hasUnverified = false;
33
+ let hasRuntimeIssue = false;
34
+ for (const { agent, installed, outdated, verified, runtimeReady, configPath } of statuses) {
35
+ const icon = installed
36
+ ? (!runtimeReady ? '[!!]' : (outdated ? '[!!]' : (verified ? '[OK]' : '[??]')))
37
+ : '[ ]';
38
+ const label = agent.charAt(0).toUpperCase() + agent.slice(1);
39
+ const tier = AGENT_SUPPORT_TIER[agent];
40
40
  const tierLabel = tier === 'core' ? '[core]' : tier === 'extended' ? '[extended]' : '[community]';
41
- const suffix = !runtimeReady && installed
42
- ? ' (installed but runtime NOT ready — missing pwsh on Windows)'
43
- : outdated
44
- ? ' (outdated - re-run `memorix hooks install`)'
45
- : (!verified && installed ? ' (plugin file installed, runtime load unverified)' : '');
46
- console.log(`${icon} ${tierLabel} ${label.padEnd(12)} ${installed ? configPath + suffix : '(not installed)'}`);
47
- if (outdated) hasOutdated = true;
48
- if (!verified && installed) hasUnverified = true;
49
- if (!runtimeReady && installed) hasRuntimeIssue = true;
50
- }
51
-
41
+ const suffix = !runtimeReady && installed
42
+ ? ' (installed but runtime NOT ready — missing pwsh on Windows)'
43
+ : outdated
44
+ ? ' (outdated - re-run `memorix hooks install`)'
45
+ : (!verified && installed ? ' (plugin file installed, runtime load unverified)' : '');
46
+ console.log(`${icon} ${tierLabel} ${label.padEnd(12)} ${installed ? configPath + suffix : '(not installed)'}`);
47
+ if (outdated) hasOutdated = true;
48
+ if (!verified && installed) hasUnverified = true;
49
+ if (!runtimeReady && installed) hasRuntimeIssue = true;
50
+ }
51
+
52
52
  console.log('\nSupport tiers: [core] core | [extended] extended | [community] community');
53
-
54
- if (hasOutdated) {
55
- console.log('\n[warn] Outdated hooks detected. Run `memorix hooks install` to update.');
56
- }
57
- if (hasUnverified) {
58
- console.log('\n[info] [??] = plugin file installed but runtime load unverified.');
59
- console.log(' For OpenCode: verify by running `opencode --log-level DEBUG` and checking');
60
- console.log(' for [memorix-plugin] messages in the log output.');
61
- }
62
- if (hasRuntimeIssue) {
63
- console.log('\n[warn] [!!] = installed but runtime will fail.');
64
- console.log(' Copilot on Windows requires PowerShell v6+ (pwsh.exe) for hook execution.');
65
- console.log(' Install pwsh: https://learn.microsoft.com/powershell/scripting/install/installing-powershell');
66
- console.log(' Without pwsh, Copilot hooks will fail at runtime with "spawn pwsh.exe ENOENT".');
67
- }
68
-
69
- console.log('\nRun `memorix hooks install` to set up hooks for detected agents.');
70
- },
71
- });
53
+
54
+ if (hasOutdated) {
55
+ console.log('\n[warn] Outdated hooks detected. Run `memorix hooks install` to update.');
56
+ }
57
+ if (hasUnverified) {
58
+ console.log('\n[info] [??] = plugin file installed but runtime load unverified.');
59
+ console.log(' For OpenCode: verify by running `opencode --log-level DEBUG` and checking');
60
+ console.log(' for [memorix-plugin] messages in the log output.');
61
+ }
62
+ if (hasRuntimeIssue) {
63
+ console.log('\n[warn] [!!] = installed but runtime will fail.');
64
+ console.log(' Copilot on Windows requires PowerShell v6+ (pwsh.exe) for hook execution.');
65
+ console.log(' Install pwsh: https://learn.microsoft.com/powershell/scripting/install/installing-powershell');
66
+ console.log(' Without pwsh, Copilot hooks will fail at runtime with "spawn pwsh.exe ENOENT".');
67
+ }
68
+
69
+ console.log('\nRun `memorix hooks install` to set up hooks for detected agents.');
70
+ },
71
+ });
@@ -0,0 +1,116 @@
1
+ import { defineCommand } from 'citty';
2
+ import { AGENT_TYPE_ROLE_MAP } from '../../team/team-store.js';
3
+ import { clearCliIdentity, loadCliIdentity, saveCliIdentity } from '../identity.js';
4
+ import { emitError, emitResult, getCliProjectContext, parseCsvList } from './operator-shared.js';
5
+
6
+ function requiredText(value: unknown, label: string): string {
7
+ const text = typeof value === 'string' ? value.trim() : '';
8
+ if (!text) throw new Error(`${label} is required`);
9
+ return text;
10
+ }
11
+
12
+ export default defineCommand({
13
+ meta: {
14
+ name: 'identity',
15
+ description: 'Select the explicit CLI actor used for private or team-scoped operations',
16
+ },
17
+ args: {
18
+ agentId: { type: 'string', description: 'Existing active coordination agent ID' },
19
+ agentType: { type: 'string', description: 'Agent type when joining (for example codex)' },
20
+ instanceId: { type: 'string', description: 'Stable instance identity when joining' },
21
+ name: { type: 'string', description: 'Optional display name when joining' },
22
+ role: { type: 'string', description: 'Optional coordination role when joining' },
23
+ capabilities: { type: 'string', description: 'Comma-separated capability list when joining' },
24
+ json: { type: 'boolean', description: 'Emit machine-readable JSON output' },
25
+ },
26
+ run: async ({ args }) => {
27
+ const action = ((args._ as string[])?.[0] || 'status').toLowerCase();
28
+ const asJson = !!args.json;
29
+
30
+ try {
31
+ const { project, dataDir, teamStore, identity, reader, identityWarning } = await getCliProjectContext();
32
+
33
+ switch (action) {
34
+ case 'status': {
35
+ const saved = await loadCliIdentity(dataDir, project.id);
36
+ const agent = saved ? teamStore.getAgent(saved.agentId) : undefined;
37
+ emitResult(
38
+ {
39
+ project,
40
+ identity,
41
+ savedIdentity: saved,
42
+ agent: agent ?? null,
43
+ reader,
44
+ ...(identityWarning ? { warning: identityWarning } : {}),
45
+ },
46
+ identity
47
+ ? `CLI identity: ${agent?.name ?? identity.agentId} (${identity.agentId})`
48
+ : identityWarning ?? 'CLI identity: project-scoped only. Use "memorix identity join" or "memorix identity use --agent-id <id>" for private/team operations.',
49
+ asJson,
50
+ );
51
+ return;
52
+ }
53
+
54
+ case 'join': {
55
+ const agentType = requiredText(args.agentType, 'agentType');
56
+ const agent = teamStore.registerAgent({
57
+ projectId: project.id,
58
+ agentType,
59
+ instanceId: args.instanceId as string | undefined,
60
+ name: args.name as string | undefined,
61
+ role: (args.role as string | undefined) || AGENT_TYPE_ROLE_MAP[agentType] || 'engineer',
62
+ capabilities: parseCsvList(args.capabilities as string | undefined),
63
+ });
64
+ const nextIdentity = {
65
+ agentId: agent.agent_id,
66
+ projectId: project.id,
67
+ activatedAt: new Date().toISOString(),
68
+ };
69
+ await saveCliIdentity(dataDir, nextIdentity);
70
+ emitResult(
71
+ { project, identity: nextIdentity, agent },
72
+ `CLI identity activated: ${agent.name} (${agent.agent_id})`,
73
+ asJson,
74
+ );
75
+ return;
76
+ }
77
+
78
+ case 'use': {
79
+ const agentId = requiredText(args.agentId, 'agentId');
80
+ const agent = teamStore.getAgent(agentId);
81
+ if (!agent || agent.project_id !== project.id || agent.status !== 'active') {
82
+ emitError('agentId must identify an active coordination member of this project.', asJson);
83
+ return;
84
+ }
85
+ const nextIdentity = {
86
+ agentId: agent.agent_id,
87
+ projectId: project.id,
88
+ activatedAt: new Date().toISOString(),
89
+ };
90
+ await saveCliIdentity(dataDir, nextIdentity);
91
+ emitResult(
92
+ { project, identity: nextIdentity, agent },
93
+ `CLI identity activated: ${agent.name} (${agent.agent_id})`,
94
+ asJson,
95
+ );
96
+ return;
97
+ }
98
+
99
+ case 'clear': {
100
+ await clearCliIdentity(dataDir);
101
+ emitResult(
102
+ { project, cleared: true },
103
+ 'CLI identity cleared. Subsequent commands use project-scoped access only.',
104
+ asJson,
105
+ );
106
+ return;
107
+ }
108
+
109
+ default:
110
+ emitError(`unknown identity action "${action}". Use "status", "join", "use", or "clear".`, asJson);
111
+ }
112
+ } catch (error) {
113
+ emitError(error instanceof Error ? error.message : String(error), asJson);
114
+ }
115
+ },
116
+ });
@@ -1,153 +1,153 @@
1
- /**
2
- * CLI Command: memorix ingest commit
3
- *
4
- * Extract engineering knowledge from a git commit and store as memory.
5
- * This is the Git→Memory direction — unique to Memorix.
6
- */
7
-
8
- import { defineCommand } from 'citty';
9
- import * as p from '@clack/prompts';
10
-
11
- export default defineCommand({
12
- meta: {
13
- name: 'commit',
14
- description: 'Ingest a git commit as memory',
15
- },
16
- args: {
17
- ref: {
18
- type: 'string',
19
- description: 'Commit ref (default: HEAD)',
20
- required: false,
21
- },
22
- auto: {
23
- type: 'boolean',
24
- description: 'Non-interactive mode (used by git post-commit hook)',
25
- required: false,
26
- },
27
- force: {
28
- type: 'boolean',
29
- description: 'Bypass Git noise filter and ingest anyway',
30
- required: false,
31
- },
32
- },
33
- run: async ({ args }) => {
34
- const os = await import('node:os');
35
- let cwd: string;
36
- try { cwd = process.cwd(); } catch { cwd = os.homedir(); }
37
-
38
- const ref = args.ref || 'HEAD';
39
- const auto = !!args.auto;
40
- const force = !!args.force;
41
-
42
- if (!auto) p.intro(`Ingest commit: ${ref}`);
43
-
44
- try {
45
- const { getCommitInfo, ingestCommit } = await import('../../git/extractor.js');
46
- const commit = getCommitInfo(cwd, ref);
47
-
48
- // Noise filter: skip low-value commits (typo, format, lockfile, merge, etc.)
49
- const { shouldFilterCommit } = await import('../../git/noise-filter.js');
50
- const { getGitConfig } = await import('../../config.js');
51
- const gitCfg = getGitConfig();
52
- const filterResult = shouldFilterCommit(commit, {
53
- skipMergeCommits: gitCfg.skipMergeCommits,
54
- excludePatterns: gitCfg.excludePatterns,
55
- noiseKeywords: gitCfg.noiseKeywords,
56
- });
57
- if (filterResult.skip && !force) {
58
- if (auto) {
59
- console.error(`[memorix] Skipped ${commit.shortHash}: ${filterResult.reason}`);
60
- process.exit(0);
61
- } else {
62
- p.log.warn(`Commit ${commit.shortHash} filtered as noise: ${filterResult.reason}`);
63
- p.outro('Use --force to override the noise filter.');
64
- }
65
- return;
66
- }
67
-
68
- const result = ingestCommit(commit);
69
-
70
- // Store via memorix_store logic
71
- const { initObservations, storeObservation } = await import('../../memory/observations.js');
72
- const { getProjectDataDir } = await import('../../store/persistence.js');
73
- const { detectProject } = await import('../../project/detector.js');
74
- const { initObservationStore, getObservationStore: getStore } = await import('../../store/obs-store.js');
75
-
76
- const project = detectProject(cwd);
77
- if (!project) {
78
- if (!auto) p.log.error('No .git found — not a project directory.');
79
- return;
80
- }
81
- const dataDir = await getProjectDataDir(project.id);
82
- await initObservationStore(dataDir);
83
- await initObservations(dataDir);
84
-
85
- // Dedup: skip if this commit hash was already ingested
86
- const existingObs = await getStore().loadAll() as Array<{ commitHash?: string }>;
87
- if (existingObs.some(o => o.commitHash === commit.hash)) {
88
- if (!auto) p.log.warn(`Commit ${commit.shortHash} already ingested. Skipping.`);
89
- if (auto) process.exit(0);
90
- return;
91
- }
92
-
93
- if (!auto) {
94
- // Interactive: show details and confirm
95
- console.log('');
96
- console.log(`Commit: ${commit.shortHash} — ${commit.subject}`);
97
- console.log(`Author: ${commit.author} @ ${commit.date}`);
98
- console.log(`Files: ${commit.filesChanged.length} changed (+${commit.insertions}/-${commit.deletions})`);
99
- console.log('');
100
- console.log(`Entity: ${result.entityName}`);
101
- console.log(`Type: ${result.type}`);
102
- console.log(`Title: ${result.title}`);
103
- console.log('');
104
- console.log('Facts:');
105
- for (const fact of result.facts) {
106
- console.log(` - ${fact}`);
107
- }
108
- if (result.concepts.length > 0) {
109
- console.log(`Concepts: ${result.concepts.join(', ')}`);
110
- }
111
- console.log('');
112
-
113
- const confirmed = await p.confirm({
114
- message: 'Store this commit as memory?',
115
- });
116
-
117
- if (p.isCancel(confirmed) || !confirmed) {
118
- p.outro('Ingest cancelled.');
119
- return;
120
- }
121
- }
122
-
123
- await storeObservation({
124
- entityName: result.entityName,
125
- type: result.type as any,
126
- title: result.title,
127
- sourceDetail: 'git-ingest',
128
- narrative: result.narrative,
129
- facts: result.facts,
130
- concepts: result.concepts,
131
- filesModified: result.filesModified,
132
- projectId: project.id,
133
- source: 'git',
134
- commitHash: commit.hash,
135
- });
136
-
137
- if (auto) {
138
- console.error(`[memorix] Git memory: ${commit.shortHash} — ${commit.subject}`);
139
- process.exit(0);
140
- } else {
141
- p.outro(`Memory stored from commit ${commit.shortHash}.`);
142
- }
143
- } catch (err) {
144
- if (auto) {
145
- console.error(`[memorix] Git hook ingest failed: ${err}`);
146
- process.exit(1);
147
- } else {
148
- console.error(`Failed to ingest commit: ${err}`);
149
- p.outro('Ingest failed. Make sure you are in a git repository.');
150
- }
151
- }
152
- },
153
- });
1
+ /**
2
+ * CLI Command: memorix ingest commit
3
+ *
4
+ * Extract engineering knowledge from a git commit and store as memory.
5
+ * This is the Git→Memory direction — unique to Memorix.
6
+ */
7
+
8
+ import { defineCommand } from 'citty';
9
+ import * as p from '@clack/prompts';
10
+
11
+ export default defineCommand({
12
+ meta: {
13
+ name: 'commit',
14
+ description: 'Ingest a git commit as memory',
15
+ },
16
+ args: {
17
+ ref: {
18
+ type: 'string',
19
+ description: 'Commit ref (default: HEAD)',
20
+ required: false,
21
+ },
22
+ auto: {
23
+ type: 'boolean',
24
+ description: 'Non-interactive mode (used by git post-commit hook)',
25
+ required: false,
26
+ },
27
+ force: {
28
+ type: 'boolean',
29
+ description: 'Bypass Git noise filter and ingest anyway',
30
+ required: false,
31
+ },
32
+ },
33
+ run: async ({ args }) => {
34
+ const os = await import('node:os');
35
+ let cwd: string;
36
+ try { cwd = process.cwd(); } catch { cwd = os.homedir(); }
37
+
38
+ const ref = args.ref || 'HEAD';
39
+ const auto = !!args.auto;
40
+ const force = !!args.force;
41
+
42
+ if (!auto) p.intro(`Ingest commit: ${ref}`);
43
+
44
+ try {
45
+ const { getCommitInfo, ingestCommit } = await import('../../git/extractor.js');
46
+ const commit = getCommitInfo(cwd, ref);
47
+
48
+ // Noise filter: skip low-value commits (typo, format, lockfile, merge, etc.)
49
+ const { shouldFilterCommit } = await import('../../git/noise-filter.js');
50
+ const { getGitConfig } = await import('../../config.js');
51
+ const gitCfg = getGitConfig();
52
+ const filterResult = shouldFilterCommit(commit, {
53
+ skipMergeCommits: gitCfg.skipMergeCommits,
54
+ excludePatterns: gitCfg.excludePatterns,
55
+ noiseKeywords: gitCfg.noiseKeywords,
56
+ });
57
+ if (filterResult.skip && !force) {
58
+ if (auto) {
59
+ console.error(`[memorix] Skipped ${commit.shortHash}: ${filterResult.reason}`);
60
+ process.exit(0);
61
+ } else {
62
+ p.log.warn(`Commit ${commit.shortHash} filtered as noise: ${filterResult.reason}`);
63
+ p.outro('Use --force to override the noise filter.');
64
+ }
65
+ return;
66
+ }
67
+
68
+ const result = ingestCommit(commit);
69
+
70
+ // Store via memorix_store logic
71
+ const { initObservations, storeObservation } = await import('../../memory/observations.js');
72
+ const { getProjectDataDir } = await import('../../store/persistence.js');
73
+ const { detectProject } = await import('../../project/detector.js');
74
+ const { initObservationStore, getObservationStore: getStore } = await import('../../store/obs-store.js');
75
+
76
+ const project = detectProject(cwd);
77
+ if (!project) {
78
+ if (!auto) p.log.error('No .git found — not a project directory.');
79
+ return;
80
+ }
81
+ const dataDir = await getProjectDataDir(project.id);
82
+ await initObservationStore(dataDir);
83
+ await initObservations(dataDir);
84
+
85
+ // Dedup: skip if this commit hash was already ingested
86
+ const existingObs = await getStore().loadAll() as Array<{ commitHash?: string }>;
87
+ if (existingObs.some(o => o.commitHash === commit.hash)) {
88
+ if (!auto) p.log.warn(`Commit ${commit.shortHash} already ingested. Skipping.`);
89
+ if (auto) process.exit(0);
90
+ return;
91
+ }
92
+
93
+ if (!auto) {
94
+ // Interactive: show details and confirm
95
+ console.log('');
96
+ console.log(`Commit: ${commit.shortHash} — ${commit.subject}`);
97
+ console.log(`Author: ${commit.author} @ ${commit.date}`);
98
+ console.log(`Files: ${commit.filesChanged.length} changed (+${commit.insertions}/-${commit.deletions})`);
99
+ console.log('');
100
+ console.log(`Entity: ${result.entityName}`);
101
+ console.log(`Type: ${result.type}`);
102
+ console.log(`Title: ${result.title}`);
103
+ console.log('');
104
+ console.log('Facts:');
105
+ for (const fact of result.facts) {
106
+ console.log(` - ${fact}`);
107
+ }
108
+ if (result.concepts.length > 0) {
109
+ console.log(`Concepts: ${result.concepts.join(', ')}`);
110
+ }
111
+ console.log('');
112
+
113
+ const confirmed = await p.confirm({
114
+ message: 'Store this commit as memory?',
115
+ });
116
+
117
+ if (p.isCancel(confirmed) || !confirmed) {
118
+ p.outro('Ingest cancelled.');
119
+ return;
120
+ }
121
+ }
122
+
123
+ await storeObservation({
124
+ entityName: result.entityName,
125
+ type: result.type as any,
126
+ title: result.title,
127
+ sourceDetail: 'git-ingest',
128
+ narrative: result.narrative,
129
+ facts: result.facts,
130
+ concepts: result.concepts,
131
+ filesModified: result.filesModified,
132
+ projectId: project.id,
133
+ source: 'git',
134
+ commitHash: commit.hash,
135
+ });
136
+
137
+ if (auto) {
138
+ console.error(`[memorix] Git memory: ${commit.shortHash} — ${commit.subject}`);
139
+ process.exit(0);
140
+ } else {
141
+ p.outro(`Memory stored from commit ${commit.shortHash}.`);
142
+ }
143
+ } catch (err) {
144
+ if (auto) {
145
+ console.error(`[memorix] Git hook ingest failed: ${err}`);
146
+ process.exit(1);
147
+ } else {
148
+ console.error(`Failed to ingest commit: ${err}`);
149
+ p.outro('Ingest failed. Make sure you are in a git repository.');
150
+ }
151
+ }
152
+ },
153
+ });