@kernlang/evolve 3.0.0

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 (104) hide show
  1. package/LICENSE +661 -0
  2. package/dist/concept-gap-adapter.d.ts +17 -0
  3. package/dist/concept-gap-adapter.js +43 -0
  4. package/dist/concept-gap-adapter.js.map +1 -0
  5. package/dist/detector-registry.d.ts +31 -0
  6. package/dist/detector-registry.js +89 -0
  7. package/dist/detector-registry.js.map +1 -0
  8. package/dist/detectors/animation.d.ts +5 -0
  9. package/dist/detectors/animation.js +67 -0
  10. package/dist/detectors/animation.js.map +1 -0
  11. package/dist/detectors/data-fetching.d.ts +5 -0
  12. package/dist/detectors/data-fetching.js +117 -0
  13. package/dist/detectors/data-fetching.js.map +1 -0
  14. package/dist/detectors/express-middleware.d.ts +5 -0
  15. package/dist/detectors/express-middleware.js +52 -0
  16. package/dist/detectors/express-middleware.js.map +1 -0
  17. package/dist/detectors/react-forms.d.ts +5 -0
  18. package/dist/detectors/react-forms.js +89 -0
  19. package/dist/detectors/react-forms.js.map +1 -0
  20. package/dist/detectors/schema-validation.d.ts +5 -0
  21. package/dist/detectors/schema-validation.js +92 -0
  22. package/dist/detectors/schema-validation.js.map +1 -0
  23. package/dist/detectors/state-mgmt.d.ts +5 -0
  24. package/dist/detectors/state-mgmt.js +102 -0
  25. package/dist/detectors/state-mgmt.js.map +1 -0
  26. package/dist/detectors/structural.d.ts +10 -0
  27. package/dist/detectors/structural.js +271 -0
  28. package/dist/detectors/structural.js.map +1 -0
  29. package/dist/detectors/testing.d.ts +5 -0
  30. package/dist/detectors/testing.js +56 -0
  31. package/dist/detectors/testing.js.map +1 -0
  32. package/dist/detectors/vue-composables.d.ts +5 -0
  33. package/dist/detectors/vue-composables.js +57 -0
  34. package/dist/detectors/vue-composables.js.map +1 -0
  35. package/dist/evolve-dedup.d.ts +12 -0
  36. package/dist/evolve-dedup.js +50 -0
  37. package/dist/evolve-dedup.js.map +1 -0
  38. package/dist/evolve-rollback.d.ts +58 -0
  39. package/dist/evolve-rollback.js +242 -0
  40. package/dist/evolve-rollback.js.map +1 -0
  41. package/dist/evolve-runner.d.ts +22 -0
  42. package/dist/evolve-runner.js +183 -0
  43. package/dist/evolve-runner.js.map +1 -0
  44. package/dist/evolve-validator-v4.d.ts +11 -0
  45. package/dist/evolve-validator-v4.js +253 -0
  46. package/dist/evolve-validator-v4.js.map +1 -0
  47. package/dist/evolved-node-loader.d.ts +61 -0
  48. package/dist/evolved-node-loader.js +228 -0
  49. package/dist/evolved-node-loader.js.map +1 -0
  50. package/dist/evolved-types.d.ts +110 -0
  51. package/dist/evolved-types.js +8 -0
  52. package/dist/evolved-types.js.map +1 -0
  53. package/dist/expressibility-scorer.d.ts +19 -0
  54. package/dist/expressibility-scorer.js +78 -0
  55. package/dist/expressibility-scorer.js.map +1 -0
  56. package/dist/gap-detector.d.ts +26 -0
  57. package/dist/gap-detector.js +141 -0
  58. package/dist/gap-detector.js.map +1 -0
  59. package/dist/golden-test-runner.d.ts +27 -0
  60. package/dist/golden-test-runner.js +120 -0
  61. package/dist/golden-test-runner.js.map +1 -0
  62. package/dist/graduation.d.ts +36 -0
  63. package/dist/graduation.js +175 -0
  64. package/dist/graduation.js.map +1 -0
  65. package/dist/index.d.ts +40 -0
  66. package/dist/index.js +58 -0
  67. package/dist/index.js.map +1 -0
  68. package/dist/llm-discovery.d.ts +55 -0
  69. package/dist/llm-discovery.js +352 -0
  70. package/dist/llm-discovery.js.map +1 -0
  71. package/dist/llm-provider.d.ts +35 -0
  72. package/dist/llm-provider.js +143 -0
  73. package/dist/llm-provider.js.map +1 -0
  74. package/dist/node-governance.d.ts +23 -0
  75. package/dist/node-governance.js +45 -0
  76. package/dist/node-governance.js.map +1 -0
  77. package/dist/node-proposer.d.ts +27 -0
  78. package/dist/node-proposer.js +127 -0
  79. package/dist/node-proposer.js.map +1 -0
  80. package/dist/node-validator.d.ts +16 -0
  81. package/dist/node-validator.js +74 -0
  82. package/dist/node-validator.js.map +1 -0
  83. package/dist/pattern-analyzer.d.ts +28 -0
  84. package/dist/pattern-analyzer.js +181 -0
  85. package/dist/pattern-analyzer.js.map +1 -0
  86. package/dist/quality-scorer.d.ts +16 -0
  87. package/dist/quality-scorer.js +79 -0
  88. package/dist/quality-scorer.js.map +1 -0
  89. package/dist/sandboxed-generator.d.ts +26 -0
  90. package/dist/sandboxed-generator.js +133 -0
  91. package/dist/sandboxed-generator.js.map +1 -0
  92. package/dist/staging.d.ts +81 -0
  93. package/dist/staging.js +414 -0
  94. package/dist/staging.js.map +1 -0
  95. package/dist/template-proposer.d.ts +24 -0
  96. package/dist/template-proposer.js +103 -0
  97. package/dist/template-proposer.js.map +1 -0
  98. package/dist/template-validator.d.ts +18 -0
  99. package/dist/template-validator.js +174 -0
  100. package/dist/template-validator.js.map +1 -0
  101. package/dist/types.d.ts +169 -0
  102. package/dist/types.js +7 -0
  103. package/dist/types.js.map +1 -0
  104. package/package.json +27 -0
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Evolve v4 Types — Self-Extending IR
3
+ *
4
+ * These types define the schema for evolved nodes: proposals from LLM discovery,
5
+ * graduated node definitions on disk, parser hints, and validation results.
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=evolved-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evolved-types.js","sourceRoot":"","sources":["../src/evolved-types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Expressibility Scorer — measures how well the current IR handles a pattern.
3
+ *
4
+ * Scores from 0-10 where higher = less expressible (needs a new node type).
5
+ * Used to decide when to propose new IR nodes vs. just new templates.
6
+ */
7
+ import type { PatternGap, ExpressibilityScore } from './types.js';
8
+ /** Score above which a pattern is a candidate for a new IR node. */
9
+ export declare const EXPRESSIBILITY_NODE_THRESHOLD = 7;
10
+ /**
11
+ * Score how well the current IR can express the patterns found in these gaps.
12
+ *
13
+ * - Handler escapes (<<<...>>>) mean the IR can't express the logic — weight 3
14
+ * - Non-standard attributes mean the node types lack proper slots — weight 2
15
+ * - Semantic leaks (raw code in props) mean the abstraction is leaking — weight 1.5
16
+ */
17
+ export declare function scoreExpressibility(gaps: PatternGap[], snippets: string[]): ExpressibilityScore;
18
+ /** Check if an expressibility score warrants proposing a new IR node. */
19
+ export declare function isNodeCandidate(score: ExpressibilityScore): boolean;
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Expressibility Scorer — measures how well the current IR handles a pattern.
3
+ *
4
+ * Scores from 0-10 where higher = less expressible (needs a new node type).
5
+ * Used to decide when to propose new IR nodes vs. just new templates.
6
+ */
7
+ // Weights for different signal types
8
+ const HANDLER_ESCAPE_WEIGHT = 3;
9
+ const NON_STANDARD_ATTR_WEIGHT = 2;
10
+ const SEMANTIC_LEAK_WEIGHT = 1.5;
11
+ // Max counts for normalization (clamp above these)
12
+ const MAX_HANDLER_ESCAPES = 5;
13
+ const MAX_NON_STANDARD_ATTRS = 8;
14
+ const MAX_SEMANTIC_LEAKS = 6;
15
+ /** Score above which a pattern is a candidate for a new IR node. */
16
+ export const EXPRESSIBILITY_NODE_THRESHOLD = 7.0;
17
+ /**
18
+ * Score how well the current IR can express the patterns found in these gaps.
19
+ *
20
+ * - Handler escapes (<<<...>>>) mean the IR can't express the logic — weight 3
21
+ * - Non-standard attributes mean the node types lack proper slots — weight 2
22
+ * - Semantic leaks (raw code in props) mean the abstraction is leaking — weight 1.5
23
+ */
24
+ export function scoreExpressibility(gaps, snippets) {
25
+ let handlerEscapes = 0;
26
+ let nonStandardAttrs = 0;
27
+ let semanticLeaks = 0;
28
+ for (const snippet of snippets) {
29
+ // Count handler escapes: <<<...>>>
30
+ const handlerMatches = snippet.match(/<<<[^]*?>>>/g);
31
+ if (handlerMatches)
32
+ handlerEscapes += handlerMatches.length;
33
+ // Count non-standard attributes (attrs that aren't name, value, type, etc.)
34
+ const attrMatches = snippet.match(/\b[a-z][a-zA-Z]*=/g);
35
+ if (attrMatches) {
36
+ const standard = new Set(['name', 'value', 'type', 'key', 'id', 'class', 'src', 'href', 'label', 'placeholder']);
37
+ for (const attr of attrMatches) {
38
+ const attrName = attr.slice(0, -1);
39
+ if (!standard.has(attrName))
40
+ nonStandardAttrs++;
41
+ }
42
+ }
43
+ // Count semantic leaks: raw code expressions in {{ }}
44
+ const exprMatches = snippet.match(/\{\{[^}]+\}\}/g);
45
+ if (exprMatches)
46
+ semanticLeaks += exprMatches.length;
47
+ }
48
+ // Also count from gap snippets not already in snippets array
49
+ const snippetSet = new Set(snippets);
50
+ for (const gap of gaps) {
51
+ if (snippetSet.has(gap.snippet))
52
+ continue;
53
+ if (gap.snippet.includes('<<<'))
54
+ handlerEscapes++;
55
+ if (gap.snippet.includes('{{'))
56
+ semanticLeaks++;
57
+ }
58
+ // Normalize each dimension to 0-10
59
+ const normHandler = Math.min(handlerEscapes / MAX_HANDLER_ESCAPES, 1) * 10;
60
+ const normAttrs = Math.min(nonStandardAttrs / MAX_NON_STANDARD_ATTRS, 1) * 10;
61
+ const normLeaks = Math.min(semanticLeaks / MAX_SEMANTIC_LEAKS, 1) * 10;
62
+ // Weighted average
63
+ const totalWeight = HANDLER_ESCAPE_WEIGHT + NON_STANDARD_ATTR_WEIGHT + SEMANTIC_LEAK_WEIGHT;
64
+ const overall = (normHandler * HANDLER_ESCAPE_WEIGHT +
65
+ normAttrs * NON_STANDARD_ATTR_WEIGHT +
66
+ normLeaks * SEMANTIC_LEAK_WEIGHT) / totalWeight;
67
+ return {
68
+ handlerEscapes,
69
+ nonStandardAttrs,
70
+ semanticLeaks,
71
+ overall: Math.round(overall * 100) / 100,
72
+ };
73
+ }
74
+ /** Check if an expressibility score warrants proposing a new IR node. */
75
+ export function isNodeCandidate(score) {
76
+ return score.overall > EXPRESSIBILITY_NODE_THRESHOLD;
77
+ }
78
+ //# sourceMappingURL=expressibility-scorer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expressibility-scorer.js","sourceRoot":"","sources":["../src/expressibility-scorer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Gap Detector — Phase 1 of the evolve pipeline.
3
+ *
4
+ * Uses @kernlang/review to find what IS covered, then runs pluggable
5
+ * detector packs to find what's NOT covered (gaps).
6
+ */
7
+ import { Project, type SourceFile } from 'ts-morph';
8
+ import type { ReviewReport } from '@kernlang/review';
9
+ import type { PatternGap } from './types.js';
10
+ /**
11
+ * Reset the gap ID counter (for test isolation).
12
+ */
13
+ export declare function resetGapIds(): void;
14
+ /**
15
+ * Detect gaps in a single TS file: patterns that exist in the code
16
+ * but are NOT covered by existing KERN templates.
17
+ */
18
+ export declare function detectGaps(sourceFile: SourceFile, filePath: string, existingCoverage?: ReviewReport): PatternGap[];
19
+ /**
20
+ * Detect gaps across multiple files.
21
+ */
22
+ export declare function detectGapsInFiles(filePaths: string[], project?: Project): PatternGap[];
23
+ /**
24
+ * Detect gaps from a source string (useful for testing).
25
+ */
26
+ export declare function detectGapsFromSource(source: string, filePath?: string, project?: Project): PatternGap[];
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Gap Detector — Phase 1 of the evolve pipeline.
3
+ *
4
+ * Uses @kernlang/review to find what IS covered, then runs pluggable
5
+ * detector packs to find what's NOT covered (gaps).
6
+ */
7
+ import { Project } from 'ts-morph';
8
+ import { reviewFile } from '@kernlang/review';
9
+ import { getDetectorsForImport, getUniversalDetectors } from './detector-registry.js';
10
+ import { detectConceptualGaps } from './concept-gap-adapter.js';
11
+ let _gapIdCounter = 0;
12
+ function nextGapId(detectorId) {
13
+ return `gap-${detectorId}-${++_gapIdCounter}`;
14
+ }
15
+ /**
16
+ * Reset the gap ID counter (for test isolation).
17
+ */
18
+ export function resetGapIds() {
19
+ _gapIdCounter = 0;
20
+ }
21
+ /**
22
+ * Detect gaps in a single TS file: patterns that exist in the code
23
+ * but are NOT covered by existing KERN templates.
24
+ */
25
+ export function detectGaps(sourceFile, filePath, existingCoverage) {
26
+ const fullText = sourceFile.getFullText();
27
+ const imports = sourceFile.getImportDeclarations();
28
+ const gaps = [];
29
+ // Collect existing template match ranges so we skip already-covered code
30
+ const coveredRanges = new Set();
31
+ if (existingCoverage) {
32
+ for (const tm of existingCoverage.templateMatches) {
33
+ for (let l = tm.startLine; l <= tm.endLine; l++) {
34
+ coveredRanges.add(`${l}`);
35
+ }
36
+ }
37
+ }
38
+ // Find which detectors are relevant for this file's imports
39
+ const relevantDetectors = new Set();
40
+ for (const imp of imports) {
41
+ const importPath = imp.getModuleSpecifierValue();
42
+ for (const det of getDetectorsForImport(importPath)) {
43
+ relevantDetectors.add(det);
44
+ }
45
+ }
46
+ // Run each relevant detector (import-matched)
47
+ for (const detector of relevantDetectors) {
48
+ const detections = detector.detect(sourceFile, fullText);
49
+ for (const detection of detections) {
50
+ // Skip if any line in this range is already covered by existing templates
51
+ let isCovered = false;
52
+ for (let l = detection.startLine; l <= detection.endLine; l++) {
53
+ if (coveredRanges.has(`${l}`)) {
54
+ isCovered = true;
55
+ break;
56
+ }
57
+ }
58
+ if (isCovered)
59
+ continue;
60
+ gaps.push({
61
+ id: nextGapId(detector.id),
62
+ detectorId: detector.id,
63
+ libraryName: detector.libraryName,
64
+ patternKind: detector.patternKind,
65
+ anchorImport: detection.anchorImport,
66
+ startLine: detection.startLine,
67
+ endLine: detection.endLine,
68
+ snippet: detection.snippet,
69
+ extractedParams: detection.extractedParams,
70
+ confidencePct: detection.confidencePct,
71
+ filePath,
72
+ });
73
+ }
74
+ }
75
+ // Run universal detectors (import-agnostic structural patterns)
76
+ for (const detector of getUniversalDetectors()) {
77
+ if (relevantDetectors.has(detector))
78
+ continue; // already ran
79
+ const detections = detector.detect(sourceFile, fullText);
80
+ for (const detection of detections) {
81
+ let isCovered = false;
82
+ for (let l = detection.startLine; l <= detection.endLine; l++) {
83
+ if (coveredRanges.has(`${l}`)) {
84
+ isCovered = true;
85
+ break;
86
+ }
87
+ }
88
+ if (isCovered)
89
+ continue;
90
+ gaps.push({
91
+ id: nextGapId(detector.id),
92
+ detectorId: detector.id,
93
+ libraryName: detector.libraryName,
94
+ patternKind: detector.patternKind,
95
+ anchorImport: detection.anchorImport,
96
+ startLine: detection.startLine,
97
+ endLine: detection.endLine,
98
+ snippet: detection.snippet,
99
+ extractedParams: detection.extractedParams,
100
+ confidencePct: detection.confidencePct,
101
+ filePath,
102
+ });
103
+ }
104
+ }
105
+ // Run concept-based detection (universal structural gaps)
106
+ const conceptGaps = detectConceptualGaps(sourceFile, filePath);
107
+ return [...gaps, ...conceptGaps];
108
+ }
109
+ /**
110
+ * Detect gaps across multiple files.
111
+ */
112
+ export function detectGapsInFiles(filePaths, project) {
113
+ const tsProject = project || new Project({ skipAddingFilesFromTsConfig: true });
114
+ const allGaps = [];
115
+ for (const filePath of filePaths) {
116
+ try {
117
+ const sourceFile = tsProject.addSourceFileAtPath(filePath);
118
+ // Get existing coverage from review
119
+ let coverage;
120
+ try {
121
+ coverage = reviewFile(filePath);
122
+ }
123
+ catch { // file may not exist
124
+ }
125
+ const gaps = detectGaps(sourceFile, filePath, coverage);
126
+ allGaps.push(...gaps);
127
+ }
128
+ catch { // file may not exist
129
+ }
130
+ }
131
+ return allGaps;
132
+ }
133
+ /**
134
+ * Detect gaps from a source string (useful for testing).
135
+ */
136
+ export function detectGapsFromSource(source, filePath = 'input.ts', project) {
137
+ const tsProject = project || new Project({ skipAddingFilesFromTsConfig: true });
138
+ const sourceFile = tsProject.createSourceFile(filePath, source, { overwrite: true });
139
+ return detectGaps(sourceFile, filePath);
140
+ }
141
+ //# sourceMappingURL=gap-detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gap-detector.js","sourceRoot":"","sources":["../src/gap-detector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Golden Test Runner — AST-based comparison of codegen output vs expected output.
3
+ *
4
+ * Whitespace-insensitive: normalizes both sides before comparing.
5
+ * Used by validation pipeline and `kern evolve test` command.
6
+ */
7
+ export interface GoldenTestResult {
8
+ keyword: string;
9
+ pass: boolean;
10
+ error?: string;
11
+ actual?: string;
12
+ expected?: string;
13
+ }
14
+ /**
15
+ * Compare two code strings with whitespace normalization.
16
+ * Returns true if they're structurally equivalent.
17
+ */
18
+ export declare function compareGoldenOutput(actual: string, expected: string): boolean;
19
+ /**
20
+ * Run golden tests for all evolved nodes in .kern/evolved/.
21
+ * Each node must have template.kern + expected-output.ts.
22
+ */
23
+ export declare function runGoldenTests(baseDir?: string): GoldenTestResult[];
24
+ /**
25
+ * Format golden test results for terminal display.
26
+ */
27
+ export declare function formatGoldenTestResults(results: GoldenTestResult[]): string;
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Golden Test Runner — AST-based comparison of codegen output vs expected output.
3
+ *
4
+ * Whitespace-insensitive: normalizes both sides before comparing.
5
+ * Used by validation pipeline and `kern evolve test` command.
6
+ */
7
+ import { existsSync, readFileSync } from 'fs';
8
+ import { resolve, join } from 'path';
9
+ import { parse } from '@kernlang/core';
10
+ import { loadSandboxedGenerator } from './sandboxed-generator.js';
11
+ import { registerParserHints, unregisterParserHints } from '@kernlang/core';
12
+ /**
13
+ * Compare two code strings with whitespace normalization.
14
+ * Returns true if they're structurally equivalent.
15
+ */
16
+ export function compareGoldenOutput(actual, expected) {
17
+ return normalize(actual) === normalize(expected);
18
+ }
19
+ /**
20
+ * Normalize code for comparison:
21
+ * - Trim each line
22
+ * - Remove empty lines
23
+ * - Collapse multiple spaces
24
+ * - Normalize line endings
25
+ */
26
+ function normalize(code) {
27
+ return code
28
+ .split('\n')
29
+ .map(l => l.trim().replace(/\s+/g, ' '))
30
+ .filter(l => l.length > 0)
31
+ .join('\n')
32
+ .trim();
33
+ }
34
+ /**
35
+ * Run golden tests for all evolved nodes in .kern/evolved/.
36
+ * Each node must have template.kern + expected-output.ts.
37
+ */
38
+ export function runGoldenTests(baseDir = process.cwd()) {
39
+ const evolvedDir = resolve(baseDir, '.kern', 'evolved');
40
+ const manifestPath = join(evolvedDir, 'manifest.json');
41
+ if (!existsSync(manifestPath))
42
+ return [];
43
+ let manifest;
44
+ try {
45
+ manifest = JSON.parse(readFileSync(manifestPath, 'utf-8'));
46
+ }
47
+ catch {
48
+ return [{ keyword: '*', pass: false, error: 'Failed to parse manifest.json' }];
49
+ }
50
+ const results = [];
51
+ for (const [keyword, entry] of Object.entries(manifest.nodes)) {
52
+ const nodeDir = join(evolvedDir, keyword);
53
+ const templatePath = join(nodeDir, 'template.kern');
54
+ const expectedPath = join(nodeDir, 'expected-output.ts');
55
+ const codegenPath = join(nodeDir, 'codegen.js');
56
+ // Check required files exist
57
+ if (!existsSync(templatePath)) {
58
+ results.push({ keyword, pass: false, error: 'Missing template.kern' });
59
+ continue;
60
+ }
61
+ if (!existsSync(expectedPath)) {
62
+ results.push({ keyword, pass: false, error: 'Missing expected-output.ts' });
63
+ continue;
64
+ }
65
+ if (!existsSync(codegenPath)) {
66
+ results.push({ keyword, pass: false, error: 'Missing codegen.js' });
67
+ continue;
68
+ }
69
+ // Register parser hints temporarily
70
+ if (entry.parserHints) {
71
+ registerParserHints(keyword, entry.parserHints);
72
+ }
73
+ try {
74
+ const kernSource = readFileSync(templatePath, 'utf-8');
75
+ const expectedOutput = readFileSync(expectedPath, 'utf-8');
76
+ const generator = loadSandboxedGenerator(codegenPath);
77
+ const ast = parse(kernSource);
78
+ const actual = generator(ast).join('\n');
79
+ const pass = compareGoldenOutput(actual, expectedOutput);
80
+ results.push({
81
+ keyword,
82
+ pass,
83
+ ...(pass ? {} : { actual, expected: expectedOutput }),
84
+ });
85
+ }
86
+ catch (err) {
87
+ results.push({ keyword, pass: false, error: err.message });
88
+ }
89
+ finally {
90
+ if (entry.parserHints) {
91
+ unregisterParserHints(keyword);
92
+ }
93
+ }
94
+ }
95
+ return results;
96
+ }
97
+ /**
98
+ * Format golden test results for terminal display.
99
+ */
100
+ export function formatGoldenTestResults(results) {
101
+ if (results.length === 0)
102
+ return ' No evolved nodes to test.';
103
+ const lines = [];
104
+ let passed = 0;
105
+ let failed = 0;
106
+ for (const r of results) {
107
+ if (r.pass) {
108
+ lines.push(` \u2713 ${r.keyword}`);
109
+ passed++;
110
+ }
111
+ else {
112
+ lines.push(` \u2717 ${r.keyword}: ${r.error || 'Golden diff mismatch'}`);
113
+ failed++;
114
+ }
115
+ }
116
+ lines.push('');
117
+ lines.push(` ${passed} passed, ${failed} failed, ${results.length} total`);
118
+ return lines.join('\n');
119
+ }
120
+ //# sourceMappingURL=golden-test-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"golden-test-runner.js","sourceRoot":"","sources":["../src/golden-test-runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Graduation — writes approved node proposals to .kern/evolved/.
3
+ *
4
+ * Compiles the codegen TypeScript source to JS, writes all files,
5
+ * updates the manifest, and makes the node available at next compile.
6
+ */
7
+ import type { EvolveNodeProposal, EvolvedNodeDefinition } from './evolved-types.js';
8
+ /**
9
+ * Graduate an approved proposal: write all files to .kern/evolved/<keyword>/.
10
+ */
11
+ export declare function graduateNode(proposal: EvolveNodeProposal, compiledJs: string, graduatedBy?: string, baseDir?: string): {
12
+ success: boolean;
13
+ error?: string;
14
+ path?: string;
15
+ };
16
+ /**
17
+ * Compile codegen TypeScript source to JavaScript for the sandbox.
18
+ * Strips types, imports, and converts to CommonJS.
19
+ */
20
+ export declare function compileCodegenToJS(tsSource: string): string;
21
+ /**
22
+ * Promote a graduated evolved node → core.
23
+ *
24
+ * Reads the node from .kern/evolved/<keyword>/, returns all the data
25
+ * needed to add it to the core package. Does NOT write to core files
26
+ * (that's the CLI's job — it knows the file paths).
27
+ */
28
+ export declare function promoteNode(keyword: string, baseDir?: string): {
29
+ success: boolean;
30
+ error?: string;
31
+ codegenTs?: string;
32
+ goldenKern?: string;
33
+ goldenOutput?: string;
34
+ definition?: EvolvedNodeDefinition;
35
+ };
36
+ export declare function removeFromManifest(evolvedDir: string, keyword: string): boolean;
@@ -0,0 +1,175 @@
1
+ /**
2
+ * Graduation — writes approved node proposals to .kern/evolved/.
3
+ *
4
+ * Compiles the codegen TypeScript source to JS, writes all files,
5
+ * updates the manifest, and makes the node available at next compile.
6
+ */
7
+ import { mkdirSync, writeFileSync, readFileSync, existsSync } from 'fs';
8
+ import { resolve, join } from 'path';
9
+ import { createHash } from 'crypto';
10
+ import { KERN_VERSION } from '@kernlang/core';
11
+ /**
12
+ * Graduate an approved proposal: write all files to .kern/evolved/<keyword>/.
13
+ */
14
+ export function graduateNode(proposal, compiledJs, graduatedBy = 'user', baseDir = process.cwd()) {
15
+ const evolvedDir = resolve(baseDir, '.kern', 'evolved');
16
+ const nodeDir = join(evolvedDir, proposal.keyword);
17
+ // Prevent overwriting existing graduated node
18
+ if (existsSync(nodeDir)) {
19
+ return { success: false, error: `Node '${proposal.keyword}' is already graduated. Rollback first.` };
20
+ }
21
+ try {
22
+ mkdirSync(nodeDir, { recursive: true });
23
+ // 1. Write codegen.js (pre-compiled)
24
+ writeFileSync(join(nodeDir, 'codegen.js'), compiledJs);
25
+ // 2. Write codegen.ts (source, for editing/review)
26
+ writeFileSync(join(nodeDir, 'codegen.ts'), proposal.codegenSource);
27
+ // 3. Write template.kern (golden input)
28
+ writeFileSync(join(nodeDir, 'template.kern'), proposal.kernExample);
29
+ // 4. Write expected-output.ts (golden output)
30
+ writeFileSync(join(nodeDir, 'expected-output.ts'), proposal.expectedOutput);
31
+ // 5. Write definition.json
32
+ const hash = 'sha256:' + createHash('sha256').update(compiledJs).digest('hex');
33
+ const definition = {
34
+ keyword: proposal.keyword,
35
+ displayName: proposal.displayName,
36
+ description: proposal.description,
37
+ props: proposal.props,
38
+ childTypes: proposal.childTypes,
39
+ parserHints: proposal.parserHints,
40
+ reason: proposal.reason,
41
+ hash,
42
+ graduatedBy,
43
+ graduatedAt: new Date().toISOString(),
44
+ evolveRunId: proposal.evolveRunId,
45
+ kernVersion: KERN_VERSION,
46
+ };
47
+ writeFileSync(join(nodeDir, 'definition.json'), JSON.stringify(definition, null, 2));
48
+ // 6. Write target overrides if any
49
+ if (proposal.targetOverrides) {
50
+ const targetsDir = join(nodeDir, 'targets');
51
+ mkdirSync(targetsDir, { recursive: true });
52
+ for (const [target, source] of Object.entries(proposal.targetOverrides)) {
53
+ writeFileSync(join(targetsDir, `${target}.js`), source);
54
+ }
55
+ }
56
+ // 7. Update manifest
57
+ updateManifest(evolvedDir, proposal.keyword, {
58
+ keyword: proposal.keyword,
59
+ displayName: proposal.displayName,
60
+ codegenTier: proposal.codegenTier,
61
+ childTypes: proposal.childTypes,
62
+ parserHints: proposal.parserHints,
63
+ hash,
64
+ graduatedBy,
65
+ graduatedAt: definition.graduatedAt,
66
+ evolveRunId: proposal.evolveRunId,
67
+ kernVersion: KERN_VERSION,
68
+ });
69
+ return { success: true, path: nodeDir };
70
+ }
71
+ catch (err) {
72
+ return { success: false, error: err.message };
73
+ }
74
+ }
75
+ /**
76
+ * Compile codegen TypeScript source to JavaScript for the sandbox.
77
+ * Strips types, imports, and converts to CommonJS.
78
+ */
79
+ export function compileCodegenToJS(tsSource) {
80
+ let js = tsSource;
81
+ // Strip import statements
82
+ js = js.replace(/import\s+.*?from\s+['"].*?['"];?\n?/g, '');
83
+ js = js.replace(/import\s+type\s+.*?from\s+['"].*?['"];?\n?/g, '');
84
+ // Convert export default function → module.exports = function
85
+ js = js.replace(/^export\s+default\s+function/m, 'module.exports = function');
86
+ js = js.replace(/^export\s+function\s+(\w+)/m, 'module.exports = function $1');
87
+ // If no module.exports yet, wrap
88
+ if (!js.includes('module.exports')) {
89
+ // Try to find a standalone function declaration
90
+ const fnMatch = js.match(/^function\s{1,20}\w+/m);
91
+ if (fnMatch) {
92
+ js = js.replace(/^(function\s{1,20}\w+)/m, 'module.exports = $1');
93
+ }
94
+ }
95
+ // Strip TypeScript type annotations from function signatures and variable declarations.
96
+ // Only match annotations that follow an identifier or closing paren — not inside strings.
97
+ // Pattern: (identifier|))\s*:\s*Type → strip the `: Type` part
98
+ js = js.replace(/(\w|\))\s*:\s*IRNode\b/g, '$1');
99
+ js = js.replace(/(\w|\))\s*:\s*string\[\]/g, '$1');
100
+ js = js.replace(/(\w|\))\s*:\s*string\b/g, '$1');
101
+ js = js.replace(/(\w|\))\s*:\s*number\b/g, '$1');
102
+ js = js.replace(/(\w|\))\s*:\s*boolean\b/g, '$1');
103
+ js = js.replace(/(\w|\))\s*:\s*void\b/g, '$1');
104
+ js = js.replace(/(\w|\))\s*:\s*any\b/g, '$1');
105
+ js = js.replace(/(\w|\))\s*:\s*CodegenHelpers\b/g, '$1');
106
+ js = js.replace(/(\w|\))\s*:\s*Record<[^>]+>/g, '$1');
107
+ js = js.replace(/<[A-Z]\w+(?:,\s?[A-Z]\w+){0,10}>/g, '');
108
+ js = js.replace(/\bas\s{1,20}\w+/g, '');
109
+ return js;
110
+ }
111
+ // ── Manifest management ──────────────────────────────────────────────────
112
+ function readManifest(evolvedDir) {
113
+ const manifestPath = join(evolvedDir, 'manifest.json');
114
+ if (!existsSync(manifestPath)) {
115
+ return { version: 1, nodes: {} };
116
+ }
117
+ try {
118
+ return JSON.parse(readFileSync(manifestPath, 'utf-8'));
119
+ }
120
+ catch {
121
+ return { version: 1, nodes: {} };
122
+ }
123
+ }
124
+ function writeManifest(evolvedDir, manifest) {
125
+ mkdirSync(evolvedDir, { recursive: true });
126
+ writeFileSync(join(evolvedDir, 'manifest.json'), JSON.stringify(manifest, null, 2));
127
+ }
128
+ function updateManifest(evolvedDir, keyword, entry) {
129
+ const manifest = readManifest(evolvedDir);
130
+ manifest.nodes[keyword] = entry;
131
+ writeManifest(evolvedDir, manifest);
132
+ }
133
+ /**
134
+ * Promote a graduated evolved node → core.
135
+ *
136
+ * Reads the node from .kern/evolved/<keyword>/, returns all the data
137
+ * needed to add it to the core package. Does NOT write to core files
138
+ * (that's the CLI's job — it knows the file paths).
139
+ */
140
+ export function promoteNode(keyword, baseDir = process.cwd()) {
141
+ const evolvedDir = resolve(baseDir, '.kern', 'evolved');
142
+ const nodeDir = join(evolvedDir, keyword);
143
+ if (!existsSync(nodeDir)) {
144
+ return { success: false, error: `Node '${keyword}' is not graduated` };
145
+ }
146
+ const codegenTsPath = join(nodeDir, 'codegen.ts');
147
+ const templateKernPath = join(nodeDir, 'template.kern');
148
+ const expectedOutputPath = join(nodeDir, 'expected-output.ts');
149
+ const defPath = join(nodeDir, 'definition.json');
150
+ if (!existsSync(codegenTsPath)) {
151
+ return { success: false, error: `Missing codegen.ts for '${keyword}'` };
152
+ }
153
+ if (!existsSync(defPath)) {
154
+ return { success: false, error: `Missing definition.json for '${keyword}'` };
155
+ }
156
+ try {
157
+ const codegenTs = readFileSync(codegenTsPath, 'utf-8');
158
+ const goldenKern = existsSync(templateKernPath) ? readFileSync(templateKernPath, 'utf-8') : undefined;
159
+ const goldenOutput = existsSync(expectedOutputPath) ? readFileSync(expectedOutputPath, 'utf-8') : undefined;
160
+ const definition = JSON.parse(readFileSync(defPath, 'utf-8'));
161
+ return { success: true, codegenTs, goldenKern, goldenOutput, definition };
162
+ }
163
+ catch (err) {
164
+ return { success: false, error: err.message };
165
+ }
166
+ }
167
+ export function removeFromManifest(evolvedDir, keyword) {
168
+ const manifest = readManifest(evolvedDir);
169
+ if (!manifest.nodes[keyword])
170
+ return false;
171
+ delete manifest.nodes[keyword];
172
+ writeManifest(evolvedDir, manifest);
173
+ return true;
174
+ }
175
+ //# sourceMappingURL=graduation.js.map