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
@@ -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
+ });