@kentwynn/kgraph 0.2.20 → 0.2.21
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/cli/commands/workflow.js +94 -20
- package/dist/config/config.js +1 -0
- package/package.json +1 -1
|
@@ -4,6 +4,7 @@ import { loadConfig } from '../../config/config.js';
|
|
|
4
4
|
import { queryContext } from '../../context/context-query.js';
|
|
5
5
|
import { refreshKnowledgeAtomStatuses } from '../../knowledge/atom-store.js';
|
|
6
6
|
import { getWorkingTreeChanges } from '../../scanner/git-utils.js';
|
|
7
|
+
import { shouldExclude } from '../../scanner/file-classifier.js';
|
|
7
8
|
import { scanRepository } from '../../scanner/repo-scanner.js';
|
|
8
9
|
import { listInboxNotes } from '../../storage/cognition-store.js';
|
|
9
10
|
import { assertWorkspace, pathExists, resolveWorkspace, } from '../../storage/kgraph-paths.js';
|
|
@@ -74,7 +75,7 @@ export async function runDefaultWorkflow(query, options = {}) {
|
|
|
74
75
|
const activeAtoms = atoms.filter((atom) => atom.status === 'active');
|
|
75
76
|
const captureCheck = await buildCaptureCheck(workspace.rootPath, {
|
|
76
77
|
topic,
|
|
77
|
-
previousFiles: previousMaps.fileMap.files,
|
|
78
|
+
previousFiles: previousMaps.fileMap.files.filter((file) => !shouldExclude(file.path, config)),
|
|
78
79
|
files: scan.files,
|
|
79
80
|
atoms,
|
|
80
81
|
});
|
|
@@ -107,7 +108,7 @@ export async function runDefaultWorkflow(query, options = {}) {
|
|
|
107
108
|
if (options.final) {
|
|
108
109
|
console.log('');
|
|
109
110
|
renderFinalCaptureCheck(captureCheck, topic);
|
|
110
|
-
if (captureCheck.required) {
|
|
111
|
+
if (captureCheck.required || captureCheck.unresolvedAtoms.length > 0) {
|
|
111
112
|
process.exitCode = 1;
|
|
112
113
|
}
|
|
113
114
|
return;
|
|
@@ -146,6 +147,11 @@ async function buildCaptureCheck(rootPath, input) {
|
|
|
146
147
|
return Number.isFinite(createdAt) && createdAt >= recentCutoff;
|
|
147
148
|
});
|
|
148
149
|
const invalidatedAtoms = matchingInvalidatedAtoms(input.atoms, input.topic).filter((atom) => !isInvalidatedAtomCovered(atom, recentActiveAtoms));
|
|
150
|
+
const unresolvedAtoms = input.atoms.filter((atom) => atom.status === 'needs-review' || atom.status === 'stale');
|
|
151
|
+
const reviewItems = unresolvedAtoms.map((atom) => ({
|
|
152
|
+
atom,
|
|
153
|
+
replacement: findReplacementAtom(atom, recentActiveAtoms),
|
|
154
|
+
}));
|
|
149
155
|
const covered = new Set();
|
|
150
156
|
for (const atom of recentActiveAtoms) {
|
|
151
157
|
for (const ref of atom.evidenceRefs) {
|
|
@@ -165,32 +171,75 @@ async function buildCaptureCheck(rootPath, input) {
|
|
|
165
171
|
changedFiles,
|
|
166
172
|
coveredFiles: [...covered],
|
|
167
173
|
invalidatedAtoms,
|
|
174
|
+
unresolvedAtoms,
|
|
175
|
+
reviewItems,
|
|
168
176
|
};
|
|
169
177
|
}
|
|
170
178
|
function isInvalidatedAtomCovered(invalidated, recentActiveAtoms) {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
179
|
+
return recentActiveAtoms.some((atom) => atomsOverlap(invalidated, atom));
|
|
180
|
+
}
|
|
181
|
+
function findReplacementAtom(invalidated, recentActiveAtoms) {
|
|
182
|
+
return recentActiveAtoms.find((atom) => atomsHaveReplacementSignal(invalidated, atom));
|
|
183
|
+
}
|
|
184
|
+
function atomsOverlap(a, b) {
|
|
185
|
+
const invalidatedFiles = new Set(a.scopeRefs.files);
|
|
186
|
+
const invalidatedSymbols = new Set(a.scopeRefs.symbols);
|
|
187
|
+
for (const ref of a.evidenceRefs) {
|
|
174
188
|
if (ref.type === 'file')
|
|
175
189
|
invalidatedFiles.add(ref.path);
|
|
176
190
|
if (ref.type === 'symbol')
|
|
177
191
|
invalidatedSymbols.add(ref.name);
|
|
178
192
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
193
|
+
const atomFiles = new Set(b.scopeRefs.files);
|
|
194
|
+
const atomSymbols = new Set(b.scopeRefs.symbols);
|
|
195
|
+
for (const ref of b.evidenceRefs) {
|
|
196
|
+
if (ref.type === 'file')
|
|
197
|
+
atomFiles.add(ref.path);
|
|
198
|
+
if (ref.type === 'symbol')
|
|
199
|
+
atomSymbols.add(ref.name);
|
|
200
|
+
}
|
|
201
|
+
const fileOverlap = [...invalidatedFiles].some((file) => atomFiles.has(file));
|
|
202
|
+
const symbolOverlap = [...invalidatedSymbols].some((symbol) => atomSymbols.has(symbol));
|
|
203
|
+
if (fileOverlap || symbolOverlap)
|
|
204
|
+
return true;
|
|
205
|
+
return tokenOverlap(a.topic, b.topic);
|
|
206
|
+
}
|
|
207
|
+
function atomsHaveReplacementSignal(a, b) {
|
|
208
|
+
const aSymbols = new Set(a.scopeRefs.symbols);
|
|
209
|
+
for (const ref of a.evidenceRefs) {
|
|
210
|
+
if (ref.type === 'symbol')
|
|
211
|
+
aSymbols.add(ref.name);
|
|
212
|
+
}
|
|
213
|
+
const bSymbols = new Set(b.scopeRefs.symbols);
|
|
214
|
+
for (const ref of b.evidenceRefs) {
|
|
215
|
+
if (ref.type === 'symbol')
|
|
216
|
+
bSymbols.add(ref.name);
|
|
217
|
+
}
|
|
218
|
+
const symbolOverlap = [...aSymbols].some((symbol) => bSymbols.has(symbol));
|
|
219
|
+
return symbolOverlap || meaningfulTopicOverlap(a.topic, b.topic);
|
|
220
|
+
}
|
|
221
|
+
function meaningfulTopicOverlap(a, b) {
|
|
222
|
+
const weakTokens = new Set([
|
|
223
|
+
'add',
|
|
224
|
+
'after',
|
|
225
|
+
'behavior',
|
|
226
|
+
'change',
|
|
227
|
+
'changed',
|
|
228
|
+
'new',
|
|
229
|
+
'old',
|
|
230
|
+
'review',
|
|
231
|
+
'update',
|
|
232
|
+
'with',
|
|
233
|
+
]);
|
|
234
|
+
const aTokens = new Set(a
|
|
235
|
+
.toLowerCase()
|
|
236
|
+
.split(/[^a-z0-9]+/)
|
|
237
|
+
.filter((token) => token.length > 2 && !weakTokens.has(token)));
|
|
238
|
+
return b
|
|
239
|
+
.toLowerCase()
|
|
240
|
+
.split(/[^a-z0-9]+/)
|
|
241
|
+
.filter((token) => token.length > 2 && !weakTokens.has(token))
|
|
242
|
+
.some((token) => aTokens.has(token));
|
|
194
243
|
}
|
|
195
244
|
function tokenOverlap(a, b) {
|
|
196
245
|
const aTokens = new Set(a
|
|
@@ -228,11 +277,19 @@ function matchingInvalidatedAtoms(atoms, topic) {
|
|
|
228
277
|
}
|
|
229
278
|
function renderFinalCaptureCheck(check, topic) {
|
|
230
279
|
console.log('KGraph Final Check');
|
|
280
|
+
if (!check.required && check.unresolvedAtoms.length > 0) {
|
|
281
|
+
console.log(' status memory-review-required');
|
|
282
|
+
console.log(` unresolved ${check.unresolvedAtoms.length}`);
|
|
283
|
+
console.log(' conclusion stale or needs-review atoms remain');
|
|
284
|
+
renderMemoryReviewItems(check.reviewItems);
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
231
287
|
if (check.changedFiles.length === 0) {
|
|
232
288
|
if (check.required) {
|
|
233
289
|
console.log(' status capture-required');
|
|
234
290
|
console.log(' changed files 0');
|
|
235
291
|
console.log(` invalid atoms ${check.invalidatedAtoms.length}`);
|
|
292
|
+
renderMemoryReviewItems(check.reviewItems.filter((item) => check.invalidatedAtoms.some((atom) => atom.id === item.atom.id)));
|
|
236
293
|
console.log(' conclusion missing for needs-review or stale knowledge');
|
|
237
294
|
console.log(` next kgraph "${topic || '<topic>'}" --capture "<durable conclusion>" --capture-file <path>`);
|
|
238
295
|
return;
|
|
@@ -251,7 +308,24 @@ function renderFinalCaptureCheck(check, topic) {
|
|
|
251
308
|
console.log(` changed files ${check.changedFiles.length}`);
|
|
252
309
|
if (check.invalidatedAtoms.length > 0) {
|
|
253
310
|
console.log(` invalid atoms ${check.invalidatedAtoms.length}`);
|
|
311
|
+
renderMemoryReviewItems(check.reviewItems.filter((item) => check.invalidatedAtoms.some((atom) => atom.id === item.atom.id)));
|
|
254
312
|
}
|
|
255
313
|
console.log(' conclusion missing for one or more changed files');
|
|
256
314
|
console.log(` next kgraph "${topic || '<topic>'}" --capture "<durable conclusion>" --capture-file <path>`);
|
|
257
315
|
}
|
|
316
|
+
function renderMemoryReviewItems(items) {
|
|
317
|
+
const visible = items.slice(0, 3);
|
|
318
|
+
for (const item of visible) {
|
|
319
|
+
console.log(` review atom ${item.atom.id}`);
|
|
320
|
+
console.log(` review topic ${item.atom.status}: ${item.atom.topic}`);
|
|
321
|
+
if (item.replacement) {
|
|
322
|
+
console.log(` supersede kgraph knowledge supersede ${item.atom.id} ${item.replacement.id}`);
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
325
|
+
console.log(` inspect kgraph knowledge get ${item.atom.id}`);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
if (items.length > visible.length) {
|
|
329
|
+
console.log(` review more ${items.length - visible.length} more atom(s)`);
|
|
330
|
+
}
|
|
331
|
+
}
|
package/dist/config/config.js
CHANGED