flex-md 3.5.0 → 4.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.
- package/README.md +423 -39
- package/dist/index.cjs +62 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/ofs/parser.js +31 -10
- package/dist/tokens/auto-fix.d.ts +10 -0
- package/dist/tokens/auto-fix.js +56 -0
- package/dist/tokens/cognitive-cost.d.ts +10 -0
- package/dist/tokens/cognitive-cost.js +205 -0
- package/dist/tokens/compliance.d.ts +10 -0
- package/dist/tokens/compliance.js +70 -0
- package/dist/tokens/confidence.d.ts +6 -0
- package/dist/tokens/confidence.js +332 -0
- package/dist/tokens/estimator.d.ts +12 -0
- package/dist/tokens/estimator.js +138 -0
- package/dist/tokens/improvements.d.ts +10 -0
- package/dist/tokens/improvements.js +697 -0
- package/dist/tokens/index.d.ts +24 -0
- package/dist/tokens/index.js +31 -0
- package/dist/tokens/parser.d.ts +3 -0
- package/dist/tokens/parser.js +97 -0
- package/dist/tokens/patterns.d.ts +9 -0
- package/dist/tokens/patterns.js +20 -0
- package/dist/tokens/smart-report.d.ts +10 -0
- package/dist/tokens/smart-report.js +187 -0
- package/dist/tokens/spec-estimator.d.ts +7 -0
- package/dist/tokens/spec-estimator.js +68 -0
- package/dist/tokens/types.d.ts +185 -0
- package/dist/tokens/validator.d.ts +16 -0
- package/dist/tokens/validator.js +59 -0
- package/docs/Recommended New Strategies for AI Request Builder.md +691 -0
- package/package.json +5 -4
- package/dist/detection/detector.d.ts +0 -6
- package/dist/detection/detector.js +0 -104
- package/dist/detection/extractor.d.ts +0 -10
- package/dist/detection/extractor.js +0 -54
- package/dist/issues/build.d.ts +0 -26
- package/dist/issues/build.js +0 -62
- package/dist/md/lists.d.ts +0 -14
- package/dist/md/lists.js +0 -33
- package/dist/md/tables.d.ts +0 -25
- package/dist/md/tables.js +0 -72
- package/dist/ofs/extractor.d.ts +0 -9
- package/dist/ofs/extractor.js +0 -75
- package/dist/ofs/issues.d.ts +0 -14
- package/dist/ofs/issues.js +0 -92
- package/dist/ofs/validator.d.ts +0 -10
- package/dist/ofs/validator.js +0 -91
- package/dist/outline/builder.d.ts +0 -10
- package/dist/outline/builder.js +0 -85
- package/dist/outline/renderer.d.ts +0 -6
- package/dist/outline/renderer.js +0 -23
- package/dist/parser.d.ts +0 -2
- package/dist/parser.js +0 -199
- package/dist/parsers/lists.d.ts +0 -6
- package/dist/parsers/lists.js +0 -36
- package/dist/parsers/tables.d.ts +0 -10
- package/dist/parsers/tables.js +0 -58
- package/dist/stringify.d.ts +0 -2
- package/dist/stringify.js +0 -110
- package/dist/test-pipeline.js +0 -53
- package/dist/test-runner.d.ts +0 -1
- package/dist/test-runner.js +0 -331
- package/dist/test-strictness.d.ts +0 -1
- package/dist/test-strictness.js +0 -213
- package/dist/util.d.ts +0 -5
- package/dist/util.js +0 -64
- package/dist/validate/policy.d.ts +0 -10
- package/dist/validate/policy.js +0 -17
- package/dist/validator.d.ts +0 -2
- package/dist/validator.js +0 -80
- /package/dist/{test-pipeline.d.ts → tokens/types.js} +0 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import { parseSystemPart } from './parser.js';
|
|
2
|
+
import { detectSystemPartLevel } from './compliance.js';
|
|
3
|
+
/**
|
|
4
|
+
* Calculate confidence in token estimation
|
|
5
|
+
*/
|
|
6
|
+
export function calculateConfidence(spec) {
|
|
7
|
+
const bySection = [];
|
|
8
|
+
const factors = [];
|
|
9
|
+
let totalConfidence = 0;
|
|
10
|
+
let totalWeight = 0;
|
|
11
|
+
// Analyze each section
|
|
12
|
+
for (const section of spec.sections) {
|
|
13
|
+
const sectionConf = analyzeSectionConfidence(section);
|
|
14
|
+
bySection.push(sectionConf);
|
|
15
|
+
// Weight required sections more heavily
|
|
16
|
+
const weight = section.required !== false ? 2 : 1;
|
|
17
|
+
totalConfidence += sectionConf.confidence * weight;
|
|
18
|
+
totalWeight += weight;
|
|
19
|
+
}
|
|
20
|
+
// Calculate coverage factor
|
|
21
|
+
const coverageFactor = calculateCoverageFactor(spec);
|
|
22
|
+
factors.push(coverageFactor);
|
|
23
|
+
// Calculate specificity factor
|
|
24
|
+
const specificityFactor = calculateSpecificityFactor(bySection);
|
|
25
|
+
factors.push(specificityFactor);
|
|
26
|
+
// Calculate consistency factor
|
|
27
|
+
const consistencyFactor = calculateConsistencyFactor(bySection);
|
|
28
|
+
factors.push(consistencyFactor);
|
|
29
|
+
// Calculate required coverage factor
|
|
30
|
+
const requiredFactor = calculateRequiredCoverageFactor(spec, bySection);
|
|
31
|
+
factors.push(requiredFactor);
|
|
32
|
+
// Combine all factors
|
|
33
|
+
let overall = totalWeight > 0 ? totalConfidence / totalWeight : 0;
|
|
34
|
+
// Apply factor adjustments
|
|
35
|
+
for (const factor of factors) {
|
|
36
|
+
overall += factor.score;
|
|
37
|
+
}
|
|
38
|
+
overall = Math.max(0, Math.min(100, overall));
|
|
39
|
+
return {
|
|
40
|
+
overall,
|
|
41
|
+
grade: getConfidenceGrade(overall),
|
|
42
|
+
bySection,
|
|
43
|
+
factors,
|
|
44
|
+
recommendations: generateConfidenceRecommendations(overall, factors, bySection)
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Analyze confidence for individual section
|
|
49
|
+
*/
|
|
50
|
+
function analyzeSectionConfidence(section) {
|
|
51
|
+
const kind = section.kind || 'text';
|
|
52
|
+
const systemPart = parseSystemPart(section.instruction, kind);
|
|
53
|
+
const hasSystemPart = systemPart !== null;
|
|
54
|
+
const level = systemPart ? detectSystemPartLevel(systemPart, kind) : null;
|
|
55
|
+
let confidence = 0;
|
|
56
|
+
let quality = 'none';
|
|
57
|
+
if (systemPart && level !== null) {
|
|
58
|
+
// Base confidence from level
|
|
59
|
+
const levelConfidence = { 0: 0, 1: 60, 2: 80, 3: 90 };
|
|
60
|
+
confidence = levelConfidence[level] || 0;
|
|
61
|
+
// Adjust based on section characteristics
|
|
62
|
+
const adjustment = getConfidenceAdjustment(systemPart, section);
|
|
63
|
+
confidence += adjustment;
|
|
64
|
+
confidence = Math.max(0, Math.min(100, confidence));
|
|
65
|
+
// Determine quality
|
|
66
|
+
if (confidence >= 85)
|
|
67
|
+
quality = 'excellent';
|
|
68
|
+
else if (confidence >= 70)
|
|
69
|
+
quality = 'good';
|
|
70
|
+
else if (confidence >= 50)
|
|
71
|
+
quality = 'fair';
|
|
72
|
+
else
|
|
73
|
+
quality = 'poor';
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
sectionName: section.name,
|
|
77
|
+
confidence,
|
|
78
|
+
hasSystemPart,
|
|
79
|
+
level,
|
|
80
|
+
estimateQuality: quality
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function getConfidenceAdjustment(systemPart, _section) {
|
|
84
|
+
let adjustment = 0;
|
|
85
|
+
const { parsed } = systemPart;
|
|
86
|
+
switch (parsed.type) {
|
|
87
|
+
case 'length':
|
|
88
|
+
// Specific values are better than vague ones
|
|
89
|
+
if (['1 sentence', '1-2 sentences', '1 paragraph', '2-3 paragraphs'].includes(parsed.value)) {
|
|
90
|
+
adjustment += 10; // Concrete measurements
|
|
91
|
+
}
|
|
92
|
+
break;
|
|
93
|
+
case 'items':
|
|
94
|
+
// Exact numbers or tight ranges are better
|
|
95
|
+
if (parsed.max !== null) {
|
|
96
|
+
const range = parsed.max - parsed.min;
|
|
97
|
+
if (range <= 2)
|
|
98
|
+
adjustment += 10; // Tight range
|
|
99
|
+
else if (range <= 5)
|
|
100
|
+
adjustment += 5; // Reasonable range
|
|
101
|
+
}
|
|
102
|
+
else if (!parsed.atLeast) {
|
|
103
|
+
adjustment += 10; // Exact number
|
|
104
|
+
}
|
|
105
|
+
break;
|
|
106
|
+
case 'table':
|
|
107
|
+
// Both dimensions specified tightly
|
|
108
|
+
const rowsExact = parsed.rows.max === null && !parsed.rows.atLeast;
|
|
109
|
+
const colsExact = parsed.columns.max === null && !parsed.columns.atLeast;
|
|
110
|
+
if (rowsExact && colsExact) {
|
|
111
|
+
adjustment += 10; // Both exact
|
|
112
|
+
}
|
|
113
|
+
else if (rowsExact || colsExact) {
|
|
114
|
+
adjustment += 5; // One exact
|
|
115
|
+
}
|
|
116
|
+
break;
|
|
117
|
+
case 'lines':
|
|
118
|
+
// Non-approximate is better
|
|
119
|
+
if (!parsed.approximate) {
|
|
120
|
+
adjustment += 5;
|
|
121
|
+
}
|
|
122
|
+
if (parsed.max !== null) {
|
|
123
|
+
const range = parsed.max - parsed.min;
|
|
124
|
+
if (range <= 10)
|
|
125
|
+
adjustment += 5; // Tight range
|
|
126
|
+
}
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
// Penalty for very vague patterns
|
|
130
|
+
if (parsed.type === 'items' && parsed.atLeast && parsed.min < 3) {
|
|
131
|
+
adjustment -= 10; // "at least 1" is very vague
|
|
132
|
+
}
|
|
133
|
+
return adjustment;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Calculate coverage factor (what % of sections have system parts)
|
|
137
|
+
*/
|
|
138
|
+
function calculateCoverageFactor(spec) {
|
|
139
|
+
const total = spec.sections.length;
|
|
140
|
+
const withSystemParts = spec.sections.filter(s => parseSystemPart(s.instruction, s.kind || 'text') !== null).length;
|
|
141
|
+
const coverage = total > 0 ? (withSystemParts / total) * 100 : 0;
|
|
142
|
+
let score = 0;
|
|
143
|
+
let impact = 'neutral';
|
|
144
|
+
if (coverage >= 90) {
|
|
145
|
+
score = 10;
|
|
146
|
+
impact = 'positive';
|
|
147
|
+
}
|
|
148
|
+
else if (coverage >= 70) {
|
|
149
|
+
score = 5;
|
|
150
|
+
impact = 'positive';
|
|
151
|
+
}
|
|
152
|
+
else if (coverage < 50) {
|
|
153
|
+
score = -10;
|
|
154
|
+
impact = 'negative';
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
score = -5;
|
|
158
|
+
impact = 'negative';
|
|
159
|
+
}
|
|
160
|
+
return {
|
|
161
|
+
factor: 'coverage',
|
|
162
|
+
impact,
|
|
163
|
+
score,
|
|
164
|
+
description: `${coverage.toFixed(0)}% of sections have system parts`
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Calculate specificity factor (are system parts specific or vague?)
|
|
169
|
+
*/
|
|
170
|
+
function calculateSpecificityFactor(sections) {
|
|
171
|
+
const withSystemParts = sections.filter(s => s.hasSystemPart);
|
|
172
|
+
if (withSystemParts.length === 0) {
|
|
173
|
+
return {
|
|
174
|
+
factor: 'specificity',
|
|
175
|
+
impact: 'negative',
|
|
176
|
+
score: -15,
|
|
177
|
+
description: 'No system parts to evaluate'
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
const avgLevel = withSystemParts.reduce((sum, s) => sum + (s.level || 0), 0) / withSystemParts.length;
|
|
181
|
+
let score = 0;
|
|
182
|
+
let impact = 'neutral';
|
|
183
|
+
if (avgLevel >= 2.5) {
|
|
184
|
+
score = 10;
|
|
185
|
+
impact = 'positive';
|
|
186
|
+
}
|
|
187
|
+
else if (avgLevel >= 2) {
|
|
188
|
+
score = 5;
|
|
189
|
+
impact = 'positive';
|
|
190
|
+
}
|
|
191
|
+
else if (avgLevel < 1.5) {
|
|
192
|
+
score = -5;
|
|
193
|
+
impact = 'negative';
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
factor: 'specificity',
|
|
197
|
+
impact,
|
|
198
|
+
score,
|
|
199
|
+
description: `Average specificity level: L${avgLevel.toFixed(1)}`
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Calculate consistency factor (are all sections at similar level?)
|
|
204
|
+
*/
|
|
205
|
+
function calculateConsistencyFactor(sections) {
|
|
206
|
+
const levels = sections
|
|
207
|
+
.filter(s => s.level !== null)
|
|
208
|
+
.map(s => s.level);
|
|
209
|
+
if (levels.length === 0) {
|
|
210
|
+
return {
|
|
211
|
+
factor: 'consistency',
|
|
212
|
+
impact: 'neutral',
|
|
213
|
+
score: 0,
|
|
214
|
+
description: 'No levels to compare'
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
const min = Math.min(...levels);
|
|
218
|
+
const max = Math.max(...levels);
|
|
219
|
+
const variance = max - min;
|
|
220
|
+
let score = 0;
|
|
221
|
+
let impact = 'neutral';
|
|
222
|
+
if (variance === 0) {
|
|
223
|
+
score = 5;
|
|
224
|
+
impact = 'positive';
|
|
225
|
+
}
|
|
226
|
+
else if (variance >= 3) {
|
|
227
|
+
score = -5;
|
|
228
|
+
impact = 'negative';
|
|
229
|
+
}
|
|
230
|
+
return {
|
|
231
|
+
factor: 'consistency',
|
|
232
|
+
impact,
|
|
233
|
+
score,
|
|
234
|
+
description: variance === 0
|
|
235
|
+
? 'All sections at same level (consistent)'
|
|
236
|
+
: `Level variance: ${variance} (${min} to ${max})`
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Calculate required coverage (do all required sections have good system parts?)
|
|
241
|
+
*/
|
|
242
|
+
function calculateRequiredCoverageFactor(spec, sections) {
|
|
243
|
+
const requiredSections = spec.sections.filter(s => s.required !== false);
|
|
244
|
+
if (requiredSections.length === 0) {
|
|
245
|
+
return {
|
|
246
|
+
factor: 'required_coverage',
|
|
247
|
+
impact: 'neutral',
|
|
248
|
+
score: 0,
|
|
249
|
+
description: 'No required sections'
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
const requiredWithSystemParts = sections.filter(s => {
|
|
253
|
+
const section = spec.sections.find(sec => sec.name === s.sectionName);
|
|
254
|
+
return section?.required !== false && s.hasSystemPart;
|
|
255
|
+
}).length;
|
|
256
|
+
const coverage = (requiredWithSystemParts / requiredSections.length) * 100;
|
|
257
|
+
let score = 0;
|
|
258
|
+
let impact = 'neutral';
|
|
259
|
+
if (coverage === 100) {
|
|
260
|
+
score = 15;
|
|
261
|
+
impact = 'positive';
|
|
262
|
+
}
|
|
263
|
+
else if (coverage >= 80) {
|
|
264
|
+
score = 5;
|
|
265
|
+
impact = 'positive';
|
|
266
|
+
}
|
|
267
|
+
else if (coverage < 50) {
|
|
268
|
+
score = -15;
|
|
269
|
+
impact = 'negative';
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
score = -5;
|
|
273
|
+
impact = 'negative';
|
|
274
|
+
}
|
|
275
|
+
return {
|
|
276
|
+
factor: 'required_coverage',
|
|
277
|
+
impact,
|
|
278
|
+
score,
|
|
279
|
+
description: `${coverage.toFixed(0)}% of required sections have system parts`
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
function getConfidenceGrade(score) {
|
|
283
|
+
if (score >= 90)
|
|
284
|
+
return 'A';
|
|
285
|
+
if (score >= 80)
|
|
286
|
+
return 'B';
|
|
287
|
+
if (score >= 70)
|
|
288
|
+
return 'C';
|
|
289
|
+
if (score >= 60)
|
|
290
|
+
return 'D';
|
|
291
|
+
return 'F';
|
|
292
|
+
}
|
|
293
|
+
function generateConfidenceRecommendations(overall, factors, sections) {
|
|
294
|
+
const recommendations = [];
|
|
295
|
+
// Overall quality
|
|
296
|
+
if (overall < 70) {
|
|
297
|
+
recommendations.push('Overall confidence is low. Consider adding more specific system parts.');
|
|
298
|
+
}
|
|
299
|
+
// Check negative factors
|
|
300
|
+
for (const factor of factors) {
|
|
301
|
+
if (factor.impact === 'negative') {
|
|
302
|
+
switch (factor.factor) {
|
|
303
|
+
case 'coverage':
|
|
304
|
+
recommendations.push('Add system parts to more sections to improve coverage.');
|
|
305
|
+
break;
|
|
306
|
+
case 'specificity':
|
|
307
|
+
recommendations.push('Upgrade some L1 system parts to L2 for better precision.');
|
|
308
|
+
break;
|
|
309
|
+
case 'consistency':
|
|
310
|
+
recommendations.push('Consider standardizing compliance level across sections.');
|
|
311
|
+
break;
|
|
312
|
+
case 'required_coverage':
|
|
313
|
+
recommendations.push('Ensure all required sections have system parts.');
|
|
314
|
+
break;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
// Check poor quality sections
|
|
319
|
+
const poorSections = sections.filter(s => s.estimateQuality === 'poor' || s.estimateQuality === 'none');
|
|
320
|
+
if (poorSections.length > 0) {
|
|
321
|
+
const names = poorSections.map(s => s.sectionName).join(', ');
|
|
322
|
+
recommendations.push(`Improve system parts for: ${names}`);
|
|
323
|
+
}
|
|
324
|
+
// Positive feedback
|
|
325
|
+
if (overall >= 90) {
|
|
326
|
+
recommendations.push('Excellent! Token estimation will be highly accurate.');
|
|
327
|
+
}
|
|
328
|
+
else if (overall >= 80) {
|
|
329
|
+
recommendations.push('Good confidence. Minor improvements possible.');
|
|
330
|
+
}
|
|
331
|
+
return recommendations;
|
|
332
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { SystemPart, TokenEstimate, LengthValue } from './types.js';
|
|
2
|
+
import type { SectionKind } from '../types.js';
|
|
3
|
+
export declare const TOKEN_CONSTANTS: {
|
|
4
|
+
readonly lengths: Record<LengthValue, number>;
|
|
5
|
+
readonly perItem: 40;
|
|
6
|
+
readonly perTableCell: 20;
|
|
7
|
+
readonly perCodeLine: 5;
|
|
8
|
+
readonly headingOverhead: 10;
|
|
9
|
+
readonly baseOverhead: 50;
|
|
10
|
+
};
|
|
11
|
+
export declare function estimateTokens(systemPart: SystemPart): TokenEstimate;
|
|
12
|
+
export declare function getFallbackEstimate(kind: SectionKind, required: boolean): TokenEstimate;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// Calibrated token constants
|
|
2
|
+
export const TOKEN_CONSTANTS = {
|
|
3
|
+
// Text lengths (enumerated)
|
|
4
|
+
lengths: {
|
|
5
|
+
'1 sentence': 25,
|
|
6
|
+
'1-2 sentences': 40,
|
|
7
|
+
'1 paragraph': 100,
|
|
8
|
+
'2-3 paragraphs': 250,
|
|
9
|
+
'brief': 75,
|
|
10
|
+
'moderate': 150,
|
|
11
|
+
'detailed': 300,
|
|
12
|
+
'extensive': 500
|
|
13
|
+
},
|
|
14
|
+
// Per-unit estimates
|
|
15
|
+
perItem: 40,
|
|
16
|
+
perTableCell: 20,
|
|
17
|
+
perCodeLine: 5,
|
|
18
|
+
// Overhead
|
|
19
|
+
headingOverhead: 10,
|
|
20
|
+
baseOverhead: 50
|
|
21
|
+
};
|
|
22
|
+
export function estimateTokens(systemPart) {
|
|
23
|
+
const { parsed } = systemPart;
|
|
24
|
+
switch (parsed.type) {
|
|
25
|
+
case 'length':
|
|
26
|
+
return estimateTextLength(parsed.value);
|
|
27
|
+
case 'items':
|
|
28
|
+
return estimateItems(parsed);
|
|
29
|
+
case 'table':
|
|
30
|
+
return estimateTable(parsed);
|
|
31
|
+
case 'lines':
|
|
32
|
+
return estimateCodeLines(parsed);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function estimateTextLength(value) {
|
|
36
|
+
const estimated = TOKEN_CONSTANTS.lengths[value];
|
|
37
|
+
return {
|
|
38
|
+
estimated,
|
|
39
|
+
min: Math.floor(estimated * 0.7),
|
|
40
|
+
max: Math.ceil(estimated * 1.3),
|
|
41
|
+
confidence: 'high'
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function estimateItems(parsed) {
|
|
45
|
+
const { min, max, atLeast } = parsed;
|
|
46
|
+
if (max !== null) {
|
|
47
|
+
// Range: "3-5" → use average
|
|
48
|
+
const avg = (min + max) / 2;
|
|
49
|
+
const estimated = Math.round(avg * TOKEN_CONSTANTS.perItem);
|
|
50
|
+
return {
|
|
51
|
+
estimated,
|
|
52
|
+
min: min * TOKEN_CONSTANTS.perItem,
|
|
53
|
+
max: max * TOKEN_CONSTANTS.perItem,
|
|
54
|
+
confidence: 'high'
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
if (atLeast) {
|
|
58
|
+
// "at least 3" → estimate at min + 50%
|
|
59
|
+
const estimated = Math.round(min * 1.5 * TOKEN_CONSTANTS.perItem);
|
|
60
|
+
return {
|
|
61
|
+
estimated,
|
|
62
|
+
min: min * TOKEN_CONSTANTS.perItem,
|
|
63
|
+
max: min * 3 * TOKEN_CONSTANTS.perItem,
|
|
64
|
+
confidence: 'medium'
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
// Exact: "3" → use exact value
|
|
68
|
+
const estimated = min * TOKEN_CONSTANTS.perItem;
|
|
69
|
+
return {
|
|
70
|
+
estimated,
|
|
71
|
+
min: Math.floor(estimated * 0.8),
|
|
72
|
+
max: Math.ceil(estimated * 1.2),
|
|
73
|
+
confidence: 'high'
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function estimateTable(parsed) {
|
|
77
|
+
const rowAvg = parsed.rows.max
|
|
78
|
+
? (parsed.rows.min + parsed.rows.max) / 2
|
|
79
|
+
: parsed.rows.atLeast
|
|
80
|
+
? parsed.rows.min * 1.5
|
|
81
|
+
: parsed.rows.min;
|
|
82
|
+
const colAvg = parsed.columns.max
|
|
83
|
+
? (parsed.columns.min + parsed.columns.max) / 2
|
|
84
|
+
: parsed.columns.atLeast
|
|
85
|
+
? parsed.columns.min * 1.5
|
|
86
|
+
: parsed.columns.min;
|
|
87
|
+
const estimated = Math.round(rowAvg * colAvg * TOKEN_CONSTANTS.perTableCell);
|
|
88
|
+
const minCells = parsed.rows.min * parsed.columns.min;
|
|
89
|
+
const maxRows = parsed.rows.max || (parsed.rows.atLeast ? parsed.rows.min * 2 : parsed.rows.min);
|
|
90
|
+
const maxCols = parsed.columns.max || (parsed.columns.atLeast ? parsed.columns.min * 2 : parsed.columns.min);
|
|
91
|
+
const maxCells = maxRows * maxCols;
|
|
92
|
+
return {
|
|
93
|
+
estimated,
|
|
94
|
+
min: minCells * TOKEN_CONSTANTS.perTableCell,
|
|
95
|
+
max: maxCells * TOKEN_CONSTANTS.perTableCell,
|
|
96
|
+
confidence: (parsed.rows.max && parsed.columns.max) ? 'high' : 'medium'
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function estimateCodeLines(parsed) {
|
|
100
|
+
const { min, max, approximate } = parsed;
|
|
101
|
+
if (max !== null) {
|
|
102
|
+
// Range: "10-20"
|
|
103
|
+
const avg = (min + max) / 2;
|
|
104
|
+
const estimated = Math.round(avg * TOKEN_CONSTANTS.perCodeLine);
|
|
105
|
+
return {
|
|
106
|
+
estimated,
|
|
107
|
+
min: min * TOKEN_CONSTANTS.perCodeLine,
|
|
108
|
+
max: max * TOKEN_CONSTANTS.perCodeLine,
|
|
109
|
+
confidence: 'high'
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
// Exact or approximate: "10" or "~10"
|
|
113
|
+
const estimated = min * TOKEN_CONSTANTS.perCodeLine;
|
|
114
|
+
const variance = approximate ? 0.3 : 0.15;
|
|
115
|
+
return {
|
|
116
|
+
estimated,
|
|
117
|
+
min: Math.floor(estimated * (1 - variance)),
|
|
118
|
+
max: Math.ceil(estimated * (1 + variance)),
|
|
119
|
+
confidence: approximate ? 'medium' : 'high'
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
// Fallback for sections without system parts
|
|
123
|
+
export function getFallbackEstimate(kind, required) {
|
|
124
|
+
const fallbacks = {
|
|
125
|
+
text: 150,
|
|
126
|
+
list: 200,
|
|
127
|
+
'ordered-list': 200,
|
|
128
|
+
table: 300,
|
|
129
|
+
code: 400
|
|
130
|
+
};
|
|
131
|
+
const base = fallbacks[kind] || 150;
|
|
132
|
+
return {
|
|
133
|
+
estimated: base,
|
|
134
|
+
min: Math.floor(base * 0.5),
|
|
135
|
+
max: Math.ceil(base * 2),
|
|
136
|
+
confidence: 'low'
|
|
137
|
+
};
|
|
138
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ImprovementAnalysis, ComplianceLevel } from './types.js';
|
|
2
|
+
import type { OutputFormatSpec } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Main improvement detector
|
|
5
|
+
*/
|
|
6
|
+
export declare function detectImprovements(spec: OutputFormatSpec, targetLevel?: ComplianceLevel): ImprovementAnalysis;
|
|
7
|
+
/**
|
|
8
|
+
* Format improvement report
|
|
9
|
+
*/
|
|
10
|
+
export declare function formatImprovementReport(analysis: ImprovementAnalysis): string;
|