oxe-cc 1.7.0 → 1.8.3
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/CHANGELOG.md +106 -0
- package/README.md +37 -37
- package/bin/lib/oxe-agent-install.cjs +24 -8
- package/bin/lib/oxe-manifest.cjs +20 -13
- package/bin/lib/oxe-operational.cjs +234 -41
- package/bin/lib/oxe-project-health.cjs +219 -52
- package/bin/lib/oxe-rationality.cjs +9 -7
- package/bin/oxe-cc.js +443 -236
- package/lib/runtime/compiler/graph-compiler.js +1 -1
- package/lib/runtime/executor/action-tool-map.js +4 -0
- package/lib/runtime/executor/built-in-tools.js +27 -0
- package/lib/runtime/executor/llm-task-executor.d.ts +4 -1
- package/lib/runtime/executor/llm-task-executor.js +41 -5
- package/lib/runtime/executor/node-prompt-builder.d.ts +4 -1
- package/lib/runtime/executor/node-prompt-builder.js +13 -2
- package/lib/runtime/models/failure.d.ts +1 -1
- package/lib/runtime/scheduler/scheduler.d.ts +5 -1
- package/lib/runtime/scheduler/scheduler.js +82 -14
- package/lib/runtime/verification/verification-compiler.js +7 -5
- package/lib/sdk/index.cjs +48 -44
- package/oxe/templates/PLAN.template.md +23 -9
- package/oxe/templates/SPEC.template.md +55 -22
- package/oxe/workflows/plan.md +18 -6
- package/oxe/workflows/spec.md +31 -9
- package/package.json +103 -100
- package/packages/runtime/package.json +14 -14
- package/packages/runtime/src/compiler/graph-compiler.ts +1 -1
- package/packages/runtime/src/evidence/evidence-store.ts +2 -2
- package/packages/runtime/src/executor/action-tool-map.ts +4 -0
- package/packages/runtime/src/executor/built-in-tools.ts +29 -0
- package/packages/runtime/src/executor/llm-task-executor.ts +46 -4
- package/packages/runtime/src/executor/node-prompt-builder.ts +18 -1
- package/packages/runtime/src/models/failure.ts +2 -0
- package/packages/runtime/src/scheduler/scheduler.ts +93 -15
- package/packages/runtime/src/verification/verification-compiler.ts +7 -5
- package/vscode-extension/package.json +184 -184
- package/vscode-extension/oxe-agents-0.9.1.vsix +0 -0
- package/vscode-extension/oxe-agents-0.9.2.vsix +0 -0
- package/vscode-extension/oxe-agents-1.0.0.vsix +0 -0
- package/vscode-extension/oxe-agents-1.4.0.vsix +0 -0
- package/vscode-extension/oxe-agents-1.5.0.vsix +0 -0
- package/vscode-extension/oxe-agents-1.5.1.vsix +0 -0
- package/vscode-extension/oxe-agents-1.6.0.vsix +0 -0
- package/vscode-extension/oxe-agents-1.7.0.vsix +0 -0
|
@@ -809,19 +809,52 @@ function copilotLegacyHome() {
|
|
|
809
809
|
return path.join(os.homedir(), '.copilot');
|
|
810
810
|
}
|
|
811
811
|
|
|
812
|
-
function copilotLegacyPaths() {
|
|
813
|
-
const root = copilotLegacyHome();
|
|
814
|
-
return {
|
|
815
|
-
root,
|
|
816
|
-
promptsDir: path.join(root, 'prompts'),
|
|
817
|
-
instructions: path.join(root, 'copilot-instructions.md'),
|
|
818
|
-
};
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
812
|
+
function copilotLegacyPaths() {
|
|
813
|
+
const root = copilotLegacyHome();
|
|
814
|
+
return {
|
|
815
|
+
root,
|
|
816
|
+
promptsDir: path.join(root, 'prompts'),
|
|
817
|
+
instructions: path.join(root, 'copilot-instructions.md'),
|
|
818
|
+
};
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
function expandHomePath(value) {
|
|
822
|
+
if (typeof value !== 'string') return value;
|
|
823
|
+
if (value === '~') return os.homedir();
|
|
824
|
+
if (value.startsWith('~/') || value.startsWith('~\\')) return path.join(os.homedir(), value.slice(2));
|
|
825
|
+
return value;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
function codexHome() {
|
|
829
|
+
if (process.env.CODEX_HOME) return path.resolve(expandHomePath(process.env.CODEX_HOME));
|
|
830
|
+
return path.join(os.homedir(), '.codex');
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
function codexIntegrationPaths() {
|
|
834
|
+
const root = codexHome();
|
|
835
|
+
return {
|
|
836
|
+
root,
|
|
837
|
+
promptsDir: path.join(root, 'prompts'),
|
|
838
|
+
skillsRoot: path.join(os.homedir(), '.agents', 'skills'),
|
|
839
|
+
};
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
/**
|
|
843
|
+
* @param {string} target
|
|
844
|
+
*/
|
|
845
|
+
function codexWorkspacePaths(target) {
|
|
846
|
+
const root = path.resolve(target);
|
|
847
|
+
return {
|
|
848
|
+
root,
|
|
849
|
+
promptsDir: path.join(root, '.codex', 'prompts'),
|
|
850
|
+
skillsRoot: path.join(root, '.agents', 'skills'),
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
/**
|
|
855
|
+
* @param {string} filePath
|
|
856
|
+
*/
|
|
857
|
+
function readJsonFileSafe(filePath) {
|
|
825
858
|
if (!fs.existsSync(filePath)) return { ok: false, data: null, error: null };
|
|
826
859
|
try {
|
|
827
860
|
return { ok: true, data: JSON.parse(fs.readFileSync(filePath, 'utf8')), error: null };
|
|
@@ -1087,11 +1120,21 @@ function summarizeRecoveryState(target, activeSession, activeRun, verificationAr
|
|
|
1087
1120
|
};
|
|
1088
1121
|
}
|
|
1089
1122
|
|
|
1090
|
-
function summarizeEnterpriseRuntime(target, activeRun, activeSession, config) {
|
|
1091
|
-
const pendingGates = readExecutionGates(target, activeSession);
|
|
1092
|
-
const auditSummary = summarizeAuditTrail(target, activeRun && activeRun.run_id ? activeRun.run_id : null);
|
|
1093
|
-
const
|
|
1094
|
-
const providerCatalog = operational.buildRuntimeProviderCatalog(target);
|
|
1123
|
+
function summarizeEnterpriseRuntime(target, activeRun, activeSession, config) {
|
|
1124
|
+
const pendingGates = readExecutionGates(target, activeSession);
|
|
1125
|
+
const auditSummary = summarizeAuditTrail(target, activeRun && activeRun.run_id ? activeRun.run_id : null);
|
|
1126
|
+
const runtimeModeBase = operational.buildRuntimeModeStatus(activeRun);
|
|
1127
|
+
const providerCatalog = operational.buildRuntimeProviderCatalog(target);
|
|
1128
|
+
const enterprisePackageAvailable = Boolean(providerCatalog && providerCatalog.available);
|
|
1129
|
+
const runtimeMode = enterprisePackageAvailable && runtimeModeBase.enterprise_available === false
|
|
1130
|
+
? {
|
|
1131
|
+
...runtimeModeBase,
|
|
1132
|
+
enterprise_available: true,
|
|
1133
|
+
reason: runtimeModeBase.reason === 'Nenhum ACTIVE-RUN encontrado para o escopo atual.'
|
|
1134
|
+
? 'Runtime enterprise disponível no pacote, mas ainda sem ACTIVE-RUN canónico neste escopo.'
|
|
1135
|
+
: 'Runtime enterprise disponível no pacote; a run atual ainda não materializou artefatos canónicos.',
|
|
1136
|
+
}
|
|
1137
|
+
: runtimeModeBase;
|
|
1095
1138
|
if (!activeRun || !activeRun.run_id) {
|
|
1096
1139
|
return {
|
|
1097
1140
|
runtimeMode,
|
|
@@ -1270,19 +1313,45 @@ function summarizeEnterpriseRuntime(target, activeRun, activeSession, config) {
|
|
|
1270
1313
|
* @param {string} dir
|
|
1271
1314
|
* @returns {string[]}
|
|
1272
1315
|
*/
|
|
1273
|
-
function listOxePromptFiles(dir) {
|
|
1274
|
-
if (!fs.existsSync(dir)) return [];
|
|
1275
|
-
return fs
|
|
1276
|
-
.readdirSync(dir, { withFileTypes: true })
|
|
1277
|
-
.filter((entry) => entry.isFile() && /^oxe-.*\.prompt\.md$/i.test(entry.name))
|
|
1316
|
+
function listOxePromptFiles(dir) {
|
|
1317
|
+
if (!fs.existsSync(dir)) return [];
|
|
1318
|
+
return fs
|
|
1319
|
+
.readdirSync(dir, { withFileTypes: true })
|
|
1320
|
+
.filter((entry) => entry.isFile() && /^oxe-.*\.prompt\.md$/i.test(entry.name))
|
|
1278
1321
|
.map((entry) => path.join(dir, entry.name))
|
|
1279
|
-
.sort();
|
|
1280
|
-
}
|
|
1281
|
-
|
|
1282
|
-
/**
|
|
1283
|
-
* @param {string}
|
|
1284
|
-
* @returns {
|
|
1285
|
-
*/
|
|
1322
|
+
.sort();
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
/**
|
|
1326
|
+
* @param {string} dir
|
|
1327
|
+
* @returns {string[]}
|
|
1328
|
+
*/
|
|
1329
|
+
function listOxeCodexPromptFiles(dir) {
|
|
1330
|
+
if (!fs.existsSync(dir)) return [];
|
|
1331
|
+
return fs
|
|
1332
|
+
.readdirSync(dir, { withFileTypes: true })
|
|
1333
|
+
.filter((entry) => entry.isFile() && (entry.name === 'oxe.md' || /^oxe-.*\.md$/i.test(entry.name)))
|
|
1334
|
+
.map((entry) => path.join(dir, entry.name))
|
|
1335
|
+
.sort();
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
/**
|
|
1339
|
+
* @param {string} root
|
|
1340
|
+
* @returns {string[]}
|
|
1341
|
+
*/
|
|
1342
|
+
function listOxeSkillDirs(root) {
|
|
1343
|
+
if (!fs.existsSync(root)) return [];
|
|
1344
|
+
return fs
|
|
1345
|
+
.readdirSync(root, { withFileTypes: true })
|
|
1346
|
+
.filter((entry) => entry.isDirectory() && /^oxe($|-)/i.test(entry.name) && fs.existsSync(path.join(root, entry.name, 'SKILL.md')))
|
|
1347
|
+
.map((entry) => path.join(root, entry.name))
|
|
1348
|
+
.sort();
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
/**
|
|
1352
|
+
* @param {string} filePath
|
|
1353
|
+
* @returns {boolean}
|
|
1354
|
+
*/
|
|
1286
1355
|
function hasOxeInstructionBlock(filePath) {
|
|
1287
1356
|
if (!fs.existsSync(filePath)) return false;
|
|
1288
1357
|
const text = fs.readFileSync(filePath, 'utf8');
|
|
@@ -1430,13 +1499,95 @@ function copilotIntegrationReport(target) {
|
|
|
1430
1499
|
},
|
|
1431
1500
|
manifest,
|
|
1432
1501
|
warnings,
|
|
1433
|
-
};
|
|
1434
|
-
}
|
|
1435
|
-
|
|
1436
|
-
/**
|
|
1437
|
-
*
|
|
1438
|
-
|
|
1439
|
-
|
|
1502
|
+
};
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
/**
|
|
1506
|
+
* @param {string} target
|
|
1507
|
+
*/
|
|
1508
|
+
function codexIntegrationReport(target) {
|
|
1509
|
+
const workspace = codexWorkspacePaths(target);
|
|
1510
|
+
const globalPaths = codexIntegrationPaths();
|
|
1511
|
+
const workspacePromptFiles = listOxeCodexPromptFiles(workspace.promptsDir);
|
|
1512
|
+
const workspaceSkillDirs = listOxeSkillDirs(workspace.skillsRoot);
|
|
1513
|
+
const globalPromptFiles = listOxeCodexPromptFiles(globalPaths.promptsDir);
|
|
1514
|
+
const globalSkillDirs = listOxeSkillDirs(globalPaths.skillsRoot);
|
|
1515
|
+
const workspaceDetected = workspacePromptFiles.length > 0 || workspaceSkillDirs.length > 0;
|
|
1516
|
+
const globalDetected = globalPromptFiles.length > 0 || globalSkillDirs.length > 0;
|
|
1517
|
+
const promptFiles = workspaceDetected ? workspacePromptFiles : globalPromptFiles;
|
|
1518
|
+
const skillDirs = workspaceDetected ? workspaceSkillDirs : globalSkillDirs;
|
|
1519
|
+
const promptNames = promptFiles.map((filePath) => path.basename(filePath));
|
|
1520
|
+
const skillNames = skillDirs.map((dirPath) => path.basename(dirPath));
|
|
1521
|
+
const promptPathWarnings = [];
|
|
1522
|
+
for (const filePath of promptFiles) {
|
|
1523
|
+
for (const warning of promptWorkflowPathWarnings(filePath, target)) promptPathWarnings.push(warning);
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
/** @type {string[]} */
|
|
1527
|
+
const warnings = [];
|
|
1528
|
+
const detected = workspaceDetected || globalDetected;
|
|
1529
|
+
const promptSource = workspaceDetected ? 'workspace' : globalDetected ? 'global' : 'missing';
|
|
1530
|
+
const commandsReady = promptNames.includes('oxe.md');
|
|
1531
|
+
const skillsReady = skillNames.includes('oxe');
|
|
1532
|
+
if (!workspaceDetected && globalDetected) {
|
|
1533
|
+
warnings.push('Codex OXE foi encontrado apenas no ambiente global do usuário; este projeto não tem integração local instalada.');
|
|
1534
|
+
}
|
|
1535
|
+
if (detected && promptFiles.length === 0) {
|
|
1536
|
+
warnings.push('Codex tem skills OXE instaladas, mas o diretório de prompts ativo não contém prompts OXE; a barra / não listará /oxe.');
|
|
1537
|
+
}
|
|
1538
|
+
if (promptFiles.length > 0 && !commandsReady) {
|
|
1539
|
+
warnings.push('Codex prompts OXE existem, mas o entrypoint principal oxe.md está ausente.');
|
|
1540
|
+
}
|
|
1541
|
+
if (skillDirs.length > 0 && !skillsReady) {
|
|
1542
|
+
warnings.push('Codex skills OXE existem, mas o skill raiz oxe está ausente.');
|
|
1543
|
+
}
|
|
1544
|
+
if (promptFiles.length > 0 && skillDirs.length === 0) {
|
|
1545
|
+
warnings.push('Codex prompts OXE existem, mas ~/.agents/skills não contém skills OXE; recursos especializados podem não aparecer.');
|
|
1546
|
+
}
|
|
1547
|
+
for (const warning of promptPathWarnings) warnings.push(warning);
|
|
1548
|
+
|
|
1549
|
+
let status = 'not_installed';
|
|
1550
|
+
if (commandsReady && skillsReady && promptPathWarnings.length === 0) {
|
|
1551
|
+
status = warnings.length ? 'warning' : 'healthy';
|
|
1552
|
+
} else if (detected) {
|
|
1553
|
+
status = commandsReady ? 'warning' : 'broken';
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
return {
|
|
1557
|
+
status,
|
|
1558
|
+
detected,
|
|
1559
|
+
commandsReady,
|
|
1560
|
+
skillsReady,
|
|
1561
|
+
promptSource,
|
|
1562
|
+
root: workspaceDetected ? workspace.root : globalPaths.root,
|
|
1563
|
+
promptsDir: workspaceDetected ? workspace.promptsDir : globalPaths.promptsDir,
|
|
1564
|
+
skillsRoot: workspaceDetected ? workspace.skillsRoot : globalPaths.skillsRoot,
|
|
1565
|
+
promptFiles,
|
|
1566
|
+
skillDirs,
|
|
1567
|
+
workspace: {
|
|
1568
|
+
root: workspace.root,
|
|
1569
|
+
promptsDir: workspace.promptsDir,
|
|
1570
|
+
skillsRoot: workspace.skillsRoot,
|
|
1571
|
+
promptFiles: workspacePromptFiles,
|
|
1572
|
+
skillDirs: workspaceSkillDirs,
|
|
1573
|
+
detected: workspaceDetected,
|
|
1574
|
+
},
|
|
1575
|
+
global: {
|
|
1576
|
+
root: globalPaths.root,
|
|
1577
|
+
promptsDir: globalPaths.promptsDir,
|
|
1578
|
+
skillsRoot: globalPaths.skillsRoot,
|
|
1579
|
+
promptFiles: globalPromptFiles,
|
|
1580
|
+
skillDirs: globalSkillDirs,
|
|
1581
|
+
detected: globalDetected,
|
|
1582
|
+
},
|
|
1583
|
+
warnings,
|
|
1584
|
+
};
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
/**
|
|
1588
|
+
* Valida o arquivo plan-agents.json (se existir) e retorna avisos.
|
|
1589
|
+
* @param {string} target
|
|
1590
|
+
* @returns {string[]}
|
|
1440
1591
|
*/
|
|
1441
1592
|
function planAgentsWarnings(target) {
|
|
1442
1593
|
const p = oxePaths(target);
|
|
@@ -1925,12 +2076,21 @@ function suggestNextStep(target, cfg = {}) {
|
|
|
1925
2076
|
};
|
|
1926
2077
|
}
|
|
1927
2078
|
|
|
2079
|
+
if (!mapsComplete && !has(p.quick) && !has(p.spec) && !has(p.plan)) {
|
|
2080
|
+
return {
|
|
2081
|
+
step: 'oxe',
|
|
2082
|
+
cursorCmd: '/oxe',
|
|
2083
|
+
reason: 'Projeto recém-inicializado e sem SPEC/PLAN — use a entrada universal para começar o fluxo e escolher o primeiro scan.',
|
|
2084
|
+
artifacts: ['.oxe/STATE.md', '.oxe/codebase/'],
|
|
2085
|
+
};
|
|
2086
|
+
}
|
|
2087
|
+
|
|
1928
2088
|
if (!mapsComplete && !has(p.quick)) {
|
|
1929
|
-
return {
|
|
1930
|
-
step: 'scan',
|
|
1931
|
-
cursorCmd: '/oxe-scan',
|
|
1932
|
-
reason: 'Mapas do codebase incompletos e sem QUICK.md — atualize o contexto com scan',
|
|
1933
|
-
artifacts: ['.oxe/codebase/'],
|
|
2089
|
+
return {
|
|
2090
|
+
step: 'scan',
|
|
2091
|
+
cursorCmd: '/oxe-scan',
|
|
2092
|
+
reason: 'Mapas do codebase incompletos e sem QUICK.md — atualize o contexto com scan',
|
|
2093
|
+
artifacts: ['.oxe/codebase/'],
|
|
1934
2094
|
};
|
|
1935
2095
|
}
|
|
1936
2096
|
|
|
@@ -2221,10 +2381,12 @@ function buildHealthReport(target) {
|
|
|
2221
2381
|
...planAgentsWarnings(target),
|
|
2222
2382
|
];
|
|
2223
2383
|
const planWarn = suppressExecutionWorkspaceGates ? [] : executionPlanWarn;
|
|
2224
|
-
const sessionWarn = sessionWarnings(target, activeSession);
|
|
2384
|
+
const sessionWarn = sessionWarnings(target, activeSession);
|
|
2225
2385
|
const installWarn = installationCompletenessWarnings(target);
|
|
2226
2386
|
const copilot = copilotIntegrationReport(target);
|
|
2227
2387
|
const copilotWarn = copilot.warnings;
|
|
2388
|
+
const codex = codexIntegrationReport(target);
|
|
2389
|
+
const codexWarn = codex.warnings;
|
|
2228
2390
|
const reviewWarn = suppressExecutionWorkspaceGates ? [] : planReviewWarnings(stateText, p);
|
|
2229
2391
|
const planSelfEvaluation = {
|
|
2230
2392
|
...parsedPlanSelfEvaluation,
|
|
@@ -2418,10 +2580,11 @@ function buildHealthReport(target) {
|
|
|
2418
2580
|
planWarningCount +
|
|
2419
2581
|
capabilityWarn.length +
|
|
2420
2582
|
investigationWarn.length +
|
|
2421
|
-
sessionWarn.length +
|
|
2422
|
-
installWarn.length +
|
|
2423
|
-
copilotWarn.length +
|
|
2424
|
-
|
|
2583
|
+
sessionWarn.length +
|
|
2584
|
+
installWarn.length +
|
|
2585
|
+
copilotWarn.length +
|
|
2586
|
+
codexWarn.length +
|
|
2587
|
+
contextWarn.length +
|
|
2425
2588
|
semanticsWarn.length +
|
|
2426
2589
|
(azureReport ? azureReport.warnings.length : 0) +
|
|
2427
2590
|
(sumWarn ? 1 : 0);
|
|
@@ -2450,9 +2613,11 @@ function buildHealthReport(target) {
|
|
|
2450
2613
|
installWarn,
|
|
2451
2614
|
copilotWarn,
|
|
2452
2615
|
contextWarn,
|
|
2453
|
-
semanticsWarn,
|
|
2454
|
-
copilot,
|
|
2455
|
-
|
|
2616
|
+
semanticsWarn,
|
|
2617
|
+
copilot,
|
|
2618
|
+
codexWarn,
|
|
2619
|
+
codex,
|
|
2620
|
+
summaryGapWarn: sumWarn,
|
|
2456
2621
|
specWarn,
|
|
2457
2622
|
planWarn,
|
|
2458
2623
|
planSelfEvaluation,
|
|
@@ -2532,9 +2697,11 @@ module.exports = {
|
|
|
2532
2697
|
parsePlanReviewStatus,
|
|
2533
2698
|
isStaleScan,
|
|
2534
2699
|
isStaleLessons,
|
|
2535
|
-
copilotWorkspacePaths,
|
|
2536
|
-
copilotLegacyPaths,
|
|
2700
|
+
copilotWorkspacePaths,
|
|
2701
|
+
copilotLegacyPaths,
|
|
2537
2702
|
copilotIntegrationReport,
|
|
2703
|
+
codexIntegrationPaths,
|
|
2704
|
+
codexIntegrationReport,
|
|
2538
2705
|
normalizePlanConfidenceThreshold,
|
|
2539
2706
|
isExecutablePlanConfidence,
|
|
2540
2707
|
hasExecutablePlanSelfEvaluation,
|
|
@@ -350,14 +350,16 @@ function buildExecutionRationality(paths = {}) {
|
|
|
350
350
|
const implementationPack = summarizeImplementationPack(paths.implementationPackJson || null, planTasks);
|
|
351
351
|
const referenceAnchors = summarizeReferenceAnchors(paths.referenceAnchors || null, externalRefs);
|
|
352
352
|
const fixturePack = summarizeFixturePack(paths.fixturePackJson || null, planTasks, implementationPack);
|
|
353
|
-
const criticalExecutionGaps = Array.from(
|
|
354
|
-
new Set([
|
|
355
|
-
...implementationPack.criticalGaps,
|
|
356
|
-
...referenceAnchors.criticalGaps,
|
|
357
|
-
...fixturePack.criticalGaps,
|
|
358
|
-
])
|
|
359
|
-
);
|
|
360
353
|
const applicable = Boolean(paths.plan && fs.existsSync(paths.plan));
|
|
354
|
+
const criticalExecutionGaps = applicable
|
|
355
|
+
? Array.from(
|
|
356
|
+
new Set([
|
|
357
|
+
...implementationPack.criticalGaps,
|
|
358
|
+
...referenceAnchors.criticalGaps,
|
|
359
|
+
...fixturePack.criticalGaps,
|
|
360
|
+
])
|
|
361
|
+
)
|
|
362
|
+
: [];
|
|
361
363
|
return {
|
|
362
364
|
applicable,
|
|
363
365
|
planTaskCount: planTasks.length,
|