oxe-cc 0.6.4 → 0.6.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/.cursor/commands/oxe-ask.md +11 -0
  2. package/.cursor/commands/oxe-execute.md +1 -1
  3. package/.cursor/commands/oxe-session.md +11 -0
  4. package/.github/prompts/oxe-ask.prompt.md +12 -0
  5. package/.github/prompts/oxe-execute.prompt.md +1 -1
  6. package/.github/prompts/oxe-session.prompt.md +12 -0
  7. package/README.md +363 -323
  8. package/bin/banner.txt +1 -1
  9. package/bin/lib/oxe-project-health.cjs +284 -91
  10. package/bin/oxe-cc.js +305 -123
  11. package/commands/oxe/ask.md +14 -0
  12. package/commands/oxe/session.md +16 -0
  13. package/oxe/templates/CONFIG.md +3 -2
  14. package/oxe/templates/PLAN.template.md +22 -7
  15. package/oxe/templates/SESSION.template.md +32 -0
  16. package/oxe/templates/STATE.md +10 -5
  17. package/oxe/templates/config.template.json +3 -2
  18. package/oxe/workflows/ask.md +62 -0
  19. package/oxe/workflows/checkpoint.md +10 -9
  20. package/oxe/workflows/debug.md +6 -5
  21. package/oxe/workflows/discuss.md +8 -7
  22. package/oxe/workflows/execute.md +37 -28
  23. package/oxe/workflows/forensics.md +6 -6
  24. package/oxe/workflows/help.md +39 -19
  25. package/oxe/workflows/milestone.md +12 -13
  26. package/oxe/workflows/next.md +16 -13
  27. package/oxe/workflows/obs.md +9 -8
  28. package/oxe/workflows/plan-agent.md +6 -4
  29. package/oxe/workflows/plan.md +73 -32
  30. package/oxe/workflows/project.md +1 -1
  31. package/oxe/workflows/quick.md +11 -7
  32. package/oxe/workflows/references/flow-robustness-contract.md +80 -0
  33. package/oxe/workflows/references/session-path-resolution.md +71 -0
  34. package/oxe/workflows/research.md +9 -8
  35. package/oxe/workflows/security.md +7 -6
  36. package/oxe/workflows/session.md +153 -0
  37. package/oxe/workflows/spec.md +41 -27
  38. package/oxe/workflows/ui-review.md +3 -3
  39. package/oxe/workflows/ui-spec.md +3 -3
  40. package/oxe/workflows/validate-gaps.md +5 -4
  41. package/oxe/workflows/verify.md +37 -21
  42. package/oxe/workflows/workstream.md +16 -15
  43. package/package.json +1 -1
package/bin/banner.txt CHANGED
@@ -15,4 +15,4 @@
15
15
 
16
16
  ══════════════════════════════════════════════════════
17
17
 
18
- v0.6.4
18
+ v0.6.5
@@ -17,9 +17,10 @@ const ALLOWED_CONFIG_KEYS = [
17
17
  'scan_ignore_globs',
18
18
  'spec_required_sections',
19
19
  'plan_max_tasks_per_wave',
20
- 'profile',
21
- 'verification_depth',
22
- 'security_in_verify',
20
+ 'profile',
21
+ 'verification_depth',
22
+ 'plan_confidence_threshold',
23
+ 'security_in_verify',
23
24
  'install',
24
25
  'plugins',
25
26
  'workstreams',
@@ -116,9 +117,10 @@ function loadOxeConfigMerged(targetProject) {
116
117
  discuss_before_plan: false,
117
118
  after_verify_suggest_pr: true,
118
119
  after_verify_draft_commit: true,
119
- after_verify_suggest_uat: false,
120
- verification_depth: 'standard',
121
- default_verify_command: '',
120
+ after_verify_suggest_uat: false,
121
+ verification_depth: 'standard',
122
+ plan_confidence_threshold: 70,
123
+ default_verify_command: '',
122
124
  scan_max_age_days: 0,
123
125
  compact_max_age_days: 0,
124
126
  scan_focus_globs: [],
@@ -211,13 +213,16 @@ function validateConfigShape(cfg) {
211
213
  typeErrors.push(`profile deve ser um de: ${EXECUTION_PROFILES.join(', ')}`);
212
214
  }
213
215
  }
214
- if (cfg.verification_depth != null) {
215
- if (typeof cfg.verification_depth !== 'string') {
216
- typeErrors.push('verification_depth deve ser string');
217
- } else if (!VERIFICATION_DEPTHS.includes(cfg.verification_depth)) {
218
- typeErrors.push(`verification_depth deve ser um de: ${VERIFICATION_DEPTHS.join(', ')}`);
219
- }
220
- }
216
+ if (cfg.verification_depth != null) {
217
+ if (typeof cfg.verification_depth !== 'string') {
218
+ typeErrors.push('verification_depth deve ser string');
219
+ } else if (!VERIFICATION_DEPTHS.includes(cfg.verification_depth)) {
220
+ typeErrors.push(`verification_depth deve ser um de: ${VERIFICATION_DEPTHS.join(', ')}`);
221
+ }
222
+ }
223
+ if (cfg.plan_confidence_threshold != null && typeof cfg.plan_confidence_threshold !== 'number') {
224
+ typeErrors.push('plan_confidence_threshold deve ser número (percentual de 0 a 100)');
225
+ }
221
226
  if (cfg.after_verify_suggest_uat != null && typeof cfg.after_verify_suggest_uat !== 'boolean') {
222
227
  typeErrors.push('after_verify_suggest_uat deve ser boolean');
223
228
  }
@@ -286,12 +291,25 @@ function parseLastCompactDate(stateText) {
286
291
  * @param {string} stateText
287
292
  * @returns {Date | null}
288
293
  */
289
- function parseLastRetroDate(stateText) {
290
- const m = stateText.match(/\blast_retro\s*:\s*(\d{4}-\d{2}-\d{2})/i);
291
- if (!m) return null;
292
- const iso = Date.parse(m[1]);
293
- return Number.isNaN(iso) ? null : new Date(iso);
294
- }
294
+ function parseLastRetroDate(stateText) {
295
+ const m = stateText.match(/\blast_retro\s*:\s*(\d{4}-\d{2}-\d{2})/i);
296
+ if (!m) return null;
297
+ const iso = Date.parse(m[1]);
298
+ return Number.isNaN(iso) ? null : new Date(iso);
299
+ }
300
+
301
+ /**
302
+ * @param {string} stateText
303
+ * @returns {string | null}
304
+ */
305
+ function parseActiveSession(stateText) {
306
+ if (!stateText) return null;
307
+ const m = stateText.match(/\*\*active_session:\*\*\s*`?([^\n`]+?)`?\s*(?:\n|$)/i);
308
+ if (!m) return null;
309
+ const raw = m[1].trim();
310
+ if (!raw || raw === '—' || /^none$/i.test(raw)) return null;
311
+ return raw.replace(/\\/g, '/');
312
+ }
295
313
 
296
314
  /**
297
315
  * @param {Date | null} scanDate
@@ -316,12 +334,18 @@ function isStaleLessons(retroDate, maxAgeDays) {
316
334
  /**
317
335
  * @param {string} target
318
336
  */
319
- function oxePaths(target) {
320
- const oxe = path.join(target, '.oxe');
321
- return {
322
- oxe,
323
- state: path.join(oxe, 'STATE.md'),
324
- spec: path.join(oxe, 'SPEC.md'),
337
+ function oxePaths(target) {
338
+ const oxe = path.join(target, '.oxe');
339
+ return {
340
+ oxe,
341
+ state: path.join(oxe, 'STATE.md'),
342
+ sessionsIndex: path.join(oxe, 'SESSIONS.md'),
343
+ globalDir: path.join(oxe, 'global'),
344
+ globalLessons: path.join(oxe, 'global', 'LESSONS.md'),
345
+ globalMilestones: path.join(oxe, 'global', 'MILESTONES.md'),
346
+ globalMilestonesDir: path.join(oxe, 'global', 'milestones'),
347
+ sessionsDir: path.join(oxe, 'sessions'),
348
+ spec: path.join(oxe, 'SPEC.md'),
325
349
  plan: path.join(oxe, 'PLAN.md'),
326
350
  quick: path.join(oxe, 'QUICK.md'),
327
351
  verify: path.join(oxe, 'VERIFY.md'),
@@ -330,8 +354,32 @@ function oxePaths(target) {
330
354
  codebase: path.join(oxe, 'codebase'),
331
355
  lessons: path.join(oxe, 'LESSONS.md'),
332
356
  planAgents: path.join(oxe, 'plan-agents.json'),
333
- };
334
- }
357
+ };
358
+ }
359
+
360
+ /**
361
+ * @param {string} target
362
+ * @param {string | null} activeSession
363
+ */
364
+ function scopedOxePaths(target, activeSession) {
365
+ const base = oxePaths(target);
366
+ if (!activeSession) return { ...base, activeSession: null, scopedRoot: base.oxe };
367
+ const sessionRoot = path.join(base.oxe, ...activeSession.split('/'));
368
+ return {
369
+ ...base,
370
+ activeSession,
371
+ scopedRoot: sessionRoot,
372
+ sessionRoot,
373
+ sessionManifest: path.join(sessionRoot, 'SESSION.md'),
374
+ spec: path.join(sessionRoot, 'spec', 'SPEC.md'),
375
+ discuss: path.join(sessionRoot, 'spec', 'DISCUSS.md'),
376
+ plan: path.join(sessionRoot, 'plan', 'PLAN.md'),
377
+ quick: path.join(sessionRoot, 'plan', 'QUICK.md'),
378
+ verify: path.join(sessionRoot, 'verification', 'VERIFY.md'),
379
+ summary: path.join(sessionRoot, 'verification', 'SUMMARY.md'),
380
+ executionState: path.join(sessionRoot, 'execution', 'STATE.md'),
381
+ };
382
+ }
335
383
 
336
384
  /**
337
385
  * Valida o arquivo plan-agents.json (se existir) e retorna avisos.
@@ -459,7 +507,7 @@ function planTaskAceiteWarnings(planPath) {
459
507
  return out;
460
508
  }
461
509
 
462
- function planWaveWarningsFixed(planPath, maxPerWave) {
510
+ function planWaveWarningsFixed(planPath, maxPerWave) {
463
511
  if (!maxPerWave || maxPerWave <= 0 || !fs.existsSync(planPath)) return [];
464
512
  const raw = fs.readFileSync(planPath, 'utf8');
465
513
  const lines = raw.split('\n');
@@ -480,19 +528,131 @@ function planWaveWarningsFixed(planPath, maxPerWave) {
480
528
  w.push(`PLAN.md: onda ${wN} tem ${count} tarefas (máximo configurado: ${maxPerWave} — considere dividir ondas)`);
481
529
  }
482
530
  }
483
- return w;
484
- }
531
+ return w;
532
+ }
533
+
534
+ /**
535
+ * @param {string} planPath
536
+ * @returns {{
537
+ * hasSection: boolean,
538
+ * bestPlan: string | null,
539
+ * confidence: number | null,
540
+ * warnings: string[],
541
+ * }}
542
+ */
543
+ function parsePlanSelfEvaluation(planPath) {
544
+ const empty = { hasSection: false, bestPlan: null, confidence: null, warnings: [] };
545
+ if (!fs.existsSync(planPath)) return empty;
546
+ const raw = fs.readFileSync(planPath, 'utf8');
547
+ const m = raw.match(/##\s*Autoavaliação do Plano\s*([\s\S]*?)(?=\n## |\n#[^\#]|$)/i);
548
+ if (!m) {
549
+ return {
550
+ ...empty,
551
+ warnings: ['PLAN.md sem a seção obrigatória "## Autoavaliação do Plano"'],
552
+ };
553
+ }
554
+ const body = m[1];
555
+ const best = body.match(/\*\*Melhor plano atual:\*\*\s*(sim|não|nao)/i);
556
+ const confidence = body.match(/\*\*Confiança:\*\*\s*(\d{1,3})\s*%/i);
557
+ /** @type {string[]} */
558
+ const warnings = [];
559
+ const rubricLabels = [
560
+ 'Completude dos requisitos',
561
+ 'Dependências conhecidas',
562
+ 'Risco técnico',
563
+ 'Impacto no código existente',
564
+ 'Clareza da validação / testes',
565
+ 'Lacunas externas / decisões pendentes',
566
+ ];
567
+ if (!best) warnings.push('PLAN.md: autoavaliação sem "Melhor plano atual: sim|não"');
568
+ if (!confidence) warnings.push('PLAN.md: autoavaliação sem "Confiança: NN%"');
569
+ if (!/\*\*Principais incertezas:\*\*/i.test(body)) warnings.push('PLAN.md: autoavaliação sem "Principais incertezas"');
570
+ if (!/\*\*Alternativas descartadas:\*\*/i.test(body)) warnings.push('PLAN.md: autoavaliação sem "Alternativas descartadas"');
571
+ if (!/\*\*Condição para replanejar:\*\*/i.test(body)) warnings.push('PLAN.md: autoavaliação sem "Condição para replanejar"');
572
+ for (const label of rubricLabels) {
573
+ if (!body.includes(label)) warnings.push(`PLAN.md: rubrica sem "${label}"`);
574
+ }
575
+ const parsedConfidence = confidence ? Number(confidence[1]) : null;
576
+ if (parsedConfidence != null && (parsedConfidence < 0 || parsedConfidence > 100)) {
577
+ warnings.push('PLAN.md: confiança fora do intervalo 0–100%');
578
+ }
579
+ return {
580
+ hasSection: true,
581
+ bestPlan: best ? best[1].toLowerCase().replace('nao', 'não') : null,
582
+ confidence: parsedConfidence,
583
+ warnings,
584
+ };
585
+ }
586
+
587
+ /**
588
+ * @param {string} planPath
589
+ * @param {number} threshold
590
+ * @returns {string[]}
591
+ */
592
+ function planSelfEvaluationWarnings(planPath, threshold) {
593
+ const info = parsePlanSelfEvaluation(planPath);
594
+ const warns = [...info.warnings];
595
+ if (!fs.existsSync(planPath)) return warns;
596
+ if (info.bestPlan === 'não') warns.push('PLAN.md: autoavaliação declara que este não é o melhor plano atual');
597
+ if (info.confidence != null && info.confidence < threshold) {
598
+ warns.push(`PLAN.md: confiança ${info.confidence}% abaixo do limiar executável (${threshold}%)`);
599
+ }
600
+ return warns;
601
+ }
602
+
603
+ /**
604
+ * @param {string} target
605
+ * @param {string | null} activeSession
606
+ * @returns {string[]}
607
+ */
608
+ function sessionWarnings(target, activeSession) {
609
+ if (!activeSession) return [];
610
+ const base = oxePaths(target);
611
+ const scoped = scopedOxePaths(target, activeSession);
612
+ /** @type {string[]} */
613
+ const warns = [];
614
+ if (!/^sessions\/s\d{3}-/.test(activeSession)) {
615
+ warns.push(`active_session "${activeSession}" não segue o formato sessions/sNNN-slug`);
616
+ }
617
+ if (!fs.existsSync(scoped.sessionRoot)) {
618
+ warns.push(`active_session aponta para ${activeSession}, mas a pasta da sessão não existe em .oxe/`);
619
+ return warns;
620
+ }
621
+ if (!fs.existsSync(scoped.sessionManifest)) warns.push(`Sessão ativa ${activeSession} sem SESSION.md`);
622
+ if (!fs.existsSync(base.sessionsIndex)) warns.push('Sessão ativa definida, mas .oxe/SESSIONS.md não existe');
623
+ return warns;
624
+ }
625
+
626
+ /**
627
+ * @param {string} target
628
+ * @returns {string[]}
629
+ */
630
+ function installationCompletenessWarnings(target) {
631
+ const p = oxePaths(target);
632
+ /** @type {string[]} */
633
+ const warns = [];
634
+ if (!fs.existsSync(p.oxe)) return warns;
635
+ if (!fs.existsSync(p.globalDir)) warns.push('.oxe/global/ ausente');
636
+ if (!fs.existsSync(p.globalLessons)) warns.push('.oxe/global/LESSONS.md ausente');
637
+ if (!fs.existsSync(p.globalMilestones)) warns.push('.oxe/global/MILESTONES.md ausente');
638
+ if (!fs.existsSync(p.globalMilestonesDir)) warns.push('.oxe/global/milestones/ ausente');
639
+ if (!fs.existsSync(p.sessionsDir)) warns.push('.oxe/sessions/ ausente');
640
+ return warns;
641
+ }
485
642
 
486
643
  /**
487
644
  * Próximo passo único (espelha o workflow next.md).
488
645
  * @param {string} target
489
646
  * @param {{ discuss_before_plan?: boolean }} cfg
490
647
  */
491
- function suggestNextStep(target, cfg = {}) {
492
- const p = oxePaths(target);
493
- const discussBefore = Boolean(cfg.discuss_before_plan);
494
- const has = (/** @type {string} */ f) => fs.existsSync(f);
495
- const mapsComplete = EXPECTED_CODEBASE_MAPS.every((f) => has(path.join(p.codebase, f)));
648
+ function suggestNextStep(target, cfg = {}) {
649
+ const base = oxePaths(target);
650
+ const stateText = fs.existsSync(base.state) ? fs.readFileSync(base.state, 'utf8') : '';
651
+ const p = scopedOxePaths(target, parseActiveSession(stateText));
652
+ const discussBefore = Boolean(cfg.discuss_before_plan);
653
+ const threshold = Number(cfg.plan_confidence_threshold) || 70;
654
+ const has = (/** @type {string} */ f) => fs.existsSync(f);
655
+ const mapsComplete = EXPECTED_CODEBASE_MAPS.every((f) => has(path.join(p.codebase, f)));
496
656
 
497
657
  if (!has(p.oxe) || !has(p.state)) {
498
658
  return {
@@ -503,8 +663,7 @@ function suggestNextStep(target, cfg = {}) {
503
663
  };
504
664
  }
505
665
 
506
- const stateText = fs.readFileSync(p.state, 'utf8');
507
- const phase = parseStatePhase(stateText);
666
+ const phase = parseStatePhase(stateText);
508
667
 
509
668
  if (!mapsComplete && !has(p.quick)) {
510
669
  return {
@@ -542,14 +701,24 @@ function suggestNextStep(target, cfg = {}) {
542
701
  };
543
702
  }
544
703
 
545
- if (!has(p.plan)) {
546
- return {
547
- step: 'plan',
548
- cursorCmd: '/oxe-plan',
549
- reason: 'SPEC existe mas PLAN.md não — gere o plano com verificação por tarefa',
550
- artifacts: ['.oxe/PLAN.md'],
551
- };
552
- }
704
+ if (!has(p.plan)) {
705
+ return {
706
+ step: 'plan',
707
+ cursorCmd: '/oxe-plan',
708
+ reason: 'SPEC existe mas PLAN.md não — gere o plano com verificação por tarefa',
709
+ artifacts: ['.oxe/PLAN.md'],
710
+ };
711
+ }
712
+
713
+ const selfEval = parsePlanSelfEvaluation(p.plan);
714
+ if (selfEval.bestPlan === 'não' || (selfEval.confidence != null && selfEval.confidence < threshold)) {
715
+ return {
716
+ step: 'plan',
717
+ cursorCmd: '/oxe-plan --replan',
718
+ reason: `O plano atual ainda não atingiu confiança executável (limiar ${threshold}%)`,
719
+ artifacts: ['.oxe/PLAN.md', '.oxe/STATE.md'],
720
+ };
721
+ }
553
722
 
554
723
  if (!has(p.verify)) {
555
724
  return {
@@ -622,53 +791,72 @@ function suggestNextStep(target, cfg = {}) {
622
791
  /**
623
792
  * @param {string} target
624
793
  */
625
- function buildHealthReport(target) {
626
- const { config, path: cfgPath, parseError } = loadOxeConfigMerged(target);
627
- const shape = validateConfigShape(config);
628
- const p = oxePaths(target);
629
- let stateText = '';
630
- if (fs.existsSync(p.state)) {
631
- try {
632
- stateText = fs.readFileSync(p.state, 'utf8');
633
- } catch {
634
- stateText = '';
635
- }
636
- }
637
- const phase = parseStatePhase(stateText);
794
+ function buildHealthReport(target) {
795
+ const { config, path: cfgPath, parseError } = loadOxeConfigMerged(target);
796
+ const shape = validateConfigShape(config);
797
+ const base = oxePaths(target);
798
+ let stateText = '';
799
+ if (fs.existsSync(base.state)) {
800
+ try {
801
+ stateText = fs.readFileSync(base.state, 'utf8');
802
+ } catch {
803
+ stateText = '';
804
+ }
805
+ }
806
+ const activeSession = parseActiveSession(stateText);
807
+ const p = scopedOxePaths(target, activeSession);
808
+ const phase = parseStatePhase(stateText);
638
809
  const scanDate = parseLastScanDate(stateText);
639
810
  const stale = isStaleScan(scanDate, Number(config.scan_max_age_days) || 0);
640
811
  const compactDate = parseLastCompactDate(stateText);
641
812
  const staleCompact = isStaleScan(compactDate, Number(config.compact_max_age_days) || 0);
642
813
  const retroDate = parseLastRetroDate(stateText);
643
- const staleLessons = isStaleLessons(retroDate, Number(config.lessons_max_age_days) || 0);
644
- const phaseWarn = phase ? phaseCoherenceWarnings(phase, p) : [];
645
- const sumWarn = verifyGapsWithoutSummaryWarning(p.verify, p.summary);
646
- const specReq = Array.isArray(config.spec_required_sections) ? config.spec_required_sections : [];
647
- const specWarn = specSectionWarnings(p.spec, specReq.map(String));
648
- const planWarn = [
649
- ...planWaveWarningsFixed(p.plan, Number(config.plan_max_tasks_per_wave) || 0),
650
- ...planTaskAceiteWarnings(p.plan),
651
- ...planAgentsWarnings(target),
652
- ];
653
- const next = suggestNextStep(target, { discuss_before_plan: config.discuss_before_plan });
654
-
655
- return {
814
+ const staleLessons = isStaleLessons(retroDate, Number(config.lessons_max_age_days) || 0);
815
+ const phaseWarn = phase ? phaseCoherenceWarnings(phase, p) : [];
816
+ const sumWarn = verifyGapsWithoutSummaryWarning(p.verify, p.summary);
817
+ const specReq = Array.isArray(config.spec_required_sections) ? config.spec_required_sections : [];
818
+ const specWarn = specSectionWarnings(p.spec, specReq.map(String));
819
+ const threshold = Number(config.plan_confidence_threshold) || 70;
820
+ const planWarn = [
821
+ ...planWaveWarningsFixed(p.plan, Number(config.plan_max_tasks_per_wave) || 0),
822
+ ...planTaskAceiteWarnings(p.plan),
823
+ ...planSelfEvaluationWarnings(p.plan, threshold),
824
+ ...planAgentsWarnings(target),
825
+ ];
826
+ const sessionWarn = sessionWarnings(target, activeSession);
827
+ const installWarn = installationCompletenessWarnings(target);
828
+ const planSelfEvaluation = parsePlanSelfEvaluation(p.plan);
829
+ const next = suggestNextStep(target, {
830
+ discuss_before_plan: config.discuss_before_plan,
831
+ plan_confidence_threshold: threshold,
832
+ });
833
+ const hardFailure = Boolean(parseError) || sessionWarn.some((w) => /não existe|sem SESSION\.md/i.test(w));
834
+ const warningCount =
835
+ phaseWarn.length + specWarn.length + planWarn.length + sessionWarn.length + installWarn.length + (sumWarn ? 1 : 0);
836
+ const healthStatus = hardFailure ? 'broken' : warningCount > 0 ? 'warning' : 'healthy';
837
+
838
+ return {
656
839
  configPath: cfgPath,
657
840
  configParseError: parseError,
658
841
  unknownConfigKeys: shape.unknownKeys,
659
- typeErrors: shape.typeErrors,
660
- phase,
661
- scanDate,
842
+ typeErrors: shape.typeErrors,
843
+ phase,
844
+ activeSession,
845
+ scanDate,
662
846
  stale,
663
847
  compactDate,
664
848
  staleCompact,
665
849
  retroDate,
666
- staleLessons,
667
- phaseWarn,
668
- summaryGapWarn: sumWarn,
669
- specWarn,
670
- planWarn,
671
- next,
850
+ staleLessons,
851
+ phaseWarn,
852
+ sessionWarn,
853
+ installWarn,
854
+ summaryGapWarn: sumWarn,
855
+ specWarn,
856
+ planWarn,
857
+ planSelfEvaluation,
858
+ healthStatus,
859
+ next,
672
860
  scanFocusGlobs: config.scan_focus_globs,
673
861
  scanIgnoreGlobs: config.scan_ignore_globs,
674
862
  };
@@ -686,18 +874,23 @@ module.exports = {
686
874
  loadOxeConfigMerged,
687
875
  validateConfigShape,
688
876
  parseStatePhase,
689
- parseLastScanDate,
690
- parseLastCompactDate,
691
- parseLastRetroDate,
692
- isStaleScan,
693
- isStaleLessons,
694
- planAgentsWarnings,
695
- phaseCoherenceWarnings,
877
+ parseLastScanDate,
878
+ parseLastCompactDate,
879
+ parseLastRetroDate,
880
+ parseActiveSession,
881
+ isStaleScan,
882
+ isStaleLessons,
883
+ planAgentsWarnings,
884
+ installationCompletenessWarnings,
885
+ parsePlanSelfEvaluation,
886
+ planSelfEvaluationWarnings,
887
+ phaseCoherenceWarnings,
696
888
  verifyGapsWithoutSummaryWarning,
697
889
  specSectionWarnings,
698
890
  planWaveWarningsFixed,
699
891
  planTaskAceiteWarnings,
700
- suggestNextStep,
701
- buildHealthReport,
702
- oxePaths,
703
- };
892
+ suggestNextStep,
893
+ buildHealthReport,
894
+ oxePaths,
895
+ scopedOxePaths,
896
+ };