@svashevchenko/ez-know 0.1.0
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 +91 -0
- package/dist/cli.js +4709 -0
- package/dist/cli.js.map +7 -0
- package/dist/guides/business-rules.md +47 -0
- package/dist/guides/capabilities.md +41 -0
- package/dist/guides/conflicts.md +16 -0
- package/dist/guides/constraints.md +35 -0
- package/dist/guides/domains.md +17 -0
- package/dist/guides/entities.md +44 -0
- package/dist/guides/evidence.md +16 -0
- package/dist/guides/features.md +43 -0
- package/dist/guides/questions.md +17 -0
- package/dist/guides/rationales.md +17 -0
- package/dist/guides/relationships.md +19 -0
- package/dist/public/assets/index-BvKMODjW.js +331 -0
- package/dist/public/assets/index-CzQmD36n.css +1 -0
- package/dist/public/index.html +17 -0
- package/dist/reference/ez-know-extract-knowledge-from-code/SKILL.md +339 -0
- package/dist/reference/ez-know-update-knowledge-base/SKILL.md +312 -0
- package/dist/rest/server.js +3237 -0
- package/dist/rest/server.js.map +7 -0
- package/dist/server.js +3357 -0
- package/dist/server.js.map +7 -0
- package/package.json +73 -0
package/dist/server.js
ADDED
|
@@ -0,0 +1,3357 @@
|
|
|
1
|
+
// packages/mcp/src/run.ts
|
|
2
|
+
import { McpServer, StdioServerTransport } from "@modelcontextprotocol/server";
|
|
3
|
+
|
|
4
|
+
// packages/core/src/knowledge/schema.ts
|
|
5
|
+
import { extendZodWithOpenApi } from "@asteasolutions/zod-to-openapi";
|
|
6
|
+
import { z } from "zod/v4";
|
|
7
|
+
extendZodWithOpenApi(z);
|
|
8
|
+
var confidenceSchema = z.object({
|
|
9
|
+
score: z.number().min(0).max(1),
|
|
10
|
+
level: z.enum(["Low", "Medium", "High"]),
|
|
11
|
+
reason: z.string().optional()
|
|
12
|
+
});
|
|
13
|
+
var certaintySchema = z.enum([
|
|
14
|
+
"confirmed",
|
|
15
|
+
"direct_evidence",
|
|
16
|
+
"inferred",
|
|
17
|
+
"weak_inference",
|
|
18
|
+
"unknown"
|
|
19
|
+
]);
|
|
20
|
+
var withConfidenceSchema = z.object({
|
|
21
|
+
certainty: certaintySchema,
|
|
22
|
+
confidence: confidenceSchema,
|
|
23
|
+
evidence_ids: z.array(z.string()),
|
|
24
|
+
questions: z.array(z.string())
|
|
25
|
+
});
|
|
26
|
+
var evidenceSchema = z.object({
|
|
27
|
+
id: z.string(),
|
|
28
|
+
source_type: z.enum([
|
|
29
|
+
"code",
|
|
30
|
+
"test",
|
|
31
|
+
"ui_text",
|
|
32
|
+
"translation",
|
|
33
|
+
"api",
|
|
34
|
+
"route",
|
|
35
|
+
"schema",
|
|
36
|
+
"comment",
|
|
37
|
+
"documentation",
|
|
38
|
+
"repowise_search",
|
|
39
|
+
"human",
|
|
40
|
+
"increment_description",
|
|
41
|
+
"git_diff"
|
|
42
|
+
]),
|
|
43
|
+
description: z.string(),
|
|
44
|
+
location: z.object({
|
|
45
|
+
path: z.string().optional(),
|
|
46
|
+
symbol: z.string().optional(),
|
|
47
|
+
route: z.string().optional(),
|
|
48
|
+
api: z.string().optional(),
|
|
49
|
+
query: z.string().optional()
|
|
50
|
+
}).optional(),
|
|
51
|
+
supports: z.array(z.string()),
|
|
52
|
+
excerpt: z.string().optional(),
|
|
53
|
+
confidence: confidenceSchema
|
|
54
|
+
});
|
|
55
|
+
var domainSchema = withConfidenceSchema.extend({
|
|
56
|
+
id: z.string(),
|
|
57
|
+
name: z.string(),
|
|
58
|
+
purpose: z.string(),
|
|
59
|
+
why_it_exists: z.string().optional(),
|
|
60
|
+
entity_ids: z.array(z.string()),
|
|
61
|
+
capability_ids: z.array(z.string())
|
|
62
|
+
});
|
|
63
|
+
var entityTechnicalRepresentationSchema = z.object({
|
|
64
|
+
kind: z.enum([
|
|
65
|
+
"type",
|
|
66
|
+
"api",
|
|
67
|
+
"route",
|
|
68
|
+
"database",
|
|
69
|
+
"ui_label",
|
|
70
|
+
"translation",
|
|
71
|
+
"unknown"
|
|
72
|
+
]),
|
|
73
|
+
name: z.string(),
|
|
74
|
+
location_hint: z.string().optional()
|
|
75
|
+
});
|
|
76
|
+
var entitySchema = withConfidenceSchema.extend({
|
|
77
|
+
id: z.string(),
|
|
78
|
+
name: z.string(),
|
|
79
|
+
business_definition: z.string(),
|
|
80
|
+
description: z.string(),
|
|
81
|
+
purpose: z.string().optional(),
|
|
82
|
+
aliases: z.array(z.string()),
|
|
83
|
+
ambiguous_terms: z.array(z.string()),
|
|
84
|
+
technical_representations: z.array(entityTechnicalRepresentationSchema),
|
|
85
|
+
domain_ids: z.array(z.string()),
|
|
86
|
+
state_values: z.array(z.string()).optional(),
|
|
87
|
+
relationship_ids: z.array(z.string()),
|
|
88
|
+
capability_ids: z.array(z.string()),
|
|
89
|
+
feature_ids: z.array(z.string()),
|
|
90
|
+
rule_ids: z.array(z.string())
|
|
91
|
+
});
|
|
92
|
+
var relationshipSchema = withConfidenceSchema.extend({
|
|
93
|
+
id: z.string(),
|
|
94
|
+
from_entity_id: z.string(),
|
|
95
|
+
to_entity_id: z.string(),
|
|
96
|
+
relationship_type: z.enum([
|
|
97
|
+
"has_many",
|
|
98
|
+
"has_one",
|
|
99
|
+
"belongs_to",
|
|
100
|
+
"depends_on",
|
|
101
|
+
"references",
|
|
102
|
+
"transitions_to",
|
|
103
|
+
"unknown"
|
|
104
|
+
]),
|
|
105
|
+
description: z.string()
|
|
106
|
+
});
|
|
107
|
+
var capabilitySchema = withConfidenceSchema.extend({
|
|
108
|
+
id: z.string(),
|
|
109
|
+
name: z.string(),
|
|
110
|
+
description: z.string(),
|
|
111
|
+
why_it_exists: z.string().optional(),
|
|
112
|
+
domain_ids: z.array(z.string()),
|
|
113
|
+
entity_ids: z.array(z.string()),
|
|
114
|
+
parent_capability_ids: z.array(z.string()),
|
|
115
|
+
child_capability_ids: z.array(z.string()),
|
|
116
|
+
feature_ids: z.array(z.string())
|
|
117
|
+
});
|
|
118
|
+
var featureSchema = withConfidenceSchema.extend({
|
|
119
|
+
id: z.string(),
|
|
120
|
+
name: z.string(),
|
|
121
|
+
description: z.string(),
|
|
122
|
+
purpose: z.string().optional(),
|
|
123
|
+
why_it_exists: z.string().optional(),
|
|
124
|
+
domain_ids: z.array(z.string()),
|
|
125
|
+
capability_ids: z.array(z.string()),
|
|
126
|
+
entity_ids: z.array(z.string()),
|
|
127
|
+
rule_ids: z.array(z.string()),
|
|
128
|
+
constraint_ids: z.array(z.string())
|
|
129
|
+
});
|
|
130
|
+
var ruleConditionSchema = z.object({
|
|
131
|
+
subject: z.string(),
|
|
132
|
+
field: z.string().optional(),
|
|
133
|
+
operator: z.enum([
|
|
134
|
+
"equals",
|
|
135
|
+
"not_equals",
|
|
136
|
+
"contains",
|
|
137
|
+
"not_contains",
|
|
138
|
+
"exists",
|
|
139
|
+
"not_exists",
|
|
140
|
+
"greater_than",
|
|
141
|
+
"less_than",
|
|
142
|
+
"succeeds",
|
|
143
|
+
"fails",
|
|
144
|
+
"unknown"
|
|
145
|
+
]),
|
|
146
|
+
value: z.unknown().optional()
|
|
147
|
+
});
|
|
148
|
+
var ruleTriggerSchema = z.object({
|
|
149
|
+
actor: z.string(),
|
|
150
|
+
action: z.string(),
|
|
151
|
+
subject: z.string().optional(),
|
|
152
|
+
field: z.string().optional(),
|
|
153
|
+
operator: z.enum([
|
|
154
|
+
"equals",
|
|
155
|
+
"not_equals",
|
|
156
|
+
"contains",
|
|
157
|
+
"not_contains",
|
|
158
|
+
"exists",
|
|
159
|
+
"not_exists",
|
|
160
|
+
"greater_than",
|
|
161
|
+
"less_than",
|
|
162
|
+
"succeeds",
|
|
163
|
+
"fails",
|
|
164
|
+
"unknown"
|
|
165
|
+
]).optional(),
|
|
166
|
+
value: z.unknown().optional()
|
|
167
|
+
});
|
|
168
|
+
var ruleEffectSchema = z.object({
|
|
169
|
+
effect: z.enum([
|
|
170
|
+
"allow_action",
|
|
171
|
+
"disallow_action",
|
|
172
|
+
"hide_action",
|
|
173
|
+
"disable_action",
|
|
174
|
+
"require_action",
|
|
175
|
+
"show_message",
|
|
176
|
+
"change_state",
|
|
177
|
+
"calculate_value",
|
|
178
|
+
"unknown"
|
|
179
|
+
]),
|
|
180
|
+
action: z.string().optional(),
|
|
181
|
+
message: z.string().optional(),
|
|
182
|
+
target_state: z.string().optional(),
|
|
183
|
+
value: z.unknown().optional()
|
|
184
|
+
});
|
|
185
|
+
var businessRuleSchema = withConfidenceSchema.extend({
|
|
186
|
+
id: z.string(),
|
|
187
|
+
name: z.string(),
|
|
188
|
+
statement: z.string(),
|
|
189
|
+
rule_type: z.enum([
|
|
190
|
+
"validation",
|
|
191
|
+
"permission",
|
|
192
|
+
"state_transition",
|
|
193
|
+
"visibility",
|
|
194
|
+
"workflow",
|
|
195
|
+
"calculation",
|
|
196
|
+
"integration",
|
|
197
|
+
"integration_behavior",
|
|
198
|
+
"data_consistency",
|
|
199
|
+
"acceptance_criterion",
|
|
200
|
+
"unknown"
|
|
201
|
+
]),
|
|
202
|
+
behavior_type: z.enum([
|
|
203
|
+
"allow",
|
|
204
|
+
"disallow",
|
|
205
|
+
"require",
|
|
206
|
+
"calculate",
|
|
207
|
+
"show",
|
|
208
|
+
"hide",
|
|
209
|
+
"transition",
|
|
210
|
+
"notify",
|
|
211
|
+
"block",
|
|
212
|
+
"default",
|
|
213
|
+
"unknown"
|
|
214
|
+
]),
|
|
215
|
+
applies_to: z.object({
|
|
216
|
+
domain_ids: z.array(z.string()).optional(),
|
|
217
|
+
entity_ids: z.array(z.string()).optional(),
|
|
218
|
+
capability_ids: z.array(z.string()).optional(),
|
|
219
|
+
feature_ids: z.array(z.string()).optional()
|
|
220
|
+
}),
|
|
221
|
+
condition: z.object({
|
|
222
|
+
given: z.array(ruleConditionSchema),
|
|
223
|
+
when: z.array(ruleTriggerSchema),
|
|
224
|
+
then: z.array(ruleEffectSchema)
|
|
225
|
+
}).optional(),
|
|
226
|
+
enforcement: z.object({
|
|
227
|
+
level: z.enum([
|
|
228
|
+
"ui",
|
|
229
|
+
"api",
|
|
230
|
+
"backend",
|
|
231
|
+
"database",
|
|
232
|
+
"external_system",
|
|
233
|
+
"unknown"
|
|
234
|
+
]),
|
|
235
|
+
description: z.string().optional()
|
|
236
|
+
}).optional(),
|
|
237
|
+
related_constraint_ids: z.array(z.string()),
|
|
238
|
+
rationale_ids: z.array(z.string())
|
|
239
|
+
});
|
|
240
|
+
var constraintSchema = withConfidenceSchema.extend({
|
|
241
|
+
id: z.string(),
|
|
242
|
+
name: z.string(),
|
|
243
|
+
description: z.string(),
|
|
244
|
+
constraint_type: z.enum([
|
|
245
|
+
"business",
|
|
246
|
+
"technical",
|
|
247
|
+
"regulatory",
|
|
248
|
+
"compliance",
|
|
249
|
+
"integration",
|
|
250
|
+
"legacy",
|
|
251
|
+
"architecture",
|
|
252
|
+
"infrastructure",
|
|
253
|
+
"performance",
|
|
254
|
+
"security",
|
|
255
|
+
"operational",
|
|
256
|
+
"data_model",
|
|
257
|
+
"unknown"
|
|
258
|
+
]),
|
|
259
|
+
cause: z.object({
|
|
260
|
+
source: z.string(),
|
|
261
|
+
source_type: z.enum([
|
|
262
|
+
"external_system",
|
|
263
|
+
"database",
|
|
264
|
+
"framework",
|
|
265
|
+
"infrastructure",
|
|
266
|
+
"architecture",
|
|
267
|
+
"policy",
|
|
268
|
+
"regulation",
|
|
269
|
+
"business_process",
|
|
270
|
+
"legacy_code",
|
|
271
|
+
"unknown"
|
|
272
|
+
]),
|
|
273
|
+
description: z.string()
|
|
274
|
+
}).optional(),
|
|
275
|
+
impact: z.object({
|
|
276
|
+
affected_change_types: z.array(z.string()),
|
|
277
|
+
severity: z.enum(["low", "medium", "high", "critical", "unknown"]),
|
|
278
|
+
description: z.string()
|
|
279
|
+
}).optional(),
|
|
280
|
+
applies_to: z.object({
|
|
281
|
+
domain_ids: z.array(z.string()).optional(),
|
|
282
|
+
entity_ids: z.array(z.string()).optional(),
|
|
283
|
+
capability_ids: z.array(z.string()).optional(),
|
|
284
|
+
feature_ids: z.array(z.string()).optional(),
|
|
285
|
+
business_rule_ids: z.array(z.string()).optional()
|
|
286
|
+
}),
|
|
287
|
+
workaround: z.object({
|
|
288
|
+
exists: z.enum(["yes", "no", "unknown"]),
|
|
289
|
+
description: z.string().optional()
|
|
290
|
+
}).optional(),
|
|
291
|
+
removability: z.object({
|
|
292
|
+
can_be_removed_by_refactor: z.enum(["yes", "no", "partially", "unknown"]),
|
|
293
|
+
requires: z.array(z.string()),
|
|
294
|
+
risk: z.enum(["low", "medium", "high", "critical", "unknown"])
|
|
295
|
+
}).optional()
|
|
296
|
+
});
|
|
297
|
+
var rationaleSchema = withConfidenceSchema.extend({
|
|
298
|
+
id: z.string(),
|
|
299
|
+
statement: z.string(),
|
|
300
|
+
explains: z.array(z.string())
|
|
301
|
+
});
|
|
302
|
+
var questionSchema = z.object({
|
|
303
|
+
id: z.string(),
|
|
304
|
+
question: z.string(),
|
|
305
|
+
category: z.enum([
|
|
306
|
+
"rationale",
|
|
307
|
+
"business_rule",
|
|
308
|
+
"constraint",
|
|
309
|
+
"terminology",
|
|
310
|
+
"domain_boundary",
|
|
311
|
+
"lifecycle",
|
|
312
|
+
"permission",
|
|
313
|
+
"legacy",
|
|
314
|
+
"ownership",
|
|
315
|
+
"enforcement"
|
|
316
|
+
]),
|
|
317
|
+
reason: z.string(),
|
|
318
|
+
impact: z.string().optional(),
|
|
319
|
+
related_ids: z.array(z.string()),
|
|
320
|
+
evidence_ids: z.array(z.string()),
|
|
321
|
+
priority: z.enum(["low", "medium", "high"]),
|
|
322
|
+
status: z.enum(["open", "answered", "obsolete"]),
|
|
323
|
+
answer: z.string().nullable().optional(),
|
|
324
|
+
answered_by: z.string().nullable().optional(),
|
|
325
|
+
answered_at: z.string().nullable().optional()
|
|
326
|
+
});
|
|
327
|
+
var conflictSchema = z.object({
|
|
328
|
+
id: z.string(),
|
|
329
|
+
type: z.enum([
|
|
330
|
+
"domain_boundary",
|
|
331
|
+
"terminology",
|
|
332
|
+
"rule_conflict",
|
|
333
|
+
"rationale_conflict",
|
|
334
|
+
"entity_duplicate",
|
|
335
|
+
"relationship_conflict"
|
|
336
|
+
]),
|
|
337
|
+
description: z.string(),
|
|
338
|
+
related_ids: z.array(z.string()),
|
|
339
|
+
evidence_ids: z.array(z.string()),
|
|
340
|
+
status: z.enum(["needs_human_input", "resolved", "obsolete"]),
|
|
341
|
+
questions: z.array(z.string()),
|
|
342
|
+
resolution: z.string().optional(),
|
|
343
|
+
resolved_by_evidence_id: z.string().optional()
|
|
344
|
+
});
|
|
345
|
+
var knowledgeCollectionSchemas = {
|
|
346
|
+
domains: z.array(domainSchema),
|
|
347
|
+
entities: z.array(entitySchema),
|
|
348
|
+
relationships: z.array(relationshipSchema),
|
|
349
|
+
capabilities: z.array(capabilitySchema),
|
|
350
|
+
features: z.array(featureSchema),
|
|
351
|
+
business_rules: z.array(businessRuleSchema),
|
|
352
|
+
constraints: z.array(constraintSchema),
|
|
353
|
+
rationales: z.array(rationaleSchema),
|
|
354
|
+
evidence: z.array(evidenceSchema),
|
|
355
|
+
questions: z.array(questionSchema),
|
|
356
|
+
conflicts: z.array(conflictSchema)
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
// packages/core/src/knowledge/extraction-registry.ts
|
|
360
|
+
var KNOWLEDGE_EXTRACTION_TYPE_REGISTRY = {
|
|
361
|
+
domains: {
|
|
362
|
+
collection: "domains",
|
|
363
|
+
title: "Domains",
|
|
364
|
+
storageFile: "domain.json",
|
|
365
|
+
idConvention: {
|
|
366
|
+
pattern: "domain.<domain-slug>",
|
|
367
|
+
prefix: "domain",
|
|
368
|
+
example: "domain.orders"
|
|
369
|
+
},
|
|
370
|
+
schemaName: "Domain",
|
|
371
|
+
schema: domainSchema,
|
|
372
|
+
guideFilePath: "packages/core/src/knowledge/guides/domains.md",
|
|
373
|
+
aliases: []
|
|
374
|
+
},
|
|
375
|
+
entities: {
|
|
376
|
+
collection: "entities",
|
|
377
|
+
title: "Entities",
|
|
378
|
+
storageFile: "entities.json",
|
|
379
|
+
idConvention: {
|
|
380
|
+
pattern: "entity.<domain-slug>.<entity-slug>",
|
|
381
|
+
prefix: "entity",
|
|
382
|
+
example: "entity.orders.order"
|
|
383
|
+
},
|
|
384
|
+
schemaName: "Entity",
|
|
385
|
+
schema: entitySchema,
|
|
386
|
+
guideFilePath: "packages/core/src/knowledge/guides/entities.md",
|
|
387
|
+
aliases: []
|
|
388
|
+
},
|
|
389
|
+
relationships: {
|
|
390
|
+
collection: "relationships",
|
|
391
|
+
title: "Relationships",
|
|
392
|
+
storageFile: "relationships.json",
|
|
393
|
+
idConvention: {
|
|
394
|
+
pattern: "rel.<domain-slug>.<relationship-slug>",
|
|
395
|
+
prefix: "rel",
|
|
396
|
+
example: "rel.orders.order-has-items"
|
|
397
|
+
},
|
|
398
|
+
schemaName: "EntityRelationship",
|
|
399
|
+
schema: relationshipSchema,
|
|
400
|
+
guideFilePath: "packages/core/src/knowledge/guides/relationships.md",
|
|
401
|
+
aliases: ["entity-relationships"]
|
|
402
|
+
},
|
|
403
|
+
capabilities: {
|
|
404
|
+
collection: "capabilities",
|
|
405
|
+
title: "Capabilities",
|
|
406
|
+
storageFile: "capabilities.json",
|
|
407
|
+
idConvention: {
|
|
408
|
+
pattern: "cap.<domain-slug>.<capability-slug>",
|
|
409
|
+
prefix: "cap",
|
|
410
|
+
example: "cap.orders.order-lifecycle"
|
|
411
|
+
},
|
|
412
|
+
schemaName: "Capability",
|
|
413
|
+
schema: capabilitySchema,
|
|
414
|
+
guideFilePath: "packages/core/src/knowledge/guides/capabilities.md",
|
|
415
|
+
aliases: []
|
|
416
|
+
},
|
|
417
|
+
features: {
|
|
418
|
+
collection: "features",
|
|
419
|
+
title: "Features",
|
|
420
|
+
storageFile: "features.json",
|
|
421
|
+
idConvention: {
|
|
422
|
+
pattern: "feature.<domain-slug>.<feature-slug>",
|
|
423
|
+
prefix: "feature",
|
|
424
|
+
example: "feature.orders.cancel-order"
|
|
425
|
+
},
|
|
426
|
+
schemaName: "Feature",
|
|
427
|
+
schema: featureSchema,
|
|
428
|
+
guideFilePath: "packages/core/src/knowledge/guides/features.md",
|
|
429
|
+
aliases: []
|
|
430
|
+
},
|
|
431
|
+
business_rules: {
|
|
432
|
+
collection: "business_rules",
|
|
433
|
+
title: "Business Rules",
|
|
434
|
+
storageFile: "business-rules.json",
|
|
435
|
+
idConvention: {
|
|
436
|
+
pattern: "rule.<domain-slug>.<rule-slug>",
|
|
437
|
+
prefix: "rule",
|
|
438
|
+
example: "rule.orders.no-direct-cancel-paid-order"
|
|
439
|
+
},
|
|
440
|
+
schemaName: "BusinessRule",
|
|
441
|
+
schema: businessRuleSchema,
|
|
442
|
+
guideFilePath: "packages/core/src/knowledge/guides/business-rules.md",
|
|
443
|
+
aliases: ["business-rules"]
|
|
444
|
+
},
|
|
445
|
+
constraints: {
|
|
446
|
+
collection: "constraints",
|
|
447
|
+
title: "Constraints",
|
|
448
|
+
storageFile: "constraints.json",
|
|
449
|
+
idConvention: {
|
|
450
|
+
pattern: "constraint.<domain-slug>.<constraint-slug>",
|
|
451
|
+
prefix: "constraint",
|
|
452
|
+
example: "constraint.orders.erp-sync-one-way"
|
|
453
|
+
},
|
|
454
|
+
schemaName: "Constraint",
|
|
455
|
+
schema: constraintSchema,
|
|
456
|
+
guideFilePath: "packages/core/src/knowledge/guides/constraints.md",
|
|
457
|
+
aliases: []
|
|
458
|
+
},
|
|
459
|
+
rationales: {
|
|
460
|
+
collection: "rationales",
|
|
461
|
+
title: "Rationales",
|
|
462
|
+
storageFile: "rationales.json",
|
|
463
|
+
idConvention: {
|
|
464
|
+
pattern: "why.<domain-slug>.<rationale-slug>",
|
|
465
|
+
prefix: "why",
|
|
466
|
+
example: "why.orders.refund-required-for-paid-order"
|
|
467
|
+
},
|
|
468
|
+
schemaName: "Rationale",
|
|
469
|
+
schema: rationaleSchema,
|
|
470
|
+
guideFilePath: "packages/core/src/knowledge/guides/rationales.md",
|
|
471
|
+
aliases: []
|
|
472
|
+
},
|
|
473
|
+
evidence: {
|
|
474
|
+
collection: "evidence",
|
|
475
|
+
title: "Evidence",
|
|
476
|
+
storageFile: "evidence.json",
|
|
477
|
+
idConvention: {
|
|
478
|
+
pattern: "ev.<domain-slug>.<evidence-slug>",
|
|
479
|
+
prefix: "ev",
|
|
480
|
+
example: "ev.orders.cancel-disabled-paid"
|
|
481
|
+
},
|
|
482
|
+
schemaName: "Evidence",
|
|
483
|
+
schema: evidenceSchema,
|
|
484
|
+
guideFilePath: "packages/core/src/knowledge/guides/evidence.md",
|
|
485
|
+
aliases: []
|
|
486
|
+
},
|
|
487
|
+
questions: {
|
|
488
|
+
collection: "questions",
|
|
489
|
+
title: "Questions",
|
|
490
|
+
storageFile: "questions.json",
|
|
491
|
+
idConvention: {
|
|
492
|
+
pattern: "q.<domain-slug>.<question-slug>",
|
|
493
|
+
prefix: "q",
|
|
494
|
+
example: "q.orders.paid-cancel-rationale"
|
|
495
|
+
},
|
|
496
|
+
schemaName: "Question",
|
|
497
|
+
schema: questionSchema,
|
|
498
|
+
guideFilePath: "packages/core/src/knowledge/guides/questions.md",
|
|
499
|
+
aliases: []
|
|
500
|
+
},
|
|
501
|
+
conflicts: {
|
|
502
|
+
collection: "conflicts",
|
|
503
|
+
title: "Conflicts",
|
|
504
|
+
storageFile: "conflicts.json",
|
|
505
|
+
idConvention: {
|
|
506
|
+
pattern: "conflict.<domain-slug>.<conflict-slug>",
|
|
507
|
+
prefix: "conflict",
|
|
508
|
+
example: "conflict.invoice-domain"
|
|
509
|
+
},
|
|
510
|
+
schemaName: "Conflict",
|
|
511
|
+
schema: conflictSchema,
|
|
512
|
+
guideFilePath: "packages/core/src/knowledge/guides/conflicts.md",
|
|
513
|
+
aliases: []
|
|
514
|
+
}
|
|
515
|
+
};
|
|
516
|
+
var KNOWLEDGE_EXTRACTION_TYPES = Object.values(
|
|
517
|
+
KNOWLEDGE_EXTRACTION_TYPE_REGISTRY
|
|
518
|
+
);
|
|
519
|
+
var KNOWLEDGE_EXTRACTION_TYPE_LOOKUP = new Map(
|
|
520
|
+
KNOWLEDGE_EXTRACTION_TYPES.flatMap((definition) => [
|
|
521
|
+
[definition.collection, definition],
|
|
522
|
+
...definition.aliases.map((alias) => [alias, definition])
|
|
523
|
+
])
|
|
524
|
+
);
|
|
525
|
+
function listKnowledgeExtractionTypes() {
|
|
526
|
+
return KNOWLEDGE_EXTRACTION_TYPES.slice();
|
|
527
|
+
}
|
|
528
|
+
function getKnowledgeExtractionTypeDefinition(type) {
|
|
529
|
+
const normalized = type.trim().toLowerCase().replace(/-/g, "_");
|
|
530
|
+
const exactMatch = KNOWLEDGE_EXTRACTION_TYPE_LOOKUP.get(normalized);
|
|
531
|
+
if (exactMatch) {
|
|
532
|
+
return exactMatch;
|
|
533
|
+
}
|
|
534
|
+
return KNOWLEDGE_EXTRACTION_TYPE_LOOKUP.get(type.trim().toLowerCase());
|
|
535
|
+
}
|
|
536
|
+
function requireKnowledgeExtractionTypeDefinition(type) {
|
|
537
|
+
const definition = getKnowledgeExtractionTypeDefinition(type);
|
|
538
|
+
if (!definition) {
|
|
539
|
+
throw new Error(
|
|
540
|
+
`Unsupported extraction type "${type}". Supported types: ${KNOWLEDGE_EXTRACTION_TYPES.map(
|
|
541
|
+
(entry) => entry.collection
|
|
542
|
+
).join(", ")}`
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
return definition;
|
|
546
|
+
}
|
|
547
|
+
function buildKnowledgeExtractionSchemaUri(type) {
|
|
548
|
+
return `ez-know://extraction/schema/${requireKnowledgeExtractionTypeDefinition(type).collection}`;
|
|
549
|
+
}
|
|
550
|
+
function buildKnowledgeExtractionGuideUri(type) {
|
|
551
|
+
return `ez-know://extraction/guide/${requireKnowledgeExtractionTypeDefinition(type).collection}`;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// packages/core/src/knowledge/extraction-resources.ts
|
|
555
|
+
import { readFile } from "node:fs/promises";
|
|
556
|
+
import { z as z2 } from "zod/v4";
|
|
557
|
+
var KNOWLEDGE_EXTRACTION_TYPES_RESOURCE_URI = "ez-know://extraction/types";
|
|
558
|
+
var KNOWLEDGE_EXTRACTION_SCHEMA_RESOURCE_URI = "ez-know://extraction/schema/{type}";
|
|
559
|
+
var KNOWLEDGE_EXTRACTION_GUIDE_RESOURCE_URI = "ez-know://extraction/guide/{type}";
|
|
560
|
+
function createKnowledgeExtractionTypeResource(definition) {
|
|
561
|
+
return {
|
|
562
|
+
collection: definition.collection,
|
|
563
|
+
title: definition.title,
|
|
564
|
+
aliases: definition.aliases.slice(),
|
|
565
|
+
storageFile: definition.storageFile,
|
|
566
|
+
idConvention: { ...definition.idConvention },
|
|
567
|
+
schemaUri: buildKnowledgeExtractionSchemaUri(definition.collection),
|
|
568
|
+
guideUri: buildKnowledgeExtractionGuideUri(definition.collection)
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
function buildKnowledgeExtractionTypeIndexDocument() {
|
|
572
|
+
const supportedTypes = listKnowledgeExtractionTypes().map(
|
|
573
|
+
createKnowledgeExtractionTypeResource
|
|
574
|
+
);
|
|
575
|
+
return {
|
|
576
|
+
resource: {
|
|
577
|
+
uri: KNOWLEDGE_EXTRACTION_TYPES_RESOURCE_URI,
|
|
578
|
+
title: "Knowledge Extraction Types",
|
|
579
|
+
description: "Canonical registry of knowledge types supported by the extraction workflow."
|
|
580
|
+
},
|
|
581
|
+
source: {
|
|
582
|
+
kind: "registry",
|
|
583
|
+
count: supportedTypes.length
|
|
584
|
+
},
|
|
585
|
+
supportedTypes
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
function buildKnowledgeExtractionSchemaDocument(type) {
|
|
589
|
+
const definition = requireKnowledgeExtractionTypeDefinition(type);
|
|
590
|
+
const resource = createKnowledgeExtractionTypeResource(definition);
|
|
591
|
+
return {
|
|
592
|
+
resource: {
|
|
593
|
+
uri: resource.schemaUri,
|
|
594
|
+
title: `${definition.title} Extraction Schema`,
|
|
595
|
+
description: "Runtime JSON Schema view of the canonical knowledge object model."
|
|
596
|
+
},
|
|
597
|
+
source: {
|
|
598
|
+
kind: "runtime-zod",
|
|
599
|
+
collection: definition.collection,
|
|
600
|
+
schemaName: definition.schemaName,
|
|
601
|
+
storageFile: definition.storageFile,
|
|
602
|
+
guideUri: resource.guideUri
|
|
603
|
+
},
|
|
604
|
+
type: resource,
|
|
605
|
+
schema: z2.toJSONSchema(definition.schema)
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
async function readKnowledgeExtractionGuideMarkdown(type) {
|
|
609
|
+
const definition = requireKnowledgeExtractionTypeDefinition(type);
|
|
610
|
+
const guideFileName = definition.guideFilePath.split("/").pop();
|
|
611
|
+
if (!guideFileName) {
|
|
612
|
+
throw new Error(
|
|
613
|
+
`Invalid guide file path for extraction type "${definition.collection}".`
|
|
614
|
+
);
|
|
615
|
+
}
|
|
616
|
+
const guideUrl = new URL(`./guides/${guideFileName}`, import.meta.url);
|
|
617
|
+
const markdown = await readFile(guideUrl, "utf8");
|
|
618
|
+
return {
|
|
619
|
+
definition,
|
|
620
|
+
markdown
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
async function buildKnowledgeExtractionGuideDocument(type) {
|
|
624
|
+
const definition = requireKnowledgeExtractionTypeDefinition(type);
|
|
625
|
+
const resource = createKnowledgeExtractionTypeResource(definition);
|
|
626
|
+
const { markdown } = await readKnowledgeExtractionGuideMarkdown(type);
|
|
627
|
+
return {
|
|
628
|
+
resource: {
|
|
629
|
+
uri: resource.guideUri,
|
|
630
|
+
title: `${definition.title} Extraction Guide`,
|
|
631
|
+
description: "Checked-in Markdown guidance for extracting this knowledge type."
|
|
632
|
+
},
|
|
633
|
+
source: {
|
|
634
|
+
kind: "checked-in-markdown",
|
|
635
|
+
filePath: definition.guideFilePath
|
|
636
|
+
},
|
|
637
|
+
type: resource,
|
|
638
|
+
markdown
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
function listKnowledgeExtractionResourceTypes() {
|
|
642
|
+
return listKnowledgeExtractionTypes().map(
|
|
643
|
+
createKnowledgeExtractionTypeResource
|
|
644
|
+
);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// packages/core/src/knowledge/loader.ts
|
|
648
|
+
import { existsSync } from "node:fs";
|
|
649
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
650
|
+
import { basename, join } from "node:path";
|
|
651
|
+
import { z as z3 } from "zod/v4";
|
|
652
|
+
|
|
653
|
+
// packages/core/src/knowledge/root.ts
|
|
654
|
+
import { resolve } from "node:path";
|
|
655
|
+
function resolveKnowledgeRootFromEnv(envValue, cwd = process.cwd()) {
|
|
656
|
+
return envValue ? resolve(cwd, envValue) : resolve(cwd, "ez-know");
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// packages/core/src/knowledge/loader.ts
|
|
660
|
+
var COLLECTION_SCHEMAS = {
|
|
661
|
+
domains: z3.array(domainSchema),
|
|
662
|
+
entities: z3.array(entitySchema),
|
|
663
|
+
relationships: z3.array(relationshipSchema),
|
|
664
|
+
capabilities: z3.array(capabilitySchema),
|
|
665
|
+
features: z3.array(featureSchema),
|
|
666
|
+
business_rules: z3.array(businessRuleSchema),
|
|
667
|
+
constraints: z3.array(constraintSchema),
|
|
668
|
+
rationales: z3.array(rationaleSchema),
|
|
669
|
+
evidence: z3.array(evidenceSchema),
|
|
670
|
+
questions: z3.array(questionSchema),
|
|
671
|
+
conflicts: z3.array(conflictSchema)
|
|
672
|
+
};
|
|
673
|
+
var COLLECTION_DEFINITIONS = KNOWLEDGE_EXTRACTION_TYPE_REGISTRY;
|
|
674
|
+
var KnowledgeStoreLoadError = class extends Error {
|
|
675
|
+
diagnostics;
|
|
676
|
+
constructor(message, diagnostics) {
|
|
677
|
+
super(message);
|
|
678
|
+
this.name = "KnowledgeStoreLoadError";
|
|
679
|
+
this.diagnostics = diagnostics;
|
|
680
|
+
}
|
|
681
|
+
};
|
|
682
|
+
function resolveKnowledgeRoot(envValue, cwd = process.cwd()) {
|
|
683
|
+
return resolveKnowledgeRootFromEnv(envValue, cwd);
|
|
684
|
+
}
|
|
685
|
+
async function discoverDomainFolders(rootDir) {
|
|
686
|
+
const { readdir: readdir3 } = await import("node:fs/promises");
|
|
687
|
+
const discovered = [];
|
|
688
|
+
const entries = await readdir3(rootDir, { withFileTypes: true });
|
|
689
|
+
for (const entry of entries) {
|
|
690
|
+
if (entry.isDirectory() && !entry.name.startsWith(".")) {
|
|
691
|
+
discovered.push(join(rootDir, entry.name));
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
discovered.sort((left, right) => left.localeCompare(right));
|
|
695
|
+
return discovered;
|
|
696
|
+
}
|
|
697
|
+
function readDiagnostic(kind, path, message, id) {
|
|
698
|
+
return {
|
|
699
|
+
kind,
|
|
700
|
+
path,
|
|
701
|
+
id,
|
|
702
|
+
message
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
async function readCanonicalFile(filePath, schema) {
|
|
706
|
+
let raw;
|
|
707
|
+
try {
|
|
708
|
+
raw = await readFile2(filePath, "utf8");
|
|
709
|
+
} catch (error) {
|
|
710
|
+
throw new KnowledgeStoreLoadError(
|
|
711
|
+
`Failed to read canonical knowledge file: ${filePath}`,
|
|
712
|
+
[
|
|
713
|
+
readDiagnostic(
|
|
714
|
+
"missing_domain_file",
|
|
715
|
+
filePath,
|
|
716
|
+
`Unable to read canonical file: ${filePath}`
|
|
717
|
+
)
|
|
718
|
+
]
|
|
719
|
+
);
|
|
720
|
+
}
|
|
721
|
+
let parsed;
|
|
722
|
+
try {
|
|
723
|
+
parsed = JSON.parse(raw);
|
|
724
|
+
} catch (error) {
|
|
725
|
+
throw new KnowledgeStoreLoadError(
|
|
726
|
+
`Invalid JSON in canonical knowledge file: ${filePath}`,
|
|
727
|
+
[readDiagnostic("invalid_json", filePath, `Invalid JSON in ${filePath}`)]
|
|
728
|
+
);
|
|
729
|
+
}
|
|
730
|
+
try {
|
|
731
|
+
return schema.parse(parsed);
|
|
732
|
+
} catch (error) {
|
|
733
|
+
throw new KnowledgeStoreLoadError(
|
|
734
|
+
`Invalid knowledge schema in canonical file: ${filePath}`,
|
|
735
|
+
[
|
|
736
|
+
readDiagnostic(
|
|
737
|
+
"invalid_shape",
|
|
738
|
+
filePath,
|
|
739
|
+
`Invalid canonical file shape in ${filePath}`
|
|
740
|
+
)
|
|
741
|
+
]
|
|
742
|
+
);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
function validateIdPrefix(collection, id, domainSlug) {
|
|
746
|
+
const definition = COLLECTION_DEFINITIONS[collection];
|
|
747
|
+
if (collection === "domains") {
|
|
748
|
+
const expectedId = `domain.${domainSlug}`;
|
|
749
|
+
if (id !== expectedId) {
|
|
750
|
+
return `Expected ${id} to equal ${expectedId}`;
|
|
751
|
+
}
|
|
752
|
+
return void 0;
|
|
753
|
+
}
|
|
754
|
+
const expectedPrefix = `${definition.idConvention.prefix}.${domainSlug}.`;
|
|
755
|
+
if (!id.startsWith(expectedPrefix)) {
|
|
756
|
+
return `Expected ${id} to start with ${expectedPrefix}`;
|
|
757
|
+
}
|
|
758
|
+
return void 0;
|
|
759
|
+
}
|
|
760
|
+
function getDomainId(domainSlug) {
|
|
761
|
+
return `domain.${domainSlug}`;
|
|
762
|
+
}
|
|
763
|
+
function emptyStore(rootDir) {
|
|
764
|
+
return {
|
|
765
|
+
rootDir,
|
|
766
|
+
sourceFiles: [],
|
|
767
|
+
domains: [],
|
|
768
|
+
entities: [],
|
|
769
|
+
relationships: [],
|
|
770
|
+
capabilities: [],
|
|
771
|
+
features: [],
|
|
772
|
+
business_rules: [],
|
|
773
|
+
constraints: [],
|
|
774
|
+
rationales: [],
|
|
775
|
+
evidence: [],
|
|
776
|
+
questions: [],
|
|
777
|
+
conflicts: [],
|
|
778
|
+
byCollection: {
|
|
779
|
+
domains: /* @__PURE__ */ new Map(),
|
|
780
|
+
entities: /* @__PURE__ */ new Map(),
|
|
781
|
+
relationships: /* @__PURE__ */ new Map(),
|
|
782
|
+
capabilities: /* @__PURE__ */ new Map(),
|
|
783
|
+
features: /* @__PURE__ */ new Map(),
|
|
784
|
+
business_rules: /* @__PURE__ */ new Map(),
|
|
785
|
+
constraints: /* @__PURE__ */ new Map(),
|
|
786
|
+
rationales: /* @__PURE__ */ new Map(),
|
|
787
|
+
evidence: /* @__PURE__ */ new Map(),
|
|
788
|
+
questions: /* @__PURE__ */ new Map(),
|
|
789
|
+
conflicts: /* @__PURE__ */ new Map()
|
|
790
|
+
},
|
|
791
|
+
byId: /* @__PURE__ */ new Map(),
|
|
792
|
+
reverseReferences: /* @__PURE__ */ new Map(),
|
|
793
|
+
diagnostics: []
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
function addRecord(store, collection, record2, diagnostics) {
|
|
797
|
+
if (store.byId.has(record2.id)) {
|
|
798
|
+
diagnostics.push(
|
|
799
|
+
readDiagnostic(
|
|
800
|
+
"duplicate_id",
|
|
801
|
+
void 0,
|
|
802
|
+
`Duplicate knowledge id detected: ${record2.id}`,
|
|
803
|
+
record2.id
|
|
804
|
+
)
|
|
805
|
+
);
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
store.byId.set(record2.id, record2);
|
|
809
|
+
store.byCollection[collection].set(record2.id, record2);
|
|
810
|
+
store[collection].push(record2);
|
|
811
|
+
}
|
|
812
|
+
function validateFolderRecordIds(collection, records, domainSlug, diagnostics) {
|
|
813
|
+
for (const record2 of records) {
|
|
814
|
+
const message = validateIdPrefix(collection, record2.id, domainSlug);
|
|
815
|
+
if (message) {
|
|
816
|
+
diagnostics.push(
|
|
817
|
+
readDiagnostic("invalid_domain_id", void 0, message, record2.id)
|
|
818
|
+
);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
function collectReferenceIds(record2) {
|
|
823
|
+
if ("relationship_type" in record2) {
|
|
824
|
+
return [
|
|
825
|
+
record2.from_entity_id,
|
|
826
|
+
record2.to_entity_id
|
|
827
|
+
];
|
|
828
|
+
}
|
|
829
|
+
if ("business_definition" in record2) {
|
|
830
|
+
const entity = record2;
|
|
831
|
+
return [
|
|
832
|
+
...entity.domain_ids,
|
|
833
|
+
...entity.relationship_ids,
|
|
834
|
+
...entity.capability_ids,
|
|
835
|
+
...entity.feature_ids,
|
|
836
|
+
...entity.rule_ids
|
|
837
|
+
];
|
|
838
|
+
}
|
|
839
|
+
if ("behavior_type" in record2) {
|
|
840
|
+
const rule = record2;
|
|
841
|
+
return [
|
|
842
|
+
...rule.applies_to.domain_ids ?? [],
|
|
843
|
+
...rule.applies_to.entity_ids ?? [],
|
|
844
|
+
...rule.applies_to.capability_ids ?? [],
|
|
845
|
+
...rule.applies_to.feature_ids ?? [],
|
|
846
|
+
...rule.related_constraint_ids,
|
|
847
|
+
...rule.rationale_ids
|
|
848
|
+
];
|
|
849
|
+
}
|
|
850
|
+
if ("constraint_type" in record2) {
|
|
851
|
+
const constraint = record2;
|
|
852
|
+
return [
|
|
853
|
+
...constraint.applies_to.domain_ids ?? [],
|
|
854
|
+
...constraint.applies_to.entity_ids ?? [],
|
|
855
|
+
...constraint.applies_to.capability_ids ?? [],
|
|
856
|
+
...constraint.applies_to.feature_ids ?? [],
|
|
857
|
+
...constraint.applies_to.business_rule_ids ?? []
|
|
858
|
+
];
|
|
859
|
+
}
|
|
860
|
+
if ("parent_capability_ids" in record2) {
|
|
861
|
+
const capability = record2;
|
|
862
|
+
return [
|
|
863
|
+
...capability.domain_ids,
|
|
864
|
+
...capability.entity_ids,
|
|
865
|
+
...capability.parent_capability_ids,
|
|
866
|
+
...capability.child_capability_ids,
|
|
867
|
+
...capability.feature_ids
|
|
868
|
+
];
|
|
869
|
+
}
|
|
870
|
+
if ("rule_ids" in record2 && "constraint_ids" in record2) {
|
|
871
|
+
const feature = record2;
|
|
872
|
+
return [
|
|
873
|
+
...feature.domain_ids,
|
|
874
|
+
...feature.capability_ids,
|
|
875
|
+
...feature.entity_ids,
|
|
876
|
+
...feature.rule_ids,
|
|
877
|
+
...feature.constraint_ids
|
|
878
|
+
];
|
|
879
|
+
}
|
|
880
|
+
if ("entity_ids" in record2 && "capability_ids" in record2 && "why_it_exists" in record2 && !("business_definition" in record2) && !("parent_capability_ids" in record2) && !("constraint_ids" in record2) && !("rule_ids" in record2)) {
|
|
881
|
+
const domain = record2;
|
|
882
|
+
return [...domain.entity_ids, ...domain.capability_ids];
|
|
883
|
+
}
|
|
884
|
+
if ("explains" in record2) {
|
|
885
|
+
return record2.explains;
|
|
886
|
+
}
|
|
887
|
+
if ("source_type" in record2) {
|
|
888
|
+
return record2.supports;
|
|
889
|
+
}
|
|
890
|
+
if ("priority" in record2) {
|
|
891
|
+
const question = record2;
|
|
892
|
+
return [...question.related_ids, ...question.evidence_ids];
|
|
893
|
+
}
|
|
894
|
+
const conflict = record2;
|
|
895
|
+
return [
|
|
896
|
+
...conflict.related_ids,
|
|
897
|
+
...conflict.evidence_ids,
|
|
898
|
+
...conflict.questions
|
|
899
|
+
];
|
|
900
|
+
}
|
|
901
|
+
function buildReverseReferences(store) {
|
|
902
|
+
for (const record2 of store.byId.values()) {
|
|
903
|
+
const references = collectReferenceIds(record2);
|
|
904
|
+
for (const referenceId of references) {
|
|
905
|
+
const reverse = store.reverseReferences.get(referenceId) ?? [];
|
|
906
|
+
reverse.push(record2.id);
|
|
907
|
+
store.reverseReferences.set(referenceId, reverse);
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
function validateReferences(store, diagnostics) {
|
|
912
|
+
for (const record2 of store.byId.values()) {
|
|
913
|
+
for (const referenceId of collectReferenceIds(record2)) {
|
|
914
|
+
if (!store.byId.has(referenceId)) {
|
|
915
|
+
diagnostics.push(
|
|
916
|
+
readDiagnostic(
|
|
917
|
+
"invalid_reference",
|
|
918
|
+
void 0,
|
|
919
|
+
`Unknown reference id "${referenceId}" from record "${record2.id}"`,
|
|
920
|
+
record2.id
|
|
921
|
+
)
|
|
922
|
+
);
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
function finalizeStore(store, diagnostics) {
|
|
928
|
+
store.diagnostics = diagnostics;
|
|
929
|
+
if (diagnostics.length > 0) {
|
|
930
|
+
throw new KnowledgeStoreLoadError(
|
|
931
|
+
diagnostics.map((diagnostic) => diagnostic.message).join("; "),
|
|
932
|
+
diagnostics
|
|
933
|
+
);
|
|
934
|
+
}
|
|
935
|
+
return store;
|
|
936
|
+
}
|
|
937
|
+
async function loadKnowledgeStoreFromRoot(rootDir) {
|
|
938
|
+
if (!existsSync(rootDir)) {
|
|
939
|
+
throw new KnowledgeStoreLoadError(
|
|
940
|
+
`Knowledge root does not exist: ${rootDir}`,
|
|
941
|
+
[
|
|
942
|
+
readDiagnostic(
|
|
943
|
+
"missing_root",
|
|
944
|
+
rootDir,
|
|
945
|
+
`Knowledge root does not exist: ${rootDir}`
|
|
946
|
+
)
|
|
947
|
+
]
|
|
948
|
+
);
|
|
949
|
+
}
|
|
950
|
+
const diagnostics = [];
|
|
951
|
+
const store = emptyStore(rootDir);
|
|
952
|
+
const domainFolders = await discoverDomainFolders(rootDir);
|
|
953
|
+
const sourceFiles = [];
|
|
954
|
+
for (const domainFolder of domainFolders) {
|
|
955
|
+
const domainSlug = basename(domainFolder);
|
|
956
|
+
const domainFile = join(
|
|
957
|
+
domainFolder,
|
|
958
|
+
COLLECTION_DEFINITIONS.domains.storageFile
|
|
959
|
+
);
|
|
960
|
+
if (!existsSync(domainFile)) {
|
|
961
|
+
diagnostics.push(
|
|
962
|
+
readDiagnostic(
|
|
963
|
+
"missing_domain_file",
|
|
964
|
+
domainFile,
|
|
965
|
+
`Missing canonical domain file: ${domainFile}`
|
|
966
|
+
)
|
|
967
|
+
);
|
|
968
|
+
continue;
|
|
969
|
+
}
|
|
970
|
+
const domain = await readCanonicalFile(
|
|
971
|
+
domainFile,
|
|
972
|
+
domainSchema
|
|
973
|
+
);
|
|
974
|
+
sourceFiles.push(domainFile);
|
|
975
|
+
validateFolderRecordIds("domains", [domain], domainSlug, diagnostics);
|
|
976
|
+
const domainId = getDomainId(domainSlug);
|
|
977
|
+
if (domain.id !== domainId) {
|
|
978
|
+
diagnostics.push(
|
|
979
|
+
readDiagnostic(
|
|
980
|
+
"invalid_domain_id",
|
|
981
|
+
domainFile,
|
|
982
|
+
`Domain id "${domain.id}" must match folder slug "${domainSlug}"`,
|
|
983
|
+
domain.id
|
|
984
|
+
)
|
|
985
|
+
);
|
|
986
|
+
}
|
|
987
|
+
addRecord(store, "domains", domain, diagnostics);
|
|
988
|
+
for (const collection of Object.keys(
|
|
989
|
+
COLLECTION_DEFINITIONS
|
|
990
|
+
)) {
|
|
991
|
+
if (collection === "domains") {
|
|
992
|
+
continue;
|
|
993
|
+
}
|
|
994
|
+
const fileName = COLLECTION_DEFINITIONS[collection].storageFile;
|
|
995
|
+
const filePath = join(domainFolder, fileName);
|
|
996
|
+
if (!existsSync(filePath)) {
|
|
997
|
+
continue;
|
|
998
|
+
}
|
|
999
|
+
const parsed = await readCanonicalFile(
|
|
1000
|
+
filePath,
|
|
1001
|
+
COLLECTION_SCHEMAS[collection]
|
|
1002
|
+
);
|
|
1003
|
+
sourceFiles.push(filePath);
|
|
1004
|
+
validateFolderRecordIds(
|
|
1005
|
+
collection,
|
|
1006
|
+
parsed,
|
|
1007
|
+
domainSlug,
|
|
1008
|
+
diagnostics
|
|
1009
|
+
);
|
|
1010
|
+
for (const record2 of parsed) {
|
|
1011
|
+
addRecord(store, collection, record2, diagnostics);
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
store.sourceFiles = sourceFiles.sort(
|
|
1016
|
+
(left, right) => left.localeCompare(right)
|
|
1017
|
+
);
|
|
1018
|
+
buildReverseReferences(store);
|
|
1019
|
+
validateReferences(store, diagnostics);
|
|
1020
|
+
return finalizeStore(store, diagnostics);
|
|
1021
|
+
}
|
|
1022
|
+
async function loadKnowledgeStoreFromEnv(envValue, cwd = process.cwd()) {
|
|
1023
|
+
const rootDir = resolveKnowledgeRoot(envValue, cwd);
|
|
1024
|
+
if (!rootDir || !existsSync(rootDir)) {
|
|
1025
|
+
return void 0;
|
|
1026
|
+
}
|
|
1027
|
+
return loadKnowledgeStoreFromRoot(rootDir);
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
// packages/core/src/knowledge/patch/storage.ts
|
|
1031
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
1032
|
+
import { mkdir, readFile as readFile3, readdir, writeFile } from "node:fs/promises";
|
|
1033
|
+
import { isAbsolute, join as join3, normalize, resolve as resolve4, sep } from "node:path";
|
|
1034
|
+
import "zod/v4";
|
|
1035
|
+
|
|
1036
|
+
// packages/core/src/knowledge/patch/constants.ts
|
|
1037
|
+
var KNOWLEDGE_PATCH_SCHEMA_VERSION = 1;
|
|
1038
|
+
var KNOWLEDGE_PATCH_FOLDER_NAME = ".patches";
|
|
1039
|
+
var SUPPORTED_OPERATION_TYPES = Object.keys(
|
|
1040
|
+
KNOWLEDGE_EXTRACTION_TYPE_REGISTRY
|
|
1041
|
+
);
|
|
1042
|
+
var supportedOperationTypeSet = new Set(
|
|
1043
|
+
SUPPORTED_OPERATION_TYPES
|
|
1044
|
+
);
|
|
1045
|
+
|
|
1046
|
+
// packages/core/src/knowledge/patch/paths.ts
|
|
1047
|
+
import { join as join2 } from "node:path";
|
|
1048
|
+
function getKnowledgeRoot(envValue, cwd = process.cwd()) {
|
|
1049
|
+
return resolveKnowledgeRootFromEnv(envValue, cwd);
|
|
1050
|
+
}
|
|
1051
|
+
function resolveKnowledgePatchRoot(envValue, cwd = process.cwd()) {
|
|
1052
|
+
return join2(getKnowledgeRoot(envValue, cwd), KNOWLEDGE_PATCH_FOLDER_NAME);
|
|
1053
|
+
}
|
|
1054
|
+
function resolveKnowledgePatchDirectory(envValue, patchId, cwd = process.cwd()) {
|
|
1055
|
+
return join2(resolveKnowledgePatchRoot(envValue, cwd), patchId);
|
|
1056
|
+
}
|
|
1057
|
+
function resolveKnowledgeRoot2(envValue, cwd = process.cwd()) {
|
|
1058
|
+
return getKnowledgeRoot(envValue, cwd);
|
|
1059
|
+
}
|
|
1060
|
+
function patchMetadataPath(patchDir) {
|
|
1061
|
+
return join2(patchDir, "patch.json");
|
|
1062
|
+
}
|
|
1063
|
+
function patchOperationsPath(patchDir) {
|
|
1064
|
+
return join2(patchDir, "operations.json");
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
// packages/core/src/knowledge/patch/schemas.ts
|
|
1068
|
+
import { z as z4 } from "zod/v4";
|
|
1069
|
+
var patchStatusSchema = z4.enum(["draft", "closed"]);
|
|
1070
|
+
var canonicalOperationTypeSchema = z4.string().trim().min(1).superRefine((value, ctx) => {
|
|
1071
|
+
if (!supportedOperationTypeSet.has(value)) {
|
|
1072
|
+
ctx.addIssue({
|
|
1073
|
+
code: "custom",
|
|
1074
|
+
message: `Unsupported patch operation type "${value}". Supported types: ${SUPPORTED_OPERATION_TYPES.join(
|
|
1075
|
+
", "
|
|
1076
|
+
)}`
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
}).transform((value) => value);
|
|
1080
|
+
var knowledgePatchMetadataSchema = z4.object({
|
|
1081
|
+
id: z4.string().min(1),
|
|
1082
|
+
title: z4.string().trim().min(1),
|
|
1083
|
+
description: z4.string().trim().min(1).optional(),
|
|
1084
|
+
status: patchStatusSchema,
|
|
1085
|
+
rootFolder: z4.string().trim().min(1),
|
|
1086
|
+
omittedFiles: z4.array(
|
|
1087
|
+
z4.object({
|
|
1088
|
+
path: z4.string().trim().min(1),
|
|
1089
|
+
reason: z4.string().trim().min(1)
|
|
1090
|
+
})
|
|
1091
|
+
),
|
|
1092
|
+
schemaVersion: z4.literal(KNOWLEDGE_PATCH_SCHEMA_VERSION),
|
|
1093
|
+
createdAt: z4.string().datetime({ offset: true }),
|
|
1094
|
+
updatedAt: z4.string().datetime({ offset: true }),
|
|
1095
|
+
closedAt: z4.string().datetime({ offset: true }).optional()
|
|
1096
|
+
});
|
|
1097
|
+
var operationBaseSchema = z4.object({
|
|
1098
|
+
id: z4.string().min(1),
|
|
1099
|
+
type: canonicalOperationTypeSchema,
|
|
1100
|
+
domain: z4.string().trim().min(1)
|
|
1101
|
+
});
|
|
1102
|
+
var knowledgePatchCreateOperationSchema = operationBaseSchema.extend({
|
|
1103
|
+
kind: z4.literal("create"),
|
|
1104
|
+
payload: z4.record(z4.string(), z4.unknown())
|
|
1105
|
+
});
|
|
1106
|
+
var knowledgePatchUpdateOperationSchema = operationBaseSchema.extend({
|
|
1107
|
+
kind: z4.literal("update"),
|
|
1108
|
+
targetId: z4.string().min(1),
|
|
1109
|
+
payload: z4.record(z4.string(), z4.unknown())
|
|
1110
|
+
});
|
|
1111
|
+
var knowledgePatchDeleteOperationSchema = operationBaseSchema.extend({
|
|
1112
|
+
kind: z4.literal("delete"),
|
|
1113
|
+
targetId: z4.string().min(1),
|
|
1114
|
+
reason: z4.string().trim().min(1)
|
|
1115
|
+
});
|
|
1116
|
+
var knowledgePatchOperationSchema = z4.discriminatedUnion("kind", [
|
|
1117
|
+
knowledgePatchCreateOperationSchema,
|
|
1118
|
+
knowledgePatchUpdateOperationSchema,
|
|
1119
|
+
knowledgePatchDeleteOperationSchema
|
|
1120
|
+
]);
|
|
1121
|
+
var knowledgePatchOperationsEnvelopeSchema = z4.object({
|
|
1122
|
+
schemaVersion: z4.literal(KNOWLEDGE_PATCH_SCHEMA_VERSION),
|
|
1123
|
+
operations: z4.array(knowledgePatchOperationSchema)
|
|
1124
|
+
});
|
|
1125
|
+
var knowledgePatchDiagnosticSchema = z4.object({
|
|
1126
|
+
severity: z4.enum(["error", "critical"]),
|
|
1127
|
+
code: z4.string(),
|
|
1128
|
+
message: z4.string(),
|
|
1129
|
+
patchId: z4.string().optional(),
|
|
1130
|
+
operationId: z4.string().optional(),
|
|
1131
|
+
targetId: z4.string().optional(),
|
|
1132
|
+
path: z4.array(z4.union([z4.string(), z4.number()])).optional(),
|
|
1133
|
+
supportedTypes: z4.array(z4.string()).optional()
|
|
1134
|
+
});
|
|
1135
|
+
var knowledgePatchValidationResultSchema = z4.object({
|
|
1136
|
+
valid: z4.boolean(),
|
|
1137
|
+
fingerprint: z4.string().optional(),
|
|
1138
|
+
diagnostics: z4.array(knowledgePatchDiagnosticSchema),
|
|
1139
|
+
operationCount: z4.number().int().nonnegative(),
|
|
1140
|
+
appliedOperationIds: z4.array(z4.string()),
|
|
1141
|
+
affectedFiles: z4.array(z4.string())
|
|
1142
|
+
});
|
|
1143
|
+
var knowledgePatchApplyApprovalSchema = z4.object({
|
|
1144
|
+
approved: z4.boolean(),
|
|
1145
|
+
validationFingerprint: z4.string().min(1)
|
|
1146
|
+
});
|
|
1147
|
+
var createPatchInputSchema = z4.object({
|
|
1148
|
+
title: z4.string().trim().min(1),
|
|
1149
|
+
description: z4.string().trim().min(1).optional(),
|
|
1150
|
+
rootFolder: z4.string().trim().min(1),
|
|
1151
|
+
omittedFiles: z4.array(
|
|
1152
|
+
z4.object({
|
|
1153
|
+
path: z4.string().trim().min(1),
|
|
1154
|
+
reason: z4.string().trim().min(1)
|
|
1155
|
+
})
|
|
1156
|
+
).optional()
|
|
1157
|
+
});
|
|
1158
|
+
var addOperationInputSchema = z4.discriminatedUnion("kind", [
|
|
1159
|
+
knowledgePatchCreateOperationSchema.omit({ id: true }).extend({
|
|
1160
|
+
id: z4.string().min(1).optional()
|
|
1161
|
+
}),
|
|
1162
|
+
knowledgePatchUpdateOperationSchema.omit({ id: true }).extend({
|
|
1163
|
+
id: z4.string().min(1).optional()
|
|
1164
|
+
}),
|
|
1165
|
+
knowledgePatchDeleteOperationSchema.omit({ id: true }).extend({
|
|
1166
|
+
id: z4.string().min(1).optional()
|
|
1167
|
+
})
|
|
1168
|
+
]);
|
|
1169
|
+
var updateOperationInputSchema = z4.object({
|
|
1170
|
+
operationId: z4.string().min(1),
|
|
1171
|
+
operation: addOperationInputSchema
|
|
1172
|
+
});
|
|
1173
|
+
var updatePatchMetadataInputSchema = z4.object({
|
|
1174
|
+
rootFolder: z4.string().trim().min(1).optional(),
|
|
1175
|
+
omittedFiles: z4.array(
|
|
1176
|
+
z4.object({
|
|
1177
|
+
path: z4.string().trim().min(1),
|
|
1178
|
+
reason: z4.string().trim().min(1)
|
|
1179
|
+
})
|
|
1180
|
+
)
|
|
1181
|
+
});
|
|
1182
|
+
var knowledgePatchToolInputSchema = z4.discriminatedUnion("action", [
|
|
1183
|
+
z4.object({
|
|
1184
|
+
action: z4.literal("create"),
|
|
1185
|
+
patch: createPatchInputSchema
|
|
1186
|
+
}),
|
|
1187
|
+
z4.object({
|
|
1188
|
+
action: z4.literal("list")
|
|
1189
|
+
}),
|
|
1190
|
+
z4.object({
|
|
1191
|
+
action: z4.literal("get"),
|
|
1192
|
+
patchId: z4.string().min(1)
|
|
1193
|
+
}),
|
|
1194
|
+
z4.object({
|
|
1195
|
+
action: z4.literal("add_operation"),
|
|
1196
|
+
patchId: z4.string().min(1),
|
|
1197
|
+
operation: addOperationInputSchema
|
|
1198
|
+
}),
|
|
1199
|
+
z4.object({
|
|
1200
|
+
action: z4.literal("update_operation"),
|
|
1201
|
+
patchId: z4.string().min(1),
|
|
1202
|
+
update: updateOperationInputSchema
|
|
1203
|
+
}),
|
|
1204
|
+
z4.object({
|
|
1205
|
+
action: z4.literal("update_metadata"),
|
|
1206
|
+
patchId: z4.string().min(1),
|
|
1207
|
+
metadata: updatePatchMetadataInputSchema
|
|
1208
|
+
}),
|
|
1209
|
+
z4.object({
|
|
1210
|
+
action: z4.literal("delete_operation"),
|
|
1211
|
+
patchId: z4.string().min(1),
|
|
1212
|
+
operationId: z4.string().min(1)
|
|
1213
|
+
}),
|
|
1214
|
+
z4.object({
|
|
1215
|
+
action: z4.literal("validate"),
|
|
1216
|
+
patchId: z4.string().min(1)
|
|
1217
|
+
}),
|
|
1218
|
+
z4.object({
|
|
1219
|
+
action: z4.literal("close"),
|
|
1220
|
+
patchId: z4.string().min(1)
|
|
1221
|
+
})
|
|
1222
|
+
]);
|
|
1223
|
+
var knowledgePatchApplyToolInputSchema = z4.object({
|
|
1224
|
+
patchId: z4.string().min(1),
|
|
1225
|
+
approval: knowledgePatchApplyApprovalSchema
|
|
1226
|
+
});
|
|
1227
|
+
|
|
1228
|
+
// packages/core/src/knowledge/patch/utils.ts
|
|
1229
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
1230
|
+
function buildDiagnostic(diagnostic) {
|
|
1231
|
+
return knowledgePatchDiagnosticSchema.parse(diagnostic);
|
|
1232
|
+
}
|
|
1233
|
+
function nowIso() {
|
|
1234
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1235
|
+
}
|
|
1236
|
+
function slugifyPatchTitle(title) {
|
|
1237
|
+
const normalized = title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
1238
|
+
return normalized || "patch";
|
|
1239
|
+
}
|
|
1240
|
+
function buildPatchId(title) {
|
|
1241
|
+
return `${slugifyPatchTitle(title)}-${randomUUID().slice(0, 8)}`;
|
|
1242
|
+
}
|
|
1243
|
+
function buildOperationId() {
|
|
1244
|
+
return `op_${randomUUID().slice(0, 10)}`;
|
|
1245
|
+
}
|
|
1246
|
+
function sha256(value) {
|
|
1247
|
+
return createHash("sha256").update(value).digest("hex");
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
// packages/core/src/knowledge/patch/storage.ts
|
|
1251
|
+
async function ensurePatchRoot(rootDir) {
|
|
1252
|
+
await mkdir(rootDir, { recursive: true });
|
|
1253
|
+
}
|
|
1254
|
+
async function writeJsonFile(filePath, value) {
|
|
1255
|
+
await writeFile(filePath, `${JSON.stringify(value, null, 2)}
|
|
1256
|
+
`, "utf8");
|
|
1257
|
+
}
|
|
1258
|
+
async function readJsonFile(filePath, schema) {
|
|
1259
|
+
const raw = await readFile3(filePath, "utf8");
|
|
1260
|
+
const parsed = JSON.parse(raw);
|
|
1261
|
+
return schema.parse(parsed);
|
|
1262
|
+
}
|
|
1263
|
+
function normalizeRootFolder(rootFolder, cwd = process.cwd()) {
|
|
1264
|
+
return resolve4(cwd, rootFolder);
|
|
1265
|
+
}
|
|
1266
|
+
function normalizeRepositoryRelativePath(inputPath) {
|
|
1267
|
+
const trimmed = inputPath.trim().replaceAll("\\", "/");
|
|
1268
|
+
if (!trimmed) {
|
|
1269
|
+
throw new Error("Omitted file path must not be empty.");
|
|
1270
|
+
}
|
|
1271
|
+
if (isAbsolute(trimmed)) {
|
|
1272
|
+
throw new Error(
|
|
1273
|
+
`Omitted file path "${inputPath}" must be repository-relative.`
|
|
1274
|
+
);
|
|
1275
|
+
}
|
|
1276
|
+
const normalized = normalize(trimmed).replaceAll(sep, "/");
|
|
1277
|
+
if (normalized === "." || normalized === ".." || normalized.startsWith("../")) {
|
|
1278
|
+
throw new Error(
|
|
1279
|
+
`Omitted file path "${inputPath}" must stay within the repository root.`
|
|
1280
|
+
);
|
|
1281
|
+
}
|
|
1282
|
+
return normalized;
|
|
1283
|
+
}
|
|
1284
|
+
function normalizeOmittedFiles(omittedFiles) {
|
|
1285
|
+
return (omittedFiles ?? []).map((entry) => ({
|
|
1286
|
+
path: normalizeRepositoryRelativePath(entry.path),
|
|
1287
|
+
reason: entry.reason.trim()
|
|
1288
|
+
}));
|
|
1289
|
+
}
|
|
1290
|
+
async function loadPatchStateByDir(patchDir) {
|
|
1291
|
+
const patch = await readJsonFile(
|
|
1292
|
+
patchMetadataPath(patchDir),
|
|
1293
|
+
knowledgePatchMetadataSchema
|
|
1294
|
+
);
|
|
1295
|
+
const envelope = await readJsonFile(
|
|
1296
|
+
patchOperationsPath(patchDir),
|
|
1297
|
+
knowledgePatchOperationsEnvelopeSchema
|
|
1298
|
+
);
|
|
1299
|
+
return {
|
|
1300
|
+
patch,
|
|
1301
|
+
operations: envelope.operations
|
|
1302
|
+
};
|
|
1303
|
+
}
|
|
1304
|
+
async function readKnowledgePatch(envValue, patchId, cwd = process.cwd()) {
|
|
1305
|
+
const patchDir = resolveKnowledgePatchDirectory(envValue, patchId, cwd);
|
|
1306
|
+
return loadPatchStateByDir(patchDir);
|
|
1307
|
+
}
|
|
1308
|
+
function assertDraftPatch(patch) {
|
|
1309
|
+
if (patch.status !== "draft") {
|
|
1310
|
+
throw new Error(`Patch "${patch.id}" is closed and cannot be modified.`);
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
async function savePatchState(patchDir, state) {
|
|
1314
|
+
const envelope = {
|
|
1315
|
+
schemaVersion: KNOWLEDGE_PATCH_SCHEMA_VERSION,
|
|
1316
|
+
operations: state.operations
|
|
1317
|
+
};
|
|
1318
|
+
await writeJsonFile(patchMetadataPath(patchDir), state.patch);
|
|
1319
|
+
await writeJsonFile(patchOperationsPath(patchDir), envelope);
|
|
1320
|
+
}
|
|
1321
|
+
async function createKnowledgePatch(envValue, input, cwd = process.cwd()) {
|
|
1322
|
+
const patchRoot = resolveKnowledgePatchRoot(envValue, cwd);
|
|
1323
|
+
await ensurePatchRoot(patchRoot);
|
|
1324
|
+
const patchInput = createPatchInputSchema.parse(input);
|
|
1325
|
+
const patchId = buildPatchId(patchInput.title);
|
|
1326
|
+
const timestamp = nowIso();
|
|
1327
|
+
const patch = {
|
|
1328
|
+
id: patchId,
|
|
1329
|
+
title: patchInput.title,
|
|
1330
|
+
description: patchInput.description,
|
|
1331
|
+
status: "draft",
|
|
1332
|
+
rootFolder: normalizeRootFolder(patchInput.rootFolder, cwd),
|
|
1333
|
+
omittedFiles: normalizeOmittedFiles(patchInput.omittedFiles),
|
|
1334
|
+
schemaVersion: KNOWLEDGE_PATCH_SCHEMA_VERSION,
|
|
1335
|
+
createdAt: timestamp,
|
|
1336
|
+
updatedAt: timestamp
|
|
1337
|
+
};
|
|
1338
|
+
const patchDir = join3(patchRoot, patchId);
|
|
1339
|
+
await mkdir(patchDir, { recursive: true });
|
|
1340
|
+
await savePatchState(patchDir, { patch, operations: [] });
|
|
1341
|
+
return readKnowledgePatch(envValue, patchId, cwd);
|
|
1342
|
+
}
|
|
1343
|
+
async function listKnowledgePatches(envValue, cwd = process.cwd()) {
|
|
1344
|
+
const patchRoot = resolveKnowledgePatchRoot(envValue, cwd);
|
|
1345
|
+
if (!existsSync2(patchRoot)) {
|
|
1346
|
+
return [];
|
|
1347
|
+
}
|
|
1348
|
+
const entries = await readdir(patchRoot, { withFileTypes: true });
|
|
1349
|
+
const summaries = [];
|
|
1350
|
+
for (const entry of entries) {
|
|
1351
|
+
if (!entry.isDirectory()) {
|
|
1352
|
+
continue;
|
|
1353
|
+
}
|
|
1354
|
+
const patchDir = join3(patchRoot, entry.name);
|
|
1355
|
+
const state = await loadPatchStateByDir(patchDir);
|
|
1356
|
+
summaries.push({
|
|
1357
|
+
id: state.patch.id,
|
|
1358
|
+
status: state.patch.status,
|
|
1359
|
+
title: state.patch.title,
|
|
1360
|
+
description: state.patch.description,
|
|
1361
|
+
rootFolder: state.patch.rootFolder,
|
|
1362
|
+
omittedFiles: state.patch.omittedFiles,
|
|
1363
|
+
operationCount: state.operations.length,
|
|
1364
|
+
createdAt: state.patch.createdAt,
|
|
1365
|
+
updatedAt: state.patch.updatedAt
|
|
1366
|
+
});
|
|
1367
|
+
}
|
|
1368
|
+
summaries.sort(
|
|
1369
|
+
(left, right) => left.createdAt === right.createdAt ? left.id.localeCompare(right.id) : left.createdAt.localeCompare(right.createdAt)
|
|
1370
|
+
);
|
|
1371
|
+
return summaries;
|
|
1372
|
+
}
|
|
1373
|
+
async function addKnowledgePatchOperation(envValue, patchId, input, cwd = process.cwd()) {
|
|
1374
|
+
const patchDir = resolveKnowledgePatchDirectory(envValue, patchId, cwd);
|
|
1375
|
+
const state = await loadPatchStateByDir(patchDir);
|
|
1376
|
+
assertDraftPatch(state.patch);
|
|
1377
|
+
const operation = knowledgePatchOperationSchema.parse({
|
|
1378
|
+
...input,
|
|
1379
|
+
id: input.id ?? buildOperationId()
|
|
1380
|
+
});
|
|
1381
|
+
state.operations.push(operation);
|
|
1382
|
+
state.patch.updatedAt = nowIso();
|
|
1383
|
+
await savePatchState(patchDir, state);
|
|
1384
|
+
return state;
|
|
1385
|
+
}
|
|
1386
|
+
async function updateKnowledgePatchMetadata(envValue, patchId, input, cwd = process.cwd()) {
|
|
1387
|
+
const patchDir = resolveKnowledgePatchDirectory(envValue, patchId, cwd);
|
|
1388
|
+
const state = await loadPatchStateByDir(patchDir);
|
|
1389
|
+
assertDraftPatch(state.patch);
|
|
1390
|
+
const metadata = updatePatchMetadataInputSchema.parse(input);
|
|
1391
|
+
const nextMetadata = {
|
|
1392
|
+
omittedFiles: normalizeOmittedFiles(metadata.omittedFiles)
|
|
1393
|
+
};
|
|
1394
|
+
if (metadata.rootFolder) {
|
|
1395
|
+
nextMetadata.rootFolder = normalizeRootFolder(metadata.rootFolder, cwd);
|
|
1396
|
+
}
|
|
1397
|
+
state.patch = knowledgePatchMetadataSchema.parse({
|
|
1398
|
+
...state.patch,
|
|
1399
|
+
...nextMetadata,
|
|
1400
|
+
updatedAt: nowIso()
|
|
1401
|
+
});
|
|
1402
|
+
await savePatchState(patchDir, state);
|
|
1403
|
+
return state;
|
|
1404
|
+
}
|
|
1405
|
+
async function updateKnowledgePatchOperation(envValue, patchId, input, cwd = process.cwd()) {
|
|
1406
|
+
const patchDir = resolveKnowledgePatchDirectory(envValue, patchId, cwd);
|
|
1407
|
+
const state = await loadPatchStateByDir(patchDir);
|
|
1408
|
+
assertDraftPatch(state.patch);
|
|
1409
|
+
const update = updateOperationInputSchema.parse(input);
|
|
1410
|
+
const index = state.operations.findIndex(
|
|
1411
|
+
(operation) => operation.id === update.operationId
|
|
1412
|
+
);
|
|
1413
|
+
if (index === -1) {
|
|
1414
|
+
throw new Error(
|
|
1415
|
+
`Patch "${patchId}" does not contain operation "${update.operationId}".`
|
|
1416
|
+
);
|
|
1417
|
+
}
|
|
1418
|
+
state.operations[index] = knowledgePatchOperationSchema.parse({
|
|
1419
|
+
...update.operation,
|
|
1420
|
+
id: update.operation.id ?? update.operationId
|
|
1421
|
+
});
|
|
1422
|
+
state.patch.updatedAt = nowIso();
|
|
1423
|
+
await savePatchState(patchDir, state);
|
|
1424
|
+
return state;
|
|
1425
|
+
}
|
|
1426
|
+
async function deleteKnowledgePatchOperation(envValue, patchId, operationId, cwd = process.cwd()) {
|
|
1427
|
+
const patchDir = resolveKnowledgePatchDirectory(envValue, patchId, cwd);
|
|
1428
|
+
const state = await loadPatchStateByDir(patchDir);
|
|
1429
|
+
assertDraftPatch(state.patch);
|
|
1430
|
+
const nextOperations = state.operations.filter(
|
|
1431
|
+
(operation) => operation.id !== operationId
|
|
1432
|
+
);
|
|
1433
|
+
if (nextOperations.length === state.operations.length) {
|
|
1434
|
+
throw new Error(
|
|
1435
|
+
`Patch "${patchId}" does not contain operation "${operationId}".`
|
|
1436
|
+
);
|
|
1437
|
+
}
|
|
1438
|
+
state.operations = nextOperations;
|
|
1439
|
+
state.patch.updatedAt = nowIso();
|
|
1440
|
+
await savePatchState(patchDir, state);
|
|
1441
|
+
return state;
|
|
1442
|
+
}
|
|
1443
|
+
async function closeKnowledgePatch(envValue, patchId, cwd = process.cwd()) {
|
|
1444
|
+
const patchDir = resolveKnowledgePatchDirectory(envValue, patchId, cwd);
|
|
1445
|
+
const state = await loadPatchStateByDir(patchDir);
|
|
1446
|
+
assertDraftPatch(state.patch);
|
|
1447
|
+
const timestamp = nowIso();
|
|
1448
|
+
state.patch.status = "closed";
|
|
1449
|
+
state.patch.updatedAt = timestamp;
|
|
1450
|
+
state.patch.closedAt = timestamp;
|
|
1451
|
+
await savePatchState(patchDir, state);
|
|
1452
|
+
return state;
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
// packages/core/src/knowledge/patch/simulation.ts
|
|
1456
|
+
function cloneStoreState(store) {
|
|
1457
|
+
const byCollection = Object.fromEntries(
|
|
1458
|
+
SUPPORTED_OPERATION_TYPES.map((collection) => [
|
|
1459
|
+
collection,
|
|
1460
|
+
new Map(store.byCollection[collection])
|
|
1461
|
+
])
|
|
1462
|
+
);
|
|
1463
|
+
return {
|
|
1464
|
+
byCollection,
|
|
1465
|
+
byId: new Map(store.byId)
|
|
1466
|
+
};
|
|
1467
|
+
}
|
|
1468
|
+
function validateRecordIdForDomain(type, id, domain) {
|
|
1469
|
+
const definition = KNOWLEDGE_EXTRACTION_TYPE_REGISTRY[type];
|
|
1470
|
+
if (type === "domains") {
|
|
1471
|
+
const expectedId = `domain.${domain}`;
|
|
1472
|
+
return id === expectedId ? void 0 : `Expected domain record id "${id}" to equal "${expectedId}".`;
|
|
1473
|
+
}
|
|
1474
|
+
const expectedPrefix = `${definition.idConvention.prefix}.${domain}.`;
|
|
1475
|
+
return id.startsWith(expectedPrefix) ? void 0 : `Expected record id "${id}" to start with "${expectedPrefix}".`;
|
|
1476
|
+
}
|
|
1477
|
+
function collectReferenceIds2(record2) {
|
|
1478
|
+
if ("relationship_type" in record2) {
|
|
1479
|
+
return [
|
|
1480
|
+
record2.from_entity_id,
|
|
1481
|
+
record2.to_entity_id
|
|
1482
|
+
];
|
|
1483
|
+
}
|
|
1484
|
+
if ("business_definition" in record2) {
|
|
1485
|
+
const entity = record2;
|
|
1486
|
+
return [
|
|
1487
|
+
...entity.domain_ids,
|
|
1488
|
+
...entity.relationship_ids,
|
|
1489
|
+
...entity.capability_ids,
|
|
1490
|
+
...entity.feature_ids,
|
|
1491
|
+
...entity.rule_ids
|
|
1492
|
+
];
|
|
1493
|
+
}
|
|
1494
|
+
if ("behavior_type" in record2) {
|
|
1495
|
+
const rule = record2;
|
|
1496
|
+
return [
|
|
1497
|
+
...rule.applies_to.domain_ids ?? [],
|
|
1498
|
+
...rule.applies_to.entity_ids ?? [],
|
|
1499
|
+
...rule.applies_to.capability_ids ?? [],
|
|
1500
|
+
...rule.applies_to.feature_ids ?? [],
|
|
1501
|
+
...rule.related_constraint_ids,
|
|
1502
|
+
...rule.rationale_ids
|
|
1503
|
+
];
|
|
1504
|
+
}
|
|
1505
|
+
if ("constraint_type" in record2) {
|
|
1506
|
+
const constraint = record2;
|
|
1507
|
+
return [
|
|
1508
|
+
...constraint.applies_to.domain_ids ?? [],
|
|
1509
|
+
...constraint.applies_to.entity_ids ?? [],
|
|
1510
|
+
...constraint.applies_to.capability_ids ?? [],
|
|
1511
|
+
...constraint.applies_to.feature_ids ?? [],
|
|
1512
|
+
...constraint.applies_to.business_rule_ids ?? []
|
|
1513
|
+
];
|
|
1514
|
+
}
|
|
1515
|
+
if ("parent_capability_ids" in record2) {
|
|
1516
|
+
const capability = record2;
|
|
1517
|
+
return [
|
|
1518
|
+
...capability.domain_ids,
|
|
1519
|
+
...capability.entity_ids,
|
|
1520
|
+
...capability.parent_capability_ids,
|
|
1521
|
+
...capability.child_capability_ids,
|
|
1522
|
+
...capability.feature_ids
|
|
1523
|
+
];
|
|
1524
|
+
}
|
|
1525
|
+
if ("rule_ids" in record2 && "constraint_ids" in record2) {
|
|
1526
|
+
const feature = record2;
|
|
1527
|
+
return [
|
|
1528
|
+
...feature.domain_ids,
|
|
1529
|
+
...feature.capability_ids,
|
|
1530
|
+
...feature.entity_ids,
|
|
1531
|
+
...feature.rule_ids,
|
|
1532
|
+
...feature.constraint_ids
|
|
1533
|
+
];
|
|
1534
|
+
}
|
|
1535
|
+
if ("entity_ids" in record2 && "capability_ids" in record2 && "why_it_exists" in record2 && !("business_definition" in record2) && !("parent_capability_ids" in record2) && !("constraint_ids" in record2) && !("rule_ids" in record2)) {
|
|
1536
|
+
const domain = record2;
|
|
1537
|
+
return [...domain.entity_ids, ...domain.capability_ids];
|
|
1538
|
+
}
|
|
1539
|
+
if ("explains" in record2) {
|
|
1540
|
+
return record2.explains;
|
|
1541
|
+
}
|
|
1542
|
+
if ("source_type" in record2) {
|
|
1543
|
+
return record2.supports;
|
|
1544
|
+
}
|
|
1545
|
+
if ("priority" in record2) {
|
|
1546
|
+
const question = record2;
|
|
1547
|
+
return [...question.related_ids, ...question.evidence_ids];
|
|
1548
|
+
}
|
|
1549
|
+
const conflict = record2;
|
|
1550
|
+
return [
|
|
1551
|
+
...conflict.related_ids,
|
|
1552
|
+
...conflict.evidence_ids,
|
|
1553
|
+
...conflict.questions,
|
|
1554
|
+
...conflict.resolved_by_evidence_id ? [conflict.resolved_by_evidence_id] : []
|
|
1555
|
+
];
|
|
1556
|
+
}
|
|
1557
|
+
function inferDomainFromRecordId(recordId) {
|
|
1558
|
+
const [, domain] = recordId.split(".");
|
|
1559
|
+
return domain;
|
|
1560
|
+
}
|
|
1561
|
+
function inferDomainForRecord(record2) {
|
|
1562
|
+
return inferDomainFromRecordId(record2.id);
|
|
1563
|
+
}
|
|
1564
|
+
function parsePatchRecord(operation, diagnostics) {
|
|
1565
|
+
const definition = KNOWLEDGE_EXTRACTION_TYPE_REGISTRY[operation.type];
|
|
1566
|
+
if (operation.kind === "delete") {
|
|
1567
|
+
return void 0;
|
|
1568
|
+
}
|
|
1569
|
+
const parsed = definition.schema.safeParse(operation.payload);
|
|
1570
|
+
if (!parsed.success) {
|
|
1571
|
+
diagnostics.push(
|
|
1572
|
+
buildDiagnostic({
|
|
1573
|
+
severity: "error",
|
|
1574
|
+
code: "invalid_payload",
|
|
1575
|
+
message: `Operation "${operation.id}" payload is invalid for type "${operation.type}".`,
|
|
1576
|
+
operationId: operation.id,
|
|
1577
|
+
path: ["operations", operation.id, "payload"]
|
|
1578
|
+
})
|
|
1579
|
+
);
|
|
1580
|
+
return void 0;
|
|
1581
|
+
}
|
|
1582
|
+
return parsed.data;
|
|
1583
|
+
}
|
|
1584
|
+
function simulateKnowledgePatch(store, operations, patchId) {
|
|
1585
|
+
const state = cloneStoreState(store);
|
|
1586
|
+
const diagnostics = [];
|
|
1587
|
+
const deleteTargets = [];
|
|
1588
|
+
const appliedOperationIds = [];
|
|
1589
|
+
for (const operation of operations) {
|
|
1590
|
+
if (!supportedOperationTypeSet.has(operation.type)) {
|
|
1591
|
+
diagnostics.push(
|
|
1592
|
+
buildDiagnostic({
|
|
1593
|
+
severity: "error",
|
|
1594
|
+
code: "invalid_type",
|
|
1595
|
+
message: `Unsupported patch operation type "${operation.type}".`,
|
|
1596
|
+
patchId,
|
|
1597
|
+
operationId: operation.id,
|
|
1598
|
+
supportedTypes: SUPPORTED_OPERATION_TYPES
|
|
1599
|
+
})
|
|
1600
|
+
);
|
|
1601
|
+
continue;
|
|
1602
|
+
}
|
|
1603
|
+
if (operation.kind === "delete") {
|
|
1604
|
+
const existing = state.byCollection[operation.type].get(
|
|
1605
|
+
operation.targetId
|
|
1606
|
+
);
|
|
1607
|
+
if (!existing) {
|
|
1608
|
+
diagnostics.push(
|
|
1609
|
+
buildDiagnostic({
|
|
1610
|
+
severity: "error",
|
|
1611
|
+
code: "missing_target",
|
|
1612
|
+
message: `Delete target "${operation.targetId}" does not exist in "${operation.type}".`,
|
|
1613
|
+
patchId,
|
|
1614
|
+
operationId: operation.id,
|
|
1615
|
+
targetId: operation.targetId
|
|
1616
|
+
})
|
|
1617
|
+
);
|
|
1618
|
+
continue;
|
|
1619
|
+
}
|
|
1620
|
+
state.byCollection[operation.type].delete(operation.targetId);
|
|
1621
|
+
state.byId.delete(operation.targetId);
|
|
1622
|
+
deleteTargets.push(operation.targetId);
|
|
1623
|
+
appliedOperationIds.push(operation.id);
|
|
1624
|
+
continue;
|
|
1625
|
+
}
|
|
1626
|
+
const parsedRecord = parsePatchRecord(operation, diagnostics);
|
|
1627
|
+
if (!parsedRecord) {
|
|
1628
|
+
continue;
|
|
1629
|
+
}
|
|
1630
|
+
const idMessage = validateRecordIdForDomain(
|
|
1631
|
+
operation.type,
|
|
1632
|
+
parsedRecord.id,
|
|
1633
|
+
operation.domain
|
|
1634
|
+
);
|
|
1635
|
+
if (idMessage) {
|
|
1636
|
+
diagnostics.push(
|
|
1637
|
+
buildDiagnostic({
|
|
1638
|
+
severity: "error",
|
|
1639
|
+
code: "invalid_id_convention",
|
|
1640
|
+
message: idMessage,
|
|
1641
|
+
patchId,
|
|
1642
|
+
operationId: operation.id,
|
|
1643
|
+
targetId: parsedRecord.id
|
|
1644
|
+
})
|
|
1645
|
+
);
|
|
1646
|
+
continue;
|
|
1647
|
+
}
|
|
1648
|
+
if (operation.kind === "create" && state.byId.has(parsedRecord.id)) {
|
|
1649
|
+
diagnostics.push(
|
|
1650
|
+
buildDiagnostic({
|
|
1651
|
+
severity: "error",
|
|
1652
|
+
code: "duplicate_id",
|
|
1653
|
+
message: `Create operation would duplicate existing id "${parsedRecord.id}".`,
|
|
1654
|
+
patchId,
|
|
1655
|
+
operationId: operation.id,
|
|
1656
|
+
targetId: parsedRecord.id
|
|
1657
|
+
})
|
|
1658
|
+
);
|
|
1659
|
+
continue;
|
|
1660
|
+
}
|
|
1661
|
+
if (operation.kind === "update") {
|
|
1662
|
+
if (parsedRecord.id !== operation.targetId) {
|
|
1663
|
+
diagnostics.push(
|
|
1664
|
+
buildDiagnostic({
|
|
1665
|
+
severity: "error",
|
|
1666
|
+
code: "update_target_mismatch",
|
|
1667
|
+
message: `Update payload id "${parsedRecord.id}" must match target "${operation.targetId}".`,
|
|
1668
|
+
patchId,
|
|
1669
|
+
operationId: operation.id,
|
|
1670
|
+
targetId: operation.targetId
|
|
1671
|
+
})
|
|
1672
|
+
);
|
|
1673
|
+
continue;
|
|
1674
|
+
}
|
|
1675
|
+
if (!state.byCollection[operation.type].has(operation.targetId)) {
|
|
1676
|
+
diagnostics.push(
|
|
1677
|
+
buildDiagnostic({
|
|
1678
|
+
severity: "error",
|
|
1679
|
+
code: "missing_target",
|
|
1680
|
+
message: `Update target "${operation.targetId}" does not exist in "${operation.type}".`,
|
|
1681
|
+
patchId,
|
|
1682
|
+
operationId: operation.id,
|
|
1683
|
+
targetId: operation.targetId
|
|
1684
|
+
})
|
|
1685
|
+
);
|
|
1686
|
+
continue;
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
state.byCollection[operation.type].set(parsedRecord.id, parsedRecord);
|
|
1690
|
+
state.byId.set(parsedRecord.id, parsedRecord);
|
|
1691
|
+
appliedOperationIds.push(operation.id);
|
|
1692
|
+
}
|
|
1693
|
+
for (const record2 of state.byId.values()) {
|
|
1694
|
+
for (const referenceId of collectReferenceIds2(record2)) {
|
|
1695
|
+
if (!state.byId.has(referenceId)) {
|
|
1696
|
+
diagnostics.push(
|
|
1697
|
+
buildDiagnostic({
|
|
1698
|
+
severity: "error",
|
|
1699
|
+
code: "invalid_reference",
|
|
1700
|
+
message: `Record "${record2.id}" references unknown id "${referenceId}".`,
|
|
1701
|
+
patchId,
|
|
1702
|
+
targetId: record2.id
|
|
1703
|
+
})
|
|
1704
|
+
);
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
const inboundReferenceMap = /* @__PURE__ */ new Map();
|
|
1709
|
+
for (const record2 of state.byId.values()) {
|
|
1710
|
+
for (const referenceId of collectReferenceIds2(record2)) {
|
|
1711
|
+
const inbound = inboundReferenceMap.get(referenceId) ?? [];
|
|
1712
|
+
inbound.push(record2.id);
|
|
1713
|
+
inboundReferenceMap.set(referenceId, inbound);
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1716
|
+
for (const deleteTarget of deleteTargets) {
|
|
1717
|
+
const inbound = inboundReferenceMap.get(deleteTarget) ?? [];
|
|
1718
|
+
if (inbound.length > 0) {
|
|
1719
|
+
diagnostics.push(
|
|
1720
|
+
buildDiagnostic({
|
|
1721
|
+
severity: "error",
|
|
1722
|
+
code: "delete_has_inbound_references",
|
|
1723
|
+
message: `Delete target "${deleteTarget}" still has inbound references from: ${inbound.join(
|
|
1724
|
+
", "
|
|
1725
|
+
)}`,
|
|
1726
|
+
patchId,
|
|
1727
|
+
targetId: deleteTarget
|
|
1728
|
+
})
|
|
1729
|
+
);
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
return {
|
|
1733
|
+
state,
|
|
1734
|
+
diagnostics,
|
|
1735
|
+
appliedOperationIds,
|
|
1736
|
+
deleteTargets
|
|
1737
|
+
};
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
// packages/core/src/knowledge/effective-state.ts
|
|
1741
|
+
function toSimulationStore(rootDir, sourceFiles, state) {
|
|
1742
|
+
const store = {
|
|
1743
|
+
rootDir,
|
|
1744
|
+
sourceFiles,
|
|
1745
|
+
domains: [...state.byCollection.domains.values()],
|
|
1746
|
+
entities: [...state.byCollection.entities.values()],
|
|
1747
|
+
relationships: [...state.byCollection.relationships.values()],
|
|
1748
|
+
capabilities: [...state.byCollection.capabilities.values()],
|
|
1749
|
+
features: [...state.byCollection.features.values()],
|
|
1750
|
+
business_rules: [...state.byCollection.business_rules.values()],
|
|
1751
|
+
constraints: [...state.byCollection.constraints.values()],
|
|
1752
|
+
rationales: [...state.byCollection.rationales.values()],
|
|
1753
|
+
evidence: [...state.byCollection.evidence.values()],
|
|
1754
|
+
questions: [...state.byCollection.questions.values()],
|
|
1755
|
+
conflicts: [...state.byCollection.conflicts.values()],
|
|
1756
|
+
byCollection: state.byCollection,
|
|
1757
|
+
byId: state.byId,
|
|
1758
|
+
reverseReferences: /* @__PURE__ */ new Map(),
|
|
1759
|
+
diagnostics: []
|
|
1760
|
+
};
|
|
1761
|
+
for (const record2 of store.byId.values()) {
|
|
1762
|
+
for (const referenceId of collectReferenceIds2(record2)) {
|
|
1763
|
+
const reverse = store.reverseReferences.get(referenceId) ?? [];
|
|
1764
|
+
reverse.push(record2.id);
|
|
1765
|
+
store.reverseReferences.set(referenceId, reverse);
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
return store;
|
|
1769
|
+
}
|
|
1770
|
+
function selectOrderedPatches(patches, options) {
|
|
1771
|
+
const visiblePatches = options.includeAllStatuses ? patches : patches.filter((patch) => patch.status === "draft");
|
|
1772
|
+
if (!options.selectedPatchId) {
|
|
1773
|
+
return visiblePatches;
|
|
1774
|
+
}
|
|
1775
|
+
const selectedIndex = visiblePatches.findIndex(
|
|
1776
|
+
(patch) => patch.id === options.selectedPatchId
|
|
1777
|
+
);
|
|
1778
|
+
if (selectedIndex === -1) {
|
|
1779
|
+
throw new Error(`Patch "${options.selectedPatchId}" was not found.`);
|
|
1780
|
+
}
|
|
1781
|
+
const endIndex = options.includeSelectedPatch === false ? selectedIndex : selectedIndex + 1;
|
|
1782
|
+
return visiblePatches.slice(0, endIndex);
|
|
1783
|
+
}
|
|
1784
|
+
async function composeEffectiveKnowledgeState(store, envValue, options = {}, cwd = process.cwd()) {
|
|
1785
|
+
const orderedPatches = selectOrderedPatches(
|
|
1786
|
+
await listKnowledgePatches(envValue, cwd),
|
|
1787
|
+
options
|
|
1788
|
+
);
|
|
1789
|
+
let currentState = store;
|
|
1790
|
+
const provenance = /* @__PURE__ */ new Map();
|
|
1791
|
+
const diagnostics = [];
|
|
1792
|
+
for (const patch of orderedPatches) {
|
|
1793
|
+
const patchState = await readKnowledgePatch(envValue, patch.id, cwd);
|
|
1794
|
+
const simulation = simulateKnowledgePatch(
|
|
1795
|
+
currentState,
|
|
1796
|
+
patchState.operations,
|
|
1797
|
+
patch.id
|
|
1798
|
+
);
|
|
1799
|
+
diagnostics.push(...simulation.diagnostics);
|
|
1800
|
+
for (const operationId of simulation.appliedOperationIds) {
|
|
1801
|
+
const operation = patchState.operations.find(
|
|
1802
|
+
(entry) => entry.id === operationId
|
|
1803
|
+
);
|
|
1804
|
+
if (!operation || operation.kind === "delete") {
|
|
1805
|
+
continue;
|
|
1806
|
+
}
|
|
1807
|
+
const recordId = operation.kind === "update" ? operation.targetId : operation.payload.id;
|
|
1808
|
+
const existing = provenance.get(recordId);
|
|
1809
|
+
const patchIds = new Set(existing?.patchIds ?? []);
|
|
1810
|
+
patchIds.add(patch.id);
|
|
1811
|
+
provenance.set(recordId, {
|
|
1812
|
+
patchIds: [...patchIds],
|
|
1813
|
+
patched: true
|
|
1814
|
+
});
|
|
1815
|
+
}
|
|
1816
|
+
currentState = toSimulationStore(
|
|
1817
|
+
store.rootDir,
|
|
1818
|
+
store.sourceFiles,
|
|
1819
|
+
simulation.state
|
|
1820
|
+
);
|
|
1821
|
+
}
|
|
1822
|
+
return {
|
|
1823
|
+
store: currentState,
|
|
1824
|
+
patches: orderedPatches,
|
|
1825
|
+
patchIds: orderedPatches.map((patch) => patch.id),
|
|
1826
|
+
provenance,
|
|
1827
|
+
diagnostics
|
|
1828
|
+
};
|
|
1829
|
+
}
|
|
1830
|
+
async function loadEffectiveKnowledgeStoreFromRoot(rootDir, options = {}) {
|
|
1831
|
+
const store = await loadKnowledgeStoreFromRoot(rootDir);
|
|
1832
|
+
return composeEffectiveKnowledgeState(store, rootDir, options);
|
|
1833
|
+
}
|
|
1834
|
+
async function loadEffectiveKnowledgeStoreFromEnv(envValue, cwd = process.cwd(), options = {}) {
|
|
1835
|
+
const store = await loadKnowledgeStoreFromEnv(envValue, cwd);
|
|
1836
|
+
if (!store) {
|
|
1837
|
+
return void 0;
|
|
1838
|
+
}
|
|
1839
|
+
return composeEffectiveKnowledgeState(store, envValue, options, cwd);
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
// packages/core/src/knowledge/graphql.ts
|
|
1843
|
+
import {
|
|
1844
|
+
GraphQLBoolean,
|
|
1845
|
+
GraphQLFloat,
|
|
1846
|
+
GraphQLID,
|
|
1847
|
+
GraphQLList,
|
|
1848
|
+
GraphQLObjectType,
|
|
1849
|
+
GraphQLSchema,
|
|
1850
|
+
GraphQLString,
|
|
1851
|
+
graphql
|
|
1852
|
+
} from "graphql";
|
|
1853
|
+
function stringifyValue(value) {
|
|
1854
|
+
if (value === void 0 || value === null) {
|
|
1855
|
+
return null;
|
|
1856
|
+
}
|
|
1857
|
+
return typeof value === "string" ? value : JSON.stringify(value);
|
|
1858
|
+
}
|
|
1859
|
+
var confidenceType = new GraphQLObjectType({
|
|
1860
|
+
name: "Confidence",
|
|
1861
|
+
fields: {
|
|
1862
|
+
score: { type: GraphQLFloat },
|
|
1863
|
+
level: { type: GraphQLString },
|
|
1864
|
+
reason: { type: GraphQLString }
|
|
1865
|
+
}
|
|
1866
|
+
});
|
|
1867
|
+
var locationType = new GraphQLObjectType({
|
|
1868
|
+
name: "EvidenceLocation",
|
|
1869
|
+
fields: {
|
|
1870
|
+
path: { type: GraphQLString },
|
|
1871
|
+
symbol: { type: GraphQLString },
|
|
1872
|
+
route: { type: GraphQLString },
|
|
1873
|
+
api: { type: GraphQLString },
|
|
1874
|
+
query: { type: GraphQLString }
|
|
1875
|
+
}
|
|
1876
|
+
});
|
|
1877
|
+
var evidenceType = new GraphQLObjectType({
|
|
1878
|
+
name: "Evidence",
|
|
1879
|
+
fields: () => ({
|
|
1880
|
+
id: { type: GraphQLID },
|
|
1881
|
+
source_type: { type: GraphQLString },
|
|
1882
|
+
description: { type: GraphQLString },
|
|
1883
|
+
location: { type: locationType },
|
|
1884
|
+
supports: { type: new GraphQLList(GraphQLID) },
|
|
1885
|
+
excerpt: { type: GraphQLString },
|
|
1886
|
+
confidence: { type: confidenceType }
|
|
1887
|
+
})
|
|
1888
|
+
});
|
|
1889
|
+
var entityTechnicalRepresentationType = new GraphQLObjectType({
|
|
1890
|
+
name: "EntityTechnicalRepresentation",
|
|
1891
|
+
fields: {
|
|
1892
|
+
kind: { type: GraphQLString },
|
|
1893
|
+
name: { type: GraphQLString },
|
|
1894
|
+
location_hint: { type: GraphQLString }
|
|
1895
|
+
}
|
|
1896
|
+
});
|
|
1897
|
+
var entityType = new GraphQLObjectType({
|
|
1898
|
+
name: "Entity",
|
|
1899
|
+
fields: () => ({
|
|
1900
|
+
id: { type: GraphQLID },
|
|
1901
|
+
name: { type: GraphQLString },
|
|
1902
|
+
business_definition: { type: GraphQLString },
|
|
1903
|
+
description: { type: GraphQLString },
|
|
1904
|
+
purpose: { type: GraphQLString },
|
|
1905
|
+
aliases: { type: new GraphQLList(GraphQLString) },
|
|
1906
|
+
ambiguous_terms: { type: new GraphQLList(GraphQLString) },
|
|
1907
|
+
technical_representations: {
|
|
1908
|
+
type: new GraphQLList(entityTechnicalRepresentationType)
|
|
1909
|
+
},
|
|
1910
|
+
domain_ids: { type: new GraphQLList(GraphQLID) },
|
|
1911
|
+
state_values: { type: new GraphQLList(GraphQLString) },
|
|
1912
|
+
relationship_ids: { type: new GraphQLList(GraphQLID) },
|
|
1913
|
+
capability_ids: { type: new GraphQLList(GraphQLID) },
|
|
1914
|
+
feature_ids: { type: new GraphQLList(GraphQLID) },
|
|
1915
|
+
rule_ids: { type: new GraphQLList(GraphQLID) },
|
|
1916
|
+
domains: {
|
|
1917
|
+
type: new GraphQLList(domainType),
|
|
1918
|
+
resolve: (entity, _args, context) => resolveByIds(context, "domains", entity.domain_ids)
|
|
1919
|
+
},
|
|
1920
|
+
relationships: {
|
|
1921
|
+
type: new GraphQLList(entityRelationshipType),
|
|
1922
|
+
resolve: (entity, _args, context) => resolveByIds(context, "relationships", entity.relationship_ids)
|
|
1923
|
+
},
|
|
1924
|
+
capabilities: {
|
|
1925
|
+
type: new GraphQLList(capabilityType),
|
|
1926
|
+
resolve: (entity, _args, context) => resolveByIds(context, "capabilities", entity.capability_ids)
|
|
1927
|
+
},
|
|
1928
|
+
features: {
|
|
1929
|
+
type: new GraphQLList(featureType),
|
|
1930
|
+
resolve: (entity, _args, context) => resolveByIds(context, "features", entity.feature_ids)
|
|
1931
|
+
},
|
|
1932
|
+
business_rules: {
|
|
1933
|
+
type: new GraphQLList(businessRuleType),
|
|
1934
|
+
resolve: (entity, _args, context) => resolveByIds(context, "business_rules", entity.rule_ids)
|
|
1935
|
+
},
|
|
1936
|
+
certainty: { type: GraphQLString },
|
|
1937
|
+
confidence: { type: confidenceType }
|
|
1938
|
+
})
|
|
1939
|
+
});
|
|
1940
|
+
var ruleConditionType = new GraphQLObjectType({
|
|
1941
|
+
name: "RuleCondition",
|
|
1942
|
+
fields: {
|
|
1943
|
+
subject: { type: GraphQLString },
|
|
1944
|
+
field: { type: GraphQLString },
|
|
1945
|
+
operator: { type: GraphQLString },
|
|
1946
|
+
value: {
|
|
1947
|
+
type: GraphQLString,
|
|
1948
|
+
resolve: (condition) => stringifyValue(condition.value)
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
});
|
|
1952
|
+
var ruleTriggerType = new GraphQLObjectType({
|
|
1953
|
+
name: "RuleTrigger",
|
|
1954
|
+
fields: {
|
|
1955
|
+
actor: { type: GraphQLString },
|
|
1956
|
+
action: { type: GraphQLString },
|
|
1957
|
+
subject: { type: GraphQLString },
|
|
1958
|
+
field: { type: GraphQLString },
|
|
1959
|
+
operator: { type: GraphQLString },
|
|
1960
|
+
value: {
|
|
1961
|
+
type: GraphQLString,
|
|
1962
|
+
resolve: (trigger) => stringifyValue(trigger.value)
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
});
|
|
1966
|
+
var ruleEffectType = new GraphQLObjectType({
|
|
1967
|
+
name: "RuleEffect",
|
|
1968
|
+
fields: {
|
|
1969
|
+
effect: { type: GraphQLString },
|
|
1970
|
+
action: { type: GraphQLString },
|
|
1971
|
+
message: { type: GraphQLString },
|
|
1972
|
+
target_state: { type: GraphQLString },
|
|
1973
|
+
value: {
|
|
1974
|
+
type: GraphQLString,
|
|
1975
|
+
resolve: (effect) => stringifyValue(effect.value)
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
});
|
|
1979
|
+
var businessRuleConditionType = new GraphQLObjectType({
|
|
1980
|
+
name: "BusinessRuleCondition",
|
|
1981
|
+
fields: {
|
|
1982
|
+
given: { type: new GraphQLList(ruleConditionType) },
|
|
1983
|
+
when: { type: new GraphQLList(ruleTriggerType) },
|
|
1984
|
+
then: { type: new GraphQLList(ruleEffectType) }
|
|
1985
|
+
}
|
|
1986
|
+
});
|
|
1987
|
+
var businessRuleAppliesToType = new GraphQLObjectType({
|
|
1988
|
+
name: "BusinessRuleAppliesTo",
|
|
1989
|
+
fields: {
|
|
1990
|
+
domain_ids: { type: new GraphQLList(GraphQLID) },
|
|
1991
|
+
entity_ids: { type: new GraphQLList(GraphQLID) },
|
|
1992
|
+
capability_ids: { type: new GraphQLList(GraphQLID) },
|
|
1993
|
+
feature_ids: { type: new GraphQLList(GraphQLID) }
|
|
1994
|
+
}
|
|
1995
|
+
});
|
|
1996
|
+
var businessRuleEnforcementType = new GraphQLObjectType({
|
|
1997
|
+
name: "BusinessRuleEnforcement",
|
|
1998
|
+
fields: {
|
|
1999
|
+
level: { type: GraphQLString },
|
|
2000
|
+
description: { type: GraphQLString }
|
|
2001
|
+
}
|
|
2002
|
+
});
|
|
2003
|
+
var businessRuleType = new GraphQLObjectType({
|
|
2004
|
+
name: "BusinessRule",
|
|
2005
|
+
fields: () => ({
|
|
2006
|
+
id: { type: GraphQLID },
|
|
2007
|
+
name: { type: GraphQLString },
|
|
2008
|
+
statement: { type: GraphQLString },
|
|
2009
|
+
rule_type: { type: GraphQLString },
|
|
2010
|
+
behavior_type: { type: GraphQLString },
|
|
2011
|
+
applies_to: { type: businessRuleAppliesToType },
|
|
2012
|
+
condition: { type: businessRuleConditionType },
|
|
2013
|
+
enforcement: { type: businessRuleEnforcementType },
|
|
2014
|
+
related_constraint_ids: { type: new GraphQLList(GraphQLID) },
|
|
2015
|
+
rationale_ids: { type: new GraphQLList(GraphQLID) },
|
|
2016
|
+
certainty: { type: GraphQLString },
|
|
2017
|
+
confidence: { type: confidenceType }
|
|
2018
|
+
})
|
|
2019
|
+
});
|
|
2020
|
+
var constraintCauseType = new GraphQLObjectType({
|
|
2021
|
+
name: "ConstraintCause",
|
|
2022
|
+
fields: {
|
|
2023
|
+
source: { type: GraphQLString },
|
|
2024
|
+
source_type: { type: GraphQLString },
|
|
2025
|
+
description: { type: GraphQLString }
|
|
2026
|
+
}
|
|
2027
|
+
});
|
|
2028
|
+
var constraintImpactType = new GraphQLObjectType({
|
|
2029
|
+
name: "ConstraintImpact",
|
|
2030
|
+
fields: {
|
|
2031
|
+
affected_change_types: { type: new GraphQLList(GraphQLString) },
|
|
2032
|
+
severity: { type: GraphQLString },
|
|
2033
|
+
description: { type: GraphQLString }
|
|
2034
|
+
}
|
|
2035
|
+
});
|
|
2036
|
+
var constraintAppliesToType = new GraphQLObjectType({
|
|
2037
|
+
name: "ConstraintAppliesTo",
|
|
2038
|
+
fields: {
|
|
2039
|
+
domain_ids: { type: new GraphQLList(GraphQLID) },
|
|
2040
|
+
entity_ids: { type: new GraphQLList(GraphQLID) },
|
|
2041
|
+
capability_ids: { type: new GraphQLList(GraphQLID) },
|
|
2042
|
+
feature_ids: { type: new GraphQLList(GraphQLID) },
|
|
2043
|
+
business_rule_ids: { type: new GraphQLList(GraphQLID) }
|
|
2044
|
+
}
|
|
2045
|
+
});
|
|
2046
|
+
var constraintWorkaroundType = new GraphQLObjectType({
|
|
2047
|
+
name: "ConstraintWorkaround",
|
|
2048
|
+
fields: {
|
|
2049
|
+
exists: { type: GraphQLString },
|
|
2050
|
+
description: { type: GraphQLString }
|
|
2051
|
+
}
|
|
2052
|
+
});
|
|
2053
|
+
var constraintRemovabilityType = new GraphQLObjectType({
|
|
2054
|
+
name: "ConstraintRemovability",
|
|
2055
|
+
fields: {
|
|
2056
|
+
can_be_removed_by_refactor: { type: GraphQLString },
|
|
2057
|
+
requires: { type: new GraphQLList(GraphQLString) },
|
|
2058
|
+
risk: { type: GraphQLString }
|
|
2059
|
+
}
|
|
2060
|
+
});
|
|
2061
|
+
var constraintType = new GraphQLObjectType({
|
|
2062
|
+
name: "Constraint",
|
|
2063
|
+
fields: () => ({
|
|
2064
|
+
id: { type: GraphQLID },
|
|
2065
|
+
name: { type: GraphQLString },
|
|
2066
|
+
description: { type: GraphQLString },
|
|
2067
|
+
constraint_type: { type: GraphQLString },
|
|
2068
|
+
cause: { type: constraintCauseType },
|
|
2069
|
+
impact: { type: constraintImpactType },
|
|
2070
|
+
applies_to: { type: constraintAppliesToType },
|
|
2071
|
+
workaround: { type: constraintWorkaroundType },
|
|
2072
|
+
removability: { type: constraintRemovabilityType },
|
|
2073
|
+
certainty: { type: GraphQLString },
|
|
2074
|
+
confidence: { type: confidenceType }
|
|
2075
|
+
})
|
|
2076
|
+
});
|
|
2077
|
+
var rationaleType = new GraphQLObjectType({
|
|
2078
|
+
name: "Rationale",
|
|
2079
|
+
fields: () => ({
|
|
2080
|
+
id: { type: GraphQLID },
|
|
2081
|
+
statement: { type: GraphQLString },
|
|
2082
|
+
explains: { type: new GraphQLList(GraphQLID) },
|
|
2083
|
+
certainty: { type: GraphQLString },
|
|
2084
|
+
confidence: { type: confidenceType }
|
|
2085
|
+
})
|
|
2086
|
+
});
|
|
2087
|
+
var questionType = new GraphQLObjectType({
|
|
2088
|
+
name: "Question",
|
|
2089
|
+
fields: () => ({
|
|
2090
|
+
id: { type: GraphQLID },
|
|
2091
|
+
question: { type: GraphQLString },
|
|
2092
|
+
category: { type: GraphQLString },
|
|
2093
|
+
reason: { type: GraphQLString },
|
|
2094
|
+
impact: { type: GraphQLString },
|
|
2095
|
+
related_ids: { type: new GraphQLList(GraphQLID) },
|
|
2096
|
+
evidence_ids: { type: new GraphQLList(GraphQLID) },
|
|
2097
|
+
priority: { type: GraphQLString },
|
|
2098
|
+
status: { type: GraphQLString },
|
|
2099
|
+
answer: { type: GraphQLString },
|
|
2100
|
+
answered_by: { type: GraphQLString },
|
|
2101
|
+
answered_at: { type: GraphQLString }
|
|
2102
|
+
})
|
|
2103
|
+
});
|
|
2104
|
+
var conflictType = new GraphQLObjectType({
|
|
2105
|
+
name: "Conflict",
|
|
2106
|
+
fields: () => ({
|
|
2107
|
+
id: { type: GraphQLID },
|
|
2108
|
+
type: { type: GraphQLString },
|
|
2109
|
+
description: { type: GraphQLString },
|
|
2110
|
+
related_ids: { type: new GraphQLList(GraphQLID) },
|
|
2111
|
+
evidence_ids: { type: new GraphQLList(GraphQLID) },
|
|
2112
|
+
status: { type: GraphQLString },
|
|
2113
|
+
questions: { type: new GraphQLList(GraphQLID) },
|
|
2114
|
+
resolution: { type: GraphQLString },
|
|
2115
|
+
resolved_by_evidence_id: { type: GraphQLID }
|
|
2116
|
+
})
|
|
2117
|
+
});
|
|
2118
|
+
var domainType = new GraphQLObjectType({
|
|
2119
|
+
name: "Domain",
|
|
2120
|
+
fields: () => ({
|
|
2121
|
+
id: { type: GraphQLID },
|
|
2122
|
+
name: { type: GraphQLString },
|
|
2123
|
+
purpose: { type: GraphQLString },
|
|
2124
|
+
why_it_exists: { type: GraphQLString },
|
|
2125
|
+
entity_ids: { type: new GraphQLList(GraphQLID) },
|
|
2126
|
+
capability_ids: { type: new GraphQLList(GraphQLID) },
|
|
2127
|
+
entities: {
|
|
2128
|
+
type: new GraphQLList(entityType),
|
|
2129
|
+
resolve: (domain, _args, context) => resolveByIds(context, "entities", domain.entity_ids)
|
|
2130
|
+
},
|
|
2131
|
+
capabilities: {
|
|
2132
|
+
type: new GraphQLList(capabilityType),
|
|
2133
|
+
resolve: (domain, _args, context) => resolveByIds(context, "capabilities", domain.capability_ids)
|
|
2134
|
+
},
|
|
2135
|
+
certainty: { type: GraphQLString },
|
|
2136
|
+
confidence: { type: confidenceType }
|
|
2137
|
+
})
|
|
2138
|
+
});
|
|
2139
|
+
var entityRelationshipType = new GraphQLObjectType({
|
|
2140
|
+
name: "EntityRelationship",
|
|
2141
|
+
fields: () => ({
|
|
2142
|
+
id: { type: GraphQLID },
|
|
2143
|
+
from_entity_id: { type: GraphQLID },
|
|
2144
|
+
to_entity_id: { type: GraphQLID },
|
|
2145
|
+
relationship_type: { type: GraphQLString },
|
|
2146
|
+
description: { type: GraphQLString },
|
|
2147
|
+
from_entity: {
|
|
2148
|
+
type: entityType,
|
|
2149
|
+
resolve: (relationship, _args, context) => resolveByIds(context, "entities", [relationship.from_entity_id])[0] ?? null
|
|
2150
|
+
},
|
|
2151
|
+
to_entity: {
|
|
2152
|
+
type: entityType,
|
|
2153
|
+
resolve: (relationship, _args, context) => resolveByIds(context, "entities", [relationship.to_entity_id])[0] ?? null
|
|
2154
|
+
},
|
|
2155
|
+
certainty: { type: GraphQLString },
|
|
2156
|
+
confidence: { type: confidenceType }
|
|
2157
|
+
})
|
|
2158
|
+
});
|
|
2159
|
+
var capabilityType = new GraphQLObjectType({
|
|
2160
|
+
name: "Capability",
|
|
2161
|
+
fields: () => ({
|
|
2162
|
+
id: { type: GraphQLID },
|
|
2163
|
+
name: { type: GraphQLString },
|
|
2164
|
+
description: { type: GraphQLString },
|
|
2165
|
+
why_it_exists: { type: GraphQLString },
|
|
2166
|
+
domain_ids: { type: new GraphQLList(GraphQLID) },
|
|
2167
|
+
entity_ids: { type: new GraphQLList(GraphQLID) },
|
|
2168
|
+
parent_capability_ids: { type: new GraphQLList(GraphQLID) },
|
|
2169
|
+
child_capability_ids: { type: new GraphQLList(GraphQLID) },
|
|
2170
|
+
feature_ids: { type: new GraphQLList(GraphQLID) },
|
|
2171
|
+
domains: {
|
|
2172
|
+
type: new GraphQLList(domainType),
|
|
2173
|
+
resolve: (capability, _args, context) => resolveByIds(context, "domains", capability.domain_ids)
|
|
2174
|
+
},
|
|
2175
|
+
entities: {
|
|
2176
|
+
type: new GraphQLList(entityType),
|
|
2177
|
+
resolve: (capability, _args, context) => resolveByIds(context, "entities", capability.entity_ids)
|
|
2178
|
+
},
|
|
2179
|
+
parent_capabilities: {
|
|
2180
|
+
type: new GraphQLList(capabilityType),
|
|
2181
|
+
resolve: (capability, _args, context) => resolveByIds(context, "capabilities", capability.parent_capability_ids)
|
|
2182
|
+
},
|
|
2183
|
+
child_capabilities: {
|
|
2184
|
+
type: new GraphQLList(capabilityType),
|
|
2185
|
+
resolve: (capability, _args, context) => resolveByIds(context, "capabilities", capability.child_capability_ids)
|
|
2186
|
+
},
|
|
2187
|
+
features: {
|
|
2188
|
+
type: new GraphQLList(featureType),
|
|
2189
|
+
resolve: (capability, _args, context) => resolveByIds(context, "features", capability.feature_ids)
|
|
2190
|
+
},
|
|
2191
|
+
certainty: { type: GraphQLString },
|
|
2192
|
+
confidence: { type: confidenceType }
|
|
2193
|
+
})
|
|
2194
|
+
});
|
|
2195
|
+
var featureType = new GraphQLObjectType({
|
|
2196
|
+
name: "Feature",
|
|
2197
|
+
fields: () => ({
|
|
2198
|
+
id: { type: GraphQLID },
|
|
2199
|
+
name: { type: GraphQLString },
|
|
2200
|
+
description: { type: GraphQLString },
|
|
2201
|
+
purpose: { type: GraphQLString },
|
|
2202
|
+
why_it_exists: { type: GraphQLString },
|
|
2203
|
+
domain_ids: { type: new GraphQLList(GraphQLID) },
|
|
2204
|
+
capability_ids: { type: new GraphQLList(GraphQLID) },
|
|
2205
|
+
entity_ids: { type: new GraphQLList(GraphQLID) },
|
|
2206
|
+
rule_ids: { type: new GraphQLList(GraphQLID) },
|
|
2207
|
+
constraint_ids: { type: new GraphQLList(GraphQLID) },
|
|
2208
|
+
domains: {
|
|
2209
|
+
type: new GraphQLList(domainType),
|
|
2210
|
+
resolve: (feature, _args, context) => resolveByIds(context, "domains", feature.domain_ids)
|
|
2211
|
+
},
|
|
2212
|
+
capabilities: {
|
|
2213
|
+
type: new GraphQLList(capabilityType),
|
|
2214
|
+
resolve: (feature, _args, context) => resolveByIds(context, "capabilities", feature.capability_ids)
|
|
2215
|
+
},
|
|
2216
|
+
entities: {
|
|
2217
|
+
type: new GraphQLList(entityType),
|
|
2218
|
+
resolve: (feature, _args, context) => resolveByIds(context, "entities", feature.entity_ids)
|
|
2219
|
+
},
|
|
2220
|
+
business_rules: {
|
|
2221
|
+
type: new GraphQLList(businessRuleType),
|
|
2222
|
+
resolve: (feature, _args, context) => resolveByIds(context, "business_rules", feature.rule_ids)
|
|
2223
|
+
},
|
|
2224
|
+
constraints: {
|
|
2225
|
+
type: new GraphQLList(constraintType),
|
|
2226
|
+
resolve: (feature, _args, context) => resolveByIds(context, "constraints", feature.constraint_ids)
|
|
2227
|
+
},
|
|
2228
|
+
certainty: { type: GraphQLString },
|
|
2229
|
+
confidence: { type: confidenceType }
|
|
2230
|
+
})
|
|
2231
|
+
});
|
|
2232
|
+
function resolveCollection(store, collection) {
|
|
2233
|
+
if (!store) {
|
|
2234
|
+
return [];
|
|
2235
|
+
}
|
|
2236
|
+
return store[collection];
|
|
2237
|
+
}
|
|
2238
|
+
function resolveByIds(store, collection, ids) {
|
|
2239
|
+
if (!store || !ids?.length) {
|
|
2240
|
+
return [];
|
|
2241
|
+
}
|
|
2242
|
+
const typedCollection = store.byCollection[collection];
|
|
2243
|
+
return ids.map((id) => typedCollection.get(id)).filter((item) => item !== void 0);
|
|
2244
|
+
}
|
|
2245
|
+
var queryType = new GraphQLObjectType({
|
|
2246
|
+
name: "Query",
|
|
2247
|
+
fields: () => ({
|
|
2248
|
+
store_available: {
|
|
2249
|
+
type: GraphQLBoolean,
|
|
2250
|
+
resolve: (_source, _args, context) => Boolean(context)
|
|
2251
|
+
},
|
|
2252
|
+
domains: {
|
|
2253
|
+
type: new GraphQLList(domainType),
|
|
2254
|
+
resolve: (_source, _args, context) => resolveCollection(context, "domains")
|
|
2255
|
+
},
|
|
2256
|
+
domain: {
|
|
2257
|
+
type: domainType,
|
|
2258
|
+
args: {
|
|
2259
|
+
id: { type: GraphQLID }
|
|
2260
|
+
},
|
|
2261
|
+
resolve: (_source, args, context) => context?.byCollection.domains.get(args.id) ?? null
|
|
2262
|
+
},
|
|
2263
|
+
entities: {
|
|
2264
|
+
type: new GraphQLList(entityType),
|
|
2265
|
+
resolve: (_source, _args, context) => resolveCollection(context, "entities")
|
|
2266
|
+
},
|
|
2267
|
+
entity: {
|
|
2268
|
+
type: entityType,
|
|
2269
|
+
args: {
|
|
2270
|
+
id: { type: GraphQLID }
|
|
2271
|
+
},
|
|
2272
|
+
resolve: (_source, args, context) => context?.byCollection.entities.get(args.id) ?? null
|
|
2273
|
+
},
|
|
2274
|
+
relationships: {
|
|
2275
|
+
type: new GraphQLList(entityRelationshipType),
|
|
2276
|
+
resolve: (_source, _args, context) => resolveCollection(context, "relationships")
|
|
2277
|
+
},
|
|
2278
|
+
capabilities: {
|
|
2279
|
+
type: new GraphQLList(capabilityType),
|
|
2280
|
+
resolve: (_source, _args, context) => resolveCollection(context, "capabilities")
|
|
2281
|
+
},
|
|
2282
|
+
capability: {
|
|
2283
|
+
type: capabilityType,
|
|
2284
|
+
args: {
|
|
2285
|
+
id: { type: GraphQLID }
|
|
2286
|
+
},
|
|
2287
|
+
resolve: (_source, args, context) => context?.byCollection.capabilities.get(args.id) ?? null
|
|
2288
|
+
},
|
|
2289
|
+
features: {
|
|
2290
|
+
type: new GraphQLList(featureType),
|
|
2291
|
+
resolve: (_source, _args, context) => resolveCollection(context, "features")
|
|
2292
|
+
},
|
|
2293
|
+
feature: {
|
|
2294
|
+
type: featureType,
|
|
2295
|
+
args: {
|
|
2296
|
+
id: { type: GraphQLID }
|
|
2297
|
+
},
|
|
2298
|
+
resolve: (_source, args, context) => context?.byCollection.features.get(args.id) ?? null
|
|
2299
|
+
},
|
|
2300
|
+
business_rules: {
|
|
2301
|
+
type: new GraphQLList(businessRuleType),
|
|
2302
|
+
resolve: (_source, _args, context) => resolveCollection(context, "business_rules")
|
|
2303
|
+
},
|
|
2304
|
+
business_rule: {
|
|
2305
|
+
type: businessRuleType,
|
|
2306
|
+
args: {
|
|
2307
|
+
id: { type: GraphQLID }
|
|
2308
|
+
},
|
|
2309
|
+
resolve: (_source, args, context) => context?.byCollection.business_rules.get(args.id) ?? null
|
|
2310
|
+
},
|
|
2311
|
+
constraints: {
|
|
2312
|
+
type: new GraphQLList(constraintType),
|
|
2313
|
+
resolve: (_source, _args, context) => resolveCollection(context, "constraints")
|
|
2314
|
+
},
|
|
2315
|
+
constraint: {
|
|
2316
|
+
type: constraintType,
|
|
2317
|
+
args: {
|
|
2318
|
+
id: { type: GraphQLID }
|
|
2319
|
+
},
|
|
2320
|
+
resolve: (_source, args, context) => context?.byCollection.constraints.get(args.id) ?? null
|
|
2321
|
+
},
|
|
2322
|
+
rationales: {
|
|
2323
|
+
type: new GraphQLList(rationaleType),
|
|
2324
|
+
resolve: (_source, _args, context) => resolveCollection(context, "rationales")
|
|
2325
|
+
},
|
|
2326
|
+
rationale: {
|
|
2327
|
+
type: rationaleType,
|
|
2328
|
+
args: {
|
|
2329
|
+
id: { type: GraphQLID }
|
|
2330
|
+
},
|
|
2331
|
+
resolve: (_source, args, context) => context?.byCollection.rationales.get(args.id) ?? null
|
|
2332
|
+
},
|
|
2333
|
+
evidence: {
|
|
2334
|
+
type: new GraphQLList(evidenceType),
|
|
2335
|
+
resolve: (_source, _args, context) => resolveCollection(context, "evidence")
|
|
2336
|
+
},
|
|
2337
|
+
question: {
|
|
2338
|
+
type: questionType,
|
|
2339
|
+
args: {
|
|
2340
|
+
id: { type: GraphQLID }
|
|
2341
|
+
},
|
|
2342
|
+
resolve: (_source, args, context) => context?.byCollection.questions.get(args.id) ?? null
|
|
2343
|
+
},
|
|
2344
|
+
questions: {
|
|
2345
|
+
type: new GraphQLList(questionType),
|
|
2346
|
+
resolve: (_source, _args, context) => resolveCollection(context, "questions")
|
|
2347
|
+
},
|
|
2348
|
+
conflict: {
|
|
2349
|
+
type: conflictType,
|
|
2350
|
+
args: {
|
|
2351
|
+
id: { type: GraphQLID }
|
|
2352
|
+
},
|
|
2353
|
+
resolve: (_source, args, context) => context?.byCollection.conflicts.get(args.id) ?? null
|
|
2354
|
+
},
|
|
2355
|
+
conflicts: {
|
|
2356
|
+
type: new GraphQLList(conflictType),
|
|
2357
|
+
resolve: (_source, _args, context) => resolveCollection(context, "conflicts")
|
|
2358
|
+
}
|
|
2359
|
+
})
|
|
2360
|
+
});
|
|
2361
|
+
var knowledgeGraphSchema = new GraphQLSchema({
|
|
2362
|
+
query: queryType
|
|
2363
|
+
});
|
|
2364
|
+
async function executeKnowledgeQuery(store, query, variables, operationName) {
|
|
2365
|
+
if (!store) {
|
|
2366
|
+
return {
|
|
2367
|
+
data: null,
|
|
2368
|
+
errors: [
|
|
2369
|
+
{
|
|
2370
|
+
message: "Knowledge store is not loaded. Set EZ_KNOW_ROOT to the knowledge root to enable querying."
|
|
2371
|
+
}
|
|
2372
|
+
]
|
|
2373
|
+
};
|
|
2374
|
+
}
|
|
2375
|
+
return graphql({
|
|
2376
|
+
schema: knowledgeGraphSchema,
|
|
2377
|
+
source: query,
|
|
2378
|
+
contextValue: store,
|
|
2379
|
+
variableValues: variables,
|
|
2380
|
+
operationName
|
|
2381
|
+
});
|
|
2382
|
+
}
|
|
2383
|
+
function buildKnowledgeGraphQLText(result) {
|
|
2384
|
+
return JSON.stringify({
|
|
2385
|
+
data: result.data,
|
|
2386
|
+
errors: result.errors?.map((error) => ({ message: error.message }))
|
|
2387
|
+
});
|
|
2388
|
+
}
|
|
2389
|
+
|
|
2390
|
+
// packages/core/src/knowledge/patch/apply.ts
|
|
2391
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
2392
|
+
import { mkdir as mkdir2, rm, unlink, writeFile as writeFile2 } from "node:fs/promises";
|
|
2393
|
+
import { dirname } from "node:path";
|
|
2394
|
+
|
|
2395
|
+
// packages/core/src/knowledge/patch/validation.ts
|
|
2396
|
+
import { readFile as readFile4, stat } from "node:fs/promises";
|
|
2397
|
+
import { join as join4 } from "node:path";
|
|
2398
|
+
async function buildSourceFingerprintInput(sourceFiles) {
|
|
2399
|
+
const entries = await Promise.all(
|
|
2400
|
+
sourceFiles.map(async (filePath) => {
|
|
2401
|
+
const [fileStat, content] = await Promise.all([
|
|
2402
|
+
stat(filePath),
|
|
2403
|
+
readFile4(filePath, "utf8")
|
|
2404
|
+
]);
|
|
2405
|
+
return {
|
|
2406
|
+
path: filePath,
|
|
2407
|
+
size: fileStat.size,
|
|
2408
|
+
mtimeMs: fileStat.mtimeMs,
|
|
2409
|
+
contentHash: sha256(content)
|
|
2410
|
+
};
|
|
2411
|
+
})
|
|
2412
|
+
);
|
|
2413
|
+
entries.sort((left, right) => left.path.localeCompare(right.path));
|
|
2414
|
+
return entries;
|
|
2415
|
+
}
|
|
2416
|
+
function buildCanonicalPathForCollection(rootDir, domain, type) {
|
|
2417
|
+
return join4(
|
|
2418
|
+
rootDir,
|
|
2419
|
+
domain,
|
|
2420
|
+
KNOWLEDGE_EXTRACTION_TYPE_REGISTRY[type].storageFile
|
|
2421
|
+
);
|
|
2422
|
+
}
|
|
2423
|
+
function buildAffectedFileList(knowledgeRoot, operations) {
|
|
2424
|
+
const affected = /* @__PURE__ */ new Set();
|
|
2425
|
+
for (const operation of operations) {
|
|
2426
|
+
affected.add(
|
|
2427
|
+
buildCanonicalPathForCollection(
|
|
2428
|
+
knowledgeRoot,
|
|
2429
|
+
operation.domain,
|
|
2430
|
+
operation.type
|
|
2431
|
+
)
|
|
2432
|
+
);
|
|
2433
|
+
}
|
|
2434
|
+
return [...affected].sort((left, right) => left.localeCompare(right));
|
|
2435
|
+
}
|
|
2436
|
+
function buildCollectionRecordsForDomain(state, type, domain) {
|
|
2437
|
+
const records = [...state.byCollection[type].values()].filter(
|
|
2438
|
+
(record2) => inferDomainForRecord(record2) === domain
|
|
2439
|
+
);
|
|
2440
|
+
records.sort((left, right) => left.id.localeCompare(right.id));
|
|
2441
|
+
return records;
|
|
2442
|
+
}
|
|
2443
|
+
async function computeValidationResult(knowledgeRoot, patchId, patch, operations) {
|
|
2444
|
+
const effectiveBase = await loadEffectiveKnowledgeStoreFromRoot(
|
|
2445
|
+
knowledgeRoot,
|
|
2446
|
+
{
|
|
2447
|
+
selectedPatchId: patchId,
|
|
2448
|
+
includeSelectedPatch: false
|
|
2449
|
+
}
|
|
2450
|
+
);
|
|
2451
|
+
const simulation = simulateKnowledgePatch(
|
|
2452
|
+
effectiveBase.store,
|
|
2453
|
+
operations,
|
|
2454
|
+
patchId
|
|
2455
|
+
);
|
|
2456
|
+
const affectedFiles = buildAffectedFileList(knowledgeRoot, operations);
|
|
2457
|
+
const diagnostics = [
|
|
2458
|
+
...effectiveBase.diagnostics,
|
|
2459
|
+
...simulation.diagnostics
|
|
2460
|
+
];
|
|
2461
|
+
if (diagnostics.length > 0) {
|
|
2462
|
+
return knowledgePatchValidationResultSchema.parse({
|
|
2463
|
+
valid: false,
|
|
2464
|
+
diagnostics,
|
|
2465
|
+
operationCount: operations.length,
|
|
2466
|
+
appliedOperationIds: simulation.appliedOperationIds,
|
|
2467
|
+
affectedFiles
|
|
2468
|
+
});
|
|
2469
|
+
}
|
|
2470
|
+
const sourceFingerprint = await buildSourceFingerprintInput(
|
|
2471
|
+
effectiveBase.store.sourceFiles
|
|
2472
|
+
);
|
|
2473
|
+
const fingerprint = sha256(
|
|
2474
|
+
JSON.stringify({
|
|
2475
|
+
schemaVersion: KNOWLEDGE_PATCH_SCHEMA_VERSION,
|
|
2476
|
+
patch: {
|
|
2477
|
+
id: patch.id,
|
|
2478
|
+
status: patch.status,
|
|
2479
|
+
updatedAt: patch.updatedAt
|
|
2480
|
+
},
|
|
2481
|
+
operations,
|
|
2482
|
+
sourceFingerprint
|
|
2483
|
+
})
|
|
2484
|
+
);
|
|
2485
|
+
return knowledgePatchValidationResultSchema.parse({
|
|
2486
|
+
valid: true,
|
|
2487
|
+
fingerprint,
|
|
2488
|
+
diagnostics,
|
|
2489
|
+
operationCount: operations.length,
|
|
2490
|
+
appliedOperationIds: simulation.appliedOperationIds,
|
|
2491
|
+
affectedFiles
|
|
2492
|
+
});
|
|
2493
|
+
}
|
|
2494
|
+
async function validateKnowledgePatch(envValue, patchId, cwd = process.cwd()) {
|
|
2495
|
+
const knowledgeRoot = resolveKnowledgeRoot2(envValue, cwd);
|
|
2496
|
+
const state = await readKnowledgePatch(envValue, patchId, cwd);
|
|
2497
|
+
return computeValidationResult(
|
|
2498
|
+
knowledgeRoot,
|
|
2499
|
+
patchId,
|
|
2500
|
+
state.patch,
|
|
2501
|
+
state.operations
|
|
2502
|
+
);
|
|
2503
|
+
}
|
|
2504
|
+
|
|
2505
|
+
// packages/core/src/knowledge/patch/apply.ts
|
|
2506
|
+
async function writeCanonicalFilesFromSimulation(knowledgeRoot, operations, simulationState) {
|
|
2507
|
+
const plannedWrites = /* @__PURE__ */ new Map();
|
|
2508
|
+
for (const operation of operations) {
|
|
2509
|
+
const filePath = buildCanonicalPathForCollection(
|
|
2510
|
+
knowledgeRoot,
|
|
2511
|
+
operation.domain,
|
|
2512
|
+
operation.type
|
|
2513
|
+
);
|
|
2514
|
+
const records = buildCollectionRecordsForDomain(
|
|
2515
|
+
simulationState,
|
|
2516
|
+
operation.type,
|
|
2517
|
+
operation.domain
|
|
2518
|
+
);
|
|
2519
|
+
if (operation.type === "domains") {
|
|
2520
|
+
if (records.length === 0) {
|
|
2521
|
+
if (existsSync3(filePath)) {
|
|
2522
|
+
await unlink(filePath);
|
|
2523
|
+
}
|
|
2524
|
+
continue;
|
|
2525
|
+
}
|
|
2526
|
+
plannedWrites.set(filePath, `${JSON.stringify(records[0], null, 2)}
|
|
2527
|
+
`);
|
|
2528
|
+
continue;
|
|
2529
|
+
}
|
|
2530
|
+
plannedWrites.set(filePath, `${JSON.stringify(records, null, 2)}
|
|
2531
|
+
`);
|
|
2532
|
+
}
|
|
2533
|
+
for (const filePath of plannedWrites.keys()) {
|
|
2534
|
+
await mkdir2(dirname(filePath), { recursive: true });
|
|
2535
|
+
}
|
|
2536
|
+
for (const [filePath, content] of plannedWrites.entries()) {
|
|
2537
|
+
await writeFile2(filePath, content, "utf8");
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2540
|
+
async function applyKnowledgePatch(envValue, input, cwd = process.cwd()) {
|
|
2541
|
+
const parsedInput = knowledgePatchApplyToolInputSchema.parse(input);
|
|
2542
|
+
const knowledgeRoot = resolveKnowledgeRoot2(envValue, cwd);
|
|
2543
|
+
const patchDir = resolveKnowledgePatchDirectory(
|
|
2544
|
+
envValue,
|
|
2545
|
+
parsedInput.patchId,
|
|
2546
|
+
cwd
|
|
2547
|
+
);
|
|
2548
|
+
const patchState = await loadPatchStateByDir(patchDir);
|
|
2549
|
+
const validation = await computeValidationResult(
|
|
2550
|
+
knowledgeRoot,
|
|
2551
|
+
patchState.patch.id,
|
|
2552
|
+
patchState.patch,
|
|
2553
|
+
patchState.operations
|
|
2554
|
+
);
|
|
2555
|
+
const diagnostics = [...validation.diagnostics];
|
|
2556
|
+
const pendingPatches = (await listKnowledgePatches(envValue, cwd)).filter(
|
|
2557
|
+
(patch) => patch.status === "draft"
|
|
2558
|
+
);
|
|
2559
|
+
const firstPendingPatchId = pendingPatches[0]?.id;
|
|
2560
|
+
if (firstPendingPatchId !== void 0 && firstPendingPatchId !== patchState.patch.id) {
|
|
2561
|
+
diagnostics.push(
|
|
2562
|
+
buildDiagnostic({
|
|
2563
|
+
severity: "error",
|
|
2564
|
+
code: "apply_out_of_order",
|
|
2565
|
+
message: `Patch "${patchState.patch.id}" cannot be applied while earlier pending patch "${firstPendingPatchId}" still exists.`,
|
|
2566
|
+
patchId: patchState.patch.id
|
|
2567
|
+
})
|
|
2568
|
+
);
|
|
2569
|
+
}
|
|
2570
|
+
if (!parsedInput.approval.approved) {
|
|
2571
|
+
diagnostics.push(
|
|
2572
|
+
buildDiagnostic({
|
|
2573
|
+
severity: "error",
|
|
2574
|
+
code: "approval_required",
|
|
2575
|
+
message: "Patch apply requires explicit approval.",
|
|
2576
|
+
patchId: patchState.patch.id
|
|
2577
|
+
})
|
|
2578
|
+
);
|
|
2579
|
+
}
|
|
2580
|
+
if (firstPendingPatchId !== void 0 && firstPendingPatchId !== patchState.patch.id || !parsedInput.approval.approved || !validation.valid || parsedInput.approval.validationFingerprint !== validation.fingerprint) {
|
|
2581
|
+
if (validation.valid && parsedInput.approval.validationFingerprint !== validation.fingerprint) {
|
|
2582
|
+
diagnostics.push(
|
|
2583
|
+
buildDiagnostic({
|
|
2584
|
+
severity: "error",
|
|
2585
|
+
code: "stale_validation_fingerprint",
|
|
2586
|
+
message: "Patch validation fingerprint is stale. Revalidate before apply.",
|
|
2587
|
+
patchId: patchState.patch.id
|
|
2588
|
+
})
|
|
2589
|
+
);
|
|
2590
|
+
}
|
|
2591
|
+
return {
|
|
2592
|
+
patchId: patchState.patch.id,
|
|
2593
|
+
applied: false,
|
|
2594
|
+
validation,
|
|
2595
|
+
diagnostics,
|
|
2596
|
+
deletedPatch: false
|
|
2597
|
+
};
|
|
2598
|
+
}
|
|
2599
|
+
const store = await loadKnowledgeStoreFromRoot(knowledgeRoot);
|
|
2600
|
+
const simulation = simulateKnowledgePatch(
|
|
2601
|
+
store,
|
|
2602
|
+
patchState.operations,
|
|
2603
|
+
patchState.patch.id
|
|
2604
|
+
);
|
|
2605
|
+
if (simulation.diagnostics.length > 0) {
|
|
2606
|
+
return {
|
|
2607
|
+
patchId: patchState.patch.id,
|
|
2608
|
+
applied: false,
|
|
2609
|
+
validation: {
|
|
2610
|
+
...validation,
|
|
2611
|
+
valid: false,
|
|
2612
|
+
diagnostics: simulation.diagnostics
|
|
2613
|
+
},
|
|
2614
|
+
diagnostics: simulation.diagnostics,
|
|
2615
|
+
deletedPatch: false
|
|
2616
|
+
};
|
|
2617
|
+
}
|
|
2618
|
+
await writeCanonicalFilesFromSimulation(
|
|
2619
|
+
knowledgeRoot,
|
|
2620
|
+
patchState.operations,
|
|
2621
|
+
simulation.state
|
|
2622
|
+
);
|
|
2623
|
+
try {
|
|
2624
|
+
await loadKnowledgeStoreFromRoot(knowledgeRoot);
|
|
2625
|
+
} catch (error) {
|
|
2626
|
+
const reloadDiagnostics = error instanceof KnowledgeStoreLoadError ? error.diagnostics.map(
|
|
2627
|
+
(diagnostic) => buildDiagnostic({
|
|
2628
|
+
severity: "critical",
|
|
2629
|
+
code: diagnostic.kind,
|
|
2630
|
+
message: diagnostic.message,
|
|
2631
|
+
path: diagnostic.path ? [diagnostic.path] : void 0,
|
|
2632
|
+
targetId: diagnostic.id
|
|
2633
|
+
})
|
|
2634
|
+
) : [
|
|
2635
|
+
buildDiagnostic({
|
|
2636
|
+
severity: "critical",
|
|
2637
|
+
code: "reload_failed",
|
|
2638
|
+
message: "Canonical knowledge store reload failed after apply.",
|
|
2639
|
+
patchId: patchState.patch.id
|
|
2640
|
+
})
|
|
2641
|
+
];
|
|
2642
|
+
return {
|
|
2643
|
+
patchId: patchState.patch.id,
|
|
2644
|
+
applied: false,
|
|
2645
|
+
validation,
|
|
2646
|
+
diagnostics: reloadDiagnostics,
|
|
2647
|
+
deletedPatch: false
|
|
2648
|
+
};
|
|
2649
|
+
}
|
|
2650
|
+
await rm(patchDir, { recursive: true, force: true });
|
|
2651
|
+
return {
|
|
2652
|
+
patchId: patchState.patch.id,
|
|
2653
|
+
applied: true,
|
|
2654
|
+
validation,
|
|
2655
|
+
diagnostics: [],
|
|
2656
|
+
deletedPatch: true
|
|
2657
|
+
};
|
|
2658
|
+
}
|
|
2659
|
+
|
|
2660
|
+
// packages/core/src/knowledge/patch/scope-audit.ts
|
|
2661
|
+
import { readdir as readdir2 } from "node:fs/promises";
|
|
2662
|
+
import { isAbsolute as isAbsolute2, relative, resolve as resolve5, sep as sep2 } from "node:path";
|
|
2663
|
+
import { z as z6 } from "zod/v4";
|
|
2664
|
+
var compareScopeWithEvidenceInputSchema = z6.object({
|
|
2665
|
+
patchId: z6.string().min(1),
|
|
2666
|
+
rootFolder: z6.string().trim().min(1).optional()
|
|
2667
|
+
});
|
|
2668
|
+
var compareScopeWithEvidenceResultSchema = z6.object({
|
|
2669
|
+
patchId: z6.string().min(1),
|
|
2670
|
+
rootFolder: z6.string().min(1),
|
|
2671
|
+
scopeFiles: z6.array(z6.string()),
|
|
2672
|
+
coveredFiles: z6.array(z6.string()),
|
|
2673
|
+
omittedFiles: z6.array(
|
|
2674
|
+
z6.object({
|
|
2675
|
+
path: z6.string(),
|
|
2676
|
+
reason: z6.string()
|
|
2677
|
+
})
|
|
2678
|
+
),
|
|
2679
|
+
uncoveredFiles: z6.array(z6.string()),
|
|
2680
|
+
outOfRootEvidenceFiles: z6.array(z6.string()),
|
|
2681
|
+
message: z6.string()
|
|
2682
|
+
});
|
|
2683
|
+
function toPosixPath(value) {
|
|
2684
|
+
return value.replaceAll(sep2, "/");
|
|
2685
|
+
}
|
|
2686
|
+
function normalizeAuditedRoot(rootFolder) {
|
|
2687
|
+
return resolve5(rootFolder);
|
|
2688
|
+
}
|
|
2689
|
+
function isPathWithinRoot(rootFolder, filePath) {
|
|
2690
|
+
const relativePath = toPosixPath(relative(rootFolder, filePath));
|
|
2691
|
+
return Boolean(
|
|
2692
|
+
relativePath && relativePath !== ".." && !relativePath.startsWith("../")
|
|
2693
|
+
);
|
|
2694
|
+
}
|
|
2695
|
+
function normalizePathWithinRoot(rootFolder, filePath, outputRoot = rootFolder) {
|
|
2696
|
+
let candidate;
|
|
2697
|
+
if (isAbsolute2(filePath)) {
|
|
2698
|
+
candidate = resolve5(filePath);
|
|
2699
|
+
} else {
|
|
2700
|
+
const repoRelativeCandidate = resolve5(outputRoot, filePath);
|
|
2701
|
+
candidate = isPathWithinRoot(rootFolder, repoRelativeCandidate) ? repoRelativeCandidate : resolve5(rootFolder, filePath);
|
|
2702
|
+
}
|
|
2703
|
+
const relativePath = toPosixPath(relative(outputRoot, candidate));
|
|
2704
|
+
if (!relativePath || relativePath === ".." || relativePath.startsWith("../")) {
|
|
2705
|
+
return null;
|
|
2706
|
+
}
|
|
2707
|
+
return relativePath;
|
|
2708
|
+
}
|
|
2709
|
+
async function collectScopeFiles(currentDir, auditedRoot, patchStorageRoot, outputRoot) {
|
|
2710
|
+
if (currentDir === patchStorageRoot) {
|
|
2711
|
+
return [];
|
|
2712
|
+
}
|
|
2713
|
+
const entries = await readdir2(currentDir, { withFileTypes: true });
|
|
2714
|
+
const files = [];
|
|
2715
|
+
for (const entry of entries) {
|
|
2716
|
+
const entryPath = resolve5(currentDir, entry.name);
|
|
2717
|
+
if (entry.isDirectory()) {
|
|
2718
|
+
files.push(
|
|
2719
|
+
...await collectScopeFiles(
|
|
2720
|
+
entryPath,
|
|
2721
|
+
auditedRoot,
|
|
2722
|
+
patchStorageRoot,
|
|
2723
|
+
outputRoot
|
|
2724
|
+
)
|
|
2725
|
+
);
|
|
2726
|
+
continue;
|
|
2727
|
+
}
|
|
2728
|
+
if (!entry.isFile()) {
|
|
2729
|
+
continue;
|
|
2730
|
+
}
|
|
2731
|
+
const normalized = normalizePathWithinRoot(auditedRoot, entryPath, outputRoot);
|
|
2732
|
+
if (normalized) {
|
|
2733
|
+
files.push(normalized);
|
|
2734
|
+
}
|
|
2735
|
+
}
|
|
2736
|
+
return files;
|
|
2737
|
+
}
|
|
2738
|
+
async function compareScopeWithEvidence(envValue, input, cwd = process.cwd()) {
|
|
2739
|
+
const parsedInput = compareScopeWithEvidenceInputSchema.parse(input);
|
|
2740
|
+
const patchState = await readKnowledgePatch(envValue, parsedInput.patchId, cwd);
|
|
2741
|
+
const auditedRoot = normalizeAuditedRoot(
|
|
2742
|
+
parsedInput.rootFolder ?? patchState.patch.rootFolder
|
|
2743
|
+
);
|
|
2744
|
+
const repositoryRoot = resolve5(cwd);
|
|
2745
|
+
const effectiveState = await loadEffectiveKnowledgeStoreFromRoot(
|
|
2746
|
+
resolveKnowledgeRoot2(envValue, cwd),
|
|
2747
|
+
{
|
|
2748
|
+
selectedPatchId: parsedInput.patchId
|
|
2749
|
+
}
|
|
2750
|
+
);
|
|
2751
|
+
const effectivePatchIds = new Set(effectiveState.patchIds);
|
|
2752
|
+
const coveredFiles = /* @__PURE__ */ new Set();
|
|
2753
|
+
const outOfRootEvidenceFiles = /* @__PURE__ */ new Set();
|
|
2754
|
+
for (const evidence of effectiveState.store.evidence) {
|
|
2755
|
+
const provenance = effectiveState.provenance.get(evidence.id);
|
|
2756
|
+
if (!provenance?.patchIds.some((patchId) => effectivePatchIds.has(patchId))) {
|
|
2757
|
+
continue;
|
|
2758
|
+
}
|
|
2759
|
+
const sourcePath = evidence.location?.path;
|
|
2760
|
+
if (!sourcePath) {
|
|
2761
|
+
continue;
|
|
2762
|
+
}
|
|
2763
|
+
const normalized = normalizePathWithinRoot(
|
|
2764
|
+
auditedRoot,
|
|
2765
|
+
sourcePath,
|
|
2766
|
+
repositoryRoot
|
|
2767
|
+
);
|
|
2768
|
+
if (normalized) {
|
|
2769
|
+
coveredFiles.add(normalized);
|
|
2770
|
+
continue;
|
|
2771
|
+
}
|
|
2772
|
+
outOfRootEvidenceFiles.add(sourcePath);
|
|
2773
|
+
}
|
|
2774
|
+
const omittedFiles = patchState.patch.omittedFiles.map((entry) => ({
|
|
2775
|
+
path: entry.path,
|
|
2776
|
+
reason: entry.reason
|
|
2777
|
+
}));
|
|
2778
|
+
const omittedFileSet = new Set(omittedFiles.map((entry) => entry.path));
|
|
2779
|
+
const patchStorageRoot = resolve5(auditedRoot, KNOWLEDGE_PATCH_FOLDER_NAME);
|
|
2780
|
+
const scopeFiles = (await collectScopeFiles(
|
|
2781
|
+
auditedRoot,
|
|
2782
|
+
auditedRoot,
|
|
2783
|
+
patchStorageRoot,
|
|
2784
|
+
repositoryRoot
|
|
2785
|
+
)).sort((left, right) => left.localeCompare(right));
|
|
2786
|
+
const uncoveredFiles = scopeFiles.filter(
|
|
2787
|
+
(filePath) => !coveredFiles.has(filePath) && !omittedFileSet.has(filePath)
|
|
2788
|
+
);
|
|
2789
|
+
const sortedCoveredFiles = [...coveredFiles].sort(
|
|
2790
|
+
(left, right) => left.localeCompare(right)
|
|
2791
|
+
);
|
|
2792
|
+
const sortedOutOfRootEvidenceFiles = [...outOfRootEvidenceFiles].sort(
|
|
2793
|
+
(left, right) => left.localeCompare(right)
|
|
2794
|
+
);
|
|
2795
|
+
const message = uncoveredFiles.length > 0 ? `Recheck ${uncoveredFiles.length} uncovered file(s): add evidence-backed knowledge or mark them omitted with a reason.` : "Scope audit complete. No uncovered files remain.";
|
|
2796
|
+
return compareScopeWithEvidenceResultSchema.parse({
|
|
2797
|
+
patchId: parsedInput.patchId,
|
|
2798
|
+
rootFolder: auditedRoot,
|
|
2799
|
+
scopeFiles,
|
|
2800
|
+
coveredFiles: sortedCoveredFiles,
|
|
2801
|
+
omittedFiles,
|
|
2802
|
+
uncoveredFiles,
|
|
2803
|
+
outOfRootEvidenceFiles: sortedOutOfRootEvidenceFiles,
|
|
2804
|
+
message
|
|
2805
|
+
});
|
|
2806
|
+
}
|
|
2807
|
+
|
|
2808
|
+
// packages/core/src/knowledge/patch/index.ts
|
|
2809
|
+
async function executeKnowledgePatchAction(envValue, input, cwd = process.cwd()) {
|
|
2810
|
+
switch (input.action) {
|
|
2811
|
+
case "create":
|
|
2812
|
+
return createKnowledgePatch(envValue, input.patch, cwd);
|
|
2813
|
+
case "list":
|
|
2814
|
+
return listKnowledgePatches(envValue, cwd);
|
|
2815
|
+
case "get":
|
|
2816
|
+
return readKnowledgePatch(envValue, input.patchId, cwd);
|
|
2817
|
+
case "add_operation":
|
|
2818
|
+
return addKnowledgePatchOperation(
|
|
2819
|
+
envValue,
|
|
2820
|
+
input.patchId,
|
|
2821
|
+
input.operation,
|
|
2822
|
+
cwd
|
|
2823
|
+
);
|
|
2824
|
+
case "update_operation":
|
|
2825
|
+
return updateKnowledgePatchOperation(
|
|
2826
|
+
envValue,
|
|
2827
|
+
input.patchId,
|
|
2828
|
+
input.update,
|
|
2829
|
+
cwd
|
|
2830
|
+
);
|
|
2831
|
+
case "update_metadata":
|
|
2832
|
+
return updateKnowledgePatchMetadata(
|
|
2833
|
+
envValue,
|
|
2834
|
+
input.patchId,
|
|
2835
|
+
input.metadata,
|
|
2836
|
+
cwd
|
|
2837
|
+
);
|
|
2838
|
+
case "delete_operation":
|
|
2839
|
+
return deleteKnowledgePatchOperation(
|
|
2840
|
+
envValue,
|
|
2841
|
+
input.patchId,
|
|
2842
|
+
input.operationId,
|
|
2843
|
+
cwd
|
|
2844
|
+
);
|
|
2845
|
+
case "validate":
|
|
2846
|
+
return validateKnowledgePatch(envValue, input.patchId, cwd);
|
|
2847
|
+
case "close":
|
|
2848
|
+
return closeKnowledgePatch(envValue, input.patchId, cwd);
|
|
2849
|
+
}
|
|
2850
|
+
}
|
|
2851
|
+
function buildKnowledgePatchToolText(result) {
|
|
2852
|
+
return JSON.stringify(result, null, 2);
|
|
2853
|
+
}
|
|
2854
|
+
|
|
2855
|
+
// packages/core/src/knowledge/schema-discovery.ts
|
|
2856
|
+
import {
|
|
2857
|
+
isAbstractType,
|
|
2858
|
+
isCompositeType,
|
|
2859
|
+
isListType,
|
|
2860
|
+
isNonNullType,
|
|
2861
|
+
isObjectType,
|
|
2862
|
+
isScalarType,
|
|
2863
|
+
isUnionType
|
|
2864
|
+
} from "graphql";
|
|
2865
|
+
var KNOWLEDGE_SCHEMA_RESOURCE_URI = "knowledge-schema://schema";
|
|
2866
|
+
var KNOWLEDGE_SCHEMA_LOOKUP_URI = "knowledge-schema://lookup/{target}";
|
|
2867
|
+
function unwrapType(type) {
|
|
2868
|
+
if (isNonNullType(type) || isListType(type)) {
|
|
2869
|
+
return unwrapType(type.ofType);
|
|
2870
|
+
}
|
|
2871
|
+
if (isObjectType(type) || isScalarType(type) || isUnionType(type) || isAbstractType(type)) {
|
|
2872
|
+
return { name: type.name, composite: type };
|
|
2873
|
+
}
|
|
2874
|
+
return { name: "Unknown", composite: type };
|
|
2875
|
+
}
|
|
2876
|
+
function describeType(type) {
|
|
2877
|
+
if (isNonNullType(type)) {
|
|
2878
|
+
return `${describeType(type.ofType)}!`;
|
|
2879
|
+
}
|
|
2880
|
+
if (isListType(type)) {
|
|
2881
|
+
return `[${describeType(type.ofType)}]`;
|
|
2882
|
+
}
|
|
2883
|
+
if (isObjectType(type) || isScalarType(type) || isUnionType(type)) {
|
|
2884
|
+
return type.name;
|
|
2885
|
+
}
|
|
2886
|
+
if (isAbstractType(type)) {
|
|
2887
|
+
return type.name;
|
|
2888
|
+
}
|
|
2889
|
+
return "Unknown";
|
|
2890
|
+
}
|
|
2891
|
+
function formatDefaultValue(value) {
|
|
2892
|
+
if (value === void 0 || value === null) {
|
|
2893
|
+
return null;
|
|
2894
|
+
}
|
|
2895
|
+
return JSON.stringify(value) ?? String(value);
|
|
2896
|
+
}
|
|
2897
|
+
function summarizeField(field) {
|
|
2898
|
+
const traversedType = unwrapType(field.type).composite;
|
|
2899
|
+
const traversesTo = isCompositeType(traversedType) && !isScalarType(traversedType) ? traversedType.name : null;
|
|
2900
|
+
return {
|
|
2901
|
+
name: field.name,
|
|
2902
|
+
type: describeType(field.type),
|
|
2903
|
+
description: field.description ?? null,
|
|
2904
|
+
arguments: field.args?.map((argument) => ({
|
|
2905
|
+
name: argument.name,
|
|
2906
|
+
type: describeType(argument.type),
|
|
2907
|
+
description: argument.description ?? null,
|
|
2908
|
+
defaultValue: formatDefaultValue(argument.defaultValue)
|
|
2909
|
+
})) ?? [],
|
|
2910
|
+
traversesTo
|
|
2911
|
+
};
|
|
2912
|
+
}
|
|
2913
|
+
function summarizeObjectType(type) {
|
|
2914
|
+
const fields = Object.values(type.getFields()).map(summarizeField);
|
|
2915
|
+
return {
|
|
2916
|
+
name: type.name,
|
|
2917
|
+
description: type.description ?? null,
|
|
2918
|
+
fields,
|
|
2919
|
+
traversalFields: fields.filter((field) => field.traversesTo !== null).map((field) => field.name)
|
|
2920
|
+
};
|
|
2921
|
+
}
|
|
2922
|
+
function getObjectTypes(schema) {
|
|
2923
|
+
return Object.values(schema.getTypeMap()).filter((type) => isObjectType(type)).filter((type) => !type.name.startsWith("__")).filter((type) => type.name !== schema.getQueryType()?.name);
|
|
2924
|
+
}
|
|
2925
|
+
function buildKnowledgeSchemaDiscoveryDocument(schema) {
|
|
2926
|
+
const queryType2 = schema.getQueryType();
|
|
2927
|
+
if (!queryType2) {
|
|
2928
|
+
throw new Error("Knowledge GraphQL schema has no root query type.");
|
|
2929
|
+
}
|
|
2930
|
+
return {
|
|
2931
|
+
resource: {
|
|
2932
|
+
uri: KNOWLEDGE_SCHEMA_RESOURCE_URI,
|
|
2933
|
+
title: "Knowledge GraphQL Schema",
|
|
2934
|
+
description: "Canonical discovery view of the runtime knowledge GraphQL schema."
|
|
2935
|
+
},
|
|
2936
|
+
source: {
|
|
2937
|
+
kind: "runtime-schema",
|
|
2938
|
+
queryType: queryType2.name
|
|
2939
|
+
},
|
|
2940
|
+
rootQuery: summarizeObjectType(queryType2),
|
|
2941
|
+
objectTypes: getObjectTypes(schema).map(summarizeObjectType)
|
|
2942
|
+
};
|
|
2943
|
+
}
|
|
2944
|
+
function getLookupMetadata(schema, target) {
|
|
2945
|
+
const queryType2 = schema.getQueryType();
|
|
2946
|
+
if (!queryType2) {
|
|
2947
|
+
throw new Error("Knowledge GraphQL schema has no root query type.");
|
|
2948
|
+
}
|
|
2949
|
+
if (target === queryType2.name) {
|
|
2950
|
+
return {
|
|
2951
|
+
resource: {
|
|
2952
|
+
uri: `knowledge-schema://lookup/${target}`,
|
|
2953
|
+
title: `Knowledge schema lookup: ${target}`,
|
|
2954
|
+
description: "Focused lookup of the root query fields and their arguments."
|
|
2955
|
+
},
|
|
2956
|
+
source: {
|
|
2957
|
+
kind: "runtime-schema",
|
|
2958
|
+
queryType: queryType2.name
|
|
2959
|
+
},
|
|
2960
|
+
lookup: {
|
|
2961
|
+
target: queryType2.name,
|
|
2962
|
+
kind: "root-query"
|
|
2963
|
+
},
|
|
2964
|
+
rootQuery: summarizeObjectType(queryType2)
|
|
2965
|
+
};
|
|
2966
|
+
}
|
|
2967
|
+
const rootField = queryType2.getFields()[target];
|
|
2968
|
+
if (rootField) {
|
|
2969
|
+
return {
|
|
2970
|
+
resource: {
|
|
2971
|
+
uri: `knowledge-schema://lookup/${target}`,
|
|
2972
|
+
title: `Knowledge schema lookup: ${target}`,
|
|
2973
|
+
description: "Focused lookup of a single root query field and its arguments."
|
|
2974
|
+
},
|
|
2975
|
+
source: {
|
|
2976
|
+
kind: "runtime-schema",
|
|
2977
|
+
queryType: queryType2.name
|
|
2978
|
+
},
|
|
2979
|
+
lookup: {
|
|
2980
|
+
target,
|
|
2981
|
+
kind: "root-field"
|
|
2982
|
+
},
|
|
2983
|
+
field: summarizeField(rootField),
|
|
2984
|
+
parentType: queryType2.name
|
|
2985
|
+
};
|
|
2986
|
+
}
|
|
2987
|
+
const objectType = schema.getType(target);
|
|
2988
|
+
if (objectType && isObjectType(objectType)) {
|
|
2989
|
+
const typeSummary = summarizeObjectType(objectType);
|
|
2990
|
+
const rootFields = Object.values(queryType2.getFields()).filter((field) => unwrapType(field.type).name === target).map((field) => field.name);
|
|
2991
|
+
return {
|
|
2992
|
+
resource: {
|
|
2993
|
+
uri: `knowledge-schema://lookup/${target}`,
|
|
2994
|
+
title: `Knowledge schema lookup: ${target}`,
|
|
2995
|
+
description: "Focused lookup of a single GraphQL type and its nested traversal fields."
|
|
2996
|
+
},
|
|
2997
|
+
source: {
|
|
2998
|
+
kind: "runtime-schema",
|
|
2999
|
+
queryType: queryType2.name
|
|
3000
|
+
},
|
|
3001
|
+
lookup: {
|
|
3002
|
+
target,
|
|
3003
|
+
kind: "object-type"
|
|
3004
|
+
},
|
|
3005
|
+
type: typeSummary,
|
|
3006
|
+
rootFields
|
|
3007
|
+
};
|
|
3008
|
+
}
|
|
3009
|
+
throw new Error(
|
|
3010
|
+
`Unknown schema lookup target "${target}". Use a root field name, a type name, or "Query".`
|
|
3011
|
+
);
|
|
3012
|
+
}
|
|
3013
|
+
function buildKnowledgeSchemaLookupDocument(schema, target) {
|
|
3014
|
+
return getLookupMetadata(schema, target);
|
|
3015
|
+
}
|
|
3016
|
+
function createKnowledgeSchemaResourceText(schema) {
|
|
3017
|
+
return JSON.stringify(buildKnowledgeSchemaDiscoveryDocument(schema), null, 2);
|
|
3018
|
+
}
|
|
3019
|
+
function createKnowledgeSchemaLookupText(schema, target) {
|
|
3020
|
+
return JSON.stringify(
|
|
3021
|
+
buildKnowledgeSchemaLookupDocument(schema, target),
|
|
3022
|
+
null,
|
|
3023
|
+
2
|
|
3024
|
+
);
|
|
3025
|
+
}
|
|
3026
|
+
function listKnowledgeSchemaTargets(schema) {
|
|
3027
|
+
const queryType2 = schema.getQueryType();
|
|
3028
|
+
if (!queryType2) {
|
|
3029
|
+
throw new Error("Knowledge GraphQL schema has no root query type.");
|
|
3030
|
+
}
|
|
3031
|
+
const objectTypeNames = getObjectTypes(schema).map((type) => type.name);
|
|
3032
|
+
const rootFieldNames = Object.keys(queryType2.getFields());
|
|
3033
|
+
const targetNames = ["Query", ...rootFieldNames, ...objectTypeNames];
|
|
3034
|
+
return Array.from(new Set(targetNames)).sort(
|
|
3035
|
+
(left, right) => left.localeCompare(right)
|
|
3036
|
+
);
|
|
3037
|
+
}
|
|
3038
|
+
|
|
3039
|
+
// packages/mcp/src/resources/knowledge-extraction.ts
|
|
3040
|
+
import { ResourceTemplate } from "@modelcontextprotocol/server";
|
|
3041
|
+
function createJsonResourceContent(uri, text) {
|
|
3042
|
+
return {
|
|
3043
|
+
contents: [
|
|
3044
|
+
{
|
|
3045
|
+
uri,
|
|
3046
|
+
mimeType: "application/json",
|
|
3047
|
+
text
|
|
3048
|
+
}
|
|
3049
|
+
]
|
|
3050
|
+
};
|
|
3051
|
+
}
|
|
3052
|
+
function createMarkdownResourceContent(uri, text) {
|
|
3053
|
+
return {
|
|
3054
|
+
contents: [
|
|
3055
|
+
{
|
|
3056
|
+
uri,
|
|
3057
|
+
mimeType: "text/markdown",
|
|
3058
|
+
text
|
|
3059
|
+
}
|
|
3060
|
+
]
|
|
3061
|
+
};
|
|
3062
|
+
}
|
|
3063
|
+
function listExtractionTypeNames() {
|
|
3064
|
+
return listKnowledgeExtractionResourceTypes().flatMap((definition) => [
|
|
3065
|
+
definition.collection,
|
|
3066
|
+
...definition.aliases
|
|
3067
|
+
]);
|
|
3068
|
+
}
|
|
3069
|
+
function registerKnowledgeExtractionResources(server) {
|
|
3070
|
+
server.registerResource(
|
|
3071
|
+
"knowledge-extraction-types",
|
|
3072
|
+
KNOWLEDGE_EXTRACTION_TYPES_RESOURCE_URI,
|
|
3073
|
+
{
|
|
3074
|
+
title: "Knowledge Extraction Types",
|
|
3075
|
+
description: "Canonical registry of knowledge types supported by the extraction workflow.",
|
|
3076
|
+
mimeType: "application/json"
|
|
3077
|
+
},
|
|
3078
|
+
async (uri) => createJsonResourceContent(
|
|
3079
|
+
uri.href,
|
|
3080
|
+
JSON.stringify(buildKnowledgeExtractionTypeIndexDocument(), null, 2)
|
|
3081
|
+
)
|
|
3082
|
+
);
|
|
3083
|
+
server.registerResource(
|
|
3084
|
+
"knowledge-extraction-schema",
|
|
3085
|
+
new ResourceTemplate(KNOWLEDGE_EXTRACTION_SCHEMA_RESOURCE_URI, {
|
|
3086
|
+
list: async () => ({
|
|
3087
|
+
resources: listKnowledgeExtractionResourceTypes().map((definition) => ({
|
|
3088
|
+
uri: `ez-know://extraction/schema/${definition.collection}`,
|
|
3089
|
+
name: definition.collection,
|
|
3090
|
+
title: `${definition.title} Extraction Schema`,
|
|
3091
|
+
description: `Runtime schema discovery for ${definition.title.toLowerCase()}.`,
|
|
3092
|
+
mimeType: "application/json"
|
|
3093
|
+
}))
|
|
3094
|
+
}),
|
|
3095
|
+
complete: {
|
|
3096
|
+
type: async (value) => listExtractionTypeNames().filter(
|
|
3097
|
+
(name) => name.toLowerCase().startsWith(value.toLowerCase())
|
|
3098
|
+
)
|
|
3099
|
+
}
|
|
3100
|
+
}),
|
|
3101
|
+
{
|
|
3102
|
+
title: "Knowledge Extraction Schema",
|
|
3103
|
+
description: "Runtime JSON-schema discovery for a supported knowledge type.",
|
|
3104
|
+
mimeType: "application/json"
|
|
3105
|
+
},
|
|
3106
|
+
async (_uri, variables) => {
|
|
3107
|
+
const type = variables.type;
|
|
3108
|
+
if (typeof type !== "string" || type.length === 0) {
|
|
3109
|
+
throw new Error("Resource type is required.");
|
|
3110
|
+
}
|
|
3111
|
+
const document = buildKnowledgeExtractionSchemaDocument(type);
|
|
3112
|
+
return createJsonResourceContent(
|
|
3113
|
+
`ez-know://extraction/schema/${document.type.collection}`,
|
|
3114
|
+
JSON.stringify(document, null, 2)
|
|
3115
|
+
);
|
|
3116
|
+
}
|
|
3117
|
+
);
|
|
3118
|
+
server.registerResource(
|
|
3119
|
+
"knowledge-extraction-guide",
|
|
3120
|
+
new ResourceTemplate(KNOWLEDGE_EXTRACTION_GUIDE_RESOURCE_URI, {
|
|
3121
|
+
list: async () => ({
|
|
3122
|
+
resources: listKnowledgeExtractionResourceTypes().map((definition) => ({
|
|
3123
|
+
uri: `ez-know://extraction/guide/${definition.collection}`,
|
|
3124
|
+
name: definition.collection,
|
|
3125
|
+
title: `${definition.title} Extraction Guide`,
|
|
3126
|
+
description: `Checked-in extraction guidance for ${definition.title.toLowerCase()}.`,
|
|
3127
|
+
mimeType: "text/markdown"
|
|
3128
|
+
}))
|
|
3129
|
+
}),
|
|
3130
|
+
complete: {
|
|
3131
|
+
type: async (value) => listExtractionTypeNames().filter(
|
|
3132
|
+
(name) => name.toLowerCase().startsWith(value.toLowerCase())
|
|
3133
|
+
)
|
|
3134
|
+
}
|
|
3135
|
+
}),
|
|
3136
|
+
{
|
|
3137
|
+
title: "Knowledge Extraction Guide",
|
|
3138
|
+
description: "Checked-in Markdown guidance for a supported knowledge type.",
|
|
3139
|
+
mimeType: "text/markdown"
|
|
3140
|
+
},
|
|
3141
|
+
async (_uri, variables) => {
|
|
3142
|
+
const type = variables.type;
|
|
3143
|
+
if (typeof type !== "string" || type.length === 0) {
|
|
3144
|
+
throw new Error("Resource type is required.");
|
|
3145
|
+
}
|
|
3146
|
+
const document = await buildKnowledgeExtractionGuideDocument(type);
|
|
3147
|
+
return createMarkdownResourceContent(
|
|
3148
|
+
`ez-know://extraction/guide/${document.type.collection}`,
|
|
3149
|
+
document.markdown
|
|
3150
|
+
);
|
|
3151
|
+
}
|
|
3152
|
+
);
|
|
3153
|
+
}
|
|
3154
|
+
|
|
3155
|
+
// packages/mcp/src/tools/compare-scope-with-evidence.ts
|
|
3156
|
+
function registerCompareScopeWithEvidenceTool(server) {
|
|
3157
|
+
server.registerTool(
|
|
3158
|
+
"compare_scope_with_evidence",
|
|
3159
|
+
{
|
|
3160
|
+
title: "Compare Scope With Evidence",
|
|
3161
|
+
description: "Compares a patch evidence footprint against the audited repository root and reports uncovered files that still need review.",
|
|
3162
|
+
inputSchema: compareScopeWithEvidenceInputSchema
|
|
3163
|
+
},
|
|
3164
|
+
async (input) => {
|
|
3165
|
+
const parsedInput = compareScopeWithEvidenceInputSchema.parse(input);
|
|
3166
|
+
const result = await compareScopeWithEvidence(
|
|
3167
|
+
process.env.EZ_KNOW_ROOT,
|
|
3168
|
+
parsedInput
|
|
3169
|
+
);
|
|
3170
|
+
return {
|
|
3171
|
+
content: [
|
|
3172
|
+
{
|
|
3173
|
+
type: "text",
|
|
3174
|
+
text: buildKnowledgePatchToolText(result)
|
|
3175
|
+
}
|
|
3176
|
+
]
|
|
3177
|
+
};
|
|
3178
|
+
}
|
|
3179
|
+
);
|
|
3180
|
+
}
|
|
3181
|
+
|
|
3182
|
+
// packages/mcp/src/tools/knowledge-graph.ts
|
|
3183
|
+
import * as z7 from "zod/v4";
|
|
3184
|
+
function registerKnowledgeGraphTool(server, getStore) {
|
|
3185
|
+
server.registerTool(
|
|
3186
|
+
"knowledge_graph_query",
|
|
3187
|
+
{
|
|
3188
|
+
title: "Knowledge Graph Query",
|
|
3189
|
+
description: "Executes a GraphQL query against the loaded knowledge store.",
|
|
3190
|
+
inputSchema: z7.object({
|
|
3191
|
+
query: z7.string(),
|
|
3192
|
+
variables: z7.record(z7.string(), z7.unknown()).optional(),
|
|
3193
|
+
operationName: z7.string().optional()
|
|
3194
|
+
})
|
|
3195
|
+
},
|
|
3196
|
+
async ({ query, variables, operationName }) => {
|
|
3197
|
+
const result = await executeKnowledgeQuery(
|
|
3198
|
+
await getStore(),
|
|
3199
|
+
query,
|
|
3200
|
+
variables,
|
|
3201
|
+
operationName
|
|
3202
|
+
);
|
|
3203
|
+
return {
|
|
3204
|
+
content: [
|
|
3205
|
+
{
|
|
3206
|
+
type: "text",
|
|
3207
|
+
text: buildKnowledgeGraphQLText(result)
|
|
3208
|
+
}
|
|
3209
|
+
]
|
|
3210
|
+
};
|
|
3211
|
+
}
|
|
3212
|
+
);
|
|
3213
|
+
}
|
|
3214
|
+
|
|
3215
|
+
// packages/mcp/src/tools/knowledge-patch-apply.ts
|
|
3216
|
+
function registerKnowledgePatchApplyTool(server) {
|
|
3217
|
+
server.registerTool(
|
|
3218
|
+
"knowledge_patch_apply",
|
|
3219
|
+
{
|
|
3220
|
+
title: "Knowledge Patch Apply",
|
|
3221
|
+
description: "Applies a validated knowledge patch to canonical files after explicit approval.",
|
|
3222
|
+
inputSchema: knowledgePatchApplyToolInputSchema
|
|
3223
|
+
},
|
|
3224
|
+
async (input) => {
|
|
3225
|
+
const parsedInput = knowledgePatchApplyToolInputSchema.parse(input);
|
|
3226
|
+
const result = await applyKnowledgePatch(
|
|
3227
|
+
process.env.EZ_KNOW_ROOT,
|
|
3228
|
+
parsedInput
|
|
3229
|
+
);
|
|
3230
|
+
return {
|
|
3231
|
+
content: [
|
|
3232
|
+
{
|
|
3233
|
+
type: "text",
|
|
3234
|
+
text: buildKnowledgePatchToolText(result)
|
|
3235
|
+
}
|
|
3236
|
+
]
|
|
3237
|
+
};
|
|
3238
|
+
}
|
|
3239
|
+
);
|
|
3240
|
+
}
|
|
3241
|
+
|
|
3242
|
+
// packages/mcp/src/tools/knowledge-patch.ts
|
|
3243
|
+
function registerKnowledgePatchTool(server) {
|
|
3244
|
+
server.registerTool(
|
|
3245
|
+
"knowledge_patch",
|
|
3246
|
+
{
|
|
3247
|
+
title: "Knowledge Patch",
|
|
3248
|
+
description: "Manages draft knowledge patches without mutating canonical knowledge files.",
|
|
3249
|
+
inputSchema: knowledgePatchToolInputSchema
|
|
3250
|
+
},
|
|
3251
|
+
async (input) => {
|
|
3252
|
+
const parsedInput = knowledgePatchToolInputSchema.parse(input);
|
|
3253
|
+
const result = await executeKnowledgePatchAction(
|
|
3254
|
+
process.env.EZ_KNOW_ROOT,
|
|
3255
|
+
parsedInput
|
|
3256
|
+
);
|
|
3257
|
+
return {
|
|
3258
|
+
content: [
|
|
3259
|
+
{
|
|
3260
|
+
type: "text",
|
|
3261
|
+
text: buildKnowledgePatchToolText(result)
|
|
3262
|
+
}
|
|
3263
|
+
]
|
|
3264
|
+
};
|
|
3265
|
+
}
|
|
3266
|
+
);
|
|
3267
|
+
}
|
|
3268
|
+
|
|
3269
|
+
// packages/mcp/src/resources/knowledge-schema.ts
|
|
3270
|
+
import { ResourceTemplate as ResourceTemplate2 } from "@modelcontextprotocol/server";
|
|
3271
|
+
function createJsonResourceContent2(uri, text) {
|
|
3272
|
+
return {
|
|
3273
|
+
contents: [
|
|
3274
|
+
{
|
|
3275
|
+
uri,
|
|
3276
|
+
mimeType: "application/json",
|
|
3277
|
+
text
|
|
3278
|
+
}
|
|
3279
|
+
]
|
|
3280
|
+
};
|
|
3281
|
+
}
|
|
3282
|
+
function registerKnowledgeSchemaResources(server) {
|
|
3283
|
+
server.registerResource(
|
|
3284
|
+
"knowledge-schema",
|
|
3285
|
+
KNOWLEDGE_SCHEMA_RESOURCE_URI,
|
|
3286
|
+
{
|
|
3287
|
+
title: "Knowledge GraphQL Schema",
|
|
3288
|
+
description: "Canonical runtime GraphQL schema discovery payload for the knowledge layer.",
|
|
3289
|
+
mimeType: "application/json"
|
|
3290
|
+
},
|
|
3291
|
+
async (uri) => createJsonResourceContent2(
|
|
3292
|
+
uri.href,
|
|
3293
|
+
createKnowledgeSchemaResourceText(knowledgeGraphSchema)
|
|
3294
|
+
)
|
|
3295
|
+
);
|
|
3296
|
+
server.registerResource(
|
|
3297
|
+
"knowledge-schema-lookup",
|
|
3298
|
+
new ResourceTemplate2(KNOWLEDGE_SCHEMA_LOOKUP_URI, {
|
|
3299
|
+
list: async () => ({
|
|
3300
|
+
resources: listKnowledgeSchemaTargets(knowledgeGraphSchema).map(
|
|
3301
|
+
(target) => ({
|
|
3302
|
+
uri: `knowledge-schema://lookup/${target}`,
|
|
3303
|
+
name: target,
|
|
3304
|
+
title: `Knowledge schema lookup: ${target}`,
|
|
3305
|
+
description: target === "Query" ? "Focused lookup of the root query fields." : `Focused lookup of ${target}.`,
|
|
3306
|
+
mimeType: "application/json"
|
|
3307
|
+
})
|
|
3308
|
+
)
|
|
3309
|
+
}),
|
|
3310
|
+
complete: {
|
|
3311
|
+
target: async (value) => listKnowledgeSchemaTargets(knowledgeGraphSchema).filter(
|
|
3312
|
+
(target) => target.toLowerCase().startsWith(value.toLowerCase())
|
|
3313
|
+
)
|
|
3314
|
+
}
|
|
3315
|
+
}),
|
|
3316
|
+
{
|
|
3317
|
+
title: "Knowledge Schema Lookup",
|
|
3318
|
+
description: "Focused runtime GraphQL schema lookup for a type name, root field, or Query.",
|
|
3319
|
+
mimeType: "application/json"
|
|
3320
|
+
},
|
|
3321
|
+
async (_uri, variables) => {
|
|
3322
|
+
const target = variables.target;
|
|
3323
|
+
if (typeof target !== "string" || target.length === 0) {
|
|
3324
|
+
throw new Error("Resource target is required.");
|
|
3325
|
+
}
|
|
3326
|
+
return createJsonResourceContent2(
|
|
3327
|
+
`knowledge-schema://lookup/${target}`,
|
|
3328
|
+
createKnowledgeSchemaLookupText(knowledgeGraphSchema, target)
|
|
3329
|
+
);
|
|
3330
|
+
}
|
|
3331
|
+
);
|
|
3332
|
+
}
|
|
3333
|
+
|
|
3334
|
+
// packages/mcp/src/run.ts
|
|
3335
|
+
async function startMcpServer() {
|
|
3336
|
+
const server = new McpServer({
|
|
3337
|
+
name: "ez-know",
|
|
3338
|
+
version: "0.1.0"
|
|
3339
|
+
});
|
|
3340
|
+
registerKnowledgeSchemaResources(server);
|
|
3341
|
+
registerKnowledgeExtractionResources(server);
|
|
3342
|
+
registerKnowledgeGraphTool(server, async () => {
|
|
3343
|
+
const effectiveState = await loadEffectiveKnowledgeStoreFromEnv(
|
|
3344
|
+
process.env.EZ_KNOW_ROOT
|
|
3345
|
+
);
|
|
3346
|
+
return effectiveState?.store;
|
|
3347
|
+
});
|
|
3348
|
+
registerKnowledgePatchTool(server);
|
|
3349
|
+
registerKnowledgePatchApplyTool(server);
|
|
3350
|
+
registerCompareScopeWithEvidenceTool(server);
|
|
3351
|
+
const transport = new StdioServerTransport();
|
|
3352
|
+
await server.connect(transport);
|
|
3353
|
+
}
|
|
3354
|
+
|
|
3355
|
+
// src/server.ts
|
|
3356
|
+
await startMcpServer();
|
|
3357
|
+
//# sourceMappingURL=server.js.map
|