memorix 1.1.7 → 1.1.8
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.
- package/CHANGELOG.md +13 -0
- package/CLAUDE.md +6 -1
- package/README.md +21 -0
- package/README.zh-CN.md +21 -0
- package/TEAM.md +86 -86
- package/dist/cli/index.js +730 -150
- package/dist/cli/index.js.map +1 -1
- package/dist/dashboard/static/index.html +201 -201
- package/dist/dashboard/static/style.css +3584 -3584
- package/dist/index.js +15 -8
- package/dist/index.js.map +1 -1
- package/dist/memcode-runtime/CHANGELOG.md +13 -0
- package/dist/memcode-runtime/package.json +4 -4
- package/dist/sdk.js +15 -8
- package/dist/sdk.js.map +1 -1
- package/docs/AGENT_OPERATOR_PLAYBOOK.md +18 -0
- package/docs/DESIGN_DECISIONS.md +357 -357
- package/docs/SETUP.md +10 -0
- package/docs/dev-log/progress.txt +35 -26
- package/package.json +1 -1
- package/src/audit/index.ts +156 -156
- package/src/cli/commands/agent-integrations.ts +623 -0
- package/src/cli/commands/audit-list.ts +89 -89
- package/src/cli/commands/background.ts +659 -659
- package/src/cli/commands/cleanup.ts +255 -255
- package/src/cli/commands/doctor.ts +23 -0
- package/src/cli/commands/formation.ts +48 -48
- package/src/cli/commands/git-hook-install.ts +111 -111
- package/src/cli/commands/handoff.ts +66 -66
- package/src/cli/commands/hooks-status.ts +63 -63
- package/src/cli/commands/ingest-commit.ts +153 -153
- package/src/cli/commands/ingest-image.ts +73 -73
- package/src/cli/commands/ingest-log.ts +180 -180
- package/src/cli/commands/ingest.ts +44 -44
- package/src/cli/commands/integrate-shared.ts +15 -15
- package/src/cli/commands/lock.ts +96 -96
- package/src/cli/commands/message.ts +121 -121
- package/src/cli/commands/poll.ts +70 -70
- package/src/cli/commands/purge-all-memory.ts +85 -85
- package/src/cli/commands/purge-project-memory.ts +83 -83
- package/src/cli/commands/reasoning.ts +132 -132
- package/src/cli/commands/repair.ts +60 -0
- package/src/cli/commands/retention.ts +108 -108
- package/src/cli/commands/serve-shared.ts +118 -118
- package/src/cli/commands/setup.ts +3 -3
- package/src/cli/commands/skills.ts +123 -123
- package/src/cli/commands/task.ts +192 -192
- package/src/cli/commands/transfer.ts +73 -73
- package/src/cli/commands/uninstall-project-artifacts.ts +85 -85
- package/src/cli/index.ts +3 -1
- package/src/cli/tui/ChatView.tsx +234 -234
- package/src/cli/tui/CommandBar.tsx +312 -312
- package/src/cli/tui/ContextRail.tsx +118 -118
- package/src/cli/tui/HeaderBar.tsx +72 -72
- package/src/cli/tui/LogoBanner.tsx +51 -51
- package/src/cli/tui/Panels.tsx +632 -632
- package/src/cli/tui/Sidebar.tsx +179 -179
- package/src/cli/tui/chat-service.ts +742 -742
- package/src/cli/tui/data.ts +547 -547
- package/src/cli/tui/index.ts +41 -41
- package/src/cli/tui/markdown-render.tsx +371 -371
- package/src/cli/tui/theme.ts +178 -178
- package/src/cli/tui/use-mouse.ts +157 -157
- package/src/cli/tui/useNavigation.ts +56 -56
- package/src/cli/update-checker.ts +211 -211
- package/src/cli/version.ts +7 -7
- package/src/cli/workbench.ts +1 -1
- package/src/codegraph/context-pack.ts +5 -1
- package/src/codegraph/lite-provider.ts +5 -1
- package/src/codegraph/project-context.ts +5 -1
- package/src/compact/token-budget.ts +74 -74
- package/src/config/behavior.ts +59 -59
- package/src/dashboard/project-classification.ts +64 -64
- package/src/dashboard/static/index.html +201 -201
- package/src/dashboard/static/style.css +3584 -3584
- package/src/embedding/fastembed-provider.ts +142 -142
- package/src/embedding/transformers-provider.ts +111 -111
- package/src/git/extractor.ts +209 -209
- package/src/git/hooks-path.ts +85 -85
- package/src/git/noise-filter.ts +210 -210
- package/src/hooks/installers/index.ts +4 -4
- package/src/hooks/official-skills.ts +1 -1
- package/src/hooks/pattern-detector.ts +173 -173
- package/src/hooks/rules/memorix-agent-rules.md +2 -2
- package/src/hooks/significance-filter.ts +250 -250
- package/src/llm/memory-manager.ts +328 -328
- package/src/llm/provider.ts +885 -885
- package/src/llm/quality.ts +248 -248
- package/src/memory/attribution-guard.ts +249 -249
- package/src/memory/auto-relations.ts +107 -107
- package/src/memory/consolidation.ts +302 -302
- package/src/memory/disclosure-policy.ts +141 -141
- package/src/memory/entity-extractor.ts +197 -197
- package/src/memory/formation/evaluate.ts +217 -217
- package/src/memory/formation/extract.ts +361 -361
- package/src/memory/formation/index.ts +417 -417
- package/src/memory/formation/resolve.ts +344 -344
- package/src/memory/formation/types.ts +315 -315
- package/src/memory/freshness.ts +122 -122
- package/src/memory/graph.ts +197 -197
- package/src/memory/refs.ts +94 -94
- package/src/memory/retention.ts +433 -433
- package/src/memory/secret-filter.ts +79 -79
- package/src/memory/session.ts +523 -523
- package/src/multimodal/image-loader.ts +143 -143
- package/src/orchestrate/adapters/claude-stream.ts +192 -192
- package/src/orchestrate/adapters/claude.ts +111 -111
- package/src/orchestrate/adapters/codex-stream.ts +134 -134
- package/src/orchestrate/adapters/codex.ts +41 -41
- package/src/orchestrate/adapters/gemini-stream.ts +166 -166
- package/src/orchestrate/adapters/gemini.ts +42 -42
- package/src/orchestrate/adapters/index.ts +73 -73
- package/src/orchestrate/adapters/opencode-stream.ts +143 -143
- package/src/orchestrate/adapters/opencode.ts +47 -47
- package/src/orchestrate/adapters/spawn-helper.ts +286 -286
- package/src/orchestrate/adapters/types.ts +77 -77
- package/src/orchestrate/capability-router.ts +284 -284
- package/src/orchestrate/context-compact.ts +188 -188
- package/src/orchestrate/cost-tracker.ts +219 -219
- package/src/orchestrate/error-recovery.ts +191 -191
- package/src/orchestrate/evidence.ts +140 -140
- package/src/orchestrate/ledger.ts +110 -110
- package/src/orchestrate/memorix-bridge.ts +380 -380
- package/src/orchestrate/output-budget.ts +80 -80
- package/src/orchestrate/permission.ts +152 -152
- package/src/orchestrate/pipeline-trace.ts +131 -131
- package/src/orchestrate/prompt-builder.ts +155 -155
- package/src/orchestrate/ring-buffer.ts +37 -37
- package/src/orchestrate/task-graph.ts +389 -389
- package/src/orchestrate/verify-gate.ts +219 -219
- package/src/orchestrate/worktree.ts +232 -232
- package/src/project/aliases.ts +374 -374
- package/src/project/detector.ts +268 -268
- package/src/rules/adapters/claude-code.ts +99 -99
- package/src/rules/adapters/codex.ts +97 -97
- package/src/rules/adapters/copilot.ts +124 -124
- package/src/rules/adapters/cursor.ts +114 -114
- package/src/rules/adapters/kiro.ts +126 -126
- package/src/rules/adapters/trae.ts +56 -56
- package/src/rules/adapters/windsurf.ts +83 -83
- package/src/rules/syncer.ts +235 -235
- package/src/sdk.ts +327 -327
- package/src/search/intent-detector.ts +289 -289
- package/src/search/query-expansion.ts +52 -52
- package/src/server/formation-timeout.ts +27 -27
- package/src/skills/mini-skills.ts +386 -386
- package/src/store/chat-store.ts +119 -119
- package/src/store/file-lock.ts +100 -100
- package/src/store/graph-store.ts +249 -249
- package/src/store/mini-skill-store.ts +349 -349
- package/src/store/obs-store.ts +255 -255
- package/src/store/persistence-json.ts +212 -212
- package/src/store/persistence.ts +291 -291
- package/src/store/project-affinity.ts +195 -195
- package/src/store/session-store.ts +259 -259
- package/src/store/sqlite-store.ts +339 -339
- package/src/team/event-bus.ts +76 -76
- package/src/team/file-locks.ts +173 -173
- package/src/team/handoff.ts +167 -167
- package/src/team/messages.ts +203 -203
- package/src/team/poll.ts +132 -132
- package/src/team/tasks.ts +211 -211
- package/src/wiki/generator.ts +237 -237
- package/src/wiki/knowledge-graph.ts +334 -334
- package/src/wiki/types.ts +85 -85
- package/src/workspace/mcp-adapters/codex.ts +191 -191
- package/src/workspace/mcp-adapters/copilot.ts +105 -105
- package/src/workspace/mcp-adapters/cursor.ts +53 -53
- package/src/workspace/mcp-adapters/kiro.ts +64 -64
- package/src/workspace/mcp-adapters/opencode.ts +123 -123
- package/src/workspace/mcp-adapters/trae.ts +134 -134
- package/src/workspace/mcp-adapters/windsurf.ts +91 -91
- package/src/workspace/sanitizer.ts +60 -60
- package/src/workspace/workflow-sync.ts +131 -131
|
@@ -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
|
+
});
|
|
@@ -1,73 +1,73 @@
|
|
|
1
|
-
import { defineCommand } from 'citty';
|
|
2
|
-
import { readFileSync } from 'node:fs';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { analyzeImage } from '../../multimodal/image-loader.js';
|
|
5
|
-
import { storeObservation } from '../../memory/observations.js';
|
|
6
|
-
import { emitError, emitResult, getCliProjectContext } from './operator-shared.js';
|
|
7
|
-
|
|
8
|
-
function inferMimeType(filePath: string): string {
|
|
9
|
-
const ext = path.extname(filePath).toLowerCase();
|
|
10
|
-
switch (ext) {
|
|
11
|
-
case '.jpg':
|
|
12
|
-
case '.jpeg':
|
|
13
|
-
return 'image/jpeg';
|
|
14
|
-
case '.gif':
|
|
15
|
-
return 'image/gif';
|
|
16
|
-
case '.webp':
|
|
17
|
-
return 'image/webp';
|
|
18
|
-
default:
|
|
19
|
-
return 'image/png';
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export default defineCommand({
|
|
24
|
-
meta: {
|
|
25
|
-
name: 'image',
|
|
26
|
-
description: 'Analyze an image and store the result as memory',
|
|
27
|
-
},
|
|
28
|
-
args: {
|
|
29
|
-
path: { type: 'string', description: 'Path to the image file' },
|
|
30
|
-
prompt: { type: 'string', description: 'Custom analysis prompt' },
|
|
31
|
-
mimeType: { type: 'string', description: 'Explicit MIME type override' },
|
|
32
|
-
json: { type: 'boolean', description: 'Emit machine-readable JSON output' },
|
|
33
|
-
},
|
|
34
|
-
run: async ({ args }) => {
|
|
35
|
-
const asJson = !!args.json;
|
|
36
|
-
try {
|
|
37
|
-
const imagePath = (args.path as string | undefined)?.trim();
|
|
38
|
-
if (!imagePath) {
|
|
39
|
-
emitError('path is required for "memorix ingest image"', asJson);
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
const { project } = await getCliProjectContext();
|
|
43
|
-
const resolvedPath = path.resolve(process.cwd(), imagePath);
|
|
44
|
-
const base64 = readFileSync(resolvedPath).toString('base64');
|
|
45
|
-
const filename = path.basename(resolvedPath);
|
|
46
|
-
const analysis = await analyzeImage({
|
|
47
|
-
base64,
|
|
48
|
-
filename,
|
|
49
|
-
mimeType: (args.mimeType as string | undefined) || inferMimeType(resolvedPath),
|
|
50
|
-
prompt: args.prompt as string | undefined,
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
const result = await storeObservation({
|
|
54
|
-
entityName: filename.replace(/\.[^.]+$/, '') || `image-${Date.now()}`,
|
|
55
|
-
type: 'discovery',
|
|
56
|
-
title: `Image analysis: ${filename}`,
|
|
57
|
-
narrative: analysis.description,
|
|
58
|
-
concepts: analysis.tags,
|
|
59
|
-
facts: analysis.entities,
|
|
60
|
-
projectId: project.id,
|
|
61
|
-
source: 'manual',
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
emitResult(
|
|
65
|
-
{ project, analysis, observation: result.observation },
|
|
66
|
-
`Stored image analysis #${result.observation.id}: ${filename}`,
|
|
67
|
-
asJson,
|
|
68
|
-
);
|
|
69
|
-
} catch (error) {
|
|
70
|
-
emitError(error instanceof Error ? error.message : String(error), asJson);
|
|
71
|
-
}
|
|
72
|
-
},
|
|
73
|
-
});
|
|
1
|
+
import { defineCommand } from 'citty';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { analyzeImage } from '../../multimodal/image-loader.js';
|
|
5
|
+
import { storeObservation } from '../../memory/observations.js';
|
|
6
|
+
import { emitError, emitResult, getCliProjectContext } from './operator-shared.js';
|
|
7
|
+
|
|
8
|
+
function inferMimeType(filePath: string): string {
|
|
9
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
10
|
+
switch (ext) {
|
|
11
|
+
case '.jpg':
|
|
12
|
+
case '.jpeg':
|
|
13
|
+
return 'image/jpeg';
|
|
14
|
+
case '.gif':
|
|
15
|
+
return 'image/gif';
|
|
16
|
+
case '.webp':
|
|
17
|
+
return 'image/webp';
|
|
18
|
+
default:
|
|
19
|
+
return 'image/png';
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default defineCommand({
|
|
24
|
+
meta: {
|
|
25
|
+
name: 'image',
|
|
26
|
+
description: 'Analyze an image and store the result as memory',
|
|
27
|
+
},
|
|
28
|
+
args: {
|
|
29
|
+
path: { type: 'string', description: 'Path to the image file' },
|
|
30
|
+
prompt: { type: 'string', description: 'Custom analysis prompt' },
|
|
31
|
+
mimeType: { type: 'string', description: 'Explicit MIME type override' },
|
|
32
|
+
json: { type: 'boolean', description: 'Emit machine-readable JSON output' },
|
|
33
|
+
},
|
|
34
|
+
run: async ({ args }) => {
|
|
35
|
+
const asJson = !!args.json;
|
|
36
|
+
try {
|
|
37
|
+
const imagePath = (args.path as string | undefined)?.trim();
|
|
38
|
+
if (!imagePath) {
|
|
39
|
+
emitError('path is required for "memorix ingest image"', asJson);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const { project } = await getCliProjectContext();
|
|
43
|
+
const resolvedPath = path.resolve(process.cwd(), imagePath);
|
|
44
|
+
const base64 = readFileSync(resolvedPath).toString('base64');
|
|
45
|
+
const filename = path.basename(resolvedPath);
|
|
46
|
+
const analysis = await analyzeImage({
|
|
47
|
+
base64,
|
|
48
|
+
filename,
|
|
49
|
+
mimeType: (args.mimeType as string | undefined) || inferMimeType(resolvedPath),
|
|
50
|
+
prompt: args.prompt as string | undefined,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const result = await storeObservation({
|
|
54
|
+
entityName: filename.replace(/\.[^.]+$/, '') || `image-${Date.now()}`,
|
|
55
|
+
type: 'discovery',
|
|
56
|
+
title: `Image analysis: ${filename}`,
|
|
57
|
+
narrative: analysis.description,
|
|
58
|
+
concepts: analysis.tags,
|
|
59
|
+
facts: analysis.entities,
|
|
60
|
+
projectId: project.id,
|
|
61
|
+
source: 'manual',
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
emitResult(
|
|
65
|
+
{ project, analysis, observation: result.observation },
|
|
66
|
+
`Stored image analysis #${result.observation.id}: ${filename}`,
|
|
67
|
+
asJson,
|
|
68
|
+
);
|
|
69
|
+
} catch (error) {
|
|
70
|
+
emitError(error instanceof Error ? error.message : String(error), asJson);
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
});
|