memorix 1.1.13 → 1.2.1
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 +33 -1
- package/README.md +6 -3
- package/README.zh-CN.md +6 -3
- package/dist/cli/index.js +36639 -31279
- package/dist/cli/index.js.map +1 -1
- package/dist/dashboard/static/app.js +50 -30
- package/dist/index.js +6636 -1778
- package/dist/index.js.map +1 -1
- package/dist/maintenance-runner.d.ts +1 -1
- package/dist/maintenance-runner.js +3775 -302
- package/dist/maintenance-runner.js.map +1 -1
- package/dist/memcode-runtime/CHANGELOG.md +33 -1
- package/dist/sdk.d.ts +1 -1
- package/dist/sdk.js +6634 -1776
- package/dist/sdk.js.map +1 -1
- package/docs/1.2.0-CLAIM-LEDGER.md +72 -0
- package/docs/1.2.0-CODE-STATE.md +61 -0
- package/docs/1.2.0-DEVELOPMENT-CHARTER.md +255 -0
- package/docs/1.2.0-DYNAMIC-LIFECYCLE.md +71 -0
- package/docs/1.2.0-EVALUATION-HARNESS.md +51 -0
- package/docs/1.2.0-IMPLEMENTATION-PLAN.md +554 -0
- package/docs/1.2.0-KNOWLEDGE-WORKFLOW-RESEARCH.md +205 -0
- package/docs/1.2.0-KNOWLEDGE-WORKSPACE.md +68 -0
- package/docs/1.2.0-PRODUCT-STORY.md +234 -0
- package/docs/1.2.0-PROVIDER-QUALITY.md +189 -0
- package/docs/1.2.0-WORKFLOW-INHERITANCE.md +101 -0
- package/docs/1.2.0-WORKSET-RETRIEVAL.md +80 -0
- package/docs/AGENT_OPERATOR_PLAYBOOK.md +6 -3
- package/docs/API_REFERENCE.md +27 -6
- package/docs/CONFIGURATION.md +21 -3
- package/docs/DEVELOPMENT.md +4 -0
- package/docs/README.md +17 -2
- package/docs/SETUP.md +7 -1
- package/docs/dev-log/progress.txt +120 -40
- package/docs/knowledge/workflows/memorix-release.md +57 -0
- package/llms-full.txt +16 -2
- package/llms.txt +9 -4
- package/package.json +3 -2
- package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
- package/src/cli/capability-map.ts +1 -0
- package/src/cli/commands/codegraph.ts +111 -9
- package/src/cli/commands/context.ts +2 -0
- package/src/cli/commands/doctor.ts +73 -4
- package/src/cli/commands/knowledge.ts +322 -0
- package/src/cli/commands/serve-http.ts +26 -42
- package/src/cli/commands/setup.ts +9 -3
- package/src/cli/index.ts +3 -1
- package/src/cli/tui/App.tsx +1 -1
- package/src/cli/tui/Panels.tsx +8 -8
- package/src/cli/tui/theme.ts +1 -1
- package/src/cli/tui/views/GraphView.tsx +8 -7
- package/src/cli/tui/views/KnowledgeView.tsx +9 -9
- package/src/codegraph/auto-context.ts +169 -19
- package/src/codegraph/code-state.ts +95 -0
- package/src/codegraph/context-pack.ts +82 -1
- package/src/codegraph/current-facts.ts +19 -1
- package/src/codegraph/external-provider.ts +581 -0
- package/src/codegraph/lite-provider.ts +64 -19
- package/src/codegraph/project-context.ts +9 -1
- package/src/codegraph/store.ts +154 -6
- package/src/codegraph/task-lens.ts +49 -5
- package/src/codegraph/types.ts +117 -0
- package/src/config/resolved-config.ts +28 -0
- package/src/config/toml-loader.ts +3 -0
- package/src/config/yaml-loader.ts +6 -0
- package/src/dashboard/server.ts +30 -47
- package/src/evaluation/workset-evaluation.ts +120 -0
- package/src/hooks/handler.ts +48 -6
- package/src/knowledge/claim-store.ts +267 -0
- package/src/knowledge/claims.ts +587 -0
- package/src/knowledge/markdown.ts +129 -0
- package/src/knowledge/types.ts +158 -0
- package/src/knowledge/wiki.ts +524 -0
- package/src/knowledge/workflow-store.ts +168 -0
- package/src/knowledge/workflow-types.ts +95 -0
- package/src/knowledge/workflows.ts +774 -0
- package/src/knowledge/workset.ts +515 -0
- package/src/knowledge/workspace-store.ts +220 -0
- package/src/knowledge/workspace-types.ts +106 -0
- package/src/knowledge/workspace.ts +220 -0
- package/src/memory/auto-relations.ts +21 -0
- package/src/memory/graph-scope.ts +46 -0
- package/src/memory/observations.ts +19 -0
- package/src/orchestrate/verify-gate.ts +33 -10
- package/src/runtime/control-plane-maintenance.ts +5 -0
- package/src/runtime/isolated-maintenance.ts +5 -0
- package/src/runtime/lifecycle-status.ts +102 -0
- package/src/runtime/lifecycle.ts +107 -0
- package/src/runtime/maintenance-jobs.ts +5 -0
- package/src/runtime/project-maintenance.ts +190 -0
- package/src/server/tool-profile.ts +3 -2
- package/src/server.ts +424 -22
- package/src/store/file-lock.ts +24 -4
- package/src/store/sqlite-db.ts +307 -0
- package/src/wiki/generator.ts +4 -2
- package/src/wiki/knowledge-graph.ts +7 -4
- package/src/wiki/types.ts +16 -4
|
@@ -319,9 +319,11 @@ export default defineCommand({
|
|
|
319
319
|
const { CodeGraphStore } = await import('../../codegraph/store.js');
|
|
320
320
|
const { buildProjectContextOverview } = await import('../../codegraph/project-context.js');
|
|
321
321
|
const { getResolvedConfig } = await import('../../config/resolved-config.js');
|
|
322
|
+
const { inspectExternalCodeGraph } = await import('../../codegraph/external-provider.js');
|
|
322
323
|
const codeStore = new CodeGraphStore();
|
|
323
324
|
await codeStore.init(dataDir);
|
|
324
|
-
const
|
|
325
|
+
const codegraphConfig = getResolvedConfig({ projectRoot: projectRoot || process.cwd() }).codegraph;
|
|
326
|
+
const exclude = codegraphConfig.excludePatterns;
|
|
325
327
|
const overview = buildProjectContextOverview({
|
|
326
328
|
project: {
|
|
327
329
|
id: projectId,
|
|
@@ -335,6 +337,12 @@ export default defineCommand({
|
|
|
335
337
|
const languageText = overview.code.languages.length > 0
|
|
336
338
|
? overview.code.languages.map((item: any) => `${item.language} ${item.files}`).join(', ')
|
|
337
339
|
: 'none indexed yet';
|
|
340
|
+
const providerQuality = await inspectExternalCodeGraph({
|
|
341
|
+
projectRoot: projectRoot || process.cwd(),
|
|
342
|
+
mode: codegraphConfig.externalContext,
|
|
343
|
+
command: codegraphConfig.externalCommand,
|
|
344
|
+
timeoutMs: codegraphConfig.externalTimeoutMs,
|
|
345
|
+
});
|
|
338
346
|
|
|
339
347
|
if (overview.code.files > 0) {
|
|
340
348
|
lines.push(ok(`Code memory: ${overview.code.files} files, ${overview.code.symbols} symbols, ${overview.code.refs} memory links`));
|
|
@@ -344,6 +352,15 @@ export default defineCommand({
|
|
|
344
352
|
tips.push('Run `memorix codegraph refresh` once to seed project context.');
|
|
345
353
|
}
|
|
346
354
|
|
|
355
|
+
lines.push(info(
|
|
356
|
+
`Code provider: Lite durable index; external semantic graph ${providerQuality.health.state}`,
|
|
357
|
+
));
|
|
358
|
+
if (providerQuality.health.state !== 'ready'
|
|
359
|
+
&& providerQuality.health.state !== 'not-detected'
|
|
360
|
+
&& providerQuality.health.state !== 'disabled') {
|
|
361
|
+
lines.push(warn(`External CodeGraph: ${providerQuality.health.reason ?? providerQuality.health.state}`));
|
|
362
|
+
}
|
|
363
|
+
|
|
347
364
|
if (overview.freshness.stale > 0 || overview.freshness.suspect > 0) {
|
|
348
365
|
lines.push(warn(`Freshness: ${overview.freshness.current} current, ${overview.freshness.suspect} suspect, ${overview.freshness.stale} stale`));
|
|
349
366
|
} else {
|
|
@@ -362,6 +379,7 @@ export default defineCommand({
|
|
|
362
379
|
refs: overview.code.refs,
|
|
363
380
|
languages: overview.code.languages,
|
|
364
381
|
freshness: overview.freshness,
|
|
382
|
+
providerQuality: providerQuality.quality,
|
|
365
383
|
...(overview.code.indexedAt ? { indexedAt: overview.code.indexedAt } : {}),
|
|
366
384
|
};
|
|
367
385
|
} catch (e) {
|
|
@@ -373,7 +391,58 @@ export default defineCommand({
|
|
|
373
391
|
lines.push(info('Project context unavailable without a detected project.'));
|
|
374
392
|
}
|
|
375
393
|
|
|
376
|
-
// ── 6.
|
|
394
|
+
// ── 6. Knowledge Lifecycle ──────────────────────────────────
|
|
395
|
+
lines.push('');
|
|
396
|
+
lines.push('┌─ Knowledge Lifecycle ─────────────────────────────');
|
|
397
|
+
|
|
398
|
+
if (projectId && dataDir) {
|
|
399
|
+
try {
|
|
400
|
+
const { collectLifecycleDiagnostics } = await import('../../runtime/lifecycle-status.js');
|
|
401
|
+
const lifecycle = await collectLifecycleDiagnostics({ dataDir, projectId });
|
|
402
|
+
report.lifecycle = lifecycle;
|
|
403
|
+
|
|
404
|
+
const queued = lifecycle.maintenance.summary.pending + lifecycle.maintenance.summary.retrying;
|
|
405
|
+
const running = lifecycle.maintenance.summary.running;
|
|
406
|
+
if (lifecycle.maintenance.summary.failed > 0) {
|
|
407
|
+
lines.push(warn(`Maintenance: ${lifecycle.maintenance.summary.failed} failed job(s)`));
|
|
408
|
+
for (const job of lifecycle.maintenance.failedJobs) {
|
|
409
|
+
lines.push(info(`Failed: ${job.kind} after ${job.attempts} attempt(s)`));
|
|
410
|
+
}
|
|
411
|
+
issues.push('Knowledge maintenance has failed work. Run `memorix doctor --json` to inspect the affected job kind.');
|
|
412
|
+
} else if (queued > 0 || running > 0) {
|
|
413
|
+
lines.push(info(`Maintenance: ${queued} queued, ${running} running`));
|
|
414
|
+
} else {
|
|
415
|
+
lines.push(ok('Maintenance: idle'));
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
lines.push(info(
|
|
419
|
+
`Claims: ${lifecycle.claims.active} active, ${lifecycle.claims.needsReview} need review, ${lifecycle.claims.unknown} unknown`,
|
|
420
|
+
));
|
|
421
|
+
if (lifecycle.claims.conflicts > 0) {
|
|
422
|
+
lines.push(warn(`Claim conflicts: ${lifecycle.claims.conflicts}`));
|
|
423
|
+
issues.push('Source-backed claims conflict and need review before they guide work.');
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
if (lifecycle.workspaces.length === 0) {
|
|
427
|
+
lines.push(info('Knowledge workspace: not initialized'));
|
|
428
|
+
} else {
|
|
429
|
+
for (const workspace of lifecycle.workspaces) {
|
|
430
|
+
lines.push(info(
|
|
431
|
+
`${workspace.mode} workspace: ${workspace.pages} pages, ${workspace.pendingProposals} pending proposal(s), ${workspace.status}`,
|
|
432
|
+
));
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
lines.push(info(`Workflows: ${lifecycle.workflows.active}/${lifecycle.workflows.total} active, ${lifecycle.workflows.failedRuns} failed run(s)`));
|
|
436
|
+
} catch (e) {
|
|
437
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
438
|
+
lines.push(warn(`Knowledge lifecycle unavailable: ${message}`));
|
|
439
|
+
report.lifecycle = { error: message };
|
|
440
|
+
}
|
|
441
|
+
} else {
|
|
442
|
+
lines.push(info('Knowledge lifecycle unavailable without a detected project.'));
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// ── 7. Conflict Check ────────────────────────────────────────
|
|
377
446
|
lines.push('');
|
|
378
447
|
lines.push('┌─ Conflict Check ──────────────────────────────────');
|
|
379
448
|
|
|
@@ -403,7 +472,7 @@ export default defineCommand({
|
|
|
403
472
|
}
|
|
404
473
|
report.conflicts = { found: conflictsFound };
|
|
405
474
|
|
|
406
|
-
// ──
|
|
475
|
+
// ── 8. LLM Status ────────────────────────────────────────────
|
|
407
476
|
lines.push('');
|
|
408
477
|
lines.push('┌─ LLM Enhanced Mode ───────────────────────────────');
|
|
409
478
|
|
|
@@ -427,7 +496,7 @@ export default defineCommand({
|
|
|
427
496
|
report.llm = { enabled: false };
|
|
428
497
|
}
|
|
429
498
|
|
|
430
|
-
// ──
|
|
499
|
+
// ── 9. Auto-Update Status ──────────────────────────────────
|
|
431
500
|
lines.push('');
|
|
432
501
|
lines.push('┌─ Auto-Update ─────────────────────────────────────');
|
|
433
502
|
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import { defineCommand } from 'citty';
|
|
2
|
+
import { CodeGraphStore } from '../../codegraph/store.js';
|
|
3
|
+
import { ClaimStore } from '../../knowledge/claim-store.js';
|
|
4
|
+
import { reviewClaim } from '../../knowledge/claims.js';
|
|
5
|
+
import { applyKnowledgeProposal, compileKnowledgeWorkspace, lintKnowledgeWorkspace } from '../../knowledge/wiki.js';
|
|
6
|
+
import { initializeKnowledgeWorkspace, loadKnowledgeWorkspace } from '../../knowledge/workspace.js';
|
|
7
|
+
import { KnowledgeWorkspaceStore } from '../../knowledge/workspace-store.js';
|
|
8
|
+
import { WorkflowStore } from '../../knowledge/workflow-store.js';
|
|
9
|
+
import {
|
|
10
|
+
applyWorkflowAdapter,
|
|
11
|
+
importWindsurfWorkflows,
|
|
12
|
+
previewWorkflowAdapter,
|
|
13
|
+
recordWorkflowRun,
|
|
14
|
+
selectWorkspaceWorkflows,
|
|
15
|
+
syncCanonicalWorkflows,
|
|
16
|
+
} from '../../knowledge/workflows.js';
|
|
17
|
+
import { emitError, emitResult, getCliProjectContext } from './operator-shared.js';
|
|
18
|
+
|
|
19
|
+
function modeFrom(value: unknown): 'local' | 'versioned' {
|
|
20
|
+
if (!value || value === 'local') return 'local';
|
|
21
|
+
if (value === 'versioned') return 'versioned';
|
|
22
|
+
throw new Error('knowledge mode must be local or versioned');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function usage(): string {
|
|
26
|
+
return [
|
|
27
|
+
'Memorix Knowledge Commands',
|
|
28
|
+
'',
|
|
29
|
+
' memorix knowledge init [--mode local]',
|
|
30
|
+
' memorix knowledge init --mode versioned --path C:\\project\\docs\\knowledge',
|
|
31
|
+
' memorix knowledge status',
|
|
32
|
+
' memorix knowledge claims',
|
|
33
|
+
' memorix knowledge review --id <claim-id> --review approved --detail "checked current source evidence"',
|
|
34
|
+
' memorix knowledge compile',
|
|
35
|
+
' memorix knowledge lint',
|
|
36
|
+
' memorix knowledge apply --proposal <id> [--force]',
|
|
37
|
+
' memorix knowledge workflow import',
|
|
38
|
+
' memorix knowledge workflow list',
|
|
39
|
+
' memorix knowledge workflow select --task "prepare a release"',
|
|
40
|
+
' memorix knowledge workflow preview --id <workflow-id> --agent codex',
|
|
41
|
+
' memorix knowledge workflow apply --id <workflow-id> --agent codex',
|
|
42
|
+
' memorix knowledge workflow run --id <workflow-id> --task "..." --outcome passed',
|
|
43
|
+
].join('\n');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function requiredText(value: unknown, field: string): string {
|
|
47
|
+
const text = typeof value === 'string' ? value.trim() : '';
|
|
48
|
+
if (!text) throw new Error(field + ' is required');
|
|
49
|
+
return text;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function workflowOutcome(value: unknown): 'passed' | 'failed' | 'cancelled' | 'in-progress' {
|
|
53
|
+
if (value === 'passed' || value === 'failed' || value === 'cancelled' || value === 'in-progress') return value;
|
|
54
|
+
throw new Error('workflow outcome must be passed, failed, cancelled, or in-progress');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function workflowVerdict(value: unknown): 'passed' | 'failed' | 'not-run' | undefined {
|
|
58
|
+
if (value === undefined || value === null || value === '') return undefined;
|
|
59
|
+
if (value === 'passed' || value === 'failed' || value === 'not-run') return value;
|
|
60
|
+
throw new Error('workflow verdict must be passed, failed, or not-run');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function claimReviewState(value: unknown): 'approved' | 'rejected' {
|
|
64
|
+
if (value === 'approved' || value === 'rejected') return value;
|
|
65
|
+
throw new Error('claim review must be approved or rejected');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function evidenceList(value: unknown): string[] {
|
|
69
|
+
if (typeof value !== 'string' || !value.trim()) return [];
|
|
70
|
+
return [...new Set(value.split(',').map(item => item.trim()).filter(Boolean))];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export default defineCommand({
|
|
74
|
+
meta: {
|
|
75
|
+
name: 'knowledge',
|
|
76
|
+
description: 'Initialize, review, and lint the Memorix Knowledge Workspace',
|
|
77
|
+
},
|
|
78
|
+
args: {
|
|
79
|
+
action: { type: 'string', description: 'Action: init, status, compile, lint, or apply' },
|
|
80
|
+
mode: { type: 'string', description: 'Workspace mode: local or versioned' },
|
|
81
|
+
path: { type: 'string', description: 'Explicit versioned workspace path for init' },
|
|
82
|
+
proposal: { type: 'string', description: 'Proposal id for apply' },
|
|
83
|
+
force: { type: 'boolean', description: 'Explicitly allow an approved proposal to replace manual page edits' },
|
|
84
|
+
id: { type: 'string', description: 'Canonical workflow id' },
|
|
85
|
+
agent: { type: 'string', description: 'Target agent for a workflow adapter' },
|
|
86
|
+
task: { type: 'string', description: 'Task wording for workflow selection or a workflow run' },
|
|
87
|
+
outcome: { type: 'string', description: 'Workflow run outcome: passed, failed, cancelled, or in-progress' },
|
|
88
|
+
verdict: { type: 'string', description: 'Workflow verification verdict: passed, failed, or not-run' },
|
|
89
|
+
failure: { type: 'string', description: 'Sanitized failure reason for a workflow run' },
|
|
90
|
+
snapshot: { type: 'string', description: 'Starting code snapshot id for a workflow run' },
|
|
91
|
+
evidence: { type: 'string', description: 'Comma-separated evidence ids selected for a workflow run' },
|
|
92
|
+
review: { type: 'string', description: 'Claim review verdict: approved or rejected' },
|
|
93
|
+
detail: { type: 'string', description: 'Evidence check performed for a claim review' },
|
|
94
|
+
json: { type: 'boolean', description: 'Emit machine-readable JSON output' },
|
|
95
|
+
},
|
|
96
|
+
run: async ({ args }) => {
|
|
97
|
+
const positional = (args._ as string[]) ?? [];
|
|
98
|
+
const action = (positional[0] || (args.action as string | undefined) || 'status').toLowerCase();
|
|
99
|
+
const asJson = !!args.json;
|
|
100
|
+
try {
|
|
101
|
+
const { project, dataDir } = await getCliProjectContext();
|
|
102
|
+
const mode = modeFrom(args.mode);
|
|
103
|
+
if (action === 'init') {
|
|
104
|
+
const workspace = await initializeKnowledgeWorkspace({
|
|
105
|
+
projectId: project.id,
|
|
106
|
+
dataDir,
|
|
107
|
+
mode,
|
|
108
|
+
...(mode === 'versioned'
|
|
109
|
+
? {
|
|
110
|
+
projectRoot: project.rootPath,
|
|
111
|
+
rootPath: (args.path as string | undefined) ?? '',
|
|
112
|
+
}
|
|
113
|
+
: {}),
|
|
114
|
+
});
|
|
115
|
+
emitResult(
|
|
116
|
+
{ project, workspace },
|
|
117
|
+
'Knowledge workspace initialized at ' + workspace.rootPath,
|
|
118
|
+
asJson,
|
|
119
|
+
);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const workspace = await loadKnowledgeWorkspace({ projectId: project.id, dataDir, mode });
|
|
124
|
+
if (!workspace) {
|
|
125
|
+
emitError('Knowledge workspace is not initialized. Run "memorix knowledge init" first.', asJson);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const claimStore = new ClaimStore();
|
|
129
|
+
await claimStore.init(dataDir);
|
|
130
|
+
const workspaceStore = new KnowledgeWorkspaceStore();
|
|
131
|
+
await workspaceStore.init(dataDir);
|
|
132
|
+
|
|
133
|
+
if (action === 'claims') {
|
|
134
|
+
const claims = claimStore.listClaims(project.id, { limit: 100 });
|
|
135
|
+
emitResult(
|
|
136
|
+
{ project, workspace, claims },
|
|
137
|
+
'Claims: ' + claims.length + ' total, ' + claims.filter(claim => claim.reviewState === 'needs-review').length + ' awaiting review.',
|
|
138
|
+
asJson,
|
|
139
|
+
);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (action === 'review') {
|
|
144
|
+
const claimId = requiredText(args.id, 'claim id');
|
|
145
|
+
const existing = claimStore.getClaim(claimId);
|
|
146
|
+
if (!existing || existing.projectId !== project.id) {
|
|
147
|
+
emitError('Claim was not found for this knowledge workspace.', asJson);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const claim = reviewClaim(claimStore, {
|
|
151
|
+
claimId,
|
|
152
|
+
reviewState: claimReviewState(args.review),
|
|
153
|
+
detail: requiredText(args.detail, 'review detail'),
|
|
154
|
+
});
|
|
155
|
+
emitResult(
|
|
156
|
+
{ project, workspace, claim },
|
|
157
|
+
'Claim ' + claim.id + ' marked ' + claim.reviewState + '.',
|
|
158
|
+
asJson,
|
|
159
|
+
);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (action === 'workflow') {
|
|
164
|
+
const workflowAction = (positional[1] || 'list').toLowerCase();
|
|
165
|
+
const workflowStore = new WorkflowStore();
|
|
166
|
+
await workflowStore.init(dataDir);
|
|
167
|
+
const projectRoot = workspace.projectRoot ?? project.rootPath;
|
|
168
|
+
|
|
169
|
+
if (workflowAction === 'import') {
|
|
170
|
+
const result = await importWindsurfWorkflows({ workspace, projectRoot });
|
|
171
|
+
emitResult(
|
|
172
|
+
{ project, workspace, result },
|
|
173
|
+
'Imported ' + result.imported.length + ' Windsurf workflow(s); preserved ' + result.skipped.length + ' existing source or canonical file(s).',
|
|
174
|
+
asJson,
|
|
175
|
+
);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const synced = await syncCanonicalWorkflows(workspace);
|
|
180
|
+
if (workflowAction === 'list') {
|
|
181
|
+
emitResult(
|
|
182
|
+
{
|
|
183
|
+
project,
|
|
184
|
+
workspace,
|
|
185
|
+
workflows: workflowStore.listWorkflows(workspace.id),
|
|
186
|
+
parseErrors: synced.errors,
|
|
187
|
+
},
|
|
188
|
+
[
|
|
189
|
+
'Canonical workflows: ' + workflowStore.listWorkflows(workspace.id).length,
|
|
190
|
+
'Parse errors: ' + synced.errors.length,
|
|
191
|
+
].join('\n'),
|
|
192
|
+
asJson,
|
|
193
|
+
);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (workflowAction === 'select') {
|
|
198
|
+
const task = requiredText(args.task, 'task');
|
|
199
|
+
const result = await selectWorkspaceWorkflows({ workspace, task });
|
|
200
|
+
emitResult(
|
|
201
|
+
{ project, workspace, task, ...result },
|
|
202
|
+
result.selections.length
|
|
203
|
+
? 'Selected ' + result.selections.length + ' workflow(s) for this task.'
|
|
204
|
+
: 'No project workflow matches this task.',
|
|
205
|
+
asJson,
|
|
206
|
+
);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const workflowId = requiredText(args.id, 'workflow id');
|
|
211
|
+
const workflow = workflowStore.getWorkflow(workflowId);
|
|
212
|
+
if (!workflow || workflow.workspaceId !== workspace.id) {
|
|
213
|
+
emitError('Workflow was not found for this knowledge workspace.', asJson);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (workflowAction === 'preview' || workflowAction === 'apply') {
|
|
218
|
+
const agent = requiredText(args.agent, 'agent');
|
|
219
|
+
const result = workflowAction === 'preview'
|
|
220
|
+
? await previewWorkflowAdapter({ workflow, projectRoot, agent: agent as any })
|
|
221
|
+
: await applyWorkflowAdapter({ workflow, projectRoot, agent: agent as any });
|
|
222
|
+
emitResult(
|
|
223
|
+
{ project, workspace, result },
|
|
224
|
+
'Workflow adapter ' + result.status + ': ' + result.reason,
|
|
225
|
+
asJson,
|
|
226
|
+
);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (workflowAction === 'run') {
|
|
231
|
+
const task = requiredText(args.task, 'task');
|
|
232
|
+
const outcome = workflowOutcome(args.outcome);
|
|
233
|
+
const result = await recordWorkflowRun({
|
|
234
|
+
workspace,
|
|
235
|
+
run: {
|
|
236
|
+
workflowId,
|
|
237
|
+
projectId: project.id,
|
|
238
|
+
task,
|
|
239
|
+
outcome,
|
|
240
|
+
...(workflowVerdict(args.verdict) ? { verificationVerdict: workflowVerdict(args.verdict) } : {}),
|
|
241
|
+
...(typeof args.failure === 'string' && args.failure.trim() ? { failureReason: args.failure.trim() } : {}),
|
|
242
|
+
...(typeof args.snapshot === 'string' && args.snapshot.trim() ? { startingSnapshotId: args.snapshot.trim() } : {}),
|
|
243
|
+
selectedEvidence: evidenceList(args.evidence),
|
|
244
|
+
},
|
|
245
|
+
});
|
|
246
|
+
emitResult(
|
|
247
|
+
{ project, workspace, result },
|
|
248
|
+
'Recorded workflow run ' + result.id + ' (' + result.outcome + ').',
|
|
249
|
+
asJson,
|
|
250
|
+
);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
emitResult({ usage: usage() }, usage(), asJson);
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
switch (action) {
|
|
259
|
+
case 'status': {
|
|
260
|
+
const pages = workspaceStore.listPages(workspace.id);
|
|
261
|
+
const pending = workspaceStore.listProposals(workspace.id, 'pending');
|
|
262
|
+
emitResult(
|
|
263
|
+
{ project, workspace, pages, pendingProposals: pending },
|
|
264
|
+
[
|
|
265
|
+
'Knowledge workspace: ' + workspace.rootPath,
|
|
266
|
+
'- Mode: ' + workspace.mode,
|
|
267
|
+
'- Published pages: ' + pages.filter(page => page.status === 'active').length,
|
|
268
|
+
'- Pending proposals: ' + pending.length,
|
|
269
|
+
'- Status: ' + workspace.status,
|
|
270
|
+
].join('\n'),
|
|
271
|
+
asJson,
|
|
272
|
+
);
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
case 'compile': {
|
|
276
|
+
const result = await compileKnowledgeWorkspace({ workspace, claims: claimStore });
|
|
277
|
+
emitResult(
|
|
278
|
+
{ project, workspace, result },
|
|
279
|
+
'Knowledge compilation completed: ' + result.proposals.length + ' proposal(s), ' + result.published.length + ' unchanged published page(s).',
|
|
280
|
+
asJson,
|
|
281
|
+
);
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
case 'lint': {
|
|
285
|
+
const codeStore = new CodeGraphStore();
|
|
286
|
+
await codeStore.init(dataDir);
|
|
287
|
+
const result = await lintKnowledgeWorkspace({ workspace, claims: claimStore, codeStore });
|
|
288
|
+
emitResult(
|
|
289
|
+
{ project, workspace, result },
|
|
290
|
+
result.valid
|
|
291
|
+
? 'Knowledge workspace lint passed.'
|
|
292
|
+
: 'Knowledge workspace lint found ' + result.issues.length + ' issue(s).',
|
|
293
|
+
asJson,
|
|
294
|
+
);
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
case 'apply': {
|
|
298
|
+
const proposalId = (args.proposal as string | undefined)?.trim();
|
|
299
|
+
if (!proposalId) {
|
|
300
|
+
emitError('proposal is required for "memorix knowledge apply"', asJson);
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
const result = await applyKnowledgeProposal({
|
|
304
|
+
workspace,
|
|
305
|
+
proposalId,
|
|
306
|
+
allowManualOverwrite: !!args.force,
|
|
307
|
+
});
|
|
308
|
+
emitResult(
|
|
309
|
+
{ project, workspace, result },
|
|
310
|
+
'Knowledge proposal applied to ' + result.targetPath,
|
|
311
|
+
asJson,
|
|
312
|
+
);
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
default:
|
|
316
|
+
emitResult({ usage: usage() }, usage(), asJson);
|
|
317
|
+
}
|
|
318
|
+
} catch (error) {
|
|
319
|
+
emitError(error instanceof Error ? error.message : String(error), asJson);
|
|
320
|
+
}
|
|
321
|
+
},
|
|
322
|
+
});
|
|
@@ -23,6 +23,7 @@ import { defineCommand } from 'citty';
|
|
|
23
23
|
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
24
24
|
import type { ObservationStore } from '../../store/obs-store.js';
|
|
25
25
|
import { resolveToolProfile } from '../../server/tool-profile.js';
|
|
26
|
+
import { scopeKnowledgeGraphToProject } from '../../memory/graph-scope.js';
|
|
26
27
|
|
|
27
28
|
export const DEFAULT_SESSION_TIMEOUT_MS = 30 * 60 * 1000;
|
|
28
29
|
|
|
@@ -764,6 +765,7 @@ export default defineCommand({
|
|
|
764
765
|
source?: string;
|
|
765
766
|
commitHash?: string;
|
|
766
767
|
filesModified?: string[];
|
|
768
|
+
relatedEntities?: string[];
|
|
767
769
|
status?: string;
|
|
768
770
|
importance?: number;
|
|
769
771
|
accessCount?: number;
|
|
@@ -860,22 +862,20 @@ export default defineCommand({
|
|
|
860
862
|
} catch { /* best effort */ }
|
|
861
863
|
|
|
862
864
|
// Project-scoped graph counts (must match /graph and /export)
|
|
863
|
-
const
|
|
864
|
-
observations.filter(o => o.entityName).map(o => o.entityName!)
|
|
865
|
-
);
|
|
866
|
-
const projectEntities = graph.entities.filter((e: any) => projectEntityNames.has(e.name));
|
|
867
|
-
const projectEntitySet = new Set(projectEntities.map((e: any) => e.name));
|
|
868
|
-
const projectRelations = graph.relations.filter((r: any) => projectEntitySet.has(r.from) && projectEntitySet.has(r.to));
|
|
865
|
+
const projectGraph = scopeKnowledgeGraphToProject(graph, observations);
|
|
869
866
|
|
|
870
867
|
let maintenance = { total: 0, pending: 0, running: 0, retrying: 0, completed: 0, failed: 0 };
|
|
868
|
+
let lifecycle: unknown;
|
|
871
869
|
try {
|
|
872
870
|
const { MaintenanceJobStore } = await import('../../runtime/maintenance-jobs.js');
|
|
873
871
|
maintenance = new MaintenanceJobStore(statsDataDir).summary(statsProjectId);
|
|
872
|
+
const { collectLifecycleDiagnostics } = await import('../../runtime/lifecycle-status.js');
|
|
873
|
+
lifecycle = await collectLifecycleDiagnostics({ dataDir: statsDataDir, projectId: statsProjectId });
|
|
874
874
|
} catch { /* optional maintenance diagnostics */ }
|
|
875
875
|
|
|
876
876
|
sendJson({
|
|
877
|
-
entities:
|
|
878
|
-
relations:
|
|
877
|
+
entities: projectGraph.entities.length,
|
|
878
|
+
relations: projectGraph.relations.length,
|
|
879
879
|
observations: observations.length,
|
|
880
880
|
nextId,
|
|
881
881
|
typeCounts,
|
|
@@ -892,17 +892,25 @@ export default defineCommand({
|
|
|
892
892
|
},
|
|
893
893
|
retentionSummary,
|
|
894
894
|
maintenance,
|
|
895
|
+
...(lifecycle ? { lifecycle } : {}),
|
|
895
896
|
});
|
|
896
897
|
return;
|
|
897
898
|
}
|
|
898
899
|
|
|
899
900
|
if (apiPath === '/maintenance') {
|
|
900
901
|
const { projectId: maintenanceProjectId, dataDir: maintenanceDataDir } = await resolveRequestProject(url);
|
|
901
|
-
const { MaintenanceJobStore } = await
|
|
902
|
+
const [{ MaintenanceJobStore }, { collectLifecycleDiagnostics }] = await Promise.all([
|
|
903
|
+
import('../../runtime/maintenance-jobs.js'),
|
|
904
|
+
import('../../runtime/lifecycle-status.js'),
|
|
905
|
+
]);
|
|
902
906
|
const queue = new MaintenanceJobStore(maintenanceDataDir);
|
|
903
907
|
sendJson({
|
|
904
908
|
summary: queue.summary(maintenanceProjectId),
|
|
905
909
|
jobs: queue.list({ projectId: maintenanceProjectId, limit: 50 }),
|
|
910
|
+
lifecycle: await collectLifecycleDiagnostics({
|
|
911
|
+
dataDir: maintenanceDataDir,
|
|
912
|
+
projectId: maintenanceProjectId,
|
|
913
|
+
}),
|
|
906
914
|
});
|
|
907
915
|
return;
|
|
908
916
|
}
|
|
@@ -919,19 +927,9 @@ export default defineCommand({
|
|
|
919
927
|
await initGraphStore(graphDataDir);
|
|
920
928
|
const fullGraph = { entities: getGraphStore().loadEntities(), relations: getGraphStore().loadRelations() };
|
|
921
929
|
|
|
922
|
-
|
|
923
|
-
const
|
|
924
|
-
|
|
925
|
-
allObs
|
|
926
|
-
.filter(o => o.entityName)
|
|
927
|
-
.map(o => o.entityName!)
|
|
928
|
-
);
|
|
929
|
-
|
|
930
|
-
const entities = fullGraph.entities.filter(e => projectEntityNames.has(e.name));
|
|
931
|
-
const entityNameSet = new Set(entities.map(e => e.name));
|
|
932
|
-
const relations = fullGraph.relations.filter(r => entityNameSet.has(r.from) && entityNameSet.has(r.to));
|
|
933
|
-
|
|
934
|
-
sendJson({ entities, relations });
|
|
930
|
+
const allObs = await loadDashboardProjectObservations(graphDataDir, graphProjectId, 'active');
|
|
931
|
+
const scoped = scopeKnowledgeGraphToProject(fullGraph, allObs);
|
|
932
|
+
sendJson({ entities: scoped.entities, relations: scoped.relations });
|
|
935
933
|
return;
|
|
936
934
|
}
|
|
937
935
|
|
|
@@ -1001,24 +999,15 @@ export default defineCommand({
|
|
|
1001
999
|
const allObs = await loadDashboardProjectObservations(kgDataDir, kgProjectId, 'active');
|
|
1002
1000
|
const skills = await getMiniSkillStore().loadByProject(kgProjectId);
|
|
1003
1001
|
|
|
1004
|
-
// Project-scope graph store (same logic as /api/graph)
|
|
1005
1002
|
const fullGraph = { entities: getGraphStore().loadEntities(), relations: getGraphStore().loadRelations() };
|
|
1006
|
-
const
|
|
1007
|
-
const projectEntityNames = new Set(
|
|
1008
|
-
graphObs
|
|
1009
|
-
.filter(o => o.entityName)
|
|
1010
|
-
.map(o => o.entityName!)
|
|
1011
|
-
);
|
|
1012
|
-
const scopedEntities = fullGraph.entities.filter(e => projectEntityNames.has(e.name));
|
|
1013
|
-
const scopedEntityNameSet = new Set(scopedEntities.map(e => e.name));
|
|
1014
|
-
const scopedRelations = fullGraph.relations.filter(r => scopedEntityNameSet.has(r.from) && scopedEntityNameSet.has(r.to));
|
|
1003
|
+
const scoped = scopeKnowledgeGraphToProject(fullGraph, allObs);
|
|
1015
1004
|
|
|
1016
1005
|
const graph = generateKnowledgeGraph({
|
|
1017
1006
|
projectId: kgProjectId,
|
|
1018
1007
|
observations: allObs,
|
|
1019
1008
|
miniSkills: skills,
|
|
1020
|
-
graphEntities:
|
|
1021
|
-
graphRelations:
|
|
1009
|
+
graphEntities: scoped.entities,
|
|
1010
|
+
graphRelations: scoped.relations,
|
|
1022
1011
|
});
|
|
1023
1012
|
|
|
1024
1013
|
sendJson(graph);
|
|
@@ -1358,19 +1347,14 @@ export default defineCommand({
|
|
|
1358
1347
|
const { initGraphStore, getGraphStore } = await import('../../store/graph-store.js');
|
|
1359
1348
|
await initGraphStore(expDataDir);
|
|
1360
1349
|
const fullGraph = { entities: getGraphStore().loadEntities(), relations: getGraphStore().loadRelations() };
|
|
1361
|
-
const observations = await loadDashboardProjectObservations(expDataDir, expProjectId, 'active')
|
|
1350
|
+
const observations = await loadDashboardProjectObservations(expDataDir, expProjectId, 'active');
|
|
1362
1351
|
const expStore = await getDashboardObservationStore(expDataDir);
|
|
1363
1352
|
const nextId = await expStore.loadIdCounter();
|
|
1364
|
-
const
|
|
1365
|
-
observations.filter(o => o.entityName).map(o => o.entityName!),
|
|
1366
|
-
);
|
|
1367
|
-
const exportEntities = fullGraph.entities.filter((e: any) => exportEntityNames.has(e.name));
|
|
1368
|
-
const exportEntitySet = new Set(exportEntities.map((e: any) => e.name));
|
|
1369
|
-
const exportRelations = fullGraph.relations.filter((r: any) => exportEntitySet.has(r.from) && exportEntitySet.has(r.to));
|
|
1353
|
+
const scoped = scopeKnowledgeGraphToProject(fullGraph, observations);
|
|
1370
1354
|
const exportData = {
|
|
1371
1355
|
project: { id: expProjectId, name: expProjectName },
|
|
1372
1356
|
exportedAt: new Date().toISOString(),
|
|
1373
|
-
graph: { entities:
|
|
1357
|
+
graph: { entities: scoped.entities, relations: scoped.relations },
|
|
1374
1358
|
observations,
|
|
1375
1359
|
nextId,
|
|
1376
1360
|
};
|
|
@@ -1062,7 +1062,7 @@ function tryInstallOmpPackage(packagePath: string): { ok: boolean; message: stri
|
|
|
1062
1062
|
};
|
|
1063
1063
|
}
|
|
1064
1064
|
|
|
1065
|
-
async function installAgentSetup(agent: AgentName, plan: SetupPlan, global: boolean): Promise<void> {
|
|
1065
|
+
export async function installAgentSetup(agent: AgentName, plan: SetupPlan, global: boolean): Promise<void> {
|
|
1066
1066
|
const { installAgentGuidance, installHooks } = await import('../../hooks/installers/index.js');
|
|
1067
1067
|
const targetRoot = global ? homedir() : process.cwd();
|
|
1068
1068
|
const hasPluginPackage = plan.actions.includes('plugin-package') && PLUGIN_PACKAGE_AGENTS.has(agent);
|
|
@@ -1200,13 +1200,19 @@ async function installAgentSetup(agent: AgentName, plan: SetupPlan, global: bool
|
|
|
1200
1200
|
} else if (hasOmpPackage) {
|
|
1201
1201
|
p.log.info(`${agent}: extension command, hook events, and skills are bundled in the Oh-my-Pi package`);
|
|
1202
1202
|
} else if (hasExtensionPackage) {
|
|
1203
|
-
if (plan.actions.includes('hooks')
|
|
1203
|
+
if (plan.actions.includes('hooks')) {
|
|
1204
1204
|
const result = await installHooks(agent, targetRoot, global);
|
|
1205
1205
|
p.log.success(`${agent}: integration files -> ${result.configPath}`);
|
|
1206
|
+
} else if (plan.actions.includes('project-guidance')) {
|
|
1207
|
+
const rulesPath = await installAgentGuidance(agent, targetRoot, global);
|
|
1208
|
+
p.log.success(`${agent}: guidance -> ${rulesPath}`);
|
|
1206
1209
|
}
|
|
1207
|
-
} else if (
|
|
1210
|
+
} else if (plan.actions.includes('hooks')) {
|
|
1208
1211
|
const result = await installHooks(agent, targetRoot, global);
|
|
1209
1212
|
p.log.success(`${agent}: integration files -> ${result.configPath}`);
|
|
1213
|
+
} else if (plan.actions.includes('project-guidance')) {
|
|
1214
|
+
const rulesPath = await installAgentGuidance(agent, targetRoot, global);
|
|
1215
|
+
p.log.success(`${agent}: guidance -> ${rulesPath}`);
|
|
1210
1216
|
}
|
|
1211
1217
|
|
|
1212
1218
|
if (agent === 'pi') {
|
package/src/cli/index.ts
CHANGED
|
@@ -989,6 +989,7 @@ const main = defineCommand({
|
|
|
989
989
|
context: () => import('./commands/context.js').then(m => m.default),
|
|
990
990
|
explain: () => import('./commands/explain.js').then(m => m.default),
|
|
991
991
|
codegraph: () => import('./commands/codegraph.js').then(m => m.default),
|
|
992
|
+
knowledge: () => import('./commands/knowledge.js').then(m => m.default),
|
|
992
993
|
reasoning: () => import('./commands/reasoning.js').then(m => m.default),
|
|
993
994
|
retention: () => import('./commands/retention.js').then(m => m.default),
|
|
994
995
|
formation: () => import('./commands/formation.js').then(m => m.default),
|
|
@@ -1048,7 +1049,7 @@ const main = defineCommand({
|
|
|
1048
1049
|
// Detect by checking if the first CLI arg matches a registered subcommand name.
|
|
1049
1050
|
const firstArg = process.argv[2];
|
|
1050
1051
|
const knownSubs = ['ask', 'search', 'remember', 'recent', 'memcode', 'config',
|
|
1051
|
-
'init', 'setup', 'integrate', 'memory', 'context', 'explain', 'codegraph', 'reasoning', 'retention', 'formation', 'audit', 'transfer', 'skills',
|
|
1052
|
+
'init', 'setup', 'integrate', 'memory', 'context', 'explain', 'codegraph', 'knowledge', 'reasoning', 'retention', 'formation', 'audit', 'transfer', 'skills',
|
|
1052
1053
|
'session', 'team', 'task', 'message', 'lock', 'handoff', 'poll',
|
|
1053
1054
|
'receipt',
|
|
1054
1055
|
'serve', 'serve-http', 'status', 'sync',
|
|
@@ -1088,6 +1089,7 @@ const main = defineCommand({
|
|
|
1088
1089
|
console.error(' context Show the Memory Autopilot brief for this project');
|
|
1089
1090
|
console.error(' explain Explain where Memorix project context comes from');
|
|
1090
1091
|
console.error(' codegraph Refresh/status/context-pack for CodeGraph Memory');
|
|
1092
|
+
console.error(' knowledge Review source-backed knowledge pages and project workflows');
|
|
1091
1093
|
console.error(' reasoning Store/search decision rationale');
|
|
1092
1094
|
console.error(' retention Inspect stale/archive status');
|
|
1093
1095
|
console.error(' formation Inspect Memory Formation metrics');
|
package/src/cli/tui/App.tsx
CHANGED
|
@@ -598,7 +598,7 @@ export function WorkbenchApp({ version, onExitForInteractive }: AppProps): React
|
|
|
598
598
|
setKnowledgeSelectedIdx(0);
|
|
599
599
|
setLoading(false);
|
|
600
600
|
navigateTo('knowledge');
|
|
601
|
-
setStatusMsg({ text: kb ? `
|
|
601
|
+
setStatusMsg({ text: kb ? `Memory Overview: ${kb.stats.observationsUsed} obs, ${kb.stats.miniSkillsUsed} skills` : 'No memory overview available', type: 'info' });
|
|
602
602
|
break;
|
|
603
603
|
}
|
|
604
604
|
case 'memory': {
|