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