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.
- package/CHANGELOG.md +30 -1
- package/README.md +18 -4
- package/README.zh-CN.md +18 -4
- package/TEAM.md +86 -86
- package/dist/cli/index.js +15919 -14055
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +1997 -1021
- package/dist/index.js.map +1 -1
- package/dist/maintenance-runner.d.ts +1 -1
- package/dist/maintenance-runner.js +8481 -8005
- package/dist/maintenance-runner.js.map +1 -1
- package/dist/memcode-runtime/CHANGELOG.md +30 -1
- package/dist/sdk.d.ts +7 -2
- package/dist/sdk.js +2022 -1024
- package/dist/sdk.js.map +1 -1
- package/dist/types.d.ts +49 -1
- package/dist/types.js.map +1 -1
- package/docs/1.2.2-MEMORY-CONTROL-PLANE.md +434 -0
- package/docs/AGENT_OPERATOR_PLAYBOOK.md +4 -0
- package/docs/API_REFERENCE.md +27 -5
- package/docs/DESIGN_DECISIONS.md +357 -357
- package/docs/DEVELOPMENT.md +4 -0
- package/docs/README.md +1 -1
- package/docs/SETUP.md +7 -1
- package/docs/dev-log/progress.txt +91 -11
- package/docs/knowledge/workflows/memorix-release.md +57 -0
- package/package.json +1 -1
- package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
- package/src/audit/index.ts +156 -156
- package/src/cli/command-guide.ts +192 -0
- package/src/cli/commands/audit-list.ts +89 -89
- package/src/cli/commands/audit.ts +9 -4
- package/src/cli/commands/background.ts +659 -659
- package/src/cli/commands/cleanup.ts +5 -1
- package/src/cli/commands/codegraph.ts +17 -8
- package/src/cli/commands/context.ts +3 -2
- package/src/cli/commands/doctor.ts +4 -2
- package/src/cli/commands/explain.ts +9 -3
- package/src/cli/commands/formation.ts +48 -48
- package/src/cli/commands/git-hook-install.ts +111 -111
- package/src/cli/commands/handoff.ts +75 -61
- package/src/cli/commands/hooks-status.ts +63 -63
- package/src/cli/commands/identity.ts +116 -0
- package/src/cli/commands/ingest-commit.ts +153 -153
- package/src/cli/commands/ingest-image.ts +71 -69
- 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/knowledge.ts +40 -0
- package/src/cli/commands/lock.ts +93 -92
- package/src/cli/commands/memory.ts +58 -21
- package/src/cli/commands/message.ts +123 -118
- package/src/cli/commands/operator-shared.ts +98 -3
- package/src/cli/commands/poll.ts +74 -64
- 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 +135 -121
- package/src/cli/commands/retention.ts +9 -4
- package/src/cli/commands/serve-http.ts +22 -43
- package/src/cli/commands/serve-shared.ts +118 -118
- package/src/cli/commands/session.ts +29 -3
- package/src/cli/commands/setup.ts +9 -3
- package/src/cli/commands/skills.ts +124 -119
- package/src/cli/commands/status.ts +4 -3
- package/src/cli/commands/task.ts +193 -184
- package/src/cli/commands/team.ts +14 -10
- package/src/cli/commands/transfer.ts +108 -55
- package/src/cli/commands/uninstall-project-artifacts.ts +85 -85
- package/src/cli/identity.ts +89 -0
- package/src/cli/index.ts +96 -19
- package/src/cli/invocation.ts +115 -0
- 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/Sidebar.tsx +179 -179
- package/src/cli/tui/chat-service.ts +41 -18
- package/src/cli/tui/data.ts +23 -44
- package/src/cli/tui/index.ts +41 -41
- package/src/cli/tui/markdown-render.tsx +371 -371
- package/src/cli/tui/operator-context.ts +60 -0
- package/src/cli/tui/use-mouse.ts +157 -157
- package/src/cli/tui/useNavigation.ts +56 -56
- package/src/cli/tui/views/MemoryView.tsx +10 -8
- 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/auto-context.ts +34 -17
- package/src/codegraph/context-pack.ts +1 -0
- package/src/codegraph/current-facts.ts +19 -1
- package/src/codegraph/project-context.ts +2 -0
- package/src/codegraph/task-lens.ts +49 -5
- package/src/compact/engine.ts +26 -10
- package/src/compact/index-format.ts +25 -2
- package/src/compact/token-budget.ts +74 -74
- package/src/dashboard/project-classification.ts +64 -64
- package/src/dashboard/server.ts +58 -52
- 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/hooks/admission.ts +117 -0
- package/src/hooks/handler.ts +98 -91
- package/src/hooks/pattern-detector.ts +173 -173
- package/src/hooks/significance-filter.ts +250 -250
- package/src/knowledge/claims.ts +51 -1
- package/src/knowledge/context-assembly.ts +97 -0
- package/src/knowledge/types.ts +1 -0
- package/src/knowledge/workflows.ts +34 -3
- package/src/knowledge/workset.ts +179 -10
- 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/admission.ts +57 -0
- package/src/memory/attribution-guard.ts +249 -249
- package/src/memory/auto-relations.ts +21 -0
- package/src/memory/consolidation.ts +13 -2
- package/src/memory/disclosure-policy.ts +140 -135
- package/src/memory/entity-extractor.ts +197 -197
- package/src/memory/export-import.ts +11 -3
- 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-context.ts +8 -2
- package/src/memory/graph-scope.ts +46 -0
- package/src/memory/graph.ts +197 -197
- package/src/memory/observations.ts +162 -4
- package/src/memory/quality-audit.ts +2 -0
- package/src/memory/refs.ts +94 -94
- package/src/memory/retention.ts +22 -2
- package/src/memory/secret-filter.ts +79 -79
- package/src/memory/session.ts +5 -2
- package/src/memory/visibility.ts +80 -0
- 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 +378 -340
- 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 +33 -10
- 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/runtime/control-plane-maintenance.ts +1 -0
- package/src/runtime/isolated-maintenance.ts +1 -0
- package/src/runtime/lifecycle.ts +18 -0
- package/src/runtime/maintenance-jobs.ts +1 -0
- package/src/runtime/maintenance-runner.ts +2 -0
- package/src/runtime/project-maintenance.ts +89 -0
- package/src/sdk.ts +334 -304
- 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/server.ts +334 -93
- package/src/skills/mini-skills.ts +386 -386
- package/src/store/chat-store.ts +119 -119
- package/src/store/graph-store.ts +249 -249
- package/src/store/mini-skill-store.ts +349 -349
- package/src/store/orama-store.ts +61 -6
- 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/sqlite-db.ts +23 -1
- package/src/store/sqlite-store.ts +12 -2
- package/src/team/event-bus.ts +76 -76
- package/src/team/file-locks.ts +173 -173
- package/src/team/handoff.ts +168 -161
- package/src/team/messages.ts +203 -203
- package/src/team/poll.ts +132 -132
- package/src/team/tasks.ts +211 -211
- package/src/types.ts +51 -0
- package/src/wiki/generator.ts +2 -0
- 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
|
@@ -19,6 +19,7 @@ import { detectProject } from '../../project/detector.js';
|
|
|
19
19
|
import { getProjectDataDir } from '../../store/persistence.js';
|
|
20
20
|
import type { ObservationStore } from '../../store/obs-store.js';
|
|
21
21
|
import { getObservationStore, initObservationStore } from '../../store/obs-store.js';
|
|
22
|
+
import { filterReadableObservations } from '../../memory/visibility.js';
|
|
22
23
|
|
|
23
24
|
/** Patterns that indicate auto-generated, low-value observations */
|
|
24
25
|
const LOW_QUALITY_PATTERNS = [
|
|
@@ -148,7 +149,10 @@ export default defineCommand({
|
|
|
148
149
|
const dataDir = await getProjectDataDir(projectId);
|
|
149
150
|
await initObservationStore(dataDir);
|
|
150
151
|
const store = getObservationStore();
|
|
151
|
-
const projectObs =
|
|
152
|
+
const projectObs = filterReadableObservations(
|
|
153
|
+
await store.loadByProject(projectId, { status: 'active' }),
|
|
154
|
+
{ projectId },
|
|
155
|
+
) as Array<{
|
|
152
156
|
id?: number;
|
|
153
157
|
type?: string;
|
|
154
158
|
title?: string;
|
|
@@ -3,12 +3,13 @@ import { CodeGraphStore } from '../../codegraph/store.js';
|
|
|
3
3
|
import { refreshProjectLite } from '../../codegraph/lite-provider.js';
|
|
4
4
|
import { assembleContextPackForTask, attachTaskWorkset, buildContextPackPrompt } from '../../codegraph/context-pack.js';
|
|
5
5
|
import { backfillMissingObservationCodeRefs } from '../../codegraph/binder.js';
|
|
6
|
-
import { collectCurrentProjectFacts } from '../../codegraph/current-facts.js';
|
|
6
|
+
import { collectCurrentProjectFacts, formatGitFact } from '../../codegraph/current-facts.js';
|
|
7
7
|
import { resolveTaskLens } from '../../codegraph/task-lens.js';
|
|
8
8
|
import { getExternalCodeGraphContext, inspectExternalCodeGraph } from '../../codegraph/external-provider.js';
|
|
9
9
|
import type { CodeGraphProviderQuality } from '../../codegraph/types.js';
|
|
10
10
|
import { getResolvedConfig } from '../../config/resolved-config.js';
|
|
11
11
|
import { getAllObservations } from '../../memory/observations.js';
|
|
12
|
+
import { filterReadableObservations } from '../../memory/visibility.js';
|
|
12
13
|
import { emitError, emitResult, getCliProjectContext, parsePositiveInt } from './operator-shared.js';
|
|
13
14
|
|
|
14
15
|
function formatSnapshotStatus(status: ReturnType<CodeGraphStore['status']>): string[] {
|
|
@@ -71,8 +72,7 @@ function compactFacts(project: { rootPath: string }): { facts: string[]; dirty:
|
|
|
71
72
|
facts.push('Latest changelog: ' + current.latestChangelog.version
|
|
72
73
|
+ (current.latestChangelog.date ? ' (' + current.latestChangelog.date + ')' : ''));
|
|
73
74
|
}
|
|
74
|
-
facts.push(
|
|
75
|
-
+ (current.git.dirty ? 'dirty worktree' : 'clean worktree'));
|
|
75
|
+
facts.push(formatGitFact(current.git));
|
|
76
76
|
return { facts, dirty: current.git.dirty };
|
|
77
77
|
}
|
|
78
78
|
|
|
@@ -93,7 +93,7 @@ export default defineCommand({
|
|
|
93
93
|
const asJson = !!args.json;
|
|
94
94
|
|
|
95
95
|
try {
|
|
96
|
-
const { project, dataDir } = await getCliProjectContext();
|
|
96
|
+
const { project, dataDir, reader } = await getCliProjectContext();
|
|
97
97
|
const store = new CodeGraphStore();
|
|
98
98
|
await store.init(dataDir);
|
|
99
99
|
const explicitAction = Boolean(positional[0] || (args.action as string | undefined));
|
|
@@ -126,13 +126,21 @@ export default defineCommand({
|
|
|
126
126
|
const activeObservations = getAllObservations()
|
|
127
127
|
.filter(obs => obs.projectId === project.id && (obs.status ?? 'active') === 'active');
|
|
128
128
|
const backfill = await backfillMissingObservationCodeRefs(store, activeObservations);
|
|
129
|
-
const {
|
|
129
|
+
const {
|
|
130
|
+
enqueueClaimRequalification,
|
|
131
|
+
enqueueObservationQualification,
|
|
132
|
+
} = await import('../../runtime/lifecycle.js');
|
|
130
133
|
enqueueClaimRequalification({
|
|
131
134
|
dataDir,
|
|
132
135
|
projectId: project.id,
|
|
133
136
|
source: 'manual-codegraph-refresh',
|
|
134
137
|
snapshotId: refresh.snapshot.id,
|
|
135
138
|
});
|
|
139
|
+
enqueueObservationQualification({
|
|
140
|
+
dataDir,
|
|
141
|
+
projectId: project.id,
|
|
142
|
+
source: 'manual-codegraph-refresh',
|
|
143
|
+
});
|
|
136
144
|
const status = store.status(project.id);
|
|
137
145
|
const providerQuality = await inspectExternalCodeGraph({
|
|
138
146
|
projectRoot: project.rootPath,
|
|
@@ -161,9 +169,10 @@ export default defineCommand({
|
|
|
161
169
|
return;
|
|
162
170
|
}
|
|
163
171
|
const limit = parsePositiveInt(args.limit as string | undefined, 20);
|
|
164
|
-
const observations =
|
|
165
|
-
.filter(obs => obs.projectId === project.id && (obs.status ?? 'active') === 'active')
|
|
166
|
-
|
|
172
|
+
const observations = filterReadableObservations(
|
|
173
|
+
getAllObservations().filter(obs => obs.projectId === project.id && (obs.status ?? 'active') === 'active'),
|
|
174
|
+
reader,
|
|
175
|
+
).reverse();
|
|
167
176
|
const basePack = assembleContextPackForTask({
|
|
168
177
|
store,
|
|
169
178
|
projectId: project.id,
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
type AutoContextRefreshMode,
|
|
7
7
|
} from '../../codegraph/auto-context.js';
|
|
8
8
|
import { getAllObservations } from '../../memory/observations.js';
|
|
9
|
+
import { filterReadableObservations } from '../../memory/visibility.js';
|
|
9
10
|
import { emitError, emitResult, getCliProjectContext } from './operator-shared.js';
|
|
10
11
|
|
|
11
12
|
function coerceRefreshMode(input?: string): AutoContextRefreshMode {
|
|
@@ -28,11 +29,11 @@ export default defineCommand({
|
|
|
28
29
|
const asJson = !!args.json;
|
|
29
30
|
|
|
30
31
|
try {
|
|
31
|
-
const { project, dataDir } = await getCliProjectContext();
|
|
32
|
+
const { project, dataDir, reader } = await getCliProjectContext();
|
|
32
33
|
const context = await buildAutoProjectContext({
|
|
33
34
|
project,
|
|
34
35
|
dataDir,
|
|
35
|
-
observations: getAllObservations(),
|
|
36
|
+
observations: filterReadableObservations(getAllObservations(), reader),
|
|
36
37
|
task: args.task as string | undefined,
|
|
37
38
|
refresh: coerceRefreshMode(args.refresh as string | undefined),
|
|
38
39
|
});
|
|
@@ -257,8 +257,10 @@ export default defineCommand({
|
|
|
257
257
|
await initObservationStore(dataDir);
|
|
258
258
|
const store = getObservationStore();
|
|
259
259
|
backendName = store.getBackendName();
|
|
260
|
-
const
|
|
261
|
-
projectObservations =
|
|
260
|
+
const { filterReadableObservations } = await import('../../memory/visibility.js');
|
|
261
|
+
projectObservations = projectId
|
|
262
|
+
? filterReadableObservations(await store.loadAll(), { projectId })
|
|
263
|
+
: [];
|
|
262
264
|
obsCount = projectObservations.length;
|
|
263
265
|
activeCount = projectObservations.filter((o: any) => (o.status ?? 'active') === 'active').length;
|
|
264
266
|
ranCount = projectObservations.filter((o: any) => /^Ran:\s/i.test(o.title ?? '')).length;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { defineCommand } from 'citty';
|
|
2
2
|
import { buildAutoProjectContext, type AutoContextRefreshMode } from '../../codegraph/auto-context.js';
|
|
3
3
|
import { formatProjectContextExplain } from '../../codegraph/project-context.js';
|
|
4
|
+
import { formatContextReceipt } from '../../knowledge/context-assembly.js';
|
|
4
5
|
import { getAllObservations } from '../../memory/observations.js';
|
|
6
|
+
import { filterReadableObservations } from '../../memory/visibility.js';
|
|
5
7
|
import { emitError, emitResult, getCliProjectContext } from './operator-shared.js';
|
|
6
8
|
|
|
7
9
|
function coerceRefreshMode(input?: string): AutoContextRefreshMode {
|
|
@@ -23,15 +25,19 @@ export default defineCommand({
|
|
|
23
25
|
const asJson = !!args.json;
|
|
24
26
|
|
|
25
27
|
try {
|
|
26
|
-
const { project, dataDir } = await getCliProjectContext();
|
|
28
|
+
const { project, dataDir, reader } = await getCliProjectContext();
|
|
27
29
|
const context = await buildAutoProjectContext({
|
|
28
30
|
project,
|
|
29
31
|
dataDir,
|
|
30
|
-
observations: getAllObservations(),
|
|
32
|
+
observations: filterReadableObservations(getAllObservations(), reader),
|
|
31
33
|
refresh: coerceRefreshMode(args.refresh as string | undefined),
|
|
32
34
|
});
|
|
33
35
|
|
|
34
|
-
emitResult(
|
|
36
|
+
emitResult(
|
|
37
|
+
{ project, explain: context.explain, receipt: context.workset.receipt, refresh: context.refresh },
|
|
38
|
+
formatProjectContextExplain(context.explain) + '\n\n' + formatContextReceipt(context.workset.receipt),
|
|
39
|
+
asJson,
|
|
40
|
+
);
|
|
35
41
|
} catch (error) {
|
|
36
42
|
emitError(error instanceof Error ? error.message : String(error), asJson);
|
|
37
43
|
}
|
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
import { defineCommand } from 'citty';
|
|
2
|
-
import { getBeforeAfterMetrics, getMetricsSummary } from '../../memory/formation/index.js';
|
|
3
|
-
import { emitError, emitResult } from './operator-shared.js';
|
|
4
|
-
|
|
5
|
-
export default defineCommand({
|
|
6
|
-
meta: {
|
|
7
|
-
name: 'formation',
|
|
8
|
-
description: 'Inspect Memory Formation Pipeline runtime metrics',
|
|
9
|
-
},
|
|
10
|
-
args: {
|
|
11
|
-
json: { type: 'boolean', description: 'Emit machine-readable JSON output' },
|
|
12
|
-
},
|
|
13
|
-
run: async ({ args }) => {
|
|
14
|
-
const action = (args._ as string[])?.[0] || '';
|
|
15
|
-
const asJson = !!args.json;
|
|
16
|
-
|
|
17
|
-
try {
|
|
18
|
-
switch (action) {
|
|
19
|
-
case 'metrics': {
|
|
20
|
-
const summary = getMetricsSummary();
|
|
21
|
-
const beforeAfter = getBeforeAfterMetrics();
|
|
22
|
-
emitResult(
|
|
23
|
-
{ summary, beforeAfter },
|
|
24
|
-
summary.total === 0
|
|
25
|
-
? 'Formation Pipeline: No metrics collected yet.'
|
|
26
|
-
: [
|
|
27
|
-
'Formation Pipeline Metrics',
|
|
28
|
-
`- Total observations processed: ${summary.total}`,
|
|
29
|
-
`- Average value score: ${summary.avgValueScore.toFixed(3)}`,
|
|
30
|
-
`- Average processing time: ${summary.avgDurationMs.toFixed(1)}ms`,
|
|
31
|
-
].join('\n'),
|
|
32
|
-
asJson,
|
|
33
|
-
);
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
default:
|
|
38
|
-
console.log('Memorix Formation Commands');
|
|
39
|
-
console.log('');
|
|
40
|
-
console.log('Usage:');
|
|
41
|
-
console.log(' memorix formation metrics');
|
|
42
|
-
}
|
|
43
|
-
} catch (error) {
|
|
44
|
-
emitError(error instanceof Error ? error.message : String(error), asJson);
|
|
45
|
-
}
|
|
46
|
-
},
|
|
47
|
-
});
|
|
48
|
-
|
|
1
|
+
import { defineCommand } from 'citty';
|
|
2
|
+
import { getBeforeAfterMetrics, getMetricsSummary } from '../../memory/formation/index.js';
|
|
3
|
+
import { emitError, emitResult } from './operator-shared.js';
|
|
4
|
+
|
|
5
|
+
export default defineCommand({
|
|
6
|
+
meta: {
|
|
7
|
+
name: 'formation',
|
|
8
|
+
description: 'Inspect Memory Formation Pipeline runtime metrics',
|
|
9
|
+
},
|
|
10
|
+
args: {
|
|
11
|
+
json: { type: 'boolean', description: 'Emit machine-readable JSON output' },
|
|
12
|
+
},
|
|
13
|
+
run: async ({ args }) => {
|
|
14
|
+
const action = (args._ as string[])?.[0] || '';
|
|
15
|
+
const asJson = !!args.json;
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
switch (action) {
|
|
19
|
+
case 'metrics': {
|
|
20
|
+
const summary = getMetricsSummary();
|
|
21
|
+
const beforeAfter = getBeforeAfterMetrics();
|
|
22
|
+
emitResult(
|
|
23
|
+
{ summary, beforeAfter },
|
|
24
|
+
summary.total === 0
|
|
25
|
+
? 'Formation Pipeline: No metrics collected yet.'
|
|
26
|
+
: [
|
|
27
|
+
'Formation Pipeline Metrics',
|
|
28
|
+
`- Total observations processed: ${summary.total}`,
|
|
29
|
+
`- Average value score: ${summary.avgValueScore.toFixed(3)}`,
|
|
30
|
+
`- Average processing time: ${summary.avgDurationMs.toFixed(1)}ms`,
|
|
31
|
+
].join('\n'),
|
|
32
|
+
asJson,
|
|
33
|
+
);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
default:
|
|
38
|
+
console.log('Memorix Formation Commands');
|
|
39
|
+
console.log('');
|
|
40
|
+
console.log('Usage:');
|
|
41
|
+
console.log(' memorix formation metrics');
|
|
42
|
+
}
|
|
43
|
+
} catch (error) {
|
|
44
|
+
emitError(error instanceof Error ? error.message : String(error), asJson);
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
|
|
@@ -1,111 +1,111 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CLI Command: memorix git-hook install
|
|
3
|
-
*
|
|
4
|
-
* Installs a git post-commit hook that automatically ingests commits as memories.
|
|
5
|
-
* This is the Git→Memory automation — every commit becomes a searchable memory.
|
|
6
|
-
*
|
|
7
|
-
* Cross-platform: generates both bash (Unix) and PowerShell (Windows) hooks.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { defineCommand } from 'citty';
|
|
11
|
-
import * as p from '@clack/prompts';
|
|
12
|
-
import { existsSync, readFileSync, writeFileSync, chmodSync } from 'node:fs';
|
|
13
|
-
import { ensureHooksDir } from '../../git/hooks-path.js';
|
|
14
|
-
|
|
15
|
-
const HOOK_MARKER = '# [memorix-git-hook]';
|
|
16
|
-
|
|
17
|
-
export default defineCommand({
|
|
18
|
-
meta: {
|
|
19
|
-
name: 'git-hook-install',
|
|
20
|
-
description: 'Install git post-commit hook for automatic memory capture',
|
|
21
|
-
},
|
|
22
|
-
args: {
|
|
23
|
-
cwd: {
|
|
24
|
-
type: 'string',
|
|
25
|
-
description: 'Project directory (default: current directory)',
|
|
26
|
-
required: false,
|
|
27
|
-
},
|
|
28
|
-
force: {
|
|
29
|
-
type: 'boolean',
|
|
30
|
-
description: 'Overwrite existing hook without asking',
|
|
31
|
-
required: false,
|
|
32
|
-
},
|
|
33
|
-
},
|
|
34
|
-
run: async ({ args }) => {
|
|
35
|
-
const projectDir = args.cwd || process.cwd();
|
|
36
|
-
|
|
37
|
-
p.intro('Install Git post-commit hook');
|
|
38
|
-
|
|
39
|
-
// 1. Resolve git hooks directory (handles normal repos and worktrees)
|
|
40
|
-
const resolved = ensureHooksDir(projectDir);
|
|
41
|
-
if (!resolved) {
|
|
42
|
-
p.log.error(`No .git found in ${projectDir} (checked both directory and worktree file)`);
|
|
43
|
-
p.outro('Run this command from a git repository root.');
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const { hookPath } = resolved;
|
|
48
|
-
|
|
49
|
-
// 3. Check for existing hook
|
|
50
|
-
if (existsSync(hookPath)) {
|
|
51
|
-
const existing = readFileSync(hookPath, 'utf-8');
|
|
52
|
-
|
|
53
|
-
if (existing.includes(HOOK_MARKER)) {
|
|
54
|
-
p.log.warn('Memorix git hook is already installed.');
|
|
55
|
-
p.outro('Use `memorix git-hook uninstall` to remove it first.');
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Existing non-memorix hook — ask to append
|
|
60
|
-
if (!args.force) {
|
|
61
|
-
const action = await p.select({
|
|
62
|
-
message: 'A post-commit hook already exists. What would you like to do?',
|
|
63
|
-
options: [
|
|
64
|
-
{ value: 'append', label: 'Append', hint: 'Add memorix hook after existing content' },
|
|
65
|
-
{ value: 'skip', label: 'Skip', hint: 'Do not modify the existing hook' },
|
|
66
|
-
],
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
if (p.isCancel(action) || action === 'skip') {
|
|
70
|
-
p.outro('Installation cancelled.');
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Append to existing hook
|
|
76
|
-
const appended = existing.trimEnd() + '\n\n' + generateHookScript();
|
|
77
|
-
writeFileSync(hookPath, appended, 'utf-8');
|
|
78
|
-
try { chmodSync(hookPath, 0o755); } catch { /* Windows doesn't need chmod */ }
|
|
79
|
-
|
|
80
|
-
p.log.success('Memorix hook appended to existing post-commit hook.');
|
|
81
|
-
} else {
|
|
82
|
-
// Create new hook
|
|
83
|
-
const script = '#!/bin/sh\n' + generateHookScript();
|
|
84
|
-
writeFileSync(hookPath, script, 'utf-8');
|
|
85
|
-
try { chmodSync(hookPath, 0o755); } catch { /* Windows doesn't need chmod */ }
|
|
86
|
-
|
|
87
|
-
p.log.success('Git post-commit hook installed.');
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
p.log.info(`Hook path: ${hookPath}`);
|
|
91
|
-
p.log.info('Every commit will now be automatically captured as a memory.');
|
|
92
|
-
p.log.info('Memories are tagged with source="git" and include the commit hash.');
|
|
93
|
-
p.outro('Done! Try making a commit to see it in action.');
|
|
94
|
-
},
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Generate the hook script content.
|
|
99
|
-
* Uses `memorix ingest commit --auto` for non-interactive ingest.
|
|
100
|
-
* Runs in background to not block the commit.
|
|
101
|
-
*/
|
|
102
|
-
function generateHookScript(): string {
|
|
103
|
-
return `${HOOK_MARKER}
|
|
104
|
-
# Memorix: Auto-ingest git commits as memories
|
|
105
|
-
# Runs in background — does not block your commit workflow.
|
|
106
|
-
# To remove: memorix git-hook uninstall
|
|
107
|
-
if command -v memorix >/dev/null 2>&1; then
|
|
108
|
-
memorix ingest commit --auto >/dev/null 2>&1 &
|
|
109
|
-
fi
|
|
110
|
-
`;
|
|
111
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* CLI Command: memorix git-hook install
|
|
3
|
+
*
|
|
4
|
+
* Installs a git post-commit hook that automatically ingests commits as memories.
|
|
5
|
+
* This is the Git→Memory automation — every commit becomes a searchable memory.
|
|
6
|
+
*
|
|
7
|
+
* Cross-platform: generates both bash (Unix) and PowerShell (Windows) hooks.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { defineCommand } from 'citty';
|
|
11
|
+
import * as p from '@clack/prompts';
|
|
12
|
+
import { existsSync, readFileSync, writeFileSync, chmodSync } from 'node:fs';
|
|
13
|
+
import { ensureHooksDir } from '../../git/hooks-path.js';
|
|
14
|
+
|
|
15
|
+
const HOOK_MARKER = '# [memorix-git-hook]';
|
|
16
|
+
|
|
17
|
+
export default defineCommand({
|
|
18
|
+
meta: {
|
|
19
|
+
name: 'git-hook-install',
|
|
20
|
+
description: 'Install git post-commit hook for automatic memory capture',
|
|
21
|
+
},
|
|
22
|
+
args: {
|
|
23
|
+
cwd: {
|
|
24
|
+
type: 'string',
|
|
25
|
+
description: 'Project directory (default: current directory)',
|
|
26
|
+
required: false,
|
|
27
|
+
},
|
|
28
|
+
force: {
|
|
29
|
+
type: 'boolean',
|
|
30
|
+
description: 'Overwrite existing hook without asking',
|
|
31
|
+
required: false,
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
run: async ({ args }) => {
|
|
35
|
+
const projectDir = args.cwd || process.cwd();
|
|
36
|
+
|
|
37
|
+
p.intro('Install Git post-commit hook');
|
|
38
|
+
|
|
39
|
+
// 1. Resolve git hooks directory (handles normal repos and worktrees)
|
|
40
|
+
const resolved = ensureHooksDir(projectDir);
|
|
41
|
+
if (!resolved) {
|
|
42
|
+
p.log.error(`No .git found in ${projectDir} (checked both directory and worktree file)`);
|
|
43
|
+
p.outro('Run this command from a git repository root.');
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const { hookPath } = resolved;
|
|
48
|
+
|
|
49
|
+
// 3. Check for existing hook
|
|
50
|
+
if (existsSync(hookPath)) {
|
|
51
|
+
const existing = readFileSync(hookPath, 'utf-8');
|
|
52
|
+
|
|
53
|
+
if (existing.includes(HOOK_MARKER)) {
|
|
54
|
+
p.log.warn('Memorix git hook is already installed.');
|
|
55
|
+
p.outro('Use `memorix git-hook uninstall` to remove it first.');
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Existing non-memorix hook — ask to append
|
|
60
|
+
if (!args.force) {
|
|
61
|
+
const action = await p.select({
|
|
62
|
+
message: 'A post-commit hook already exists. What would you like to do?',
|
|
63
|
+
options: [
|
|
64
|
+
{ value: 'append', label: 'Append', hint: 'Add memorix hook after existing content' },
|
|
65
|
+
{ value: 'skip', label: 'Skip', hint: 'Do not modify the existing hook' },
|
|
66
|
+
],
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
if (p.isCancel(action) || action === 'skip') {
|
|
70
|
+
p.outro('Installation cancelled.');
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Append to existing hook
|
|
76
|
+
const appended = existing.trimEnd() + '\n\n' + generateHookScript();
|
|
77
|
+
writeFileSync(hookPath, appended, 'utf-8');
|
|
78
|
+
try { chmodSync(hookPath, 0o755); } catch { /* Windows doesn't need chmod */ }
|
|
79
|
+
|
|
80
|
+
p.log.success('Memorix hook appended to existing post-commit hook.');
|
|
81
|
+
} else {
|
|
82
|
+
// Create new hook
|
|
83
|
+
const script = '#!/bin/sh\n' + generateHookScript();
|
|
84
|
+
writeFileSync(hookPath, script, 'utf-8');
|
|
85
|
+
try { chmodSync(hookPath, 0o755); } catch { /* Windows doesn't need chmod */ }
|
|
86
|
+
|
|
87
|
+
p.log.success('Git post-commit hook installed.');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
p.log.info(`Hook path: ${hookPath}`);
|
|
91
|
+
p.log.info('Every commit will now be automatically captured as a memory.');
|
|
92
|
+
p.log.info('Memories are tagged with source="git" and include the commit hash.');
|
|
93
|
+
p.outro('Done! Try making a commit to see it in action.');
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Generate the hook script content.
|
|
99
|
+
* Uses `memorix ingest commit --auto` for non-interactive ingest.
|
|
100
|
+
* Runs in background to not block the commit.
|
|
101
|
+
*/
|
|
102
|
+
function generateHookScript(): string {
|
|
103
|
+
return `${HOOK_MARKER}
|
|
104
|
+
# Memorix: Auto-ingest git commits as memories
|
|
105
|
+
# Runs in background — does not block your commit workflow.
|
|
106
|
+
# To remove: memorix git-hook uninstall
|
|
107
|
+
if command -v memorix >/dev/null 2>&1; then
|
|
108
|
+
memorix ingest commit --auto >/dev/null 2>&1 &
|
|
109
|
+
fi
|
|
110
|
+
`;
|
|
111
|
+
}
|
|
@@ -1,66 +1,80 @@
|
|
|
1
|
-
import { defineCommand } from 'citty';
|
|
2
|
-
import { storeObservation } from '../../memory/observations.js';
|
|
3
|
-
import { createHandoffArtifact } from '../../team/handoff.js';
|
|
4
|
-
import { emitError, emitResult, getCliProjectContext, parseCsvList, shortId } from './operator-shared.js';
|
|
5
|
-
|
|
6
|
-
export default defineCommand({
|
|
7
|
-
meta: {
|
|
8
|
-
name: 'handoff',
|
|
9
|
-
description: 'Create structured handoff artifacts between agents',
|
|
10
|
-
},
|
|
11
|
-
args: {
|
|
12
|
-
fromAgentId: { type: 'string', description: 'Sender agent ID' },
|
|
13
|
-
toAgentId: { type: 'string', description: 'Optional recipient agent ID' },
|
|
14
|
-
taskId: { type: 'string', description: 'Optional related task ID' },
|
|
15
|
-
summary: { type: 'string', description: 'Human-readable summary of the handoff' },
|
|
16
|
-
context: { type: 'string', description: 'Detailed machine-readable context for the next agent' },
|
|
17
|
-
filesModified: { type: 'string', description: 'Comma-separated file list' },
|
|
18
|
-
concepts: { type: 'string', description: 'Comma-separated discoverability concepts' },
|
|
19
|
-
json: { type: 'boolean', description: 'Emit machine-readable JSON output' },
|
|
20
|
-
},
|
|
21
|
-
run: async ({ args }) => {
|
|
22
|
-
const action = (args._ as string[])?.[0] || '';
|
|
23
|
-
const asJson = !!args.json;
|
|
24
|
-
|
|
25
|
-
try {
|
|
26
|
-
const { project, teamStore } = await getCliProjectContext();
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
1
|
+
import { defineCommand } from 'citty';
|
|
2
|
+
import { storeObservation } from '../../memory/observations.js';
|
|
3
|
+
import { createHandoffArtifact } from '../../team/handoff.js';
|
|
4
|
+
import { emitError, emitResult, getCliProjectContext, parseCsvList, resolveCliActorId, shortId } from './operator-shared.js';
|
|
5
|
+
|
|
6
|
+
export default defineCommand({
|
|
7
|
+
meta: {
|
|
8
|
+
name: 'handoff',
|
|
9
|
+
description: 'Create structured handoff artifacts between agents',
|
|
10
|
+
},
|
|
11
|
+
args: {
|
|
12
|
+
fromAgentId: { type: 'string', description: 'Sender agent ID' },
|
|
13
|
+
toAgentId: { type: 'string', description: 'Optional recipient agent ID' },
|
|
14
|
+
taskId: { type: 'string', description: 'Optional related task ID' },
|
|
15
|
+
summary: { type: 'string', description: 'Human-readable summary of the handoff' },
|
|
16
|
+
context: { type: 'string', description: 'Detailed machine-readable context for the next agent' },
|
|
17
|
+
filesModified: { type: 'string', description: 'Comma-separated file list' },
|
|
18
|
+
concepts: { type: 'string', description: 'Comma-separated discoverability concepts' },
|
|
19
|
+
json: { type: 'boolean', description: 'Emit machine-readable JSON output' },
|
|
20
|
+
},
|
|
21
|
+
run: async ({ args }) => {
|
|
22
|
+
const action = (args._ as string[])?.[0] || '';
|
|
23
|
+
const asJson = !!args.json;
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
const { project, teamStore, identity } = await getCliProjectContext();
|
|
27
|
+
const fromAgentId = resolveCliActorId(args.fromAgentId, identity, 'fromAgentId');
|
|
28
|
+
|
|
29
|
+
switch (action) {
|
|
30
|
+
case 'send': {
|
|
31
|
+
if (!fromAgentId || !args.summary || !args.context) {
|
|
32
|
+
emitError('summary, context, and an active CLI identity or fromAgentId are required for "memorix handoff send"', asJson);
|
|
32
33
|
return;
|
|
33
34
|
}
|
|
35
|
+
const sender = teamStore.getAgent(fromAgentId);
|
|
36
|
+
if (!sender || sender.project_id !== project.id || sender.status !== 'active') {
|
|
37
|
+
emitError('The handoff sender must be an active coordination member of this project.', asJson);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const toAgentId = (args.toAgentId as string | undefined)?.trim();
|
|
41
|
+
if (toAgentId) {
|
|
42
|
+
const recipient = teamStore.getAgent(toAgentId);
|
|
43
|
+
if (!recipient || recipient.project_id !== project.id || recipient.status !== 'active') {
|
|
44
|
+
emitError('The handoff recipient must be an active coordination member of this project.', asJson);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
34
48
|
const result = await createHandoffArtifact(
|
|
35
49
|
{
|
|
36
50
|
projectId: project.id,
|
|
37
|
-
fromAgentId
|
|
38
|
-
toAgentId
|
|
39
|
-
taskId: args.taskId as string | undefined,
|
|
40
|
-
summary: args.summary as string,
|
|
41
|
-
context: args.context as string,
|
|
42
|
-
filesModified: parseCsvList(args.filesModified as string | undefined),
|
|
43
|
-
concepts: parseCsvList(args.concepts as string | undefined),
|
|
44
|
-
},
|
|
45
|
-
storeObservation,
|
|
46
|
-
teamStore,
|
|
47
|
-
);
|
|
48
|
-
emitResult(
|
|
49
|
-
{ project, handoff: result },
|
|
50
|
-
`Handoff created: observation #${result.observationId} from ${shortId(result.fromAgentId)}${result.toAgentId ? ` to ${shortId(result.toAgentId)}` : ' (broadcast)'}`,
|
|
51
|
-
asJson,
|
|
52
|
-
);
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
default:
|
|
57
|
-
console.log('Memorix Handoff Commands');
|
|
58
|
-
console.log('');
|
|
59
|
-
console.log('Usage:');
|
|
60
|
-
console.log(' memorix handoff send --
|
|
61
|
-
}
|
|
62
|
-
} catch (error) {
|
|
63
|
-
emitError(error instanceof Error ? error.message : String(error), asJson);
|
|
64
|
-
}
|
|
65
|
-
},
|
|
66
|
-
});
|
|
51
|
+
fromAgentId,
|
|
52
|
+
toAgentId,
|
|
53
|
+
taskId: args.taskId as string | undefined,
|
|
54
|
+
summary: args.summary as string,
|
|
55
|
+
context: args.context as string,
|
|
56
|
+
filesModified: parseCsvList(args.filesModified as string | undefined),
|
|
57
|
+
concepts: parseCsvList(args.concepts as string | undefined),
|
|
58
|
+
},
|
|
59
|
+
storeObservation,
|
|
60
|
+
teamStore,
|
|
61
|
+
);
|
|
62
|
+
emitResult(
|
|
63
|
+
{ project, handoff: result },
|
|
64
|
+
`Handoff created: observation #${result.observationId} from ${shortId(result.fromAgentId)}${result.toAgentId ? ` to ${shortId(result.toAgentId)}` : ' (broadcast)'}`,
|
|
65
|
+
asJson,
|
|
66
|
+
);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
default:
|
|
71
|
+
console.log('Memorix Handoff Commands');
|
|
72
|
+
console.log('');
|
|
73
|
+
console.log('Usage:');
|
|
74
|
+
console.log(' memorix handoff send --summary "..." --context "..." [--fromAgentId <id>] [--toAgentId <id>] [--taskId <id>]');
|
|
75
|
+
}
|
|
76
|
+
} catch (error) {
|
|
77
|
+
emitError(error instanceof Error ? error.message : String(error), asJson);
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
});
|