@unified-product-graph/mcp-server 0.7.4 → 0.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +369 -135
- package/dist/index.js.map +1 -1
- package/dist/tools-manifest.json +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -63,6 +63,7 @@ async function hashFile(filePath) {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
// ../upg-spec/dist/index.js
|
|
66
|
+
import { createHash as createHash2 } from "crypto";
|
|
66
67
|
var UPG_DOMAINS = [
|
|
67
68
|
{
|
|
68
69
|
id: "strategy",
|
|
@@ -12115,26 +12116,6 @@ var UPG_FRAMEWORKS = [
|
|
|
12115
12116
|
"label": "Items to score",
|
|
12116
12117
|
"entityTypeId": "feature",
|
|
12117
12118
|
"description": "Features, opportunities, or solutions being evaluated"
|
|
12118
|
-
},
|
|
12119
|
-
{
|
|
12120
|
-
"label": "Reach",
|
|
12121
|
-
"entityTypeId": "feature",
|
|
12122
|
-
"description": "How many users does this affect per quarter?"
|
|
12123
|
-
},
|
|
12124
|
-
{
|
|
12125
|
-
"label": "Impact",
|
|
12126
|
-
"entityTypeId": "feature",
|
|
12127
|
-
"description": "How much does this move the target outcome?"
|
|
12128
|
-
},
|
|
12129
|
-
{
|
|
12130
|
-
"label": "Confidence",
|
|
12131
|
-
"entityTypeId": "feature",
|
|
12132
|
-
"description": "How sure are we about these estimates?"
|
|
12133
|
-
},
|
|
12134
|
-
{
|
|
12135
|
-
"label": "Effort",
|
|
12136
|
-
"entityTypeId": "feature",
|
|
12137
|
-
"description": "How many person-months to build?"
|
|
12138
12119
|
}
|
|
12139
12120
|
],
|
|
12140
12121
|
"data": {
|
|
@@ -12148,31 +12129,35 @@ var UPG_FRAMEWORKS = [
|
|
|
12148
12129
|
"feature": [
|
|
12149
12130
|
{
|
|
12150
12131
|
"property": "reach",
|
|
12151
|
-
"type": "
|
|
12132
|
+
"type": "assessment",
|
|
12133
|
+
"scale_id": "reach_5",
|
|
12152
12134
|
"required": true,
|
|
12153
12135
|
"label": "Reach",
|
|
12154
12136
|
"description": "How many users will this impact per quarter?"
|
|
12155
12137
|
},
|
|
12156
12138
|
{
|
|
12157
12139
|
"property": "impact",
|
|
12158
|
-
"type": "
|
|
12140
|
+
"type": "assessment",
|
|
12141
|
+
"scale_id": "impact_5",
|
|
12159
12142
|
"required": true,
|
|
12160
12143
|
"label": "Impact",
|
|
12161
|
-
"description": "How much will this impact each user
|
|
12144
|
+
"description": "How much will this impact each user, on the impact scale?"
|
|
12162
12145
|
},
|
|
12163
12146
|
{
|
|
12164
12147
|
"property": "confidence",
|
|
12165
|
-
"type": "
|
|
12148
|
+
"type": "assessment",
|
|
12149
|
+
"scale_id": "confidence_5",
|
|
12166
12150
|
"required": true,
|
|
12167
12151
|
"label": "Confidence",
|
|
12168
|
-
"description": "How confident are you in the
|
|
12152
|
+
"description": "How confident are you in the reach, impact, and effort estimates?"
|
|
12169
12153
|
},
|
|
12170
12154
|
{
|
|
12171
12155
|
"property": "effort",
|
|
12172
|
-
"type": "
|
|
12156
|
+
"type": "assessment",
|
|
12157
|
+
"scale_id": "effort_5",
|
|
12173
12158
|
"required": true,
|
|
12174
12159
|
"label": "Effort",
|
|
12175
|
-
"description": "
|
|
12160
|
+
"description": "How much work is required to build and ship this, on the effort scale?"
|
|
12176
12161
|
}
|
|
12177
12162
|
]
|
|
12178
12163
|
},
|
|
@@ -12366,7 +12351,7 @@ var UPG_FRAMEWORKS = [
|
|
|
12366
12351
|
{
|
|
12367
12352
|
"label": "Performance",
|
|
12368
12353
|
"entityTypeId": "feature",
|
|
12369
|
-
"description": "More is better
|
|
12354
|
+
"description": "More is better: linear satisfaction increase"
|
|
12370
12355
|
},
|
|
12371
12356
|
{
|
|
12372
12357
|
"label": "Delighters",
|
|
@@ -12615,24 +12600,9 @@ var UPG_FRAMEWORKS = [
|
|
|
12615
12600
|
],
|
|
12616
12601
|
"slots": [
|
|
12617
12602
|
{
|
|
12618
|
-
"label": "
|
|
12603
|
+
"label": "Requirements to categorise",
|
|
12619
12604
|
"entityTypeId": "feature",
|
|
12620
|
-
"description": "
|
|
12621
|
-
},
|
|
12622
|
-
{
|
|
12623
|
-
"label": "Should Have",
|
|
12624
|
-
"entityTypeId": "feature",
|
|
12625
|
-
"description": "Important but not critical; a workaround exists"
|
|
12626
|
-
},
|
|
12627
|
-
{
|
|
12628
|
-
"label": "Could Have",
|
|
12629
|
-
"entityTypeId": "feature",
|
|
12630
|
-
"description": "Nice to have; include if time permits"
|
|
12631
|
-
},
|
|
12632
|
-
{
|
|
12633
|
-
"label": "Won't Have",
|
|
12634
|
-
"entityTypeId": "feature",
|
|
12635
|
-
"description": "Explicitly out of scope for now"
|
|
12605
|
+
"description": "Features or requirements sorted into the four MoSCoW buckets"
|
|
12636
12606
|
}
|
|
12637
12607
|
],
|
|
12638
12608
|
"data": {
|
|
@@ -12642,7 +12612,23 @@ var UPG_FRAMEWORKS = [
|
|
|
12642
12612
|
"role": "scored_item"
|
|
12643
12613
|
}
|
|
12644
12614
|
],
|
|
12645
|
-
"required_properties": {
|
|
12615
|
+
"required_properties": {
|
|
12616
|
+
"feature": [
|
|
12617
|
+
{
|
|
12618
|
+
"property": "moscow",
|
|
12619
|
+
"type": "enum",
|
|
12620
|
+
"required": true,
|
|
12621
|
+
"label": "MoSCoW priority",
|
|
12622
|
+
"description": "Which scope bucket this requirement falls into for the current release",
|
|
12623
|
+
"enum_values": [
|
|
12624
|
+
"must",
|
|
12625
|
+
"should",
|
|
12626
|
+
"could",
|
|
12627
|
+
"wont"
|
|
12628
|
+
]
|
|
12629
|
+
}
|
|
12630
|
+
]
|
|
12631
|
+
},
|
|
12646
12632
|
"computed_properties": [
|
|
12647
12633
|
{
|
|
12648
12634
|
"property": "must_have_ratio",
|
|
@@ -12662,28 +12648,19 @@ var UPG_FRAMEWORKS = [
|
|
|
12662
12648
|
"columns": [
|
|
12663
12649
|
{
|
|
12664
12650
|
"property": "title",
|
|
12665
|
-
"label": "
|
|
12651
|
+
"label": "Requirement",
|
|
12666
12652
|
"sortable": true
|
|
12667
12653
|
},
|
|
12668
12654
|
{
|
|
12669
|
-
"property": "
|
|
12670
|
-
"label": "
|
|
12671
|
-
"sortable": true
|
|
12672
|
-
|
|
12673
|
-
{
|
|
12674
|
-
"property": "title",
|
|
12675
|
-
"label": "Could Have",
|
|
12676
|
-
"sortable": true
|
|
12677
|
-
},
|
|
12678
|
-
{
|
|
12679
|
-
"property": "title",
|
|
12680
|
-
"label": "Won't Have",
|
|
12681
|
-
"sortable": true
|
|
12655
|
+
"property": "moscow",
|
|
12656
|
+
"label": "Priority",
|
|
12657
|
+
"sortable": true,
|
|
12658
|
+
"format": "badge"
|
|
12682
12659
|
}
|
|
12683
12660
|
]
|
|
12684
12661
|
},
|
|
12685
12662
|
"sort_by": {
|
|
12686
|
-
"property": "
|
|
12663
|
+
"property": "moscow",
|
|
12687
12664
|
"direction": "asc"
|
|
12688
12665
|
},
|
|
12689
12666
|
"colour_by": "type",
|
|
@@ -13234,24 +13211,9 @@ var UPG_FRAMEWORKS = [
|
|
|
13234
13211
|
],
|
|
13235
13212
|
"slots": [
|
|
13236
13213
|
{
|
|
13237
|
-
"label": "
|
|
13238
|
-
"entityTypeId": "metric",
|
|
13239
|
-
"description": "How often you deploy to production"
|
|
13240
|
-
},
|
|
13241
|
-
{
|
|
13242
|
-
"label": "Lead Time for Changes",
|
|
13214
|
+
"label": "Delivery metric",
|
|
13243
13215
|
"entityTypeId": "metric",
|
|
13244
|
-
"description": "
|
|
13245
|
-
},
|
|
13246
|
-
{
|
|
13247
|
-
"label": "Change Failure Rate",
|
|
13248
|
-
"entityTypeId": "metric",
|
|
13249
|
-
"description": "Percentage of deployments causing failures"
|
|
13250
|
-
},
|
|
13251
|
-
{
|
|
13252
|
-
"label": "Time to Restore",
|
|
13253
|
-
"entityTypeId": "metric",
|
|
13254
|
-
"description": "How long to recover from a failure"
|
|
13216
|
+
"description": "One of the four DORA software-delivery performance metrics"
|
|
13255
13217
|
}
|
|
13256
13218
|
],
|
|
13257
13219
|
"data": {
|
|
@@ -13261,7 +13223,36 @@ var UPG_FRAMEWORKS = [
|
|
|
13261
13223
|
"role": "item"
|
|
13262
13224
|
}
|
|
13263
13225
|
],
|
|
13264
|
-
"required_properties": {
|
|
13226
|
+
"required_properties": {
|
|
13227
|
+
"metric": [
|
|
13228
|
+
{
|
|
13229
|
+
"property": "dora_metric",
|
|
13230
|
+
"type": "enum",
|
|
13231
|
+
"required": true,
|
|
13232
|
+
"label": "DORA metric",
|
|
13233
|
+
"description": "Which of the four DORA metrics this measures",
|
|
13234
|
+
"enum_values": [
|
|
13235
|
+
"deployment_frequency",
|
|
13236
|
+
"lead_time_for_changes",
|
|
13237
|
+
"change_failure_rate",
|
|
13238
|
+
"time_to_restore"
|
|
13239
|
+
]
|
|
13240
|
+
},
|
|
13241
|
+
{
|
|
13242
|
+
"property": "performance_tier",
|
|
13243
|
+
"type": "enum",
|
|
13244
|
+
"required": false,
|
|
13245
|
+
"label": "Performance tier",
|
|
13246
|
+
"description": "Where this metric sits on the DORA elite-to-low benchmark",
|
|
13247
|
+
"enum_values": [
|
|
13248
|
+
"elite",
|
|
13249
|
+
"high",
|
|
13250
|
+
"medium",
|
|
13251
|
+
"low"
|
|
13252
|
+
]
|
|
13253
|
+
}
|
|
13254
|
+
]
|
|
13255
|
+
},
|
|
13265
13256
|
"computed_properties": [
|
|
13266
13257
|
{
|
|
13267
13258
|
"property": "stability_index",
|
|
@@ -13417,29 +13408,9 @@ var UPG_FRAMEWORKS = [
|
|
|
13417
13408
|
],
|
|
13418
13409
|
"slots": [
|
|
13419
13410
|
{
|
|
13420
|
-
"label": "
|
|
13421
|
-
"entityTypeId": "metric",
|
|
13422
|
-
"description": "How do users find you?"
|
|
13423
|
-
},
|
|
13424
|
-
{
|
|
13425
|
-
"label": "Activation",
|
|
13411
|
+
"label": "Lifecycle metric",
|
|
13426
13412
|
"entityTypeId": "metric",
|
|
13427
|
-
"description": "
|
|
13428
|
-
},
|
|
13429
|
-
{
|
|
13430
|
-
"label": "Retention",
|
|
13431
|
-
"entityTypeId": "metric",
|
|
13432
|
-
"description": "Do users come back?"
|
|
13433
|
-
},
|
|
13434
|
-
{
|
|
13435
|
-
"label": "Revenue",
|
|
13436
|
-
"entityTypeId": "metric",
|
|
13437
|
-
"description": "Can you monetise the behaviour?"
|
|
13438
|
-
},
|
|
13439
|
-
{
|
|
13440
|
-
"label": "Referral",
|
|
13441
|
-
"entityTypeId": "metric",
|
|
13442
|
-
"description": "Do users tell others?"
|
|
13413
|
+
"description": "A metric tracking one stage of the customer lifecycle funnel"
|
|
13443
13414
|
}
|
|
13444
13415
|
],
|
|
13445
13416
|
"data": {
|
|
@@ -13449,7 +13420,24 @@ var UPG_FRAMEWORKS = [
|
|
|
13449
13420
|
"role": "item"
|
|
13450
13421
|
}
|
|
13451
13422
|
],
|
|
13452
|
-
"required_properties": {
|
|
13423
|
+
"required_properties": {
|
|
13424
|
+
"metric": [
|
|
13425
|
+
{
|
|
13426
|
+
"property": "lifecycle_stage",
|
|
13427
|
+
"type": "enum",
|
|
13428
|
+
"required": true,
|
|
13429
|
+
"label": "Lifecycle stage",
|
|
13430
|
+
"description": "Which AARRR funnel stage this metric measures",
|
|
13431
|
+
"enum_values": [
|
|
13432
|
+
"acquisition",
|
|
13433
|
+
"activation",
|
|
13434
|
+
"retention",
|
|
13435
|
+
"revenue",
|
|
13436
|
+
"referral"
|
|
13437
|
+
]
|
|
13438
|
+
}
|
|
13439
|
+
]
|
|
13440
|
+
},
|
|
13453
13441
|
"computed_properties": [
|
|
13454
13442
|
{
|
|
13455
13443
|
"property": "conversion_rate",
|
|
@@ -13461,7 +13449,39 @@ var UPG_FRAMEWORKS = [
|
|
|
13461
13449
|
]
|
|
13462
13450
|
},
|
|
13463
13451
|
"structure": {
|
|
13464
|
-
"pattern": "funnel"
|
|
13452
|
+
"pattern": "funnel",
|
|
13453
|
+
"stages": [
|
|
13454
|
+
{
|
|
13455
|
+
"id": "acquisition",
|
|
13456
|
+
"label": "Acquisition",
|
|
13457
|
+
"order": 0,
|
|
13458
|
+
"entity_type": "metric"
|
|
13459
|
+
},
|
|
13460
|
+
{
|
|
13461
|
+
"id": "activation",
|
|
13462
|
+
"label": "Activation",
|
|
13463
|
+
"order": 1,
|
|
13464
|
+
"entity_type": "metric"
|
|
13465
|
+
},
|
|
13466
|
+
{
|
|
13467
|
+
"id": "retention",
|
|
13468
|
+
"label": "Retention",
|
|
13469
|
+
"order": 2,
|
|
13470
|
+
"entity_type": "metric"
|
|
13471
|
+
},
|
|
13472
|
+
{
|
|
13473
|
+
"id": "revenue",
|
|
13474
|
+
"label": "Revenue",
|
|
13475
|
+
"order": 3,
|
|
13476
|
+
"entity_type": "metric"
|
|
13477
|
+
},
|
|
13478
|
+
{
|
|
13479
|
+
"id": "referral",
|
|
13480
|
+
"label": "Referral",
|
|
13481
|
+
"order": 4,
|
|
13482
|
+
"entity_type": "metric"
|
|
13483
|
+
}
|
|
13484
|
+
]
|
|
13465
13485
|
},
|
|
13466
13486
|
"presentation": {
|
|
13467
13487
|
"layout": {
|
|
@@ -13508,24 +13528,9 @@ var UPG_FRAMEWORKS = [
|
|
|
13508
13528
|
],
|
|
13509
13529
|
"slots": [
|
|
13510
13530
|
{
|
|
13511
|
-
"label": "
|
|
13512
|
-
"entityTypeId": "metric",
|
|
13513
|
-
"description": "The single metric reflecting core value delivered"
|
|
13514
|
-
},
|
|
13515
|
-
{
|
|
13516
|
-
"label": "Input Metric 1",
|
|
13517
|
-
"entityTypeId": "metric",
|
|
13518
|
-
"description": "A driver metric you can directly influence"
|
|
13519
|
-
},
|
|
13520
|
-
{
|
|
13521
|
-
"label": "Input Metric 2",
|
|
13522
|
-
"entityTypeId": "metric",
|
|
13523
|
-
"description": "A driver metric you can directly influence"
|
|
13524
|
-
},
|
|
13525
|
-
{
|
|
13526
|
-
"label": "Input Metric 3",
|
|
13531
|
+
"label": "Metric",
|
|
13527
13532
|
"entityTypeId": "metric",
|
|
13528
|
-
"description": "
|
|
13533
|
+
"description": "The North Star metric or one of its 3-5 input (driver) metrics"
|
|
13529
13534
|
}
|
|
13530
13535
|
],
|
|
13531
13536
|
"data": {
|
|
@@ -13535,7 +13540,28 @@ var UPG_FRAMEWORKS = [
|
|
|
13535
13540
|
"role": "item"
|
|
13536
13541
|
}
|
|
13537
13542
|
],
|
|
13538
|
-
"required_properties": {
|
|
13543
|
+
"required_properties": {
|
|
13544
|
+
"metric": [
|
|
13545
|
+
{
|
|
13546
|
+
"property": "metric_role",
|
|
13547
|
+
"type": "enum",
|
|
13548
|
+
"required": true,
|
|
13549
|
+
"label": "Metric role",
|
|
13550
|
+
"description": "Whether this is the single North Star or a driver that feeds it",
|
|
13551
|
+
"enum_values": [
|
|
13552
|
+
"north_star",
|
|
13553
|
+
"input"
|
|
13554
|
+
]
|
|
13555
|
+
},
|
|
13556
|
+
{
|
|
13557
|
+
"property": "leverage",
|
|
13558
|
+
"type": "number",
|
|
13559
|
+
"required": false,
|
|
13560
|
+
"label": "Leverage",
|
|
13561
|
+
"description": "How strongly this input metric moves the North Star (input metrics only)"
|
|
13562
|
+
}
|
|
13563
|
+
]
|
|
13564
|
+
},
|
|
13539
13565
|
"computed_properties": [
|
|
13540
13566
|
{
|
|
13541
13567
|
"property": "nsm_impact",
|
|
@@ -13835,7 +13861,7 @@ var UPG_FRAMEWORKS = [
|
|
|
13835
13861
|
]
|
|
13836
13862
|
},
|
|
13837
13863
|
"education": {
|
|
13838
|
-
"purpose": "Design the product itself as the primary growth driver (free tier, self-serve onboarding, in-product virality)
|
|
13864
|
+
"purpose": "Design the product itself as the primary growth driver (free tier, self-serve onboarding, in-product virality) to reduce dependence on sales-led acquisition.",
|
|
13839
13865
|
"core_question": "Can our product acquire, activate, and expand users without human intervention, and where in the loop do we still need sales?",
|
|
13840
13866
|
"when_to_use": [
|
|
13841
13867
|
"You are launching a new product, feature, or entering a new market",
|
|
@@ -14245,7 +14271,7 @@ var UPG_FRAMEWORKS = [
|
|
|
14245
14271
|
{
|
|
14246
14272
|
"label": "Team",
|
|
14247
14273
|
"entityTypeId": "team",
|
|
14248
|
-
"description": "Team conducting the health check
|
|
14274
|
+
"description": "Team conducting the health check; all members vote anonymously on each dimension"
|
|
14249
14275
|
},
|
|
14250
14276
|
{
|
|
14251
14277
|
"label": "Retrospective",
|
|
@@ -14334,7 +14360,7 @@ var UPG_FRAMEWORKS = [
|
|
|
14334
14360
|
},
|
|
14335
14361
|
"education": {
|
|
14336
14362
|
"purpose": "Regularly assess team health across dimensions (psychological safety, autonomy, mission clarity, fun, speed, learning) to catch problems early and celebrate strengths.",
|
|
14337
|
-
"core_question": "How is the team really doing
|
|
14363
|
+
"core_question": "How is the team really doing: where do they feel strong, where do they feel stuck, and what has changed since last check?",
|
|
14338
14364
|
"when_to_use": [
|
|
14339
14365
|
"You need to improve team collaboration, clarity, or effectiveness",
|
|
14340
14366
|
"Roles and responsibilities are unclear or causing friction",
|
|
@@ -14350,7 +14376,7 @@ var UPG_FRAMEWORKS = [
|
|
|
14350
14376
|
"id": "raid-log",
|
|
14351
14377
|
"name": "RAID Log",
|
|
14352
14378
|
"version": "1.0.0",
|
|
14353
|
-
"description": "A project management register tracking Risks, Assumptions, Issues, and Dependencies
|
|
14379
|
+
"description": "A project management register tracking Risks, Assumptions, Issues, and Dependencies, the four categories most likely to derail a project if left unmanaged.",
|
|
14354
14380
|
"category": "program_mgmt",
|
|
14355
14381
|
"origin": {
|
|
14356
14382
|
"type": "practitioner",
|
|
@@ -22520,6 +22546,222 @@ var UPG_STRUCTURE_PATTERNS = [
|
|
|
22520
22546
|
"quadrant",
|
|
22521
22547
|
"flow"
|
|
22522
22548
|
];
|
|
22549
|
+
var UPG_CANONICAL_FORMAT_VERSION = "1.0.0";
|
|
22550
|
+
var INTEGRITY_HASH_PRIMITIVE = "sha256";
|
|
22551
|
+
var INTEGRITY_DIGEST_HEX = 32;
|
|
22552
|
+
var INTEGRITY_ALGORITHM = "sha256-128";
|
|
22553
|
+
function canonicalizeOpen(value) {
|
|
22554
|
+
if (Array.isArray(value)) return value.map(canonicalizeOpen);
|
|
22555
|
+
if (value !== null && typeof value === "object") {
|
|
22556
|
+
const out = {};
|
|
22557
|
+
for (const key of Object.keys(value).sort(byCodeUnit)) {
|
|
22558
|
+
out[key] = canonicalizeOpen(value[key]);
|
|
22559
|
+
}
|
|
22560
|
+
return out;
|
|
22561
|
+
}
|
|
22562
|
+
return value;
|
|
22563
|
+
}
|
|
22564
|
+
function byCodeUnit(a, b) {
|
|
22565
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
22566
|
+
}
|
|
22567
|
+
function isEmpty(value) {
|
|
22568
|
+
if (value === null || value === void 0) return true;
|
|
22569
|
+
if (typeof value === "string") return value.length === 0;
|
|
22570
|
+
if (Array.isArray(value)) return value.length === 0;
|
|
22571
|
+
if (typeof value === "object") return Object.keys(value).length === 0;
|
|
22572
|
+
return false;
|
|
22573
|
+
}
|
|
22574
|
+
function orderedObject(source, keyOrder, opts = {}) {
|
|
22575
|
+
const force = new Set(opts.forceKeys ?? []);
|
|
22576
|
+
const open = new Set(opts.openKeys ?? []);
|
|
22577
|
+
const out = {};
|
|
22578
|
+
const emit = (key) => {
|
|
22579
|
+
if (!(key in source)) return;
|
|
22580
|
+
const raw = source[key];
|
|
22581
|
+
if (!force.has(key) && isEmpty(raw)) return;
|
|
22582
|
+
out[key] = open.has(key) ? canonicalizeOpen(raw) : raw;
|
|
22583
|
+
};
|
|
22584
|
+
for (const key of keyOrder) emit(key);
|
|
22585
|
+
for (const key of Object.keys(source).sort(byCodeUnit)) {
|
|
22586
|
+
if (!keyOrder.includes(key)) emit(key);
|
|
22587
|
+
}
|
|
22588
|
+
return out;
|
|
22589
|
+
}
|
|
22590
|
+
function repairNodeDrift(node) {
|
|
22591
|
+
const out = { ...node };
|
|
22592
|
+
if (typeof out.properties === "string") {
|
|
22593
|
+
out.properties = parseDriftString(out.properties, "object", `node ${node.id}.properties`);
|
|
22594
|
+
}
|
|
22595
|
+
if (typeof out.tags === "string") {
|
|
22596
|
+
out.tags = parseDriftString(out.tags, "array", `node ${node.id}.tags`);
|
|
22597
|
+
}
|
|
22598
|
+
return out;
|
|
22599
|
+
}
|
|
22600
|
+
function parseDriftString(raw, expect, where) {
|
|
22601
|
+
let parsed;
|
|
22602
|
+
try {
|
|
22603
|
+
parsed = JSON.parse(raw);
|
|
22604
|
+
} catch {
|
|
22605
|
+
throw new Error(`[upg fmt] ${where} is a string that is not valid JSON: ${truncate(raw)}`);
|
|
22606
|
+
}
|
|
22607
|
+
const ok = expect === "array" ? Array.isArray(parsed) : parsed !== null && typeof parsed === "object" && !Array.isArray(parsed);
|
|
22608
|
+
if (!ok) throw new Error(`[upg fmt] ${where} is a string that does not parse to a JSON ${expect}: ${truncate(raw)}`);
|
|
22609
|
+
return parsed;
|
|
22610
|
+
}
|
|
22611
|
+
function truncate(s) {
|
|
22612
|
+
return s.length > 60 ? s.slice(0, 57) + "..." : s;
|
|
22613
|
+
}
|
|
22614
|
+
function deriveSummary(description) {
|
|
22615
|
+
if (!description) return void 0;
|
|
22616
|
+
const firstLine = description.split("\n").map((l) => l.trim()).find((l) => l.length > 0);
|
|
22617
|
+
if (!firstLine) return void 0;
|
|
22618
|
+
return firstLine.length > 200 ? firstLine.slice(0, 197) + "..." : firstLine;
|
|
22619
|
+
}
|
|
22620
|
+
function sortNodes(nodes) {
|
|
22621
|
+
return [...nodes].sort(
|
|
22622
|
+
(a, b) => byCodeUnit(a.type ?? "", b.type ?? "") || byCodeUnit(a.slug ?? a.title ?? "", b.slug ?? b.title ?? "") || byCodeUnit(a.id ?? "", b.id ?? "")
|
|
22623
|
+
);
|
|
22624
|
+
}
|
|
22625
|
+
function sortEdges(edges) {
|
|
22626
|
+
return [...edges].sort(
|
|
22627
|
+
(a, b) => byCodeUnit(a.source ?? "", b.source ?? "") || byCodeUnit(a.target ?? "", b.target ?? "") || byCodeUnit(a.type ?? "", b.type ?? "") || byCodeUnit(a.id ?? "", b.id ?? "")
|
|
22628
|
+
);
|
|
22629
|
+
}
|
|
22630
|
+
var NODE_KEY_ORDER = [
|
|
22631
|
+
"id",
|
|
22632
|
+
"type",
|
|
22633
|
+
"title",
|
|
22634
|
+
"slug",
|
|
22635
|
+
"aliases",
|
|
22636
|
+
"description",
|
|
22637
|
+
"tags",
|
|
22638
|
+
"status",
|
|
22639
|
+
"lifecycle_status",
|
|
22640
|
+
"source_id",
|
|
22641
|
+
"source_type",
|
|
22642
|
+
"mapping_confidence",
|
|
22643
|
+
"external_tool",
|
|
22644
|
+
"external_ref",
|
|
22645
|
+
"external_id",
|
|
22646
|
+
"sort_order",
|
|
22647
|
+
"properties"
|
|
22648
|
+
];
|
|
22649
|
+
var EDGE_KEY_ORDER = ["id", "source", "target", "type", "mapping_confidence"];
|
|
22650
|
+
var CROSS_EDGE_KEY_ORDER = ["id", "source", "target", "type", "source_product_id", "target_product_id", "mapping_confidence"];
|
|
22651
|
+
var PRODUCT_KEY_ORDER = ["id", "title", "description", "stage", "properties"];
|
|
22652
|
+
function canonicalNode(node) {
|
|
22653
|
+
const repaired = repairNodeDrift(node);
|
|
22654
|
+
if (Array.isArray(repaired.tags)) {
|
|
22655
|
+
repaired.tags = [...new Set(repaired.tags)].sort(byCodeUnit);
|
|
22656
|
+
}
|
|
22657
|
+
return orderedObject(repaired, NODE_KEY_ORDER, { forceKeys: ["id", "type", "title"], openKeys: ["properties"] });
|
|
22658
|
+
}
|
|
22659
|
+
function canonicalEdge(edge) {
|
|
22660
|
+
return orderedObject(edge, EDGE_KEY_ORDER, {
|
|
22661
|
+
forceKeys: ["id", "source", "target", "type"]
|
|
22662
|
+
});
|
|
22663
|
+
}
|
|
22664
|
+
function canonicalCrossEdge(edge) {
|
|
22665
|
+
return orderedObject(edge, CROSS_EDGE_KEY_ORDER, {
|
|
22666
|
+
forceKeys: ["id", "source", "target", "type"]
|
|
22667
|
+
});
|
|
22668
|
+
}
|
|
22669
|
+
function canonicalProduct(product) {
|
|
22670
|
+
return orderedObject(product, PRODUCT_KEY_ORDER, { forceKeys: ["id", "title"], openKeys: ["properties"] });
|
|
22671
|
+
}
|
|
22672
|
+
function singleBody(doc) {
|
|
22673
|
+
return {
|
|
22674
|
+
product: canonicalProduct(doc.product),
|
|
22675
|
+
nodes: sortNodes(doc.nodes ?? []).map(canonicalNode),
|
|
22676
|
+
edges: sortEdges(doc.edges ?? []).map(canonicalEdge)
|
|
22677
|
+
};
|
|
22678
|
+
}
|
|
22679
|
+
function portfolioBody(doc) {
|
|
22680
|
+
const products = [...doc.products ?? []].sort((a, b) => byCodeUnit(a.id ?? "", b.id ?? "")).map((p) => {
|
|
22681
|
+
const { nodes, edges, ...rest } = p;
|
|
22682
|
+
return {
|
|
22683
|
+
...canonicalProduct(rest),
|
|
22684
|
+
nodes: sortNodes(nodes ?? []).map(canonicalNode),
|
|
22685
|
+
edges: sortEdges(edges ?? []).map(canonicalEdge)
|
|
22686
|
+
};
|
|
22687
|
+
});
|
|
22688
|
+
return {
|
|
22689
|
+
organization: orderedObject(doc.organization, ["id", "title", "description", "logo_url", "industry"], { forceKeys: ["id", "title"] }),
|
|
22690
|
+
product_areas: [...doc.product_areas ?? []].sort((a, b) => byCodeUnit(a.id ?? "", b.id ?? "")).map((a) => orderedObject(a, ["id", "title", "description", "parent_area_id", "strategic_priority", "products"], { forceKeys: ["id", "title"] })),
|
|
22691
|
+
portfolios: [...doc.portfolios ?? []].sort((a, b) => byCodeUnit(a.id ?? "", b.id ?? "")).map((p) => orderedObject(p, ["id", "title", "description", "parent_portfolio_id", "hierarchy_model", "products"], { forceKeys: ["id", "title"] })),
|
|
22692
|
+
products,
|
|
22693
|
+
cross_edges: sortEdges(doc.cross_edges ?? []).map(canonicalCrossEdge)
|
|
22694
|
+
};
|
|
22695
|
+
}
|
|
22696
|
+
function computeBodyChecksum(doc) {
|
|
22697
|
+
const body = isPortfolio(doc) ? portfolioBody(doc) : singleBody(doc);
|
|
22698
|
+
const content = JSON.stringify(body);
|
|
22699
|
+
return createHash2(INTEGRITY_HASH_PRIMITIVE).update(content).digest("hex").slice(0, INTEGRITY_DIGEST_HEX);
|
|
22700
|
+
}
|
|
22701
|
+
function isPortfolio(doc) {
|
|
22702
|
+
return doc.type === "portfolio" || "cross_edges" in doc;
|
|
22703
|
+
}
|
|
22704
|
+
function serializeCanonical(doc, opts = {}) {
|
|
22705
|
+
if (isPortfolio(doc)) return serializePortfolioWithHeader(doc, opts);
|
|
22706
|
+
return serializeSingleWithHeader(doc, opts);
|
|
22707
|
+
}
|
|
22708
|
+
function buildProvenance(doc, opts) {
|
|
22709
|
+
const source = opts.source ?? doc.source ?? { tool: "unknown" };
|
|
22710
|
+
return orderedObject(
|
|
22711
|
+
{
|
|
22712
|
+
tool: source.tool,
|
|
22713
|
+
tool_version: source.tool_version,
|
|
22714
|
+
workspace_id: source.workspace_id,
|
|
22715
|
+
exported_at: opts.exportedAt ?? doc.exported_at
|
|
22716
|
+
},
|
|
22717
|
+
["tool", "tool_version", "workspace_id", "exported_at"],
|
|
22718
|
+
{ forceKeys: ["tool"] }
|
|
22719
|
+
);
|
|
22720
|
+
}
|
|
22721
|
+
function serializeSingleWithHeader(doc, opts) {
|
|
22722
|
+
const body = singleBody(doc);
|
|
22723
|
+
const product = doc.product;
|
|
22724
|
+
const header = {
|
|
22725
|
+
format_version: UPG_CANONICAL_FORMAT_VERSION,
|
|
22726
|
+
spec_version: doc.upg_version,
|
|
22727
|
+
product: orderedObject(
|
|
22728
|
+
{ id: product.id, title: product.title, stage: product.stage },
|
|
22729
|
+
["id", "title", "stage"],
|
|
22730
|
+
{ forceKeys: ["id", "title"] }
|
|
22731
|
+
)
|
|
22732
|
+
};
|
|
22733
|
+
const summary = deriveSummary(product.description);
|
|
22734
|
+
if (summary) header.summary = summary;
|
|
22735
|
+
header.counts = { nodes: doc.nodes?.length ?? 0, edges: doc.edges?.length ?? 0 };
|
|
22736
|
+
header.provenance = buildProvenance(doc, opts);
|
|
22737
|
+
header.integrity = { algorithm: INTEGRITY_ALGORITHM, body: computeBodyChecksum(doc) };
|
|
22738
|
+
return JSON.stringify({ $upg: header, ...body }, null, 2) + "\n";
|
|
22739
|
+
}
|
|
22740
|
+
function serializePortfolioWithHeader(doc, opts) {
|
|
22741
|
+
const body = portfolioBody(doc);
|
|
22742
|
+
const org = doc.organization;
|
|
22743
|
+
const header = {
|
|
22744
|
+
format_version: UPG_CANONICAL_FORMAT_VERSION,
|
|
22745
|
+
spec_version: doc.upg_version,
|
|
22746
|
+
kind: "portfolio",
|
|
22747
|
+
organization: orderedObject(
|
|
22748
|
+
{ id: org.id, title: org.title },
|
|
22749
|
+
["id", "title"],
|
|
22750
|
+
{ forceKeys: ["id", "title"] }
|
|
22751
|
+
)
|
|
22752
|
+
};
|
|
22753
|
+
const summary = deriveSummary(org.description);
|
|
22754
|
+
if (summary) header.summary = summary;
|
|
22755
|
+
header.counts = {
|
|
22756
|
+
products: doc.products?.length ?? 0,
|
|
22757
|
+
product_areas: doc.product_areas?.length ?? 0,
|
|
22758
|
+
portfolios: doc.portfolios?.length ?? 0,
|
|
22759
|
+
cross_edges: doc.cross_edges?.length ?? 0
|
|
22760
|
+
};
|
|
22761
|
+
header.provenance = buildProvenance(doc, opts);
|
|
22762
|
+
header.integrity = { algorithm: INTEGRITY_ALGORITHM, body: computeBodyChecksum(doc) };
|
|
22763
|
+
return JSON.stringify({ $upg: header, ...body }, null, 2) + "\n";
|
|
22764
|
+
}
|
|
22523
22765
|
var UPG_VERSION = "0.7.3";
|
|
22524
22766
|
var MARKDOWN_FORMAT_VERSION = "0.1";
|
|
22525
22767
|
var UPG_TYPES = getTypes();
|
|
@@ -28112,11 +28354,7 @@ async function runMcpServer() {
|
|
|
28112
28354
|
edges: []
|
|
28113
28355
|
};
|
|
28114
28356
|
await fs3.mkdir(path6.dirname(defaultFile), { recursive: true });
|
|
28115
|
-
await fs3.writeFile(
|
|
28116
|
-
defaultFile,
|
|
28117
|
-
JSON.stringify(blank, null, 2) + "\n",
|
|
28118
|
-
"utf-8"
|
|
28119
|
-
);
|
|
28357
|
+
await fs3.writeFile(defaultFile, serializeCanonical(blank), "utf-8");
|
|
28120
28358
|
process.stderr.write(`Created new UPG file: ${defaultFile}
|
|
28121
28359
|
`);
|
|
28122
28360
|
resolvedPath = defaultFile;
|
|
@@ -28140,11 +28378,7 @@ async function runMcpServer() {
|
|
|
28140
28378
|
edges: []
|
|
28141
28379
|
};
|
|
28142
28380
|
await fs3.mkdir(path6.dirname(resolvedPath), { recursive: true });
|
|
28143
|
-
await fs3.writeFile(
|
|
28144
|
-
resolvedPath,
|
|
28145
|
-
JSON.stringify(blank, null, 2) + "\n",
|
|
28146
|
-
"utf-8"
|
|
28147
|
-
);
|
|
28381
|
+
await fs3.writeFile(resolvedPath, serializeCanonical(blank), "utf-8");
|
|
28148
28382
|
process.stderr.write(`Created new UPG file: ${resolvedPath}
|
|
28149
28383
|
`);
|
|
28150
28384
|
}
|