flex-md 3.5.0 → 4.1.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 (74) hide show
  1. package/README.md +423 -39
  2. package/dist/index.cjs +62 -3
  3. package/dist/index.d.ts +1 -0
  4. package/dist/index.js +2 -0
  5. package/dist/md/parse.d.ts +1 -0
  6. package/dist/md/parse.js +12 -0
  7. package/dist/ofs/parser.js +31 -10
  8. package/dist/tokens/auto-fix.d.ts +10 -0
  9. package/dist/tokens/auto-fix.js +56 -0
  10. package/dist/tokens/cognitive-cost.d.ts +10 -0
  11. package/dist/tokens/cognitive-cost.js +205 -0
  12. package/dist/tokens/compliance.d.ts +10 -0
  13. package/dist/tokens/compliance.js +70 -0
  14. package/dist/tokens/confidence.d.ts +6 -0
  15. package/dist/tokens/confidence.js +332 -0
  16. package/dist/tokens/estimator.d.ts +12 -0
  17. package/dist/tokens/estimator.js +138 -0
  18. package/dist/tokens/improvements.d.ts +10 -0
  19. package/dist/tokens/improvements.js +697 -0
  20. package/dist/tokens/index.d.ts +24 -0
  21. package/dist/tokens/index.js +31 -0
  22. package/dist/tokens/parser.d.ts +3 -0
  23. package/dist/tokens/parser.js +97 -0
  24. package/dist/tokens/patterns.d.ts +9 -0
  25. package/dist/tokens/patterns.js +20 -0
  26. package/dist/tokens/smart-report.d.ts +10 -0
  27. package/dist/tokens/smart-report.js +187 -0
  28. package/dist/tokens/spec-estimator.d.ts +7 -0
  29. package/dist/tokens/spec-estimator.js +68 -0
  30. package/dist/tokens/types.d.ts +185 -0
  31. package/dist/tokens/validator.d.ts +16 -0
  32. package/dist/tokens/validator.js +59 -0
  33. package/docs/Recommended New Strategies for AI Request Builder.md +691 -0
  34. package/package.json +7 -3
  35. package/dist/detection/detector.d.ts +0 -6
  36. package/dist/detection/detector.js +0 -104
  37. package/dist/detection/extractor.d.ts +0 -10
  38. package/dist/detection/extractor.js +0 -54
  39. package/dist/issues/build.d.ts +0 -26
  40. package/dist/issues/build.js +0 -62
  41. package/dist/md/lists.d.ts +0 -14
  42. package/dist/md/lists.js +0 -33
  43. package/dist/md/tables.d.ts +0 -25
  44. package/dist/md/tables.js +0 -72
  45. package/dist/ofs/extractor.d.ts +0 -9
  46. package/dist/ofs/extractor.js +0 -75
  47. package/dist/ofs/issues.d.ts +0 -14
  48. package/dist/ofs/issues.js +0 -92
  49. package/dist/ofs/validator.d.ts +0 -10
  50. package/dist/ofs/validator.js +0 -91
  51. package/dist/outline/builder.d.ts +0 -10
  52. package/dist/outline/builder.js +0 -85
  53. package/dist/outline/renderer.d.ts +0 -6
  54. package/dist/outline/renderer.js +0 -23
  55. package/dist/parser.d.ts +0 -2
  56. package/dist/parser.js +0 -199
  57. package/dist/parsers/lists.d.ts +0 -6
  58. package/dist/parsers/lists.js +0 -36
  59. package/dist/parsers/tables.d.ts +0 -10
  60. package/dist/parsers/tables.js +0 -58
  61. package/dist/stringify.d.ts +0 -2
  62. package/dist/stringify.js +0 -110
  63. package/dist/test-pipeline.js +0 -53
  64. package/dist/test-runner.d.ts +0 -1
  65. package/dist/test-runner.js +0 -331
  66. package/dist/test-strictness.d.ts +0 -1
  67. package/dist/test-strictness.js +0 -213
  68. package/dist/util.d.ts +0 -5
  69. package/dist/util.js +0 -64
  70. package/dist/validate/policy.d.ts +0 -10
  71. package/dist/validate/policy.js +0 -17
  72. package/dist/validator.d.ts +0 -2
  73. package/dist/validator.js +0 -80
  74. /package/dist/{test-pipeline.d.ts → tokens/types.js} +0 -0
@@ -0,0 +1,697 @@
1
+ import { parseSystemPart } from './parser.js';
2
+ import { detectSystemPartLevel } from './compliance.js';
3
+ /**
4
+ * Main improvement detector
5
+ */
6
+ export function detectImprovements(spec, targetLevel = 2) {
7
+ const improvements = [];
8
+ const antiPatterns = [];
9
+ const optimizations = [];
10
+ // 1. Check each section for improvements
11
+ for (const section of spec.sections) {
12
+ improvements.push(...detectSectionImprovements(section, targetLevel));
13
+ antiPatterns.push(...detectSectionAntiPatterns(section));
14
+ }
15
+ // 2. Detect spec-level optimizations
16
+ optimizations.push(...detectSpecOptimizations(spec));
17
+ // 3. Identify quick wins
18
+ const quickWins = improvements.filter(imp => imp.effort === 'trivial' || imp.effort === 'easy').filter(imp => imp.impact.confidenceGain >= 10 || imp.impact.estimateAccuracyGain >= 10);
19
+ // 4. Calculate summary
20
+ const summary = calculateImprovementSummary(improvements, quickWins);
21
+ return {
22
+ improvements: improvements.sort(byPriorityAndImpact),
23
+ quickWins: quickWins.sort(byImpact),
24
+ antiPatterns,
25
+ optimizations,
26
+ summary
27
+ };
28
+ }
29
+ /**
30
+ * Detect improvements for individual section
31
+ */
32
+ function detectSectionImprovements(section, targetLevel) {
33
+ const improvements = [];
34
+ const kind = (section.kind || 'text');
35
+ const systemPart = parseSystemPart(section.instruction, kind);
36
+ const currentLevel = systemPart ? detectSystemPartLevel(systemPart, kind) : null;
37
+ // 1. Missing system part
38
+ if (!systemPart) {
39
+ improvements.push({
40
+ sectionName: section.name,
41
+ priority: section.required !== false ? 'critical' : 'high',
42
+ category: 'missing',
43
+ issue: 'No system part present',
44
+ impact: {
45
+ confidenceGain: 20,
46
+ cognitiveLoadChange: targetLevel === 1 ? 1 : targetLevel === 2 ? 3 : 5,
47
+ estimateAccuracyGain: 25,
48
+ description: 'Token estimation will use fallback values without system part'
49
+ },
50
+ suggestion: `Add system part for ${kind} section`,
51
+ before: section.instruction || '(no instruction)',
52
+ after: generateSuggestedInstruction(section, targetLevel),
53
+ effort: 'easy',
54
+ autoFixable: true
55
+ });
56
+ return improvements;
57
+ }
58
+ // 2. Vague or overly simple values
59
+ const vagueness = detectVagueness(systemPart, kind);
60
+ if (vagueness) {
61
+ vagueness.sectionName = section.name; // Fill in section name
62
+ improvements.push(vagueness);
63
+ }
64
+ // 3. Insufficient level for target
65
+ if (currentLevel !== null && currentLevel < targetLevel) {
66
+ improvements.push({
67
+ sectionName: section.name,
68
+ priority: section.required !== false ? 'high' : 'medium',
69
+ category: 'upgrade',
70
+ issue: `Current level (L${currentLevel}) below target (L${targetLevel})`,
71
+ impact: {
72
+ confidenceGain: (targetLevel - currentLevel) * 8,
73
+ cognitiveLoadChange: (targetLevel - currentLevel) * 2,
74
+ estimateAccuracyGain: (targetLevel - currentLevel) * 10,
75
+ description: `Upgrading to L${targetLevel} will improve estimation accuracy`
76
+ },
77
+ suggestion: `Upgrade from L${currentLevel} to L${targetLevel}`,
78
+ before: section.instruction,
79
+ after: upgradeInstruction(section, systemPart, currentLevel, targetLevel),
80
+ effort: targetLevel - currentLevel === 1 ? 'easy' : 'moderate',
81
+ autoFixable: true
82
+ });
83
+ }
84
+ // 4. Invalid ranges
85
+ const rangeIssue = detectRangeIssues(systemPart, section);
86
+ if (rangeIssue) {
87
+ improvements.push(rangeIssue);
88
+ }
89
+ // 5. Unreasonable values
90
+ const unreasonable = detectUnreasonableValues(systemPart, section);
91
+ if (unreasonable) {
92
+ improvements.push(unreasonable);
93
+ }
94
+ // 6. Missing guidance part
95
+ if (!systemPart.guidance && kind !== 'table' && kind !== 'ordered_table') {
96
+ improvements.push({
97
+ sectionName: section.name,
98
+ priority: 'low',
99
+ category: 'optimize',
100
+ issue: 'System part exists but no guidance for LLM',
101
+ impact: {
102
+ confidenceGain: 0,
103
+ cognitiveLoadChange: 1,
104
+ estimateAccuracyGain: 0,
105
+ description: 'Adding guidance helps LLM understand intent better'
106
+ },
107
+ suggestion: 'Add guidance after system part',
108
+ before: section.instruction,
109
+ after: `${systemPart.raw}. ${generateDefaultGuidance(kind)}`,
110
+ effort: 'easy',
111
+ autoFixable: true
112
+ });
113
+ }
114
+ return improvements;
115
+ }
116
+ /**
117
+ * Detect vagueness in system parts
118
+ */
119
+ function detectVagueness(systemPart, kind) {
120
+ const { parsed } = systemPart;
121
+ // Check for overly vague "at least" with low minimum
122
+ if (parsed.type === 'items' && parsed.atLeast && parsed.min <= 2) {
123
+ return {
124
+ sectionName: '', // Will be filled by caller
125
+ priority: 'medium',
126
+ category: 'vague',
127
+ issue: `"at least ${parsed.min}" is too vague`,
128
+ impact: {
129
+ confidenceGain: 15,
130
+ cognitiveLoadChange: 1,
131
+ estimateAccuracyGain: 20,
132
+ description: 'Very low minimums create wide estimation ranges'
133
+ },
134
+ suggestion: 'Use specific range or higher minimum',
135
+ before: systemPart.raw,
136
+ after: `Items: ${Math.max(3, parsed.min)}-${Math.max(5, parsed.min + 2)}`,
137
+ effort: 'trivial',
138
+ autoFixable: true
139
+ };
140
+ }
141
+ // Check for overly wide ranges
142
+ if (parsed.type === 'items' && parsed.max !== null) {
143
+ const range = parsed.max - parsed.min;
144
+ if (range > 10) {
145
+ return {
146
+ sectionName: '',
147
+ priority: 'medium',
148
+ category: 'vague',
149
+ issue: `Range too wide (${parsed.min}-${parsed.max})`,
150
+ impact: {
151
+ confidenceGain: 10,
152
+ cognitiveLoadChange: 0,
153
+ estimateAccuracyGain: 15,
154
+ description: 'Wide ranges reduce estimation accuracy'
155
+ },
156
+ suggestion: 'Narrow the range to ≤5-7 items',
157
+ before: systemPart.raw,
158
+ after: `Items: ${parsed.min}-${Math.min(parsed.max, parsed.min + 5)}`,
159
+ effort: 'trivial',
160
+ autoFixable: true
161
+ };
162
+ }
163
+ }
164
+ // Check for vague text lengths
165
+ if (parsed.type === 'length' && ['moderate', 'detailed', 'extensive'].includes(parsed.value)) {
166
+ return {
167
+ sectionName: '',
168
+ priority: 'low',
169
+ category: 'vague',
170
+ issue: `"${parsed.value}" is generic - consider specific measurement`,
171
+ impact: {
172
+ confidenceGain: 8,
173
+ cognitiveLoadChange: 1,
174
+ estimateAccuracyGain: 10,
175
+ description: 'Specific measurements (sentences/paragraphs) are more precise'
176
+ },
177
+ suggestion: 'Use specific sentence or paragraph count',
178
+ before: systemPart.raw,
179
+ after: 'Length: 2-3 paragraphs',
180
+ effort: 'trivial',
181
+ autoFixable: true
182
+ };
183
+ }
184
+ return null;
185
+ }
186
+ /**
187
+ * Detect range issues (max < min, etc.)
188
+ */
189
+ function detectRangeIssues(systemPart, section) {
190
+ const { parsed } = systemPart;
191
+ if (parsed.type === 'items' || parsed.type === 'lines') {
192
+ if (parsed.max !== null && parsed.max < parsed.min) {
193
+ return {
194
+ sectionName: section.name,
195
+ priority: 'critical',
196
+ category: 'fix',
197
+ issue: `Invalid range: max (${parsed.max}) < min (${parsed.min})`,
198
+ impact: {
199
+ confidenceGain: 0,
200
+ cognitiveLoadChange: 0,
201
+ estimateAccuracyGain: 0,
202
+ description: 'Invalid range will cause errors'
203
+ },
204
+ suggestion: 'Fix invalid range',
205
+ before: systemPart.raw,
206
+ after: `${parsed.type === 'items' ? 'Items' : 'Lines'}: ${parsed.min}-${parsed.min + 3}`,
207
+ effort: 'trivial',
208
+ autoFixable: true
209
+ };
210
+ }
211
+ }
212
+ if (parsed.type === 'table') {
213
+ const issues = [];
214
+ if (parsed.rows.max !== null && parsed.rows.max < parsed.rows.min) {
215
+ issues.push(`rows: ${parsed.rows.min}-${parsed.rows.max}`);
216
+ }
217
+ if (parsed.columns.max !== null && parsed.columns.max < parsed.columns.min) {
218
+ issues.push(`columns: ${parsed.columns.min}-${parsed.columns.max}`);
219
+ }
220
+ if (issues.length > 0) {
221
+ return {
222
+ sectionName: section.name,
223
+ priority: 'critical',
224
+ category: 'fix',
225
+ issue: `Invalid table range: ${issues.join(', ')}`,
226
+ impact: {
227
+ confidenceGain: 0,
228
+ cognitiveLoadChange: 0,
229
+ estimateAccuracyGain: 0,
230
+ description: 'Invalid range will cause errors'
231
+ },
232
+ suggestion: 'Fix invalid table dimensions',
233
+ before: systemPart.raw,
234
+ after: `Rows: ${parsed.rows.min}-${Math.max(parsed.rows.min + 2, parsed.rows.max || 0)}, Columns: ${parsed.columns.min}`,
235
+ effort: 'trivial',
236
+ autoFixable: true
237
+ };
238
+ }
239
+ }
240
+ return null;
241
+ }
242
+ /**
243
+ * Detect unreasonable values
244
+ */
245
+ function detectUnreasonableValues(systemPart, section) {
246
+ const { parsed } = systemPart;
247
+ if (parsed.type === 'items') {
248
+ if (parsed.min > 50) {
249
+ return {
250
+ sectionName: section.name,
251
+ priority: 'high',
252
+ category: 'fix',
253
+ issue: `${parsed.min} items seems unreasonably high`,
254
+ impact: {
255
+ confidenceGain: 5,
256
+ cognitiveLoadChange: -2,
257
+ estimateAccuracyGain: 10,
258
+ description: 'Very high item counts are unusual and may be errors'
259
+ },
260
+ suggestion: 'Verify item count or reduce',
261
+ before: systemPart.raw,
262
+ after: `Items: 10-15`,
263
+ effort: 'easy',
264
+ autoFixable: false
265
+ };
266
+ }
267
+ }
268
+ if (parsed.type === 'table') {
269
+ if (parsed.rows.min > 30 || parsed.columns.min > 15) {
270
+ return {
271
+ sectionName: section.name,
272
+ priority: 'high',
273
+ category: 'fix',
274
+ issue: `Table size (${parsed.rows.min}×${parsed.columns.min}) seems unreasonably large`,
275
+ impact: {
276
+ confidenceGain: 5,
277
+ cognitiveLoadChange: -2,
278
+ estimateAccuracyGain: 10,
279
+ description: 'Very large tables are difficult for LLMs to generate accurately'
280
+ },
281
+ suggestion: 'Consider reducing table size or splitting',
282
+ before: systemPart.raw,
283
+ after: `Rows: 5-10, Columns: ${Math.min(5, parsed.columns.min)}`,
284
+ effort: 'moderate',
285
+ autoFixable: false
286
+ };
287
+ }
288
+ }
289
+ if (parsed.type === 'lines') {
290
+ if (parsed.min > 200) {
291
+ return {
292
+ sectionName: section.name,
293
+ priority: 'medium',
294
+ category: 'fix',
295
+ issue: `${parsed.min} lines of code is very large`,
296
+ impact: {
297
+ confidenceGain: 3,
298
+ cognitiveLoadChange: -1,
299
+ estimateAccuracyGain: 5,
300
+ description: 'Very long code sections may exceed context limits'
301
+ },
302
+ suggestion: 'Consider breaking into multiple sections',
303
+ before: systemPart.raw,
304
+ after: `Lines: 30-50`,
305
+ effort: 'moderate',
306
+ autoFixable: false
307
+ };
308
+ }
309
+ }
310
+ return null;
311
+ }
312
+ /**
313
+ * Detect anti-patterns
314
+ */
315
+ function detectSectionAntiPatterns(section) {
316
+ const patterns = [];
317
+ const kind = (section.kind || 'text');
318
+ const systemPart = parseSystemPart(section.instruction, kind);
319
+ const isRequired = section.required !== false;
320
+ // Anti-pattern: Required section with no instruction at all
321
+ if (isRequired && !section.instruction) {
322
+ patterns.push({
323
+ sectionName: section.name,
324
+ pattern: 'required_no_instruction',
325
+ severity: 'error',
326
+ why: 'Required sections should always have instructions',
327
+ fix: 'Add instruction with system part and guidance',
328
+ example: 'Length: 2-3 paragraphs. Explain the methodology and findings.'
329
+ });
330
+ }
331
+ // Anti-pattern: System part without period separator before guidance
332
+ if (section.instruction && systemPart && systemPart.guidance === null) {
333
+ const hasMoreText = section.instruction.length > systemPart.raw.length + 5;
334
+ if (hasMoreText) {
335
+ patterns.push({
336
+ sectionName: section.name,
337
+ pattern: 'missing_separator',
338
+ severity: 'warning',
339
+ why: 'System part should end with ". " to separate from guidance',
340
+ fix: 'Add period and space after system part',
341
+ example: 'Items: 3-5. Focus on the most important points.'
342
+ });
343
+ }
344
+ }
345
+ // Anti-pattern: Optional section with higher compliance than required
346
+ if (!isRequired && systemPart) {
347
+ const level = detectSystemPartLevel(systemPart, kind);
348
+ if (level === 3) {
349
+ patterns.push({
350
+ sectionName: section.name,
351
+ pattern: 'optional_high_complexity',
352
+ severity: 'info',
353
+ why: "Optional sections typically don't need L3 precision",
354
+ fix: 'Consider downgrading to L2 to reduce cognitive load',
355
+ example: 'Items: 3-5 (instead of "Items: at least 3")'
356
+ });
357
+ }
358
+ }
359
+ // Anti-pattern: Table with only one dimension specified as range
360
+ if (systemPart?.parsed.type === 'table') {
361
+ const rowsHasRange = systemPart.parsed.rows.max !== null;
362
+ const columnsHasRange = systemPart.parsed.columns.max !== null;
363
+ if (rowsHasRange !== columnsHasRange) {
364
+ patterns.push({
365
+ sectionName: section.name,
366
+ pattern: 'asymmetric_table_spec',
367
+ severity: 'info',
368
+ why: 'Inconsistent precision between rows and columns',
369
+ fix: 'Either specify both as ranges or both as exact numbers',
370
+ example: 'Rows: 3-5, Columns: 3-4 (both ranges)'
371
+ });
372
+ }
373
+ }
374
+ // Anti-pattern: Using approximate (~) for small numbers
375
+ if (systemPart?.parsed.type === 'lines' && systemPart.parsed.approximate && systemPart.parsed.min < 20) {
376
+ patterns.push({
377
+ sectionName: section.name,
378
+ pattern: 'approximate_small_value',
379
+ severity: 'warning',
380
+ why: 'Approximate (~) should be used for larger values (50+)',
381
+ fix: 'Use exact number or range for small values',
382
+ example: 'Lines: 15-20 (instead of ~15)'
383
+ });
384
+ }
385
+ return patterns;
386
+ }
387
+ /**
388
+ * Detect spec-level optimizations
389
+ */
390
+ function detectSpecOptimizations(spec) {
391
+ const opts = [];
392
+ if (spec.sections.length === 0)
393
+ return opts;
394
+ const firstKind = (spec.sections[0].kind || 'text');
395
+ // Optimization: All sections at L1, could upgrade key ones to L2
396
+ const levels = spec.sections
397
+ .map(s => parseSystemPart(s.instruction, (s.kind || 'text')))
398
+ .filter(sp => sp !== null)
399
+ .map(sp => detectSystemPartLevel(sp, firstKind));
400
+ const allL1 = levels.length > 0 && levels.every(l => l === 1);
401
+ if (allL1 && levels.length >= 3) {
402
+ opts.push({
403
+ type: 'rebalance',
404
+ scope: 'spec',
405
+ description: 'All sections are L1 - consider upgrading required sections to L2',
406
+ benefit: 'Better token estimation accuracy with minimal effort increase',
407
+ effort: 'easy',
408
+ sections: spec.sections.filter(s => s.required !== false).map(s => s.name)
409
+ });
410
+ }
411
+ // Optimization: Mix of L1, L2, L3 - could standardize
412
+ const hasL1 = levels.some(l => l === 1);
413
+ const hasL2 = levels.some(l => l === 2);
414
+ const hasL3 = levels.some(l => l === 3);
415
+ const varietyCount = [hasL1, hasL2, hasL3].filter(Boolean).length;
416
+ if (varietyCount === 3) {
417
+ opts.push({
418
+ type: 'rebalance',
419
+ scope: 'spec',
420
+ description: 'Spec has sections at L1, L2, and L3 - consider standardizing',
421
+ benefit: 'More consistent cognitive load and maintenance',
422
+ effort: 'moderate',
423
+ sections: undefined
424
+ });
425
+ }
426
+ // Optimization: Too many sections without system parts
427
+ const withoutSystemParts = spec.sections.filter(s => parseSystemPart(s.instruction, (s.kind || 'text')) === null);
428
+ if (withoutSystemParts.length > spec.sections.length * 0.3) {
429
+ opts.push({
430
+ type: 'simplify',
431
+ scope: 'spec',
432
+ description: `${withoutSystemParts.length} sections missing system parts`,
433
+ benefit: 'Adding system parts will greatly improve token estimation',
434
+ effort: 'easy',
435
+ sections: withoutSystemParts.map(s => s.name)
436
+ });
437
+ }
438
+ // Optimization: Very large spec (10+ sections)
439
+ if (spec.sections.length > 10) {
440
+ opts.push({
441
+ type: 'split',
442
+ scope: 'spec',
443
+ description: `Spec has ${spec.sections.length} sections - consider splitting`,
444
+ benefit: 'Smaller specs are easier to maintain and use',
445
+ effort: 'hard',
446
+ sections: undefined
447
+ });
448
+ }
449
+ // Optimization: All text sections could benefit from more specific lengths
450
+ const textSections = spec.sections.filter(s => (s.kind || 'text') === 'text');
451
+ const vagueLengths = textSections.filter(s => {
452
+ const sp = parseSystemPart(s.instruction, 'text');
453
+ return sp?.parsed.type === 'length' &&
454
+ ['brief', 'moderate', 'detailed', 'extensive'].includes(sp.parsed.value);
455
+ });
456
+ if (textSections.length > 0 && vagueLengths.length === textSections.length && textSections.length >= 3) {
457
+ opts.push({
458
+ type: 'simplify',
459
+ scope: 'spec',
460
+ description: 'All text sections use generic lengths (brief/moderate/etc)',
461
+ benefit: 'Upgrading to specific counts (1-2 sentences, 2-3 paragraphs) improves accuracy',
462
+ effort: 'easy',
463
+ sections: vagueLengths.map(s => s.name)
464
+ });
465
+ }
466
+ return opts;
467
+ }
468
+ /**
469
+ * Helper: Generate suggested instruction
470
+ */
471
+ function generateSuggestedInstruction(section, level) {
472
+ const kind = (section.kind || 'text');
473
+ const systemPart = suggestSystemPartForLevel(kind, level);
474
+ const guidance = generateDefaultGuidance(kind);
475
+ if (section.instruction) {
476
+ return `${systemPart}. ${section.instruction}`;
477
+ }
478
+ return `${systemPart}. ${guidance}`;
479
+ }
480
+ /**
481
+ * Helper: Suggest system part for level
482
+ */
483
+ function suggestSystemPartForLevel(kind, level) {
484
+ if (level <= 1) {
485
+ const defaults = {
486
+ 'text': 'Length: brief',
487
+ 'list': 'Items: 3',
488
+ 'ordered_list': 'Items: 3',
489
+ 'code': 'Lines: 10',
490
+ 'table': 'Rows: 3, Columns: 3',
491
+ 'ordered_table': 'Rows: 3, Columns: 3'
492
+ };
493
+ return defaults[kind] || 'Length: moderate';
494
+ }
495
+ if (level === 2) {
496
+ const defaults = {
497
+ 'text': 'Length: 1-2 sentences',
498
+ 'list': 'Items: 3-5',
499
+ 'ordered_list': 'Items: 3-5',
500
+ 'code': 'Lines: 10-20',
501
+ 'table': 'Rows: 3-5, Columns: 3',
502
+ 'ordered_table': 'Rows: 3-5, Columns: 3'
503
+ };
504
+ return defaults[kind] || 'Length: 1-2 sentences';
505
+ }
506
+ // Level 3
507
+ const defaults = {
508
+ 'text': 'Length: 1-2 sentences',
509
+ 'list': 'Items: at least 3',
510
+ 'ordered_list': 'Items: at least 3',
511
+ 'code': 'Lines: ~20',
512
+ 'table': 'Rows: at least 3, Columns: 3',
513
+ 'ordered_table': 'Rows: at least 3, Columns: 3'
514
+ };
515
+ return defaults[kind] || 'Length: 1-2 sentences';
516
+ }
517
+ /**
518
+ * Helper: Upgrade instruction to higher level
519
+ */
520
+ function upgradeInstruction(section, systemPart, currentLevel, targetLevel) {
521
+ const { parsed, guidance } = systemPart;
522
+ let newSystemPart = '';
523
+ if (currentLevel === 1 && targetLevel >= 2) {
524
+ // L1 \u2192 L2: Add ranges
525
+ switch (parsed.type) {
526
+ case 'length':
527
+ newSystemPart = 'Length: 2-3 paragraphs';
528
+ break;
529
+ case 'items':
530
+ newSystemPart = `Items: ${parsed.min}-${parsed.min + 2}`;
531
+ break;
532
+ case 'lines':
533
+ newSystemPart = `Lines: ${parsed.min}-${parsed.min + 10}`;
534
+ break;
535
+ case 'table':
536
+ newSystemPart = `Rows: ${parsed.rows.min}-${parsed.rows.min + 2}, Columns: ${parsed.columns.min}`;
537
+ break;
538
+ }
539
+ }
540
+ else if (currentLevel === 2 && targetLevel === 3) {
541
+ // L2 \u2192 L3: Add "at least" or "~"
542
+ switch (parsed.type) {
543
+ case 'items':
544
+ newSystemPart = `Items: at least ${parsed.min}`;
545
+ break;
546
+ case 'lines':
547
+ newSystemPart = `Lines: ~${parsed.min}`;
548
+ break;
549
+ case 'table':
550
+ newSystemPart = `Rows: at least ${parsed.rows.min}, Columns: ${parsed.columns.min}`;
551
+ break;
552
+ default:
553
+ newSystemPart = systemPart.raw;
554
+ }
555
+ }
556
+ else {
557
+ newSystemPart = systemPart.raw;
558
+ }
559
+ return guidance ? `${newSystemPart}. ${guidance}` : newSystemPart;
560
+ }
561
+ /**
562
+ * Helper: Generate default guidance
563
+ */
564
+ function generateDefaultGuidance(kind) {
565
+ const defaults = {
566
+ 'text': 'Provide clear and concise explanation',
567
+ 'list': 'Focus on the most important points',
568
+ 'ordered_list': 'List in order of priority or sequence',
569
+ 'table': 'Include relevant data with clear headers',
570
+ 'ordered_table': 'Include relevant data with clear headers',
571
+ 'code': 'Include comments and error handling'
572
+ };
573
+ return defaults[kind] || 'Provide relevant content';
574
+ }
575
+ /**
576
+ * Calculate improvement summary
577
+ */
578
+ function calculateImprovementSummary(improvements, quickWins) {
579
+ const criticalCount = improvements.filter(i => i.priority === 'critical').length;
580
+ const potentialConfidenceGain = improvements.reduce((sum, i) => sum + i.impact.confidenceGain, 0);
581
+ let estimatedEffort = 'minimal';
582
+ if (improvements.length > 10)
583
+ estimatedEffort = 'significant';
584
+ else if (improvements.length > 5)
585
+ estimatedEffort = 'moderate';
586
+ return {
587
+ totalImprovements: improvements.length,
588
+ criticalCount,
589
+ potentialConfidenceGain: Math.min(100, potentialConfidenceGain),
590
+ quickWinCount: quickWins.length,
591
+ estimatedEffort
592
+ };
593
+ }
594
+ // Sorting functions
595
+ function byPriorityAndImpact(a, b) {
596
+ const priorityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
597
+ const pDiff = priorityOrder[a.priority] - priorityOrder[b.priority];
598
+ if (pDiff !== 0)
599
+ return pDiff;
600
+ return b.impact.confidenceGain - a.impact.confidenceGain;
601
+ }
602
+ function byImpact(a, b) {
603
+ return b.impact.confidenceGain - a.impact.confidenceGain;
604
+ }
605
+ /**
606
+ * Format improvement report
607
+ */
608
+ export function formatImprovementReport(analysis) {
609
+ const lines = [];
610
+ lines.push('\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2551');
611
+ lines.push('\u2551 IMPROVEMENT DETECTION REPORT \u2551');
612
+ lines.push('\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d');
613
+ lines.push('');
614
+ // Summary
615
+ lines.push('\ud83d\udcca SUMMARY');
616
+ lines.push('\u2500'.repeat(50));
617
+ lines.push(`Total improvements found: ${analysis.summary.totalImprovements}`);
618
+ lines.push(`Critical issues: ${analysis.summary.criticalCount}`);
619
+ lines.push(`Quick wins available: ${analysis.summary.quickWinCount}`);
620
+ lines.push(`Potential confidence gain: +${analysis.summary.potentialConfidenceGain}%`);
621
+ lines.push(`Estimated effort: ${analysis.summary.estimatedEffort}`);
622
+ lines.push('');
623
+ // Quick Wins
624
+ if (analysis.quickWins.length > 0) {
625
+ lines.push('\u26a1 QUICK WINS (Low Effort, High Impact)');
626
+ lines.push('\u2500'.repeat(50));
627
+ analysis.quickWins.slice(0, 5).forEach((imp, i) => {
628
+ lines.push(`${i + 1}. "${imp.sectionName}" - ${imp.issue}`);
629
+ lines.push(` Impact: +${imp.impact.confidenceGain}% confidence, +${imp.impact.estimateAccuracyGain}% accuracy`);
630
+ lines.push(` Before: ${imp.before}`);
631
+ lines.push(` After: ${imp.after}`);
632
+ lines.push('');
633
+ });
634
+ }
635
+ // Critical Issues
636
+ const critical = analysis.improvements.filter(i => i.priority === 'critical');
637
+ if (critical.length > 0) {
638
+ lines.push('\ud83d\udd34 CRITICAL ISSUES');
639
+ lines.push('\u2500'.repeat(50));
640
+ critical.forEach((imp, i) => {
641
+ lines.push(`${i + 1}. "${imp.sectionName}" - ${imp.issue}`);
642
+ lines.push(` Fix: ${imp.suggestion}`);
643
+ lines.push(` Before: ${imp.before}`);
644
+ lines.push(` After: ${imp.after}`);
645
+ lines.push('');
646
+ });
647
+ }
648
+ // Anti-Patterns
649
+ if (analysis.antiPatterns.length > 0) {
650
+ lines.push('\u26a0\ufe0f ANTI-PATTERNS DETECTED');
651
+ lines.push('\u2500'.repeat(50));
652
+ analysis.antiPatterns.forEach((ap, i) => {
653
+ const icon = ap.severity === 'error' ? '\u274c' : ap.severity === 'warning' ? '\u26a0\ufe0f' : '\u2139\ufe0f';
654
+ lines.push(`${i + 1}. ${icon} "${ap.sectionName}" - ${ap.pattern}`);
655
+ lines.push(` Why: ${ap.why}`);
656
+ lines.push(` Fix: ${ap.fix}`);
657
+ lines.push(` Example: ${ap.example}`);
658
+ lines.push('');
659
+ });
660
+ }
661
+ // Optimizations
662
+ if (analysis.optimizations.length > 0) {
663
+ lines.push('\ud83d\udca1 OPTIMIZATION OPPORTUNITIES');
664
+ lines.push('\u2500'.repeat(50));
665
+ analysis.optimizations.forEach((opt, i) => {
666
+ lines.push(`${i + 1}. [${opt.type.toUpperCase()}] ${opt.description}`);
667
+ lines.push(` Benefit: ${opt.benefit}`);
668
+ lines.push(` Effort: ${opt.effort}`);
669
+ if (opt.sections) {
670
+ lines.push(` Sections: ${opt.sections.join(', ')}`);
671
+ }
672
+ lines.push('');
673
+ });
674
+ }
675
+ // All Improvements (grouped by category)
676
+ lines.push('\ud83d\udccb ALL IMPROVEMENTS');
677
+ lines.push('\u2500'.repeat(50));
678
+ const byCategory = {
679
+ missing: analysis.improvements.filter(i => i.category === 'missing'),
680
+ vague: analysis.improvements.filter(i => i.category === 'vague'),
681
+ upgrade: analysis.improvements.filter(i => i.category === 'upgrade'),
682
+ fix: analysis.improvements.filter(i => i.category === 'fix'),
683
+ optimize: analysis.improvements.filter(i => i.category === 'optimize')
684
+ };
685
+ Object.entries(byCategory).forEach(([category, imps]) => {
686
+ if (imps.length > 0) {
687
+ lines.push(`\n${category.toUpperCase()} (${imps.length}):`);
688
+ imps.forEach(imp => {
689
+ const icon = imp.priority === 'critical' ? '\ud83d\udd34' :
690
+ imp.priority === 'high' ? '\ud83d\udfe0' :
691
+ imp.priority === 'medium' ? '\ud83d\udfe1' : '\ud83d\udfe2';
692
+ lines.push(` ${icon} ${imp.sectionName}: ${imp.issue}`);
693
+ });
694
+ }
695
+ });
696
+ return lines.join('\n');
697
+ }