@kentwynn/kgraph 0.2.37 → 0.2.38
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/pack.js +30 -0
- package/dist/context/ranking.js +23 -2
- package/dist/integrations/adapters/claude-code.js +16 -0
- package/dist/integrations/adapters/cline.js +2 -0
- package/dist/integrations/adapters/cursor.js +2 -0
- package/dist/integrations/adapters/gemini.js +5 -2
- package/dist/integrations/adapters/windsurf.js +3 -0
- package/package.json +1 -1
|
@@ -2,6 +2,7 @@ import path from 'node:path';
|
|
|
2
2
|
import { loadConfig } from '../../config/config.js';
|
|
3
3
|
import { buildContextPack } from '../../context/context-pack.js';
|
|
4
4
|
import { queryContext } from '../../context/context-query.js';
|
|
5
|
+
import { getCurrentCommit, isGitRepo } from '../../scanner/git-utils.js';
|
|
5
6
|
import { assertSessionAgent, recordSessionEvent, } from '../../session/session-store.js';
|
|
6
7
|
import { listInboxNotes } from '../../storage/cognition-store.js';
|
|
7
8
|
import { assertWorkspace } from '../../storage/kgraph-paths.js';
|
|
@@ -34,6 +35,35 @@ export function registerPackCommand(program) {
|
|
|
34
35
|
]);
|
|
35
36
|
const response = await queryContext(workspace, config, maps, task);
|
|
36
37
|
const pack = buildContextPack(response, budget, workspace.rootPath);
|
|
38
|
+
// P4: warn when maps are behind HEAD (user edited files without re-scanning)
|
|
39
|
+
if (await isGitRepo(workspace.rootPath)) {
|
|
40
|
+
const head = await getCurrentCommit(workspace.rootPath);
|
|
41
|
+
if (head &&
|
|
42
|
+
maps.fileMap.scannedAtCommit &&
|
|
43
|
+
maps.fileMap.scannedAtCommit !== head) {
|
|
44
|
+
pack.warnings = [
|
|
45
|
+
`Maps are behind HEAD — run \`kgraph scan\` or \`kgraph "${task}"${agent ? ` --agent ${agent}` : ''}\` for accurate atom status. Knowledge freshness may be reduced.`,
|
|
46
|
+
...pack.warnings,
|
|
47
|
+
];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// P1: warn when needs-review or stale atoms are served in context
|
|
51
|
+
const degradedAtoms = pack.items.filter((item) => item.kind === 'atom' &&
|
|
52
|
+
(item.data.status === 'needs-review' ||
|
|
53
|
+
item.data.status === 'stale'));
|
|
54
|
+
if (degradedAtoms.length > 0) {
|
|
55
|
+
const needsReview = degradedAtoms.filter((item) => item.data.status === 'needs-review').length;
|
|
56
|
+
const stale = degradedAtoms.filter((item) => item.data.status === 'stale').length;
|
|
57
|
+
const parts = [];
|
|
58
|
+
if (needsReview > 0)
|
|
59
|
+
parts.push(`${needsReview} needs-review`);
|
|
60
|
+
if (stale > 0)
|
|
61
|
+
parts.push(`${stale} stale`);
|
|
62
|
+
pack.warnings = [
|
|
63
|
+
...pack.warnings,
|
|
64
|
+
`${parts.join(', ')} atom(s) in context may reflect outdated knowledge — run \`kgraph stale\` to inspect, or \`kgraph "${task}" --final${agent ? ` --agent ${agent}` : ''}\` to resolve.`,
|
|
65
|
+
];
|
|
66
|
+
}
|
|
37
67
|
if (agent) {
|
|
38
68
|
const omittedTokens = pack.omitted.reduce((sum, item) => sum + item.tokenEstimate, 0);
|
|
39
69
|
await recordSessionEvent(workspace, {
|
package/dist/context/ranking.js
CHANGED
|
@@ -14,7 +14,10 @@ export function rankByFields(query, items, fields) {
|
|
|
14
14
|
for (const field of fields) {
|
|
15
15
|
const value = field.value(item);
|
|
16
16
|
const values = Array.isArray(value) ? value : value ? [value] : [];
|
|
17
|
-
const haystack = values
|
|
17
|
+
const haystack = values
|
|
18
|
+
.flatMap((value) => [value, splitIdentifier(value).join(' ')])
|
|
19
|
+
.join(' ')
|
|
20
|
+
.toLowerCase();
|
|
18
21
|
for (const token of tokens) {
|
|
19
22
|
if (haystack.includes(token)) {
|
|
20
23
|
const baseScore = field.name === 'path' || field.name === 'name'
|
|
@@ -36,7 +39,25 @@ export function rankByFields(query, items, fields) {
|
|
|
36
39
|
.sort((a, b) => b.score - a.score);
|
|
37
40
|
}
|
|
38
41
|
function expandTokens(tokens) {
|
|
39
|
-
return [
|
|
42
|
+
return [
|
|
43
|
+
...new Set(tokens.flatMap((token) => {
|
|
44
|
+
const parts = [token, ...splitIdentifier(token)];
|
|
45
|
+
// Add prefix stems so "authentication" also matches "auth",
|
|
46
|
+
// "configuration" matches "config", etc. Only for longer tokens
|
|
47
|
+
// to avoid over-matching short words.
|
|
48
|
+
// Use a short 4-char stem (catches root prefixes like "auth", "conf", "init")
|
|
49
|
+
// and a half-length stem (catches "config" from "configuration", "authent" from
|
|
50
|
+
// "authentication"). Both are narrower than the full token so they find real matches
|
|
51
|
+
// without over-fetching.
|
|
52
|
+
if (token.length >= 8) {
|
|
53
|
+
parts.push(token.slice(0, 4));
|
|
54
|
+
const half = Math.floor(token.length * 0.5);
|
|
55
|
+
if (half > 4)
|
|
56
|
+
parts.push(token.slice(0, half));
|
|
57
|
+
}
|
|
58
|
+
return parts;
|
|
59
|
+
})),
|
|
60
|
+
];
|
|
40
61
|
}
|
|
41
62
|
function splitIdentifier(value) {
|
|
42
63
|
return value
|
|
@@ -202,6 +202,22 @@ const filePath = payload?.tool_input?.file_path || payload?.toolInput?.file_path
|
|
|
202
202
|
if (!filePath) process.exit(0);
|
|
203
203
|
args.push(filePath);`
|
|
204
204
|
: '';
|
|
205
|
+
// On session end: run stale check first (refreshes atom statuses against current
|
|
206
|
+
// maps without needing a topic), then end the session with --conclude so any
|
|
207
|
+
// pending knowledge is captured. Both run regardless of each other's exit code.
|
|
208
|
+
if (event === 'end') {
|
|
209
|
+
return `#!/usr/bin/env node
|
|
210
|
+
const { spawnSync } = require('node:child_process');
|
|
211
|
+
|
|
212
|
+
// P3: refresh atom statuses against current maps so needs-review/stale atoms
|
|
213
|
+
// are up to date before the session ends. Output ignored — hook is silent.
|
|
214
|
+
spawnSync('kgraph', ['stale', '--json'], { stdio: 'ignore' });
|
|
215
|
+
|
|
216
|
+
// P2: end the session and trigger conclude so durable knowledge is captured.
|
|
217
|
+
const result = spawnSync('kgraph', ['session', 'end', '--agent', 'claude-code', '--conclude', '--source', 'automatic'], { stdio: 'ignore' });
|
|
218
|
+
process.exit(result.status || 0);
|
|
219
|
+
`;
|
|
220
|
+
}
|
|
205
221
|
return `#!/usr/bin/env node
|
|
206
222
|
const { spawnSync } = require('node:child_process');
|
|
207
223
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { agentSkillFiles } from '../agent-skills.js';
|
|
1
2
|
import { bulletWorkflow } from '../workflow-steps.js';
|
|
2
3
|
export const clineAdapter = {
|
|
3
4
|
name: 'cline',
|
|
@@ -7,4 +8,5 @@ export const clineAdapter = {
|
|
|
7
8
|
|
|
8
9
|
${bulletWorkflow('cline')}
|
|
9
10
|
`,
|
|
11
|
+
commandFiles: agentSkillFiles('cline'),
|
|
10
12
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { agentSkillFiles } from '../agent-skills.js';
|
|
1
2
|
import { bulletWorkflow } from '../workflow-steps.js';
|
|
2
3
|
export const cursorAdapter = {
|
|
3
4
|
name: 'cursor',
|
|
@@ -12,5 +13,6 @@ alwaysApply: true
|
|
|
12
13
|
|
|
13
14
|
${bulletWorkflow('cursor')}
|
|
14
15
|
`,
|
|
16
|
+
commandFiles: agentSkillFiles('cursor'),
|
|
15
17
|
obsoleteCommandFiles: ['.cursor/rules/kgraph-commands.mdc'],
|
|
16
18
|
};
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { agentSkillFiles } from '../agent-skills.js';
|
|
2
|
+
import { numberedWorkflow } from '../workflow-steps.js';
|
|
2
3
|
export const geminiAdapter = {
|
|
3
4
|
name: 'gemini',
|
|
4
5
|
label: 'Gemini CLI',
|
|
5
6
|
targetPath: 'GEMINI.md',
|
|
6
7
|
instructions: `## KGraph Workflow
|
|
7
8
|
|
|
8
|
-
${
|
|
9
|
+
${numberedWorkflow('gemini')}
|
|
9
10
|
`,
|
|
11
|
+
commandFiles: agentSkillFiles('gemini'),
|
|
12
|
+
obsoleteCommandFiles: [],
|
|
10
13
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { agentSkillFiles } from '../agent-skills.js';
|
|
1
2
|
import { bulletWorkflow } from '../workflow-steps.js';
|
|
2
3
|
export const windsurfAdapter = {
|
|
3
4
|
name: 'windsurf',
|
|
@@ -7,4 +8,6 @@ export const windsurfAdapter = {
|
|
|
7
8
|
|
|
8
9
|
${bulletWorkflow('windsurf')}
|
|
9
10
|
`,
|
|
11
|
+
commandFiles: agentSkillFiles('windsurf'),
|
|
12
|
+
obsoleteCommandFiles: [],
|
|
10
13
|
};
|