@kentwynn/kgraph 0.2.9 → 0.2.11
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/README.md +33 -2
- package/dist/cli/commands/blame.d.ts +2 -0
- package/dist/cli/commands/blame.js +52 -0
- package/dist/cli/commands/doctor.js +17 -0
- package/dist/cli/commands/init.js +2 -0
- package/dist/cli/commands/knowledge.d.ts +2 -0
- package/dist/cli/commands/knowledge.js +137 -0
- package/dist/cli/commands/pack.d.ts +2 -0
- package/dist/cli/commands/pack.js +49 -0
- package/dist/cli/commands/stale.d.ts +2 -0
- package/dist/cli/commands/stale.js +33 -0
- package/dist/cli/help.js +6 -0
- package/dist/cli/index.js +8 -0
- package/dist/cognition/cognition-updater.js +14 -0
- package/dist/cognition/compact.js +129 -28
- package/dist/cognition/conclusion.d.ts +2 -0
- package/dist/cognition/conclusion.js +22 -0
- package/dist/context/context-pack.d.ts +3 -0
- package/dist/context/context-pack.js +71 -0
- package/dist/context/context-query.js +53 -28
- package/dist/integrations/adapters/claude-code.js +21 -1
- package/dist/integrations/adapters/codex.js +1 -1
- package/dist/integrations/adapters/copilot.js +44 -1
- package/dist/integrations/workflow-steps.js +16 -7
- package/dist/knowledge/atom-store.d.ts +60 -0
- package/dist/knowledge/atom-store.js +484 -0
- package/dist/storage/kgraph-paths.js +5 -2
- package/dist/types/config.d.ts +1 -0
- package/dist/types/knowledge.d.ts +92 -0
- package/dist/types/knowledge.js +1 -0
- package/package.json +1 -1
|
@@ -1,58 +1,89 @@
|
|
|
1
1
|
import { mkdir, rename } from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import {
|
|
3
|
+
import { atomToCognitionNote, refreshKnowledgeAtomStatuses, writeKnowledgeAtoms, } from '../knowledge/atom-store.js';
|
|
4
|
+
import { overwriteDomainRecord, readDomainRecords, slugify, writeCognitionNote, } from '../storage/cognition-store.js';
|
|
4
5
|
import { pathExists } from '../storage/kgraph-paths.js';
|
|
5
6
|
import { readMaps } from '../storage/map-store.js';
|
|
6
7
|
import { evaluateReferenceStatus } from './cognition-updater.js';
|
|
7
8
|
export async function compactCognition(workspace, dryRun = false) {
|
|
8
|
-
const notes = await readCognitionNotes(workspace);
|
|
9
9
|
const maps = await readMaps(workspace);
|
|
10
|
-
const
|
|
10
|
+
const refreshed = await refreshKnowledgeAtomStatuses(workspace, { fileMap: maps.fileMap, symbolMap: maps.symbolMap }, dryRun);
|
|
11
|
+
const atoms = refreshed.atoms;
|
|
12
|
+
const groups = groupDuplicateAtoms(atoms);
|
|
11
13
|
const result = { merged: [], archived: [] };
|
|
12
14
|
const consumed = new Set();
|
|
13
15
|
const archived = new Set();
|
|
14
|
-
const
|
|
16
|
+
const mergedAtoms = [];
|
|
15
17
|
for (const group of groups.filter((items) => items.length > 1)) {
|
|
16
|
-
const merged =
|
|
18
|
+
const merged = mergeAtoms(group, {
|
|
17
19
|
files: maps.fileMap.files,
|
|
18
20
|
symbols: maps.symbolMap.symbols,
|
|
19
21
|
});
|
|
20
|
-
const sourceIds = group.map((
|
|
22
|
+
const sourceIds = group.map((atom) => atom.id);
|
|
21
23
|
result.merged.push({
|
|
22
24
|
targetId: merged.id,
|
|
23
25
|
sourceIds,
|
|
24
|
-
title: merged.
|
|
26
|
+
title: merged.topic,
|
|
25
27
|
});
|
|
26
28
|
sourceIds.forEach((id) => consumed.add(id));
|
|
27
|
-
|
|
28
|
-
if (!dryRun) {
|
|
29
|
-
await writeCognitionNote(workspace, merged);
|
|
30
|
-
for (const note of group) {
|
|
31
|
-
await archiveNote(workspace, note, `superseded-by-${merged.id}`);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
29
|
+
mergedAtoms.push(merged);
|
|
34
30
|
}
|
|
35
|
-
for (const
|
|
36
|
-
if (consumed.has(
|
|
31
|
+
for (const atom of atoms) {
|
|
32
|
+
if (consumed.has(atom.id) || atom.status === 'archived')
|
|
37
33
|
continue;
|
|
38
|
-
if (
|
|
39
|
-
(
|
|
34
|
+
if (atom.confidence === 'low' &&
|
|
35
|
+
(atom.status === 'stale' || atom.status === 'needs-review')) {
|
|
40
36
|
result.archived.push({
|
|
41
|
-
id:
|
|
42
|
-
title:
|
|
43
|
-
reason:
|
|
37
|
+
id: atom.id,
|
|
38
|
+
title: atom.topic,
|
|
39
|
+
reason: `low-confidence ${atom.status} atom`,
|
|
44
40
|
});
|
|
45
|
-
archived.add(
|
|
46
|
-
if (!dryRun) {
|
|
47
|
-
await archiveNote(workspace, note, 'low-confidence-stale');
|
|
48
|
-
}
|
|
41
|
+
archived.add(atom.id);
|
|
49
42
|
}
|
|
50
43
|
}
|
|
51
44
|
if (!dryRun && (consumed.size > 0 || archived.size > 0)) {
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
...
|
|
45
|
+
const now = new Date().toISOString();
|
|
46
|
+
const nextAtoms = [
|
|
47
|
+
...atoms.map((atom) => {
|
|
48
|
+
if (consumed.has(atom.id)) {
|
|
49
|
+
const merged = mergedAtoms.find((candidate) => candidate.lifecycle.supersedes.includes(atom.id));
|
|
50
|
+
return {
|
|
51
|
+
...atom,
|
|
52
|
+
status: 'archived',
|
|
53
|
+
confidence: 'low',
|
|
54
|
+
lifecycle: {
|
|
55
|
+
...atom.lifecycle,
|
|
56
|
+
supersededBy: merged?.id,
|
|
57
|
+
archivedAt: now,
|
|
58
|
+
},
|
|
59
|
+
provenance: { ...atom.provenance, updatedAt: now },
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
if (archived.has(atom.id)) {
|
|
63
|
+
return {
|
|
64
|
+
...atom,
|
|
65
|
+
status: 'archived',
|
|
66
|
+
lifecycle: { ...atom.lifecycle, archivedAt: now },
|
|
67
|
+
provenance: { ...atom.provenance, updatedAt: now },
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return atom;
|
|
71
|
+
}),
|
|
72
|
+
...mergedAtoms,
|
|
55
73
|
];
|
|
74
|
+
await writeKnowledgeAtoms(workspace, nextAtoms);
|
|
75
|
+
for (const atom of [...atoms.filter((item) => consumed.has(item.id) || archived.has(item.id)), ...mergedAtoms]) {
|
|
76
|
+
const note = atomToCognitionNote(atom);
|
|
77
|
+
if (mergedAtoms.some((merged) => merged.id === atom.id)) {
|
|
78
|
+
await writeCognitionNote(workspace, note);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
await archiveNote(workspace, note, consumed.has(atom.id) ? `superseded-by-${note.supersededBy ?? 'compact'}` : 'low-confidence-stale');
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const activeNotes = nextAtoms
|
|
85
|
+
.filter((atom) => atom.status !== 'archived')
|
|
86
|
+
.map(atomToCognitionNote);
|
|
56
87
|
await rebuildDomainRecords(workspace, activeNotes, {
|
|
57
88
|
files: maps.fileMap.files,
|
|
58
89
|
symbols: maps.symbolMap.symbols,
|
|
@@ -60,6 +91,59 @@ export async function compactCognition(workspace, dryRun = false) {
|
|
|
60
91
|
}
|
|
61
92
|
return result;
|
|
62
93
|
}
|
|
94
|
+
function groupDuplicateAtoms(atoms) {
|
|
95
|
+
const byKey = new Map();
|
|
96
|
+
for (const atom of atoms) {
|
|
97
|
+
if (atom.status === 'archived' || atom.lifecycle.supersededBy)
|
|
98
|
+
continue;
|
|
99
|
+
const key = [
|
|
100
|
+
atom.type,
|
|
101
|
+
atom.scopeRefs.domains[0] ?? 'general',
|
|
102
|
+
normalizeText(atom.topic),
|
|
103
|
+
normalizeText(atom.claim),
|
|
104
|
+
stableList(atom.scopeRefs.files),
|
|
105
|
+
stableList(atom.scopeRefs.symbols),
|
|
106
|
+
].join('\0');
|
|
107
|
+
const group = byKey.get(key) ?? [];
|
|
108
|
+
group.push(atom);
|
|
109
|
+
byKey.set(key, group);
|
|
110
|
+
}
|
|
111
|
+
return [...byKey.values()];
|
|
112
|
+
}
|
|
113
|
+
function mergeAtoms(atoms, currentMaps) {
|
|
114
|
+
const sorted = [...atoms].sort((left, right) => left.provenance.createdAt.localeCompare(right.provenance.createdAt));
|
|
115
|
+
const base = sorted[sorted.length - 1];
|
|
116
|
+
const now = new Date().toISOString();
|
|
117
|
+
const id = `${now.replace(/[:.]/g, '-')}-${slugify(base.topic) || 'compacted'}`;
|
|
118
|
+
const summaries = unique(sorted
|
|
119
|
+
.map((atom) => atom.summary ?? atom.claim)
|
|
120
|
+
.filter((summary) => Boolean(summary?.trim())));
|
|
121
|
+
const files = unique(sorted.flatMap((atom) => atom.scopeRefs.files));
|
|
122
|
+
const symbols = unique(sorted.flatMap((atom) => atom.scopeRefs.symbols));
|
|
123
|
+
const domains = unique(sorted.flatMap((atom) => atom.scopeRefs.domains));
|
|
124
|
+
const packages = unique(sorted.flatMap((atom) => atom.scopeRefs.packages));
|
|
125
|
+
const status = atomStatusFromReferenceStatus(evaluateReferenceStatus(files, symbols, currentMaps));
|
|
126
|
+
return {
|
|
127
|
+
...base,
|
|
128
|
+
id,
|
|
129
|
+
claim: summaries[0] ?? base.claim,
|
|
130
|
+
summary: summaries.join('\n'),
|
|
131
|
+
confidence: highestConfidence(sorted.map((atom) => atom.confidence)),
|
|
132
|
+
status,
|
|
133
|
+
evidenceRefs: uniqueEvidence(sorted.flatMap((atom) => atom.evidenceRefs)),
|
|
134
|
+
scopeRefs: { files, symbols, domains, packages },
|
|
135
|
+
provenance: {
|
|
136
|
+
sourceCommand: 'compact',
|
|
137
|
+
agent: base.provenance.agent,
|
|
138
|
+
sessionId: base.provenance.sessionId,
|
|
139
|
+
commit: base.provenance.commit,
|
|
140
|
+
createdAt: now,
|
|
141
|
+
},
|
|
142
|
+
lifecycle: {
|
|
143
|
+
supersedes: sorted.map((atom) => atom.id),
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
}
|
|
63
147
|
function groupDuplicates(notes) {
|
|
64
148
|
const byKey = new Map();
|
|
65
149
|
for (const note of notes) {
|
|
@@ -153,6 +237,23 @@ function highestConfidence(values) {
|
|
|
153
237
|
return 'medium';
|
|
154
238
|
return 'low';
|
|
155
239
|
}
|
|
240
|
+
function atomStatusFromReferenceStatus(status) {
|
|
241
|
+
if (status === 'current')
|
|
242
|
+
return 'active';
|
|
243
|
+
if (status === 'mixed')
|
|
244
|
+
return 'needs-review';
|
|
245
|
+
return 'stale';
|
|
246
|
+
}
|
|
247
|
+
function uniqueEvidence(items) {
|
|
248
|
+
const seen = new Set();
|
|
249
|
+
return items.filter((item) => {
|
|
250
|
+
const key = JSON.stringify(item);
|
|
251
|
+
if (seen.has(key))
|
|
252
|
+
return false;
|
|
253
|
+
seen.add(key);
|
|
254
|
+
return true;
|
|
255
|
+
});
|
|
256
|
+
}
|
|
156
257
|
function normalizeTitle(value) {
|
|
157
258
|
return normalizeText(value);
|
|
158
259
|
}
|
|
@@ -10,6 +10,8 @@ export interface ConclusionInput {
|
|
|
10
10
|
relatedFiles?: string[];
|
|
11
11
|
relatedSymbols?: string[];
|
|
12
12
|
source: CognitionSource;
|
|
13
|
+
agent?: string;
|
|
14
|
+
sessionId?: string;
|
|
13
15
|
}
|
|
14
16
|
export declare function concludeTopic(workspace: KGraphWorkspace, input: ConclusionInput): Promise<CognitionNote>;
|
|
15
17
|
export declare function concludeActiveSession(workspace: KGraphWorkspace, agent: string, input: Omit<ConclusionInput, 'source' | 'relatedFiles' | 'relatedSymbols'>): Promise<CognitionNote>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readMaps } from '../storage/map-store.js';
|
|
2
2
|
import { slugify, writeCognitionNote, writeDomainRecord } from '../storage/cognition-store.js';
|
|
3
3
|
import { KGraphError } from '../cli/errors.js';
|
|
4
|
+
import { createKnowledgeAtom } from '../knowledge/atom-store.js';
|
|
4
5
|
import { evaluateReferenceStatus } from './cognition-updater.js';
|
|
5
6
|
import { readSessionState } from '../session/session-store.js';
|
|
6
7
|
export async function concludeTopic(workspace, input) {
|
|
@@ -36,6 +37,25 @@ export async function concludeTopic(workspace, input) {
|
|
|
36
37
|
files: maps.fileMap.files,
|
|
37
38
|
symbols: maps.symbolMap.symbols,
|
|
38
39
|
}));
|
|
40
|
+
await createKnowledgeAtom(workspace, {
|
|
41
|
+
type: note.kind,
|
|
42
|
+
topic: note.title,
|
|
43
|
+
claim: note.summary ?? note.title,
|
|
44
|
+
summary: note.summary,
|
|
45
|
+
confidence: note.confidence,
|
|
46
|
+
files: note.relatedFiles,
|
|
47
|
+
symbols: note.relatedSymbols,
|
|
48
|
+
domains: note.domain ? [note.domain] : [],
|
|
49
|
+
sourceCommand: input.source === 'session-conclude'
|
|
50
|
+
? 'session-conclude'
|
|
51
|
+
: input.source === 'compact'
|
|
52
|
+
? 'compact'
|
|
53
|
+
: 'conclude',
|
|
54
|
+
agent: input.agent,
|
|
55
|
+
sessionId: input.sessionId,
|
|
56
|
+
createdAt: note.createdAt,
|
|
57
|
+
idSeed: note.id,
|
|
58
|
+
}, maps);
|
|
39
59
|
return note;
|
|
40
60
|
}
|
|
41
61
|
export async function concludeActiveSession(workspace, agent, input) {
|
|
@@ -76,6 +96,8 @@ export async function buildActiveSessionConclusion(workspace, agent, input) {
|
|
|
76
96
|
body,
|
|
77
97
|
source: 'session-conclude',
|
|
78
98
|
relatedFiles: touchedFiles,
|
|
99
|
+
agent,
|
|
100
|
+
sessionId: active.sessionId,
|
|
79
101
|
};
|
|
80
102
|
}
|
|
81
103
|
function normalizeBody(value) {
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { estimateTokens } from '../session/token-estimator.js';
|
|
2
|
+
export function buildContextPack(response, budget) {
|
|
3
|
+
const candidates = [
|
|
4
|
+
...response.relevantFiles.map((ranked) => ({
|
|
5
|
+
kind: 'file',
|
|
6
|
+
id: ranked.item.path,
|
|
7
|
+
title: ranked.item.path,
|
|
8
|
+
tokenEstimate: ranked.item.tokenEstimate ?? 0,
|
|
9
|
+
reasons: ranked.reasons,
|
|
10
|
+
data: ranked.item,
|
|
11
|
+
})),
|
|
12
|
+
...response.relevantSymbols.map((ranked) => ({
|
|
13
|
+
kind: 'symbol',
|
|
14
|
+
id: ranked.item.id,
|
|
15
|
+
title: ranked.item.name,
|
|
16
|
+
tokenEstimate: 20,
|
|
17
|
+
reasons: ranked.reasons,
|
|
18
|
+
data: ranked.item,
|
|
19
|
+
})),
|
|
20
|
+
...response.relevantCognition.map((ranked) => ({
|
|
21
|
+
kind: 'atom',
|
|
22
|
+
id: ranked.item.id,
|
|
23
|
+
title: ranked.item.title,
|
|
24
|
+
tokenEstimate: estimateTokens([ranked.item.title, ranked.item.summary ?? ''].join('\n'), `${ranked.item.id}.md`),
|
|
25
|
+
reasons: ranked.reasons,
|
|
26
|
+
data: ranked.item,
|
|
27
|
+
})),
|
|
28
|
+
...response.relationships.map((relationship) => ({
|
|
29
|
+
kind: 'relationship',
|
|
30
|
+
id: [
|
|
31
|
+
relationship.sourceId,
|
|
32
|
+
relationship.relationshipType,
|
|
33
|
+
relationship.targetId,
|
|
34
|
+
].join(' -> '),
|
|
35
|
+
title: `${relationship.sourceId} ${relationship.relationshipType} ${relationship.targetId}`,
|
|
36
|
+
tokenEstimate: 16,
|
|
37
|
+
reasons: response.relationshipExplanations?.find((item) => item.relationship.sourceId === relationship.sourceId &&
|
|
38
|
+
item.relationship.targetId === relationship.targetId &&
|
|
39
|
+
item.relationship.relationshipType === relationship.relationshipType)?.reasons ?? ['related graph edge'],
|
|
40
|
+
data: relationship,
|
|
41
|
+
})),
|
|
42
|
+
...(response.gitChanges ?? []).map((change) => ({
|
|
43
|
+
kind: 'git-change',
|
|
44
|
+
id: change.path,
|
|
45
|
+
title: `${change.status}: ${change.path}`,
|
|
46
|
+
tokenEstimate: 12,
|
|
47
|
+
reasons: [change.reason],
|
|
48
|
+
data: change,
|
|
49
|
+
})),
|
|
50
|
+
];
|
|
51
|
+
const items = [];
|
|
52
|
+
const omitted = [];
|
|
53
|
+
let usedTokens = 0;
|
|
54
|
+
for (const candidate of candidates) {
|
|
55
|
+
if (usedTokens + candidate.tokenEstimate <= budget) {
|
|
56
|
+
items.push(candidate);
|
|
57
|
+
usedTokens += candidate.tokenEstimate;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
omitted.push(candidate);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
task: response.query,
|
|
65
|
+
budget,
|
|
66
|
+
usedTokens,
|
|
67
|
+
items,
|
|
68
|
+
omitted,
|
|
69
|
+
warnings: response.warnings,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import { getRecentlyCommittedFiles, getWorkingTreeChangesDetailed, isGitRepo, } from '../scanner/git-utils.js';
|
|
2
|
-
import {
|
|
2
|
+
import { readDomainRecords } from '../storage/cognition-store.js';
|
|
3
3
|
import { readSessionState } from '../session/session-store.js';
|
|
4
|
+
import { atomToCognitionNote, refreshKnowledgeAtomStatuses, } from '../knowledge/atom-store.js';
|
|
4
5
|
import { rankByFields } from './ranking.js';
|
|
5
6
|
export async function queryContext(workspace, config, maps, query) {
|
|
6
|
-
const
|
|
7
|
+
const refreshedAtoms = await refreshKnowledgeAtomStatuses(workspace, {
|
|
8
|
+
fileMap: maps.fileMap,
|
|
9
|
+
symbolMap: maps.symbolMap,
|
|
10
|
+
});
|
|
11
|
+
const atoms = refreshedAtoms.atoms;
|
|
12
|
+
const cognition = atoms
|
|
13
|
+
.filter((atom) => atom.status !== 'archived')
|
|
14
|
+
.map(atomToCognitionNote);
|
|
7
15
|
const domains = await readDomainRecords(workspace);
|
|
8
16
|
const session = await readSessionState(workspace);
|
|
9
17
|
const sessionTouchedPaths = new Set(session.events
|
|
@@ -31,17 +39,23 @@ export async function queryContext(workspace, config, maps, query) {
|
|
|
31
39
|
{ name: 'kind', value: (symbol) => symbol.kind },
|
|
32
40
|
{ name: 'parent', value: (symbol) => symbol.parentName },
|
|
33
41
|
]).slice(0, max);
|
|
34
|
-
const relevantCognition = rankByFields(query,
|
|
35
|
-
{ name: '
|
|
36
|
-
{ name: '
|
|
37
|
-
{ name: '
|
|
38
|
-
{ name: '
|
|
39
|
-
{ name: '
|
|
40
|
-
{ name: '
|
|
41
|
-
{ name: '
|
|
42
|
-
{ name: '
|
|
42
|
+
const relevantCognition = rankByFields(query, atoms.filter((atom) => atom.status !== 'archived'), [
|
|
43
|
+
{ name: 'topic', value: (atom) => atom.topic },
|
|
44
|
+
{ name: 'claim', value: (atom) => atom.claim },
|
|
45
|
+
{ name: 'type', value: (atom) => atom.type },
|
|
46
|
+
{ name: 'confidence', value: (atom) => atom.confidence },
|
|
47
|
+
{ name: 'status', value: (atom) => atom.status },
|
|
48
|
+
{ name: 'source', value: (atom) => atom.provenance.sourceCommand },
|
|
49
|
+
{ name: 'domains', value: (atom) => atom.scopeRefs.domains },
|
|
50
|
+
{ name: 'files', value: (atom) => atom.scopeRefs.files },
|
|
51
|
+
{ name: 'symbols', value: (atom) => atom.scopeRefs.symbols },
|
|
52
|
+
{ name: 'summary', value: (atom) => atom.summary },
|
|
43
53
|
])
|
|
44
|
-
.map((ranked) =>
|
|
54
|
+
.map((ranked) => applyAtomRankAdjustments(ranked))
|
|
55
|
+
.map((ranked) => ({
|
|
56
|
+
...ranked,
|
|
57
|
+
item: atomToCognitionNote(ranked.item),
|
|
58
|
+
}))
|
|
45
59
|
.sort((a, b) => b.score - a.score)
|
|
46
60
|
.slice(0, max);
|
|
47
61
|
const matchedDomains = rankByFields(query, domains, [
|
|
@@ -292,33 +306,44 @@ function explainRelationships(relationships, context) {
|
|
|
292
306
|
return { relationship, reasons: [...reasons] };
|
|
293
307
|
});
|
|
294
308
|
}
|
|
295
|
-
function
|
|
309
|
+
function applyAtomRankAdjustments(ranked) {
|
|
296
310
|
const reasons = [...ranked.reasons];
|
|
297
311
|
let score = ranked.score;
|
|
298
312
|
if (ranked.item.confidence === 'high') {
|
|
313
|
+
score += 4;
|
|
314
|
+
reasons.push('high confidence atom');
|
|
315
|
+
}
|
|
316
|
+
else if (ranked.item.confidence === 'medium') {
|
|
317
|
+
score += 1;
|
|
318
|
+
reasons.push('medium confidence atom');
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
score -= 3;
|
|
322
|
+
reasons.push('low confidence penalty');
|
|
323
|
+
}
|
|
324
|
+
if (ranked.item.status === 'active') {
|
|
299
325
|
score += 3;
|
|
300
|
-
reasons.push('
|
|
326
|
+
reasons.push('active atom evidence');
|
|
301
327
|
}
|
|
302
|
-
else if (ranked.item.
|
|
328
|
+
else if (ranked.item.status === 'needs-review') {
|
|
303
329
|
score -= 2;
|
|
304
|
-
reasons.push('
|
|
330
|
+
reasons.push('needs-review stale penalty');
|
|
305
331
|
}
|
|
306
|
-
if (ranked.item.
|
|
332
|
+
else if (ranked.item.status === 'stale') {
|
|
333
|
+
score -= 6;
|
|
334
|
+
reasons.push('stale atom penalty');
|
|
335
|
+
}
|
|
336
|
+
if (ranked.item.type === 'decision' || ranked.item.type === 'gotcha') {
|
|
307
337
|
score += 2;
|
|
308
|
-
reasons.push(
|
|
338
|
+
reasons.push(`${ranked.item.type} atom`);
|
|
309
339
|
}
|
|
310
|
-
|
|
340
|
+
if (ranked.item.provenance.sourceCommand === 'legacy-migration') {
|
|
311
341
|
score -= 1;
|
|
312
|
-
reasons.push('
|
|
313
|
-
}
|
|
314
|
-
else if (ranked.item.referencesStatus === 'stale' ||
|
|
315
|
-
ranked.item.referencesStatus === 'unresolved') {
|
|
316
|
-
score -= 4;
|
|
317
|
-
reasons.push('stale reference penalty');
|
|
342
|
+
reasons.push('legacy compatibility atom');
|
|
318
343
|
}
|
|
319
|
-
if (ranked.item.
|
|
320
|
-
score
|
|
321
|
-
reasons.push(
|
|
344
|
+
if (ranked.item.lifecycle.supersededBy) {
|
|
345
|
+
score -= 8;
|
|
346
|
+
reasons.push('superseded atom penalty');
|
|
322
347
|
}
|
|
323
348
|
return { ...ranked, score, reasons };
|
|
324
349
|
}
|
|
@@ -5,7 +5,7 @@ export const claudeCodeAdapter = {
|
|
|
5
5
|
targetPath: 'CLAUDE.md',
|
|
6
6
|
instructions: `## KGraph Workflow
|
|
7
7
|
|
|
8
|
-
{{KGRAPH_CONTEXT_POLICY}} Use /kgraph for the full automated workflow. Run \`kgraph conclude\` for durable typed engineering memory and \`kgraph compact --dry-run\` when cognition looks duplicated or stale. Run \`kgraph doctor\` when setup or generated maps look wrong. Run \`kgraph scan\`, \`kgraph update\`, and \`kgraph context\` manually only when you need one specific step.
|
|
8
|
+
{{KGRAPH_CONTEXT_POLICY}} Use /kgraph for the full automated workflow. Run \`kgraph pack "<task>" --budget 8000 --json\` for a machine-readable token-budgeted context pack, \`kgraph knowledge list\` or \`kgraph knowledge get <atom-id>\` to inspect durable atoms, \`kgraph stale\` and \`kgraph blame <atom-id>\` when lifecycle/provenance matters, \`kgraph conclude\` for durable typed engineering memory, and \`kgraph compact --dry-run\` when cognition looks duplicated or stale. Run \`kgraph doctor\` when setup or generated maps look wrong. Run \`kgraph scan\`, \`kgraph update\`, and \`kgraph context\` manually only when you need one specific step.
|
|
9
9
|
`,
|
|
10
10
|
commandFiles: [
|
|
11
11
|
{
|
|
@@ -28,6 +28,26 @@ ${numberedWorkflow('claude-code', { sessionQualifier: 'when native hooks are una
|
|
|
28
28
|
{
|
|
29
29
|
path: '.claude/commands/kgraph-compact.md',
|
|
30
30
|
content: `Run \`kgraph compact --dry-run\` first and summarize duplicate cognition groups and stale low-confidence notes. Run \`kgraph compact\` only when the user asks to apply compaction.
|
|
31
|
+
`,
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
path: '.claude/commands/kgraph-pack.md',
|
|
35
|
+
content: `Run \`kgraph pack "$ARGUMENTS" --budget 8000 --json\` to build a machine-readable context pack. Summarize token use, included files, symbols, relationships, git changes, session history, atoms, and omitted items with the inclusion reasons.
|
|
36
|
+
`,
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
path: '.claude/commands/kgraph-knowledge.md',
|
|
40
|
+
content: `Use \`kgraph knowledge list\` and \`kgraph knowledge get <atom-id>\` to inspect durable atoms, evidence, provenance, and lifecycle. Run \`kgraph knowledge archive <atom-id>\` or \`kgraph knowledge supersede <old-id> <new-id>\` only when the user explicitly asks to mutate atom lifecycle.
|
|
41
|
+
`,
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
path: '.claude/commands/kgraph-stale.md',
|
|
45
|
+
content: `Run \`kgraph stale\` to refresh atom status against the current scan and summarize stale or needs-review atoms with invalidation reasons.
|
|
46
|
+
`,
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
path: '.claude/commands/kgraph-blame.md',
|
|
50
|
+
content: `Run \`kgraph blame "$ARGUMENTS"\` to show who or what created a knowledge atom, the source command/session/commit, evidence refs, and lifecycle links.
|
|
31
51
|
`,
|
|
32
52
|
},
|
|
33
53
|
{
|
|
@@ -5,7 +5,7 @@ export const codexAdapter = {
|
|
|
5
5
|
targetPath: 'AGENTS.md',
|
|
6
6
|
instructions: `## KGraph Workflow
|
|
7
7
|
|
|
8
|
-
{{KGRAPH_CONTEXT_POLICY}} The /kgraph skill handles the full automated workflow. Run \`kgraph conclude\` for durable typed engineering memory and \`kgraph compact --dry-run\` when cognition looks duplicated or stale. Run \`kgraph doctor\` when setup or generated maps look wrong. Run \`kgraph scan\`, \`kgraph update\`, and \`kgraph context\` manually only when you need one specific step.
|
|
8
|
+
{{KGRAPH_CONTEXT_POLICY}} The /kgraph skill handles the full automated workflow. Run \`kgraph pack "<task>" --budget 8000 --json\` for a machine-readable token-budgeted context pack, \`kgraph knowledge list\` or \`kgraph knowledge get <atom-id>\` to inspect durable atoms, \`kgraph stale\` and \`kgraph blame <atom-id>\` when lifecycle/provenance matters, \`kgraph conclude\` for durable typed engineering memory, and \`kgraph compact --dry-run\` when cognition looks duplicated or stale. Run \`kgraph doctor\` when setup or generated maps look wrong. Run \`kgraph scan\`, \`kgraph update\`, and \`kgraph context\` manually only when you need one specific step.
|
|
9
9
|
`,
|
|
10
10
|
commandFiles: [
|
|
11
11
|
{
|
|
@@ -12,7 +12,7 @@ ${numberedWorkflow('copilot')}
|
|
|
12
12
|
path: '.github/agents/kgraph.agent.md',
|
|
13
13
|
content: `---
|
|
14
14
|
name: kgraph
|
|
15
|
-
description: Use KGraph persistent repo intelligence to answer questions about this codebase. Runs kgraph context, scan, update, conclude, compact, impact, history, and session commands to ground responses in durable local knowledge.
|
|
15
|
+
description: Use KGraph persistent repo intelligence to answer questions about this codebase. Runs kgraph context, pack, knowledge, stale, blame, scan, update, conclude, compact, impact, history, and session commands to ground responses in durable local knowledge.
|
|
16
16
|
tools:
|
|
17
17
|
- run_in_terminal
|
|
18
18
|
- read_file
|
|
@@ -58,6 +58,49 @@ argument-hint: "--dry-run or apply"
|
|
|
58
58
|
---
|
|
59
59
|
|
|
60
60
|
Run \`kgraph compact --dry-run\` first and summarize duplicate cognition groups and stale low-confidence notes. Run \`kgraph compact\` only when the user asks to apply compaction.
|
|
61
|
+
`,
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
path: '.github/prompts/kgraph-pack.prompt.md',
|
|
65
|
+
content: `---
|
|
66
|
+
description: Build a budget-aware KGraph context pack
|
|
67
|
+
agent: agent
|
|
68
|
+
argument-hint: "Task description"
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
Run \`kgraph pack "$ARGUMENTS" --budget 8000 --json\` to build a machine-readable context pack. Summarize token use, included files, symbols, relationships, git changes, session history, atoms, and omitted items with the inclusion reasons.
|
|
72
|
+
`,
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
path: '.github/prompts/kgraph-knowledge.prompt.md',
|
|
76
|
+
content: `---
|
|
77
|
+
description: Inspect or manage KGraph canonical knowledge atoms
|
|
78
|
+
agent: agent
|
|
79
|
+
argument-hint: "list, get <atom-id>, archive <atom-id>, or supersede <old-id> <new-id>"
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
Use \`kgraph knowledge list\` and \`kgraph knowledge get <atom-id>\` to inspect durable atoms, evidence, provenance, and lifecycle. Run \`kgraph knowledge archive <atom-id>\` or \`kgraph knowledge supersede <old-id> <new-id>\` only when the user explicitly asks to mutate atom lifecycle.
|
|
83
|
+
`,
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
path: '.github/prompts/kgraph-stale.prompt.md',
|
|
87
|
+
content: `---
|
|
88
|
+
description: Show KGraph knowledge invalidated by changed or missing refs
|
|
89
|
+
agent: agent
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
Run \`kgraph stale\` to refresh atom status against the current scan and summarize stale or needs-review atoms with invalidation reasons.
|
|
93
|
+
`,
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
path: '.github/prompts/kgraph-blame.prompt.md',
|
|
97
|
+
content: `---
|
|
98
|
+
description: Show KGraph atom provenance and evidence
|
|
99
|
+
agent: agent
|
|
100
|
+
argument-hint: "Atom id"
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
Run \`kgraph blame "$ARGUMENTS"\` to show who or what created a knowledge atom, the source command/session/commit, evidence refs, and lifecycle links.
|
|
61
104
|
`,
|
|
62
105
|
},
|
|
63
106
|
{
|
|
@@ -7,6 +7,9 @@ const IMPACT_STEP = `Run \`kgraph impact "<file-or-symbol>"\` when the user asks
|
|
|
7
7
|
const REPAIR_STEP = `Run \`kgraph repair --dry-run\` before cleanup when stale/noisy cognition needs fixing. Run \`kgraph repair\` only when the user asks to apply that cleanup.`;
|
|
8
8
|
const COMPACT_STEP = `Run \`kgraph compact --dry-run\` when cognition looks duplicated, noisy, or stale. Run \`kgraph compact\` only when the user asks to merge/archive cognition.`;
|
|
9
9
|
const HISTORY_STEP = `Run \`kgraph history\` or \`kgraph history "<topic>"\` to review past cognition sessions with git author attribution.`;
|
|
10
|
+
const KNOWLEDGE_STEP = `Run \`kgraph knowledge list --topic "<topic>"\` or \`kgraph knowledge get <atom-id>\` when the user asks what KGraph remembers or atom provenance/lifecycle matters.`;
|
|
11
|
+
const PACK_STEP = `Run \`kgraph pack "<task>" --budget 8000 --json\` when an agent needs a machine-readable, token-budgeted context pack instead of human Markdown context.`;
|
|
12
|
+
const STALE_STEP = `Run \`kgraph stale\` when changed or deleted code may have invalidated durable knowledge. Run \`kgraph blame <atom-id>\` when provenance or evidence for a memory matters.`;
|
|
10
13
|
function sessionStep(agentName, qualifier) {
|
|
11
14
|
const base = `Track meaningful session activity with \`kgraph session start --agent ${agentName}\`, \`kgraph session read <path> --agent ${agentName}\`, \`kgraph session write <path> --agent ${agentName}\`, and \`kgraph session end --agent ${agentName} --conclude --topic "<topic>"\` when durable session memory is useful`;
|
|
12
15
|
return qualifier ? `${base} ${qualifier}.` : `${base}.`;
|
|
@@ -19,16 +22,19 @@ export function numberedWorkflow(agentName, options = {}) {
|
|
|
19
22
|
return `1. Infer the topic from the user's request.
|
|
20
23
|
2. {{KGRAPH_CONTEXT_POLICY}}
|
|
21
24
|
3. Use the returned files, symbols, relationships, and cognition before broad exploration.
|
|
22
|
-
4. ${
|
|
23
|
-
5. ${
|
|
24
|
-
6. ${
|
|
25
|
+
4. ${PACK_STEP}
|
|
26
|
+
5. ${KNOWLEDGE_STEP}
|
|
27
|
+
6. ${DOCTOR_STEP}
|
|
28
|
+
7. ${STALE_STEP}
|
|
29
|
+
8. ${sessionStep(agentName, options.sessionQualifier)}
|
|
30
|
+
9. ${IMPACT_STEP}
|
|
25
31
|
|
|
26
32
|
{{KGRAPH_CAPTURE_POLICY}}
|
|
27
33
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
10. ${REPAIR_STEP}
|
|
35
|
+
11. ${COMPACT_STEP}
|
|
36
|
+
12. Run \`kgraph visualize\` when the user wants to inspect the dependency graph — opens an interactive graph at http://localhost:4242 with PNG export.
|
|
37
|
+
13. ${HISTORY_STEP}`;
|
|
32
38
|
}
|
|
33
39
|
/**
|
|
34
40
|
* Returns the bullet-list workflow for rules files.
|
|
@@ -36,7 +42,10 @@ export function numberedWorkflow(agentName, options = {}) {
|
|
|
36
42
|
*/
|
|
37
43
|
export function bulletWorkflow(agentName, options = {}) {
|
|
38
44
|
return `- {{KGRAPH_CONTEXT_POLICY}}
|
|
45
|
+
- ${PACK_STEP}
|
|
46
|
+
- ${KNOWLEDGE_STEP}
|
|
39
47
|
- ${DOCTOR_STEP}
|
|
48
|
+
- ${STALE_STEP}
|
|
40
49
|
- ${sessionStep(agentName, options.sessionQualifier)}
|
|
41
50
|
- ${IMPACT_STEP}
|
|
42
51
|
{{KGRAPH_CAPTURE_POLICY}}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { CognitionConfidence, CognitionNote } from '../types/cognition.js';
|
|
2
|
+
import type { KGraphWorkspace } from '../types/config.js';
|
|
3
|
+
import type { KnowledgeAtom, KnowledgeValidationIssue } from '../types/knowledge.js';
|
|
4
|
+
import type { FileMap, SymbolMap } from '../types/maps.js';
|
|
5
|
+
export declare const KNOWLEDGE_SCHEMA_VERSION = 1;
|
|
6
|
+
export interface AtomInput {
|
|
7
|
+
type: KnowledgeAtom['type'];
|
|
8
|
+
topic: string;
|
|
9
|
+
claim: string;
|
|
10
|
+
summary?: string;
|
|
11
|
+
confidence?: CognitionConfidence;
|
|
12
|
+
files?: string[];
|
|
13
|
+
symbols?: string[];
|
|
14
|
+
domains?: string[];
|
|
15
|
+
packages?: string[];
|
|
16
|
+
sourceCommand: KnowledgeAtom['provenance']['sourceCommand'];
|
|
17
|
+
agent?: string;
|
|
18
|
+
sessionId?: string;
|
|
19
|
+
commit?: string;
|
|
20
|
+
createdAt?: string;
|
|
21
|
+
idSeed?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface AtomStatusRefreshResult {
|
|
24
|
+
atoms: KnowledgeAtom[];
|
|
25
|
+
updated: Array<{
|
|
26
|
+
atomId: string;
|
|
27
|
+
previousStatus: KnowledgeAtom['status'];
|
|
28
|
+
nextStatus: KnowledgeAtom['status'];
|
|
29
|
+
reasons: string[];
|
|
30
|
+
}>;
|
|
31
|
+
}
|
|
32
|
+
export declare function ensureKnowledgeStore(workspace: KGraphWorkspace): Promise<void>;
|
|
33
|
+
export declare function readKnowledgeAtoms(workspace: KGraphWorkspace): Promise<KnowledgeAtom[]>;
|
|
34
|
+
export declare function readAtomsFile(workspace: KGraphWorkspace): Promise<KnowledgeAtom[]>;
|
|
35
|
+
export declare function parseAtomsJsonl(raw: string): KnowledgeAtom[];
|
|
36
|
+
export declare function writeKnowledgeAtoms(workspace: KGraphWorkspace, atoms: KnowledgeAtom[]): Promise<void>;
|
|
37
|
+
export declare function appendKnowledgeAtom(workspace: KGraphWorkspace, atom: KnowledgeAtom): Promise<KnowledgeAtom>;
|
|
38
|
+
export declare function createKnowledgeAtom(workspace: KGraphWorkspace, input: AtomInput, maps?: {
|
|
39
|
+
fileMap: FileMap;
|
|
40
|
+
symbolMap: SymbolMap;
|
|
41
|
+
}): Promise<KnowledgeAtom>;
|
|
42
|
+
export declare function migrateLegacyCognitionToAtoms(workspace: KGraphWorkspace): Promise<void>;
|
|
43
|
+
export declare function updateKnowledgeAtom(workspace: KGraphWorkspace, atomId: string, updater: (atom: KnowledgeAtom) => KnowledgeAtom): Promise<KnowledgeAtom>;
|
|
44
|
+
export declare function refreshKnowledgeAtomStatuses(workspace: KGraphWorkspace, maps: {
|
|
45
|
+
fileMap: FileMap;
|
|
46
|
+
symbolMap: SymbolMap;
|
|
47
|
+
}, dryRun?: boolean): Promise<AtomStatusRefreshResult>;
|
|
48
|
+
export declare function validateKnowledgeStore(workspace: KGraphWorkspace, maps?: {
|
|
49
|
+
fileMap: FileMap;
|
|
50
|
+
symbolMap: SymbolMap;
|
|
51
|
+
}): Promise<KnowledgeValidationIssue[]>;
|
|
52
|
+
export declare function atomToCognitionNote(atom: KnowledgeAtom): CognitionNote;
|
|
53
|
+
export declare function knowledgePaths(workspace: KGraphWorkspace): {
|
|
54
|
+
atoms: string;
|
|
55
|
+
schema: string;
|
|
56
|
+
indexes: string;
|
|
57
|
+
terms: string;
|
|
58
|
+
refs: string;
|
|
59
|
+
topics: string;
|
|
60
|
+
};
|