memorix 1.1.9 → 1.1.10
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 +6 -0
- package/dist/cli/index.js +174 -34
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +157 -33
- package/dist/index.js.map +1 -1
- package/dist/memcode-runtime/CHANGELOG.md +6 -0
- package/dist/memcode-runtime/package.json +4 -4
- package/dist/sdk.js +157 -33
- package/dist/sdk.js.map +1 -1
- package/docs/dev-log/progress.txt +21 -18
- package/package.json +1 -1
- package/src/codegraph/auto-context.ts +1 -7
- package/src/codegraph/binder.ts +109 -16
- package/src/codegraph/lite-provider.ts +2 -2
- package/src/codegraph/project-context.ts +35 -12
- package/src/codegraph/store.ts +59 -0
|
@@ -4,32 +4,35 @@
|
|
|
4
4
|
> exist under subdirectories; prefer this file for repository-wide state.
|
|
5
5
|
|
|
6
6
|
## Current State
|
|
7
|
-
- Phase: 1.1.
|
|
8
|
-
- Branch:
|
|
9
|
-
-
|
|
10
|
-
- Last updated: 2026-07-
|
|
11
|
-
- Goal:
|
|
7
|
+
- Phase: 1.1.10 emergency patch release
|
|
8
|
+
- Branch: codex/issue-124-project-context-timeout
|
|
9
|
+
- Issue: #124
|
|
10
|
+
- Last updated: 2026-07-13
|
|
11
|
+
- Goal: release the large-project `memorix_project_context` timeout fix and follow up with the reporter.
|
|
12
12
|
|
|
13
13
|
## Completed Recently
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
14
|
+
- Reproduced #124 with 3308 active memories and confirmed the old refresh path exceeded the MCP 60-second timeout.
|
|
15
|
+
- Replaced broad per-observation symbol scans with indexed exact-name lookup and bulk existing-ref reads.
|
|
16
|
+
- Changed first-use backfill to load one project graph snapshot, match all missing observations in memory, and persist refs in one transaction.
|
|
17
|
+
- Reused one graph snapshot per project-context request and batched project refs/referenced symbols.
|
|
18
|
+
- Prevented prose-only and ambiguous symbol mentions from creating large volumes of noisy code refs.
|
|
19
|
+
- Preserved Ruby namespace and punctuated method names through Lite indexing and both binding paths.
|
|
20
|
+
- Bumped root and workspace metadata to `1.1.10`.
|
|
19
21
|
|
|
20
22
|
## Current Follow-Up
|
|
21
|
-
-
|
|
23
|
+
- Commit, publish `memorix@1.1.10`, create the GitHub release, and reply to #124 with the upgrade path.
|
|
22
24
|
|
|
23
25
|
## Verification Snapshot
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
26
|
+
- TDD regression coverage passes for CodeGraph store, binder, project context, and auto context.
|
|
27
|
+
- A copied real database with all refs removed improved from 57.9 seconds for 173 active memories to 12.4 seconds.
|
|
28
|
+
- A scaled 3308-active first-use refresh completed in 13.3 seconds, including a forced code scan and full code-ref backfill.
|
|
29
|
+
- Built stdio MCP smoke returned `memorix_project_context` successfully in 5.79 seconds.
|
|
30
|
+
- Final release verification: `git diff --check`, `npm run lint`, `npm run build`, `npm test`, package dry-run, and clean-install npm smoke.
|
|
27
31
|
|
|
28
32
|
## Known UX Notes
|
|
29
|
-
-
|
|
30
|
-
- `memorix context --refresh never --task "<task>"` is fast and stable for first-contact context. `--refresh auto` can take longer on this repository.
|
|
33
|
+
- Before 1.1.10, `refresh: "never"` is a temporary workaround for very large active-memory corpora.
|
|
31
34
|
- Current code and release facts outrank stale handoff notes or older dev logs when they conflict.
|
|
32
35
|
|
|
33
36
|
## Next Steps
|
|
34
|
-
- Commit and push the 1.1.
|
|
35
|
-
- Tag
|
|
37
|
+
- Commit and push the 1.1.10 release patch.
|
|
38
|
+
- Tag, publish the root package, create the GitHub release, and reply to #124.
|
package/package.json
CHANGED
|
@@ -7,7 +7,6 @@ import { collectCurrentProjectFacts, type CurrentProjectFacts } from './current-
|
|
|
7
7
|
import { indexProjectLite } from './lite-provider.js';
|
|
8
8
|
import {
|
|
9
9
|
buildProjectContextExplain,
|
|
10
|
-
buildProjectContextOverview,
|
|
11
10
|
type ProjectContextExplain,
|
|
12
11
|
type ProjectContextObservation,
|
|
13
12
|
type ProjectContextOverview,
|
|
@@ -147,18 +146,13 @@ export async function buildAutoProjectContext(input: {
|
|
|
147
146
|
}
|
|
148
147
|
}
|
|
149
148
|
|
|
150
|
-
const overview = buildProjectContextOverview({
|
|
151
|
-
project: input.project,
|
|
152
|
-
store,
|
|
153
|
-
observations: input.observations,
|
|
154
|
-
exclude,
|
|
155
|
-
});
|
|
156
149
|
const explain = buildProjectContextExplain({
|
|
157
150
|
project: input.project,
|
|
158
151
|
store,
|
|
159
152
|
observations: input.observations,
|
|
160
153
|
exclude,
|
|
161
154
|
});
|
|
155
|
+
const overview = explain.overview;
|
|
162
156
|
|
|
163
157
|
return {
|
|
164
158
|
project: input.project,
|
package/src/codegraph/binder.ts
CHANGED
|
@@ -27,6 +27,24 @@ function mentionsSymbol(text: string, symbol: CodeSymbol): boolean {
|
|
|
27
27
|
return new RegExp(`(^|[^\\w$])${name}([^\\w$]|$)`).test(text);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
function identifierCandidates(text: string): string[] {
|
|
31
|
+
return [...new Set(text.match(/[A-Za-z_$][\w$]*(?:::[A-Za-z_$][\w$]*)*[!?=]?/g) ?? [])];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function codeIdentifierCandidates(text: string): string[] {
|
|
35
|
+
const explicit = new Set<string>();
|
|
36
|
+
for (const match of text.matchAll(/`([A-Za-z_$][\w$]*(?:::[A-Za-z_$][\w$]*)*[!?=]?)`/g)) explicit.add(match[1]);
|
|
37
|
+
for (const match of text.matchAll(/\b([A-Za-z_$][\w$]*(?:::[A-Za-z_$][\w$]*)*[!?=]?)\s*\(/g)) explicit.add(match[1]);
|
|
38
|
+
return identifierCandidates(text).filter(candidate =>
|
|
39
|
+
explicit.has(candidate) ||
|
|
40
|
+
candidate.includes('::') ||
|
|
41
|
+
/[!?=]$/.test(candidate) ||
|
|
42
|
+
/^[A-Z]/.test(candidate) ||
|
|
43
|
+
/[_$]/.test(candidate) ||
|
|
44
|
+
/[a-z0-9][A-Z]/.test(candidate) ||
|
|
45
|
+
/[A-Z]{2}/.test(candidate));
|
|
46
|
+
}
|
|
47
|
+
|
|
30
48
|
function fileRef(projectId: string, obs: BindableObservation, file: CodeFile): ObservationCodeRef {
|
|
31
49
|
return {
|
|
32
50
|
id: makeObservationCodeRefId(projectId, obs.id, file.id),
|
|
@@ -55,35 +73,89 @@ function symbolRef(projectId: string, obs: BindableObservation, file: CodeFile,
|
|
|
55
73
|
};
|
|
56
74
|
}
|
|
57
75
|
|
|
58
|
-
|
|
59
|
-
|
|
76
|
+
interface CodeBindingLookup {
|
|
77
|
+
findFile(path: string): CodeFile | undefined;
|
|
78
|
+
findSymbols(names: string[], hintedFileIds: string[]): CodeSymbol[];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function resolveObservationCodeRefs(
|
|
60
82
|
obs: BindableObservation,
|
|
61
|
-
|
|
83
|
+
lookup: CodeBindingLookup,
|
|
84
|
+
): ObservationCodeRef[] {
|
|
62
85
|
const refs = new Map<string, ObservationCodeRef>();
|
|
63
86
|
const text = observationText(obs);
|
|
64
87
|
const candidateFiles = new Map<string, CodeFile>();
|
|
65
88
|
|
|
66
89
|
for (const rawPath of obs.filesModified ?? []) {
|
|
67
|
-
const file =
|
|
90
|
+
const file = lookup.findFile(normalizeCodePath(rawPath));
|
|
68
91
|
if (!file) continue;
|
|
69
92
|
candidateFiles.set(file.id, file);
|
|
70
93
|
const ref = fileRef(obs.projectId, obs, file);
|
|
71
94
|
refs.set(ref.id, ref);
|
|
72
95
|
}
|
|
73
96
|
|
|
74
|
-
const
|
|
97
|
+
const hintedFileIds = new Set(candidateFiles.keys());
|
|
98
|
+
const symbolNames = hintedFileIds.size > 0 ? identifierCandidates(text) : codeIdentifierCandidates(text);
|
|
99
|
+
const symbols = lookup.findSymbols(symbolNames, [...hintedFileIds]);
|
|
100
|
+
const symbolsByName = new Map<string, CodeSymbol[]>();
|
|
75
101
|
for (const symbol of symbols) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
102
|
+
const group = symbolsByName.get(symbol.name) ?? [];
|
|
103
|
+
group.push(symbol);
|
|
104
|
+
symbolsByName.set(symbol.name, group);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
for (const group of symbolsByName.values()) {
|
|
108
|
+
const candidates = group.length === 1 ? group : [];
|
|
109
|
+
for (const symbol of candidates) {
|
|
110
|
+
if (!mentionsSymbol(text, symbol)) continue;
|
|
111
|
+
const file = candidateFiles.get(symbol.fileId) ?? lookup.findFile(symbol.path);
|
|
112
|
+
if (!file) continue;
|
|
113
|
+
candidateFiles.set(file.id, file);
|
|
114
|
+
const ref = symbolRef(obs.projectId, obs, file, symbol);
|
|
115
|
+
refs.set(ref.id, ref);
|
|
116
|
+
}
|
|
82
117
|
}
|
|
83
118
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
119
|
+
return [...refs.values()];
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function createStoreLookup(store: CodeGraphStore, projectId: string): CodeBindingLookup {
|
|
123
|
+
return {
|
|
124
|
+
findFile: path => store.getFile(projectId, path) ?? undefined,
|
|
125
|
+
findSymbols: (names, hintedFileIds) => store.findSymbolsByNames(projectId, names, hintedFileIds),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function createSnapshotLookup(files: CodeFile[], symbols: CodeSymbol[]): CodeBindingLookup {
|
|
130
|
+
const filesByPath = new Map(files.map(file => [normalizeCodePath(file.path), file]));
|
|
131
|
+
const symbolsByName = new Map<string, CodeSymbol[]>();
|
|
132
|
+
for (const symbol of symbols) {
|
|
133
|
+
const group = symbolsByName.get(symbol.name) ?? [];
|
|
134
|
+
group.push(symbol);
|
|
135
|
+
symbolsByName.set(symbol.name, group);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
findFile: path => filesByPath.get(normalizeCodePath(path)),
|
|
140
|
+
findSymbols: (names, hintedFileIds) => {
|
|
141
|
+
const hinted = new Set(hintedFileIds);
|
|
142
|
+
const found: CodeSymbol[] = [];
|
|
143
|
+
for (const name of names) {
|
|
144
|
+
const candidates = symbolsByName.get(name) ?? [];
|
|
145
|
+
found.push(...(hinted.size > 0 ? candidates.filter(symbol => hinted.has(symbol.fileId)) : candidates));
|
|
146
|
+
}
|
|
147
|
+
return found;
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export async function bindObservationToCode(
|
|
153
|
+
store: CodeGraphStore,
|
|
154
|
+
obs: BindableObservation,
|
|
155
|
+
): Promise<ObservationCodeRef[]> {
|
|
156
|
+
const refs = resolveObservationCodeRefs(obs, createStoreLookup(store, obs.projectId));
|
|
157
|
+
store.replaceObservationRefs(obs.projectId, obs.id, refs);
|
|
158
|
+
return refs;
|
|
87
159
|
}
|
|
88
160
|
|
|
89
161
|
export async function backfillMissingObservationCodeRefs(
|
|
@@ -92,14 +164,35 @@ export async function backfillMissingObservationCodeRefs(
|
|
|
92
164
|
): Promise<CodeRefBackfillResult> {
|
|
93
165
|
let observationsBackfilled = 0;
|
|
94
166
|
let refsBackfilled = 0;
|
|
167
|
+
const boundObservationIdsByProject = new Map<string, Set<number>>();
|
|
168
|
+
const lookupByProject = new Map<string, CodeBindingLookup>();
|
|
169
|
+
|
|
170
|
+
for (const projectId of new Set(observations.map(observation => observation.projectId))) {
|
|
171
|
+
boundObservationIdsByProject.set(
|
|
172
|
+
projectId,
|
|
173
|
+
new Set(store.listProjectObservationRefs(projectId).map(ref => ref.observationId)),
|
|
174
|
+
);
|
|
175
|
+
lookupByProject.set(
|
|
176
|
+
projectId,
|
|
177
|
+
createSnapshotLookup(store.listFiles(projectId), store.listSymbols(projectId)),
|
|
178
|
+
);
|
|
179
|
+
}
|
|
95
180
|
|
|
181
|
+
const refsToInsert: ObservationCodeRef[] = [];
|
|
96
182
|
for (const observation of observations) {
|
|
97
|
-
if (
|
|
98
|
-
|
|
183
|
+
if (boundObservationIdsByProject.get(observation.projectId)?.has(observation.id)) continue;
|
|
184
|
+
if ((observation.filesModified?.length ?? 0) === 0 && codeIdentifierCandidates(observationText(observation)).length === 0) {
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
const lookup = lookupByProject.get(observation.projectId);
|
|
188
|
+
if (!lookup) continue;
|
|
189
|
+
const refs = resolveObservationCodeRefs(observation, lookup);
|
|
99
190
|
if (refs.length === 0) continue;
|
|
100
191
|
observationsBackfilled += 1;
|
|
101
192
|
refsBackfilled += refs.length;
|
|
193
|
+
refsToInsert.push(...refs);
|
|
102
194
|
}
|
|
195
|
+
store.upsertObservationRefs(refsToInsert);
|
|
103
196
|
|
|
104
197
|
return {
|
|
105
198
|
observationsScanned: observations.length,
|
|
@@ -141,8 +141,8 @@ const languageProfiles: Record<string, LanguageProfile> = {
|
|
|
141
141
|
},
|
|
142
142
|
ruby: {
|
|
143
143
|
symbols: [
|
|
144
|
-
{ kind: 'class', re: /^class\s+([A-Za-z_][\w
|
|
145
|
-
{ kind: 'function', re: /^def\s+([A-Za-z_][\w!?=]
|
|
144
|
+
{ kind: 'class', re: /^class\s+([A-Za-z_][\w]*(?:::[A-Za-z_][\w]*)*)/gm },
|
|
145
|
+
{ kind: 'function', re: /^def\s+([A-Za-z_][\w]*[!?=]?)/gm },
|
|
146
146
|
],
|
|
147
147
|
imports: [/require\s+['"]([^'"]+)['"]/g],
|
|
148
148
|
},
|
|
@@ -107,11 +107,13 @@ function collectGraph(
|
|
|
107
107
|
suggestedReads: string[];
|
|
108
108
|
} {
|
|
109
109
|
const files = store.listFiles(projectId);
|
|
110
|
-
const symbols =
|
|
110
|
+
const symbols = store.listReferencedSymbols(projectId);
|
|
111
111
|
const filesById = new Map(files.map(file => [file.id, file]));
|
|
112
112
|
const symbolsById = new Map(symbols.map(symbol => [symbol.id, symbol]));
|
|
113
113
|
const observationsById = new Map(observations.map(obs => [obs.id, obs]));
|
|
114
|
-
const
|
|
114
|
+
const activeObservationIds = new Set(observations.map(obs => obs.id));
|
|
115
|
+
const refs = store.listProjectObservationRefs(projectId)
|
|
116
|
+
.filter(ref => activeObservationIds.has(ref.observationId));
|
|
115
117
|
const freshness: ProjectContextOverview['freshness'] = {
|
|
116
118
|
current: 0,
|
|
117
119
|
suspect: 0,
|
|
@@ -153,16 +155,14 @@ function collectGraph(
|
|
|
153
155
|
};
|
|
154
156
|
}
|
|
155
157
|
|
|
156
|
-
|
|
158
|
+
function overviewFromGraph(input: {
|
|
157
159
|
project: Pick<ProjectInfo, 'id' | 'name' | 'rootPath'>;
|
|
158
160
|
store: CodeGraphStore;
|
|
159
161
|
observations: ProjectContextObservation[];
|
|
160
|
-
|
|
162
|
+
active: ProjectContextObservation[];
|
|
163
|
+
graph: ReturnType<typeof collectGraph>;
|
|
161
164
|
}): ProjectContextOverview {
|
|
162
|
-
const active = activeObservations(input.observations, input.project.id);
|
|
163
|
-
const graph = collectGraph(input.store, input.project.id, active, input.exclude);
|
|
164
165
|
const status = input.store.status(input.project.id);
|
|
165
|
-
|
|
166
166
|
return {
|
|
167
167
|
project: input.project,
|
|
168
168
|
code: {
|
|
@@ -172,26 +172,49 @@ export function buildProjectContextOverview(input: {
|
|
|
172
172
|
edges: status.edges,
|
|
173
173
|
refs: status.refs,
|
|
174
174
|
...(status.indexedAt ? { indexedAt: status.indexedAt } : {}),
|
|
175
|
-
languages: countLanguages(graph.files),
|
|
175
|
+
languages: countLanguages(input.graph.files),
|
|
176
176
|
},
|
|
177
177
|
memory: {
|
|
178
178
|
total: input.observations.filter(obs => obs.projectId === input.project.id).length,
|
|
179
|
-
active: active.length,
|
|
179
|
+
active: input.active.length,
|
|
180
180
|
},
|
|
181
|
-
freshness: graph.freshness,
|
|
182
|
-
suggestedReads: graph.suggestedReads,
|
|
181
|
+
freshness: input.graph.freshness,
|
|
182
|
+
suggestedReads: input.graph.suggestedReads,
|
|
183
183
|
};
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
+
export function buildProjectContextOverview(input: {
|
|
187
|
+
project: Pick<ProjectInfo, 'id' | 'name' | 'rootPath'>;
|
|
188
|
+
store: CodeGraphStore;
|
|
189
|
+
observations: ProjectContextObservation[];
|
|
190
|
+
exclude?: string[];
|
|
191
|
+
}): ProjectContextOverview {
|
|
192
|
+
const active = activeObservations(input.observations, input.project.id);
|
|
193
|
+
const graph = collectGraph(input.store, input.project.id, active, input.exclude);
|
|
194
|
+
return overviewFromGraph({
|
|
195
|
+
project: input.project,
|
|
196
|
+
store: input.store,
|
|
197
|
+
observations: input.observations,
|
|
198
|
+
active,
|
|
199
|
+
graph,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
186
203
|
export function buildProjectContextExplain(input: {
|
|
187
204
|
project: Pick<ProjectInfo, 'id' | 'name' | 'rootPath'>;
|
|
188
205
|
store: CodeGraphStore;
|
|
189
206
|
observations: ProjectContextObservation[];
|
|
190
207
|
exclude?: string[];
|
|
191
208
|
}): ProjectContextExplain {
|
|
192
|
-
const overview = buildProjectContextOverview(input);
|
|
193
209
|
const active = activeObservations(input.observations, input.project.id);
|
|
194
210
|
const graph = collectGraph(input.store, input.project.id, active, input.exclude);
|
|
211
|
+
const overview = overviewFromGraph({
|
|
212
|
+
project: input.project,
|
|
213
|
+
store: input.store,
|
|
214
|
+
observations: input.observations,
|
|
215
|
+
active,
|
|
216
|
+
graph,
|
|
217
|
+
});
|
|
195
218
|
return {
|
|
196
219
|
project: input.project,
|
|
197
220
|
sources: graph.sources.sort((a, b) => a.observationId - b.observationId || (a.path ?? '').localeCompare(b.path ?? '')),
|
package/src/codegraph/store.ts
CHANGED
|
@@ -316,6 +316,47 @@ export class CodeGraphStore {
|
|
|
316
316
|
`).all(projectId, like, like, like, limit).map(rowToSymbol);
|
|
317
317
|
}
|
|
318
318
|
|
|
319
|
+
listSymbols(projectId: string): CodeSymbol[] {
|
|
320
|
+
return this.db.prepare(`
|
|
321
|
+
SELECT * FROM code_symbols
|
|
322
|
+
WHERE projectId = ? AND stale = 0
|
|
323
|
+
ORDER BY path, startLine
|
|
324
|
+
`).all(projectId).map(rowToSymbol);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
findSymbolsByNames(projectId: string, names: string[], fileIds: string[] = []): CodeSymbol[] {
|
|
328
|
+
const candidates = [...new Set(names.map(name => name.trim()).filter(Boolean))];
|
|
329
|
+
if (candidates.length === 0) return [];
|
|
330
|
+
const candidateJson = JSON.stringify(candidates);
|
|
331
|
+
const hintedFiles = [...new Set(fileIds.map(fileId => fileId.trim()).filter(Boolean))];
|
|
332
|
+
if (hintedFiles.length > 0) {
|
|
333
|
+
return this.db.prepare(`
|
|
334
|
+
SELECT * FROM code_symbols
|
|
335
|
+
WHERE projectId = ?
|
|
336
|
+
AND stale = 0
|
|
337
|
+
AND name IN (SELECT value FROM json_each(?))
|
|
338
|
+
AND fileId IN (SELECT value FROM json_each(?))
|
|
339
|
+
ORDER BY path, startLine
|
|
340
|
+
`).all(projectId, candidateJson, JSON.stringify(hintedFiles)).map(rowToSymbol);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
return this.db.prepare(`
|
|
344
|
+
SELECT symbols.*
|
|
345
|
+
FROM code_symbols AS symbols
|
|
346
|
+
INNER JOIN (
|
|
347
|
+
SELECT name
|
|
348
|
+
FROM code_symbols
|
|
349
|
+
WHERE projectId = ?
|
|
350
|
+
AND stale = 0
|
|
351
|
+
AND name IN (SELECT value FROM json_each(?))
|
|
352
|
+
GROUP BY name
|
|
353
|
+
HAVING COUNT(*) = 1
|
|
354
|
+
) AS unambiguous ON unambiguous.name = symbols.name
|
|
355
|
+
WHERE symbols.projectId = ? AND symbols.stale = 0
|
|
356
|
+
ORDER BY symbols.path, symbols.startLine
|
|
357
|
+
`).all(projectId, candidateJson, projectId).map(rowToSymbol);
|
|
358
|
+
}
|
|
359
|
+
|
|
319
360
|
listSymbolsForFile(fileId: string): CodeSymbol[] {
|
|
320
361
|
return this.db.prepare(`SELECT * FROM code_symbols WHERE fileId = ? AND stale = 0 ORDER BY startLine`).all(fileId).map(rowToSymbol);
|
|
321
362
|
}
|
|
@@ -332,6 +373,24 @@ export class CodeGraphStore {
|
|
|
332
373
|
`).all(projectId, observationId).map(rowToRef);
|
|
333
374
|
}
|
|
334
375
|
|
|
376
|
+
listProjectObservationRefs(projectId: string): ObservationCodeRef[] {
|
|
377
|
+
return this.db.prepare(`
|
|
378
|
+
SELECT * FROM observation_code_refs
|
|
379
|
+
WHERE projectId = ?
|
|
380
|
+
ORDER BY observationId, status, id
|
|
381
|
+
`).all(projectId).map(rowToRef);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
listReferencedSymbols(projectId: string): CodeSymbol[] {
|
|
385
|
+
return this.db.prepare(`
|
|
386
|
+
SELECT DISTINCT symbols.*
|
|
387
|
+
FROM code_symbols AS symbols
|
|
388
|
+
INNER JOIN observation_code_refs AS refs ON refs.symbolId = symbols.id
|
|
389
|
+
WHERE refs.projectId = ? AND symbols.projectId = ? AND symbols.stale = 0
|
|
390
|
+
ORDER BY symbols.path, symbols.startLine
|
|
391
|
+
`).all(projectId, projectId).map(rowToSymbol);
|
|
392
|
+
}
|
|
393
|
+
|
|
335
394
|
status(projectId: string): CodeGraphStatus {
|
|
336
395
|
const files = this.db.prepare(`SELECT COUNT(*) AS count FROM code_files WHERE projectId = ?`).get(projectId).count;
|
|
337
396
|
const symbols = this.db.prepare(`SELECT COUNT(*) AS count FROM code_symbols WHERE projectId = ? AND stale = 0`).get(projectId).count;
|