pi-harness-runtime 0.10.27 → 0.10.43
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/.pi/plugins/herdr-clipboard/herdr-plugin.toml +22 -0
- package/.pi/plugins/herdr-clipboard/start-bridge.sh +29 -0
- package/.pi/plugins/herdr-clipboard/tmux-watch.sh +39 -0
- package/CHANGELOG.md +19 -0
- package/README.md +87 -1
- package/config.toml.example +35 -0
- package/package.json +5 -3
- package/packages/architecture-generator/dist/adr.d.ts +1 -1
- package/packages/architecture-generator/dist/adr.d.ts.map +1 -1
- package/packages/architecture-generator/dist/adr.js +2 -2
- package/packages/architecture-generator/dist/adr.js.map +1 -1
- package/packages/architecture-generator/dist/index.d.ts +3 -3
- package/packages/architecture-generator/dist/index.js +3 -3
- package/packages/architecture-generator/dist/mermaid.d.ts +1 -1
- package/packages/architecture-generator/dist/mermaid.d.ts.map +1 -1
- package/packages/architecture-generator/dist/mermaid.js +20 -20
- package/packages/architecture-generator/dist/mermaid.js.map +1 -1
- package/packages/architecture-generator/dist/types.d.ts +2 -2
- package/packages/architecture-generator/dist/types.d.ts.map +1 -1
- package/packages/architecture-generator/src/adr.ts +16 -16
- package/packages/architecture-generator/src/index.ts +3 -3
- package/packages/architecture-generator/src/mermaid.ts +48 -54
- package/packages/architecture-generator/src/types.ts +15 -15
- package/packages/capability-registry/src/types.js +10 -0
- package/packages/clipboard/dist/src/copy-sync.d.ts.map +1 -1
- package/packages/clipboard/dist/src/copy-sync.js +45 -3
- package/packages/clipboard/dist/src/copy-sync.js.map +1 -1
- package/packages/clipboard/dist/src/index.d.ts +1 -0
- package/packages/clipboard/dist/src/index.d.ts.map +1 -1
- package/packages/clipboard/dist/src/index.js +1 -0
- package/packages/clipboard/dist/src/index.js.map +1 -1
- package/packages/clipboard/dist/src/paste-bridge.d.ts +19 -0
- package/packages/clipboard/dist/src/paste-bridge.d.ts.map +1 -0
- package/packages/clipboard/dist/src/paste-bridge.js +78 -0
- package/packages/clipboard/dist/src/paste-bridge.js.map +1 -0
- package/packages/clipboard/src/copy-sync.ts +52 -3
- package/packages/clipboard/src/index.ts +1 -0
- package/packages/clipboard/src/paste-bridge.ts +86 -0
- package/packages/clipboard-plugin/dist/index.d.ts +28 -0
- package/packages/clipboard-plugin/dist/index.d.ts.map +1 -0
- package/packages/clipboard-plugin/dist/index.js +64 -0
- package/packages/clipboard-plugin/dist/index.js.map +1 -0
- package/packages/clipboard-plugin/dist/types.d.ts +30 -0
- package/packages/clipboard-plugin/dist/types.d.ts.map +1 -0
- package/packages/clipboard-plugin/dist/types.js +5 -0
- package/packages/clipboard-plugin/dist/types.js.map +1 -0
- package/packages/clipboard-plugin/src/index.ts +74 -0
- package/packages/clipboard-plugin/src/types.ts +30 -0
- package/packages/file-copy-helper/dist/src/extension.js +84 -25
- package/packages/file-copy-helper/src/extension.debug.ts +226 -0
- package/packages/file-copy-helper/src/extension.ts +89 -59
- package/packages/knowledge-graph/dist/graph-extractor.d.ts +1 -1
- package/packages/knowledge-graph/dist/graph-extractor.d.ts.map +1 -1
- package/packages/knowledge-graph/dist/graph-extractor.js +46 -55
- package/packages/knowledge-graph/dist/graph-extractor.js.map +1 -1
- package/packages/knowledge-graph/dist/index.d.ts +4 -4
- package/packages/knowledge-graph/dist/index.js +4 -4
- package/packages/knowledge-graph/dist/mcp-client.d.ts +2 -2
- package/packages/knowledge-graph/dist/mcp-client.d.ts.map +1 -1
- package/packages/knowledge-graph/dist/mcp-client.js +22 -22
- package/packages/knowledge-graph/dist/mcp-client.js.map +1 -1
- package/packages/knowledge-graph/dist/provenance-linker.d.ts +1 -1
- package/packages/knowledge-graph/dist/provenance-linker.d.ts.map +1 -1
- package/packages/knowledge-graph/dist/provenance-linker.js +14 -15
- package/packages/knowledge-graph/dist/provenance-linker.js.map +1 -1
- package/packages/knowledge-graph/dist/types.d.ts +2 -2
- package/packages/knowledge-graph/dist/types.d.ts.map +1 -1
- package/packages/knowledge-graph/src/graph-extractor.ts +263 -280
- package/packages/knowledge-graph/src/index.ts +4 -4
- package/packages/knowledge-graph/src/mcp-client.ts +188 -197
- package/packages/knowledge-graph/src/provenance-linker.ts +69 -73
- package/packages/knowledge-graph/src/types.ts +46 -46
- package/packages/milestone-manager/dist/engine.d.ts +1 -1
- package/packages/milestone-manager/dist/engine.d.ts.map +1 -1
- package/packages/milestone-manager/dist/engine.js +7 -9
- package/packages/milestone-manager/dist/engine.js.map +1 -1
- package/packages/milestone-manager/dist/index.d.ts +2 -2
- package/packages/milestone-manager/dist/index.js +2 -2
- package/packages/milestone-manager/dist/types.d.ts +2 -2
- package/packages/milestone-manager/dist/types.d.ts.map +1 -1
- package/packages/milestone-manager/src/engine.ts +83 -91
- package/packages/milestone-manager/src/index.ts +2 -2
- package/packages/milestone-manager/src/types.ts +13 -13
- package/packages/notification/dist/adapters/line-adapter.d.ts +17 -0
- package/packages/notification/dist/adapters/line-adapter.d.ts.map +1 -0
- package/packages/notification/dist/adapters/line-adapter.js +99 -0
- package/packages/notification/dist/adapters/line-adapter.js.map +1 -0
- package/packages/notification/dist/index.d.ts +1 -0
- package/packages/notification/dist/index.d.ts.map +1 -1
- package/packages/notification/dist/index.js +1 -0
- package/packages/notification/dist/index.js.map +1 -1
- package/packages/notification/dist/notification-center.d.ts.map +1 -1
- package/packages/notification/dist/notification-center.js +4 -0
- package/packages/notification/dist/notification-center.js.map +1 -1
- package/packages/notification/dist/types.d.ts +6 -2
- package/packages/notification/dist/types.d.ts.map +1 -1
- package/packages/notification/src/adapters/line-adapter.ts +119 -0
- package/packages/notification/src/index.ts +1 -0
- package/packages/notification/src/notification-center.ts +6 -0
- package/packages/notification/src/types.ts +7 -2
- package/packages/project-bootstrap/dist/bootstrap.d.ts +1 -1
- package/packages/project-bootstrap/dist/bootstrap.d.ts.map +1 -1
- package/packages/project-bootstrap/dist/bootstrap.js +37 -45
- package/packages/project-bootstrap/dist/bootstrap.js.map +1 -1
- package/packages/project-bootstrap/dist/frappe.d.ts +1 -1
- package/packages/project-bootstrap/dist/frappe.d.ts.map +1 -1
- package/packages/project-bootstrap/dist/frappe.js +34 -34
- package/packages/project-bootstrap/dist/frappe.js.map +1 -1
- package/packages/project-bootstrap/dist/index.d.ts +5 -5
- package/packages/project-bootstrap/dist/index.js +5 -5
- package/packages/project-bootstrap/dist/minimal.d.ts +1 -1
- package/packages/project-bootstrap/dist/minimal.js +13 -13
- package/packages/project-bootstrap/dist/minimal.js.map +1 -1
- package/packages/project-bootstrap/dist/types.d.ts +1 -1
- package/packages/project-bootstrap/dist/types.d.ts.map +1 -1
- package/packages/project-bootstrap/dist/typescript.d.ts +1 -1
- package/packages/project-bootstrap/dist/typescript.d.ts.map +1 -1
- package/packages/project-bootstrap/dist/typescript.js +38 -38
- package/packages/project-bootstrap/dist/typescript.js.map +1 -1
- package/packages/project-bootstrap/src/bootstrap.ts +121 -136
- package/packages/project-bootstrap/src/frappe.ts +65 -71
- package/packages/project-bootstrap/src/index.ts +5 -5
- package/packages/project-bootstrap/src/minimal.ts +31 -31
- package/packages/project-bootstrap/src/types.ts +38 -38
- package/packages/project-bootstrap/src/typescript.ts +69 -78
- package/packages/provider-router/dist/src/types.js +1 -1
- package/packages/write-review/src/index.ts +47 -0
- package/packages/write-review/src/injection.ts +21 -1
- package/prompts/agents/reviewer.md +57 -0
- package/prompts/agents/writer.md +42 -0
- package/prompts/slash-commands.md +40 -0
- package/prompts/write-review.md +50 -0
- package/scripts/copy-plugins.js +56 -0
|
@@ -5,242 +5,233 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
} from
|
|
8
|
+
KnowledgeNode,
|
|
9
|
+
KnowledgeEdge,
|
|
10
|
+
KnowledgeGraph,
|
|
11
|
+
SearchOptions,
|
|
12
|
+
SearchResult,
|
|
13
|
+
TencentDBConfig,
|
|
14
|
+
} from './types.js';
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Sync graph to TencentDB Knowledge Service
|
|
18
18
|
*/
|
|
19
19
|
export async function syncGraphToKnowledgeService(
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
graph: KnowledgeGraph,
|
|
21
|
+
config: TencentDBConfig
|
|
22
22
|
): Promise<{ synced: number; errors: string[] }> {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return { synced: graph.nodes.length, errors: [] };
|
|
23
|
+
const response = await fetch(`${config.knowledgeUrl}/v3/knowledge/graph/sync`, {
|
|
24
|
+
method: 'POST',
|
|
25
|
+
headers: {
|
|
26
|
+
'Content-Type': 'application/json',
|
|
27
|
+
'x-tdai-user-key': config.userKey,
|
|
28
|
+
'x-tdai-service-id': config.serviceId,
|
|
29
|
+
},
|
|
30
|
+
body: JSON.stringify({
|
|
31
|
+
nodes: graph.nodes.map(n => ({
|
|
32
|
+
id: n.id,
|
|
33
|
+
type: n.type,
|
|
34
|
+
...n.data,
|
|
35
|
+
})),
|
|
36
|
+
edges: graph.edges.map(e => ({
|
|
37
|
+
from: e.from,
|
|
38
|
+
to: e.to,
|
|
39
|
+
type: e.type,
|
|
40
|
+
})),
|
|
41
|
+
}),
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
if (!response.ok) {
|
|
45
|
+
const error = await response.text();
|
|
46
|
+
return { synced: 0, errors: [error] };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return { synced: graph.nodes.length, errors: [] };
|
|
53
50
|
}
|
|
54
51
|
|
|
55
52
|
/**
|
|
56
53
|
* Search knowledge graph
|
|
57
54
|
*/
|
|
58
55
|
export async function searchGraph(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
56
|
+
query: string,
|
|
57
|
+
config: TencentDBConfig,
|
|
58
|
+
options: SearchOptions = {}
|
|
62
59
|
): Promise<SearchResult[]> {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
},
|
|
108
|
-
score: r.score,
|
|
109
|
-
}));
|
|
60
|
+
const limit = options.limit ?? 10;
|
|
61
|
+
|
|
62
|
+
const response = await fetch(`${config.knowledgeUrl}/v3/knowledge/graph/search`, {
|
|
63
|
+
method: 'POST',
|
|
64
|
+
headers: {
|
|
65
|
+
'Content-Type': 'application/json',
|
|
66
|
+
'x-tdai-user-key': config.userKey,
|
|
67
|
+
'x-tdai-service-id': config.serviceId,
|
|
68
|
+
},
|
|
69
|
+
body: JSON.stringify({
|
|
70
|
+
query,
|
|
71
|
+
limit,
|
|
72
|
+
type: options.type,
|
|
73
|
+
tags: options.tags,
|
|
74
|
+
}),
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
if (!response.ok) {
|
|
78
|
+
throw new Error(`Search failed: ${response.statusText}`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const data = (await response.json()) as {
|
|
82
|
+
results: Array<{
|
|
83
|
+
id: string;
|
|
84
|
+
type: string;
|
|
85
|
+
title: string;
|
|
86
|
+
description?: string;
|
|
87
|
+
tags: string[];
|
|
88
|
+
score: number;
|
|
89
|
+
}>;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
return data.results.map(r => ({
|
|
93
|
+
node: {
|
|
94
|
+
id: r.id,
|
|
95
|
+
type: r.type as KnowledgeNode['type'],
|
|
96
|
+
data: {
|
|
97
|
+
title: r.title,
|
|
98
|
+
description: r.description,
|
|
99
|
+
tags: r.tags,
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
score: r.score,
|
|
103
|
+
}));
|
|
110
104
|
}
|
|
111
105
|
|
|
112
106
|
/**
|
|
113
107
|
* Query graph by relationship type
|
|
114
108
|
*/
|
|
115
109
|
export async function queryRelationships(
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
110
|
+
config: TencentDBConfig,
|
|
111
|
+
options: {
|
|
112
|
+
type?: KnowledgeEdge['type'];
|
|
113
|
+
from?: string;
|
|
114
|
+
to?: string;
|
|
115
|
+
}
|
|
122
116
|
): Promise<KnowledgeEdge[]> {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
to: e.to,
|
|
151
|
-
type: e.type as KnowledgeEdge["type"],
|
|
152
|
-
}));
|
|
117
|
+
const response = await fetch(`${config.knowledgeUrl}/v3/knowledge/graph/query`, {
|
|
118
|
+
method: 'POST',
|
|
119
|
+
headers: {
|
|
120
|
+
'Content-Type': 'application/json',
|
|
121
|
+
'x-tdai-user-key': config.userKey,
|
|
122
|
+
'x-tdai-service-id': config.serviceId,
|
|
123
|
+
},
|
|
124
|
+
body: JSON.stringify(options),
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
if (!response.ok) {
|
|
128
|
+
throw new Error(`Query failed: ${response.statusText}`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const data = (await response.json()) as {
|
|
132
|
+
edges: Array<{
|
|
133
|
+
from: string;
|
|
134
|
+
to: string;
|
|
135
|
+
type: string;
|
|
136
|
+
}>;
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
return data.edges.map(e => ({
|
|
140
|
+
from: e.from,
|
|
141
|
+
to: e.to,
|
|
142
|
+
type: e.type as KnowledgeEdge['type'],
|
|
143
|
+
}));
|
|
153
144
|
}
|
|
154
145
|
|
|
155
146
|
/**
|
|
156
147
|
* Get a specific node by ID
|
|
157
148
|
*/
|
|
158
149
|
export async function getNode(
|
|
159
|
-
|
|
160
|
-
|
|
150
|
+
nodeId: string,
|
|
151
|
+
config: TencentDBConfig
|
|
161
152
|
): Promise<KnowledgeNode | null> {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
153
|
+
const response = await fetch(
|
|
154
|
+
`${config.knowledgeUrl}/v3/knowledge/graph/node/${encodeURIComponent(nodeId)}`,
|
|
155
|
+
{
|
|
156
|
+
headers: {
|
|
157
|
+
'x-tdai-user-key': config.userKey,
|
|
158
|
+
'x-tdai-service-id': config.serviceId,
|
|
159
|
+
},
|
|
160
|
+
}
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
if (response.status === 404) {
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (!response.ok) {
|
|
168
|
+
throw new Error(`Get node failed: ${response.statusText}`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const data = (await response.json()) as {
|
|
172
|
+
id: string;
|
|
173
|
+
type: string;
|
|
174
|
+
title: string;
|
|
175
|
+
description?: string;
|
|
176
|
+
tags: string[];
|
|
177
|
+
source?: string;
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
id: data.id,
|
|
182
|
+
type: data.type as KnowledgeNode['type'],
|
|
183
|
+
data: {
|
|
184
|
+
title: data.title,
|
|
185
|
+
description: data.description,
|
|
186
|
+
tags: data.tags,
|
|
187
|
+
source: data.source,
|
|
188
|
+
},
|
|
189
|
+
};
|
|
199
190
|
}
|
|
200
191
|
|
|
201
192
|
/**
|
|
202
193
|
* Find skills that implement a specific RFC
|
|
203
194
|
*/
|
|
204
195
|
export async function findByRFC(
|
|
205
|
-
|
|
206
|
-
|
|
196
|
+
rfcId: string,
|
|
197
|
+
config: TencentDBConfig
|
|
207
198
|
): Promise<KnowledgeNode[]> {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
199
|
+
const edges = await queryRelationships(config, {
|
|
200
|
+
type: 'implements',
|
|
201
|
+
to: rfcId.startsWith('rfc:') ? rfcId : `rfc:${rfcId}`,
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
const nodes: KnowledgeNode[] = [];
|
|
205
|
+
for (const edge of edges) {
|
|
206
|
+
const node = await getNode(edge.from, config);
|
|
207
|
+
if (node) {
|
|
208
|
+
nodes.push(node);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return nodes;
|
|
222
213
|
}
|
|
223
214
|
|
|
224
215
|
/**
|
|
225
216
|
* Find related skills
|
|
226
217
|
*/
|
|
227
218
|
export async function findRelated(
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
219
|
+
skillId: string,
|
|
220
|
+
config: TencentDBConfig,
|
|
221
|
+
maxResults = 5
|
|
231
222
|
): Promise<KnowledgeNode[]> {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
223
|
+
const edges = await queryRelationships(config, {
|
|
224
|
+
type: 'related_to',
|
|
225
|
+
from: skillId.startsWith('skill:') ? skillId : `skill:${skillId}`,
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
const nodes: KnowledgeNode[] = [];
|
|
229
|
+
for (const edge of edges.slice(0, maxResults)) {
|
|
230
|
+
const node = await getNode(edge.to, config);
|
|
231
|
+
if (node) {
|
|
232
|
+
nodes.push(node);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return nodes;
|
|
246
237
|
}
|
|
@@ -4,102 +4,98 @@
|
|
|
4
4
|
* Provenance linking between skills, RFCs, and implementations.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import type { KnowledgeNode, KnowledgeEdge, KnowledgeGraph } from
|
|
7
|
+
import type { KnowledgeNode, KnowledgeEdge, KnowledgeGraph } from './types.js';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Build provenance edges based on metadata
|
|
11
11
|
*/
|
|
12
12
|
export function buildProvenanceEdges(graph: KnowledgeGraph): KnowledgeEdge[] {
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
const edges: KnowledgeEdge[] = [];
|
|
14
|
+
const nodeMap = new Map(graph.nodes.map(n => [n.id, n]));
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
16
|
+
for (const node of graph.nodes) {
|
|
17
|
+
if (node.type === 'skill' && node.data.tags.includes('implements')) {
|
|
18
|
+
// Link to RFCs found in tags
|
|
19
|
+
const rfcTag = node.data.tags.find(t => t.startsWith('rfc-'));
|
|
20
|
+
if (rfcTag) {
|
|
21
|
+
edges.push({
|
|
22
|
+
from: node.id,
|
|
23
|
+
to: `rfc:${rfcTag.toUpperCase()}`,
|
|
24
|
+
type: 'implements',
|
|
25
|
+
metadata: { source: 'tag:implements' },
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
30
|
+
// Implementation -> documents -> RFC
|
|
31
|
+
if (node.type === 'implementation') {
|
|
32
|
+
const rfcMatch = node.id.match(/implementation:RFC-(\d+)/i);
|
|
33
|
+
if (rfcMatch) {
|
|
34
|
+
edges.push({
|
|
35
|
+
from: node.id,
|
|
36
|
+
to: `rfc:RFC-${rfcMatch[1].padStart(4, '0')}`,
|
|
37
|
+
type: 'documents',
|
|
38
|
+
metadata: { source: 'id:parsing' },
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
return edges;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
/**
|
|
48
48
|
* Infer additional edges from content analysis
|
|
49
49
|
*/
|
|
50
50
|
export function inferRelationships(graph: KnowledgeGraph): KnowledgeEdge[] {
|
|
51
|
-
|
|
51
|
+
const edges: KnowledgeEdge[] = [];
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
53
|
+
// Group skills by shared tags
|
|
54
|
+
const tagGroups = new Map<string, KnowledgeNode[]>();
|
|
55
|
+
for (const node of graph.nodes) {
|
|
56
|
+
if (node.type !== 'skill') continue;
|
|
57
|
+
for (const tag of node.data.tags) {
|
|
58
|
+
if (!tagGroups.has(tag)) {
|
|
59
|
+
tagGroups.set(tag, []);
|
|
60
|
+
}
|
|
61
|
+
tagGroups.get(tag)!.push(node);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
// Skills sharing 3+ tags are related
|
|
66
|
+
for (const [, nodes] of tagGroups) {
|
|
67
|
+
if (nodes.length < 2) continue;
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
69
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
70
|
+
for (let j = i + 1; j < nodes.length; j++) {
|
|
71
|
+
const sharedTags = nodes[i].data.tags.filter(t => nodes[j].data.tags.includes(t));
|
|
72
|
+
if (sharedTags.length >= 3) {
|
|
73
|
+
edges.push({
|
|
74
|
+
from: nodes[i].id,
|
|
75
|
+
to: nodes[j].id,
|
|
76
|
+
type: 'related_to',
|
|
77
|
+
metadata: {
|
|
78
|
+
source: 'inferred:shared-tags',
|
|
79
|
+
confidence: sharedTags.length / Math.max(nodes[i].data.tags.length, nodes[j].data.tags.length),
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
90
86
|
|
|
91
|
-
|
|
87
|
+
return edges;
|
|
92
88
|
}
|
|
93
89
|
|
|
94
90
|
/**
|
|
95
91
|
* Complete graph with all provenance links
|
|
96
92
|
*/
|
|
97
93
|
export function completeGraph(graph: KnowledgeGraph): KnowledgeGraph {
|
|
98
|
-
|
|
99
|
-
|
|
94
|
+
const provenanceEdges = buildProvenanceEdges(graph);
|
|
95
|
+
const inferredEdges = inferRelationships(graph);
|
|
100
96
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
97
|
+
return {
|
|
98
|
+
nodes: graph.nodes,
|
|
99
|
+
edges: [...graph.edges, ...provenanceEdges, ...inferredEdges],
|
|
100
|
+
};
|
|
105
101
|
}
|