fraim 2.0.154 → 2.0.160
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 +1 -1
- package/dist/src/ai-hub/cert-store.js +70 -0
- package/dist/src/ai-hub/desktop-main.js +225 -50
- package/dist/src/ai-hub/hosts.js +135 -8
- package/dist/src/ai-hub/manager-turns.js +38 -0
- package/dist/src/ai-hub/office-sideload.js +138 -0
- package/dist/src/ai-hub/openclaw-bridge.js +239 -0
- package/dist/src/ai-hub/server.js +479 -48
- package/dist/src/ai-hub/word-sideload.js +95 -0
- package/dist/src/cli/commands/add-ide.js +9 -0
- package/dist/src/cli/commands/init-project.js +46 -34
- package/dist/src/cli/commands/login.js +1 -2
- package/dist/src/cli/commands/setup.js +0 -2
- package/dist/src/cli/commands/sync.js +41 -11
- package/dist/src/cli/doctor/checks/mcp-connectivity-checks.js +66 -2
- package/dist/src/cli/doctor/checks/workflow-checks.js +1 -65
- package/dist/src/cli/mcp/fraim-mcp-latest-launcher.js +136 -0
- package/dist/src/cli/mcp/mcp-server-registry.js +14 -10
- package/dist/src/cli/setup/auto-mcp-setup.js +1 -1
- package/dist/src/cli/setup/ide-invocation-surfaces.js +2 -2
- package/dist/src/cli/utils/fraim-gitignore.js +11 -0
- package/dist/src/cli/utils/github-workflow-sync.js +231 -0
- package/dist/src/cli/utils/managed-agent-paths.js +1 -1
- package/dist/src/cli/utils/project-bootstrap.js +6 -3
- package/dist/src/cli/utils/remote-sync.js +1 -1
- package/dist/src/core/ai-mentor.js +46 -37
- package/dist/src/core/config-loader.js +69 -2
- package/dist/src/core/fraim-config-schema.generated.js +267 -6
- package/dist/src/core/types.js +0 -1
- package/dist/src/core/utils/fraim-labels.js +182 -0
- package/dist/src/core/utils/git-utils.js +22 -1
- package/dist/src/core/utils/project-fraim-paths.js +58 -0
- package/dist/src/first-run/session-service.js +3 -3
- package/dist/src/first-run/types.js +1 -1
- package/dist/src/local-mcp-server/learning-context-builder.js +77 -52
- package/dist/src/local-mcp-server/stdio-server.js +212 -13
- package/package.json +6 -2
- package/public/ai-hub/index.html +289 -229
- package/public/ai-hub/powerpoint-taskpane/icon-64.png +0 -0
- package/public/ai-hub/powerpoint-taskpane/index.html +235 -0
- package/public/ai-hub/powerpoint-taskpane/manifest.xml +30 -0
- package/public/ai-hub/script.js +1155 -586
- package/public/ai-hub/styles.css +1226 -722
- package/public/first-run/index.html +35 -35
- package/public/first-run/script.js +667 -667
|
@@ -25,10 +25,69 @@ function normalizeCustomerCommunication(config) {
|
|
|
25
25
|
deliveryProvider: current.deliveryProvider
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
|
+
function normalizeIntegrations(config) {
|
|
29
|
+
const current = config?.integrations;
|
|
30
|
+
if (!current || typeof current !== 'object')
|
|
31
|
+
return undefined;
|
|
32
|
+
return {
|
|
33
|
+
itsm: current.itsm && typeof current.itsm === 'object'
|
|
34
|
+
? {
|
|
35
|
+
provider: current.itsm.provider,
|
|
36
|
+
instanceUrl: current.itsm.instanceUrl
|
|
37
|
+
}
|
|
38
|
+
: undefined,
|
|
39
|
+
identity: current.identity && typeof current.identity === 'object'
|
|
40
|
+
? { provider: current.identity.provider }
|
|
41
|
+
: undefined
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function normalizeAutomation(config) {
|
|
45
|
+
const support = config?.automation?.support;
|
|
46
|
+
if (!support || typeof support !== 'object')
|
|
47
|
+
return undefined;
|
|
48
|
+
return {
|
|
49
|
+
support: {
|
|
50
|
+
startMode: support.startMode,
|
|
51
|
+
defaultDecisionMode: support.defaultDecisionMode,
|
|
52
|
+
contextResolver: support.contextResolver && typeof support.contextResolver === 'object'
|
|
53
|
+
? {
|
|
54
|
+
scriptPath: support.contextResolver.scriptPath,
|
|
55
|
+
arguments: Array.isArray(support.contextResolver.arguments) ? support.contextResolver.arguments : undefined,
|
|
56
|
+
timeoutMs: support.contextResolver.timeoutMs
|
|
57
|
+
}
|
|
58
|
+
: undefined,
|
|
59
|
+
queue: support.queue && typeof support.queue === 'object'
|
|
60
|
+
? {
|
|
61
|
+
provider: support.queue.provider,
|
|
62
|
+
table: support.queue.table,
|
|
63
|
+
assignmentGroup: support.queue.assignmentGroup,
|
|
64
|
+
claimField: support.queue.claimField,
|
|
65
|
+
fixturePath: support.queue.fixturePath,
|
|
66
|
+
eligibleStates: Array.isArray(support.queue.eligibleStates) ? support.queue.eligibleStates : undefined,
|
|
67
|
+
pollIntervalSeconds: support.queue.pollIntervalSeconds,
|
|
68
|
+
successState: support.queue.successState,
|
|
69
|
+
failureState: support.queue.failureState,
|
|
70
|
+
closeState: support.queue.closeState
|
|
71
|
+
}
|
|
72
|
+
: undefined,
|
|
73
|
+
requestTypes: support.requestTypes && typeof support.requestTypes === 'object'
|
|
74
|
+
? support.requestTypes
|
|
75
|
+
: undefined,
|
|
76
|
+
communication: support.communication && typeof support.communication === 'object'
|
|
77
|
+
? {
|
|
78
|
+
channel: support.communication.channel,
|
|
79
|
+
deliveryMode: support.communication.deliveryMode,
|
|
80
|
+
recipientField: support.communication.recipientField,
|
|
81
|
+
includeTemporaryPassword: support.communication.includeTemporaryPassword,
|
|
82
|
+
messageTemplate: support.communication.messageTemplate
|
|
83
|
+
}
|
|
84
|
+
: undefined
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}
|
|
28
88
|
function normalizeFraimConfig(config) {
|
|
29
89
|
// Handle backward compatibility and migration
|
|
30
90
|
const mergedConfig = {
|
|
31
|
-
version: config.version || types_1.DEFAULT_FRAIM_CONFIG.version,
|
|
32
91
|
project: {
|
|
33
92
|
...types_1.DEFAULT_FRAIM_CONFIG.project,
|
|
34
93
|
...(config.project || {})
|
|
@@ -70,6 +129,14 @@ function normalizeFraimConfig(config) {
|
|
|
70
129
|
if (customerCommunication) {
|
|
71
130
|
mergedConfig.customerCommunication = customerCommunication;
|
|
72
131
|
}
|
|
132
|
+
const integrations = normalizeIntegrations(config);
|
|
133
|
+
if (integrations) {
|
|
134
|
+
mergedConfig.integrations = integrations;
|
|
135
|
+
}
|
|
136
|
+
const automation = normalizeAutomation(config);
|
|
137
|
+
if (automation) {
|
|
138
|
+
mergedConfig.automation = automation;
|
|
139
|
+
}
|
|
73
140
|
return mergedConfig;
|
|
74
141
|
}
|
|
75
142
|
/**
|
|
@@ -86,7 +153,7 @@ function loadFraimConfig(configPath = (0, project_fraim_paths_1.getWorkspaceConf
|
|
|
86
153
|
const configContent = (0, fs_1.readFileSync)(configPath, 'utf-8');
|
|
87
154
|
const config = JSON.parse(configContent);
|
|
88
155
|
const mergedConfig = normalizeFraimConfig(config);
|
|
89
|
-
console.log(`Loaded FRAIM config from ${displayPath}
|
|
156
|
+
console.log(`Loaded FRAIM config from ${displayPath}`);
|
|
90
157
|
if (config.git && !config.repository) {
|
|
91
158
|
console.warn('Deprecated: "git" config detected. Consider migrating to "repository" config.');
|
|
92
159
|
}
|
|
@@ -8,10 +8,6 @@ exports.SUPPORTED_FRAIM_CONFIG_PATHS = exports.FRAIM_CONFIG_SCHEMA = void 0;
|
|
|
8
8
|
exports.FRAIM_CONFIG_SCHEMA = {
|
|
9
9
|
"kind": "object",
|
|
10
10
|
"properties": {
|
|
11
|
-
"version": {
|
|
12
|
-
"kind": "string",
|
|
13
|
-
"required": true
|
|
14
|
-
},
|
|
15
11
|
"project": {
|
|
16
12
|
"kind": "object",
|
|
17
13
|
"properties": {
|
|
@@ -142,6 +138,14 @@ exports.FRAIM_CONFIG_SCHEMA = {
|
|
|
142
138
|
}
|
|
143
139
|
}
|
|
144
140
|
},
|
|
141
|
+
"githubWorkflows": {
|
|
142
|
+
"kind": "object",
|
|
143
|
+
"properties": {
|
|
144
|
+
"enabled": {
|
|
145
|
+
"kind": "boolean"
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
},
|
|
145
149
|
"validation": {
|
|
146
150
|
"kind": "object",
|
|
147
151
|
"properties": {
|
|
@@ -198,6 +202,9 @@ exports.FRAIM_CONFIG_SCHEMA = {
|
|
|
198
202
|
},
|
|
199
203
|
"scoreThreshold": {
|
|
200
204
|
"kind": "number"
|
|
205
|
+
},
|
|
206
|
+
"globalPath": {
|
|
207
|
+
"kind": "string"
|
|
201
208
|
}
|
|
202
209
|
}
|
|
203
210
|
},
|
|
@@ -262,12 +269,220 @@ exports.FRAIM_CONFIG_SCHEMA = {
|
|
|
262
269
|
]
|
|
263
270
|
}
|
|
264
271
|
}
|
|
272
|
+
},
|
|
273
|
+
"crm": {
|
|
274
|
+
"kind": "object",
|
|
275
|
+
"properties": {
|
|
276
|
+
"provider": {
|
|
277
|
+
"kind": "enum",
|
|
278
|
+
"values": [
|
|
279
|
+
"salesforce",
|
|
280
|
+
"customereq"
|
|
281
|
+
]
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
},
|
|
285
|
+
"integrations": {
|
|
286
|
+
"kind": "object",
|
|
287
|
+
"properties": {
|
|
288
|
+
"itsm": {
|
|
289
|
+
"kind": "object",
|
|
290
|
+
"properties": {
|
|
291
|
+
"provider": {
|
|
292
|
+
"kind": "enum",
|
|
293
|
+
"values": [
|
|
294
|
+
"servicenow"
|
|
295
|
+
]
|
|
296
|
+
},
|
|
297
|
+
"instanceUrl": {
|
|
298
|
+
"kind": "string"
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
},
|
|
302
|
+
"identity": {
|
|
303
|
+
"kind": "object",
|
|
304
|
+
"properties": {
|
|
305
|
+
"provider": {
|
|
306
|
+
"kind": "enum",
|
|
307
|
+
"values": [
|
|
308
|
+
"microsoft365"
|
|
309
|
+
]
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
"automation": {
|
|
316
|
+
"kind": "object",
|
|
317
|
+
"properties": {
|
|
318
|
+
"support": {
|
|
319
|
+
"kind": "object",
|
|
320
|
+
"properties": {
|
|
321
|
+
"startMode": {
|
|
322
|
+
"kind": "enum",
|
|
323
|
+
"values": [
|
|
324
|
+
"manual",
|
|
325
|
+
"autonomous"
|
|
326
|
+
]
|
|
327
|
+
},
|
|
328
|
+
"defaultDecisionMode": {
|
|
329
|
+
"kind": "enum",
|
|
330
|
+
"values": [
|
|
331
|
+
"hitl",
|
|
332
|
+
"autonomous"
|
|
333
|
+
]
|
|
334
|
+
},
|
|
335
|
+
"contextResolver": {
|
|
336
|
+
"kind": "object",
|
|
337
|
+
"properties": {
|
|
338
|
+
"scriptPath": {
|
|
339
|
+
"kind": "string"
|
|
340
|
+
},
|
|
341
|
+
"arguments": {
|
|
342
|
+
"kind": "array",
|
|
343
|
+
"element": {
|
|
344
|
+
"kind": "string"
|
|
345
|
+
}
|
|
346
|
+
},
|
|
347
|
+
"timeoutMs": {
|
|
348
|
+
"kind": "number"
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
},
|
|
352
|
+
"queue": {
|
|
353
|
+
"kind": "object",
|
|
354
|
+
"properties": {
|
|
355
|
+
"provider": {
|
|
356
|
+
"kind": "enum",
|
|
357
|
+
"values": [
|
|
358
|
+
"servicenow",
|
|
359
|
+
"fixture"
|
|
360
|
+
]
|
|
361
|
+
},
|
|
362
|
+
"table": {
|
|
363
|
+
"kind": "string"
|
|
364
|
+
},
|
|
365
|
+
"assignmentGroup": {
|
|
366
|
+
"kind": "string"
|
|
367
|
+
},
|
|
368
|
+
"claimField": {
|
|
369
|
+
"kind": "string"
|
|
370
|
+
},
|
|
371
|
+
"fixturePath": {
|
|
372
|
+
"kind": "string"
|
|
373
|
+
},
|
|
374
|
+
"eligibleStates": {
|
|
375
|
+
"kind": "array",
|
|
376
|
+
"element": {
|
|
377
|
+
"kind": "string"
|
|
378
|
+
}
|
|
379
|
+
},
|
|
380
|
+
"pollIntervalSeconds": {
|
|
381
|
+
"kind": "number"
|
|
382
|
+
},
|
|
383
|
+
"successState": {
|
|
384
|
+
"kind": "string"
|
|
385
|
+
},
|
|
386
|
+
"failureState": {
|
|
387
|
+
"kind": "string"
|
|
388
|
+
},
|
|
389
|
+
"closeState": {
|
|
390
|
+
"kind": "string"
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
},
|
|
394
|
+
"requestTypes": {
|
|
395
|
+
"kind": "object",
|
|
396
|
+
"properties": {
|
|
397
|
+
"password_reset": {
|
|
398
|
+
"kind": "object",
|
|
399
|
+
"properties": {
|
|
400
|
+
"mode": {
|
|
401
|
+
"kind": "enum",
|
|
402
|
+
"values": [
|
|
403
|
+
"hitl",
|
|
404
|
+
"autonomous"
|
|
405
|
+
]
|
|
406
|
+
},
|
|
407
|
+
"requireApprovalState": {
|
|
408
|
+
"kind": "string"
|
|
409
|
+
},
|
|
410
|
+
"selector": {
|
|
411
|
+
"kind": "object",
|
|
412
|
+
"properties": {
|
|
413
|
+
"shortDescriptionContains": {
|
|
414
|
+
"kind": "array",
|
|
415
|
+
"element": {
|
|
416
|
+
"kind": "string"
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
},
|
|
421
|
+
"microsoftPasswordReset": {
|
|
422
|
+
"kind": "object",
|
|
423
|
+
"properties": {
|
|
424
|
+
"resetType": {
|
|
425
|
+
"kind": "enum",
|
|
426
|
+
"values": [
|
|
427
|
+
"temporary_generated",
|
|
428
|
+
"temporary_provided",
|
|
429
|
+
"permanent_provided"
|
|
430
|
+
]
|
|
431
|
+
},
|
|
432
|
+
"providedPasswordEnvVar": {
|
|
433
|
+
"kind": "string"
|
|
434
|
+
},
|
|
435
|
+
"forceChangePasswordNextSignIn": {
|
|
436
|
+
"kind": "boolean"
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
},
|
|
444
|
+
"communication": {
|
|
445
|
+
"kind": "object",
|
|
446
|
+
"properties": {
|
|
447
|
+
"channel": {
|
|
448
|
+
"kind": "enum",
|
|
449
|
+
"values": [
|
|
450
|
+
"email",
|
|
451
|
+
"sms",
|
|
452
|
+
"teams",
|
|
453
|
+
"manual-handoff"
|
|
454
|
+
]
|
|
455
|
+
},
|
|
456
|
+
"deliveryMode": {
|
|
457
|
+
"kind": "enum",
|
|
458
|
+
"values": [
|
|
459
|
+
"direct",
|
|
460
|
+
"operator-relay"
|
|
461
|
+
]
|
|
462
|
+
},
|
|
463
|
+
"recipientField": {
|
|
464
|
+
"kind": "enum",
|
|
465
|
+
"values": [
|
|
466
|
+
"requested_for",
|
|
467
|
+
"requested_by"
|
|
468
|
+
]
|
|
469
|
+
},
|
|
470
|
+
"includeTemporaryPassword": {
|
|
471
|
+
"kind": "boolean"
|
|
472
|
+
},
|
|
473
|
+
"messageTemplate": {
|
|
474
|
+
"kind": "string"
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
265
481
|
}
|
|
266
482
|
},
|
|
267
483
|
"required": true
|
|
268
484
|
};
|
|
269
485
|
exports.SUPPORTED_FRAIM_CONFIG_PATHS = [
|
|
270
|
-
"version",
|
|
271
486
|
"project",
|
|
272
487
|
"project.name",
|
|
273
488
|
"project.industry",
|
|
@@ -302,6 +517,8 @@ exports.SUPPORTED_FRAIM_CONFIG_PATHS = [
|
|
|
302
517
|
"customizations.designSystem",
|
|
303
518
|
"customizations.designSystem.path",
|
|
304
519
|
"customizations.designSystem.brand",
|
|
520
|
+
"customizations.githubWorkflows",
|
|
521
|
+
"customizations.githubWorkflows.enabled",
|
|
305
522
|
"customizations.validation",
|
|
306
523
|
"customizations.validation.buildCommand",
|
|
307
524
|
"customizations.validation.testSuiteCommand",
|
|
@@ -316,6 +533,7 @@ exports.SUPPORTED_FRAIM_CONFIG_PATHS = [
|
|
|
316
533
|
"learning",
|
|
317
534
|
"learning.lastSynthesisDate",
|
|
318
535
|
"learning.scoreThreshold",
|
|
536
|
+
"learning.globalPath",
|
|
319
537
|
"customer-communication",
|
|
320
538
|
"customer-communication.productName",
|
|
321
539
|
"customer-communication.productUrl",
|
|
@@ -329,5 +547,48 @@ exports.SUPPORTED_FRAIM_CONFIG_PATHS = [
|
|
|
329
547
|
"stakeholderUpdate.cadence",
|
|
330
548
|
"stakeholderUpdate.historyPath",
|
|
331
549
|
"operatingReview",
|
|
332
|
-
"operatingReview.cadence"
|
|
550
|
+
"operatingReview.cadence",
|
|
551
|
+
"crm",
|
|
552
|
+
"crm.provider",
|
|
553
|
+
"integrations",
|
|
554
|
+
"integrations.itsm",
|
|
555
|
+
"integrations.itsm.provider",
|
|
556
|
+
"integrations.itsm.instanceUrl",
|
|
557
|
+
"integrations.identity",
|
|
558
|
+
"integrations.identity.provider",
|
|
559
|
+
"automation",
|
|
560
|
+
"automation.support",
|
|
561
|
+
"automation.support.startMode",
|
|
562
|
+
"automation.support.defaultDecisionMode",
|
|
563
|
+
"automation.support.contextResolver",
|
|
564
|
+
"automation.support.contextResolver.scriptPath",
|
|
565
|
+
"automation.support.contextResolver.arguments",
|
|
566
|
+
"automation.support.contextResolver.timeoutMs",
|
|
567
|
+
"automation.support.queue",
|
|
568
|
+
"automation.support.queue.provider",
|
|
569
|
+
"automation.support.queue.table",
|
|
570
|
+
"automation.support.queue.assignmentGroup",
|
|
571
|
+
"automation.support.queue.claimField",
|
|
572
|
+
"automation.support.queue.fixturePath",
|
|
573
|
+
"automation.support.queue.eligibleStates",
|
|
574
|
+
"automation.support.queue.pollIntervalSeconds",
|
|
575
|
+
"automation.support.queue.successState",
|
|
576
|
+
"automation.support.queue.failureState",
|
|
577
|
+
"automation.support.queue.closeState",
|
|
578
|
+
"automation.support.requestTypes",
|
|
579
|
+
"automation.support.requestTypes.password_reset",
|
|
580
|
+
"automation.support.requestTypes.password_reset.mode",
|
|
581
|
+
"automation.support.requestTypes.password_reset.requireApprovalState",
|
|
582
|
+
"automation.support.requestTypes.password_reset.selector",
|
|
583
|
+
"automation.support.requestTypes.password_reset.selector.shortDescriptionContains",
|
|
584
|
+
"automation.support.requestTypes.password_reset.microsoftPasswordReset",
|
|
585
|
+
"automation.support.requestTypes.password_reset.microsoftPasswordReset.resetType",
|
|
586
|
+
"automation.support.requestTypes.password_reset.microsoftPasswordReset.providedPasswordEnvVar",
|
|
587
|
+
"automation.support.requestTypes.password_reset.microsoftPasswordReset.forceChangePasswordNextSignIn",
|
|
588
|
+
"automation.support.communication",
|
|
589
|
+
"automation.support.communication.channel",
|
|
590
|
+
"automation.support.communication.deliveryMode",
|
|
591
|
+
"automation.support.communication.recipientField",
|
|
592
|
+
"automation.support.communication.includeTemporaryPassword",
|
|
593
|
+
"automation.support.communication.messageTemplate"
|
|
333
594
|
];
|
package/dist/src/core/types.js
CHANGED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.FRAIM_STATUS_LABELS = exports.FRAIM_PHASE_LABELS = exports.LEGACY_FRAIM_STATUS_LABELS = void 0;
|
|
7
|
+
exports.isFraimPhaseLabel = isFraimPhaseLabel;
|
|
8
|
+
exports.isFraimStatusLabel = isFraimStatusLabel;
|
|
9
|
+
exports.isLegacyFraimStatusLabel = isLegacyFraimStatusLabel;
|
|
10
|
+
exports.normalizeFraimPhaseLabel = normalizeFraimPhaseLabel;
|
|
11
|
+
exports.normalizeFraimStatusLabel = normalizeFraimStatusLabel;
|
|
12
|
+
exports.partitionFraimLabels = partitionFraimLabels;
|
|
13
|
+
exports.mergeFraimGitHubLabels = mergeFraimGitHubLabels;
|
|
14
|
+
exports.applyIssueStatusTransition = applyIssueStatusTransition;
|
|
15
|
+
exports.applyIssuePhaseTransition = applyIssuePhaseTransition;
|
|
16
|
+
exports.applyIssuePhaseAndStatusTransition = applyIssuePhaseAndStatusTransition;
|
|
17
|
+
const fs_1 = __importDefault(require("fs"));
|
|
18
|
+
const path_1 = __importDefault(require("path"));
|
|
19
|
+
const REQUIRED_FRAIM_PHASE_LABELS = [
|
|
20
|
+
'phase:spec',
|
|
21
|
+
'phase:design',
|
|
22
|
+
'phase:tests',
|
|
23
|
+
'phase:impl'
|
|
24
|
+
];
|
|
25
|
+
const REQUIRED_FRAIM_STATUS_LABELS = [
|
|
26
|
+
'status:wip',
|
|
27
|
+
'status:needs-review',
|
|
28
|
+
'status:complete'
|
|
29
|
+
];
|
|
30
|
+
exports.LEGACY_FRAIM_STATUS_LABELS = [
|
|
31
|
+
'status:in-progress',
|
|
32
|
+
'status:design-review-passed'
|
|
33
|
+
];
|
|
34
|
+
function normalizeLabel(label) {
|
|
35
|
+
return label.trim().toLowerCase();
|
|
36
|
+
}
|
|
37
|
+
function findLabelsCatalogPath() {
|
|
38
|
+
let currentDir = __dirname;
|
|
39
|
+
for (let i = 0; i < 10; i++) {
|
|
40
|
+
const candidate = path_1.default.join(currentDir, 'labels.json');
|
|
41
|
+
if (fs_1.default.existsSync(candidate)) {
|
|
42
|
+
return candidate;
|
|
43
|
+
}
|
|
44
|
+
const parentDir = path_1.default.dirname(currentDir);
|
|
45
|
+
if (parentDir === currentDir) {
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
currentDir = parentDir;
|
|
49
|
+
}
|
|
50
|
+
const cwdCandidate = path_1.default.join(process.cwd(), 'labels.json');
|
|
51
|
+
if (fs_1.default.existsSync(cwdCandidate)) {
|
|
52
|
+
return cwdCandidate;
|
|
53
|
+
}
|
|
54
|
+
throw new Error('labels.json not found; FRAIM GitHub label helpers require the repo label catalog.');
|
|
55
|
+
}
|
|
56
|
+
function loadCatalogLabels() {
|
|
57
|
+
const catalogPath = findLabelsCatalogPath();
|
|
58
|
+
try {
|
|
59
|
+
const parsed = JSON.parse(fs_1.default.readFileSync(catalogPath, 'utf8'));
|
|
60
|
+
return new Set(parsed
|
|
61
|
+
.map((entry) => (typeof entry?.name === 'string' ? normalizeLabel(entry.name) : ''))
|
|
62
|
+
.filter(Boolean));
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
66
|
+
throw new Error(`Failed to read labels.json for FRAIM GitHub label helpers: ${message}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function requireCatalogLabels(expected, kind) {
|
|
70
|
+
const catalogLabels = loadCatalogLabels();
|
|
71
|
+
const missing = expected.filter((label) => !catalogLabels.has(label));
|
|
72
|
+
if (missing.length > 0) {
|
|
73
|
+
throw new Error(`labels.json is missing canonical FRAIM ${kind} labels: ${missing.join(', ')}`);
|
|
74
|
+
}
|
|
75
|
+
return expected;
|
|
76
|
+
}
|
|
77
|
+
exports.FRAIM_PHASE_LABELS = requireCatalogLabels(REQUIRED_FRAIM_PHASE_LABELS, 'phase');
|
|
78
|
+
exports.FRAIM_STATUS_LABELS = requireCatalogLabels(REQUIRED_FRAIM_STATUS_LABELS, 'status');
|
|
79
|
+
const FRAIM_PHASE_LABEL_SET = new Set(exports.FRAIM_PHASE_LABELS);
|
|
80
|
+
const FRAIM_STATUS_LABEL_SET = new Set(exports.FRAIM_STATUS_LABELS);
|
|
81
|
+
const LEGACY_FRAIM_STATUS_LABEL_SET = new Set(exports.LEGACY_FRAIM_STATUS_LABELS);
|
|
82
|
+
function dedupeLabels(labels) {
|
|
83
|
+
const seen = new Set();
|
|
84
|
+
const deduped = [];
|
|
85
|
+
for (const rawLabel of labels) {
|
|
86
|
+
const trimmed = rawLabel.trim();
|
|
87
|
+
if (!trimmed)
|
|
88
|
+
continue;
|
|
89
|
+
const normalized = normalizeLabel(trimmed);
|
|
90
|
+
if (seen.has(normalized))
|
|
91
|
+
continue;
|
|
92
|
+
seen.add(normalized);
|
|
93
|
+
deduped.push(trimmed);
|
|
94
|
+
}
|
|
95
|
+
return deduped;
|
|
96
|
+
}
|
|
97
|
+
function mapLegacyStatus(label) {
|
|
98
|
+
const normalized = normalizeLabel(label);
|
|
99
|
+
switch (normalized) {
|
|
100
|
+
case 'status:in-progress':
|
|
101
|
+
return 'status:wip';
|
|
102
|
+
case 'status:design-review-passed':
|
|
103
|
+
return 'status:complete';
|
|
104
|
+
default:
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function isFraimPhaseLabel(label) {
|
|
109
|
+
return FRAIM_PHASE_LABEL_SET.has(normalizeLabel(label));
|
|
110
|
+
}
|
|
111
|
+
function isFraimStatusLabel(label) {
|
|
112
|
+
return FRAIM_STATUS_LABEL_SET.has(normalizeLabel(label));
|
|
113
|
+
}
|
|
114
|
+
function isLegacyFraimStatusLabel(label) {
|
|
115
|
+
return LEGACY_FRAIM_STATUS_LABEL_SET.has(normalizeLabel(label));
|
|
116
|
+
}
|
|
117
|
+
function normalizeFraimPhaseLabel(label) {
|
|
118
|
+
const normalized = normalizeLabel(label);
|
|
119
|
+
if (!FRAIM_PHASE_LABEL_SET.has(normalized)) {
|
|
120
|
+
throw new Error(`Unsupported FRAIM phase label: ${label}`);
|
|
121
|
+
}
|
|
122
|
+
return normalized;
|
|
123
|
+
}
|
|
124
|
+
function normalizeFraimStatusLabel(label) {
|
|
125
|
+
const normalized = normalizeLabel(label);
|
|
126
|
+
if (FRAIM_STATUS_LABEL_SET.has(normalized)) {
|
|
127
|
+
return normalized;
|
|
128
|
+
}
|
|
129
|
+
const mappedLegacy = mapLegacyStatus(normalized);
|
|
130
|
+
if (mappedLegacy) {
|
|
131
|
+
return mappedLegacy;
|
|
132
|
+
}
|
|
133
|
+
throw new Error(`Unsupported FRAIM status label: ${label}`);
|
|
134
|
+
}
|
|
135
|
+
function partitionFraimLabels(labels) {
|
|
136
|
+
const nonFraimLabels = [];
|
|
137
|
+
const phaseLabels = [];
|
|
138
|
+
const statusLabels = [];
|
|
139
|
+
for (const label of dedupeLabels(labels)) {
|
|
140
|
+
if (isFraimPhaseLabel(label)) {
|
|
141
|
+
phaseLabels.push(normalizeFraimPhaseLabel(label));
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (isFraimStatusLabel(label) || isLegacyFraimStatusLabel(label)) {
|
|
145
|
+
statusLabels.push(normalizeFraimStatusLabel(label));
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
nonFraimLabels.push(label);
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
nonFraimLabels,
|
|
152
|
+
phaseLabels,
|
|
153
|
+
statusLabels
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
function mergeFraimGitHubLabels(labels, updates) {
|
|
157
|
+
const { nonFraimLabels, phaseLabels, statusLabels } = partitionFraimLabels(labels);
|
|
158
|
+
const nextPhase = updates.phase
|
|
159
|
+
? normalizeFraimPhaseLabel(updates.phase)
|
|
160
|
+
: phaseLabels[0];
|
|
161
|
+
const nextStatus = updates.status
|
|
162
|
+
? normalizeFraimStatusLabel(updates.status)
|
|
163
|
+
: statusLabels[0];
|
|
164
|
+
const merged = [...nonFraimLabels];
|
|
165
|
+
if (nextPhase)
|
|
166
|
+
merged.push(nextPhase);
|
|
167
|
+
if (nextStatus)
|
|
168
|
+
merged.push(nextStatus);
|
|
169
|
+
return dedupeLabels(merged);
|
|
170
|
+
}
|
|
171
|
+
function applyIssueStatusTransition(labels, status) {
|
|
172
|
+
return mergeFraimGitHubLabels(labels, { status });
|
|
173
|
+
}
|
|
174
|
+
function applyIssuePhaseTransition(labels, phase) {
|
|
175
|
+
return mergeFraimGitHubLabels(labels, {
|
|
176
|
+
phase,
|
|
177
|
+
status: 'status:wip'
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
function applyIssuePhaseAndStatusTransition(labels, phase, status) {
|
|
181
|
+
return mergeFraimGitHubLabels(labels, { phase, status });
|
|
182
|
+
}
|
|
@@ -32,11 +32,32 @@ function isExplicitLocalRepoPath(repoUrl) {
|
|
|
32
32
|
trimmed.startsWith('../') ||
|
|
33
33
|
trimmed.startsWith('file://'));
|
|
34
34
|
}
|
|
35
|
+
function readExplicitPortFromEnv() {
|
|
36
|
+
for (const value of [
|
|
37
|
+
process.env.PLAYWRIGHT_FRAIM_MCP_PORT,
|
|
38
|
+
process.env.PLAYWRIGHT_WEBSITE_PORT,
|
|
39
|
+
process.env.FRAIM_MCP_PORT,
|
|
40
|
+
process.env.PORT,
|
|
41
|
+
process.env.WEBSITES_PORT
|
|
42
|
+
]) {
|
|
43
|
+
const port = Number(value);
|
|
44
|
+
if (Number.isFinite(port) && port > 0) {
|
|
45
|
+
return port;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
35
50
|
/**
|
|
36
51
|
* Gets a unique port based on the current git branch name (if it's an issue branch)
|
|
37
52
|
* Default to 15302 if not on an issue branch
|
|
38
53
|
*/
|
|
39
54
|
function getPort() {
|
|
55
|
+
// Explicit runtime ports must win so tests and subprocesses all resolve the
|
|
56
|
+
// same active server instead of drifting back to a branch-derived default.
|
|
57
|
+
const explicitPort = readExplicitPortFromEnv();
|
|
58
|
+
if (explicitPort) {
|
|
59
|
+
return explicitPort;
|
|
60
|
+
}
|
|
40
61
|
try {
|
|
41
62
|
const branchName = process.env.FRAIM_BRANCH || (0, child_process_1.execSync)('git rev-parse --abbrev-ref HEAD').toString().trim();
|
|
42
63
|
// Match issue-123 or 123-feature-name or feature/123-name
|
|
@@ -50,7 +71,7 @@ function getPort() {
|
|
|
50
71
|
catch (e) {
|
|
51
72
|
// Silently fail and use default
|
|
52
73
|
}
|
|
53
|
-
return
|
|
74
|
+
return 15302;
|
|
54
75
|
}
|
|
55
76
|
/**
|
|
56
77
|
* Determines the database name based on the git branch
|