@cleocode/core 2026.4.30 → 2026.4.31

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 (89) hide show
  1. package/dist/bootstrap.d.ts +35 -0
  2. package/dist/bootstrap.d.ts.map +1 -1
  3. package/dist/code/index.d.ts +8 -4
  4. package/dist/code/index.d.ts.map +1 -1
  5. package/dist/code/parser.d.ts +22 -9
  6. package/dist/code/parser.d.ts.map +1 -1
  7. package/dist/hooks/handlers/session-hooks.d.ts +11 -4
  8. package/dist/hooks/handlers/session-hooks.d.ts.map +1 -1
  9. package/dist/hooks/payload-schemas.d.ts +6 -6
  10. package/dist/index.d.ts +2 -0
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +3859 -3008
  13. package/dist/index.js.map +4 -4
  14. package/dist/internal.d.ts +10 -7
  15. package/dist/internal.d.ts.map +1 -1
  16. package/dist/lib/tree-sitter-languages.d.ts +11 -7
  17. package/dist/lib/tree-sitter-languages.d.ts.map +1 -1
  18. package/dist/memory/auto-extract.d.ts +27 -15
  19. package/dist/memory/auto-extract.d.ts.map +1 -1
  20. package/dist/memory/brain-backfill.d.ts +59 -0
  21. package/dist/memory/brain-backfill.d.ts.map +1 -0
  22. package/dist/memory/brain-purge.d.ts +51 -0
  23. package/dist/memory/brain-purge.d.ts.map +1 -0
  24. package/dist/memory/brain-retrieval.d.ts.map +1 -1
  25. package/dist/memory/brain-search.d.ts.map +1 -1
  26. package/dist/memory/decisions.d.ts.map +1 -1
  27. package/dist/memory/engine-compat.d.ts +71 -0
  28. package/dist/memory/engine-compat.d.ts.map +1 -1
  29. package/dist/memory/graph-auto-populate.d.ts +65 -0
  30. package/dist/memory/graph-auto-populate.d.ts.map +1 -0
  31. package/dist/memory/graph-queries.d.ts +127 -0
  32. package/dist/memory/graph-queries.d.ts.map +1 -0
  33. package/dist/memory/learnings.d.ts +2 -0
  34. package/dist/memory/learnings.d.ts.map +1 -1
  35. package/dist/memory/patterns.d.ts +2 -0
  36. package/dist/memory/patterns.d.ts.map +1 -1
  37. package/dist/memory/quality-scoring.d.ts +90 -0
  38. package/dist/memory/quality-scoring.d.ts.map +1 -0
  39. package/dist/sessions/session-memory-bridge.d.ts +16 -10
  40. package/dist/sessions/session-memory-bridge.d.ts.map +1 -1
  41. package/dist/store/brain-accessor.d.ts +7 -0
  42. package/dist/store/brain-accessor.d.ts.map +1 -1
  43. package/dist/store/brain-schema.d.ts +185 -11
  44. package/dist/store/brain-schema.d.ts.map +1 -1
  45. package/dist/store/brain-sqlite.d.ts.map +1 -1
  46. package/dist/store/nexus-schema.d.ts +480 -2
  47. package/dist/store/nexus-schema.d.ts.map +1 -1
  48. package/dist/store/tasks-schema.d.ts +9 -9
  49. package/dist/store/validation-schemas.d.ts +44 -28
  50. package/dist/store/validation-schemas.d.ts.map +1 -1
  51. package/dist/system/dependencies.d.ts +43 -0
  52. package/dist/system/dependencies.d.ts.map +1 -0
  53. package/dist/system/health.d.ts +3 -0
  54. package/dist/system/health.d.ts.map +1 -1
  55. package/dist/tasks/complete.d.ts.map +1 -1
  56. package/package.json +19 -19
  57. package/src/bootstrap.ts +124 -0
  58. package/src/code/index.ts +20 -4
  59. package/src/code/parser.ts +310 -110
  60. package/src/hooks/handlers/__tests__/hook-automation-e2e.test.ts +19 -45
  61. package/src/hooks/handlers/__tests__/session-hooks.test.ts +42 -54
  62. package/src/hooks/handlers/session-hooks.ts +11 -33
  63. package/src/index.ts +14 -0
  64. package/src/internal.ts +37 -7
  65. package/src/lib/tree-sitter-languages.ts +11 -7
  66. package/src/memory/__tests__/auto-extract.test.ts +20 -82
  67. package/src/memory/__tests__/embedding-pipeline.test.ts +389 -0
  68. package/src/memory/auto-extract.ts +34 -120
  69. package/src/memory/brain-backfill.ts +471 -0
  70. package/src/memory/brain-purge.ts +315 -0
  71. package/src/memory/brain-retrieval.ts +43 -2
  72. package/src/memory/brain-search.ts +23 -6
  73. package/src/memory/decisions.ts +76 -3
  74. package/src/memory/engine-compat.ts +168 -0
  75. package/src/memory/graph-auto-populate.ts +173 -0
  76. package/src/memory/graph-queries.ts +424 -0
  77. package/src/memory/learnings.ts +55 -7
  78. package/src/memory/patterns.ts +66 -13
  79. package/src/memory/quality-scoring.ts +173 -0
  80. package/src/sessions/__tests__/session-memory-bridge.test.ts +27 -49
  81. package/src/sessions/session-memory-bridge.ts +19 -47
  82. package/src/store/__tests__/brain-accessor-pageindex.test.ts +93 -22
  83. package/src/store/brain-accessor.ts +48 -2
  84. package/src/store/brain-schema.ts +165 -13
  85. package/src/store/brain-sqlite.ts +35 -0
  86. package/src/store/nexus-schema.ts +257 -3
  87. package/src/system/dependencies.ts +534 -0
  88. package/src/system/health.ts +126 -22
  89. package/src/tasks/complete.ts +40 -0
@@ -4,11 +4,11 @@
4
4
  * @task T4795
5
5
  */
6
6
 
7
- import { execFile, execFileSync } from 'node:child_process';
7
+ import { execFileSync } from 'node:child_process';
8
8
  import { existsSync, readFileSync, statSync } from 'node:fs';
9
9
  import { createRequire } from 'node:module';
10
10
  import { join } from 'node:path';
11
- import { promisify } from 'node:util';
11
+ import type { DependencyReport } from '@cleocode/contracts';
12
12
  import { checkGitHooks, type HookCheckResult } from '../hooks.js';
13
13
  import { checkInjection } from '../injection.js';
14
14
  import { getAgentsHome, isProjectInitialized } from '../paths.js';
@@ -37,9 +37,15 @@ import {
37
37
  checkNodeVersion,
38
38
  checkVitalFilesTracked,
39
39
  } from '../validation/doctor/checks.js';
40
+ import { checkAllDependencies } from './dependencies.js';
40
41
  import { checkStorageMigration } from './storage-preflight.js';
41
42
 
42
- const execAsync = promisify(execFile);
43
+ // NOTE on storage-migration preflight consolidation (T511):
44
+ // `checkStorageMigration` lives in ./storage-preflight.ts and is the single
45
+ // source of truth. Both `self-update.ts` and `upgrade.ts` import it from
46
+ // `@cleocode/core/internal`. No duplication exists — this comment documents
47
+ // that the consolidation is already in place.
48
+
43
49
  const _require = createRequire(import.meta.url);
44
50
 
45
51
  type SqliteModule = typeof import('node:sqlite');
@@ -449,15 +455,8 @@ export interface DoctorReport {
449
455
  errors: number;
450
456
  warnings: number;
451
457
  checks: DoctorCheck[];
452
- }
453
-
454
- async function commandExists(cmd: string): Promise<string | null> {
455
- try {
456
- const { stdout } = await execAsync('which', [cmd]);
457
- return stdout.trim();
458
- } catch {
459
- return null;
460
- }
458
+ /** Dependency registry report — populated by `checkAllDependencies()`. */
459
+ dependencies?: DependencyReport;
461
460
  }
462
461
 
463
462
  async function fileSize(path: string): Promise<number> {
@@ -601,6 +600,63 @@ function checkContributorChannel(projectRoot: string): DoctorCheck {
601
600
  }
602
601
  }
603
602
 
603
+ /**
604
+ * Run adapter health checks for all discovered adapters and return doctor
605
+ * check entries. Returns an empty array when no adapters are initialized
606
+ * (adapters are optional — their absence is not an error).
607
+ *
608
+ * @param projectRoot - Absolute path to the project root
609
+ * @returns Array of DoctorCheck entries, one per adapter (plus a summary if none found)
610
+ */
611
+ async function checkAdapterHealth(projectRoot: string): Promise<DoctorCheck[]> {
612
+ const results: DoctorCheck[] = [];
613
+ try {
614
+ const { AdapterManager } = await import('../adapters/index.js');
615
+ const { getPackageRoot } = await import('../scaffold.js');
616
+ // Prefer the caller-supplied projectRoot; fall back to the package root for
617
+ // adapter manifest discovery when projectRoot is not a package root itself.
618
+ const pkgRoot = projectRoot || getPackageRoot();
619
+ const manager = AdapterManager.getInstance(pkgRoot);
620
+
621
+ // Discover manifests (idempotent if already discovered)
622
+ manager.discover();
623
+
624
+ // Run health checks on any already-initialized adapters
625
+ const healthMap = await manager.healthCheckAll();
626
+
627
+ if (healthMap.size === 0) {
628
+ // No adapters initialized — adapters are optional, report as info
629
+ results.push({
630
+ check: 'adapter_health',
631
+ status: 'ok',
632
+ message: 'No adapters initialized (adapters are optional)',
633
+ });
634
+ } else {
635
+ for (const [adapterId, status] of healthMap) {
636
+ results.push({
637
+ check: `adapter_${adapterId.replace(/-/g, '_')}`,
638
+ status: status.healthy ? 'ok' : 'warning',
639
+ message: status.healthy
640
+ ? `Adapter ${adapterId} (${status.provider}) healthy`
641
+ : `Adapter ${adapterId} (${status.provider}) unhealthy`,
642
+ ...(status.details && Object.keys(status.details).length > 0
643
+ ? { details: status.details as Record<string, unknown> }
644
+ : {}),
645
+ ...(!status.healthy ? { fix: `cleo adapter health ${adapterId}` } : {}),
646
+ });
647
+ }
648
+ }
649
+ } catch {
650
+ // Adapter system is non-critical — never let it block the doctor report
651
+ results.push({
652
+ check: 'adapter_health',
653
+ status: 'warning',
654
+ message: 'Adapter health check unavailable (adapter system not reachable)',
655
+ });
656
+ }
657
+ return results;
658
+ }
659
+
604
660
  /**
605
661
  * Run comprehensive doctor diagnostics combining dependency checks,
606
662
  * directory checks, data file checks, gitignore checks, and environment info.
@@ -609,15 +665,31 @@ function checkContributorChannel(projectRoot: string): DoctorCheck {
609
665
  export async function coreDoctorReport(projectRoot: string): Promise<DoctorReport> {
610
666
  const checks: DoctorCheck[] = [];
611
667
 
612
- // 1. Check dependencies (jq removed no longer needed since SQLite migration, ADR-006)
613
- const gitPath = await commandExists('git');
614
- checks.push({
615
- check: 'git_installed',
616
- status: gitPath ? 'ok' : 'warning',
617
- message: gitPath
618
- ? `git found: ${gitPath}`
619
- : 'git not found (optional, needed for version control features)',
620
- });
668
+ // 1. Dependency registry single source of truth for all external deps (T507)
669
+ const depReport = await checkAllDependencies();
670
+ // Wire individual dependency results into the doctor check list so existing
671
+ // consumers (CLI renderers, tests) see familiar DoctorCheck entries.
672
+ for (const dep of depReport.results) {
673
+ const isRequired = dep.category === 'required';
674
+ const status: 'ok' | 'warning' | 'error' = dep.healthy
675
+ ? dep.installed
676
+ ? 'ok'
677
+ : 'warning' // optional/feature not installed — warn, not error
678
+ : isRequired
679
+ ? 'error'
680
+ : 'warning';
681
+
682
+ const message = dep.installed
683
+ ? `${dep.name}${dep.version ? ` ${dep.version}` : ''}${dep.location ? ` (${dep.location})` : ''}`
684
+ : (dep.error ?? `${dep.name} not found`);
685
+
686
+ checks.push({
687
+ check: `dep_${dep.name.replace(/-/g, '_')}`,
688
+ status,
689
+ message,
690
+ ...(dep.suggestedFix ? { fix: dep.suggestedFix } : {}),
691
+ });
692
+ }
621
693
 
622
694
  // 2. Check CLEO directories
623
695
  const cleoDir = join(projectRoot, '.cleo');
@@ -798,6 +870,38 @@ export async function coreDoctorReport(projectRoot: string): Promise<DoctorRepor
798
870
  ...(existsSync(agentDefPath) ? {} : { fix: 'cleo init' }),
799
871
  });
800
872
 
873
+ // 5e. brain.db and memory-bridge checks (T511 — consolidate startup checks into doctor)
874
+ // These are checked in startupHealthCheck() as warnings. The doctor report is the
875
+ // comprehensive view so it must include them too, making startupHealthCheck() a subset.
876
+ const brainDbCheckResult = checkBrainDb(projectRoot);
877
+ checks.push({
878
+ check: 'brain_db',
879
+ status:
880
+ brainDbCheckResult.status === 'passed'
881
+ ? 'ok'
882
+ : brainDbCheckResult.status === 'warning'
883
+ ? 'warning'
884
+ : 'error',
885
+ message: brainDbCheckResult.message,
886
+ ...(brainDbCheckResult.fix ? { fix: brainDbCheckResult.fix } : {}),
887
+ });
888
+
889
+ const memBridgeCheckResult = checkMemoryBridge(projectRoot);
890
+ checks.push({
891
+ check: 'memory_bridge',
892
+ status: memBridgeCheckResult.status === 'passed' ? 'ok' : 'warning',
893
+ message: memBridgeCheckResult.message,
894
+ ...(memBridgeCheckResult.fix ? { fix: memBridgeCheckResult.fix } : {}),
895
+ });
896
+
897
+ // 5f. Adapter health checks (T511 — wire adapter health into doctor)
898
+ // `cleo adapter health` was previously isolated from the doctor report.
899
+ // The doctor is the comprehensive diagnostic view so adapter health belongs here.
900
+ const adapterChecks = await checkAdapterHealth(projectRoot);
901
+ for (const ac of adapterChecks) {
902
+ checks.push(ac);
903
+ }
904
+
801
905
  // 6a. GitHub templates presence (informational only — non-critical)
802
906
  const gitDir = join(projectRoot, '.git');
803
907
  const gitHubTemplatesDir = join(projectRoot, '.github', 'ISSUE_TEMPLATE');
@@ -838,7 +942,7 @@ export async function coreDoctorReport(projectRoot: string): Promise<DoctorRepor
838
942
  const warningCount = checks.filter((c) => c.status === 'warning').length;
839
943
  const healthy = errorCount === 0;
840
944
 
841
- return { healthy, errors: errorCount, warnings: warningCount, checks };
945
+ return { healthy, errors: errorCount, warnings: warningCount, checks, dependencies: depReport };
842
946
  }
843
947
 
844
948
  // ============================================================================
@@ -297,6 +297,46 @@ export async function completeTask(
297
297
  /* Memory extraction is best-effort */
298
298
  });
299
299
 
300
+ // Auto-populate brain graph nodes for the completed task (best-effort, T537).
301
+ // Runs SEPARATE from the gutted extractTaskCompletionMemory — this only writes
302
+ // graph topology, not memory table rows.
303
+ import('../memory/graph-auto-populate.js')
304
+ .then(({ upsertGraphNode, addGraphEdge }) =>
305
+ (async () => {
306
+ const projectRoot = cwd ?? process.cwd();
307
+ await upsertGraphNode(
308
+ projectRoot,
309
+ `task:${task.id}`,
310
+ 'task',
311
+ `${task.id}: ${task.title}`.substring(0, 200),
312
+ 1.0,
313
+ task.title,
314
+ { status: 'done', priority: task.priority },
315
+ );
316
+ if (task.parentId) {
317
+ await upsertGraphNode(
318
+ projectRoot,
319
+ `epic:${task.parentId}`,
320
+ 'epic',
321
+ task.parentId,
322
+ 1.0,
323
+ '',
324
+ );
325
+ await addGraphEdge(
326
+ projectRoot,
327
+ `task:${task.id}`,
328
+ `epic:${task.parentId}`,
329
+ 'part_of',
330
+ 1.0,
331
+ 'auto:task-complete',
332
+ );
333
+ }
334
+ })(),
335
+ )
336
+ .catch(() => {
337
+ /* Graph population is best-effort */
338
+ });
339
+
300
340
  return {
301
341
  task,
302
342
  ...(autoCompleted.length > 0 && { autoCompleted }),