@rigour-labs/core 6.0.0 → 6.1.0-beta.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.
Files changed (48) hide show
  1. package/dist/context/automatic-index.d.ts +18 -0
  2. package/dist/context/automatic-index.js +142 -0
  3. package/dist/context/automatic-index.test.d.ts +1 -0
  4. package/dist/context/automatic-index.test.js +45 -0
  5. package/dist/context/cache-engine.d.ts +25 -0
  6. package/dist/context/dependency-graph.d.ts +13 -0
  7. package/dist/context/dependency-graph.js +97 -0
  8. package/dist/context/dependency-graph.test.d.ts +1 -0
  9. package/dist/context/dependency-graph.test.js +13 -0
  10. package/dist/context/index-bridge.js +21 -2
  11. package/dist/index.d.ts +9 -0
  12. package/dist/index.js +7 -0
  13. package/dist/pattern-index/embeddings.js +10 -3
  14. package/dist/pattern-index/indexer.d.ts +2 -0
  15. package/dist/pattern-index/indexer.js +72 -0
  16. package/dist/services/agent-history.d.ts +76 -0
  17. package/dist/services/agent-history.js +201 -0
  18. package/dist/services/agent-history.test.d.ts +1 -0
  19. package/dist/services/agent-history.test.js +69 -0
  20. package/dist/services/context-telemetry-service.d.ts +21 -0
  21. package/dist/services/context-telemetry-service.js +36 -7
  22. package/dist/services/context-telemetry-service.test.js +13 -0
  23. package/dist/services/engineering-knowledge-graph.d.ts +55 -0
  24. package/dist/services/engineering-knowledge-graph.js +177 -0
  25. package/dist/services/engineering-knowledge-graph.test.d.ts +1 -0
  26. package/dist/services/engineering-knowledge-graph.test.js +59 -0
  27. package/dist/services/model-pricing.d.ts +2 -0
  28. package/dist/services/model-pricing.js +2 -0
  29. package/dist/services/system-health.d.ts +34 -0
  30. package/dist/services/system-health.js +53 -0
  31. package/dist/storage/db.js +130 -1
  32. package/dist/storage/index.d.ts +4 -0
  33. package/dist/storage/index.js +2 -0
  34. package/dist/storage/lessons.d.ts +60 -0
  35. package/dist/storage/lessons.js +322 -0
  36. package/dist/storage/lessons.test.d.ts +1 -0
  37. package/dist/storage/lessons.test.js +50 -0
  38. package/dist/storage/local-encryption.d.ts +2 -0
  39. package/dist/storage/local-encryption.js +41 -0
  40. package/dist/storage/team-store.d.ts +75 -0
  41. package/dist/storage/team-store.js +321 -0
  42. package/dist/storage/team-store.test.d.ts +1 -0
  43. package/dist/storage/team-store.test.js +11 -0
  44. package/dist/storage/team-vector-store.d.ts +25 -0
  45. package/dist/storage/team-vector-store.js +183 -0
  46. package/dist/storage/team-vector-store.test.d.ts +1 -0
  47. package/dist/storage/team-vector-store.test.js +42 -0
  48. package/package.json +8 -6
@@ -0,0 +1,18 @@
1
+ import type { PatternIndex } from '../pattern-index/types.js';
2
+ export type SemanticIndexStatus = 'disabled' | 'warming' | 'ready' | 'degraded';
3
+ export interface AutomaticIndexStatus {
4
+ structural: 'missing' | 'ready' | 'degraded';
5
+ graph: 'missing' | 'ready' | 'degraded';
6
+ semantic: SemanticIndexStatus;
7
+ message?: string;
8
+ updatedAt: string;
9
+ }
10
+ export declare function mergeSemanticEmbeddings(latest: PatternIndex, enriched: PatternIndex): PatternIndex;
11
+ export declare function getAutomaticIndexStatus(cwd: string): Promise<AutomaticIndexStatus>;
12
+ export declare function ensureAutomaticIndex(cwd: string, options?: {
13
+ backgroundSemantic?: boolean;
14
+ }): Promise<PatternIndex>;
15
+ export declare function updateAutomaticIndexForFiles(cwd: string, changedFiles: string[]): Promise<{
16
+ index: PatternIndex;
17
+ affectedFiles: string[];
18
+ }>;
@@ -0,0 +1,142 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import { PatternIndexer, getDefaultIndexPath, loadPatternIndex, savePatternIndex } from '../pattern-index/indexer.js';
4
+ import { affectedDependents, buildDependencyGraph, updateDependencyGraph } from './dependency-graph.js';
5
+ import { syncIndexToContextCache } from './index-bridge.js';
6
+ const enrichmentJobs = new Map();
7
+ function projectRelativeFiles(cwd, files) {
8
+ const root = path.resolve(cwd);
9
+ return files.flatMap(file => {
10
+ const relative = path.relative(root, path.resolve(root, file));
11
+ if (!relative || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative))
12
+ return [];
13
+ return [relative.split(path.sep).join('/')];
14
+ });
15
+ }
16
+ export function mergeSemanticEmbeddings(latest, enriched) {
17
+ const embeddings = new Map(enriched.patterns
18
+ .filter(pattern => pattern.embedding?.length)
19
+ .map(pattern => [pattern.id, pattern.embedding]));
20
+ return {
21
+ ...latest,
22
+ lastUpdated: new Date().toISOString(),
23
+ patterns: latest.patterns.map(pattern => ({
24
+ ...pattern,
25
+ embedding: embeddings.get(pattern.id) ?? pattern.embedding,
26
+ })),
27
+ };
28
+ }
29
+ function statusPath(cwd) {
30
+ return path.join(cwd, '.rigour', 'index-status.json');
31
+ }
32
+ async function writeStatus(cwd, status) {
33
+ await fs.ensureDir(path.join(cwd, '.rigour'));
34
+ await fs.writeJson(statusPath(cwd), status, { spaces: 2 });
35
+ }
36
+ export async function getAutomaticIndexStatus(cwd) {
37
+ try {
38
+ return await fs.readJson(statusPath(cwd));
39
+ }
40
+ catch {
41
+ return {
42
+ structural: 'missing',
43
+ graph: 'missing',
44
+ semantic: 'disabled',
45
+ updatedAt: new Date().toISOString(),
46
+ };
47
+ }
48
+ }
49
+ async function enrichSemantic(cwd, index) {
50
+ const indexPath = getDefaultIndexPath(cwd);
51
+ try {
52
+ await writeStatus(cwd, {
53
+ structural: 'ready', graph: 'ready', semantic: 'warming', updatedAt: new Date().toISOString(),
54
+ });
55
+ const enriched = await new PatternIndexer(cwd, { useEmbeddings: true }).enrichIndex(index);
56
+ const latest = await loadPatternIndex(indexPath) ?? index;
57
+ const merged = mergeSemanticEmbeddings(latest, enriched);
58
+ const embedded = merged.patterns.filter((pattern) => (pattern.embedding?.length ?? 0) > 0).length;
59
+ await savePatternIndex(merged, indexPath);
60
+ await syncIndexToContextCache(cwd, merged);
61
+ await writeStatus(cwd, {
62
+ structural: 'ready',
63
+ graph: 'ready',
64
+ semantic: embedded > 0 ? 'ready' : 'degraded',
65
+ message: embedded > 0 ? `${embedded} patterns enriched` : 'Local embedding model unavailable; text matching remains active.',
66
+ updatedAt: new Date().toISOString(),
67
+ });
68
+ }
69
+ catch (error) {
70
+ await writeStatus(cwd, {
71
+ structural: 'ready',
72
+ graph: 'ready',
73
+ semantic: 'degraded',
74
+ message: error instanceof Error ? error.message : String(error),
75
+ updatedAt: new Date().toISOString(),
76
+ });
77
+ }
78
+ finally {
79
+ enrichmentJobs.delete(path.resolve(cwd));
80
+ }
81
+ }
82
+ function startEnrichment(cwd, index) {
83
+ const key = path.resolve(cwd);
84
+ if (enrichmentJobs.has(key))
85
+ return;
86
+ const job = enrichSemantic(cwd, index);
87
+ enrichmentJobs.set(key, job);
88
+ }
89
+ export async function ensureAutomaticIndex(cwd, options = {}) {
90
+ const indexPath = getDefaultIndexPath(cwd);
91
+ const existing = await loadPatternIndex(indexPath);
92
+ const indexer = new PatternIndexer(cwd, { useEmbeddings: false });
93
+ const index = existing ?? await indexer.buildIndex();
94
+ if (!existing)
95
+ await savePatternIndex(index, indexPath);
96
+ await syncIndexToContextCache(cwd, index);
97
+ if (!(await fs.pathExists(path.join(cwd, '.rigour', 'dependency-graph.json')))) {
98
+ await buildDependencyGraph(cwd, index);
99
+ }
100
+ const hasEmbeddings = index.patterns.some((pattern) => (pattern.embedding?.length ?? 0) > 0);
101
+ await writeStatus(cwd, {
102
+ structural: 'ready',
103
+ graph: 'ready',
104
+ semantic: hasEmbeddings ? 'ready' : options.backgroundSemantic === false ? 'disabled' : 'warming',
105
+ updatedAt: new Date().toISOString(),
106
+ });
107
+ if (!hasEmbeddings && options.backgroundSemantic !== false)
108
+ startEnrichment(cwd, index);
109
+ return index;
110
+ }
111
+ export async function updateAutomaticIndexForFiles(cwd, changedFiles) {
112
+ const normalizedChangedFiles = projectRelativeFiles(cwd, changedFiles);
113
+ const existing = await loadPatternIndex(getDefaultIndexPath(cwd));
114
+ if (!existing)
115
+ return { index: await ensureAutomaticIndex(cwd), affectedFiles: normalizedChangedFiles };
116
+ const previousStatus = await getAutomaticIndexStatus(cwd);
117
+ let graph = null;
118
+ try {
119
+ graph = await fs.readJson(path.join(cwd, '.rigour', 'dependency-graph.json'));
120
+ }
121
+ catch { /* baseline below */ }
122
+ const affectedFiles = graph ? affectedDependents(graph, normalizedChangedFiles) : normalizedChangedFiles;
123
+ const indexer = new PatternIndexer(cwd, { useEmbeddings: false });
124
+ const index = await indexer.updateFiles(existing, affectedFiles);
125
+ await savePatternIndex(index, getDefaultIndexPath(cwd));
126
+ await syncIndexToContextCache(cwd, index);
127
+ if (graph)
128
+ await updateDependencyGraph(cwd, index, graph, affectedFiles);
129
+ else
130
+ await buildDependencyGraph(cwd, index);
131
+ await writeStatus(cwd, {
132
+ structural: 'ready', graph: 'ready',
133
+ semantic: index.patterns.some(pattern => (pattern.embedding?.length ?? 0) > 0)
134
+ ? 'ready'
135
+ : previousStatus.semantic === 'disabled' ? 'disabled' : 'warming',
136
+ message: `${normalizedChangedFiles.length} changed file(s); ${affectedFiles.length} affected file(s) refreshed without a full scan.`,
137
+ updatedAt: new Date().toISOString(),
138
+ });
139
+ if (previousStatus.semantic !== 'disabled')
140
+ startEnrichment(cwd, index);
141
+ return { index, affectedFiles };
142
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,45 @@
1
+ import fs from 'fs-extra';
2
+ import os from 'os';
3
+ import path from 'path';
4
+ import { afterEach, describe, expect, it } from 'vitest';
5
+ import { ensureAutomaticIndex, mergeSemanticEmbeddings, updateAutomaticIndexForFiles } from './automatic-index.js';
6
+ describe('automatic incremental index', () => {
7
+ const cwd = path.join(os.tmpdir(), `rigour-auto-index-${process.pid}`);
8
+ afterEach(() => fs.remove(cwd));
9
+ it('builds once and refreshes only changed files and their dependents', async () => {
10
+ await fs.outputFile(path.join(cwd, 'src/value.ts'), 'export const value = 1;');
11
+ await fs.outputFile(path.join(cwd, 'src/consumer.ts'), "import { value } from './value';\nexport const doubled = value * 2;");
12
+ const baseline = await ensureAutomaticIndex(cwd, { backgroundSemantic: false });
13
+ const untouched = baseline.files.find(file => file.path === 'src/consumer.ts')?.indexedAt;
14
+ await fs.writeFile(path.join(cwd, 'src/value.ts'), 'export const value = 2;');
15
+ const result = await updateAutomaticIndexForFiles(cwd, ['src/value.ts']);
16
+ expect(result.affectedFiles.sort()).toEqual(['src/consumer.ts', 'src/value.ts']);
17
+ expect(result.index.files.find(file => file.path === 'src/consumer.ts')?.indexedAt).not.toBe(untouched);
18
+ });
19
+ it('ignores changed-file paths outside the repository', async () => {
20
+ await fs.outputFile(path.join(cwd, 'src/value.ts'), 'export const value = 1;');
21
+ const baseline = await ensureAutomaticIndex(cwd, { backgroundSemantic: false });
22
+ const result = await updateAutomaticIndexForFiles(cwd, [path.join(cwd, '..', 'outside.ts')]);
23
+ expect(result.affectedFiles).toEqual([]);
24
+ expect(result.index.patterns).toEqual(baseline.patterns);
25
+ });
26
+ it('merges background embeddings onto the latest structural snapshot', () => {
27
+ const pattern = (id, file, embedding) => ({
28
+ id, file, embedding, type: 'function', name: id, line: 1, endLine: 1,
29
+ signature: '', description: '', keywords: [], hash: id, exported: true,
30
+ usageCount: 0, indexedAt: '2026-09-10T00:00:00.000Z',
31
+ });
32
+ const makeIndex = (patterns) => ({
33
+ version: '1.0.0', lastUpdated: '2026-09-10T00:00:00.000Z', rootDir: cwd,
34
+ patterns,
35
+ files: patterns.map(entry => ({ path: entry.file, hash: entry.hash, patternCount: 1, indexedAt: entry.indexedAt })),
36
+ stats: { totalPatterns: patterns.length, totalFiles: patterns.length, byType: { function: patterns.length }, indexDurationMs: 1 },
37
+ });
38
+ const latest = makeIndex([pattern('changed', 'src/changed.ts'), pattern('new', 'src/new.ts')]);
39
+ const enriched = makeIndex([pattern('changed', 'src/old.ts', [0.1, 0.2]), pattern('deleted', 'src/deleted.ts', [0.3])]);
40
+ const merged = mergeSemanticEmbeddings(latest, enriched);
41
+ expect(merged.patterns.map(entry => entry.id)).toEqual(['changed', 'new']);
42
+ expect(merged.patterns[0].file).toBe('src/changed.ts');
43
+ expect(merged.patterns[0].embedding).toEqual([0.1, 0.2]);
44
+ });
45
+ });
@@ -36,6 +36,31 @@ export interface SemanticQueryEntry {
36
36
  evidence: string[];
37
37
  commitSha: string;
38
38
  confidence: number;
39
+ sourceTokens?: number;
40
+ candidateFiles?: number;
41
+ returnedFiles?: number;
42
+ guidance?: {
43
+ kind: 'context-scope' | 'pattern' | 'memory';
44
+ query?: string;
45
+ recommendation: string;
46
+ patternRefs?: Array<{
47
+ id: string;
48
+ label: string;
49
+ file?: string;
50
+ }>;
51
+ lessonRefs?: Array<{
52
+ id: string;
53
+ label: string;
54
+ visibility?: string;
55
+ }>;
56
+ memoryRefs?: Array<{
57
+ id: string;
58
+ label: string;
59
+ }>;
60
+ selectedFiles?: string[];
61
+ excludedFileCount?: number;
62
+ conflicts?: number;
63
+ };
39
64
  }
40
65
  export interface TaskCheckpointPacket {
41
66
  taskId: string;
@@ -0,0 +1,13 @@
1
+ import type { PatternIndex } from '../pattern-index/types.js';
2
+ export interface DependencyGraph {
3
+ version: 1;
4
+ generatedAt: string;
5
+ nodes: string[];
6
+ edges: Array<{
7
+ from: string;
8
+ to: string;
9
+ }>;
10
+ }
11
+ export declare function buildDependencyGraph(cwd: string, index: PatternIndex): Promise<DependencyGraph>;
12
+ export declare function updateDependencyGraph(cwd: string, index: PatternIndex, existing: DependencyGraph, changedFiles: string[]): Promise<DependencyGraph>;
13
+ export declare function affectedDependents(graph: DependencyGraph, changedFiles: string[]): string[];
@@ -0,0 +1,97 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ const IMPORT_PATTERNS = [
4
+ /(?:import|export)\s+(?:[^'"\n]+?\s+from\s+)?['"]([^'"]+)['"]/g,
5
+ /require\(\s*['"]([^'"]+)['"]\s*\)/g,
6
+ /from\s+([\w.]+)\s+import\s+/g,
7
+ ];
8
+ function resolveImport(from, specifier, files) {
9
+ if (!specifier.startsWith('.') && !specifier.startsWith('/'))
10
+ return null;
11
+ const base = path.posix.normalize(path.posix.join(path.posix.dirname(from), specifier));
12
+ const candidates = [base, `${base}.ts`, `${base}.tsx`, `${base}.js`, `${base}.jsx`, `${base}.py`];
13
+ for (const indexName of ['index.ts', 'index.tsx', 'index.js', 'index.jsx']) {
14
+ candidates.push(path.posix.join(base, indexName));
15
+ }
16
+ return candidates.find((candidate) => files.has(candidate)) ?? null;
17
+ }
18
+ function importsFrom(content) {
19
+ const imports = new Set();
20
+ for (const pattern of IMPORT_PATTERNS) {
21
+ pattern.lastIndex = 0;
22
+ let match;
23
+ while ((match = pattern.exec(content)) !== null)
24
+ imports.add(match[1]);
25
+ }
26
+ return [...imports];
27
+ }
28
+ export async function buildDependencyGraph(cwd, index) {
29
+ const files = new Set(index.files.map((file) => file.path.split(path.sep).join('/')));
30
+ const edges = [];
31
+ for (const from of files) {
32
+ try {
33
+ const content = await fs.readFile(path.join(cwd, from), 'utf8');
34
+ for (const specifier of importsFrom(content)) {
35
+ const to = resolveImport(from, specifier, files);
36
+ if (to)
37
+ edges.push({ from, to });
38
+ }
39
+ }
40
+ catch {
41
+ // A disappearing file is handled by the next incremental index update.
42
+ }
43
+ }
44
+ const graph = {
45
+ version: 1,
46
+ generatedAt: new Date().toISOString(),
47
+ nodes: [...files],
48
+ edges,
49
+ };
50
+ const graphPath = path.join(cwd, '.rigour', 'dependency-graph.json');
51
+ await fs.ensureDir(path.dirname(graphPath));
52
+ await fs.writeJson(graphPath, graph, { spaces: 2 });
53
+ return graph;
54
+ }
55
+ export async function updateDependencyGraph(cwd, index, existing, changedFiles) {
56
+ const files = new Set(index.files.map(file => file.path.split(path.sep).join('/')));
57
+ const root = path.resolve(cwd);
58
+ const changed = new Set(changedFiles.flatMap(file => {
59
+ const relative = path.relative(root, path.resolve(root, file));
60
+ if (!relative || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative))
61
+ return [];
62
+ return [relative.split(path.sep).join('/')];
63
+ }));
64
+ const edges = existing.edges.filter(edge => !changed.has(edge.from) && files.has(edge.from) && files.has(edge.to));
65
+ for (const from of changed) {
66
+ if (!files.has(from))
67
+ continue;
68
+ try {
69
+ const content = await fs.readFile(path.join(cwd, from), 'utf8');
70
+ for (const specifier of importsFrom(content)) {
71
+ const to = resolveImport(from, specifier, files);
72
+ if (to)
73
+ edges.push({ from, to });
74
+ }
75
+ }
76
+ catch {
77
+ // Deleted files are removed from the node and edge sets.
78
+ }
79
+ }
80
+ const graph = { version: 1, generatedAt: new Date().toISOString(), nodes: [...files], edges };
81
+ await fs.writeJson(path.join(cwd, '.rigour', 'dependency-graph.json'), graph, { spaces: 2 });
82
+ return graph;
83
+ }
84
+ export function affectedDependents(graph, changedFiles) {
85
+ const affected = new Set(changedFiles);
86
+ let changed = true;
87
+ while (changed) {
88
+ changed = false;
89
+ for (const edge of graph.edges) {
90
+ if (affected.has(edge.to) && !affected.has(edge.from)) {
91
+ affected.add(edge.from);
92
+ changed = true;
93
+ }
94
+ }
95
+ }
96
+ return [...affected];
97
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,13 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { affectedDependents } from './dependency-graph.js';
3
+ describe('affectedDependents', () => {
4
+ it('includes direct and transitive consumers', () => {
5
+ const affected = affectedDependents({
6
+ version: 1,
7
+ generatedAt: '2026-09-10T00:00:00Z',
8
+ nodes: ['a.ts', 'b.ts', 'c.ts'],
9
+ edges: [{ from: 'b.ts', to: 'a.ts' }, { from: 'c.ts', to: 'b.ts' }],
10
+ }, ['a.ts']);
11
+ expect(new Set(affected)).toEqual(new Set(['a.ts', 'b.ts', 'c.ts']));
12
+ });
13
+ });
@@ -5,10 +5,29 @@
5
5
  import fs from 'fs-extra';
6
6
  import path from 'path';
7
7
  import { setStaticCache, setComponentCache, hashContent } from './cache-engine.js';
8
+ import { getRepositoryId } from '../storage/lessons.js';
8
9
  function getIndexPath(cwd) {
9
10
  return path.join(cwd, '.rigour', 'patterns.json');
10
11
  }
11
12
  const DEFAULT_STALE_MS = 24 * 60 * 60 * 1000;
13
+ async function resolveGitBranch(cwd) {
14
+ try {
15
+ let gitDir = path.join(cwd, '.git');
16
+ const gitStat = await fs.stat(gitDir);
17
+ if (gitStat.isFile()) {
18
+ const pointer = await fs.readFile(gitDir, 'utf8');
19
+ const match = pointer.match(/^gitdir:\s*(.+)$/m);
20
+ if (!match)
21
+ return 'workspace';
22
+ gitDir = path.resolve(cwd, match[1].trim());
23
+ }
24
+ const head = (await fs.readFile(path.join(gitDir, 'HEAD'), 'utf8')).trim();
25
+ return head.startsWith('ref: refs/heads/') ? head.slice('ref: refs/heads/'.length) : head.slice(0, 12);
26
+ }
27
+ catch {
28
+ return 'workspace';
29
+ }
30
+ }
12
31
  function groupByComponent(patterns) {
13
32
  const groups = new Map();
14
33
  for (const p of patterns) {
@@ -41,8 +60,8 @@ function buildDossier(component, patterns) {
41
60
  */
42
61
  export async function syncIndexToContextCache(cwd, index) {
43
62
  const resolved = path.resolve(cwd);
44
- const repo = path.basename(index.rootDir || resolved);
45
- const branch = 'main';
63
+ const repo = await getRepositoryId(resolved);
64
+ const branch = await resolveGitBranch(resolved);
46
65
  const commitSha = hashContent(index.lastUpdated);
47
66
  let synced = 0;
48
67
  for (const file of index.files) {
package/dist/index.d.ts CHANGED
@@ -24,6 +24,10 @@ export { openDatabase, isSQLiteAvailable, compactDatabase, getDatabaseSize, rese
24
24
  export type { RigourDB, CompactResult } from './storage/index.js';
25
25
  export { checkLocalPatterns, persistAndReinforce, getProjectStats } from './storage/index.js';
26
26
  export type { ProjectStats } from './storage/index.js';
27
+ export { recordInteractionEvidence, countInteractionEvidence, recordInteractionLesson, listLessons, listKnowledgeLessons, getApplicableLessons, transitionLesson, getRepositoryId } from './storage/index.js';
28
+ export type { ApplicableLessons, InteractionEvidence, LessonRecord, LessonState, LessonVisibility } from './storage/index.js';
29
+ export { loadTeamConfiguration, saveTeamConfiguration, initializeTeamSchema, getTeamModeStatus, doctorTeamConnection, syncTeamOutbox, searchTeamKnowledge, backfillTeamEmbeddings, TEAM_CONFIG_PATH, validateTeamDatabaseUrl, } from './storage/index.js';
30
+ export type { TeamConfiguration, TeamSemanticConfiguration, TeamModeStatus, TeamDoctorResult, SemanticKnowledgeCandidate, SemanticKnowledgeResult, } from './storage/index.js';
27
31
  export { generateTemporalDriftReport, formatDriftSummary } from './services/temporal-drift.js';
28
32
  export type { TemporalDriftReport, ProvenanceStream, MonthlyBucket, WeeklyBucket, DriftDirection } from './services/temporal-drift.js';
29
33
  export { getProvenanceTrends, getQualityTrend } from './services/adaptive-thresholds.js';
@@ -35,6 +39,11 @@ export type { RenderOptions, GateResult } from './services/terminal-renderer.js'
35
39
  export * from './context/cache-engine.js';
36
40
  export * from './context/context-session.js';
37
41
  export * from './context/index-bridge.js';
42
+ export * from './context/automatic-index.js';
43
+ export * from './context/dependency-graph.js';
38
44
  export * from './services/context-telemetry-service.js';
45
+ export * from './services/system-health.js';
46
+ export * from './services/agent-history.js';
47
+ export * from './services/engineering-knowledge-graph.js';
39
48
  export * from './firewall/index.js';
40
49
  export { recordContextEvent, recordModelUsage, recordCheckpointMetric, getContextEvents, getModelUsages, getCheckpointMetrics, } from './storage/index.js';
package/dist/index.js CHANGED
@@ -24,6 +24,8 @@ export { extractFacts, factsToPromptString } from './deep/fact-extractor.js';
24
24
  export { openDatabase, isSQLiteAvailable, compactDatabase, getDatabaseSize, resetDatabase, insertScan, insertFindings, getRecentScans, getScoreTrendFromDB, getTopIssues, reinforcePattern, getStrongPatterns } from './storage/index.js';
25
25
  // Local Project Memory (hybrid intelligence — SQLite-backed per-project learning)
26
26
  export { checkLocalPatterns, persistAndReinforce, getProjectStats } from './storage/index.js';
27
+ export { recordInteractionEvidence, countInteractionEvidence, recordInteractionLesson, listLessons, listKnowledgeLessons, getApplicableLessons, transitionLesson, getRepositoryId } from './storage/index.js';
28
+ export { loadTeamConfiguration, saveTeamConfiguration, initializeTeamSchema, getTeamModeStatus, doctorTeamConnection, syncTeamOutbox, searchTeamKnowledge, backfillTeamEmbeddings, TEAM_CONFIG_PATH, validateTeamDatabaseUrl, } from './storage/index.js';
27
29
  // Temporal Drift Engine (v5 — cross-session trend analysis + per-provenance EWMA)
28
30
  export { generateTemporalDriftReport, formatDriftSummary } from './services/temporal-drift.js';
29
31
  // Adaptive Thresholds (v5 — Z-score + per-provenance trends)
@@ -36,7 +38,12 @@ export { renderScoreGauge, renderSeveritySection, renderGateGrid, renderBrainSta
36
38
  export * from './context/cache-engine.js';
37
39
  export * from './context/context-session.js';
38
40
  export * from './context/index-bridge.js';
41
+ export * from './context/automatic-index.js';
42
+ export * from './context/dependency-graph.js';
39
43
  export * from './services/context-telemetry-service.js';
44
+ export * from './services/system-health.js';
45
+ export * from './services/agent-history.js';
46
+ export * from './services/engineering-knowledge-graph.js';
40
47
  export * from './firewall/index.js';
41
48
  export { recordContextEvent, recordModelUsage, recordCheckpointMetric, getContextEvents, getModelUsages, getCheckpointMetrics, } from './storage/index.js';
42
49
  // Pattern Index is intentionally NOT exported here to prevent
@@ -7,6 +7,7 @@
7
7
  * Singleton for the embedding pipeline to avoid re-loading the model.
8
8
  */
9
9
  let embeddingPipeline = null;
10
+ let embeddingUnavailable = false;
10
11
  /**
11
12
  * Get or initialize the embedding pipeline.
12
13
  */
@@ -21,6 +22,8 @@ async function getPipeline() {
21
22
  return { data: new Float32Array(vector) };
22
23
  };
23
24
  }
25
+ if (embeddingUnavailable)
26
+ return null;
24
27
  if (!embeddingPipeline) {
25
28
  try {
26
29
  // Dynamic import to isolate native dependency issues (like sharp)
@@ -29,8 +32,9 @@ async function getPipeline() {
29
32
  embeddingPipeline = await pipeline('feature-extraction', 'Xenova/all-MiniLM-L6-v2');
30
33
  }
31
34
  catch (error) {
32
- console.error('Failed to initialize embedding pipeline:', error);
33
- throw error;
35
+ embeddingUnavailable = true;
36
+ console.warn('Semantic enrichment is degraded; structural and text retrieval remain available.');
37
+ return null;
34
38
  }
35
39
  }
36
40
  return embeddingPipeline;
@@ -41,11 +45,14 @@ async function getPipeline() {
41
45
  export async function generateEmbedding(text) {
42
46
  try {
43
47
  const extractor = await getPipeline();
48
+ if (!extractor)
49
+ return [];
44
50
  const output = await extractor(text, { pooling: 'mean', normalize: true });
45
51
  return Array.from(output.data);
46
52
  }
47
53
  catch (error) {
48
- console.warn('Semantic reasoning disabled: Embedding generation failed.', error);
54
+ embeddingUnavailable = true;
55
+ console.warn('Semantic enrichment is degraded; structural and text retrieval remain available.');
49
56
  return [];
50
57
  }
51
58
  }
@@ -15,6 +15,8 @@ export declare class PatternIndexer {
15
15
  constructor(rootDir: string, config?: Partial<PatternIndexConfig>);
16
16
  buildIndex(): Promise<PatternIndex>;
17
17
  updateIndex(existingIndex: PatternIndex): Promise<PatternIndex>;
18
+ updateFiles(existingIndex: PatternIndex, changedFiles: string[]): Promise<PatternIndex>;
19
+ enrichIndex(existingIndex: PatternIndex): Promise<PatternIndex>;
18
20
  private findFiles;
19
21
  private extractPatterns;
20
22
  private getScriptKind;
@@ -12,6 +12,7 @@ import * as fs from 'fs/promises';
12
12
  import * as path from 'path';
13
13
  import { globby } from 'globby';
14
14
  import ts from 'typescript';
15
+ import micromatch from 'micromatch';
15
16
  import { generateEmbedding } from './embeddings.js';
16
17
  import { hashContent } from './indexer-helpers.js';
17
18
  import { extractGoPatterns, extractRustPatterns, extractJVMStylePatterns, extractPythonPatterns, extractGenericCPatterns, } from './indexer-lang.js';
@@ -156,6 +157,77 @@ export class PatternIndexer {
156
157
  files: updatedFiles,
157
158
  };
158
159
  }
160
+ async updateFiles(existingIndex, changedFiles) {
161
+ const startTime = Date.now();
162
+ const root = path.resolve(this.rootDir);
163
+ const changed = new Set(changedFiles.flatMap(file => {
164
+ const absolute = path.resolve(root, file);
165
+ const relative = path.relative(root, absolute);
166
+ if (!relative || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative))
167
+ return [];
168
+ return [relative.split(path.sep).join('/')];
169
+ }));
170
+ const patterns = existingIndex.patterns.filter(pattern => !changed.has(pattern.file));
171
+ const files = existingIndex.files.filter(file => !changed.has(file.path));
172
+ for (const relativePath of changed) {
173
+ const absolutePath = path.join(root, relativePath);
174
+ const supported = this.config.extensions.includes(path.extname(relativePath).toLowerCase());
175
+ const included = micromatch.isMatch(relativePath.replace(/\\/g, '/'), this.config.include, { dot: false });
176
+ const excluded = micromatch.isMatch(relativePath.replace(/\\/g, '/'), this.config.exclude, { dot: true });
177
+ if (!supported || !included || excluded)
178
+ continue;
179
+ try {
180
+ const content = await fs.readFile(absolutePath, 'utf-8');
181
+ const filePatterns = await this.extractPatterns(absolutePath, content);
182
+ if (this.config.useEmbeddings) {
183
+ await Promise.all(filePatterns.map(async (pattern) => {
184
+ pattern.embedding = await generateEmbedding(`${pattern.name} ${pattern.type} ${pattern.description}`);
185
+ }));
186
+ }
187
+ patterns.push(...filePatterns);
188
+ files.push({
189
+ path: relativePath,
190
+ hash: hashContent(content),
191
+ patternCount: filePatterns.length,
192
+ indexedAt: new Date().toISOString(),
193
+ });
194
+ }
195
+ catch (error) {
196
+ if (error?.code !== 'ENOENT')
197
+ throw error;
198
+ }
199
+ }
200
+ return {
201
+ version: INDEX_VERSION,
202
+ lastUpdated: new Date().toISOString(),
203
+ rootDir: this.rootDir,
204
+ patterns,
205
+ files,
206
+ stats: this.calculateStats(patterns, files, Date.now() - startTime),
207
+ };
208
+ }
209
+ async enrichIndex(existingIndex) {
210
+ const startTime = Date.now();
211
+ const patterns = existingIndex.patterns.map(pattern => ({ ...pattern }));
212
+ const pending = patterns.filter(pattern => !pattern.embedding?.length);
213
+ if (pending.length > 0) {
214
+ pending[0].embedding = await generateEmbedding(`${pending[0].name} ${pending[0].type} ${pending[0].description}`);
215
+ if (!pending[0].embedding.length)
216
+ return existingIndex;
217
+ }
218
+ const batchSize = 10;
219
+ for (let i = 1; i < pending.length; i += batchSize) {
220
+ await Promise.all(pending.slice(i, i + batchSize).map(async (pattern) => {
221
+ pattern.embedding = await generateEmbedding(`${pattern.name} ${pattern.type} ${pattern.description}`);
222
+ }));
223
+ }
224
+ return {
225
+ ...existingIndex,
226
+ lastUpdated: new Date().toISOString(),
227
+ patterns,
228
+ stats: this.calculateStats(patterns, existingIndex.files, Date.now() - startTime),
229
+ };
230
+ }
159
231
  // -------------------------------------------------------------------------
160
232
  // Private helpers
161
233
  // -------------------------------------------------------------------------