@teamlens/core 0.1.0
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/dist/analytics/analytics-engine.d.ts +45 -0
- package/dist/analytics/analytics-engine.d.ts.map +1 -0
- package/dist/analytics/analytics-engine.js +176 -0
- package/dist/analytics/analytics-engine.js.map +1 -0
- package/dist/distribution/distributor.d.ts +19 -0
- package/dist/distribution/distributor.d.ts.map +1 -0
- package/dist/distribution/distributor.js +220 -0
- package/dist/distribution/distributor.js.map +1 -0
- package/dist/extractor/git-extractor.d.ts +36 -0
- package/dist/extractor/git-extractor.d.ts.map +1 -0
- package/dist/extractor/git-extractor.js +198 -0
- package/dist/extractor/git-extractor.js.map +1 -0
- package/dist/index.d.ts +118 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +313 -0
- package/dist/index.js.map +1 -0
- package/dist/retrieval/retriever.d.ts +33 -0
- package/dist/retrieval/retriever.d.ts.map +1 -0
- package/dist/retrieval/retriever.js +126 -0
- package/dist/retrieval/retriever.js.map +1 -0
- package/dist/session/insight-detector.d.ts +10 -0
- package/dist/session/insight-detector.d.ts.map +1 -0
- package/dist/session/insight-detector.js +87 -0
- package/dist/session/insight-detector.js.map +1 -0
- package/dist/session/session-manager.d.ts +49 -0
- package/dist/session/session-manager.d.ts.map +1 -0
- package/dist/session/session-manager.js +228 -0
- package/dist/session/session-manager.js.map +1 -0
- package/dist/staleness/staleness-engine.d.ts +36 -0
- package/dist/staleness/staleness-engine.d.ts.map +1 -0
- package/dist/staleness/staleness-engine.js +141 -0
- package/dist/staleness/staleness-engine.js.map +1 -0
- package/dist/store/database.d.ts +121 -0
- package/dist/store/database.d.ts.map +1 -0
- package/dist/store/database.js +677 -0
- package/dist/store/database.js.map +1 -0
- package/dist/store/embeddings.d.ts +21 -0
- package/dist/store/embeddings.d.ts.map +1 -0
- package/dist/store/embeddings.js +70 -0
- package/dist/store/embeddings.js.map +1 -0
- package/dist/sync/team-sync.d.ts +77 -0
- package/dist/sync/team-sync.d.ts.map +1 -0
- package/dist/sync/team-sync.js +230 -0
- package/dist/sync/team-sync.js.map +1 -0
- package/dist/types.d.ts +223 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +24 -0
- package/dist/types.js.map +1 -0
- package/package.json +39 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { MemoryDatabase } from '../store/database.js';
|
|
2
|
+
import { EmbeddingProvider } from '../store/embeddings.js';
|
|
3
|
+
import type { RetrievalQuery, ScoredMemory, Memory, TeamLensConfig } from '../types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Multi-signal retriever — ranks memories using:
|
|
6
|
+
* 0.40 × semantic similarity (embedding cosine)
|
|
7
|
+
* 0.25 × file proximity (same directory as scope)
|
|
8
|
+
* 0.20 × recency (newer = higher)
|
|
9
|
+
* 0.15 × confidence (author confidence score)
|
|
10
|
+
* – staleness penalty (stale memories pushed down)
|
|
11
|
+
*
|
|
12
|
+
* Falls back to keyword matching when embeddings aren't available.
|
|
13
|
+
*/
|
|
14
|
+
export declare class MemoryRetriever {
|
|
15
|
+
private db;
|
|
16
|
+
private embeddings;
|
|
17
|
+
private config;
|
|
18
|
+
constructor(db: MemoryDatabase, embeddings: EmbeddingProvider, config: TeamLensConfig);
|
|
19
|
+
query(request: RetrievalQuery): Promise<ScoredMemory[]>;
|
|
20
|
+
/** Convenience: get all conventions (no query needed). */
|
|
21
|
+
getConventions(): Promise<Memory[]>;
|
|
22
|
+
/** Convenience: get decisions for a scope. */
|
|
23
|
+
getDecisions(scope?: string): Promise<Memory[]>;
|
|
24
|
+
private scoreSemantic;
|
|
25
|
+
private scoreFileProximity;
|
|
26
|
+
private scoreRecency;
|
|
27
|
+
/**
|
|
28
|
+
* Simple keyword overlap score when embeddings aren't available.
|
|
29
|
+
* Counts how many query words appear in the content.
|
|
30
|
+
*/
|
|
31
|
+
private keywordScore;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=retriever.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retriever.d.ts","sourceRoot":"","sources":["../../src/retrieval/retriever.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EACV,cAAc,EACd,YAAY,EACZ,MAAM,EACN,cAAc,EACf,MAAM,aAAa,CAAC;AAErB;;;;;;;;;GASG;AACH,qBAAa,eAAe;IAExB,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,MAAM;gBAFN,EAAE,EAAE,cAAc,EAClB,UAAU,EAAE,iBAAiB,EAC7B,MAAM,EAAE,cAAc;IAG1B,KAAK,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAgE7D,0DAA0D;IACpD,cAAc,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAIzC,8CAA8C;IACxC,YAAY,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAUrD,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,kBAAkB;IAU1B,OAAO,CAAC,YAAY;IAQpB;;;OAGG;IACH,OAAO,CAAC,YAAY;CAYrB"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { EmbeddingProvider } from '../store/embeddings.js';
|
|
2
|
+
/**
|
|
3
|
+
* Multi-signal retriever — ranks memories using:
|
|
4
|
+
* 0.40 × semantic similarity (embedding cosine)
|
|
5
|
+
* 0.25 × file proximity (same directory as scope)
|
|
6
|
+
* 0.20 × recency (newer = higher)
|
|
7
|
+
* 0.15 × confidence (author confidence score)
|
|
8
|
+
* – staleness penalty (stale memories pushed down)
|
|
9
|
+
*
|
|
10
|
+
* Falls back to keyword matching when embeddings aren't available.
|
|
11
|
+
*/
|
|
12
|
+
export class MemoryRetriever {
|
|
13
|
+
db;
|
|
14
|
+
embeddings;
|
|
15
|
+
config;
|
|
16
|
+
constructor(db, embeddings, config) {
|
|
17
|
+
this.db = db;
|
|
18
|
+
this.embeddings = embeddings;
|
|
19
|
+
this.config = config;
|
|
20
|
+
}
|
|
21
|
+
async query(request) {
|
|
22
|
+
const limit = request.limit ?? this.config.defaultLimit;
|
|
23
|
+
const includeStale = request.includeStale ?? false;
|
|
24
|
+
// Get candidate memories
|
|
25
|
+
let candidates;
|
|
26
|
+
if (request.tier) {
|
|
27
|
+
candidates = this.db.getMemoriesByTier(request.tier, includeStale);
|
|
28
|
+
}
|
|
29
|
+
else if (request.category) {
|
|
30
|
+
candidates = this.db.getMemoriesByCategory(request.category);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
candidates = this.db.getAllMemories(includeStale);
|
|
34
|
+
}
|
|
35
|
+
// Further filter by category if both tier and category specified
|
|
36
|
+
if (request.tier && request.category) {
|
|
37
|
+
candidates = candidates.filter((m) => m.category === request.category);
|
|
38
|
+
}
|
|
39
|
+
// Filter by scope (directory prefix)
|
|
40
|
+
if (request.scope) {
|
|
41
|
+
candidates = candidates.filter((m) => m.relatedFiles.some((f) => f.startsWith(request.scope)));
|
|
42
|
+
}
|
|
43
|
+
if (candidates.length === 0)
|
|
44
|
+
return [];
|
|
45
|
+
// Try to get query embedding for semantic scoring
|
|
46
|
+
const queryEmbedding = await this.embeddings.embed(request.query);
|
|
47
|
+
// Score each candidate
|
|
48
|
+
const scored = candidates.map((memory) => {
|
|
49
|
+
const semantic = this.scoreSemantic(memory, request.query, queryEmbedding);
|
|
50
|
+
const fileProximity = this.scoreFileProximity(memory, request.scope);
|
|
51
|
+
const recency = this.scoreRecency(memory);
|
|
52
|
+
const confidence = memory.confidence;
|
|
53
|
+
const stalenessPenalty = memory.staleness * 0.5;
|
|
54
|
+
const score = 0.4 * semantic +
|
|
55
|
+
0.25 * fileProximity +
|
|
56
|
+
0.2 * recency +
|
|
57
|
+
0.15 * confidence -
|
|
58
|
+
stalenessPenalty;
|
|
59
|
+
return {
|
|
60
|
+
memory,
|
|
61
|
+
score,
|
|
62
|
+
breakdown: {
|
|
63
|
+
semantic,
|
|
64
|
+
fileProximity,
|
|
65
|
+
recency,
|
|
66
|
+
confidence,
|
|
67
|
+
stalenessPenalty,
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
// Sort by score descending, return top-K
|
|
72
|
+
scored.sort((a, b) => b.score - a.score);
|
|
73
|
+
return scored.slice(0, limit);
|
|
74
|
+
}
|
|
75
|
+
/** Convenience: get all conventions (no query needed). */
|
|
76
|
+
async getConventions() {
|
|
77
|
+
return this.db.getMemoriesByCategory('convention');
|
|
78
|
+
}
|
|
79
|
+
/** Convenience: get decisions for a scope. */
|
|
80
|
+
async getDecisions(scope) {
|
|
81
|
+
const decisions = this.db.getMemoriesByCategory('decision');
|
|
82
|
+
if (!scope)
|
|
83
|
+
return decisions;
|
|
84
|
+
return decisions.filter((m) => m.relatedFiles.some((f) => f.startsWith(scope)));
|
|
85
|
+
}
|
|
86
|
+
// ── Scoring Functions ──
|
|
87
|
+
scoreSemantic(memory, query, queryEmbedding) {
|
|
88
|
+
// If we have embeddings for both, use cosine similarity
|
|
89
|
+
if (queryEmbedding && memory.embedding) {
|
|
90
|
+
return EmbeddingProvider.cosineSimilarity(queryEmbedding, memory.embedding);
|
|
91
|
+
}
|
|
92
|
+
// Fallback: keyword matching
|
|
93
|
+
return this.keywordScore(memory.content, query);
|
|
94
|
+
}
|
|
95
|
+
scoreFileProximity(memory, scope) {
|
|
96
|
+
if (!scope)
|
|
97
|
+
return 0.5; // Neutral when no scope
|
|
98
|
+
const matches = memory.relatedFiles.filter((f) => f.startsWith(scope)).length;
|
|
99
|
+
const total = memory.relatedFiles.length;
|
|
100
|
+
if (total === 0)
|
|
101
|
+
return 0.3;
|
|
102
|
+
return Math.min(matches / total, 1.0);
|
|
103
|
+
}
|
|
104
|
+
scoreRecency(memory) {
|
|
105
|
+
const ageMs = Date.now() - new Date(memory.createdAt).getTime();
|
|
106
|
+
const ageDays = ageMs / (1000 * 60 * 60 * 24);
|
|
107
|
+
// Exponential decay: half-life of 30 days
|
|
108
|
+
return Math.exp(-ageDays / 30);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Simple keyword overlap score when embeddings aren't available.
|
|
112
|
+
* Counts how many query words appear in the content.
|
|
113
|
+
*/
|
|
114
|
+
keywordScore(content, query) {
|
|
115
|
+
const queryWords = query
|
|
116
|
+
.toLowerCase()
|
|
117
|
+
.split(/\s+/)
|
|
118
|
+
.filter((w) => w.length > 2);
|
|
119
|
+
const contentLower = content.toLowerCase();
|
|
120
|
+
if (queryWords.length === 0)
|
|
121
|
+
return 0;
|
|
122
|
+
const matches = queryWords.filter((w) => contentLower.includes(w)).length;
|
|
123
|
+
return matches / queryWords.length;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=retriever.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retriever.js","sourceRoot":"","sources":["../../src/retrieval/retriever.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { InsightType, MemoryCategory } from '../types.js';
|
|
2
|
+
export declare class InsightDetector {
|
|
3
|
+
/** Detect the most likely insight type from content. */
|
|
4
|
+
detect(content: string): InsightType;
|
|
5
|
+
/** Score confidence in the detected type (0.0 - 1.0). */
|
|
6
|
+
scoreConfidence(content: string, type: InsightType): number;
|
|
7
|
+
/** Convert InsightType to MemoryCategory. */
|
|
8
|
+
toCategory(type: InsightType): MemoryCategory;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=insight-detector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"insight-detector.d.ts","sourceRoot":"","sources":["../../src/session/insight-detector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAkD/D,qBAAa,eAAe;IAC1B,wDAAwD;IACxD,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW;IAoBpC,yDAAyD;IACzD,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,MAAM;IAkB3D,6CAA6C;IAC7C,UAAU,CAAC,IAAI,EAAE,WAAW,GAAG,cAAc;CAG9C"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
const PATTERNS = {
|
|
2
|
+
gotcha: [
|
|
3
|
+
/\b(gotcha|pitfall|trap|caveat|watch out|careful|beware|silently|unexpect|quirk|subtle|hidden)\b/i,
|
|
4
|
+
/\b(doesn'?t work|broke|breaking|fail|error when|crash|bug|issue with)\b/i,
|
|
5
|
+
/\b(actually|turns out|it seems|surprisingly|counterintuitive)\b/i,
|
|
6
|
+
],
|
|
7
|
+
convention: [
|
|
8
|
+
/\b(convention|naming|style|pattern|always use|never use|prefer|standard|consistent)\b/i,
|
|
9
|
+
/\b(format|lint|eslint|prettier|coding style|best practice)\b/i,
|
|
10
|
+
/\b(should be|must be|required to|expected to)\b/i,
|
|
11
|
+
],
|
|
12
|
+
architecture: [
|
|
13
|
+
/\b(architect|structure|design|pattern|module|component|layer|service|system)\b/i,
|
|
14
|
+
/\b(split|extract|decouple|separate|organize|restructure)\b/i,
|
|
15
|
+
/\b(data flow|pipeline|middleware|handler|controller|provider)\b/i,
|
|
16
|
+
],
|
|
17
|
+
dependency: [
|
|
18
|
+
/\b(depend|package|library|version|npm|import|require|install|upgrade)\b/i,
|
|
19
|
+
/\b(compatible|incompatible|peer|resolution|conflict)\b/i,
|
|
20
|
+
/\b(deprecated|removed|replaced|alternative)\b/i,
|
|
21
|
+
],
|
|
22
|
+
decision: [
|
|
23
|
+
/\b(decided|chose|picked|selected|went with|opted for|reason)\b/i,
|
|
24
|
+
/\b(because|instead of|trade-?off|pros? and cons?|alternative)\b/i,
|
|
25
|
+
/\b(approach|strategy|solution|design decision)\b/i,
|
|
26
|
+
],
|
|
27
|
+
correction: [
|
|
28
|
+
/\b(wrong|incorrect|mistake|fix|correct|should not|don'?t|avoid|never)\b/i,
|
|
29
|
+
/\b(deprecated|outdated|stale|obsolete|removed)\b/i,
|
|
30
|
+
/\b(instead|replace|update|change to)\b/i,
|
|
31
|
+
],
|
|
32
|
+
discovery: [
|
|
33
|
+
/\b(found|discover|learn|realize|notice|understand|figure out)\b/i,
|
|
34
|
+
/\b(how .+ works|works by|implemented as|under the hood)\b/i,
|
|
35
|
+
/\b(interesting|notable|important|key insight)\b/i,
|
|
36
|
+
],
|
|
37
|
+
};
|
|
38
|
+
const INSIGHT_TO_CATEGORY = {
|
|
39
|
+
gotcha: 'gotcha',
|
|
40
|
+
convention: 'convention',
|
|
41
|
+
architecture: 'architecture',
|
|
42
|
+
dependency: 'dependency',
|
|
43
|
+
decision: 'decision',
|
|
44
|
+
correction: 'correction',
|
|
45
|
+
discovery: 'discovery',
|
|
46
|
+
};
|
|
47
|
+
export class InsightDetector {
|
|
48
|
+
/** Detect the most likely insight type from content. */
|
|
49
|
+
detect(content) {
|
|
50
|
+
let bestType = 'discovery';
|
|
51
|
+
let bestScore = 0;
|
|
52
|
+
for (const [type, patterns] of Object.entries(PATTERNS)) {
|
|
53
|
+
let score = 0;
|
|
54
|
+
for (const pattern of patterns) {
|
|
55
|
+
if (pattern.test(content)) {
|
|
56
|
+
score++;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (score > bestScore) {
|
|
60
|
+
bestScore = score;
|
|
61
|
+
bestType = type;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return bestType;
|
|
65
|
+
}
|
|
66
|
+
/** Score confidence in the detected type (0.0 - 1.0). */
|
|
67
|
+
scoreConfidence(content, type) {
|
|
68
|
+
const patterns = PATTERNS[type];
|
|
69
|
+
if (!patterns)
|
|
70
|
+
return 0.5;
|
|
71
|
+
let matches = 0;
|
|
72
|
+
for (const pattern of patterns) {
|
|
73
|
+
if (pattern.test(content))
|
|
74
|
+
matches++;
|
|
75
|
+
}
|
|
76
|
+
// Base confidence from pattern matches
|
|
77
|
+
const matchRatio = matches / patterns.length;
|
|
78
|
+
// Boost for longer content (more context = more confident)
|
|
79
|
+
const lengthBoost = Math.min(content.length / 200, 0.2);
|
|
80
|
+
return Math.min(0.5 + matchRatio * 0.4 + lengthBoost, 1.0);
|
|
81
|
+
}
|
|
82
|
+
/** Convert InsightType to MemoryCategory. */
|
|
83
|
+
toCategory(type) {
|
|
84
|
+
return INSIGHT_TO_CATEGORY[type];
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=insight-detector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"insight-detector.js","sourceRoot":"","sources":["../../src/session/insight-detector.ts"],"names":[],"mappings":"AAEA,MAAM,QAAQ,GAAkC;IAC9C,MAAM,EAAE;QACN,kGAAkG;QAClG,0EAA0E;QAC1E,kEAAkE;KACnE;IACD,UAAU,EAAE;QACV,wFAAwF;QACxF,+DAA+D;QAC/D,kDAAkD;KACnD;IACD,YAAY,EAAE;QACZ,iFAAiF;QACjF,6DAA6D;QAC7D,kEAAkE;KACnE;IACD,UAAU,EAAE;QACV,0EAA0E;QAC1E,yDAAyD;QACzD,gDAAgD;KACjD;IACD,QAAQ,EAAE;QACR,iEAAiE;QACjE,kEAAkE;QAClE,mDAAmD;KACpD;IACD,UAAU,EAAE;QACV,0EAA0E;QAC1E,mDAAmD;QACnD,yCAAyC;KAC1C;IACD,SAAS,EAAE;QACT,kEAAkE;QAClE,4DAA4D;QAC5D,kDAAkD;KACnD;CACF,CAAC;AAEF,MAAM,mBAAmB,GAAwC;IAC/D,MAAM,EAAE,QAAQ;IAChB,UAAU,EAAE,YAAY;IACxB,YAAY,EAAE,cAAc;IAC5B,UAAU,EAAE,YAAY;IACxB,QAAQ,EAAE,UAAU;IACpB,UAAU,EAAE,YAAY;IACxB,SAAS,EAAE,WAAW;CACvB,CAAC;AAEF,MAAM,OAAO,eAAe;IAC1B,wDAAwD;IACxD,MAAM,CAAC,OAAe;QACpB,IAAI,QAAQ,GAAgB,WAAW,CAAC;QACxC,IAAI,SAAS,GAAG,CAAC,CAAC;QAElB,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAA8B,EAAE,CAAC;YACrF,IAAI,KAAK,GAAG,CAAC,CAAC;YACd,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;gBAC/B,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;oBAC1B,KAAK,EAAE,CAAC;gBACV,CAAC;YACH,CAAC;YACD,IAAI,KAAK,GAAG,SAAS,EAAE,CAAC;gBACtB,SAAS,GAAG,KAAK,CAAC;gBAClB,QAAQ,GAAG,IAAI,CAAC;YAClB,CAAC;QACH,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,yDAAyD;IACzD,eAAe,CAAC,OAAe,EAAE,IAAiB;QAChD,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QAChC,IAAI,CAAC,QAAQ;YAAE,OAAO,GAAG,CAAC;QAE1B,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;gBAAE,OAAO,EAAE,CAAC;QACvC,CAAC;QAED,uCAAuC;QACvC,MAAM,UAAU,GAAG,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC;QAE7C,2DAA2D;QAC3D,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,GAAG,GAAG,EAAE,GAAG,CAAC,CAAC;QAExD,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,UAAU,GAAG,GAAG,GAAG,WAAW,EAAE,GAAG,CAAC,CAAC;IAC7D,CAAC;IAED,6CAA6C;IAC7C,UAAU,CAAC,IAAiB;QAC1B,OAAO,mBAAmB,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;CACF"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { MemoryDatabase } from '../store/database.js';
|
|
2
|
+
import type { TeamLensConfig, Session, ActivityType, Memory } from '../types.js';
|
|
3
|
+
import type { TeamSync } from '../sync/team-sync.js';
|
|
4
|
+
import type { EmbeddingProvider } from '../store/embeddings.js';
|
|
5
|
+
export declare class SessionManager {
|
|
6
|
+
private db;
|
|
7
|
+
private config;
|
|
8
|
+
private teamSync;
|
|
9
|
+
private embeddings;
|
|
10
|
+
private currentSessionId;
|
|
11
|
+
private sessionJustCreated;
|
|
12
|
+
private insightDetector;
|
|
13
|
+
constructor(db: MemoryDatabase, config: TeamLensConfig, teamSync: TeamSync, embeddings: EmbeddingProvider);
|
|
14
|
+
/** Get or create an active session. Auto-session pattern. */
|
|
15
|
+
getOrCreateSession(toolName?: string): Session;
|
|
16
|
+
/** Check if the last getOrCreateSession call created a new session. */
|
|
17
|
+
wasSessionJustCreated(): boolean;
|
|
18
|
+
/** Explicitly start a session with task context. */
|
|
19
|
+
startSession(task?: string, toolName?: string): Session;
|
|
20
|
+
/** End a session with optional summary. */
|
|
21
|
+
endSession(sessionId?: string, summary?: string): Session | null;
|
|
22
|
+
/** Share an insight with the team. Auto-detects type, auto-creates session if needed. */
|
|
23
|
+
shareInsight(content: string, relatedFiles?: string[], tags?: string[]): Promise<{
|
|
24
|
+
memoryId: string;
|
|
25
|
+
insightType: string;
|
|
26
|
+
sessionId: string;
|
|
27
|
+
}>;
|
|
28
|
+
/** Log an activity event. Auto-creates session if needed. */
|
|
29
|
+
logActivity(type: ActivityType, description?: string, files?: string[]): {
|
|
30
|
+
eventId: string;
|
|
31
|
+
sessionId: string;
|
|
32
|
+
};
|
|
33
|
+
/** Add a file to the session's files_touched list. */
|
|
34
|
+
trackFile(sessionId: string, filePath: string): void;
|
|
35
|
+
/** Close stale sessions that have been active too long. */
|
|
36
|
+
cleanupStaleSessions(timeoutMinutes?: number): number;
|
|
37
|
+
/** Get the current active session. */
|
|
38
|
+
getActiveSession(): Session | null;
|
|
39
|
+
/** Get team context for injection at session start. */
|
|
40
|
+
getTeamContext(limit?: number): Memory[];
|
|
41
|
+
/**
|
|
42
|
+
* Ingest hook events from .teamlens/hooks.jsonl into the database.
|
|
43
|
+
* Called by the MCP server before each tool handler to pick up
|
|
44
|
+
* activity logged by Claude Code PostToolUse hooks.
|
|
45
|
+
*/
|
|
46
|
+
ingestHookEvents(repoPath: string): number;
|
|
47
|
+
private createSession;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=session-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../../src/session/session-manager.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,OAAO,EAAiB,YAAY,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAEhG,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhE,qBAAa,cAAc;IAMvB,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,UAAU;IARpB,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,eAAe,CAAkB;gBAG/B,EAAE,EAAE,cAAc,EAClB,MAAM,EAAE,cAAc,EACtB,QAAQ,EAAE,QAAQ,EAClB,UAAU,EAAE,iBAAiB;IAKvC,6DAA6D;IAC7D,kBAAkB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO;IAgB9C,uEAAuE;IACvE,qBAAqB,IAAI,OAAO;IAIhC,oDAAoD;IACpD,YAAY,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO;IAavD,2CAA2C;IAC3C,UAAU,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI;IAahE,yFAAyF;IACnF,YAAY,CAChB,OAAO,EAAE,MAAM,EACf,YAAY,GAAE,MAAM,EAAO,EAC3B,IAAI,GAAE,MAAM,EAAO,GAClB,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAqDxE,6DAA6D;IAC7D,WAAW,CACT,IAAI,EAAE,YAAY,EAClB,WAAW,GAAE,MAAW,EACxB,KAAK,GAAE,MAAM,EAAO,GACnB;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE;IAyBzC,sDAAsD;IACtD,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAYpD,2DAA2D;IAC3D,oBAAoB,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,MAAM;IAKrD,sCAAsC;IACtC,gBAAgB,IAAI,OAAO,GAAG,IAAI;IAQlC,uDAAuD;IACvD,cAAc,CAAC,KAAK,SAAK,GAAG,MAAM,EAAE;IAIpC;;;;OAIG;IACH,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAuD1C,OAAO,CAAC,aAAa;CA0BtB"}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { InsightDetector } from './insight-detector.js';
|
|
5
|
+
export class SessionManager {
|
|
6
|
+
db;
|
|
7
|
+
config;
|
|
8
|
+
teamSync;
|
|
9
|
+
embeddings;
|
|
10
|
+
currentSessionId = null;
|
|
11
|
+
sessionJustCreated = false;
|
|
12
|
+
insightDetector;
|
|
13
|
+
constructor(db, config, teamSync, embeddings) {
|
|
14
|
+
this.db = db;
|
|
15
|
+
this.config = config;
|
|
16
|
+
this.teamSync = teamSync;
|
|
17
|
+
this.embeddings = embeddings;
|
|
18
|
+
this.insightDetector = new InsightDetector();
|
|
19
|
+
}
|
|
20
|
+
/** Get or create an active session. Auto-session pattern. */
|
|
21
|
+
getOrCreateSession(toolName) {
|
|
22
|
+
// Check for existing active session
|
|
23
|
+
const developer = this.config.developer !== 'unknown' ? this.config.developer : this.config.author;
|
|
24
|
+
const existing = this.db.getActiveSession(developer);
|
|
25
|
+
if (existing) {
|
|
26
|
+
this.currentSessionId = existing.id;
|
|
27
|
+
this.sessionJustCreated = false;
|
|
28
|
+
return existing;
|
|
29
|
+
}
|
|
30
|
+
// Create new session
|
|
31
|
+
const session = this.createSession(toolName);
|
|
32
|
+
this.sessionJustCreated = true;
|
|
33
|
+
return session;
|
|
34
|
+
}
|
|
35
|
+
/** Check if the last getOrCreateSession call created a new session. */
|
|
36
|
+
wasSessionJustCreated() {
|
|
37
|
+
return this.sessionJustCreated;
|
|
38
|
+
}
|
|
39
|
+
/** Explicitly start a session with task context. */
|
|
40
|
+
startSession(task, toolName) {
|
|
41
|
+
const developer = this.config.developer !== 'unknown' ? this.config.developer : this.config.author;
|
|
42
|
+
// Close any existing active session
|
|
43
|
+
const existing = this.db.getActiveSession(developer);
|
|
44
|
+
if (existing) {
|
|
45
|
+
this.db.closeSession(existing.id);
|
|
46
|
+
}
|
|
47
|
+
const session = this.createSession(toolName, task);
|
|
48
|
+
return session;
|
|
49
|
+
}
|
|
50
|
+
/** End a session with optional summary. */
|
|
51
|
+
endSession(sessionId, summary) {
|
|
52
|
+
const id = sessionId ?? this.currentSessionId;
|
|
53
|
+
if (!id)
|
|
54
|
+
return null;
|
|
55
|
+
this.db.closeSession(id, summary);
|
|
56
|
+
if (id === this.currentSessionId) {
|
|
57
|
+
this.currentSessionId = null;
|
|
58
|
+
}
|
|
59
|
+
return this.db.getSession(id);
|
|
60
|
+
}
|
|
61
|
+
/** Share an insight with the team. Auto-detects type, auto-creates session if needed. */
|
|
62
|
+
async shareInsight(content, relatedFiles = [], tags = []) {
|
|
63
|
+
// Ensure session exists
|
|
64
|
+
const session = this.getOrCreateSession();
|
|
65
|
+
// Auto-detect insight type
|
|
66
|
+
const insightType = this.insightDetector.detect(content);
|
|
67
|
+
const category = this.insightDetector.toCategory(insightType);
|
|
68
|
+
const confidence = this.insightDetector.scoreConfidence(content, insightType);
|
|
69
|
+
// Store as team memory with session context
|
|
70
|
+
const memory = this.db.insertMemory({
|
|
71
|
+
content,
|
|
72
|
+
category,
|
|
73
|
+
relatedFiles,
|
|
74
|
+
tags: [...tags, insightType],
|
|
75
|
+
confidence,
|
|
76
|
+
commitSha: null,
|
|
77
|
+
}, this.config.developer !== 'unknown' ? this.config.developer : this.config.author, 'team', session.id);
|
|
78
|
+
// Generate embedding
|
|
79
|
+
const embedding = await this.embeddings.embed(content);
|
|
80
|
+
if (embedding) {
|
|
81
|
+
this.db.updateEmbedding(memory.id, embedding);
|
|
82
|
+
}
|
|
83
|
+
// Update session insight count
|
|
84
|
+
this.db.updateSession(session.id, {
|
|
85
|
+
insightCount: session.insightCount + 1,
|
|
86
|
+
});
|
|
87
|
+
// Export to team.jsonl (append-only, not full rewrite)
|
|
88
|
+
this.teamSync.appendMemory(memory);
|
|
89
|
+
// Auto-commit and push so teammates get it immediately
|
|
90
|
+
this.teamSync.autoCommitAndPush();
|
|
91
|
+
// Track related files
|
|
92
|
+
for (const file of relatedFiles) {
|
|
93
|
+
this.trackFile(session.id, file);
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
memoryId: memory.id,
|
|
97
|
+
insightType,
|
|
98
|
+
sessionId: session.id,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/** Log an activity event. Auto-creates session if needed. */
|
|
102
|
+
logActivity(type, description = '', files = []) {
|
|
103
|
+
const session = this.getOrCreateSession();
|
|
104
|
+
const event = {
|
|
105
|
+
id: randomUUID(),
|
|
106
|
+
sessionId: session.id,
|
|
107
|
+
type,
|
|
108
|
+
description,
|
|
109
|
+
files,
|
|
110
|
+
timestamp: new Date().toISOString(),
|
|
111
|
+
};
|
|
112
|
+
this.db.insertActivityEvent(event);
|
|
113
|
+
// Track files
|
|
114
|
+
for (const file of files) {
|
|
115
|
+
this.trackFile(session.id, file);
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
eventId: event.id,
|
|
119
|
+
sessionId: session.id,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/** Add a file to the session's files_touched list. */
|
|
123
|
+
trackFile(sessionId, filePath) {
|
|
124
|
+
const session = this.db.getSession(sessionId);
|
|
125
|
+
if (!session)
|
|
126
|
+
return;
|
|
127
|
+
if (!session.filesTouched.includes(filePath)) {
|
|
128
|
+
session.filesTouched.push(filePath);
|
|
129
|
+
this.db.updateSession(sessionId, {
|
|
130
|
+
filesTouched: session.filesTouched,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/** Close stale sessions that have been active too long. */
|
|
135
|
+
cleanupStaleSessions(timeoutMinutes) {
|
|
136
|
+
const timeout = timeoutMinutes ?? this.config.sessionTimeoutMinutes;
|
|
137
|
+
return this.db.cleanupStaleSessions(timeout);
|
|
138
|
+
}
|
|
139
|
+
/** Get the current active session. */
|
|
140
|
+
getActiveSession() {
|
|
141
|
+
if (this.currentSessionId) {
|
|
142
|
+
return this.db.getSession(this.currentSessionId);
|
|
143
|
+
}
|
|
144
|
+
const developer = this.config.developer !== 'unknown' ? this.config.developer : this.config.author;
|
|
145
|
+
return this.db.getActiveSession(developer);
|
|
146
|
+
}
|
|
147
|
+
/** Get team context for injection at session start. */
|
|
148
|
+
getTeamContext(limit = 10) {
|
|
149
|
+
return this.db.getRecentInsights(limit);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Ingest hook events from .teamlens/hooks.jsonl into the database.
|
|
153
|
+
* Called by the MCP server before each tool handler to pick up
|
|
154
|
+
* activity logged by Claude Code PostToolUse hooks.
|
|
155
|
+
*/
|
|
156
|
+
ingestHookEvents(repoPath) {
|
|
157
|
+
const hooksFile = path.join(repoPath, this.config.storageDir, 'hooks.jsonl');
|
|
158
|
+
if (!fs.existsSync(hooksFile))
|
|
159
|
+
return 0;
|
|
160
|
+
const content = fs.readFileSync(hooksFile, 'utf-8').trim();
|
|
161
|
+
if (!content)
|
|
162
|
+
return 0;
|
|
163
|
+
const lines = content.split('\n').filter(Boolean);
|
|
164
|
+
if (lines.length === 0)
|
|
165
|
+
return 0;
|
|
166
|
+
const session = this.getOrCreateSession();
|
|
167
|
+
let ingested = 0;
|
|
168
|
+
for (const line of lines) {
|
|
169
|
+
try {
|
|
170
|
+
const hookEvent = JSON.parse(line);
|
|
171
|
+
const event = {
|
|
172
|
+
id: randomUUID(),
|
|
173
|
+
sessionId: session.id,
|
|
174
|
+
type: hookEvent.type ?? 'other',
|
|
175
|
+
description: hookEvent.description ?? hookEvent.tool ?? '',
|
|
176
|
+
files: hookEvent.files ?? [],
|
|
177
|
+
timestamp: hookEvent.timestamp ?? new Date().toISOString(),
|
|
178
|
+
};
|
|
179
|
+
this.db.insertActivityEvent(event);
|
|
180
|
+
// Track files
|
|
181
|
+
for (const file of event.files) {
|
|
182
|
+
this.trackFile(session.id, file);
|
|
183
|
+
}
|
|
184
|
+
ingested++;
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
// Skip malformed lines
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
// Update session activity count
|
|
191
|
+
if (ingested > 0) {
|
|
192
|
+
const updated = this.db.getSession(session.id);
|
|
193
|
+
if (updated) {
|
|
194
|
+
this.db.updateSession(session.id, {
|
|
195
|
+
activityCount: updated.activityCount + ingested,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// Clear the hooks file after ingestion
|
|
200
|
+
fs.writeFileSync(hooksFile, '');
|
|
201
|
+
return ingested;
|
|
202
|
+
}
|
|
203
|
+
// ── Private ──
|
|
204
|
+
createSession(toolName, task) {
|
|
205
|
+
const developer = this.config.developer !== 'unknown' ? this.config.developer : this.config.author;
|
|
206
|
+
const session = {
|
|
207
|
+
id: randomUUID(),
|
|
208
|
+
developer,
|
|
209
|
+
task: task ?? '',
|
|
210
|
+
status: 'active',
|
|
211
|
+
toolName: toolName ?? 'unknown',
|
|
212
|
+
startedAt: new Date().toISOString(),
|
|
213
|
+
endedAt: null,
|
|
214
|
+
durationSeconds: null,
|
|
215
|
+
filesTouched: [],
|
|
216
|
+
summary: null,
|
|
217
|
+
insightCount: 0,
|
|
218
|
+
activityCount: 0,
|
|
219
|
+
duplicatesPrevented: 0,
|
|
220
|
+
};
|
|
221
|
+
this.db.insertSession(session);
|
|
222
|
+
this.currentSessionId = session.id;
|
|
223
|
+
// Cleanup any stale sessions
|
|
224
|
+
this.cleanupStaleSessions();
|
|
225
|
+
return session;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
//# sourceMappingURL=session-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-manager.js","sourceRoot":"","sources":["../../src/session/session-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { MemoryDatabase } from '../store/database.js';
|
|
2
|
+
import type { GitExtractor } from '../extractor/git-extractor.js';
|
|
3
|
+
import type { StalenessCheck, Memory } from '../types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Staleness Engine — auto-invalidates memories when referenced files change.
|
|
6
|
+
*
|
|
7
|
+
* On each git event:
|
|
8
|
+
* 1. Get changed files from diff
|
|
9
|
+
* 2. Find memories that reference those files
|
|
10
|
+
* 3. Score staleness based on change magnitude
|
|
11
|
+
* 4. Update memory staleness scores
|
|
12
|
+
*
|
|
13
|
+
* Stale memories are downranked in retrieval, not deleted.
|
|
14
|
+
*/
|
|
15
|
+
export declare class StalenessEngine {
|
|
16
|
+
private db;
|
|
17
|
+
private git;
|
|
18
|
+
constructor(db: MemoryDatabase, git: GitExtractor);
|
|
19
|
+
/** Check all memories against current file states. Returns list of changes made. */
|
|
20
|
+
checkAll(): Promise<StalenessCheck[]>;
|
|
21
|
+
/** Check a single memory against its referenced files. */
|
|
22
|
+
checkMemory(memory: Memory): Promise<StalenessCheck[]>;
|
|
23
|
+
/** Process a set of changed files (e.g., from a git hook). */
|
|
24
|
+
processChangedFiles(changedFiles: string[]): Promise<StalenessCheck[]>;
|
|
25
|
+
private applyStale;
|
|
26
|
+
/**
|
|
27
|
+
* Estimate how much a file change should affect staleness.
|
|
28
|
+
*
|
|
29
|
+
* Heuristic based on file name patterns:
|
|
30
|
+
* - Config files changing → high impact (0.7)
|
|
31
|
+
* - Test files changing → low impact (0.1)
|
|
32
|
+
* - Source files → medium (0.3–0.5)
|
|
33
|
+
*/
|
|
34
|
+
private estimateChangeMagnitude;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=staleness-engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"staleness-engine.d.ts","sourceRoot":"","sources":["../../src/staleness/staleness-engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1D;;;;;;;;;;GAUG;AACH,qBAAa,eAAe;IAExB,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,GAAG;gBADH,EAAE,EAAE,cAAc,EAClB,GAAG,EAAE,YAAY;IAG3B,oFAAoF;IAC9E,QAAQ,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAY3C,0DAA0D;IACpD,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IA4C5D,8DAA8D;IACxD,mBAAmB,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAqC5E,OAAO,CAAC,UAAU;IAoBlB;;;;;;;OAOG;YACW,uBAAuB;CAiCtC"}
|