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
package/src/wiki/generator.ts
CHANGED
|
@@ -1,237 +1,237 @@
|
|
|
1
|
-
import type { Observation, MiniSkill } from '../types.js';
|
|
2
|
-
import type { KnowledgeSourceRef, KnowledgeItem, KnowledgeSection, ProjectKnowledgeOverview } from './types.js';
|
|
3
|
-
|
|
4
|
-
const COMMAND_LOG_TITLE = /^(Ran:|Command:|Executed:)\s/i;
|
|
5
|
-
|
|
6
|
-
interface SectionDef {
|
|
7
|
-
id: string;
|
|
8
|
-
title: string;
|
|
9
|
-
typeMatch: (o: Observation) => boolean;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const SECTION_DEFS: SectionDef[] = [
|
|
13
|
-
{
|
|
14
|
-
id: 'core-decisions',
|
|
15
|
-
title: 'Core Decisions',
|
|
16
|
-
typeMatch: (o) => o.type === 'decision' || o.type === 'trade-off' || o.type === 'reasoning',
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
id: 'operational-knowledge',
|
|
20
|
-
title: 'Operational Knowledge',
|
|
21
|
-
typeMatch: (o) =>
|
|
22
|
-
o.type === 'how-it-works' ||
|
|
23
|
-
o.type === 'what-changed' ||
|
|
24
|
-
o.type === 'why-it-exists' ||
|
|
25
|
-
o.type === 'discovery' ||
|
|
26
|
-
o.type === 'session-request',
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
id: 'known-gotchas',
|
|
30
|
-
title: 'Known Gotchas',
|
|
31
|
-
typeMatch: (o) => o.type === 'gotcha' || o.type === 'problem-solution',
|
|
32
|
-
},
|
|
33
|
-
];
|
|
34
|
-
|
|
35
|
-
function isExcludedType(o: Observation): boolean {
|
|
36
|
-
return o.type === 'probe';
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function isCommandLog(o: Observation): boolean {
|
|
40
|
-
return COMMAND_LOG_TITLE.test(o.title || '');
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function isInactive(o: Observation): boolean {
|
|
44
|
-
const status = o.status ?? 'active';
|
|
45
|
-
return status !== 'active';
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function isOtherProject(o: Observation, projectId: string): boolean {
|
|
49
|
-
return o.projectId !== projectId;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function contextualHasSubstance(o: Observation): boolean {
|
|
53
|
-
if (o.valueCategory !== 'contextual') return true;
|
|
54
|
-
const hasFacts = (o.facts?.length ?? 0) > 0;
|
|
55
|
-
const hasConcepts = (o.concepts?.length ?? 0) > 0;
|
|
56
|
-
const hasFiles = (o.filesModified?.length ?? 0) > 0;
|
|
57
|
-
const hasEntity = !!(o.entityName && o.entityName !== 'quick-note' && o.entityName !== 'unknown');
|
|
58
|
-
return hasFacts || hasConcepts || hasFiles || hasEntity;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function isEligible(o: Observation, projectId: string): boolean {
|
|
62
|
-
if (isExcludedType(o)) return false;
|
|
63
|
-
if (isCommandLog(o)) return false;
|
|
64
|
-
if (isInactive(o)) return false;
|
|
65
|
-
if (isOtherProject(o, projectId)) return false;
|
|
66
|
-
if (o.valueCategory === 'ephemeral') return false;
|
|
67
|
-
if (!contextualHasSubstance(o)) return false;
|
|
68
|
-
return true;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function obsToItem(o: Observation): KnowledgeItem {
|
|
72
|
-
const ref: KnowledgeSourceRef = {
|
|
73
|
-
kind: o.source === 'git' ? 'git' : 'observation',
|
|
74
|
-
id: `obs:${o.id}`,
|
|
75
|
-
title: o.title,
|
|
76
|
-
};
|
|
77
|
-
return {
|
|
78
|
-
title: o.title,
|
|
79
|
-
summary: o.narrative?.slice(0, 200) || '',
|
|
80
|
-
type: o.type,
|
|
81
|
-
entityName: o.entityName || undefined,
|
|
82
|
-
refs: [ref],
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function skillToItem(s: MiniSkill): KnowledgeItem {
|
|
87
|
-
const ref: KnowledgeSourceRef = {
|
|
88
|
-
kind: 'mini-skill',
|
|
89
|
-
id: `skill:${s.id}`,
|
|
90
|
-
title: s.title,
|
|
91
|
-
};
|
|
92
|
-
const obsRefs: KnowledgeSourceRef[] = s.sourceObservationIds.map(
|
|
93
|
-
(oid) => ({ kind: 'observation' as const, id: `obs:${oid}` }),
|
|
94
|
-
);
|
|
95
|
-
return {
|
|
96
|
-
title: s.title,
|
|
97
|
-
summary: s.instruction?.slice(0, 200) || '',
|
|
98
|
-
type: 'mini-skill',
|
|
99
|
-
entityName: s.sourceEntity || undefined,
|
|
100
|
-
refs: [ref, ...obsRefs],
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function buildGitSection(observations: Observation[]): KnowledgeSection {
|
|
105
|
-
const gitObs = observations.filter(
|
|
106
|
-
(o) => o.source === 'git' && o.sourceDetail === 'git-ingest',
|
|
107
|
-
);
|
|
108
|
-
const items = gitObs.map(obsToItem);
|
|
109
|
-
return {
|
|
110
|
-
id: 'git-backed-facts',
|
|
111
|
-
title: 'Git-backed Facts',
|
|
112
|
-
items,
|
|
113
|
-
empty: items.length === 0,
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function buildSkillsSection(skills: MiniSkill[]): KnowledgeSection {
|
|
118
|
-
const items = skills.map(skillToItem);
|
|
119
|
-
return {
|
|
120
|
-
id: 'promoted-skills',
|
|
121
|
-
title: 'Promoted Skills',
|
|
122
|
-
items,
|
|
123
|
-
empty: items.length === 0,
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function buildProjectOverview(
|
|
128
|
-
projectId: string,
|
|
129
|
-
eligibleObs: Observation[],
|
|
130
|
-
skills: MiniSkill[],
|
|
131
|
-
): KnowledgeSection {
|
|
132
|
-
const refs: KnowledgeSourceRef[] = [
|
|
133
|
-
...eligibleObs.slice(0, 5).map((o) => ({
|
|
134
|
-
kind: o.source === 'git' ? 'git' as const : 'observation' as const,
|
|
135
|
-
id: `obs:${o.id}`,
|
|
136
|
-
title: o.title,
|
|
137
|
-
})),
|
|
138
|
-
...skills.slice(0, 5).map((s) => ({
|
|
139
|
-
kind: 'mini-skill' as const,
|
|
140
|
-
id: `skill:${s.id}`,
|
|
141
|
-
title: s.title,
|
|
142
|
-
})),
|
|
143
|
-
];
|
|
144
|
-
|
|
145
|
-
if (refs.length === 0) {
|
|
146
|
-
return {
|
|
147
|
-
id: 'project-overview',
|
|
148
|
-
title: 'Project Overview',
|
|
149
|
-
items: [],
|
|
150
|
-
empty: true,
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const lines: string[] = [];
|
|
155
|
-
lines.push(`Project: ${projectId}`);
|
|
156
|
-
lines.push(`Observations in KB: ${eligibleObs.length}`);
|
|
157
|
-
lines.push(`Promoted skills: ${skills.length}`);
|
|
158
|
-
|
|
159
|
-
const entityCounts = new Map<string, number>();
|
|
160
|
-
for (const o of eligibleObs) {
|
|
161
|
-
if (o.entityName) {
|
|
162
|
-
entityCounts.set(o.entityName, (entityCounts.get(o.entityName) || 0) + 1);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
const topEntities = [...entityCounts.entries()]
|
|
166
|
-
.sort((a, b) => b[1] - a[1])
|
|
167
|
-
.slice(0, 5);
|
|
168
|
-
if (topEntities.length > 0) {
|
|
169
|
-
lines.push(`Top entities: ${topEntities.map(([name, count]) => `${name} (${count})`).join(', ')}`);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
return {
|
|
173
|
-
id: 'project-overview',
|
|
174
|
-
title: 'Project Overview',
|
|
175
|
-
items: [{
|
|
176
|
-
title: projectId,
|
|
177
|
-
summary: lines.join('\n'),
|
|
178
|
-
type: 'overview',
|
|
179
|
-
refs,
|
|
180
|
-
}],
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
export interface GenerateOptions {
|
|
185
|
-
projectId: string;
|
|
186
|
-
observations: Observation[];
|
|
187
|
-
miniSkills: MiniSkill[];
|
|
188
|
-
generatedAt?: string;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
export function generateKnowledgeBase(options: GenerateOptions): ProjectKnowledgeOverview {
|
|
192
|
-
const { projectId, observations, miniSkills } = options;
|
|
193
|
-
|
|
194
|
-
const eligible = observations.filter((o) => isEligible(o, projectId));
|
|
195
|
-
const scopedMiniSkills = miniSkills.filter(s => s.projectId === projectId);
|
|
196
|
-
|
|
197
|
-
const typedSections: KnowledgeSection[] = SECTION_DEFS.map((def) => {
|
|
198
|
-
const matched = eligible.filter(def.typeMatch);
|
|
199
|
-
const items = matched.map(obsToItem);
|
|
200
|
-
return {
|
|
201
|
-
id: def.id,
|
|
202
|
-
title: def.title,
|
|
203
|
-
items,
|
|
204
|
-
empty: items.length === 0,
|
|
205
|
-
};
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
const projectOverview = buildProjectOverview(projectId, eligible, scopedMiniSkills);
|
|
209
|
-
const gitSection = buildGitSection(eligible);
|
|
210
|
-
const skillsSection = buildSkillsSection(scopedMiniSkills);
|
|
211
|
-
|
|
212
|
-
const sections: KnowledgeSection[] = [
|
|
213
|
-
projectOverview,
|
|
214
|
-
...typedSections,
|
|
215
|
-
gitSection,
|
|
216
|
-
skillsSection,
|
|
217
|
-
];
|
|
218
|
-
|
|
219
|
-
const allRefs = sections.flatMap((s) => s.items.flatMap((i) => i.refs));
|
|
220
|
-
const obsRefCount = allRefs.filter((r) => r.kind === 'observation' || r.kind === 'git').length;
|
|
221
|
-
const skillRefCount = allRefs.filter((r) => r.kind === 'mini-skill').length;
|
|
222
|
-
|
|
223
|
-
return {
|
|
224
|
-
title: 'Knowledge Base',
|
|
225
|
-
subtitle: 'LLM Wiki',
|
|
226
|
-
projectId,
|
|
227
|
-
generatedAt: options.generatedAt ?? new Date().toISOString(),
|
|
228
|
-
sections,
|
|
229
|
-
stats: {
|
|
230
|
-
observationsUsed: eligible.length,
|
|
231
|
-
miniSkillsUsed: scopedMiniSkills.length,
|
|
232
|
-
refs: obsRefCount + skillRefCount,
|
|
233
|
-
},
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
export { isEligible, isExcludedType, isCommandLog, contextualHasSubstance };
|
|
1
|
+
import type { Observation, MiniSkill } from '../types.js';
|
|
2
|
+
import type { KnowledgeSourceRef, KnowledgeItem, KnowledgeSection, ProjectKnowledgeOverview } from './types.js';
|
|
3
|
+
|
|
4
|
+
const COMMAND_LOG_TITLE = /^(Ran:|Command:|Executed:)\s/i;
|
|
5
|
+
|
|
6
|
+
interface SectionDef {
|
|
7
|
+
id: string;
|
|
8
|
+
title: string;
|
|
9
|
+
typeMatch: (o: Observation) => boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const SECTION_DEFS: SectionDef[] = [
|
|
13
|
+
{
|
|
14
|
+
id: 'core-decisions',
|
|
15
|
+
title: 'Core Decisions',
|
|
16
|
+
typeMatch: (o) => o.type === 'decision' || o.type === 'trade-off' || o.type === 'reasoning',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
id: 'operational-knowledge',
|
|
20
|
+
title: 'Operational Knowledge',
|
|
21
|
+
typeMatch: (o) =>
|
|
22
|
+
o.type === 'how-it-works' ||
|
|
23
|
+
o.type === 'what-changed' ||
|
|
24
|
+
o.type === 'why-it-exists' ||
|
|
25
|
+
o.type === 'discovery' ||
|
|
26
|
+
o.type === 'session-request',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: 'known-gotchas',
|
|
30
|
+
title: 'Known Gotchas',
|
|
31
|
+
typeMatch: (o) => o.type === 'gotcha' || o.type === 'problem-solution',
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
function isExcludedType(o: Observation): boolean {
|
|
36
|
+
return o.type === 'probe';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function isCommandLog(o: Observation): boolean {
|
|
40
|
+
return COMMAND_LOG_TITLE.test(o.title || '');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function isInactive(o: Observation): boolean {
|
|
44
|
+
const status = o.status ?? 'active';
|
|
45
|
+
return status !== 'active';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function isOtherProject(o: Observation, projectId: string): boolean {
|
|
49
|
+
return o.projectId !== projectId;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function contextualHasSubstance(o: Observation): boolean {
|
|
53
|
+
if (o.valueCategory !== 'contextual') return true;
|
|
54
|
+
const hasFacts = (o.facts?.length ?? 0) > 0;
|
|
55
|
+
const hasConcepts = (o.concepts?.length ?? 0) > 0;
|
|
56
|
+
const hasFiles = (o.filesModified?.length ?? 0) > 0;
|
|
57
|
+
const hasEntity = !!(o.entityName && o.entityName !== 'quick-note' && o.entityName !== 'unknown');
|
|
58
|
+
return hasFacts || hasConcepts || hasFiles || hasEntity;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function isEligible(o: Observation, projectId: string): boolean {
|
|
62
|
+
if (isExcludedType(o)) return false;
|
|
63
|
+
if (isCommandLog(o)) return false;
|
|
64
|
+
if (isInactive(o)) return false;
|
|
65
|
+
if (isOtherProject(o, projectId)) return false;
|
|
66
|
+
if (o.valueCategory === 'ephemeral') return false;
|
|
67
|
+
if (!contextualHasSubstance(o)) return false;
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function obsToItem(o: Observation): KnowledgeItem {
|
|
72
|
+
const ref: KnowledgeSourceRef = {
|
|
73
|
+
kind: o.source === 'git' ? 'git' : 'observation',
|
|
74
|
+
id: `obs:${o.id}`,
|
|
75
|
+
title: o.title,
|
|
76
|
+
};
|
|
77
|
+
return {
|
|
78
|
+
title: o.title,
|
|
79
|
+
summary: o.narrative?.slice(0, 200) || '',
|
|
80
|
+
type: o.type,
|
|
81
|
+
entityName: o.entityName || undefined,
|
|
82
|
+
refs: [ref],
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function skillToItem(s: MiniSkill): KnowledgeItem {
|
|
87
|
+
const ref: KnowledgeSourceRef = {
|
|
88
|
+
kind: 'mini-skill',
|
|
89
|
+
id: `skill:${s.id}`,
|
|
90
|
+
title: s.title,
|
|
91
|
+
};
|
|
92
|
+
const obsRefs: KnowledgeSourceRef[] = s.sourceObservationIds.map(
|
|
93
|
+
(oid) => ({ kind: 'observation' as const, id: `obs:${oid}` }),
|
|
94
|
+
);
|
|
95
|
+
return {
|
|
96
|
+
title: s.title,
|
|
97
|
+
summary: s.instruction?.slice(0, 200) || '',
|
|
98
|
+
type: 'mini-skill',
|
|
99
|
+
entityName: s.sourceEntity || undefined,
|
|
100
|
+
refs: [ref, ...obsRefs],
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function buildGitSection(observations: Observation[]): KnowledgeSection {
|
|
105
|
+
const gitObs = observations.filter(
|
|
106
|
+
(o) => o.source === 'git' && o.sourceDetail === 'git-ingest',
|
|
107
|
+
);
|
|
108
|
+
const items = gitObs.map(obsToItem);
|
|
109
|
+
return {
|
|
110
|
+
id: 'git-backed-facts',
|
|
111
|
+
title: 'Git-backed Facts',
|
|
112
|
+
items,
|
|
113
|
+
empty: items.length === 0,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function buildSkillsSection(skills: MiniSkill[]): KnowledgeSection {
|
|
118
|
+
const items = skills.map(skillToItem);
|
|
119
|
+
return {
|
|
120
|
+
id: 'promoted-skills',
|
|
121
|
+
title: 'Promoted Skills',
|
|
122
|
+
items,
|
|
123
|
+
empty: items.length === 0,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function buildProjectOverview(
|
|
128
|
+
projectId: string,
|
|
129
|
+
eligibleObs: Observation[],
|
|
130
|
+
skills: MiniSkill[],
|
|
131
|
+
): KnowledgeSection {
|
|
132
|
+
const refs: KnowledgeSourceRef[] = [
|
|
133
|
+
...eligibleObs.slice(0, 5).map((o) => ({
|
|
134
|
+
kind: o.source === 'git' ? 'git' as const : 'observation' as const,
|
|
135
|
+
id: `obs:${o.id}`,
|
|
136
|
+
title: o.title,
|
|
137
|
+
})),
|
|
138
|
+
...skills.slice(0, 5).map((s) => ({
|
|
139
|
+
kind: 'mini-skill' as const,
|
|
140
|
+
id: `skill:${s.id}`,
|
|
141
|
+
title: s.title,
|
|
142
|
+
})),
|
|
143
|
+
];
|
|
144
|
+
|
|
145
|
+
if (refs.length === 0) {
|
|
146
|
+
return {
|
|
147
|
+
id: 'project-overview',
|
|
148
|
+
title: 'Project Overview',
|
|
149
|
+
items: [],
|
|
150
|
+
empty: true,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const lines: string[] = [];
|
|
155
|
+
lines.push(`Project: ${projectId}`);
|
|
156
|
+
lines.push(`Observations in KB: ${eligibleObs.length}`);
|
|
157
|
+
lines.push(`Promoted skills: ${skills.length}`);
|
|
158
|
+
|
|
159
|
+
const entityCounts = new Map<string, number>();
|
|
160
|
+
for (const o of eligibleObs) {
|
|
161
|
+
if (o.entityName) {
|
|
162
|
+
entityCounts.set(o.entityName, (entityCounts.get(o.entityName) || 0) + 1);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
const topEntities = [...entityCounts.entries()]
|
|
166
|
+
.sort((a, b) => b[1] - a[1])
|
|
167
|
+
.slice(0, 5);
|
|
168
|
+
if (topEntities.length > 0) {
|
|
169
|
+
lines.push(`Top entities: ${topEntities.map(([name, count]) => `${name} (${count})`).join(', ')}`);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
id: 'project-overview',
|
|
174
|
+
title: 'Project Overview',
|
|
175
|
+
items: [{
|
|
176
|
+
title: projectId,
|
|
177
|
+
summary: lines.join('\n'),
|
|
178
|
+
type: 'overview',
|
|
179
|
+
refs,
|
|
180
|
+
}],
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export interface GenerateOptions {
|
|
185
|
+
projectId: string;
|
|
186
|
+
observations: Observation[];
|
|
187
|
+
miniSkills: MiniSkill[];
|
|
188
|
+
generatedAt?: string;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function generateKnowledgeBase(options: GenerateOptions): ProjectKnowledgeOverview {
|
|
192
|
+
const { projectId, observations, miniSkills } = options;
|
|
193
|
+
|
|
194
|
+
const eligible = observations.filter((o) => isEligible(o, projectId));
|
|
195
|
+
const scopedMiniSkills = miniSkills.filter(s => s.projectId === projectId);
|
|
196
|
+
|
|
197
|
+
const typedSections: KnowledgeSection[] = SECTION_DEFS.map((def) => {
|
|
198
|
+
const matched = eligible.filter(def.typeMatch);
|
|
199
|
+
const items = matched.map(obsToItem);
|
|
200
|
+
return {
|
|
201
|
+
id: def.id,
|
|
202
|
+
title: def.title,
|
|
203
|
+
items,
|
|
204
|
+
empty: items.length === 0,
|
|
205
|
+
};
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
const projectOverview = buildProjectOverview(projectId, eligible, scopedMiniSkills);
|
|
209
|
+
const gitSection = buildGitSection(eligible);
|
|
210
|
+
const skillsSection = buildSkillsSection(scopedMiniSkills);
|
|
211
|
+
|
|
212
|
+
const sections: KnowledgeSection[] = [
|
|
213
|
+
projectOverview,
|
|
214
|
+
...typedSections,
|
|
215
|
+
gitSection,
|
|
216
|
+
skillsSection,
|
|
217
|
+
];
|
|
218
|
+
|
|
219
|
+
const allRefs = sections.flatMap((s) => s.items.flatMap((i) => i.refs));
|
|
220
|
+
const obsRefCount = allRefs.filter((r) => r.kind === 'observation' || r.kind === 'git').length;
|
|
221
|
+
const skillRefCount = allRefs.filter((r) => r.kind === 'mini-skill').length;
|
|
222
|
+
|
|
223
|
+
return {
|
|
224
|
+
title: 'Knowledge Base',
|
|
225
|
+
subtitle: 'LLM Wiki',
|
|
226
|
+
projectId,
|
|
227
|
+
generatedAt: options.generatedAt ?? new Date().toISOString(),
|
|
228
|
+
sections,
|
|
229
|
+
stats: {
|
|
230
|
+
observationsUsed: eligible.length,
|
|
231
|
+
miniSkillsUsed: scopedMiniSkills.length,
|
|
232
|
+
refs: obsRefCount + skillRefCount,
|
|
233
|
+
},
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export { isEligible, isExcludedType, isCommandLog, contextualHasSubstance };
|